insforge 1.2.10 → 1.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (506) hide show
  1. package/.claude-plugin/marketplace.json +20 -20
  2. package/.dockerignore +60 -60
  3. package/.env.example +83 -77
  4. package/.github/ISSUE_TEMPLATE/bug_report.yml +36 -36
  5. package/.github/ISSUE_TEMPLATE/config.yml +11 -11
  6. package/.github/ISSUE_TEMPLATE/feature_request.yml +26 -26
  7. package/.github/PULL_REQUEST_TEMPLATE.md +7 -7
  8. package/.github/copilot-instructions.md +146 -146
  9. package/.github/workflows/build-image.yml +65 -65
  10. package/.github/workflows/ci-premerge-check.yml +23 -23
  11. package/.github/workflows/e2e.yml +63 -63
  12. package/.github/workflows/lint-and-format.yml +32 -32
  13. package/.prettierignore +64 -64
  14. package/CHANGELOG.md +46 -44
  15. package/CLAUDE_PLUGIN.md +104 -104
  16. package/CODE_OF_CONDUCT.md +128 -128
  17. package/CONTRIBUTING.md +125 -125
  18. package/Dockerfile +30 -30
  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 -182
  23. package/assets/Dark.svg +23 -23
  24. package/auth/package.json +30 -28
  25. package/auth/src/lib/broadcastService.ts +4 -4
  26. package/auth/src/lib/insforge.ts +8 -0
  27. package/auth/src/main.tsx +2 -4
  28. package/auth/src/pages/SignInPage.tsx +5 -2
  29. package/auth/src/pages/SignUpPage.tsx +5 -2
  30. package/auth/src/pages/VerifyEmailPage.tsx +18 -0
  31. package/auth/tsconfig.json +33 -32
  32. package/auth/tsconfig.node.json +11 -11
  33. package/backend/package.json +82 -75
  34. package/backend/src/api/middlewares/rate-limiters.ts +127 -127
  35. package/backend/src/api/routes/ai/index.routes.ts +475 -468
  36. package/backend/src/api/routes/auth/index.routes.ts +720 -570
  37. package/backend/src/api/routes/auth/oauth.routes.ts +478 -448
  38. package/backend/src/api/routes/database/advance.routes.ts +37 -16
  39. package/backend/src/api/routes/database/index.routes.ts +80 -1
  40. package/backend/src/api/routes/database/records.routes.ts +48 -184
  41. package/backend/src/api/routes/database/rpc.routes.ts +69 -0
  42. package/backend/src/api/routes/database/tables.routes.ts +0 -14
  43. package/backend/src/api/routes/deployments/index.routes.ts +192 -0
  44. package/backend/src/api/routes/docs/index.routes.ts +76 -76
  45. package/backend/src/api/routes/email/index.routes.ts +35 -0
  46. package/backend/src/api/routes/functions/index.routes.ts +21 -15
  47. package/backend/src/api/routes/metadata/index.routes.ts +38 -0
  48. package/backend/src/api/routes/realtime/channels.routes.ts +81 -0
  49. package/backend/src/api/routes/realtime/index.routes.ts +12 -0
  50. package/backend/src/api/routes/realtime/messages.routes.ts +48 -0
  51. package/backend/src/api/routes/realtime/permissions.routes.ts +19 -0
  52. package/backend/src/api/routes/storage/index.routes.ts +18 -12
  53. package/backend/src/api/routes/usage/index.routes.ts +6 -4
  54. package/backend/src/api/routes/webhooks/index.routes.ts +109 -0
  55. package/backend/src/infra/database/database.manager.ts +14 -11
  56. package/backend/src/infra/database/migrations/000_create-base-tables.sql +141 -141
  57. package/backend/src/infra/database/migrations/001_create-helper-functions.sql +40 -40
  58. package/backend/src/infra/database/migrations/002_rename-auth-tables.sql +29 -29
  59. package/backend/src/infra/database/migrations/003_create-users-table.sql +55 -55
  60. package/backend/src/infra/database/migrations/004_add-reload-postgrest-func.sql +23 -23
  61. package/backend/src/infra/database/migrations/005_enable-project-admin-modify-users.sql +29 -29
  62. package/backend/src/infra/database/migrations/006_modify-ai-usage-table.sql +24 -24
  63. package/backend/src/infra/database/migrations/007_drop-metadata-table.sql +1 -1
  64. package/backend/src/infra/database/migrations/008_add-system-tables.sql +76 -76
  65. package/backend/src/infra/database/migrations/009_add-function-secrets.sql +23 -23
  66. package/backend/src/infra/database/migrations/010_modify-ai-config-modalities.sql +93 -93
  67. package/backend/src/infra/database/migrations/011_refactor-secrets-table.sql +15 -15
  68. package/backend/src/infra/database/migrations/012_add-storage-uploaded-by.sql +7 -7
  69. package/backend/src/infra/database/migrations/013_create-auth-schema-functions.sql +44 -44
  70. package/backend/src/infra/database/migrations/014_add-updated-at-trigger-user-table.sql +7 -7
  71. package/backend/src/infra/database/migrations/015_create-auth-config-and-email-otp-tables.sql +59 -59
  72. package/backend/src/infra/database/migrations/016_update-auth-config-and-email-otp.sql +24 -24
  73. package/backend/src/infra/database/migrations/017_create-realtime-schema.sql +233 -0
  74. package/backend/src/infra/database/migrations/018_schema-rework.sql +441 -0
  75. package/backend/src/infra/database/migrations/019_create-deployments-table.sql +36 -0
  76. package/backend/src/infra/database/migrations/020_add-audio-modality.sql +11 -0
  77. package/backend/src/infra/database/migrations/bootstrap/bootstrap-migrations.js +103 -0
  78. package/backend/src/infra/realtime/realtime.manager.ts +246 -0
  79. package/backend/src/infra/realtime/webhook-sender.ts +82 -0
  80. package/backend/src/infra/security/token.manager.ts +216 -125
  81. package/backend/src/infra/socket/socket.manager.ts +198 -64
  82. package/backend/src/providers/ai/openrouter.provider.ts +24 -12
  83. package/backend/src/providers/database/base.provider.ts +39 -0
  84. package/backend/src/providers/database/cloud.provider.ts +159 -0
  85. package/backend/src/providers/deployments/vercel.provider.ts +516 -0
  86. package/backend/src/providers/email/base.provider.ts +4 -7
  87. package/backend/src/providers/email/cloud.provider.ts +84 -0
  88. package/backend/src/providers/oauth/apple.provider.ts +266 -0
  89. package/backend/src/providers/oauth/index.ts +1 -0
  90. package/backend/src/server.ts +329 -284
  91. package/backend/src/services/ai/ai-config.service.ts +6 -6
  92. package/backend/src/services/ai/ai-model.service.ts +60 -60
  93. package/backend/src/services/ai/ai-usage.service.ts +7 -7
  94. package/backend/src/services/ai/chat-completion.service.ts +415 -220
  95. package/backend/src/services/ai/helpers.ts +64 -64
  96. package/backend/src/services/ai/image-generation.service.ts +3 -3
  97. package/backend/src/services/ai/index.ts +13 -13
  98. package/backend/src/services/auth/auth-config.service.ts +4 -4
  99. package/backend/src/services/auth/auth-otp.service.ts +6 -6
  100. package/backend/src/services/auth/auth.service.ts +148 -74
  101. package/backend/src/services/auth/index.ts +4 -4
  102. package/backend/src/services/auth/oauth-config.service.ts +12 -12
  103. package/backend/src/services/database/database-advance.service.ts +19 -55
  104. package/backend/src/services/database/database-table.service.ts +38 -94
  105. package/backend/src/services/database/database.service.ts +127 -0
  106. package/backend/src/services/database/postgrest-proxy.service.ts +165 -0
  107. package/backend/src/services/deployments/deployment.service.ts +693 -0
  108. package/backend/src/services/email/email.service.ts +5 -7
  109. package/backend/src/services/functions/function.service.ts +61 -41
  110. package/backend/src/services/logs/audit.service.ts +10 -10
  111. package/backend/src/services/realtime/index.ts +3 -0
  112. package/backend/src/services/realtime/realtime-auth.service.ts +104 -0
  113. package/backend/src/services/realtime/realtime-channel.service.ts +237 -0
  114. package/backend/src/services/realtime/realtime-message.service.ts +260 -0
  115. package/backend/src/services/secrets/secret.service.ts +101 -27
  116. package/backend/src/services/storage/storage.service.ts +30 -30
  117. package/backend/src/services/usage/usage.service.ts +6 -6
  118. package/backend/src/types/ai.ts +8 -0
  119. package/backend/src/types/auth.ts +16 -1
  120. package/backend/src/types/database.ts +2 -0
  121. package/backend/src/types/deployments.ts +33 -0
  122. package/backend/src/types/realtime.ts +18 -0
  123. package/backend/src/types/socket.ts +7 -31
  124. package/backend/src/types/storage.ts +1 -1
  125. package/backend/src/types/webhooks.ts +45 -0
  126. package/backend/src/utils/cookies.ts +34 -0
  127. package/backend/src/utils/environment.ts +0 -14
  128. package/backend/src/utils/s3-config-loader.ts +64 -0
  129. package/backend/src/utils/seed.ts +79 -43
  130. package/backend/src/utils/sql-parser.ts +216 -0
  131. package/backend/src/utils/utils.ts +114 -114
  132. package/backend/src/utils/validations.ts +10 -10
  133. package/backend/tests/README.md +133 -133
  134. package/backend/tests/cleanup-all-test-data.sh +230 -230
  135. package/backend/tests/cloud/test-s3-multitenant.sh +131 -131
  136. package/backend/tests/local/comprehensive-curl-tests.sh +155 -155
  137. package/backend/tests/local/test-ai-config.sh +129 -129
  138. package/backend/tests/local/test-ai-usage.sh +80 -80
  139. package/backend/tests/local/test-auth-router.sh +143 -143
  140. package/backend/tests/local/test-database-router.sh +222 -222
  141. package/backend/tests/local/test-e2e.sh +240 -240
  142. package/backend/tests/local/test-fk-errors.sh +96 -96
  143. package/backend/tests/local/test-functions.sh +123 -123
  144. package/backend/tests/local/test-id-field.sh +200 -200
  145. package/backend/tests/local/test-logs.sh +132 -132
  146. package/backend/tests/local/test-public-bucket.sh +264 -264
  147. package/backend/tests/local/test-rpc.sh +141 -0
  148. package/backend/tests/local/test-secrets.sh +249 -249
  149. package/backend/tests/local/test-serverless-functions.sh.disabled +325 -325
  150. package/backend/tests/local/test-traditional-rest.sh +208 -208
  151. package/backend/tests/manual/README.md +50 -50
  152. package/backend/tests/manual/create-large-table-simple.sql +10 -10
  153. package/backend/tests/manual/seed-large-table.sql +100 -100
  154. package/backend/tests/manual/setup-large-table-extras.sql +33 -33
  155. package/backend/tests/manual/test-ai-model-plugins.sh +258 -0
  156. package/backend/tests/manual/test-bulk-upsert.sh +409 -409
  157. package/backend/tests/manual/test-database-advance.sh +296 -296
  158. package/backend/tests/manual/test-postgrest-stability.sh +191 -191
  159. package/backend/tests/manual/test-rawsql-export-import.sh +411 -411
  160. package/backend/tests/manual/test-rawsql-modes.sh +244 -244
  161. package/backend/tests/manual/test-universal-storage.sh +263 -263
  162. package/backend/tests/manual/test-users.sql +17 -17
  163. package/backend/tests/run-all-tests.sh +139 -139
  164. package/backend/tests/setup.ts +0 -0
  165. package/backend/tests/test-config.sh +338 -338
  166. package/backend/tests/unit/analyze-query.test.ts +697 -0
  167. package/backend/tests/unit/database-advance.test.ts +326 -0
  168. package/backend/tests/unit/helpers.test.ts +2 -2
  169. package/backend/tsconfig.json +22 -22
  170. package/claude-plugin/.claude-plugin/plugin.json +24 -24
  171. package/claude-plugin/README.md +133 -133
  172. package/claude-plugin/skills/insforge-schema-patterns/SKILL.md +273 -270
  173. package/docker-compose.prod.yml +204 -200
  174. package/docker-compose.yml +232 -228
  175. package/docker-init/db/db-init.sql +97 -97
  176. package/docker-init/db/jwt.sql +5 -5
  177. package/docker-init/db/postgresql.conf +16 -16
  178. package/docker-init/logs/vector.yml +236 -236
  179. package/docs/README.md +44 -44
  180. package/docs/agent-docs/deployment.md +79 -0
  181. package/docs/agent-docs/real-time.md +269 -0
  182. package/docs/changelog.mdx +212 -67
  183. package/docs/core-concepts/ai/architecture.mdx +350 -372
  184. package/docs/core-concepts/ai/sdk.mdx +238 -213
  185. package/docs/core-concepts/authentication/architecture.mdx +276 -278
  186. package/docs/core-concepts/authentication/sdk.mdx +710 -414
  187. package/docs/core-concepts/authentication/ui-components/customization.mdx +733 -529
  188. package/docs/core-concepts/authentication/ui-components/nextjs.mdx +247 -221
  189. package/docs/core-concepts/authentication/ui-components/react-router.mdx +183 -184
  190. package/docs/core-concepts/authentication/ui-components/react.mdx +136 -129
  191. package/docs/core-concepts/database/architecture.mdx +292 -255
  192. package/docs/core-concepts/database/pgvector.mdx +138 -0
  193. package/docs/core-concepts/database/sdk.mdx +382 -382
  194. package/docs/core-concepts/deployments/architecture.mdx +152 -0
  195. package/docs/core-concepts/email/architecture.mdx +103 -0
  196. package/docs/core-concepts/email/sdk.mdx +53 -0
  197. package/docs/core-concepts/functions/architecture.mdx +105 -105
  198. package/docs/core-concepts/functions/sdk.mdx +183 -184
  199. package/docs/core-concepts/realtime/architecture.mdx +446 -0
  200. package/docs/core-concepts/realtime/sdk.mdx +409 -0
  201. package/docs/core-concepts/storage/architecture.mdx +243 -243
  202. package/docs/core-concepts/storage/sdk.mdx +253 -253
  203. package/docs/deployment/README.md +94 -94
  204. package/docs/deployment/deploy-to-aws-ec2.md +564 -564
  205. package/docs/deployment/deploy-to-azure-virtual-machines.md +312 -312
  206. package/docs/deployment/deploy-to-google-cloud-compute-engine.md +613 -613
  207. package/docs/deployment/deploy-to-render.md +441 -441
  208. package/docs/deprecated/insforge-auth-api.md +214 -214
  209. package/docs/deprecated/insforge-auth-sdk.md +99 -99
  210. package/docs/deprecated/insforge-db-api.md +358 -358
  211. package/docs/deprecated/insforge-db-sdk.md +139 -139
  212. package/docs/deprecated/insforge-debug-sdk.md +156 -156
  213. package/docs/deprecated/insforge-debug.md +64 -64
  214. package/docs/deprecated/insforge-instructions.md +123 -123
  215. package/docs/deprecated/insforge-project.md +117 -117
  216. package/docs/deprecated/insforge-storage-api.md +278 -278
  217. package/docs/deprecated/insforge-storage-sdk.md +158 -158
  218. package/docs/docs.json +240 -210
  219. package/docs/examples/framework-guides/nextjs.mdx +131 -131
  220. package/docs/examples/framework-guides/nuxt.mdx +165 -165
  221. package/docs/examples/framework-guides/react.mdx +165 -165
  222. package/docs/examples/framework-guides/svelte.mdx +153 -153
  223. package/docs/examples/framework-guides/vue.mdx +159 -159
  224. package/docs/examples/overview.mdx +67 -67
  225. package/docs/favicon.png +0 -0
  226. package/docs/favicon.svg +4 -19
  227. package/docs/images/changelog/dec-2025/ai-integration.png +0 -0
  228. package/docs/images/changelog/dec-2025/ai-models.webp +0 -0
  229. package/docs/images/changelog/dec-2025/alipay-payment.webp +0 -0
  230. package/docs/images/changelog/dec-2025/apple-login.jpg +0 -0
  231. package/docs/images/changelog/dec-2025/apple-oauth.mp4 +0 -0
  232. package/docs/images/changelog/dec-2025/mcp-installer.png +0 -0
  233. package/docs/images/changelog/dec-2025/moreModels.png +0 -0
  234. package/docs/images/changelog/dec-2025/multi-region.webp +0 -0
  235. package/docs/images/changelog/dec-2025/postgres-connection.webp +0 -0
  236. package/docs/images/changelog/dec-2025/realtime-module.jpg +0 -0
  237. package/docs/images/changelog/dec-2025/realtime2.png +0 -0
  238. package/docs/images/icons/ai.svg +4 -4
  239. package/docs/images/logos/nextjs.svg +4 -4
  240. package/docs/images/logos/nuxt.svg +4 -4
  241. package/docs/images/logos/react.svg +5 -5
  242. package/docs/images/logos/svelte.svg +4 -4
  243. package/docs/images/logos/vue.svg +5 -5
  244. package/docs/images/mcp-setup/CC-MCP-1.mp4 +0 -0
  245. package/docs/images/mcp-setup/CC-MCP-2.mp4 +0 -0
  246. package/docs/images/mcp-setup/Cursor-MCP-1.mp4 +0 -0
  247. package/docs/images/mcp-setup/Cursor-MCP-2.mp4 +0 -0
  248. package/docs/images/mcp-setup/Cursor-MCP-3.mp4 +0 -0
  249. package/docs/images/mcp-setup/claude-code-connect.png +0 -0
  250. package/docs/images/mcp-setup/cline-1.png +0 -0
  251. package/docs/images/mcp-setup/cline-2.png +0 -0
  252. package/docs/images/mcp-setup/cline-3.png +0 -0
  253. package/docs/images/mcp-setup/connect-project.png +0 -0
  254. package/docs/images/mcp-setup/copilot-1.png +0 -0
  255. package/docs/images/mcp-setup/copilot-2.png +0 -0
  256. package/docs/images/mcp-setup/copilot-3.png +0 -0
  257. package/docs/images/mcp-setup/mcp-json-1.png +0 -0
  258. package/docs/images/mcp-setup/mcp-json-2.png +0 -0
  259. package/docs/images/mcp-setup/qoder-1.png +0 -0
  260. package/docs/images/mcp-setup/qoder-2.png +0 -0
  261. package/docs/images/mcp-setup/roocode-1.png +0 -0
  262. package/docs/images/mcp-setup/roocode-2.png +0 -0
  263. package/docs/images/mcp-setup/trae-1.png +0 -0
  264. package/docs/images/mcp-setup/trae-2.png +0 -0
  265. package/docs/images/mcp-setup/trae-3.png +0 -0
  266. package/docs/images/mcp-setup/trae-4.png +0 -0
  267. package/docs/images/mcp-setup/trae-5.png +0 -0
  268. package/docs/images/mcp-setup/windsurf-1.png +0 -0
  269. package/docs/images/mcp-setup/windsurf-2.png +0 -0
  270. package/docs/insforge-instructions-sdk.md +93 -88
  271. package/docs/introduction.mdx +46 -45
  272. package/docs/logo/dark.svg +22 -22
  273. package/docs/logo/light.svg +20 -20
  274. package/docs/mcp-setup.mdx +332 -0
  275. package/docs/oauth-server.mdx +563 -0
  276. package/docs/partnership.mdx +720 -646
  277. package/docs/quickstart.mdx +82 -82
  278. package/docs/showcase.mdx +52 -52
  279. package/docs/snippets/sdk-installation.mdx +21 -21
  280. package/docs/snippets/service-icons.mdx +27 -27
  281. package/docs/vscode-extension.mdx +74 -0
  282. package/eslint.config.js +1 -0
  283. package/examples/oauth/frontend-oauth-example.html +250 -250
  284. package/examples/response-examples.md +443 -443
  285. package/frontend/components.json +17 -17
  286. package/frontend/package.json +69 -69
  287. package/frontend/src/App.tsx +8 -3
  288. package/frontend/src/assets/icons/checkbox_checked.svg +6 -6
  289. package/frontend/src/assets/icons/checkbox_undetermined.svg +6 -6
  290. package/frontend/src/assets/icons/checked.svg +3 -3
  291. package/frontend/src/assets/icons/connected.svg +3 -3
  292. package/frontend/src/assets/icons/error.svg +3 -3
  293. package/frontend/src/assets/icons/loader.svg +9 -9
  294. package/frontend/src/assets/icons/pencil.svg +4 -4
  295. package/frontend/src/assets/icons/refresh.svg +4 -4
  296. package/frontend/src/assets/icons/step_active.svg +3 -3
  297. package/frontend/src/assets/icons/step_inactive.svg +11 -11
  298. package/frontend/src/assets/icons/warning.svg +3 -3
  299. package/frontend/src/assets/logos/antigravity.svg +1 -0
  300. package/frontend/src/assets/logos/apple.svg +3 -3
  301. package/frontend/src/assets/logos/claude_code.svg +3 -3
  302. package/frontend/src/assets/logos/cline.svg +6 -6
  303. package/frontend/src/assets/logos/copilot.svg +10 -0
  304. package/frontend/src/assets/logos/cursor.svg +20 -20
  305. package/frontend/src/assets/logos/deepseek.svg +139 -0
  306. package/frontend/src/assets/logos/discord.svg +8 -8
  307. package/frontend/src/assets/logos/facebook.svg +3 -3
  308. package/frontend/src/assets/logos/gemini.svg +19 -19
  309. package/frontend/src/assets/logos/github.svg +5 -5
  310. package/frontend/src/assets/logos/google.svg +13 -13
  311. package/frontend/src/assets/logos/grok.svg +10 -10
  312. package/frontend/src/assets/logos/insforge_dark.svg +15 -15
  313. package/frontend/src/assets/logos/insforge_light.svg +15 -15
  314. package/frontend/src/assets/logos/instagram.svg +1 -1
  315. package/frontend/src/assets/logos/kiro.svg +9 -0
  316. package/frontend/src/assets/logos/linkedin.svg +3 -3
  317. package/frontend/src/assets/logos/openai.svg +10 -10
  318. package/frontend/src/assets/logos/qoder.svg +4 -0
  319. package/frontend/src/assets/logos/qwen.svg +15 -0
  320. package/frontend/src/assets/logos/roo_code.svg +9 -9
  321. package/frontend/src/assets/logos/spotify.svg +16 -16
  322. package/frontend/src/assets/logos/tiktok.svg +5 -5
  323. package/frontend/src/assets/logos/trae.svg +3 -3
  324. package/frontend/src/assets/logos/windsurf.svg +10 -10
  325. package/frontend/src/assets/logos/x.svg +3 -3
  326. package/frontend/src/components/CodeBlock.tsx +2 -2
  327. package/frontend/src/components/ConnectCTA.tsx +3 -2
  328. package/frontend/src/components/datagrid/DataGrid.tsx +90 -62
  329. package/frontend/src/components/datagrid/datagridTypes.tsx +2 -1
  330. package/frontend/src/components/datagrid/index.ts +1 -1
  331. package/frontend/src/components/index.ts +0 -1
  332. package/frontend/src/components/layout/AppHeader.tsx +13 -37
  333. package/frontend/src/components/layout/AppSidebar.tsx +85 -100
  334. package/frontend/src/components/layout/Layout.tsx +34 -32
  335. package/frontend/src/components/layout/PrimaryMenu.tsx +12 -4
  336. package/frontend/src/components/radix/Select.tsx +151 -151
  337. package/frontend/src/features/ai/components/AIConfigCard.tsx +200 -200
  338. package/frontend/src/features/ai/components/AIEmptyState.tsx +23 -23
  339. package/frontend/src/features/ai/components/ModalityFilterSidebar.tsx +102 -101
  340. package/frontend/src/features/ai/components/ModelSelectionDialog.tsx +135 -135
  341. package/frontend/src/features/ai/components/ModelSelectionGrid.tsx +51 -51
  342. package/frontend/src/features/ai/components/SystemPromptDialog.tsx +118 -118
  343. package/frontend/src/features/ai/components/index.ts +6 -6
  344. package/frontend/src/features/ai/helpers.ts +147 -141
  345. package/frontend/src/features/ai/{page → pages}/AIPage.tsx +166 -166
  346. package/frontend/src/features/auth/components/AuthPreview.tsx +96 -96
  347. package/frontend/src/features/auth/components/OAuthConfigDialog.tsx +1 -0
  348. package/frontend/src/features/auth/components/UsersDataGrid.tsx +61 -31
  349. package/frontend/src/features/auth/components/index.ts +5 -5
  350. package/frontend/src/features/auth/helpers.tsx +8 -0
  351. package/frontend/src/features/auth/{page → pages}/AuthMethodsPage.tsx +275 -275
  352. package/frontend/src/features/auth/{page → pages}/UsersPage.tsx +0 -28
  353. package/frontend/src/features/dashboard/{page → pages}/DashboardPage.tsx +1 -1
  354. package/frontend/src/features/database/components/DatabaseDataGrid.tsx +0 -2
  355. package/frontend/src/features/database/components/ForeignKeyCell.tsx +38 -11
  356. package/frontend/src/features/database/components/ForeignKeyPopover.tsx +18 -8
  357. package/frontend/src/features/database/components/LinkRecordModal.tsx +61 -13
  358. package/frontend/src/features/database/components/RecordFormField.tsx +1 -1
  359. package/frontend/src/features/database/components/SQLModal.tsx +75 -0
  360. package/frontend/src/features/database/components/TableForm.tsx +0 -4
  361. package/frontend/src/features/database/components/TableSidebar.tsx +0 -3
  362. package/frontend/src/features/database/components/TablesEmptyState.tsx +1 -1
  363. package/frontend/src/features/database/components/TemplatePreview.tsx +1 -2
  364. package/frontend/src/features/database/constants.ts +16 -28
  365. package/frontend/src/features/database/hooks/useCSVImport.ts +3 -2
  366. package/frontend/src/features/database/hooks/useDatabase.ts +66 -0
  367. package/frontend/src/features/database/hooks/useRawSQL.ts +3 -2
  368. package/frontend/src/features/database/hooks/useTables.ts +30 -28
  369. package/frontend/src/features/database/index.ts +1 -0
  370. package/frontend/src/features/database/{page → pages}/FunctionsPage.tsx +29 -42
  371. package/frontend/src/features/database/{page → pages}/IndexesPage.tsx +34 -51
  372. package/frontend/src/features/database/{page → pages}/PoliciesPage.tsx +42 -58
  373. package/frontend/src/features/database/{page → pages}/SQLEditorPage.tsx +2 -2
  374. package/frontend/src/features/database/{page → pages}/TablesPage.tsx +0 -42
  375. package/frontend/src/features/database/{page → pages}/TriggersPage.tsx +34 -51
  376. package/frontend/src/features/database/services/advance.service.ts +1 -41
  377. package/frontend/src/features/database/services/database.service.ts +55 -0
  378. package/frontend/src/features/database/services/record.service.ts +4 -20
  379. package/frontend/src/features/database/services/table.service.ts +1 -10
  380. package/frontend/src/features/database/templates/ai-chatbot.ts +6 -6
  381. package/frontend/src/features/database/templates/ecommerce-platform.ts +2 -2
  382. package/frontend/src/features/database/templates/instagram-clone.ts +10 -10
  383. package/frontend/src/features/database/templates/notion-clone.ts +8 -8
  384. package/frontend/src/features/database/templates/reddit-clone.ts +10 -10
  385. package/frontend/src/features/deployments/components/DeploymentRow.tsx +93 -0
  386. package/frontend/src/features/deployments/components/DeploymentsEmptyState.tsx +15 -0
  387. package/frontend/src/features/deployments/hooks/useDeployments.ts +157 -0
  388. package/frontend/src/features/deployments/pages/DeploymentsPage.tsx +318 -0
  389. package/frontend/src/features/deployments/services/deployments.service.ts +63 -0
  390. package/frontend/src/features/functions/components/FunctionRow.tsx +72 -72
  391. package/frontend/src/features/functions/components/FunctionsSidebar.tsx +56 -56
  392. package/frontend/src/features/functions/components/SecretRow.tsx +3 -3
  393. package/frontend/src/features/functions/components/index.ts +5 -5
  394. package/frontend/src/features/functions/hooks/useFunctions.ts +5 -4
  395. package/frontend/src/features/functions/hooks/useSecrets.ts +6 -9
  396. package/frontend/src/features/functions/{page → pages}/FunctionsPage.tsx +21 -44
  397. package/frontend/src/features/functions/{page → pages}/SecretsPage.tsx +118 -116
  398. package/frontend/src/features/functions/services/function.service.ts +8 -25
  399. package/frontend/src/features/functions/services/secret.service.ts +23 -41
  400. package/frontend/src/features/login/{page → pages}/CloudLoginPage.tsx +125 -118
  401. package/frontend/src/features/logs/components/LogDetailPanel.tsx +41 -0
  402. package/frontend/src/features/logs/components/LogsDataGrid.tsx +32 -1
  403. package/frontend/src/features/logs/components/index.ts +1 -0
  404. package/frontend/src/features/logs/hooks/useMcpUsage.ts +13 -66
  405. package/frontend/src/features/logs/{page → pages}/LogsPage.tsx +36 -6
  406. package/frontend/src/features/onboard/components/ApiCredentialsSection.tsx +59 -0
  407. package/frontend/src/features/onboard/components/ConnectionStringSection.tsx +180 -0
  408. package/frontend/src/features/onboard/components/McpConnectionSection.tsx +159 -0
  409. package/frontend/src/features/onboard/components/OnboardingController.tsx +68 -0
  410. package/frontend/src/features/onboard/components/OnboardingModal.tsx +121 -267
  411. package/frontend/src/features/onboard/components/ShowPasswordButton.tsx +21 -0
  412. package/frontend/src/features/onboard/components/index.ts +9 -4
  413. package/frontend/src/features/onboard/components/mcp/CursorDeeplinkGenerator.tsx +1 -1
  414. package/frontend/src/features/onboard/components/mcp/QoderDeeplinkGenerator.tsx +36 -0
  415. package/frontend/src/features/onboard/components/mcp/helpers.tsx +123 -98
  416. package/frontend/src/features/onboard/components/mcp/index.ts +4 -3
  417. package/frontend/src/features/onboard/index.ts +17 -13
  418. package/frontend/src/features/realtime/components/ChannelRow.tsx +83 -0
  419. package/frontend/src/features/realtime/components/EditChannelModal.tsx +246 -0
  420. package/frontend/src/features/realtime/components/MessageRow.tsx +85 -0
  421. package/frontend/src/features/realtime/components/RealtimeEmptyState.tsx +30 -0
  422. package/frontend/src/features/realtime/hooks/useRealtime.ts +218 -0
  423. package/frontend/src/features/realtime/index.ts +11 -0
  424. package/frontend/src/features/realtime/pages/RealtimeChannelsPage.tsx +172 -0
  425. package/frontend/src/features/realtime/pages/RealtimeMessagesPage.tsx +211 -0
  426. package/frontend/src/features/realtime/pages/RealtimePermissionsPage.tsx +191 -0
  427. package/frontend/src/features/realtime/services/realtime.service.ts +107 -0
  428. package/frontend/src/features/settings/pages/SettingsPage.tsx +349 -0
  429. package/frontend/src/features/storage/{page → pages}/StoragePage.tsx +1 -29
  430. package/frontend/src/features/visualizer/components/AuthNode.tsx +4 -4
  431. package/frontend/src/features/visualizer/components/SchemaVisualizer.tsx +24 -11
  432. package/frontend/src/features/visualizer/{page → pages}/VisualizerPage.tsx +11 -36
  433. package/frontend/src/index.css +249 -249
  434. package/frontend/src/lib/contexts/ModalContext.tsx +35 -0
  435. package/frontend/src/lib/contexts/SocketContext.tsx +119 -75
  436. package/frontend/src/lib/hooks/useMetadata.ts +45 -1
  437. package/frontend/src/lib/hooks/useModal.tsx +2 -0
  438. package/frontend/src/lib/routing/AppRoutes.tsx +103 -84
  439. package/frontend/src/lib/services/metadata.service.ts +20 -3
  440. package/frontend/src/lib/utils/cloudMessaging.ts +1 -1
  441. package/frontend/src/lib/utils/menuItems.ts +223 -183
  442. package/frontend/src/lib/utils/utils.ts +196 -183
  443. package/frontend/tsconfig.json +25 -25
  444. package/frontend/tsconfig.node.json +9 -9
  445. package/functions/deno.json +24 -24
  446. package/functions/server.ts +6 -6
  447. package/functions/worker-template.js +1 -1
  448. package/i18n/README.ar.md +130 -130
  449. package/i18n/README.de.md +130 -130
  450. package/i18n/README.es.md +154 -154
  451. package/i18n/README.fr.md +134 -134
  452. package/i18n/README.hi.md +129 -129
  453. package/i18n/README.ja.md +174 -174
  454. package/i18n/README.ko.md +136 -136
  455. package/i18n/README.pt-BR.md +131 -131
  456. package/i18n/README.ru.md +129 -129
  457. package/i18n/README.zh-CN.md +133 -133
  458. package/openapi/ai.yaml +825 -715
  459. package/openapi/auth.yaml +1324 -1244
  460. package/openapi/email.yaml +158 -0
  461. package/openapi/functions.yaml +475 -475
  462. package/openapi/health.yaml +29 -29
  463. package/openapi/logs.yaml +221 -223
  464. package/openapi/metadata.yaml +175 -177
  465. package/openapi/realtime.yaml +699 -0
  466. package/openapi/records.yaml +381 -381
  467. package/openapi/secrets.yaml +370 -370
  468. package/openapi/storage.yaml +875 -875
  469. package/openapi/tables.yaml +462 -463
  470. package/package.json +97 -97
  471. package/shared-schemas/package.json +31 -31
  472. package/shared-schemas/src/ai-api.schema.ts +251 -143
  473. package/shared-schemas/src/ai.schema.ts +8 -4
  474. package/shared-schemas/src/auth-api.schema.ts +380 -339
  475. package/shared-schemas/src/auth.schema.ts +18 -11
  476. package/shared-schemas/src/cloud-events.schema.ts +26 -0
  477. package/shared-schemas/src/database-api.schema.ts +32 -1
  478. package/shared-schemas/src/database.schema.ts +39 -0
  479. package/shared-schemas/src/deployments-api.schema.ts +55 -0
  480. package/shared-schemas/src/deployments.schema.ts +30 -0
  481. package/shared-schemas/src/docs.schema.ts +32 -0
  482. package/shared-schemas/src/email-api.schema.ts +30 -0
  483. package/shared-schemas/src/functions-api.schema.ts +13 -4
  484. package/shared-schemas/src/functions.schema.ts +1 -1
  485. package/shared-schemas/src/index.ts +22 -14
  486. package/shared-schemas/src/metadata.schema.ts +39 -4
  487. package/shared-schemas/src/realtime-api.schema.ts +111 -0
  488. package/shared-schemas/src/realtime.schema.ts +143 -0
  489. package/shared-schemas/src/secrets-api.schema.ts +44 -0
  490. package/shared-schemas/src/secrets.schema.ts +15 -0
  491. package/shared-schemas/tsconfig.json +21 -21
  492. package/tsconfig.json +7 -7
  493. package/zeabur/README.md +26 -13
  494. package/zeabur/template.yml +1001 -1032
  495. package/.cursor/rules/cursor-rules.mdc +0 -94
  496. package/backend/src/types/profile.ts +0 -55
  497. package/frontend/src/components/ProjectInfoModal.tsx +0 -128
  498. package/frontend/src/features/database/hooks/useFullMetadata.ts +0 -18
  499. package/test-gemini.sh +0 -35
  500. package/test-usage-admin.sh +0 -57
  501. package/test-usage.sh +0 -50
  502. /package/frontend/src/features/auth/{page → pages}/ConfigurationPage.tsx +0 -0
  503. /package/frontend/src/features/database/{page → pages}/TemplatesPage.tsx +0 -0
  504. /package/frontend/src/features/login/{page → pages}/LoginPage.tsx +0 -0
  505. /package/frontend/src/features/logs/{page → pages}/AuditsPage.tsx +0 -0
  506. /package/frontend/src/features/logs/{page → pages}/MCPLogsPage.tsx +0 -0
