insforge 0.3.2 → 1.2.10

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 (507) hide show
  1. package/.claude-plugin/marketplace.json +20 -0
  2. package/.cursor/rules/cursor-rules.mdc +94 -0
  3. package/.dockerignore +3 -0
  4. package/.env.example +33 -4
  5. package/.github/ISSUE_TEMPLATE/bug_report.yml +13 -60
  6. package/.github/ISSUE_TEMPLATE/config.yml +2 -2
  7. package/.github/ISSUE_TEMPLATE/feature_request.yml +10 -63
  8. package/.github/PULL_REQUEST_TEMPLATE.md +7 -0
  9. package/.github/workflows/build-image.yml +2 -1
  10. package/.github/workflows/e2e.yml +63 -0
  11. package/CHANGELOG.md +41 -0
  12. package/CLAUDE_PLUGIN.md +104 -0
  13. package/CODE_OF_CONDUCT.md +128 -0
  14. package/CONTRIBUTING.md +1 -1
  15. package/Dockerfile +4 -1
  16. package/README.md +66 -18
  17. package/assets/mcpInstallv2.png +0 -0
  18. package/assets/sampleResponse.png +0 -0
  19. package/auth/index.html +13 -0
  20. package/auth/package.json +28 -0
  21. package/auth/public/favicon.ico +0 -0
  22. package/auth/src/App.tsx +33 -0
  23. package/auth/src/components/ErrorCard.tsx +37 -0
  24. package/auth/src/components/Layout.tsx +13 -0
  25. package/auth/src/index.css +19 -0
  26. package/auth/src/lib/broadcastService.ts +115 -0
  27. package/auth/src/lib/utils.ts +11 -0
  28. package/auth/src/main.tsx +22 -0
  29. package/auth/src/pages/ForgotPasswordPage.tsx +11 -0
  30. package/auth/src/pages/ResetPasswordPage.tsx +11 -0
  31. package/auth/src/pages/SignInPage.tsx +57 -0
  32. package/auth/src/pages/SignUpPage.tsx +57 -0
  33. package/auth/src/pages/VerifyEmailPage.tsx +20 -0
  34. package/auth/src/vite-env.d.ts +10 -0
  35. package/auth/tsconfig.json +32 -0
  36. package/auth/tsconfig.node.json +11 -0
  37. package/auth/vite.config.ts +25 -0
  38. package/backend/package.json +9 -9
  39. package/backend/src/api/{middleware → middlewares}/auth.ts +8 -9
  40. package/backend/src/api/middlewares/rate-limiters.ts +127 -0
  41. package/backend/src/api/routes/{ai.ts → ai/index.routes.ts} +20 -24
  42. package/backend/src/api/routes/auth/index.routes.ts +570 -0
  43. package/backend/src/api/routes/auth/oauth.routes.ts +448 -0
  44. package/backend/src/api/routes/{database.advance.ts → database/advance.routes.ts} +107 -65
  45. package/backend/src/api/routes/database/index.routes.ts +13 -0
  46. package/backend/src/api/routes/{database.records.ts → database/records.routes.ts} +22 -8
  47. package/backend/src/api/routes/{database.tables.ts → database/tables.routes.ts} +20 -23
  48. package/backend/src/api/routes/docs/index.routes.ts +76 -0
  49. package/backend/src/api/routes/functions/index.routes.ts +188 -0
  50. package/backend/src/api/routes/{logs.ts → logs/index.routes.ts} +25 -30
  51. package/backend/src/api/routes/{metadata.ts → metadata/index.routes.ts} +21 -31
  52. package/backend/src/api/routes/{secrets.ts → secrets/index.routes.ts} +27 -22
  53. package/backend/src/api/routes/{storage.ts → storage/index.routes.ts} +34 -53
  54. package/backend/src/api/routes/usage/index.routes.ts +89 -0
  55. package/backend/src/infra/config/app.config.ts +51 -0
  56. package/backend/src/{core/database/manager.ts → infra/database/database.manager.ts} +76 -85
  57. package/backend/src/infra/database/migrations/013_create-auth-schema-functions.sql +44 -0
  58. package/backend/src/infra/database/migrations/014_add-updated-at-trigger-user-table.sql +8 -0
  59. package/backend/src/infra/database/migrations/015_create-auth-config-and-email-otp-tables.sql +60 -0
  60. package/backend/src/infra/database/migrations/016_update-auth-config-and-email-otp.sql +24 -0
  61. package/backend/src/{core/secrets/encryption.ts → infra/security/encryption.manager.ts} +3 -2
  62. package/backend/src/infra/security/token.manager.ts +125 -0
  63. package/backend/src/{core/socket/socket.ts → infra/socket/socket.manager.ts} +15 -15
  64. package/backend/src/providers/ai/openrouter.provider.ts +377 -0
  65. package/backend/src/providers/email/base.provider.ts +41 -0
  66. package/backend/src/providers/email/cloud.provider.ts +187 -0
  67. package/backend/src/{core/logs/providers → providers/logs}/base.provider.ts +11 -11
  68. package/backend/src/{core/logs/providers → providers/logs}/cloudwatch.provider.ts +61 -38
  69. package/backend/src/providers/logs/local.provider.ts +185 -0
  70. package/backend/src/providers/oauth/base.provider.ts +29 -0
  71. package/backend/src/providers/oauth/discord.provider.ts +195 -0
  72. package/backend/src/providers/oauth/facebook.provider.ts +194 -0
  73. package/backend/src/providers/oauth/github.provider.ts +208 -0
  74. package/backend/src/providers/oauth/google.provider.ts +249 -0
  75. package/backend/src/providers/oauth/index.ts +7 -0
  76. package/backend/src/providers/oauth/linkedin.provider.ts +240 -0
  77. package/backend/src/providers/oauth/microsoft.provider.ts +169 -0
  78. package/backend/src/providers/oauth/x.provider.ts +202 -0
  79. package/backend/src/providers/storage/base.provider.ts +29 -0
  80. package/backend/src/providers/storage/local.provider.ts +103 -0
  81. package/backend/src/providers/storage/s3.provider.ts +313 -0
  82. package/backend/src/server.ts +70 -74
  83. package/backend/src/{core/ai/config.ts → services/ai/ai-config.service.ts} +19 -24
  84. package/backend/src/services/ai/ai-model.service.ts +60 -0
  85. package/backend/src/{core/ai/usage.ts → services/ai/ai-usage.service.ts} +28 -35
  86. package/backend/src/{core/ai/chat.ts → services/ai/chat-completion.service.ts} +37 -24
  87. package/backend/src/services/ai/helpers.ts +64 -0
  88. package/backend/src/{core/ai/image.ts → services/ai/image-generation.service.ts} +17 -19
  89. package/backend/src/services/ai/index.ts +13 -0
  90. package/backend/src/services/auth/auth-config.service.ts +250 -0
  91. package/backend/src/services/auth/auth-otp.service.ts +424 -0
  92. package/backend/src/services/auth/auth.service.ts +1136 -0
  93. package/backend/src/services/auth/index.ts +4 -0
  94. package/backend/src/{core/auth/oauth.ts → services/auth/oauth-config.service.ts} +106 -52
  95. package/backend/src/{core/database/advance.ts → services/database/database-advance.service.ts} +97 -131
  96. package/backend/src/services/database/database-table.service.ts +811 -0
  97. package/backend/src/services/email/email.service.ts +75 -0
  98. package/backend/src/{core/functions/functions.ts → services/functions/function.service.ts} +95 -88
  99. package/backend/src/{core/logs/audit.ts → services/logs/audit.service.ts} +92 -75
  100. package/backend/src/services/logs/log.service.ts +73 -0
  101. package/backend/src/{core/secrets/secrets.ts → services/secrets/secret.service.ts} +48 -66
  102. package/backend/src/services/storage/storage.service.ts +617 -0
  103. package/backend/src/services/usage/usage.service.ts +149 -0
  104. package/backend/src/types/auth.ts +66 -2
  105. package/backend/src/types/email.ts +8 -0
  106. package/backend/src/types/error-constants.ts +4 -0
  107. package/backend/src/types/logs.ts +0 -29
  108. package/backend/src/{core/socket/types.ts → types/socket.ts} +5 -6
  109. package/backend/src/utils/environment.ts +9 -3
  110. package/backend/src/utils/logger.ts +20 -2
  111. package/backend/src/utils/seed.ts +150 -57
  112. package/backend/src/utils/sql-parser.ts +1 -1
  113. package/backend/src/utils/utils.ts +114 -0
  114. package/backend/src/utils/validations.ts +40 -4
  115. package/backend/tests/local/test-ai-config.sh +129 -0
  116. package/backend/tests/local/test-ai-usage.sh +80 -0
  117. package/backend/tests/local/test-auth-router.sh +1 -1
  118. package/backend/tests/local/test-e2e.sh +1 -1
  119. package/backend/tests/local/test-functions.sh +123 -0
  120. package/backend/tests/local/test-logs.sh +132 -0
  121. package/backend/tests/local/test-public-bucket.sh +3 -3
  122. package/backend/tests/local/test-secrets.sh +14 -12
  123. package/backend/tests/local/test-traditional-rest.sh +2 -2
  124. package/backend/tests/manual/test-rawsql-modes.sh +244 -0
  125. package/backend/tests/test-config.sh +37 -1
  126. package/backend/tests/unit/cloud-token.test.ts +48 -0
  127. package/backend/tests/unit/constant.test.ts +8 -0
  128. package/backend/tests/unit/email.test.ts +372 -0
  129. package/backend/tests/unit/environment.test.ts +59 -0
  130. package/backend/tests/unit/helpers.test.ts +63 -0
  131. package/backend/tests/unit/logger.test.ts +22 -0
  132. package/backend/tests/unit/rate-limit.test.ts +154 -0
  133. package/backend/tests/unit/response.test.ts +58 -0
  134. package/backend/tests/unit/sql-parser.test.ts +74 -0
  135. package/backend/tests/unit/uuid.test.ts +21 -0
  136. package/backend/tests/unit/validations.test.ts +80 -0
  137. package/backend/tsconfig.json +1 -1
  138. package/backend/vitest.config.ts +11 -0
  139. package/claude-plugin/.claude-plugin/plugin.json +24 -0
  140. package/claude-plugin/README.md +133 -0
  141. package/claude-plugin/skills/insforge-schema-patterns/SKILL.md +270 -0
  142. package/docker-compose.prod.yml +60 -4
  143. package/docker-compose.yml +65 -4
  144. package/docker-init/db/db-init.sql +6 -34
  145. package/docker-init/logs/vector.yml +236 -0
  146. package/docs/README.md +44 -0
  147. package/docs/changelog.mdx +67 -0
  148. package/docs/core-concepts/ai/architecture.mdx +373 -0
  149. package/docs/core-concepts/ai/sdk.mdx +213 -0
  150. package/docs/core-concepts/authentication/architecture.mdx +278 -0
  151. package/docs/core-concepts/authentication/sdk.mdx +414 -0
  152. package/docs/core-concepts/authentication/ui-components/customization.mdx +529 -0
  153. package/docs/core-concepts/authentication/ui-components/nextjs.mdx +221 -0
  154. package/docs/core-concepts/authentication/ui-components/react-router.mdx +184 -0
  155. package/docs/core-concepts/authentication/ui-components/react.mdx +129 -0
  156. package/docs/core-concepts/database/architecture.mdx +256 -0
  157. package/docs/core-concepts/database/sdk.mdx +382 -0
  158. package/docs/core-concepts/functions/architecture.mdx +105 -0
  159. package/docs/core-concepts/functions/sdk.mdx +184 -0
  160. package/docs/core-concepts/storage/architecture.mdx +243 -0
  161. package/docs/core-concepts/storage/sdk.mdx +253 -0
  162. package/docs/deployment/README.md +94 -0
  163. package/docs/deployment/deploy-to-aws-ec2.md +565 -0
  164. package/docs/deployment/deploy-to-azure-virtual-machines.md +313 -0
  165. package/docs/deployment/deploy-to-google-cloud-compute-engine.md +613 -0
  166. package/docs/deployment/deploy-to-render.md +441 -0
  167. package/docs/docs.json +210 -0
  168. package/docs/examples/framework-guides/nextjs.mdx +131 -0
  169. package/docs/examples/framework-guides/nuxt.mdx +165 -0
  170. package/docs/examples/framework-guides/react.mdx +165 -0
  171. package/docs/examples/framework-guides/svelte.mdx +153 -0
  172. package/docs/examples/framework-guides/vue.mdx +159 -0
  173. package/docs/examples/overview.mdx +67 -0
  174. package/docs/favicon.svg +19 -0
  175. package/docs/images/changelog/nov-2025/auth-components.webp +0 -0
  176. package/docs/images/changelog/nov-2025/database-metadata.webp +0 -0
  177. package/docs/images/changelog/nov-2025/quickstart-prompts.webp +0 -0
  178. package/docs/images/changelog/nov-2025/sql-editor.webp +0 -0
  179. package/docs/images/changelog/nov-2025/usage-page.webp +0 -0
  180. package/docs/images/changelog/october-2025/csv-upload.webp +0 -0
  181. package/docs/images/changelog/october-2025/logs-feature.webp +0 -0
  182. package/docs/images/changelog/october-2025/oauth-providers.webp +0 -0
  183. package/docs/images/checks-passed.png +0 -0
  184. package/docs/images/dashboard-connect-expanded.png +0 -0
  185. package/docs/images/dashboard-connect.png +0 -0
  186. package/docs/images/hero-dark.png +0 -0
  187. package/docs/images/hero-light.png +0 -0
  188. package/docs/images/icons/ai.svg +4 -0
  189. package/docs/images/icons/auth.svg +1 -0
  190. package/docs/images/icons/database.svg +1 -0
  191. package/docs/images/icons/function.svg +1 -0
  192. package/docs/images/icons/storage.svg +1 -0
  193. package/docs/images/logos/nextjs.svg +4 -0
  194. package/docs/images/logos/nuxt.svg +4 -0
  195. package/docs/images/logos/react.svg +5 -0
  196. package/docs/images/logos/svelte.svg +4 -0
  197. package/docs/images/logos/vue.svg +5 -0
  198. package/docs/images/mcp-install.png +0 -0
  199. package/docs/images/onboarding-mcp.png +0 -0
  200. package/docs/insforge-instructions-sdk.md +55 -374
  201. package/docs/introduction.mdx +45 -0
  202. package/docs/logo/dark.svg +22 -0
  203. package/docs/logo/light.svg +20 -0
  204. package/docs/partnership.mdx +647 -0
  205. package/docs/quickstart.mdx +83 -0
  206. package/docs/showcase/2048-arena.png +0 -0
  207. package/docs/showcase/framegen-cloud.png +0 -0
  208. package/docs/showcase/line-connect-race.png +0 -0
  209. package/docs/showcase/moment-vibe.png +0 -0
  210. package/docs/showcase/national-flags.png +0 -0
  211. package/docs/showcase/pokemon-vibe.png +0 -0
  212. package/docs/showcase/pure-browse-buy.png +0 -0
  213. package/docs/showcase.mdx +52 -0
  214. package/docs/snippets/sdk-installation.mdx +22 -0
  215. package/docs/snippets/service-icons.mdx +27 -0
  216. package/eslint.config.js +10 -3
  217. package/frontend/package.json +10 -4
  218. package/frontend/src/App.tsx +13 -82
  219. package/frontend/src/assets/icons/connected.svg +3 -0
  220. package/frontend/src/assets/icons/loader.svg +9 -0
  221. package/frontend/src/assets/logos/apple.svg +4 -0
  222. package/frontend/src/assets/logos/discord.svg +1 -1
  223. package/frontend/src/assets/logos/facebook.svg +3 -0
  224. package/frontend/src/assets/logos/instagram.svg +2 -0
  225. package/frontend/src/assets/logos/linkedin.svg +3 -0
  226. package/frontend/src/assets/logos/microsoft.svg +1 -0
  227. package/frontend/src/assets/logos/spotify.svg +17 -0
  228. package/frontend/src/assets/logos/tiktok.svg +6 -0
  229. package/frontend/src/assets/logos/x.svg +3 -0
  230. package/frontend/src/components/Checkbox.tsx +27 -29
  231. package/frontend/src/components/CodeBlock.tsx +55 -2
  232. package/frontend/src/components/CodeEditor.tsx +92 -0
  233. package/frontend/src/components/ConfirmDialog.tsx +1 -1
  234. package/frontend/src/components/ConnectCTA.tsx +38 -0
  235. package/frontend/src/components/CopyButton.tsx +52 -15
  236. package/frontend/src/components/ErrorState.tsx +1 -2
  237. package/frontend/src/components/FeatureSidebar.tsx +6 -6
  238. package/frontend/src/components/FeatureSidebarItem.tsx +2 -2
  239. package/frontend/src/components/JsonHighlight.tsx +21 -9
  240. package/frontend/src/components/ProjectInfoModal.tsx +128 -0
  241. package/frontend/src/components/PromptDialog.tsx +1 -4
  242. package/frontend/src/components/SearchInput.tsx +1 -2
  243. package/frontend/src/components/Stepper.tsx +53 -0
  244. package/frontend/src/components/ThemeToggle.tsx +3 -3
  245. package/frontend/src/components/datagrid/DataGrid.tsx +25 -32
  246. package/frontend/src/components/datagrid/cell-editors/DateCellEditor.tsx +1 -2
  247. package/frontend/src/components/datagrid/cell-editors/JsonCellEditor.tsx +2 -4
  248. package/frontend/src/components/datagrid/index.ts +23 -0
  249. package/frontend/src/components/index.ts +23 -30
  250. package/frontend/src/components/layout/AppHeader.tsx +133 -92
  251. package/frontend/src/components/layout/AppSidebar.tsx +80 -170
  252. package/frontend/src/components/layout/Layout.tsx +12 -23
  253. package/frontend/src/components/layout/PrimaryMenu.tsx +187 -0
  254. package/frontend/src/components/layout/SecondaryMenu.tsx +70 -0
  255. package/frontend/src/components/layout/index.ts +5 -0
  256. package/frontend/src/components/radix/Tooltip.tsx +24 -13
  257. package/frontend/src/components/radix/index.ts +22 -0
  258. package/frontend/src/features/ai/components/AIConfigCard.tsx +129 -83
  259. package/frontend/src/features/ai/components/AIEmptyState.tsx +12 -7
  260. package/frontend/src/features/ai/components/ModalityFilterSidebar.tsx +101 -0
  261. package/frontend/src/features/ai/components/ModelSelectionDialog.tsx +135 -0
  262. package/frontend/src/features/ai/components/ModelSelectionGrid.tsx +51 -0
  263. package/frontend/src/features/ai/components/SystemPromptDialog.tsx +118 -0
  264. package/frontend/src/features/ai/components/index.ts +6 -0
  265. package/frontend/src/features/ai/helpers.ts +57 -71
  266. package/frontend/src/features/ai/hooks/useAIConfigs.ts +39 -113
  267. package/frontend/src/features/ai/hooks/useAIUsage.ts +0 -2
  268. package/frontend/src/features/ai/page/AIPage.tsx +67 -79
  269. package/frontend/src/features/ai/services/ai.service.ts +5 -5
  270. package/frontend/src/features/auth/components/AuthPreview.tsx +96 -0
  271. package/frontend/src/features/auth/components/OAuthConfigDialog.tsx +53 -30
  272. package/frontend/src/features/auth/components/UserFormDialog.tsx +13 -6
  273. package/frontend/src/features/auth/components/UsersDataGrid.tsx +44 -14
  274. package/frontend/src/features/auth/components/index.ts +5 -0
  275. package/frontend/src/features/auth/helpers.tsx +200 -0
  276. package/frontend/src/features/auth/hooks/useAnonToken.ts +30 -0
  277. package/frontend/src/features/auth/hooks/useAuthConfig.ts +48 -0
  278. package/frontend/src/features/auth/hooks/useOAuthConfig.ts +14 -10
  279. package/frontend/src/features/auth/hooks/useUsers.ts +43 -5
  280. package/frontend/src/features/auth/index.ts +3 -2
  281. package/frontend/src/features/auth/page/AuthMethodsPage.tsx +275 -0
  282. package/frontend/src/features/auth/page/ConfigurationPage.tsx +395 -0
  283. package/frontend/src/features/auth/page/UsersPage.tsx +285 -0
  284. package/frontend/src/features/auth/services/anonToken.service.ts +11 -0
  285. package/frontend/src/features/auth/services/config.service.ts +19 -0
  286. package/frontend/src/features/auth/services/{oauth.service.ts → oauth-config.service.ts} +4 -4
  287. package/frontend/src/features/auth/services/{auth.service.ts → user.service.ts} +7 -53
  288. package/frontend/src/features/dashboard/components/ConnectionSuccessBanner.tsx +35 -0
  289. package/frontend/src/features/dashboard/components/PromptCard.tsx +21 -0
  290. package/frontend/src/features/dashboard/components/PromptDialog.tsx +103 -0
  291. package/frontend/src/features/dashboard/components/StatsCard.tsx +50 -0
  292. package/frontend/src/features/dashboard/components/index.ts +4 -0
  293. package/frontend/src/features/dashboard/page/DashboardPage.tsx +187 -169
  294. package/frontend/src/features/dashboard/prompts/ai-chatbot.ts +13 -0
  295. package/frontend/src/features/dashboard/prompts/crm-system.ts +13 -0
  296. package/frontend/src/features/dashboard/prompts/ecommerce-platform.ts +12 -0
  297. package/frontend/src/features/dashboard/prompts/index.ts +31 -0
  298. package/frontend/src/features/dashboard/prompts/instagram-clone.ts +11 -0
  299. package/frontend/src/features/dashboard/prompts/notion-clone.ts +14 -0
  300. package/frontend/src/features/dashboard/prompts/reddit-clone.ts +12 -0
  301. package/frontend/src/features/database/components/DatabaseDataGrid.tsx +48 -17
  302. package/frontend/src/features/database/components/ForeignKeyCell.tsx +15 -34
  303. package/frontend/src/features/database/components/ForeignKeyPopover.tsx +19 -20
  304. package/frontend/src/features/database/components/LinkRecordModal.tsx +120 -125
  305. package/frontend/src/features/database/components/RecordFormDialog.tsx +22 -33
  306. package/frontend/src/features/database/components/RecordFormField.tsx +45 -47
  307. package/frontend/src/features/database/components/TableEmptyState.tsx +6 -5
  308. package/frontend/src/features/database/components/TableForm.tsx +28 -15
  309. package/frontend/src/features/database/components/TableFormColumn.tsx +2 -3
  310. package/frontend/src/features/database/components/TableSidebar.tsx +1 -1
  311. package/frontend/src/features/database/components/TablesEmptyState.tsx +48 -0
  312. package/frontend/src/features/database/components/TemplateCard.tsx +37 -0
  313. package/frontend/src/features/database/components/TemplatePreview.tsx +92 -0
  314. package/frontend/src/features/database/components/index.ts +19 -0
  315. package/frontend/src/features/database/constants.ts +28 -2
  316. package/frontend/src/features/database/contexts/SQLEditorContext.tsx +188 -0
  317. package/frontend/src/features/database/helpers.ts +2 -2
  318. package/frontend/src/features/database/hooks/useCSVImport.ts +29 -0
  319. package/frontend/src/features/database/hooks/useFullMetadata.ts +18 -0
  320. package/frontend/src/features/database/hooks/useRawSQL.ts +55 -0
  321. package/frontend/src/features/database/hooks/useRecords.ts +139 -0
  322. package/frontend/src/features/database/hooks/useTables.ts +131 -0
  323. package/frontend/src/features/database/index.ts +6 -1
  324. package/frontend/src/features/database/page/FunctionsPage.tsx +211 -0
  325. package/frontend/src/features/database/page/IndexesPage.tsx +240 -0
  326. package/frontend/src/features/database/page/PoliciesPage.tsx +248 -0
  327. package/frontend/src/features/database/page/SQLEditorPage.tsx +382 -0
  328. package/frontend/src/features/database/page/{DatabasePage.tsx → TablesPage.tsx} +186 -185
  329. package/frontend/src/features/database/page/TemplatesPage.tsx +39 -0
  330. package/frontend/src/features/database/page/TriggersPage.tsx +242 -0
  331. package/frontend/src/features/database/services/advance.service.ts +66 -0
  332. package/frontend/src/features/database/services/{database.service.ts → record.service.ts} +67 -64
  333. package/frontend/src/features/database/services/table.service.ts +64 -0
  334. package/frontend/src/features/database/templates/ai-chatbot.ts +402 -0
  335. package/frontend/src/features/database/templates/crm-system.ts +528 -0
  336. package/frontend/src/features/database/templates/ecommerce-platform.ts +553 -0
  337. package/frontend/src/features/database/templates/index.ts +34 -0
  338. package/frontend/src/features/database/templates/instagram-clone.ts +222 -0
  339. package/frontend/src/features/database/templates/notion-clone.ts +483 -0
  340. package/frontend/src/features/database/templates/reddit-clone.ts +526 -0
  341. package/frontend/src/features/functions/components/FunctionRow.tsx +2 -1
  342. package/frontend/src/features/functions/components/FunctionsSidebar.tsx +1 -1
  343. package/frontend/src/features/functions/components/SecretRow.tsx +1 -1
  344. package/frontend/src/features/functions/components/index.ts +5 -0
  345. package/frontend/src/features/functions/hooks/useFunctions.ts +4 -4
  346. package/frontend/src/features/{secrets → functions}/hooks/useSecrets.ts +5 -5
  347. package/frontend/src/features/functions/page/FunctionsPage.tsx +160 -17
  348. package/frontend/src/features/functions/{components/SecretsContent.tsx → page/SecretsPage.tsx} +8 -12
  349. package/frontend/src/features/functions/services/{functions.service.ts → function.service.ts} +2 -2
  350. package/frontend/src/features/{secrets/services/secrets.service.ts → functions/services/secret.service.ts} +2 -2
  351. package/frontend/src/features/login/hooks/usePartnerOrigin.ts +27 -0
  352. package/frontend/src/features/login/page/CloudLoginPage.tsx +79 -54
  353. package/frontend/src/features/login/page/LoginPage.tsx +16 -23
  354. package/frontend/src/features/login/services/partnership.service.ts +65 -0
  355. package/frontend/src/features/logs/components/LogsDataGrid.tsx +89 -0
  356. package/frontend/src/features/logs/components/SeverityBadge.tsx +18 -0
  357. package/frontend/src/features/logs/components/index.ts +2 -0
  358. package/frontend/src/features/logs/helpers.ts +24 -0
  359. package/frontend/src/features/logs/hooks/useAuditLogs.ts +4 -4
  360. package/frontend/src/features/logs/hooks/useLogSources.ts +137 -0
  361. package/frontend/src/features/logs/hooks/useLogs.ts +163 -0
  362. package/frontend/src/features/logs/hooks/useMcpUsage.ts +181 -0
  363. package/frontend/src/features/logs/index.ts +8 -2
  364. package/frontend/src/features/logs/page/AuditsPage.tsx +91 -38
  365. package/frontend/src/features/logs/page/LogsPage.tsx +152 -0
  366. package/frontend/src/features/logs/page/MCPLogsPage.tsx +84 -0
  367. package/frontend/src/features/logs/services/audit.service.ts +63 -0
  368. package/frontend/src/features/logs/services/log.service.ts +15 -110
  369. package/frontend/src/features/logs/services/usage.service.ts +31 -0
  370. package/frontend/src/features/onboard/components/McpConnectionStatus.tsx +68 -0
  371. package/frontend/src/features/onboard/components/OnboardingModal.tsx +267 -0
  372. package/frontend/src/features/onboard/components/VideoDemoModal.tsx +38 -0
  373. package/frontend/src/features/onboard/components/index.ts +4 -0
  374. package/frontend/src/features/onboard/components/mcp/CursorDeeplinkGenerator.tsx +2 -2
  375. package/frontend/src/features/onboard/components/mcp/{mcp-helper.tsx → helpers.tsx} +8 -8
  376. package/frontend/src/features/onboard/components/mcp/index.ts +2 -3
  377. package/frontend/src/features/onboard/index.ts +13 -3
  378. package/frontend/src/features/storage/components/BucketEmptyState.tsx +9 -6
  379. package/frontend/src/features/storage/components/BucketFormDialog.tsx +25 -41
  380. package/frontend/src/features/storage/components/FilePreviewDialog.tsx +20 -8
  381. package/frontend/src/features/storage/components/StorageDataGrid.tsx +4 -3
  382. package/frontend/src/features/storage/components/StorageManager.tsx +23 -34
  383. package/frontend/src/features/storage/components/index.ts +12 -0
  384. package/frontend/src/features/storage/hooks/useStorage.ts +208 -0
  385. package/frontend/src/features/storage/page/StoragePage.tsx +41 -115
  386. package/frontend/src/features/storage/services/storage.service.ts +22 -1
  387. package/frontend/src/features/visualizer/components/AuthNode.tsx +72 -56
  388. package/frontend/src/features/visualizer/components/BucketNode.tsx +4 -4
  389. package/frontend/src/features/visualizer/components/SchemaVisualizer.tsx +108 -80
  390. package/frontend/src/features/visualizer/components/TableNode.tsx +34 -41
  391. package/frontend/src/features/visualizer/components/VisualizerSkeleton.tsx +12 -4
  392. package/frontend/src/features/visualizer/page/VisualizerPage.tsx +33 -29
  393. package/frontend/src/index.css +1 -0
  394. package/frontend/src/lib/analytics/posthog.tsx +27 -0
  395. package/frontend/src/lib/contexts/AuthContext.tsx +38 -31
  396. package/frontend/src/lib/contexts/SocketContext.tsx +5 -6
  397. package/frontend/src/{features/metadata → lib}/hooks/useMetadata.ts +1 -1
  398. package/frontend/src/lib/hooks/useToast.tsx +6 -2
  399. package/frontend/src/lib/routing/AppRoutes.tsx +84 -0
  400. package/frontend/src/lib/routing/RequireAuth.tsx +27 -0
  401. package/frontend/src/lib/utils/cloudMessaging.ts +20 -0
  402. package/frontend/src/lib/utils/menuItems.ts +183 -0
  403. package/frontend/src/lib/utils/{validation-schemas.ts → schemaValidations.ts} +10 -5
  404. package/frontend/src/lib/utils/utils.ts +19 -1
  405. package/frontend/src/vite-env.d.ts +1 -0
  406. package/frontend/vite.config.ts +5 -3
  407. package/functions/server.ts +28 -3
  408. package/functions/worker-template.js +15 -4
  409. package/i18n/README.ar.md +130 -0
  410. package/i18n/README.de.md +130 -0
  411. package/i18n/README.es.md +154 -0
  412. package/i18n/README.fr.md +134 -0
  413. package/i18n/README.hi.md +129 -0
  414. package/i18n/README.ja.md +174 -0
  415. package/i18n/README.ko.md +137 -0
  416. package/i18n/README.pt-BR.md +131 -0
  417. package/i18n/README.ru.md +129 -0
  418. package/i18n/README.zh-CN.md +133 -0
  419. package/openapi/ai.yaml +31 -4
  420. package/openapi/auth.yaml +827 -146
  421. package/package.json +16 -7
  422. package/shared-schemas/package.json +1 -1
  423. package/shared-schemas/src/ai-api.schema.ts +34 -58
  424. package/shared-schemas/src/ai.schema.ts +5 -0
  425. package/shared-schemas/src/auth-api.schema.ts +154 -8
  426. package/shared-schemas/src/auth.schema.ts +42 -6
  427. package/shared-schemas/src/cloud-events.schema.ts +57 -0
  428. package/shared-schemas/src/database-api.schema.ts +3 -3
  429. package/shared-schemas/src/database.schema.ts +1 -1
  430. package/shared-schemas/src/index.ts +1 -0
  431. package/shared-schemas/src/logs-api.schema.ts +7 -1
  432. package/shared-schemas/src/logs.schema.ts +26 -0
  433. package/shared-schemas/src/metadata.schema.ts +9 -4
  434. package/test-gemini.sh +35 -0
  435. package/test-usage-admin.sh +57 -0
  436. package/test-usage.sh +50 -0
  437. package/zeabur/README.md +13 -0
  438. package/zeabur/template.yml +1032 -0
  439. package/.github/workflows/deploy-aws.yml +0 -130
  440. package/backend/src/api/routes/agent.ts +0 -29
  441. package/backend/src/api/routes/auth.oauth.ts +0 -482
  442. package/backend/src/api/routes/auth.ts +0 -386
  443. package/backend/src/api/routes/docs.ts +0 -66
  444. package/backend/src/api/routes/functions.ts +0 -183
  445. package/backend/src/api/routes/openapi.ts +0 -82
  446. package/backend/src/api/routes/usage.ts +0 -96
  447. package/backend/src/core/ai/client.ts +0 -242
  448. package/backend/src/core/ai/model.ts +0 -117
  449. package/backend/src/core/auth/auth.ts +0 -781
  450. package/backend/src/core/database/table.ts +0 -772
  451. package/backend/src/core/documentation/agent.ts +0 -689
  452. package/backend/src/core/documentation/openapi.ts +0 -856
  453. package/backend/src/core/logs/analytics.ts +0 -76
  454. package/backend/src/core/logs/providers/localdb.provider.ts +0 -246
  455. package/backend/src/core/storage/storage.ts +0 -923
  456. package/backend/src/utils/cloud-token.ts +0 -39
  457. package/backend/src/utils/helpers.ts +0 -49
  458. package/backend/src/utils/uuid.ts +0 -9
  459. package/backend/tests/manual/test-better-auth.sh +0 -303
  460. package/docker-init/db/logs.sql +0 -9
  461. package/frontend/README.md +0 -112
  462. package/frontend/src/components/datagrid/index.tsx +0 -20
  463. package/frontend/src/components/layout/CloudLayout.tsx +0 -95
  464. package/frontend/src/features/ai/components/AIConfigDialog.tsx +0 -76
  465. package/frontend/src/features/ai/components/AIConfigForm.tsx +0 -222
  466. package/frontend/src/features/ai/components/fields/ModalityField.tsx +0 -87
  467. package/frontend/src/features/ai/components/fields/ModelSelectionField.tsx +0 -134
  468. package/frontend/src/features/ai/components/fields/SystemPromptField.tsx +0 -33
  469. package/frontend/src/features/auth/components/AddOAuthDialog.tsx +0 -106
  470. package/frontend/src/features/auth/components/AuthMethodTab.tsx +0 -238
  471. package/frontend/src/features/auth/components/UsersTab.tsx +0 -114
  472. package/frontend/src/features/auth/page/AuthenticationPage.tsx +0 -169
  473. package/frontend/src/features/database/hooks/UseLinkModal.tsx +0 -78
  474. package/frontend/src/features/functions/components/FunctionViewer.tsx +0 -46
  475. package/frontend/src/features/functions/components/FunctionsContent.tsx +0 -88
  476. package/frontend/src/features/login/components/AuthErrorBoundary.tsx +0 -87
  477. package/frontend/src/features/login/components/PrivateRoute.tsx +0 -24
  478. package/frontend/src/features/logs/components/AnalyticsLogsTable.tsx +0 -313
  479. package/frontend/src/features/logs/components/LogsTable.tsx +0 -199
  480. package/frontend/src/features/logs/page/AnalyticsLogsPage.tsx +0 -530
  481. package/frontend/src/features/metadata/index.ts +0 -0
  482. package/frontend/src/features/metadata/page/MetadataPage.tsx +0 -136
  483. package/frontend/src/features/onboard/components/CompletionCard.tsx +0 -41
  484. package/frontend/src/features/onboard/components/OnboardButton.tsx +0 -84
  485. package/frontend/src/features/onboard/components/StepContent.tsx +0 -91
  486. package/frontend/src/features/onboard/components/TestConnectionStep.tsx +0 -53
  487. package/frontend/src/features/onboard/components/mcp/McpInstallation.tsx +0 -144
  488. package/frontend/src/features/onboard/page/OnBoardPage.tsx +0 -104
  489. package/frontend/src/features/onboard/types.ts +0 -8
  490. package/frontend/src/lib/contexts/OnboardStepContext.tsx +0 -68
  491. package/frontend/src/lib/hooks/useOnboardingCompletion.ts +0 -29
  492. /package/backend/src/api/{middleware → middlewares}/error.ts +0 -0
  493. /package/backend/src/api/{middleware → middlewares}/upload.ts +0 -0
  494. /package/backend/{migrations → src/infra/database/migrations}/000_create-base-tables.sql +0 -0
  495. /package/backend/{migrations → src/infra/database/migrations}/001_create-helper-functions.sql +0 -0
  496. /package/backend/{migrations → src/infra/database/migrations}/002_rename-auth-tables.sql +0 -0
  497. /package/backend/{migrations → src/infra/database/migrations}/003_create-users-table.sql +0 -0
  498. /package/backend/{migrations → src/infra/database/migrations}/004_add-reload-postgrest-func.sql +0 -0
  499. /package/backend/{migrations → src/infra/database/migrations}/005_enable-project-admin-modify-users.sql +0 -0
  500. /package/backend/{migrations → src/infra/database/migrations}/006_modify-ai-usage-table.sql +0 -0
  501. /package/backend/{migrations → src/infra/database/migrations}/007_drop-metadata-table.sql +0 -0
  502. /package/backend/{migrations → src/infra/database/migrations}/008_add-system-tables.sql +0 -0
  503. /package/backend/{migrations → src/infra/database/migrations}/009_add-function-secrets.sql +0 -0
  504. /package/backend/{migrations → src/infra/database/migrations}/010_modify-ai-config-modalities.sql +0 -0
  505. /package/backend/{migrations → src/infra/database/migrations}/011_refactor-secrets-table.sql +0 -0
  506. /package/backend/{migrations → src/infra/database/migrations}/012_add-storage-uploaded-by.sql +0 -0
  507. /package/frontend/src/{features/metadata → lib}/services/metadata.service.ts +0 -0
