insforge 1.3.0 → 1.4.8
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 +2 -0
- package/auth/package.json +5 -3
- package/auth/src/lib/broadcastService.ts +115 -117
- package/auth/src/lib/insforge.ts +8 -0
- package/auth/src/main.tsx +2 -4
- package/auth/src/pages/SignInPage.tsx +60 -60
- package/auth/src/pages/SignUpPage.tsx +60 -60
- package/auth/src/pages/VerifyEmailPage.tsx +18 -0
- package/auth/tsconfig.json +2 -1
- package/backend/package.json +10 -6
- package/backend/src/api/middlewares/rate-limiters.ts +127 -127
- package/backend/src/api/routes/ai/index.routes.ts +475 -468
- package/backend/src/api/routes/auth/index.routes.ts +85 -32
- package/backend/src/api/routes/auth/oauth.routes.ts +11 -6
- package/backend/src/api/routes/database/index.routes.ts +2 -0
- package/backend/src/api/routes/database/records.routes.ts +39 -175
- package/backend/src/api/routes/database/rpc.routes.ts +69 -0
- package/backend/src/api/routes/deployments/index.routes.ts +192 -0
- package/backend/src/api/routes/docs/index.routes.ts +3 -2
- package/backend/src/api/routes/email/index.routes.ts +35 -35
- package/backend/src/api/routes/functions/index.routes.ts +3 -3
- package/backend/src/api/routes/metadata/index.routes.ts +26 -0
- package/backend/src/api/routes/webhooks/index.routes.ts +109 -0
- package/backend/src/infra/database/database.manager.ts +0 -10
- package/backend/src/infra/database/migrations/018_schema-rework.sql +441 -0
- package/backend/src/infra/database/migrations/019_create-deployments-table.sql +36 -0
- package/backend/src/infra/database/migrations/020_add-audio-modality.sql +11 -0
- package/backend/src/infra/database/migrations/bootstrap/bootstrap-migrations.js +103 -0
- package/backend/src/infra/security/token.manager.ts +1 -4
- package/backend/src/providers/ai/openrouter.provider.ts +12 -3
- package/backend/src/providers/database/base.provider.ts +39 -0
- package/backend/src/providers/database/cloud.provider.ts +159 -0
- package/backend/src/providers/deployments/vercel.provider.ts +516 -0
- package/backend/src/server.ts +19 -7
- package/backend/src/services/ai/ai-config.service.ts +6 -6
- package/backend/src/services/ai/ai-model.service.ts +60 -60
- package/backend/src/services/ai/ai-usage.service.ts +7 -7
- package/backend/src/services/ai/chat-completion.service.ts +415 -220
- package/backend/src/services/ai/helpers.ts +64 -64
- package/backend/src/services/ai/index.ts +13 -13
- package/backend/src/services/auth/auth-config.service.ts +4 -4
- package/backend/src/services/auth/auth-otp.service.ts +6 -6
- package/backend/src/services/auth/auth.service.ts +134 -74
- package/backend/src/services/auth/index.ts +4 -4
- package/backend/src/services/auth/oauth-config.service.ts +12 -12
- package/backend/src/services/database/database-advance.service.ts +19 -55
- package/backend/src/services/database/database-table.service.ts +38 -85
- package/backend/src/services/database/postgrest-proxy.service.ts +165 -0
- package/backend/src/services/deployments/deployment.service.ts +693 -0
- package/backend/src/services/functions/function.service.ts +61 -41
- package/backend/src/services/logs/audit.service.ts +10 -10
- package/backend/src/services/secrets/secret.service.ts +101 -27
- package/backend/src/services/storage/storage.service.ts +30 -30
- package/backend/src/services/usage/usage.service.ts +6 -6
- package/backend/src/types/ai.ts +8 -0
- package/backend/src/types/auth.ts +5 -1
- package/backend/src/types/database.ts +2 -0
- package/backend/src/types/deployments.ts +33 -0
- package/backend/src/types/storage.ts +1 -1
- package/backend/src/types/webhooks.ts +45 -0
- package/backend/src/utils/cookies.ts +34 -35
- package/backend/src/utils/environment.ts +0 -14
- package/backend/src/utils/s3-config-loader.ts +64 -64
- package/backend/src/utils/seed.ts +334 -301
- package/backend/src/utils/sql-parser.ts +126 -0
- package/backend/src/utils/utils.ts +114 -114
- package/backend/src/utils/validations.ts +10 -10
- package/backend/tests/local/test-rpc.sh +141 -0
- package/backend/tests/local/test-secrets.sh +1 -1
- package/backend/tests/manual/test-ai-model-plugins.sh +258 -0
- package/backend/tests/manual/test-rawsql-modes.sh +24 -24
- package/backend/tests/unit/database-advance.test.ts +326 -0
- package/backend/tests/unit/helpers.test.ts +2 -2
- package/claude-plugin/skills/insforge-schema-patterns/SKILL.md +13 -10
- package/docker-compose.prod.yml +1 -1
- package/docker-compose.yml +1 -1
- package/docs/agent-docs/deployment.md +79 -0
- package/docs/changelog.mdx +165 -72
- package/docs/core-concepts/ai/architecture.mdx +1 -23
- package/docs/core-concepts/ai/sdk.mdx +26 -1
- package/docs/core-concepts/authentication/architecture.mdx +6 -8
- package/docs/core-concepts/authentication/sdk.mdx +387 -91
- package/docs/core-concepts/authentication/ui-components/customization.mdx +460 -256
- package/docs/core-concepts/authentication/ui-components/nextjs.mdx +50 -24
- package/docs/core-concepts/authentication/ui-components/react-router.mdx +18 -19
- package/docs/core-concepts/authentication/ui-components/react.mdx +26 -19
- package/docs/core-concepts/database/architecture.mdx +58 -21
- package/docs/core-concepts/database/pgvector.mdx +138 -0
- package/docs/core-concepts/database/sdk.mdx +17 -17
- package/docs/core-concepts/deployments/architecture.mdx +152 -0
- package/docs/core-concepts/email/architecture.mdx +4 -2
- package/docs/core-concepts/functions/architecture.mdx +1 -1
- package/docs/core-concepts/functions/sdk.mdx +0 -1
- package/docs/core-concepts/realtime/architecture.mdx +1 -1
- package/docs/core-concepts/storage/architecture.mdx +1 -1
- package/docs/core-concepts/storage/sdk.mdx +25 -25
- package/docs/docs.json +14 -6
- package/docs/favicon.png +0 -0
- package/docs/favicon.svg +3 -18
- package/docs/images/changelog/dec-2025/apple-oauth.mp4 +0 -0
- package/docs/images/changelog/dec-2025/moreModels.png +0 -0
- package/docs/images/changelog/dec-2025/multi-region.webp +0 -0
- package/docs/images/changelog/dec-2025/postgres-connection.webp +0 -0
- package/docs/images/changelog/dec-2025/realtime2.png +0 -0
- package/docs/images/mcp-setup/CC-MCP-1.mp4 +0 -0
- package/docs/images/mcp-setup/CC-MCP-2.mp4 +0 -0
- package/docs/images/mcp-setup/Cursor-MCP-1.mp4 +0 -0
- package/docs/images/mcp-setup/Cursor-MCP-2.mp4 +0 -0
- package/docs/images/mcp-setup/Cursor-MCP-3.mp4 +0 -0
- package/docs/images/mcp-setup/claude-code-connect.png +0 -0
- package/docs/images/mcp-setup/cline-1.png +0 -0
- package/docs/images/mcp-setup/cline-2.png +0 -0
- package/docs/images/mcp-setup/cline-3.png +0 -0
- package/docs/images/mcp-setup/connect-project.png +0 -0
- package/docs/images/mcp-setup/copilot-1.png +0 -0
- package/docs/images/mcp-setup/copilot-2.png +0 -0
- package/docs/images/mcp-setup/copilot-3.png +0 -0
- package/docs/images/mcp-setup/mcp-json-1.png +0 -0
- package/docs/images/mcp-setup/mcp-json-2.png +0 -0
- package/docs/images/mcp-setup/qoder-1.png +0 -0
- package/docs/images/mcp-setup/qoder-2.png +0 -0
- package/docs/images/mcp-setup/roocode-1.png +0 -0
- package/docs/images/mcp-setup/roocode-2.png +0 -0
- package/docs/images/mcp-setup/trae-1.png +0 -0
- package/docs/images/mcp-setup/trae-2.png +0 -0
- package/docs/images/mcp-setup/trae-3.png +0 -0
- package/docs/images/mcp-setup/trae-4.png +0 -0
- package/docs/images/mcp-setup/trae-5.png +0 -0
- package/docs/images/mcp-setup/windsurf-1.png +0 -0
- package/docs/images/mcp-setup/windsurf-2.png +0 -0
- package/docs/insforge-instructions-sdk.md +7 -3
- package/docs/introduction.mdx +9 -8
- package/docs/mcp-setup.mdx +332 -0
- package/docs/oauth-server.mdx +563 -0
- package/docs/partnership.mdx +79 -10
- package/docs/quickstart.mdx +1 -1
- package/docs/vscode-extension.mdx +74 -0
- package/eslint.config.js +1 -0
- package/examples/response-examples.md +1 -1
- package/frontend/package.json +1 -1
- package/frontend/src/App.tsx +8 -3
- package/frontend/src/assets/logos/antigravity.svg +1 -0
- package/frontend/src/assets/logos/copilot.svg +10 -0
- package/frontend/src/assets/logos/deepseek.svg +139 -0
- package/frontend/src/assets/logos/kiro.svg +9 -0
- package/frontend/src/assets/logos/qoder.svg +4 -0
- package/frontend/src/assets/logos/qwen.svg +15 -0
- package/frontend/src/components/CodeBlock.tsx +2 -2
- package/frontend/src/components/ConnectCTA.tsx +3 -2
- package/frontend/src/components/datagrid/DataGrid.tsx +90 -62
- package/frontend/src/components/datagrid/datagridTypes.tsx +2 -1
- package/frontend/src/components/datagrid/index.ts +1 -1
- package/frontend/src/components/index.ts +0 -1
- package/frontend/src/components/layout/AppHeader.tsx +4 -27
- package/frontend/src/components/layout/AppSidebar.tsx +85 -100
- package/frontend/src/components/layout/Layout.tsx +34 -32
- package/frontend/src/components/layout/PrimaryMenu.tsx +12 -4
- package/frontend/src/components/radix/Select.tsx +151 -151
- package/frontend/src/features/ai/components/AIConfigCard.tsx +200 -200
- package/frontend/src/features/ai/components/AIEmptyState.tsx +23 -23
- package/frontend/src/features/ai/components/ModalityFilterSidebar.tsx +102 -101
- package/frontend/src/features/ai/components/ModelSelectionDialog.tsx +135 -135
- package/frontend/src/features/ai/components/ModelSelectionGrid.tsx +51 -51
- package/frontend/src/features/ai/components/SystemPromptDialog.tsx +118 -118
- package/frontend/src/features/ai/components/index.ts +6 -6
- package/frontend/src/features/ai/helpers.ts +147 -141
- package/frontend/src/features/ai/pages/AIPage.tsx +166 -166
- package/frontend/src/features/auth/components/AuthPreview.tsx +96 -96
- package/frontend/src/features/auth/components/UsersDataGrid.tsx +55 -31
- package/frontend/src/features/auth/components/index.ts +5 -5
- package/frontend/src/features/auth/pages/AuthMethodsPage.tsx +275 -275
- package/frontend/src/features/dashboard/pages/DashboardPage.tsx +1 -1
- package/frontend/src/features/database/components/DatabaseDataGrid.tsx +0 -2
- package/frontend/src/features/database/components/ForeignKeyCell.tsx +38 -11
- package/frontend/src/features/database/components/ForeignKeyPopover.tsx +18 -8
- package/frontend/src/features/database/components/LinkRecordModal.tsx +61 -13
- package/frontend/src/features/database/components/RecordFormField.tsx +1 -1
- package/frontend/src/features/database/components/TableSidebar.tsx +0 -3
- package/frontend/src/features/database/components/TablesEmptyState.tsx +1 -1
- package/frontend/src/features/database/components/TemplatePreview.tsx +1 -2
- package/frontend/src/features/database/constants.ts +16 -28
- package/frontend/src/features/database/hooks/useCSVImport.ts +3 -2
- package/frontend/src/features/database/hooks/useRawSQL.ts +3 -2
- package/frontend/src/features/database/hooks/useTables.ts +5 -7
- package/frontend/src/features/database/pages/FunctionsPage.tsx +0 -5
- package/frontend/src/features/database/pages/IndexesPage.tsx +0 -5
- package/frontend/src/features/database/pages/PoliciesPage.tsx +0 -5
- package/frontend/src/features/database/pages/SQLEditorPage.tsx +2 -2
- package/frontend/src/features/database/pages/TriggersPage.tsx +0 -5
- package/frontend/src/features/database/services/advance.service.ts +1 -15
- package/frontend/src/features/database/services/record.service.ts +4 -20
- package/frontend/src/features/database/services/table.service.ts +1 -4
- package/frontend/src/features/database/templates/ai-chatbot.ts +6 -6
- package/frontend/src/features/database/templates/ecommerce-platform.ts +2 -2
- package/frontend/src/features/database/templates/instagram-clone.ts +10 -10
- package/frontend/src/features/database/templates/notion-clone.ts +8 -8
- package/frontend/src/features/database/templates/reddit-clone.ts +10 -10
- package/frontend/src/features/deployments/components/DeploymentRow.tsx +93 -0
- package/frontend/src/features/deployments/components/DeploymentsEmptyState.tsx +15 -0
- package/frontend/src/features/deployments/hooks/useDeployments.ts +157 -0
- package/frontend/src/features/deployments/pages/DeploymentsPage.tsx +318 -0
- package/frontend/src/features/deployments/services/deployments.service.ts +63 -0
- package/frontend/src/features/functions/components/FunctionRow.tsx +72 -72
- package/frontend/src/features/functions/components/FunctionsSidebar.tsx +56 -56
- package/frontend/src/features/functions/components/SecretRow.tsx +3 -3
- package/frontend/src/features/functions/components/index.ts +5 -5
- package/frontend/src/features/functions/hooks/useFunctions.ts +5 -4
- package/frontend/src/features/functions/hooks/useSecrets.ts +6 -9
- package/frontend/src/features/functions/pages/SecretsPage.tsx +118 -118
- package/frontend/src/features/functions/services/function.service.ts +8 -25
- package/frontend/src/features/functions/services/secret.service.ts +23 -41
- package/frontend/src/features/login/pages/CloudLoginPage.tsx +125 -118
- package/frontend/src/features/logs/components/LogDetailPanel.tsx +41 -0
- package/frontend/src/features/logs/components/LogsDataGrid.tsx +32 -1
- package/frontend/src/features/logs/components/index.ts +1 -0
- package/frontend/src/features/logs/pages/LogsPage.tsx +36 -6
- package/frontend/src/features/onboard/components/ApiCredentialsSection.tsx +59 -0
- package/frontend/src/features/onboard/components/ConnectionStringSection.tsx +180 -0
- package/frontend/src/features/onboard/components/McpConnectionSection.tsx +159 -0
- package/frontend/src/features/onboard/components/OnboardingController.tsx +68 -0
- package/frontend/src/features/onboard/components/OnboardingModal.tsx +121 -267
- package/frontend/src/features/onboard/components/ShowPasswordButton.tsx +21 -0
- package/frontend/src/features/onboard/components/index.ts +9 -4
- package/frontend/src/features/onboard/components/mcp/CursorDeeplinkGenerator.tsx +1 -1
- package/frontend/src/features/onboard/components/mcp/QoderDeeplinkGenerator.tsx +36 -0
- package/frontend/src/features/onboard/components/mcp/helpers.tsx +123 -98
- package/frontend/src/features/onboard/components/mcp/index.ts +4 -3
- package/frontend/src/features/onboard/index.ts +17 -13
- package/frontend/src/features/settings/pages/SettingsPage.tsx +349 -0
- package/frontend/src/features/visualizer/components/AuthNode.tsx +4 -4
- package/frontend/src/features/visualizer/components/SchemaVisualizer.tsx +21 -8
- package/frontend/src/features/visualizer/pages/VisualizerPage.tsx +10 -1
- package/frontend/src/index.css +249 -249
- package/frontend/src/lib/contexts/ModalContext.tsx +35 -0
- package/frontend/src/lib/hooks/useMetadata.ts +45 -1
- package/frontend/src/lib/hooks/useModal.tsx +2 -0
- package/frontend/src/lib/routing/AppRoutes.tsx +103 -99
- package/frontend/src/lib/services/metadata.service.ts +20 -3
- package/frontend/src/lib/utils/menuItems.ts +223 -207
- package/frontend/src/lib/utils/utils.ts +196 -196
- package/functions/server.ts +315 -315
- package/functions/worker-template.js +1 -1
- package/openapi/ai.yaml +115 -5
- package/openapi/auth.yaml +97 -17
- package/openapi/logs.yaml +0 -2
- package/openapi/metadata.yaml +0 -2
- package/openapi/records.yaml +21 -21
- package/openapi/tables.yaml +1 -2
- package/package.json +1 -1
- package/shared-schemas/package.json +1 -1
- package/shared-schemas/src/ai-api.schema.ts +251 -143
- package/shared-schemas/src/ai.schema.ts +63 -63
- package/shared-schemas/src/auth-api.schema.ts +34 -6
- package/shared-schemas/src/auth.schema.ts +17 -10
- package/shared-schemas/src/cloud-events.schema.ts +26 -0
- package/shared-schemas/src/deployments-api.schema.ts +55 -0
- package/shared-schemas/src/deployments.schema.ts +30 -0
- package/shared-schemas/src/docs.schema.ts +8 -2
- package/shared-schemas/src/email-api.schema.ts +30 -30
- package/shared-schemas/src/functions-api.schema.ts +13 -4
- package/shared-schemas/src/functions.schema.ts +1 -1
- package/shared-schemas/src/index.ts +22 -18
- package/shared-schemas/src/metadata.schema.ts +30 -4
- package/shared-schemas/src/secrets-api.schema.ts +44 -0
- package/shared-schemas/src/secrets.schema.ts +15 -0
- package/zeabur/README.md +13 -0
- package/zeabur/template.yml +20 -51
- package/backend/src/types/profile.ts +0 -55
- package/frontend/src/components/ProjectInfoModal.tsx +0 -128
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { Router, Response, NextFunction } from 'express';
|
|
2
|
+
import { DeploymentService } from '@/services/deployments/deployment.service.js';
|
|
3
|
+
import { verifyAdmin, AuthRequest } from '@/api/middlewares/auth.js';
|
|
4
|
+
import { AuditService } from '@/services/logs/audit.service.js';
|
|
5
|
+
import { AppError } from '@/api/middlewares/error.js';
|
|
6
|
+
import { ERROR_CODES } from '@/types/error-constants.js';
|
|
7
|
+
import { successResponse, paginatedResponse } from '@/utils/response.js';
|
|
8
|
+
import { startDeploymentRequestSchema } from '@insforge/shared-schemas';
|
|
9
|
+
|
|
10
|
+
const router = Router();
|
|
11
|
+
const deploymentService = DeploymentService.getInstance();
|
|
12
|
+
const auditService = AuditService.getInstance();
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Create a new deployment record with WAITING status
|
|
16
|
+
* Returns presigned URL for uploading source zip file
|
|
17
|
+
* POST /api/deployments
|
|
18
|
+
*/
|
|
19
|
+
router.post('/', verifyAdmin, async (req: AuthRequest, res: Response, next: NextFunction) => {
|
|
20
|
+
try {
|
|
21
|
+
// Check if deployment service is configured
|
|
22
|
+
if (!deploymentService.isConfigured()) {
|
|
23
|
+
throw new AppError(
|
|
24
|
+
'Deployment service is not configured. Please set VERCEL_TOKEN, VERCEL_TEAM_ID, and VERCEL_PROJECT_ID environment variables.',
|
|
25
|
+
503,
|
|
26
|
+
ERROR_CODES.INTERNAL_ERROR
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const response = await deploymentService.createDeployment();
|
|
31
|
+
|
|
32
|
+
// Log audit
|
|
33
|
+
await auditService.log({
|
|
34
|
+
actor: req.user?.email || 'api-key',
|
|
35
|
+
action: 'CREATE_DEPLOYMENT',
|
|
36
|
+
module: 'DEPLOYMENTS',
|
|
37
|
+
details: { id: response.id },
|
|
38
|
+
ip_address: req.ip,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
successResponse(res, response, 201);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
next(error);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Start a deployment - downloads zip from S3, uploads to Vercel, creates deployment
|
|
49
|
+
* POST /api/deployments/:id/start
|
|
50
|
+
*/
|
|
51
|
+
router.post(
|
|
52
|
+
'/:id/start',
|
|
53
|
+
verifyAdmin,
|
|
54
|
+
async (req: AuthRequest, res: Response, next: NextFunction) => {
|
|
55
|
+
try {
|
|
56
|
+
// Check if deployment service is configured
|
|
57
|
+
if (!deploymentService.isConfigured()) {
|
|
58
|
+
throw new AppError(
|
|
59
|
+
'Deployment service is not configured. Please set VERCEL_TOKEN, VERCEL_TEAM_ID, and VERCEL_PROJECT_ID environment variables.',
|
|
60
|
+
503,
|
|
61
|
+
ERROR_CODES.INTERNAL_ERROR
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const { id } = req.params;
|
|
66
|
+
|
|
67
|
+
const validationResult = startDeploymentRequestSchema.safeParse(req.body);
|
|
68
|
+
if (!validationResult.success) {
|
|
69
|
+
throw new AppError(
|
|
70
|
+
validationResult.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', '),
|
|
71
|
+
400,
|
|
72
|
+
ERROR_CODES.INVALID_INPUT
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const deployment = await deploymentService.startDeployment(id, validationResult.data);
|
|
77
|
+
|
|
78
|
+
// Log audit
|
|
79
|
+
await auditService.log({
|
|
80
|
+
actor: req.user?.email || 'api-key',
|
|
81
|
+
action: 'START_DEPLOYMENT',
|
|
82
|
+
module: 'DEPLOYMENTS',
|
|
83
|
+
details: {
|
|
84
|
+
id: deployment.id,
|
|
85
|
+
providerDeploymentId: deployment.providerDeploymentId,
|
|
86
|
+
provider: deployment.provider,
|
|
87
|
+
status: deployment.status,
|
|
88
|
+
},
|
|
89
|
+
ip_address: req.ip,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
successResponse(res, deployment);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
next(error);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* List all deployments
|
|
101
|
+
* GET /api/deployments
|
|
102
|
+
*/
|
|
103
|
+
router.get('/', verifyAdmin, async (req: AuthRequest, res: Response, next: NextFunction) => {
|
|
104
|
+
try {
|
|
105
|
+
const limit = parseInt(req.query.limit as string) || 50;
|
|
106
|
+
const offset = parseInt(req.query.offset as string) || 0;
|
|
107
|
+
|
|
108
|
+
const { deployments, total } = await deploymentService.listDeployments(limit, offset);
|
|
109
|
+
|
|
110
|
+
paginatedResponse(res, deployments, total, offset);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
next(error);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Get deployment by database ID
|
|
118
|
+
* GET /api/deployments/:id
|
|
119
|
+
*/
|
|
120
|
+
router.get('/:id', verifyAdmin, async (req: AuthRequest, res: Response, next: NextFunction) => {
|
|
121
|
+
try {
|
|
122
|
+
const { id } = req.params;
|
|
123
|
+
|
|
124
|
+
const deployment = await deploymentService.getDeploymentById(id);
|
|
125
|
+
|
|
126
|
+
if (!deployment) {
|
|
127
|
+
throw new AppError(`Deployment not found: ${id}`, 404, ERROR_CODES.NOT_FOUND);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
successResponse(res, deployment);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
next(error);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Sync deployment status from Vercel and update database
|
|
138
|
+
* POST /api/deployments/:id/sync
|
|
139
|
+
*/
|
|
140
|
+
router.post(
|
|
141
|
+
'/:id/sync',
|
|
142
|
+
verifyAdmin,
|
|
143
|
+
async (req: AuthRequest, res: Response, next: NextFunction) => {
|
|
144
|
+
try {
|
|
145
|
+
const { id } = req.params;
|
|
146
|
+
|
|
147
|
+
const deployment = await deploymentService.syncDeploymentById(id);
|
|
148
|
+
|
|
149
|
+
if (!deployment) {
|
|
150
|
+
throw new AppError(`Deployment not found: ${id}`, 404, ERROR_CODES.NOT_FOUND);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
successResponse(res, deployment);
|
|
154
|
+
} catch (error) {
|
|
155
|
+
next(error);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Cancel a deployment
|
|
162
|
+
* POST /api/deployments/:id/cancel
|
|
163
|
+
*/
|
|
164
|
+
router.post(
|
|
165
|
+
'/:id/cancel',
|
|
166
|
+
verifyAdmin,
|
|
167
|
+
async (req: AuthRequest, res: Response, next: NextFunction) => {
|
|
168
|
+
try {
|
|
169
|
+
const { id } = req.params;
|
|
170
|
+
|
|
171
|
+
await deploymentService.cancelDeploymentById(id);
|
|
172
|
+
|
|
173
|
+
// Log audit
|
|
174
|
+
await auditService.log({
|
|
175
|
+
actor: req.user?.email || 'api-key',
|
|
176
|
+
action: 'CANCEL_DEPLOYMENT',
|
|
177
|
+
module: 'DEPLOYMENTS',
|
|
178
|
+
details: { id },
|
|
179
|
+
ip_address: req.ip,
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
successResponse(res, {
|
|
183
|
+
success: true,
|
|
184
|
+
message: `Deployment ${id} has been cancelled`,
|
|
185
|
+
});
|
|
186
|
+
} catch (error) {
|
|
187
|
+
next(error);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
export { router as deploymentsRouter };
|
|
@@ -16,15 +16,16 @@ const router = Router();
|
|
|
16
16
|
const DOCS_MAP: Record<DocTypeSchema, string> = {
|
|
17
17
|
instructions: 'insforge-instructions-sdk.md',
|
|
18
18
|
'db-sdk': 'core-concepts/database/sdk.mdx',
|
|
19
|
-
|
|
19
|
+
'auth-sdk': 'core-concepts/authentication/sdk.mdx',
|
|
20
20
|
// UI Components - Framework-specific
|
|
21
21
|
'auth-components-react': 'core-concepts/authentication/ui-components/react.mdx',
|
|
22
|
-
|
|
22
|
+
'auth-components-nextjs': 'core-concepts/authentication/ui-components/nextjs.mdx',
|
|
23
23
|
// 'auth-components-react-router': 'core-concepts/authentication/ui-components/react-router.mdx',
|
|
24
24
|
'storage-sdk': 'core-concepts/storage/sdk.mdx',
|
|
25
25
|
'functions-sdk': 'core-concepts/functions/sdk.mdx',
|
|
26
26
|
'ai-integration-sdk': 'core-concepts/ai/sdk.mdx',
|
|
27
27
|
'real-time': 'agent-docs/real-time.md',
|
|
28
|
+
'deployment': 'agent-docs/deployment.md',
|
|
28
29
|
};
|
|
29
30
|
|
|
30
31
|
// GET /api/docs/:docType - Get specific documentation
|
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
import { Router, Response, NextFunction } from 'express';
|
|
2
|
-
import { AuthRequest, verifyUser } from '@/api/middlewares/auth.js';
|
|
3
|
-
import { EmailService } from '@/services/email/email.service.js';
|
|
4
|
-
import { AppError } from '@/api/middlewares/error.js';
|
|
5
|
-
import { ERROR_CODES } from '@/types/error-constants.js';
|
|
6
|
-
import { sendRawEmailRequestSchema } from '@insforge/shared-schemas';
|
|
7
|
-
import { successResponse } from '@/utils/response.js';
|
|
8
|
-
|
|
9
|
-
const router = Router();
|
|
10
|
-
const emailService = EmailService.getInstance();
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* POST /api/email/send-raw
|
|
14
|
-
* Send a raw/custom email with explicit to, subject, and body
|
|
15
|
-
*/
|
|
16
|
-
router.post(
|
|
17
|
-
'/send-raw',
|
|
18
|
-
verifyUser,
|
|
19
|
-
async (req: AuthRequest, res: Response, next: NextFunction) => {
|
|
20
|
-
try {
|
|
21
|
-
const validation = sendRawEmailRequestSchema.safeParse(req.body);
|
|
22
|
-
if (!validation.success) {
|
|
23
|
-
throw new AppError(JSON.stringify(validation.error.issues), 400, ERROR_CODES.INVALID_INPUT);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
await emailService.sendRaw(validation.data);
|
|
27
|
-
|
|
28
|
-
successResponse(res, {});
|
|
29
|
-
} catch (error) {
|
|
30
|
-
next(error);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
export const emailRouter = router;
|
|
1
|
+
import { Router, Response, NextFunction } from 'express';
|
|
2
|
+
import { AuthRequest, verifyUser } from '@/api/middlewares/auth.js';
|
|
3
|
+
import { EmailService } from '@/services/email/email.service.js';
|
|
4
|
+
import { AppError } from '@/api/middlewares/error.js';
|
|
5
|
+
import { ERROR_CODES } from '@/types/error-constants.js';
|
|
6
|
+
import { sendRawEmailRequestSchema } from '@insforge/shared-schemas';
|
|
7
|
+
import { successResponse } from '@/utils/response.js';
|
|
8
|
+
|
|
9
|
+
const router = Router();
|
|
10
|
+
const emailService = EmailService.getInstance();
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* POST /api/email/send-raw
|
|
14
|
+
* Send a raw/custom email with explicit to, subject, and body
|
|
15
|
+
*/
|
|
16
|
+
router.post(
|
|
17
|
+
'/send-raw',
|
|
18
|
+
verifyUser,
|
|
19
|
+
async (req: AuthRequest, res: Response, next: NextFunction) => {
|
|
20
|
+
try {
|
|
21
|
+
const validation = sendRawEmailRequestSchema.safeParse(req.body);
|
|
22
|
+
if (!validation.success) {
|
|
23
|
+
throw new AppError(JSON.stringify(validation.error.issues), 400, ERROR_CODES.INVALID_INPUT);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
await emailService.sendRaw(validation.data);
|
|
27
|
+
|
|
28
|
+
successResponse(res, {});
|
|
29
|
+
} catch (error) {
|
|
30
|
+
next(error);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
export const emailRouter = router;
|
|
@@ -5,7 +5,7 @@ import { AuditService } from '@/services/logs/audit.service.js';
|
|
|
5
5
|
import { AppError } from '@/api/middlewares/error.js';
|
|
6
6
|
import { ERROR_CODES } from '@/types/error-constants.js';
|
|
7
7
|
import logger from '@/utils/logger.js';
|
|
8
|
-
import {
|
|
8
|
+
import { uploadFunctionRequestSchema, updateFunctionRequestSchema } from '@insforge/shared-schemas';
|
|
9
9
|
import { SocketManager } from '@/infra/socket/socket.manager.js';
|
|
10
10
|
import { DataUpdateResourceType, ServerEvents } from '@/types/socket.js';
|
|
11
11
|
import { successResponse } from '@/utils/response.js';
|
|
@@ -53,7 +53,7 @@ router.get('/:slug', verifyAdmin, async (req: AuthRequest, res: Response, next:
|
|
|
53
53
|
*/
|
|
54
54
|
router.post('/', verifyAdmin, async (req: AuthRequest, res: Response, next: NextFunction) => {
|
|
55
55
|
try {
|
|
56
|
-
const validation =
|
|
56
|
+
const validation = uploadFunctionRequestSchema.safeParse(req.body);
|
|
57
57
|
if (!validation.success) {
|
|
58
58
|
throw new AppError(JSON.stringify(validation.error.issues), 400, ERROR_CODES.INVALID_INPUT);
|
|
59
59
|
}
|
|
@@ -103,7 +103,7 @@ router.post('/', verifyAdmin, async (req: AuthRequest, res: Response, next: Next
|
|
|
103
103
|
router.put('/:slug', verifyAdmin, async (req: AuthRequest, res: Response, next: NextFunction) => {
|
|
104
104
|
try {
|
|
105
105
|
const { slug } = req.params;
|
|
106
|
-
const validation =
|
|
106
|
+
const validation = updateFunctionRequestSchema.safeParse(req.body);
|
|
107
107
|
|
|
108
108
|
if (!validation.success) {
|
|
109
109
|
throw new AppError(JSON.stringify(validation.error.issues), 400, ERROR_CODES.INVALID_INPUT);
|
|
@@ -12,6 +12,7 @@ import { AppError } from '@/api/middlewares/error.js';
|
|
|
12
12
|
import type { AppMetadataSchema } from '@insforge/shared-schemas';
|
|
13
13
|
import { SecretService } from '@/services/secrets/secret.service.js';
|
|
14
14
|
import { DatabaseManager } from '@/infra/database/database.manager.js';
|
|
15
|
+
import { CloudDatabaseProvider } from '@/providers/database/cloud.provider.js';
|
|
15
16
|
|
|
16
17
|
const router = Router();
|
|
17
18
|
const authService = AuthService.getInstance();
|
|
@@ -128,6 +129,31 @@ router.get('/api-key', async (req: AuthRequest, res: Response, next: NextFunctio
|
|
|
128
129
|
}
|
|
129
130
|
});
|
|
130
131
|
|
|
132
|
+
// Get database connection string from cloud backend (admin only)
|
|
133
|
+
router.get(
|
|
134
|
+
'/database-connection-string',
|
|
135
|
+
async (_req: AuthRequest, res: Response, next: NextFunction) => {
|
|
136
|
+
try {
|
|
137
|
+
const cloudDbProvider = CloudDatabaseProvider.getInstance();
|
|
138
|
+
const connectionInfo = await cloudDbProvider.getDatabaseConnectionString();
|
|
139
|
+
successResponse(res, connectionInfo);
|
|
140
|
+
} catch (error) {
|
|
141
|
+
next(error);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
// Get database password from cloud backend (admin only)
|
|
147
|
+
router.get('/database-password', async (_req: AuthRequest, res: Response, next: NextFunction) => {
|
|
148
|
+
try {
|
|
149
|
+
const cloudDbProvider = CloudDatabaseProvider.getInstance();
|
|
150
|
+
const passwordInfo = await cloudDbProvider.getDatabasePassword();
|
|
151
|
+
successResponse(res, passwordInfo);
|
|
152
|
+
} catch (error) {
|
|
153
|
+
next(error);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
131
157
|
// get metadata for a table.
|
|
132
158
|
// Notice: must be after endpoint /api-key in case of conflict.
|
|
133
159
|
router.get('/:tableName', async (req: AuthRequest, res: Response, next: NextFunction) => {
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
import { Router, Request, Response, NextFunction } from 'express';
|
|
3
|
+
import { DeploymentService } from '@/services/deployments/deployment.service.js';
|
|
4
|
+
import { SecretService } from '@/services/secrets/secret.service.js';
|
|
5
|
+
import { AppError } from '@/api/middlewares/error.js';
|
|
6
|
+
import { ERROR_CODES } from '@/types/error-constants.js';
|
|
7
|
+
import {
|
|
8
|
+
VERCEL_EVENT_TO_STATUS,
|
|
9
|
+
type VercelWebhookPayload,
|
|
10
|
+
type VercelDeploymentEventType,
|
|
11
|
+
} from '@/types/webhooks.js';
|
|
12
|
+
import logger from '@/utils/logger.js';
|
|
13
|
+
|
|
14
|
+
const router = Router();
|
|
15
|
+
const deploymentService = DeploymentService.getInstance();
|
|
16
|
+
const secretService = SecretService.getInstance();
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Vercel webhook endpoint
|
|
20
|
+
* POST /api/webhooks/vercel
|
|
21
|
+
*
|
|
22
|
+
* Receives deployment events from Vercel and updates the database accordingly.
|
|
23
|
+
* Verifies the request using HMAC-SHA1 signature in x-vercel-signature header.
|
|
24
|
+
*/
|
|
25
|
+
router.post('/vercel', async (req: Request, res: Response, next: NextFunction) => {
|
|
26
|
+
try {
|
|
27
|
+
const signature = req.headers['x-vercel-signature'] as string | undefined;
|
|
28
|
+
|
|
29
|
+
if (!signature) {
|
|
30
|
+
throw new AppError('Missing x-vercel-signature header', 401, ERROR_CODES.AUTH_UNAUTHORIZED);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Get the webhook secret from secrets service
|
|
34
|
+
const webhookSecret = await secretService.getSecretByKey('VERCEL_WEBHOOK_SECRET');
|
|
35
|
+
|
|
36
|
+
if (!webhookSecret) {
|
|
37
|
+
logger.error('VERCEL_WEBHOOK_SECRET not found in secrets');
|
|
38
|
+
throw new AppError('Webhook not configured', 500, ERROR_CODES.INTERNAL_ERROR);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// req.body is raw Buffer (express.raw middleware applied in server.ts)
|
|
42
|
+
const rawBody = req.body as Buffer;
|
|
43
|
+
|
|
44
|
+
// Verify the signature using HMAC-SHA1 on original bytes
|
|
45
|
+
const expectedSignature = crypto
|
|
46
|
+
.createHmac('sha1', webhookSecret)
|
|
47
|
+
.update(rawBody)
|
|
48
|
+
.digest('hex');
|
|
49
|
+
|
|
50
|
+
// Use timing-safe comparison to prevent timing attacks
|
|
51
|
+
const signatureBuffer = Buffer.from(signature);
|
|
52
|
+
const expectedBuffer = Buffer.from(expectedSignature);
|
|
53
|
+
|
|
54
|
+
if (
|
|
55
|
+
signatureBuffer.length !== expectedBuffer.length ||
|
|
56
|
+
!crypto.timingSafeEqual(signatureBuffer, expectedBuffer)
|
|
57
|
+
) {
|
|
58
|
+
logger.warn('Invalid Vercel webhook signature');
|
|
59
|
+
throw new AppError('Invalid signature', 401, ERROR_CODES.AUTH_UNAUTHORIZED);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Parse the webhook payload after signature verification
|
|
63
|
+
const webhookPayload = JSON.parse(rawBody.toString()) as VercelWebhookPayload;
|
|
64
|
+
const eventType = webhookPayload.type;
|
|
65
|
+
|
|
66
|
+
// Check if this is a deployment event we handle
|
|
67
|
+
if (!(eventType in VERCEL_EVENT_TO_STATUS)) {
|
|
68
|
+
logger.info('Ignoring unhandled Vercel webhook event', { eventType });
|
|
69
|
+
return res.status(200).json({ received: true, handled: false });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const status = VERCEL_EVENT_TO_STATUS[eventType as VercelDeploymentEventType];
|
|
73
|
+
const deploymentId = webhookPayload.payload.deployment.id;
|
|
74
|
+
const url = webhookPayload.payload.deployment.url
|
|
75
|
+
? `https://${webhookPayload.payload.deployment.url}`
|
|
76
|
+
: null;
|
|
77
|
+
|
|
78
|
+
// Update the deployment in our database
|
|
79
|
+
const deployment = await deploymentService.updateDeploymentFromWebhook(
|
|
80
|
+
deploymentId,
|
|
81
|
+
status,
|
|
82
|
+
url,
|
|
83
|
+
{
|
|
84
|
+
webhookEventId: webhookPayload.id,
|
|
85
|
+
webhookEventType: eventType,
|
|
86
|
+
target: webhookPayload.payload.target,
|
|
87
|
+
projectId: webhookPayload.payload.project?.id,
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
if (!deployment) {
|
|
92
|
+
// Deployment not found in our database - this is ok, might be from another source
|
|
93
|
+
logger.info('Deployment not found for webhook, ignoring', { deploymentId });
|
|
94
|
+
return res.status(200).json({ received: true, handled: false });
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
logger.info('Vercel webhook processed successfully', {
|
|
98
|
+
eventType,
|
|
99
|
+
deploymentId,
|
|
100
|
+
status,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
res.status(200).json({ received: true, handled: true });
|
|
104
|
+
} catch (error) {
|
|
105
|
+
next(error);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
export { router as webhooksRouter };
|
|
@@ -37,14 +37,6 @@ export class DatabaseManager {
|
|
|
37
37
|
idleTimeoutMillis: 30000,
|
|
38
38
|
connectionTimeoutMillis: 2000,
|
|
39
39
|
});
|
|
40
|
-
|
|
41
|
-
const client = await this.pool.connect();
|
|
42
|
-
await client.query('BEGIN');
|
|
43
|
-
|
|
44
|
-
// Note: Schema migrations are now handled by node-pg-migrate
|
|
45
|
-
// Run: npm run migrate:up
|
|
46
|
-
|
|
47
|
-
await client.query('COMMIT');
|
|
48
40
|
}
|
|
49
41
|
|
|
50
42
|
static async getColumnTypeMap(tableName: string): Promise<Record<string, string>> {
|
|
@@ -74,7 +66,6 @@ export class DatabaseManager {
|
|
|
74
66
|
FROM information_schema.tables
|
|
75
67
|
WHERE table_schema = 'public'
|
|
76
68
|
AND table_type = 'BASE TABLE'
|
|
77
|
-
AND (table_name NOT LIKE '\\_%')
|
|
78
69
|
ORDER BY table_name
|
|
79
70
|
`
|
|
80
71
|
);
|
|
@@ -100,7 +91,6 @@ export class DatabaseManager {
|
|
|
100
91
|
FROM information_schema.tables
|
|
101
92
|
WHERE table_schema = 'public'
|
|
102
93
|
AND table_type = 'BASE TABLE'
|
|
103
|
-
AND (table_name NOT LIKE '\\_%')
|
|
104
94
|
ORDER BY table_name
|
|
105
95
|
`
|
|
106
96
|
);
|