insforge 1.2.10 → 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 (335) hide show
  1. package/.claude-plugin/marketplace.json +20 -20
  2. package/.dockerignore +60 -60
  3. package/.env.example +83 -77
  4. package/.github/ISSUE_TEMPLATE/bug_report.yml +36 -36
  5. package/.github/ISSUE_TEMPLATE/config.yml +11 -11
  6. package/.github/ISSUE_TEMPLATE/feature_request.yml +26 -26
  7. package/.github/PULL_REQUEST_TEMPLATE.md +7 -7
  8. package/.github/copilot-instructions.md +146 -146
  9. package/.github/workflows/build-image.yml +65 -65
  10. package/.github/workflows/ci-premerge-check.yml +23 -23
  11. package/.github/workflows/e2e.yml +63 -63
  12. package/.github/workflows/lint-and-format.yml +32 -32
  13. package/.prettierignore +64 -64
  14. package/CHANGELOG.md +44 -44
  15. package/CLAUDE_PLUGIN.md +104 -104
  16. package/CODE_OF_CONDUCT.md +128 -128
  17. package/CONTRIBUTING.md +125 -125
  18. package/Dockerfile +30 -30
  19. package/GITHUB_OAUTH_SETUP.md +49 -49
  20. package/GOOGLE_OAUTH_SETUP.md +148 -148
  21. package/LICENSE +201 -201
  22. package/README.md +182 -182
  23. package/assets/Dark.svg +23 -23
  24. package/auth/package.json +28 -28
  25. package/auth/src/lib/broadcastService.ts +117 -115
  26. package/auth/src/pages/SignInPage.tsx +60 -57
  27. package/auth/src/pages/SignUpPage.tsx +60 -57
  28. package/auth/tsconfig.json +32 -32
  29. package/auth/tsconfig.node.json +11 -11
  30. package/backend/package.json +78 -75
  31. package/backend/src/api/routes/ai/index.routes.ts +3 -3
  32. package/backend/src/api/routes/auth/index.routes.ts +667 -570
  33. package/backend/src/api/routes/auth/oauth.routes.ts +473 -448
  34. package/backend/src/api/routes/database/advance.routes.ts +37 -16
  35. package/backend/src/api/routes/database/index.routes.ts +78 -1
  36. package/backend/src/api/routes/database/records.routes.ts +10 -10
  37. package/backend/src/api/routes/database/tables.routes.ts +0 -14
  38. package/backend/src/api/routes/docs/index.routes.ts +75 -76
  39. package/backend/src/api/routes/email/index.routes.ts +35 -0
  40. package/backend/src/api/routes/functions/index.routes.ts +18 -12
  41. package/backend/src/api/routes/metadata/index.routes.ts +12 -0
  42. package/backend/src/api/routes/realtime/channels.routes.ts +81 -0
  43. package/backend/src/api/routes/realtime/index.routes.ts +12 -0
  44. package/backend/src/api/routes/realtime/messages.routes.ts +48 -0
  45. package/backend/src/api/routes/realtime/permissions.routes.ts +19 -0
  46. package/backend/src/api/routes/storage/index.routes.ts +18 -12
  47. package/backend/src/api/routes/usage/index.routes.ts +6 -4
  48. package/backend/src/infra/database/database.manager.ts +14 -1
  49. package/backend/src/infra/database/migrations/000_create-base-tables.sql +141 -141
  50. package/backend/src/infra/database/migrations/001_create-helper-functions.sql +40 -40
  51. package/backend/src/infra/database/migrations/002_rename-auth-tables.sql +29 -29
  52. package/backend/src/infra/database/migrations/003_create-users-table.sql +55 -55
  53. package/backend/src/infra/database/migrations/004_add-reload-postgrest-func.sql +23 -23
  54. package/backend/src/infra/database/migrations/005_enable-project-admin-modify-users.sql +29 -29
  55. package/backend/src/infra/database/migrations/006_modify-ai-usage-table.sql +24 -24
  56. package/backend/src/infra/database/migrations/007_drop-metadata-table.sql +1 -1
  57. package/backend/src/infra/database/migrations/008_add-system-tables.sql +76 -76
  58. package/backend/src/infra/database/migrations/009_add-function-secrets.sql +23 -23
  59. package/backend/src/infra/database/migrations/010_modify-ai-config-modalities.sql +93 -93
  60. package/backend/src/infra/database/migrations/011_refactor-secrets-table.sql +15 -15
  61. package/backend/src/infra/database/migrations/012_add-storage-uploaded-by.sql +7 -7
  62. package/backend/src/infra/database/migrations/013_create-auth-schema-functions.sql +44 -44
  63. package/backend/src/infra/database/migrations/014_add-updated-at-trigger-user-table.sql +7 -7
  64. package/backend/src/infra/database/migrations/015_create-auth-config-and-email-otp-tables.sql +59 -59
  65. package/backend/src/infra/database/migrations/016_update-auth-config-and-email-otp.sql +24 -24
  66. package/backend/src/infra/database/migrations/017_create-realtime-schema.sql +233 -0
  67. package/backend/src/infra/realtime/realtime.manager.ts +246 -0
  68. package/backend/src/infra/realtime/webhook-sender.ts +82 -0
  69. package/backend/src/infra/security/token.manager.ts +219 -125
  70. package/backend/src/infra/socket/socket.manager.ts +198 -64
  71. package/backend/src/providers/ai/openrouter.provider.ts +12 -9
  72. package/backend/src/providers/email/base.provider.ts +4 -7
  73. package/backend/src/providers/email/cloud.provider.ts +84 -0
  74. package/backend/src/providers/oauth/apple.provider.ts +266 -0
  75. package/backend/src/providers/oauth/index.ts +1 -0
  76. package/backend/src/server.ts +317 -284
  77. package/backend/src/services/ai/ai-model.service.ts +5 -5
  78. package/backend/src/services/ai/chat-completion.service.ts +4 -4
  79. package/backend/src/services/ai/image-generation.service.ts +3 -3
  80. package/backend/src/services/auth/auth.service.ts +14 -0
  81. package/backend/src/services/database/database-table.service.ts +0 -9
  82. package/backend/src/services/database/database.service.ts +127 -0
  83. package/backend/src/services/email/email.service.ts +5 -7
  84. package/backend/src/services/realtime/index.ts +3 -0
  85. package/backend/src/services/realtime/realtime-auth.service.ts +104 -0
  86. package/backend/src/services/realtime/realtime-channel.service.ts +237 -0
  87. package/backend/src/services/realtime/realtime-message.service.ts +260 -0
  88. package/backend/src/types/auth.ts +11 -0
  89. package/backend/src/types/realtime.ts +18 -0
  90. package/backend/src/types/socket.ts +7 -31
  91. package/backend/src/utils/cookies.ts +35 -0
  92. package/backend/src/utils/s3-config-loader.ts +64 -0
  93. package/backend/src/utils/seed.ts +301 -298
  94. package/backend/src/utils/sql-parser.ts +90 -0
  95. package/backend/tests/README.md +133 -133
  96. package/backend/tests/cleanup-all-test-data.sh +230 -230
  97. package/backend/tests/cloud/test-s3-multitenant.sh +131 -131
  98. package/backend/tests/local/comprehensive-curl-tests.sh +155 -155
  99. package/backend/tests/local/test-ai-config.sh +129 -129
  100. package/backend/tests/local/test-ai-usage.sh +80 -80
  101. package/backend/tests/local/test-auth-router.sh +143 -143
  102. package/backend/tests/local/test-database-router.sh +222 -222
  103. package/backend/tests/local/test-e2e.sh +240 -240
  104. package/backend/tests/local/test-fk-errors.sh +96 -96
  105. package/backend/tests/local/test-functions.sh +123 -123
  106. package/backend/tests/local/test-id-field.sh +200 -200
  107. package/backend/tests/local/test-logs.sh +132 -132
  108. package/backend/tests/local/test-public-bucket.sh +264 -264
  109. package/backend/tests/local/test-secrets.sh +249 -249
  110. package/backend/tests/local/test-serverless-functions.sh.disabled +325 -325
  111. package/backend/tests/local/test-traditional-rest.sh +208 -208
  112. package/backend/tests/manual/README.md +50 -50
  113. package/backend/tests/manual/create-large-table-simple.sql +10 -10
  114. package/backend/tests/manual/seed-large-table.sql +100 -100
  115. package/backend/tests/manual/setup-large-table-extras.sql +33 -33
  116. package/backend/tests/manual/test-bulk-upsert.sh +409 -409
  117. package/backend/tests/manual/test-database-advance.sh +296 -296
  118. package/backend/tests/manual/test-postgrest-stability.sh +191 -191
  119. package/backend/tests/manual/test-rawsql-export-import.sh +411 -411
  120. package/backend/tests/manual/test-rawsql-modes.sh +244 -244
  121. package/backend/tests/manual/test-universal-storage.sh +263 -263
  122. package/backend/tests/manual/test-users.sql +17 -17
  123. package/backend/tests/run-all-tests.sh +139 -139
  124. package/backend/tests/setup.ts +0 -0
  125. package/backend/tests/test-config.sh +338 -338
  126. package/backend/tests/unit/analyze-query.test.ts +697 -0
  127. package/backend/tsconfig.json +22 -22
  128. package/claude-plugin/.claude-plugin/plugin.json +24 -24
  129. package/claude-plugin/README.md +133 -133
  130. package/claude-plugin/skills/insforge-schema-patterns/SKILL.md +270 -270
  131. package/docker-compose.prod.yml +204 -200
  132. package/docker-compose.yml +232 -228
  133. package/docker-init/db/db-init.sql +97 -97
  134. package/docker-init/db/jwt.sql +5 -5
  135. package/docker-init/db/postgresql.conf +16 -16
  136. package/docker-init/logs/vector.yml +236 -236
  137. package/docs/README.md +44 -44
  138. package/docs/agent-docs/real-time.md +269 -0
  139. package/docs/changelog.mdx +119 -67
  140. package/docs/core-concepts/ai/architecture.mdx +372 -372
  141. package/docs/core-concepts/ai/sdk.mdx +213 -213
  142. package/docs/core-concepts/authentication/architecture.mdx +278 -278
  143. package/docs/core-concepts/authentication/sdk.mdx +414 -414
  144. package/docs/core-concepts/authentication/ui-components/customization.mdx +529 -529
  145. package/docs/core-concepts/authentication/ui-components/nextjs.mdx +221 -221
  146. package/docs/core-concepts/authentication/ui-components/react-router.mdx +184 -184
  147. package/docs/core-concepts/authentication/ui-components/react.mdx +129 -129
  148. package/docs/core-concepts/database/architecture.mdx +255 -255
  149. package/docs/core-concepts/database/sdk.mdx +382 -382
  150. package/docs/core-concepts/email/architecture.mdx +101 -0
  151. package/docs/core-concepts/email/sdk.mdx +53 -0
  152. package/docs/core-concepts/functions/architecture.mdx +105 -105
  153. package/docs/core-concepts/functions/sdk.mdx +184 -184
  154. package/docs/core-concepts/realtime/architecture.mdx +446 -0
  155. package/docs/core-concepts/realtime/sdk.mdx +409 -0
  156. package/docs/core-concepts/storage/architecture.mdx +243 -243
  157. package/docs/core-concepts/storage/sdk.mdx +253 -253
  158. package/docs/deployment/README.md +94 -94
  159. package/docs/deployment/deploy-to-aws-ec2.md +564 -564
  160. package/docs/deployment/deploy-to-azure-virtual-machines.md +312 -312
  161. package/docs/deployment/deploy-to-google-cloud-compute-engine.md +613 -613
  162. package/docs/deployment/deploy-to-render.md +441 -441
  163. package/docs/deprecated/insforge-auth-api.md +214 -214
  164. package/docs/deprecated/insforge-auth-sdk.md +99 -99
  165. package/docs/deprecated/insforge-db-api.md +358 -358
  166. package/docs/deprecated/insforge-db-sdk.md +139 -139
  167. package/docs/deprecated/insforge-debug-sdk.md +156 -156
  168. package/docs/deprecated/insforge-debug.md +64 -64
  169. package/docs/deprecated/insforge-instructions.md +123 -123
  170. package/docs/deprecated/insforge-project.md +117 -117
  171. package/docs/deprecated/insforge-storage-api.md +278 -278
  172. package/docs/deprecated/insforge-storage-sdk.md +158 -158
  173. package/docs/docs.json +232 -210
  174. package/docs/examples/framework-guides/nextjs.mdx +131 -131
  175. package/docs/examples/framework-guides/nuxt.mdx +165 -165
  176. package/docs/examples/framework-guides/react.mdx +165 -165
  177. package/docs/examples/framework-guides/svelte.mdx +153 -153
  178. package/docs/examples/framework-guides/vue.mdx +159 -159
  179. package/docs/examples/overview.mdx +67 -67
  180. package/docs/favicon.svg +19 -19
  181. package/docs/images/changelog/dec-2025/ai-integration.png +0 -0
  182. package/docs/images/changelog/dec-2025/ai-models.webp +0 -0
  183. package/docs/images/changelog/dec-2025/alipay-payment.webp +0 -0
  184. package/docs/images/changelog/dec-2025/apple-login.jpg +0 -0
  185. package/docs/images/changelog/dec-2025/mcp-installer.png +0 -0
  186. package/docs/images/changelog/dec-2025/realtime-module.jpg +0 -0
  187. package/docs/images/icons/ai.svg +4 -4
  188. package/docs/images/logos/nextjs.svg +4 -4
  189. package/docs/images/logos/nuxt.svg +4 -4
  190. package/docs/images/logos/react.svg +5 -5
  191. package/docs/images/logos/svelte.svg +4 -4
  192. package/docs/images/logos/vue.svg +5 -5
  193. package/docs/insforge-instructions-sdk.md +89 -88
  194. package/docs/introduction.mdx +45 -45
  195. package/docs/logo/dark.svg +22 -22
  196. package/docs/logo/light.svg +20 -20
  197. package/docs/partnership.mdx +651 -646
  198. package/docs/quickstart.mdx +82 -82
  199. package/docs/showcase.mdx +52 -52
  200. package/docs/snippets/sdk-installation.mdx +21 -21
  201. package/docs/snippets/service-icons.mdx +27 -27
  202. package/examples/oauth/frontend-oauth-example.html +250 -250
  203. package/examples/response-examples.md +443 -443
  204. package/frontend/components.json +17 -17
  205. package/frontend/package.json +69 -69
  206. package/frontend/src/assets/icons/checkbox_checked.svg +6 -6
  207. package/frontend/src/assets/icons/checkbox_undetermined.svg +6 -6
  208. package/frontend/src/assets/icons/checked.svg +3 -3
  209. package/frontend/src/assets/icons/connected.svg +3 -3
  210. package/frontend/src/assets/icons/error.svg +3 -3
  211. package/frontend/src/assets/icons/loader.svg +9 -9
  212. package/frontend/src/assets/icons/pencil.svg +4 -4
  213. package/frontend/src/assets/icons/refresh.svg +4 -4
  214. package/frontend/src/assets/icons/step_active.svg +3 -3
  215. package/frontend/src/assets/icons/step_inactive.svg +11 -11
  216. package/frontend/src/assets/icons/warning.svg +3 -3
  217. package/frontend/src/assets/logos/apple.svg +3 -3
  218. package/frontend/src/assets/logos/claude_code.svg +3 -3
  219. package/frontend/src/assets/logos/cline.svg +6 -6
  220. package/frontend/src/assets/logos/cursor.svg +20 -20
  221. package/frontend/src/assets/logos/discord.svg +8 -8
  222. package/frontend/src/assets/logos/facebook.svg +3 -3
  223. package/frontend/src/assets/logos/gemini.svg +19 -19
  224. package/frontend/src/assets/logos/github.svg +5 -5
  225. package/frontend/src/assets/logos/google.svg +13 -13
  226. package/frontend/src/assets/logos/grok.svg +10 -10
  227. package/frontend/src/assets/logos/insforge_dark.svg +15 -15
  228. package/frontend/src/assets/logos/insforge_light.svg +15 -15
  229. package/frontend/src/assets/logos/instagram.svg +1 -1
  230. package/frontend/src/assets/logos/linkedin.svg +3 -3
  231. package/frontend/src/assets/logos/openai.svg +10 -10
  232. package/frontend/src/assets/logos/roo_code.svg +9 -9
  233. package/frontend/src/assets/logos/spotify.svg +16 -16
  234. package/frontend/src/assets/logos/tiktok.svg +5 -5
  235. package/frontend/src/assets/logos/trae.svg +3 -3
  236. package/frontend/src/assets/logos/windsurf.svg +10 -10
  237. package/frontend/src/assets/logos/x.svg +3 -3
  238. package/frontend/src/components/layout/AppHeader.tsx +9 -10
  239. package/frontend/src/features/auth/components/OAuthConfigDialog.tsx +1 -0
  240. package/frontend/src/features/auth/components/UsersDataGrid.tsx +6 -0
  241. package/frontend/src/features/auth/helpers.tsx +8 -0
  242. package/frontend/src/features/auth/{page → pages}/UsersPage.tsx +0 -28
  243. package/frontend/src/features/database/components/SQLModal.tsx +75 -0
  244. package/frontend/src/features/database/components/TableForm.tsx +0 -4
  245. package/frontend/src/features/database/hooks/useDatabase.ts +66 -0
  246. package/frontend/src/features/database/hooks/useTables.ts +32 -28
  247. package/frontend/src/features/database/index.ts +1 -0
  248. package/frontend/src/features/database/{page → pages}/FunctionsPage.tsx +29 -37
  249. package/frontend/src/features/database/{page → pages}/IndexesPage.tsx +35 -47
  250. package/frontend/src/features/database/{page → pages}/PoliciesPage.tsx +43 -54
  251. package/frontend/src/features/database/{page → pages}/TablesPage.tsx +0 -42
  252. package/frontend/src/features/database/{page → pages}/TriggersPage.tsx +35 -47
  253. package/frontend/src/features/database/services/advance.service.ts +0 -26
  254. package/frontend/src/features/database/services/database.service.ts +55 -0
  255. package/frontend/src/features/database/services/table.service.ts +0 -6
  256. package/frontend/src/features/functions/{page → pages}/FunctionsPage.tsx +21 -44
  257. package/frontend/src/features/functions/{page → pages}/SecretsPage.tsx +11 -9
  258. package/frontend/src/features/logs/hooks/useMcpUsage.ts +13 -66
  259. package/frontend/src/features/realtime/components/ChannelRow.tsx +83 -0
  260. package/frontend/src/features/realtime/components/EditChannelModal.tsx +246 -0
  261. package/frontend/src/features/realtime/components/MessageRow.tsx +85 -0
  262. package/frontend/src/features/realtime/components/RealtimeEmptyState.tsx +30 -0
  263. package/frontend/src/features/realtime/hooks/useRealtime.ts +218 -0
  264. package/frontend/src/features/realtime/index.ts +11 -0
  265. package/frontend/src/features/realtime/pages/RealtimeChannelsPage.tsx +172 -0
  266. package/frontend/src/features/realtime/pages/RealtimeMessagesPage.tsx +211 -0
  267. package/frontend/src/features/realtime/pages/RealtimePermissionsPage.tsx +191 -0
  268. package/frontend/src/features/realtime/services/realtime.service.ts +107 -0
  269. package/frontend/src/features/storage/{page → pages}/StoragePage.tsx +1 -29
  270. package/frontend/src/features/visualizer/components/SchemaVisualizer.tsx +3 -3
  271. package/frontend/src/features/visualizer/{page → pages}/VisualizerPage.tsx +1 -35
  272. package/frontend/src/lib/contexts/SocketContext.tsx +119 -75
  273. package/frontend/src/lib/routing/AppRoutes.tsx +35 -20
  274. package/frontend/src/lib/utils/cloudMessaging.ts +1 -1
  275. package/frontend/src/lib/utils/menuItems.ts +24 -0
  276. package/frontend/src/lib/utils/utils.ts +14 -1
  277. package/frontend/tsconfig.json +25 -25
  278. package/frontend/tsconfig.node.json +9 -9
  279. package/functions/deno.json +24 -24
  280. package/functions/server.ts +315 -315
  281. package/i18n/README.ar.md +130 -130
  282. package/i18n/README.de.md +130 -130
  283. package/i18n/README.es.md +154 -154
  284. package/i18n/README.fr.md +134 -134
  285. package/i18n/README.hi.md +129 -129
  286. package/i18n/README.ja.md +174 -174
  287. package/i18n/README.ko.md +136 -136
  288. package/i18n/README.pt-BR.md +131 -131
  289. package/i18n/README.ru.md +129 -129
  290. package/i18n/README.zh-CN.md +133 -133
  291. package/openapi/ai.yaml +715 -715
  292. package/openapi/auth.yaml +1244 -1244
  293. package/openapi/email.yaml +158 -0
  294. package/openapi/functions.yaml +475 -475
  295. package/openapi/health.yaml +29 -29
  296. package/openapi/logs.yaml +223 -223
  297. package/openapi/metadata.yaml +177 -177
  298. package/openapi/realtime.yaml +699 -0
  299. package/openapi/records.yaml +381 -381
  300. package/openapi/secrets.yaml +370 -370
  301. package/openapi/storage.yaml +875 -875
  302. package/openapi/tables.yaml +463 -463
  303. package/package.json +97 -97
  304. package/shared-schemas/package.json +31 -31
  305. package/shared-schemas/src/ai.schema.ts +63 -59
  306. package/shared-schemas/src/auth-api.schema.ts +352 -339
  307. package/shared-schemas/src/auth.schema.ts +1 -1
  308. package/shared-schemas/src/database-api.schema.ts +32 -1
  309. package/shared-schemas/src/database.schema.ts +39 -0
  310. package/shared-schemas/src/docs.schema.ts +26 -0
  311. package/shared-schemas/src/email-api.schema.ts +30 -0
  312. package/shared-schemas/src/index.ts +4 -0
  313. package/shared-schemas/src/metadata.schema.ts +9 -0
  314. package/shared-schemas/src/realtime-api.schema.ts +111 -0
  315. package/shared-schemas/src/realtime.schema.ts +143 -0
  316. package/shared-schemas/tsconfig.json +21 -21
  317. package/tsconfig.json +7 -7
  318. package/zeabur/README.md +13 -13
  319. package/zeabur/template.yml +1032 -1032
  320. package/.cursor/rules/cursor-rules.mdc +0 -94
  321. package/frontend/src/features/database/hooks/useFullMetadata.ts +0 -18
  322. package/test-gemini.sh +0 -35
  323. package/test-usage-admin.sh +0 -57
  324. package/test-usage.sh +0 -50
  325. /package/frontend/src/features/ai/{page → pages}/AIPage.tsx +0 -0
  326. /package/frontend/src/features/auth/{page → pages}/AuthMethodsPage.tsx +0 -0
  327. /package/frontend/src/features/auth/{page → pages}/ConfigurationPage.tsx +0 -0
  328. /package/frontend/src/features/dashboard/{page → pages}/DashboardPage.tsx +0 -0
  329. /package/frontend/src/features/database/{page → pages}/SQLEditorPage.tsx +0 -0
  330. /package/frontend/src/features/database/{page → pages}/TemplatesPage.tsx +0 -0
  331. /package/frontend/src/features/login/{page → pages}/CloudLoginPage.tsx +0 -0
  332. /package/frontend/src/features/login/{page → pages}/LoginPage.tsx +0 -0
  333. /package/frontend/src/features/logs/{page → pages}/AuditsPage.tsx +0 -0
  334. /package/frontend/src/features/logs/{page → pages}/LogsPage.tsx +0 -0
  335. /package/frontend/src/features/logs/{page → pages}/MCPLogsPage.tsx +0 -0
