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,250 @@
1
+ import { Pool } from 'pg';
2
+ import { DatabaseManager } from '@/infra/database/database.manager.js';
3
+ import { AppError } from '@/api/middlewares/error.js';
4
+ import { ERROR_CODES } from '@/types/error-constants.js';
5
+ import logger from '@/utils/logger.js';
6
+ import type { AuthConfigSchema, UpdateAuthConfigRequest } from '@insforge/shared-schemas';
7
+
8
+ export class AuthConfigService {
9
+ private static instance: AuthConfigService;
10
+ private pool: Pool | null = null;
11
+
12
+ private constructor() {
13
+ logger.info('AuthConfigService initialized');
14
+ }
15
+
16
+ public static getInstance(): AuthConfigService {
17
+ if (!AuthConfigService.instance) {
18
+ AuthConfigService.instance = new AuthConfigService();
19
+ }
20
+ return AuthConfigService.instance;
21
+ }
22
+
23
+ private getPool(): Pool {
24
+ if (!this.pool) {
25
+ this.pool = DatabaseManager.getInstance().getPool();
26
+ }
27
+ return this.pool;
28
+ }
29
+
30
+ /**
31
+ * Get public authentication configuration (safe for public API)
32
+ * Returns all configuration fields except metadata (id, created_at, updated_at)
33
+ */
34
+ async getPublicAuthConfig() {
35
+ try {
36
+ const result = await this.getPool().query(
37
+ `SELECT
38
+ require_email_verification as "requireEmailVerification",
39
+ password_min_length as "passwordMinLength",
40
+ require_number as "requireNumber",
41
+ require_lowercase as "requireLowercase",
42
+ require_uppercase as "requireUppercase",
43
+ require_special_char as "requireSpecialChar",
44
+ verify_email_method as "verifyEmailMethod",
45
+ reset_password_method as "resetPasswordMethod"
46
+ FROM _auth_configs
47
+ LIMIT 1`
48
+ );
49
+
50
+ // If no config exists, return fallback values
51
+ if (!result.rows.length) {
52
+ logger.warn('No auth config found, returning default fallback values');
53
+ return {
54
+ requireEmailVerification: false,
55
+ passwordMinLength: 6,
56
+ requireNumber: false,
57
+ requireLowercase: false,
58
+ requireUppercase: false,
59
+ requireSpecialChar: false,
60
+ verifyEmailMethod: 'code' as const,
61
+ resetPasswordMethod: 'code' as const,
62
+ };
63
+ }
64
+
65
+ return result.rows[0];
66
+ } catch (error) {
67
+ logger.error('Failed to get public auth config', { error });
68
+ throw new AppError(
69
+ 'Failed to get authentication configuration',
70
+ 500,
71
+ ERROR_CODES.INTERNAL_ERROR
72
+ );
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Get authentication configuration
78
+ * Returns the singleton configuration row with all columns
79
+ */
80
+ async getAuthConfig(): Promise<AuthConfigSchema> {
81
+ try {
82
+ const result = await this.getPool().query(
83
+ `SELECT
84
+ id,
85
+ require_email_verification as "requireEmailVerification",
86
+ password_min_length as "passwordMinLength",
87
+ require_number as "requireNumber",
88
+ require_lowercase as "requireLowercase",
89
+ require_uppercase as "requireUppercase",
90
+ require_special_char as "requireSpecialChar",
91
+ verify_email_method as "verifyEmailMethod",
92
+ reset_password_method as "resetPasswordMethod",
93
+ sign_in_redirect_to as "signInRedirectTo",
94
+ created_at as "createdAt",
95
+ updated_at as "updatedAt"
96
+ FROM _auth_configs
97
+ LIMIT 1`
98
+ );
99
+
100
+ // If no config exists, return fallback values
101
+ if (!result.rows.length) {
102
+ logger.warn('No auth config found, returning default fallback values');
103
+ // Return a config with fallback values and generate a temporary ID
104
+ return {
105
+ id: '00000000-0000-0000-0000-000000000000',
106
+ requireEmailVerification: false,
107
+ passwordMinLength: 6,
108
+ requireNumber: false,
109
+ requireLowercase: false,
110
+ requireUppercase: false,
111
+ requireSpecialChar: false,
112
+ verifyEmailMethod: 'code' as const,
113
+ resetPasswordMethod: 'code' as const,
114
+ signInRedirectTo: null,
115
+ createdAt: new Date().toISOString(),
116
+ updatedAt: new Date().toISOString(),
117
+ };
118
+ }
119
+
120
+ return result.rows[0];
121
+ } catch (error) {
122
+ logger.error('Failed to get auth config', { error });
123
+ throw new AppError(
124
+ 'Failed to get authentication configuration',
125
+ 500,
126
+ ERROR_CODES.INTERNAL_ERROR
127
+ );
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Update authentication configuration
133
+ * Updates the singleton configuration row
134
+ */
135
+ async updateAuthConfig(input: UpdateAuthConfigRequest): Promise<AuthConfigSchema> {
136
+ const client = await this.getPool().connect();
137
+ try {
138
+ await client.query('BEGIN');
139
+
140
+ // Ensure config exists and lock row to prevent concurrent modifications
141
+ const existingResult = await client.query('SELECT id FROM _auth_configs LIMIT 1 FOR UPDATE');
142
+
143
+ if (!existingResult.rows.length) {
144
+ // Config doesn't exist, rollback and throw error
145
+ // The migration should have created the default config
146
+ await client.query('ROLLBACK');
147
+ throw new AppError(
148
+ 'Authentication configuration not found. Please run migrations.',
149
+ 500,
150
+ ERROR_CODES.INTERNAL_ERROR
151
+ );
152
+ }
153
+
154
+ // Build update query
155
+ const updates: string[] = [];
156
+ const values: (string | number | boolean | null)[] = [];
157
+ let paramCount = 1;
158
+
159
+ if (input.requireEmailVerification !== undefined) {
160
+ updates.push(`require_email_verification = $${paramCount++}`);
161
+ values.push(input.requireEmailVerification);
162
+ }
163
+
164
+ if (input.passwordMinLength !== undefined) {
165
+ updates.push(`password_min_length = $${paramCount++}`);
166
+ values.push(input.passwordMinLength);
167
+ }
168
+
169
+ if (input.requireNumber !== undefined) {
170
+ updates.push(`require_number = $${paramCount++}`);
171
+ values.push(input.requireNumber);
172
+ }
173
+
174
+ if (input.requireLowercase !== undefined) {
175
+ updates.push(`require_lowercase = $${paramCount++}`);
176
+ values.push(input.requireLowercase);
177
+ }
178
+
179
+ if (input.requireUppercase !== undefined) {
180
+ updates.push(`require_uppercase = $${paramCount++}`);
181
+ values.push(input.requireUppercase);
182
+ }
183
+
184
+ if (input.requireSpecialChar !== undefined) {
185
+ updates.push(`require_special_char = $${paramCount++}`);
186
+ values.push(input.requireSpecialChar);
187
+ }
188
+
189
+ if (input.verifyEmailMethod !== undefined) {
190
+ updates.push(`verify_email_method = $${paramCount++}`);
191
+ values.push(input.verifyEmailMethod);
192
+ }
193
+
194
+ if (input.resetPasswordMethod !== undefined) {
195
+ updates.push(`reset_password_method = $${paramCount++}`);
196
+ values.push(input.resetPasswordMethod);
197
+ }
198
+
199
+ if (input.signInRedirectTo !== undefined) {
200
+ updates.push(`sign_in_redirect_to = $${paramCount++}`);
201
+ values.push(input.signInRedirectTo);
202
+ }
203
+
204
+ if (!updates.length) {
205
+ await client.query('COMMIT');
206
+ // Return current config if no updates
207
+ return await this.getAuthConfig();
208
+ }
209
+
210
+ // Add updated_at to updates
211
+ updates.push('updated_at = NOW()');
212
+
213
+ const result = await client.query(
214
+ `UPDATE _auth_configs
215
+ SET ${updates.join(', ')}
216
+ RETURNING
217
+ id,
218
+ require_email_verification as "requireEmailVerification",
219
+ password_min_length as "passwordMinLength",
220
+ require_number as "requireNumber",
221
+ require_lowercase as "requireLowercase",
222
+ require_uppercase as "requireUppercase",
223
+ require_special_char as "requireSpecialChar",
224
+ verify_email_method as "verifyEmailMethod",
225
+ reset_password_method as "resetPasswordMethod",
226
+ sign_in_redirect_to as "signInRedirectTo",
227
+ created_at as "createdAt",
228
+ updated_at as "updatedAt"`,
229
+ values
230
+ );
231
+
232
+ await client.query('COMMIT');
233
+ logger.info('Auth config updated', { updatedFields: Object.keys(input) });
234
+ return result.rows[0];
235
+ } catch (error) {
236
+ await client.query('ROLLBACK');
237
+ logger.error('Failed to update auth config', { error });
238
+ if (error instanceof AppError) {
239
+ throw error;
240
+ }
241
+ throw new AppError(
242
+ 'Failed to update authentication configuration',
243
+ 500,
244
+ ERROR_CODES.INTERNAL_ERROR
245
+ );
246
+ } finally {
247
+ client.release();
248
+ }
249
+ }
250
+ }
@@ -0,0 +1,424 @@
1
+ import { Pool, PoolClient } from 'pg';
2
+ import bcrypt from 'bcryptjs';
3
+ import crypto from 'crypto';
4
+ import { DatabaseManager } from '@/infra/database/database.manager.js';
5
+ import { AppError } from '@/api/middlewares/error.js';
6
+ import { ERROR_CODES } from '@/types/error-constants.js';
7
+ import logger from '@/utils/logger.js';
8
+ import { generateNumericCode, generateSecureToken } from '@/utils/utils.js';
9
+
10
+ /**
11
+ * OTP purpose types - used to categorize different OTP use cases
12
+ */
13
+ export enum OTPPurpose {
14
+ VERIFY_EMAIL = 'VERIFY_EMAIL',
15
+ RESET_PASSWORD = 'RESET_PASSWORD',
16
+ }
17
+
18
+ /**
19
+ * Token type - determines token format and expiration
20
+ */
21
+ export enum OTPType {
22
+ NUMERIC_CODE = 'NUMERIC_CODE', // Short 6-digit numeric code for manual entry
23
+ HASH_TOKEN = 'HASH_TOKEN', // Long cryptographic token with hash-based lookup
24
+ }
25
+
26
+ /**
27
+ * Result of OTP creation
28
+ */
29
+ export interface CreateOTPResult {
30
+ success: boolean;
31
+ otp: string;
32
+ expiresAt: Date;
33
+ }
34
+
35
+ /**
36
+ * Result of OTP verification
37
+ */
38
+ export interface VerifyOTPResult {
39
+ success: boolean;
40
+ email: string;
41
+ purpose: OTPPurpose;
42
+ }
43
+
44
+ /**
45
+ * Service for managing email-based one-time passwords (OTPs)
46
+ *
47
+ * Supports two delivery methods:
48
+ * 1. Short numeric codes (6 digits) - displayed in email for manual entry
49
+ * - Stored as bcrypt hash (defense against brute force if DB compromised)
50
+ * - Brute force protection handled by API-level rate limiting
51
+ * 2. Long cryptographic tokens (64 chars) - embedded in clickable links for one-click verification
52
+ * - Stored as SHA-256 hash (high entropy makes bcrypt unnecessary, allows direct lookup)
53
+ *
54
+ * The dual hashing strategy balances security and performance:
55
+ * - NUMERIC_CODE: Low entropy (10^6 combinations) requires slow bcrypt + API rate limiting
56
+ * - HASH_TOKEN: High entropy (2^256 combinations) only needs fast SHA-256
57
+ */
58
+ export class AuthOTPService {
59
+ private static instance: AuthOTPService;
60
+ private pool: Pool | null = null;
61
+
62
+ // Configuration constants
63
+ private readonly NUMERIC_CODE_LENGTH = 6; // 6 digits = 1 million combinations
64
+ private readonly NUMERIC_CODE_EXPIRY_MINUTES = 15; // 15 minutes expiry for numeric codes
65
+ private readonly HASH_TOKEN_BYTES = 32; // 32 bytes = 64 hex characters = 256 bits entropy
66
+ private readonly HASH_TOKEN_EXPIRY_HOURS = 24; // 24 hours expiry for hash tokens
67
+ private readonly BCRYPT_SALT_ROUNDS = 10; // Salt rounds for numeric codes (2^10 iterations)
68
+
69
+ private constructor() {
70
+ logger.info('AuthOTPService initialized');
71
+ }
72
+
73
+ public static getInstance(): AuthOTPService {
74
+ if (!AuthOTPService.instance) {
75
+ AuthOTPService.instance = new AuthOTPService();
76
+ }
77
+ return AuthOTPService.instance;
78
+ }
79
+
80
+ private getPool(): Pool {
81
+ if (!this.pool) {
82
+ this.pool = DatabaseManager.getInstance().getPool();
83
+ }
84
+ return this.pool;
85
+ }
86
+
87
+ /**
88
+ * Create or update an email OTP
89
+ * Supports both short numeric codes (for manual entry) and long cryptographic tokens (for clickable links)
90
+ * Uses upsert to ensure only one active token exists per email/purpose combination
91
+ *
92
+ * Hashing strategy:
93
+ * - NUMERIC_CODE: Uses bcrypt (slow hash) due to low entropy (10^6 combinations)
94
+ * - HASH_TOKEN: Uses SHA-256 (fast hash) - high entropy (2^256) makes bcrypt unnecessary
95
+ *
96
+ * @param email - The email address for the token
97
+ * @param purpose - The purpose of the token (e.g., 'VERIFY_EMAIL', 'RESET_PASSWORD')
98
+ * @param otpType - The type of token to generate ('NUMERIC_CODE' or 'HASH_TOKEN')
99
+ * @returns Promise with creation result including the token and expiry time
100
+ */
101
+ async createEmailOTP(
102
+ email: string,
103
+ purpose: OTPPurpose,
104
+ otpType: OTPType = OTPType.NUMERIC_CODE
105
+ ): Promise<CreateOTPResult> {
106
+ try {
107
+ // Generate token based on type
108
+ let otp: string;
109
+ let expiresAt: Date;
110
+ let otpHash: string;
111
+
112
+ if (otpType === OTPType.NUMERIC_CODE) {
113
+ // Generate 6-digit numeric code for manual entry
114
+ otp = generateNumericCode(this.NUMERIC_CODE_LENGTH);
115
+ expiresAt = new Date(Date.now() + this.NUMERIC_CODE_EXPIRY_MINUTES * 60 * 1000);
116
+ // Use bcrypt for low-entropy codes (defense against brute force)
117
+ otpHash = await bcrypt.hash(otp, this.BCRYPT_SALT_ROUNDS);
118
+ } else {
119
+ // Generate cryptographically secure token for hash-based lookup
120
+ otp = generateSecureToken(this.HASH_TOKEN_BYTES);
121
+ expiresAt = new Date(Date.now() + this.HASH_TOKEN_EXPIRY_HOURS * 60 * 60 * 1000);
122
+ // Use SHA-256 for high-entropy tokens (enables direct lookup)
123
+ otpHash = crypto.createHash('sha256').update(otp).digest('hex');
124
+ }
125
+
126
+ // Upsert token record - insert or update if email+purpose combination already exists
127
+ // This ensures only one active token per email/purpose (replaces any existing token)
128
+ await this.getPool().query(
129
+ `INSERT INTO _email_otps (email, purpose, otp_hash, expires_at, consumed_at)
130
+ VALUES ($1, $2, $3, $4, NULL)
131
+ ON CONFLICT (email, purpose)
132
+ DO UPDATE SET
133
+ otp_hash = EXCLUDED.otp_hash,
134
+ expires_at = EXCLUDED.expires_at,
135
+ consumed_at = NULL,
136
+ updated_at = NOW()`,
137
+ [email, purpose, otpHash, expiresAt]
138
+ );
139
+
140
+ logger.info('Email verification token created successfully', {
141
+ purpose,
142
+ otpType,
143
+ expiresAt: expiresAt.toISOString(),
144
+ });
145
+
146
+ return {
147
+ success: true,
148
+ otp,
149
+ expiresAt,
150
+ };
151
+ } catch (error) {
152
+ logger.error('Failed to create email verification token', { error, purpose, otpType });
153
+ throw new AppError('Failed to create verification token', 500, ERROR_CODES.INTERNAL_ERROR);
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Verify a numeric OTP code (6 digits)
159
+ * Looks up by email and verifies the bcrypt-hashed code
160
+ *
161
+ * Brute force protection is handled by API-level rate limiting.
162
+ *
163
+ * @param email - The email address associated with the OTP
164
+ * @param purpose - The purpose of the OTP
165
+ * @param code - The 6-digit numeric code to verify
166
+ * @param externalClient - Optional external database client for transaction support
167
+ * @returns Promise with verification result
168
+ * @throws AppError if verification fails (with generic error message)
169
+ */
170
+ async verifyEmailOTPWithCode(
171
+ email: string,
172
+ purpose: OTPPurpose,
173
+ code: string,
174
+ externalClient?: PoolClient
175
+ ): Promise<VerifyOTPResult> {
176
+ const client = externalClient || (await this.getPool().connect());
177
+ const shouldManageTransaction = !externalClient;
178
+
179
+ try {
180
+ if (shouldManageTransaction) {
181
+ await client.query('BEGIN');
182
+ }
183
+
184
+ // Lookup by email and lock the row
185
+ const result = await client.query(
186
+ `SELECT id, email, purpose, otp_hash, expires_at, consumed_at
187
+ FROM _email_otps
188
+ WHERE email = $1 AND purpose = $2
189
+ FOR UPDATE`,
190
+ [email, purpose]
191
+ );
192
+
193
+ // Check if OTP record exists
194
+ if (result.rows.length === 0) {
195
+ throw new AppError('Invalid or expired verification code', 400, ERROR_CODES.INVALID_INPUT);
196
+ }
197
+
198
+ const otpRecord = result.rows[0];
199
+
200
+ // Validate OTP record is still usable
201
+ if (new Date() > new Date(otpRecord.expires_at) || otpRecord.consumed_at !== null) {
202
+ throw new AppError('Invalid or expired verification code', 400, ERROR_CODES.INVALID_INPUT);
203
+ }
204
+
205
+ // Verify bcrypt hash
206
+ const isValid = await bcrypt.compare(code, otpRecord.otp_hash);
207
+
208
+ if (!isValid) {
209
+ throw new AppError('Invalid or expired verification code', 400, ERROR_CODES.INVALID_INPUT);
210
+ }
211
+
212
+ // Mark OTP as consumed atomically
213
+ const consume = await client.query(
214
+ `UPDATE _email_otps
215
+ SET consumed_at = NOW(), updated_at = NOW()
216
+ WHERE id = $1 AND consumed_at IS NULL`,
217
+ [otpRecord.id]
218
+ );
219
+
220
+ if (consume.rowCount !== 1) {
221
+ throw new AppError('Invalid or expired verification code', 400, ERROR_CODES.INVALID_INPUT);
222
+ }
223
+
224
+ if (shouldManageTransaction) {
225
+ await client.query('COMMIT');
226
+ }
227
+
228
+ logger.info('Numeric OTP code verified successfully', { purpose });
229
+
230
+ return {
231
+ success: true,
232
+ email: otpRecord.email,
233
+ purpose: otpRecord.purpose,
234
+ };
235
+ } catch (error) {
236
+ if (shouldManageTransaction) {
237
+ await client.query('ROLLBACK');
238
+ }
239
+
240
+ if (error instanceof AppError) {
241
+ throw error;
242
+ }
243
+
244
+ logger.error('Failed to verify numeric OTP code', { error, purpose });
245
+ throw new AppError('Failed to verify code', 500, ERROR_CODES.INTERNAL_ERROR);
246
+ } finally {
247
+ if (shouldManageTransaction) {
248
+ client.release();
249
+ }
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Verify a hash token (64 hex characters)
255
+ * Performs direct lookup using SHA-256 hash without knowing the email
256
+ *
257
+ * @param purpose - The purpose of the OTP
258
+ * @param token - The 64-character hex token to verify
259
+ * @param externalClient - Optional external database client for transaction support
260
+ * @returns Promise with verification result including the associated email
261
+ * @throws AppError if verification fails (with generic error message)
262
+ */
263
+ async verifyEmailOTPWithToken(
264
+ purpose: OTPPurpose,
265
+ token: string,
266
+ externalClient?: PoolClient
267
+ ): Promise<VerifyOTPResult> {
268
+ const client = externalClient || (await this.getPool().connect());
269
+ const shouldManageTransaction = !externalClient;
270
+
271
+ try {
272
+ if (shouldManageTransaction) {
273
+ await client.query('BEGIN');
274
+ }
275
+
276
+ // Hash the token and perform direct lookup
277
+ const tokenHash = crypto.createHash('sha256').update(token).digest('hex');
278
+
279
+ // Direct lookup by hash - O(1) with index on otp_hash
280
+ const result = await client.query(
281
+ `SELECT id, email, purpose, otp_hash, expires_at, consumed_at
282
+ FROM _email_otps
283
+ WHERE purpose = $1
284
+ AND otp_hash = $2
285
+ AND expires_at > NOW()
286
+ AND consumed_at IS NULL
287
+ FOR UPDATE`,
288
+ [purpose, tokenHash]
289
+ );
290
+
291
+ // Check if token exists and is valid
292
+ if (result.rows.length === 0) {
293
+ throw new AppError('Invalid or expired verification token', 400, ERROR_CODES.INVALID_INPUT);
294
+ }
295
+
296
+ const otpRecord = result.rows[0];
297
+
298
+ // Mark OTP as consumed atomically
299
+ const consume = await client.query(
300
+ `UPDATE _email_otps
301
+ SET consumed_at = NOW(), updated_at = NOW()
302
+ WHERE id = $1 AND consumed_at IS NULL`,
303
+ [otpRecord.id]
304
+ );
305
+
306
+ if (consume.rowCount !== 1) {
307
+ throw new AppError('Invalid or expired verification token', 400, ERROR_CODES.INVALID_INPUT);
308
+ }
309
+
310
+ if (shouldManageTransaction) {
311
+ await client.query('COMMIT');
312
+ }
313
+
314
+ logger.info('Hash token verified successfully', { purpose });
315
+
316
+ return {
317
+ success: true,
318
+ email: otpRecord.email,
319
+ purpose: otpRecord.purpose,
320
+ };
321
+ } catch (error) {
322
+ if (shouldManageTransaction) {
323
+ await client.query('ROLLBACK');
324
+ }
325
+
326
+ if (error instanceof AppError) {
327
+ throw error;
328
+ }
329
+
330
+ logger.error('Failed to verify hash token', { error, purpose });
331
+ throw new AppError('Failed to verify token', 500, ERROR_CODES.INTERNAL_ERROR);
332
+ } finally {
333
+ if (shouldManageTransaction) {
334
+ client.release();
335
+ }
336
+ }
337
+ }
338
+
339
+ /**
340
+ * Exchange a verified numeric code for a long-lived hash token
341
+ * This is a common pattern in multi-step verification flows:
342
+ * 1. User receives numeric code via email
343
+ * 2. User submits code to verify
344
+ * 3. System issues a long-lived token for subsequent operations
345
+ *
346
+ * The entire exchange happens atomically within a single transaction to ensure:
347
+ * - Numeric code is consumed only if token creation succeeds
348
+ * - No race conditions between verification and token issuance
349
+ *
350
+ * Example use cases:
351
+ * - Password reset: verify code → get reset token → reset password
352
+ * - Email verification: verify code → get session token → auto-login
353
+ *
354
+ * @param email - The email address associated with the code
355
+ * @param purpose - The purpose of the OTP (e.g., RESET_PASSWORD)
356
+ * @param numericCode - The 6-digit numeric code to verify
357
+ * @param externalClient - Optional external database client for broader transaction support
358
+ * @returns Promise with the long-lived token and its expiration
359
+ * @throws AppError if verification fails or token creation fails
360
+ */
361
+ async exchangeCodeForToken(
362
+ email: string,
363
+ purpose: OTPPurpose,
364
+ numericCode: string,
365
+ externalClient?: PoolClient
366
+ ): Promise<{ token: string; expiresAt: Date }> {
367
+ const client = externalClient || (await this.getPool().connect());
368
+ const shouldManageTransaction = !externalClient;
369
+ let transactionActive = false;
370
+
371
+ try {
372
+ if (shouldManageTransaction) {
373
+ await client.query('BEGIN');
374
+ transactionActive = true;
375
+ }
376
+
377
+ // Step 1: Verify the numeric code (consumes it atomically)
378
+ await this.verifyEmailOTPWithCode(email, purpose, numericCode, client);
379
+
380
+ // Step 2: Generate a long-lived hash token
381
+ const token = generateSecureToken(this.HASH_TOKEN_BYTES);
382
+ const expiresAt = new Date(Date.now() + this.HASH_TOKEN_EXPIRY_HOURS * 60 * 60 * 1000);
383
+ const tokenHash = crypto.createHash('sha256').update(token).digest('hex');
384
+
385
+ // Step 3: Insert the new token (replaces the consumed numeric code)
386
+ // Uses upsert to overwrite the consumed code record with the new token
387
+ await client.query(
388
+ `INSERT INTO _email_otps (email, purpose, otp_hash, expires_at, consumed_at)
389
+ VALUES ($1, $2, $3, $4, NULL)
390
+ ON CONFLICT (email, purpose)
391
+ DO UPDATE SET
392
+ otp_hash = EXCLUDED.otp_hash,
393
+ expires_at = EXCLUDED.expires_at,
394
+ consumed_at = NULL,
395
+ updated_at = NOW()`,
396
+ [email, purpose, tokenHash, expiresAt]
397
+ );
398
+
399
+ if (shouldManageTransaction) {
400
+ await client.query('COMMIT');
401
+ transactionActive = false;
402
+ }
403
+
404
+ logger.info('Successfully exchanged numeric code for hash token', { email, purpose });
405
+
406
+ return { token, expiresAt };
407
+ } catch (error) {
408
+ if (shouldManageTransaction && transactionActive) {
409
+ await client.query('ROLLBACK');
410
+ }
411
+
412
+ if (error instanceof AppError) {
413
+ throw error;
414
+ }
415
+
416
+ logger.error('Failed to exchange code for token', { error, email, purpose });
417
+ throw new AppError('Failed to exchange verification code', 500, ERROR_CODES.INTERNAL_ERROR);
418
+ } finally {
419
+ if (shouldManageTransaction) {
420
+ client.release();
421
+ }
422
+ }
423
+ }
424
+ }