fix(ai-roles): harden model override, role-name uniqueness, id validation, list least-privilege
Follow-up fixes on the agent-roles feature: - ai.service: a cross-driver override to the ollama driver (when the workspace driver is not ollama) now fails with an explicit 503 instead of silently reusing the workspace base URL, which belongs to a different provider. Same-driver ollama and openai/gemini overrides are unchanged. - migration: add a partial unique index on (workspace_id, name) WHERE deleted_at IS NULL so role names are unique per workspace without soft-deleted rows blocking re-creation; map Postgres 23505 to a 409 ConflictException on create/update. - dto: validate the role id as @IsUUID instead of @IsString. - roles list: do not expose instructions/modelConfig to non-admin members. The list endpoint now returns a picker view (id/name/emoji/description/ enabled) to members and the full view only to admins (same gate as the CRUD endpoints). Client IAiRole fields made optional accordingly. Adds tests for the cross-driver-ollama throw, the 23505->409 mapping, and the non-admin picker-view security invariant. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -52,6 +52,17 @@ export async function up(db: Kysely<any>): Promise<void> {
|
||||
.column('workspace_id')
|
||||
.execute();
|
||||
|
||||
// A role name is unique per workspace. Partial (WHERE deleted_at IS NULL) so a
|
||||
// soft-deleted role does not block re-creating a role with the same name.
|
||||
await db.schema
|
||||
.createIndex('ai_agent_roles_workspace_id_name_unique')
|
||||
.ifNotExists()
|
||||
.on('ai_agent_roles')
|
||||
.columns(['workspace_id', 'name'])
|
||||
.unique()
|
||||
.where(sql.ref('deleted_at'), 'is', null)
|
||||
.execute();
|
||||
|
||||
// Bind a chat to a role. ON DELETE SET NULL: a hard-deleted role degrades the
|
||||
// chat to the universal assistant instead of breaking it. The role is read
|
||||
// from this column on every turn — the client only sends roleId on chat
|
||||
@@ -66,5 +77,9 @@ export async function up(db: Kysely<any>): Promise<void> {
|
||||
|
||||
export async function down(db: Kysely<any>): Promise<void> {
|
||||
await db.schema.alterTable('ai_chats').dropColumn('role_id').execute();
|
||||
await db.schema
|
||||
.dropIndex('ai_agent_roles_workspace_id_name_unique')
|
||||
.ifExists()
|
||||
.execute();
|
||||
await db.schema.dropTable('ai_agent_roles').execute();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user