History

feat(cloud): add team management page (#157)

* feat(cloud): add team management page with WorkOS org members Custom team page using WorkOS API for listing members, inviting, removing, and changing roles. Admin-gated write operations with server-side role checks. Uses Effect API layer pattern with typed endpoints and effect-atom for client-side state. Also fixes Sonner toaster to use system theme detection instead of next-themes, removes next-themes dependency. * style: format * chore: remove unused widget token endpoint and getWidgetToken * fix: remove any types from team handler tests * style: format * fix: add @types/node to storage-postgres for tsgo compat * fix: resolve merge conflict in test file * style: format * fix: resolve tsgo typecheck errors in billing and test files * fix: add explicit Plan types for tsgo compatibility * style: format * fix: cast empty array for tsgo type inference

Rhys Sullivan committed 7 months ago

Commit ec8d8861 parent 261c7b219 files · +1079 −16Browse files

Showing the changes to apps/cloud/src/api.ts (1 of 19 files). Show all

Unified · Split

apps/cloud/src/api.ts+27−0
@@ -46,6 +46,8 @@ import { · 45 unmodified lines
4646 import { WorkOSAuth } from "./auth/workos";
4747 import { DbService } from "./services/db";
4848 import { createOrgExecutor } from "./services/executor";
49+import { TeamOrgApi } from "./team/compose";
50+import { TeamHandlers } from "./team/handlers";
4951 import { server } from "./env";
5052
5153 // ---------------------------------------------------------------------------
@@ -90,6 +92,11 @@ const NonProtectedApiLive = HttpApiBuilder.api(NonProtectedApi).pipe( · 38 unmodified lines
9092 Layer.provideMerge(SessionAuthLive),
9193 );
9294
95+const TeamApiLive = HttpApiBuilder.api(TeamOrgApi).pipe(
96+ Layer.provide(TeamHandlers),
97+ Layer.provideMerge(OrgAuthLive),
98+);
99+
93100 // ---------------------------------------------------------------------------
94101 // Public auth web handler
95102 // ---------------------------------------------------------------------------
@@ -113,6 +120,12 @@ const createNonProtectedHandler = () => · 17 unmodified lines
113120 { middleware: HttpMiddleware.logger },
114121 );
115122
123+const createTeamHandler = () =>
124+ HttpApiBuilder.toWebHandler(
125+ TeamApiLive.pipe(Layer.provideMerge(SharedServices), Layer.provideMerge(RouterConfig)),
126+ { middleware: HttpMiddleware.logger },
127+ );
128+
116129 // ---------------------------------------------------------------------------
117130 // Protected handler — must be built per-request because the executor varies
118131 // ---------------------------------------------------------------------------
@@ -158,6 +171,7 @@ const buildProtectedHandler = ( · 39 unmodified lines
158171
159172 const isAuthPath = (pathname: string): boolean => pathname.startsWith("/auth/");
160173 const isAutumnPath = (pathname: string): boolean => pathname.startsWith("/autumn/");
174+const isTeamPath = (pathname: string): boolean => pathname.startsWith("/team/");
161175 const isExecutionPath = (pathname: string): boolean =>
162176 pathname === "/executions" || /^\/executions\/[^/]+\/resume$/.test(pathname);
163177
@@ -241,9 +255,22 @@ const handleAutumnRequest = async (request: Request): Promise<Response> => { · 77 unmodified lines
241255 );
242256 };
243257
258+// ---------------------------------------------------------------------------
259+// Widget token endpoint — returns a WorkOS widget token for the session user
260+// ---------------------------------------------------------------------------
261+
244262 export const handleApiRequest = async (request: Request): Promise<Response> => {
245263 const pathname = new URL(request.url).pathname;
246264
265+ if (isTeamPath(pathname)) {
266+ const handler = createTeamHandler();
267+ try {
268+ return await handler.handler(request);
269+ } finally {
270+ await handler.dispose();
271+ }
272+ }
273+
247274 if (isAutumnPath(pathname)) {
248275 return handleAutumnRequest(request);
249276 }