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,802 @@
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 {
6
+ COLUMN_TYPES,
7
+ ForeignKeyRow,
8
+ ColumnInfo,
9
+ PrimaryKeyInfo,
10
+ ForeignKeyInfo,
11
+ } from '@/types/database.js';
12
+ import {
13
+ ColumnSchema,
14
+ ColumnType,
15
+ CreateTableResponse,
16
+ GetTableSchemaResponse,
17
+ UpdateTableSchemaRequest,
18
+ UpdateTableSchemaResponse,
19
+ DeleteTableResponse,
20
+ OnDeleteActionSchema,
21
+ OnUpdateActionSchema,
22
+ ForeignKeySchema,
23
+ } from '@insforge/shared-schemas';
24
+ import { validateIdentifier } from '@/utils/validations.js';
25
+ import { convertSqlTypeToColumnType } from '@/utils/utils.js';
26
+
27
+ const reservedColumns = {
28
+ id: ColumnType.UUID,
29
+ created_at: ColumnType.DATETIME,
30
+ updated_at: ColumnType.DATETIME,
31
+ };
32
+
33
+ const userTableFrozenColumns = ['nickname', 'avatar_url'];
34
+
35
+ const SAFE_FUNCS = new Set(['now()', 'gen_random_uuid()']);
36
+
37
+ function getSafeDollarQuotedLiteral(s: string) {
38
+ let tag = 'val';
39
+ while (s.includes(`$${tag}$`)) {
40
+ tag += '_';
41
+ }
42
+ return `$${tag}$${s}$${tag}$`;
43
+ }
44
+
45
+ function getSystemDefault(columnType?: ColumnType, isNullable?: boolean): string | null {
46
+ if (!columnType || isNullable) {
47
+ return null;
48
+ }
49
+ const fieldType = COLUMN_TYPES[columnType];
50
+ if (!fieldType?.defaultValue) {
51
+ return null;
52
+ }
53
+
54
+ const def = fieldType.defaultValue.trim().toLowerCase();
55
+ if (SAFE_FUNCS.has(def)) {
56
+ return `DEFAULT ${def}`;
57
+ }
58
+ return `DEFAULT ${getSafeDollarQuotedLiteral(def)}`;
59
+ }
60
+
61
+ export function formatDefaultValue(
62
+ input: string | null | undefined,
63
+ columnType?: ColumnType,
64
+ isNullable?: boolean
65
+ ): string {
66
+ if (!input) {
67
+ return getSystemDefault(columnType, isNullable) ?? '';
68
+ }
69
+ const value = input.trim();
70
+ const lowered = value.toLowerCase();
71
+
72
+ if (SAFE_FUNCS.has(lowered)) {
73
+ return `DEFAULT ${lowered}`;
74
+ }
75
+ return `DEFAULT ${getSafeDollarQuotedLiteral(value)}`;
76
+ }
77
+
78
+ export class DatabaseTableService {
79
+ private static instance: DatabaseTableService;
80
+ private pool: Pool | null = null;
81
+
82
+ private constructor() {}
83
+
84
+ private getPool(): Pool {
85
+ if (!this.pool) {
86
+ this.pool = DatabaseManager.getInstance().getPool();
87
+ }
88
+ return this.pool;
89
+ }
90
+
91
+ public static getInstance(): DatabaseTableService {
92
+ if (!DatabaseTableService.instance) {
93
+ DatabaseTableService.instance = new DatabaseTableService();
94
+ }
95
+ return DatabaseTableService.instance;
96
+ }
97
+
98
+ /**
99
+ * List all tables
100
+ */
101
+ async listTables(): Promise<string[]> {
102
+ const result = await this.getPool().query(
103
+ `
104
+ SELECT table_name as name
105
+ FROM information_schema.tables
106
+ WHERE table_schema = 'public'
107
+ AND table_type = 'BASE TABLE'
108
+ AND table_name NOT LIKE '\\_%'
109
+ `
110
+ );
111
+
112
+ return result.rows.map((t: { name: string }) => t.name);
113
+ }
114
+
115
+ /**
116
+ * Create a new table
117
+ */
118
+ async createTable(
119
+ table_name: string,
120
+ columns: ColumnSchema[],
121
+ use_RLS = true
122
+ ): Promise<CreateTableResponse> {
123
+ // Validate table name
124
+ validateIdentifier(table_name, 'table');
125
+ // Prevent creation of system tables
126
+ if (table_name.startsWith('_')) {
127
+ throw new AppError(
128
+ 'Cannot create system tables',
129
+ 403,
130
+ ERROR_CODES.FORBIDDEN,
131
+ 'Table names starting with underscore are reserved for system tables'
132
+ );
133
+ }
134
+
135
+ // Filter out reserved fields with matching types, throw error for mismatched types
136
+ const validatedColumns = this.validateReservedFields(columns);
137
+
138
+ // Ensure at least one user-defined column exists
139
+ if (validatedColumns.length === 0) {
140
+ throw new AppError(
141
+ 'Table must have at least one user-defined column',
142
+ 400,
143
+ ERROR_CODES.DATABASE_VALIDATION_ERROR,
144
+ 'Please add at least one custom column (not id, created_at, or updated_at) to the table.'
145
+ );
146
+ }
147
+ // Validate remaining columns - only need to validate column names since Zod handles type validation
148
+ validatedColumns.forEach((col: ColumnSchema, index: number) => {
149
+ // Validate column name
150
+ try {
151
+ validateIdentifier(col.columnName, 'column');
152
+ } catch (error) {
153
+ if (error instanceof AppError) {
154
+ throw new AppError(
155
+ `Invalid column name at index ${index}: ${error.message}`,
156
+ error.statusCode,
157
+ error.code,
158
+ error.nextActions
159
+ );
160
+ }
161
+ throw error;
162
+ }
163
+ });
164
+
165
+ const client = await this.getPool().connect();
166
+ try {
167
+ // Check if table exists
168
+ const tableExistsResult = await client.query(
169
+ `
170
+ SELECT EXISTS (
171
+ SELECT FROM information_schema.tables
172
+ WHERE table_schema = 'public'
173
+ AND table_name = $1
174
+ ) as exists
175
+ `,
176
+ [table_name]
177
+ );
178
+
179
+ if (tableExistsResult.rows[0]?.exists) {
180
+ throw new AppError(
181
+ `table ${table_name} already exists`,
182
+ 400,
183
+ ERROR_CODES.DATABASE_DUPLICATE,
184
+ `table ${table_name} already exists. Please check the table name, it must be a unique table name.`
185
+ );
186
+ }
187
+
188
+ // Map columns to SQL with proper type conversion
189
+ const columnDefs = validatedColumns
190
+ .map((col: ColumnSchema) => {
191
+ const fieldType = COLUMN_TYPES[col.type as ColumnType];
192
+ const sqlType = fieldType.sqlType;
193
+
194
+ // Handle default values
195
+ const defaultClause = formatDefaultValue(
196
+ col.defaultValue,
197
+ col.type as ColumnType,
198
+ col.isNullable
199
+ );
200
+
201
+ const nullable = col.isNullable ? '' : 'NOT NULL';
202
+ const unique = col.isUnique ? 'UNIQUE' : '';
203
+
204
+ return `${this.quoteIdentifier(col.columnName)} ${sqlType} ${nullable} ${unique} ${defaultClause}`.trim();
205
+ })
206
+ .join(', ');
207
+
208
+ // Prepare foreign key constraints
209
+ const foreignKeyConstraints = validatedColumns
210
+ .filter((col) => col.foreignKey)
211
+ .map((col) => this.generateFkeyConstraintStatement(col, true))
212
+ .join(', ');
213
+
214
+ // Create table with auto fields and foreign keys
215
+ const tableDefinition = [
216
+ 'id UUID PRIMARY KEY DEFAULT gen_random_uuid()',
217
+ columnDefs,
218
+ 'created_at TIMESTAMPTZ DEFAULT now()',
219
+ 'updated_at TIMESTAMPTZ DEFAULT now()',
220
+ foreignKeyConstraints,
221
+ ]
222
+ .filter(Boolean)
223
+ .join(', ');
224
+
225
+ await client.query(
226
+ `
227
+ CREATE TABLE ${this.quoteIdentifier(table_name)} (
228
+ ${tableDefinition}
229
+ );
230
+ NOTIFY pgrst, 'reload schema';
231
+ `
232
+ );
233
+
234
+ if (use_RLS) {
235
+ // Enable RLS policies
236
+ await client.query(
237
+ `
238
+ ALTER TABLE ${this.quoteIdentifier(table_name)} ENABLE ROW LEVEL SECURITY;
239
+ `
240
+ );
241
+ }
242
+
243
+ // Create trigger for updated_at
244
+ await client.query(
245
+ `
246
+ CREATE TRIGGER ${this.quoteIdentifier(table_name + '_update_timestamp')}
247
+ BEFORE UPDATE ON ${this.quoteIdentifier(table_name)}
248
+ FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
249
+ `
250
+ );
251
+
252
+ // Update metadata
253
+ // Metadata is now updated on-demand
254
+
255
+ return {
256
+ message: 'table created successfully',
257
+ tableName: table_name,
258
+ columns: validatedColumns.map((col) => ({
259
+ ...col,
260
+ sqlType: COLUMN_TYPES[col.type as ColumnType].sqlType,
261
+ })),
262
+ autoFields: ['id', 'created_at', 'updated_at'],
263
+ nextActions:
264
+ 'you can now use the table with the POST /api/database/tables/{table} endpoint',
265
+ };
266
+ } finally {
267
+ client.release();
268
+ }
269
+ }
270
+
271
+ /**
272
+ * Get table schema
273
+ */
274
+ /**
275
+ * Parse PostgreSQL default value format
276
+ * Extracts the actual value from formats like 'abc'::text or 123::integer
277
+ */
278
+ private parseDefaultValue(defaultValue: string | null): string | undefined {
279
+ if (!defaultValue) {
280
+ return undefined;
281
+ }
282
+ // Handle string literals with type casting (e.g., 'abc'::text)
283
+ const stringMatch = defaultValue.match(/^'([^']*)'::[\w\s]+$/);
284
+ if (stringMatch) {
285
+ return stringMatch[1];
286
+ }
287
+
288
+ // Handle numeric/boolean values with type casting (e.g., 123::integer, true::boolean)
289
+ const typeCastMatch = defaultValue.match(/^(.+?)::[\w\s]+$/);
290
+ if (typeCastMatch) {
291
+ return typeCastMatch[1];
292
+ }
293
+
294
+ // Return as-is if no type casting pattern found
295
+ return defaultValue;
296
+ }
297
+
298
+ async getTableSchema(table: string): Promise<GetTableSchemaResponse> {
299
+ const client = await this.getPool().connect();
300
+ try {
301
+ // Get column information from information_schema
302
+ const columnsResult = await client.query(
303
+ `
304
+ SELECT
305
+ column_name,
306
+ data_type,
307
+ is_nullable,
308
+ column_default,
309
+ character_maximum_length
310
+ FROM information_schema.columns
311
+ WHERE table_schema = 'public'
312
+ AND table_name = $1
313
+ ORDER BY ordinal_position
314
+ `,
315
+ [table]
316
+ );
317
+
318
+ const columns = columnsResult.rows;
319
+
320
+ if (columns.length === 0) {
321
+ throw new AppError(
322
+ 'table not found',
323
+ 404,
324
+ ERROR_CODES.DATABASE_NOT_FOUND,
325
+ 'table not found. Please check the table name, it must be a valid table name, or you can create a new table with the POST /api/database/tables endpoint'
326
+ );
327
+ }
328
+
329
+ // Get foreign key information
330
+ const foreignKeyMap = await this.getFkeyConstraints(table);
331
+
332
+ // Get primary key information
333
+ const primaryKeysResult = await client.query(
334
+ `
335
+ SELECT column_name
336
+ FROM information_schema.key_column_usage
337
+ WHERE table_schema = 'public'
338
+ AND table_name = $1
339
+ AND constraint_name = (
340
+ SELECT constraint_name
341
+ FROM information_schema.table_constraints
342
+ WHERE table_schema = 'public'
343
+ AND table_name = $2
344
+ AND constraint_type = 'PRIMARY KEY'
345
+ )
346
+ `,
347
+ [table, table]
348
+ );
349
+
350
+ const primaryKeys = primaryKeysResult.rows;
351
+ const pkSet = new Set(primaryKeys.map((pk: PrimaryKeyInfo) => pk.column_name));
352
+
353
+ // Get unique columns
354
+ const uniqueColumnsResult = await client.query(
355
+ `
356
+ SELECT DISTINCT kcu.column_name
357
+ FROM information_schema.table_constraints tc
358
+ JOIN information_schema.key_column_usage kcu
359
+ ON tc.constraint_name = kcu.constraint_name
360
+ AND tc.table_schema = kcu.table_schema
361
+ WHERE tc.table_schema = 'public'
362
+ AND tc.table_name = $1
363
+ AND tc.constraint_type = 'UNIQUE'
364
+ `,
365
+ [table]
366
+ );
367
+
368
+ const uniqueColumns = uniqueColumnsResult.rows;
369
+ const uniqueSet = new Set(uniqueColumns.map((u: { column_name: string }) => u.column_name));
370
+
371
+ // Get row count
372
+ const rowCountResult = await client.query(`SELECT COUNT(*) as row_count FROM "${table}"`);
373
+ const row_count = rowCountResult.rows[0].row_count;
374
+
375
+ return {
376
+ tableName: table,
377
+ columns: columns.map((col: ColumnInfo) => ({
378
+ columnName: col.column_name,
379
+ type: convertSqlTypeToColumnType(col.data_type),
380
+ isNullable: col.is_nullable === 'YES',
381
+ isPrimaryKey: pkSet.has(col.column_name),
382
+ isUnique: pkSet.has(col.column_name) || uniqueSet.has(col.column_name),
383
+ defaultValue: this.parseDefaultValue(col.column_default),
384
+ ...(foreignKeyMap.has(col.column_name) && {
385
+ foreignKey: foreignKeyMap.get(col.column_name),
386
+ }),
387
+ })),
388
+ recordCount: row_count,
389
+ };
390
+ } finally {
391
+ client.release();
392
+ }
393
+ }
394
+
395
+ /**
396
+ * Update table schema
397
+ */
398
+ async updateTableSchema(
399
+ tableName: string,
400
+ operations: UpdateTableSchemaRequest
401
+ ): Promise<UpdateTableSchemaResponse> {
402
+ const { addColumns, dropColumns, updateColumns, addForeignKeys, dropForeignKeys, renameTable } =
403
+ operations;
404
+
405
+ // Prevent modification of system tables
406
+ if (tableName.startsWith('_')) {
407
+ throw new AppError(
408
+ 'System tables cannot be modified',
409
+ 403,
410
+ ERROR_CODES.DATABASE_FORBIDDEN,
411
+ 'System tables cannot be modified. System tables are prefixed with underscore.'
412
+ );
413
+ }
414
+
415
+ const client = await this.getPool().connect();
416
+ try {
417
+ // Check if table exists
418
+ const tableExistsResult = await client.query(
419
+ `
420
+ SELECT EXISTS (
421
+ SELECT FROM information_schema.tables
422
+ WHERE table_schema = 'public'
423
+ AND table_name = $1
424
+ ) as exists
425
+ `,
426
+ [tableName]
427
+ );
428
+
429
+ if (!tableExistsResult.rows[0]?.exists) {
430
+ throw new AppError(
431
+ 'table not found',
432
+ 404,
433
+ ERROR_CODES.DATABASE_NOT_FOUND,
434
+ 'Please check the table name, it must be a valid table name, or you can create a new table with the POST /api/database/tables endpoint'
435
+ );
436
+ }
437
+ const currentSchema = await this.getTableSchema(tableName);
438
+ const currentUserColumns = currentSchema.columns.filter(
439
+ (col) => !Object.keys(reservedColumns).includes(col.columnName)
440
+ );
441
+
442
+ // Filter dropped and added user columns
443
+ const droppedUserColumns = dropColumns
444
+ ? dropColumns.filter((col) => !Object.keys(reservedColumns).includes(col))
445
+ : [];
446
+ const addedUserColumns = addColumns ? this.validateReservedFields(addColumns) : [];
447
+
448
+ // Calculate final user column count
449
+ const finalUserColumnsCount =
450
+ currentUserColumns.length - droppedUserColumns.length + addedUserColumns.length;
451
+
452
+ if (finalUserColumnsCount <= 0) {
453
+ throw new AppError(
454
+ 'Table must have at least one user-defined column after update',
455
+ 400,
456
+ ERROR_CODES.DATABASE_VALIDATION_ERROR,
457
+ 'The update would leave the table with no custom columns. Please add columns or avoid dropping all user-defined columns.'
458
+ );
459
+ }
460
+
461
+ const safeTableName = this.quoteIdentifier(tableName);
462
+ const foreignKeyMap = await this.getFkeyConstraints(tableName);
463
+ const completedOperations: string[] = [];
464
+
465
+ // Execute operations
466
+
467
+ // Drop foreign key constraints
468
+ if (dropForeignKeys && Array.isArray(dropForeignKeys)) {
469
+ for (const col of dropForeignKeys) {
470
+ const constraintName = foreignKeyMap.get(col)?.constraint_name;
471
+ if (constraintName) {
472
+ await client.query(
473
+ `
474
+ ALTER TABLE ${safeTableName}
475
+ DROP CONSTRAINT ${this.quoteIdentifier(constraintName)}
476
+ `
477
+ );
478
+
479
+ completedOperations.push(`Dropped foreign key constraint on column: ${col}`);
480
+ }
481
+ }
482
+ }
483
+
484
+ // Drop columns first (to avoid conflicts with renames)
485
+ if (dropColumns && Array.isArray(dropColumns)) {
486
+ for (const col of dropColumns) {
487
+ if (Object.keys(reservedColumns).includes(col)) {
488
+ throw new AppError(
489
+ 'cannot drop system columns',
490
+ 404,
491
+ ERROR_CODES.DATABASE_FORBIDDEN,
492
+ `You cannot drop the system column '${col}'`
493
+ );
494
+ }
495
+ if (tableName === 'users' && userTableFrozenColumns.includes(col)) {
496
+ throw new AppError(
497
+ 'cannot drop frozen users columns',
498
+ 403,
499
+ ERROR_CODES.FORBIDDEN,
500
+ `You cannot drop the frozen users column '${col}'`
501
+ );
502
+ }
503
+ await client.query(
504
+ `
505
+ ALTER TABLE ${safeTableName}
506
+ DROP COLUMN ${this.quoteIdentifier(col)}
507
+ `
508
+ );
509
+
510
+ completedOperations.push(`Dropped column: ${col}`);
511
+ }
512
+ }
513
+
514
+ // Update columns
515
+ if (updateColumns && Array.isArray(updateColumns)) {
516
+ for (const column of updateColumns) {
517
+ if (Object.keys(reservedColumns).includes(column.columnName)) {
518
+ throw new AppError(
519
+ 'cannot update system columns',
520
+ 404,
521
+ ERROR_CODES.DATABASE_FORBIDDEN,
522
+ `You cannot update the system column '${column.columnName}'`
523
+ );
524
+ }
525
+ if (tableName === 'users' && userTableFrozenColumns.includes(column.columnName)) {
526
+ throw new AppError(
527
+ 'cannot update frozen user columns',
528
+ 403,
529
+ ERROR_CODES.FORBIDDEN,
530
+ `You cannot update the frozen users column '${column.columnName}'`
531
+ );
532
+ }
533
+
534
+ // Handle default value changes
535
+ if (column.defaultValue !== undefined) {
536
+ if (column.defaultValue === '') {
537
+ // Drop default
538
+ await client.query(
539
+ `
540
+ ALTER TABLE ${safeTableName}
541
+ ALTER COLUMN ${this.quoteIdentifier(column.columnName)} DROP DEFAULT
542
+ `
543
+ );
544
+ } else {
545
+ // Set default
546
+ await client.query(
547
+ `
548
+ ALTER TABLE ${safeTableName}
549
+ ALTER COLUMN ${this.quoteIdentifier(column.columnName)} SET ${formatDefaultValue(column.defaultValue)}
550
+ `
551
+ );
552
+ }
553
+ }
554
+
555
+ // Handle column rename - do this last to avoid issues with other operations
556
+ if (column.newColumnName) {
557
+ await client.query(
558
+ `
559
+ ALTER TABLE ${safeTableName}
560
+ RENAME COLUMN ${this.quoteIdentifier(column.columnName)} TO ${this.quoteIdentifier(column.newColumnName as string)}
561
+ `
562
+ );
563
+ }
564
+ completedOperations.push(`Updated column: ${column.columnName}`);
565
+ }
566
+ }
567
+
568
+ // Add new columns
569
+ if (addColumns && Array.isArray(addColumns)) {
570
+ // Validate and filter reserved fields
571
+ const columnsToAdd = this.validateReservedFields(addColumns);
572
+
573
+ for (const col of columnsToAdd) {
574
+ const fieldType = COLUMN_TYPES[col.type as ColumnType];
575
+ let sqlType = fieldType.sqlType;
576
+ if (col.type === ColumnType.UUID) {
577
+ sqlType = 'UUID';
578
+ }
579
+
580
+ const nullable = col.isNullable !== false ? '' : 'NOT NULL';
581
+ const unique = col.isUnique ? 'UNIQUE' : '';
582
+ const defaultClause = formatDefaultValue(
583
+ col.defaultValue,
584
+ col.type as ColumnType,
585
+ col.isNullable
586
+ );
587
+
588
+ await client.query(
589
+ `
590
+ ALTER TABLE ${safeTableName}
591
+ ADD COLUMN ${this.quoteIdentifier(col.columnName)} ${sqlType} ${nullable} ${unique} ${defaultClause}
592
+ `
593
+ );
594
+
595
+ completedOperations.push(`Added column: ${col.columnName}`);
596
+ }
597
+ }
598
+
599
+ // Add foreign key constraints
600
+ if (addForeignKeys && Array.isArray(addForeignKeys)) {
601
+ for (const col of addForeignKeys) {
602
+ if (Object.keys(reservedColumns).includes(col.columnName)) {
603
+ throw new AppError(
604
+ 'cannot add foreign key on system columns',
605
+ 404,
606
+ ERROR_CODES.DATABASE_FORBIDDEN,
607
+ `You cannot add foreign key on the system column '${col.columnName}'`
608
+ );
609
+ }
610
+ const fkeyConstraint = this.generateFkeyConstraintStatement(col, true);
611
+ await client.query(
612
+ `
613
+ ALTER TABLE ${safeTableName}
614
+ ADD ${fkeyConstraint}
615
+ `
616
+ );
617
+
618
+ completedOperations.push(`Added foreign key constraint on column: ${col.columnName}`);
619
+ }
620
+ }
621
+
622
+ if (renameTable && renameTable.newTableName) {
623
+ if (tableName === 'users') {
624
+ throw new AppError('Cannot rename users table', 403, ERROR_CODES.FORBIDDEN);
625
+ }
626
+ // Prevent renaming to system tables
627
+ if (renameTable.newTableName.startsWith('_')) {
628
+ throw new AppError(
629
+ 'Cannot rename to system table',
630
+ 403,
631
+ ERROR_CODES.FORBIDDEN,
632
+ 'Table names starting with underscore are reserved for system tables'
633
+ );
634
+ }
635
+
636
+ const safeNewTableName = this.quoteIdentifier(renameTable.newTableName);
637
+ // Rename the table
638
+ await client.query(
639
+ `
640
+ ALTER TABLE ${safeTableName}
641
+ RENAME TO ${safeNewTableName}
642
+ `
643
+ );
644
+
645
+ completedOperations.push(`Renamed table from ${tableName} to ${renameTable.newTableName}`);
646
+ }
647
+
648
+ // Update metadata after schema changes
649
+ // Metadata is now updated on-demand
650
+
651
+ // enable postgrest to query this table
652
+ await client.query(
653
+ `
654
+ NOTIFY pgrst, 'reload schema';
655
+ `
656
+ );
657
+
658
+ return {
659
+ message: 'table schema updated successfully',
660
+ tableName,
661
+ operations: completedOperations,
662
+ };
663
+ } finally {
664
+ client.release();
665
+ }
666
+ }
667
+
668
+ /**
669
+ * Delete a table
670
+ */
671
+ async deleteTable(table: string): Promise<DeleteTableResponse> {
672
+ // Prevent deletion of system tables
673
+ if (table.startsWith('_')) {
674
+ throw new AppError(
675
+ 'System tables cannot be deleted',
676
+ 403,
677
+ ERROR_CODES.DATABASE_FORBIDDEN,
678
+ 'System tables cannot be deleted. System tables are prefixed with underscore.'
679
+ );
680
+ }
681
+ if (table === 'users') {
682
+ throw new AppError('Cannot delete users table', 403, ERROR_CODES.DATABASE_FORBIDDEN);
683
+ }
684
+
685
+ const client = await this.getPool().connect();
686
+ try {
687
+ await client.query(`DROP TABLE IF EXISTS ${this.quoteIdentifier(table)} CASCADE`);
688
+
689
+ // Update metadata
690
+ // Metadata is now updated on-demand
691
+
692
+ // enable postgrest to query this table
693
+ await client.query(
694
+ `
695
+ NOTIFY pgrst, 'reload schema';
696
+ `
697
+ );
698
+
699
+ return {
700
+ message: 'table deleted successfully',
701
+ tableName: table,
702
+ nextActions:
703
+ 'table deleted successfully, you can create a new table with the POST /api/database/tables endpoint',
704
+ };
705
+ } finally {
706
+ client.release();
707
+ }
708
+ }
709
+
710
+ // Helper methods
711
+ private quoteIdentifier(identifier: string): string {
712
+ return `"${identifier.replace(/"/g, '""')}"`;
713
+ }
714
+
715
+ private validateReservedFields(columns: ColumnSchema[]): ColumnSchema[] {
716
+ return columns.filter((col: ColumnSchema) => {
717
+ const reservedType = reservedColumns[col.columnName as keyof typeof reservedColumns];
718
+ if (reservedType) {
719
+ // If it's a reserved field name
720
+ if (col.type === reservedType) {
721
+ // Type matches - silently ignore this column
722
+ return false;
723
+ } else {
724
+ // Type doesn't match - throw error
725
+ throw new AppError(
726
+ `Column '${col.columnName}' is a reserved field that requires type '${reservedType}', but got '${col.type}'`,
727
+ 400,
728
+ ERROR_CODES.DATABASE_VALIDATION_ERROR,
729
+ 'Please check the column name and type, id/created_at/updated_at are reserved fields and cannot be used as column names'
730
+ );
731
+ }
732
+ }
733
+ return true;
734
+ });
735
+ }
736
+
737
+ private generateFkeyConstraintStatement(
738
+ col: { columnName: string; foreignKey?: ForeignKeySchema },
739
+ include_source_column: boolean = true
740
+ ) {
741
+ if (!col.foreignKey) {
742
+ return '';
743
+ }
744
+ // Store foreign_key in a const to avoid repeated non-null assertions
745
+ const fk = col.foreignKey;
746
+ const constraintName = `fk_${col.columnName}_${fk.referenceTable}_${fk.referenceColumn}`;
747
+ const onDelete = fk.onDelete || 'RESTRICT';
748
+ const onUpdate = fk.onUpdate || 'RESTRICT';
749
+
750
+ if (include_source_column) {
751
+ return `CONSTRAINT ${this.quoteIdentifier(constraintName)} FOREIGN KEY (${this.quoteIdentifier(col.columnName)}) REFERENCES ${this.quoteIdentifier(fk.referenceTable)}(${this.quoteIdentifier(fk.referenceColumn)}) ON DELETE ${onDelete} ON UPDATE ${onUpdate}`;
752
+ } else {
753
+ return `CONSTRAINT ${this.quoteIdentifier(constraintName)} REFERENCES ${this.quoteIdentifier(fk.referenceTable)}(${this.quoteIdentifier(fk.referenceColumn)}) ON DELETE ${onDelete} ON UPDATE ${onUpdate}`;
754
+ }
755
+ }
756
+
757
+ private async getFkeyConstraints(table: string): Promise<Map<string, ForeignKeyInfo>> {
758
+ const result = await this.getPool().query(
759
+ `
760
+ SELECT
761
+ tc.constraint_name,
762
+ kcu.column_name as from_column,
763
+ ccu.table_name AS foreign_table,
764
+ ccu.column_name AS foreign_column,
765
+ rc.delete_rule as on_delete,
766
+ rc.update_rule as on_update
767
+ FROM information_schema.table_constraints AS tc
768
+ JOIN information_schema.key_column_usage AS kcu
769
+ ON tc.constraint_name = kcu.constraint_name
770
+ AND tc.table_schema = kcu.table_schema
771
+ JOIN information_schema.constraint_column_usage AS ccu
772
+ ON ccu.constraint_name = tc.constraint_name
773
+ AND ccu.table_schema = tc.table_schema
774
+ JOIN information_schema.referential_constraints AS rc
775
+ ON rc.constraint_name = tc.constraint_name
776
+ AND rc.constraint_schema = tc.table_schema
777
+ WHERE tc.constraint_type = 'FOREIGN KEY'
778
+ AND tc.table_name = $1
779
+ `,
780
+ [table]
781
+ );
782
+
783
+ const foreignKeys = result.rows;
784
+
785
+ // Create a map of column names to their foreign key info
786
+ const foreignKeyMap = new Map<string, ForeignKeyInfo>();
787
+ foreignKeys.forEach((fk: ForeignKeyRow) => {
788
+ if (fk.foreign_table.startsWith('_')) {
789
+ // hiden internal table.
790
+ return;
791
+ }
792
+ foreignKeyMap.set(fk.from_column, {
793
+ constraint_name: fk.constraint_name,
794
+ referenceTable: fk.foreign_table,
795
+ referenceColumn: fk.foreign_column,
796
+ onDelete: fk.on_delete as OnDeleteActionSchema,
797
+ onUpdate: fk.on_update as OnUpdateActionSchema,
798
+ });
799
+ });
800
+ return foreignKeyMap;
801
+ }
802
+ }