insforge 0.3.3 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (635) hide show
  1. package/.claude-plugin/marketplace.json +20 -0
  2. package/.dockerignore +60 -57
  3. package/.env.example +84 -49
  4. package/.github/ISSUE_TEMPLATE/bug_report.yml +36 -83
  5. package/.github/ISSUE_TEMPLATE/config.yml +11 -11
  6. package/.github/ISSUE_TEMPLATE/feature_request.yml +26 -79
  7. package/.github/PULL_REQUEST_TEMPLATE.md +7 -0
  8. package/.github/copilot-instructions.md +146 -146
  9. package/.github/workflows/build-image.yml +66 -65
  10. package/.github/workflows/ci-premerge-check.yml +23 -23
  11. package/.github/workflows/e2e.yml +63 -0
  12. package/.github/workflows/lint-and-format.yml +32 -32
  13. package/.prettierignore +64 -64
  14. package/CHANGELOG.md +44 -3
  15. package/CLAUDE_PLUGIN.md +104 -0
  16. package/CODE_OF_CONDUCT.md +128 -0
  17. package/CONTRIBUTING.md +125 -125
  18. package/Dockerfile +30 -27
  19. package/GITHUB_OAUTH_SETUP.md +49 -49
  20. package/GOOGLE_OAUTH_SETUP.md +148 -148
  21. package/LICENSE +201 -201
  22. package/README.md +182 -134
  23. package/assets/Dark.svg +23 -23
  24. package/assets/mcpInstallv2.png +0 -0
  25. package/assets/sampleResponse.png +0 -0
  26. package/auth/index.html +13 -0
  27. package/auth/package.json +28 -0
  28. package/auth/public/favicon.ico +0 -0
  29. package/auth/src/App.tsx +33 -0
  30. package/auth/src/components/ErrorCard.tsx +37 -0
  31. package/auth/src/components/Layout.tsx +13 -0
  32. package/auth/src/index.css +19 -0
  33. package/auth/src/lib/broadcastService.ts +117 -0
  34. package/auth/src/lib/utils.ts +11 -0
  35. package/auth/src/main.tsx +22 -0
  36. package/auth/src/pages/ForgotPasswordPage.tsx +11 -0
  37. package/auth/src/pages/ResetPasswordPage.tsx +11 -0
  38. package/auth/src/pages/SignInPage.tsx +60 -0
  39. package/auth/src/pages/SignUpPage.tsx +60 -0
  40. package/auth/src/pages/VerifyEmailPage.tsx +20 -0
  41. package/auth/src/vite-env.d.ts +10 -0
  42. package/auth/tsconfig.json +32 -0
  43. package/auth/tsconfig.node.json +11 -0
  44. package/auth/vite.config.ts +25 -0
  45. package/backend/package.json +78 -75
  46. package/backend/src/api/{middleware → middlewares}/auth.ts +8 -9
  47. package/backend/src/api/middlewares/rate-limiters.ts +127 -0
  48. package/backend/src/api/routes/{ai.ts → ai/index.routes.ts} +22 -26
  49. package/backend/src/api/routes/auth/index.routes.ts +667 -0
  50. package/backend/src/api/routes/auth/oauth.routes.ts +473 -0
  51. package/backend/src/api/routes/{database.advance.ts → database/advance.routes.ts} +128 -65
  52. package/backend/src/api/routes/database/index.routes.ts +90 -0
  53. package/backend/src/api/routes/{database.records.ts → database/records.routes.ts} +26 -12
  54. package/backend/src/api/routes/{database.tables.ts → database/tables.routes.ts} +6 -23
  55. package/backend/src/api/routes/docs/index.routes.ts +75 -0
  56. package/backend/src/api/routes/email/index.routes.ts +35 -0
  57. package/backend/src/api/routes/functions/index.routes.ts +194 -0
  58. package/backend/src/api/routes/{logs.ts → logs/index.routes.ts} +25 -30
  59. package/backend/src/api/routes/{metadata.ts → metadata/index.routes.ts} +33 -31
  60. package/backend/src/api/routes/realtime/channels.routes.ts +81 -0
  61. package/backend/src/api/routes/realtime/index.routes.ts +12 -0
  62. package/backend/src/api/routes/realtime/messages.routes.ts +48 -0
  63. package/backend/src/api/routes/realtime/permissions.routes.ts +19 -0
  64. package/backend/src/api/routes/{secrets.ts → secrets/index.routes.ts} +27 -22
  65. package/backend/src/api/routes/{storage.ts → storage/index.routes.ts} +48 -61
  66. package/backend/src/api/routes/usage/index.routes.ts +91 -0
  67. package/backend/src/infra/config/app.config.ts +51 -0
  68. package/backend/src/infra/database/database.manager.ts +182 -0
  69. package/backend/{migrations → src/infra/database/migrations}/000_create-base-tables.sql +141 -141
  70. package/backend/{migrations → src/infra/database/migrations}/001_create-helper-functions.sql +40 -40
  71. package/backend/{migrations → src/infra/database/migrations}/002_rename-auth-tables.sql +29 -29
  72. package/backend/{migrations → src/infra/database/migrations}/003_create-users-table.sql +55 -55
  73. package/backend/{migrations → src/infra/database/migrations}/004_add-reload-postgrest-func.sql +23 -23
  74. package/backend/{migrations → src/infra/database/migrations}/005_enable-project-admin-modify-users.sql +29 -29
  75. package/backend/{migrations → src/infra/database/migrations}/006_modify-ai-usage-table.sql +24 -24
  76. package/backend/{migrations → src/infra/database/migrations}/007_drop-metadata-table.sql +1 -1
  77. package/backend/{migrations → src/infra/database/migrations}/008_add-system-tables.sql +76 -76
  78. package/backend/{migrations → src/infra/database/migrations}/009_add-function-secrets.sql +23 -23
  79. package/backend/{migrations → src/infra/database/migrations}/010_modify-ai-config-modalities.sql +93 -93
  80. package/backend/{migrations → src/infra/database/migrations}/011_refactor-secrets-table.sql +15 -15
  81. package/backend/{migrations → src/infra/database/migrations}/012_add-storage-uploaded-by.sql +7 -7
  82. package/backend/src/infra/database/migrations/013_create-auth-schema-functions.sql +44 -0
  83. package/backend/src/infra/database/migrations/014_add-updated-at-trigger-user-table.sql +8 -0
  84. package/backend/src/infra/database/migrations/015_create-auth-config-and-email-otp-tables.sql +60 -0
  85. package/backend/src/infra/database/migrations/016_update-auth-config-and-email-otp.sql +24 -0
  86. package/backend/src/infra/database/migrations/017_create-realtime-schema.sql +233 -0
  87. package/backend/src/infra/realtime/realtime.manager.ts +246 -0
  88. package/backend/src/infra/realtime/webhook-sender.ts +82 -0
  89. package/backend/src/{core/secrets/encryption.ts → infra/security/encryption.manager.ts} +3 -2
  90. package/backend/src/infra/security/token.manager.ts +219 -0
  91. package/backend/src/infra/socket/socket.manager.ts +522 -0
  92. package/backend/src/providers/ai/openrouter.provider.ts +380 -0
  93. package/backend/src/providers/email/base.provider.ts +38 -0
  94. package/backend/src/providers/email/cloud.provider.ts +271 -0
  95. package/backend/src/{core/logs/providers → providers/logs}/base.provider.ts +11 -11
  96. package/backend/src/{core/logs/providers → providers/logs}/cloudwatch.provider.ts +61 -38
  97. package/backend/src/providers/logs/local.provider.ts +185 -0
  98. package/backend/src/providers/oauth/apple.provider.ts +266 -0
  99. package/backend/src/providers/oauth/base.provider.ts +29 -0
  100. package/backend/src/providers/oauth/discord.provider.ts +195 -0
  101. package/backend/src/providers/oauth/facebook.provider.ts +194 -0
  102. package/backend/src/providers/oauth/github.provider.ts +208 -0
  103. package/backend/src/providers/oauth/google.provider.ts +249 -0
  104. package/backend/src/providers/oauth/index.ts +8 -0
  105. package/backend/src/providers/oauth/linkedin.provider.ts +240 -0
  106. package/backend/src/providers/oauth/microsoft.provider.ts +169 -0
  107. package/backend/src/providers/oauth/x.provider.ts +202 -0
  108. package/backend/src/providers/storage/base.provider.ts +29 -0
  109. package/backend/src/providers/storage/local.provider.ts +103 -0
  110. package/backend/src/providers/storage/s3.provider.ts +313 -0
  111. package/backend/src/server.ts +317 -288
  112. package/backend/src/{core/ai/config.ts → services/ai/ai-config.service.ts} +19 -24
  113. package/backend/src/services/ai/ai-model.service.ts +60 -0
  114. package/backend/src/{core/ai/usage.ts → services/ai/ai-usage.service.ts} +28 -35
  115. package/backend/src/{core/ai/chat.ts → services/ai/chat-completion.service.ts} +37 -24
  116. package/backend/src/services/ai/helpers.ts +64 -0
  117. package/backend/src/{core/ai/image.ts → services/ai/image-generation.service.ts} +17 -19
  118. package/backend/src/services/ai/index.ts +13 -0
  119. package/backend/src/services/auth/auth-config.service.ts +250 -0
  120. package/backend/src/services/auth/auth-otp.service.ts +424 -0
  121. package/backend/src/services/auth/auth.service.ts +1150 -0
  122. package/backend/src/services/auth/index.ts +4 -0
  123. package/backend/src/{core/auth/oauth.ts → services/auth/oauth-config.service.ts} +106 -52
  124. package/backend/src/{core/database/advance.ts → services/database/database-advance.service.ts} +97 -131
  125. package/backend/src/services/database/database-table.service.ts +802 -0
  126. package/backend/src/services/database/database.service.ts +127 -0
  127. package/backend/src/services/email/email.service.ts +73 -0
  128. package/backend/src/{core/functions/functions.ts → services/functions/function.service.ts} +95 -88
  129. package/backend/src/{core/logs/audit.ts → services/logs/audit.service.ts} +92 -75
  130. package/backend/src/services/logs/log.service.ts +73 -0
  131. package/backend/src/services/realtime/index.ts +3 -0
  132. package/backend/src/services/realtime/realtime-auth.service.ts +104 -0
  133. package/backend/src/services/realtime/realtime-channel.service.ts +237 -0
  134. package/backend/src/services/realtime/realtime-message.service.ts +260 -0
  135. package/backend/src/{core/secrets/secrets.ts → services/secrets/secret.service.ts} +48 -66
  136. package/backend/src/services/storage/storage.service.ts +617 -0
  137. package/backend/src/services/usage/usage.service.ts +149 -0
  138. package/backend/src/types/auth.ts +77 -2
  139. package/backend/src/types/email.ts +8 -0
  140. package/backend/src/types/error-constants.ts +4 -0
  141. package/backend/src/types/logs.ts +0 -29
  142. package/backend/src/types/realtime.ts +18 -0
  143. package/backend/src/{core/socket/types.ts → types/socket.ts} +11 -36
  144. package/backend/src/utils/cookies.ts +35 -0
  145. package/backend/src/utils/environment.ts +9 -3
  146. package/backend/src/utils/logger.ts +20 -2
  147. package/backend/src/utils/s3-config-loader.ts +64 -0
  148. package/backend/src/utils/seed.ts +301 -205
  149. package/backend/src/utils/sql-parser.ts +91 -1
  150. package/backend/src/utils/utils.ts +114 -0
  151. package/backend/src/utils/validations.ts +40 -4
  152. package/backend/tests/README.md +133 -133
  153. package/backend/tests/cleanup-all-test-data.sh +230 -230
  154. package/backend/tests/cloud/test-s3-multitenant.sh +131 -131
  155. package/backend/tests/local/comprehensive-curl-tests.sh +155 -155
  156. package/backend/tests/local/test-ai-config.sh +129 -0
  157. package/backend/tests/local/test-ai-usage.sh +80 -0
  158. package/backend/tests/local/test-auth-router.sh +143 -143
  159. package/backend/tests/local/test-database-router.sh +222 -222
  160. package/backend/tests/local/test-e2e.sh +240 -240
  161. package/backend/tests/local/test-fk-errors.sh +96 -96
  162. package/backend/tests/local/test-functions.sh +123 -0
  163. package/backend/tests/local/test-id-field.sh +200 -200
  164. package/backend/tests/local/test-logs.sh +132 -0
  165. package/backend/tests/local/test-public-bucket.sh +264 -264
  166. package/backend/tests/local/test-secrets.sh +249 -247
  167. package/backend/tests/local/test-serverless-functions.sh.disabled +325 -325
  168. package/backend/tests/local/test-traditional-rest.sh +208 -208
  169. package/backend/tests/manual/README.md +50 -50
  170. package/backend/tests/manual/create-large-table-simple.sql +10 -10
  171. package/backend/tests/manual/seed-large-table.sql +100 -100
  172. package/backend/tests/manual/setup-large-table-extras.sql +33 -33
  173. package/backend/tests/manual/test-bulk-upsert.sh +409 -409
  174. package/backend/tests/manual/test-database-advance.sh +296 -296
  175. package/backend/tests/manual/test-postgrest-stability.sh +191 -191
  176. package/backend/tests/manual/test-rawsql-export-import.sh +411 -411
  177. package/backend/tests/manual/test-rawsql-modes.sh +244 -0
  178. package/backend/tests/manual/test-universal-storage.sh +263 -263
  179. package/backend/tests/manual/test-users.sql +17 -17
  180. package/backend/tests/run-all-tests.sh +139 -139
  181. package/backend/tests/setup.ts +0 -0
  182. package/backend/tests/test-config.sh +338 -302
  183. package/backend/tests/unit/analyze-query.test.ts +697 -0
  184. package/backend/tests/unit/cloud-token.test.ts +48 -0
  185. package/backend/tests/unit/constant.test.ts +8 -0
  186. package/backend/tests/unit/email.test.ts +372 -0
  187. package/backend/tests/unit/environment.test.ts +59 -0
  188. package/backend/tests/unit/helpers.test.ts +63 -0
  189. package/backend/tests/unit/logger.test.ts +22 -0
  190. package/backend/tests/unit/rate-limit.test.ts +154 -0
  191. package/backend/tests/unit/response.test.ts +58 -0
  192. package/backend/tests/unit/sql-parser.test.ts +74 -0
  193. package/backend/tests/unit/uuid.test.ts +21 -0
  194. package/backend/tests/unit/validations.test.ts +80 -0
  195. package/backend/tsconfig.json +22 -22
  196. package/backend/vitest.config.ts +11 -0
  197. package/claude-plugin/.claude-plugin/plugin.json +24 -0
  198. package/claude-plugin/README.md +133 -0
  199. package/claude-plugin/skills/insforge-schema-patterns/SKILL.md +270 -0
  200. package/docker-compose.prod.yml +204 -144
  201. package/docker-compose.yml +232 -167
  202. package/docker-init/db/db-init.sql +97 -125
  203. package/docker-init/db/jwt.sql +5 -5
  204. package/docker-init/db/postgresql.conf +16 -16
  205. package/docker-init/logs/vector.yml +236 -0
  206. package/docs/README.md +44 -0
  207. package/docs/agent-docs/real-time.md +269 -0
  208. package/docs/changelog.mdx +119 -0
  209. package/docs/core-concepts/ai/architecture.mdx +373 -0
  210. package/docs/core-concepts/ai/sdk.mdx +213 -0
  211. package/docs/core-concepts/authentication/architecture.mdx +278 -0
  212. package/docs/core-concepts/authentication/sdk.mdx +414 -0
  213. package/docs/core-concepts/authentication/ui-components/customization.mdx +529 -0
  214. package/docs/core-concepts/authentication/ui-components/nextjs.mdx +221 -0
  215. package/docs/core-concepts/authentication/ui-components/react-router.mdx +184 -0
  216. package/docs/core-concepts/authentication/ui-components/react.mdx +129 -0
  217. package/docs/core-concepts/database/architecture.mdx +256 -0
  218. package/docs/core-concepts/database/sdk.mdx +382 -0
  219. package/docs/core-concepts/email/architecture.mdx +101 -0
  220. package/docs/core-concepts/email/sdk.mdx +53 -0
  221. package/docs/core-concepts/functions/architecture.mdx +105 -0
  222. package/docs/core-concepts/functions/sdk.mdx +184 -0
  223. package/docs/core-concepts/realtime/architecture.mdx +446 -0
  224. package/docs/core-concepts/realtime/sdk.mdx +409 -0
  225. package/docs/core-concepts/storage/architecture.mdx +243 -0
  226. package/docs/core-concepts/storage/sdk.mdx +253 -0
  227. package/docs/deployment/README.md +94 -0
  228. package/docs/deployment/deploy-to-aws-ec2.md +565 -0
  229. package/docs/deployment/deploy-to-azure-virtual-machines.md +313 -0
  230. package/docs/deployment/deploy-to-google-cloud-compute-engine.md +613 -0
  231. package/docs/deployment/deploy-to-render.md +441 -0
  232. package/docs/deprecated/insforge-auth-api.md +214 -214
  233. package/docs/deprecated/insforge-auth-sdk.md +99 -99
  234. package/docs/deprecated/insforge-db-api.md +358 -358
  235. package/docs/deprecated/insforge-db-sdk.md +139 -139
  236. package/docs/deprecated/insforge-debug-sdk.md +156 -156
  237. package/docs/deprecated/insforge-debug.md +64 -64
  238. package/docs/deprecated/insforge-instructions.md +123 -123
  239. package/docs/deprecated/insforge-project.md +117 -117
  240. package/docs/deprecated/insforge-storage-api.md +278 -278
  241. package/docs/deprecated/insforge-storage-sdk.md +158 -158
  242. package/docs/docs.json +232 -0
  243. package/docs/examples/framework-guides/nextjs.mdx +131 -0
  244. package/docs/examples/framework-guides/nuxt.mdx +165 -0
  245. package/docs/examples/framework-guides/react.mdx +165 -0
  246. package/docs/examples/framework-guides/svelte.mdx +153 -0
  247. package/docs/examples/framework-guides/vue.mdx +159 -0
  248. package/docs/examples/overview.mdx +67 -0
  249. package/docs/favicon.svg +19 -0
  250. package/docs/images/changelog/dec-2025/ai-integration.png +0 -0
  251. package/docs/images/changelog/dec-2025/ai-models.webp +0 -0
  252. package/docs/images/changelog/dec-2025/alipay-payment.webp +0 -0
  253. package/docs/images/changelog/dec-2025/apple-login.jpg +0 -0
  254. package/docs/images/changelog/dec-2025/mcp-installer.png +0 -0
  255. package/docs/images/changelog/dec-2025/realtime-module.jpg +0 -0
  256. package/docs/images/changelog/nov-2025/auth-components.webp +0 -0
  257. package/docs/images/changelog/nov-2025/database-metadata.webp +0 -0
  258. package/docs/images/changelog/nov-2025/quickstart-prompts.webp +0 -0
  259. package/docs/images/changelog/nov-2025/sql-editor.webp +0 -0
  260. package/docs/images/changelog/nov-2025/usage-page.webp +0 -0
  261. package/docs/images/changelog/october-2025/csv-upload.webp +0 -0
  262. package/docs/images/changelog/october-2025/logs-feature.webp +0 -0
  263. package/docs/images/changelog/october-2025/oauth-providers.webp +0 -0
  264. package/docs/images/checks-passed.png +0 -0
  265. package/docs/images/dashboard-connect-expanded.png +0 -0
  266. package/docs/images/dashboard-connect.png +0 -0
  267. package/docs/images/hero-dark.png +0 -0
  268. package/docs/images/hero-light.png +0 -0
  269. package/docs/images/icons/ai.svg +4 -0
  270. package/docs/images/icons/auth.svg +1 -0
  271. package/docs/images/icons/database.svg +1 -0
  272. package/docs/images/icons/function.svg +1 -0
  273. package/docs/images/icons/storage.svg +1 -0
  274. package/docs/images/logos/nextjs.svg +4 -0
  275. package/docs/images/logos/nuxt.svg +4 -0
  276. package/docs/images/logos/react.svg +5 -0
  277. package/docs/images/logos/svelte.svg +4 -0
  278. package/docs/images/logos/vue.svg +5 -0
  279. package/docs/images/mcp-install.png +0 -0
  280. package/docs/images/onboarding-mcp.png +0 -0
  281. package/docs/insforge-instructions-sdk.md +89 -407
  282. package/docs/introduction.mdx +45 -0
  283. package/docs/logo/dark.svg +22 -0
  284. package/docs/logo/light.svg +20 -0
  285. package/docs/partnership.mdx +652 -0
  286. package/docs/quickstart.mdx +83 -0
  287. package/docs/showcase/2048-arena.png +0 -0
  288. package/docs/showcase/framegen-cloud.png +0 -0
  289. package/docs/showcase/line-connect-race.png +0 -0
  290. package/docs/showcase/moment-vibe.png +0 -0
  291. package/docs/showcase/national-flags.png +0 -0
  292. package/docs/showcase/pokemon-vibe.png +0 -0
  293. package/docs/showcase/pure-browse-buy.png +0 -0
  294. package/docs/showcase.mdx +52 -0
  295. package/docs/snippets/sdk-installation.mdx +22 -0
  296. package/docs/snippets/service-icons.mdx +27 -0
  297. package/eslint.config.js +10 -3
  298. package/examples/oauth/frontend-oauth-example.html +250 -250
  299. package/examples/response-examples.md +443 -443
  300. package/frontend/components.json +17 -17
  301. package/frontend/package.json +69 -63
  302. package/frontend/src/App.tsx +13 -82
  303. package/frontend/src/assets/icons/checkbox_checked.svg +6 -6
  304. package/frontend/src/assets/icons/checkbox_undetermined.svg +6 -6
  305. package/frontend/src/assets/icons/checked.svg +3 -3
  306. package/frontend/src/assets/icons/connected.svg +3 -0
  307. package/frontend/src/assets/icons/error.svg +3 -3
  308. package/frontend/src/assets/icons/loader.svg +9 -0
  309. package/frontend/src/assets/icons/pencil.svg +4 -4
  310. package/frontend/src/assets/icons/refresh.svg +4 -4
  311. package/frontend/src/assets/icons/step_active.svg +3 -3
  312. package/frontend/src/assets/icons/step_inactive.svg +11 -11
  313. package/frontend/src/assets/icons/warning.svg +3 -3
  314. package/frontend/src/assets/logos/apple.svg +4 -0
  315. package/frontend/src/assets/logos/claude_code.svg +3 -3
  316. package/frontend/src/assets/logos/cline.svg +6 -6
  317. package/frontend/src/assets/logos/cursor.svg +20 -20
  318. package/frontend/src/assets/logos/discord.svg +8 -8
  319. package/frontend/src/assets/logos/facebook.svg +3 -0
  320. package/frontend/src/assets/logos/gemini.svg +19 -19
  321. package/frontend/src/assets/logos/github.svg +5 -5
  322. package/frontend/src/assets/logos/google.svg +13 -13
  323. package/frontend/src/assets/logos/grok.svg +10 -10
  324. package/frontend/src/assets/logos/insforge_dark.svg +15 -15
  325. package/frontend/src/assets/logos/insforge_light.svg +15 -15
  326. package/frontend/src/assets/logos/instagram.svg +2 -0
  327. package/frontend/src/assets/logos/linkedin.svg +3 -0
  328. package/frontend/src/assets/logos/microsoft.svg +1 -0
  329. package/frontend/src/assets/logos/openai.svg +10 -10
  330. package/frontend/src/assets/logos/roo_code.svg +9 -9
  331. package/frontend/src/assets/logos/spotify.svg +17 -0
  332. package/frontend/src/assets/logos/tiktok.svg +6 -0
  333. package/frontend/src/assets/logos/trae.svg +3 -3
  334. package/frontend/src/assets/logos/windsurf.svg +10 -10
  335. package/frontend/src/assets/logos/x.svg +3 -0
  336. package/frontend/src/components/Checkbox.tsx +27 -29
  337. package/frontend/src/components/CodeBlock.tsx +55 -2
  338. package/frontend/src/components/CodeEditor.tsx +92 -0
  339. package/frontend/src/components/ConfirmDialog.tsx +1 -1
  340. package/frontend/src/components/ConnectCTA.tsx +38 -0
  341. package/frontend/src/components/CopyButton.tsx +52 -15
  342. package/frontend/src/components/ErrorState.tsx +1 -2
  343. package/frontend/src/components/FeatureSidebar.tsx +6 -6
  344. package/frontend/src/components/FeatureSidebarItem.tsx +2 -2
  345. package/frontend/src/components/JsonHighlight.tsx +21 -9
  346. package/frontend/src/components/ProjectInfoModal.tsx +128 -0
  347. package/frontend/src/components/PromptDialog.tsx +1 -4
  348. package/frontend/src/components/SearchInput.tsx +1 -2
  349. package/frontend/src/components/Stepper.tsx +53 -0
  350. package/frontend/src/components/ThemeToggle.tsx +3 -3
  351. package/frontend/src/components/datagrid/DataGrid.tsx +25 -32
  352. package/frontend/src/components/datagrid/cell-editors/DateCellEditor.tsx +1 -2
  353. package/frontend/src/components/datagrid/cell-editors/JsonCellEditor.tsx +2 -4
  354. package/frontend/src/components/datagrid/index.ts +23 -0
  355. package/frontend/src/components/index.ts +23 -30
  356. package/frontend/src/components/layout/AppHeader.tsx +131 -91
  357. package/frontend/src/components/layout/AppSidebar.tsx +80 -170
  358. package/frontend/src/components/layout/Layout.tsx +12 -23
  359. package/frontend/src/components/layout/PrimaryMenu.tsx +187 -0
  360. package/frontend/src/components/layout/SecondaryMenu.tsx +70 -0
  361. package/frontend/src/components/layout/index.ts +5 -0
  362. package/frontend/src/components/radix/Tooltip.tsx +24 -13
  363. package/frontend/src/components/radix/index.ts +22 -0
  364. package/frontend/src/features/ai/components/AIConfigCard.tsx +129 -83
  365. package/frontend/src/features/ai/components/AIEmptyState.tsx +12 -7
  366. package/frontend/src/features/ai/components/ModalityFilterSidebar.tsx +101 -0
  367. package/frontend/src/features/ai/components/ModelSelectionDialog.tsx +135 -0
  368. package/frontend/src/features/ai/components/ModelSelectionGrid.tsx +51 -0
  369. package/frontend/src/features/ai/components/SystemPromptDialog.tsx +118 -0
  370. package/frontend/src/features/ai/components/index.ts +6 -0
  371. package/frontend/src/features/ai/helpers.ts +57 -71
  372. package/frontend/src/features/ai/hooks/useAIConfigs.ts +39 -113
  373. package/frontend/src/features/ai/hooks/useAIUsage.ts +0 -2
  374. package/frontend/src/features/ai/pages/AIPage.tsx +166 -0
  375. package/frontend/src/features/ai/services/ai.service.ts +5 -5
  376. package/frontend/src/features/auth/components/AuthPreview.tsx +96 -0
  377. package/frontend/src/features/auth/components/OAuthConfigDialog.tsx +54 -30
  378. package/frontend/src/features/auth/components/UserFormDialog.tsx +13 -6
  379. package/frontend/src/features/auth/components/UsersDataGrid.tsx +50 -14
  380. package/frontend/src/features/auth/components/index.ts +5 -0
  381. package/frontend/src/features/auth/helpers.tsx +208 -0
  382. package/frontend/src/features/auth/hooks/useAnonToken.ts +30 -0
  383. package/frontend/src/features/auth/hooks/useAuthConfig.ts +48 -0
  384. package/frontend/src/features/auth/hooks/useOAuthConfig.ts +14 -10
  385. package/frontend/src/features/auth/hooks/useUsers.ts +43 -5
  386. package/frontend/src/features/auth/index.ts +3 -2
  387. package/frontend/src/features/auth/pages/AuthMethodsPage.tsx +275 -0
  388. package/frontend/src/features/auth/pages/ConfigurationPage.tsx +395 -0
  389. package/frontend/src/features/auth/pages/UsersPage.tsx +257 -0
  390. package/frontend/src/features/auth/services/anonToken.service.ts +11 -0
  391. package/frontend/src/features/auth/services/config.service.ts +19 -0
  392. package/frontend/src/features/auth/services/{oauth.service.ts → oauth-config.service.ts} +4 -4
  393. package/frontend/src/features/auth/services/{auth.service.ts → user.service.ts} +7 -53
  394. package/frontend/src/features/dashboard/components/ConnectionSuccessBanner.tsx +35 -0
  395. package/frontend/src/features/dashboard/components/PromptCard.tsx +21 -0
  396. package/frontend/src/features/dashboard/components/PromptDialog.tsx +103 -0
  397. package/frontend/src/features/dashboard/components/StatsCard.tsx +50 -0
  398. package/frontend/src/features/dashboard/components/index.ts +4 -0
  399. package/frontend/src/features/dashboard/pages/DashboardPage.tsx +212 -0
  400. package/frontend/src/features/dashboard/prompts/ai-chatbot.ts +13 -0
  401. package/frontend/src/features/dashboard/prompts/crm-system.ts +13 -0
  402. package/frontend/src/features/dashboard/prompts/ecommerce-platform.ts +12 -0
  403. package/frontend/src/features/dashboard/prompts/index.ts +31 -0
  404. package/frontend/src/features/dashboard/prompts/instagram-clone.ts +11 -0
  405. package/frontend/src/features/dashboard/prompts/notion-clone.ts +14 -0
  406. package/frontend/src/features/dashboard/prompts/reddit-clone.ts +12 -0
  407. package/frontend/src/features/database/components/DatabaseDataGrid.tsx +48 -17
  408. package/frontend/src/features/database/components/ForeignKeyCell.tsx +15 -34
  409. package/frontend/src/features/database/components/ForeignKeyPopover.tsx +19 -20
  410. package/frontend/src/features/database/components/LinkRecordModal.tsx +120 -125
  411. package/frontend/src/features/database/components/RecordFormDialog.tsx +22 -33
  412. package/frontend/src/features/database/components/RecordFormField.tsx +45 -47
  413. package/frontend/src/features/database/components/SQLModal.tsx +75 -0
  414. package/frontend/src/features/database/components/TableEmptyState.tsx +6 -5
  415. package/frontend/src/features/database/components/TableForm.tsx +28 -19
  416. package/frontend/src/features/database/components/TableFormColumn.tsx +2 -3
  417. package/frontend/src/features/database/components/TableSidebar.tsx +1 -1
  418. package/frontend/src/features/database/components/TablesEmptyState.tsx +48 -0
  419. package/frontend/src/features/database/components/TemplateCard.tsx +37 -0
  420. package/frontend/src/features/database/components/TemplatePreview.tsx +92 -0
  421. package/frontend/src/features/database/components/index.ts +19 -0
  422. package/frontend/src/features/database/constants.ts +28 -2
  423. package/frontend/src/features/database/contexts/SQLEditorContext.tsx +188 -0
  424. package/frontend/src/features/database/helpers.ts +2 -2
  425. package/frontend/src/features/database/hooks/useCSVImport.ts +29 -0
  426. package/frontend/src/features/database/hooks/useDatabase.ts +66 -0
  427. package/frontend/src/features/database/hooks/useRawSQL.ts +55 -0
  428. package/frontend/src/features/database/hooks/useRecords.ts +139 -0
  429. package/frontend/src/features/database/hooks/useTables.ts +135 -0
  430. package/frontend/src/features/database/index.ts +7 -1
  431. package/frontend/src/features/database/pages/FunctionsPage.tsx +203 -0
  432. package/frontend/src/features/database/pages/IndexesPage.tsx +228 -0
  433. package/frontend/src/features/database/pages/PoliciesPage.tsx +237 -0
  434. package/frontend/src/features/database/pages/SQLEditorPage.tsx +382 -0
  435. package/frontend/src/features/database/{page/DatabasePage.tsx → pages/TablesPage.tsx} +168 -209
  436. package/frontend/src/features/database/pages/TemplatesPage.tsx +39 -0
  437. package/frontend/src/features/database/pages/TriggersPage.tsx +230 -0
  438. package/frontend/src/features/database/services/advance.service.ts +40 -0
  439. package/frontend/src/features/database/services/database.service.ts +33 -194
  440. package/frontend/src/features/database/services/record.service.ts +219 -0
  441. package/frontend/src/features/database/services/table.service.ts +58 -0
  442. package/frontend/src/features/database/templates/ai-chatbot.ts +402 -0
  443. package/frontend/src/features/database/templates/crm-system.ts +528 -0
  444. package/frontend/src/features/database/templates/ecommerce-platform.ts +553 -0
  445. package/frontend/src/features/database/templates/index.ts +34 -0
  446. package/frontend/src/features/database/templates/instagram-clone.ts +222 -0
  447. package/frontend/src/features/database/templates/notion-clone.ts +483 -0
  448. package/frontend/src/features/database/templates/reddit-clone.ts +526 -0
  449. package/frontend/src/features/functions/components/FunctionRow.tsx +2 -1
  450. package/frontend/src/features/functions/components/FunctionsSidebar.tsx +1 -1
  451. package/frontend/src/features/functions/components/SecretRow.tsx +1 -1
  452. package/frontend/src/features/functions/components/index.ts +5 -0
  453. package/frontend/src/features/functions/hooks/useFunctions.ts +4 -4
  454. package/frontend/src/features/{secrets → functions}/hooks/useSecrets.ts +5 -5
  455. package/frontend/src/features/functions/pages/FunctionsPage.tsx +148 -0
  456. package/frontend/src/features/functions/{components/SecretsContent.tsx → pages/SecretsPage.tsx} +19 -21
  457. package/frontend/src/features/functions/services/{functions.service.ts → function.service.ts} +2 -2
  458. package/frontend/src/features/{secrets/services/secrets.service.ts → functions/services/secret.service.ts} +2 -2
  459. package/frontend/src/features/login/hooks/usePartnerOrigin.ts +27 -0
  460. package/frontend/src/features/login/pages/CloudLoginPage.tsx +118 -0
  461. package/frontend/src/features/login/{page → pages}/LoginPage.tsx +16 -23
  462. package/frontend/src/features/login/services/partnership.service.ts +65 -0
  463. package/frontend/src/features/logs/components/LogsDataGrid.tsx +89 -0
  464. package/frontend/src/features/logs/components/SeverityBadge.tsx +18 -0
  465. package/frontend/src/features/logs/components/index.ts +2 -0
  466. package/frontend/src/features/logs/helpers.ts +24 -0
  467. package/frontend/src/features/logs/hooks/useAuditLogs.ts +4 -4
  468. package/frontend/src/features/logs/hooks/useLogSources.ts +137 -0
  469. package/frontend/src/features/logs/hooks/useLogs.ts +163 -0
  470. package/frontend/src/features/logs/hooks/useMcpUsage.ts +128 -0
  471. package/frontend/src/features/logs/index.ts +8 -2
  472. package/frontend/src/features/logs/{page → pages}/AuditsPage.tsx +91 -38
  473. package/frontend/src/features/logs/pages/LogsPage.tsx +152 -0
  474. package/frontend/src/features/logs/pages/MCPLogsPage.tsx +84 -0
  475. package/frontend/src/features/logs/services/audit.service.ts +63 -0
  476. package/frontend/src/features/logs/services/log.service.ts +15 -110
  477. package/frontend/src/features/logs/services/usage.service.ts +31 -0
  478. package/frontend/src/features/onboard/components/McpConnectionStatus.tsx +68 -0
  479. package/frontend/src/features/onboard/components/OnboardingModal.tsx +267 -0
  480. package/frontend/src/features/onboard/components/VideoDemoModal.tsx +38 -0
  481. package/frontend/src/features/onboard/components/index.ts +4 -0
  482. package/frontend/src/features/onboard/components/mcp/CursorDeeplinkGenerator.tsx +2 -2
  483. package/frontend/src/features/onboard/components/mcp/{mcp-helper.tsx → helpers.tsx} +8 -8
  484. package/frontend/src/features/onboard/components/mcp/index.ts +2 -3
  485. package/frontend/src/features/onboard/index.ts +13 -3
  486. package/frontend/src/features/realtime/components/ChannelRow.tsx +83 -0
  487. package/frontend/src/features/realtime/components/EditChannelModal.tsx +246 -0
  488. package/frontend/src/features/realtime/components/MessageRow.tsx +85 -0
  489. package/frontend/src/features/realtime/components/RealtimeEmptyState.tsx +30 -0
  490. package/frontend/src/features/realtime/hooks/useRealtime.ts +218 -0
  491. package/frontend/src/features/realtime/index.ts +11 -0
  492. package/frontend/src/features/realtime/pages/RealtimeChannelsPage.tsx +172 -0
  493. package/frontend/src/features/realtime/pages/RealtimeMessagesPage.tsx +211 -0
  494. package/frontend/src/features/realtime/pages/RealtimePermissionsPage.tsx +191 -0
  495. package/frontend/src/features/realtime/services/realtime.service.ts +107 -0
  496. package/frontend/src/features/storage/components/BucketEmptyState.tsx +9 -6
  497. package/frontend/src/features/storage/components/BucketFormDialog.tsx +25 -41
  498. package/frontend/src/features/storage/components/FilePreviewDialog.tsx +20 -8
  499. package/frontend/src/features/storage/components/StorageDataGrid.tsx +4 -3
  500. package/frontend/src/features/storage/components/StorageManager.tsx +23 -34
  501. package/frontend/src/features/storage/components/index.ts +12 -0
  502. package/frontend/src/features/storage/hooks/useStorage.ts +208 -0
  503. package/frontend/src/features/storage/{page → pages}/StoragePage.tsx +41 -143
  504. package/frontend/src/features/storage/services/storage.service.ts +22 -1
  505. package/frontend/src/features/visualizer/components/AuthNode.tsx +72 -56
  506. package/frontend/src/features/visualizer/components/BucketNode.tsx +4 -4
  507. package/frontend/src/features/visualizer/components/SchemaVisualizer.tsx +108 -80
  508. package/frontend/src/features/visualizer/components/TableNode.tsx +34 -41
  509. package/frontend/src/features/visualizer/components/VisualizerSkeleton.tsx +12 -4
  510. package/frontend/src/features/visualizer/pages/VisualizerPage.tsx +97 -0
  511. package/frontend/src/index.css +1 -0
  512. package/frontend/src/lib/analytics/posthog.tsx +27 -0
  513. package/frontend/src/lib/contexts/AuthContext.tsx +38 -31
  514. package/frontend/src/lib/contexts/SocketContext.tsx +123 -80
  515. package/frontend/src/{features/metadata → lib}/hooks/useMetadata.ts +1 -1
  516. package/frontend/src/lib/hooks/useToast.tsx +6 -2
  517. package/frontend/src/lib/routing/AppRoutes.tsx +99 -0
  518. package/frontend/src/lib/routing/RequireAuth.tsx +27 -0
  519. package/frontend/src/lib/utils/cloudMessaging.ts +20 -0
  520. package/frontend/src/lib/utils/menuItems.ts +207 -0
  521. package/frontend/src/lib/utils/{validation-schemas.ts → schemaValidations.ts} +10 -5
  522. package/frontend/src/lib/utils/utils.ts +32 -1
  523. package/frontend/src/vite-env.d.ts +1 -0
  524. package/frontend/tsconfig.json +25 -25
  525. package/frontend/tsconfig.node.json +9 -9
  526. package/frontend/vite.config.ts +5 -3
  527. package/functions/deno.json +24 -24
  528. package/functions/server.ts +315 -290
  529. package/functions/worker-template.js +15 -4
  530. package/i18n/README.ar.md +130 -0
  531. package/i18n/README.de.md +130 -0
  532. package/i18n/README.es.md +154 -0
  533. package/i18n/README.fr.md +134 -0
  534. package/i18n/README.hi.md +129 -0
  535. package/i18n/README.ja.md +174 -0
  536. package/i18n/README.ko.md +137 -0
  537. package/i18n/README.pt-BR.md +131 -0
  538. package/i18n/README.ru.md +129 -0
  539. package/i18n/README.zh-CN.md +133 -0
  540. package/openapi/ai.yaml +715 -688
  541. package/openapi/auth.yaml +1244 -563
  542. package/openapi/email.yaml +158 -0
  543. package/openapi/functions.yaml +475 -475
  544. package/openapi/health.yaml +29 -29
  545. package/openapi/logs.yaml +223 -223
  546. package/openapi/metadata.yaml +177 -177
  547. package/openapi/realtime.yaml +699 -0
  548. package/openapi/records.yaml +381 -381
  549. package/openapi/secrets.yaml +370 -370
  550. package/openapi/storage.yaml +875 -875
  551. package/openapi/tables.yaml +463 -463
  552. package/package.json +97 -88
  553. package/shared-schemas/package.json +31 -31
  554. package/shared-schemas/src/ai-api.schema.ts +34 -58
  555. package/shared-schemas/src/ai.schema.ts +63 -54
  556. package/shared-schemas/src/auth-api.schema.ts +352 -193
  557. package/shared-schemas/src/auth.schema.ts +43 -7
  558. package/shared-schemas/src/cloud-events.schema.ts +57 -0
  559. package/shared-schemas/src/database-api.schema.ts +35 -4
  560. package/shared-schemas/src/database.schema.ts +40 -1
  561. package/shared-schemas/src/docs.schema.ts +26 -0
  562. package/shared-schemas/src/email-api.schema.ts +30 -0
  563. package/shared-schemas/src/index.ts +5 -0
  564. package/shared-schemas/src/logs-api.schema.ts +7 -1
  565. package/shared-schemas/src/logs.schema.ts +26 -0
  566. package/shared-schemas/src/metadata.schema.ts +18 -4
  567. package/shared-schemas/src/realtime-api.schema.ts +111 -0
  568. package/shared-schemas/src/realtime.schema.ts +143 -0
  569. package/shared-schemas/tsconfig.json +21 -21
  570. package/tsconfig.json +7 -7
  571. package/zeabur/README.md +13 -0
  572. package/zeabur/template.yml +1032 -0
  573. package/.github/workflows/deploy-aws.yml +0 -130
  574. package/backend/src/api/routes/agent.ts +0 -29
  575. package/backend/src/api/routes/auth.oauth.ts +0 -482
  576. package/backend/src/api/routes/auth.ts +0 -386
  577. package/backend/src/api/routes/docs.ts +0 -66
  578. package/backend/src/api/routes/functions.ts +0 -183
  579. package/backend/src/api/routes/openapi.ts +0 -82
  580. package/backend/src/api/routes/usage.ts +0 -96
  581. package/backend/src/core/ai/client.ts +0 -242
  582. package/backend/src/core/ai/model.ts +0 -117
  583. package/backend/src/core/auth/auth.ts +0 -780
  584. package/backend/src/core/database/manager.ts +0 -178
  585. package/backend/src/core/database/table.ts +0 -772
  586. package/backend/src/core/documentation/agent.ts +0 -689
  587. package/backend/src/core/documentation/openapi.ts +0 -856
  588. package/backend/src/core/logs/analytics.ts +0 -76
  589. package/backend/src/core/logs/providers/localdb.provider.ts +0 -246
  590. package/backend/src/core/socket/socket.ts +0 -388
  591. package/backend/src/core/storage/storage.ts +0 -923
  592. package/backend/src/utils/cloud-token.ts +0 -39
  593. package/backend/src/utils/helpers.ts +0 -49
  594. package/backend/src/utils/uuid.ts +0 -9
  595. package/backend/tests/manual/test-better-auth.sh +0 -303
  596. package/docker-init/db/logs.sql +0 -9
  597. package/frontend/README.md +0 -112
  598. package/frontend/src/components/datagrid/index.tsx +0 -20
  599. package/frontend/src/components/layout/CloudLayout.tsx +0 -95
  600. package/frontend/src/features/ai/components/AIConfigDialog.tsx +0 -76
  601. package/frontend/src/features/ai/components/AIConfigForm.tsx +0 -222
  602. package/frontend/src/features/ai/components/fields/ModalityField.tsx +0 -87
  603. package/frontend/src/features/ai/components/fields/ModelSelectionField.tsx +0 -134
  604. package/frontend/src/features/ai/components/fields/SystemPromptField.tsx +0 -33
  605. package/frontend/src/features/ai/page/AIPage.tsx +0 -178
  606. package/frontend/src/features/auth/components/AddOAuthDialog.tsx +0 -106
  607. package/frontend/src/features/auth/components/AuthMethodTab.tsx +0 -238
  608. package/frontend/src/features/auth/components/UsersTab.tsx +0 -114
  609. package/frontend/src/features/auth/page/AuthenticationPage.tsx +0 -169
  610. package/frontend/src/features/dashboard/page/DashboardPage.tsx +0 -194
  611. package/frontend/src/features/database/hooks/UseLinkModal.tsx +0 -78
  612. package/frontend/src/features/functions/components/FunctionViewer.tsx +0 -46
  613. package/frontend/src/features/functions/components/FunctionsContent.tsx +0 -88
  614. package/frontend/src/features/functions/page/FunctionsPage.tsx +0 -28
  615. package/frontend/src/features/login/components/AuthErrorBoundary.tsx +0 -87
  616. package/frontend/src/features/login/components/PrivateRoute.tsx +0 -24
  617. package/frontend/src/features/login/page/CloudLoginPage.tsx +0 -93
  618. package/frontend/src/features/logs/components/AnalyticsLogsTable.tsx +0 -313
  619. package/frontend/src/features/logs/components/LogsTable.tsx +0 -199
  620. package/frontend/src/features/logs/page/AnalyticsLogsPage.tsx +0 -530
  621. package/frontend/src/features/metadata/index.ts +0 -0
  622. package/frontend/src/features/metadata/page/MetadataPage.tsx +0 -136
  623. package/frontend/src/features/onboard/components/CompletionCard.tsx +0 -41
  624. package/frontend/src/features/onboard/components/OnboardButton.tsx +0 -84
  625. package/frontend/src/features/onboard/components/StepContent.tsx +0 -91
  626. package/frontend/src/features/onboard/components/TestConnectionStep.tsx +0 -53
  627. package/frontend/src/features/onboard/components/mcp/McpInstallation.tsx +0 -144
  628. package/frontend/src/features/onboard/page/OnBoardPage.tsx +0 -104
  629. package/frontend/src/features/onboard/types.ts +0 -8
  630. package/frontend/src/features/visualizer/page/VisualizerPage.tsx +0 -127
  631. package/frontend/src/lib/contexts/OnboardStepContext.tsx +0 -68
  632. package/frontend/src/lib/hooks/useOnboardingCompletion.ts +0 -29
  633. /package/backend/src/api/{middleware → middlewares}/error.ts +0 -0
  634. /package/backend/src/api/{middleware → middlewares}/upload.ts +0 -0
  635. /package/frontend/src/{features/metadata → lib}/services/metadata.service.ts +0 -0
