insforge 0.3.2 → 1.2.10

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 (507) hide show
  1. package/.claude-plugin/marketplace.json +20 -0
  2. package/.cursor/rules/cursor-rules.mdc +94 -0
  3. package/.dockerignore +3 -0
  4. package/.env.example +33 -4
  5. package/.github/ISSUE_TEMPLATE/bug_report.yml +13 -60
  6. package/.github/ISSUE_TEMPLATE/config.yml +2 -2
  7. package/.github/ISSUE_TEMPLATE/feature_request.yml +10 -63
  8. package/.github/PULL_REQUEST_TEMPLATE.md +7 -0
  9. package/.github/workflows/build-image.yml +2 -1
  10. package/.github/workflows/e2e.yml +63 -0
  11. package/CHANGELOG.md +41 -0
  12. package/CLAUDE_PLUGIN.md +104 -0
  13. package/CODE_OF_CONDUCT.md +128 -0
  14. package/CONTRIBUTING.md +1 -1
  15. package/Dockerfile +4 -1
  16. package/README.md +66 -18
  17. package/assets/mcpInstallv2.png +0 -0
  18. package/assets/sampleResponse.png +0 -0
  19. package/auth/index.html +13 -0
  20. package/auth/package.json +28 -0
  21. package/auth/public/favicon.ico +0 -0
  22. package/auth/src/App.tsx +33 -0
  23. package/auth/src/components/ErrorCard.tsx +37 -0
  24. package/auth/src/components/Layout.tsx +13 -0
  25. package/auth/src/index.css +19 -0
  26. package/auth/src/lib/broadcastService.ts +115 -0
  27. package/auth/src/lib/utils.ts +11 -0
  28. package/auth/src/main.tsx +22 -0
  29. package/auth/src/pages/ForgotPasswordPage.tsx +11 -0
  30. package/auth/src/pages/ResetPasswordPage.tsx +11 -0
  31. package/auth/src/pages/SignInPage.tsx +57 -0
  32. package/auth/src/pages/SignUpPage.tsx +57 -0
  33. package/auth/src/pages/VerifyEmailPage.tsx +20 -0
  34. package/auth/src/vite-env.d.ts +10 -0
  35. package/auth/tsconfig.json +32 -0
  36. package/auth/tsconfig.node.json +11 -0
  37. package/auth/vite.config.ts +25 -0
  38. package/backend/package.json +9 -9
  39. package/backend/src/api/{middleware → middlewares}/auth.ts +8 -9
  40. package/backend/src/api/middlewares/rate-limiters.ts +127 -0
  41. package/backend/src/api/routes/{ai.ts → ai/index.routes.ts} +20 -24
  42. package/backend/src/api/routes/auth/index.routes.ts +570 -0
  43. package/backend/src/api/routes/auth/oauth.routes.ts +448 -0
  44. package/backend/src/api/routes/{database.advance.ts → database/advance.routes.ts} +107 -65
  45. package/backend/src/api/routes/database/index.routes.ts +13 -0
  46. package/backend/src/api/routes/{database.records.ts → database/records.routes.ts} +22 -8
  47. package/backend/src/api/routes/{database.tables.ts → database/tables.routes.ts} +20 -23
  48. package/backend/src/api/routes/docs/index.routes.ts +76 -0
  49. package/backend/src/api/routes/functions/index.routes.ts +188 -0
  50. package/backend/src/api/routes/{logs.ts → logs/index.routes.ts} +25 -30
  51. package/backend/src/api/routes/{metadata.ts → metadata/index.routes.ts} +21 -31
  52. package/backend/src/api/routes/{secrets.ts → secrets/index.routes.ts} +27 -22
  53. package/backend/src/api/routes/{storage.ts → storage/index.routes.ts} +34 -53
  54. package/backend/src/api/routes/usage/index.routes.ts +89 -0
  55. package/backend/src/infra/config/app.config.ts +51 -0
  56. package/backend/src/{core/database/manager.ts → infra/database/database.manager.ts} +76 -85
  57. package/backend/src/infra/database/migrations/013_create-auth-schema-functions.sql +44 -0
  58. package/backend/src/infra/database/migrations/014_add-updated-at-trigger-user-table.sql +8 -0
  59. package/backend/src/infra/database/migrations/015_create-auth-config-and-email-otp-tables.sql +60 -0
  60. package/backend/src/infra/database/migrations/016_update-auth-config-and-email-otp.sql +24 -0
  61. package/backend/src/{core/secrets/encryption.ts → infra/security/encryption.manager.ts} +3 -2
  62. package/backend/src/infra/security/token.manager.ts +125 -0
  63. package/backend/src/{core/socket/socket.ts → infra/socket/socket.manager.ts} +15 -15
  64. package/backend/src/providers/ai/openrouter.provider.ts +377 -0
  65. package/backend/src/providers/email/base.provider.ts +41 -0
  66. package/backend/src/providers/email/cloud.provider.ts +187 -0
  67. package/backend/src/{core/logs/providers → providers/logs}/base.provider.ts +11 -11
  68. package/backend/src/{core/logs/providers → providers/logs}/cloudwatch.provider.ts +61 -38
  69. package/backend/src/providers/logs/local.provider.ts +185 -0
  70. package/backend/src/providers/oauth/base.provider.ts +29 -0
  71. package/backend/src/providers/oauth/discord.provider.ts +195 -0
  72. package/backend/src/providers/oauth/facebook.provider.ts +194 -0
  73. package/backend/src/providers/oauth/github.provider.ts +208 -0
  74. package/backend/src/providers/oauth/google.provider.ts +249 -0
  75. package/backend/src/providers/oauth/index.ts +7 -0
  76. package/backend/src/providers/oauth/linkedin.provider.ts +240 -0
  77. package/backend/src/providers/oauth/microsoft.provider.ts +169 -0
  78. package/backend/src/providers/oauth/x.provider.ts +202 -0
  79. package/backend/src/providers/storage/base.provider.ts +29 -0
  80. package/backend/src/providers/storage/local.provider.ts +103 -0
  81. package/backend/src/providers/storage/s3.provider.ts +313 -0
  82. package/backend/src/server.ts +70 -74
  83. package/backend/src/{core/ai/config.ts → services/ai/ai-config.service.ts} +19 -24
  84. package/backend/src/services/ai/ai-model.service.ts +60 -0
  85. package/backend/src/{core/ai/usage.ts → services/ai/ai-usage.service.ts} +28 -35
  86. package/backend/src/{core/ai/chat.ts → services/ai/chat-completion.service.ts} +37 -24
  87. package/backend/src/services/ai/helpers.ts +64 -0
  88. package/backend/src/{core/ai/image.ts → services/ai/image-generation.service.ts} +17 -19
  89. package/backend/src/services/ai/index.ts +13 -0
  90. package/backend/src/services/auth/auth-config.service.ts +250 -0
  91. package/backend/src/services/auth/auth-otp.service.ts +424 -0
  92. package/backend/src/services/auth/auth.service.ts +1136 -0
  93. package/backend/src/services/auth/index.ts +4 -0
  94. package/backend/src/{core/auth/oauth.ts → services/auth/oauth-config.service.ts} +106 -52
  95. package/backend/src/{core/database/advance.ts → services/database/database-advance.service.ts} +97 -131
  96. package/backend/src/services/database/database-table.service.ts +811 -0
  97. package/backend/src/services/email/email.service.ts +75 -0
  98. package/backend/src/{core/functions/functions.ts → services/functions/function.service.ts} +95 -88
  99. package/backend/src/{core/logs/audit.ts → services/logs/audit.service.ts} +92 -75
  100. package/backend/src/services/logs/log.service.ts +73 -0
  101. package/backend/src/{core/secrets/secrets.ts → services/secrets/secret.service.ts} +48 -66
  102. package/backend/src/services/storage/storage.service.ts +617 -0
  103. package/backend/src/services/usage/usage.service.ts +149 -0
  104. package/backend/src/types/auth.ts +66 -2
  105. package/backend/src/types/email.ts +8 -0
  106. package/backend/src/types/error-constants.ts +4 -0
  107. package/backend/src/types/logs.ts +0 -29
  108. package/backend/src/{core/socket/types.ts → types/socket.ts} +5 -6
  109. package/backend/src/utils/environment.ts +9 -3
  110. package/backend/src/utils/logger.ts +20 -2
  111. package/backend/src/utils/seed.ts +150 -57
  112. package/backend/src/utils/sql-parser.ts +1 -1
  113. package/backend/src/utils/utils.ts +114 -0
  114. package/backend/src/utils/validations.ts +40 -4
  115. package/backend/tests/local/test-ai-config.sh +129 -0
  116. package/backend/tests/local/test-ai-usage.sh +80 -0
  117. package/backend/tests/local/test-auth-router.sh +1 -1
  118. package/backend/tests/local/test-e2e.sh +1 -1
  119. package/backend/tests/local/test-functions.sh +123 -0
  120. package/backend/tests/local/test-logs.sh +132 -0
  121. package/backend/tests/local/test-public-bucket.sh +3 -3
  122. package/backend/tests/local/test-secrets.sh +14 -12
  123. package/backend/tests/local/test-traditional-rest.sh +2 -2
  124. package/backend/tests/manual/test-rawsql-modes.sh +244 -0
  125. package/backend/tests/test-config.sh +37 -1
  126. package/backend/tests/unit/cloud-token.test.ts +48 -0
  127. package/backend/tests/unit/constant.test.ts +8 -0
  128. package/backend/tests/unit/email.test.ts +372 -0
  129. package/backend/tests/unit/environment.test.ts +59 -0
  130. package/backend/tests/unit/helpers.test.ts +63 -0
  131. package/backend/tests/unit/logger.test.ts +22 -0
  132. package/backend/tests/unit/rate-limit.test.ts +154 -0
  133. package/backend/tests/unit/response.test.ts +58 -0
  134. package/backend/tests/unit/sql-parser.test.ts +74 -0
  135. package/backend/tests/unit/uuid.test.ts +21 -0
  136. package/backend/tests/unit/validations.test.ts +80 -0
  137. package/backend/tsconfig.json +1 -1
  138. package/backend/vitest.config.ts +11 -0
  139. package/claude-plugin/.claude-plugin/plugin.json +24 -0
  140. package/claude-plugin/README.md +133 -0
  141. package/claude-plugin/skills/insforge-schema-patterns/SKILL.md +270 -0
  142. package/docker-compose.prod.yml +60 -4
  143. package/docker-compose.yml +65 -4
  144. package/docker-init/db/db-init.sql +6 -34
  145. package/docker-init/logs/vector.yml +236 -0
  146. package/docs/README.md +44 -0
  147. package/docs/changelog.mdx +67 -0
  148. package/docs/core-concepts/ai/architecture.mdx +373 -0
  149. package/docs/core-concepts/ai/sdk.mdx +213 -0
  150. package/docs/core-concepts/authentication/architecture.mdx +278 -0
  151. package/docs/core-concepts/authentication/sdk.mdx +414 -0
  152. package/docs/core-concepts/authentication/ui-components/customization.mdx +529 -0
  153. package/docs/core-concepts/authentication/ui-components/nextjs.mdx +221 -0
  154. package/docs/core-concepts/authentication/ui-components/react-router.mdx +184 -0
  155. package/docs/core-concepts/authentication/ui-components/react.mdx +129 -0
  156. package/docs/core-concepts/database/architecture.mdx +256 -0
  157. package/docs/core-concepts/database/sdk.mdx +382 -0
  158. package/docs/core-concepts/functions/architecture.mdx +105 -0
  159. package/docs/core-concepts/functions/sdk.mdx +184 -0
  160. package/docs/core-concepts/storage/architecture.mdx +243 -0
  161. package/docs/core-concepts/storage/sdk.mdx +253 -0
  162. package/docs/deployment/README.md +94 -0
  163. package/docs/deployment/deploy-to-aws-ec2.md +565 -0
  164. package/docs/deployment/deploy-to-azure-virtual-machines.md +313 -0
  165. package/docs/deployment/deploy-to-google-cloud-compute-engine.md +613 -0
  166. package/docs/deployment/deploy-to-render.md +441 -0
  167. package/docs/docs.json +210 -0
  168. package/docs/examples/framework-guides/nextjs.mdx +131 -0
  169. package/docs/examples/framework-guides/nuxt.mdx +165 -0
  170. package/docs/examples/framework-guides/react.mdx +165 -0
  171. package/docs/examples/framework-guides/svelte.mdx +153 -0
  172. package/docs/examples/framework-guides/vue.mdx +159 -0
  173. package/docs/examples/overview.mdx +67 -0
  174. package/docs/favicon.svg +19 -0
  175. package/docs/images/changelog/nov-2025/auth-components.webp +0 -0
  176. package/docs/images/changelog/nov-2025/database-metadata.webp +0 -0
  177. package/docs/images/changelog/nov-2025/quickstart-prompts.webp +0 -0
  178. package/docs/images/changelog/nov-2025/sql-editor.webp +0 -0
  179. package/docs/images/changelog/nov-2025/usage-page.webp +0 -0
  180. package/docs/images/changelog/october-2025/csv-upload.webp +0 -0
  181. package/docs/images/changelog/october-2025/logs-feature.webp +0 -0
  182. package/docs/images/changelog/october-2025/oauth-providers.webp +0 -0
  183. package/docs/images/checks-passed.png +0 -0
  184. package/docs/images/dashboard-connect-expanded.png +0 -0
  185. package/docs/images/dashboard-connect.png +0 -0
  186. package/docs/images/hero-dark.png +0 -0
  187. package/docs/images/hero-light.png +0 -0
  188. package/docs/images/icons/ai.svg +4 -0
  189. package/docs/images/icons/auth.svg +1 -0
  190. package/docs/images/icons/database.svg +1 -0
  191. package/docs/images/icons/function.svg +1 -0
  192. package/docs/images/icons/storage.svg +1 -0
  193. package/docs/images/logos/nextjs.svg +4 -0
  194. package/docs/images/logos/nuxt.svg +4 -0
  195. package/docs/images/logos/react.svg +5 -0
  196. package/docs/images/logos/svelte.svg +4 -0
  197. package/docs/images/logos/vue.svg +5 -0
  198. package/docs/images/mcp-install.png +0 -0
  199. package/docs/images/onboarding-mcp.png +0 -0
  200. package/docs/insforge-instructions-sdk.md +55 -374
  201. package/docs/introduction.mdx +45 -0
  202. package/docs/logo/dark.svg +22 -0
  203. package/docs/logo/light.svg +20 -0
  204. package/docs/partnership.mdx +647 -0
  205. package/docs/quickstart.mdx +83 -0
  206. package/docs/showcase/2048-arena.png +0 -0
  207. package/docs/showcase/framegen-cloud.png +0 -0
  208. package/docs/showcase/line-connect-race.png +0 -0
  209. package/docs/showcase/moment-vibe.png +0 -0
  210. package/docs/showcase/national-flags.png +0 -0
  211. package/docs/showcase/pokemon-vibe.png +0 -0
  212. package/docs/showcase/pure-browse-buy.png +0 -0
  213. package/docs/showcase.mdx +52 -0
  214. package/docs/snippets/sdk-installation.mdx +22 -0
  215. package/docs/snippets/service-icons.mdx +27 -0
  216. package/eslint.config.js +10 -3
  217. package/frontend/package.json +10 -4
  218. package/frontend/src/App.tsx +13 -82
  219. package/frontend/src/assets/icons/connected.svg +3 -0
  220. package/frontend/src/assets/icons/loader.svg +9 -0
  221. package/frontend/src/assets/logos/apple.svg +4 -0
  222. package/frontend/src/assets/logos/discord.svg +1 -1
  223. package/frontend/src/assets/logos/facebook.svg +3 -0
  224. package/frontend/src/assets/logos/instagram.svg +2 -0
  225. package/frontend/src/assets/logos/linkedin.svg +3 -0
  226. package/frontend/src/assets/logos/microsoft.svg +1 -0
  227. package/frontend/src/assets/logos/spotify.svg +17 -0
  228. package/frontend/src/assets/logos/tiktok.svg +6 -0
  229. package/frontend/src/assets/logos/x.svg +3 -0
  230. package/frontend/src/components/Checkbox.tsx +27 -29
  231. package/frontend/src/components/CodeBlock.tsx +55 -2
  232. package/frontend/src/components/CodeEditor.tsx +92 -0
  233. package/frontend/src/components/ConfirmDialog.tsx +1 -1
  234. package/frontend/src/components/ConnectCTA.tsx +38 -0
  235. package/frontend/src/components/CopyButton.tsx +52 -15
  236. package/frontend/src/components/ErrorState.tsx +1 -2
  237. package/frontend/src/components/FeatureSidebar.tsx +6 -6
  238. package/frontend/src/components/FeatureSidebarItem.tsx +2 -2
  239. package/frontend/src/components/JsonHighlight.tsx +21 -9
  240. package/frontend/src/components/ProjectInfoModal.tsx +128 -0
  241. package/frontend/src/components/PromptDialog.tsx +1 -4
  242. package/frontend/src/components/SearchInput.tsx +1 -2
  243. package/frontend/src/components/Stepper.tsx +53 -0
  244. package/frontend/src/components/ThemeToggle.tsx +3 -3
  245. package/frontend/src/components/datagrid/DataGrid.tsx +25 -32
  246. package/frontend/src/components/datagrid/cell-editors/DateCellEditor.tsx +1 -2
  247. package/frontend/src/components/datagrid/cell-editors/JsonCellEditor.tsx +2 -4
  248. package/frontend/src/components/datagrid/index.ts +23 -0
  249. package/frontend/src/components/index.ts +23 -30
  250. package/frontend/src/components/layout/AppHeader.tsx +133 -92
  251. package/frontend/src/components/layout/AppSidebar.tsx +80 -170
  252. package/frontend/src/components/layout/Layout.tsx +12 -23
  253. package/frontend/src/components/layout/PrimaryMenu.tsx +187 -0
  254. package/frontend/src/components/layout/SecondaryMenu.tsx +70 -0
  255. package/frontend/src/components/layout/index.ts +5 -0
  256. package/frontend/src/components/radix/Tooltip.tsx +24 -13
  257. package/frontend/src/components/radix/index.ts +22 -0
  258. package/frontend/src/features/ai/components/AIConfigCard.tsx +129 -83
  259. package/frontend/src/features/ai/components/AIEmptyState.tsx +12 -7
  260. package/frontend/src/features/ai/components/ModalityFilterSidebar.tsx +101 -0
  261. package/frontend/src/features/ai/components/ModelSelectionDialog.tsx +135 -0
  262. package/frontend/src/features/ai/components/ModelSelectionGrid.tsx +51 -0
  263. package/frontend/src/features/ai/components/SystemPromptDialog.tsx +118 -0
  264. package/frontend/src/features/ai/components/index.ts +6 -0
  265. package/frontend/src/features/ai/helpers.ts +57 -71
  266. package/frontend/src/features/ai/hooks/useAIConfigs.ts +39 -113
  267. package/frontend/src/features/ai/hooks/useAIUsage.ts +0 -2
  268. package/frontend/src/features/ai/page/AIPage.tsx +67 -79
  269. package/frontend/src/features/ai/services/ai.service.ts +5 -5
  270. package/frontend/src/features/auth/components/AuthPreview.tsx +96 -0
  271. package/frontend/src/features/auth/components/OAuthConfigDialog.tsx +53 -30
  272. package/frontend/src/features/auth/components/UserFormDialog.tsx +13 -6
  273. package/frontend/src/features/auth/components/UsersDataGrid.tsx +44 -14
  274. package/frontend/src/features/auth/components/index.ts +5 -0
  275. package/frontend/src/features/auth/helpers.tsx +200 -0
  276. package/frontend/src/features/auth/hooks/useAnonToken.ts +30 -0
  277. package/frontend/src/features/auth/hooks/useAuthConfig.ts +48 -0
  278. package/frontend/src/features/auth/hooks/useOAuthConfig.ts +14 -10
  279. package/frontend/src/features/auth/hooks/useUsers.ts +43 -5
  280. package/frontend/src/features/auth/index.ts +3 -2
  281. package/frontend/src/features/auth/page/AuthMethodsPage.tsx +275 -0
  282. package/frontend/src/features/auth/page/ConfigurationPage.tsx +395 -0
  283. package/frontend/src/features/auth/page/UsersPage.tsx +285 -0
  284. package/frontend/src/features/auth/services/anonToken.service.ts +11 -0
  285. package/frontend/src/features/auth/services/config.service.ts +19 -0
  286. package/frontend/src/features/auth/services/{oauth.service.ts → oauth-config.service.ts} +4 -4
  287. package/frontend/src/features/auth/services/{auth.service.ts → user.service.ts} +7 -53
  288. package/frontend/src/features/dashboard/components/ConnectionSuccessBanner.tsx +35 -0
  289. package/frontend/src/features/dashboard/components/PromptCard.tsx +21 -0
  290. package/frontend/src/features/dashboard/components/PromptDialog.tsx +103 -0
  291. package/frontend/src/features/dashboard/components/StatsCard.tsx +50 -0
  292. package/frontend/src/features/dashboard/components/index.ts +4 -0
  293. package/frontend/src/features/dashboard/page/DashboardPage.tsx +187 -169
  294. package/frontend/src/features/dashboard/prompts/ai-chatbot.ts +13 -0
  295. package/frontend/src/features/dashboard/prompts/crm-system.ts +13 -0
  296. package/frontend/src/features/dashboard/prompts/ecommerce-platform.ts +12 -0
  297. package/frontend/src/features/dashboard/prompts/index.ts +31 -0
  298. package/frontend/src/features/dashboard/prompts/instagram-clone.ts +11 -0
  299. package/frontend/src/features/dashboard/prompts/notion-clone.ts +14 -0
  300. package/frontend/src/features/dashboard/prompts/reddit-clone.ts +12 -0
  301. package/frontend/src/features/database/components/DatabaseDataGrid.tsx +48 -17
  302. package/frontend/src/features/database/components/ForeignKeyCell.tsx +15 -34
  303. package/frontend/src/features/database/components/ForeignKeyPopover.tsx +19 -20
  304. package/frontend/src/features/database/components/LinkRecordModal.tsx +120 -125
  305. package/frontend/src/features/database/components/RecordFormDialog.tsx +22 -33
  306. package/frontend/src/features/database/components/RecordFormField.tsx +45 -47
  307. package/frontend/src/features/database/components/TableEmptyState.tsx +6 -5
  308. package/frontend/src/features/database/components/TableForm.tsx +28 -15
  309. package/frontend/src/features/database/components/TableFormColumn.tsx +2 -3
  310. package/frontend/src/features/database/components/TableSidebar.tsx +1 -1
  311. package/frontend/src/features/database/components/TablesEmptyState.tsx +48 -0
  312. package/frontend/src/features/database/components/TemplateCard.tsx +37 -0
  313. package/frontend/src/features/database/components/TemplatePreview.tsx +92 -0
  314. package/frontend/src/features/database/components/index.ts +19 -0
  315. package/frontend/src/features/database/constants.ts +28 -2
  316. package/frontend/src/features/database/contexts/SQLEditorContext.tsx +188 -0
  317. package/frontend/src/features/database/helpers.ts +2 -2
  318. package/frontend/src/features/database/hooks/useCSVImport.ts +29 -0
  319. package/frontend/src/features/database/hooks/useFullMetadata.ts +18 -0
  320. package/frontend/src/features/database/hooks/useRawSQL.ts +55 -0
  321. package/frontend/src/features/database/hooks/useRecords.ts +139 -0
  322. package/frontend/src/features/database/hooks/useTables.ts +131 -0
  323. package/frontend/src/features/database/index.ts +6 -1
  324. package/frontend/src/features/database/page/FunctionsPage.tsx +211 -0
  325. package/frontend/src/features/database/page/IndexesPage.tsx +240 -0
  326. package/frontend/src/features/database/page/PoliciesPage.tsx +248 -0
  327. package/frontend/src/features/database/page/SQLEditorPage.tsx +382 -0
  328. package/frontend/src/features/database/page/{DatabasePage.tsx → TablesPage.tsx} +186 -185
  329. package/frontend/src/features/database/page/TemplatesPage.tsx +39 -0
  330. package/frontend/src/features/database/page/TriggersPage.tsx +242 -0
  331. package/frontend/src/features/database/services/advance.service.ts +66 -0
  332. package/frontend/src/features/database/services/{database.service.ts → record.service.ts} +67 -64
  333. package/frontend/src/features/database/services/table.service.ts +64 -0
  334. package/frontend/src/features/database/templates/ai-chatbot.ts +402 -0
  335. package/frontend/src/features/database/templates/crm-system.ts +528 -0
  336. package/frontend/src/features/database/templates/ecommerce-platform.ts +553 -0
  337. package/frontend/src/features/database/templates/index.ts +34 -0
  338. package/frontend/src/features/database/templates/instagram-clone.ts +222 -0
  339. package/frontend/src/features/database/templates/notion-clone.ts +483 -0
  340. package/frontend/src/features/database/templates/reddit-clone.ts +526 -0
  341. package/frontend/src/features/functions/components/FunctionRow.tsx +2 -1
  342. package/frontend/src/features/functions/components/FunctionsSidebar.tsx +1 -1
  343. package/frontend/src/features/functions/components/SecretRow.tsx +1 -1
  344. package/frontend/src/features/functions/components/index.ts +5 -0
  345. package/frontend/src/features/functions/hooks/useFunctions.ts +4 -4
  346. package/frontend/src/features/{secrets → functions}/hooks/useSecrets.ts +5 -5
  347. package/frontend/src/features/functions/page/FunctionsPage.tsx +160 -17
  348. package/frontend/src/features/functions/{components/SecretsContent.tsx → page/SecretsPage.tsx} +8 -12
  349. package/frontend/src/features/functions/services/{functions.service.ts → function.service.ts} +2 -2
  350. package/frontend/src/features/{secrets/services/secrets.service.ts → functions/services/secret.service.ts} +2 -2
  351. package/frontend/src/features/login/hooks/usePartnerOrigin.ts +27 -0
  352. package/frontend/src/features/login/page/CloudLoginPage.tsx +79 -54
  353. package/frontend/src/features/login/page/LoginPage.tsx +16 -23
  354. package/frontend/src/features/login/services/partnership.service.ts +65 -0
  355. package/frontend/src/features/logs/components/LogsDataGrid.tsx +89 -0
  356. package/frontend/src/features/logs/components/SeverityBadge.tsx +18 -0
  357. package/frontend/src/features/logs/components/index.ts +2 -0
  358. package/frontend/src/features/logs/helpers.ts +24 -0
  359. package/frontend/src/features/logs/hooks/useAuditLogs.ts +4 -4
  360. package/frontend/src/features/logs/hooks/useLogSources.ts +137 -0
  361. package/frontend/src/features/logs/hooks/useLogs.ts +163 -0
  362. package/frontend/src/features/logs/hooks/useMcpUsage.ts +181 -0
  363. package/frontend/src/features/logs/index.ts +8 -2
  364. package/frontend/src/features/logs/page/AuditsPage.tsx +91 -38
  365. package/frontend/src/features/logs/page/LogsPage.tsx +152 -0
  366. package/frontend/src/features/logs/page/MCPLogsPage.tsx +84 -0
  367. package/frontend/src/features/logs/services/audit.service.ts +63 -0
  368. package/frontend/src/features/logs/services/log.service.ts +15 -110
  369. package/frontend/src/features/logs/services/usage.service.ts +31 -0
  370. package/frontend/src/features/onboard/components/McpConnectionStatus.tsx +68 -0
  371. package/frontend/src/features/onboard/components/OnboardingModal.tsx +267 -0
  372. package/frontend/src/features/onboard/components/VideoDemoModal.tsx +38 -0
  373. package/frontend/src/features/onboard/components/index.ts +4 -0
  374. package/frontend/src/features/onboard/components/mcp/CursorDeeplinkGenerator.tsx +2 -2
  375. package/frontend/src/features/onboard/components/mcp/{mcp-helper.tsx → helpers.tsx} +8 -8
  376. package/frontend/src/features/onboard/components/mcp/index.ts +2 -3
  377. package/frontend/src/features/onboard/index.ts +13 -3
  378. package/frontend/src/features/storage/components/BucketEmptyState.tsx +9 -6
  379. package/frontend/src/features/storage/components/BucketFormDialog.tsx +25 -41
  380. package/frontend/src/features/storage/components/FilePreviewDialog.tsx +20 -8
  381. package/frontend/src/features/storage/components/StorageDataGrid.tsx +4 -3
  382. package/frontend/src/features/storage/components/StorageManager.tsx +23 -34
  383. package/frontend/src/features/storage/components/index.ts +12 -0
  384. package/frontend/src/features/storage/hooks/useStorage.ts +208 -0
  385. package/frontend/src/features/storage/page/StoragePage.tsx +41 -115
  386. package/frontend/src/features/storage/services/storage.service.ts +22 -1
  387. package/frontend/src/features/visualizer/components/AuthNode.tsx +72 -56
  388. package/frontend/src/features/visualizer/components/BucketNode.tsx +4 -4
  389. package/frontend/src/features/visualizer/components/SchemaVisualizer.tsx +108 -80
  390. package/frontend/src/features/visualizer/components/TableNode.tsx +34 -41
  391. package/frontend/src/features/visualizer/components/VisualizerSkeleton.tsx +12 -4
  392. package/frontend/src/features/visualizer/page/VisualizerPage.tsx +33 -29
  393. package/frontend/src/index.css +1 -0
  394. package/frontend/src/lib/analytics/posthog.tsx +27 -0
  395. package/frontend/src/lib/contexts/AuthContext.tsx +38 -31
  396. package/frontend/src/lib/contexts/SocketContext.tsx +5 -6
  397. package/frontend/src/{features/metadata → lib}/hooks/useMetadata.ts +1 -1
  398. package/frontend/src/lib/hooks/useToast.tsx +6 -2
  399. package/frontend/src/lib/routing/AppRoutes.tsx +84 -0
  400. package/frontend/src/lib/routing/RequireAuth.tsx +27 -0
  401. package/frontend/src/lib/utils/cloudMessaging.ts +20 -0
  402. package/frontend/src/lib/utils/menuItems.ts +183 -0
  403. package/frontend/src/lib/utils/{validation-schemas.ts → schemaValidations.ts} +10 -5
  404. package/frontend/src/lib/utils/utils.ts +19 -1
  405. package/frontend/src/vite-env.d.ts +1 -0
  406. package/frontend/vite.config.ts +5 -3
  407. package/functions/server.ts +28 -3
  408. package/functions/worker-template.js +15 -4
  409. package/i18n/README.ar.md +130 -0
  410. package/i18n/README.de.md +130 -0
  411. package/i18n/README.es.md +154 -0
  412. package/i18n/README.fr.md +134 -0
  413. package/i18n/README.hi.md +129 -0
  414. package/i18n/README.ja.md +174 -0
  415. package/i18n/README.ko.md +137 -0
  416. package/i18n/README.pt-BR.md +131 -0
  417. package/i18n/README.ru.md +129 -0
  418. package/i18n/README.zh-CN.md +133 -0
  419. package/openapi/ai.yaml +31 -4
  420. package/openapi/auth.yaml +827 -146
  421. package/package.json +16 -7
  422. package/shared-schemas/package.json +1 -1
  423. package/shared-schemas/src/ai-api.schema.ts +34 -58
  424. package/shared-schemas/src/ai.schema.ts +5 -0
  425. package/shared-schemas/src/auth-api.schema.ts +154 -8
  426. package/shared-schemas/src/auth.schema.ts +42 -6
  427. package/shared-schemas/src/cloud-events.schema.ts +57 -0
  428. package/shared-schemas/src/database-api.schema.ts +3 -3
  429. package/shared-schemas/src/database.schema.ts +1 -1
  430. package/shared-schemas/src/index.ts +1 -0
  431. package/shared-schemas/src/logs-api.schema.ts +7 -1
  432. package/shared-schemas/src/logs.schema.ts +26 -0
  433. package/shared-schemas/src/metadata.schema.ts +9 -4
  434. package/test-gemini.sh +35 -0
  435. package/test-usage-admin.sh +57 -0
  436. package/test-usage.sh +50 -0
  437. package/zeabur/README.md +13 -0
  438. package/zeabur/template.yml +1032 -0
  439. package/.github/workflows/deploy-aws.yml +0 -130
  440. package/backend/src/api/routes/agent.ts +0 -29
  441. package/backend/src/api/routes/auth.oauth.ts +0 -482
  442. package/backend/src/api/routes/auth.ts +0 -386
  443. package/backend/src/api/routes/docs.ts +0 -66
  444. package/backend/src/api/routes/functions.ts +0 -183
  445. package/backend/src/api/routes/openapi.ts +0 -82
  446. package/backend/src/api/routes/usage.ts +0 -96
  447. package/backend/src/core/ai/client.ts +0 -242
  448. package/backend/src/core/ai/model.ts +0 -117
  449. package/backend/src/core/auth/auth.ts +0 -781
  450. package/backend/src/core/database/table.ts +0 -772
  451. package/backend/src/core/documentation/agent.ts +0 -689
  452. package/backend/src/core/documentation/openapi.ts +0 -856
  453. package/backend/src/core/logs/analytics.ts +0 -76
  454. package/backend/src/core/logs/providers/localdb.provider.ts +0 -246
  455. package/backend/src/core/storage/storage.ts +0 -923
  456. package/backend/src/utils/cloud-token.ts +0 -39
  457. package/backend/src/utils/helpers.ts +0 -49
  458. package/backend/src/utils/uuid.ts +0 -9
  459. package/backend/tests/manual/test-better-auth.sh +0 -303
  460. package/docker-init/db/logs.sql +0 -9
  461. package/frontend/README.md +0 -112
  462. package/frontend/src/components/datagrid/index.tsx +0 -20
  463. package/frontend/src/components/layout/CloudLayout.tsx +0 -95
  464. package/frontend/src/features/ai/components/AIConfigDialog.tsx +0 -76
  465. package/frontend/src/features/ai/components/AIConfigForm.tsx +0 -222
  466. package/frontend/src/features/ai/components/fields/ModalityField.tsx +0 -87
  467. package/frontend/src/features/ai/components/fields/ModelSelectionField.tsx +0 -134
  468. package/frontend/src/features/ai/components/fields/SystemPromptField.tsx +0 -33
  469. package/frontend/src/features/auth/components/AddOAuthDialog.tsx +0 -106
  470. package/frontend/src/features/auth/components/AuthMethodTab.tsx +0 -238
  471. package/frontend/src/features/auth/components/UsersTab.tsx +0 -114
  472. package/frontend/src/features/auth/page/AuthenticationPage.tsx +0 -169
  473. package/frontend/src/features/database/hooks/UseLinkModal.tsx +0 -78
  474. package/frontend/src/features/functions/components/FunctionViewer.tsx +0 -46
  475. package/frontend/src/features/functions/components/FunctionsContent.tsx +0 -88
  476. package/frontend/src/features/login/components/AuthErrorBoundary.tsx +0 -87
  477. package/frontend/src/features/login/components/PrivateRoute.tsx +0 -24
  478. package/frontend/src/features/logs/components/AnalyticsLogsTable.tsx +0 -313
  479. package/frontend/src/features/logs/components/LogsTable.tsx +0 -199
  480. package/frontend/src/features/logs/page/AnalyticsLogsPage.tsx +0 -530
  481. package/frontend/src/features/metadata/index.ts +0 -0
  482. package/frontend/src/features/metadata/page/MetadataPage.tsx +0 -136
  483. package/frontend/src/features/onboard/components/CompletionCard.tsx +0 -41
  484. package/frontend/src/features/onboard/components/OnboardButton.tsx +0 -84
  485. package/frontend/src/features/onboard/components/StepContent.tsx +0 -91
  486. package/frontend/src/features/onboard/components/TestConnectionStep.tsx +0 -53
  487. package/frontend/src/features/onboard/components/mcp/McpInstallation.tsx +0 -144
  488. package/frontend/src/features/onboard/page/OnBoardPage.tsx +0 -104
  489. package/frontend/src/features/onboard/types.ts +0 -8
  490. package/frontend/src/lib/contexts/OnboardStepContext.tsx +0 -68
  491. package/frontend/src/lib/hooks/useOnboardingCompletion.ts +0 -29
  492. /package/backend/src/api/{middleware → middlewares}/error.ts +0 -0
  493. /package/backend/src/api/{middleware → middlewares}/upload.ts +0 -0
  494. /package/backend/{migrations → src/infra/database/migrations}/000_create-base-tables.sql +0 -0
  495. /package/backend/{migrations → src/infra/database/migrations}/001_create-helper-functions.sql +0 -0
  496. /package/backend/{migrations → src/infra/database/migrations}/002_rename-auth-tables.sql +0 -0
  497. /package/backend/{migrations → src/infra/database/migrations}/003_create-users-table.sql +0 -0
  498. /package/backend/{migrations → src/infra/database/migrations}/004_add-reload-postgrest-func.sql +0 -0
  499. /package/backend/{migrations → src/infra/database/migrations}/005_enable-project-admin-modify-users.sql +0 -0
  500. /package/backend/{migrations → src/infra/database/migrations}/006_modify-ai-usage-table.sql +0 -0
  501. /package/backend/{migrations → src/infra/database/migrations}/007_drop-metadata-table.sql +0 -0
  502. /package/backend/{migrations → src/infra/database/migrations}/008_add-system-tables.sql +0 -0
  503. /package/backend/{migrations → src/infra/database/migrations}/009_add-function-secrets.sql +0 -0
  504. /package/backend/{migrations → src/infra/database/migrations}/010_modify-ai-config-modalities.sql +0 -0
  505. /package/backend/{migrations → src/infra/database/migrations}/011_refactor-secrets-table.sql +0 -0
  506. /package/backend/{migrations → src/infra/database/migrations}/012_add-storage-uploaded-by.sql +0 -0
  507. /package/frontend/src/{features/metadata → lib}/services/metadata.service.ts +0 -0