@@ -15,6 +15,7 @@ import logger from '@/utils/logger.js';
15
15
  import { SocketManager } from '@/infra/socket/socket.manager.js';
16
16
  import { DataUpdateResourceType, ServerEvents } from '@/types/socket.js';
17
17
  import { successResponse } from '@/utils/response.js';
18
+ import { analyzeQuery, DatabaseResourceUpdate } from '@/utils/sql-parser.js';
18
19
 
19
20
  const router = Router();
20
21
  const dbAdvanceService = DatabaseAdvanceService.getInstance();
@@ -64,10 +65,17 @@ router.post(
64
65
  ip_address: req.ip,
65
66
  });
66
67
 
67
- const socket = SocketManager.getInstance();
68
- socket.broadcastToRoom('role:project_admin', ServerEvents.DATA_UPDATE, {
69
- resource: DataUpdateResourceType.DATABASE,
70
- });
68
+ // Broadcast changes if any modifying statements detected
69
+ const changes = analyzeQuery(query);
70
+ if (changes.length > 0) {
71
+ const socket = SocketManager.getInstance();
72
+ socket.broadcastToRoom(
73
+ 'role:project_admin',
74
+ ServerEvents.DATA_UPDATE,
75
+ { resource: DataUpdateResourceType.DATABASE, data: { changes } },
76
+ 'system'
77
+ );
78
+ }
71
79
 
