feat(api-key): core-модуль выдачи и валидации api-ключей + kill-switch
Форк не несёт EE-модуль ee/api-key, поэтому api_key-токен сегодня отвергается на любом /api/*. Вводим core-модуль: - ApiKeyRepo (kysely): insert/findById/findByCreator/findAllInWorkspace/ softDelete/touchLastUsed, фильтр deleted_at IS NULL. - ApiKeyService.create — mint-then-insert: сперва uuid7 id, затем чеканка JWT без exp, затем строка (сбой чеканки → строки нет; потерянный ответ → осиротевшая строка видна в list и самолечится). Токен отдаётся один раз, в таблице не хранится. - ApiKeyService.validate — единый валидатор для jwt.strategy И /mcp. Владелец истины о сроке/отзыве — строка api_keys, не JWT. Любой определённый негатив (нет строки / expired / disabled / workspace mismatch / kill-switch off) → единый bare-401 (анти-enumeration); инфра-ошибка пробрасывается (→5xx), не маскируется. Без кэша — немедленная ревокация. last_used_at троттлится 1ч, fire-and-forget. - REST create/list/revoke: create — self + UserThrottlerGuard; list/revoke — admin (CASL Manage на API) видит/отзывает все ключи воркспейса, член — только свои. api_key-принципал отвергается на всех трёх (токен не управляет токенами — GitHub-PAT-семантика). Аудит API_KEY_CREATED/DELETED + структурный лог с apiKeyId и IP. - jwt.strategy: прямой вызов ApiKeyService.validate вместо динамического EE-require; штампует req.raw.authType='api_key' и apiKeyId для гварда выше. - Kill-switch API_KEYS_ENABLED: дефолт ON (деплой без переменной не убивает агентов), строгий парс @IsIn(['true','false']) (=0/=off падают на boot, а не молча значат «включено»), boot-лог состояния. off → validate deny и эндпоинты 404. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -37,6 +37,7 @@ import { AiProviderCredentialsRepo } from '@docmost/db/repos/ai-chat/ai-provider
|
||||
import { AiMcpServerRepo } from '@docmost/db/repos/ai-chat/ai-mcp-server.repo';
|
||||
import { AiAgentRoleRepo } from '@docmost/db/repos/ai-agent-roles/ai-agent-roles.repo';
|
||||
import { PageEmbeddingRepo } from '@docmost/db/repos/ai-chat/page-embedding.repo';
|
||||
import { ApiKeyRepo } from '@docmost/db/repos/api-key/api-key.repo';
|
||||
import { PageListener } from '@docmost/db/listeners/page.listener';
|
||||
import { PostgresJSDialect } from 'kysely-postgres-js';
|
||||
import * as postgres from 'postgres';
|
||||
@@ -129,6 +130,7 @@ import { firstSqlToken } from '../integrations/metrics/metrics.constants';
|
||||
AiMcpServerRepo,
|
||||
AiAgentRoleRepo,
|
||||
PageEmbeddingRepo,
|
||||
ApiKeyRepo,
|
||||
PageListener,
|
||||
],
|
||||
exports: [
|
||||
@@ -164,6 +166,7 @@ import { firstSqlToken } from '../integrations/metrics/metrics.constants';
|
||||
AiMcpServerRepo,
|
||||
AiAgentRoleRepo,
|
||||
PageEmbeddingRepo,
|
||||
ApiKeyRepo,
|
||||
],
|
||||
})
|
||||
export class DatabaseModule implements OnApplicationBootstrap {
|
||||
|
||||
Reference in New Issue
Block a user