fix(api): add WorkspaceGuard to controllers and fix route ordering
This commit is contained in:
@@ -1,59 +1,44 @@
|
||||
import { Controller, Get, Query, Param, UseGuards, Request } from "@nestjs/common";
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Query,
|
||||
Param,
|
||||
UseGuards,
|
||||
} from "@nestjs/common";
|
||||
import { ActivityService } from "./activity.service";
|
||||
import { EntityType } from "@prisma/client";
|
||||
import type { QueryActivityLogDto } from "./dto";
|
||||
import { AuthGuard } from "../auth/guards/auth.guard";
|
||||
import { WorkspaceGuard, PermissionGuard } from "../common/guards";
|
||||
import { Workspace, Permission, RequirePermission } from "../common/decorators";
|
||||
|
||||
/**
|
||||
* Controller for activity log endpoints
|
||||
* All endpoints require authentication
|
||||
*/
|
||||
@Controller("activity")
|
||||
@UseGuards(AuthGuard)
|
||||
@UseGuards(AuthGuard, WorkspaceGuard, PermissionGuard)
|
||||
export class ActivityController {
|
||||
constructor(private readonly activityService: ActivityService) {}
|
||||
|
||||
/**
|
||||
* GET /api/activity
|
||||
* Get paginated activity logs with optional filters
|
||||
* workspaceId is extracted from authenticated user context
|
||||
*/
|
||||
@Get()
|
||||
async findAll(@Query() query: QueryActivityLogDto, @Request() req: any) {
|
||||
// Extract workspaceId from authenticated user
|
||||
const workspaceId = req.user?.workspaceId || query.workspaceId;
|
||||
@RequirePermission(Permission.WORKSPACE_ANY)
|
||||
async findAll(
|
||||
@Query() query: QueryActivityLogDto,
|
||||
@Workspace() workspaceId: string
|
||||
) {
|
||||
return this.activityService.findAll({ ...query, workspaceId });
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/activity/:id
|
||||
* Get a single activity log by ID
|
||||
* workspaceId is extracted from authenticated user context
|
||||
*/
|
||||
@Get(":id")
|
||||
async findOne(@Param("id") id: string, @Request() req: any) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
if (!workspaceId) {
|
||||
throw new Error("User workspaceId not found");
|
||||
}
|
||||
@RequirePermission(Permission.WORKSPACE_ANY)
|
||||
async findOne(@Param("id") id: string, @Workspace() workspaceId: string) {
|
||||
return this.activityService.findOne(id, workspaceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/activity/audit/:entityType/:entityId
|
||||
* Get audit trail for a specific entity
|
||||
* workspaceId is extracted from authenticated user context
|
||||
*/
|
||||
@Get("audit/:entityType/:entityId")
|
||||
@RequirePermission(Permission.WORKSPACE_ANY)
|
||||
async getAuditTrail(
|
||||
@Request() req: any,
|
||||
@Param("entityType") entityType: EntityType,
|
||||
@Param("entityId") entityId: string
|
||||
@Param("entityId") entityId: string,
|
||||
@Workspace() workspaceId: string
|
||||
) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
if (!workspaceId) {
|
||||
throw new Error("User workspaceId not found");
|
||||
}
|
||||
return this.activityService.getAuditTrail(workspaceId, entityType, entityId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,28 +8,6 @@ import { CurrentUser } from "./decorators/current-user.decorator";
|
||||
export class AuthController {
|
||||
constructor(private readonly authService: AuthService) {}
|
||||
|
||||
/**
|
||||
* Handle all BetterAuth routes
|
||||
* BetterAuth provides built-in handlers for:
|
||||
* - /auth/sign-in
|
||||
* - /auth/sign-up
|
||||
* - /auth/sign-out
|
||||
* - /auth/callback/authentik
|
||||
* - /auth/session
|
||||
* etc.
|
||||
*
|
||||
* Note: BetterAuth expects a Fetch API-compatible Request object.
|
||||
* NestJS converts the incoming Express request to be compatible at runtime.
|
||||
*/
|
||||
@All("*")
|
||||
async handleAuth(@Req() req: Request) {
|
||||
const auth = this.authService.getAuth();
|
||||
return auth.handler(req);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current user profile (protected route example)
|
||||
*/
|
||||
@Get("profile")
|
||||
@UseGuards(AuthGuard)
|
||||
getProfile(@CurrentUser() user: AuthUser) {
|
||||
@@ -39,4 +17,10 @@ export class AuthController {
|
||||
name: user.name,
|
||||
};
|
||||
}
|
||||
|
||||
@All("*")
|
||||
async handleAuth(@Req() req: Request) {
|
||||
const auth = this.authService.getAuth();
|
||||
return auth.handler(req);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,97 +8,62 @@ import {
|
||||
Param,
|
||||
Query,
|
||||
UseGuards,
|
||||
Request,
|
||||
UnauthorizedException,
|
||||
} from "@nestjs/common";
|
||||
import { DomainsService } from "./domains.service";
|
||||
import { CreateDomainDto, UpdateDomainDto, QueryDomainsDto } from "./dto";
|
||||
import { AuthGuard } from "../auth/guards/auth.guard";
|
||||
import { WorkspaceGuard, PermissionGuard } from "../common/guards";
|
||||
import { Workspace, Permission, RequirePermission } from "../common/decorators";
|
||||
import { CurrentUser } from "../auth/decorators/current-user.decorator";
|
||||
|
||||
/**
|
||||
* Controller for domain endpoints
|
||||
* All endpoints require authentication
|
||||
*/
|
||||
@Controller("domains")
|
||||
@UseGuards(AuthGuard)
|
||||
@UseGuards(AuthGuard, WorkspaceGuard, PermissionGuard)
|
||||
export class DomainsController {
|
||||
constructor(private readonly domainsService: DomainsService) {}
|
||||
|
||||
/**
|
||||
* POST /api/domains
|
||||
* Create a new domain
|
||||
*/
|
||||
@Post()
|
||||
async create(@Body() createDomainDto: CreateDomainDto, @Request() req: any) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
const userId = req.user?.id;
|
||||
|
||||
if (!workspaceId || !userId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
|
||||
return this.domainsService.create(workspaceId, userId, createDomainDto);
|
||||
@RequirePermission(Permission.WORKSPACE_MEMBER)
|
||||
async create(
|
||||
@Body() createDomainDto: CreateDomainDto,
|
||||
@Workspace() workspaceId: string,
|
||||
@CurrentUser() user: any
|
||||
) {
|
||||
return this.domainsService.create(workspaceId, user.id, createDomainDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/domains
|
||||
* Get paginated domains with optional filters
|
||||
*/
|
||||
@Get()
|
||||
async findAll(@Query() query: QueryDomainsDto, @Request() req: any) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
if (!workspaceId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
@RequirePermission(Permission.WORKSPACE_ANY)
|
||||
async findAll(
|
||||
@Query() query: QueryDomainsDto,
|
||||
@Workspace() workspaceId: string
|
||||
) {
|
||||
return this.domainsService.findAll({ ...query, workspaceId });
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/domains/:id
|
||||
* Get a single domain by ID
|
||||
*/
|
||||
@Get(":id")
|
||||
async findOne(@Param("id") id: string, @Request() req: any) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
if (!workspaceId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
@RequirePermission(Permission.WORKSPACE_ANY)
|
||||
async findOne(@Param("id") id: string, @Workspace() workspaceId: string) {
|
||||
return this.domainsService.findOne(id, workspaceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* PATCH /api/domains/:id
|
||||
* Update a domain
|
||||
*/
|
||||
@Patch(":id")
|
||||
@RequirePermission(Permission.WORKSPACE_MEMBER)
|
||||
async update(
|
||||
@Param("id") id: string,
|
||||
@Body() updateDomainDto: UpdateDomainDto,
|
||||
@Request() req: any
|
||||
@Workspace() workspaceId: string,
|
||||
@CurrentUser() user: any
|
||||
) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
const userId = req.user?.id;
|
||||
|
||||
if (!workspaceId || !userId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
|
||||
return this.domainsService.update(id, workspaceId, userId, updateDomainDto);
|
||||
return this.domainsService.update(id, workspaceId, user.id, updateDomainDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE /api/domains/:id
|
||||
* Delete a domain
|
||||
*/
|
||||
@Delete(":id")
|
||||
async remove(@Param("id") id: string, @Request() req: any) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
const userId = req.user?.id;
|
||||
|
||||
if (!workspaceId || !userId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
|
||||
return this.domainsService.remove(id, workspaceId, userId);
|
||||
@RequirePermission(Permission.WORKSPACE_ADMIN)
|
||||
async remove(
|
||||
@Param("id") id: string,
|
||||
@Workspace() workspaceId: string,
|
||||
@CurrentUser() user: any
|
||||
) {
|
||||
return this.domainsService.remove(id, workspaceId, user.id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,97 +8,71 @@ import {
|
||||
Param,
|
||||
Query,
|
||||
UseGuards,
|
||||
Request,
|
||||
UnauthorizedException,
|
||||
} from "@nestjs/common";
|
||||
import { EventsService } from "./events.service";
|
||||
import { CreateEventDto, UpdateEventDto, QueryEventsDto } from "./dto";
|
||||
import { AuthGuard } from "../auth/guards/auth.guard";
|
||||
import { WorkspaceGuard, PermissionGuard } from "../common/guards";
|
||||
import { Workspace, Permission, RequirePermission } from "../common/decorators";
|
||||
import { CurrentUser } from "../auth/decorators/current-user.decorator";
|
||||
|
||||
/**
|
||||
* Controller for event endpoints
|
||||
* All endpoints require authentication
|
||||
* All endpoints require authentication and workspace context
|
||||
*
|
||||
* Guards are applied in order:
|
||||
* 1. AuthGuard - Verifies user authentication
|
||||
* 2. WorkspaceGuard - Validates workspace access and sets RLS context
|
||||
* 3. PermissionGuard - Checks role-based permissions
|
||||
*/
|
||||
@Controller("events")
|
||||
@UseGuards(AuthGuard)
|
||||
@UseGuards(AuthGuard, WorkspaceGuard, PermissionGuard)
|
||||
export class EventsController {
|
||||
constructor(private readonly eventsService: EventsService) {}
|
||||
|
||||
/**
|
||||
* POST /api/events
|
||||
* Create a new event
|
||||
*/
|
||||
@Post()
|
||||
async create(@Body() createEventDto: CreateEventDto, @Request() req: any) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
const userId = req.user?.id;
|
||||
|
||||
if (!workspaceId || !userId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
|
||||
return this.eventsService.create(workspaceId, userId, createEventDto);
|
||||
@RequirePermission(Permission.WORKSPACE_MEMBER)
|
||||
async create(
|
||||
@Body() createEventDto: CreateEventDto,
|
||||
@Workspace() workspaceId: string,
|
||||
@CurrentUser() user: any
|
||||
) {
|
||||
return this.eventsService.create(workspaceId, user.id, createEventDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/events
|
||||
* Get paginated events with optional filters
|
||||
*/
|
||||
@Get()
|
||||
async findAll(@Query() query: QueryEventsDto, @Request() req: any) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
if (!workspaceId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
@RequirePermission(Permission.WORKSPACE_ANY)
|
||||
async findAll(
|
||||
@Query() query: QueryEventsDto,
|
||||
@Workspace() workspaceId: string
|
||||
) {
|
||||
return this.eventsService.findAll({ ...query, workspaceId });
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/events/:id
|
||||
* Get a single event by ID
|
||||
*/
|
||||
@Get(":id")
|
||||
async findOne(@Param("id") id: string, @Request() req: any) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
if (!workspaceId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
@RequirePermission(Permission.WORKSPACE_ANY)
|
||||
async findOne(@Param("id") id: string, @Workspace() workspaceId: string) {
|
||||
return this.eventsService.findOne(id, workspaceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* PATCH /api/events/:id
|
||||
* Update an event
|
||||
*/
|
||||
@Patch(":id")
|
||||
@RequirePermission(Permission.WORKSPACE_MEMBER)
|
||||
async update(
|
||||
@Param("id") id: string,
|
||||
@Body() updateEventDto: UpdateEventDto,
|
||||
@Request() req: any
|
||||
@Workspace() workspaceId: string,
|
||||
@CurrentUser() user: any
|
||||
) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
const userId = req.user?.id;
|
||||
|
||||
if (!workspaceId || !userId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
|
||||
return this.eventsService.update(id, workspaceId, userId, updateEventDto);
|
||||
return this.eventsService.update(id, workspaceId, user.id, updateEventDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE /api/events/:id
|
||||
* Delete an event
|
||||
*/
|
||||
@Delete(":id")
|
||||
async remove(@Param("id") id: string, @Request() req: any) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
const userId = req.user?.id;
|
||||
|
||||
if (!workspaceId || !userId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
|
||||
return this.eventsService.remove(id, workspaceId, userId);
|
||||
@RequirePermission(Permission.WORKSPACE_ADMIN)
|
||||
async remove(
|
||||
@Param("id") id: string,
|
||||
@Workspace() workspaceId: string,
|
||||
@CurrentUser() user: any
|
||||
) {
|
||||
return this.eventsService.remove(id, workspaceId, user.id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@ import {
|
||||
Param,
|
||||
Query,
|
||||
UseGuards,
|
||||
Request,
|
||||
UnauthorizedException,
|
||||
} from "@nestjs/common";
|
||||
import { IdeasService } from "./ideas.service";
|
||||
import {
|
||||
@@ -19,112 +17,68 @@ import {
|
||||
QueryIdeasDto,
|
||||
} from "./dto";
|
||||
import { AuthGuard } from "../auth/guards/auth.guard";
|
||||
import { WorkspaceGuard, PermissionGuard } from "../common/guards";
|
||||
import { Workspace, Permission, RequirePermission } from "../common/decorators";
|
||||
import { CurrentUser } from "../auth/decorators/current-user.decorator";
|
||||
|
||||
/**
|
||||
* Controller for idea endpoints
|
||||
* All endpoints require authentication
|
||||
*/
|
||||
@Controller("ideas")
|
||||
@UseGuards(AuthGuard)
|
||||
@UseGuards(AuthGuard, WorkspaceGuard, PermissionGuard)
|
||||
export class IdeasController {
|
||||
constructor(private readonly ideasService: IdeasService) {}
|
||||
|
||||
/**
|
||||
* POST /api/ideas/capture
|
||||
* Quick capture endpoint for rapid idea capture
|
||||
* Requires minimal fields: content only (title optional)
|
||||
*/
|
||||
@Post("capture")
|
||||
@RequirePermission(Permission.WORKSPACE_MEMBER)
|
||||
async capture(
|
||||
@Body() captureIdeaDto: CaptureIdeaDto,
|
||||
@Request() req: any
|
||||
@Workspace() workspaceId: string,
|
||||
@CurrentUser() user: any
|
||||
) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
const userId = req.user?.id;
|
||||
|
||||
if (!workspaceId || !userId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
|
||||
return this.ideasService.capture(workspaceId, userId, captureIdeaDto);
|
||||
return this.ideasService.capture(workspaceId, user.id, captureIdeaDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/ideas
|
||||
* Create a new idea with full categorization options
|
||||
*/
|
||||
@Post()
|
||||
async create(@Body() createIdeaDto: CreateIdeaDto, @Request() req: any) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
const userId = req.user?.id;
|
||||
|
||||
if (!workspaceId || !userId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
|
||||
return this.ideasService.create(workspaceId, userId, createIdeaDto);
|
||||
@RequirePermission(Permission.WORKSPACE_MEMBER)
|
||||
async create(
|
||||
@Body() createIdeaDto: CreateIdeaDto,
|
||||
@Workspace() workspaceId: string,
|
||||
@CurrentUser() user: any
|
||||
) {
|
||||
return this.ideasService.create(workspaceId, user.id, createIdeaDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/ideas
|
||||
* Get paginated ideas with optional filters
|
||||
* Supports status, domain, project, category, and search filters
|
||||
*/
|
||||
@Get()
|
||||
async findAll(@Query() query: QueryIdeasDto, @Request() req: any) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
if (!workspaceId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
@RequirePermission(Permission.WORKSPACE_ANY)
|
||||
async findAll(
|
||||
@Query() query: QueryIdeasDto,
|
||||
@Workspace() workspaceId: string
|
||||
) {
|
||||
return this.ideasService.findAll({ ...query, workspaceId });
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/ideas/:id
|
||||
* Get a single idea by ID
|
||||
*/
|
||||
@Get(":id")
|
||||
async findOne(@Param("id") id: string, @Request() req: any) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
if (!workspaceId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
@RequirePermission(Permission.WORKSPACE_ANY)
|
||||
async findOne(@Param("id") id: string, @Workspace() workspaceId: string) {
|
||||
return this.ideasService.findOne(id, workspaceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* PATCH /api/ideas/:id
|
||||
* Update an idea
|
||||
*/
|
||||
@Patch(":id")
|
||||
@RequirePermission(Permission.WORKSPACE_MEMBER)
|
||||
async update(
|
||||
@Param("id") id: string,
|
||||
@Body() updateIdeaDto: UpdateIdeaDto,
|
||||
@Request() req: any
|
||||
@Workspace() workspaceId: string,
|
||||
@CurrentUser() user: any
|
||||
) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
const userId = req.user?.id;
|
||||
|
||||
if (!workspaceId || !userId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
|
||||
return this.ideasService.update(id, workspaceId, userId, updateIdeaDto);
|
||||
return this.ideasService.update(id, workspaceId, user.id, updateIdeaDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE /api/ideas/:id
|
||||
* Delete an idea
|
||||
*/
|
||||
@Delete(":id")
|
||||
async remove(@Param("id") id: string, @Request() req: any) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
const userId = req.user?.id;
|
||||
|
||||
if (!workspaceId || !userId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
|
||||
return this.ideasService.remove(id, workspaceId, userId);
|
||||
@RequirePermission(Permission.WORKSPACE_ADMIN)
|
||||
async remove(
|
||||
@Param("id") id: string,
|
||||
@Workspace() workspaceId: string,
|
||||
@CurrentUser() user: any
|
||||
) {
|
||||
return this.ideasService.remove(id, workspaceId, user.id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,175 +7,70 @@ import {
|
||||
Body,
|
||||
Param,
|
||||
UseGuards,
|
||||
Request,
|
||||
UnauthorizedException,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
} from "@nestjs/common";
|
||||
import { TagsService } from "./tags.service";
|
||||
import { CreateTagDto, UpdateTagDto } from "./dto";
|
||||
import { AuthGuard } from "../auth/guards/auth.guard";
|
||||
import { WorkspaceGuard, PermissionGuard } from "../common/guards";
|
||||
import { Workspace, Permission, RequirePermission } from "../common/decorators";
|
||||
|
||||
/**
|
||||
* Controller for knowledge tag endpoints
|
||||
* All endpoints require authentication and operate within workspace context
|
||||
*/
|
||||
@Controller("knowledge/tags")
|
||||
@UseGuards(AuthGuard)
|
||||
@UseGuards(AuthGuard, WorkspaceGuard, PermissionGuard)
|
||||
export class TagsController {
|
||||
constructor(private readonly tagsService: TagsService) {}
|
||||
|
||||
/**
|
||||
* POST /api/knowledge/tags
|
||||
* Create a new tag
|
||||
*/
|
||||
@Post()
|
||||
@RequirePermission(Permission.WORKSPACE_MEMBER)
|
||||
async create(
|
||||
@Body() createTagDto: CreateTagDto,
|
||||
@Request() req: any
|
||||
): Promise<{
|
||||
id: string;
|
||||
workspaceId: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
color: string | null;
|
||||
description: string | null;
|
||||
}> {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
|
||||
if (!workspaceId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
|
||||
@Workspace() workspaceId: string
|
||||
) {
|
||||
return this.tagsService.create(workspaceId, createTagDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/knowledge/tags
|
||||
* List all tags in the workspace
|
||||
*/
|
||||
@Get()
|
||||
async findAll(@Request() req: any): Promise<
|
||||
Array<{
|
||||
id: string;
|
||||
workspaceId: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
color: string | null;
|
||||
description: string | null;
|
||||
_count: {
|
||||
entries: number;
|
||||
};
|
||||
}>
|
||||
> {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
|
||||
if (!workspaceId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
|
||||
@RequirePermission(Permission.WORKSPACE_ANY)
|
||||
async findAll(@Workspace() workspaceId: string) {
|
||||
return this.tagsService.findAll(workspaceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/knowledge/tags/:slug
|
||||
* Get a single tag by slug
|
||||
*/
|
||||
@Get(":slug")
|
||||
@RequirePermission(Permission.WORKSPACE_ANY)
|
||||
async findOne(
|
||||
@Param("slug") slug: string,
|
||||
@Request() req: any
|
||||
): Promise<{
|
||||
id: string;
|
||||
workspaceId: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
color: string | null;
|
||||
description: string | null;
|
||||
_count: {
|
||||
entries: number;
|
||||
};
|
||||
}> {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
|
||||
if (!workspaceId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
|
||||
@Workspace() workspaceId: string
|
||||
) {
|
||||
return this.tagsService.findOne(slug, workspaceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* PUT /api/knowledge/tags/:slug
|
||||
* Update a tag
|
||||
*/
|
||||
@Put(":slug")
|
||||
@RequirePermission(Permission.WORKSPACE_MEMBER)
|
||||
async update(
|
||||
@Param("slug") slug: string,
|
||||
@Body() updateTagDto: UpdateTagDto,
|
||||
@Request() req: any
|
||||
): Promise<{
|
||||
id: string;
|
||||
workspaceId: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
color: string | null;
|
||||
description: string | null;
|
||||
}> {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
|
||||
if (!workspaceId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
|
||||
@Workspace() workspaceId: string
|
||||
) {
|
||||
return this.tagsService.update(slug, workspaceId, updateTagDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE /api/knowledge/tags/:slug
|
||||
* Delete a tag
|
||||
*/
|
||||
@Delete(":slug")
|
||||
@HttpCode(HttpStatus.NO_CONTENT)
|
||||
@RequirePermission(Permission.WORKSPACE_ADMIN)
|
||||
async remove(
|
||||
@Param("slug") slug: string,
|
||||
@Request() req: any
|
||||
): Promise<void> {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
|
||||
if (!workspaceId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
|
||||
@Workspace() workspaceId: string
|
||||
) {
|
||||
await this.tagsService.remove(slug, workspaceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/knowledge/tags/:slug/entries
|
||||
* Get all entries with this tag
|
||||
*/
|
||||
@Get(":slug/entries")
|
||||
@RequirePermission(Permission.WORKSPACE_ANY)
|
||||
async getEntries(
|
||||
@Param("slug") slug: string,
|
||||
@Request() req: any
|
||||
): Promise<
|
||||
Array<{
|
||||
id: string;
|
||||
slug: string;
|
||||
title: string;
|
||||
summary: string | null;
|
||||
status: string;
|
||||
visibility: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}>
|
||||
> {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
|
||||
if (!workspaceId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
|
||||
@Workspace() workspaceId: string
|
||||
) {
|
||||
return this.tagsService.getEntriesWithTag(slug, workspaceId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,122 +7,75 @@ import {
|
||||
Body,
|
||||
Param,
|
||||
UseGuards,
|
||||
Request,
|
||||
UnauthorizedException,
|
||||
} from "@nestjs/common";
|
||||
import { LayoutsService } from "./layouts.service";
|
||||
import { CreateLayoutDto, UpdateLayoutDto } from "./dto";
|
||||
import { AuthGuard } from "../auth/guards/auth.guard";
|
||||
import { WorkspaceGuard, PermissionGuard } from "../common/guards";
|
||||
import { Workspace, Permission, RequirePermission } from "../common/decorators";
|
||||
import { CurrentUser } from "../auth/decorators/current-user.decorator";
|
||||
|
||||
/**
|
||||
* Controller for user layout endpoints
|
||||
* All endpoints require authentication
|
||||
*/
|
||||
@Controller("layouts")
|
||||
@UseGuards(AuthGuard)
|
||||
@UseGuards(AuthGuard, WorkspaceGuard, PermissionGuard)
|
||||
export class LayoutsController {
|
||||
constructor(private readonly layoutsService: LayoutsService) {}
|
||||
|
||||
/**
|
||||
* GET /api/layouts
|
||||
* Get all layouts for the authenticated user
|
||||
*/
|
||||
@Get()
|
||||
async findAll(@Request() req: any) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
const userId = req.user?.id;
|
||||
|
||||
if (!workspaceId || !userId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
|
||||
return this.layoutsService.findAll(workspaceId, userId);
|
||||
@RequirePermission(Permission.WORKSPACE_ANY)
|
||||
async findAll(
|
||||
@Workspace() workspaceId: string,
|
||||
@CurrentUser() user: any
|
||||
) {
|
||||
return this.layoutsService.findAll(workspaceId, user.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/layouts/:id
|
||||
* Get a single layout by ID
|
||||
*/
|
||||
@Get(":id")
|
||||
async findOne(@Param("id") id: string, @Request() req: any) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
const userId = req.user?.id;
|
||||
|
||||
if (!workspaceId || !userId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
|
||||
return this.layoutsService.findOne(id, workspaceId, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/layouts/default
|
||||
* Get the default layout for the authenticated user
|
||||
* Falls back to the most recently created layout if no default exists
|
||||
*/
|
||||
@Get("default")
|
||||
async findDefault(@Request() req: any) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
const userId = req.user?.id;
|
||||
|
||||
if (!workspaceId || !userId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
|
||||
return this.layoutsService.findDefault(workspaceId, userId);
|
||||
@RequirePermission(Permission.WORKSPACE_ANY)
|
||||
async findDefault(
|
||||
@Workspace() workspaceId: string,
|
||||
@CurrentUser() user: any
|
||||
) {
|
||||
return this.layoutsService.findDefault(workspaceId, user.id);
|
||||
}
|
||||
|
||||
@Get(":id")
|
||||
@RequirePermission(Permission.WORKSPACE_ANY)
|
||||
async findOne(
|
||||
@Param("id") id: string,
|
||||
@Workspace() workspaceId: string,
|
||||
@CurrentUser() user: any
|
||||
) {
|
||||
return this.layoutsService.findOne(id, workspaceId, user.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/layouts
|
||||
* Create a new layout
|
||||
* If isDefault is true, any existing default layout will be unset
|
||||
*/
|
||||
@Post()
|
||||
async create(@Body() createLayoutDto: CreateLayoutDto, @Request() req: any) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
const userId = req.user?.id;
|
||||
|
||||
if (!workspaceId || !userId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
|
||||
return this.layoutsService.create(workspaceId, userId, createLayoutDto);
|
||||
@RequirePermission(Permission.WORKSPACE_MEMBER)
|
||||
async create(
|
||||
@Body() createLayoutDto: CreateLayoutDto,
|
||||
@Workspace() workspaceId: string,
|
||||
@CurrentUser() user: any
|
||||
) {
|
||||
return this.layoutsService.create(workspaceId, user.id, createLayoutDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* PATCH /api/layouts/:id
|
||||
* Update a layout
|
||||
* If isDefault is set to true, any existing default layout will be unset
|
||||
*/
|
||||
@Patch(":id")
|
||||
@RequirePermission(Permission.WORKSPACE_MEMBER)
|
||||
async update(
|
||||
@Param("id") id: string,
|
||||
@Body() updateLayoutDto: UpdateLayoutDto,
|
||||
@Request() req: any
|
||||
@Workspace() workspaceId: string,
|
||||
@CurrentUser() user: any
|
||||
) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
const userId = req.user?.id;
|
||||
|
||||
if (!workspaceId || !userId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
|
||||
return this.layoutsService.update(id, workspaceId, userId, updateLayoutDto);
|
||||
return this.layoutsService.update(id, workspaceId, user.id, updateLayoutDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE /api/layouts/:id
|
||||
* Delete a layout
|
||||
*/
|
||||
@Delete(":id")
|
||||
async remove(@Param("id") id: string, @Request() req: any) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
const userId = req.user?.id;
|
||||
|
||||
if (!workspaceId || !userId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
|
||||
return this.layoutsService.remove(id, workspaceId, userId);
|
||||
@RequirePermission(Permission.WORKSPACE_MEMBER)
|
||||
async remove(
|
||||
@Param("id") id: string,
|
||||
@Workspace() workspaceId: string,
|
||||
@CurrentUser() user: any
|
||||
) {
|
||||
return this.layoutsService.remove(id, workspaceId, user.id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,97 +8,62 @@ import {
|
||||
Param,
|
||||
Query,
|
||||
UseGuards,
|
||||
Request,
|
||||
UnauthorizedException,
|
||||
} from "@nestjs/common";
|
||||
import { ProjectsService } from "./projects.service";
|
||||
import { CreateProjectDto, UpdateProjectDto, QueryProjectsDto } from "./dto";
|
||||
import { AuthGuard } from "../auth/guards/auth.guard";
|
||||
import { WorkspaceGuard, PermissionGuard } from "../common/guards";
|
||||
import { Workspace, Permission, RequirePermission } from "../common/decorators";
|
||||
import { CurrentUser } from "../auth/decorators/current-user.decorator";
|
||||
|
||||
/**
|
||||
* Controller for project endpoints
|
||||
* All endpoints require authentication
|
||||
*/
|
||||
@Controller("projects")
|
||||
@UseGuards(AuthGuard)
|
||||
@UseGuards(AuthGuard, WorkspaceGuard, PermissionGuard)
|
||||
export class ProjectsController {
|
||||
constructor(private readonly projectsService: ProjectsService) {}
|
||||
|
||||
/**
|
||||
* POST /api/projects
|
||||
* Create a new project
|
||||
*/
|
||||
@Post()
|
||||
async create(@Body() createProjectDto: CreateProjectDto, @Request() req: any) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
const userId = req.user?.id;
|
||||
|
||||
if (!workspaceId || !userId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
|
||||
return this.projectsService.create(workspaceId, userId, createProjectDto);
|
||||
@RequirePermission(Permission.WORKSPACE_MEMBER)
|
||||
async create(
|
||||
@Body() createProjectDto: CreateProjectDto,
|
||||
@Workspace() workspaceId: string,
|
||||
@CurrentUser() user: any
|
||||
) {
|
||||
return this.projectsService.create(workspaceId, user.id, createProjectDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/projects
|
||||
* Get paginated projects with optional filters
|
||||
*/
|
||||
@Get()
|
||||
async findAll(@Query() query: QueryProjectsDto, @Request() req: any) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
if (!workspaceId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
@RequirePermission(Permission.WORKSPACE_ANY)
|
||||
async findAll(
|
||||
@Query() query: QueryProjectsDto,
|
||||
@Workspace() workspaceId: string
|
||||
) {
|
||||
return this.projectsService.findAll({ ...query, workspaceId });
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/projects/:id
|
||||
* Get a single project by ID
|
||||
*/
|
||||
@Get(":id")
|
||||
async findOne(@Param("id") id: string, @Request() req: any) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
if (!workspaceId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
@RequirePermission(Permission.WORKSPACE_ANY)
|
||||
async findOne(@Param("id") id: string, @Workspace() workspaceId: string) {
|
||||
return this.projectsService.findOne(id, workspaceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* PATCH /api/projects/:id
|
||||
* Update a project
|
||||
*/
|
||||
@Patch(":id")
|
||||
@RequirePermission(Permission.WORKSPACE_MEMBER)
|
||||
async update(
|
||||
@Param("id") id: string,
|
||||
@Body() updateProjectDto: UpdateProjectDto,
|
||||
@Request() req: any
|
||||
@Workspace() workspaceId: string,
|
||||
@CurrentUser() user: any
|
||||
) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
const userId = req.user?.id;
|
||||
|
||||
if (!workspaceId || !userId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
|
||||
return this.projectsService.update(id, workspaceId, userId, updateProjectDto);
|
||||
return this.projectsService.update(id, workspaceId, user.id, updateProjectDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE /api/projects/:id
|
||||
* Delete a project
|
||||
*/
|
||||
@Delete(":id")
|
||||
async remove(@Param("id") id: string, @Request() req: any) {
|
||||
const workspaceId = req.user?.workspaceId;
|
||||
const userId = req.user?.id;
|
||||
|
||||
if (!workspaceId || !userId) {
|
||||
throw new UnauthorizedException("Authentication required");
|
||||
}
|
||||
|
||||
return this.projectsService.remove(id, workspaceId, userId);
|
||||
@RequirePermission(Permission.WORKSPACE_ADMIN)
|
||||
async remove(
|
||||
@Param("id") id: string,
|
||||
@Workspace() workspaceId: string,
|
||||
@CurrentUser() user: any
|
||||
) {
|
||||
return this.projectsService.remove(id, workspaceId, user.id);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user