@@ -0,0 +1,60 @@
1
+ -- Migration: 015 - Create email OTP verification table and email auth configs
2
+ -- This migration creates:
3
+ -- 1. _email_otps: Stores one-time tokens for email verification purposes
4
+ -- - Supports both short numeric codes (6 digits) for manual entry
5
+ -- - Supports long cryptographic tokens (64 chars) for magic links
6
+ -- - Uses dual hashing strategy:
7
+ -- * NUMERIC_CODE (6 digits): Bcrypt hash (slow, defense against brute force)
8
+ -- * LINK_TOKEN (64 hex chars): SHA-256 hash (fast, enables direct O(1) lookup)
9
+ -- 2. _auth_configs: Stores email authentication configuration (single-row table)
10
+
11
+ -- 1. Create email OTP verification table
12
+ CREATE TABLE IF NOT EXISTS _email_otps (
13
+ id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
14
+ email TEXT NOT NULL,
15
+ purpose TEXT NOT NULL,
16
+ otp_hash TEXT NOT NULL, -- Hash of OTP: bcrypt for NUMERIC_CODE, SHA-256 for LINK_TOKEN
17
+ expires_at TIMESTAMPTZ NOT NULL,
18
+ consumed_at TIMESTAMPTZ,
19
+ attempts_count INTEGER DEFAULT 0 NOT NULL,
20
+ created_at TIMESTAMPTZ DEFAULT NOW(),
21
+ updated_at TIMESTAMPTZ DEFAULT NOW(),
22
+ UNIQUE (email, purpose) -- Only one active token per email/purpose combination
23
+ );
24
+
25
+ -- Create indexes for better query performance
26
+ CREATE INDEX IF NOT EXISTS idx_email_otps_email_purpose ON _email_otps(email, purpose);
27
+ CREATE INDEX IF NOT EXISTS idx_email_otps_expires_at ON _email_otps(expires_at);
28
+ CREATE INDEX IF NOT EXISTS idx_email_otps_otp_hash ON _email_otps(otp_hash); -- For direct LINK_TOKEN lookup
29
+
30
+ -- Add trigger for updated_at
31
+ DROP TRIGGER IF EXISTS update__email_otps_updated_at ON _email_otps;
32
+ CREATE TRIGGER update__email_otps_updated_at
33
+ BEFORE UPDATE ON _email_otps
34
+ FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
35
+
36
+ -- 2. Create email authentication configuration table (single-row design)
37
+ -- This table stores global email authentication settings for the project
38
+ CREATE TABLE IF NOT EXISTS _auth_configs (
39
+ id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
40
+ require_email_verification BOOLEAN DEFAULT FALSE NOT NULL,
41
+ password_min_length INTEGER DEFAULT 6 NOT NULL CHECK (password_min_length >= 4 AND password_min_length <= 128),
42
+ require_number BOOLEAN DEFAULT FALSE NOT NULL,
43
+ require_lowercase BOOLEAN DEFAULT FALSE NOT NULL,
44
+ require_uppercase BOOLEAN DEFAULT FALSE NOT NULL,
45
+ require_special_char BOOLEAN DEFAULT FALSE NOT NULL,
46
+ verify_email_redirect_to TEXT, -- Custom URL to redirect after successful email verification (defaults to no redirect if NULL)
47
+ reset_password_redirect_to TEXT, -- Custom URL to redirect after successful password reset (defaults to no redirect if NULL)
48
+ created_at TIMESTAMPTZ DEFAULT NOW(),
49
+ updated_at TIMESTAMPTZ DEFAULT NOW()
50
+ );
51
+
52
+ -- Ensure only one row exists (singleton pattern)
53
+ -- This constraint prevents multiple configuration rows
54
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_auth_configs_singleton ON _auth_configs ((1));
55
+
56
+ -- Add trigger for updated_at
57
+ DROP TRIGGER IF EXISTS update__auth_configs_updated_at ON _auth_configs;
58
+ CREATE TRIGGER update__auth_configs_updated_at
59
+ BEFORE UPDATE ON _auth_configs
60
+ FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
@@ -0,0 +1,24 @@
1
+ -- Migration 016: Update _email_otps and _auth_configs tables
2
+ --
3
+ -- Changes:
4
+ -- 1. _email_otps table:
5
+ -- - Remove attempts_count column (brute force protection moved to API rate limiter)
6
+ -- 2. _auth_configs table:
7
+ -- - Remove verify_email_redirect_to and reset_password_redirect_to columns
8
+ -- - Add verify_email_method and reset_password_method columns (code or link)
9
+ -- - Add sign_in_redirect_to column
10
+
11
+ -- Update _email_otps: Remove attempts_count column
12
+ ALTER TABLE _email_otps DROP COLUMN IF EXISTS attempts_count;
13
+
14
+ -- Update _auth_configs: Remove old redirect columns
15
+ ALTER TABLE _auth_configs
16
+ DROP COLUMN IF EXISTS verify_email_redirect_to,
17
+ DROP COLUMN IF EXISTS reset_password_redirect_to;
18
+
19
+ -- Add new columns to _auth_configs
20
+ -- Note: DEFAULT 'code' NOT NULL ensures existing rows automatically get 'code' value
21
+ ALTER TABLE _auth_configs
22
+ ADD COLUMN IF NOT EXISTS verify_email_method TEXT DEFAULT 'code' NOT NULL CHECK (verify_email_method IN ('code', 'link')),
23
+ ADD COLUMN IF NOT EXISTS reset_password_method TEXT DEFAULT 'code' NOT NULL CHECK (reset_password_method IN ('code', 'link')),
24
+ ADD COLUMN IF NOT EXISTS sign_in_redirect_to TEXT;
@@ -1,9 +1,10 @@
1
1
  import crypto from 'crypto';
