insforge 0.3.2 → 1.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +20 -0
- package/.cursor/rules/cursor-rules.mdc +94 -0
- package/.dockerignore +3 -0
- package/.env.example +33 -4
- package/.github/ISSUE_TEMPLATE/bug_report.yml +13 -60
- package/.github/ISSUE_TEMPLATE/config.yml +2 -2
- package/.github/ISSUE_TEMPLATE/feature_request.yml +10 -63
- package/.github/PULL_REQUEST_TEMPLATE.md +7 -0
- package/.github/workflows/build-image.yml +2 -1
- package/.github/workflows/e2e.yml +63 -0
- package/CHANGELOG.md +41 -0
- package/CLAUDE_PLUGIN.md +104 -0
- package/CODE_OF_CONDUCT.md +128 -0
- package/CONTRIBUTING.md +1 -1
- package/Dockerfile +4 -1
- package/README.md +66 -18
- package/assets/mcpInstallv2.png +0 -0
- package/assets/sampleResponse.png +0 -0
- package/auth/index.html +13 -0
- package/auth/package.json +28 -0
- package/auth/public/favicon.ico +0 -0
- package/auth/src/App.tsx +33 -0
- package/auth/src/components/ErrorCard.tsx +37 -0
- package/auth/src/components/Layout.tsx +13 -0
- package/auth/src/index.css +19 -0
- package/auth/src/lib/broadcastService.ts +115 -0
- package/auth/src/lib/utils.ts +11 -0
- package/auth/src/main.tsx +22 -0
- package/auth/src/pages/ForgotPasswordPage.tsx +11 -0
- package/auth/src/pages/ResetPasswordPage.tsx +11 -0
- package/auth/src/pages/SignInPage.tsx +57 -0
- package/auth/src/pages/SignUpPage.tsx +57 -0
- package/auth/src/pages/VerifyEmailPage.tsx +20 -0
- package/auth/src/vite-env.d.ts +10 -0
- package/auth/tsconfig.json +32 -0
- package/auth/tsconfig.node.json +11 -0
- package/auth/vite.config.ts +25 -0
- package/backend/package.json +9 -9
- package/backend/src/api/{middleware → middlewares}/auth.ts +8 -9
- package/backend/src/api/middlewares/rate-limiters.ts +127 -0
- package/backend/src/api/routes/{ai.ts → ai/index.routes.ts} +20 -24
- package/backend/src/api/routes/auth/index.routes.ts +570 -0
- package/backend/src/api/routes/auth/oauth.routes.ts +448 -0
- package/backend/src/api/routes/{database.advance.ts → database/advance.routes.ts} +107 -65
- package/backend/src/api/routes/database/index.routes.ts +13 -0
- package/backend/src/api/routes/{database.records.ts → database/records.routes.ts} +22 -8
- package/backend/src/api/routes/{database.tables.ts → database/tables.routes.ts} +20 -23
- package/backend/src/api/routes/docs/index.routes.ts +76 -0
- package/backend/src/api/routes/functions/index.routes.ts +188 -0
- package/backend/src/api/routes/{logs.ts → logs/index.routes.ts} +25 -30
- package/backend/src/api/routes/{metadata.ts → metadata/index.routes.ts} +21 -31
- package/backend/src/api/routes/{secrets.ts → secrets/index.routes.ts} +27 -22
- package/backend/src/api/routes/{storage.ts → storage/index.routes.ts} +34 -53
- package/backend/src/api/routes/usage/index.routes.ts +89 -0
- package/backend/src/infra/config/app.config.ts +51 -0
- package/backend/src/{core/database/manager.ts → infra/database/database.manager.ts} +76 -85
- package/backend/src/infra/database/migrations/013_create-auth-schema-functions.sql +44 -0
- package/backend/src/infra/database/migrations/014_add-updated-at-trigger-user-table.sql +8 -0
- package/backend/src/infra/database/migrations/015_create-auth-config-and-email-otp-tables.sql +60 -0
- package/backend/src/infra/database/migrations/016_update-auth-config-and-email-otp.sql +24 -0
- package/backend/src/{core/secrets/encryption.ts → infra/security/encryption.manager.ts} +3 -2
- package/backend/src/infra/security/token.manager.ts +125 -0
- package/backend/src/{core/socket/socket.ts → infra/socket/socket.manager.ts} +15 -15
- package/backend/src/providers/ai/openrouter.provider.ts +377 -0
- package/backend/src/providers/email/base.provider.ts +41 -0
- package/backend/src/providers/email/cloud.provider.ts +187 -0
- package/backend/src/{core/logs/providers → providers/logs}/base.provider.ts +11 -11
- package/backend/src/{core/logs/providers → providers/logs}/cloudwatch.provider.ts +61 -38
- package/backend/src/providers/logs/local.provider.ts +185 -0
- package/backend/src/providers/oauth/base.provider.ts +29 -0
- package/backend/src/providers/oauth/discord.provider.ts +195 -0
- package/backend/src/providers/oauth/facebook.provider.ts +194 -0
- package/backend/src/providers/oauth/github.provider.ts +208 -0
- package/backend/src/providers/oauth/google.provider.ts +249 -0
- package/backend/src/providers/oauth/index.ts +7 -0
- package/backend/src/providers/oauth/linkedin.provider.ts +240 -0
- package/backend/src/providers/oauth/microsoft.provider.ts +169 -0
- package/backend/src/providers/oauth/x.provider.ts +202 -0
- package/backend/src/providers/storage/base.provider.ts +29 -0
- package/backend/src/providers/storage/local.provider.ts +103 -0
- package/backend/src/providers/storage/s3.provider.ts +313 -0
- package/backend/src/server.ts +70 -74
- package/backend/src/{core/ai/config.ts → services/ai/ai-config.service.ts} +19 -24
- package/backend/src/services/ai/ai-model.service.ts +60 -0
- package/backend/src/{core/ai/usage.ts → services/ai/ai-usage.service.ts} +28 -35
- package/backend/src/{core/ai/chat.ts → services/ai/chat-completion.service.ts} +37 -24
- package/backend/src/services/ai/helpers.ts +64 -0
- package/backend/src/{core/ai/image.ts → services/ai/image-generation.service.ts} +17 -19
- package/backend/src/services/ai/index.ts +13 -0
- package/backend/src/services/auth/auth-config.service.ts +250 -0
- package/backend/src/services/auth/auth-otp.service.ts +424 -0
- package/backend/src/services/auth/auth.service.ts +1136 -0
- package/backend/src/services/auth/index.ts +4 -0
- package/backend/src/{core/auth/oauth.ts → services/auth/oauth-config.service.ts} +106 -52
- package/backend/src/{core/database/advance.ts → services/database/database-advance.service.ts} +97 -131
- package/backend/src/services/database/database-table.service.ts +811 -0
- package/backend/src/services/email/email.service.ts +75 -0
- package/backend/src/{core/functions/functions.ts → services/functions/function.service.ts} +95 -88
- package/backend/src/{core/logs/audit.ts → services/logs/audit.service.ts} +92 -75
- package/backend/src/services/logs/log.service.ts +73 -0
- package/backend/src/{core/secrets/secrets.ts → services/secrets/secret.service.ts} +48 -66
- package/backend/src/services/storage/storage.service.ts +617 -0
- package/backend/src/services/usage/usage.service.ts +149 -0
- package/backend/src/types/auth.ts +66 -2
- package/backend/src/types/email.ts +8 -0
- package/backend/src/types/error-constants.ts +4 -0
- package/backend/src/types/logs.ts +0 -29
- package/backend/src/{core/socket/types.ts → types/socket.ts} +5 -6
- package/backend/src/utils/environment.ts +9 -3
- package/backend/src/utils/logger.ts +20 -2
- package/backend/src/utils/seed.ts +150 -57
- package/backend/src/utils/sql-parser.ts +1 -1
- package/backend/src/utils/utils.ts +114 -0
- package/backend/src/utils/validations.ts +40 -4
- package/backend/tests/local/test-ai-config.sh +129 -0
- package/backend/tests/local/test-ai-usage.sh +80 -0
- package/backend/tests/local/test-auth-router.sh +1 -1
- package/backend/tests/local/test-e2e.sh +1 -1
- package/backend/tests/local/test-functions.sh +123 -0
- package/backend/tests/local/test-logs.sh +132 -0
- package/backend/tests/local/test-public-bucket.sh +3 -3
- package/backend/tests/local/test-secrets.sh +14 -12
- package/backend/tests/local/test-traditional-rest.sh +2 -2
- package/backend/tests/manual/test-rawsql-modes.sh +244 -0
- package/backend/tests/test-config.sh +37 -1
- package/backend/tests/unit/cloud-token.test.ts +48 -0
- package/backend/tests/unit/constant.test.ts +8 -0
- package/backend/tests/unit/email.test.ts +372 -0
- package/backend/tests/unit/environment.test.ts +59 -0
- package/backend/tests/unit/helpers.test.ts +63 -0
- package/backend/tests/unit/logger.test.ts +22 -0
- package/backend/tests/unit/rate-limit.test.ts +154 -0
- package/backend/tests/unit/response.test.ts +58 -0
- package/backend/tests/unit/sql-parser.test.ts +74 -0
- package/backend/tests/unit/uuid.test.ts +21 -0
- package/backend/tests/unit/validations.test.ts +80 -0
- package/backend/tsconfig.json +1 -1
- package/backend/vitest.config.ts +11 -0
- package/claude-plugin/.claude-plugin/plugin.json +24 -0
- package/claude-plugin/README.md +133 -0
- package/claude-plugin/skills/insforge-schema-patterns/SKILL.md +270 -0
- package/docker-compose.prod.yml +60 -4
- package/docker-compose.yml +65 -4
- package/docker-init/db/db-init.sql +6 -34
- package/docker-init/logs/vector.yml +236 -0
- package/docs/README.md +44 -0
- package/docs/changelog.mdx +67 -0
- package/docs/core-concepts/ai/architecture.mdx +373 -0
- package/docs/core-concepts/ai/sdk.mdx +213 -0
- package/docs/core-concepts/authentication/architecture.mdx +278 -0
- package/docs/core-concepts/authentication/sdk.mdx +414 -0
- package/docs/core-concepts/authentication/ui-components/customization.mdx +529 -0
- package/docs/core-concepts/authentication/ui-components/nextjs.mdx +221 -0
- package/docs/core-concepts/authentication/ui-components/react-router.mdx +184 -0
- package/docs/core-concepts/authentication/ui-components/react.mdx +129 -0
- package/docs/core-concepts/database/architecture.mdx +256 -0
- package/docs/core-concepts/database/sdk.mdx +382 -0
- package/docs/core-concepts/functions/architecture.mdx +105 -0
- package/docs/core-concepts/functions/sdk.mdx +184 -0
- package/docs/core-concepts/storage/architecture.mdx +243 -0
- package/docs/core-concepts/storage/sdk.mdx +253 -0
- package/docs/deployment/README.md +94 -0
- package/docs/deployment/deploy-to-aws-ec2.md +565 -0
- package/docs/deployment/deploy-to-azure-virtual-machines.md +313 -0
- package/docs/deployment/deploy-to-google-cloud-compute-engine.md +613 -0
- package/docs/deployment/deploy-to-render.md +441 -0
- package/docs/docs.json +210 -0
- package/docs/examples/framework-guides/nextjs.mdx +131 -0
- package/docs/examples/framework-guides/nuxt.mdx +165 -0
- package/docs/examples/framework-guides/react.mdx +165 -0
- package/docs/examples/framework-guides/svelte.mdx +153 -0
- package/docs/examples/framework-guides/vue.mdx +159 -0
- package/docs/examples/overview.mdx +67 -0
- package/docs/favicon.svg +19 -0
- package/docs/images/changelog/nov-2025/auth-components.webp +0 -0
- package/docs/images/changelog/nov-2025/database-metadata.webp +0 -0
- package/docs/images/changelog/nov-2025/quickstart-prompts.webp +0 -0
- package/docs/images/changelog/nov-2025/sql-editor.webp +0 -0
- package/docs/images/changelog/nov-2025/usage-page.webp +0 -0
- package/docs/images/changelog/october-2025/csv-upload.webp +0 -0
- package/docs/images/changelog/october-2025/logs-feature.webp +0 -0
- package/docs/images/changelog/october-2025/oauth-providers.webp +0 -0
- package/docs/images/checks-passed.png +0 -0
- package/docs/images/dashboard-connect-expanded.png +0 -0
- package/docs/images/dashboard-connect.png +0 -0
- package/docs/images/hero-dark.png +0 -0
- package/docs/images/hero-light.png +0 -0
- package/docs/images/icons/ai.svg +4 -0
- package/docs/images/icons/auth.svg +1 -0
- package/docs/images/icons/database.svg +1 -0
- package/docs/images/icons/function.svg +1 -0
- package/docs/images/icons/storage.svg +1 -0
- package/docs/images/logos/nextjs.svg +4 -0
- package/docs/images/logos/nuxt.svg +4 -0
- package/docs/images/logos/react.svg +5 -0
- package/docs/images/logos/svelte.svg +4 -0
- package/docs/images/logos/vue.svg +5 -0
- package/docs/images/mcp-install.png +0 -0
- package/docs/images/onboarding-mcp.png +0 -0
- package/docs/insforge-instructions-sdk.md +55 -374
- package/docs/introduction.mdx +45 -0
- package/docs/logo/dark.svg +22 -0
- package/docs/logo/light.svg +20 -0
- package/docs/partnership.mdx +647 -0
- package/docs/quickstart.mdx +83 -0
- package/docs/showcase/2048-arena.png +0 -0
- package/docs/showcase/framegen-cloud.png +0 -0
- package/docs/showcase/line-connect-race.png +0 -0
- package/docs/showcase/moment-vibe.png +0 -0
- package/docs/showcase/national-flags.png +0 -0
- package/docs/showcase/pokemon-vibe.png +0 -0
- package/docs/showcase/pure-browse-buy.png +0 -0
- package/docs/showcase.mdx +52 -0
- package/docs/snippets/sdk-installation.mdx +22 -0
- package/docs/snippets/service-icons.mdx +27 -0
- package/eslint.config.js +10 -3
- package/frontend/package.json +10 -4
- package/frontend/src/App.tsx +13 -82
- package/frontend/src/assets/icons/connected.svg +3 -0
- package/frontend/src/assets/icons/loader.svg +9 -0
- package/frontend/src/assets/logos/apple.svg +4 -0
- package/frontend/src/assets/logos/discord.svg +1 -1
- package/frontend/src/assets/logos/facebook.svg +3 -0
- package/frontend/src/assets/logos/instagram.svg +2 -0
- package/frontend/src/assets/logos/linkedin.svg +3 -0
- package/frontend/src/assets/logos/microsoft.svg +1 -0
- package/frontend/src/assets/logos/spotify.svg +17 -0
- package/frontend/src/assets/logos/tiktok.svg +6 -0
- package/frontend/src/assets/logos/x.svg +3 -0
- package/frontend/src/components/Checkbox.tsx +27 -29
- package/frontend/src/components/CodeBlock.tsx +55 -2
- package/frontend/src/components/CodeEditor.tsx +92 -0
- package/frontend/src/components/ConfirmDialog.tsx +1 -1
- package/frontend/src/components/ConnectCTA.tsx +38 -0
- package/frontend/src/components/CopyButton.tsx +52 -15
- package/frontend/src/components/ErrorState.tsx +1 -2
- package/frontend/src/components/FeatureSidebar.tsx +6 -6
- package/frontend/src/components/FeatureSidebarItem.tsx +2 -2
- package/frontend/src/components/JsonHighlight.tsx +21 -9
- package/frontend/src/components/ProjectInfoModal.tsx +128 -0
- package/frontend/src/components/PromptDialog.tsx +1 -4
- package/frontend/src/components/SearchInput.tsx +1 -2
- package/frontend/src/components/Stepper.tsx +53 -0
- package/frontend/src/components/ThemeToggle.tsx +3 -3
- package/frontend/src/components/datagrid/DataGrid.tsx +25 -32
- package/frontend/src/components/datagrid/cell-editors/DateCellEditor.tsx +1 -2
- package/frontend/src/components/datagrid/cell-editors/JsonCellEditor.tsx +2 -4
- package/frontend/src/components/datagrid/index.ts +23 -0
- package/frontend/src/components/index.ts +23 -30
- package/frontend/src/components/layout/AppHeader.tsx +133 -92
- package/frontend/src/components/layout/AppSidebar.tsx +80 -170
- package/frontend/src/components/layout/Layout.tsx +12 -23
- package/frontend/src/components/layout/PrimaryMenu.tsx +187 -0
- package/frontend/src/components/layout/SecondaryMenu.tsx +70 -0
- package/frontend/src/components/layout/index.ts +5 -0
- package/frontend/src/components/radix/Tooltip.tsx +24 -13
- package/frontend/src/components/radix/index.ts +22 -0
- package/frontend/src/features/ai/components/AIConfigCard.tsx +129 -83
- package/frontend/src/features/ai/components/AIEmptyState.tsx +12 -7
- package/frontend/src/features/ai/components/ModalityFilterSidebar.tsx +101 -0
- package/frontend/src/features/ai/components/ModelSelectionDialog.tsx +135 -0
- package/frontend/src/features/ai/components/ModelSelectionGrid.tsx +51 -0
- package/frontend/src/features/ai/components/SystemPromptDialog.tsx +118 -0
- package/frontend/src/features/ai/components/index.ts +6 -0
- package/frontend/src/features/ai/helpers.ts +57 -71
- package/frontend/src/features/ai/hooks/useAIConfigs.ts +39 -113
- package/frontend/src/features/ai/hooks/useAIUsage.ts +0 -2
- package/frontend/src/features/ai/page/AIPage.tsx +67 -79
- package/frontend/src/features/ai/services/ai.service.ts +5 -5
- package/frontend/src/features/auth/components/AuthPreview.tsx +96 -0
- package/frontend/src/features/auth/components/OAuthConfigDialog.tsx +53 -30
- package/frontend/src/features/auth/components/UserFormDialog.tsx +13 -6
- package/frontend/src/features/auth/components/UsersDataGrid.tsx +44 -14
- package/frontend/src/features/auth/components/index.ts +5 -0
- package/frontend/src/features/auth/helpers.tsx +200 -0
- package/frontend/src/features/auth/hooks/useAnonToken.ts +30 -0
- package/frontend/src/features/auth/hooks/useAuthConfig.ts +48 -0
- package/frontend/src/features/auth/hooks/useOAuthConfig.ts +14 -10
- package/frontend/src/features/auth/hooks/useUsers.ts +43 -5
- package/frontend/src/features/auth/index.ts +3 -2
- package/frontend/src/features/auth/page/AuthMethodsPage.tsx +275 -0
- package/frontend/src/features/auth/page/ConfigurationPage.tsx +395 -0
- package/frontend/src/features/auth/page/UsersPage.tsx +285 -0
- package/frontend/src/features/auth/services/anonToken.service.ts +11 -0
- package/frontend/src/features/auth/services/config.service.ts +19 -0
- package/frontend/src/features/auth/services/{oauth.service.ts → oauth-config.service.ts} +4 -4
- package/frontend/src/features/auth/services/{auth.service.ts → user.service.ts} +7 -53
- package/frontend/src/features/dashboard/components/ConnectionSuccessBanner.tsx +35 -0
- package/frontend/src/features/dashboard/components/PromptCard.tsx +21 -0
- package/frontend/src/features/dashboard/components/PromptDialog.tsx +103 -0
- package/frontend/src/features/dashboard/components/StatsCard.tsx +50 -0
- package/frontend/src/features/dashboard/components/index.ts +4 -0
- package/frontend/src/features/dashboard/page/DashboardPage.tsx +187 -169
- package/frontend/src/features/dashboard/prompts/ai-chatbot.ts +13 -0
- package/frontend/src/features/dashboard/prompts/crm-system.ts +13 -0
- package/frontend/src/features/dashboard/prompts/ecommerce-platform.ts +12 -0
- package/frontend/src/features/dashboard/prompts/index.ts +31 -0
- package/frontend/src/features/dashboard/prompts/instagram-clone.ts +11 -0
- package/frontend/src/features/dashboard/prompts/notion-clone.ts +14 -0
- package/frontend/src/features/dashboard/prompts/reddit-clone.ts +12 -0
- package/frontend/src/features/database/components/DatabaseDataGrid.tsx +48 -17
- package/frontend/src/features/database/components/ForeignKeyCell.tsx +15 -34
- package/frontend/src/features/database/components/ForeignKeyPopover.tsx +19 -20
- package/frontend/src/features/database/components/LinkRecordModal.tsx +120 -125
- package/frontend/src/features/database/components/RecordFormDialog.tsx +22 -33
- package/frontend/src/features/database/components/RecordFormField.tsx +45 -47
- package/frontend/src/features/database/components/TableEmptyState.tsx +6 -5
- package/frontend/src/features/database/components/TableForm.tsx +28 -15
- package/frontend/src/features/database/components/TableFormColumn.tsx +2 -3
- package/frontend/src/features/database/components/TableSidebar.tsx +1 -1
- package/frontend/src/features/database/components/TablesEmptyState.tsx +48 -0
- package/frontend/src/features/database/components/TemplateCard.tsx +37 -0
- package/frontend/src/features/database/components/TemplatePreview.tsx +92 -0
- package/frontend/src/features/database/components/index.ts +19 -0
- package/frontend/src/features/database/constants.ts +28 -2
- package/frontend/src/features/database/contexts/SQLEditorContext.tsx +188 -0
- package/frontend/src/features/database/helpers.ts +2 -2
- package/frontend/src/features/database/hooks/useCSVImport.ts +29 -0
- package/frontend/src/features/database/hooks/useFullMetadata.ts +18 -0
- package/frontend/src/features/database/hooks/useRawSQL.ts +55 -0
- package/frontend/src/features/database/hooks/useRecords.ts +139 -0
- package/frontend/src/features/database/hooks/useTables.ts +131 -0
- package/frontend/src/features/database/index.ts +6 -1
- package/frontend/src/features/database/page/FunctionsPage.tsx +211 -0
- package/frontend/src/features/database/page/IndexesPage.tsx +240 -0
- package/frontend/src/features/database/page/PoliciesPage.tsx +248 -0
- package/frontend/src/features/database/page/SQLEditorPage.tsx +382 -0
- package/frontend/src/features/database/page/{DatabasePage.tsx → TablesPage.tsx} +186 -185
- package/frontend/src/features/database/page/TemplatesPage.tsx +39 -0
- package/frontend/src/features/database/page/TriggersPage.tsx +242 -0
- package/frontend/src/features/database/services/advance.service.ts +66 -0
- package/frontend/src/features/database/services/{database.service.ts → record.service.ts} +67 -64
- package/frontend/src/features/database/services/table.service.ts +64 -0
- package/frontend/src/features/database/templates/ai-chatbot.ts +402 -0
- package/frontend/src/features/database/templates/crm-system.ts +528 -0
- package/frontend/src/features/database/templates/ecommerce-platform.ts +553 -0
- package/frontend/src/features/database/templates/index.ts +34 -0
- package/frontend/src/features/database/templates/instagram-clone.ts +222 -0
- package/frontend/src/features/database/templates/notion-clone.ts +483 -0
- package/frontend/src/features/database/templates/reddit-clone.ts +526 -0
- package/frontend/src/features/functions/components/FunctionRow.tsx +2 -1
- package/frontend/src/features/functions/components/FunctionsSidebar.tsx +1 -1
- package/frontend/src/features/functions/components/SecretRow.tsx +1 -1
- package/frontend/src/features/functions/components/index.ts +5 -0
- package/frontend/src/features/functions/hooks/useFunctions.ts +4 -4
- package/frontend/src/features/{secrets → functions}/hooks/useSecrets.ts +5 -5
- package/frontend/src/features/functions/page/FunctionsPage.tsx +160 -17
- package/frontend/src/features/functions/{components/SecretsContent.tsx → page/SecretsPage.tsx} +8 -12
- package/frontend/src/features/functions/services/{functions.service.ts → function.service.ts} +2 -2
- package/frontend/src/features/{secrets/services/secrets.service.ts → functions/services/secret.service.ts} +2 -2
- package/frontend/src/features/login/hooks/usePartnerOrigin.ts +27 -0
- package/frontend/src/features/login/page/CloudLoginPage.tsx +79 -54
- package/frontend/src/features/login/page/LoginPage.tsx +16 -23
- package/frontend/src/features/login/services/partnership.service.ts +65 -0
- package/frontend/src/features/logs/components/LogsDataGrid.tsx +89 -0
- package/frontend/src/features/logs/components/SeverityBadge.tsx +18 -0
- package/frontend/src/features/logs/components/index.ts +2 -0
- package/frontend/src/features/logs/helpers.ts +24 -0
- package/frontend/src/features/logs/hooks/useAuditLogs.ts +4 -4
- package/frontend/src/features/logs/hooks/useLogSources.ts +137 -0
- package/frontend/src/features/logs/hooks/useLogs.ts +163 -0
- package/frontend/src/features/logs/hooks/useMcpUsage.ts +181 -0
- package/frontend/src/features/logs/index.ts +8 -2
- package/frontend/src/features/logs/page/AuditsPage.tsx +91 -38
- package/frontend/src/features/logs/page/LogsPage.tsx +152 -0
- package/frontend/src/features/logs/page/MCPLogsPage.tsx +84 -0
- package/frontend/src/features/logs/services/audit.service.ts +63 -0
- package/frontend/src/features/logs/services/log.service.ts +15 -110
- package/frontend/src/features/logs/services/usage.service.ts +31 -0
- package/frontend/src/features/onboard/components/McpConnectionStatus.tsx +68 -0
- package/frontend/src/features/onboard/components/OnboardingModal.tsx +267 -0
- package/frontend/src/features/onboard/components/VideoDemoModal.tsx +38 -0
- package/frontend/src/features/onboard/components/index.ts +4 -0
- package/frontend/src/features/onboard/components/mcp/CursorDeeplinkGenerator.tsx +2 -2
- package/frontend/src/features/onboard/components/mcp/{mcp-helper.tsx → helpers.tsx} +8 -8
- package/frontend/src/features/onboard/components/mcp/index.ts +2 -3
- package/frontend/src/features/onboard/index.ts +13 -3
- package/frontend/src/features/storage/components/BucketEmptyState.tsx +9 -6
- package/frontend/src/features/storage/components/BucketFormDialog.tsx +25 -41
- package/frontend/src/features/storage/components/FilePreviewDialog.tsx +20 -8
- package/frontend/src/features/storage/components/StorageDataGrid.tsx +4 -3
- package/frontend/src/features/storage/components/StorageManager.tsx +23 -34
- package/frontend/src/features/storage/components/index.ts +12 -0
- package/frontend/src/features/storage/hooks/useStorage.ts +208 -0
- package/frontend/src/features/storage/page/StoragePage.tsx +41 -115
- package/frontend/src/features/storage/services/storage.service.ts +22 -1
- package/frontend/src/features/visualizer/components/AuthNode.tsx +72 -56
- package/frontend/src/features/visualizer/components/BucketNode.tsx +4 -4
- package/frontend/src/features/visualizer/components/SchemaVisualizer.tsx +108 -80
- package/frontend/src/features/visualizer/components/TableNode.tsx +34 -41
- package/frontend/src/features/visualizer/components/VisualizerSkeleton.tsx +12 -4
- package/frontend/src/features/visualizer/page/VisualizerPage.tsx +33 -29
- package/frontend/src/index.css +1 -0
- package/frontend/src/lib/analytics/posthog.tsx +27 -0
- package/frontend/src/lib/contexts/AuthContext.tsx +38 -31
- package/frontend/src/lib/contexts/SocketContext.tsx +5 -6
- package/frontend/src/{features/metadata → lib}/hooks/useMetadata.ts +1 -1
- package/frontend/src/lib/hooks/useToast.tsx +6 -2
- package/frontend/src/lib/routing/AppRoutes.tsx +84 -0
- package/frontend/src/lib/routing/RequireAuth.tsx +27 -0
- package/frontend/src/lib/utils/cloudMessaging.ts +20 -0
- package/frontend/src/lib/utils/menuItems.ts +183 -0
- package/frontend/src/lib/utils/{validation-schemas.ts → schemaValidations.ts} +10 -5
- package/frontend/src/lib/utils/utils.ts +19 -1
- package/frontend/src/vite-env.d.ts +1 -0
- package/frontend/vite.config.ts +5 -3
- package/functions/server.ts +28 -3
- package/functions/worker-template.js +15 -4
- package/i18n/README.ar.md +130 -0
- package/i18n/README.de.md +130 -0
- package/i18n/README.es.md +154 -0
- package/i18n/README.fr.md +134 -0
- package/i18n/README.hi.md +129 -0
- package/i18n/README.ja.md +174 -0
- package/i18n/README.ko.md +137 -0
- package/i18n/README.pt-BR.md +131 -0
- package/i18n/README.ru.md +129 -0
- package/i18n/README.zh-CN.md +133 -0
- package/openapi/ai.yaml +31 -4
- package/openapi/auth.yaml +827 -146
- package/package.json +16 -7
- package/shared-schemas/package.json +1 -1
- package/shared-schemas/src/ai-api.schema.ts +34 -58
- package/shared-schemas/src/ai.schema.ts +5 -0
- package/shared-schemas/src/auth-api.schema.ts +154 -8
- package/shared-schemas/src/auth.schema.ts +42 -6
- package/shared-schemas/src/cloud-events.schema.ts +57 -0
- package/shared-schemas/src/database-api.schema.ts +3 -3
- package/shared-schemas/src/database.schema.ts +1 -1
- package/shared-schemas/src/index.ts +1 -0
- package/shared-schemas/src/logs-api.schema.ts +7 -1
- package/shared-schemas/src/logs.schema.ts +26 -0
- package/shared-schemas/src/metadata.schema.ts +9 -4
- package/test-gemini.sh +35 -0
- package/test-usage-admin.sh +57 -0
- package/test-usage.sh +50 -0
- package/zeabur/README.md +13 -0
- package/zeabur/template.yml +1032 -0
- package/.github/workflows/deploy-aws.yml +0 -130
- package/backend/src/api/routes/agent.ts +0 -29
- package/backend/src/api/routes/auth.oauth.ts +0 -482
- package/backend/src/api/routes/auth.ts +0 -386
- package/backend/src/api/routes/docs.ts +0 -66
- package/backend/src/api/routes/functions.ts +0 -183
- package/backend/src/api/routes/openapi.ts +0 -82
- package/backend/src/api/routes/usage.ts +0 -96
- package/backend/src/core/ai/client.ts +0 -242
- package/backend/src/core/ai/model.ts +0 -117
- package/backend/src/core/auth/auth.ts +0 -781
- package/backend/src/core/database/table.ts +0 -772
- package/backend/src/core/documentation/agent.ts +0 -689
- package/backend/src/core/documentation/openapi.ts +0 -856
- package/backend/src/core/logs/analytics.ts +0 -76
- package/backend/src/core/logs/providers/localdb.provider.ts +0 -246
- package/backend/src/core/storage/storage.ts +0 -923
- package/backend/src/utils/cloud-token.ts +0 -39
- package/backend/src/utils/helpers.ts +0 -49
- package/backend/src/utils/uuid.ts +0 -9
- package/backend/tests/manual/test-better-auth.sh +0 -303
- package/docker-init/db/logs.sql +0 -9
- package/frontend/README.md +0 -112
- package/frontend/src/components/datagrid/index.tsx +0 -20
- package/frontend/src/components/layout/CloudLayout.tsx +0 -95
- package/frontend/src/features/ai/components/AIConfigDialog.tsx +0 -76
- package/frontend/src/features/ai/components/AIConfigForm.tsx +0 -222
- package/frontend/src/features/ai/components/fields/ModalityField.tsx +0 -87
- package/frontend/src/features/ai/components/fields/ModelSelectionField.tsx +0 -134
- package/frontend/src/features/ai/components/fields/SystemPromptField.tsx +0 -33
- package/frontend/src/features/auth/components/AddOAuthDialog.tsx +0 -106
- package/frontend/src/features/auth/components/AuthMethodTab.tsx +0 -238
- package/frontend/src/features/auth/components/UsersTab.tsx +0 -114
- package/frontend/src/features/auth/page/AuthenticationPage.tsx +0 -169
- package/frontend/src/features/database/hooks/UseLinkModal.tsx +0 -78
- package/frontend/src/features/functions/components/FunctionViewer.tsx +0 -46
- package/frontend/src/features/functions/components/FunctionsContent.tsx +0 -88
- package/frontend/src/features/login/components/AuthErrorBoundary.tsx +0 -87
- package/frontend/src/features/login/components/PrivateRoute.tsx +0 -24
- package/frontend/src/features/logs/components/AnalyticsLogsTable.tsx +0 -313
- package/frontend/src/features/logs/components/LogsTable.tsx +0 -199
- package/frontend/src/features/logs/page/AnalyticsLogsPage.tsx +0 -530
- package/frontend/src/features/metadata/index.ts +0 -0
- package/frontend/src/features/metadata/page/MetadataPage.tsx +0 -136
- package/frontend/src/features/onboard/components/CompletionCard.tsx +0 -41
- package/frontend/src/features/onboard/components/OnboardButton.tsx +0 -84
- package/frontend/src/features/onboard/components/StepContent.tsx +0 -91
- package/frontend/src/features/onboard/components/TestConnectionStep.tsx +0 -53
- package/frontend/src/features/onboard/components/mcp/McpInstallation.tsx +0 -144
- package/frontend/src/features/onboard/page/OnBoardPage.tsx +0 -104
- package/frontend/src/features/onboard/types.ts +0 -8
- package/frontend/src/lib/contexts/OnboardStepContext.tsx +0 -68
- package/frontend/src/lib/hooks/useOnboardingCompletion.ts +0 -29
- /package/backend/src/api/{middleware → middlewares}/error.ts +0 -0
- /package/backend/src/api/{middleware → middlewares}/upload.ts +0 -0
- /package/backend/{migrations → src/infra/database/migrations}/000_create-base-tables.sql +0 -0
- /package/backend/{migrations → src/infra/database/migrations}/001_create-helper-functions.sql +0 -0
- /package/backend/{migrations → src/infra/database/migrations}/002_rename-auth-tables.sql +0 -0
- /package/backend/{migrations → src/infra/database/migrations}/003_create-users-table.sql +0 -0
- /package/backend/{migrations → src/infra/database/migrations}/004_add-reload-postgrest-func.sql +0 -0
- /package/backend/{migrations → src/infra/database/migrations}/005_enable-project-admin-modify-users.sql +0 -0
- /package/backend/{migrations → src/infra/database/migrations}/006_modify-ai-usage-table.sql +0 -0
- /package/backend/{migrations → src/infra/database/migrations}/007_drop-metadata-table.sql +0 -0
- /package/backend/{migrations → src/infra/database/migrations}/008_add-system-tables.sql +0 -0
- /package/backend/{migrations → src/infra/database/migrations}/009_add-function-secrets.sql +0 -0
- /package/backend/{migrations → src/infra/database/migrations}/010_modify-ai-config-modalities.sql +0 -0
- /package/backend/{migrations → src/infra/database/migrations}/011_refactor-secrets-table.sql +0 -0
- /package/backend/{migrations → src/infra/database/migrations}/012_add-storage-uploaded-by.sql +0 -0
- /package/frontend/src/{features/metadata → lib}/services/metadata.service.ts +0 -0
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
import { Router, Request, Response, NextFunction } from 'express';
|
|
2
|
+
import { AuthService } from '@/services/auth/auth.service.js';
|
|
3
|
+
import { OAuthConfigService } from '@/services/auth/oauth-config.service.js';
|
|
4
|
+
import { AuditService } from '@/services/logs/audit.service.js';
|
|
5
|
+
import { AppError } from '@/api/middlewares/error.js';
|
|
6
|
+
import { ERROR_CODES } from '@/types/error-constants.js';
|
|
7
|
+
import { successResponse } from '@/utils/response.js';
|
|
8
|
+
import { AuthRequest, verifyAdmin } from '@/api/middlewares/auth.js';
|
|
9
|
+
import logger from '@/utils/logger.js';
|
|
10
|
+
import jwt from 'jsonwebtoken';
|
|
11
|
+
import {
|
|
12
|
+
createOAuthConfigRequestSchema,
|
|
13
|
+
updateOAuthConfigRequestSchema,
|
|
14
|
+
type ListOAuthConfigsResponse,
|
|
15
|
+
oAuthProvidersSchema,
|
|
16
|
+
} from '@insforge/shared-schemas';
|
|
17
|
+
import { isOAuthSharedKeysAvailable } from '@/utils/environment.js';
|
|
18
|
+
|
|
19
|
+
const router = Router();
|
|
20
|
+
const authService = AuthService.getInstance();
|
|
21
|
+
const oAuthConfigService = OAuthConfigService.getInstance();
|
|
22
|
+
const auditService = AuditService.getInstance();
|
|
23
|
+
|
|
24
|
+
// Helper function to validate JWT_SECRET
|
|
25
|
+
const validateJwtSecret = (): string => {
|
|
26
|
+
const jwtSecret = process.env.JWT_SECRET;
|
|
27
|
+
if (!jwtSecret || jwtSecret.trim() === '') {
|
|
28
|
+
throw new AppError(
|
|
29
|
+
'JWT_SECRET environment variable is not configured.',
|
|
30
|
+
500,
|
|
31
|
+
ERROR_CODES.INTERNAL_ERROR
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
return jwtSecret;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// OAuth Configuration Management Routes (must come before wildcard routes)
|
|
38
|
+
// GET /api/auth/oauth/configs - List all OAuth configurations (admin only)
|
|
39
|
+
router.get('/configs', verifyAdmin, async (req: AuthRequest, res: Response, next: NextFunction) => {
|
|
40
|
+
try {
|
|
41
|
+
const configs = await oAuthConfigService.getAllConfigs();
|
|
42
|
+
const response: ListOAuthConfigsResponse = {
|
|
43
|
+
data: configs,
|
|
44
|
+
count: configs.length,
|
|
45
|
+
};
|
|
46
|
+
successResponse(res, response);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
logger.error('Failed to list OAuth configurations', { error });
|
|
49
|
+
next(error);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// GET /api/auth/oauth/:provider/config - Get specific OAuth configuration (admin only)
|
|
54
|
+
router.get(
|
|
55
|
+
'/:provider/config',
|
|
56
|
+
verifyAdmin,
|
|
57
|
+
async (req: AuthRequest, res: Response, next: NextFunction) => {
|
|
58
|
+
try {
|
|
59
|
+
const { provider } = req.params;
|
|
60
|
+
const config = await oAuthConfigService.getConfigByProvider(provider);
|
|
61
|
+
const clientSecret = await oAuthConfigService.getClientSecretByProvider(provider);
|
|
62
|
+
|
|
63
|
+
if (!config) {
|
|
64
|
+
throw new AppError(
|
|
65
|
+
`OAuth configuration for ${provider} not found`,
|
|
66
|
+
404,
|
|
67
|
+
ERROR_CODES.NOT_FOUND
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
successResponse(res, {
|
|
72
|
+
...config,
|
|
73
|
+
clientSecret: clientSecret || undefined,
|
|
74
|
+
});
|
|
75
|
+
} catch (error) {
|
|
76
|
+
logger.error('Failed to get OAuth config by provider', {
|
|
77
|
+
provider: req.params.provider,
|
|
78
|
+
error,
|
|
79
|
+
});
|
|
80
|
+
next(error);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// POST /api/auth/oauth/configs - Create new OAuth configuration (admin only)
|
|
86
|
+
router.post(
|
|
87
|
+
'/configs',
|
|
88
|
+
verifyAdmin,
|
|
89
|
+
async (req: AuthRequest, res: Response, next: NextFunction) => {
|
|
90
|
+
try {
|
|
91
|
+
const validationResult = createOAuthConfigRequestSchema.safeParse(req.body);
|
|
92
|
+
if (!validationResult.success) {
|
|
93
|
+
throw new AppError(
|
|
94
|
+
validationResult.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', '),
|
|
95
|
+
400,
|
|
96
|
+
ERROR_CODES.INVALID_INPUT
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const input = validationResult.data;
|
|
101
|
+
|
|
102
|
+
// Check if using shared keys when not allowed
|
|
103
|
+
if (input.useSharedKey && !isOAuthSharedKeysAvailable()) {
|
|
104
|
+
throw new AppError(
|
|
105
|
+
'Shared OAuth keys are not enabled in this environment',
|
|
106
|
+
400,
|
|
107
|
+
ERROR_CODES.AUTH_OAUTH_CONFIG_ERROR
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const config = await oAuthConfigService.createConfig(input);
|
|
112
|
+
|
|
113
|
+
await auditService.log({
|
|
114
|
+
actor: req.user?.email || 'api-key',
|
|
115
|
+
action: 'CREATE_OAUTH_CONFIG',
|
|
116
|
+
module: 'AUTH',
|
|
117
|
+
details: {
|
|
118
|
+
provider: input.provider,
|
|
119
|
+
useSharedKey: input.useSharedKey || false,
|
|
120
|
+
},
|
|
121
|
+
ip_address: req.ip,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
successResponse(res, config);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
logger.error('Failed to create OAuth configuration', { error });
|
|
127
|
+
next(error);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
// PUT /api/auth/oauth/:provider/config - Update OAuth configuration (admin only)
|
|
133
|
+
router.put(
|
|
134
|
+
'/:provider/config',
|
|
135
|
+
verifyAdmin,
|
|
136
|
+
async (req: AuthRequest, res: Response, next: NextFunction) => {
|
|
137
|
+
try {
|
|
138
|
+
const provider = req.params.provider;
|
|
139
|
+
if (!provider || provider.length === 0 || provider.length > 50) {
|
|
140
|
+
throw new AppError('Invalid provider name', 400, ERROR_CODES.INVALID_INPUT);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const validationResult = updateOAuthConfigRequestSchema.safeParse(req.body);
|
|
144
|
+
if (!validationResult.success) {
|
|
145
|
+
throw new AppError(
|
|
146
|
+
validationResult.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', '),
|
|
147
|
+
400,
|
|
148
|
+
ERROR_CODES.INVALID_INPUT
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const input = validationResult.data;
|
|
153
|
+
|
|
154
|
+
// Check if using shared keys when not allowed
|
|
155
|
+
if (input.useSharedKey && !isOAuthSharedKeysAvailable()) {
|
|
156
|
+
throw new AppError(
|
|
157
|
+
'Shared OAuth keys are not enabled in this environment',
|
|
158
|
+
400,
|
|
159
|
+
ERROR_CODES.AUTH_OAUTH_CONFIG_ERROR
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const config = await oAuthConfigService.updateConfig(provider, input);
|
|
164
|
+
|
|
165
|
+
await auditService.log({
|
|
166
|
+
actor: req.user?.email || 'api-key',
|
|
167
|
+
action: 'UPDATE_OAUTH_CONFIG',
|
|
168
|
+
module: 'AUTH',
|
|
169
|
+
details: {
|
|
170
|
+
provider,
|
|
171
|
+
updatedFields: Object.keys(input),
|
|
172
|
+
},
|
|
173
|
+
ip_address: req.ip,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
successResponse(res, config);
|
|
177
|
+
} catch (error) {
|
|
178
|
+
logger.error('Failed to update OAuth configuration', {
|
|
179
|
+
error,
|
|
180
|
+
provider: req.params.provider,
|
|
181
|
+
});
|
|
182
|
+
next(error);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
// DELETE /api/auth/oauth/:provider/config - Delete OAuth configuration (admin only)
|
|
188
|
+
router.delete(
|
|
189
|
+
'/:provider/config',
|
|
190
|
+
verifyAdmin,
|
|
191
|
+
async (req: AuthRequest, res: Response, next: NextFunction) => {
|
|
192
|
+
try {
|
|
193
|
+
const provider = req.params.provider;
|
|
194
|
+
if (!provider || provider.length === 0 || provider.length > 50) {
|
|
195
|
+
throw new AppError('Invalid provider name', 400, ERROR_CODES.INVALID_INPUT);
|
|
196
|
+
}
|
|
197
|
+
const deleted = await oAuthConfigService.deleteConfig(provider);
|
|
198
|
+
|
|
199
|
+
if (!deleted) {
|
|
200
|
+
throw new AppError(
|
|
201
|
+
`OAuth configuration for ${provider} not found`,
|
|
202
|
+
404,
|
|
203
|
+
ERROR_CODES.NOT_FOUND
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
await auditService.log({
|
|
208
|
+
actor: req.user?.email || 'api-key',
|
|
209
|
+
action: 'DELETE_OAUTH_CONFIG',
|
|
210
|
+
module: 'AUTH',
|
|
211
|
+
details: { provider },
|
|
212
|
+
ip_address: req.ip,
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
successResponse(res, {
|
|
216
|
+
success: true,
|
|
217
|
+
message: `OAuth configuration for ${provider} deleted successfully`,
|
|
218
|
+
});
|
|
219
|
+
} catch (error) {
|
|
220
|
+
logger.error('Failed to delete OAuth configuration', {
|
|
221
|
+
error,
|
|
222
|
+
provider: req.params.provider,
|
|
223
|
+
});
|
|
224
|
+
next(error);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
// OAuth Flow Routes
|
|
230
|
+
// GET /api/auth/oauth/:provider - Initialize OAuth flow for any supported provider
|
|
231
|
+
router.get('/:provider', async (req: Request, res: Response, next: NextFunction) => {
|
|
232
|
+
try {
|
|
233
|
+
const { provider } = req.params;
|
|
234
|
+
const { redirect_uri } = req.query;
|
|
235
|
+
|
|
236
|
+
// Validate provider using OAuthProvidersSchema
|
|
237
|
+
const providerValidation = oAuthProvidersSchema.safeParse(provider);
|
|
238
|
+
if (!providerValidation.success) {
|
|
239
|
+
throw new AppError(
|
|
240
|
+
`Unsupported OAuth provider: ${provider}. Supported providers: ${oAuthProvidersSchema.options.join(', ')}`,
|
|
241
|
+
400,
|
|
242
|
+
ERROR_CODES.INVALID_INPUT
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const validatedProvider = providerValidation.data;
|
|
247
|
+
|
|
248
|
+
if (!redirect_uri) {
|
|
249
|
+
throw new AppError('Redirect URI is required', 400, ERROR_CODES.INVALID_INPUT);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const jwtPayload = {
|
|
253
|
+
provider: validatedProvider,
|
|
254
|
+
redirectUri: redirect_uri ? (redirect_uri as string) : undefined,
|
|
255
|
+
createdAt: Date.now(),
|
|
256
|
+
};
|
|
257
|
+
const jwtSecret = validateJwtSecret();
|
|
258
|
+
const state = jwt.sign(jwtPayload, jwtSecret, {
|
|
259
|
+
algorithm: 'HS256',
|
|
260
|
+
expiresIn: '1h', // Set expiration time for the state token
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
const authUrl = await authService.generateOAuthUrl(validatedProvider, state);
|
|
264
|
+
|
|
265
|
+
successResponse(res, { authUrl });
|
|
266
|
+
} catch (error) {
|
|
267
|
+
logger.error(`${req.params.provider} OAuth error`, { error });
|
|
268
|
+
|
|
269
|
+
// If it's already an AppError, pass it through
|
|
270
|
+
if (error instanceof AppError) {
|
|
271
|
+
next(error);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// For other errors, return the generic OAuth configuration error
|
|
276
|
+
next(
|
|
277
|
+
new AppError(
|
|
278
|
+
`${req.params.provider} OAuth is not properly configured. Please check your oauth configurations.`,
|
|
279
|
+
500,
|
|
280
|
+
ERROR_CODES.AUTH_OAUTH_CONFIG_ERROR
|
|
281
|
+
)
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// GET /api/auth/oauth/shared/callback/:state - Shared callback for OAuth providers
|
|
287
|
+
router.get('/shared/callback/:state', async (req: Request, res: Response, next: NextFunction) => {
|
|
288
|
+
try {
|
|
289
|
+
const { state } = req.params;
|
|
290
|
+
const { success, error, payload } = req.query;
|
|
291
|
+
|
|
292
|
+
if (!state) {
|
|
293
|
+
logger.warn('Shared OAuth callback called without state parameter');
|
|
294
|
+
throw new AppError('State parameter is required', 400, ERROR_CODES.INVALID_INPUT);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
let redirectUri: string;
|
|
298
|
+
let provider: string;
|
|
299
|
+
try {
|
|
300
|
+
const jwtSecret = validateJwtSecret();
|
|
301
|
+
const decodedState = jwt.verify(state, jwtSecret) as {
|
|
302
|
+
provider: string;
|
|
303
|
+
redirectUri: string;
|
|
304
|
+
};
|
|
305
|
+
redirectUri = decodedState.redirectUri || '';
|
|
306
|
+
provider = decodedState.provider || '';
|
|
307
|
+
} catch {
|
|
308
|
+
logger.warn('Invalid state parameter', { state });
|
|
309
|
+
throw new AppError('Invalid state parameter', 400, ERROR_CODES.INVALID_INPUT);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Validate provider using OAuthProvidersSchema
|
|
313
|
+
const providerValidation = oAuthProvidersSchema.safeParse(provider);
|
|
314
|
+
if (!providerValidation.success) {
|
|
315
|
+
logger.warn('Invalid provider in state', { provider });
|
|
316
|
+
throw new AppError(
|
|
317
|
+
`Invalid provider in state: ${provider}. Supported providers: ${oAuthProvidersSchema.options.join(', ')}`,
|
|
318
|
+
400,
|
|
319
|
+
ERROR_CODES.INVALID_INPUT
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
const validatedProvider = providerValidation.data;
|
|
323
|
+
if (!redirectUri) {
|
|
324
|
+
throw new AppError('redirectUri is required', 400, ERROR_CODES.INVALID_INPUT);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (success !== 'true') {
|
|
328
|
+
const errorMessage = error || 'OAuth Authentication Failed';
|
|
329
|
+
logger.warn('Shared OAuth callback failed', { error: errorMessage, provider });
|
|
330
|
+
return res.redirect(`${redirectUri}/?error=${encodeURIComponent(String(errorMessage))}`);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (!payload) {
|
|
334
|
+
throw new AppError('No payload provided in callback', 400, ERROR_CODES.INVALID_INPUT);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const payloadData = JSON.parse(
|
|
338
|
+
Buffer.from(payload as string, 'base64').toString('utf8')
|
|
339
|
+
) as Record<string, unknown>;
|
|
340
|
+
|
|
341
|
+
// Handle shared callback - transforms payload and creates/finds user
|
|
342
|
+
const result = await authService.handleSharedCallback(validatedProvider, payloadData);
|
|
343
|
+
|
|
344
|
+
const params = new URLSearchParams();
|
|
345
|
+
params.set('access_token', result?.accessToken ?? '');
|
|
346
|
+
params.set('user_id', result?.user?.id ?? '');
|
|
347
|
+
params.set('email', result?.user?.email ?? '');
|
|
348
|
+
params.set('name', result?.user?.name ?? '');
|
|
349
|
+
|
|
350
|
+
res.redirect(`${redirectUri}?${params.toString()}`);
|
|
351
|
+
} catch (error) {
|
|
352
|
+
logger.error('Shared OAuth callback error', { error });
|
|
353
|
+
next(error);
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
// GET /api/auth/oauth/:provider/callback - OAuth provider callback
|
|
358
|
+
router.get('/:provider/callback', async (req: Request, res: Response, next: NextFunction) => {
|
|
359
|
+
try {
|
|
360
|
+
const { provider } = req.params;
|
|
361
|
+
const { code, state, token } = req.query;
|
|
362
|
+
|
|
363
|
+
if (!state) {
|
|
364
|
+
logger.warn('OAuth callback called without state parameter');
|
|
365
|
+
throw new AppError('State parameter is required', 400, ERROR_CODES.INVALID_INPUT);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Decode redirectUri from state (needed for both success and error paths)
|
|
369
|
+
let redirectUri: string;
|
|
370
|
+
|
|
371
|
+
try {
|
|
372
|
+
const jwtSecret = validateJwtSecret();
|
|
373
|
+
const stateData = jwt.verify(state as string, jwtSecret) as {
|
|
374
|
+
provider: string;
|
|
375
|
+
redirectUri: string;
|
|
376
|
+
};
|
|
377
|
+
redirectUri = stateData.redirectUri || '';
|
|
378
|
+
} catch {
|
|
379
|
+
// Invalid state
|
|
380
|
+
logger.warn('Invalid state in provider callback', { state });
|
|
381
|
+
throw new AppError('Invalid state parameter', 400, ERROR_CODES.INVALID_INPUT);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (!redirectUri) {
|
|
385
|
+
throw new AppError('redirectUri is required', 400, ERROR_CODES.INVALID_INPUT);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
try {
|
|
389
|
+
// Validate provider using OAuthProvidersSchema
|
|
390
|
+
const providerValidation = oAuthProvidersSchema.safeParse(provider);
|
|
391
|
+
if (!providerValidation.success) {
|
|
392
|
+
throw new AppError(
|
|
393
|
+
`Unsupported OAuth provider: ${provider}. Supported providers: ${oAuthProvidersSchema.options.join(', ')}`,
|
|
394
|
+
400,
|
|
395
|
+
ERROR_CODES.INVALID_INPUT
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const validatedProvider = providerValidation.data;
|
|
400
|
+
|
|
401
|
+
const result = await authService.handleOAuthCallback(validatedProvider, {
|
|
402
|
+
code: code as string | undefined,
|
|
403
|
+
token: token as string | undefined,
|
|
404
|
+
state: state as string | undefined,
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
// Construct redirect URL with query parameters
|
|
408
|
+
const params = new URLSearchParams();
|
|
409
|
+
params.set('access_token', result?.accessToken ?? '');
|
|
410
|
+
params.set('user_id', result?.user?.id ?? '');
|
|
411
|
+
params.set('email', result?.user?.email ?? '');
|
|
412
|
+
params.set('name', result?.user?.name ?? '');
|
|
413
|
+
|
|
414
|
+
const finalRedirectUri = `${redirectUri}?${params.toString()}`;
|
|
415
|
+
|
|
416
|
+
logger.info('OAuth callback successful, redirecting with token', {
|
|
417
|
+
redirectUri: finalRedirectUri,
|
|
418
|
+
hasAccessToken: !!result?.accessToken,
|
|
419
|
+
hasUserId: !!result?.user?.id,
|
|
420
|
+
provider: validatedProvider,
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
return res.redirect(finalRedirectUri);
|
|
424
|
+
} catch (error) {
|
|
425
|
+
logger.error('OAuth callback error', {
|
|
426
|
+
error: error instanceof Error ? error.message : error,
|
|
427
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
428
|
+
provider: req.params.provider,
|
|
429
|
+
hasCode: !!req.query.code,
|
|
430
|
+
hasState: !!req.query.state,
|
|
431
|
+
hasToken: !!req.query.token,
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
const errorMessage = error instanceof Error ? error.message : 'OAuth Authentication Failed';
|
|
435
|
+
|
|
436
|
+
// Redirect with error in URL parameters
|
|
437
|
+
const params = new URLSearchParams();
|
|
438
|
+
params.set('error', errorMessage);
|
|
439
|
+
|
|
440
|
+
return res.redirect(`${redirectUri}?${params.toString()}`);
|
|
441
|
+
}
|
|
442
|
+
} catch (error) {
|
|
443
|
+
logger.error('OAuth callback error', { error });
|
|
444
|
+
next(error);
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
export default router;
|
|
@@ -1,27 +1,87 @@
|
|
|
1
|
-
import { Router, Response } from 'express';
|
|
2
|
-
import { DatabaseAdvanceService } from '@/
|
|
3
|
-
import { AuditService } from '@/
|
|
4
|
-
import { verifyAdmin, AuthRequest } from '@/api/
|
|
5
|
-
import { AppError } from '@/api/
|
|
1
|
+
import { Router, Response, NextFunction } from 'express';
|
|
2
|
+
import { DatabaseAdvanceService } from '@/services/database/database-advance.service.js';
|
|
3
|
+
import { AuditService } from '@/services/logs/audit.service.js';
|
|
4
|
+
import { verifyAdmin, AuthRequest } from '@/api/middlewares/auth.js';
|
|
5
|
+
import { AppError } from '@/api/middlewares/error.js';
|
|
6
6
|
import { ERROR_CODES } from '@/types/error-constants.js';
|
|
7
|
-
import { upload, handleUploadError } from '@/api/
|
|
7
|
+
import { upload, handleUploadError } from '@/api/middlewares/upload.js';
|
|
8
8
|
import {
|
|
9
9
|
rawSQLRequestSchema,
|
|
10
10
|
exportRequestSchema,
|
|
11
11
|
importRequestSchema,
|
|
12
12
|
bulkUpsertRequestSchema,
|
|
13
13
|
} from '@insforge/shared-schemas';
|
|
14
|
-
import logger from '@/utils/logger';
|
|
14
|
+
import logger from '@/utils/logger.js';
|
|
15
|
+
import { SocketManager } from '@/infra/socket/socket.manager.js';
|
|
16
|
+
import { DataUpdateResourceType, ServerEvents } from '@/types/socket.js';
|
|
17
|
+
import { successResponse } from '@/utils/response.js';
|
|
15
18
|
|
|
16
19
|
const router = Router();
|
|
17
|
-
const dbAdvanceService =
|
|
20
|
+
const dbAdvanceService = DatabaseAdvanceService.getInstance();
|
|
18
21
|
const auditService = AuditService.getInstance();
|
|
19
22
|
|
|
20
23
|
/**
|
|
21
|
-
* Execute raw SQL query
|
|
24
|
+
* Execute raw SQL query with relaxed sanitization (Power User Mode)
|
|
25
|
+
* POST /api/database/advance/rawsql/unrestricted
|
|
26
|
+
*
|
|
27
|
+
* ⚠️ This endpoint has relaxed restrictions compared to /rawsql
|
|
28
|
+
* - Allows SELECT and INSERT into system tables and users table
|
|
29
|
+
*/
|
|
30
|
+
router.post(
|
|
31
|
+
'/rawsql/unrestricted',
|
|
32
|
+
verifyAdmin,
|
|
33
|
+
async (req: AuthRequest, res: Response, next: NextFunction) => {
|
|
34
|
+
try {
|
|
35
|
+
// Validate request body
|
|
36
|
+
const validation = rawSQLRequestSchema.safeParse(req.body);
|
|
37
|
+
if (!validation.success) {
|
|
38
|
+
throw new AppError(
|
|
39
|
+
validation.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', '),
|
|
40
|
+
400,
|
|
41
|
+
ERROR_CODES.INVALID_INPUT
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const { query, params = [] } = validation.data;
|
|
46
|
+
|
|
47
|
+
// Sanitize query with relaxed mode
|
|
48
|
+
const sanitizedQuery = dbAdvanceService.sanitizeQuery(query, 'relaxed');
|
|
49
|
+
|
|
50
|
+
// Execute SQL
|
|
51
|
+
const response = await dbAdvanceService.executeRawSQL(sanitizedQuery, params);
|
|
52
|
+
|
|
53
|
+
// Log audit for relaxed raw SQL execution
|
|
54
|
+
await auditService.log({
|
|
55
|
+
actor: req.user?.email || 'api-key',
|
|
56
|
+
action: 'EXECUTE_RAW_SQL_RELAXED',
|
|
57
|
+
module: 'DATABASE',
|
|
58
|
+
details: {
|
|
59
|
+
query: query.substring(0, 300), // Limit query length in audit log
|
|
60
|
+
paramCount: params.length,
|
|
61
|
+
rowsAffected: response.rowCount,
|
|
62
|
+
mode: 'relaxed',
|
|
63
|
+
},
|
|
64
|
+
ip_address: req.ip,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const socket = SocketManager.getInstance();
|
|
68
|
+
socket.broadcastToRoom('role:project_admin', ServerEvents.DATA_UPDATE, {
|
|
69
|
+
resource: DataUpdateResourceType.DATABASE,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
successResponse(res, response);
|
|
73
|
+
} catch (error: unknown) {
|
|
74
|
+
logger.warn('Relaxed raw SQL execution error:', error);
|
|
75
|
+
next(error);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Execute raw SQL query with strict sanitization
|
|
22
82
|
* POST /api/database/advance/rawsql
|
|
23
83
|
*/
|
|
24
|
-
router.post('/rawsql', verifyAdmin, async (req: AuthRequest, res: Response) => {
|
|
84
|
+
router.post('/rawsql', verifyAdmin, async (req: AuthRequest, res: Response, next: NextFunction) => {
|
|
25
85
|
try {
|
|
26
86
|
// Validate request body
|
|
27
87
|
const validation = rawSQLRequestSchema.safeParse(req.body);
|
|
@@ -34,9 +94,14 @@ router.post('/rawsql', verifyAdmin, async (req: AuthRequest, res: Response) => {
|
|
|
34
94
|
}
|
|
35
95
|
|
|
36
96
|
const { query, params = [] } = validation.data;
|
|
37
|
-
const response = await dbAdvanceService.executeRawSQL(query, params);
|
|
38
97
|
|
|
39
|
-
//
|
|
98
|
+
// Sanitize query with strict mode
|
|
99
|
+
const sanitizedQuery = dbAdvanceService.sanitizeQuery(query, 'strict');
|
|
100
|
+
|
|
101
|
+
// Execute SQL
|
|
102
|
+
const response = await dbAdvanceService.executeRawSQL(sanitizedQuery, params);
|
|
103
|
+
|
|
104
|
+
// Log audit for strict raw SQL execution
|
|
40
105
|
await auditService.log({
|
|
41
106
|
actor: req.user?.email || 'api-key',
|
|
42
107
|
action: 'EXECUTE_RAW_SQL',
|
|
@@ -45,27 +110,20 @@ router.post('/rawsql', verifyAdmin, async (req: AuthRequest, res: Response) => {
|
|
|
45
110
|
query: query.substring(0, 300), // Limit query length in audit log
|
|
46
111
|
paramCount: params.length,
|
|
47
112
|
rowsAffected: response.rowCount,
|
|
113
|
+
mode: 'strict',
|
|
48
114
|
},
|
|
49
115
|
ip_address: req.ip,
|
|
50
116
|
});
|
|
51
117
|
|
|
52
|
-
|
|
118
|
+
const socket = SocketManager.getInstance();
|
|
119
|
+
socket.broadcastToRoom('role:project_admin', ServerEvents.DATA_UPDATE, {
|
|
120
|
+
resource: DataUpdateResourceType.DATABASE,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
successResponse(res, response);
|
|
53
124
|
} catch (error: unknown) {
|
|
54
125
|
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
|
-
}
|
|
126
|
+
next(error);
|
|
69
127
|
}
|
|
70
128
|
});
|
|
71
129
|
|
|
@@ -73,7 +131,7 @@ router.post('/rawsql', verifyAdmin, async (req: AuthRequest, res: Response) => {
|
|
|
73
131
|
* Export database data
|
|
74
132
|
* POST /api/database/advance/export
|
|
75
133
|
*/
|
|
76
|
-
router.post('/export', verifyAdmin, async (req: AuthRequest, res: Response) => {
|
|
134
|
+
router.post('/export', verifyAdmin, async (req: AuthRequest, res: Response, next: NextFunction) => {
|
|
77
135
|
try {
|
|
78
136
|
// Validate request body
|
|
79
137
|
const validation = exportRequestSchema.safeParse(req.body);
|
|
@@ -115,14 +173,10 @@ router.post('/export', verifyAdmin, async (req: AuthRequest, res: Response) => {
|
|
|
115
173
|
ip_address: req.ip,
|
|
116
174
|
});
|
|
117
175
|
|
|
118
|
-
res
|
|
176
|
+
successResponse(res, response);
|
|
119
177
|
} catch (error: unknown) {
|
|
120
178
|
logger.warn('Database export error:', error);
|
|
121
|
-
|
|
122
|
-
error: 'EXPORT_ERROR',
|
|
123
|
-
message: error instanceof Error ? error.message : 'Failed to export database',
|
|
124
|
-
statusCode: 500,
|
|
125
|
-
});
|
|
179
|
+
next(error);
|
|
126
180
|
}
|
|
127
181
|
});
|
|
128
182
|
|
|
@@ -139,7 +193,7 @@ router.post(
|
|
|
139
193
|
verifyAdmin,
|
|
140
194
|
upload.single('file'),
|
|
141
195
|
handleUploadError,
|
|
142
|
-
async (req: AuthRequest, res: Response) => {
|
|
196
|
+
async (req: AuthRequest, res: Response, next: NextFunction) => {
|
|
143
197
|
try {
|
|
144
198
|
if (!req.file) {
|
|
145
199
|
throw new AppError('File is required', 400, ERROR_CODES.INVALID_INPUT);
|
|
@@ -180,23 +234,19 @@ router.post(
|
|
|
180
234
|
ip_address: req.ip,
|
|
181
235
|
});
|
|
182
236
|
|
|
183
|
-
|
|
237
|
+
const socket = SocketManager.getInstance();
|
|
238
|
+
socket.broadcastToRoom('role:project_admin', ServerEvents.DATA_UPDATE, {
|
|
239
|
+
resource: DataUpdateResourceType.RECORDS,
|
|
240
|
+
data: {
|
|
241
|
+
tableName: table,
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
successResponse(res, response);
|
|
184
246
|
} catch (error: unknown) {
|
|
185
247
|
logger.warn('Bulk upsert error:', error);
|
|
186
248
|
|
|
187
|
-
|
|
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
|
-
}
|
|
249
|
+
next(error);
|
|
200
250
|
}
|
|
201
251
|
}
|
|
202
252
|
);
|
|
@@ -211,7 +261,7 @@ router.post(
|
|
|
211
261
|
verifyAdmin,
|
|
212
262
|
upload.single('file'),
|
|
213
263
|
handleUploadError,
|
|
214
|
-
async (req: AuthRequest, res: Response) => {
|
|
264
|
+
async (req: AuthRequest, res: Response, next: NextFunction) => {
|
|
215
265
|
try {
|
|
216
266
|
// Validate request body
|
|
217
267
|
const validation = importRequestSchema.safeParse(req.body);
|
|
@@ -251,23 +301,15 @@ router.post(
|
|
|
251
301
|
ip_address: req.ip,
|
|
252
302
|
});
|
|
253
303
|
|
|
254
|
-
|
|
304
|
+
const socket = SocketManager.getInstance();
|
|
305
|
+
socket.broadcastToRoom('role:project_admin', ServerEvents.DATA_UPDATE, {
|
|
306
|
+
resource: DataUpdateResourceType.DATABASE,
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
successResponse(res, response);
|
|
255
310
|
} catch (error: unknown) {
|
|
256
311
|
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
|
-
}
|
|
312
|
+
next(error);
|
|
271
313
|
}
|
|
272
314
|
}
|
|
273
315
|
);
|