insforge 0.3.1

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 (395) hide show
  1. package/.dockerignore +58 -0
  2. package/.env.example +49 -0
  3. package/.github/ISSUE_TEMPLATE/bug_report.yml +83 -0
  4. package/.github/ISSUE_TEMPLATE/config.yml +11 -0
  5. package/.github/ISSUE_TEMPLATE/feature_request.yml +79 -0
  6. package/.github/copilot-instructions.md +147 -0
  7. package/.github/workflows/build-image.yml +65 -0
  8. package/.github/workflows/ci-premerge-check.yml +24 -0
  9. package/.github/workflows/deploy-aws.yml +130 -0
  10. package/.github/workflows/lint-and-format.yml +33 -0
  11. package/.prettierignore +65 -0
  12. package/.prettierrc +9 -0
  13. package/CHANGELOG.md +3 -0
  14. package/CONTRIBUTING.md +126 -0
  15. package/Dockerfile +27 -0
  16. package/GITHUB_OAUTH_SETUP.md +49 -0
  17. package/GOOGLE_OAUTH_SETUP.md +148 -0
  18. package/LICENSE +201 -0
  19. package/README.md +134 -0
  20. package/assets/Dark.svg +23 -0
  21. package/assets/archDiagram.png +0 -0
  22. package/assets/banner.png +0 -0
  23. package/assets/mcpInstallv2.png +0 -0
  24. package/assets/sampleResponse.png +0 -0
  25. package/assets/signin.png +0 -0
  26. package/assets/userflow.png +0 -0
  27. package/backend/migrations/000_create-base-tables.sql +142 -0
  28. package/backend/migrations/001_create-helper-functions.sql +41 -0
  29. package/backend/migrations/002_rename-auth-tables.sql +30 -0
  30. package/backend/migrations/003_create-users-table.sql +56 -0
  31. package/backend/migrations/004_add-reload-postgrest-func.sql +24 -0
  32. package/backend/migrations/005_enable-project-admin-modify-users.sql +30 -0
  33. package/backend/migrations/006_modify-ai-usage-table.sql +25 -0
  34. package/backend/migrations/007_drop-metadata-table.sql +2 -0
  35. package/backend/migrations/008_add-system-tables.sql +77 -0
  36. package/backend/migrations/009_add-function-secrets.sql +24 -0
  37. package/backend/migrations/010_modify-ai-config-modalities.sql +93 -0
  38. package/backend/migrations/011_refactor-secrets-table.sql +15 -0
  39. package/backend/migrations/012_add-storage-uploaded-by.sql +8 -0
  40. package/backend/package.json +75 -0
  41. package/backend/src/api/middleware/auth.ts +240 -0
  42. package/backend/src/api/middleware/error.ts +231 -0
  43. package/backend/src/api/middleware/upload.ts +59 -0
  44. package/backend/src/api/routes/agent.ts +29 -0
  45. package/backend/src/api/routes/ai.ts +472 -0
  46. package/backend/src/api/routes/auth.oauth.ts +482 -0
  47. package/backend/src/api/routes/auth.ts +386 -0
  48. package/backend/src/api/routes/database.advance.ts +275 -0
  49. package/backend/src/api/routes/database.records.ts +246 -0
  50. package/backend/src/api/routes/database.tables.ts +161 -0
  51. package/backend/src/api/routes/docs.ts +66 -0
  52. package/backend/src/api/routes/functions.ts +183 -0
  53. package/backend/src/api/routes/logs.ts +150 -0
  54. package/backend/src/api/routes/metadata.ts +160 -0
  55. package/backend/src/api/routes/openapi.ts +82 -0
  56. package/backend/src/api/routes/secrets.ts +199 -0
  57. package/backend/src/api/routes/storage.ts +547 -0
  58. package/backend/src/api/routes/usage.ts +96 -0
  59. package/backend/src/core/ai/chat.ts +207 -0
  60. package/backend/src/core/ai/client.ts +242 -0
  61. package/backend/src/core/ai/config.ts +187 -0
  62. package/backend/src/core/ai/image.ts +156 -0
  63. package/backend/src/core/ai/model.ts +117 -0
  64. package/backend/src/core/ai/usage.ts +290 -0
  65. package/backend/src/core/auth/auth.ts +781 -0
  66. package/backend/src/core/auth/oauth.ts +398 -0
  67. package/backend/src/core/database/advance.ts +1074 -0
  68. package/backend/src/core/database/manager.ts +178 -0
  69. package/backend/src/core/database/table.ts +772 -0
  70. package/backend/src/core/documentation/agent.ts +689 -0
  71. package/backend/src/core/documentation/openapi.ts +856 -0
  72. package/backend/src/core/functions/functions.ts +310 -0
  73. package/backend/src/core/logs/analytics.ts +76 -0
  74. package/backend/src/core/logs/audit.ts +255 -0
  75. package/backend/src/core/logs/providers/base.provider.ts +83 -0
  76. package/backend/src/core/logs/providers/cloudwatch.provider.ts +510 -0
  77. package/backend/src/core/logs/providers/localdb.provider.ts +246 -0
  78. package/backend/src/core/secrets/encryption.ts +58 -0
  79. package/backend/src/core/secrets/secrets.ts +410 -0
  80. package/backend/src/core/socket/socket.ts +388 -0
  81. package/backend/src/core/socket/types.ts +79 -0
  82. package/backend/src/core/storage/storage.ts +923 -0
  83. package/backend/src/server.ts +288 -0
  84. package/backend/src/types/ai.ts +46 -0
  85. package/backend/src/types/auth.ts +90 -0
  86. package/backend/src/types/database.ts +136 -0
  87. package/backend/src/types/error-constants.ts +86 -0
  88. package/backend/src/types/logs.ts +47 -0
  89. package/backend/src/types/profile.ts +55 -0
  90. package/backend/src/types/storage.ts +23 -0
  91. package/backend/src/utils/cloud-token.ts +39 -0
  92. package/backend/src/utils/constants.ts +1 -0
  93. package/backend/src/utils/environment.ts +35 -0
  94. package/backend/src/utils/helpers.ts +49 -0
  95. package/backend/src/utils/logger.ts +13 -0
  96. package/backend/src/utils/response.ts +62 -0
  97. package/backend/src/utils/seed.ts +205 -0
  98. package/backend/src/utils/sql-parser.ts +63 -0
  99. package/backend/src/utils/uuid.ts +9 -0
  100. package/backend/src/utils/validations.ts +129 -0
  101. package/backend/tests/README.md +134 -0
  102. package/backend/tests/cleanup-all-test-data.sh +231 -0
  103. package/backend/tests/cloud/test-s3-multitenant.sh +132 -0
  104. package/backend/tests/local/comprehensive-curl-tests.sh +156 -0
  105. package/backend/tests/local/test-auth-router.sh +144 -0
  106. package/backend/tests/local/test-database-router.sh +222 -0
  107. package/backend/tests/local/test-e2e.sh +241 -0
  108. package/backend/tests/local/test-fk-errors.sh +97 -0
  109. package/backend/tests/local/test-id-field.sh +201 -0
  110. package/backend/tests/local/test-public-bucket.sh +265 -0
  111. package/backend/tests/local/test-secrets.sh +248 -0
  112. package/backend/tests/local/test-serverless-functions.sh.disabled +325 -0
  113. package/backend/tests/local/test-traditional-rest.sh +209 -0
  114. package/backend/tests/manual/README.md +51 -0
  115. package/backend/tests/manual/create-large-table-simple.sql +11 -0
  116. package/backend/tests/manual/seed-large-table.sql +101 -0
  117. package/backend/tests/manual/setup-large-table-extras.sql +34 -0
  118. package/backend/tests/manual/test-better-auth.sh +303 -0
  119. package/backend/tests/manual/test-bulk-upsert.sh +410 -0
  120. package/backend/tests/manual/test-database-advance.sh +297 -0
  121. package/backend/tests/manual/test-postgrest-stability.sh +192 -0
  122. package/backend/tests/manual/test-rawsql-export-import.sh +412 -0
  123. package/backend/tests/manual/test-universal-storage.sh +264 -0
  124. package/backend/tests/manual/test-users.sql +18 -0
  125. package/backend/tests/run-all-tests.sh +140 -0
  126. package/backend/tests/setup.ts +22 -0
  127. package/backend/tests/test-config.sh +303 -0
  128. package/backend/tsconfig.json +23 -0
  129. package/backend/tsup.config.ts +18 -0
  130. package/backend/vitest.config.ts +22 -0
  131. package/docker-compose.prod.yml +145 -0
  132. package/docker-compose.yml +167 -0
  133. package/docker-init/db/db-init.sql +125 -0
  134. package/docker-init/db/jwt.sql +5 -0
  135. package/docker-init/db/logs.sql +9 -0
  136. package/docker-init/db/postgresql.conf +17 -0
  137. package/docs/deprecated/insforge-auth-api.md +215 -0
  138. package/docs/deprecated/insforge-auth-sdk.md +100 -0
  139. package/docs/deprecated/insforge-db-api.md +359 -0
  140. package/docs/deprecated/insforge-db-sdk.md +140 -0
  141. package/docs/deprecated/insforge-debug-sdk.md +157 -0
  142. package/docs/deprecated/insforge-debug.md +65 -0
  143. package/docs/deprecated/insforge-instructions.md +124 -0
  144. package/docs/deprecated/insforge-project.md +118 -0
  145. package/docs/deprecated/insforge-storage-api.md +279 -0
  146. package/docs/deprecated/insforge-storage-sdk.md +159 -0
  147. package/docs/insforge-instructions-sdk.md +407 -0
  148. package/eslint.config.js +317 -0
  149. package/examples/oauth/frontend-oauth-example.html +251 -0
  150. package/examples/response-examples.md +444 -0
  151. package/frontend/README.md +112 -0
  152. package/frontend/components.json +17 -0
  153. package/frontend/index.html +13 -0
  154. package/frontend/package.json +63 -0
  155. package/frontend/public/favicon.ico +0 -0
  156. package/frontend/src/App.tsx +106 -0
  157. package/frontend/src/assets/icons/checkbox_checked.svg +6 -0
  158. package/frontend/src/assets/icons/checkbox_undetermined.svg +6 -0
  159. package/frontend/src/assets/icons/checked.svg +3 -0
  160. package/frontend/src/assets/icons/error.svg +3 -0
  161. package/frontend/src/assets/icons/pencil.svg +4 -0
  162. package/frontend/src/assets/icons/refresh.svg +4 -0
  163. package/frontend/src/assets/icons/step_active.svg +3 -0
  164. package/frontend/src/assets/icons/step_inactive.svg +11 -0
  165. package/frontend/src/assets/icons/warning.svg +3 -0
  166. package/frontend/src/assets/logos/amazon.svg +1 -0
  167. package/frontend/src/assets/logos/claude_code.svg +3 -0
  168. package/frontend/src/assets/logos/cline.svg +6 -0
  169. package/frontend/src/assets/logos/cursor.svg +20 -0
  170. package/frontend/src/assets/logos/discord.svg +9 -0
  171. package/frontend/src/assets/logos/gemini.svg +19 -0
  172. package/frontend/src/assets/logos/github.svg +5 -0
  173. package/frontend/src/assets/logos/google.svg +13 -0
  174. package/frontend/src/assets/logos/grok.svg +10 -0
  175. package/frontend/src/assets/logos/insforge_dark.svg +15 -0
  176. package/frontend/src/assets/logos/insforge_light.svg +15 -0
  177. package/frontend/src/assets/logos/openai.svg +10 -0
  178. package/frontend/src/assets/logos/roo_code.svg +9 -0
  179. package/frontend/src/assets/logos/trae.svg +3 -0
  180. package/frontend/src/assets/logos/windsurf.svg +10 -0
  181. package/frontend/src/components/ButtonWithLoading.tsx +27 -0
  182. package/frontend/src/components/Checkbox.tsx +61 -0
  183. package/frontend/src/components/CodeBlock.tsx +32 -0
  184. package/frontend/src/components/ConfirmDialog.tsx +96 -0
  185. package/frontend/src/components/CopyButton.tsx +69 -0
  186. package/frontend/src/components/DeleteActionButton.tsx +42 -0
  187. package/frontend/src/components/EmptyState.tsx +41 -0
  188. package/frontend/src/components/ErrorState.tsx +35 -0
  189. package/frontend/src/components/FeatureSidebar.tsx +126 -0
  190. package/frontend/src/components/FeatureSidebarItem.tsx +101 -0
  191. package/frontend/src/components/JsonHighlight.tsx +61 -0
  192. package/frontend/src/components/LoadingState.tsx +16 -0
  193. package/frontend/src/components/PaginationControls.tsx +54 -0
  194. package/frontend/src/components/PromptDialog.tsx +68 -0
  195. package/frontend/src/components/SearchInput.tsx +90 -0
  196. package/frontend/src/components/SelectionClearButton.tsx +26 -0
  197. package/frontend/src/components/Stepper.tsx +139 -0
  198. package/frontend/src/components/ThemeToggle.tsx +58 -0
  199. package/frontend/src/components/TypeBadge.tsx +20 -0
  200. package/frontend/src/components/datagrid/DataGrid.tsx +264 -0
  201. package/frontend/src/components/datagrid/DefaultCellRenderer.tsx +114 -0
  202. package/frontend/src/components/datagrid/IdCell.tsx +44 -0
  203. package/frontend/src/components/datagrid/SortableHeader.tsx +74 -0
  204. package/frontend/src/components/datagrid/cell-editors/BooleanCellEditor.tsx +54 -0
  205. package/frontend/src/components/datagrid/cell-editors/DateCellEditor.tsx +483 -0
  206. package/frontend/src/components/datagrid/cell-editors/JsonCellEditor.tsx +362 -0
  207. package/frontend/src/components/datagrid/cell-editors/TextCellEditor.tsx +38 -0
  208. package/frontend/src/components/datagrid/cell-editors/index.ts +14 -0
  209. package/frontend/src/components/datagrid/cell-editors/types.ts +43 -0
  210. package/frontend/src/components/datagrid/datagridTypes.tsx +72 -0
  211. package/frontend/src/components/datagrid/index.tsx +20 -0
  212. package/frontend/src/components/index.ts +39 -0
  213. package/frontend/src/components/layout/AppHeader.tsx +146 -0
  214. package/frontend/src/components/layout/AppSidebar.tsx +190 -0
  215. package/frontend/src/components/layout/CloudLayout.tsx +95 -0
  216. package/frontend/src/components/layout/Layout.tsx +43 -0
  217. package/frontend/src/components/radix/Alert.tsx +45 -0
  218. package/frontend/src/components/radix/AlertDialog.tsx +115 -0
  219. package/frontend/src/components/radix/Avatar.tsx +45 -0
  220. package/frontend/src/components/radix/Badge.tsx +33 -0
  221. package/frontend/src/components/radix/Button.tsx +50 -0
  222. package/frontend/src/components/radix/Card.tsx +58 -0
  223. package/frontend/src/components/radix/Dialog.tsx +98 -0
  224. package/frontend/src/components/radix/DropdownMenu.tsx +185 -0
  225. package/frontend/src/components/radix/Form.tsx +167 -0
  226. package/frontend/src/components/radix/Input.tsx +22 -0
  227. package/frontend/src/components/radix/Label.tsx +19 -0
  228. package/frontend/src/components/radix/Popover.tsx +29 -0
  229. package/frontend/src/components/radix/ScrollArea.tsx +44 -0
  230. package/frontend/src/components/radix/Select.tsx +151 -0
  231. package/frontend/src/components/radix/Separator.tsx +26 -0
  232. package/frontend/src/components/radix/Sheet.tsx +119 -0
  233. package/frontend/src/components/radix/Skeleton.tsx +7 -0
  234. package/frontend/src/components/radix/Switch.tsx +29 -0
  235. package/frontend/src/components/radix/Tabs.tsx +50 -0
  236. package/frontend/src/components/radix/Textarea.tsx +21 -0
  237. package/frontend/src/components/radix/Tooltip.tsx +28 -0
  238. package/frontend/src/features/ai/components/AIConfigCard.tsx +154 -0
  239. package/frontend/src/features/ai/components/AIConfigDialog.tsx +76 -0
  240. package/frontend/src/features/ai/components/AIConfigForm.tsx +222 -0
  241. package/frontend/src/features/ai/components/AIEmptyState.tsx +18 -0
  242. package/frontend/src/features/ai/components/fields/ModalityField.tsx +87 -0
  243. package/frontend/src/features/ai/components/fields/ModelSelectionField.tsx +134 -0
  244. package/frontend/src/features/ai/components/fields/SystemPromptField.tsx +33 -0
  245. package/frontend/src/features/ai/helpers.ts +155 -0
  246. package/frontend/src/features/ai/hooks/useAIConfigs.ts +221 -0
  247. package/frontend/src/features/ai/hooks/useAIUsage.ts +77 -0
  248. package/frontend/src/features/ai/page/AIPage.tsx +178 -0
  249. package/frontend/src/features/ai/services/ai.service.ts +148 -0
  250. package/frontend/src/features/auth/components/AddOAuthDialog.tsx +106 -0
  251. package/frontend/src/features/auth/components/AuthMethodTab.tsx +238 -0
  252. package/frontend/src/features/auth/components/OAuthConfigDialog.tsx +303 -0
  253. package/frontend/src/features/auth/components/OAuthEmptyState.tsx +15 -0
  254. package/frontend/src/features/auth/components/UserFormDialog.tsx +248 -0
  255. package/frontend/src/features/auth/components/UsersDataGrid.tsx +183 -0
  256. package/frontend/src/features/auth/components/UsersTab.tsx +114 -0
  257. package/frontend/src/features/auth/hooks/useOAuthConfig.ts +129 -0
  258. package/frontend/src/features/auth/hooks/useUsers.ts +57 -0
  259. package/frontend/src/features/auth/index.ts +9 -0
  260. package/frontend/src/features/auth/page/AuthenticationPage.tsx +169 -0
  261. package/frontend/src/features/auth/services/auth.service.ts +112 -0
  262. package/frontend/src/features/auth/services/oauth.service.ts +49 -0
  263. package/frontend/src/features/dashboard/page/DashboardPage.tsx +194 -0
  264. package/frontend/src/features/database/components/ColumnTypeSelect.tsx +64 -0
  265. package/frontend/src/features/database/components/DatabaseDataGrid.tsx +282 -0
  266. package/frontend/src/features/database/components/ForeignKeyCell.tsx +187 -0
  267. package/frontend/src/features/database/components/ForeignKeyPopover.tsx +378 -0
  268. package/frontend/src/features/database/components/LinkRecordModal.tsx +288 -0
  269. package/frontend/src/features/database/components/RecordFormDialog.tsx +164 -0
  270. package/frontend/src/features/database/components/RecordFormField.tsx +568 -0
  271. package/frontend/src/features/database/components/TableEmptyState.tsx +21 -0
  272. package/frontend/src/features/database/components/TableForm.tsx +656 -0
  273. package/frontend/src/features/database/components/TableFormColumn.tsx +137 -0
  274. package/frontend/src/features/database/components/TableListSkeleton.tsx +9 -0
  275. package/frontend/src/features/database/components/TableSidebar.tsx +47 -0
  276. package/frontend/src/features/database/constants.ts +26 -0
  277. package/frontend/src/features/database/helpers.ts +125 -0
  278. package/frontend/src/features/database/hooks/UseLinkModal.tsx +78 -0
  279. package/frontend/src/features/database/index.ts +12 -0
  280. package/frontend/src/features/database/page/DatabasePage.tsx +626 -0
  281. package/frontend/src/features/database/schema.ts +25 -0
  282. package/frontend/src/features/database/services/database.service.ts +216 -0
  283. package/frontend/src/features/functions/components/FunctionEmptyState.tsx +15 -0
  284. package/frontend/src/features/functions/components/FunctionRow.tsx +71 -0
  285. package/frontend/src/features/functions/components/FunctionViewer.tsx +46 -0
  286. package/frontend/src/features/functions/components/FunctionsContent.tsx +88 -0
  287. package/frontend/src/features/functions/components/FunctionsSidebar.tsx +56 -0
  288. package/frontend/src/features/functions/components/SecretEmptyState.tsx +23 -0
  289. package/frontend/src/features/functions/components/SecretRow.tsx +68 -0
  290. package/frontend/src/features/functions/components/SecretsContent.tsx +120 -0
  291. package/frontend/src/features/functions/hooks/useFunctions.ts +106 -0
  292. package/frontend/src/features/functions/page/FunctionsPage.tsx +28 -0
  293. package/frontend/src/features/functions/services/functions.service.ts +48 -0
  294. package/frontend/src/features/login/components/AuthErrorBoundary.tsx +87 -0
  295. package/frontend/src/features/login/components/PrivateRoute.tsx +24 -0
  296. package/frontend/src/features/login/page/CloudLoginPage.tsx +93 -0
  297. package/frontend/src/features/login/page/LoginPage.tsx +174 -0
  298. package/frontend/src/features/logs/components/AnalyticsLogsTable.tsx +313 -0
  299. package/frontend/src/features/logs/components/LogsTable.tsx +199 -0
  300. package/frontend/src/features/logs/hooks/useAuditLogs.ts +39 -0
  301. package/frontend/src/features/logs/index.ts +5 -0
  302. package/frontend/src/features/logs/page/AnalyticsLogsPage.tsx +530 -0
  303. package/frontend/src/features/logs/page/AuditsPage.tsx +192 -0
  304. package/frontend/src/features/logs/services/log.service.ts +171 -0
  305. package/frontend/src/features/metadata/hooks/useMetadata.ts +53 -0
  306. package/frontend/src/features/metadata/index.ts +0 -0
  307. package/frontend/src/features/metadata/page/MetadataPage.tsx +136 -0
  308. package/frontend/src/features/metadata/services/metadata.service.ts +17 -0
  309. package/frontend/src/features/onboard/components/CompletionCard.tsx +41 -0
  310. package/frontend/src/features/onboard/components/OnboardButton.tsx +84 -0
  311. package/frontend/src/features/onboard/components/StepContent.tsx +91 -0
  312. package/frontend/src/features/onboard/components/TestConnectionStep.tsx +53 -0
  313. package/frontend/src/features/onboard/components/mcp/CursorDeeplinkGenerator.tsx +35 -0
  314. package/frontend/src/features/onboard/components/mcp/McpInstallation.tsx +144 -0
  315. package/frontend/src/features/onboard/components/mcp/index.ts +4 -0
  316. package/frontend/src/features/onboard/components/mcp/mcp-helper.tsx +98 -0
  317. package/frontend/src/features/onboard/index.ts +3 -0
  318. package/frontend/src/features/onboard/page/OnBoardPage.tsx +104 -0
  319. package/frontend/src/features/onboard/types.ts +8 -0
  320. package/frontend/src/features/secrets/hooks/useSecrets.ts +139 -0
  321. package/frontend/src/features/secrets/services/secrets.service.ts +57 -0
  322. package/frontend/src/features/storage/components/BucketEmptyState.tsx +19 -0
  323. package/frontend/src/features/storage/components/BucketFormDialog.tsx +194 -0
  324. package/frontend/src/features/storage/components/BucketListSkeleton.tsx +17 -0
  325. package/frontend/src/features/storage/components/FilePreviewDialog.tsx +287 -0
  326. package/frontend/src/features/storage/components/StorageDataGrid.tsx +239 -0
  327. package/frontend/src/features/storage/components/StorageManager.tsx +236 -0
  328. package/frontend/src/features/storage/components/StorageSidebar.tsx +44 -0
  329. package/frontend/src/features/storage/components/UploadToast.tsx +46 -0
  330. package/frontend/src/features/storage/index.ts +3 -0
  331. package/frontend/src/features/storage/page/StoragePage.tsx +553 -0
  332. package/frontend/src/features/storage/services/storage.service.ts +144 -0
  333. package/frontend/src/features/visualizer/components/AuthNode.tsx +107 -0
  334. package/frontend/src/features/visualizer/components/BucketNode.tsx +34 -0
  335. package/frontend/src/features/visualizer/components/SchemaVisualizer.tsx +359 -0
  336. package/frontend/src/features/visualizer/components/TableNode.tsx +152 -0
  337. package/frontend/src/features/visualizer/components/VisualizerSkeleton.tsx +24 -0
  338. package/frontend/src/features/visualizer/components/index.ts +5 -0
  339. package/frontend/src/features/visualizer/page/VisualizerPage.tsx +127 -0
  340. package/frontend/src/index.css +248 -0
  341. package/frontend/src/lib/api/client.ts +163 -0
  342. package/frontend/src/lib/contexts/AuthContext.tsx +157 -0
  343. package/frontend/src/lib/contexts/OnboardStepContext.tsx +68 -0
  344. package/frontend/src/lib/contexts/SocketContext.tsx +303 -0
  345. package/frontend/src/lib/contexts/ThemeContext.tsx +125 -0
  346. package/frontend/src/lib/hooks/useAuth.ts +4 -0
  347. package/frontend/src/lib/hooks/useConfirm.ts +55 -0
  348. package/frontend/src/lib/hooks/useInterval.ts +27 -0
  349. package/frontend/src/lib/hooks/useMediaQuery.ts +59 -0
  350. package/frontend/src/lib/hooks/useOnboardingCompletion.ts +29 -0
  351. package/frontend/src/lib/hooks/usePagination.ts +27 -0
  352. package/frontend/src/lib/hooks/useTimeout.ts +27 -0
  353. package/frontend/src/lib/hooks/useToast.tsx +229 -0
  354. package/frontend/src/lib/utils/constants.ts +38 -0
  355. package/frontend/src/lib/utils/utils.ts +165 -0
  356. package/frontend/src/lib/utils/validation-schemas.ts +126 -0
  357. package/frontend/src/main.tsx +16 -0
  358. package/frontend/src/rdg.css +194 -0
  359. package/frontend/src/vite-env.d.ts +12 -0
  360. package/frontend/tailwind.config.js +97 -0
  361. package/frontend/tsconfig.json +26 -0
  362. package/frontend/tsconfig.node.json +10 -0
  363. package/frontend/vite.config.ts +37 -0
  364. package/frontend/vitest.config.ts +36 -0
  365. package/functions/deno.json +25 -0
  366. package/functions/server.ts +290 -0
  367. package/functions/worker-template.js +126 -0
  368. package/openapi/ai.yaml +689 -0
  369. package/openapi/auth.yaml +563 -0
  370. package/openapi/functions.yaml +476 -0
  371. package/openapi/health.yaml +30 -0
  372. package/openapi/logs.yaml +224 -0
  373. package/openapi/metadata.yaml +178 -0
  374. package/openapi/records.yaml +382 -0
  375. package/openapi/secrets.yaml +371 -0
  376. package/openapi/storage.yaml +876 -0
  377. package/openapi/tables.yaml +464 -0
  378. package/package.json +88 -0
  379. package/shared-schemas/package.json +31 -0
  380. package/shared-schemas/src/ai-api.schema.ts +167 -0
  381. package/shared-schemas/src/ai.schema.ts +54 -0
  382. package/shared-schemas/src/auth-api.schema.ts +193 -0
  383. package/shared-schemas/src/auth.schema.ts +94 -0
  384. package/shared-schemas/src/database-api.schema.ts +259 -0
  385. package/shared-schemas/src/database.schema.ts +69 -0
  386. package/shared-schemas/src/functions-api.schema.ts +25 -0
  387. package/shared-schemas/src/functions.schema.ts +16 -0
  388. package/shared-schemas/src/index.ts +13 -0
  389. package/shared-schemas/src/logs-api.schema.ts +49 -0
  390. package/shared-schemas/src/logs.schema.ts +14 -0
  391. package/shared-schemas/src/metadata.schema.ts +56 -0
  392. package/shared-schemas/src/storage-api.schema.ts +65 -0
  393. package/shared-schemas/src/storage.schema.ts +19 -0
  394. package/shared-schemas/tsconfig.json +21 -0
  395. package/tsconfig.json +8 -0