@@ -1,4 +1,4 @@
1
- import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
1
+ import { useQuery, useQueries, useMutation, useQueryClient } from '@tanstack/react-query';
2
2
  import { tableService } from '../services/table.service';
3
3
  import { useToast } from '@/lib/hooks/useToast';
4
4
  import {
@@ -6,7 +6,6 @@ import {
6
6
  GetTableSchemaResponse,
7
7
  UpdateTableSchemaRequest,
8
8
  } from '@insforge/shared-schemas';
9
- import { useMemo } from 'react';
10
9
 
11
10
  export function useTables() {
12
11
  const queryClient = useQueryClient();
@@ -24,19 +23,7 @@ export function useTables() {
24
23
  staleTime: 2 * 60 * 1000, // Cache for 2 minutes
25
24
  });
26
25
 
27
- // Query to fetch all table schemas
28
- const {
29
- data: allSchemas,
30
- isLoading: isLoadingSchemas,
31
- error: schemasError,
32
- refetch: refetchAllSchemas,
33
- } = useQuery({
34
- queryKey: ['tables', 'schemas'],
35
- queryFn: () => tableService.getAllTableSchemas(),
36
- staleTime: 2 * 60 * 1000,
37
- });
38
-
39
- // Query to fetch a specific table schema
26
+ // Query to fetch a specific table schema (cached per table)
40
27
  const useTableSchema = (tableName: string, enabled = true) => {
41
28
  return useQuery({
42
29
  queryKey: ['tables', tableName, 'schema'],
@@ -52,7 +39,6 @@ export function useTables() {
52
39
  tableService.createTable(tableName, columns),
53
40
  onSuccess: () => {
54
41
  void queryClient.invalidateQueries({ queryKey: ['tables'] });
55
- void queryClient.invalidateQueries({ queryKey: ['tables', 'schemas'] });
56
42
  showToast('Table created successfully', 'success');
57
43
  },
58
44
  onError: (error: Error) => {
@@ -66,7 +52,6 @@ export function useTables() {
66
52
  mutationFn: (tableName: string) => tableService.deleteTable(tableName),
67
53
  onSuccess: () => {
68
54
  void queryClient.invalidateQueries({ queryKey: ['tables'] });
69
- void queryClient.invalidateQueries({ queryKey: ['tables', 'schemas'] });
70
55
  showToast('Table deleted successfully', 'success');
71
56
  },
72
57
  onError: (error: Error) => {
@@ -85,7 +70,6 @@ export function useTables() {
85
70
  operations: UpdateTableSchemaRequest;
86
71
  }) => tableService.updateTableSchema(tableName, operations),
87
72
  onSuccess: (_, { tableName }) => {
88
- void queryClient.invalidateQueries({ queryKey: ['tables', 'schemas'] });
89
73
  void queryClient.invalidateQueries({ queryKey: ['tables', tableName, 'schema'] });
90
74
  showToast('Table schema updated successfully', 'success');
91
75
  },
@@ -98,34 +82,54 @@ export function useTables() {
98
82
  return {
99
83
  // Data
100
84
  tables: tables || [],
101
- allSchemas: useMemo(
102
- () => allSchemas?.filter((schema) => schema.tableName !== 'users') || [],
103
- [allSchemas]
104
- ),
105
85
  tablesCount: tables?.length || 0,
106
86
 
107
87
  // Loading states
108
88
  isLoadingTables,
109
- isLoadingSchemas,
110
89
  isCreating: createTableMutation.isPending,
111
90
  isDeleting: deleteTableMutation.isPending,
112
91
  isUpdating: updateTableSchemaMutation.isPending,
113
92
 
114
93
  // Errors
115
94
  tablesError,
116
- schemasError,
117
95
 
118
96
  // Actions
119
97
  createTable: createTableMutation.mutate,
120
98
  deleteTable: deleteTableMutation.mutate,
121
99
  updateTableSchema: updateTableSchemaMutation.mutate,
122
100
  refetchTables,
123
- refetchAllSchemas,
124
101
 
125
102
  // Helpers
126
103
  useTableSchema,
127
- getSchemaByTableName: (tableName: string): GetTableSchemaResponse | undefined => {
128
- return allSchemas?.find((schema) => schema.tableName === tableName);
129
- },
104
+ };
105
+ }
106
+
107
+ /**
108
+ * Hook to fetch all table schemas on demand.
109
+ * Use this only when you need ALL schemas (e.g., for visualizations).
110
+ * For individual tables, use useTables().useTableSchema() instead.
111
+ */
112
+ export function useAllTableSchemas(enabled = true) {
113
+ const { tables, isLoadingTables } = useTables();
114
+
115
+ const { allSchemas, isLoadingSchemas } = useQueries({
116
+ queries: enabled
117
+ ? tables
118
+ .filter((name) => name !== 'users')
119
+ .map((tableName) => ({
120
+ queryKey: ['tables', tableName, 'schema'],
121
+ queryFn: () => tableService.getTableSchema(tableName),
122
+ staleTime: 2 * 60 * 1000,
123
+ }))
124
+ : [],
125
+ combine: (results) => ({
126
+ allSchemas: results.filter((r) => r.data).map((r) => r.data as GetTableSchemaResponse),
127
+ isLoadingSchemas: results.some((r) => r.isLoading),
128
+ }),
129
+ });
130
+
131
+ return {
132
+ allSchemas,
133
+ isLoading: isLoadingTables || isLoadingSchemas,
130
134
  };
131
135
  }
@@ -4,6 +4,7 @@ export { TableListSkeleton } from './components/TableListSkeleton';
4
4
  export { TableSidebar } from './components/TableSidebar';
5
5
  export { TableForm } from './components/TableForm';
6
6
  export { RecordFormDialog } from './components/RecordFormDialog';
7
+ export { SQLModal, SQLCellButton } from './components/SQLModal';
7
8
 
8
9
  // Services
9
10
  export { tableService } from './services/table.service';
@@ -1,4 +1,4 @@
1
- import { useMemo, useState, useEffect } from 'react';
1
+ import { useMemo, useState } from 'react';
2
2
  import RefreshIcon from '@/assets/icons/refresh.svg?react';
3
3
  import {
4
4
  Button,
@@ -13,16 +13,10 @@ import {
13
13
  TooltipProvider,
14
14
  TooltipTrigger,
15
15
  } from '@/components';
16
- import { useFullMetadata } from '../hooks/useFullMetadata';
17
- import type { ExportDatabaseResponse, ExportDatabaseJsonData } from '@insforge/shared-schemas';
16
+ import { useFunctions } from '../hooks/useDatabase';
17
+ import { SQLModal, SQLCellButton } from '../components/SQLModal';
18
+ import type { DatabaseFunctionsResponse } from '@insforge/shared-schemas';
18
19
  import { isSystemFunction } from '../constants';
19
- import {
20
- DataUpdatePayload,
21
- DataUpdateResourceType,
22
- ServerEvents,
23
- SocketMessage,
24
- useSocket,
25
- } from '@/lib/contexts/SocketContext';
26
20
 
27
21
  interface FunctionRow extends DataGridRowType {
28
22
  id: string;
@@ -32,15 +26,16 @@ interface FunctionRow extends DataGridRowType {
32
26
  [key: string]: ConvertedValue | { [key: string]: string }[];
33
27
  }
34
28
 
35
- function parseFunctionsFromMetadata(metadata: ExportDatabaseResponse | undefined): FunctionRow[] {
36
- if (!metadata || metadata.format !== 'json' || typeof metadata.data === 'string') {
29
+ function parseFunctionsFromResponse(
30
+ response: DatabaseFunctionsResponse | undefined
31
+ ): FunctionRow[] {
32
+ if (!response?.functions) {
37
33
  return [];
38
34
  }
39
35
 
40
- const data = metadata.data as ExportDatabaseJsonData;
41
36
  const functions: FunctionRow[] = [];
42
37
 
43
- data.functions.forEach((func) => {
38
+ response.functions.forEach((func) => {
44
39
  if (isSystemFunction(func.functionName)) {
45
40
  return;
46
41
  }
@@ -59,29 +54,10 @@ function parseFunctionsFromMetadata(metadata: ExportDatabaseResponse | undefined
59
54
  export default function FunctionsPage() {
60
55
  const [searchQuery, setSearchQuery] = useState('');
61
56
  const [isRefreshing, setIsRefreshing] = useState(false);
62
- const { data: metadata, isLoading, error, refetch } = useFullMetadata(true);
63
-
64
- const { socket, isConnected } = useSocket();
65
-
66
- const allFunctions = useMemo(() => parseFunctionsFromMetadata(metadata), [metadata]);
67
-
68
- useEffect(() => {
69
- if (!socket || !isConnected) {
70
- return;
71
- }
57
+ const { data, isLoading, error, refetch } = useFunctions(true);
58
+ const [sqlModal, setSqlModal] = useState({ open: false, title: '', value: '' });
72
59
 
73
- const handleDataUpdate = (message: SocketMessage<DataUpdatePayload>) => {
74
- if (message.payload?.resource === DataUpdateResourceType.DATABASE) {
75
- void refetch();
76
- }
77
- };
78
-
79
- socket.on(ServerEvents.DATA_UPDATE, handleDataUpdate);
80
-
81
- return () => {
82
- socket.off(ServerEvents.DATA_UPDATE, handleDataUpdate);
83
- };
84
- }, [socket, isConnected, refetch]);
60
+ const allFunctions = useMemo(() => parseFunctionsFromResponse(data), [data]);
85
61
 
86
62
  const filteredFunctions = useMemo(() => {
87
63
  if (!searchQuery.trim()) {
@@ -132,9 +108,17 @@ export default function FunctionsPage() {
132
108
  name: 'Definition',
133
109
  width: 'minmax(400px, 8fr)',
134
110
  resizable: true,
111
+ renderCell: ({ row }) => (
112
+ <SQLCellButton
113
+ value={row.functionDef}
114
+ onClick={() =>
115
+ setSqlModal({ open: true, title: 'Function Definition', value: row.functionDef })
116
+ }
117
+ />
118
+ ),
135
119
  },
136
120
  ],
137
- []
121
+ [setSqlModal]
138
122
  );
139
123
 
140
124
  if (error) {
@@ -206,6 +190,14 @@ export default function FunctionsPage() {
206
190
  />
207
191
  </div>
208
192
  )}
193
+
194
+ {/* SQL Detail Modal */}
195
+ <SQLModal
196
+ open={sqlModal.open}
197
+ onOpenChange={(open) => setSqlModal((prev) => ({ ...prev, open }))}
198
+ title={sqlModal.title}
199
+ value={sqlModal.value}
200
+ />
209
201
  </div>
210
202
  );
211
203
  }
@@ -1,4 +1,4 @@
1
- import { useMemo, useState, useEffect } from 'react';
1
+ import { useMemo, useState } from 'react';
2
2
  import RefreshIcon from '@/assets/icons/refresh.svg?react';
3
3
  import {
4
4
  Button,
@@ -13,16 +13,10 @@ import {
13
13
  TooltipProvider,
14
14
  TooltipTrigger,
15
15
  } from '@/components';
16
- import { useFullMetadata } from '../hooks/useFullMetadata';
17
- import type { ExportDatabaseResponse, ExportDatabaseJsonData } from '@insforge/shared-schemas';
16
+ import { useIndexes } from '../hooks/useDatabase';
17
+ import { SQLModal, SQLCellButton } from '../components/SQLModal';
18
+ import type { DatabaseIndexesResponse } from '@insforge/shared-schemas';
18
19
  import { isSystemTable } from '../constants';
19
- import {
20
- DataUpdatePayload,
21
- DataUpdateResourceType,
22
- ServerEvents,
23
- SocketMessage,
24
- useSocket,
25
- } from '@/lib/contexts/SocketContext';
26
20
 
27
21
  interface IndexRow extends DataGridRowType {
28
22
  id: string;
@@ -34,28 +28,25 @@ interface IndexRow extends DataGridRowType {
34
28
  [key: string]: ConvertedValue | { [key: string]: string }[];
35
29
  }
36
30
 
37
- function parseIndexesFromMetadata(metadata: ExportDatabaseResponse | undefined): IndexRow[] {
38
- if (!metadata || metadata.format !== 'json' || typeof metadata.data === 'string') {
31
+ function parseIndexesFromResponse(response: DatabaseIndexesResponse | undefined): IndexRow[] {
32
+ if (!response?.indexes) {
39
33
  return [];
40
34
  }
41
35
 
42
- const data = metadata.data as ExportDatabaseJsonData;
43
36
  const indexes: IndexRow[] = [];
44
37
 
45
- Object.entries(data.tables).forEach(([tableName, tableData]) => {
46
- if (isSystemTable(tableName)) {
38
+ response.indexes.forEach((index) => {
39
+ if (isSystemTable(index.tableName)) {
47
40
  return;
48
41
  }
49
42
 
50
- tableData.indexes.forEach((index) => {
51
- indexes.push({
52
- id: `${tableName}_${index.indexname}`,
53
- tableName,
54
- indexName: index.indexname,
55
- indexDef: index.indexdef,
56
- isUnique: index.isUnique,
57
- isPrimary: index.isPrimary,
58
- });
43
+ indexes.push({
44
+ id: `${index.tableName}_${index.indexName}`,
45
+ tableName: index.tableName,
46
+ indexName: index.indexName,
47
+ indexDef: index.indexDef,
48
+ isUnique: index.isUnique,
49
+ isPrimary: index.isPrimary,
59
50
  });
60
51
  });
61
52
 
@@ -65,29 +56,10 @@ function parseIndexesFromMetadata(metadata: ExportDatabaseResponse | undefined):
65
56
  export default function IndexesPage() {
66
57
  const [searchQuery, setSearchQuery] = useState('');
67
58
  const [isRefreshing, setIsRefreshing] = useState(false);
68
- const { data: metadata, isLoading, error, refetch } = useFullMetadata(true);
69
-
70
- const { socket, isConnected } = useSocket();
71
-
72
- const allIndexes = useMemo(() => parseIndexesFromMetadata(metadata), [metadata]);
73
-
74
- useEffect(() => {
75
- if (!socket || !isConnected) {
76
- return;
77
- }
59
+ const { data, isLoading, error, refetch } = useIndexes(true);
60
+ const [sqlModal, setSqlModal] = useState({ open: false, title: '', value: '' });
78
61
 
79
- const handleDataUpdate = (message: SocketMessage<DataUpdatePayload>) => {
80
- if (message.payload?.resource === DataUpdateResourceType.DATABASE) {
81
- void refetch();
82
- }
83
- };
84
-
85
- socket.on(ServerEvents.DATA_UPDATE, handleDataUpdate);
86
-
87
- return () => {
88
- socket.off(ServerEvents.DATA_UPDATE, handleDataUpdate);
89
- };
90
- }, [socket, isConnected, refetch]);
62
+ const allIndexes = useMemo(() => parseIndexesFromResponse(data), [data]);
91
63
 
92
64
  const filteredIndexes = useMemo(() => {
93
65
  if (!searchQuery.trim()) {
@@ -161,9 +133,17 @@ export default function IndexesPage() {
161
133
  name: 'Definition',
162
134
  width: 'minmax(300px, 5fr)',
163
135
  resizable: true,
136
+ renderCell: ({ row }) => (
137
+ <SQLCellButton
138
+ value={row.indexDef}
139
+ onClick={() =>
140
+ setSqlModal({ open: true, title: 'Index Definition', value: row.indexDef })
141
+ }
142
+ />
143
+ ),
164
144
  },
165
145
  ],
166
- []
146
+ [setSqlModal]
167
147
  );
168
148
 
169
149
  if (error) {
@@ -235,6 +215,14 @@ export default function IndexesPage() {
235
215
  />
236
216
  </div>
237
217
  )}
218
+
219
+ {/* SQL Detail Modal */}
220
+ <SQLModal
221
+ open={sqlModal.open}
222
+ onOpenChange={(open) => setSqlModal((prev) => ({ ...prev, open }))}
223
+ title={sqlModal.title}
224
+ value={sqlModal.value}
225
+ />
238
226
  </div>
239
227
  );
240
228
  }
@@ -1,4 +1,4 @@
1
- import { useMemo, useState, useEffect } from 'react';
1
+ import { useMemo, useState } from 'react';
2
2
  import RefreshIcon from '@/assets/icons/refresh.svg?react';
3
3
  import {
4
4
  Button,
@@ -13,16 +13,10 @@ import {
13
13
  TooltipProvider,
14
14
  TooltipTrigger,
15
15
  } from '@/components';
16
- import { useFullMetadata } from '../hooks/useFullMetadata';
17
- import type { ExportDatabaseResponse, ExportDatabaseJsonData } from '@insforge/shared-schemas';
16
+ import { usePolicies } from '../hooks/useDatabase';
17
+ import { SQLModal, SQLCellButton } from '../components/SQLModal';
18
+ import type { DatabasePoliciesResponse } from '@insforge/shared-schemas';
18
19
  import { isSystemTable } from '../constants';
19
- import {
20
- DataUpdatePayload,
21
- DataUpdateResourceType,
22
- ServerEvents,
23
- SocketMessage,
24
- useSocket,
25
- } from '@/lib/contexts/SocketContext';
26
20
 
27
21
  interface PolicyRow extends DataGridRowType {
28
22
  id: string;
@@ -35,29 +29,26 @@ interface PolicyRow extends DataGridRowType {
35
29
  [key: string]: ConvertedValue | { [key: string]: string }[];
36
30
  }
37
31
 
38
- function parsePoliciesFromMetadata(metadata: ExportDatabaseResponse | undefined): PolicyRow[] {
39
- if (!metadata || metadata.format !== 'json' || typeof metadata.data === 'string') {
32
+ function parsePoliciesFromResponse(response: DatabasePoliciesResponse | undefined): PolicyRow[] {
33
+ if (!response?.policies) {
40
34
  return [];
41
35
  }
42
36
 
43
- const data = metadata.data as ExportDatabaseJsonData;
44
37
  const policies: PolicyRow[] = [];
45
38
 
46
- Object.entries(data.tables).forEach(([tableName, tableData]) => {
47
- if (isSystemTable(tableName)) {
39
+ response.policies.forEach((policy) => {
40
+ if (isSystemTable(policy.tableName)) {
48
41
  return;
49
42
  }
50
43
 
51
- tableData.policies.forEach((policy) => {
52
- policies.push({
53
- id: `${tableName}_${policy.policyname}`,
54
- tableName,
55
- policyName: policy.policyname,
56
- cmd: policy.cmd,
57
- roles: Array.isArray(policy.roles) ? policy.roles.join(', ') : String(policy.roles),
58
- qual: policy.qual,
59
- withCheck: policy.withCheck,
60
- });
44
+ policies.push({
45
+ id: `${policy.tableName}_${policy.policyName}`,
46
+ tableName: policy.tableName,
47
+ policyName: policy.policyName,
48
+ cmd: policy.cmd,
49
+ roles: Array.isArray(policy.roles) ? policy.roles.join(', ') : String(policy.roles),
50
+ qual: policy.qual,
51
+ withCheck: policy.withCheck,
61
52
  });
62
53
  });
63
54
 
@@ -67,29 +58,10 @@ function parsePoliciesFromMetadata(metadata: ExportDatabaseResponse | undefined)
67
58
  export default function PoliciesPage() {
68
59
  const [searchQuery, setSearchQuery] = useState('');
69
60
  const [isRefreshing, setIsRefreshing] = useState(false);
70
- const { data: metadata, isLoading, error, refetch } = useFullMetadata(true);
71
-
72
- const { socket, isConnected } = useSocket();
73
-
74
- const allPolicies = useMemo(() => parsePoliciesFromMetadata(metadata), [metadata]);
61
+ const { data, isLoading, error, refetch } = usePolicies(true);
62
+ const [sqlModal, setSqlModal] = useState({ open: false, title: '', value: '' });
75
63
 
76
- useEffect(() => {
77
- if (!socket || !isConnected) {
78
- return;
79
- }
80
-
81
- const handleDataUpdate = (message: SocketMessage<DataUpdatePayload>) => {
82
- if (message.payload?.resource === DataUpdateResourceType.DATABASE) {
83
- void refetch();
84
- }
85
- };
86
-
87
- socket.on(ServerEvents.DATA_UPDATE, handleDataUpdate);
88
-
89
- return () => {
90
- socket.off(ServerEvents.DATA_UPDATE, handleDataUpdate);
91
- };
92
- }, [socket, isConnected, refetch]);
64
+ const allPolicies = useMemo(() => parsePoliciesFromResponse(data), [data]);
93
65
 
94
66
  const filteredPolicies = useMemo(() => {
95
67
  if (!searchQuery.trim()) {
@@ -157,21 +129,30 @@ export default function PoliciesPage() {
157
129
  name: 'Using',
158
130
  width: 'minmax(200px, 2fr)',
159
131
  resizable: true,
160
- renderCell: ({ row }) => {
161
- return <span className="text-xs font-mono">{row.qual || '-'}</span>;
162
- },
132
+ renderCell: ({ row }) => (
133
+ <SQLCellButton
134
+ value={row.qual}
135
+ onClick={() => row.qual && setSqlModal({ open: true, title: 'Using', value: row.qual })}
136
+ />
137
+ ),
163
138
  },
164
139
  {
165
140
  key: 'withCheck',
166
141
  name: 'With Check',
167
142
  width: 'minmax(200px, 2fr)',
168
143
  resizable: true,
169
- renderCell: ({ row }) => {
170
- return <span className="text-xs font-mono">{row.withCheck || '-'}</span>;
171
- },
144
+ renderCell: ({ row }) => (
145
+ <SQLCellButton
146
+ value={row.withCheck}
147
+ onClick={() =>
148
+ row.withCheck &&
149
+ setSqlModal({ open: true, title: 'With Check', value: row.withCheck })
150
+ }
151
+ />
152
+ ),
172
153
  },
173
154
  ],
174
- []
155
+ [setSqlModal]
175
156
  );
176
157
 
177
158
  if (error) {
@@ -243,6 +224,14 @@ export default function PoliciesPage() {
243
224
  />
244
225
  </div>
245
226
  )}
227
+
228
+ {/* SQL Detail Modal */}
229
+ <SQLModal
230
+ open={sqlModal.open}
231
+ onOpenChange={(open) => setSqlModal((prev) => ({ ...prev, open }))}
232
+ title={sqlModal.title}
233
+ value={sqlModal.value}
234
+ />
246
235
  </div>
247
236
  );
248
237
  }
@@ -1,5 +1,4 @@
1
1
  import { useState, useEffect, useCallback, useMemo, useRef } from 'react';
2
- import { useQueryClient } from '@tanstack/react-query';
3
2
  import { Plus, Upload } from 'lucide-react';
4
3
  import PencilIcon from '@/assets/icons/pencil.svg?react';
5
4
  import RefreshIcon from '@/assets/icons/refresh.svg?react';
@@ -31,13 +30,6 @@ import { useToast } from '@/lib/hooks/useToast';
31
30
  import { DatabaseDataGrid } from '@/features/database/components/DatabaseDataGrid';
32
31
  import { SortColumn } from 'react-data-grid';
33
32
  import { convertValueForColumn } from '@/lib/utils/utils';
34
- import {
35
- DataUpdatePayload,
36
- DataUpdateResourceType,
37
- ServerEvents,
38
- SocketMessage,
39
- useSocket,
40
- } from '@/lib/contexts/SocketContext';
41
33
  import { useCSVImport } from '@/features/database/hooks/useCSVImport';
42
34
 
43
35
  const PAGE_SIZE = 50;
@@ -62,15 +54,12 @@ export default function TablesPage() {
62
54
 
63
55
  const { confirm, confirmDialogProps } = useConfirm();
64
56
  const { showToast } = useToast();
65
- const queryClient = useQueryClient();
66
57
  const fileInputRef = useRef<HTMLInputElement>(null);
67
58
  const { tables, isLoadingTables, tablesError, deleteTable, useTableSchema, refetchTables } =
68
59
  useTables();
69
60
 
70
61
  const recordsHook = useRecords(selectedTable || '');
71
62
 
72
- const { socket, isConnected } = useSocket();
73
-
74
63
  const handleFileSelect = (event: React.ChangeEvent<HTMLInputElement>) => {
75
64
  const file = event.target.files?.[0];
76
65
  if (file && selectedTable) {
@@ -184,37 +173,6 @@ export default function TablesPage() {
184
173
  const tableError = recordsError;
185
174
  const refetchTableData = refetchRecords;
186
175
 
187
- useEffect(() => {
188
- if (!socket || !isConnected) {
189
- return;
190
- }
191
-
192
- const handleDataUpdate = (message: SocketMessage<DataUpdatePayload>) => {
193
- if (message.payload?.resource === DataUpdateResourceType.DATABASE) {
194
- // Invalidate all tables queries
195
- void queryClient.invalidateQueries({ queryKey: ['tables'] });
196
- void queryClient.invalidateQueries({ queryKey: ['records', selectedTable] });
197
- }
198
-
199
- if (message.payload?.resource === DataUpdateResourceType.RECORDS) {
200
- // Invalidate records queries for the updated table
201
- const data = message.payload.data as { tableName?: string };
202
- const updatedTableName = data?.tableName;
203
-
204
- // Only invalidate if this is the currently selected table
205
- if (updatedTableName && updatedTableName === selectedTable) {
206
- void queryClient.invalidateQueries({ queryKey: ['records', selectedTable] });
207
- }
208
- }
209
- };
210
-
211
- socket.on(ServerEvents.DATA_UPDATE, handleDataUpdate);
212
-
213
- return () => {
214
- socket.off(ServerEvents.DATA_UPDATE, handleDataUpdate);
215
- };
216
- }, [socket, isConnected, queryClient, selectedTable]);
217
-
218
176
  // Reset sorting flag when loading completes
219
177
  useEffect(() => {
220
178
  if (!isLoadingTable && isSorting) {