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,260 @@
1
+ import { Pool } from 'pg';
2
+ import { DatabaseManager } from '@/infra/database/database.manager.js';
3
+ import logger from '@/utils/logger.js';
4
+ import type { RealtimeMessage, RoleSchema } from '@insforge/shared-schemas';
5
+ import { RealtimeChannelService } from './realtime-channel.service.js';
6
+ import { RealtimeAuthService } from './realtime-auth.service.js';
7
+
8
+ export class RealtimeMessageService {
9
+ private static instance: RealtimeMessageService;
10
+ private pool: Pool | null = null;
11
+
12
+ private constructor() {}
13
+
14
+ static getInstance(): RealtimeMessageService {
15
+ if (!RealtimeMessageService.instance) {
16
+ RealtimeMessageService.instance = new RealtimeMessageService();
17
+ }
18
+ return RealtimeMessageService.instance;
19
+ }
20
+
21
+ private getPool(): Pool {
22
+ if (!this.pool) {
23
+ this.pool = DatabaseManager.getInstance().getPool();
24
+ }
25
+ return this.pool;
26
+ }
27
+
28
+ /**
29
+ * Insert a message into the channel (client-initiated send).
30
+ * RLS INSERT policy controls who can send to which channels.
31
+ * pg_notify is automatically triggered by database trigger on insert.
32
+ *
33
+ * @returns The inserted message data for broadcasting, or null if RLS denied the insert
34
+ */
35
+ async insertMessage(
36
+ channelName: string,
37
+ eventName: string,
38
+ payload: Record<string, unknown>,
39
+ userId: string | undefined,
40
+ userRole: RoleSchema
41
+ ): Promise<{
42
+ channelId: string;
43
+ channelName: string;
44
+ eventName: string;
45
+ payload: Record<string, unknown>;
46
+ senderId: string | null;
47
+ } | null> {
48
+ // Get channel info
49
+ const channelService = RealtimeChannelService.getInstance();
50
+ const channel = await channelService.getByName(channelName);
51
+
52
+ if (!channel) {
53
+ logger.debug('Channel not found for message insert', { channelName });
54
+ return null;
55
+ }
56
+
57
+ const client = await this.getPool().connect();
58
+
59
+ try {
60
+ // Begin transaction to ensure settings persist across queries
61
+ await client.query('BEGIN');
62
+
63
+ // Switch to specified role to enforce RLS policies
64
+ await client.query(`SET LOCAL ROLE ${userRole}`);
65
+
66
+ // Set user context for RLS policy evaluation
67
+ const authService = RealtimeAuthService.getInstance();
68
+ await authService.setUserContext(client, userId, channelName);
69
+
70
+ // Attempt INSERT with sender info - RLS will allow/deny based on policies
71
+ // No RETURNING clause needed - trigger handles pg_notify
72
+ await client.query(
73
+ `INSERT INTO realtime.messages (event_name, channel_id, channel_name, payload, sender_type, sender_id)
74
+ VALUES ($1, $2, $3, $4, 'user', $5)`,
75
+ [eventName, channel.id, channelName, JSON.stringify(payload), userId || null]
76
+ );
77
+
78
+ // Commit transaction - insert succeeded
79
+ await client.query('COMMIT');
80
+
81
+ logger.debug('Client message inserted', {
82
+ channelName,
83
+ eventName,
84
+ userId,
85
+ });
86
+
87
+ return {
88
+ channelId: channel.id,
89
+ channelName,
90
+ eventName,
91
+ payload,
92
+ senderId: userId || null,
93
+ };
94
+ } catch (error) {
95
+ // Rollback transaction on error
96
+ await client.query('ROLLBACK').catch(() => {});
97
+
98
+ // RLS policy denied the INSERT or other error
99
+ logger.debug('Message insert denied or failed', { channelName, eventName, userId, error });
100
+ return null;
101
+ } finally {
102
+ // Reset role back to default before releasing connection
103
+ await client.query('RESET ROLE');
104
+ client.release();
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Get a message by ID (used by RealtimeManager after pg_notify)
110
+ */
111
+ async getById(id: string): Promise<RealtimeMessage | null> {
112
+ const result = await this.getPool().query(
113
+ `SELECT
114
+ id,
115
+ event_name as "eventName",
116
+ channel_id as "channelId",
117
+ channel_name as "channelName",
118
+ payload,
119
+ sender_type as "senderType",
120
+ sender_id as "senderId",
121
+ ws_audience_count as "wsAudienceCount",
122
+ wh_audience_count as "whAudienceCount",
123
+ wh_delivered_count as "whDeliveredCount",
124
+ created_at as "createdAt"
125
+ FROM realtime.messages
126
+ WHERE id = $1`,
127
+ [id]
128
+ );
129
+ return result.rows[0] || null;
130
+ }
131
+
132
+ async list(
133
+ options: {
134
+ channelId?: string;
135
+ eventName?: string;
136
+ limit?: number;
137
+ offset?: number;
138
+ } = {}
139
+ ): Promise<RealtimeMessage[]> {
140
+ const { channelId, eventName, limit = 100, offset = 0 } = options;
141
+
142
+ let query = `
143
+ SELECT
144
+ id,
145
+ event_name as "eventName",
146
+ channel_id as "channelId",
147
+ channel_name as "channelName",
148
+ payload,
149
+ sender_type as "senderType",
150
+ sender_id as "senderId",
151
+ ws_audience_count as "wsAudienceCount",
152
+ wh_audience_count as "whAudienceCount",
153
+ wh_delivered_count as "whDeliveredCount",
154
+ created_at as "createdAt"
155
+ FROM realtime.messages
156
+ WHERE 1=1
157
+ `;
158
+
159
+ const params: (string | number)[] = [];
160
+ let paramIndex = 1;
161
+
162
+ if (channelId) {
163
+ query += ` AND channel_id = $${paramIndex++}`;
164
+ params.push(channelId);
165
+ }
166
+
167
+ if (eventName) {
168
+ query += ` AND event_name = $${paramIndex++}`;
169
+ params.push(eventName);
170
+ }
171
+
172
+ query += ` ORDER BY created_at DESC LIMIT $${paramIndex++} OFFSET $${paramIndex++}`;
173
+ params.push(limit, offset);
174
+
175
+ const result = await this.getPool().query(query, params);
176
+ return result.rows;
177
+ }
178
+
179
+ /**
180
+ * Update message record with delivery statistics
181
+ */
182
+ async updateDeliveryStats(
183
+ messageId: string,
184
+ stats: {
185
+ wsAudienceCount: number;
186
+ whAudienceCount: number;
187
+ whDeliveredCount: number;
188
+ }
189
+ ): Promise<void> {
190
+ await this.getPool().query(
191
+ `UPDATE realtime.messages
192
+ SET
193
+ ws_audience_count = $2,
194
+ wh_audience_count = $3,
195
+ wh_delivered_count = $4
196
+ WHERE id = $1`,
197
+ [messageId, stats.wsAudienceCount, stats.whAudienceCount, stats.whDeliveredCount]
198
+ );
199
+ }
200
+
201
+ async getStats(
202
+ options: {
203
+ channelId?: string;
204
+ since?: Date;
205
+ } = {}
206
+ ): Promise<{
207
+ totalMessages: number;
208
+ whDeliveryRate: number;
209
+ topEvents: { eventName: string; count: number }[];
210
+ }> {
211
+ const { channelId, since } = options;
212
+
213
+ let whereClause = '1=1';
214
+ const params: (string | Date)[] = [];
215
+ let paramIndex = 1;
216
+
217
+ if (channelId) {
218
+ whereClause += ` AND channel_id = $${paramIndex++}`;
219
+ params.push(channelId);
220
+ }
221
+
222
+ if (since) {
223
+ whereClause += ` AND created_at >= $${paramIndex++}`;
224
+ params.push(since);
225
+ }
226
+
227
+ const statsResult = await this.getPool().query(
228
+ `SELECT
229
+ COUNT(*) as total_messages,
230
+ SUM(wh_audience_count) as wh_audience_total,
231
+ SUM(wh_delivered_count) as wh_delivered_total
232
+ FROM realtime.messages
233
+ WHERE ${whereClause}`,
234
+ params
235
+ );
236
+
237
+ const topEventsResult = await this.getPool().query(
238
+ `SELECT event_name, COUNT(*) as count
239
+ FROM realtime.messages
240
+ WHERE ${whereClause}
241
+ GROUP BY event_name
242
+ ORDER BY count DESC
243
+ LIMIT 10`,
244
+ params
245
+ );
246
+
247
+ const stats = statsResult.rows[0];
248
+ const whAudienceTotal = parseInt(stats.wh_audience_total) || 0;
249
+ const whDeliveredTotal = parseInt(stats.wh_delivered_total) || 0;
250
+
251
+ return {
252
+ totalMessages: parseInt(stats.total_messages) || 0,
253
+ whDeliveryRate: whAudienceTotal > 0 ? whDeliveredTotal / whAudienceTotal : 0,
254
+ topEvents: topEventsResult.rows.map((row) => ({
255
+ eventName: row.event_name,
256
+ count: parseInt(row.count),
257
+ })),
258
+ };
259
+ }
260
+ }
@@ -1,8 +1,8 @@
1
1
  import { Pool } from 'pg';
2
2
  import crypto from 'crypto';
3
- import { DatabaseManager } from '@/core/database/manager.js';
3
+ import { DatabaseManager } from '@/infra/database/database.manager.js';
4
4
  import logger from '@/utils/logger.js';
5
- import { EncryptionUtils } from './encryption.js';
5
+ import { EncryptionManager } from '@/infra/security/encryption.manager.js';
6
6
 
7
7
  export interface SecretSchema {
8
8
  id: string;
@@ -29,11 +29,19 @@ export interface UpdateSecretInput {
29
29
  expiresAt?: Date | null;
30
30
  }
31
31
 
32
- export class SecretsService {
32
+ export class SecretService {
33
+ private static instance: SecretService;
33
34
  private pool: Pool | null = null;
34
35
 
35
- constructor() {
36
- // Encryption is now handled by the shared EncryptionUtils
36
+ private constructor() {
37
+ // Encryption is now handled by the shared EncryptionManager
38
+ }
39
+
40
+ public static getInstance(): SecretService {
41
+ if (!SecretService.instance) {
42
+ SecretService.instance = new SecretService();
43
+ }
44
+ return SecretService.instance;
37
45
  }
38
46
 
39
47
  private getPool(): Pool {
@@ -47,11 +55,10 @@ export class SecretsService {
47
55
  * Create a new secret
48
56
  */
49
57
  async createSecret(input: CreateSecretInput): Promise<{ id: string }> {
50
- const client = await this.getPool().connect();
51
58
  try {
52
- const encryptedValue = EncryptionUtils.encrypt(input.value);
59
+ const encryptedValue = EncryptionManager.encrypt(input.value);
53
60
 
54
- const result = await client.query(
61
+ const result = await this.getPool().query(
55
62
  `INSERT INTO _secrets (key, value_ciphertext, is_reserved, expires_at)
56
63
  VALUES ($1, $2, $3, $4)
57
64
  RETURNING id`,
@@ -63,8 +70,6 @@ export class SecretsService {
63
70
  } catch (error) {
64
71
  logger.error('Failed to create secret', { error, key: input.key });
65
72
  throw new Error('Failed to create secret');
66
- } finally {
67
- client.release();
68
73
  }
69
74
  }
70
75
 
@@ -72,9 +77,8 @@ export class SecretsService {
72
77
  * Get a decrypted secret by ID
73
78
  */
74
79
  async getSecretById(id: string): Promise<string | null> {
75
- const client = await this.getPool().connect();
76
80
  try {
77
- const result = await client.query(
81
+ const result = await this.getPool().query(
78
82
  `UPDATE _secrets
79
83
  SET last_used_at = NOW()
80
84
  WHERE id = $1 AND is_active = true
@@ -83,18 +87,16 @@ export class SecretsService {
83
87
  [id]
84
88
  );
85
89
 
86
- if (result.rows.length === 0) {
90
+ if (!result.rows.length) {
87
91
  return null;
88
92
  }
89
93
 
90
- const decryptedValue = EncryptionUtils.decrypt(result.rows[0].value_ciphertext);
94
+ const decryptedValue = EncryptionManager.decrypt(result.rows[0].value_ciphertext);
91
95
  logger.info('Secret retrieved', { id });
92
96
  return decryptedValue;
93
97
  } catch (error) {
94
98
  logger.error('Failed to get secret', { error, id });
95
99
  throw new Error('Failed to get secret');
96
- } finally {
97
- client.release();
98
100
  }
99
101
  }
100
102
 
@@ -102,9 +104,8 @@ export class SecretsService {
102
104
  * Get a decrypted secret by key
103
105
  */
104
106
  async getSecretByKey(key: string): Promise<string | null> {
105
- const client = await this.getPool().connect();
106
107
  try {
107
- const result = await client.query(
108
+ const result = await this.getPool().query(
108
109
  `UPDATE _secrets
109
110
  SET last_used_at = NOW()
110
111
  WHERE key = $1 AND is_active = true
@@ -113,18 +114,16 @@ export class SecretsService {
113
114
  [key]
114
115
  );
115
116
 
116
- if (result.rows.length === 0) {
117
+ if (!result.rows.length) {
117
118
  return null;
118
119
  }
119
120
 
120
- const decryptedValue = EncryptionUtils.decrypt(result.rows[0].value_ciphertext);
121
+ const decryptedValue = EncryptionManager.decrypt(result.rows[0].value_ciphertext);
121
122
  logger.info('Secret retrieved by key', { key });
122
123
  return decryptedValue;
123
124
  } catch (error) {
124
125
  logger.error('Failed to get secret by key', { error, key });
125
126
  throw new Error('Failed to get secret');
126
- } finally {
127
- client.release();
128
127
  }
129
128
  }
130
129
 
@@ -132,9 +131,8 @@ export class SecretsService {
132
131
  * List all secrets (without decrypting values)
133
132
  */
134
133
  async listSecrets(): Promise<SecretSchema[]> {
135
- const client = await this.getPool().connect();
136
134
  try {
137
- const result = await client.query(
135
+ const result = await this.getPool().query(
138
136
  `SELECT
139
137
  id,
140
138
  key,
@@ -152,8 +150,6 @@ export class SecretsService {
152
150
  } catch (error) {
153
151
  logger.error('Failed to list secrets', { error });
154
152
  throw new Error('Failed to list secrets');
155
- } finally {
156
- client.release();
157
153
  }
158
154
  }
159
155
 
@@ -161,14 +157,13 @@ export class SecretsService {
161
157
  * Update a secret
162
158
  */
163
159
  async updateSecret(id: string, input: UpdateSecretInput): Promise<boolean> {
164
- const client = await this.getPool().connect();
165
160
  try {
166
161
  const updates: string[] = [];
167
162
  const values: (string | boolean | Date | null)[] = [];
168
163
  let paramCount = 1;
169
164
 
170
165
  if (input.value !== undefined) {
171
- const encryptedValue = EncryptionUtils.encrypt(input.value);
166
+ const encryptedValue = EncryptionManager.encrypt(input.value);
172
167
  updates.push(`value_ciphertext = $${paramCount++}`);
173
168
  values.push(encryptedValue);
174
169
  }
@@ -190,7 +185,7 @@ export class SecretsService {
190
185
 
191
186
  values.push(id);
192
187
 
193
- const result = await client.query(
188
+ const result = await this.getPool().query(
194
189
  `UPDATE _secrets
195
190
  SET ${updates.join(', ')}
196
191
  WHERE id = $${paramCount}`,
@@ -205,8 +200,6 @@ export class SecretsService {
205
200
  } catch (error) {
206
201
  logger.error('Failed to update secret', { error, id });
207
202
  throw new Error('Failed to update secret');
208
- } finally {
209
- client.release();
210
203
  }
211
204
  }
212
205
 
@@ -214,34 +207,27 @@ export class SecretsService {
214
207
  * Check if a secret value matches the stored value
215
208
  */
216
209
  async checkSecretByKey(key: string, value: string): Promise<boolean> {
217
- const client = await this.getPool().connect();
218
210
  try {
219
- const result = await client.query(
220
- `SELECT value_ciphertext FROM _secrets
211
+ // Optimized: Single query that retrieves and updates in one operation
212
+ const result = await this.getPool().query(
213
+ `UPDATE _secrets
214
+ SET last_used_at = NOW()
221
215
  WHERE key = $1
222
216
  AND is_active = true
223
217
  AND (expires_at IS NULL OR expires_at > NOW())
224
- LIMIT 1`,
218
+ RETURNING value_ciphertext`,
225
219
  [key]
226
220
  );
227
221
 
228
- if (result.rows.length === 0) {
222
+ if (!result.rows.length) {
229
223
  logger.warn('Secret not found for verification', { key });
230
224
  return false;
231
225
  }
232
226
 
233
- const decryptedValue = EncryptionUtils.decrypt(result.rows[0].value_ciphertext);
227
+ const decryptedValue = EncryptionManager.decrypt(result.rows[0].value_ciphertext);
234
228
  const matches = decryptedValue === value;
235
229
 
236
- // Update last_used_at if the check was successful
237
230
  if (matches) {
238
- await client.query(
239
- `UPDATE _secrets
240
- SET last_used_at = NOW()
241
- WHERE key = $1
242
- AND is_active = true`,
243
- [key]
244
- );
245
231
  logger.info('Secret check successful', { key });
246
232
  } else {
247
233
  logger.warn('Secret check failed - value mismatch', { key });
@@ -251,8 +237,6 @@ export class SecretsService {
251
237
  } catch (error) {
252
238
  logger.error('Failed to check secret', { error, key });
253
239
  return false;
254
- } finally {
255
- client.release();
256
240
  }
257
241
  }
258
242
 
@@ -260,29 +244,30 @@ export class SecretsService {
260
244
  * Delete a secret
261
245
  */
262
246
  async deleteSecret(id: string): Promise<boolean> {
263
- const client = await this.getPool().connect();
264
247
  try {
265
- // Check if secret is reserved first
266
- const checkResult = await client.query('SELECT is_reserved FROM _secrets WHERE id = $1', [
267
- id,
268
- ]);
269
-
270
- if (checkResult.rows.length > 0 && checkResult.rows[0].is_reserved) {
271
- throw new Error('Cannot delete reserved secret');
272
- }
273
-
274
- const result = await client.query('DELETE FROM _secrets WHERE id = $1', [id]);
248
+ // Optimized: Single query with WHERE clause to prevent deleting reserved secrets
249
+ const result = await this.getPool().query(
250
+ 'DELETE FROM _secrets WHERE id = $1 AND is_reserved = false',
251
+ [id]
252
+ );
275
253
 
276
254
  const success = (result.rowCount ?? 0) > 0;
277
255
  if (success) {
278
256
  logger.info('Secret deleted', { id });
257
+ } else {
258
+ // Check if it exists but is reserved
259
+ const checkResult = await this.getPool().query(
260
+ 'SELECT is_reserved FROM _secrets WHERE id = $1',
261
+ [id]
262
+ );
263
+ if (checkResult.rows.length && checkResult.rows[0].is_reserved) {
264
+ throw new Error('Cannot delete reserved secret');
265
+ }
279
266
  }
280
267
  return success;
281
268
  } catch (error) {
282
269
  logger.error('Failed to delete secret', { error, id });
283
270
  throw new Error('Failed to delete secret');
284
- } finally {
285
- client.release();
286
271
  }
287
272
  }
288
273
 
@@ -296,7 +281,7 @@ export class SecretsService {
296
281
 
297
282
  const oldSecretResult = await client.query(`SELECT key FROM _secrets WHERE id = $1`, [id]);
298
283
 
299
- if (oldSecretResult.rows.length === 0) {
284
+ if (!oldSecretResult.rows.length) {
300
285
  throw new Error('Secret not found');
301
286
  }
302
287
 
@@ -310,7 +295,7 @@ export class SecretsService {
310
295
  [id]
311
296
  );
312
297
 
313
- const encryptedValue = EncryptionUtils.encrypt(newValue);
298
+ const encryptedValue = EncryptionManager.encrypt(newValue);
314
299
  const newSecretResult = await client.query(
315
300
  `INSERT INTO _secrets (key, value_ciphertext)
316
301
  VALUES ($1, $2)
@@ -340,9 +325,8 @@ export class SecretsService {
340
325
  * Clean up expired secrets
341
326
  */
342
327
  async cleanupExpiredSecrets(): Promise<number> {
343
- const client = await this.getPool().connect();
344
328
  try {
345
- const result = await client.query(
329
+ const result = await this.getPool().query(
346
330
  `DELETE FROM _secrets
347
331
  WHERE expires_at IS NOT NULL
348
332
  AND expires_at < NOW()
@@ -357,8 +341,6 @@ export class SecretsService {
357
341
  } catch (error) {
358
342
  logger.error('Failed to cleanup expired secrets', { error });
359
343
  throw new Error('Failed to cleanup expired secrets');
360
- } finally {
361
- client.release();
362
344
  }
363
345
  }
364
346