@@ -0,0 +1,380 @@
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 OpenRouterProvider {
45
+ private static instance: OpenRouterProvider;
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(): OpenRouterProvider {
55
+ if (!OpenRouterProvider.instance) {
56
+ OpenRouterProvider.instance = new OpenRouterProvider();
57
+ }
58
+ return OpenRouterProvider.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
+ * Used internally by sendRequest()
104
+ */
105
+ private 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
+ // Get fresh client with renewed API key
353
+ const renewedClient = await this.getClient();
354
+
355
+ // Retry with exponential backoff (3 attempts)
356
+ const maxRetries = 3;
357
+
358
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
359
+ try {
360
+ const backoffMs = Math.pow(2, attempt - 1) * 1000; // 1s, 2s, 4s
361
+ logger.info(
362
+ `Retrying request after renewal (attempt ${attempt}/${maxRetries}), waiting ${backoffMs}ms...`
363
+ );
364
+ await new Promise((resolve) => setTimeout(resolve, backoffMs));
365
+
366
+ const result = await request(renewedClient);
367
+ logger.info('Request succeeded after API key renewal');
368
+ return result;
369
+ } catch (retryError) {
370
+ if (attempt === maxRetries) {
371
+ logger.error(`All ${maxRetries} retry attempts failed after API key renewal`);
372
+ throw retryError;
373
+ }
374
+ }
375
+ }
376
+ }
377
+ throw error;
378
+ }
379
+ }
380
+ }
@@ -0,0 +1,38 @@
1
+ import { EmailTemplate } from '@/types/email.js';
2
+ import { SendRawEmailRequest } from '@insforge/shared-schemas';
3
+
4
+ /**
5
+ * Email provider interface
6
+ * Defines the contract that all email providers must implement
7
+ */
8
+ export interface EmailProvider {
9
+ /**
10
+ * Initialize the email provider (optional)
11
+ */
12
+ initialize?(): void | Promise<void>;
13
+
14
+ /**
15
+ * Send email using predefined template
16
+ * @param email - Recipient email address
17
+ * @param name - Recipient name
18
+ * @param template - Template type
19
+ * @param variables - Variables to use in the email template
20
+ */
21
+ sendWithTemplate(
22
+ email: string,
23
+ name: string,
24
+ template: EmailTemplate,
25
+ variables?: Record<string, string>
26
+ ): Promise<void>;
27
+
28
+ /**
29
+ * Send custom/raw email (optional - not all providers may support this)
30
+ * @param options - Email options (to, subject, html, cc, bcc, from, replyTo)
31
+ */
32
+ sendRaw?(options: SendRawEmailRequest): Promise<void>;
33
+
34
+ /**
35
+ * Check if provider supports template-based emails
36
+ */
37
+ supportsTemplates(): boolean;
38
+ }
@@ -0,0 +1,271 @@
1
+ import jwt from 'jsonwebtoken';
2
+ import axios from 'axios';
3
+ import { config } from '@/infra/config/app.config.js';
4
+ import logger from '@/utils/logger.js';
5
+ import { AppError } from '@/api/middlewares/error.js';
6
+ import { ERROR_CODES } from '@/types/error-constants.js';
7
+ import { EmailTemplate } from '@/types/email.js';
8
+ import { SendRawEmailRequest } from '@insforge/shared-schemas';
9
+ import { EmailProvider } from './base.provider.js';
10
+
11
+ /**
12
+ * Cloud email provider for sending emails via Insforge cloud backend
13
+ */
14
+ export class CloudEmailProvider implements EmailProvider {
15
+ /**
16
+ * Generate JWT sign token for cloud API authentication
17
+ * @returns JWT token signed with project secret
18
+ */
19
+ private generateSignToken(): string {
20
+ const projectId = config.cloud.projectId;
21
+ const jwtSecret = config.app.jwtSecret;
22
+
23
+ if (!projectId || projectId === 'local') {
24
+ throw new AppError(
25
+ 'PROJECT_ID is not configured. Cannot send emails without cloud project setup.',
26
+ 500,
27
+ ERROR_CODES.INTERNAL_ERROR
28
+ );
29
+ }
30
+
31
+ if (!jwtSecret) {
32
+ throw new AppError(
33
+ 'JWT_SECRET is not configured. Cannot generate sign token.',
34
+ 500,
35
+ ERROR_CODES.INTERNAL_ERROR
36
+ );
37
+ }
38
+
39
+ const payload = {
40
+ sub: projectId,
41
+ };
42
+
43
+ return jwt.sign(payload, jwtSecret, {
44
+ expiresIn: '10m', // Short-lived token for API request
45
+ });
46
+ }
47
+
48
+ /**
49
+ * Check if provider supports templates
50
+ */
51
+ supportsTemplates(): boolean {
52
+ return true;
53
+ }
54
+
55
+ /**
56
+ * Send email using predefined template
57
+ * @param email - Recipient email address
58
+ * @param name - Recipient name
59
+ * @param template - Template type (email-verification or reset-password)
60
+ * @param variables - Variables to use in the email template
61
+ * @returns Promise that resolves when email is sent successfully
62
+ */
63
+ async sendWithTemplate(
64
+ email: string,
65
+ name: string,
66
+ template: EmailTemplate,
67
+ variables?: Record<string, string>
68
+ ): Promise<void> {
69
+ try {
70
+ const projectId = config.cloud.projectId;
71
+ const apiHost = config.cloud.apiHost;
72
+ const signToken = this.generateSignToken();
73
+
74
+ // Validate inputs
75
+ if (!email || !name || !template) {
76
+ throw new AppError(
77
+ 'Missing required parameters for sending email',
78
+ 400,
79
+ ERROR_CODES.INVALID_INPUT
80
+ );
81
+ }
82
+
83
+ const validTemplates: EmailTemplate[] = [
84
+ 'email-verification-code',
85
+ 'email-verification-link',
86
+ 'reset-password-code',
87
+ 'reset-password-link',
88
+ ];
89
+ if (!validTemplates.includes(template)) {
90
+ throw new AppError(
91
+ `Invalid template type: ${template}. Must be one of: ${validTemplates.join(', ')}`,
92
+ 400,
93
+ ERROR_CODES.INVALID_INPUT
94
+ );
95
+ }
96
+
97
+ const url = `${apiHost}/email/v1/${projectId}/send-with-template`;
98
+ const response = await axios.post(
99
+ url,
100
+ {
101
+ email,
102
+ name,
103
+ template,
104
+ variables,
105
+ },
106
+ {
107
+ headers: {
108
+ 'Content-Type': 'application/json',
109
+ sign: signToken,
110
+ },
111
+ timeout: 10000, // 10 second timeout
112
+ }
113
+ );
114
+
115
+ if (response.data?.success) {
116
+ logger.info('Email sent successfully', {
117
+ projectId,
118
+ template,
119
+ });
120
+ } else {
121
+ throw new AppError(
122
+ 'Email service returned unsuccessful response',
123
+ 500,
124
+ ERROR_CODES.INTERNAL_ERROR
125
+ );
126
+ }
127
+ } catch (error) {
128
+ // Handle axios errors
129
+ if (axios.isAxiosError(error)) {
130
+ const status = error.response?.status;
131
+ const message = error.response?.data?.message || error.message;
132
+
133
+ logger.error('Failed to send email via cloud backend', {
134
+ projectId: config.cloud.projectId,
135
+ template,
136
+ status,
137
+ message,
138
+ error: error.response?.data,
139
+ });
140
+
141
+ // Provide more specific error messages
142
+ if (status === 401 || status === 403) {
143
+ throw new AppError(
144
+ 'Authentication failed with cloud email service. Check PROJECT_ID and JWT_SECRET.',
145
+ status,
146
+ ERROR_CODES.AUTH_UNAUTHORIZED
147
+ );
148
+ } else if (status === 429) {
149
+ throw new AppError(
150
+ 'Email rate limit exceeded. Free plans are limited to 3000 emails per month.',
151
+ status,
152
+ ERROR_CODES.RATE_LIMITED
153
+ );
154
+ } else if (status === 400) {
155
+ throw new AppError(
156
+ `Invalid email request: ${message}`,
157
+ status,
158
+ ERROR_CODES.INVALID_INPUT
159
+ );
160
+ } else {
161
+ throw new AppError(
162
+ `Failed to send email: ${message}`,
163
+ status || 500,
164
+ ERROR_CODES.INTERNAL_ERROR
165
+ );
166
+ }
167
+ }
168
+
169
+ // Re-throw AppError
170
+ if (error instanceof AppError) {
171
+ throw error;
172
+ }
173
+
174
+ // Handle other errors
175
+ logger.error('Unexpected error sending email', {
176
+ projectId: config.cloud.projectId,
177
+ template,
178
+ error: error instanceof Error ? error.message : 'Unknown error',
179
+ });
180
+
181
+ throw new AppError(
182
+ `Failed to send email: ${error instanceof Error ? error.message : 'Unknown error'}`,
183
+ 500,
184
+ ERROR_CODES.INTERNAL_ERROR
185
+ );
186
+ }
187
+ }
188
+
189
+ /**
190
+ * Send custom/raw email via cloud backend
191
+ */
192
+ async sendRaw(options: SendRawEmailRequest): Promise<void> {
193
+ try {
194
+ const projectId = config.cloud.projectId;
195
+ const apiHost = config.cloud.apiHost;
196
+ const signToken = this.generateSignToken();
197
+
198
+ const url = `${apiHost}/email/v1/${projectId}/send-on-demand`;
199
+ const response = await axios.post(url, options, {
200
+ headers: {
201
+ 'Content-Type': 'application/json',
202
+ sign: signToken,
203
+ },
204
+ timeout: 10000,
205
+ });
206
+
207
+ if (response.data?.success) {
208
+ logger.info('Raw email sent successfully', { projectId });
209
+ } else {
210
+ throw new AppError(
211
+ 'Email service returned unsuccessful response',
212
+ 500,
213
+ ERROR_CODES.INTERNAL_ERROR
214
+ );
215
+ }
216
+ } catch (error) {
217
+ if (axios.isAxiosError(error)) {
218
+ const status = error.response?.status;
219
+ const message = error.response?.data?.message || error.message;
220
+
221
+ logger.error('Failed to send raw email via cloud backend', {
222
+ projectId: config.cloud.projectId,
223
+ status,
224
+ message,
225
+ });
226
+
227
+ if (status === 401) {
228
+ throw new AppError(
229
+ 'Authentication failed with cloud email service.',
230
+ status,
231
+ ERROR_CODES.AUTH_UNAUTHORIZED
232
+ );
233
+ } else if (status === 403) {
234
+ throw new AppError(
235
+ 'Custom email service is not available for free plan. Please upgrade to use this feature.',
236
+ status,
237
+ ERROR_CODES.FORBIDDEN
238
+ );
239
+ } else if (status === 429) {
240
+ throw new AppError(
241
+ 'Email rate limit exceeded. Starter plan is limited 10 emails per hour, and Pro plan is limited 50 emails per hour',
242
+ status,
243
+ ERROR_CODES.RATE_LIMITED
244
+ );
245
+ } else if (status === 400) {
246
+ throw new AppError(
247
+ `Invalid email request: ${message}`,
248
+ status,
249
+ ERROR_CODES.INVALID_INPUT
250
+ );
251
+ } else {
252
+ throw new AppError(
253
+ `Failed to send email: ${message}`,
254
+ status || 500,
255
+ ERROR_CODES.INTERNAL_ERROR
256
+ );
257
+ }
258
+ }
259
+
260
+ if (error instanceof AppError) {
261
+ throw error;
262
+ }
263
+
264
+ throw new AppError(
265
+ `Failed to send email: ${error instanceof Error ? error.message : 'Unknown error'}`,
266
+ 500,
267
+ ERROR_CODES.INTERNAL_ERROR
268
+ );
269
+ }
270
+ }
271
+ }