@@ -6,8 +6,8 @@ import {
6
6
  type RenderCellProps,
7
7
  type DataGridColumn,
8
8
  type DataGridRowType,
9
- } from '@/components/datagrid';
10
- import { Button } from '@/components/radix/Button';
9
+ Button,
10
+ } from '@/components';
11
11
  import { Download, Eye, Trash2, Image, FileText, Music, Video, Archive, File } from 'lucide-react';
12
12
  import { StorageFileSchema } from '@insforge/shared-schemas';
13
13
 
@@ -135,6 +135,7 @@ export function createStorageColumns(
135
135
  columns.push({
136
136
  key: 'actions',
137
137
  name: '',
138
+ minWidth: 120,
138
139
  maxWidth: 120,
139
140
  resizable: false,
140
141
  sortable: false,
@@ -178,7 +179,7 @@ export function createStorageColumns(
178
179
  <Button
179
180
  variant="ghost"
180
181
  size="icon"
181
- className="h-8 w-8 hover:bg-red-50"
182
+ className="h-8 w-8"
182
183
  onClick={(e) => {
183
184
  e.stopPropagation();
184
185
  onDelete(row as StorageFileSchema);
@@ -1,12 +1,10 @@
1
1
  import { useState, useCallback, useMemo, useEffect } from 'react';
2
- import { useQuery, useQueryClient, useMutation } from '@tanstack/react-query';
3
2
  import { Folder } from 'lucide-react';
4
- import { storageService } from '@/features/storage/services/storage.service';
3
+ import { useStorage } from '@/features/storage/hooks/useStorage';
5
4
  import { StorageFileSchema } from '@insforge/shared-schemas';
6
- import { LoadingState, ErrorState, EmptyState } from '@/components';
5
+ import { LoadingState, ErrorState, EmptyState, ConfirmDialog, ConnectCTA } from '@/components';
7
6
  import { StorageDataGrid } from './StorageDataGrid';
8
7
  import { FilePreviewDialog } from './FilePreviewDialog';
9
- import { ConfirmDialog } from '@/components/ConfirmDialog';
10
8
  import { useConfirm } from '@/lib/hooks/useConfirm';
11
9
  import { useToast } from '@/lib/hooks/useToast';
12
10
  import { SortColumn } from 'react-data-grid';
@@ -28,7 +26,6 @@ export function StorageManager({
28
26
  onSelectedFilesChange,
29
27
  isRefreshing = false,
30
28
  }: StorageManagerProps) {
31
- const queryClient = useQueryClient();
32
29
  const [downloadingFiles, setDownloadingFiles] = useState<Set<string>>(new Set());
33
30
  const [previewFile, setPreviewFile] = useState<StorageFileSchema | null>(null);
34
31
  const [showPreviewDialog, setShowPreviewDialog] = useState(false);
@@ -45,25 +42,21 @@ export function StorageManager({
45
42
  setCurrentPage(1);
46
43
  }, [searchQuery, bucketName]);
47
44
 
45
+ const { useListObjects, deleteObjects, downloadObject } = useStorage();
46
+
48
47
  // Fetch objects in selected bucket
49
48
  const {
50
49
  data: objectsData,
51
50
  isLoading: objectsLoading,
52
51
  error: objectsError,
53
- } = useQuery({
54
- queryKey: ['storage', 'objects', bucketName, currentPage, pageSize, searchQuery],
55
- queryFn: () =>
56
- storageService.listObjects(
57
- bucketName,
58
- {
59
- limit: pageSize,
60
- offset: (currentPage - 1) * pageSize,
61
- },
62
- searchQuery
63
- ),
64
- enabled: !!bucketName,
65
- placeholderData: (previousData) => previousData, // Keep previous data while loading
66
- });
52
+ } = useListObjects(
53
+ bucketName,
54
+ {
55
+ limit: pageSize,
56
+ offset: (currentPage - 1) * pageSize,
57
+ },
58
+ searchQuery
59
+ );
67
60
 
68
61
  // Calculate pagination from backend response
69
62
  const totalPages = useMemo(() => {
@@ -71,22 +64,13 @@ export function StorageManager({
71
64
  return Math.ceil(total / pageSize);
72
65
  }, [objectsData?.pagination.total, fileCount, pageSize]);
73
66
 
74
- // Delete mutation
75
- const deleteMutation = useMutation({
76
- mutationFn: ({ bucket, key }: { bucket: string; key: string }) =>
77
- storageService.deleteObject(bucket, key),
78
- onSuccess: () => {
79
- void queryClient.invalidateQueries({ queryKey: ['storage'] });
80
- },
81
- });
82
-
83
67
  // No need for client-side filtering - backend handles search
84
68
  // Just apply sorting
85
69
  const processedFiles = useMemo(() => {
86
70
  let files = objectsData?.objects || [];
87
71
 
88
72
  // Apply sorting
89
- if (sortColumns.length > 0) {
73
+ if (sortColumns.length) {
90
74
  const sortColumn = sortColumns[0];
91
75
  files = [...files].sort((a, b) => {
92
76
  const aValue = a[sortColumn.columnKey as keyof StorageFileSchema];
@@ -115,7 +99,7 @@ export function StorageManager({
115
99
  async (file: StorageFileSchema) => {
116
100
  setDownloadingFiles((prev) => new Set(prev).add(file.key));
117
101
  try {
118
- const blob = await storageService.downloadObject(bucketName, file.key);
102
+ const blob = await downloadObject(bucketName, file.key);
119
103
 
120
104
  // Create download link
121
105
  const url = window.URL.createObjectURL(blob);
@@ -137,7 +121,7 @@ export function StorageManager({
137
121
  });
138
122
  }
139
123
  },
140
- [bucketName, showToast]
124
+ [bucketName, downloadObject, showToast]
141
125
  );
142
126
 
143
127
  const handlePreview = useCallback((file: StorageFileSchema) => {
@@ -157,10 +141,10 @@ export function StorageManager({
157
141
  const shouldDelete = await confirm(confirmOptions);
158
142
 
159
143
  if (shouldDelete) {
160
- deleteMutation.mutate({ bucket: bucketName, key: file.key });
144
+ deleteObjects({ bucket: bucketName, keys: [file.key] });
161
145
  }
162
146
  },
163
- [bucketName, confirm, deleteMutation]
147
+ [bucketName, confirm, deleteObjects]
164
148
  );
165
149
 
166
150
  const isDownloading = useCallback(
@@ -217,7 +201,12 @@ export function StorageManager({
217
201
  onDownload={(file) => void handleDownload(file)}
218
202
  onDelete={(file) => void handleDelete(file)}
219
203
  isDownloading={isDownloading}
220
- emptyStateTitle={searchQuery ? 'No files match your search criteria' : 'No files found'}
204
+ emptyState={
205
+ <div className="text-sm text-zinc-500 dark:text-zinc-400">
206
+ {searchQuery ? 'No files match your search criteria' : 'No files found'}.{' '}
207
+ <ConnectCTA />
208
+ </div>
209
+ }
221
210
  />
222
211
  </div>
223
212
 
@@ -0,0 +1,12 @@
1
+ export { BucketEmptyState } from './BucketEmptyState';
2
+ export { BucketFormDialog } from './BucketFormDialog';
3
+ export { BucketListSkeleton } from './BucketListSkeleton';
4
+ export { FilePreviewDialog } from './FilePreviewDialog';
5
+ export {
6
+ createStorageColumns,
7
+ StorageDataGrid,
8
+ type StorageDataGridProps,
9
+ } from './StorageDataGrid';
10
+ export { StorageManager } from './StorageManager';
11
+ export { StorageSidebar } from './StorageSidebar';
12
+ export { useUploadToast } from './UploadToast';
@@ -0,0 +1,208 @@
1
+ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
2
+ import { storageService, type ListObjectsParams } from '../services/storage.service';
3
+ import { useToast } from '@/lib/hooks/useToast';
4
+
5
+ export function useStorage() {
6
+ const queryClient = useQueryClient();
7
+ const { showToast } = useToast();
8
+
9
+ // Query to fetch all buckets
10
+ const {
11
+ data: buckets,
12
+ isLoading: isLoadingBuckets,
13
+ error: bucketsError,
14
+ refetch: refetchBuckets,
15
+ } = useQuery({
16
+ queryKey: ['storage', 'buckets'],
17
+ queryFn: () => storageService.listBuckets(),
18
+ });
19
+
20
+ // Hook to fetch objects in a bucket
21
+ const useListObjects = (
22
+ bucketName: string,
23
+ params?: ListObjectsParams,
24
+ searchQuery?: string,
25
+ enabled = true
26
+ ) => {
27
+ return useQuery({
28
+ queryKey: ['storage', 'objects', bucketName, params?.limit, params?.offset, searchQuery],
29
+ queryFn: () => storageService.listObjects(bucketName, params, searchQuery),
30
+ enabled: enabled && !!bucketName,
31
+ placeholderData: (previousData) => previousData,
32
+ });
33
+ };
34
+
35
+ // Query to fetch bucket statistics
36
+ const useBucketStats = (enabled = true) => {
37
+ return useQuery({
38
+ queryKey: ['storage', 'bucket-stats', buckets],
39
+ queryFn: async () => {
40
+ const stats: Record<
41
+ string,
42
+ { fileCount: number; totalSize: number; public: boolean; createdAt?: string }
43
+ > = {};
44
+ const currentBuckets = buckets || [];
45
+ const promises = currentBuckets.map(async (bucket) => {
46
+ try {
47
+ const result = await storageService.listObjects(bucket.name, { limit: 1000 });
48
+ const objects = result.objects;
49
+ const totalSize = objects.reduce((sum, file) => sum + file.size, 0);
50
+ return {
51
+ bucketName: bucket.name,
52
+ stats: {
53
+ fileCount: result.pagination.total,
54
+ totalSize: totalSize,
55
+ public: bucket.public,
56
+ createdAt: bucket.createdAt,
57
+ },
58
+ };
59
+ } catch (error) {
60
+ if (error) {
61
+ console.error(error);
62
+ return null;
63
+ }
64
+ return {
65
+ bucketName: bucket.name,
66
+ stats: {
67
+ fileCount: 0,
68
+ totalSize: 0,
69
+ public: bucket.public,
70
+ createdAt: bucket.createdAt,
71
+ },
72
+ };
73
+ }
74
+ });
75
+ const results = await Promise.all(promises);
76
+ results.forEach((result) => {
77
+ if (result) {
78
+ stats[result.bucketName] = result.stats;
79
+ }
80
+ });
81
+ return stats;
82
+ },
83
+ enabled: enabled && (buckets?.length || 0) > 0,
84
+ staleTime: 30000,
85
+ });
86
+ };
87
+
88
+ // Mutation to upload an object
89
+ const uploadObjectMutation = useMutation({
90
+ mutationFn: async ({
91
+ bucket,
92
+ objectKey,
93
+ file,
94
+ }: {
95
+ bucket: string;
96
+ objectKey: string;
97
+ file: File;
98
+ }) => storageService.uploadObject(bucket, objectKey, file),
99
+ onSuccess: () => {
100
+ void queryClient.invalidateQueries({ queryKey: ['storage'] });
101
+ },
102
+ });
103
+
104
+ // Mutation to delete an object
105
+ const deleteObjectsMutation = useMutation({
106
+ mutationFn: ({ bucket, keys }: { bucket: string; keys: string[] }) =>
107
+ storageService.deleteObjects(bucket, keys),
108
+ onSuccess: (result) => {
109
+ const { success, failures } = result;
110
+ const successCount = success.length;
111
+ const failureCount = failures.length;
112
+ if (failureCount > 0 && successCount > 0) {
113
+ showToast(
114
+ `${successCount} ${successCount > 1 ? 'files' : 'file'} deleted, ${failureCount} ${failureCount > 1 ? 'files' : 'file'} failed to delete.`,
115
+ 'warn'
116
+ );
117
+ } else if (failureCount > 0) {
118
+ showToast(
119
+ `Failed to delete ${failureCount} ${failureCount > 1 ? 'files' : 'file'}`,
120
+ 'error'
121
+ );
122
+ } else if (successCount > 0) {
123
+ showToast(
124
+ `${successCount} ${successCount > 1 ? 'files' : 'file'} deleted successfully.`,
125
+ 'success'
126
+ );
127
+ }
128
+
129
+ void queryClient.invalidateQueries({ queryKey: ['storage'] });
130
+ },
131
+ onError: (error: Error) => {
132
+ const errorMessage = error instanceof Error ? error.message : 'Failed to delete file';
133
+ showToast(errorMessage, 'error');
134
+ },
135
+ });
136
+
137
+ // Mutation to create a bucket
138
+ const createBucketMutation = useMutation({
139
+ mutationFn: ({ bucketName, isPublic }: { bucketName: string; isPublic: boolean }) =>
140
+ storageService.createBucket(bucketName, isPublic),
141
+ onSuccess: () => {
142
+ void queryClient.invalidateQueries({ queryKey: ['storage', 'buckets'] });
143
+ showToast('Bucket created successfully', 'success');
144
+ },
145
+ onError: (error: Error) => {
146
+ const errorMessage = error instanceof Error ? error.message : 'Failed to create bucket';
147
+ showToast(errorMessage, 'error');
148
+ },
149
+ });
150
+
151
+ // Mutation to delete a bucket
152
+ const deleteBucketMutation = useMutation({
153
+ mutationFn: (bucketName: string) => storageService.deleteBucket(bucketName),
154
+ onSuccess: () => {
155
+ void queryClient.invalidateQueries({ queryKey: ['storage', 'buckets'] });
156
+ showToast('Bucket deleted successfully', 'success');
157
+ },
158
+ onError: (error: Error) => {
159
+ const errorMessage = error instanceof Error ? error.message : 'Failed to delete bucket';
160
+ showToast(errorMessage, 'error');
161
+ },
162
+ });
163
+
164
+ // Mutation to edit a bucket
165
+ const editBucketMutation = useMutation({
166
+ mutationFn: ({ bucketName, config }: { bucketName: string; config: { isPublic: boolean } }) =>
167
+ storageService.editBucket(bucketName, config),
168
+ onSuccess: () => {
169
+ void queryClient.invalidateQueries({ queryKey: ['storage'] });
170
+ showToast('Bucket updated successfully', 'success');
171
+ },
172
+ onError: (error: Error) => {
173
+ const errorMessage = error instanceof Error ? error.message : 'Failed to update bucket';
174
+ showToast(errorMessage, 'error');
175
+ },
176
+ });
177
+
178
+ return {
179
+ // Data
180
+ buckets: buckets || [],
181
+ bucketsCount: buckets?.length || 0,
182
+
183
+ // Loading states
184
+ isLoadingBuckets,
185
+ isUploadingObject: uploadObjectMutation.isPending,
186
+ isDeletingObject: deleteObjectsMutation.isPending,
187
+ isCreatingBucket: createBucketMutation.isPending,
188
+ isDeletingBucket: deleteBucketMutation.isPending,
189
+ isEditingBucket: editBucketMutation.isPending,
190
+
191
+ // Errors
192
+ bucketsError,
193
+
194
+ // Actions
195
+ uploadObject: uploadObjectMutation.mutateAsync,
196
+ deleteObjects: deleteObjectsMutation.mutate,
197
+ createBucket: createBucketMutation.mutateAsync,
198
+ deleteBucket: deleteBucketMutation.mutateAsync,
199
+ editBucket: editBucketMutation.mutateAsync,
200
+ refetchBuckets,
201
+
202
+ // Helpers
203
+ useListObjects,
204
+ useBucketStats,
205
+ getDownloadUrl: storageService.getDownloadUrl,
206
+ downloadObject: storageService.downloadObject,
207
+ };
208
+ }
@@ -1,26 +1,30 @@
1
1
  import React, { useState, useEffect, useRef, useCallback, type DragEvent } from 'react';
2
- import { useQuery, useQueryClient, useMutation } from '@tanstack/react-query';
2
+ import { useQueryClient } from '@tanstack/react-query';
3
3
  import { Upload } from 'lucide-react';
4
4
  import PencilIcon from '@/assets/icons/pencil.svg?react';
5
5
  import RefreshIcon from '@/assets/icons/refresh.svg?react';
6
- import { storageService } from '@/features/storage/services/storage.service';
7
- import { Button } from '@/components/radix/Button';
8
- import { Alert, AlertDescription } from '@/components/radix/Alert';
6
+ import { useStorage } from '@/features/storage/hooks/useStorage';
9
7
  import { StorageSidebar } from '@/features/storage/components/StorageSidebar';
10
8
  import { StorageManager } from '@/features/storage/components/StorageManager';
11
9
  import { BucketFormDialog } from '@/features/storage/components/BucketFormDialog';
12
- import { ConfirmDialog } from '@/components/ConfirmDialog';
13
- import { EmptyState } from '@/components/EmptyState';
10
+
11
+ import { useConfirm } from '@/lib/hooks/useConfirm';
12
+ import { useToast } from '@/lib/hooks/useToast';
13
+ import { useUploadToast } from '@/features/storage/components/UploadToast';
14
14
  import {
15
+ SearchInput,
16
+ SelectionClearButton,
17
+ DeleteActionButton,
18
+ Alert,
19
+ AlertDescription,
20
+ Button,
21
+ ConfirmDialog,
22
+ EmptyState,
15
23
  Tooltip,
16
24
  TooltipContent,
17
25
  TooltipProvider,
18
26
  TooltipTrigger,
19
- } from '@/components/radix/Tooltip';
20
- import { useConfirm } from '@/lib/hooks/useConfirm';
21
- import { useToast } from '@/lib/hooks/useToast';
22
- import { useUploadToast } from '@/features/storage/components/UploadToast';
23
- import { SearchInput, SelectionClearButton, DeleteActionButton } from '@/components';
27
+ } from '@/components';
24
28
  import {
25
29
  DataUpdatePayload,
26
30
  DataUpdateResourceType,
@@ -58,103 +62,31 @@ export default function StoragePage() {
58
62
 
59
63
  const { socket, isConnected } = useSocket();
60
64
 
61
- // Fetch buckets
62
65
  const {
63
- data: buckets = [],
64
- isLoading,
65
- error: bucketsError,
66
- refetch: refetchBuckets,
67
- } = useQuery({
68
- queryKey: ['storage', 'buckets'],
69
- queryFn: () => storageService.listBuckets(),
70
- });
71
-
72
- // Fetch bucket statistics
73
- const { data: bucketStats } = useQuery({
74
- queryKey: ['storage', 'bucket-stats', buckets],
75
- queryFn: async () => {
76
- const stats: Record<
77
- string,
78
- { fileCount: number; totalSize: number; public: boolean; createdAt?: string }
79
- > = {};
80
- const currentBuckets = buckets;
81
- const promises = currentBuckets.map(async (bucket) => {
82
- try {
83
- const result = await storageService.listObjects(bucket.name, { limit: 1000 });
84
- const objects = result.objects;
85
- const totalSize = objects.reduce((sum, file) => sum + file.size, 0);
86
- return {
87
- bucketName: bucket.name,
88
- stats: {
89
- fileCount: result.pagination.total,
90
- totalSize: totalSize,
91
- public: bucket.public,
92
- createdAt: bucket.createdAt,
93
- },
94
- };
95
- } catch (error) {
96
- if (error) {
97
- console.error(error);
98
- return null;
99
- }
100
- return {
101
- bucketName: bucket.name,
102
- stats: {
103
- fileCount: 0,
104
- totalSize: 0,
105
- public: bucket.public,
106
- createdAt: bucket.createdAt,
107
- },
108
- };
109
- }
110
- });
111
- const results = await Promise.all(promises);
112
- results.forEach((result) => {
113
- if (result) {
114
- stats[result.bucketName] = result.stats;
115
- }
116
- });
117
- return stats;
118
- },
119
- enabled: buckets.length > 0,
120
- staleTime: 30000, // Cache for 30 seconds
121
- });
66
+ buckets,
67
+ isLoadingBuckets: isLoading,
68
+ bucketsError,
69
+ refetchBuckets,
70
+ useBucketStats,
71
+ uploadObject,
72
+ deleteObjects,
73
+ deleteBucket,
74
+ } = useStorage();
75
+
76
+ const { data: bucketStats } = useBucketStats();
122
77
 
123
78
  // Build bucket info map
124
79
  const bucketInfo = React.useMemo(() => {
125
80
  return bucketStats || {};
126
81
  }, [bucketStats]);
127
82
 
128
- // Upload mutation
129
- const uploadMutation = useMutation({
130
- mutationFn: async ({
131
- bucket,
132
- file,
133
- fileName,
134
- }: {
135
- bucket: string;
136
- file: File;
137
- fileName?: string;
138
- }) => {
139
- const key = fileName || file.name;
140
- return await storageService.uploadObject(bucket, key, file);
141
- },
142
- onSuccess: () => {
143
- void queryClient.invalidateQueries({ queryKey: ['storage'] });
144
- },
145
- // Remove global onError handler - errors are now handled individually in uploadFiles
146
- });
147
-
148
83
  useEffect(() => {
149
84
  if (!socket || !isConnected) {
150
85
  return;
151
86
  }
152
87
 
153
88
  const handleDataUpdate = (message: SocketMessage<DataUpdatePayload>) => {
154
- if (
155
- message.payload?.resource === DataUpdateResourceType.METADATA ||
156
- message.payload?.resource === DataUpdateResourceType.STORAGE_SCHEMA
157
- ) {
89
+ if (message.payload?.resource === DataUpdateResourceType.BUCKETS) {
158
90
  // Invalidate all buckets queries
159
91
  void queryClient.invalidateQueries({ queryKey: ['storage'] });
160
92
  }
@@ -169,11 +101,16 @@ export default function StoragePage() {
169
101
 
170
102
  // Auto-select first bucket
171
103
  useEffect(() => {
172
- if (buckets.length > 0 && !selectedBucket) {
104
+ if (buckets.length && !selectedBucket) {
173
105
  setSelectedBucket(buckets[0].name);
174
106
  }
175
107
  }, [buckets, selectedBucket]);
176
108
 
109
+ // Clear selected files when switching buckets
110
+ useEffect(() => {
111
+ setSelectedFiles(new Set());
112
+ }, [selectedBucket]);
113
+
177
114
  const handleRefresh = async () => {
178
115
  setIsRefreshing(true);
179
116
  try {
@@ -188,7 +125,7 @@ export default function StoragePage() {
188
125
 
189
126
  // Handle bulk delete files
190
127
  const handleBulkDeleteFiles = async (fileKeys: string[]) => {
191
- if (!selectedBucket || fileKeys.length === 0) {
128
+ if (!selectedBucket || !fileKeys.length) {
192
129
  return;
193
130
  }
194
131
 
@@ -200,20 +137,13 @@ export default function StoragePage() {
200
137
  });
201
138
 
202
139
  if (shouldDelete) {
203
- try {
204
- await Promise.all(fileKeys.map((key) => storageService.deleteObject(selectedBucket, key)));
205
- void queryClient.invalidateQueries({ queryKey: ['storage'] });
206
- setSelectedFiles(new Set());
207
- showToast(`${fileKeys.length} files deleted successfully`, 'success');
208
- } catch {
209
- showToast('Failed to delete some files', 'error');
210
- }
140
+ deleteObjects({ bucket: selectedBucket, keys: fileKeys });
141
+ setSelectedFiles(new Set());
211
142
  }
212
- void queryClient.invalidateQueries({ queryKey: ['storage'] });
213
143
  };
214
144
 
215
145
  const uploadFiles = async (files: FileList | File[] | null) => {
216
- if (!files || files.length === 0 || !selectedBucket) {
146
+ if (!files || !files.length || !selectedBucket) {
217
147
  return;
218
148
  }
219
149
 
@@ -246,10 +176,10 @@ export default function StoragePage() {
246
176
  updateUploadProgress(toastId, progress);
247
177
 
248
178
  try {
249
- await uploadMutation.mutateAsync({
179
+ await uploadObject({
250
180
  bucket: selectedBucket,
181
+ objectKey: files[i].name,
251
182
  file: files[i],
252
- fileName: files[i].name, // Backend will auto-rename if needed
253
183
  });
254
184
  successCount++;
255
185
  } catch (error) {
@@ -282,7 +212,7 @@ export default function StoragePage() {
282
212
  showToast,
283
213
  showUploadToast,
284
214
  updateUploadProgress,
285
- uploadMutation,
215
+ uploadObject,
286
216
  ]);
287
217
 
288
218
  const handleFileSelect = async (event: React.ChangeEvent<HTMLInputElement>) => {
@@ -301,12 +231,8 @@ export default function StoragePage() {
301
231
 
302
232
  if (shouldDelete) {
303
233
  try {
304
- await storageService.deleteBucket(bucketName);
305
-
306
- // Refresh buckets list
234
+ await deleteBucket(bucketName);
307
235
  await refetchBuckets();
308
- showToast('Bucket deleted successfully', 'success');
309
-
310
236
  // If the deleted bucket was selected, select the first available bucket
311
237
  if (selectedBucket === bucketName) {
312
238
  const updatedBuckets =
@@ -94,7 +94,7 @@ export const storageService = {
94
94
 
95
95
  // Download an object (returns blob)
96
96
  async downloadObject(bucketName: string, objectKey: string): Promise<Blob> {
97
- const response = await fetch(this.getDownloadUrl(bucketName, objectKey), {
97
+ const response = await fetch(storageService.getDownloadUrl(bucketName, objectKey), {
98
98
  headers: {
99
99
  Authorization: `Bearer ${apiClient.getToken()}`,
100
100
  },
@@ -116,6 +116,27 @@ export const storageService = {
116
116
  );
117
117
  },
118
118
 
119
+ async deleteObjects(
120
+ bucketName: string,
121
+ objectKeys: string[]
122
+ ): Promise<{ success: string[]; failures: { key: string; error: Error }[] }> {
123
+ const results = await Promise.allSettled(
124
+ objectKeys.map((key) => this.deleteObject(bucketName, key))
125
+ );
126
+ const success: string[] = [];
127
+ const failures: { key: string; error: Error }[] = [];
128
+
129
+ results.forEach((result, index) => {
130
+ if (result.status === 'fulfilled') {
131
+ success.push(objectKeys[index]);
132
+ } else {
133
+ failures.push({ key: objectKeys[index], error: result.reason as Error });
134
+ }
135
+ });
136
+
137
+ return { success, failures };
138
+ },
139
+
119
140
  // Create a new bucket
120
141
  async createBucket(bucketName: string, isPublic: boolean = true): Promise<void> {
121
142
  await apiClient.request('/storage/buckets', {