@friggframework/core 2.0.0--canary.548.c8ae0ca.0 → 2.0.0--canary.545.c40eca4.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/CHANGELOG.md +66 -67
- package/CLAUDE.md +148 -124
- package/README.md +165 -130
- package/admin-scripts/index.js +52 -0
- package/admin-scripts/repositories/admin-api-key-repository-documentdb.js +21 -0
- package/admin-scripts/repositories/admin-api-key-repository-factory.js +51 -0
- package/admin-scripts/repositories/admin-api-key-repository-interface.js +121 -0
- package/admin-scripts/repositories/admin-api-key-repository-mongo.js +155 -0
- package/admin-scripts/repositories/admin-api-key-repository-postgres.js +189 -0
- package/admin-scripts/repositories/admin-process-repository-documentdb.js +21 -0
- package/admin-scripts/repositories/admin-process-repository-factory.js +49 -0
- package/admin-scripts/repositories/admin-process-repository-interface.js +150 -0
- package/admin-scripts/repositories/admin-process-repository-mongo.js +213 -0
- package/admin-scripts/repositories/admin-process-repository-postgres.js +251 -0
- package/admin-scripts/repositories/script-execution-repository-documentdb.js +21 -0
- package/admin-scripts/repositories/script-execution-repository-factory.js +51 -0
- package/admin-scripts/repositories/script-execution-repository-interface.js +193 -0
- package/admin-scripts/repositories/script-execution-repository-mongo.js +278 -0
- package/admin-scripts/repositories/script-execution-repository-postgres.js +320 -0
- package/admin-scripts/repositories/script-schedule-repository-documentdb.js +21 -0
- package/admin-scripts/repositories/script-schedule-repository-factory.js +49 -0
- package/admin-scripts/repositories/script-schedule-repository-interface.js +108 -0
- package/admin-scripts/repositories/script-schedule-repository-mongo.js +179 -0
- package/admin-scripts/repositories/script-schedule-repository-postgres.js +210 -0
- package/application/commands/admin-script-commands.js +348 -0
- package/application/commands/integration-commands.js +12 -6
- package/application/index.js +5 -3
- package/assertions/get.js +17 -7
- package/associations/association.js +59 -57
- package/core/CLAUDE.md +222 -156
- package/core/Worker.js +33 -12
- package/credential/credential-router.js +262 -0
- package/credential/repositories/credential-repository-documentdb.js +71 -1
- package/credential/repositories/credential-repository-factory.js +4 -6
- package/credential/repositories/credential-repository-interface.js +8 -2
- package/credential/repositories/credential-repository-mongo.js +45 -4
- package/credential/repositories/credential-repository-postgres.js +44 -3
- package/credential/repositories/credential-repository.js +3 -1
- package/credential/use-cases/delete-credential-for-user.js +50 -0
- package/credential/use-cases/get-credential-for-user.js +21 -3
- package/credential/use-cases/list-credentials-for-user.js +31 -0
- package/credential/use-cases/reauthorize-credential.js +103 -0
- package/credential/use-cases/update-authentication-status.js +5 -2
- package/database/MONGODB_TRANSACTION_FIX.md +72 -52
- package/database/config.js +13 -59
- package/database/documentdb-encryption-service.js +29 -13
- package/database/documentdb-utils.js +21 -12
- package/database/encryption/README.md +53 -36
- package/database/encryption/encryption-schema-registry.js +29 -23
- package/database/encryption/field-encryption-service.js +15 -4
- package/database/encryption/logger.js +3 -2
- package/database/encryption/prisma-encryption-extension.js +5 -8
- package/database/prisma.js +22 -14
- package/database/repositories/health-check-repository-factory.js +7 -5
- package/database/repositories/health-check-repository-interface.js +15 -5
- package/database/repositories/health-check-repository-postgres.js +1 -1
- package/database/use-cases/check-database-health-use-case.js +3 -2
- package/database/use-cases/check-database-state-use-case.js +8 -7
- package/database/use-cases/get-database-state-via-worker-use-case.js +15 -12
- package/database/use-cases/get-migration-status-use-case.js +4 -2
- package/database/use-cases/run-database-migration-use-case.js +41 -9
- package/database/use-cases/test-encryption-use-case.js +4 -5
- package/database/use-cases/trigger-database-migration-use-case.js +9 -8
- package/database/utils/mongodb-collection-utils.js +11 -3
- package/database/utils/mongodb-schema-init.js +10 -4
- package/database/utils/prisma-runner.js +144 -74
- package/database/utils/prisma-schema-parser.js +2 -2
- package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +135 -80
- package/encrypt/Cryptor.js +54 -49
- package/encrypt/aes-encryption-key-provider.js +82 -0
- package/encrypt/encryption-key-provider-interface.js +50 -0
- package/errors/fetch-error.js +1 -1
- package/generated/prisma-mongodb/edge.js +59 -4
- package/generated/prisma-mongodb/index-browser.js +49 -1
- package/generated/prisma-mongodb/index.d.ts +4023 -61
- package/generated/prisma-mongodb/index.js +59 -4
- package/generated/prisma-mongodb/package.json +1 -1
- package/generated/prisma-mongodb/schema.prisma +87 -0
- package/generated/prisma-mongodb/wasm.js +59 -4
- package/generated/prisma-postgresql/edge.js +58 -4
- package/generated/prisma-postgresql/index-browser.js +48 -1
- package/generated/prisma-postgresql/index.d.ts +4738 -54
- package/generated/prisma-postgresql/index.js +58 -4
- package/generated/prisma-postgresql/package.json +1 -1
- package/generated/prisma-postgresql/schema.prisma +85 -0
- package/generated/prisma-postgresql/wasm.js +58 -4
- package/handlers/WEBHOOKS.md +104 -81
- package/handlers/app-definition-loader.js +53 -10
- package/handlers/app-handler-helpers.js +14 -3
- package/handlers/backend-utils.js +0 -1
- package/handlers/database-migration-handler.js +64 -45
- package/handlers/middleware/admin-auth.js +53 -0
- package/handlers/routers/HEALTHCHECK.md +170 -135
- package/handlers/routers/admin.js +474 -0
- package/handlers/routers/auth.js +51 -9
- package/handlers/routers/db-migration.handler.js +2 -3
- package/handlers/routers/db-migration.js +102 -66
- package/handlers/routers/docs.js +180 -0
- package/handlers/routers/health.js +84 -283
- package/handlers/routers/integration-defined-routers.js +44 -33
- package/handlers/routers/integration-webhook-routers.js +73 -52
- package/handlers/routers/middleware/loadUser.js +39 -0
- package/handlers/routers/middleware/requireAdmin.js +43 -0
- package/handlers/routers/middleware/requireLoggedInUser.js +19 -0
- package/handlers/routers/user.js +30 -14
- package/handlers/routers/websocket.js +18 -4
- package/handlers/use-cases/check-integrations-health-use-case.js +13 -6
- package/handlers/workers/db-migration.js +74 -35
- package/handlers/workers/integration-defined-workers.js +31 -20
- package/index.js +50 -16
- package/infrastructure/scheduler/index.js +15 -11
- package/infrastructure/scheduler/netlify-scheduler-adapter.js +185 -0
- package/infrastructure/scheduler/scheduler-service-factory.js +38 -31
- package/integrations/WEBHOOK-QUICKSTART.md +21 -14
- package/integrations/index.js +4 -0
- package/integrations/integration-base.js +9 -6
- package/integrations/integration-router.js +1828 -157
- package/integrations/options.js +47 -20
- package/integrations/repositories/integration-mapping-repository-factory.js +4 -8
- package/integrations/repositories/integration-repository-documentdb.js +53 -24
- package/integrations/repositories/integration-repository-factory.js +6 -6
- package/integrations/repositories/integration-repository-interface.js +27 -9
- package/integrations/repositories/integration-repository-postgres.js +3 -3
- package/integrations/repositories/process-repository-factory.js +4 -7
- package/integrations/repositories/process-repository-interface.js +9 -5
- package/integrations/repositories/process-repository-mongo.js +7 -3
- package/integrations/repositories/process-repository-postgres.js +2 -2
- package/integrations/tests/doubles/config-capturing-integration.js +9 -8
- package/integrations/tests/doubles/dummy-integration-class.js +81 -8
- package/integrations/tests/doubles/test-integration-repository.js +51 -11
- package/integrations/use-cases/create-integration.js +36 -32
- package/integrations/use-cases/create-process.js +20 -9
- package/integrations/use-cases/delete-integration-for-user.js +5 -4
- package/integrations/use-cases/execute-proxy-request.js +564 -0
- package/integrations/use-cases/get-integration-for-user.js +25 -10
- package/integrations/use-cases/get-integration-instance-by-definition.js +13 -7
- package/integrations/use-cases/get-integrations-for-user.js +4 -1
- package/integrations/use-cases/get-possible-integrations.js +1 -1
- package/integrations/use-cases/get-process.js +5 -6
- package/integrations/use-cases/index.js +3 -1
- package/integrations/use-cases/load-integration-context.js +1 -5
- package/integrations/use-cases/update-process-state.js +10 -8
- package/integrations/utils/map-integration-dto.js +9 -3
- package/jest-setup.js +1 -1
- package/jest-teardown.js +1 -1
- package/lambda/TimeoutCatcher.js +32 -30
- package/lambda/index.js +1 -1
- package/logs/index.js +3 -3
- package/modules/domain/entities/AuthorizationSession.js +131 -0
- package/modules/domain/entities/index.js +10 -0
- package/modules/module.js +4 -9
- package/modules/repositories/authorization-session-repository-factory.js +53 -0
- package/modules/repositories/authorization-session-repository-interface.js +75 -0
- package/modules/repositories/authorization-session-repository-mongo.js +169 -0
- package/modules/repositories/authorization-session-repository-postgres.js +170 -0
- package/modules/repositories/module-repository-documentdb.js +89 -41
- package/modules/repositories/module-repository-factory.js +4 -6
- package/modules/repositories/module-repository-interface.js +13 -0
- package/modules/repositories/module-repository-mongo.js +40 -5
- package/modules/repositories/module-repository-postgres.js +42 -5
- package/modules/repositories/module-repository.js +28 -0
- package/modules/requester/api-key.js +0 -2
- package/modules/requester/basic.js +0 -1
- package/modules/requester/oauth-2.js +3 -14
- package/modules/test/mock-api/definition.js +15 -10
- package/modules/test/mock-api/mocks/hubspot.js +36 -36
- package/modules/tests/doubles/test-module-factory.js +10 -4
- package/modules/tests/doubles/test-module-repository.js +1 -1
- package/modules/use-cases/delete-module-entity.js +23 -0
- package/modules/use-cases/get-authorization-requirements.js +110 -0
- package/modules/use-cases/get-entities-for-user.js +1 -1
- package/modules/use-cases/get-entity-options-by-id.js +8 -6
- package/modules/use-cases/get-module-entity-by-id.js +16 -0
- package/modules/use-cases/get-module.js +12 -9
- package/modules/use-cases/process-authorization-callback.js +12 -6
- package/modules/use-cases/process-authorization-step.js +160 -0
- package/modules/use-cases/refresh-entity-options.js +8 -6
- package/modules/use-cases/start-authorization-session.js +86 -0
- package/modules/use-cases/test-module-auth.js +8 -6
- package/modules/use-cases/update-module-entity.js +27 -0
- package/modules/utils/map-module-dto.js +2 -2
- package/openapi/openapi-spec-generator.js +370 -0
- package/openapi/openapi-v1.yaml +1360 -0
- package/openapi/openapi-v2.yaml +1535 -0
- package/package.json +7 -9
- package/prisma-mongodb/schema.prisma +87 -0
- package/prisma-postgresql/migrations/20260222000000_add_admin_process_and_script_schedule/migration.sql +55 -0
- package/prisma-postgresql/schema.prisma +85 -0
- package/providers/index.js +13 -0
- package/providers/resolve-provider.js +93 -0
- package/queues/index.js +19 -0
- package/queues/providers/index.js +7 -0
- package/queues/providers/netlify-background-provider.js +76 -0
- package/queues/providers/qstash-queue-provider.js +122 -0
- package/queues/queue-client-interface.js +55 -0
- package/queues/queue-provider-factory.js +93 -0
- package/queues/queue-provider.js +46 -0
- package/queues/queuer-util.js +33 -25
- package/syncs/repositories/sync-repository-documentdb.js +39 -19
- package/syncs/repositories/sync-repository-factory.js +4 -4
- package/syncs/repositories/sync-repository-interface.js +6 -2
- package/syncs/repositories/sync-repository-postgres.js +19 -13
- package/syncs/sync.js +104 -100
- package/token/repositories/token-repository-factory.js +4 -6
- package/types/assertions/index.d.ts +72 -72
- package/types/core/index.d.ts +98 -53
- package/types/errors/index.d.ts +61 -54
- package/types/eslint-config/index.d.ts +40 -40
- package/types/lambda/index.d.ts +26 -26
- package/types/logs/index.d.ts +4 -4
- package/types/prettier-config/index.d.ts +5 -5
- package/types/test-environment/index.d.ts +14 -14
- package/types/tsconfig.json +95 -93
- package/user/repositories/user-repository-factory.js +4 -4
- package/user/tests/doubles/test-user-repository.js +5 -2
- package/user/use-cases/authenticate-user.js +2 -4
- package/user/use-cases/authenticate-with-shared-secret.js +1 -1
- package/user/use-cases/create-individual-user.js +8 -7
- package/user/use-cases/create-organization-user.js +1 -1
- package/user/use-cases/create-token-for-user-id.js +1 -1
- package/user/use-cases/delete-user.js +47 -0
- package/user/use-cases/get-user-from-adopter-jwt.js +0 -2
- package/user/use-cases/get-user-from-bearer-token.js +7 -3
- package/user/use-cases/get-user-from-x-frigg-headers.js +14 -11
- package/user/use-cases/login-user.js +8 -8
- package/user/user.js +18 -4
- package/utils/backend-path.js +2 -2
- package/utils/index.js +4 -1
- package/websocket/repositories/websocket-connection-repository-documentdb.js +46 -21
- package/websocket/repositories/websocket-connection-repository-factory.js +4 -8
- package/websocket/repositories/websocket-connection-repository-mongo.js +26 -16
- package/websocket/repositories/websocket-connection-repository-postgres.js +26 -15
- package/websocket/repositories/websocket-connection-repository.js +26 -20
- package/websocket/websocket-message-sender-interface.js +38 -0
- package/database/adapters/lambda-invoker.js +0 -97
- package/database/repositories/migration-status-repository-s3.js +0 -137
- package/infrastructure/scheduler/eventbridge-scheduler-adapter.js +0 -184
|
@@ -4,12 +4,14 @@
|
|
|
4
4
|
* HTTP API for triggering and monitoring database migrations.
|
|
5
5
|
*
|
|
6
6
|
* Endpoints:
|
|
7
|
-
* - GET /db-migrate/status - Check if migrations are pending
|
|
8
|
-
* - POST /db-migrate - Trigger async migration (queues job)
|
|
9
|
-
* - GET /db-migrate/:processId - Check migration status
|
|
7
|
+
* - GET /admin/db-migrate/status - Check if migrations are pending
|
|
8
|
+
* - POST /admin/db-migrate - Trigger async migration (queues job)
|
|
9
|
+
* - GET /admin/db-migrate/:processId - Check migration status
|
|
10
|
+
* - POST /admin/db-migrate/resolve - Resolve failed migration
|
|
10
11
|
*
|
|
11
12
|
* Security:
|
|
12
|
-
* - Requires
|
|
13
|
+
* - Requires x-frigg-admin-api-key header for all requests
|
|
14
|
+
* - Uses shared validateAdminApiKey middleware
|
|
13
15
|
*
|
|
14
16
|
* Architecture:
|
|
15
17
|
* - Router (Adapter Layer) → Use Cases (Domain) → Repositories (Infrastructure)
|
|
@@ -18,7 +20,8 @@
|
|
|
18
20
|
|
|
19
21
|
const { Router } = require('express');
|
|
20
22
|
const catchAsyncError = require('express-async-handler');
|
|
21
|
-
const {
|
|
23
|
+
const { validateAdminApiKey } = require('../middleware/admin-auth');
|
|
24
|
+
const { resolveProvider } = require('../../providers/resolve-provider');
|
|
22
25
|
const {
|
|
23
26
|
TriggerDatabaseMigrationUseCase,
|
|
24
27
|
ValidationError: TriggerValidationError,
|
|
@@ -28,57 +31,77 @@ const {
|
|
|
28
31
|
ValidationError: GetValidationError,
|
|
29
32
|
NotFoundError,
|
|
30
33
|
} = require('../../database/use-cases/get-migration-status-use-case');
|
|
31
|
-
const { LambdaInvoker } = require('../../database/adapters/lambda-invoker');
|
|
32
34
|
const {
|
|
33
35
|
GetDatabaseStateViaWorkerUseCase,
|
|
34
36
|
} = require('../../database/use-cases/get-database-state-via-worker-use-case');
|
|
35
37
|
|
|
36
38
|
const router = Router();
|
|
37
39
|
|
|
38
|
-
// Dependency injection
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
//
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
40
|
+
// Dependency injection — lazy-initialized on first request.
|
|
41
|
+
// Uses resolveProvider() to get the correct adapters for the active platform.
|
|
42
|
+
let _migrationStatusRepository = null;
|
|
43
|
+
let _triggerMigrationUseCase = null;
|
|
44
|
+
let _getStatusUseCase = null;
|
|
45
|
+
let _functionInvoker = null;
|
|
46
|
+
|
|
47
|
+
function getMigrationDeps() {
|
|
48
|
+
if (!_migrationStatusRepository) {
|
|
49
|
+
const provider = resolveProvider();
|
|
50
|
+
|
|
51
|
+
// Migration status storage (AWS: S3, others: provider-specific)
|
|
52
|
+
const MigrationStatusRepository = provider.MigrationStatusRepositoryS3;
|
|
53
|
+
if (!MigrationStatusRepository) {
|
|
54
|
+
throw new Error(
|
|
55
|
+
`Provider '${provider.name}' does not export a MigrationStatusRepository`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
const bucketName =
|
|
59
|
+
process.env.S3_BUCKET_NAME || process.env.MIGRATION_STATUS_BUCKET;
|
|
60
|
+
_migrationStatusRepository = new MigrationStatusRepository(bucketName);
|
|
61
|
+
_triggerMigrationUseCase = new TriggerDatabaseMigrationUseCase({
|
|
62
|
+
migrationStatusRepository: _migrationStatusRepository,
|
|
63
|
+
});
|
|
64
|
+
_getStatusUseCase = new GetMigrationStatusUseCase({
|
|
65
|
+
migrationStatusRepository: _migrationStatusRepository,
|
|
66
|
+
});
|
|
58
67
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
// Function invoker (AWS: LambdaInvoker, Netlify: HTTP-based)
|
|
69
|
+
_functionInvoker = provider.invokeFunctionAdapter;
|
|
70
|
+
if (!_functionInvoker) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
`Provider '${provider.name}' does not export an invokeFunctionAdapter`
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
migrationStatusRepository: _migrationStatusRepository,
|
|
78
|
+
triggerMigrationUseCase: _triggerMigrationUseCase,
|
|
79
|
+
getStatusUseCase: _getStatusUseCase,
|
|
80
|
+
lambdaInvoker: _functionInvoker,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
let _getDatabaseStateUseCase = null;
|
|
84
|
+
function getDatabaseStateUseCaseInstance() {
|
|
85
|
+
if (!_getDatabaseStateUseCase) {
|
|
86
|
+
const { lambdaInvoker } = getMigrationDeps();
|
|
87
|
+
const workerFunctionName =
|
|
88
|
+
process.env.WORKER_FUNCTION_NAME ||
|
|
89
|
+
`${process.env.SERVICE || 'unknown'}-${
|
|
90
|
+
process.env.STAGE || 'production'
|
|
91
|
+
}-dbMigrationWorker`;
|
|
92
|
+
_getDatabaseStateUseCase = new GetDatabaseStateViaWorkerUseCase({
|
|
93
|
+
lambdaInvoker,
|
|
94
|
+
workerFunctionName,
|
|
71
95
|
});
|
|
72
96
|
}
|
|
97
|
+
return _getDatabaseStateUseCase;
|
|
98
|
+
}
|
|
73
99
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
// Apply API key validation to all routes
|
|
78
|
-
router.use(validateApiKey);
|
|
100
|
+
// Apply admin API key validation to all routes (shared middleware)
|
|
101
|
+
router.use(validateAdminApiKey);
|
|
79
102
|
|
|
80
103
|
/**
|
|
81
|
-
* POST /db-migrate
|
|
104
|
+
* POST /admin/db-migrate
|
|
82
105
|
*
|
|
83
106
|
* Trigger database migration (async via SQS queue)
|
|
84
107
|
*
|
|
@@ -99,17 +122,21 @@ router.use(validateApiKey);
|
|
|
99
122
|
* }
|
|
100
123
|
*/
|
|
101
124
|
router.post(
|
|
102
|
-
'/db-migrate',
|
|
125
|
+
'/admin/db-migrate',
|
|
103
126
|
catchAsyncError(async (req, res) => {
|
|
104
127
|
const dbType = req.body.dbType || process.env.DB_TYPE || 'postgresql';
|
|
105
128
|
const { stage } = req.body;
|
|
106
129
|
// TODO: Extract userId from JWT token when auth is implemented
|
|
107
130
|
const userId = req.body.userId || 'admin';
|
|
108
131
|
|
|
109
|
-
console.log(
|
|
132
|
+
console.log(
|
|
133
|
+
`Migration trigger request: dbType=${dbType}, stage=${
|
|
134
|
+
stage || 'auto-detect'
|
|
135
|
+
}, userId=${userId}`
|
|
136
|
+
);
|
|
110
137
|
|
|
111
138
|
try {
|
|
112
|
-
const result = await triggerMigrationUseCase.execute({
|
|
139
|
+
const result = await getMigrationDeps().triggerMigrationUseCase.execute({
|
|
113
140
|
userId,
|
|
114
141
|
dbType,
|
|
115
142
|
stage,
|
|
@@ -133,10 +160,10 @@ router.post(
|
|
|
133
160
|
);
|
|
134
161
|
|
|
135
162
|
/**
|
|
136
|
-
* GET /db-migrate/status
|
|
163
|
+
* GET /admin/db-migrate/status
|
|
137
164
|
*
|
|
138
165
|
* Check if database has pending migrations
|
|
139
|
-
*
|
|
166
|
+
*
|
|
140
167
|
* Query params:
|
|
141
168
|
* - stage: string (optional, defaults to STAGE env var or 'production')
|
|
142
169
|
*
|
|
@@ -151,15 +178,17 @@ router.post(
|
|
|
151
178
|
* }
|
|
152
179
|
*/
|
|
153
180
|
router.get(
|
|
154
|
-
'/db-migrate/status',
|
|
181
|
+
'/admin/db-migrate/status',
|
|
155
182
|
catchAsyncError(async (req, res) => {
|
|
156
183
|
const stage = req.query.stage || process.env.STAGE || 'production';
|
|
157
184
|
|
|
158
|
-
console.log(
|
|
185
|
+
console.log(
|
|
186
|
+
`Checking database state: stage=${stage}`
|
|
187
|
+
);
|
|
159
188
|
|
|
160
189
|
try {
|
|
161
190
|
// Invoke worker Lambda to check database state
|
|
162
|
-
const status = await
|
|
191
|
+
const status = await getDatabaseStateUseCaseInstance().execute(stage);
|
|
163
192
|
|
|
164
193
|
res.status(200).json(status);
|
|
165
194
|
} catch (error) {
|
|
@@ -177,7 +206,7 @@ router.get(
|
|
|
177
206
|
);
|
|
178
207
|
|
|
179
208
|
/**
|
|
180
|
-
* GET /db-migrate/:migrationId
|
|
209
|
+
* GET /admin/db-migrate/:migrationId
|
|
181
210
|
*
|
|
182
211
|
* Get migration status by migration ID
|
|
183
212
|
*
|
|
@@ -201,15 +230,17 @@ router.get(
|
|
|
201
230
|
* }
|
|
202
231
|
*/
|
|
203
232
|
router.get(
|
|
204
|
-
'/db-migrate/:migrationId',
|
|
233
|
+
'/admin/db-migrate/:migrationId',
|
|
205
234
|
catchAsyncError(async (req, res) => {
|
|
206
235
|
const { migrationId } = req.params;
|
|
207
236
|
const stage = req.query.stage || process.env.STAGE || 'production';
|
|
208
237
|
|
|
209
|
-
console.log(
|
|
238
|
+
console.log(
|
|
239
|
+
`Migration status request: migrationId=${migrationId}, stage=${stage}`
|
|
240
|
+
);
|
|
210
241
|
|
|
211
242
|
try {
|
|
212
|
-
const status = await getStatusUseCase.execute(migrationId, stage);
|
|
243
|
+
const status = await getMigrationDeps().getStatusUseCase.execute(migrationId, stage);
|
|
213
244
|
|
|
214
245
|
res.status(200).json(status);
|
|
215
246
|
} catch (error) {
|
|
@@ -236,7 +267,7 @@ router.get(
|
|
|
236
267
|
);
|
|
237
268
|
|
|
238
269
|
/**
|
|
239
|
-
* POST /db-migrate/resolve
|
|
270
|
+
* POST /admin/db-migrate/resolve
|
|
240
271
|
*
|
|
241
272
|
* Resolve a failed migration by marking it as applied or rolled back
|
|
242
273
|
*
|
|
@@ -256,24 +287,26 @@ router.get(
|
|
|
256
287
|
* }
|
|
257
288
|
*/
|
|
258
289
|
router.post(
|
|
259
|
-
'/db-migrate/resolve',
|
|
290
|
+
'/admin/db-migrate/resolve',
|
|
260
291
|
catchAsyncError(async (req, res) => {
|
|
261
292
|
const { migrationName, action = 'applied' } = req.body;
|
|
262
293
|
|
|
263
|
-
console.log(
|
|
294
|
+
console.log(
|
|
295
|
+
`Migration resolve request: migration=${migrationName}, action=${action}`
|
|
296
|
+
);
|
|
264
297
|
|
|
265
298
|
// Validation
|
|
266
299
|
if (!migrationName) {
|
|
267
300
|
return res.status(400).json({
|
|
268
301
|
success: false,
|
|
269
|
-
error: 'migrationName is required'
|
|
302
|
+
error: 'migrationName is required',
|
|
270
303
|
});
|
|
271
304
|
}
|
|
272
305
|
|
|
273
306
|
if (!['applied', 'rolled-back'].includes(action)) {
|
|
274
307
|
return res.status(400).json({
|
|
275
308
|
success: false,
|
|
276
|
-
error: 'action must be either "applied" or "rolled-back"'
|
|
309
|
+
error: 'action must be either "applied" or "rolled-back"',
|
|
277
310
|
});
|
|
278
311
|
}
|
|
279
312
|
|
|
@@ -281,12 +314,16 @@ router.post(
|
|
|
281
314
|
// Import prismaRunner here to avoid circular dependencies
|
|
282
315
|
const prismaRunner = require('../../database/utils/prisma-runner');
|
|
283
316
|
|
|
284
|
-
const result = await prismaRunner.runPrismaMigrateResolve(
|
|
317
|
+
const result = await prismaRunner.runPrismaMigrateResolve(
|
|
318
|
+
migrationName,
|
|
319
|
+
action,
|
|
320
|
+
true
|
|
321
|
+
);
|
|
285
322
|
|
|
286
323
|
if (!result.success) {
|
|
287
324
|
return res.status(500).json({
|
|
288
325
|
success: false,
|
|
289
|
-
error: `Failed to resolve migration: ${result.error}
|
|
326
|
+
error: `Failed to resolve migration: ${result.error}`,
|
|
290
327
|
});
|
|
291
328
|
}
|
|
292
329
|
|
|
@@ -294,13 +331,13 @@ router.post(
|
|
|
294
331
|
success: true,
|
|
295
332
|
message: `Migration ${migrationName} marked as ${action}`,
|
|
296
333
|
migrationName,
|
|
297
|
-
action
|
|
334
|
+
action,
|
|
298
335
|
});
|
|
299
336
|
} catch (error) {
|
|
300
337
|
console.error('Migration resolve failed:', error);
|
|
301
338
|
return res.status(500).json({
|
|
302
339
|
success: false,
|
|
303
|
-
error: error.message
|
|
340
|
+
error: error.message,
|
|
304
341
|
});
|
|
305
342
|
}
|
|
306
343
|
})
|
|
@@ -323,4 +360,3 @@ app.use((err, _req, res, _next) => {
|
|
|
323
360
|
const handler = serverlessHttp(app);
|
|
324
361
|
|
|
325
362
|
module.exports = { handler, router };
|
|
326
|
-
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Documentation Router
|
|
3
|
+
*
|
|
4
|
+
* Serves dynamic OpenAPI specs and Scalar UI documentation for both v1 and v2 APIs.
|
|
5
|
+
* Specs are generated dynamically based on appDefinition and installed modules.
|
|
6
|
+
*
|
|
7
|
+
* Endpoints:
|
|
8
|
+
* - GET /api/docs - Main documentation UI with version selector
|
|
9
|
+
* - GET /api/v1/docs - v1-specific documentation
|
|
10
|
+
* - GET /api/v2/docs - v2-specific documentation
|
|
11
|
+
* - GET /api/openapi.json - v2 spec (default/current)
|
|
12
|
+
* - GET /api/openapi-v1.json - v1 spec
|
|
13
|
+
* - GET /api/openapi-v2.json - v2 spec
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const { Router } = require('express');
|
|
17
|
+
const { createAppHandler } = require('./../app-handler-helpers');
|
|
18
|
+
const {
|
|
19
|
+
generateOpenApiSpec,
|
|
20
|
+
generateOpenApiSpecV1,
|
|
21
|
+
generateOpenApiSpecV2,
|
|
22
|
+
} = require('../../openapi/openapi-spec-generator');
|
|
23
|
+
|
|
24
|
+
const router = Router();
|
|
25
|
+
|
|
26
|
+
let cachedAppDefinition = null;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Load the appDefinition for spec generation
|
|
30
|
+
* Lazy-loads and caches to avoid performance overhead
|
|
31
|
+
*/
|
|
32
|
+
function loadAppDefinitionForDocs() {
|
|
33
|
+
if (cachedAppDefinition) return cachedAppDefinition;
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const { loadAppDefinition } = require('../app-definition-loader');
|
|
37
|
+
const { integrations } = loadAppDefinition();
|
|
38
|
+
cachedAppDefinition = { integrations };
|
|
39
|
+
return cachedAppDefinition;
|
|
40
|
+
} catch (error) {
|
|
41
|
+
// App definition not available (e.g., in test environment)
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Generate Scalar HTML with version selector
|
|
48
|
+
* @param {Object} options - Configuration options
|
|
49
|
+
* @param {string} options.specUrl - Primary spec URL
|
|
50
|
+
* @param {Array} options.sources - Array of spec sources for version selector
|
|
51
|
+
* @param {string} options.title - Page title
|
|
52
|
+
*/
|
|
53
|
+
function generateScalarHtml({
|
|
54
|
+
specUrl,
|
|
55
|
+
sources,
|
|
56
|
+
title = 'Frigg API Documentation',
|
|
57
|
+
}) {
|
|
58
|
+
// If sources provided, use multi-spec configuration
|
|
59
|
+
const config = sources ? { sources } : { url: specUrl };
|
|
60
|
+
|
|
61
|
+
return `<!DOCTYPE html>
|
|
62
|
+
<html>
|
|
63
|
+
<head>
|
|
64
|
+
<title>${title}</title>
|
|
65
|
+
<meta charset="utf-8" />
|
|
66
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
67
|
+
<style>
|
|
68
|
+
body { margin: 0; padding: 0; }
|
|
69
|
+
</style>
|
|
70
|
+
</head>
|
|
71
|
+
<body>
|
|
72
|
+
<script id="api-reference" data-configuration='${JSON.stringify(
|
|
73
|
+
config
|
|
74
|
+
)}'></script>
|
|
75
|
+
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
|
|
76
|
+
</body>
|
|
77
|
+
</html>`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ============================================================================
|
|
81
|
+
// OpenAPI Spec Endpoints
|
|
82
|
+
// ============================================================================
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* GET /api/openapi.json - Default (v2) OpenAPI spec
|
|
86
|
+
*/
|
|
87
|
+
router.get('/api/openapi.json', (req, res) => {
|
|
88
|
+
try {
|
|
89
|
+
const serverUrl = `${req.protocol}://${req.get('host')}`;
|
|
90
|
+
const appDefinition = loadAppDefinitionForDocs();
|
|
91
|
+
const spec = generateOpenApiSpecV2(appDefinition, { serverUrl });
|
|
92
|
+
res.json(spec);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.error('Failed to generate OpenAPI spec:', error.message);
|
|
95
|
+
res.status(500).json({ error: 'Failed to load API specification' });
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* GET /api/openapi-v1.json - v1 API OpenAPI spec
|
|
101
|
+
*/
|
|
102
|
+
router.get('/api/openapi-v1.json', (req, res) => {
|
|
103
|
+
try {
|
|
104
|
+
const serverUrl = `${req.protocol}://${req.get('host')}`;
|
|
105
|
+
const appDefinition = loadAppDefinitionForDocs();
|
|
106
|
+
const spec = generateOpenApiSpecV1(appDefinition, { serverUrl });
|
|
107
|
+
res.json(spec);
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.error('Failed to generate v1 OpenAPI spec:', error.message);
|
|
110
|
+
res.status(500).json({ error: 'Failed to load API specification' });
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* GET /api/openapi-v2.json - v2 API OpenAPI spec
|
|
116
|
+
*/
|
|
117
|
+
router.get('/api/openapi-v2.json', (req, res) => {
|
|
118
|
+
try {
|
|
119
|
+
const serverUrl = `${req.protocol}://${req.get('host')}`;
|
|
120
|
+
const appDefinition = loadAppDefinitionForDocs();
|
|
121
|
+
const spec = generateOpenApiSpecV2(appDefinition, { serverUrl });
|
|
122
|
+
res.json(spec);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.error('Failed to generate v2 OpenAPI spec:', error.message);
|
|
125
|
+
res.status(500).json({ error: 'Failed to load API specification' });
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// ============================================================================
|
|
130
|
+
// Documentation UI Endpoints
|
|
131
|
+
// ============================================================================
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* GET /api/docs - Main documentation with version selector
|
|
135
|
+
* Shows both v1 and v2 APIs with a dropdown to switch between them
|
|
136
|
+
*/
|
|
137
|
+
router.get('/api/docs', (_req, res) => {
|
|
138
|
+
const html = generateScalarHtml({
|
|
139
|
+
sources: [
|
|
140
|
+
{
|
|
141
|
+
title: 'API v2 (Current)',
|
|
142
|
+
slug: 'v2',
|
|
143
|
+
url: '/api/openapi-v2.json',
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
title: 'API v1 (Legacy)',
|
|
147
|
+
slug: 'v1',
|
|
148
|
+
url: '/api/openapi-v1.json',
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
title: 'Frigg API Documentation',
|
|
152
|
+
});
|
|
153
|
+
res.type('html').send(html);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* GET /api/v1/docs - v1-specific documentation
|
|
158
|
+
*/
|
|
159
|
+
router.get('/api/v1/docs', (_req, res) => {
|
|
160
|
+
const html = generateScalarHtml({
|
|
161
|
+
specUrl: '/api/openapi-v1.json',
|
|
162
|
+
title: 'Frigg API v1 Documentation',
|
|
163
|
+
});
|
|
164
|
+
res.type('html').send(html);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* GET /api/v2/docs - v2-specific documentation
|
|
169
|
+
*/
|
|
170
|
+
router.get('/api/v2/docs', (_req, res) => {
|
|
171
|
+
const html = generateScalarHtml({
|
|
172
|
+
specUrl: '/api/openapi-v2.json',
|
|
173
|
+
title: 'Frigg API v2 Documentation',
|
|
174
|
+
});
|
|
175
|
+
res.type('html').send(html);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const handler = createAppHandler('HTTP Event: Docs', router, false);
|
|
179
|
+
|
|
180
|
+
module.exports = { handler, router };
|