insforge 0.3.3 → 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 -780
  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
@@ -0,0 +1,185 @@
1
+ import { promises as fs, createReadStream } from 'fs';
2
+ import { createInterface } from 'readline';
3
+ import path from 'path';
4
+ import { LogSchema, LogSourceSchema, LogStatsSchema } from '@insforge/shared-schemas';
5
+ import { BaseLogProvider } from './base.provider.js';
6
+ import logger from '@/utils/logger.js';
7
+
8
+ export class LocalFileProvider extends BaseLogProvider {
9
+ private logsDir: string = '';
10
+ private logFiles: Record<string, string> = {
11
+ 'insforge.logs': 'insforge.logs.jsonl',
12
+ 'postgres.logs': 'postgres.logs.jsonl',
13
+ 'postgREST.logs': 'postgrest.logs.jsonl',
14
+ 'function.logs': 'function.logs.jsonl',
15
+ };
16
+
17
+ async initialize(): Promise<void> {
18
+ this.logsDir = process.env.LOGS_DIR || path.resolve(process.cwd(), 'insforge-logs');
19
+ try {
20
+ await fs.mkdir(this.logsDir, { recursive: true });
21
+ } catch {
22
+ // Directory already exists
23
+ }
24
+ logger.info(`File-based analytics initialized at: ${this.logsDir}`);
25
+ }
26
+
27
+ getLogSources(): Promise<LogSourceSchema[]> {
28
+ const sources: LogSourceSchema[] = [];
29
+ let id = 1;
30
+
31
+ for (const [name, filename] of Object.entries(this.logFiles)) {
32
+ sources.push({ id: String(id++), name, token: filename });
33
+ }
34
+
35
+ return Promise.resolve(sources);
36
+ }
37
+
38
+ async getLogsBySource(
39
+ sourceName: string,
40
+ limit: number = 100,
41
+ beforeTimestamp?: string
42
+ ): Promise<{
43
+ logs: LogSchema[];
44
+ total: number;
45
+ tableName: string;
46
+ }> {
47
+ const filename = this.logFiles[sourceName];
48
+ if (!filename) {
49
+ return { logs: [], total: 0, tableName: sourceName };
50
+ }
51
+
52
+ const filePath = path.join(this.logsDir, filename);
53
+ const logs = await this.readLogsFromFile(filePath, limit, beforeTimestamp);
54
+
55
+ return {
56
+ logs,
57
+ total: logs.length,
58
+ tableName: sourceName,
59
+ };
60
+ }
61
+
62
+ private async readLogsFromFile(
63
+ filePath: string,
64
+ limit: number,
65
+ beforeTimestamp?: string
66
+ ): Promise<LogSchema[]> {
67
+ try {
68
+ await fs.access(filePath);
69
+ } catch {
70
+ return [];
71
+ }
72
+
73
+ const logs: LogSchema[] = [];
74
+ const beforeMs = beforeTimestamp ? Date.parse(beforeTimestamp) : Date.now();
75
+
76
+ const fileStream = createReadStream(filePath);
77
+ const rl = createInterface({ input: fileStream, crlfDelay: Infinity });
78
+
79
+ for await (const line of rl) {
80
+ if (!line.trim()) {
81
+ continue;
82
+ }
83
+
84
+ try {
85
+ const log = JSON.parse(line);
86
+
87
+ // Only process Vector-transformed logs (have appname field)
88
+ if (!log.appname) {
89
+ continue;
90
+ }
91
+
92
+ const logTime = new Date(log.timestamp).getTime();
93
+
94
+ if (logTime < beforeMs) {
95
+ // For error logs, include error and stack in eventMessage to match CloudWatch display
96
+ let eventMessage = log.event_message || '';
97
+ if (log.level === 'error' && log.error) {
98
+ eventMessage = `${eventMessage}\n\nError: ${log.error}`;
99
+ if (log.stack) {
100
+ eventMessage += `\n\nStack Trace:\n${log.stack}`;
101
+ }
102
+ }
103
+
104
+ logs.push({
105
+ id: `${logTime}-${Math.random()}`,
106
+ timestamp: log.timestamp,
107
+ eventMessage,
108
+ body: log,
109
+ });
110
+ }
111
+ } catch {
112
+ // Skip invalid JSON lines
113
+ }
114
+ }
115
+
116
+ // Return most recent logs up to limit
117
+ return logs.slice(-limit);
118
+ }
119
+
120
+ async getLogSourceStats(): Promise<LogStatsSchema[]> {
121
+ const stats: LogStatsSchema[] = [];
122
+
123
+ for (const [name, filename] of Object.entries(this.logFiles)) {
124
+ const filePath = path.join(this.logsDir, filename);
125
+ try {
126
+ const fileStats = await fs.stat(filePath);
127
+ const logs = await this.readLogsFromFile(filePath, 1000);
128
+
129
+ stats.push({
130
+ source: name,
131
+ count: logs.length,
132
+ lastActivity: fileStats.mtime.toISOString(),
133
+ });
134
+ } catch {
135
+ // File doesn't exist
136
+ }
137
+ }
138
+
139
+ return stats;
140
+ }
141
+
142
+ async searchLogs(
143
+ query: string,
144
+ sourceName?: string,
145
+ limit: number = 100,
146
+ offset: number = 0
147
+ ): Promise<{
148
+ logs: (LogSchema & { source: string })[];
149
+ total: number;
150
+ }> {
151
+ const results: (LogSchema & { source: string })[] = [];
152
+ const searchLower = query.toLowerCase();
153
+
154
+ const filesToSearch = sourceName
155
+ ? [{ name: sourceName, filename: this.logFiles[sourceName] }]
156
+ : Object.entries(this.logFiles).map(([name, filename]) => ({ name, filename }));
157
+
158
+ for (const { name, filename } of filesToSearch) {
159
+ if (!filename) {
160
+ continue;
161
+ }
162
+
163
+ const filePath = path.join(this.logsDir, filename);
164
+ const logs = await this.readLogsFromFile(filePath, 10000);
165
+
166
+ for (const log of logs) {
167
+ const messageMatch = log.eventMessage.toLowerCase().includes(searchLower);
168
+ const metadataMatch = JSON.stringify(log.body).toLowerCase().includes(searchLower);
169
+
170
+ if (messageMatch || metadataMatch) {
171
+ results.push({ ...log, source: name });
172
+ }
173
+ }
174
+ }
175
+
176
+ return {
177
+ logs: results.slice(offset, offset + limit),
178
+ total: results.length,
179
+ };
180
+ }
181
+
182
+ async close(): Promise<void> {
183
+ // No cleanup needed for file-based provider
184
+ }
185
+ }
@@ -0,0 +1,29 @@
1
+ import type { OAuthUserData } from '@/types/auth.js';
2
+
3
+ /**
4
+ * OAuth provider interface
5
+ * Defines the contract that all OAuth providers must implement
6
+ */
7
+ export interface OAuthProvider {
8
+ /**
9
+ * Generate OAuth authorization URL
10
+ * @param state - Optional state parameter for CSRF protection
11
+ * @returns Authorization URL
12
+ */
13
+ generateOAuthUrl(state?: string): Promise<string>;
14
+
15
+ /**
16
+ * Handle OAuth callback and exchange code/token for user info
17
+ * @param payload - OAuth callback payload containing code or token
18
+ * @returns User data from OAuth provider
19
+ */
20
+ handleCallback(payload: { code?: string; token?: string }): Promise<OAuthUserData>;
21
+
22
+ /**
23
+ * Handle shared OAuth callback (for shared keys)
24
+ * Optional - not all providers support shared OAuth
25
+ * @param payloadData - Payload data from shared OAuth callback
26
+ * @returns User data transformed to standard format
27
+ */
28
+ handleSharedCallback?(payloadData: Record<string, unknown>): OAuthUserData;
29
+ }
@@ -0,0 +1,195 @@
1
+ import axios from 'axios';
2
+ import logger from '@/utils/logger.js';
3
+ import { getApiBaseUrl } from '@/utils/environment.js';
4
+ import { OAuthConfigService } from '@/services/auth/oauth-config.service.js';
5
+ import { OAuthProvider } from './base.provider.js';
6
+ import type { DiscordUserInfo, OAuthUserData } from '@/types/auth.js';
7
+
8
+ /**
9
+ * Discord OAuth Service
10
+ * Handles all Discord OAuth operations including URL generation, token exchange, and user info retrieval
11
+ */
12
+ export class DiscordOAuthProvider implements OAuthProvider {
13
+ private static instance: DiscordOAuthProvider;
14
+
15
+ private constructor() {
16
+ // Initialize OAuth helpers if needed
17
+ }
18
+
19
+ public static getInstance(): DiscordOAuthProvider {
20
+ if (!DiscordOAuthProvider.instance) {
21
+ DiscordOAuthProvider.instance = new DiscordOAuthProvider();
22
+ }
23
+ return DiscordOAuthProvider.instance;
24
+ }
25
+
26
+ /**
27
+ * Generate Discord OAuth authorization URL
28
+ */
29
+ async generateOAuthUrl(state?: string): Promise<string> {
30
+ const oAuthConfigService = OAuthConfigService.getInstance();
31
+ const config = await oAuthConfigService.getConfigByProvider('discord');
32
+
33
+ if (!config) {
34
+ throw new Error('Discord OAuth not configured');
35
+ }
36
+
37
+ const selfBaseUrl = getApiBaseUrl();
38
+
39
+ if (config?.useSharedKey) {
40
+ if (!state) {
41
+ logger.warn('Shared Discord OAuth called without state parameter');
42
+ throw new Error('State parameter is required for shared Discord OAuth');
43
+ }
44
+ // Use shared keys if configured
45
+ const cloudBaseUrl = process.env.CLOUD_API_HOST || 'https://api.insforge.dev';
46
+ const redirectUri = `${selfBaseUrl}/api/auth/oauth/shared/callback/${state}`;
47
+ const response = await axios.get(
48
+ `${cloudBaseUrl}/auth/v1/shared/discord?redirect_uri=${encodeURIComponent(redirectUri)}`,
49
+ {
50
+ headers: {
51
+ 'Content-Type': 'application/json',
52
+ },
53
+ }
54
+ );
55
+ return response.data.auth_url || response.data.url || '';
56
+ }
57
+
58
+ logger.debug('Discord OAuth Config (fresh from DB):', {
59
+ clientId: config.clientId ? 'SET' : 'NOT SET',
60
+ });
61
+
62
+ const authUrl = new URL('https://discord.com/api/oauth2/authorize');
63
+ authUrl.searchParams.set('client_id', config.clientId ?? '');
64
+ authUrl.searchParams.set('redirect_uri', `${selfBaseUrl}/api/auth/oauth/discord/callback`);
65
+ authUrl.searchParams.set('response_type', 'code');
66
+ authUrl.searchParams.set('scope', config.scopes ? config.scopes.join(' ') : 'identify email');
67
+ if (state) {
68
+ authUrl.searchParams.set('state', state);
69
+ }
70
+
71
+ return authUrl.toString();
72
+ }
73
+
74
+ /**
75
+ * Exchange Discord code for access token
76
+ */
77
+ async exchangeCodeToToken(code: string): Promise<string> {
78
+ const oAuthConfigService = OAuthConfigService.getInstance();
79
+ const config = await oAuthConfigService.getConfigByProvider('discord');
80
+
81
+ if (!config) {
82
+ throw new Error('Discord OAuth not configured');
83
+ }
84
+
85
+ try {
86
+ logger.info('Exchanging Discord code for token', {
87
+ hasCode: !!code,
88
+ clientId: config.clientId?.substring(0, 10) + '...',
89
+ });
90
+
91
+ const clientSecret = await oAuthConfigService.getClientSecretByProvider('discord');
92
+ const selfBaseUrl = getApiBaseUrl();
93
+ const response = await axios.post(
94
+ 'https://discord.com/api/oauth2/token',
95
+ new URLSearchParams({
96
+ client_id: config.clientId ?? '',
97
+ client_secret: clientSecret ?? '',
98
+ code,
99
+ redirect_uri: `${selfBaseUrl}/api/auth/oauth/discord/callback`,
100
+ grant_type: 'authorization_code',
101
+ }).toString(),
102
+ {
103
+ headers: {
104
+ 'Content-Type': 'application/x-www-form-urlencoded',
105
+ },
106
+ }
107
+ );
108
+
109
+ if (!response.data.access_token) {
110
+ throw new Error('Failed to get access token from Discord');
111
+ }
112
+
113
+ return response.data.access_token;
114
+ } catch (error) {
115
+ if (axios.isAxiosError(error) && error.response) {
116
+ logger.error('Discord token exchange failed', {
117
+ status: error.response.status,
118
+ error: error.response.data,
119
+ });
120
+ throw new Error(`Discord OAuth error: ${JSON.stringify(error.response.data)}`);
121
+ }
122
+ throw error;
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Get Discord user info
128
+ */
129
+ async getUserInfo(accessToken: string): Promise<DiscordUserInfo> {
130
+ try {
131
+ const response = await axios.get('https://discord.com/api/users/@me', {
132
+ headers: {
133
+ Authorization: `Bearer ${accessToken}`,
134
+ },
135
+ });
136
+
137
+ return {
138
+ id: response.data.id,
139
+ username: response.data.global_name || response.data.username,
140
+ email: response.data.email,
141
+ avatar: response.data.avatar
142
+ ? `https://cdn.discordapp.com/avatars/${response.data.id}/${response.data.avatar}.png`
143
+ : '',
144
+ };
145
+ } catch (error) {
146
+ logger.error('Discord user info retrieval failed:', error);
147
+ throw new Error(`Failed to get Discord user info: ${error}`);
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Handle Discord OAuth callback
153
+ */
154
+ async handleCallback(payload: { code?: string; token?: string }): Promise<OAuthUserData> {
155
+ if (!payload.code) {
156
+ throw new Error('No authorization code provided');
157
+ }
158
+
159
+ const accessToken = await this.exchangeCodeToToken(payload.code);
160
+ const discordUserInfo = await this.getUserInfo(accessToken);
161
+
162
+ // Transform Discord user info to generic format
163
+ const userName = discordUserInfo.username;
164
+ const email = discordUserInfo.email || `${discordUserInfo.id}@users.noreply.discord.local`;
165
+ return {
166
+ provider: 'discord',
167
+ providerId: discordUserInfo.id,
168
+ email,
169
+ userName,
170
+ avatarUrl: discordUserInfo.avatar || '',
171
+ identityData: discordUserInfo,
172
+ };
173
+ }
174
+
175
+ /**
176
+ * Handle shared callback payload transformation
177
+ */
178
+ handleSharedCallback(payloadData: Record<string, unknown>): OAuthUserData {
179
+ const providerId = String(payloadData.providerId ?? '');
180
+ const username = String(payloadData.username ?? '');
181
+ const emailField = String(payloadData.email ?? '');
182
+ const avatar = String(payloadData.avatar ?? '');
183
+
184
+ const email = emailField || `${providerId}@users.noreply.discord.local`;
185
+
186
+ return {
187
+ provider: 'discord',
188
+ providerId,
189
+ email,
190
+ userName: username,
191
+ avatarUrl: avatar,
192
+ identityData: payloadData,
193
+ };
194
+ }
195
+ }
@@ -0,0 +1,194 @@
1
+ import axios from 'axios';
2
+ import logger from '@/utils/logger.js';
3
+ import { getApiBaseUrl } from '@/utils/environment.js';
4
+ import { OAuthConfigService } from '@/services/auth/oauth-config.service.js';
5
+ import { OAuthProvider } from './base.provider.js';
6
+ import type { FacebookUserInfo, OAuthUserData } from '@/types/auth.js';
7
+
8
+ /**
9
+ * Facebook OAuth Service
10
+ * Handles all Facebook OAuth operations including URL generation, token exchange, and user info retrieval
11
+ */
12
+ export class FacebookOAuthProvider implements OAuthProvider {
13
+ private static instance: FacebookOAuthProvider;
14
+
15
+ private constructor() {
16
+ // Initialize OAuth helpers if needed
17
+ }
18
+
19
+ public static getInstance(): FacebookOAuthProvider {
20
+ if (!FacebookOAuthProvider.instance) {
21
+ FacebookOAuthProvider.instance = new FacebookOAuthProvider();
22
+ }
23
+ return FacebookOAuthProvider.instance;
24
+ }
25
+
26
+ /**
27
+ * Generate Facebook OAuth authorization URL
28
+ */
29
+ async generateOAuthUrl(state?: string): Promise<string> {
30
+ const oAuthConfigService = OAuthConfigService.getInstance();
31
+ const config = await oAuthConfigService.getConfigByProvider('facebook');
32
+
33
+ if (!config) {
34
+ throw new Error('Facebook OAuth not configured');
35
+ }
36
+
37
+ const selfBaseUrl = getApiBaseUrl();
38
+
39
+ if (config?.useSharedKey) {
40
+ if (!state) {
41
+ logger.warn('Shared Facebook OAuth called without state parameter');
42
+ throw new Error('State parameter is required for shared Facebook OAuth');
43
+ }
44
+ const cloudBaseUrl = process.env.CLOUD_API_HOST || 'https://api.insforge.dev';
45
+ const redirectUri = `${selfBaseUrl}/api/auth/oauth/shared/callback/${state}`;
46
+ const response = await axios.get(
47
+ `${cloudBaseUrl}/auth/v1/shared/facebook?redirect_uri=${encodeURIComponent(redirectUri)}`,
48
+ {
49
+ headers: {
50
+ 'Content-Type': 'application/json',
51
+ },
52
+ }
53
+ );
54
+ return response.data.auth_url || response.data.url || '';
55
+ }
56
+
57
+ logger.debug('Facebook OAuth Config (fresh from DB):', {
58
+ clientId: config.clientId ? 'SET' : 'NOT SET',
59
+ });
60
+
61
+ const authUrl = new URL('https://www.facebook.com/v21.0/dialog/oauth');
62
+ authUrl.searchParams.set('client_id', config.clientId ?? '');
63
+ authUrl.searchParams.set('redirect_uri', `${selfBaseUrl}/api/auth/oauth/facebook/callback`);
64
+ authUrl.searchParams.set('response_type', 'code');
65
+ authUrl.searchParams.set(
66
+ 'scope',
67
+ config.scopes ? config.scopes.join(',') : 'email,public_profile'
68
+ );
69
+ if (state) {
70
+ authUrl.searchParams.set('state', state);
71
+ }
72
+
73
+ return authUrl.toString();
74
+ }
75
+
76
+ /**
77
+ * Exchange Facebook code for access token
78
+ */
79
+ async exchangeCodeToToken(code: string): Promise<string> {
80
+ const oAuthConfigService = OAuthConfigService.getInstance();
81
+ const config = await oAuthConfigService.getConfigByProvider('facebook');
82
+
83
+ if (!config) {
84
+ throw new Error('Facebook OAuth not configured');
85
+ }
86
+
87
+ try {
88
+ logger.info('Exchanging Facebook code for token', {
89
+ hasCode: !!code,
90
+ clientId: config.clientId?.substring(0, 10) + '...',
91
+ });
92
+
93
+ const clientSecret = await oAuthConfigService.getClientSecretByProvider('facebook');
94
+ const selfBaseUrl = getApiBaseUrl();
95
+ const response = await axios.get('https://graph.facebook.com/v21.0/oauth/access_token', {
96
+ params: {
97
+ client_id: config.clientId,
98
+ client_secret: clientSecret,
99
+ code,
100
+ redirect_uri: `${selfBaseUrl}/api/auth/oauth/facebook/callback`,
101
+ },
102
+ });
103
+
104
+ if (!response.data.access_token) {
105
+ throw new Error('Failed to get access token from Facebook');
106
+ }
107
+
108
+ return response.data.access_token;
109
+ } catch (error) {
110
+ if (axios.isAxiosError(error) && error.response) {
111
+ logger.error('Facebook token exchange failed', {
112
+ status: error.response.status,
113
+ error: error.response.data,
114
+ });
115
+ throw new Error(`Facebook OAuth error: ${JSON.stringify(error.response.data)}`);
116
+ }
117
+ throw error;
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Get Facebook user info
123
+ */
124
+ async getUserInfo(accessToken: string): Promise<FacebookUserInfo> {
125
+ try {
126
+ const response = await axios.get('https://graph.facebook.com/v21.0/me', {
127
+ params: {
128
+ fields: 'id,email,name,first_name,last_name,picture',
129
+ access_token: accessToken,
130
+ },
131
+ });
132
+
133
+ return response.data;
134
+ } catch (error) {
135
+ logger.error('Facebook user info retrieval failed:', error);
136
+ throw new Error(`Failed to get Facebook user info: ${error}`);
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Handle Facebook OAuth callback
142
+ */
143
+ async handleCallback(payload: { code?: string; token?: string }): Promise<OAuthUserData> {
144
+ if (!payload.code) {
145
+ throw new Error('No authorization code provided');
146
+ }
147
+
148
+ const accessToken = await this.exchangeCodeToToken(payload.code);
149
+ const facebookUserInfo = await this.getUserInfo(accessToken);
150
+
151
+ // Transform Facebook user info to generic format
152
+ const email = facebookUserInfo.email || '';
153
+ const userName =
154
+ facebookUserInfo.name ||
155
+ facebookUserInfo.first_name ||
156
+ `User${facebookUserInfo.id.substring(0, 6)}`;
157
+ const avatarUrl = facebookUserInfo.picture?.data?.url || '';
158
+ return {
159
+ provider: 'facebook',
160
+ providerId: facebookUserInfo.id,
161
+ email,
162
+ userName,
163
+ avatarUrl,
164
+ identityData: facebookUserInfo,
165
+ };
166
+ }
167
+
168
+ /**
169
+ * Handle shared callback payload transformation
170
+ */
171
+ handleSharedCallback(payloadData: Record<string, unknown>): OAuthUserData {
172
+ const providerId = String(payloadData.providerId ?? '');
173
+ const email = String(payloadData.email ?? '');
174
+ const name = String(payloadData.name ?? '');
175
+ const firstName = String(payloadData.first_name ?? '');
176
+ const avatar = String(payloadData.avatar ?? '');
177
+
178
+ // Handle nested picture.data.url structure
179
+ const picture = payloadData.picture as { data?: { url?: string } } | undefined;
180
+ const pictureUrl = picture?.data?.url ?? '';
181
+
182
+ const userName = name || firstName || `User${providerId.substring(0, 6)}`;
183
+ const avatarUrl = pictureUrl || avatar;
184
+
185
+ return {
186
+ provider: 'facebook',
187
+ providerId,
188
+ email,
189
+ userName,
190
+ avatarUrl,
191
+ identityData: payloadData,
192
+ };
193
+ }
194
+ }