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.
Files changed (269) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/auth/package.json +5 -3
  3. package/auth/src/lib/broadcastService.ts +115 -117
  4. package/auth/src/lib/insforge.ts +8 -0
  5. package/auth/src/main.tsx +2 -4
  6. package/auth/src/pages/SignInPage.tsx +60 -60
  7. package/auth/src/pages/SignUpPage.tsx +60 -60
  8. package/auth/src/pages/VerifyEmailPage.tsx +18 -0
  9. package/auth/tsconfig.json +2 -1
  10. package/backend/package.json +10 -6
  11. package/backend/src/api/middlewares/rate-limiters.ts +127 -127
  12. package/backend/src/api/routes/ai/index.routes.ts +475 -468
  13. package/backend/src/api/routes/auth/index.routes.ts +85 -32
  14. package/backend/src/api/routes/auth/oauth.routes.ts +11 -6
  15. package/backend/src/api/routes/database/index.routes.ts +2 -0
  16. package/backend/src/api/routes/database/records.routes.ts +39 -175
  17. package/backend/src/api/routes/database/rpc.routes.ts +69 -0
  18. package/backend/src/api/routes/deployments/index.routes.ts +192 -0
  19. package/backend/src/api/routes/docs/index.routes.ts +3 -2
  20. package/backend/src/api/routes/email/index.routes.ts +35 -35
  21. package/backend/src/api/routes/functions/index.routes.ts +3 -3
  22. package/backend/src/api/routes/metadata/index.routes.ts +26 -0
  23. package/backend/src/api/routes/webhooks/index.routes.ts +109 -0
  24. package/backend/src/infra/database/database.manager.ts +0 -10
  25. package/backend/src/infra/database/migrations/018_schema-rework.sql +441 -0
  26. package/backend/src/infra/database/migrations/019_create-deployments-table.sql +36 -0
  27. package/backend/src/infra/database/migrations/020_add-audio-modality.sql +11 -0
  28. package/backend/src/infra/database/migrations/bootstrap/bootstrap-migrations.js +103 -0
  29. package/backend/src/infra/security/token.manager.ts +1 -4
  30. package/backend/src/providers/ai/openrouter.provider.ts +12 -3
  31. package/backend/src/providers/database/base.provider.ts +39 -0
  32. package/backend/src/providers/database/cloud.provider.ts +159 -0
  33. package/backend/src/providers/deployments/vercel.provider.ts +516 -0
  34. package/backend/src/server.ts +19 -7
  35. package/backend/src/services/ai/ai-config.service.ts +6 -6
  36. package/backend/src/services/ai/ai-model.service.ts +60 -60
  37. package/backend/src/services/ai/ai-usage.service.ts +7 -7
  38. package/backend/src/services/ai/chat-completion.service.ts +415 -220
  39. package/backend/src/services/ai/helpers.ts +64 -64
  40. package/backend/src/services/ai/index.ts +13 -13
  41. package/backend/src/services/auth/auth-config.service.ts +4 -4
  42. package/backend/src/services/auth/auth-otp.service.ts +6 -6
  43. package/backend/src/services/auth/auth.service.ts +134 -74
  44. package/backend/src/services/auth/index.ts +4 -4
  45. package/backend/src/services/auth/oauth-config.service.ts +12 -12
  46. package/backend/src/services/database/database-advance.service.ts +19 -55
  47. package/backend/src/services/database/database-table.service.ts +38 -85
  48. package/backend/src/services/database/postgrest-proxy.service.ts +165 -0
  49. package/backend/src/services/deployments/deployment.service.ts +693 -0
  50. package/backend/src/services/functions/function.service.ts +61 -41
  51. package/backend/src/services/logs/audit.service.ts +10 -10
  52. package/backend/src/services/secrets/secret.service.ts +101 -27
  53. package/backend/src/services/storage/storage.service.ts +30 -30
  54. package/backend/src/services/usage/usage.service.ts +6 -6
  55. package/backend/src/types/ai.ts +8 -0
  56. package/backend/src/types/auth.ts +5 -1
  57. package/backend/src/types/database.ts +2 -0
  58. package/backend/src/types/deployments.ts +33 -0
  59. package/backend/src/types/storage.ts +1 -1
  60. package/backend/src/types/webhooks.ts +45 -0
  61. package/backend/src/utils/cookies.ts +34 -35
  62. package/backend/src/utils/environment.ts +0 -14
  63. package/backend/src/utils/s3-config-loader.ts +64 -64
  64. package/backend/src/utils/seed.ts +334 -301
  65. package/backend/src/utils/sql-parser.ts +126 -0
  66. package/backend/src/utils/utils.ts +114 -114
  67. package/backend/src/utils/validations.ts +10 -10
  68. package/backend/tests/local/test-rpc.sh +141 -0
  69. package/backend/tests/local/test-secrets.sh +1 -1
  70. package/backend/tests/manual/test-ai-model-plugins.sh +258 -0
  71. package/backend/tests/manual/test-rawsql-modes.sh +24 -24
  72. package/backend/tests/unit/database-advance.test.ts +326 -0
  73. package/backend/tests/unit/helpers.test.ts +2 -2
  74. package/claude-plugin/skills/insforge-schema-patterns/SKILL.md +13 -10
  75. package/docker-compose.prod.yml +1 -1
  76. package/docker-compose.yml +1 -1
  77. package/docs/agent-docs/deployment.md +79 -0
  78. package/docs/changelog.mdx +165 -72
  79. package/docs/core-concepts/ai/architecture.mdx +1 -23
  80. package/docs/core-concepts/ai/sdk.mdx +26 -1
  81. package/docs/core-concepts/authentication/architecture.mdx +6 -8
  82. package/docs/core-concepts/authentication/sdk.mdx +387 -91
  83. package/docs/core-concepts/authentication/ui-components/customization.mdx +460 -256
  84. package/docs/core-concepts/authentication/ui-components/nextjs.mdx +50 -24
  85. package/docs/core-concepts/authentication/ui-components/react-router.mdx +18 -19
  86. package/docs/core-concepts/authentication/ui-components/react.mdx +26 -19
  87. package/docs/core-concepts/database/architecture.mdx +58 -21
  88. package/docs/core-concepts/database/pgvector.mdx +138 -0
  89. package/docs/core-concepts/database/sdk.mdx +17 -17
  90. package/docs/core-concepts/deployments/architecture.mdx +152 -0
  91. package/docs/core-concepts/email/architecture.mdx +4 -2
  92. package/docs/core-concepts/functions/architecture.mdx +1 -1
  93. package/docs/core-concepts/functions/sdk.mdx +0 -1
  94. package/docs/core-concepts/realtime/architecture.mdx +1 -1
  95. package/docs/core-concepts/storage/architecture.mdx +1 -1
  96. package/docs/core-concepts/storage/sdk.mdx +25 -25
  97. package/docs/docs.json +14 -6
  98. package/docs/favicon.png +0 -0
  99. package/docs/favicon.svg +3 -18
  100. package/docs/images/changelog/dec-2025/apple-oauth.mp4 +0 -0
  101. package/docs/images/changelog/dec-2025/moreModels.png +0 -0
  102. package/docs/images/changelog/dec-2025/multi-region.webp +0 -0
  103. package/docs/images/changelog/dec-2025/postgres-connection.webp +0 -0
  104. package/docs/images/changelog/dec-2025/realtime2.png +0 -0
  105. package/docs/images/mcp-setup/CC-MCP-1.mp4 +0 -0
  106. package/docs/images/mcp-setup/CC-MCP-2.mp4 +0 -0
  107. package/docs/images/mcp-setup/Cursor-MCP-1.mp4 +0 -0
  108. package/docs/images/mcp-setup/Cursor-MCP-2.mp4 +0 -0
  109. package/docs/images/mcp-setup/Cursor-MCP-3.mp4 +0 -0
  110. package/docs/images/mcp-setup/claude-code-connect.png +0 -0
  111. package/docs/images/mcp-setup/cline-1.png +0 -0
  112. package/docs/images/mcp-setup/cline-2.png +0 -0
  113. package/docs/images/mcp-setup/cline-3.png +0 -0
  114. package/docs/images/mcp-setup/connect-project.png +0 -0
  115. package/docs/images/mcp-setup/copilot-1.png +0 -0
  116. package/docs/images/mcp-setup/copilot-2.png +0 -0
  117. package/docs/images/mcp-setup/copilot-3.png +0 -0
  118. package/docs/images/mcp-setup/mcp-json-1.png +0 -0
  119. package/docs/images/mcp-setup/mcp-json-2.png +0 -0
  120. package/docs/images/mcp-setup/qoder-1.png +0 -0
  121. package/docs/images/mcp-setup/qoder-2.png +0 -0
  122. package/docs/images/mcp-setup/roocode-1.png +0 -0
  123. package/docs/images/mcp-setup/roocode-2.png +0 -0
  124. package/docs/images/mcp-setup/trae-1.png +0 -0
  125. package/docs/images/mcp-setup/trae-2.png +0 -0
  126. package/docs/images/mcp-setup/trae-3.png +0 -0
  127. package/docs/images/mcp-setup/trae-4.png +0 -0
  128. package/docs/images/mcp-setup/trae-5.png +0 -0
  129. package/docs/images/mcp-setup/windsurf-1.png +0 -0
  130. package/docs/images/mcp-setup/windsurf-2.png +0 -0
  131. package/docs/insforge-instructions-sdk.md +7 -3
  132. package/docs/introduction.mdx +9 -8
  133. package/docs/mcp-setup.mdx +332 -0
  134. package/docs/oauth-server.mdx +563 -0
  135. package/docs/partnership.mdx +79 -10
  136. package/docs/quickstart.mdx +1 -1
  137. package/docs/vscode-extension.mdx +74 -0
  138. package/eslint.config.js +1 -0
  139. package/examples/response-examples.md +1 -1
  140. package/frontend/package.json +1 -1
  141. package/frontend/src/App.tsx +8 -3
  142. package/frontend/src/assets/logos/antigravity.svg +1 -0
  143. package/frontend/src/assets/logos/copilot.svg +10 -0
  144. package/frontend/src/assets/logos/deepseek.svg +139 -0
  145. package/frontend/src/assets/logos/kiro.svg +9 -0
  146. package/frontend/src/assets/logos/qoder.svg +4 -0
  147. package/frontend/src/assets/logos/qwen.svg +15 -0
  148. package/frontend/src/components/CodeBlock.tsx +2 -2
  149. package/frontend/src/components/ConnectCTA.tsx +3 -2
  150. package/frontend/src/components/datagrid/DataGrid.tsx +90 -62
  151. package/frontend/src/components/datagrid/datagridTypes.tsx +2 -1
  152. package/frontend/src/components/datagrid/index.ts +1 -1
  153. package/frontend/src/components/index.ts +0 -1
  154. package/frontend/src/components/layout/AppHeader.tsx +4 -27
  155. package/frontend/src/components/layout/AppSidebar.tsx +85 -100
  156. package/frontend/src/components/layout/Layout.tsx +34 -32
  157. package/frontend/src/components/layout/PrimaryMenu.tsx +12 -4
  158. package/frontend/src/components/radix/Select.tsx +151 -151
  159. package/frontend/src/features/ai/components/AIConfigCard.tsx +200 -200
  160. package/frontend/src/features/ai/components/AIEmptyState.tsx +23 -23
  161. package/frontend/src/features/ai/components/ModalityFilterSidebar.tsx +102 -101
  162. package/frontend/src/features/ai/components/ModelSelectionDialog.tsx +135 -135
  163. package/frontend/src/features/ai/components/ModelSelectionGrid.tsx +51 -51
  164. package/frontend/src/features/ai/components/SystemPromptDialog.tsx +118 -118
  165. package/frontend/src/features/ai/components/index.ts +6 -6
  166. package/frontend/src/features/ai/helpers.ts +147 -141
  167. package/frontend/src/features/ai/pages/AIPage.tsx +166 -166
  168. package/frontend/src/features/auth/components/AuthPreview.tsx +96 -96
  169. package/frontend/src/features/auth/components/UsersDataGrid.tsx +55 -31
  170. package/frontend/src/features/auth/components/index.ts +5 -5
  171. package/frontend/src/features/auth/pages/AuthMethodsPage.tsx +275 -275
  172. package/frontend/src/features/dashboard/pages/DashboardPage.tsx +1 -1
  173. package/frontend/src/features/database/components/DatabaseDataGrid.tsx +0 -2
  174. package/frontend/src/features/database/components/ForeignKeyCell.tsx +38 -11
  175. package/frontend/src/features/database/components/ForeignKeyPopover.tsx +18 -8
  176. package/frontend/src/features/database/components/LinkRecordModal.tsx +61 -13
  177. package/frontend/src/features/database/components/RecordFormField.tsx +1 -1
  178. package/frontend/src/features/database/components/TableSidebar.tsx +0 -3
  179. package/frontend/src/features/database/components/TablesEmptyState.tsx +1 -1
  180. package/frontend/src/features/database/components/TemplatePreview.tsx +1 -2
  181. package/frontend/src/features/database/constants.ts +16 -28
  182. package/frontend/src/features/database/hooks/useCSVImport.ts +3 -2
  183. package/frontend/src/features/database/hooks/useRawSQL.ts +3 -2
  184. package/frontend/src/features/database/hooks/useTables.ts +5 -7
  185. package/frontend/src/features/database/pages/FunctionsPage.tsx +0 -5
  186. package/frontend/src/features/database/pages/IndexesPage.tsx +0 -5
  187. package/frontend/src/features/database/pages/PoliciesPage.tsx +0 -5
  188. package/frontend/src/features/database/pages/SQLEditorPage.tsx +2 -2
  189. package/frontend/src/features/database/pages/TriggersPage.tsx +0 -5
  190. package/frontend/src/features/database/services/advance.service.ts +1 -15
  191. package/frontend/src/features/database/services/record.service.ts +4 -20
  192. package/frontend/src/features/database/services/table.service.ts +1 -4
  193. package/frontend/src/features/database/templates/ai-chatbot.ts +6 -6
  194. package/frontend/src/features/database/templates/ecommerce-platform.ts +2 -2
  195. package/frontend/src/features/database/templates/instagram-clone.ts +10 -10
  196. package/frontend/src/features/database/templates/notion-clone.ts +8 -8
  197. package/frontend/src/features/database/templates/reddit-clone.ts +10 -10
  198. package/frontend/src/features/deployments/components/DeploymentRow.tsx +93 -0
  199. package/frontend/src/features/deployments/components/DeploymentsEmptyState.tsx +15 -0
  200. package/frontend/src/features/deployments/hooks/useDeployments.ts +157 -0
  201. package/frontend/src/features/deployments/pages/DeploymentsPage.tsx +318 -0
  202. package/frontend/src/features/deployments/services/deployments.service.ts +63 -0
  203. package/frontend/src/features/functions/components/FunctionRow.tsx +72 -72
  204. package/frontend/src/features/functions/components/FunctionsSidebar.tsx +56 -56
  205. package/frontend/src/features/functions/components/SecretRow.tsx +3 -3
  206. package/frontend/src/features/functions/components/index.ts +5 -5
  207. package/frontend/src/features/functions/hooks/useFunctions.ts +5 -4
  208. package/frontend/src/features/functions/hooks/useSecrets.ts +6 -9
  209. package/frontend/src/features/functions/pages/SecretsPage.tsx +118 -118
  210. package/frontend/src/features/functions/services/function.service.ts +8 -25
  211. package/frontend/src/features/functions/services/secret.service.ts +23 -41
  212. package/frontend/src/features/login/pages/CloudLoginPage.tsx +125 -118
  213. package/frontend/src/features/logs/components/LogDetailPanel.tsx +41 -0
  214. package/frontend/src/features/logs/components/LogsDataGrid.tsx +32 -1
  215. package/frontend/src/features/logs/components/index.ts +1 -0
  216. package/frontend/src/features/logs/pages/LogsPage.tsx +36 -6
  217. package/frontend/src/features/onboard/components/ApiCredentialsSection.tsx +59 -0
  218. package/frontend/src/features/onboard/components/ConnectionStringSection.tsx +180 -0
  219. package/frontend/src/features/onboard/components/McpConnectionSection.tsx +159 -0
  220. package/frontend/src/features/onboard/components/OnboardingController.tsx +68 -0
  221. package/frontend/src/features/onboard/components/OnboardingModal.tsx +121 -267
  222. package/frontend/src/features/onboard/components/ShowPasswordButton.tsx +21 -0
  223. package/frontend/src/features/onboard/components/index.ts +9 -4
  224. package/frontend/src/features/onboard/components/mcp/CursorDeeplinkGenerator.tsx +1 -1
  225. package/frontend/src/features/onboard/components/mcp/QoderDeeplinkGenerator.tsx +36 -0
  226. package/frontend/src/features/onboard/components/mcp/helpers.tsx +123 -98
  227. package/frontend/src/features/onboard/components/mcp/index.ts +4 -3
  228. package/frontend/src/features/onboard/index.ts +17 -13
  229. package/frontend/src/features/settings/pages/SettingsPage.tsx +349 -0
  230. package/frontend/src/features/visualizer/components/AuthNode.tsx +4 -4
  231. package/frontend/src/features/visualizer/components/SchemaVisualizer.tsx +21 -8
  232. package/frontend/src/features/visualizer/pages/VisualizerPage.tsx +10 -1
  233. package/frontend/src/index.css +249 -249
  234. package/frontend/src/lib/contexts/ModalContext.tsx +35 -0
  235. package/frontend/src/lib/hooks/useMetadata.ts +45 -1
  236. package/frontend/src/lib/hooks/useModal.tsx +2 -0
  237. package/frontend/src/lib/routing/AppRoutes.tsx +103 -99
  238. package/frontend/src/lib/services/metadata.service.ts +20 -3
  239. package/frontend/src/lib/utils/menuItems.ts +223 -207
  240. package/frontend/src/lib/utils/utils.ts +196 -196
  241. package/functions/server.ts +315 -315
  242. package/functions/worker-template.js +1 -1
  243. package/openapi/ai.yaml +115 -5
  244. package/openapi/auth.yaml +97 -17
  245. package/openapi/logs.yaml +0 -2
  246. package/openapi/metadata.yaml +0 -2
  247. package/openapi/records.yaml +21 -21
  248. package/openapi/tables.yaml +1 -2
  249. package/package.json +1 -1
  250. package/shared-schemas/package.json +1 -1
  251. package/shared-schemas/src/ai-api.schema.ts +251 -143
  252. package/shared-schemas/src/ai.schema.ts +63 -63
  253. package/shared-schemas/src/auth-api.schema.ts +34 -6
  254. package/shared-schemas/src/auth.schema.ts +17 -10
  255. package/shared-schemas/src/cloud-events.schema.ts +26 -0
  256. package/shared-schemas/src/deployments-api.schema.ts +55 -0
  257. package/shared-schemas/src/deployments.schema.ts +30 -0
  258. package/shared-schemas/src/docs.schema.ts +8 -2
  259. package/shared-schemas/src/email-api.schema.ts +30 -30
  260. package/shared-schemas/src/functions-api.schema.ts +13 -4
  261. package/shared-schemas/src/functions.schema.ts +1 -1
  262. package/shared-schemas/src/index.ts +22 -18
  263. package/shared-schemas/src/metadata.schema.ts +30 -4
  264. package/shared-schemas/src/secrets-api.schema.ts +44 -0
  265. package/shared-schemas/src/secrets.schema.ts +15 -0
  266. package/zeabur/README.md +13 -0
  267. package/zeabur/template.yml +20 -51
  268. package/backend/src/types/profile.ts +0 -55
  269. 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
- // 'auth-sdk': 'core-concepts/authentication/sdk.mdx',
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
- // 'auth-components-nextjs': 'core-concepts/authentication/ui-components/nextjs.mdx',
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 { functionUploadRequestSchema, functionUpdateRequestSchema } from '@insforge/shared-schemas';
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 = functionUploadRequestSchema.safeParse(req.body);
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 = functionUpdateRequestSchema.safeParse(req.body);
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
  );