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
@@ -1,301 +1,334 @@
1
- import { DatabaseManager } from '@/infra/database/database.manager.js';
2
- import { TokenManager } from '@/infra/security/token.manager.js';
3
- import { AIConfigService } from '@/services/ai/ai-config.service.js';
4
- import { isCloudEnvironment } from '@/utils/environment.js';
5
- import logger from '@/utils/logger.js';
6
- import { SecretService } from '@/services/secrets/secret.service.js';
7
- import { OAuthConfigService } from '@/services/auth/oauth-config.service.js';
8
- import { OAuthProvidersSchema, aiConfigurationInputSchema } from '@insforge/shared-schemas';
9
- import { z } from 'zod';
10
- import { AuthConfigService } from '@/services/auth/auth-config.service.js';
11
- import { fetchS3Config } from '@/utils/s3-config-loader.js';
12
-
13
- /**
14
- * Validates admin credentials are configured
15
- * Admin is authenticated via environment variables, not stored in DB
16
- */
17
- function ensureFirstAdmin(adminEmail: string, adminPassword: string): void {
18
- if (adminEmail && adminPassword) {
19
- logger.info(`āœ… Admin configured: ${adminEmail}`);
20
- } else {
21
- logger.warn('āš ļø Admin credentials not configured - check ADMIN_EMAIL and ADMIN_PASSWORD');
22
- }
23
- }
24
-
25
- /**
26
- * Seeds default AI configurations from S3 config
27
- */
28
- async function seedDefaultAIConfigs(): Promise<void> {
29
- const aiConfigService = AIConfigService.getInstance();
30
-
31
- const existingConfigs = await aiConfigService.findAll();
32
- if (existingConfigs.length) {
33
- return;
34
- }
35
-
36
- const defaultModels =
37
- await fetchS3Config<z.infer<typeof aiConfigurationInputSchema>[]>('default-ai-models.json');
38
-
39
- if (!defaultModels || defaultModels.length === 0) {
40
- logger.warn('āš ļø No default AI models configured - add via dashboard or check S3 config');
41
- return;
42
- }
43
-
44
- const parsed = aiConfigurationInputSchema.array().safeParse(defaultModels);
45
- if (!parsed.success) {
46
- logger.error('āŒ Invalid AI models configuration from S3', {
47
- error: parsed.error.message,
48
- });
49
- return;
50
- }
51
-
52
- const validatedModels = parsed.data;
53
- for (const model of validatedModels) {
54
- await aiConfigService.create(
55
- model.inputModality,
56
- model.outputModality,
57
- model.provider,
58
- model.modelId,
59
- model.systemPrompt
60
- );
61
- }
62
-
63
- logger.info(`āœ… Default AI models configured (${validatedModels.length} models)`);
64
- }
65
-
66
- /**
67
- * Seeds default auth configuration for cloud environments
68
- * Enables email verification with code-based verification method
69
- * Only inserts config if table is empty (first startup, never configured)
70
- */
71
- async function seedDefaultAuthConfig(): Promise<void> {
72
- const dbManager = DatabaseManager.getInstance();
73
- const pool = dbManager.getPool();
74
- const client = await pool.connect();
75
-
76
- try {
77
- const result = await client.query('SELECT COUNT(*) as count FROM _auth_configs');
78
- const hasConfig = result.rows.length > 0 && Number(result.rows[0].count) > 0;
79
-
80
- if (hasConfig) {
81
- const authConfigService = AuthConfigService.getInstance();
82
- const currentConfig = await authConfigService.getAuthConfig();
83
- logger.info(
84
- 'āœ… Email verification configured:',
85
- currentConfig.requireEmailVerification ? 'enabled' : 'disabled'
86
- );
87
- return;
88
- }
89
-
90
- // Table is empty - this is first startup, insert default cloud configuration
91
- // Note: Migration 016 will add verify_email_method, reset_password_method, sign_in_redirect_to
92
- // so we only insert fields that exist in migration 015
93
- await client.query(
94
- `INSERT INTO _auth_configs (
95
- require_email_verification,
96
- password_min_length,
97
- require_number,
98
- require_lowercase,
99
- require_uppercase,
100
- require_special_char
101
- ) VALUES ($1, $2, $3, $4, $5, $6)
102
- ON CONFLICT DO NOTHING`,
103
- [
104
- isCloudEnvironment(), // Enable email verification for cloud
105
- 6, // password_min_length
106
- false, // require_number
107
- false, // require_lowercase
108
- false, // require_uppercase
109
- false, // require_special_char
110
- ]
111
- );
112
-
113
- logger.info('āœ… Email verification enabled (cloud environment)');
114
- } catch (error) {
115
- logger.error('Failed to seed default auth config', {
116
- error: error instanceof Error ? error.message : String(error),
117
- });
118
- // Don't throw - this is not critical for app startup
119
- } finally {
120
- client.release();
121
- }
122
- }
123
-
124
- /**
125
- * Seeds default OAuth configurations for supported providers
126
- */
127
- async function seedDefaultOAuthConfigs(): Promise<void> {
128
- const oauthConfigService = OAuthConfigService.getInstance();
129
-
130
- try {
131
- // Check if OAuth configs already exist
132
- const existingConfigs = await oauthConfigService.getAllConfigs();
133
- const existingProviders = existingConfigs.map((config) => config.provider.toLowerCase());
134
-
135
- // Default providers to seed
136
- const defaultProviders: OAuthProvidersSchema[] = ['google', 'github'];
137
-
138
- for (const provider of defaultProviders) {
139
- if (!existingProviders.includes(provider)) {
140
- await oauthConfigService.createConfig({
141
- provider,
142
- useSharedKey: true,
143
- });
144
- logger.info(`āœ… Default ${provider} OAuth config created`);
145
- }
146
- }
147
- } catch (error) {
148
- logger.warn('Failed to seed OAuth configs', {
149
- error: error instanceof Error ? error.message : String(error),
150
- });
151
- // Don't throw error as OAuth configs are optional
152
- }
153
- }
154
-
155
- /**
156
- * Seeds OAuth configurations from local environment variables
157
- */
158
- async function seedLocalOAuthConfigs(): Promise<void> {
159
- const oauthConfigService = OAuthConfigService.getInstance();
160
-
161
- try {
162
- // Check if OAuth configs already exist
163
- const existingConfigs = await oauthConfigService.getAllConfigs();
164
- const existingProviders = existingConfigs.map((config) => config.provider.toLowerCase());
165
-
166
- // Environment variable mappings for OAuth providers
167
- const envMappings: Array<{
168
- provider: OAuthProvidersSchema;
169
- clientIdEnv: string;
170
- clientSecretEnv: string;
171
- }> = [
172
- {
173
- provider: 'google',
174
- clientIdEnv: 'GOOGLE_CLIENT_ID',
175
- clientSecretEnv: 'GOOGLE_CLIENT_SECRET',
176
- },
177
- {
178
- provider: 'github',
179
- clientIdEnv: 'GITHUB_CLIENT_ID',
180
- clientSecretEnv: 'GITHUB_CLIENT_SECRET',
181
- },
182
- {
183
- provider: 'discord',
184
- clientIdEnv: 'DISCORD_CLIENT_ID',
185
- clientSecretEnv: 'DISCORD_CLIENT_SECRET',
186
- },
187
- {
188
- provider: 'linkedin',
189
- clientIdEnv: 'LINKEDIN_CLIENT_ID',
190
- clientSecretEnv: 'LINKEDIN_CLIENT_SECRET',
191
- },
192
- {
193
- provider: 'microsoft',
194
- clientIdEnv: 'MICROSOFT_CLIENT_ID',
195
- clientSecretEnv: 'MICROSOFT_CLIENT_SECRET',
196
- },
197
- ];
198
-
199
- for (const { provider, clientIdEnv, clientSecretEnv } of envMappings) {
200
- const clientId = process.env[clientIdEnv];
201
- const clientSecret = process.env[clientSecretEnv];
202
-
203
- if (clientId && clientSecret && !existingProviders.includes(provider)) {
204
- await oauthConfigService.createConfig({
205
- provider,
206
- clientId,
207
- clientSecret,
208
- useSharedKey: false,
209
- });
210
- logger.info(`āœ… ${provider} OAuth config loaded from environment variables`);
211
- }
212
- }
213
- } catch (error) {
214
- logger.warn('Failed to seed local OAuth configs', {
215
- error: error instanceof Error ? error.message : String(error),
216
- });
217
- }
218
- }
219
-
220
- // Create api key, admin user, and default AI configs
221
- export async function seedBackend(): Promise<void> {
222
- const secretService = SecretService.getInstance();
223
-
224
- const dbManager = DatabaseManager.getInstance();
225
-
226
- const adminEmail = process.env.ADMIN_EMAIL || 'admin@example.com';
227
- const adminPassword = process.env.ADMIN_PASSWORD || 'change-this-password';
228
-
229
- try {
230
- logger.info(`\nšŸš€ Insforge Backend Starting...`);
231
-
232
- // Validate admin credentials are configured
233
- ensureFirstAdmin(adminEmail, adminPassword);
234
-
235
- // Initialize API key (from env or generate)
236
- const apiKey = await secretService.initializeApiKey();
237
-
238
- // Get database stats
239
- const tables = await dbManager.getUserTables();
240
-
241
- logger.info(`āœ… Database connected to PostgreSQL`, {
242
- host: process.env.POSTGRES_HOST || 'localhost',
243
- port: process.env.POSTGRES_PORT || '5432',
244
- database: process.env.POSTGRES_DB || 'insforge',
245
- });
246
- // Database connection info is already logged above
247
-
248
- if (tables.length) {
249
- logger.info(`āœ… Found ${tables.length} user tables`);
250
- }
251
-
252
- // seed default configs for cloud environment
253
- if (isCloudEnvironment()) {
254
- await seedDefaultOAuthConfigs();
255
- await seedDefaultAIConfigs();
256
- await seedDefaultAuthConfig();
257
- } else {
258
- await seedLocalOAuthConfigs();
259
- }
260
-
261
- // Initialize reserved secrets for edge functions
262
- // Add INSFORGE_INTERNAL_URL for Deno-to-backend container communication
263
- const insforgInternalUrl = 'http://insforge:7130';
264
- const existingInternalUrlSecret = await secretService.getSecretByKey('INSFORGE_INTERNAL_URL');
265
-
266
- if (existingInternalUrlSecret === null) {
267
- await secretService.createSecret({
268
- key: 'INSFORGE_INTERNAL_URL',
269
- isReserved: true,
270
- value: insforgInternalUrl,
271
- });
272
- logger.info('āœ… INSFORGE_INTERNAL_URL secret initialized');
273
- }
274
-
275
- // Add ANON_KEY for public edge function access
276
- const existingAnonKeySecret = await secretService.getSecretByKey('ANON_KEY');
277
-
278
- if (existingAnonKeySecret === null) {
279
- const tokenManager = TokenManager.getInstance();
280
- const anonToken = tokenManager.generateAnonToken();
281
-
282
- await secretService.createSecret({
283
- key: 'ANON_KEY',
284
- isReserved: true,
285
- value: anonToken,
286
- });
287
- logger.info('āœ… ANON_KEY secret initialized');
288
- }
289
-
290
- logger.info(`API key generated: ${apiKey}`);
291
- logger.info(`Setup complete:
292
- - Save this API key for your apps!
293
- - Dashboard: http://localhost:7131
294
- - API: http://localhost:7130/api
295
- `);
296
- } catch (error) {
297
- logger.error('Error during setup', {
298
- error: error instanceof Error ? error.message : String(error),
299
- });
300
- }
301
- }
1
+ import bcrypt from 'bcryptjs';
2
+ import { DatabaseManager } from '@/infra/database/database.manager.js';
3
+ import { TokenManager } from '@/infra/security/token.manager.js';
4
+ import { AIConfigService } from '@/services/ai/ai-config.service.js';
5
+ import { isCloudEnvironment } from '@/utils/environment.js';
6
+ import logger from '@/utils/logger.js';
7
+ import { SecretService } from '@/services/secrets/secret.service.js';
8
+ import { OAuthConfigService } from '@/services/auth/oauth-config.service.js';
9
+ import { OAuthProvidersSchema, aiConfigurationInputSchema } from '@insforge/shared-schemas';
10
+ import { z } from 'zod';
11
+ import { AuthConfigService } from '@/services/auth/auth-config.service.js';
12
+ import { fetchS3Config } from '@/utils/s3-config-loader.js';
13
+ import { ADMIN_ID } from '@/utils/constants.js';
14
+
15
+ /**
16
+ * Seeds the admin user if it doesn't exist in the database
17
+ * Creates an admin user with is_project_admin = true
18
+ */
19
+ async function seedAdminUser(adminEmail: string, adminPassword: string): Promise<void> {
20
+ if (!adminEmail || !adminPassword) {
21
+ logger.warn('āš ļø Admin credentials not configured - check ADMIN_EMAIL and ADMIN_PASSWORD');
22
+ return;
23
+ }
24
+
25
+ const dbManager = DatabaseManager.getInstance();
26
+ const pool = dbManager.getPool();
27
+ const client = await pool.connect();
28
+
29
+ try {
30
+ // Check if admin user already exists
31
+ const existingAdmin = await client.query('SELECT id FROM auth.users WHERE id = $1', [ADMIN_ID]);
32
+
33
+ if (existingAdmin.rows.length > 0) {
34
+ logger.info(`āœ… Admin configured: ${adminEmail}`);
35
+ return;
36
+ }
37
+
38
+ // Hash password and create admin user
39
+ const hashedPassword = await bcrypt.hash(adminPassword, 10);
40
+ const profile = JSON.stringify({ name: 'Administrator' });
41
+
42
+ await client.query(
43
+ `INSERT INTO auth.users (id, email, password, profile, email_verified, is_project_admin, is_anonymous, created_at, updated_at)
44
+ VALUES ($1, $2, $3, $4::jsonb, true, true, false, NOW(), NOW())
45
+ ON CONFLICT (id) DO NOTHING`,
46
+ [ADMIN_ID, adminEmail, hashedPassword, profile]
47
+ );
48
+
49
+ logger.info(`āœ… Admin user seeded: ${adminEmail}`);
50
+ } catch (error) {
51
+ logger.error('Failed to seed admin user', {
52
+ error: error instanceof Error ? error.message : String(error),
53
+ });
54
+ // Don't throw - this is not critical for app startup if admin already exists
55
+ } finally {
56
+ client.release();
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Seeds default AI configurations from S3 config
62
+ */
63
+ async function seedDefaultAIConfigs(): Promise<void> {
64
+ const aiConfigService = AIConfigService.getInstance();
65
+
66
+ const existingConfigs = await aiConfigService.findAll();
67
+ if (existingConfigs.length) {
68
+ return;
69
+ }
70
+
71
+ const defaultModels =
72
+ await fetchS3Config<z.infer<typeof aiConfigurationInputSchema>[]>('default-ai-models.json');
73
+
74
+ if (!defaultModels || defaultModels.length === 0) {
75
+ logger.warn('āš ļø No default AI models configured - add via dashboard or check S3 config');
76
+ return;
77
+ }
78
+
79
+ const parsed = aiConfigurationInputSchema.array().safeParse(defaultModels);
80
+ if (!parsed.success) {
81
+ logger.error('āŒ Invalid AI models configuration from S3', {
82
+ error: parsed.error.message,
83
+ });
84
+ return;
85
+ }
86
+
87
+ const validatedModels = parsed.data;
88
+ for (const model of validatedModels) {
89
+ await aiConfigService.create(
90
+ model.inputModality,
91
+ model.outputModality,
92
+ model.provider,
93
+ model.modelId,
94
+ model.systemPrompt
95
+ );
96
+ }
97
+
98
+ logger.info(`āœ… Default AI models configured (${validatedModels.length} models)`);
99
+ }
100
+
101
+ /**
102
+ * Seeds default auth configuration for cloud environments
103
+ * Enables email verification with code-based verification method
104
+ * Only inserts config if table is empty (first startup, never configured)
105
+ */
106
+ async function seedDefaultAuthConfig(): Promise<void> {
107
+ const dbManager = DatabaseManager.getInstance();
108
+ const pool = dbManager.getPool();
109
+ const client = await pool.connect();
110
+
111
+ try {
112
+ const result = await client.query('SELECT COUNT(*) as count FROM auth.configs');
113
+ const hasConfig = result.rows.length > 0 && Number(result.rows[0].count) > 0;
114
+
115
+ if (hasConfig) {
116
+ const authConfigService = AuthConfigService.getInstance();
117
+ const currentConfig = await authConfigService.getAuthConfig();
118
+ logger.info(
119
+ 'āœ… Email verification configured:',
120
+ currentConfig.requireEmailVerification ? 'enabled' : 'disabled'
121
+ );
122
+ return;
123
+ }
124
+
125
+ // Table is empty - this is first startup, insert default cloud configuration
126
+ await client.query(
127
+ `INSERT INTO auth.configs (
128
+ require_email_verification,
129
+ password_min_length,
130
+ require_number,
131
+ require_lowercase,
132
+ require_uppercase,
133
+ require_special_char
134
+ ) VALUES ($1, $2, $3, $4, $5, $6)
135
+ ON CONFLICT DO NOTHING`,
136
+ [
137
+ isCloudEnvironment(), // Enable email verification for cloud
138
+ 6, // password_min_length
139
+ false, // require_number
140
+ false, // require_lowercase
141
+ false, // require_uppercase
142
+ false, // require_special_char
143
+ ]
144
+ );
145
+
146
+ logger.info('āœ… Email verification enabled (cloud environment)');
147
+ } catch (error) {
148
+ logger.error('Failed to seed default auth config', {
149
+ error: error instanceof Error ? error.message : String(error),
150
+ });
151
+ // Don't throw - this is not critical for app startup
152
+ } finally {
153
+ client.release();
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Seeds default OAuth configurations for supported providers
159
+ */
160
+ async function seedDefaultOAuthConfigs(): Promise<void> {
161
+ const oauthConfigService = OAuthConfigService.getInstance();
162
+
163
+ try {
164
+ // Check if OAuth configs already exist
165
+ const existingConfigs = await oauthConfigService.getAllConfigs();
166
+ const existingProviders = existingConfigs.map((config) => config.provider.toLowerCase());
167
+
168
+ // Default providers to seed
169
+ const defaultProviders: OAuthProvidersSchema[] = ['google', 'github'];
170
+
171
+ for (const provider of defaultProviders) {
172
+ if (!existingProviders.includes(provider)) {
173
+ await oauthConfigService.createConfig({
174
+ provider,
175
+ useSharedKey: true,
176
+ });
177
+ logger.info(`āœ… Default ${provider} OAuth config created`);
178
+ }
179
+ }
180
+ } catch (error) {
181
+ logger.warn('Failed to seed OAuth configs', {
182
+ error: error instanceof Error ? error.message : String(error),
183
+ });
184
+ // Don't throw error as OAuth configs are optional
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Seeds OAuth configurations from local environment variables
190
+ */
191
+ async function seedLocalOAuthConfigs(): Promise<void> {
192
+ const oauthConfigService = OAuthConfigService.getInstance();
193
+
194
+ try {
195
+ // Check if OAuth configs already exist
196
+ const existingConfigs = await oauthConfigService.getAllConfigs();
197
+ const existingProviders = existingConfigs.map((config) => config.provider.toLowerCase());
198
+
199
+ // Environment variable mappings for OAuth providers
200
+ const envMappings: Array<{
201
+ provider: OAuthProvidersSchema;
202
+ clientIdEnv: string;
203
+ clientSecretEnv: string;
204
+ }> = [
205
+ {
206
+ provider: 'google',
207
+ clientIdEnv: 'GOOGLE_CLIENT_ID',
208
+ clientSecretEnv: 'GOOGLE_CLIENT_SECRET',
209
+ },
210
+ {
211
+ provider: 'github',
212
+ clientIdEnv: 'GITHUB_CLIENT_ID',
213
+ clientSecretEnv: 'GITHUB_CLIENT_SECRET',
214
+ },
215
+ {
216
+ provider: 'discord',
217
+ clientIdEnv: 'DISCORD_CLIENT_ID',
218
+ clientSecretEnv: 'DISCORD_CLIENT_SECRET',
219
+ },
220
+ {
221
+ provider: 'linkedin',
222
+ clientIdEnv: 'LINKEDIN_CLIENT_ID',
223
+ clientSecretEnv: 'LINKEDIN_CLIENT_SECRET',
224
+ },
225
+ {
226
+ provider: 'microsoft',
227
+ clientIdEnv: 'MICROSOFT_CLIENT_ID',
228
+ clientSecretEnv: 'MICROSOFT_CLIENT_SECRET',
229
+ },
230
+ ];
231
+
232
+ for (const { provider, clientIdEnv, clientSecretEnv } of envMappings) {
233
+ const clientId = process.env[clientIdEnv];
234
+ const clientSecret = process.env[clientSecretEnv];
235
+
236
+ if (clientId && clientSecret && !existingProviders.includes(provider)) {
237
+ await oauthConfigService.createConfig({
238
+ provider,
239
+ clientId,
240
+ clientSecret,
241
+ useSharedKey: false,
242
+ });
243
+ logger.info(`āœ… ${provider} OAuth config loaded from environment variables`);
244
+ }
245
+ }
246
+ } catch (error) {
247
+ logger.warn('Failed to seed local OAuth configs', {
248
+ error: error instanceof Error ? error.message : String(error),
249
+ });
250
+ }
251
+ }
252
+
253
+ // Create api key, admin user, and default AI configs
254
+ export async function seedBackend(): Promise<void> {
255
+ const secretService = SecretService.getInstance();
256
+
257
+ const dbManager = DatabaseManager.getInstance();
258
+
259
+ const adminEmail = process.env.ADMIN_EMAIL || 'admin@example.com';
260
+ const adminPassword = process.env.ADMIN_PASSWORD || 'change-this-password';
261
+
262
+ try {
263
+ logger.info(`\nšŸš€ Insforge Backend Starting...`);
264
+
265
+ // Seed admin user if not exists
266
+ await seedAdminUser(adminEmail, adminPassword);
267
+
268
+ // Initialize API key (from env or generate)
269
+ const apiKey = await secretService.initializeApiKey();
270
+
271
+ // Get database stats
272
+ const tables = await dbManager.getUserTables();
273
+
274
+ logger.info(`āœ… Database connected to PostgreSQL`, {
275
+ host: process.env.POSTGRES_HOST || 'localhost',
276
+ port: process.env.POSTGRES_PORT || '5432',
277
+ database: process.env.POSTGRES_DB || 'insforge',
278
+ });
279
+ // Database connection info is already logged above
280
+
281
+ if (tables.length) {
282
+ logger.info(`āœ… Found ${tables.length} user tables`);
283
+ }
284
+
285
+ // seed default configs for cloud environment
286
+ if (isCloudEnvironment()) {
287
+ await seedDefaultOAuthConfigs();
288
+ await seedDefaultAIConfigs();
289
+ await seedDefaultAuthConfig();
290
+ } else {
291
+ await seedLocalOAuthConfigs();
292
+ }
293
+
294
+ // Initialize reserved secrets for edge functions
295
+ // Add INSFORGE_INTERNAL_URL for Deno-to-backend container communication
296
+ const insforgInternalUrl = 'http://insforge:7130';
297
+ const existingInternalUrlSecret = await secretService.getSecretByKey('INSFORGE_INTERNAL_URL');
298
+
299
+ if (existingInternalUrlSecret === null) {
300
+ await secretService.createSecret({
301
+ key: 'INSFORGE_INTERNAL_URL',
302
+ isReserved: true,
303
+ value: insforgInternalUrl,
304
+ });
305
+ logger.info('āœ… INSFORGE_INTERNAL_URL secret initialized');
306
+ }
307
+
308
+ // Add ANON_KEY for public edge function access
309
+ const existingAnonKeySecret = await secretService.getSecretByKey('ANON_KEY');
310
+
311
+ if (existingAnonKeySecret === null) {
312
+ const tokenManager = TokenManager.getInstance();
313
+ const anonToken = tokenManager.generateAnonToken();
314
+
315
+ await secretService.createSecret({
316
+ key: 'ANON_KEY',
317
+ isReserved: true,
318
+ value: anonToken,
319
+ });
320
+ logger.info('āœ… ANON_KEY secret initialized');
321
+ }
322
+
323
+ logger.info(`API key generated: ${apiKey}`);
324
+ logger.info(`Setup complete:
325
+ - Save this API key for your apps!
326
+ - Dashboard: http://localhost:7131
327
+ - API: http://localhost:7130/api
328
+ `);
329
+ } catch (error) {
330
+ logger.error('Error during setup', {
331
+ error: error instanceof Error ? error.message : String(error),
332
+ });
333
+ }
334
+ }