2
2
 
3
3
  /**
4
- * Encryption utilities for secrets management
4
+ * EncryptionManager - Handles encryption/decryption operations
5
+ * Infrastructure layer for secrets encryption
5
6
  */
6
- export class EncryptionUtils {
7
+ export class EncryptionManager {
7
8
  private static encryptionKey: Buffer | null = null;
8
9
 
9
10
  private static getEncryptionKey(): Buffer {
@@ -0,0 +1,125 @@
1
+ import jwt from 'jsonwebtoken';
2
+ import { createRemoteJWKSet, JWTPayload, jwtVerify } from 'jose';
3
+ import { AppError } from '@/api/middlewares/error.js';
4
+ import { ERROR_CODES, NEXT_ACTION } from '@/types/error-constants.js';
5
+ import type { TokenPayloadSchema } from '@insforge/shared-schemas';
6
+
7
+ const JWT_SECRET = process.env.JWT_SECRET ?? '';
8
+ const JWT_EXPIRES_IN = '7d';
9
+
10
+ /**
11
+ * Create JWKS instance with caching and timeout configuration
12
+ * The instance will automatically cache keys and handle refetching
13
+ */
14
+ const cloudApiHost = process.env.CLOUD_API_HOST || 'https://api.insforge.dev';
15
+ const JWKS = createRemoteJWKSet(new URL(`${cloudApiHost}/.well-known/jwks.json`), {
16
+ timeoutDuration: 10000, // 10 second timeout for HTTP requests
17
+ cooldownDuration: 30000, // 30 seconds cooldown after successful fetch
18
+ cacheMaxAge: 600000, // Maximum 10 minutes between refetches
19
+ });
20
+
21
+ /**
22
+ * TokenManager - Handles JWT token operations
23
+ * Infrastructure layer for token generation and verification
24
+ */
25
+ export class TokenManager {
26
+ private static instance: TokenManager;
27
+
28
+ private constructor() {
29
+ if (!process.env.JWT_SECRET) {
30
+ throw new Error('JWT_SECRET environment variable is required');
31
+ }
32
+ }
33
+
34
+ public static getInstance(): TokenManager {
35
+ if (!TokenManager.instance) {
36
+ TokenManager.instance = new TokenManager();
37
+ }
38
+ return TokenManager.instance;
39
+ }
40
+
41
+ /**
42
+ * Generate JWT token for users and admins
43
+ */
44
+ generateToken(payload: TokenPayloadSchema): string {
45
+ return jwt.sign(payload, JWT_SECRET, {
46
+ algorithm: 'HS256',
47
+ expiresIn: JWT_EXPIRES_IN,
48
+ });
49
+ }
50
+
51
+ /**
52
+ * Generate anonymous JWT token (never expires)
53
+ */
54
+ generateAnonToken(): string {
55
+ const payload = {
56
+ sub: '12345678-1234-5678-90ab-cdef12345678',
57
+ email: 'anon@insforge.com',
58
+ role: 'anon',
59
+ };
60
+ return jwt.sign(payload, JWT_SECRET, {
61
+ algorithm: 'HS256',
62
+ // No expiresIn means token never expires
63
+ });
64
+ }
65
+
66
+ /**
67
+ * Verify JWT token
68
+ */
69
+ verifyToken(token: string): TokenPayloadSchema {
70
+ try {
71
+ const decoded = jwt.verify(token, JWT_SECRET) as TokenPayloadSchema;
72
+ return {
73
+ sub: decoded.sub,
74
+ email: decoded.email,
75
+ role: decoded.role || 'authenticated',
76
+ };
77
+ } catch {
78
+ throw new AppError('Invalid token', 401, ERROR_CODES.AUTH_UNAUTHORIZED);
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Verify cloud backend JWT token
84
+ * Validates JWT tokens from api.insforge.dev using JWKS
85
+ */
86
+ async verifyCloudToken(token: string): Promise<{ projectId: string; payload: JWTPayload }> {
87
+ try {
88
+ // JWKS handles caching internally, no need to manage it manually
89
+ const { payload } = await jwtVerify(token, JWKS, {
90
+ algorithms: ['RS256', 'RS384', 'RS512', 'ES256', 'ES384', 'ES512'],
91
+ });
92
+
93
+ // Verify project_id matches if configured
94
+ const tokenProjectId = payload['projectId'] as string;
95
+ const expectedProjectId = process.env.PROJECT_ID;
96
+
97
+ if (expectedProjectId && tokenProjectId !== expectedProjectId) {
98
+ throw new AppError(
99
+ 'Project ID mismatch',
100
+ 403,
101
+ ERROR_CODES.AUTH_UNAUTHORIZED,
102
+ NEXT_ACTION.CHECK_TOKEN
103
+ );
104
+ }
105
+
106
+ return {
107
+ projectId: tokenProjectId || expectedProjectId || 'local',
108
+ payload,
109
+ };
110
+ } catch (error) {
111
+ // Re-throw AppError as-is
112
+ if (error instanceof AppError) {
113
+ throw error;
114
+ }
115
+
116
+ // Wrap other JWT errors
117
+ throw new AppError(
118
+ `Invalid cloud authorization code: ${error instanceof Error ? error.message : 'Unknown error'}`,
119
+ 401,
120
+ ERROR_CODES.AUTH_INVALID_CREDENTIALS,
121
+ NEXT_ACTION.CHECK_TOKEN
122
+ );
123
+ }
124
+ }
125
+ }
@@ -1,7 +1,7 @@
1
1
  import { Server as HttpServer } from 'http';
2
2
  import { Server as SocketIOServer, Socket } from 'socket.io';
3
3
  import logger from '@/utils/logger.js';
4
- import { AuthService } from '../auth/auth.js';
4
+ import { TokenManager } from '@/infra/security/token.manager.js';
5
5
  import {
6
6
  ServerEvents,
7
7
  ClientEvents,
@@ -10,31 +10,31 @@ import {
10
10
  NotificationPayload,
11
11
  SubscribePayload,
12
12
  UnsubscribePayload,
13
- } from './types.js';
14
- import { AppError } from '@/api/middleware/error.js';
13
+ } from '@/types/socket.js';
14
+ import { AppError } from '@/api/middlewares/error.js';
15
15
  import { ERROR_CODES, NEXT_ACTION } from '@/types/error-constants.js';
16
16
 
17
- const authService = AuthService.getInstance();
17
+ const tokenManager = TokenManager.getInstance();
18
18
 
19
19
  /**
20
- * SocketService - Industrial-grade Socket.IO implementation
21
- * Follows best practices for real-time communication
20
+ * SocketManager - Industrial-grade Socket.IO implementation
21
+ * Infrastructure layer for real-time WebSocket communication
22
22
  */
23
- export class SocketService {
24
- private static instance: SocketService;
23
+ export class SocketManager {
24
+ private static instance: SocketManager;
25
25
  private io: SocketIOServer | null = null;
26
26
  private socketMetadata: Map<string, SocketMetadata> = new Map();
27
27
 
28
28
  private constructor() {}
29
29
 
30
30
  /**
31
- * Singleton pattern for global socket service access
31
+ * Singleton pattern for global socket manager access
32
32
  */
33
- static getInstance(): SocketService {
34
- if (!SocketService.instance) {
35
- SocketService.instance = new SocketService();
33
+ static getInstance(): SocketManager {
34
+ if (!SocketManager.instance) {
35
+ SocketManager.instance = new SocketManager();
36
36
  }
37
- return SocketService.instance;
37
+ return SocketManager.instance;
38
38
  }
39
39
 
40
40
  /**
@@ -66,7 +66,7 @@ export class SocketService {
66
66
  this.io.use((socket, next) => {
67
67
  try {
68
68
  const token = socket.handshake.auth.token;
69
- const payload = authService.verifyToken(token);
69
+ const payload = tokenManager.verifyToken(token);
70
70
  if (!payload.role) {
71
71
  throw new AppError(
72
72
  'Invalid token: missing role',
@@ -385,4 +385,4 @@ export class SocketService {
385
385
  }
386
386
 
387
387
  // Export singleton instance for convenience
388
- export const socketService = SocketService.getInstance();
388
+ export const socketService = SocketManager.getInstance();
@@ -0,0 +1,377 @@
1
+ import OpenAI from 'openai';
2
+ import jwt from 'jsonwebtoken';
3
+ import { isCloudEnvironment } from '@/utils/environment.js';
4
+ import { AppError } from '@/api/middlewares/error.js';
5
+ import { ERROR_CODES } from '@/types/error-constants.js';
6
+ import logger from '@/utils/logger.js';
7
+
8
+ interface CloudCredentialsResponse {
9
+ openrouter?: {
10
+ api_key: string;
11
+ limit?: number;
12
+ expired_at?: string | null;
13
+ usage?: number;
14
+ limit_remaining?: number;
15
+ };
16
+ }
17
+
18
+ interface CloudCredentials {
19
+ apiKey: string;
20
+ limitRemaining?: number;
21
+ }
22
+
23
+ interface OpenRouterKeyInfo {
24
+ data: {
25
+ label: string;
26
+ usage: number;
27
+ limit: number | null;
28
+ is_free_tier: boolean;
29
+ };
30
+ }
31
+
32
+ interface OpenRouterLimitation {
33
+ label: string;
34
+ credit_limit: number | null;
35
+ credit_used: number;
36
+ credit_remaining: number | null;
37
+ rate_limit?: {
38
+ requests?: number;
39
+ interval?: string;
40
+ note?: string;
41
+ };
42
+ }
43
+
44
+ export class AIClientService {
45
+ private static instance: AIClientService;
46
+ private cloudCredentials: CloudCredentials | undefined;
47
+ private openRouterClient: OpenAI | null = null;
48
+ private currentApiKey: string | undefined;
49
+ private renewalPromise: Promise<string> | null = null;
50
+ private fetchPromise: Promise<string> | null = null;
51
+
52
+ private constructor() {}
53
+
54
+ static getInstance(): AIClientService {
55
+ if (!AIClientService.instance) {
56
+ AIClientService.instance = new AIClientService();
57
+ }
58
+ return AIClientService.instance;
59
+ }
60
+
61
+ /**
62
+ * Create or recreate the OpenAI client with the given API key
63
+ */
64
+ private createClient(apiKey: string): OpenAI {
65
+ this.currentApiKey = apiKey;
66
+ return new OpenAI({
67
+ baseURL: 'https://openrouter.ai/api/v1',
68
+ apiKey,
69
+ defaultHeaders: {
70
+ 'HTTP-Referer': 'https://insforge.dev',
71
+ 'X-Title': 'InsForge',
72
+ },
73
+ });
74
+ }
75
+
76
+ /**
77
+ * Get OpenRouter API key based on environment
78
+ * In cloud environment: fetches from cloud API with JWT authentication
79
+ * In local environment: returns from environment variable
80
+ */
81
+ async getApiKey(): Promise<string> {
82
+ if (isCloudEnvironment()) {
83
+ if (this.cloudCredentials) {
84
+ return this.cloudCredentials.apiKey;
85
+ } else {
86
+ return await this.fetchCloudApiKey();
87
+ }
88
+ }
89
+
90
+ const apiKey = process.env.OPENROUTER_API_KEY;
91
+ if (!apiKey) {
92
+ throw new AppError(
93
+ 'OPENROUTER_API_KEY not found in environment variables',
94
+ 500,
95
+ ERROR_CODES.AI_INVALID_API_KEY
96
+ );
97
+ }
98
+ return apiKey;
99
+ }
100
+
101
+ /**
102
+ * Get the OpenAI client, creating or updating it as needed
103
+ * This is the main method services should use
104
+ */
105
+ async getClient(): Promise<OpenAI> {
106
+ if (!this.openRouterClient) {
107
+ this.openRouterClient = this.createClient(await this.getApiKey());
108
+ return this.openRouterClient;
109
+ }
110
+ if (isCloudEnvironment()) {
111
+ const apiKey = await this.getApiKey();
112
+ if (this.currentApiKey !== apiKey) {
113
+ this.openRouterClient = this.createClient(apiKey);
114
+ }
115
+ }
116
+ return this.openRouterClient;
117
+ }
118
+
119
+ /**
120
+ * Check if AI services are properly configured
121
+ */
122
+ isConfigured(): boolean {
123
+ if (isCloudEnvironment()) {
124
+ return true;
125
+ }
126
+ return !!process.env.OPENROUTER_API_KEY;
127
+ }
128
+
129
+ /**
130
+ * Get remaining credits for the current API key from OpenRouter
131
+ */
132
+ async getRemainingCredits(): Promise<{
133
+ usage: number;
134
+ limit: number | null;
135
+ remaining: number | null;
136
+ }> {
137
+ try {
138
+ const apiKey = await this.getApiKey();
139
+
140
+ if (isCloudEnvironment()) {
141
+ // Use InsForge API for cloud environment
142
+ const response = await fetch(
143
+ `https://api.insforge.dev/ai/v1/limitations?credential=${encodeURIComponent(apiKey)}`,
144
+ {
145
+ method: 'GET',
146
+ }
147
+ );
148
+
149
+ if (!response.ok) {
150
+ throw new Error(`Failed to fetch key info: ${response.statusText}`);
151
+ }
152
+
153
+ const result = (await response.json()) as { data: OpenRouterLimitation };
154
+ const keyInfo = result.data;
155
+
156
+ return {
157
+ usage: keyInfo.credit_used,
158
+ limit: keyInfo.credit_limit,
159
+ remaining: keyInfo.credit_remaining,
160
+ };
161
+ } else {
162
+ // Use OpenRouter API for local environment
163
+ const response = await fetch('https://openrouter.ai/api/v1/key', {
164
+ method: 'GET',
165
+ headers: {
166
+ Authorization: `Bearer ${apiKey}`,
167
+ },
168
+ });
169
+
170
+ if (!response.ok) {
171
+ throw new AppError(
172
+ `Invalid OpenRouter API Key`,
173
+ 500,
174
+ ERROR_CODES.AI_INVALID_API_KEY,
175
+ 'Check your OpenRouter key and try again.'
176
+ );
177
+ }
178
+
179
+ const keyInfo = (await response.json()) as OpenRouterKeyInfo;
180
+
181
+ return {
182
+ usage: keyInfo.data.usage,
183
+ limit: keyInfo.data.limit,
184
+ remaining: keyInfo.data.limit !== null ? keyInfo.data.limit - keyInfo.data.usage : null,
185
+ };
186
+ }
187
+ } catch (error) {
188
+ console.error('Failed to fetch remaining credits:', error);
189
+ throw error;
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Fetch API key from cloud service
195
+ * Uses promise memoization to prevent duplicate fetch requests
196
+ */
197
+ private async fetchCloudApiKey(): Promise<string> {
198
+ // If fetch is already in progress, wait for it
199
+ if (this.fetchPromise) {
200
+ logger.info('Fetch already in progress, waiting for completion...');
201
+ return this.fetchPromise;
202
+ }
203
+
204
+ // Start new fetch and store the promise
205
+ this.fetchPromise = (async () => {
206
+ try {
207
+ const projectId = process.env.PROJECT_ID;
208
+ if (!projectId) {
209
+ throw new Error('PROJECT_ID not found in environment variables');
210
+ }
211
+
212
+ const jwtSecret = process.env.JWT_SECRET;
213
+ if (!jwtSecret) {
214
+ throw new Error('JWT_SECRET not found in environment variables');
215
+ }
216
+
217
+ // Sign a token for authentication
218
+ const token = jwt.sign({ projectId }, jwtSecret, { expiresIn: '1h' });
219
+
220
+ // Fetch API key from cloud service with sign token as query parameter
221
+ const response = await fetch(
222
+ `${process.env.CLOUD_API_HOST || 'https://api.insforge.dev'}/ai/v1/credentials/${projectId}?sign=${token}`
223
+ );
224
+
225
+ if (!response.ok) {
226
+ throw new Error(`Failed to fetch cloud API key: ${response.statusText}`);
227
+ }
228
+
229
+ const data = (await response.json()) as CloudCredentialsResponse;
230
+
231
+ // Extract API key from the openrouter object in response
232
+ if (!data.openrouter?.api_key) {
233
+ throw new Error('Invalid response: missing openrouter API Key');
234
+ }
235
+
236
+ // Store credentials with metadata
237
+ this.cloudCredentials = {
238
+ apiKey: data.openrouter.api_key,
239
+ limitRemaining: data.openrouter.limit_remaining,
240
+ };
241
+
242
+ logger.info('Successfully fetched cloud API key');
243
+
244
+ return data.openrouter.api_key;
245
+ } catch (error) {
246
+ console.error('Failed to fetch cloud API key:', error);
247
+ throw error;
248
+ } finally {
249
+ // Clear the promise after completion (success or failure)
250
+ this.fetchPromise = null;
251
+ }
252
+ })();
253
+
254
+ return this.fetchPromise;
255
+ }
256
+
257
+ /**
258
+ * Renew API key from cloud service when credits are exhausted
259
+ * Uses promise memoization to prevent duplicate renewal requests
260
+ */
261
+ async renewCloudApiKey(): Promise<string> {
262
+ // If renewal is already in progress, wait for it
263
+ if (this.renewalPromise) {
264
+ logger.info('Renewal already in progress, waiting for completion...');
265
+ return this.renewalPromise;
266
+ }
267
+
268
+ // Start new renewal and store the promise
269
+ this.renewalPromise = (async () => {
270
+ try {
271
+ const projectId = process.env.PROJECT_ID;
272
+ if (!projectId) {
273
+ throw new Error('PROJECT_ID not found in environment variables');
274
+ }
275
+
276
+ const jwtSecret = process.env.JWT_SECRET;
277
+ if (!jwtSecret) {
278
+ throw new Error('JWT_SECRET not found in environment variables');
279
+ }
280
+
281
+ // Sign a token for authentication
282
+ const token = jwt.sign({ projectId }, jwtSecret, { expiresIn: '1h' });
283
+
284
+ // Renew API key from cloud service with sign token in request body
285
+ const response = await fetch(
286
+ `${process.env.CLOUD_API_HOST || 'https://api.insforge.dev'}/ai/v1/credentials/${projectId}/renew`,
287
+ {
288
+ method: 'POST',
289
+ headers: {
290
+ 'Content-Type': 'application/json',
291
+ },
292
+ body: JSON.stringify({ sign: token }),
293
+ }
294
+ );
295
+
296
+ if (!response.ok) {
297
+ throw new Error(`Failed to renew cloud API key: ${response.statusText}`);
298
+ }
299
+
300
+ const data = (await response.json()) as CloudCredentialsResponse;
301
+
302
+ // Extract API key from the openrouter object in response
303
+ if (!data.openrouter?.api_key) {
304
+ throw new Error('Invalid response: missing openrouter API Key');
305
+ }
306
+
307
+ // Store credentials with metadata
308
+ this.cloudCredentials = {
309
+ apiKey: data.openrouter.api_key,
310
+ limitRemaining: data.openrouter.limit_remaining,
311
+ };
312
+
313
+ logger.info('Successfully renewed cloud API key');
314
+
315
+ // Wait for OpenRouter to propagate the updated credits
316
+ await new Promise((resolve) => setTimeout(resolve, 1000));
317
+
318
+ return data.openrouter.api_key;
319
+ } catch (error) {
320
+ console.error('Failed to renew cloud API key:', error);
321
+ throw error;
322
+ } finally {
323
+ // Clear the promise after completion (success or failure)
324
+ this.renewalPromise = null;
325
+ }
326
+ })();
327
+
328
+ return this.renewalPromise;
329
+ }
330
+
331
+ /**
332
+ * Send a request to OpenRouter with automatic renewal and retry logic
333
+ * Handles 403 insufficient credits errors by renewing the API key and retrying
334
+ * @param request - Function that takes an OpenAI client and returns a Promise
335
+ * @returns The result of the request
336
+ */
337
+ async sendRequest<T>(request: (client: OpenAI) => Promise<T>): Promise<T> {
338
+ const client = await this.getClient();
339
+
340
+ try {
341
+ return await request(client);
342
+ } catch (error) {
343
+ // Check if error is a 402/403 insufficient credits error in cloud environment
344
+ if (
345
+ isCloudEnvironment() &&
346
+ error instanceof OpenAI.APIError &&
347
+ (error.status === 402 || error.status === 403)
348
+ ) {
349
+ logger.info(`Received ${error.status} insufficient credits, renewing API key...`);
350
+ await this.renewCloudApiKey();
351
+
352
+ // Retry with exponential backoff (3 attempts)
353
+ const maxRetries = 3;
354
+
355
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
356
+ try {
357
+ const backoffMs = Math.pow(2, attempt - 1) * 1000; // 1s, 2s, 4s
358
+ logger.info(
359
+ `Retrying request after renewal (attempt ${attempt}/${maxRetries}), waiting ${backoffMs}ms...`
360
+ );
361
+ await new Promise((resolve) => setTimeout(resolve, backoffMs));
362
+
363
+ const result = await request(client);
364
+ logger.info('Request succeeded after API key renewal');
365
+ return result;
366
+ } catch (retryError) {
367
+ if (attempt === maxRetries) {
368
+ logger.error(`All ${maxRetries} retry attempts failed after API key renewal`);
369
+ throw retryError;
370
+ }
371
+ }
372
+ }
373
+ }
374
+ throw error;
375
+ }
376
+ }
377
+ }