diff --git a/packages/workspace-manager/src/middleware/iap.ts b/packages/workspace-manager/src/middleware/iap.ts index 28b1fbf8fe..f5964ab6d7 100644 --- a/packages/workspace-manager/src/middleware/iap.ts +++ b/packages/workspace-manager/src/middleware/iap.ts @@ -10,6 +10,7 @@ export interface AuthenticatedRequest extends Request { user: { id: string; email: string; + org_id?: string; }; } @@ -20,6 +21,7 @@ export interface AuthenticatedRequest extends Request { export const iapMiddleware = (req: Request, res: Response, next: NextFunction) => { const userEmail = req.header('x-goog-authenticated-user-email'); const userId = req.header('x-goog-authenticated-user-id'); + const orgId = req.header('x-goog-authenticated-user-org'); // If running locally or without IAP, use a dev user if (!userEmail || !userId) { @@ -28,9 +30,10 @@ export const iapMiddleware = (req: Request, res: Response, next: NextFunction) = return; } - (req as AuthenticatedRequest).user = { + (req as unknown as AuthenticatedRequest).user = { id: 'dev-user-id', email: 'dev-user@google.com', + org_id: 'dev-org-id', }; next(); return; @@ -40,9 +43,10 @@ export const iapMiddleware = (req: Request, res: Response, next: NextFunction) = const cleanId = userId.replace('accounts.google.com:', ''); const cleanEmail = userEmail.replace('accounts.google.com:', ''); - (req as AuthenticatedRequest).user = { + (req as unknown as AuthenticatedRequest).user = { id: cleanId, email: cleanEmail, + org_id: orgId, }; next(); diff --git a/packages/workspace-manager/src/routes/workspaceRoutes.ts b/packages/workspace-manager/src/routes/workspaceRoutes.ts index 147ce14911..f5d933884f 100644 --- a/packages/workspace-manager/src/routes/workspaceRoutes.ts +++ b/packages/workspace-manager/src/routes/workspaceRoutes.ts @@ -20,12 +20,17 @@ const CreateWorkspaceSchema = z.object({ machineType: z.string().optional().default('e2-standard-4'), imageTag: z.string().optional().default('latest'), zone: z.string().optional().default('us-west1-a'), + orgId: z.string().optional(), + repoId: z.string().optional(), }); router.get('/', async (req, res) => { try { const authReq = req as unknown as AuthenticatedRequest; - const workspaces = await workspaceService.listWorkspaces(authReq.user.id); + const workspaces = await workspaceService.listWorkspacesForUser( + authReq.user.id, + authReq.user.org_id + ); res.json(workspaces); } catch (error) { const message = error instanceof Error ? error.message : String(error); @@ -42,7 +47,7 @@ router.post('/', async (req, res) => { return; } - const { name, machineType, imageTag, zone } = validation.data; + const { name, machineType, imageTag, zone, orgId, repoId } = validation.data; const workspaceId = uuidv4(); const instanceName = `workspace-${workspaceId.slice(0, 8)}`; @@ -57,6 +62,8 @@ router.post('/', async (req, res) => { project_id: computeService.getProjectId(), created_at: now, last_connected_at: now, + org_id: orgId || authReq.user.org_id, + repo_id: repoId, }; // 1. Save to state store diff --git a/packages/workspace-manager/src/services/workspaceService.ts b/packages/workspace-manager/src/services/workspaceService.ts index 864d83108f..b6334733ba 100644 --- a/packages/workspace-manager/src/services/workspaceService.ts +++ b/packages/workspace-manager/src/services/workspaceService.ts @@ -16,6 +16,9 @@ export interface WorkspaceData { project_id: string; created_at: string; last_connected_at: string; + team_id?: string; + org_id?: string; + repo_id?: string; } export interface WorkspaceRecord extends WorkspaceData { @@ -46,6 +49,37 @@ export class WorkspaceService { }); } + async listWorkspacesForUser(ownerId: string, orgId?: string): Promise { + // 1. Get workspaces owned by user + const userSnapshot = await this.getCollection() + .where('owner_id', '==', ownerId) + .get(); + + const userWorkspaces = userSnapshot.docs.map(doc => ({ + id: doc.id, + ...(doc.data() as WorkspaceData), + })); + + // 2. Get workspaces shared with user's org (if orgId provided) + if (orgId) { + const orgSnapshot = await this.getCollection() + .where('org_id', '==', orgId) + .get(); + + const orgWorkspaces = orgSnapshot.docs.map(doc => ({ + id: doc.id, + ...(doc.data() as WorkspaceData), + })); + + // Combine and deduplicate by ID + const combined = [...userWorkspaces, ...orgWorkspaces]; + const unique = Array.from(new Map(combined.map(ws => [ws.id, ws])).values()); + return unique; + } + + return userWorkspaces; + } + async listWorkspaces(ownerId: string): Promise { const snapshot = await this.getCollection() .where('owner_id', '==', ownerId)