libre-webui 0.25.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -22
- package/backend/dist/index.d.ts.map +1 -1
- package/backend/dist/index.js +26 -4
- package/backend/dist/index.js.map +1 -1
- package/backend/dist/middleware/auth.d.ts +7 -0
- package/backend/dist/middleware/auth.d.ts.map +1 -1
- package/backend/dist/middleware/auth.js +26 -0
- package/backend/dist/middleware/auth.js.map +1 -1
- package/backend/dist/middleware/index.d.ts +20 -0
- package/backend/dist/middleware/index.d.ts.map +1 -1
- package/backend/dist/middleware/index.js +105 -2
- package/backend/dist/middleware/index.js.map +1 -1
- package/backend/dist/models/personaModel.d.ts.map +1 -1
- package/backend/dist/models/personaModel.js +2 -0
- package/backend/dist/models/personaModel.js.map +1 -1
- package/backend/dist/observability/otel.d.ts +40 -0
- package/backend/dist/observability/otel.d.ts.map +1 -0
- package/backend/dist/observability/otel.js +337 -0
- package/backend/dist/observability/otel.js.map +1 -0
- package/backend/dist/observability/requestContext.d.ts +14 -0
- package/backend/dist/observability/requestContext.d.ts.map +1 -0
- package/backend/dist/observability/requestContext.js +21 -0
- package/backend/dist/observability/requestContext.js.map +1 -0
- package/backend/dist/persistence/chatGenerationTypes.d.ts +9 -0
- package/backend/dist/persistence/chatGenerationTypes.d.ts.map +1 -1
- package/backend/dist/persistence/extensionTypes.d.ts +13 -0
- package/backend/dist/persistence/extensionTypes.d.ts.map +1 -1
- package/backend/dist/persistence/extensionTypes.js.map +1 -1
- package/backend/dist/persistence/index.d.ts +1 -1
- package/backend/dist/persistence/index.d.ts.map +1 -1
- package/backend/dist/persistence/index.js.map +1 -1
- package/backend/dist/persistence/postgresAgentFoundationMigration.d.ts +9 -0
- package/backend/dist/persistence/postgresAgentFoundationMigration.d.ts.map +1 -0
- package/backend/dist/persistence/postgresAgentFoundationMigration.js +164 -0
- package/backend/dist/persistence/postgresAgentFoundationMigration.js.map +1 -0
- package/backend/dist/persistence/postgresExtensionRepositories.d.ts.map +1 -1
- package/backend/dist/persistence/postgresExtensionRepositories.js +46 -2
- package/backend/dist/persistence/postgresExtensionRepositories.js.map +1 -1
- package/backend/dist/persistence/postgresMediaEnterpriseOpsMigration.d.ts +10 -0
- package/backend/dist/persistence/postgresMediaEnterpriseOpsMigration.d.ts.map +1 -0
- package/backend/dist/persistence/postgresMediaEnterpriseOpsMigration.js +148 -0
- package/backend/dist/persistence/postgresMediaEnterpriseOpsMigration.js.map +1 -0
- package/backend/dist/persistence/postgresMigrationRegistry.d.ts.map +1 -1
- package/backend/dist/persistence/postgresMigrationRegistry.js +10 -0
- package/backend/dist/persistence/postgresMigrationRegistry.js.map +1 -1
- package/backend/dist/persistence/postgresNotesV2Migration.d.ts +4 -0
- package/backend/dist/persistence/postgresNotesV2Migration.d.ts.map +1 -0
- package/backend/dist/persistence/postgresNotesV2Migration.js +57 -0
- package/backend/dist/persistence/postgresNotesV2Migration.js.map +1 -0
- package/backend/dist/persistence/postgresResourceRepositories.d.ts.map +1 -1
- package/backend/dist/persistence/postgresResourceRepositories.js +1646 -11
- package/backend/dist/persistence/postgresResourceRepositories.js.map +1 -1
- package/backend/dist/persistence/postgresSkillFilesMigration.d.ts +9 -0
- package/backend/dist/persistence/postgresSkillFilesMigration.d.ts.map +1 -0
- package/backend/dist/persistence/postgresSkillFilesMigration.js +50 -0
- package/backend/dist/persistence/postgresSkillFilesMigration.js.map +1 -0
- package/backend/dist/persistence/postgresTeamCollaborationMigration.d.ts +10 -0
- package/backend/dist/persistence/postgresTeamCollaborationMigration.d.ts.map +1 -0
- package/backend/dist/persistence/postgresTeamCollaborationMigration.js +161 -0
- package/backend/dist/persistence/postgresTeamCollaborationMigration.js.map +1 -0
- package/backend/dist/persistence/resourceTypes.d.ts +541 -3
- package/backend/dist/persistence/resourceTypes.d.ts.map +1 -1
- package/backend/dist/persistence/resourceTypes.js.map +1 -1
- package/backend/dist/persistence/sqliteExtensionRepositories.d.ts.map +1 -1
- package/backend/dist/persistence/sqliteExtensionRepositories.js +26 -4
- package/backend/dist/persistence/sqliteExtensionRepositories.js.map +1 -1
- package/backend/dist/persistence/sqliteMigrations.d.ts +9 -0
- package/backend/dist/persistence/sqliteMigrations.d.ts.map +1 -1
- package/backend/dist/persistence/sqliteMigrations.js +1196 -29
- package/backend/dist/persistence/sqliteMigrations.js.map +1 -1
- package/backend/dist/persistence/sqliteResourceRepositories.d.ts.map +1 -1
- package/backend/dist/persistence/sqliteResourceRepositories.js +1523 -134
- package/backend/dist/persistence/sqliteResourceRepositories.js.map +1 -1
- package/backend/dist/persistence/sqliteToPostgresMigration.d.ts.map +1 -1
- package/backend/dist/persistence/sqliteToPostgresMigration.js +257 -3
- package/backend/dist/persistence/sqliteToPostgresMigration.js.map +1 -1
- package/backend/dist/platform/jobs/domainJobContracts.d.ts +8 -0
- package/backend/dist/platform/jobs/domainJobContracts.d.ts.map +1 -1
- package/backend/dist/platform/jobs/domainJobContracts.js +8 -0
- package/backend/dist/platform/jobs/domainJobContracts.js.map +1 -1
- package/backend/dist/platform/jobs/domainJobHandlers.d.ts.map +1 -1
- package/backend/dist/platform/jobs/domainJobHandlers.js +310 -3
- package/backend/dist/platform/jobs/domainJobHandlers.js.map +1 -1
- package/backend/dist/platform/jobs/embeddedDurableJobWorker.d.ts.map +1 -1
- package/backend/dist/platform/jobs/embeddedDurableJobWorker.js +4 -2
- package/backend/dist/platform/jobs/embeddedDurableJobWorker.js.map +1 -1
- package/backend/dist/platform/jobs/postgresDurableJobWorker.d.ts.map +1 -1
- package/backend/dist/platform/jobs/postgresDurableJobWorker.js +4 -2
- package/backend/dist/platform/jobs/postgresDurableJobWorker.js.map +1 -1
- package/backend/dist/platform/storage/pgVectorStore.d.ts +2 -1
- package/backend/dist/platform/storage/pgVectorStore.d.ts.map +1 -1
- package/backend/dist/platform/storage/pgVectorStore.js +51 -0
- package/backend/dist/platform/storage/pgVectorStore.js.map +1 -1
- package/backend/dist/platform/storage/platformDomainRepositories.d.ts +8 -2
- package/backend/dist/platform/storage/platformDomainRepositories.d.ts.map +1 -1
- package/backend/dist/platform/storage/postgresPlatformDomainRepositories.d.ts.map +1 -1
- package/backend/dist/platform/storage/postgresPlatformDomainRepositories.js +25 -4
- package/backend/dist/platform/storage/postgresPlatformDomainRepositories.js.map +1 -1
- package/backend/dist/platform/storage/sqliteEncryptedVectorStore.d.ts +2 -1
- package/backend/dist/platform/storage/sqliteEncryptedVectorStore.d.ts.map +1 -1
- package/backend/dist/platform/storage/sqliteEncryptedVectorStore.js +35 -0
- package/backend/dist/platform/storage/sqliteEncryptedVectorStore.js.map +1 -1
- package/backend/dist/platform/storage/sqlitePlatformDomainRepositories.d.ts.map +1 -1
- package/backend/dist/platform/storage/sqlitePlatformDomainRepositories.js +26 -3
- package/backend/dist/platform/storage/sqlitePlatformDomainRepositories.js.map +1 -1
- package/backend/dist/platform/storage/vectorStore.d.ts +14 -0
- package/backend/dist/platform/storage/vectorStore.d.ts.map +1 -1
- package/backend/dist/platform/storage/vectorStore.js.map +1 -1
- package/backend/dist/routes/access.d.ts.map +1 -1
- package/backend/dist/routes/access.js +84 -3
- package/backend/dist/routes/access.js.map +1 -1
- package/backend/dist/routes/calendar.d.ts.map +1 -1
- package/backend/dist/routes/calendar.js +125 -22
- package/backend/dist/routes/calendar.js.map +1 -1
- package/backend/dist/routes/channels.d.ts +3 -0
- package/backend/dist/routes/channels.d.ts.map +1 -0
- package/backend/dist/routes/channels.js +447 -0
- package/backend/dist/routes/channels.js.map +1 -0
- package/backend/dist/routes/chat.d.ts.map +1 -1
- package/backend/dist/routes/chat.js +195 -7
- package/backend/dist/routes/chat.js.map +1 -1
- package/backend/dist/routes/costs.d.ts +3 -0
- package/backend/dist/routes/costs.d.ts.map +1 -0
- package/backend/dist/routes/costs.js +191 -0
- package/backend/dist/routes/costs.js.map +1 -0
- package/backend/dist/routes/documents.d.ts.map +1 -1
- package/backend/dist/routes/documents.js +64 -21
- package/backend/dist/routes/documents.js.map +1 -1
- package/backend/dist/routes/evaluations.d.ts +3 -0
- package/backend/dist/routes/evaluations.d.ts.map +1 -0
- package/backend/dist/routes/evaluations.js +352 -0
- package/backend/dist/routes/evaluations.js.map +1 -0
- package/backend/dist/routes/imageGen.d.ts.map +1 -1
- package/backend/dist/routes/imageGen.js +2 -1
- package/backend/dist/routes/imageGen.js.map +1 -1
- package/backend/dist/routes/media.d.ts.map +1 -1
- package/backend/dist/routes/media.js +228 -7
- package/backend/dist/routes/media.js.map +1 -1
- package/backend/dist/routes/notes.d.ts.map +1 -1
- package/backend/dist/routes/notes.js +149 -36
- package/backend/dist/routes/notes.js.map +1 -1
- package/backend/dist/routes/notifications.d.ts +3 -0
- package/backend/dist/routes/notifications.d.ts.map +1 -0
- package/backend/dist/routes/notifications.js +213 -0
- package/backend/dist/routes/notifications.js.map +1 -0
- package/backend/dist/routes/openaiCompat.d.ts +3 -0
- package/backend/dist/routes/openaiCompat.d.ts.map +1 -0
- package/backend/dist/routes/openaiCompat.js +314 -0
- package/backend/dist/routes/openaiCompat.js.map +1 -0
- package/backend/dist/routes/personas.js +1 -1
- package/backend/dist/routes/personas.js.map +1 -1
- package/backend/dist/routes/prompts.d.ts +3 -0
- package/backend/dist/routes/prompts.d.ts.map +1 -0
- package/backend/dist/routes/prompts.js +173 -0
- package/backend/dist/routes/prompts.js.map +1 -0
- package/backend/dist/routes/search.d.ts.map +1 -1
- package/backend/dist/routes/search.js +29 -1
- package/backend/dist/routes/search.js.map +1 -1
- package/backend/dist/routes/skills.d.ts +3 -0
- package/backend/dist/routes/skills.d.ts.map +1 -0
- package/backend/dist/routes/skills.js +246 -0
- package/backend/dist/routes/skills.js.map +1 -0
- package/backend/dist/routes/stt.d.ts.map +1 -1
- package/backend/dist/routes/stt.js +2 -1
- package/backend/dist/routes/stt.js.map +1 -1
- package/backend/dist/routes/tools.d.ts +3 -0
- package/backend/dist/routes/tools.d.ts.map +1 -0
- package/backend/dist/routes/tools.js +359 -0
- package/backend/dist/routes/tools.js.map +1 -0
- package/backend/dist/routes/tts.d.ts.map +1 -1
- package/backend/dist/routes/tts.js +63 -8
- package/backend/dist/routes/tts.js.map +1 -1
- package/backend/dist/routes/voiceAccess.d.ts +3 -0
- package/backend/dist/routes/voiceAccess.d.ts.map +1 -0
- package/backend/dist/routes/voiceAccess.js +102 -0
- package/backend/dist/routes/voiceAccess.js.map +1 -0
- package/backend/dist/services/apiTokenService.d.ts +2 -2
- package/backend/dist/services/apiTokenService.d.ts.map +1 -1
- package/backend/dist/services/apiTokenService.js +7 -0
- package/backend/dist/services/apiTokenService.js.map +1 -1
- package/backend/dist/services/authorizationService.d.ts +8 -4
- package/backend/dist/services/authorizationService.d.ts.map +1 -1
- package/backend/dist/services/authorizationService.js +31 -1
- package/backend/dist/services/authorizationService.js.map +1 -1
- package/backend/dist/services/automationSchedulerService.d.ts.map +1 -1
- package/backend/dist/services/automationSchedulerService.js +33 -3
- package/backend/dist/services/automationSchedulerService.js.map +1 -1
- package/backend/dist/services/automationService.d.ts +4 -1
- package/backend/dist/services/automationService.d.ts.map +1 -1
- package/backend/dist/services/automationService.js +25 -2
- package/backend/dist/services/automationService.js.map +1 -1
- package/backend/dist/services/builtinToolsService.d.ts +20 -0
- package/backend/dist/services/builtinToolsService.d.ts.map +1 -0
- package/backend/dist/services/builtinToolsService.js +661 -0
- package/backend/dist/services/builtinToolsService.js.map +1 -0
- package/backend/dist/services/calendarService.d.ts +74 -0
- package/backend/dist/services/calendarService.d.ts.map +1 -0
- package/backend/dist/services/calendarService.js +339 -0
- package/backend/dist/services/calendarService.js.map +1 -0
- package/backend/dist/services/channelService.d.ts +195 -0
- package/backend/dist/services/channelService.d.ts.map +1 -0
- package/backend/dist/services/channelService.js +1057 -0
- package/backend/dist/services/channelService.js.map +1 -0
- package/backend/dist/services/chatRequestService.d.ts.map +1 -1
- package/backend/dist/services/chatRequestService.js +29 -1
- package/backend/dist/services/chatRequestService.js.map +1 -1
- package/backend/dist/services/chatService.d.ts +61 -1
- package/backend/dist/services/chatService.d.ts.map +1 -1
- package/backend/dist/services/chatService.js +218 -1
- package/backend/dist/services/chatService.js.map +1 -1
- package/backend/dist/services/chatToolRuntimeService.d.ts +55 -0
- package/backend/dist/services/chatToolRuntimeService.d.ts.map +1 -0
- package/backend/dist/services/chatToolRuntimeService.js +389 -0
- package/backend/dist/services/chatToolRuntimeService.js.map +1 -0
- package/backend/dist/services/costGovernanceService.d.ts +102 -0
- package/backend/dist/services/costGovernanceService.d.ts.map +1 -0
- package/backend/dist/services/costGovernanceService.js +463 -0
- package/backend/dist/services/costGovernanceService.js.map +1 -0
- package/backend/dist/services/dataArchiveService.d.ts +6 -0
- package/backend/dist/services/dataArchiveService.d.ts.map +1 -1
- package/backend/dist/services/dataArchiveService.js +23 -4
- package/backend/dist/services/dataArchiveService.js.map +1 -1
- package/backend/dist/services/documentService.d.ts +58 -1
- package/backend/dist/services/documentService.d.ts.map +1 -1
- package/backend/dist/services/documentService.js +289 -84
- package/backend/dist/services/documentService.js.map +1 -1
- package/backend/dist/services/durableChatGenerationService.d.ts +9 -0
- package/backend/dist/services/durableChatGenerationService.d.ts.map +1 -1
- package/backend/dist/services/durableChatGenerationService.js +152 -11
- package/backend/dist/services/durableChatGenerationService.js.map +1 -1
- package/backend/dist/services/evaluationService.d.ts +111 -0
- package/backend/dist/services/evaluationService.d.ts.map +1 -0
- package/backend/dist/services/evaluationService.js +403 -0
- package/backend/dist/services/evaluationService.js.map +1 -0
- package/backend/dist/services/galleryService.d.ts +6 -0
- package/backend/dist/services/galleryService.d.ts.map +1 -1
- package/backend/dist/services/galleryService.js +26 -0
- package/backend/dist/services/galleryService.js.map +1 -1
- package/backend/dist/services/mcpClientService.d.ts +29 -0
- package/backend/dist/services/mcpClientService.d.ts.map +1 -0
- package/backend/dist/services/mcpClientService.js +189 -0
- package/backend/dist/services/mcpClientService.js.map +1 -0
- package/backend/dist/services/noteService.d.ts +71 -0
- package/backend/dist/services/noteService.d.ts.map +1 -0
- package/backend/dist/services/noteService.js +397 -0
- package/backend/dist/services/noteService.js.map +1 -0
- package/backend/dist/services/notificationService.d.ts +55 -0
- package/backend/dist/services/notificationService.d.ts.map +1 -0
- package/backend/dist/services/notificationService.js +308 -0
- package/backend/dist/services/notificationService.js.map +1 -0
- package/backend/dist/services/openApiToolService.d.ts +38 -0
- package/backend/dist/services/openApiToolService.d.ts.map +1 -0
- package/backend/dist/services/openApiToolService.js +254 -0
- package/backend/dist/services/openApiToolService.js.map +1 -0
- package/backend/dist/services/personaService.d.ts +18 -2
- package/backend/dist/services/personaService.d.ts.map +1 -1
- package/backend/dist/services/personaService.js +113 -4
- package/backend/dist/services/personaService.js.map +1 -1
- package/backend/dist/services/pluginImageGenerationService.d.ts +19 -0
- package/backend/dist/services/pluginImageGenerationService.d.ts.map +1 -1
- package/backend/dist/services/pluginImageGenerationService.js +121 -0
- package/backend/dist/services/pluginImageGenerationService.js.map +1 -1
- package/backend/dist/services/pluginService.d.ts +7 -0
- package/backend/dist/services/pluginService.d.ts.map +1 -1
- package/backend/dist/services/pluginService.js +8 -2
- package/backend/dist/services/pluginService.js.map +1 -1
- package/backend/dist/services/promptService.d.ts +74 -0
- package/backend/dist/services/promptService.d.ts.map +1 -0
- package/backend/dist/services/promptService.js +513 -0
- package/backend/dist/services/promptService.js.map +1 -0
- package/backend/dist/services/resourceGrantService.d.ts.map +1 -1
- package/backend/dist/services/resourceGrantService.js +62 -17
- package/backend/dist/services/resourceGrantService.js.map +1 -1
- package/backend/dist/services/searchService.d.ts +29 -0
- package/backend/dist/services/searchService.d.ts.map +1 -0
- package/backend/dist/services/searchService.js +0 -0
- package/backend/dist/services/searchService.js.map +1 -0
- package/backend/dist/services/securityAuditService.d.ts.map +1 -1
- package/backend/dist/services/securityAuditService.js +2 -1
- package/backend/dist/services/securityAuditService.js.map +1 -1
- package/backend/dist/services/sharedResourceAccess.d.ts +25 -0
- package/backend/dist/services/sharedResourceAccess.d.ts.map +1 -0
- package/backend/dist/services/sharedResourceAccess.js +59 -0
- package/backend/dist/services/sharedResourceAccess.js.map +1 -0
- package/backend/dist/services/skillService.d.ts +137 -0
- package/backend/dist/services/skillService.d.ts.map +1 -0
- package/backend/dist/services/skillService.js +852 -0
- package/backend/dist/services/skillService.js.map +1 -0
- package/backend/dist/services/toolAccessService.d.ts +9 -0
- package/backend/dist/services/toolAccessService.d.ts.map +1 -0
- package/backend/dist/services/toolAccessService.js +60 -0
- package/backend/dist/services/toolAccessService.js.map +1 -0
- package/backend/dist/services/toolApprovalService.d.ts +34 -0
- package/backend/dist/services/toolApprovalService.d.ts.map +1 -0
- package/backend/dist/services/toolApprovalService.js +188 -0
- package/backend/dist/services/toolApprovalService.js.map +1 -0
- package/backend/dist/services/toolGatewayService.d.ts +53 -0
- package/backend/dist/services/toolGatewayService.d.ts.map +1 -0
- package/backend/dist/services/toolGatewayService.js +249 -0
- package/backend/dist/services/toolGatewayService.js.map +1 -0
- package/backend/dist/services/toolServerService.d.ts +57 -0
- package/backend/dist/services/toolServerService.d.ts.map +1 -0
- package/backend/dist/services/toolServerService.js +521 -0
- package/backend/dist/services/toolServerService.js.map +1 -0
- package/backend/dist/services/voiceAccessService.d.ts +10 -0
- package/backend/dist/services/voiceAccessService.d.ts.map +1 -0
- package/backend/dist/services/voiceAccessService.js +74 -0
- package/backend/dist/services/voiceAccessService.js.map +1 -0
- package/backend/dist/services/voiceProfileService.d.ts +20 -0
- package/backend/dist/services/voiceProfileService.d.ts.map +1 -1
- package/backend/dist/services/voiceProfileService.js +60 -0
- package/backend/dist/services/voiceProfileService.js.map +1 -1
- package/backend/dist/storage.d.ts +28 -1
- package/backend/dist/storage.d.ts.map +1 -1
- package/backend/dist/storage.js +100 -1
- package/backend/dist/storage.js.map +1 -1
- package/backend/dist/storageMappers.d.ts +2 -2
- package/backend/dist/storageMappers.d.ts.map +1 -1
- package/backend/dist/storageMappers.js.map +1 -1
- package/backend/dist/types/index.d.ts +197 -2
- package/backend/dist/types/index.d.ts.map +1 -1
- package/backend/dist/types/index.js.map +1 -1
- package/backend/dist/types/tools.d.ts +137 -0
- package/backend/dist/types/tools.d.ts.map +1 -0
- package/backend/dist/types/tools.js +25 -0
- package/backend/dist/types/tools.js.map +1 -0
- package/backend/dist/utils/chatDocumentContext.d.ts +26 -1
- package/backend/dist/utils/chatDocumentContext.d.ts.map +1 -1
- package/backend/dist/utils/chatDocumentContext.js +87 -18
- package/backend/dist/utils/chatDocumentContext.js.map +1 -1
- package/backend/dist/utils/documentExtraction.d.ts +69 -0
- package/backend/dist/utils/documentExtraction.d.ts.map +1 -0
- package/backend/dist/utils/documentExtraction.js +545 -0
- package/backend/dist/utils/documentExtraction.js.map +1 -0
- package/backend/dist/utils/hybridRetrieval.d.ts +34 -0
- package/backend/dist/utils/hybridRetrieval.d.ts.map +1 -0
- package/backend/dist/utils/hybridRetrieval.js +119 -0
- package/backend/dist/utils/hybridRetrieval.js.map +1 -0
- package/backend/dist/utils/icsCalendar.d.ts +27 -0
- package/backend/dist/utils/icsCalendar.d.ts.map +1 -0
- package/backend/dist/utils/icsCalendar.js +313 -0
- package/backend/dist/utils/icsCalendar.js.map +1 -0
- package/backend/dist/utils/imageEditUpload.d.ts +29 -0
- package/backend/dist/utils/imageEditUpload.d.ts.map +1 -0
- package/backend/dist/utils/imageEditUpload.js +82 -0
- package/backend/dist/utils/imageEditUpload.js.map +1 -0
- package/backend/dist/utils/logRedaction.d.ts +2 -0
- package/backend/dist/utils/logRedaction.d.ts.map +1 -0
- package/backend/dist/utils/logRedaction.js +66 -0
- package/backend/dist/utils/logRedaction.js.map +1 -0
- package/backend/dist/utils/logger.d.ts +3 -0
- package/backend/dist/utils/logger.d.ts.map +1 -1
- package/backend/dist/utils/logger.js +63 -20
- package/backend/dist/utils/logger.js.map +1 -1
- package/backend/dist/utils/pluginChatAdapter.d.ts +5 -1
- package/backend/dist/utils/pluginChatAdapter.d.ts.map +1 -1
- package/backend/dist/utils/pluginChatAdapter.js +83 -0
- package/backend/dist/utils/pluginChatAdapter.js.map +1 -1
- package/backend/dist/utils/pluginDefinitionTrust.js +1 -1
- package/backend/dist/utils/resourceLimits.d.ts +61 -2
- package/backend/dist/utils/resourceLimits.d.ts.map +1 -1
- package/backend/dist/utils/resourceLimits.js +59 -0
- package/backend/dist/utils/resourceLimits.js.map +1 -1
- package/backend/dist/utils/toolEgress.d.ts +22 -0
- package/backend/dist/utils/toolEgress.d.ts.map +1 -0
- package/backend/dist/utils/toolEgress.js +157 -0
- package/backend/dist/utils/toolEgress.js.map +1 -0
- package/backend/dist/utils/websocketMessages.d.ts +6 -0
- package/backend/dist/utils/websocketMessages.d.ts.map +1 -1
- package/backend/dist/utils/websocketMessages.js +8 -0
- package/backend/dist/utils/websocketMessages.js.map +1 -1
- package/backend/dist/utils/zipArchive.d.ts +32 -0
- package/backend/dist/utils/zipArchive.d.ts.map +1 -0
- package/backend/dist/utils/zipArchive.js +171 -0
- package/backend/dist/utils/zipArchive.js.map +1 -0
- package/backend/dist/websocketServer.d.ts.map +1 -1
- package/backend/dist/websocketServer.js +142 -35
- package/backend/dist/websocketServer.js.map +1 -1
- package/backend/package.json +1 -1
- package/frontend/dist/artifact-runtime/.build-fingerprint +1 -1
- package/frontend/dist/assets/index-BPsH9FII.js +80 -0
- package/frontend/dist/assets/index-BtXF0Oxa.css +1 -0
- package/frontend/dist/index.html +18 -18
- package/frontend/dist/js/{ArtifactContainer-CpUi6ekF.js → ArtifactContainer-CIoFoMv8.js} +1 -1
- package/frontend/dist/js/{ArtifactDemoPage-BeNs3gHp.js → ArtifactDemoPage-D0JEIAbZ.js} +1 -1
- package/frontend/dist/js/{ArtifactSandboxFrame-qsj_Y3LV.js → ArtifactSandboxFrame-REazyIrx.js} +2 -2
- package/frontend/dist/js/ArtifactSlideOutPanel-A-i94ghF.js +1 -0
- package/frontend/dist/js/AutomationsPage-Dow_lbIf.js +1 -0
- package/frontend/dist/js/{Button-BPf_pOxH.js → Button-C0r3J9G_.js} +1 -1
- package/frontend/dist/js/CalendarPage-DLjasZOd.js +1 -0
- package/frontend/dist/js/ChannelsPage-C8hvTD7J.js +1 -0
- package/frontend/dist/js/ChatPage-BAgBHUES.js +16 -0
- package/frontend/dist/js/EvaluationsPage-Bm8FRoeX.js +2 -0
- package/frontend/dist/js/{FirstTimeSetup-Df0YJni3.js → FirstTimeSetup-BxJapNul.js} +1 -1
- package/frontend/dist/js/GalleryPage-DHz9DkK7.js +1 -0
- package/frontend/dist/js/HomePage-BqNy-FWX.js +1 -0
- package/frontend/dist/js/LibreClawPage-BDzDILjY.js +1 -0
- package/frontend/dist/js/{ModelManagerModals-DcLGYN3l.js → ModelManagerModals-DX9eT5r2.js} +1 -1
- package/frontend/dist/js/ModelSelector-ol8mK2ja.js +1 -0
- package/frontend/dist/js/NotesPage-DzHEx2O4.js +3 -0
- package/frontend/dist/js/{OptimizedSyntaxHighlighter-Dz7D1LPB.js → OptimizedSyntaxHighlighter-CZNPwN0i.js} +1 -1
- package/frontend/dist/js/PersonasPage-M_WKH2a9.js +13 -0
- package/frontend/dist/js/PluginUsagePage-Ctoka6JZ.js +1 -0
- package/frontend/dist/js/RichMessageContent-BqgafH4p.js +3 -0
- package/frontend/dist/js/SettingsModal-R9EDadAV.js +19 -0
- package/frontend/dist/js/{SettingsToggle-woG_WebF.js → SettingsToggle-BIVyfGjl.js} +1 -1
- package/frontend/dist/js/SystemPage-aOkrSuY7.js +1 -0
- package/frontend/dist/js/UserManagementPage-BOuaX3ay.js +1 -0
- package/frontend/dist/js/WorkPage-DxZISerQ.js +6 -0
- package/frontend/dist/js/api-6bSMtSNh.js +14 -0
- package/frontend/dist/js/{appStore-0JnCUlEq.js → appStore-CdObhQBb.js} +2 -2
- package/frontend/dist/js/ar-DT_VLBO3.js +2 -0
- package/frontend/dist/js/{authStore-DbMdvNgJ.js → authStore-DbqqrfZv.js} +3 -3
- package/frontend/dist/js/{babel-BsYoxD-7.js → babel-DSqqk5kt.js} +1 -1
- package/frontend/dist/js/bn-BrvEqhGA.js +2 -0
- package/frontend/dist/js/chatStore-DbZPJNsu.js +3 -0
- package/frontend/dist/js/{config-DSOYQ9a3.js → config-8KdgjtEx.js} +1 -1
- package/frontend/dist/js/cs-BthnM_YI.js +2 -0
- package/frontend/dist/js/da-DCth-6_j.js +2 -0
- package/frontend/dist/js/de-BBWt0gJ_.js +2 -0
- package/frontend/dist/js/es-DuslOPqM.js +2 -0
- package/frontend/dist/js/{estree-BcnBcOAx.js → estree-BU5-LV_1.js} +3 -3
- package/frontend/dist/js/evaluationsApi-CwZJsgeG.js +1 -0
- package/frontend/dist/js/fr-DZAeg46S.js +2 -0
- package/frontend/dist/js/hi-ijDSiY77.js +2 -0
- package/frontend/dist/js/{html-DkY55Jys.js → html-DeoAy2bo.js} +2 -2
- package/frontend/dist/js/id-BS6qS0oq.js +2 -0
- package/frontend/dist/js/is-CO2PMDte.js +2 -0
- package/frontend/dist/js/it-ChhxT4hs.js +2 -0
- package/frontend/dist/js/ja-cyeqUeJN.js +2 -0
- package/frontend/dist/js/ko-CfqHTLXj.js +2 -0
- package/frontend/dist/js/{markdown-IZSI9DXw.js → markdown-DOLsYkBY.js} +3 -3
- package/frontend/dist/js/markdown-core-BK7WVOb7.js +32 -0
- package/frontend/dist/js/{markdown-math-DK846gXU.js → markdown-math-e1ByS3KY.js} +5 -5
- package/frontend/dist/js/{messageCodeStyles-BFNR9Wv6.js → messageCodeStyles-DhVgizF-.js} +2 -2
- package/frontend/dist/js/ms-DZ5pACe1.js +2 -0
- package/frontend/dist/js/nl-BsRsnOVp.js +2 -0
- package/frontend/dist/js/pl-DXSTAW4M.js +2 -0
- package/frontend/dist/js/{postcss-BJ0okz7n.js → postcss-Bg15EcFE.js} +1 -1
- package/frontend/dist/js/pt-DmDx0JxB.js +2 -0
- package/frontend/dist/js/{react-vendor-C5yShNlM.js → react-vendor-BG_HPV6X.js} +1 -1
- package/frontend/dist/js/router-vendor-CbexJIsv.js +1 -0
- package/frontend/dist/js/ru-d2g8EWfY.js +2 -0
- package/frontend/dist/js/{standalone-Csw3jfJ_.js → standalone-BO5pzI8_.js} +3 -3
- package/frontend/dist/js/sv-CIhYUTVQ.js +2 -0
- package/frontend/dist/js/{syntax-highlight-3EEX6CGL.js → syntax-highlight-DcR9Vhb7.js} +3 -3
- package/frontend/dist/js/th-B3wSxanB.js +2 -0
- package/frontend/dist/js/tr-R4UGgPOY.js +2 -0
- package/frontend/dist/js/{typescript-CnF4UVx7.js → typescript-6GJ8APmn.js} +1 -1
- package/frontend/dist/js/ui-CV0LYmkM.js +2 -0
- package/frontend/dist/js/ui-vendor-CWjIBW-z.js +177 -0
- package/frontend/dist/js/uk-BzUF4Vkt.js +2 -0
- package/frontend/dist/js/{useTranslation-Bho4HCyg.js → useTranslation-5b5UhAc9.js} +1 -1
- package/frontend/dist/js/{utils-D9W1Lnkn.js → utils-BGrdX9ie.js} +1 -1
- package/frontend/dist/js/{utils-vendor-DWh39_3x.js → utils-vendor-9bQ62WeD.js} +1 -1
- package/frontend/dist/js/vi-CKc6Fg4h.js +2 -0
- package/frontend/dist/js/{workApi-C3uy9EDT.js → workApi-BruWTlHM.js} +3 -3
- package/frontend/dist/js/{yaml-DzT5hBSH.js → yaml-DUeWcOOH.js} +2 -2
- package/frontend/dist/js/zh-CXL-kDe8.js +2 -0
- package/package.json +2 -2
- package/plugins/openai.json +52 -9
- package/frontend/dist/assets/index-6o-Hc2wS.js +0 -41
- package/frontend/dist/assets/index-D-pM957b.css +0 -1
- package/frontend/dist/js/ArtifactSlideOutPanel-CSFRluWA.js +0 -1
- package/frontend/dist/js/AutomationsPage-KUeWoZNv.js +0 -1
- package/frontend/dist/js/CalendarPage-D9I972w8.js +0 -1
- package/frontend/dist/js/ChatPage-CmBx4oAL.js +0 -16
- package/frontend/dist/js/GalleryPage-bLCrdJN_.js +0 -1
- package/frontend/dist/js/HomePage-Uc6PEg-U.js +0 -1
- package/frontend/dist/js/LibreClawPage-CG63DHg1.js +0 -1
- package/frontend/dist/js/ModelSelector-yxJC64rM.js +0 -1
- package/frontend/dist/js/ModelsPage-1R_TxgmJ.js +0 -2
- package/frontend/dist/js/NotesPage-L65j5HoW.js +0 -1
- package/frontend/dist/js/PersonasPage-CKeBpHIH.js +0 -13
- package/frontend/dist/js/PluginUsagePage-BX1SKMbW.js +0 -1
- package/frontend/dist/js/RichMessageContent-DiySVYGB.js +0 -3
- package/frontend/dist/js/SettingsModal-68SG1uZ2.js +0 -1
- package/frontend/dist/js/SystemPage-CytpZiQV.js +0 -1
- package/frontend/dist/js/UserManagementPage-BzJBvZiS.js +0 -1
- package/frontend/dist/js/WorkPage-C8gr0QJe.js +0 -6
- package/frontend/dist/js/api-BDtqBtqw.js +0 -14
- package/frontend/dist/js/ar-CSpi37Gr.js +0 -2
- package/frontend/dist/js/bn-CyH1BeDD.js +0 -2
- package/frontend/dist/js/chatStore-CHQ2uINS.js +0 -3
- package/frontend/dist/js/cs-Cv2NfRK5.js +0 -2
- package/frontend/dist/js/da-D6D6IvPK.js +0 -2
- package/frontend/dist/js/de-BNoJJTrT.js +0 -2
- package/frontend/dist/js/es-kzo9C7q_.js +0 -2
- package/frontend/dist/js/fr-CjHYvJRp.js +0 -2
- package/frontend/dist/js/hi-BU98AdCQ.js +0 -2
- package/frontend/dist/js/id-CpdedejX.js +0 -2
- package/frontend/dist/js/is-Cp5dDWBH.js +0 -2
- package/frontend/dist/js/it-CuH_cK4U.js +0 -2
- package/frontend/dist/js/ja-BWXXXiHn.js +0 -2
- package/frontend/dist/js/ko-DQgv2M1t.js +0 -2
- package/frontend/dist/js/markdown-core-BmPPZiuD.js +0 -31
- package/frontend/dist/js/ms-DCcRoIcX.js +0 -2
- package/frontend/dist/js/nl-CbKR1_EU.js +0 -2
- package/frontend/dist/js/pl-CRyJZccn.js +0 -2
- package/frontend/dist/js/pt-BXYmHFGH.js +0 -2
- package/frontend/dist/js/router-vendor-DGFGM3EP.js +0 -1
- package/frontend/dist/js/ru-M9SyZG4b.js +0 -2
- package/frontend/dist/js/sv-DJ486LTU.js +0 -2
- package/frontend/dist/js/th-Ch7gKtZv.js +0 -2
- package/frontend/dist/js/tr-ClehWhSC.js +0 -2
- package/frontend/dist/js/ui-CC-K19wO.js +0 -2
- package/frontend/dist/js/ui-vendor-B1UgO3vd.js +0 -177
- package/frontend/dist/js/uk-BqAd5zHF.js +0 -2
- package/frontend/dist/js/vi-D9gjlDVB.js +0 -2
- package/frontend/dist/js/zh-DKTyHkiR.js +0 -2
|
@@ -34,17 +34,85 @@ const namedResource = (row) => ({
|
|
|
34
34
|
});
|
|
35
35
|
const note = (row) => ({
|
|
36
36
|
...row,
|
|
37
|
+
pinned: number(row.pinned ?? 0, 'note pinned'),
|
|
37
38
|
created_at: number(row.created_at, 'note created_at'),
|
|
38
39
|
updated_at: number(row.updated_at, 'note updated_at'),
|
|
39
40
|
});
|
|
41
|
+
const noteRevision = (row) => ({
|
|
42
|
+
...row,
|
|
43
|
+
created_at: number(row.created_at, 'note revision created_at'),
|
|
44
|
+
});
|
|
45
|
+
const noteAttachment = (row) => ({
|
|
46
|
+
...row,
|
|
47
|
+
size: number(row.size, 'note attachment size'),
|
|
48
|
+
created_at: number(row.created_at, 'note attachment created_at'),
|
|
49
|
+
});
|
|
40
50
|
const calendarEvent = (row) => ({
|
|
41
51
|
...row,
|
|
42
52
|
start_at: number(row.start_at, 'calendar event start_at'),
|
|
43
53
|
end_at: row.end_at === null ? null : number(row.end_at, 'calendar event end_at'),
|
|
44
54
|
all_day: number(row.all_day, 'calendar event all_day'),
|
|
55
|
+
reminder_minutes: row.reminder_minutes === null
|
|
56
|
+
? null
|
|
57
|
+
: number(row.reminder_minutes, 'calendar event reminder_minutes'),
|
|
58
|
+
last_reminded_occurrence: row.last_reminded_occurrence === null
|
|
59
|
+
? null
|
|
60
|
+
: number(row.last_reminded_occurrence, 'calendar event last_reminded_occurrence'),
|
|
45
61
|
created_at: number(row.created_at, 'calendar event created_at'),
|
|
46
62
|
updated_at: number(row.updated_at, 'calendar event updated_at'),
|
|
47
63
|
});
|
|
64
|
+
const calendar = (row) => ({
|
|
65
|
+
...row,
|
|
66
|
+
created_at: number(row.created_at, 'calendar created_at'),
|
|
67
|
+
updated_at: number(row.updated_at, 'calendar updated_at'),
|
|
68
|
+
});
|
|
69
|
+
const channel = (row) => ({
|
|
70
|
+
...row,
|
|
71
|
+
created_at: number(row.created_at, 'channel created_at'),
|
|
72
|
+
updated_at: number(row.updated_at, 'channel updated_at'),
|
|
73
|
+
archived_at: row.archived_at === null
|
|
74
|
+
? null
|
|
75
|
+
: number(row.archived_at, 'channel archived_at'),
|
|
76
|
+
});
|
|
77
|
+
const channelMember = (row) => ({
|
|
78
|
+
...row,
|
|
79
|
+
joined_at: number(row.joined_at, 'channel member joined_at'),
|
|
80
|
+
last_read_at: number(row.last_read_at, 'channel member last_read_at'),
|
|
81
|
+
});
|
|
82
|
+
const channelMessage = (row) => ({
|
|
83
|
+
...row,
|
|
84
|
+
created_at: number(row.created_at, 'channel message created_at'),
|
|
85
|
+
updated_at: number(row.updated_at, 'channel message updated_at'),
|
|
86
|
+
edited_at: row.edited_at === null
|
|
87
|
+
? null
|
|
88
|
+
: number(row.edited_at, 'channel message edited_at'),
|
|
89
|
+
deleted_at: row.deleted_at === null
|
|
90
|
+
? null
|
|
91
|
+
: number(row.deleted_at, 'channel message deleted_at'),
|
|
92
|
+
pinned_at: row.pinned_at === null
|
|
93
|
+
? null
|
|
94
|
+
: number(row.pinned_at, 'channel message pinned_at'),
|
|
95
|
+
});
|
|
96
|
+
const channelReaction = (row) => ({
|
|
97
|
+
...row,
|
|
98
|
+
created_at: number(row.created_at, 'channel reaction created_at'),
|
|
99
|
+
});
|
|
100
|
+
const channelAttachment = (row) => ({
|
|
101
|
+
...row,
|
|
102
|
+
size: number(row.size, 'channel attachment size'),
|
|
103
|
+
created_at: number(row.created_at, 'channel attachment created_at'),
|
|
104
|
+
});
|
|
105
|
+
const notification = (row) => ({
|
|
106
|
+
...row,
|
|
107
|
+
created_at: number(row.created_at, 'notification created_at'),
|
|
108
|
+
read_at: row.read_at === null ? null : number(row.read_at, 'notification read_at'),
|
|
109
|
+
});
|
|
110
|
+
const webhookTarget = (row) => ({
|
|
111
|
+
...row,
|
|
112
|
+
enabled: number(row.enabled, 'webhook target enabled'),
|
|
113
|
+
created_at: number(row.created_at, 'webhook target created_at'),
|
|
114
|
+
updated_at: number(row.updated_at, 'webhook target updated_at'),
|
|
115
|
+
});
|
|
48
116
|
const automation = (row) => ({
|
|
49
117
|
...row,
|
|
50
118
|
next_run_at: row.next_run_at === null
|
|
@@ -68,6 +136,66 @@ const automationRun = (row) => ({
|
|
|
68
136
|
seen_at: row.seen_at === null ? null : number(row.seen_at, 'automation run seen_at'),
|
|
69
137
|
created_at: number(row.created_at, 'automation run created_at'),
|
|
70
138
|
});
|
|
139
|
+
const toolServer = (row) => ({
|
|
140
|
+
...row,
|
|
141
|
+
spec_revision: number(row.spec_revision, 'tool server spec_revision'),
|
|
142
|
+
enabled: number(row.enabled, 'tool server enabled'),
|
|
143
|
+
timeout_ms: number(row.timeout_ms, 'tool server timeout_ms'),
|
|
144
|
+
max_response_bytes: number(row.max_response_bytes, 'tool server max_response_bytes'),
|
|
145
|
+
created_at: number(row.created_at, 'tool server created_at'),
|
|
146
|
+
updated_at: number(row.updated_at, 'tool server updated_at'),
|
|
147
|
+
});
|
|
148
|
+
const toolServerTool = (row) => ({
|
|
149
|
+
...row,
|
|
150
|
+
side_effect: number(row.side_effect, 'tool side_effect'),
|
|
151
|
+
enabled: number(row.enabled, 'tool enabled'),
|
|
152
|
+
created_at: number(row.created_at, 'tool created_at'),
|
|
153
|
+
updated_at: number(row.updated_at, 'tool updated_at'),
|
|
154
|
+
});
|
|
155
|
+
const toolServerCredential = (row) => ({
|
|
156
|
+
...row,
|
|
157
|
+
created_at: number(row.created_at, 'tool credential created_at'),
|
|
158
|
+
updated_at: number(row.updated_at, 'tool credential updated_at'),
|
|
159
|
+
});
|
|
160
|
+
const toolApproval = (row) => ({
|
|
161
|
+
...row,
|
|
162
|
+
created_at: number(row.created_at, 'tool approval created_at'),
|
|
163
|
+
resolved_at: row.resolved_at === null
|
|
164
|
+
? null
|
|
165
|
+
: number(row.resolved_at, 'tool approval resolved_at'),
|
|
166
|
+
expires_at: row.expires_at === null
|
|
167
|
+
? null
|
|
168
|
+
: number(row.expires_at, 'tool approval expires_at'),
|
|
169
|
+
});
|
|
170
|
+
const prompt = (row) => ({
|
|
171
|
+
...row,
|
|
172
|
+
version: number(row.version, 'prompt version'),
|
|
173
|
+
created_at: number(row.created_at, 'prompt created_at'),
|
|
174
|
+
updated_at: number(row.updated_at, 'prompt updated_at'),
|
|
175
|
+
});
|
|
176
|
+
const promptVersion = (row) => ({
|
|
177
|
+
...row,
|
|
178
|
+
version: number(row.version, 'prompt revision version'),
|
|
179
|
+
created_at: number(row.created_at, 'prompt revision created_at'),
|
|
180
|
+
});
|
|
181
|
+
const skill = (row) => ({
|
|
182
|
+
...row,
|
|
183
|
+
enabled: number(row.enabled, 'skill enabled'),
|
|
184
|
+
version: number(row.version, 'skill version'),
|
|
185
|
+
created_at: number(row.created_at, 'skill created_at'),
|
|
186
|
+
updated_at: number(row.updated_at, 'skill updated_at'),
|
|
187
|
+
});
|
|
188
|
+
const skillVersion = (row) => ({
|
|
189
|
+
...row,
|
|
190
|
+
version: number(row.version, 'skill revision version'),
|
|
191
|
+
created_at: number(row.created_at, 'skill revision created_at'),
|
|
192
|
+
});
|
|
193
|
+
const skillFile = (row) => ({
|
|
194
|
+
...row,
|
|
195
|
+
size: number(row.size, 'skill file size'),
|
|
196
|
+
created_at: number(row.created_at, 'skill file created_at'),
|
|
197
|
+
updated_at: number(row.updated_at, 'skill file updated_at'),
|
|
198
|
+
});
|
|
71
199
|
const changes = (rowCount) => rowCount ?? 0;
|
|
72
200
|
const lockOwner = async (client, userId) => {
|
|
73
201
|
const result = await client.query('SELECT id FROM users WHERE id = $1 FOR UPDATE', [userId]);
|
|
@@ -251,6 +379,10 @@ class PostgresKnowledgeCollectionRepository {
|
|
|
251
379
|
ORDER BY lower(name), name`, [userId]);
|
|
252
380
|
return result.rows.map(namedResource);
|
|
253
381
|
}
|
|
382
|
+
async findById(collectionId) {
|
|
383
|
+
const result = await this.database.query('SELECT * FROM knowledge_collections WHERE id = $1', [collectionId]);
|
|
384
|
+
return result.rows[0] ? namedResource(result.rows[0]) : null;
|
|
385
|
+
}
|
|
254
386
|
async replace(collection) {
|
|
255
387
|
await this.database.transaction(async (client) => {
|
|
256
388
|
await lockOwner(client, collection.user_id);
|
|
@@ -282,7 +414,7 @@ class PostgresNoteRepository {
|
|
|
282
414
|
async listByOwner(userId, maximum) {
|
|
283
415
|
const result = await this.database.query(`SELECT * FROM notes
|
|
284
416
|
WHERE user_id = $1
|
|
285
|
-
ORDER BY updated_at DESC
|
|
417
|
+
ORDER BY pinned DESC, updated_at DESC
|
|
286
418
|
LIMIT $2`, [userId, maximum]);
|
|
287
419
|
return result.rows.map(note);
|
|
288
420
|
}
|
|
@@ -303,17 +435,19 @@ class PostgresNoteRepository {
|
|
|
303
435
|
throw new PersistenceResourceLimitError('note', maximum);
|
|
304
436
|
}
|
|
305
437
|
}
|
|
306
|
-
const result = await client.query(`INSERT INTO notes (id, user_id, title, content, created_at, updated_at)
|
|
307
|
-
VALUES ($1, $2, $3, $4, $5, $6)
|
|
438
|
+
const result = await client.query(`INSERT INTO notes (id, user_id, title, content, pinned, created_at, updated_at)
|
|
439
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
308
440
|
ON CONFLICT (id) DO UPDATE SET
|
|
309
441
|
title = EXCLUDED.title,
|
|
310
442
|
content = EXCLUDED.content,
|
|
443
|
+
pinned = EXCLUDED.pinned,
|
|
311
444
|
updated_at = EXCLUDED.updated_at
|
|
312
445
|
WHERE notes.user_id = EXCLUDED.user_id`, [
|
|
313
446
|
value.id,
|
|
314
447
|
value.user_id,
|
|
315
448
|
value.title,
|
|
316
449
|
value.content,
|
|
450
|
+
value.pinned,
|
|
317
451
|
value.created_at,
|
|
318
452
|
value.updated_at,
|
|
319
453
|
]);
|
|
@@ -332,6 +466,8 @@ class PostgresNoteRepository {
|
|
|
332
466
|
assign('title', patch.title);
|
|
333
467
|
if (patch.content !== undefined)
|
|
334
468
|
assign('content', patch.content);
|
|
469
|
+
if (patch.pinned !== undefined)
|
|
470
|
+
assign('pinned', patch.pinned);
|
|
335
471
|
assign('updated_at', patch.updated_at);
|
|
336
472
|
values.push(noteId, userId);
|
|
337
473
|
const result = await this.database.query(`UPDATE notes SET ${assignments.join(', ')}
|
|
@@ -342,6 +478,100 @@ class PostgresNoteRepository {
|
|
|
342
478
|
async deleteByOwner(noteId, userId) {
|
|
343
479
|
return (changes((await this.database.query('DELETE FROM notes WHERE id = $1 AND user_id = $2', [noteId, userId])).rowCount) > 0);
|
|
344
480
|
}
|
|
481
|
+
async findById(noteId) {
|
|
482
|
+
const result = await this.database.query('SELECT * FROM notes WHERE id = $1', [noteId]);
|
|
483
|
+
return result.rows[0] ? note(result.rows[0]) : null;
|
|
484
|
+
}
|
|
485
|
+
async patchById(noteId, patch) {
|
|
486
|
+
const assignments = [];
|
|
487
|
+
const values = [];
|
|
488
|
+
const assign = (column, value) => {
|
|
489
|
+
values.push(value);
|
|
490
|
+
assignments.push(`${column} = $${values.length}`);
|
|
491
|
+
};
|
|
492
|
+
if (patch.title !== undefined)
|
|
493
|
+
assign('title', patch.title);
|
|
494
|
+
if (patch.content !== undefined)
|
|
495
|
+
assign('content', patch.content);
|
|
496
|
+
if (patch.pinned !== undefined)
|
|
497
|
+
assign('pinned', patch.pinned);
|
|
498
|
+
assign('updated_at', patch.updated_at);
|
|
499
|
+
values.push(noteId);
|
|
500
|
+
const result = await this.database.query(`UPDATE notes SET ${assignments.join(', ')}
|
|
501
|
+
WHERE id = $${values.length}
|
|
502
|
+
RETURNING *`, values);
|
|
503
|
+
return result.rows[0] ? note(result.rows[0]) : null;
|
|
504
|
+
}
|
|
505
|
+
async listRevisions(noteId, maximum) {
|
|
506
|
+
const result = await this.database.query(`SELECT * FROM note_revisions
|
|
507
|
+
WHERE note_id = $1
|
|
508
|
+
ORDER BY created_at DESC, id DESC
|
|
509
|
+
LIMIT $2`, [noteId, maximum]);
|
|
510
|
+
return result.rows.map(noteRevision);
|
|
511
|
+
}
|
|
512
|
+
async findRevision(revisionId) {
|
|
513
|
+
const result = await this.database.query('SELECT * FROM note_revisions WHERE id = $1', [revisionId]);
|
|
514
|
+
return result.rows[0] ? noteRevision(result.rows[0]) : null;
|
|
515
|
+
}
|
|
516
|
+
async insertRevision(revision, maximum) {
|
|
517
|
+
await this.database.transaction(async (client) => {
|
|
518
|
+
// Strictly increasing per note so same-millisecond snapshots keep a
|
|
519
|
+
// deterministic order for listing, restore, and pruning.
|
|
520
|
+
await client.query(`INSERT INTO note_revisions (id, note_id, title, content, created_at)
|
|
521
|
+
SELECT $1, $2, $3, $4,
|
|
522
|
+
GREATEST($5::bigint, COALESCE(MAX(created_at), 0) + 1)
|
|
523
|
+
FROM note_revisions WHERE note_id = $2`, [
|
|
524
|
+
revision.id,
|
|
525
|
+
revision.note_id,
|
|
526
|
+
revision.title,
|
|
527
|
+
revision.content,
|
|
528
|
+
revision.created_at,
|
|
529
|
+
]);
|
|
530
|
+
await client.query(`DELETE FROM note_revisions
|
|
531
|
+
WHERE note_id = $1
|
|
532
|
+
AND id NOT IN (
|
|
533
|
+
SELECT id FROM note_revisions
|
|
534
|
+
WHERE note_id = $1
|
|
535
|
+
ORDER BY created_at DESC, id DESC
|
|
536
|
+
LIMIT $2
|
|
537
|
+
)`, [revision.note_id, maximum]);
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
async listAttachments(noteId) {
|
|
541
|
+
const result = await this.database.query(`SELECT * FROM note_attachments
|
|
542
|
+
WHERE note_id = $1
|
|
543
|
+
ORDER BY created_at ASC, id ASC`, [noteId]);
|
|
544
|
+
return result.rows.map(noteAttachment);
|
|
545
|
+
}
|
|
546
|
+
async findAttachment(attachmentId) {
|
|
547
|
+
const result = await this.database.query('SELECT * FROM note_attachments WHERE id = $1', [attachmentId]);
|
|
548
|
+
return result.rows[0] ? noteAttachment(result.rows[0]) : null;
|
|
549
|
+
}
|
|
550
|
+
async insertAttachmentWithLimit(attachment, maximum) {
|
|
551
|
+
await this.database.transaction(async (client) => {
|
|
552
|
+
await client.query('SELECT id FROM notes WHERE id = $1 FOR UPDATE', [
|
|
553
|
+
attachment.note_id,
|
|
554
|
+
]);
|
|
555
|
+
const count = await client.query('SELECT COUNT(*)::text AS count FROM note_attachments WHERE note_id = $1', [attachment.note_id]);
|
|
556
|
+
if (Number(count.rows[0]?.count || 0) >= maximum) {
|
|
557
|
+
throw new PersistenceResourceLimitError('note-attachment', maximum);
|
|
558
|
+
}
|
|
559
|
+
await client.query(`INSERT INTO note_attachments
|
|
560
|
+
(id, note_id, blob_id, filename, content_type, size, created_at)
|
|
561
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)`, [
|
|
562
|
+
attachment.id,
|
|
563
|
+
attachment.note_id,
|
|
564
|
+
attachment.blob_id,
|
|
565
|
+
attachment.filename,
|
|
566
|
+
attachment.content_type,
|
|
567
|
+
attachment.size,
|
|
568
|
+
attachment.created_at,
|
|
569
|
+
]);
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
async deleteAttachment(attachmentId) {
|
|
573
|
+
return (changes((await this.database.query('DELETE FROM note_attachments WHERE id = $1', [attachmentId])).rowCount) > 0);
|
|
574
|
+
}
|
|
345
575
|
}
|
|
346
576
|
class PostgresCalendarEventRepository {
|
|
347
577
|
constructor(database) {
|
|
@@ -361,10 +591,48 @@ class PostgresCalendarEventRepository {
|
|
|
361
591
|
LIMIT $2`, [userId, maximum]);
|
|
362
592
|
return result.rows.map(calendarEvent);
|
|
363
593
|
}
|
|
594
|
+
async listByCalendarsBetween(calendarIds, from, to, maximum) {
|
|
595
|
+
if (calendarIds.length === 0)
|
|
596
|
+
return [];
|
|
597
|
+
const result = await this.database.query(`SELECT * FROM calendar_events
|
|
598
|
+
WHERE calendar_id = ANY($1::text[])
|
|
599
|
+
AND start_at >= $2 AND start_at < $3
|
|
600
|
+
ORDER BY start_at ASC
|
|
601
|
+
LIMIT $4`, [[...calendarIds], from, to, maximum]);
|
|
602
|
+
return result.rows.map(calendarEvent);
|
|
603
|
+
}
|
|
604
|
+
async listRecurringByCalendars(calendarIds, maximum) {
|
|
605
|
+
if (calendarIds.length === 0)
|
|
606
|
+
return [];
|
|
607
|
+
const result = await this.database.query(`SELECT * FROM calendar_events
|
|
608
|
+
WHERE calendar_id = ANY($1::text[])
|
|
609
|
+
AND recurrence IS NOT NULL
|
|
610
|
+
ORDER BY start_at ASC
|
|
611
|
+
LIMIT $2`, [[...calendarIds], maximum]);
|
|
612
|
+
return result.rows.map(calendarEvent);
|
|
613
|
+
}
|
|
364
614
|
async findByOwner(eventId, userId) {
|
|
365
615
|
const result = await this.database.query('SELECT * FROM calendar_events WHERE id = $1 AND user_id = $2', [eventId, userId]);
|
|
366
616
|
return result.rows[0] ? calendarEvent(result.rows[0]) : null;
|
|
367
617
|
}
|
|
618
|
+
async findById(eventId) {
|
|
619
|
+
const result = await this.database.query('SELECT * FROM calendar_events WHERE id = $1', [eventId]);
|
|
620
|
+
return result.rows[0] ? calendarEvent(result.rows[0]) : null;
|
|
621
|
+
}
|
|
622
|
+
async listWithReminders(maximum) {
|
|
623
|
+
const result = await this.database.query(`SELECT * FROM calendar_events
|
|
624
|
+
WHERE reminder_minutes IS NOT NULL
|
|
625
|
+
ORDER BY start_at ASC
|
|
626
|
+
LIMIT $1`, [maximum]);
|
|
627
|
+
return result.rows.map(calendarEvent);
|
|
628
|
+
}
|
|
629
|
+
async markReminded(eventId, occurrenceStart) {
|
|
630
|
+
return (changes((await this.database.query(`UPDATE calendar_events
|
|
631
|
+
SET last_reminded_occurrence = $1
|
|
632
|
+
WHERE id = $2
|
|
633
|
+
AND (last_reminded_occurrence IS NULL
|
|
634
|
+
OR last_reminded_occurrence < $1)`, [occurrenceStart, eventId])).rowCount) > 0);
|
|
635
|
+
}
|
|
368
636
|
async replaceWithLimit(value, maximum) {
|
|
369
637
|
await this.database.transaction(async (client) => {
|
|
370
638
|
await lockOwner(client, value.user_id);
|
|
@@ -380,8 +648,9 @@ class PostgresCalendarEventRepository {
|
|
|
380
648
|
}
|
|
381
649
|
const result = await client.query(`INSERT INTO calendar_events
|
|
382
650
|
(id, user_id, title, notes, start_at, end_at, all_day,
|
|
383
|
-
recurrence,
|
|
384
|
-
|
|
651
|
+
recurrence, calendar_id, reminder_minutes,
|
|
652
|
+
last_reminded_occurrence, created_at, updated_at)
|
|
653
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
|
|
385
654
|
ON CONFLICT (id) DO UPDATE SET
|
|
386
655
|
title = EXCLUDED.title,
|
|
387
656
|
notes = EXCLUDED.notes,
|
|
@@ -389,6 +658,9 @@ class PostgresCalendarEventRepository {
|
|
|
389
658
|
end_at = EXCLUDED.end_at,
|
|
390
659
|
all_day = EXCLUDED.all_day,
|
|
391
660
|
recurrence = EXCLUDED.recurrence,
|
|
661
|
+
calendar_id = EXCLUDED.calendar_id,
|
|
662
|
+
reminder_minutes = EXCLUDED.reminder_minutes,
|
|
663
|
+
last_reminded_occurrence = EXCLUDED.last_reminded_occurrence,
|
|
392
664
|
updated_at = EXCLUDED.updated_at
|
|
393
665
|
WHERE calendar_events.user_id = EXCLUDED.user_id`, [
|
|
394
666
|
value.id,
|
|
@@ -399,6 +671,9 @@ class PostgresCalendarEventRepository {
|
|
|
399
671
|
value.end_at,
|
|
400
672
|
value.all_day,
|
|
401
673
|
value.recurrence,
|
|
674
|
+
value.calendar_id,
|
|
675
|
+
value.reminder_minutes,
|
|
676
|
+
value.last_reminded_occurrence,
|
|
402
677
|
value.created_at,
|
|
403
678
|
value.updated_at,
|
|
404
679
|
]);
|
|
@@ -410,6 +685,553 @@ class PostgresCalendarEventRepository {
|
|
|
410
685
|
return (changes((await this.database.query('DELETE FROM calendar_events WHERE id = $1 AND user_id = $2', [eventId, userId])).rowCount) > 0);
|
|
411
686
|
}
|
|
412
687
|
}
|
|
688
|
+
class PostgresCalendarRepository {
|
|
689
|
+
constructor(database) {
|
|
690
|
+
this.database = database;
|
|
691
|
+
}
|
|
692
|
+
async listByOwner(userId, maximum) {
|
|
693
|
+
const result = await this.database.query(`SELECT * FROM calendars
|
|
694
|
+
WHERE user_id = $1
|
|
695
|
+
ORDER BY created_at ASC
|
|
696
|
+
LIMIT $2`, [userId, maximum]);
|
|
697
|
+
return result.rows.map(calendar);
|
|
698
|
+
}
|
|
699
|
+
async findByOwner(calendarId, userId) {
|
|
700
|
+
const result = await this.database.query('SELECT * FROM calendars WHERE id = $1 AND user_id = $2', [calendarId, userId]);
|
|
701
|
+
return result.rows[0] ? calendar(result.rows[0]) : null;
|
|
702
|
+
}
|
|
703
|
+
async findById(calendarId) {
|
|
704
|
+
const result = await this.database.query('SELECT * FROM calendars WHERE id = $1', [calendarId]);
|
|
705
|
+
return result.rows[0] ? calendar(result.rows[0]) : null;
|
|
706
|
+
}
|
|
707
|
+
async replaceWithLimit(value, maximum) {
|
|
708
|
+
await this.database.transaction(async (client) => {
|
|
709
|
+
await lockOwner(client, value.user_id);
|
|
710
|
+
const existing = await client.query('SELECT user_id FROM calendars WHERE id = $1 FOR UPDATE', [value.id]);
|
|
711
|
+
if (existing.rows[0] && existing.rows[0].user_id !== value.user_id) {
|
|
712
|
+
throw new PersistenceResourceConflictError();
|
|
713
|
+
}
|
|
714
|
+
if (!existing.rows[0]) {
|
|
715
|
+
const count = await client.query('SELECT COUNT(*)::text AS count FROM calendars WHERE user_id = $1', [value.user_id]);
|
|
716
|
+
if (Number(count.rows[0]?.count || 0) >= maximum) {
|
|
717
|
+
throw new PersistenceResourceLimitError('calendar', maximum);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
const result = await client.query(`INSERT INTO calendars
|
|
721
|
+
(id, user_id, name, color, created_at, updated_at)
|
|
722
|
+
VALUES ($1, $2, $3, $4, $5, $6)
|
|
723
|
+
ON CONFLICT (id) DO UPDATE SET
|
|
724
|
+
name = EXCLUDED.name,
|
|
725
|
+
color = EXCLUDED.color,
|
|
726
|
+
updated_at = EXCLUDED.updated_at
|
|
727
|
+
WHERE calendars.user_id = EXCLUDED.user_id`, [
|
|
728
|
+
value.id,
|
|
729
|
+
value.user_id,
|
|
730
|
+
value.name,
|
|
731
|
+
value.color,
|
|
732
|
+
value.created_at,
|
|
733
|
+
value.updated_at,
|
|
734
|
+
]);
|
|
735
|
+
if (result.rowCount !== 1)
|
|
736
|
+
throw new PersistenceResourceConflictError();
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
async deleteAndDetach(calendarId, userId) {
|
|
740
|
+
return this.database.transaction(async (client) => {
|
|
741
|
+
const deleted = changes((await client.query('DELETE FROM calendars WHERE id = $1 AND user_id = $2', [calendarId, userId])).rowCount) > 0;
|
|
742
|
+
if (deleted) {
|
|
743
|
+
await client.query(`UPDATE calendar_events
|
|
744
|
+
SET calendar_id = NULL
|
|
745
|
+
WHERE calendar_id = $1 AND user_id = $2`, [calendarId, userId]);
|
|
746
|
+
}
|
|
747
|
+
return deleted;
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
class PostgresChannelRepository {
|
|
752
|
+
constructor(database) {
|
|
753
|
+
this.database = database;
|
|
754
|
+
}
|
|
755
|
+
async insertWithOwner(value, owner, maximumPerUser) {
|
|
756
|
+
await this.database.transaction(async (client) => {
|
|
757
|
+
await lockOwner(client, owner.user_id);
|
|
758
|
+
const count = await client.query('SELECT COUNT(*)::text AS count FROM channels WHERE created_by = $1', [owner.user_id]);
|
|
759
|
+
if (Number(count.rows[0]?.count || 0) >= maximumPerUser) {
|
|
760
|
+
throw new PersistenceResourceLimitError('channel', maximumPerUser);
|
|
761
|
+
}
|
|
762
|
+
await client.query(`INSERT INTO channels
|
|
763
|
+
(id, type, name, description, dm_key, created_by,
|
|
764
|
+
created_at, updated_at, archived_at)
|
|
765
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`, [
|
|
766
|
+
value.id,
|
|
767
|
+
value.type,
|
|
768
|
+
value.name,
|
|
769
|
+
value.description,
|
|
770
|
+
value.dm_key,
|
|
771
|
+
value.created_by,
|
|
772
|
+
value.created_at,
|
|
773
|
+
value.updated_at,
|
|
774
|
+
value.archived_at,
|
|
775
|
+
]);
|
|
776
|
+
await client.query(`INSERT INTO channel_members
|
|
777
|
+
(channel_id, user_id, role, joined_at, last_read_at)
|
|
778
|
+
VALUES ($1, $2, $3, $4, $5)`, [
|
|
779
|
+
owner.channel_id,
|
|
780
|
+
owner.user_id,
|
|
781
|
+
owner.role,
|
|
782
|
+
owner.joined_at,
|
|
783
|
+
owner.last_read_at,
|
|
784
|
+
]);
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
async findById(channelId) {
|
|
788
|
+
const result = await this.database.query('SELECT * FROM channels WHERE id = $1', [channelId]);
|
|
789
|
+
return result.rows[0] ? channel(result.rows[0]) : null;
|
|
790
|
+
}
|
|
791
|
+
async findByDmKey(dmKey) {
|
|
792
|
+
const result = await this.database.query('SELECT * FROM channels WHERE dm_key = $1', [dmKey]);
|
|
793
|
+
return result.rows[0] ? channel(result.rows[0]) : null;
|
|
794
|
+
}
|
|
795
|
+
async listForUser(userId, maximum) {
|
|
796
|
+
const result = await this.database.query(`SELECT
|
|
797
|
+
c.id, c.type, c.name, c.description, c.dm_key, c.created_by,
|
|
798
|
+
c.created_at, c.updated_at, c.archived_at,
|
|
799
|
+
m.channel_id AS member_channel_id, m.user_id AS member_user_id,
|
|
800
|
+
m.role AS member_role, m.joined_at AS member_joined_at,
|
|
801
|
+
m.last_read_at AS member_last_read_at
|
|
802
|
+
FROM channel_members m
|
|
803
|
+
JOIN channels c ON c.id = m.channel_id
|
|
804
|
+
WHERE m.user_id = $1
|
|
805
|
+
ORDER BY c.updated_at DESC
|
|
806
|
+
LIMIT $2`, [userId, maximum]);
|
|
807
|
+
return result.rows.map(row => ({
|
|
808
|
+
channel: channel(row),
|
|
809
|
+
member: channelMember({
|
|
810
|
+
channel_id: row.member_channel_id,
|
|
811
|
+
user_id: row.member_user_id,
|
|
812
|
+
role: row.member_role,
|
|
813
|
+
joined_at: row.member_joined_at,
|
|
814
|
+
last_read_at: row.member_last_read_at,
|
|
815
|
+
}),
|
|
816
|
+
}));
|
|
817
|
+
}
|
|
818
|
+
async listPublic(maximum) {
|
|
819
|
+
const result = await this.database.query(`SELECT * FROM channels
|
|
820
|
+
WHERE type = 'public' AND archived_at IS NULL
|
|
821
|
+
ORDER BY updated_at DESC
|
|
822
|
+
LIMIT $1`, [maximum]);
|
|
823
|
+
return result.rows.map(channel);
|
|
824
|
+
}
|
|
825
|
+
async update(value) {
|
|
826
|
+
await this.database.query(`UPDATE channels SET
|
|
827
|
+
name = $1,
|
|
828
|
+
description = $2,
|
|
829
|
+
updated_at = $3,
|
|
830
|
+
archived_at = $4
|
|
831
|
+
WHERE id = $5`, [
|
|
832
|
+
value.name,
|
|
833
|
+
value.description,
|
|
834
|
+
value.updated_at,
|
|
835
|
+
value.archived_at,
|
|
836
|
+
value.id,
|
|
837
|
+
]);
|
|
838
|
+
}
|
|
839
|
+
async delete(channelId) {
|
|
840
|
+
return (changes((await this.database.query('DELETE FROM channels WHERE id = $1', [
|
|
841
|
+
channelId,
|
|
842
|
+
])).rowCount) > 0);
|
|
843
|
+
}
|
|
844
|
+
async upsertMember(member, maximumMembers) {
|
|
845
|
+
await this.database.transaction(async (client) => {
|
|
846
|
+
const existing = await client.query(`SELECT 1 FROM channel_members
|
|
847
|
+
WHERE channel_id = $1 AND user_id = $2
|
|
848
|
+
FOR UPDATE`, [member.channel_id, member.user_id]);
|
|
849
|
+
if (!existing.rows[0]) {
|
|
850
|
+
const count = await client.query(`SELECT COUNT(*)::text AS count FROM channel_members
|
|
851
|
+
WHERE channel_id = $1`, [member.channel_id]);
|
|
852
|
+
if (Number(count.rows[0]?.count || 0) >= maximumMembers) {
|
|
853
|
+
throw new PersistenceResourceLimitError('channel-member', maximumMembers);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
await client.query(`INSERT INTO channel_members
|
|
857
|
+
(channel_id, user_id, role, joined_at, last_read_at)
|
|
858
|
+
VALUES ($1, $2, $3, $4, $5)
|
|
859
|
+
ON CONFLICT (channel_id, user_id) DO UPDATE SET
|
|
860
|
+
role = EXCLUDED.role`, [
|
|
861
|
+
member.channel_id,
|
|
862
|
+
member.user_id,
|
|
863
|
+
member.role,
|
|
864
|
+
member.joined_at,
|
|
865
|
+
member.last_read_at,
|
|
866
|
+
]);
|
|
867
|
+
});
|
|
868
|
+
}
|
|
869
|
+
async findMember(channelId, userId) {
|
|
870
|
+
const result = await this.database.query('SELECT * FROM channel_members WHERE channel_id = $1 AND user_id = $2', [channelId, userId]);
|
|
871
|
+
return result.rows[0] ? channelMember(result.rows[0]) : null;
|
|
872
|
+
}
|
|
873
|
+
async listMembers(channelId, maximum) {
|
|
874
|
+
const result = await this.database.query(`SELECT * FROM channel_members
|
|
875
|
+
WHERE channel_id = $1
|
|
876
|
+
ORDER BY joined_at ASC
|
|
877
|
+
LIMIT $2`, [channelId, maximum]);
|
|
878
|
+
return result.rows.map(channelMember);
|
|
879
|
+
}
|
|
880
|
+
async removeMember(channelId, userId) {
|
|
881
|
+
return (changes((await this.database.query('DELETE FROM channel_members WHERE channel_id = $1 AND user_id = $2', [channelId, userId])).rowCount) > 0);
|
|
882
|
+
}
|
|
883
|
+
async advanceLastRead(channelId, userId, lastReadAt) {
|
|
884
|
+
return (changes((await this.database.query(`UPDATE channel_members
|
|
885
|
+
SET last_read_at = $1
|
|
886
|
+
WHERE channel_id = $2 AND user_id = $3 AND last_read_at < $1`, [lastReadAt, channelId, userId])).rowCount) > 0);
|
|
887
|
+
}
|
|
888
|
+
async unreadSummaryForUser(userId) {
|
|
889
|
+
const result = await this.database.query(`SELECT
|
|
890
|
+
m.channel_id,
|
|
891
|
+
COUNT(msg.id)::text AS unread_count,
|
|
892
|
+
MAX(msg.created_at) AS latest_message_at
|
|
893
|
+
FROM channel_members m
|
|
894
|
+
LEFT JOIN channel_messages msg
|
|
895
|
+
ON msg.channel_id = m.channel_id
|
|
896
|
+
AND msg.created_at > m.last_read_at
|
|
897
|
+
AND msg.deleted_at IS NULL
|
|
898
|
+
AND (msg.user_id IS NULL OR msg.user_id != m.user_id
|
|
899
|
+
OR msg.author_kind != 'user')
|
|
900
|
+
WHERE m.user_id = $1
|
|
901
|
+
GROUP BY m.channel_id`, [userId]);
|
|
902
|
+
return result.rows.map(row => ({
|
|
903
|
+
channel_id: row.channel_id,
|
|
904
|
+
unread_count: number(row.unread_count, 'channel unread_count'),
|
|
905
|
+
latest_message_at: row.latest_message_at === null
|
|
906
|
+
? null
|
|
907
|
+
: number(row.latest_message_at, 'channel latest_message_at'),
|
|
908
|
+
}));
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
class PostgresChannelMessageRepository {
|
|
912
|
+
constructor(database) {
|
|
913
|
+
this.database = database;
|
|
914
|
+
}
|
|
915
|
+
async insertIfAbsent(message, attachments, maximumPerChannel) {
|
|
916
|
+
return this.database.transaction(async (client) => {
|
|
917
|
+
const existing = await client.query('SELECT * FROM channel_messages WHERE id = $1', [message.id]);
|
|
918
|
+
if (existing.rows[0]) {
|
|
919
|
+
return { stored: channelMessage(existing.rows[0]), inserted: false };
|
|
920
|
+
}
|
|
921
|
+
const count = await client.query(`SELECT COUNT(*)::text AS count FROM channel_messages
|
|
922
|
+
WHERE channel_id = $1`, [message.channel_id]);
|
|
923
|
+
if (Number(count.rows[0]?.count || 0) >= maximumPerChannel) {
|
|
924
|
+
throw new PersistenceResourceLimitError('channel-message', maximumPerChannel);
|
|
925
|
+
}
|
|
926
|
+
await client.query(`INSERT INTO channel_messages
|
|
927
|
+
(id, channel_id, user_id, parent_id, author_kind, model, content,
|
|
928
|
+
metadata, created_at, updated_at, edited_at, deleted_at,
|
|
929
|
+
pinned_at, pinned_by)
|
|
930
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)`, [
|
|
931
|
+
message.id,
|
|
932
|
+
message.channel_id,
|
|
933
|
+
message.user_id,
|
|
934
|
+
message.parent_id,
|
|
935
|
+
message.author_kind,
|
|
936
|
+
message.model,
|
|
937
|
+
message.content,
|
|
938
|
+
message.metadata,
|
|
939
|
+
message.created_at,
|
|
940
|
+
message.updated_at,
|
|
941
|
+
message.edited_at,
|
|
942
|
+
message.deleted_at,
|
|
943
|
+
message.pinned_at,
|
|
944
|
+
message.pinned_by,
|
|
945
|
+
]);
|
|
946
|
+
for (const attachment of attachments) {
|
|
947
|
+
if (attachment.message_id !== message.id) {
|
|
948
|
+
throw new Error('A channel attachment does not belong to its message');
|
|
949
|
+
}
|
|
950
|
+
await client.query(`INSERT INTO channel_attachments
|
|
951
|
+
(id, message_id, channel_id, blob_id, filename, content_type,
|
|
952
|
+
size, created_by, created_at)
|
|
953
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`, [
|
|
954
|
+
attachment.id,
|
|
955
|
+
attachment.message_id,
|
|
956
|
+
attachment.channel_id,
|
|
957
|
+
attachment.blob_id,
|
|
958
|
+
attachment.filename,
|
|
959
|
+
attachment.content_type,
|
|
960
|
+
attachment.size,
|
|
961
|
+
attachment.created_by,
|
|
962
|
+
attachment.created_at,
|
|
963
|
+
]);
|
|
964
|
+
}
|
|
965
|
+
await client.query('UPDATE channels SET updated_at = $1 WHERE id = $2', [
|
|
966
|
+
message.created_at,
|
|
967
|
+
message.channel_id,
|
|
968
|
+
]);
|
|
969
|
+
return { stored: message, inserted: true };
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
async findById(messageId) {
|
|
973
|
+
const result = await this.database.query('SELECT * FROM channel_messages WHERE id = $1', [messageId]);
|
|
974
|
+
return result.rows[0] ? channelMessage(result.rows[0]) : null;
|
|
975
|
+
}
|
|
976
|
+
async listPage(channelId, options) {
|
|
977
|
+
const clauses = ['channel_id = $1'];
|
|
978
|
+
const parameters = [channelId];
|
|
979
|
+
if (options.parentId === null) {
|
|
980
|
+
clauses.push('parent_id IS NULL');
|
|
981
|
+
}
|
|
982
|
+
else if (options.parentId !== undefined) {
|
|
983
|
+
parameters.push(options.parentId);
|
|
984
|
+
clauses.push(`parent_id = $${parameters.length}`);
|
|
985
|
+
}
|
|
986
|
+
if (options.before) {
|
|
987
|
+
parameters.push(options.before.created_at);
|
|
988
|
+
const createdIndex = parameters.length;
|
|
989
|
+
parameters.push(options.before.id);
|
|
990
|
+
clauses.push(`(created_at < $${createdIndex} OR ` +
|
|
991
|
+
`(created_at = $${createdIndex} AND id < $${parameters.length}))`);
|
|
992
|
+
}
|
|
993
|
+
if (options.after) {
|
|
994
|
+
parameters.push(options.after.created_at);
|
|
995
|
+
const createdIndex = parameters.length;
|
|
996
|
+
parameters.push(options.after.id);
|
|
997
|
+
clauses.push(`(created_at > $${createdIndex} OR ` +
|
|
998
|
+
`(created_at = $${createdIndex} AND id > $${parameters.length}))`);
|
|
999
|
+
}
|
|
1000
|
+
const direction = options.after ? 'ASC' : 'DESC';
|
|
1001
|
+
parameters.push(options.limit);
|
|
1002
|
+
const result = await this.database.query(`SELECT * FROM channel_messages
|
|
1003
|
+
WHERE ${clauses.join(' AND ')}
|
|
1004
|
+
ORDER BY created_at ${direction}, id ${direction}
|
|
1005
|
+
LIMIT $${parameters.length}`, parameters);
|
|
1006
|
+
return result.rows.map(channelMessage);
|
|
1007
|
+
}
|
|
1008
|
+
async listThread(parentId, maximum) {
|
|
1009
|
+
const result = await this.database.query(`SELECT * FROM channel_messages
|
|
1010
|
+
WHERE parent_id = $1
|
|
1011
|
+
ORDER BY created_at ASC, id ASC
|
|
1012
|
+
LIMIT $2`, [parentId, maximum]);
|
|
1013
|
+
return result.rows.map(channelMessage);
|
|
1014
|
+
}
|
|
1015
|
+
async countThreadReplies(parentIds) {
|
|
1016
|
+
if (parentIds.length === 0)
|
|
1017
|
+
return {};
|
|
1018
|
+
const result = await this.database.query(`SELECT parent_id, COUNT(*)::text AS count
|
|
1019
|
+
FROM channel_messages
|
|
1020
|
+
WHERE parent_id = ANY($1::text[]) AND deleted_at IS NULL
|
|
1021
|
+
GROUP BY parent_id`, [[...parentIds]]);
|
|
1022
|
+
const counts = {};
|
|
1023
|
+
for (const row of result.rows) {
|
|
1024
|
+
counts[row.parent_id] = number(row.count, 'channel thread reply count');
|
|
1025
|
+
}
|
|
1026
|
+
return counts;
|
|
1027
|
+
}
|
|
1028
|
+
async update(message) {
|
|
1029
|
+
await this.database.query(`UPDATE channel_messages SET
|
|
1030
|
+
content = $1,
|
|
1031
|
+
metadata = $2,
|
|
1032
|
+
updated_at = $3,
|
|
1033
|
+
edited_at = $4,
|
|
1034
|
+
deleted_at = $5,
|
|
1035
|
+
pinned_at = $6,
|
|
1036
|
+
pinned_by = $7
|
|
1037
|
+
WHERE id = $8`, [
|
|
1038
|
+
message.content,
|
|
1039
|
+
message.metadata,
|
|
1040
|
+
message.updated_at,
|
|
1041
|
+
message.edited_at,
|
|
1042
|
+
message.deleted_at,
|
|
1043
|
+
message.pinned_at,
|
|
1044
|
+
message.pinned_by,
|
|
1045
|
+
message.id,
|
|
1046
|
+
]);
|
|
1047
|
+
}
|
|
1048
|
+
async listPinned(channelId, maximum) {
|
|
1049
|
+
const result = await this.database.query(`SELECT * FROM channel_messages
|
|
1050
|
+
WHERE channel_id = $1 AND pinned_at IS NOT NULL
|
|
1051
|
+
AND deleted_at IS NULL
|
|
1052
|
+
ORDER BY pinned_at DESC
|
|
1053
|
+
LIMIT $2`, [channelId, maximum]);
|
|
1054
|
+
return result.rows.map(channelMessage);
|
|
1055
|
+
}
|
|
1056
|
+
async listRecentForChannels(channelIds, maximum) {
|
|
1057
|
+
if (channelIds.length === 0)
|
|
1058
|
+
return [];
|
|
1059
|
+
const result = await this.database.query(`SELECT * FROM channel_messages
|
|
1060
|
+
WHERE channel_id = ANY($1::text[]) AND deleted_at IS NULL
|
|
1061
|
+
ORDER BY created_at DESC
|
|
1062
|
+
LIMIT $2`, [[...channelIds], maximum]);
|
|
1063
|
+
return result.rows.map(channelMessage);
|
|
1064
|
+
}
|
|
1065
|
+
async addReaction(reaction, maximumPerMessage) {
|
|
1066
|
+
return this.database.transaction(async (client) => {
|
|
1067
|
+
const existing = await client.query(`SELECT 1 FROM channel_reactions
|
|
1068
|
+
WHERE message_id = $1 AND user_id = $2 AND emoji = $3`, [reaction.message_id, reaction.user_id, reaction.emoji]);
|
|
1069
|
+
if (existing.rows[0])
|
|
1070
|
+
return false;
|
|
1071
|
+
const count = await client.query(`SELECT COUNT(*)::text AS count FROM channel_reactions
|
|
1072
|
+
WHERE message_id = $1`, [reaction.message_id]);
|
|
1073
|
+
if (Number(count.rows[0]?.count || 0) >= maximumPerMessage) {
|
|
1074
|
+
throw new PersistenceResourceLimitError('channel-reaction', maximumPerMessage);
|
|
1075
|
+
}
|
|
1076
|
+
const result = await client.query(`INSERT INTO channel_reactions
|
|
1077
|
+
(id, message_id, user_id, emoji, created_at)
|
|
1078
|
+
VALUES ($1, $2, $3, $4, $5)
|
|
1079
|
+
ON CONFLICT (message_id, user_id, emoji) DO NOTHING`, [
|
|
1080
|
+
reaction.id,
|
|
1081
|
+
reaction.message_id,
|
|
1082
|
+
reaction.user_id,
|
|
1083
|
+
reaction.emoji,
|
|
1084
|
+
reaction.created_at,
|
|
1085
|
+
]);
|
|
1086
|
+
return changes(result.rowCount) > 0;
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
async removeReaction(messageId, userId, emoji) {
|
|
1090
|
+
return (changes((await this.database.query(`DELETE FROM channel_reactions
|
|
1091
|
+
WHERE message_id = $1 AND user_id = $2 AND emoji = $3`, [messageId, userId, emoji])).rowCount) > 0);
|
|
1092
|
+
}
|
|
1093
|
+
async listReactionsForMessages(messageIds) {
|
|
1094
|
+
if (messageIds.length === 0)
|
|
1095
|
+
return [];
|
|
1096
|
+
const result = await this.database.query(`SELECT * FROM channel_reactions
|
|
1097
|
+
WHERE message_id = ANY($1::text[])
|
|
1098
|
+
ORDER BY created_at ASC`, [[...messageIds]]);
|
|
1099
|
+
return result.rows.map(channelReaction);
|
|
1100
|
+
}
|
|
1101
|
+
async findAttachment(attachmentId) {
|
|
1102
|
+
const result = await this.database.query('SELECT * FROM channel_attachments WHERE id = $1', [attachmentId]);
|
|
1103
|
+
return result.rows[0] ? channelAttachment(result.rows[0]) : null;
|
|
1104
|
+
}
|
|
1105
|
+
async listAttachmentsForMessages(messageIds) {
|
|
1106
|
+
if (messageIds.length === 0)
|
|
1107
|
+
return [];
|
|
1108
|
+
const result = await this.database.query(`SELECT * FROM channel_attachments
|
|
1109
|
+
WHERE message_id = ANY($1::text[])
|
|
1110
|
+
ORDER BY created_at ASC`, [[...messageIds]]);
|
|
1111
|
+
return result.rows.map(channelAttachment);
|
|
1112
|
+
}
|
|
1113
|
+
async listAttachmentBlobIds(channelId) {
|
|
1114
|
+
const result = await this.database.query('SELECT blob_id, created_by FROM channel_attachments WHERE channel_id = $1', [channelId]);
|
|
1115
|
+
return result.rows;
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
class PostgresNotificationRepository {
|
|
1119
|
+
constructor(database) {
|
|
1120
|
+
this.database = database;
|
|
1121
|
+
}
|
|
1122
|
+
async insertWithLimit(value, maximumPerUser) {
|
|
1123
|
+
return this.database.transaction(async (client) => {
|
|
1124
|
+
await lockOwner(client, value.user_id);
|
|
1125
|
+
const result = await client.query(`INSERT INTO notifications
|
|
1126
|
+
(id, user_id, type, title, body, href, source_key,
|
|
1127
|
+
created_at, read_at)
|
|
1128
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
|
1129
|
+
ON CONFLICT (user_id, source_key) DO NOTHING`, [
|
|
1130
|
+
value.id,
|
|
1131
|
+
value.user_id,
|
|
1132
|
+
value.type,
|
|
1133
|
+
value.title,
|
|
1134
|
+
value.body,
|
|
1135
|
+
value.href,
|
|
1136
|
+
value.source_key,
|
|
1137
|
+
value.created_at,
|
|
1138
|
+
value.read_at,
|
|
1139
|
+
]);
|
|
1140
|
+
if (changes(result.rowCount) === 0)
|
|
1141
|
+
return false;
|
|
1142
|
+
await client.query(`DELETE FROM notifications
|
|
1143
|
+
WHERE user_id = $1
|
|
1144
|
+
AND id NOT IN (
|
|
1145
|
+
SELECT id FROM notifications
|
|
1146
|
+
WHERE user_id = $1
|
|
1147
|
+
ORDER BY created_at DESC, id DESC
|
|
1148
|
+
LIMIT $2
|
|
1149
|
+
)`, [value.user_id, maximumPerUser]);
|
|
1150
|
+
return true;
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1153
|
+
async listByOwner(userId, options) {
|
|
1154
|
+
const clauses = ['user_id = $1'];
|
|
1155
|
+
const parameters = [userId];
|
|
1156
|
+
if (options.before !== undefined) {
|
|
1157
|
+
parameters.push(options.before);
|
|
1158
|
+
clauses.push(`created_at < $${parameters.length}`);
|
|
1159
|
+
}
|
|
1160
|
+
if (options.unreadOnly) {
|
|
1161
|
+
clauses.push('read_at IS NULL');
|
|
1162
|
+
}
|
|
1163
|
+
parameters.push(options.limit);
|
|
1164
|
+
const result = await this.database.query(`SELECT * FROM notifications
|
|
1165
|
+
WHERE ${clauses.join(' AND ')}
|
|
1166
|
+
ORDER BY created_at DESC, id DESC
|
|
1167
|
+
LIMIT $${parameters.length}`, parameters);
|
|
1168
|
+
return result.rows.map(notification);
|
|
1169
|
+
}
|
|
1170
|
+
async countUnread(userId) {
|
|
1171
|
+
const result = await this.database.query(`SELECT COUNT(*)::text AS count FROM notifications
|
|
1172
|
+
WHERE user_id = $1 AND read_at IS NULL`, [userId]);
|
|
1173
|
+
return Number(result.rows[0]?.count || 0);
|
|
1174
|
+
}
|
|
1175
|
+
async markRead(notificationId, userId, readAt) {
|
|
1176
|
+
return (changes((await this.database.query(`UPDATE notifications SET read_at = $1
|
|
1177
|
+
WHERE id = $2 AND user_id = $3 AND read_at IS NULL`, [readAt, notificationId, userId])).rowCount) > 0);
|
|
1178
|
+
}
|
|
1179
|
+
async markAllRead(userId, readAt) {
|
|
1180
|
+
return changes((await this.database.query(`UPDATE notifications SET read_at = $1
|
|
1181
|
+
WHERE user_id = $2 AND read_at IS NULL`, [readAt, userId])).rowCount);
|
|
1182
|
+
}
|
|
1183
|
+
async deleteByOwner(notificationId, userId) {
|
|
1184
|
+
return (changes((await this.database.query('DELETE FROM notifications WHERE id = $1 AND user_id = $2', [notificationId, userId])).rowCount) > 0);
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
class PostgresWebhookTargetRepository {
|
|
1188
|
+
constructor(database) {
|
|
1189
|
+
this.database = database;
|
|
1190
|
+
}
|
|
1191
|
+
async list(maximum) {
|
|
1192
|
+
const result = await this.database.query('SELECT * FROM webhook_targets ORDER BY created_at ASC LIMIT $1', [maximum]);
|
|
1193
|
+
return result.rows.map(webhookTarget);
|
|
1194
|
+
}
|
|
1195
|
+
async findById(targetId) {
|
|
1196
|
+
const result = await this.database.query('SELECT * FROM webhook_targets WHERE id = $1', [targetId]);
|
|
1197
|
+
return result.rows[0] ? webhookTarget(result.rows[0]) : null;
|
|
1198
|
+
}
|
|
1199
|
+
async replaceWithLimit(value, maximum) {
|
|
1200
|
+
await this.database.transaction(async (client) => {
|
|
1201
|
+
const existing = await client.query('SELECT 1 FROM webhook_targets WHERE id = $1 FOR UPDATE', [value.id]);
|
|
1202
|
+
if (!existing.rows[0]) {
|
|
1203
|
+
const count = await client.query('SELECT COUNT(*)::text AS count FROM webhook_targets');
|
|
1204
|
+
if (Number(count.rows[0]?.count || 0) >= maximum) {
|
|
1205
|
+
throw new PersistenceResourceLimitError('webhook-target', maximum);
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
await client.query(`INSERT INTO webhook_targets
|
|
1209
|
+
(id, name, url, secret, events, enabled, created_by,
|
|
1210
|
+
created_at, updated_at)
|
|
1211
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
|
1212
|
+
ON CONFLICT (id) DO UPDATE SET
|
|
1213
|
+
name = EXCLUDED.name,
|
|
1214
|
+
url = EXCLUDED.url,
|
|
1215
|
+
secret = EXCLUDED.secret,
|
|
1216
|
+
events = EXCLUDED.events,
|
|
1217
|
+
enabled = EXCLUDED.enabled,
|
|
1218
|
+
updated_at = EXCLUDED.updated_at`, [
|
|
1219
|
+
value.id,
|
|
1220
|
+
value.name,
|
|
1221
|
+
value.url,
|
|
1222
|
+
value.secret,
|
|
1223
|
+
value.events,
|
|
1224
|
+
value.enabled,
|
|
1225
|
+
value.created_by,
|
|
1226
|
+
value.created_at,
|
|
1227
|
+
value.updated_at,
|
|
1228
|
+
]);
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
1231
|
+
async delete(targetId) {
|
|
1232
|
+
return (changes((await this.database.query('DELETE FROM webhook_targets WHERE id = $1', [targetId])).rowCount) > 0);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
413
1235
|
class PostgresAutomationRepository {
|
|
414
1236
|
constructor(database) {
|
|
415
1237
|
this.database = database;
|
|
@@ -583,15 +1405,504 @@ class PostgresAutomationRunRepository {
|
|
|
583
1405
|
AND finished_at <= $3`, [seenAt, userId, seenAt])).rowCount);
|
|
584
1406
|
}
|
|
585
1407
|
}
|
|
586
|
-
class
|
|
1408
|
+
class PostgresToolServerRepository {
|
|
587
1409
|
constructor(database) {
|
|
588
1410
|
this.database = database;
|
|
589
1411
|
}
|
|
590
|
-
async
|
|
591
|
-
const result = await this.database.query(`SELECT * FROM
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
1412
|
+
async list(maximum) {
|
|
1413
|
+
const result = await this.database.query(`SELECT * FROM tool_servers
|
|
1414
|
+
ORDER BY updated_at DESC
|
|
1415
|
+
LIMIT $1`, [maximum]);
|
|
1416
|
+
return result.rows.map(toolServer);
|
|
1417
|
+
}
|
|
1418
|
+
async findById(serverId) {
|
|
1419
|
+
const result = await this.database.query('SELECT * FROM tool_servers WHERE id = $1', [serverId]);
|
|
1420
|
+
return result.rows[0] ? toolServer(result.rows[0]) : null;
|
|
1421
|
+
}
|
|
1422
|
+
async replaceWithLimit(value, maximum) {
|
|
1423
|
+
await this.database.transaction(async (client) => {
|
|
1424
|
+
await lockOwner(client, value.user_id);
|
|
1425
|
+
const existing = await client.query('SELECT id FROM tool_servers WHERE id = $1 FOR UPDATE', [value.id]);
|
|
1426
|
+
if (!existing.rows[0]) {
|
|
1427
|
+
const count = await client.query('SELECT COUNT(*)::text AS count FROM tool_servers');
|
|
1428
|
+
if (Number(count.rows[0]?.count || 0) >= maximum) {
|
|
1429
|
+
throw new PersistenceResourceLimitError('tool-server', maximum);
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
await client.query(`INSERT INTO tool_servers
|
|
1433
|
+
(id, user_id, name, description, kind, base_url, spec, spec_digest,
|
|
1434
|
+
spec_revision, auth_mode, auth_header, access_mode, enabled,
|
|
1435
|
+
timeout_ms, max_response_bytes, created_at, updated_at)
|
|
1436
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14,
|
|
1437
|
+
$15, $16, $17)
|
|
1438
|
+
ON CONFLICT (id) DO UPDATE SET
|
|
1439
|
+
name = EXCLUDED.name,
|
|
1440
|
+
description = EXCLUDED.description,
|
|
1441
|
+
base_url = EXCLUDED.base_url,
|
|
1442
|
+
spec = EXCLUDED.spec,
|
|
1443
|
+
spec_digest = EXCLUDED.spec_digest,
|
|
1444
|
+
spec_revision = EXCLUDED.spec_revision,
|
|
1445
|
+
auth_mode = EXCLUDED.auth_mode,
|
|
1446
|
+
auth_header = EXCLUDED.auth_header,
|
|
1447
|
+
access_mode = EXCLUDED.access_mode,
|
|
1448
|
+
enabled = EXCLUDED.enabled,
|
|
1449
|
+
timeout_ms = EXCLUDED.timeout_ms,
|
|
1450
|
+
max_response_bytes = EXCLUDED.max_response_bytes,
|
|
1451
|
+
updated_at = EXCLUDED.updated_at`, [
|
|
1452
|
+
value.id,
|
|
1453
|
+
value.user_id,
|
|
1454
|
+
value.name,
|
|
1455
|
+
value.description,
|
|
1456
|
+
value.kind,
|
|
1457
|
+
value.base_url,
|
|
1458
|
+
value.spec,
|
|
1459
|
+
value.spec_digest,
|
|
1460
|
+
value.spec_revision,
|
|
1461
|
+
value.auth_mode,
|
|
1462
|
+
value.auth_header,
|
|
1463
|
+
value.access_mode,
|
|
1464
|
+
value.enabled,
|
|
1465
|
+
value.timeout_ms,
|
|
1466
|
+
value.max_response_bytes,
|
|
1467
|
+
value.created_at,
|
|
1468
|
+
value.updated_at,
|
|
1469
|
+
]);
|
|
1470
|
+
});
|
|
1471
|
+
}
|
|
1472
|
+
async delete(serverId) {
|
|
1473
|
+
return (changes((await this.database.query('DELETE FROM tool_servers WHERE id = $1', [
|
|
1474
|
+
serverId,
|
|
1475
|
+
])).rowCount) > 0);
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
class PostgresToolServerToolRepository {
|
|
1479
|
+
constructor(database) {
|
|
1480
|
+
this.database = database;
|
|
1481
|
+
}
|
|
1482
|
+
async listByServer(serverId) {
|
|
1483
|
+
const result = await this.database.query(`SELECT * FROM tool_server_tools
|
|
1484
|
+
WHERE server_id = $1
|
|
1485
|
+
ORDER BY name ASC`, [serverId]);
|
|
1486
|
+
return result.rows.map(toolServerTool);
|
|
1487
|
+
}
|
|
1488
|
+
async replaceAllForServer(serverId, tools) {
|
|
1489
|
+
await this.database.transaction(async (client) => {
|
|
1490
|
+
const previousResult = await client.query(`SELECT name, enabled, side_effect FROM tool_server_tools
|
|
1491
|
+
WHERE server_id = $1 FOR UPDATE`, [serverId]);
|
|
1492
|
+
const previous = new Map(previousResult.rows.map(row => [
|
|
1493
|
+
row.name,
|
|
1494
|
+
{
|
|
1495
|
+
enabled: number(row.enabled, 'tool enabled'),
|
|
1496
|
+
side_effect: number(row.side_effect, 'tool side_effect'),
|
|
1497
|
+
},
|
|
1498
|
+
]));
|
|
1499
|
+
await client.query('DELETE FROM tool_server_tools WHERE server_id = $1', [
|
|
1500
|
+
serverId,
|
|
1501
|
+
]);
|
|
1502
|
+
for (const tool of tools) {
|
|
1503
|
+
if (tool.server_id !== serverId) {
|
|
1504
|
+
throw new Error('A tool row does not belong to its server');
|
|
1505
|
+
}
|
|
1506
|
+
const kept = previous.get(tool.name);
|
|
1507
|
+
await client.query(`INSERT INTO tool_server_tools
|
|
1508
|
+
(id, server_id, name, description, params_schema, detail,
|
|
1509
|
+
side_effect, enabled, created_at, updated_at)
|
|
1510
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`, [
|
|
1511
|
+
tool.id,
|
|
1512
|
+
tool.server_id,
|
|
1513
|
+
tool.name,
|
|
1514
|
+
tool.description,
|
|
1515
|
+
tool.params_schema,
|
|
1516
|
+
tool.detail,
|
|
1517
|
+
kept ? kept.side_effect : tool.side_effect,
|
|
1518
|
+
kept ? kept.enabled : tool.enabled,
|
|
1519
|
+
tool.created_at,
|
|
1520
|
+
tool.updated_at,
|
|
1521
|
+
]);
|
|
1522
|
+
}
|
|
1523
|
+
});
|
|
1524
|
+
}
|
|
1525
|
+
async updateOverrides(serverId, toolName, overrides, updatedAt) {
|
|
1526
|
+
const assignments = ['updated_at = $1'];
|
|
1527
|
+
const values = [updatedAt];
|
|
1528
|
+
if (overrides.enabled !== undefined) {
|
|
1529
|
+
values.push(overrides.enabled);
|
|
1530
|
+
assignments.push(`enabled = $${values.length}`);
|
|
1531
|
+
}
|
|
1532
|
+
if (overrides.side_effect !== undefined) {
|
|
1533
|
+
values.push(overrides.side_effect);
|
|
1534
|
+
assignments.push(`side_effect = $${values.length}`);
|
|
1535
|
+
}
|
|
1536
|
+
values.push(serverId, toolName);
|
|
1537
|
+
const result = await this.database.query(`UPDATE tool_server_tools
|
|
1538
|
+
SET ${assignments.join(', ')}
|
|
1539
|
+
WHERE server_id = $${values.length - 1} AND name = $${values.length}
|
|
1540
|
+
RETURNING *`, values);
|
|
1541
|
+
return result.rows[0] ? toolServerTool(result.rows[0]) : null;
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
class PostgresToolServerCredentialRepository {
|
|
1545
|
+
constructor(database) {
|
|
1546
|
+
this.database = database;
|
|
1547
|
+
}
|
|
1548
|
+
async find(serverId, userId) {
|
|
1549
|
+
const result = await this.database.query(`SELECT * FROM tool_server_credentials
|
|
1550
|
+
WHERE server_id = $1 AND user_id = $2`, [serverId, userId]);
|
|
1551
|
+
return result.rows[0] ? toolServerCredential(result.rows[0]) : null;
|
|
1552
|
+
}
|
|
1553
|
+
async upsert(credential) {
|
|
1554
|
+
await this.database.query(`INSERT INTO tool_server_credentials
|
|
1555
|
+
(id, server_id, user_id, secret, created_at, updated_at)
|
|
1556
|
+
VALUES ($1, $2, $3, $4, $5, $6)
|
|
1557
|
+
ON CONFLICT (server_id, user_id) DO UPDATE SET
|
|
1558
|
+
secret = EXCLUDED.secret,
|
|
1559
|
+
updated_at = EXCLUDED.updated_at`, [
|
|
1560
|
+
credential.id,
|
|
1561
|
+
credential.server_id,
|
|
1562
|
+
credential.user_id,
|
|
1563
|
+
credential.secret,
|
|
1564
|
+
credential.created_at,
|
|
1565
|
+
credential.updated_at,
|
|
1566
|
+
]);
|
|
1567
|
+
}
|
|
1568
|
+
async delete(serverId, userId) {
|
|
1569
|
+
return (changes((await this.database.query(`DELETE FROM tool_server_credentials
|
|
1570
|
+
WHERE server_id = $1 AND user_id = $2`, [serverId, userId])).rowCount) > 0);
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
class PostgresToolApprovalRepository {
|
|
1574
|
+
constructor(database) {
|
|
1575
|
+
this.database = database;
|
|
1576
|
+
}
|
|
1577
|
+
async insert(approval) {
|
|
1578
|
+
await this.database.query(`INSERT INTO tool_approvals
|
|
1579
|
+
(id, user_id, session_id, server_id, tool_name, call_id,
|
|
1580
|
+
arguments_digest, scope, status, created_at, resolved_at, expires_at)
|
|
1581
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)`, [
|
|
1582
|
+
approval.id,
|
|
1583
|
+
approval.user_id,
|
|
1584
|
+
approval.session_id,
|
|
1585
|
+
approval.server_id,
|
|
1586
|
+
approval.tool_name,
|
|
1587
|
+
approval.call_id,
|
|
1588
|
+
approval.arguments_digest,
|
|
1589
|
+
approval.scope,
|
|
1590
|
+
approval.status,
|
|
1591
|
+
approval.created_at,
|
|
1592
|
+
approval.resolved_at,
|
|
1593
|
+
approval.expires_at,
|
|
1594
|
+
]);
|
|
1595
|
+
}
|
|
1596
|
+
async findByOwner(approvalId, userId) {
|
|
1597
|
+
const result = await this.database.query('SELECT * FROM tool_approvals WHERE id = $1 AND user_id = $2', [approvalId, userId]);
|
|
1598
|
+
return result.rows[0] ? toolApproval(result.rows[0]) : null;
|
|
1599
|
+
}
|
|
1600
|
+
async findStanding(userId, serverId, toolName, sessionId) {
|
|
1601
|
+
const result = await this.database.query(`SELECT * FROM tool_approvals
|
|
1602
|
+
WHERE user_id = $1
|
|
1603
|
+
AND status = 'approved'
|
|
1604
|
+
AND tool_name = $2
|
|
1605
|
+
AND ((server_id IS NULL AND $3::text IS NULL) OR server_id = $3)
|
|
1606
|
+
AND (scope = 'always'
|
|
1607
|
+
OR (scope = 'session' AND session_id IS NOT NULL AND session_id = $4))
|
|
1608
|
+
ORDER BY resolved_at DESC
|
|
1609
|
+
LIMIT 1`, [userId, toolName, serverId, sessionId]);
|
|
1610
|
+
return result.rows[0] ? toolApproval(result.rows[0]) : null;
|
|
1611
|
+
}
|
|
1612
|
+
async resolvePending(approvalId, userId, status, scope, resolvedAt) {
|
|
1613
|
+
const result = await this.database.query(`UPDATE tool_approvals
|
|
1614
|
+
SET status = $1, scope = $2, resolved_at = $3
|
|
1615
|
+
WHERE id = $4 AND user_id = $5 AND status = 'pending'
|
|
1616
|
+
AND (expires_at IS NULL OR expires_at > $3)
|
|
1617
|
+
RETURNING *`, [status, scope, resolvedAt, approvalId, userId]);
|
|
1618
|
+
return result.rows[0] ? toolApproval(result.rows[0]) : null;
|
|
1619
|
+
}
|
|
1620
|
+
async listPendingByOwner(userId, maximum) {
|
|
1621
|
+
const result = await this.database.query(`SELECT * FROM tool_approvals
|
|
1622
|
+
WHERE user_id = $1 AND status = 'pending'
|
|
1623
|
+
ORDER BY created_at ASC
|
|
1624
|
+
LIMIT $2`, [userId, maximum]);
|
|
1625
|
+
return result.rows.map(toolApproval);
|
|
1626
|
+
}
|
|
1627
|
+
async listStandingByOwner(userId, maximum) {
|
|
1628
|
+
const result = await this.database.query(`SELECT * FROM tool_approvals
|
|
1629
|
+
WHERE user_id = $1 AND status = 'approved'
|
|
1630
|
+
AND scope IN ('session', 'always')
|
|
1631
|
+
ORDER BY resolved_at DESC
|
|
1632
|
+
LIMIT $2`, [userId, maximum]);
|
|
1633
|
+
return result.rows.map(toolApproval);
|
|
1634
|
+
}
|
|
1635
|
+
async expirePending(now) {
|
|
1636
|
+
return changes((await this.database.query(`UPDATE tool_approvals
|
|
1637
|
+
SET status = 'expired', resolved_at = $1
|
|
1638
|
+
WHERE status = 'pending' AND expires_at IS NOT NULL
|
|
1639
|
+
AND expires_at <= $1`, [now])).rowCount);
|
|
1640
|
+
}
|
|
1641
|
+
async deleteByOwner(approvalId, userId) {
|
|
1642
|
+
return (changes((await this.database.query('DELETE FROM tool_approvals WHERE id = $1 AND user_id = $2', [approvalId, userId])).rowCount) > 0);
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
class PostgresPromptRepository {
|
|
1646
|
+
constructor(database) {
|
|
1647
|
+
this.database = database;
|
|
1648
|
+
}
|
|
1649
|
+
async listByOwner(userId, maximum) {
|
|
1650
|
+
const result = await this.database.query(`SELECT * FROM prompts
|
|
1651
|
+
WHERE user_id = $1
|
|
1652
|
+
ORDER BY updated_at DESC
|
|
1653
|
+
LIMIT $2`, [userId, maximum]);
|
|
1654
|
+
return result.rows.map(prompt);
|
|
1655
|
+
}
|
|
1656
|
+
async findByOwner(promptId, userId) {
|
|
1657
|
+
const result = await this.database.query('SELECT * FROM prompts WHERE id = $1 AND user_id = $2', [promptId, userId]);
|
|
1658
|
+
return result.rows[0] ? prompt(result.rows[0]) : null;
|
|
1659
|
+
}
|
|
1660
|
+
async findById(promptId) {
|
|
1661
|
+
const result = await this.database.query('SELECT * FROM prompts WHERE id = $1', [promptId]);
|
|
1662
|
+
return result.rows[0] ? prompt(result.rows[0]) : null;
|
|
1663
|
+
}
|
|
1664
|
+
async findBySlug(userId, slug) {
|
|
1665
|
+
const result = await this.database.query('SELECT * FROM prompts WHERE user_id = $1 AND slug = $2', [userId, slug]);
|
|
1666
|
+
return result.rows[0] ? prompt(result.rows[0]) : null;
|
|
1667
|
+
}
|
|
1668
|
+
async replaceWithLimit(value, maximum, archivedVersion) {
|
|
1669
|
+
await this.database.transaction(async (client) => {
|
|
1670
|
+
await lockOwner(client, value.user_id);
|
|
1671
|
+
const existing = await client.query('SELECT user_id FROM prompts WHERE id = $1 FOR UPDATE', [value.id]);
|
|
1672
|
+
if (existing.rows[0] && existing.rows[0].user_id !== value.user_id) {
|
|
1673
|
+
throw new PersistenceResourceConflictError();
|
|
1674
|
+
}
|
|
1675
|
+
if (!existing.rows[0]) {
|
|
1676
|
+
const count = await client.query('SELECT COUNT(*)::text AS count FROM prompts WHERE user_id = $1', [value.user_id]);
|
|
1677
|
+
if (Number(count.rows[0]?.count || 0) >= maximum) {
|
|
1678
|
+
throw new PersistenceResourceLimitError('prompt', maximum);
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
const result = await client.query(`INSERT INTO prompts
|
|
1682
|
+
(id, user_id, slug, title, description, content, variables, tags,
|
|
1683
|
+
version, created_at, updated_at)
|
|
1684
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
|
1685
|
+
ON CONFLICT (id) DO UPDATE SET
|
|
1686
|
+
slug = EXCLUDED.slug,
|
|
1687
|
+
title = EXCLUDED.title,
|
|
1688
|
+
description = EXCLUDED.description,
|
|
1689
|
+
content = EXCLUDED.content,
|
|
1690
|
+
variables = EXCLUDED.variables,
|
|
1691
|
+
tags = EXCLUDED.tags,
|
|
1692
|
+
version = EXCLUDED.version,
|
|
1693
|
+
updated_at = EXCLUDED.updated_at
|
|
1694
|
+
WHERE prompts.user_id = EXCLUDED.user_id`, [
|
|
1695
|
+
value.id,
|
|
1696
|
+
value.user_id,
|
|
1697
|
+
value.slug,
|
|
1698
|
+
value.title,
|
|
1699
|
+
value.description,
|
|
1700
|
+
value.content,
|
|
1701
|
+
value.variables,
|
|
1702
|
+
value.tags,
|
|
1703
|
+
value.version,
|
|
1704
|
+
value.created_at,
|
|
1705
|
+
value.updated_at,
|
|
1706
|
+
]);
|
|
1707
|
+
if (result.rowCount !== 1)
|
|
1708
|
+
throw new PersistenceResourceConflictError();
|
|
1709
|
+
if (archivedVersion) {
|
|
1710
|
+
await client.query(`INSERT INTO prompt_versions
|
|
1711
|
+
(id, prompt_id, version, content, variables, created_at)
|
|
1712
|
+
VALUES ($1, $2, $3, $4, $5, $6)
|
|
1713
|
+
ON CONFLICT (prompt_id, version) DO NOTHING`, [
|
|
1714
|
+
archivedVersion.id,
|
|
1715
|
+
archivedVersion.prompt_id,
|
|
1716
|
+
archivedVersion.version,
|
|
1717
|
+
archivedVersion.content,
|
|
1718
|
+
archivedVersion.variables,
|
|
1719
|
+
archivedVersion.created_at,
|
|
1720
|
+
]);
|
|
1721
|
+
}
|
|
1722
|
+
});
|
|
1723
|
+
}
|
|
1724
|
+
async listVersions(promptId, maximum) {
|
|
1725
|
+
const result = await this.database.query(`SELECT * FROM prompt_versions
|
|
1726
|
+
WHERE prompt_id = $1
|
|
1727
|
+
ORDER BY version DESC
|
|
1728
|
+
LIMIT $2`, [promptId, maximum]);
|
|
1729
|
+
return result.rows.map(promptVersion);
|
|
1730
|
+
}
|
|
1731
|
+
async findVersion(promptId, version) {
|
|
1732
|
+
const result = await this.database.query('SELECT * FROM prompt_versions WHERE prompt_id = $1 AND version = $2', [promptId, version]);
|
|
1733
|
+
return result.rows[0] ? promptVersion(result.rows[0]) : null;
|
|
1734
|
+
}
|
|
1735
|
+
async deleteByOwner(promptId, userId) {
|
|
1736
|
+
return (changes((await this.database.query('DELETE FROM prompts WHERE id = $1 AND user_id = $2', [promptId, userId])).rowCount) > 0);
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
class PostgresSkillRepository {
|
|
1740
|
+
constructor(database) {
|
|
1741
|
+
this.database = database;
|
|
1742
|
+
}
|
|
1743
|
+
async listByOwner(userId, maximum) {
|
|
1744
|
+
const result = await this.database.query(`SELECT * FROM skills
|
|
1745
|
+
WHERE user_id = $1
|
|
1746
|
+
ORDER BY updated_at DESC
|
|
1747
|
+
LIMIT $2`, [userId, maximum]);
|
|
1748
|
+
return result.rows.map(skill);
|
|
1749
|
+
}
|
|
1750
|
+
async findByOwner(skillId, userId) {
|
|
1751
|
+
const result = await this.database.query('SELECT * FROM skills WHERE id = $1 AND user_id = $2', [skillId, userId]);
|
|
1752
|
+
return result.rows[0] ? skill(result.rows[0]) : null;
|
|
1753
|
+
}
|
|
1754
|
+
async findById(skillId) {
|
|
1755
|
+
const result = await this.database.query('SELECT * FROM skills WHERE id = $1', [skillId]);
|
|
1756
|
+
return result.rows[0] ? skill(result.rows[0]) : null;
|
|
1757
|
+
}
|
|
1758
|
+
async findBySlug(userId, slug) {
|
|
1759
|
+
const result = await this.database.query('SELECT * FROM skills WHERE user_id = $1 AND slug = $2', [userId, slug]);
|
|
1760
|
+
return result.rows[0] ? skill(result.rows[0]) : null;
|
|
1761
|
+
}
|
|
1762
|
+
async replaceWithLimit(value, maximum, archivedVersion) {
|
|
1763
|
+
await this.database.transaction(async (client) => {
|
|
1764
|
+
await lockOwner(client, value.user_id);
|
|
1765
|
+
const existing = await client.query('SELECT user_id FROM skills WHERE id = $1 FOR UPDATE', [value.id]);
|
|
1766
|
+
if (existing.rows[0] && existing.rows[0].user_id !== value.user_id) {
|
|
1767
|
+
throw new PersistenceResourceConflictError();
|
|
1768
|
+
}
|
|
1769
|
+
if (!existing.rows[0]) {
|
|
1770
|
+
const count = await client.query('SELECT COUNT(*)::text AS count FROM skills WHERE user_id = $1', [value.user_id]);
|
|
1771
|
+
if (Number(count.rows[0]?.count || 0) >= maximum) {
|
|
1772
|
+
throw new PersistenceResourceLimitError('skill', maximum);
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
const result = await client.query(`INSERT INTO skills
|
|
1776
|
+
(id, user_id, slug, name, description, instructions, enabled,
|
|
1777
|
+
version, created_at, updated_at)
|
|
1778
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
|
1779
|
+
ON CONFLICT (id) DO UPDATE SET
|
|
1780
|
+
slug = EXCLUDED.slug,
|
|
1781
|
+
name = EXCLUDED.name,
|
|
1782
|
+
description = EXCLUDED.description,
|
|
1783
|
+
instructions = EXCLUDED.instructions,
|
|
1784
|
+
enabled = EXCLUDED.enabled,
|
|
1785
|
+
version = EXCLUDED.version,
|
|
1786
|
+
updated_at = EXCLUDED.updated_at
|
|
1787
|
+
WHERE skills.user_id = EXCLUDED.user_id`, [
|
|
1788
|
+
value.id,
|
|
1789
|
+
value.user_id,
|
|
1790
|
+
value.slug,
|
|
1791
|
+
value.name,
|
|
1792
|
+
value.description,
|
|
1793
|
+
value.instructions,
|
|
1794
|
+
value.enabled,
|
|
1795
|
+
value.version,
|
|
1796
|
+
value.created_at,
|
|
1797
|
+
value.updated_at,
|
|
1798
|
+
]);
|
|
1799
|
+
if (result.rowCount !== 1)
|
|
1800
|
+
throw new PersistenceResourceConflictError();
|
|
1801
|
+
if (archivedVersion) {
|
|
1802
|
+
await client.query(`INSERT INTO skill_versions
|
|
1803
|
+
(id, skill_id, version, instructions, created_at)
|
|
1804
|
+
VALUES ($1, $2, $3, $4, $5)
|
|
1805
|
+
ON CONFLICT (skill_id, version) DO NOTHING`, [
|
|
1806
|
+
archivedVersion.id,
|
|
1807
|
+
archivedVersion.skill_id,
|
|
1808
|
+
archivedVersion.version,
|
|
1809
|
+
archivedVersion.instructions,
|
|
1810
|
+
archivedVersion.created_at,
|
|
1811
|
+
]);
|
|
1812
|
+
}
|
|
1813
|
+
});
|
|
1814
|
+
}
|
|
1815
|
+
async listVersions(skillId, maximum) {
|
|
1816
|
+
const result = await this.database.query(`SELECT * FROM skill_versions
|
|
1817
|
+
WHERE skill_id = $1
|
|
1818
|
+
ORDER BY version DESC
|
|
1819
|
+
LIMIT $2`, [skillId, maximum]);
|
|
1820
|
+
return result.rows.map(skillVersion);
|
|
1821
|
+
}
|
|
1822
|
+
async findVersion(skillId, version) {
|
|
1823
|
+
const result = await this.database.query('SELECT * FROM skill_versions WHERE skill_id = $1 AND version = $2', [skillId, version]);
|
|
1824
|
+
return result.rows[0] ? skillVersion(result.rows[0]) : null;
|
|
1825
|
+
}
|
|
1826
|
+
async deleteByOwner(skillId, userId) {
|
|
1827
|
+
return (changes((await this.database.query('DELETE FROM skills WHERE id = $1 AND user_id = $2', [skillId, userId])).rowCount) > 0);
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
class PostgresSkillFileRepository {
|
|
1831
|
+
constructor(database) {
|
|
1832
|
+
this.database = database;
|
|
1833
|
+
}
|
|
1834
|
+
async listBySkill(skillId) {
|
|
1835
|
+
const result = await this.database.query(`SELECT * FROM skill_files
|
|
1836
|
+
WHERE skill_id = $1
|
|
1837
|
+
ORDER BY path ASC`, [skillId]);
|
|
1838
|
+
return result.rows.map(skillFile);
|
|
1839
|
+
}
|
|
1840
|
+
async find(skillId, path) {
|
|
1841
|
+
const result = await this.database.query('SELECT * FROM skill_files WHERE skill_id = $1 AND path = $2', [skillId, path]);
|
|
1842
|
+
return result.rows[0] ? skillFile(result.rows[0]) : null;
|
|
1843
|
+
}
|
|
1844
|
+
async upsert(file, maximumPerSkill) {
|
|
1845
|
+
await this.database.transaction(async (client) => {
|
|
1846
|
+
const existing = await client.query('SELECT id FROM skill_files WHERE skill_id = $1 AND path = $2 FOR UPDATE', [file.skill_id, file.path]);
|
|
1847
|
+
if (!existing.rows[0]) {
|
|
1848
|
+
const count = await client.query('SELECT COUNT(*)::text AS count FROM skill_files WHERE skill_id = $1', [file.skill_id]);
|
|
1849
|
+
if (Number(count.rows[0]?.count || 0) >= maximumPerSkill) {
|
|
1850
|
+
throw new PersistenceResourceLimitError('skill-file', maximumPerSkill);
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
await client.query(`INSERT INTO skill_files
|
|
1854
|
+
(id, skill_id, path, content, size, created_at, updated_at)
|
|
1855
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
1856
|
+
ON CONFLICT (skill_id, path) DO UPDATE SET
|
|
1857
|
+
content = EXCLUDED.content,
|
|
1858
|
+
size = EXCLUDED.size,
|
|
1859
|
+
updated_at = EXCLUDED.updated_at`, [
|
|
1860
|
+
file.id,
|
|
1861
|
+
file.skill_id,
|
|
1862
|
+
file.path,
|
|
1863
|
+
file.content,
|
|
1864
|
+
file.size,
|
|
1865
|
+
file.created_at,
|
|
1866
|
+
file.updated_at,
|
|
1867
|
+
]);
|
|
1868
|
+
});
|
|
1869
|
+
}
|
|
1870
|
+
async replaceAllForSkill(skillId, files) {
|
|
1871
|
+
await this.database.transaction(async (client) => {
|
|
1872
|
+
await client.query('DELETE FROM skill_files WHERE skill_id = $1', [
|
|
1873
|
+
skillId,
|
|
1874
|
+
]);
|
|
1875
|
+
for (const file of files) {
|
|
1876
|
+
if (file.skill_id !== skillId) {
|
|
1877
|
+
throw new Error('A skill file does not belong to its skill');
|
|
1878
|
+
}
|
|
1879
|
+
await client.query(`INSERT INTO skill_files
|
|
1880
|
+
(id, skill_id, path, content, size, created_at, updated_at)
|
|
1881
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)`, [
|
|
1882
|
+
file.id,
|
|
1883
|
+
file.skill_id,
|
|
1884
|
+
file.path,
|
|
1885
|
+
file.content,
|
|
1886
|
+
file.size,
|
|
1887
|
+
file.created_at,
|
|
1888
|
+
file.updated_at,
|
|
1889
|
+
]);
|
|
1890
|
+
}
|
|
1891
|
+
});
|
|
1892
|
+
}
|
|
1893
|
+
async delete(skillId, path) {
|
|
1894
|
+
return (changes((await this.database.query('DELETE FROM skill_files WHERE skill_id = $1 AND path = $2', [skillId, path])).rowCount) > 0);
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
class PostgresSessionFolderRepository {
|
|
1898
|
+
constructor(database) {
|
|
1899
|
+
this.database = database;
|
|
1900
|
+
}
|
|
1901
|
+
async listByOwner(userId, maximum) {
|
|
1902
|
+
const result = await this.database.query(`SELECT * FROM session_folders
|
|
1903
|
+
WHERE user_id = $1
|
|
1904
|
+
ORDER BY lower(name), name
|
|
1905
|
+
LIMIT $2`, [userId, maximum]);
|
|
595
1906
|
return result.rows.map(namedResource);
|
|
596
1907
|
}
|
|
597
1908
|
async replaceWithLimit(folder, maximum) {
|
|
@@ -736,6 +2047,10 @@ const ARCHIVE_TABLES = {
|
|
|
736
2047
|
'knowledge-collection': 'knowledge_collections',
|
|
737
2048
|
document: 'documents',
|
|
738
2049
|
persona: 'personas',
|
|
2050
|
+
prompt: 'prompts',
|
|
2051
|
+
skill: 'skills',
|
|
2052
|
+
'tool-server': 'tool_servers',
|
|
2053
|
+
calendar: 'calendars',
|
|
739
2054
|
};
|
|
740
2055
|
class PostgresDataArchiveRepository {
|
|
741
2056
|
constructor(database) {
|
|
@@ -1027,17 +2342,337 @@ class PostgresDataArchiveRepository {
|
|
|
1027
2342
|
});
|
|
1028
2343
|
}
|
|
1029
2344
|
}
|
|
2345
|
+
const modelTariff = (row) => ({
|
|
2346
|
+
...row,
|
|
2347
|
+
input_per_million: row.input_per_million === null ? null : Number(row.input_per_million),
|
|
2348
|
+
output_per_million: row.output_per_million === null ? null : Number(row.output_per_million),
|
|
2349
|
+
unit_price: row.unit_price === null ? null : Number(row.unit_price),
|
|
2350
|
+
effective_from: number(row.effective_from, 'tariff effective_from'),
|
|
2351
|
+
created_at: number(row.created_at, 'tariff created_at'),
|
|
2352
|
+
});
|
|
2353
|
+
const usageBudget = (row) => ({
|
|
2354
|
+
...row,
|
|
2355
|
+
amount_usd: Number(row.amount_usd),
|
|
2356
|
+
created_at: number(row.created_at, 'budget created_at'),
|
|
2357
|
+
updated_at: number(row.updated_at, 'budget updated_at'),
|
|
2358
|
+
});
|
|
2359
|
+
const messageFeedback = (row) => ({
|
|
2360
|
+
...row,
|
|
2361
|
+
rating: number(row.rating, 'feedback rating'),
|
|
2362
|
+
created_at: number(row.created_at, 'feedback created_at'),
|
|
2363
|
+
updated_at: number(row.updated_at, 'feedback updated_at'),
|
|
2364
|
+
});
|
|
2365
|
+
const arenaVote = (row) => ({
|
|
2366
|
+
...row,
|
|
2367
|
+
created_at: number(row.created_at, 'arena vote created_at'),
|
|
2368
|
+
});
|
|
2369
|
+
const evalSet = (row) => ({
|
|
2370
|
+
...row,
|
|
2371
|
+
created_at: number(row.created_at, 'eval set created_at'),
|
|
2372
|
+
updated_at: number(row.updated_at, 'eval set updated_at'),
|
|
2373
|
+
});
|
|
2374
|
+
const evalRun = (row) => ({
|
|
2375
|
+
...row,
|
|
2376
|
+
created_at: number(row.created_at, 'eval run created_at'),
|
|
2377
|
+
updated_at: number(row.updated_at, 'eval run updated_at'),
|
|
2378
|
+
completed_at: row.completed_at === null
|
|
2379
|
+
? null
|
|
2380
|
+
: number(row.completed_at, 'eval run completed_at'),
|
|
2381
|
+
});
|
|
2382
|
+
class PostgresModelTariffRepository {
|
|
2383
|
+
constructor(database) {
|
|
2384
|
+
this.database = database;
|
|
2385
|
+
}
|
|
2386
|
+
async listAll(maximum) {
|
|
2387
|
+
const result = await this.database.query(`SELECT * FROM model_tariffs
|
|
2388
|
+
ORDER BY plugin_id ASC, model ASC, effective_from DESC
|
|
2389
|
+
LIMIT $1`, [maximum]);
|
|
2390
|
+
return result.rows.map(modelTariff);
|
|
2391
|
+
}
|
|
2392
|
+
async insert(tariff) {
|
|
2393
|
+
await this.database.query(`INSERT INTO model_tariffs
|
|
2394
|
+
(id, plugin_id, model, input_per_million, output_per_million,
|
|
2395
|
+
unit_price, currency, effective_from, created_by, created_at)
|
|
2396
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`, [
|
|
2397
|
+
tariff.id,
|
|
2398
|
+
tariff.plugin_id,
|
|
2399
|
+
tariff.model,
|
|
2400
|
+
tariff.input_per_million,
|
|
2401
|
+
tariff.output_per_million,
|
|
2402
|
+
tariff.unit_price,
|
|
2403
|
+
tariff.currency,
|
|
2404
|
+
tariff.effective_from,
|
|
2405
|
+
tariff.created_by,
|
|
2406
|
+
tariff.created_at,
|
|
2407
|
+
]);
|
|
2408
|
+
}
|
|
2409
|
+
async deleteById(tariffId) {
|
|
2410
|
+
const result = await this.database.query('DELETE FROM model_tariffs WHERE id = $1', [tariffId]);
|
|
2411
|
+
return changes(result.rowCount) > 0;
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
class PostgresUsageBudgetRepository {
|
|
2415
|
+
constructor(database) {
|
|
2416
|
+
this.database = database;
|
|
2417
|
+
}
|
|
2418
|
+
async listAll(maximum) {
|
|
2419
|
+
const result = await this.database.query(`SELECT * FROM usage_budgets
|
|
2420
|
+
ORDER BY created_at ASC, id ASC
|
|
2421
|
+
LIMIT $1`, [maximum]);
|
|
2422
|
+
return result.rows.map(usageBudget);
|
|
2423
|
+
}
|
|
2424
|
+
async findById(budgetId) {
|
|
2425
|
+
const result = await this.database.query('SELECT * FROM usage_budgets WHERE id = $1', [budgetId]);
|
|
2426
|
+
return result.rows[0] ? usageBudget(result.rows[0]) : null;
|
|
2427
|
+
}
|
|
2428
|
+
async replace(budget) {
|
|
2429
|
+
await this.database.query(`INSERT INTO usage_budgets
|
|
2430
|
+
(id, name, principal_type, principal_id, period, amount_usd,
|
|
2431
|
+
mode, created_by, created_at, updated_at)
|
|
2432
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
|
2433
|
+
ON CONFLICT (id) DO UPDATE SET
|
|
2434
|
+
name = EXCLUDED.name,
|
|
2435
|
+
principal_type = EXCLUDED.principal_type,
|
|
2436
|
+
principal_id = EXCLUDED.principal_id,
|
|
2437
|
+
period = EXCLUDED.period,
|
|
2438
|
+
amount_usd = EXCLUDED.amount_usd,
|
|
2439
|
+
mode = EXCLUDED.mode,
|
|
2440
|
+
updated_at = EXCLUDED.updated_at`, [
|
|
2441
|
+
budget.id,
|
|
2442
|
+
budget.name,
|
|
2443
|
+
budget.principal_type,
|
|
2444
|
+
budget.principal_id,
|
|
2445
|
+
budget.period,
|
|
2446
|
+
budget.amount_usd,
|
|
2447
|
+
budget.mode,
|
|
2448
|
+
budget.created_by,
|
|
2449
|
+
budget.created_at,
|
|
2450
|
+
budget.updated_at,
|
|
2451
|
+
]);
|
|
2452
|
+
}
|
|
2453
|
+
async deleteById(budgetId) {
|
|
2454
|
+
const result = await this.database.query('DELETE FROM usage_budgets WHERE id = $1', [budgetId]);
|
|
2455
|
+
return changes(result.rowCount) > 0;
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2458
|
+
class PostgresMessageFeedbackRepository {
|
|
2459
|
+
constructor(database) {
|
|
2460
|
+
this.database = database;
|
|
2461
|
+
}
|
|
2462
|
+
async upsertByMessage(feedback) {
|
|
2463
|
+
await this.database.query(`INSERT INTO message_feedback
|
|
2464
|
+
(id, user_id, session_id, message_id, rating, tags, comment,
|
|
2465
|
+
model, plugin_id, snapshot, created_at, updated_at)
|
|
2466
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
|
2467
|
+
ON CONFLICT (user_id, message_id) DO UPDATE SET
|
|
2468
|
+
rating = EXCLUDED.rating,
|
|
2469
|
+
tags = EXCLUDED.tags,
|
|
2470
|
+
comment = EXCLUDED.comment,
|
|
2471
|
+
model = EXCLUDED.model,
|
|
2472
|
+
plugin_id = EXCLUDED.plugin_id,
|
|
2473
|
+
snapshot = EXCLUDED.snapshot,
|
|
2474
|
+
updated_at = EXCLUDED.updated_at`, [
|
|
2475
|
+
feedback.id,
|
|
2476
|
+
feedback.user_id,
|
|
2477
|
+
feedback.session_id,
|
|
2478
|
+
feedback.message_id,
|
|
2479
|
+
feedback.rating,
|
|
2480
|
+
feedback.tags,
|
|
2481
|
+
feedback.comment,
|
|
2482
|
+
feedback.model,
|
|
2483
|
+
feedback.plugin_id,
|
|
2484
|
+
feedback.snapshot,
|
|
2485
|
+
feedback.created_at,
|
|
2486
|
+
feedback.updated_at,
|
|
2487
|
+
]);
|
|
2488
|
+
}
|
|
2489
|
+
async deleteByMessage(userId, messageId) {
|
|
2490
|
+
const result = await this.database.query('DELETE FROM message_feedback WHERE user_id = $1 AND message_id = $2', [userId, messageId]);
|
|
2491
|
+
return changes(result.rowCount) > 0;
|
|
2492
|
+
}
|
|
2493
|
+
async listByOwner(userId, maximum) {
|
|
2494
|
+
const result = await this.database.query(`SELECT * FROM message_feedback
|
|
2495
|
+
WHERE user_id = $1
|
|
2496
|
+
ORDER BY created_at DESC, id DESC
|
|
2497
|
+
LIMIT $2`, [userId, maximum]);
|
|
2498
|
+
return result.rows.map(messageFeedback);
|
|
2499
|
+
}
|
|
2500
|
+
async listAll(maximum) {
|
|
2501
|
+
const result = await this.database.query(`SELECT * FROM message_feedback
|
|
2502
|
+
ORDER BY created_at DESC, id DESC
|
|
2503
|
+
LIMIT $1`, [maximum]);
|
|
2504
|
+
return result.rows.map(messageFeedback);
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
class PostgresArenaVoteRepository {
|
|
2508
|
+
constructor(database) {
|
|
2509
|
+
this.database = database;
|
|
2510
|
+
}
|
|
2511
|
+
async insertOnce(vote) {
|
|
2512
|
+
const result = await this.database.query(`INSERT INTO arena_votes
|
|
2513
|
+
(id, user_id, compare_group, model_a, model_b, winner, created_at)
|
|
2514
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
2515
|
+
ON CONFLICT (user_id, compare_group) DO NOTHING`, [
|
|
2516
|
+
vote.id,
|
|
2517
|
+
vote.user_id,
|
|
2518
|
+
vote.compare_group,
|
|
2519
|
+
vote.model_a,
|
|
2520
|
+
vote.model_b,
|
|
2521
|
+
vote.winner,
|
|
2522
|
+
vote.created_at,
|
|
2523
|
+
]);
|
|
2524
|
+
return changes(result.rowCount) > 0;
|
|
2525
|
+
}
|
|
2526
|
+
async listAllOrdered(maximum) {
|
|
2527
|
+
const result = await this.database.query(`SELECT * FROM arena_votes
|
|
2528
|
+
ORDER BY created_at ASC, id ASC
|
|
2529
|
+
LIMIT $1`, [maximum]);
|
|
2530
|
+
return result.rows.map(arenaVote);
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
class PostgresEvalSetRepository {
|
|
2534
|
+
constructor(database) {
|
|
2535
|
+
this.database = database;
|
|
2536
|
+
}
|
|
2537
|
+
async listByOwner(userId, maximum) {
|
|
2538
|
+
const result = await this.database.query(`SELECT * FROM eval_sets
|
|
2539
|
+
WHERE user_id = $1
|
|
2540
|
+
ORDER BY updated_at DESC, id DESC
|
|
2541
|
+
LIMIT $2`, [userId, maximum]);
|
|
2542
|
+
return result.rows.map(evalSet);
|
|
2543
|
+
}
|
|
2544
|
+
async findByOwner(setId, userId) {
|
|
2545
|
+
const result = await this.database.query('SELECT * FROM eval_sets WHERE id = $1 AND user_id = $2', [setId, userId]);
|
|
2546
|
+
return result.rows[0] ? evalSet(result.rows[0]) : null;
|
|
2547
|
+
}
|
|
2548
|
+
async replaceWithLimit(value, maximum) {
|
|
2549
|
+
await this.database.transaction(async (client) => {
|
|
2550
|
+
await lockOwner(client, value.user_id);
|
|
2551
|
+
const existing = await client.query('SELECT user_id FROM eval_sets WHERE id = $1 FOR UPDATE', [value.id]);
|
|
2552
|
+
if (existing.rows[0] && existing.rows[0].user_id !== value.user_id) {
|
|
2553
|
+
throw new PersistenceResourceConflictError();
|
|
2554
|
+
}
|
|
2555
|
+
if (!existing.rows[0]) {
|
|
2556
|
+
const count = await client.query('SELECT COUNT(*)::text AS count FROM eval_sets WHERE user_id = $1', [value.user_id]);
|
|
2557
|
+
if (Number(count.rows[0]?.count || 0) >= maximum) {
|
|
2558
|
+
throw new PersistenceResourceLimitError('eval-set', maximum);
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
const result = await client.query(`INSERT INTO eval_sets
|
|
2562
|
+
(id, user_id, name, description, items, created_at, updated_at)
|
|
2563
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
2564
|
+
ON CONFLICT (id) DO UPDATE SET
|
|
2565
|
+
name = EXCLUDED.name,
|
|
2566
|
+
description = EXCLUDED.description,
|
|
2567
|
+
items = EXCLUDED.items,
|
|
2568
|
+
updated_at = EXCLUDED.updated_at
|
|
2569
|
+
WHERE eval_sets.user_id = EXCLUDED.user_id`, [
|
|
2570
|
+
value.id,
|
|
2571
|
+
value.user_id,
|
|
2572
|
+
value.name,
|
|
2573
|
+
value.description,
|
|
2574
|
+
value.items,
|
|
2575
|
+
value.created_at,
|
|
2576
|
+
value.updated_at,
|
|
2577
|
+
]);
|
|
2578
|
+
if (result.rowCount !== 1)
|
|
2579
|
+
throw new PersistenceResourceConflictError();
|
|
2580
|
+
});
|
|
2581
|
+
}
|
|
2582
|
+
async deleteByOwner(setId, userId) {
|
|
2583
|
+
const result = await this.database.query('DELETE FROM eval_sets WHERE id = $1 AND user_id = $2', [setId, userId]);
|
|
2584
|
+
return changes(result.rowCount) > 0;
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
class PostgresEvalRunRepository {
|
|
2588
|
+
constructor(database) {
|
|
2589
|
+
this.database = database;
|
|
2590
|
+
}
|
|
2591
|
+
async listByOwner(userId, maximum) {
|
|
2592
|
+
const result = await this.database.query(`SELECT * FROM eval_runs
|
|
2593
|
+
WHERE user_id = $1
|
|
2594
|
+
ORDER BY created_at DESC, id DESC
|
|
2595
|
+
LIMIT $2`, [userId, maximum]);
|
|
2596
|
+
return result.rows.map(evalRun);
|
|
2597
|
+
}
|
|
2598
|
+
async listBySet(setId, userId, maximum) {
|
|
2599
|
+
const result = await this.database.query(`SELECT * FROM eval_runs
|
|
2600
|
+
WHERE set_id = $1 AND user_id = $2
|
|
2601
|
+
ORDER BY created_at DESC, id DESC
|
|
2602
|
+
LIMIT $3`, [setId, userId, maximum]);
|
|
2603
|
+
return result.rows.map(evalRun);
|
|
2604
|
+
}
|
|
2605
|
+
async findByOwner(runId, userId) {
|
|
2606
|
+
const result = await this.database.query('SELECT * FROM eval_runs WHERE id = $1 AND user_id = $2', [runId, userId]);
|
|
2607
|
+
return result.rows[0] ? evalRun(result.rows[0]) : null;
|
|
2608
|
+
}
|
|
2609
|
+
async insert(run) {
|
|
2610
|
+
await this.database.query(`INSERT INTO eval_runs
|
|
2611
|
+
(id, set_id, user_id, label, plugin_id, model, status, results,
|
|
2612
|
+
error, created_at, updated_at, completed_at)
|
|
2613
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)`, [
|
|
2614
|
+
run.id,
|
|
2615
|
+
run.set_id,
|
|
2616
|
+
run.user_id,
|
|
2617
|
+
run.label,
|
|
2618
|
+
run.plugin_id,
|
|
2619
|
+
run.model,
|
|
2620
|
+
run.status,
|
|
2621
|
+
run.results,
|
|
2622
|
+
run.error,
|
|
2623
|
+
run.created_at,
|
|
2624
|
+
run.updated_at,
|
|
2625
|
+
run.completed_at,
|
|
2626
|
+
]);
|
|
2627
|
+
}
|
|
2628
|
+
async update(runId, userId, updates) {
|
|
2629
|
+
const result = await this.database.query(`UPDATE eval_runs SET
|
|
2630
|
+
status = $1,
|
|
2631
|
+
results = COALESCE($2, results),
|
|
2632
|
+
error = $3,
|
|
2633
|
+
updated_at = $4,
|
|
2634
|
+
completed_at = COALESCE($5, completed_at)
|
|
2635
|
+
WHERE id = $6 AND user_id = $7`, [
|
|
2636
|
+
updates.status,
|
|
2637
|
+
updates.results ?? null,
|
|
2638
|
+
updates.error ?? null,
|
|
2639
|
+
updates.updated_at,
|
|
2640
|
+
updates.completed_at ?? null,
|
|
2641
|
+
runId,
|
|
2642
|
+
userId,
|
|
2643
|
+
]);
|
|
2644
|
+
return changes(result.rowCount) > 0;
|
|
2645
|
+
}
|
|
2646
|
+
}
|
|
1030
2647
|
export const createPostgresResourceRepositories = (database) => ({
|
|
1031
2648
|
chatSessions: new PostgresChatSessionRepository(database),
|
|
1032
2649
|
knowledgeCollections: new PostgresKnowledgeCollectionRepository(database),
|
|
1033
2650
|
notes: new PostgresNoteRepository(database),
|
|
2651
|
+
calendars: new PostgresCalendarRepository(database),
|
|
1034
2652
|
calendarEvents: new PostgresCalendarEventRepository(database),
|
|
2653
|
+
channels: new PostgresChannelRepository(database),
|
|
2654
|
+
channelMessages: new PostgresChannelMessageRepository(database),
|
|
2655
|
+
notifications: new PostgresNotificationRepository(database),
|
|
2656
|
+
webhookTargets: new PostgresWebhookTargetRepository(database),
|
|
1035
2657
|
automations: new PostgresAutomationRepository(database),
|
|
1036
2658
|
automationRuns: new PostgresAutomationRunRepository(database),
|
|
2659
|
+
toolServers: new PostgresToolServerRepository(database),
|
|
2660
|
+
toolServerTools: new PostgresToolServerToolRepository(database),
|
|
2661
|
+
toolServerCredentials: new PostgresToolServerCredentialRepository(database),
|
|
2662
|
+
toolApprovals: new PostgresToolApprovalRepository(database),
|
|
2663
|
+
prompts: new PostgresPromptRepository(database),
|
|
2664
|
+
skills: new PostgresSkillRepository(database),
|
|
2665
|
+
skillFiles: new PostgresSkillFileRepository(database),
|
|
1037
2666
|
sessionFolders: new PostgresSessionFolderRepository(database),
|
|
1038
2667
|
preferences: new PostgresPreferenceRepository(database),
|
|
1039
2668
|
systemSettings: new PostgresSystemSettingRepository(database),
|
|
1040
2669
|
archive: new PostgresDataArchiveRepository(database),
|
|
2670
|
+
modelTariffs: new PostgresModelTariffRepository(database),
|
|
2671
|
+
usageBudgets: new PostgresUsageBudgetRepository(database),
|
|
2672
|
+
messageFeedback: new PostgresMessageFeedbackRepository(database),
|
|
2673
|
+
arenaVotes: new PostgresArenaVoteRepository(database),
|
|
2674
|
+
evalSets: new PostgresEvalSetRepository(database),
|
|
2675
|
+
evalRuns: new PostgresEvalRunRepository(database),
|
|
1041
2676
|
});
|
|
1042
2677
|
/** Create transaction-scoped repositories over one pinned pooled client. */
|
|
1043
2678
|
export const createPostgresTransactionalResourceRepositories = (database, client) => {
|