Files
stack/apps/api/src/workspaces/workspaces.controller.ts
Jason Woltje 78ff8f8e70
Some checks failed
ci/woodpecker/push/ci Pipeline failed
fix: GET workspace members endpoint (#635)
Co-authored-by: Jason Woltje <jason@diversecanvas.com>
Co-committed-by: Jason Woltje <jason@diversecanvas.com>
2026-03-01 21:53:51 +00:00

105 lines
3.6 KiB
TypeScript

import { Body, Controller, Delete, Get, Param, Patch, Post, UseGuards } from "@nestjs/common";
import { WorkspacesService } from "./workspaces.service";
import { AuthGuard } from "../auth/guards/auth.guard";
import { CurrentUser } from "../auth/decorators/current-user.decorator";
import { WorkspaceGuard, PermissionGuard } from "../common/guards";
import { Permission, RequirePermission } from "../common/decorators";
import type { WorkspaceMember } from "@prisma/client";
import type { AuthenticatedUser } from "../common/types/user.types";
import { AddMemberDto, UpdateMemberRoleDto, WorkspaceResponseDto } from "./dto";
/**
* User-scoped workspace operations.
*
* Intentionally does NOT use WorkspaceGuard — these routes operate across all
* workspaces the user belongs to, not within a single workspace context.
*/
@Controller("workspaces")
@UseGuards(AuthGuard)
export class WorkspacesController {
constructor(private readonly workspacesService: WorkspacesService) {}
/**
* GET /api/workspaces
* Returns workspaces the authenticated user is a member of.
* Auto-provisions a default workspace if the user has none.
*/
@Get()
async getUserWorkspaces(@CurrentUser() user: AuthenticatedUser): Promise<WorkspaceResponseDto[]> {
return this.workspacesService.getUserWorkspaces(user.id);
}
/**
* GET /api/workspaces/:workspaceId/stats
* Returns member, project, and domain counts for a workspace.
*/
@Get(":workspaceId/stats")
async getStats(@Param("workspaceId") workspaceId: string) {
return this.workspacesService.getStats(workspaceId);
}
/**
* GET /api/workspaces/:workspaceId/members
* Returns the list of members for a workspace.
*/
@Get(":workspaceId/members")
@UseGuards(WorkspaceGuard)
async getMembers(@Param("workspaceId") workspaceId: string) {
return this.workspacesService.getMembers(workspaceId);
}
/**
* POST /api/workspaces/:workspaceId/members
* Add a member to a workspace with the specified role.
* Requires: ADMIN role or higher.
*/
@Post(":workspaceId/members")
@UseGuards(WorkspaceGuard, PermissionGuard)
@RequirePermission(Permission.WORKSPACE_ADMIN)
async addMember(
@Param("workspaceId") workspaceId: string,
@Body() addMemberDto: AddMemberDto,
@CurrentUser() user: AuthenticatedUser
): Promise<WorkspaceMember> {
return this.workspacesService.addMember(workspaceId, user.id, addMemberDto);
}
/**
* PATCH /api/workspaces/:workspaceId/members/:userId
* Change a member role in a workspace.
* Requires: ADMIN role or higher.
*/
@Patch(":workspaceId/members/:userId")
@UseGuards(WorkspaceGuard, PermissionGuard)
@RequirePermission(Permission.WORKSPACE_ADMIN)
async updateMemberRole(
@Param("workspaceId") workspaceId: string,
@Param("userId") targetUserId: string,
@Body() updateMemberRoleDto: UpdateMemberRoleDto,
@CurrentUser() user: AuthenticatedUser
): Promise<WorkspaceMember> {
return this.workspacesService.updateMemberRole(
workspaceId,
user.id,
targetUserId,
updateMemberRoleDto
);
}
/**
* DELETE /api/workspaces/:workspaceId/members/:userId
* Remove a member from a workspace.
* Requires: ADMIN role or higher.
*/
@Delete(":workspaceId/members/:userId")
@UseGuards(WorkspaceGuard, PermissionGuard)
@RequirePermission(Permission.WORKSPACE_ADMIN)
async removeMember(
@Param("workspaceId") workspaceId: string,
@Param("userId") targetUserId: string,
@CurrentUser() user: AuthenticatedUser
): Promise<void> {
await this.workspacesService.removeMember(workspaceId, user.id, targetUserId);
}
}