72
80
  successResponse(res, response);
73
81
  } catch (error: unknown) {
@@ -115,10 +123,17 @@ router.post('/rawsql', verifyAdmin, async (req: AuthRequest, res: Response, next
115
123
  ip_address: req.ip,
116
124
  });
117
125
 
118
- const socket = SocketManager.getInstance();
119
- socket.broadcastToRoom('role:project_admin', ServerEvents.DATA_UPDATE, {
120
- resource: DataUpdateResourceType.DATABASE,
121
- });
126
+ // Broadcast changes if any modifying statements detected
127
+ const changes = analyzeQuery(query);
128
+ if (changes.length > 0) {
129
+ const socket = SocketManager.getInstance();
130
+ socket.broadcastToRoom(
131
+ 'role:project_admin',
132
+ ServerEvents.DATA_UPDATE,
133
+ { resource: DataUpdateResourceType.DATABASE, data: { changes } },
134
+ 'system'
135
+ );
136
+ }
122
137
 
123
138
  successResponse(res, response);
124
139
  } catch (error: unknown) {
@@ -235,12 +250,15 @@ router.post(
235
250
  });
236
251
 
237
252
  const socket = SocketManager.getInstance();
238
- socket.broadcastToRoom('role:project_admin', ServerEvents.DATA_UPDATE, {
239
- resource: DataUpdateResourceType.RECORDS,
240
- data: {
241
- tableName: table,
253
+ socket.broadcastToRoom(
254
+ 'role:project_admin',
255
+ ServerEvents.DATA_UPDATE,
256
+ {
257
+ resource: DataUpdateResourceType.DATABASE,
258
+ data: { changes: [{ type: 'records', name: table }] as DatabaseResourceUpdate[] },
242
259
  },
243
- });
260
+ 'system'
261
+ );
244
262
 
245
263
  successResponse(res, response);
246
264
  } catch (error: unknown) {
@@ -302,9 +320,12 @@ router.post(
302
320
  });