@@ -0,0 +1,386 @@
1
+ import { Router, Request, Response, NextFunction } from 'express';
2
+ import { AuthService } from '@/core/auth/auth.js';
3
+ import { AuditService } from '@/core/logs/audit.js';
4
+ import { AppError } from '@/api/middleware/error.js';
5
+ import { ERROR_CODES } from '@/types/error-constants.js';
6
+ import { successResponse } from '@/utils/response.js';
7
+ import { AuthRequest, verifyAdmin } from '@/api/middleware/auth.js';
8
+ import oauthRouter from './auth.oauth.js';
9
+ import {
10
+ userIdSchema,
11
+ createUserRequestSchema,
12
+ createSessionRequestSchema,
13
+ createAdminSessionRequestSchema,
14
+ deleteUsersRequestSchema,
15
+ listUsersRequestSchema,
16
+ type CreateUserResponse,
17
+ type CreateSessionResponse,
18
+ type CreateAdminSessionResponse,
19
+ type GetCurrentSessionResponse,
20
+ type ListUsersResponse,
21
+ type DeleteUsersResponse,
22
+ exchangeAdminSessionRequestSchema,
23
+ } from '@insforge/shared-schemas';
24
+ import { UserRecord } from '@/types/auth.js';
25
+
26
+ const router = Router();
27
+ const authService = AuthService.getInstance();
28
+ const auditService = AuditService.getInstance();
29
+
30
+ // Mount OAuth routes
31
+ router.use('/oauth', oauthRouter);
32
+
33
+ // POST /api/auth/users - Create a new user (registration)
34
+ router.post('/users', async (req: Request, res: Response, next: NextFunction) => {
35
+ try {
36
+ const validationResult = createUserRequestSchema.safeParse(req.body);
37
+ if (!validationResult.success) {
38
+ throw new AppError(
39
+ validationResult.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', '),
40
+ 400,
41
+ ERROR_CODES.INVALID_INPUT
42
+ );
43
+ }
44
+
45
+ const { email, password, name } = validationResult.data;
46
+ const result: CreateUserResponse = await authService.register(email, password, name);
47
+
48
+ successResponse(res, result);
49
+ } catch (error) {
50
+ next(error);
51
+ }
52
+ });
53
+
54
+ // POST /api/auth/sessions - Create a new session (login)
55
+ router.post('/sessions', async (req: Request, res: Response, next: NextFunction) => {
56
+ try {
57
+ const validationResult = createSessionRequestSchema.safeParse(req.body);
58
+ if (!validationResult.success) {
59
+ throw new AppError(
60
+ validationResult.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', '),
61
+ 400,
62
+ ERROR_CODES.INVALID_INPUT
63
+ );
64
+ }
65
+
66
+ const { email, password } = validationResult.data;
67
+ const result: CreateSessionResponse = await authService.login(email, password);
68
+
69
+ successResponse(res, result);
70
+ } catch (error) {
71
+ next(error);
72
+ }
73
+ });
74
+
75
+ // POST /api/auth/admin/sessions/exchange - Create admin session
76
+ router.post('/admin/sessions/exchange', async (req: Request, res: Response, next: NextFunction) => {
77
+ try {
78
+ const validationResult = exchangeAdminSessionRequestSchema.safeParse(req.body);
79
+ if (!validationResult.success) {
80
+ throw new AppError(
81
+ validationResult.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', '),
82
+ 400,
83
+ ERROR_CODES.INVALID_INPUT
84
+ );
85
+ }
86
+
87
+ const { code } = validationResult.data;
88
+ const result: CreateAdminSessionResponse =
89
+ await authService.adminLoginWithAuthorizationCode(code);
90
+
91
+ successResponse(res, result);
92
+ } catch (error) {
93
+ if (error instanceof AppError) {
94
+ next(error);
95
+ } else {
96
+ // Convert other errors (like JWT verification errors) to 400
97
+ next(
98
+ new AppError(
99
+ 'Failed to exchange admin session' + (error instanceof Error ? `: ${error.message}` : ''),
100
+ 400,
101
+ ERROR_CODES.INVALID_INPUT
102
+ )
103
+ );
104
+ }
105
+ }
106
+ });
107
+
108
+ // POST /api/auth/admin/sessions - Create admin session
109
+ router.post('/admin/sessions', (req: Request, res: Response, next: NextFunction) => {
110
+ try {
111
+ const validationResult = createAdminSessionRequestSchema.safeParse(req.body);
112
+ if (!validationResult.success) {
113
+ throw new AppError(
114
+ validationResult.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', '),
115
+ 400,
116
+ ERROR_CODES.INVALID_INPUT
117
+ );
118
+ }
119
+
120
+ const { email, password } = validationResult.data;
121
+ const result: CreateAdminSessionResponse = authService.adminLogin(email, password);
122
+
123
+ successResponse(res, result);
124
+ } catch (error) {
125
+ next(error);
126
+ }
127
+ });
128
+
129
+ // GET /api/auth/sessions/current - Get current session user
130
+ router.get('/sessions/current', (req: Request, res: Response, next: NextFunction) => {
131
+ try {
132
+ const authHeader = req.headers.authorization;
133
+ if (!authHeader || !authHeader.startsWith('Bearer ')) {
134
+ throw new AppError('No token provided', 401, ERROR_CODES.AUTH_INVALID_CREDENTIALS);
135
+ }
136
+
137
+ const token = authHeader.substring(7);
138
+ const payload = authService.verifyToken(token);
139
+
140
+ const response: GetCurrentSessionResponse = {
141
+ user: {
142
+ id: payload.sub,
143
+ email: payload.email,
144
+ role: payload.role,
145
+ },
146
+ };
147
+
148
+ res.json(response);
149
+ } catch {
150
+ next(new AppError('Invalid token', 401, ERROR_CODES.AUTH_INVALID_CREDENTIALS));
151
+ }
152
+ });
153
+
154
+ // GET /api/auth/users - List all users (admin only)
155
+ router.get('/users', verifyAdmin, async (req: Request, res: Response, next: NextFunction) => {
156
+ try {
157
+ const queryValidation = listUsersRequestSchema.safeParse(req.query);
158
+ const queryParams = queryValidation.success ? queryValidation.data : req.query;
159
+ const { limit = '10', offset = '0', search } = queryParams || {};
160
+ const db = authService.getDb();
161
+
162
+ let query = `
163
+ SELECT
164
+ u.id,
165
+ u.email,
166
+ u.name,
167
+ u.email_verified,
168
+ u.created_at,
169
+ u.updated_at,
170
+ u.password,
171
+ STRING_AGG(a.provider, ',') as providers
172
+ FROM _accounts u
173
+ LEFT JOIN _account_providers a ON u.id = a.user_id
174
+ `;
175
+ const params: (string | number)[] = [];
176
+
177
+ if (search) {
178
+ query += ' WHERE u.email LIKE ? OR u.name LIKE ?';
179
+ params.push(`%${search}%`, `%${search}%`);
180
+ }
181
+
182
+ query += ' GROUP BY u.id ORDER BY u.created_at DESC LIMIT ? OFFSET ?';
183
+ params.push(parseInt(limit as string), parseInt(offset as string));
184
+
185
+ const dbUsers = await db.prepare(query).all(...params);
186
+
187
+ // Simple transformation - just format the provider as identities
188
+ const users = dbUsers.map((dbUser: UserRecord) => {
189
+ const identities = [];
190
+ const providers: string[] = [];
191
+
192
+ // Add social providers if any
193
+ if (dbUser.providers) {
194
+ dbUser.providers.split(',').forEach((provider: string) => {
195
+ identities.push({ provider });
196
+ providers.push(provider);
197
+ });
198
+ }
199
+
200
+ // Add email provider if password exists
201
+ if (dbUser.password) {
202
+ identities.push({ provider: 'email' });
203
+ providers.push('email');
204
+ }
205
+
206
+ // Use first provider to determine type: 'email' or 'social'
207
+ const firstProvider = providers[0];
208
+ const provider_type = firstProvider === 'email' ? 'email' : 'social';
209
+
210
+ // Return for frontend compatibility
211
+ return {
212
+ id: dbUser.id,
213
+ email: dbUser.email,
214
+ name: dbUser.name,
215
+ emailVerified: dbUser.email_verified,
216
+ createdAt: dbUser.created_at,
217
+ updatedAt: dbUser.updated_at,
218
+ identities: identities,
219
+ providerType: provider_type,
220
+ };
221
+ });
222
+
223
+ let countQuery = 'SELECT COUNT(*) as count FROM _accounts';
224
+ const countParams: string[] = [];
225
+ if (search) {
226
+ countQuery += ' WHERE email LIKE ? OR name LIKE ?';
227
+ countParams.push(`%${search}%`, `%${search}%`);
228
+ }
229
+ const { count } = (await db.prepare(countQuery).get(...countParams)) as { count: number };
230
+
231
+ const response: ListUsersResponse = {
232
+ data: users,
233
+ pagination: {
234
+ offset: parseInt(offset as string),
235
+ limit: parseInt(limit as string),
236
+ total: count,
237
+ },
238
+ };
239
+
240
+ res.json(response);
241
+ } catch (error) {
242
+ next(error);
243
+ }
244
+ });
245
+
246
+ // GET /api/auth/users/:id - Get specific user (admin only)
247
+ router.get(
248
+ '/users/:userId',
249
+ verifyAdmin,
250
+ async (req: Request, res: Response, next: NextFunction) => {
251
+ try {
252
+ // Validate userId path parameter directly
253
+ const userIdValidation = userIdSchema.safeParse(req.params.userId);
254
+ if (!userIdValidation.success) {
255
+ throw new AppError('Invalid user ID format', 400, ERROR_CODES.INVALID_INPUT);
256
+ }
257
+
258
+ const userId = userIdValidation.data;
259
+ const db = authService.getDb();
260
+
261
+ const dbUser = (await db
262
+ .prepare(
263
+ `
264
+ SELECT
265
+ u.id,
266
+ u.email,
267
+ u.name,
268
+ u.email_verified,
269
+ u.created_at,
270
+ u.updated_at,
271
+ u.password,
272
+ STRING_AGG(a.provider, ',') as providers
273
+ FROM _accounts u
274
+ LEFT JOIN _account_providers a ON u.id = a.user_id
275
+ WHERE u.id = ?
276
+ GROUP BY u.id
277
+ `
278
+ )
279
+ .get(userId)) as UserRecord | undefined;
280
+
281
+ if (!dbUser) {
282
+ throw new AppError('User not found', 404, ERROR_CODES.NOT_FOUND);
283
+ }
284
+
285
+ // Simple transformation - just format the provider as identities
286
+ const identities = [];
287
+ const providers: string[] = [];
288
+
289
+ // Add social providers if any
290
+ if (dbUser.providers) {
291
+ dbUser.providers.split(',').forEach((provider: string) => {
292
+ identities.push({ provider });
293
+ providers.push(provider);
294
+ });
295
+ }
296
+
297
+ // Add email provider if password exists
298
+ if (dbUser.password) {
299
+ identities.push({ provider: 'Email' });
300
+ providers.push('email');
301
+ }
302
+
303
+ // Use first provider to determine type: 'email' or 'social'
304
+ const firstProvider = providers[0];
305
+ const provider_type = firstProvider === 'email' ? 'Email' : 'Social';
306
+
307
+ // Return snake_case for frontend compatibility
308
+ const user = {
309
+ id: dbUser.id,
310
+ email: dbUser.email,
311
+ name: dbUser.name,
312
+ email_verified: dbUser.email_verified,
313
+ created_at: dbUser.created_at,
314
+ updated_at: dbUser.updated_at,
315
+ identities: identities,
316
+ provider_type: provider_type,
317
+ };
318
+
319
+ res.json(user);
320
+ } catch (error) {
321
+ next(error);
322
+ }
323
+ }
324
+ );
325
+
326
+ // DELETE /api/auth/users - Delete users (batch operation, admin only)
327
+ router.delete(
328
+ '/users',
329
+ verifyAdmin,
330
+ async (req: AuthRequest, res: Response, next: NextFunction) => {
331
+ try {
332
+ const validationResult = deleteUsersRequestSchema.safeParse(req.body);
333
+ if (!validationResult.success) {
334
+ throw new AppError(
335
+ validationResult.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', '),
336
+ 400,
337
+ ERROR_CODES.INVALID_INPUT
338
+ );
339
+ }
340
+
341
+ const { userIds } = validationResult.data;
342
+
343
+ const db = authService.getDb();
344
+ const placeholders = userIds.map(() => '?').join(',');
345
+
346
+ await db.prepare(`DELETE FROM _accounts WHERE id IN (${placeholders})`).run(...userIds);
347
+
348
+ // Log audit for user deletion
349
+ await auditService.log({
350
+ actor: req.user?.email || 'api-key',
351
+ action: 'DELETE_USERS',
352
+ module: 'AUTH',
353
+ details: {
354
+ userIds,
355
+ deletedCount: userIds.length,
356
+ },
357
+ ip_address: req.ip,
358
+ });
359
+
360
+ const response: DeleteUsersResponse = {
361
+ message: 'Users deleted successfully',
362
+ deletedCount: userIds.length,
363
+ };
364
+
365
+ res.json(response);
366
+ } catch (error) {
367
+ next(error);
368
+ }
369
+ }
370
+ );
371
+
372
+ // POST /api/auth/tokens/anon - Generate anonymous JWT token (never expires)
373
+ router.post('/tokens/anon', verifyAdmin, (_req: Request, res: Response, next: NextFunction) => {
374
+ try {
375
+ const token = authService.generateAnonToken();
376
+
377
+ successResponse(res, {
378
+ accessToken: token,
379
+ message: 'Anonymous token generated successfully (never expires)',
380
+ });
381
+ } catch (error) {
382
+ next(error);
383
+ }
384
+ });
385
+
386
+ export default router;
@@ -0,0 +1,275 @@
1
+ import { Router, Response } from 'express';
2
+ import { DatabaseAdvanceService } from '@/core/database/advance.js';
3
+ import { AuditService } from '@/core/logs/audit.js';
4
+ import { verifyAdmin, AuthRequest } from '@/api/middleware/auth.js';
5
+ import { AppError } from '@/api/middleware/error.js';
6
+ import { ERROR_CODES } from '@/types/error-constants.js';
7
+ import { upload, handleUploadError } from '@/api/middleware/upload.js';
8
+ import {
9
+ rawSQLRequestSchema,
10
+ exportRequestSchema,
11
+ importRequestSchema,
12
+ bulkUpsertRequestSchema,
13
+ } from '@insforge/shared-schemas';
14
+ import logger from '@/utils/logger';
15
+
16
+ const router = Router();
17
+ const dbAdvanceService = new DatabaseAdvanceService();
18
+ const auditService = AuditService.getInstance();
19
+
20
+ /**
21
+ * Execute raw SQL query
22
+ * POST /api/database/advance/rawsql
23
+ */
24
+ router.post('/rawsql', verifyAdmin, async (req: AuthRequest, res: Response) => {
25
+ try {
26
+ // Validate request body
27
+ const validation = rawSQLRequestSchema.safeParse(req.body);
28
+ if (!validation.success) {
29
+ throw new AppError(
30
+ validation.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', '),
31
+ 400,
32
+ ERROR_CODES.INVALID_INPUT
33
+ );
34
+ }
35
+
36
+ const { query, params = [] } = validation.data;
37
+ const response = await dbAdvanceService.executeRawSQL(query, params);
38
+
39
+ // Log audit for raw SQL execution
40
+ await auditService.log({
41
+ actor: req.user?.email || 'api-key',
42
+ action: 'EXECUTE_RAW_SQL',
43
+ module: 'DATABASE',
44
+ details: {
45
+ query: query.substring(0, 300), // Limit query length in audit log
46
+ paramCount: params.length,
47
+ rowsAffected: response.rowCount,
48
+ },
49
+ ip_address: req.ip,
50
+ });
51
+
52
+ res.json(response);
53
+ } catch (error: unknown) {
54
+ logger.warn('Raw SQL execution error:', error);
55
+
56
+ if (error instanceof AppError) {
57
+ res.status(error.statusCode).json({
58
+ error: 'SQL_EXECUTION_ERROR',
59
+ message: error.message,
60
+ statusCode: error.statusCode,
61
+ });
62
+ } else {
63
+ res.status(400).json({
64
+ error: 'SQL_EXECUTION_ERROR',
65
+ message: error instanceof Error ? error.message : 'Failed to execute SQL query',
66
+ statusCode: 400,
67
+ });
68
+ }
69
+ }
70
+ });
71
+
72
+ /**
73
+ * Export database data
74
+ * POST /api/database/advance/export
75
+ */
76
+ router.post('/export', verifyAdmin, async (req: AuthRequest, res: Response) => {
77
+ try {
78
+ // Validate request body
79
+ const validation = exportRequestSchema.safeParse(req.body);
80
+ if (!validation.success) {
81
+ throw new AppError(
82
+ validation.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', '),
83
+ 400,
84
+ ERROR_CODES.INVALID_INPUT
85
+ );
86
+ }
87
+
88
+ const {
89
+ tables,
90
+ format,
91
+ includeData,
92
+ includeFunctions,
93
+ includeSequences,
94
+ includeViews,
95
+ rowLimit,
96
+ } = validation.data;
97
+ const response = await dbAdvanceService.exportDatabase(
98
+ tables,
99
+ format,
100
+ includeData,
101
+ includeFunctions,
102
+ includeSequences,
103
+ includeViews,
104
+ rowLimit
105
+ );
106
+
107
+ // Log audit for database export
108
+ await auditService.log({
109
+ actor: req.user?.email || 'api-key',
110
+ action: 'EXPORT_DATABASE',
111
+ module: 'DATABASE',
112
+ details: {
113
+ format: response.format,
114
+ },
115
+ ip_address: req.ip,
116
+ });
117
+
118
+ res.json(response);
119
+ } catch (error: unknown) {
120
+ logger.warn('Database export error:', error);
121
+ res.status(500).json({
122
+ error: 'EXPORT_ERROR',
123
+ message: error instanceof Error ? error.message : 'Failed to export database',
124
+ statusCode: 500,
125
+ });
126
+ }
127
+ });
128
+
129
+ /**
130
+ * Bulk upsert data from file upload (CSV/JSON)
131
+ * POST /api/database/advance/bulk-upsert
132
+ * Expects multipart/form-data with:
133
+ * - file: CSV or JSON file
134
+ * - table: Target table name
135
+ * - upsertKey: Optional column for upsert operations
136
+ */
137
+ router.post(
138
+ '/bulk-upsert',
139
+ verifyAdmin,
140
+ upload.single('file'),
141
+ handleUploadError,
142
+ async (req: AuthRequest, res: Response) => {
143
+ try {
144
+ if (!req.file) {
145
+ throw new AppError('File is required', 400, ERROR_CODES.INVALID_INPUT);
146
+ }
147
+
148
+ // Validate request body
149
+ const validation = bulkUpsertRequestSchema.safeParse(req.body);
150
+ if (!validation.success) {
151
+ throw new AppError(
152
+ validation.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', '),
153
+ 400,
154
+ ERROR_CODES.INVALID_INPUT
155
+ );
156
+ }
157
+
158
+ const { table, upsertKey } = validation.data;
159
+
160
+ const response = await dbAdvanceService.bulkUpsertFromFile(
161
+ table,
162
+ req.file.buffer,
163
+ req.file.originalname,
164
+ upsertKey
165
+ );
166
+
167
+ // Log audit
168
+ await auditService.log({
169
+ actor: req.user?.email || 'api-key',
170
+ action: 'BULK_UPSERT',
171
+ module: 'DATABASE',
172
+ details: {
173
+ table,
174
+ filename: req.file.originalname,
175
+ fileSize: req.file.size,
176
+ upsertKey: upsertKey || null,
177
+ rowsAffected: response.rowsAffected,
178
+ totalRecords: response.totalRecords,
179
+ },
180
+ ip_address: req.ip,
181
+ });
182
+
183
+ res.json(response);
184
+ } catch (error: unknown) {
185
+ logger.warn('Bulk upsert error:', error);
186
+
187
+ if (error instanceof AppError) {
188
+ res.status(error.statusCode).json({
189
+ error: 'BULK_UPSERT_ERROR',
190
+ message: error.message,
191
+ statusCode: error.statusCode,
192
+ });
193
+ } else {
194
+ res.status(400).json({
195
+ error: 'BULK_UPSERT_ERROR',
196
+ message: error instanceof Error ? error.message : 'Failed to perform bulk upsert',
197
+ statusCode: 400,
198
+ });
199
+ }
200
+ }
201
+ }
202
+ );
203
+
204
+ /**
205
+ * Import database data from SQL file
206
+ * POST /api/database/advance/import
207
+ * Expects a SQL file upload via multipart/form-data
208
+ */
209
+ router.post(
210
+ '/import',
211
+ verifyAdmin,
212
+ upload.single('file'),
213
+ handleUploadError,
214
+ async (req: AuthRequest, res: Response) => {
215
+ try {
216
+ // Validate request body
217
+ const validation = importRequestSchema.safeParse(req.body);
218
+ if (!validation.success) {
219
+ throw new AppError(
220
+ validation.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', '),
221
+ 400,
222
+ ERROR_CODES.INVALID_INPUT
223
+ );
224
+ }
225
+
226
+ const { truncate } = validation.data;
227
+
228
+ if (!req.file) {
229
+ throw new AppError('SQL file is required', 400, ERROR_CODES.INVALID_INPUT);
230
+ }
231
+
232
+ const response = await dbAdvanceService.importDatabase(
233
+ req.file.buffer,
234
+ req.file.originalname,
235
+ req.file.size,
236
+ truncate
237
+ );
238
+
239
+ // Log audit for database import
240
+ await auditService.log({
241
+ actor: req.user?.email || 'api-key',
242
+ action: 'IMPORT_DATABASE',
243
+ module: 'DATABASE',
244
+ details: {
245
+ truncate,
246
+ filename: response.filename,
247
+ fileSize: response.fileSize,
248
+ tablesAffected: response.tables.length,
249
+ rowsImported: response.rowsImported,
250
+ },
251
+ ip_address: req.ip,
252
+ });
253
+
254
+ res.json(response);
255
+ } catch (error: unknown) {
256
+ logger.warn('Database import error:', error);
257
+
258
+ if (error instanceof AppError) {
259
+ res.status(error.statusCode).json({
260
+ error: 'IMPORT_ERROR',
261
+ message: error.message,
262
+ statusCode: error.statusCode,
263
+ });
264
+ } else {
265
+ res.status(500).json({
266
+ error: 'IMPORT_ERROR',
267
+ message: error instanceof Error ? error.message : 'Failed to import database',
268
+ statusCode: 500,
269
+ });
270
+ }
271
+ }
272
+ }
273
+ );
274
+
275
+ export default router;