feat(api): implement personalities CRUD API (#537)
All checks were successful
ci/woodpecker/push/api Pipeline was successful

Co-authored-by: Jason Woltje <jason@diversecanvas.com>
Co-committed-by: Jason Woltje <jason@diversecanvas.com>
This commit was merged in pull request #537.
This commit is contained in:
2026-02-27 10:42:50 +00:00
committed by jason.woltje
parent dd0568cf15
commit 78b71a0ecc
12 changed files with 444 additions and 358 deletions

View File

@@ -6,105 +6,122 @@ import {
Delete,
Body,
Param,
Query,
UseGuards,
Req,
HttpCode,
HttpStatus,
} from "@nestjs/common";
import { AuthGuard } from "../auth/guards/auth.guard";
import { WorkspaceGuard, PermissionGuard } from "../common/guards";
import { Workspace, Permission, RequirePermission } from "../common/decorators";
import { PersonalitiesService } from "./personalities.service";
import { CreatePersonalityDto, UpdatePersonalityDto } from "./dto";
import { Personality } from "./entities/personality.entity";
interface AuthenticatedRequest {
user: { id: string };
workspaceId: string;
}
import { CreatePersonalityDto } from "./dto/create-personality.dto";
import { UpdatePersonalityDto } from "./dto/update-personality.dto";
import { PersonalityQueryDto } from "./dto/personality-query.dto";
import type { PersonalityResponse } from "./entities/personality.entity";
/**
* Controller for managing personality/assistant configurations
* Controller for personality CRUD endpoints.
* Route: /api/personalities
*
* Guards applied in order:
* 1. AuthGuard - verifies the user is authenticated
* 2. WorkspaceGuard - validates workspace access
* 3. PermissionGuard - checks role-based permissions
*/
@Controller("personality")
@UseGuards(AuthGuard)
@Controller("personalities")
@UseGuards(AuthGuard, WorkspaceGuard, PermissionGuard)
export class PersonalitiesController {
constructor(private readonly personalitiesService: PersonalitiesService) {}
/**
* List all personalities for the workspace
* GET /api/personalities
* List all personalities for the workspace.
* Supports ?isActive=true|false filter.
*/
@Get()
async findAll(@Req() req: AuthenticatedRequest): Promise<Personality[]> {
return this.personalitiesService.findAll(req.workspaceId);
@RequirePermission(Permission.WORKSPACE_ANY)
async findAll(
@Workspace() workspaceId: string,
@Query() query: PersonalityQueryDto
): Promise<{ success: true; data: PersonalityResponse[] }> {
const data = await this.personalitiesService.findAll(workspaceId, query);
return { success: true, data };
}
/**
* Get the default personality for the workspace
* GET /api/personalities/default
* Get the default personality for the workspace.
* Must be declared before :id to avoid route conflicts.
*/
@Get("default")
async findDefault(@Req() req: AuthenticatedRequest): Promise<Personality> {
return this.personalitiesService.findDefault(req.workspaceId);
@RequirePermission(Permission.WORKSPACE_ANY)
async findDefault(@Workspace() workspaceId: string): Promise<PersonalityResponse> {
return this.personalitiesService.findDefault(workspaceId);
}
/**
* Get a personality by its unique name
*/
@Get("by-name/:name")
async findByName(
@Req() req: AuthenticatedRequest,
@Param("name") name: string
): Promise<Personality> {
return this.personalitiesService.findByName(req.workspaceId, name);
}
/**
* Get a personality by ID
* GET /api/personalities/:id
* Get a single personality by ID.
*/
@Get(":id")
async findOne(@Req() req: AuthenticatedRequest, @Param("id") id: string): Promise<Personality> {
return this.personalitiesService.findOne(req.workspaceId, id);
@RequirePermission(Permission.WORKSPACE_ANY)
async findOne(
@Workspace() workspaceId: string,
@Param("id") id: string
): Promise<PersonalityResponse> {
return this.personalitiesService.findOne(workspaceId, id);
}
/**
* Create a new personality
* POST /api/personalities
* Create a new personality.
*/
@Post()
@HttpCode(HttpStatus.CREATED)
@RequirePermission(Permission.WORKSPACE_MEMBER)
async create(
@Req() req: AuthenticatedRequest,
@Workspace() workspaceId: string,
@Body() dto: CreatePersonalityDto
): Promise<Personality> {
return this.personalitiesService.create(req.workspaceId, dto);
): Promise<PersonalityResponse> {
return this.personalitiesService.create(workspaceId, dto);
}
/**
* Update a personality
* PATCH /api/personalities/:id
* Update an existing personality.
*/
@Patch(":id")
@RequirePermission(Permission.WORKSPACE_MEMBER)
async update(
@Req() req: AuthenticatedRequest,
@Workspace() workspaceId: string,
@Param("id") id: string,
@Body() dto: UpdatePersonalityDto
): Promise<Personality> {
return this.personalitiesService.update(req.workspaceId, id, dto);
): Promise<PersonalityResponse> {
return this.personalitiesService.update(workspaceId, id, dto);
}
/**
* Delete a personality
* DELETE /api/personalities/:id
* Delete a personality.
*/
@Delete(":id")
@HttpCode(HttpStatus.NO_CONTENT)
async delete(@Req() req: AuthenticatedRequest, @Param("id") id: string): Promise<void> {
return this.personalitiesService.delete(req.workspaceId, id);
@RequirePermission(Permission.WORKSPACE_MEMBER)
async delete(@Workspace() workspaceId: string, @Param("id") id: string): Promise<void> {
return this.personalitiesService.delete(workspaceId, id);
}
/**
* Set a personality as the default
* POST /api/personalities/:id/set-default
* Convenience endpoint to set a personality as the default.
*/
@Post(":id/set-default")
@RequirePermission(Permission.WORKSPACE_MEMBER)
async setDefault(
@Req() req: AuthenticatedRequest,
@Workspace() workspaceId: string,
@Param("id") id: string
): Promise<Personality> {
return this.personalitiesService.setDefault(req.workspaceId, id);
): Promise<PersonalityResponse> {
return this.personalitiesService.setDefault(workspaceId, id);
}
}