303
321
 
304
322
  const socket = SocketManager.getInstance();
305
- socket.broadcastToRoom('role:project_admin', ServerEvents.DATA_UPDATE, {
306
- resource: DataUpdateResourceType.DATABASE,
307
- });
323
+ socket.broadcastToRoom(
324
+ 'role:project_admin',
325
+ ServerEvents.DATA_UPDATE,
326
+ { resource: DataUpdateResourceType.DATABASE },
327
+ 'system'
328
+ );
308
329
 
309
330
  successResponse(res, response);
310
331
  } catch (error: unknown) {
@@ -1,13 +1,92 @@
1
- import { Router } from 'express';
1
+ import { Router, Response, NextFunction } from 'express';
2
2
  import { databaseTablesRouter } from './tables.routes.js';
3
3
  import { databaseRecordsRouter } from './records.routes.js';
4
+ import { databaseRpcRouter } from './rpc.routes.js';
4
5
  import databaseAdvanceRouter from './advance.routes.js';
6
+ import { DatabaseService } from '@/services/database/database.service.js';
7
+ import { verifyAdmin, AuthRequest } from '@/api/middlewares/auth.js';
8
+ import { successResponse } from '@/utils/response.js';
9
+ import logger from '@/utils/logger.js';
5
10
 
6
11
  const router = Router();
12
+ const databaseService = DatabaseService.getInstance();
7
13
 
8
14
  // Mount database sub-routes
9
15
  router.use('/tables', databaseTablesRouter);
10
16
  router.use('/records', databaseRecordsRouter);
17
+ router.use('/rpc', databaseRpcRouter);
11
18
  router.use('/advance', databaseAdvanceRouter);
12
19
 
20
+ /**
21
+ * Get all database functions
22
+ * GET /api/database/functions
23
+ */
24
+ router.get(
25
+ '/functions',
26
+ verifyAdmin,
27
+ async (_req: AuthRequest, res: Response, next: NextFunction) => {
28
+ try {
29
+ const response = await databaseService.getFunctions();
30
+ successResponse(res, response);
31
+ } catch (error: unknown) {
32
+ logger.warn('Get functions error:', error);
33
+ next(error);
34
+ }
35
+ }
36
+ );
37
+
38
+ /**
39
+ * Get all database indexes
40
+ * GET /api/database/indexes
41
+ */
42
+ router.get(
43
+ '/indexes',
44
+ verifyAdmin,
45
+ async (_req: AuthRequest, res: Response, next: NextFunction) => {
46
+ try {
47
+ const response = await databaseService.getIndexes();
48
+ successResponse(res, response);
49
+ } catch (error: unknown) {
50
+ logger.warn('Get indexes error:', error);
51
+ next(error);
52
+ }
53
+ }
54
+ );
55
+
56
+ /**
57
+ * Get all RLS policies
58
+ * GET /api/database/policies
59
+ */
60
+ router.get(
61
+ '/policies',
62
+ verifyAdmin,
63
+ async (_req: AuthRequest, res: Response, next: NextFunction) => {
64
+ try {
65
+ const response = await databaseService.getPolicies();
66
+ successResponse(res, response);
67
+ } catch (error: unknown) {
68
+ logger.warn('Get policies error:', error);
69
+ next(error);
70
+ }
71
+ }
72
+ );
73
+
74
+ /**
75
+ * Get all database triggers
76
+ * GET /api/database/triggers
77
+ */
78
+ router.get(
79
+ '/triggers',
80
+ verifyAdmin,
81
+ async (_req: AuthRequest, res: Response, next: NextFunction) => {
82
+ try {
83
+ const response = await databaseService.getTriggers();
84
+ successResponse(res, response);
85
+ } catch (error: unknown) {
86
+ logger.warn('Get triggers error:', error);
87
+ next(error);
88
+ }
89
+ }
90
+ );
91
+
13
92
  export default router;
@@ -1,83 +1,41 @@
1
1
  import { Router, Response, NextFunction } from 'express';
2
2
  import axios from 'axios';
3
- import http from 'http';
4
- import https from 'https';
5
3
  import { AuthRequest, extractApiKey } from '@/api/middlewares/auth.js';
6
4
  import { DatabaseManager } from '@/infra/database/database.manager.js';
7
- import { TokenManager } from '@/infra/security/token.manager.js';
8
5
  import { AppError } from '@/api/middlewares/error.js';
9
6
  import { ERROR_CODES } from '@/types/error-constants.js';
10
7
  import { validateTableName } from '@/utils/validations.js';
11
8
  import { DatabaseRecord } from '@/types/database.js';
12
9
  import { successResponse } from '@/utils/response.js';
13
- import logger from '@/utils/logger.js';
14
- import { SecretService } from '@/services/secrets/secret.service.js';
15
10
  import { SocketManager } from '@/infra/socket/socket.manager.js';
16
11
  import { DataUpdateResourceType, ServerEvents } from '@/types/socket.js';
12
+ import { DatabaseResourceUpdate } from '@/utils/sql-parser.js';
13
+ import { PostgrestProxyService } from '@/services/database/postgrest-proxy.service.js';
17
14
 
18
15
  const router = Router();
19
- const secretService = SecretService.getInstance();
20
- const postgrestUrl = process.env.POSTGREST_BASE_URL || 'http://localhost:5430';
16
+ const proxyService = PostgrestProxyService.getInstance();
21
17
 
22
- // Create a dedicated HTTP agent with connection pooling for PostgREST
23
- // Optimized connection pool for Docker network communication
24
- const httpAgent = new http.Agent({
25
- keepAlive: true,
26
- keepAliveMsecs: 5000, // Shorter for Docker network
27
- maxSockets: 20, // Reduced for stability
28
- maxFreeSockets: 5,
29
- timeout: 10000, // Match axios timeout
30
- });
31
-
32
- const httpsAgent = new https.Agent({
33
- keepAlive: true,
34
- keepAliveMsecs: 5000,
35
- maxSockets: 20,
36
- maxFreeSockets: 5,
37
- timeout: 10000,
38
- });
39
-
40
- // Create axios instance with optimized configuration for PostgREST
41
- const postgrestAxios = axios.create({
42
- httpAgent,
43
- httpsAgent,
44
- timeout: 10000, // Increased timeout for stability
45
- maxRedirects: 0,
46
- // Additional connection stability options
47
- headers: {
48
- Connection: 'keep-alive',
49
- 'Keep-Alive': 'timeout=5, max=10',
50
- },
51
- });
52
-
53
- // Generate admin token once and reuse
54
- // If user request with api key, this token should be added automatically.
55
- const tokenManager = TokenManager.getInstance();
56
- const adminToken = tokenManager.generateToken({
57
- sub: 'project-admin-with-api-key',
58
- email: 'project-admin@email.com',
59
- role: 'project_admin',
60
- });
61
-
62
- // anonymous users can access the database, postgREST does not require authentication, however we seed to unwrap session token for better auth, thus
63
- // we need to verify user token below.
64
- // router.use(verifyUserOrApiKey);
18
+ /**
19
+ * Helper to handle PostgREST proxy errors
20
+ */
21
+ function handleProxyError(error: unknown, res: Response, next: NextFunction) {
22
+ if (axios.isAxiosError(error) && error.response) {
23
+ res.status(error.response.status).json(error.response.data);
24
+ } else {
25
+ next(error);
26
+ }
27
+ }
65
28
 
66
29
  /**
67
- * Forward database requests to PostgREST
30
+ * Forward database table requests to PostgREST
68
31
  */
69
32
  const forwardToPostgrest = async (req: AuthRequest, res: Response, next: NextFunction) => {
70
33
  const { tableName } = req.params;
71
34
  const wildcardPath = req.params[0] || '';
72
-
73
- // Build the target URL early so it's available in error handling
74
- const targetPath = wildcardPath ? `/${tableName}/${wildcardPath}` : `/${tableName}`;
75
- const targetUrl = `${postgrestUrl}${targetPath}`;
35
+ const path = wildcardPath ? `/${tableName}/${wildcardPath}` : `/${tableName}`;
76
36
 
77
37
  try {
78
- // Validate table name with operation type
79
- const method = req.method.toUpperCase();
80
-
38
+ // Validate table name
81
39
  try {
82
40
  validateTableName(tableName);
83
41
  } catch (error) {
@@ -87,11 +45,14 @@ const forwardToPostgrest = async (req: AuthRequest, res: Response, next: NextFun
87
45
  throw new AppError('Invalid table name', 400, ERROR_CODES.INVALID_INPUT);
88
46
  }
89
47
 
90
- // Process request body for POST/PATCH/PUT operations
91
- if (['POST', 'PATCH', 'PUT'].includes(method) && req.body && typeof req.body === 'object') {
48
+ // Process request body for POST/PATCH/PUT (filter empty values based on column types)
49
+ const method = req.method.toUpperCase();
50
+ let body = req.body;
51
+
52
+ if (['POST', 'PATCH', 'PUT'].includes(method) && body && typeof body === 'object') {
92
53
  const columnTypeMap = await DatabaseManager.getColumnTypeMap(tableName);
93
- if (Array.isArray(req.body)) {
94
- req.body = req.body.map((item) => {
54
+ if (Array.isArray(body)) {
55
+ body = body.map((item) => {
95
56
  if (item && typeof item === 'object') {
96
57
  const filtered: DatabaseRecord = {};
97
58
  for (const key in item) {
@@ -105,7 +66,6 @@ const forwardToPostgrest = async (req: AuthRequest, res: Response, next: NextFun
105
66
  return item;
106
67
  });
107
68
  } else {
108
- const body = req.body as DatabaseRecord;
109
69
  for (const key in body) {
110
70
  if (columnTypeMap[key] === 'uuid' && body[key] === '') {
111
71
  delete body[key];
@@ -114,142 +74,46 @@ const forwardToPostgrest = async (req: AuthRequest, res: Response, next: NextFun
114
74
  }
115
75
  }
116
76
 
117
- // Forward the request
118
- const axiosConfig: {
119
- method: string;
120
- url: string;
121
- params: unknown;
122
- headers: Record<string, string | string[] | undefined>;
123
- data?: unknown;
124
- } = {
77
+ // Forward to PostgREST via service
78
+ const result = await proxyService.forward({
125
79
  method: req.method,
126
- url: targetUrl,
127
- params: req.query,
128
- headers: {
129
- ...req.headers,
130
- host: undefined, // Remove host header
131
- 'content-length': undefined, // Let axios calculate
132
- },
133
- };
134
-
135
- // Check for API key using shared logic
136
- const apiKey = extractApiKey(req);
137
-
138
- // If we have an API key, verify it and use admin token for PostgREST
139
- if (apiKey) {
140
- const isValid = await secretService.verifyApiKey(apiKey);
141
- if (isValid) {
142
- axiosConfig.headers.authorization = `Bearer ${adminToken}`;
143
- }
144
- }
145
-
146
- // Add body for methods that support it
147
- if (['POST', 'PUT', 'PATCH'].includes(req.method)) {
148
- axiosConfig.data = req.body;
149
- }
150
-
151
- // Enhanced retry logic with improved error handling
152
- let response;
153
- let lastError;
154
- const maxRetries = 3; // Increased retries for connection resets
155
-
156
- for (let attempt = 1; attempt <= maxRetries; attempt++) {
157
- try {
158
- response = await postgrestAxios(axiosConfig);
159
- break; // Success, exit retry loop
160
- } catch (error) {
161
- lastError = error;
162
-
163
- // Retry on network errors (ECONNRESET, ECONNREFUSED, timeout) but not HTTP errors
164
- const shouldRetry = axios.isAxiosError(error) && !error.response && attempt < maxRetries;
165
-
166
- if (shouldRetry) {
167
- logger.warn(`PostgREST request failed, retrying (attempt ${attempt}/${maxRetries})`, {
168
- url: targetUrl,
169
- errorCode: error.code,
170
- message: error.message,
171
- });
172
-
173
- // Enhanced exponential backoff: 200ms, 500ms, 1000ms
174
- const backoffDelay = Math.min(200 * Math.pow(2.5, attempt - 1), 1000);
175
- await new Promise((resolve) => setTimeout(resolve, backoffDelay));
176
- } else {
177
- throw error; // Don't retry on HTTP errors or last attempt
178
- }
179
- }
180
- }
181
-
182
- if (!response) {
183
- throw lastError || new Error('Failed to get response from PostgREST');
184
- }
80
+ path,
81
+ query: req.query as Record<string, unknown>,
82
+ headers: req.headers as Record<string, string | string[] | undefined>,
83
+ body: ['POST', 'PUT', 'PATCH'].includes(req.method) ? body : undefined,
84
+ apiKey: extractApiKey(req) ?? undefined,
85
+ });
185
86
 
186
87
  // Forward response headers
187
- Object.entries(response.headers).forEach(([key, value]) => {
188
- const keyLower = key.toLowerCase();
189
- if (
190
- keyLower !== 'content-length' &&
191
- keyLower !== 'transfer-encoding' &&
192
- keyLower !== 'connection' &&
193
- keyLower !== 'content-encoding'
194
- ) {
195
- res.setHeader(key, value);
196
- }
197
- });
88
+ const headers = PostgrestProxyService.filterHeaders(result.headers);
89
+ Object.entries(headers).forEach(([key, value]) => res.setHeader(key, value));
198
90
 
199
91
  // Handle empty responses
200
- let responseData = response.data;
92
+ let responseData = result.data;
201
93
  if (
202
- response.data === undefined ||
203
- (typeof response.data === 'string' && response.data.trim() === '')
94
+ result.data === undefined ||
95
+ (typeof result.data === 'string' && result.data.trim() === '')
204
96
  ) {
205
97
  responseData = [];
206
98
  }
207
99
 
208
- // Only send socket events for mutations (POST, DELETE)
209
- const mutationMethods = ['POST', 'DELETE'];
210
- if (mutationMethods.includes(req.method.toUpperCase())) {
100
+ // Broadcast socket events for mutations
101
+ if (['POST', 'DELETE'].includes(method)) {
211
102
  const socket = SocketManager.getInstance();
212
- socket.broadcastToRoom('role:project_admin', ServerEvents.DATA_UPDATE, {
213
- resource: DataUpdateResourceType.RECORDS,
214
- data: {
215
- tableName: tableName,
103
+ socket.broadcastToRoom(
104
+ 'role:project_admin',
105
+ ServerEvents.DATA_UPDATE,
106
+ {
107
+ resource: DataUpdateResourceType.DATABASE,
108
+ data: { changes: [{ type: 'records', name: tableName }] as DatabaseResourceUpdate[] },
216
109
  },
217
- });
110
+ 'system'
111
+ );
218
112
  }
219
113
 
220
- successResponse(res, responseData, response.status);
114
+ successResponse(res, responseData, result.status);
221
115
  } catch (error) {
222
- if (axios.isAxiosError(error)) {
223
- // Log more detailed error information
224
- logger.error('PostgREST request failed', {
225
- url: targetUrl,
226
- method: req.method,
227
- error: {
228
- code: error.code,
229
- message: error.message,
230
- response: error.response?.data,
231
- responseStatus: error.response?.status,
232
- },
233
- });
234
-
235
- // Forward PostgREST errors
236
- if (error.response) {
237
- res.status(error.response.status).json(error.response.data);
238
- } else {
239
- // Network error - connection refused, DNS failure, etc.
240
- const errorMessage =
241
- error.code === 'ECONNREFUSED'
242
- ? 'PostgREST connection refused'
243
- : error.code === 'ENOTFOUND'
244
- ? 'PostgREST service not found'
245
- : 'Database service unavailable';
246
-
247
- next(new AppError(errorMessage, 503, ERROR_CODES.INTERNAL_ERROR));
248
- }
249
- } else {
250
- logger.error('Unexpected error in database route', { error });
251
- next(error);
252
- }
116
+ handleProxyError(error, res, next);
253
117
  }
254
118
  };
255
119
 
@@ -0,0 +1,69 @@
1
+ import { Router, Response, NextFunction } from 'express';
2
+ import axios from 'axios';
3
+ import { AuthRequest, extractApiKey } from '@/api/middlewares/auth.js';
4
+ import { AppError } from '@/api/middlewares/error.js';
5
+ import { ERROR_CODES } from '@/types/error-constants.js';
6
+ import { validateFunctionName } from '@/utils/validations.js';
7
+ import { successResponse } from '@/utils/response.js';
8
+ import { PostgrestProxyService } from '@/services/database/postgrest-proxy.service.js';
9
+
10
+ const router = Router();
11
+ const proxyService = PostgrestProxyService.getInstance();
12
+
13
+ /**
14
+ * Helper to handle PostgREST proxy errors
15
+ */
16
+ function handleProxyError(error: unknown, res: Response, next: NextFunction) {
17
+ if (axios.isAxiosError(error) && error.response) {
18
+ res.status(error.response.status).json(error.response.data);
19
+ } else {
20
+ next(error);
21
+ }
22
+ }
23
+
24
+ /**
25
+ * Forward RPC calls to PostgREST
26
+ */
27
+ const forwardRpcToPostgrest = async (req: AuthRequest, res: Response, next: NextFunction) => {
28
+ const { functionName } = req.params;
29
+
30
+ try {
31
+ // Validate function name
32
+ try {
33
+ validateFunctionName(functionName);
34
+ } catch (error) {
35
+ if (error instanceof AppError) {
36
+ throw error;
37
+ }
38
+ throw new AppError(`Invalid function name: ${functionName}`, 400, ERROR_CODES.INVALID_INPUT);
39
+ }
40
+
41
+ const result = await proxyService.forward({
42
+ method: req.method,
43
+ path: `/rpc/${functionName}`,
44
+ query: req.query as Record<string, unknown>,
45
+ headers: req.headers as Record<string, string | string[] | undefined>,
46
+ body: req.body,
47
+ apiKey: extractApiKey(req) ?? undefined,
48
+ });
49
+
50
+ const headers = PostgrestProxyService.filterHeaders(result.headers);
51
+ Object.entries(headers).forEach(([key, value]) => res.setHeader(key, value));
52
+
53
+ let responseData = result.data;
54
+ if (
55
+ result.data === undefined ||
56
+ (typeof result.data === 'string' && result.data.trim() === '')
57
+ ) {
58
+ responseData = null;
59
+ }
60
+
61
+ successResponse(res, responseData, result.status);
62
+ } catch (error) {
63
+ handleProxyError(error, res, next);
64
+ }
65
+ };
66
+
67
+ router.all('/:functionName', forwardRpcToPostgrest);
68
+
69
+ export { router as databaseRpcRouter };
@@ -59,20 +59,6 @@ router.post('/', verifyAdmin, async (req: AuthRequest, res: Response, next: Next
59
59
  }
60
60
  });
61
61
 
62
- // Get all table schemas
63
- router.get(
64
- '/schemas',
65
- verifyAdmin,
66
- async (_req: AuthRequest, res: Response, next: NextFunction) => {
67
- try {
68
- const schemas = await tableService.getAllTableSchemas();
69
- successResponse(res, schemas);
70
- } catch (error) {
71
- next(error);
72
- }
73
- }
74
- );
75
-
76
62
  // Get table schema
77
63
  router.get(
78
64
  '/:tableName/schema',