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.
- 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 -780
- 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,313 @@
|
|
|
1
|
+
import {
|
|
2
|
+
S3Client,
|
|
3
|
+
PutObjectCommand,
|
|
4
|
+
GetObjectCommand,
|
|
5
|
+
DeleteObjectCommand,
|
|
6
|
+
ListObjectsV2Command,
|
|
7
|
+
DeleteObjectsCommand,
|
|
8
|
+
HeadObjectCommand,
|
|
9
|
+
} from '@aws-sdk/client-s3';
|
|
10
|
+
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
11
|
+
import { createPresignedPost } from '@aws-sdk/s3-presigned-post';
|
|
12
|
+
import { getSignedUrl as getCloudFrontSignedUrl } from '@aws-sdk/cloudfront-signer';
|
|
13
|
+
import { UploadStrategyResponse, DownloadStrategyResponse } from '@insforge/shared-schemas';
|
|
14
|
+
import { StorageProvider } from './base.provider.js';
|
|
15
|
+
import logger from '@/utils/logger.js';
|
|
16
|
+
|
|
17
|
+
const ONE_HOUR_IN_SECONDS = 3600;
|
|
18
|
+
const DEFAULT_MAX_UPLOAD_SIZE_BYTES = 10485760;
|
|
19
|
+
const SEVEN_DAYS_IN_SECONDS = 604800;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* S3 storage implementation
|
|
23
|
+
*/
|
|
24
|
+
export class S3StorageProvider implements StorageProvider {
|
|
25
|
+
private s3Client: S3Client | null = null;
|
|
26
|
+
|
|
27
|
+
constructor(
|
|
28
|
+
private s3Bucket: string,
|
|
29
|
+
private appKey: string,
|
|
30
|
+
private region: string = 'us-east-2'
|
|
31
|
+
) {}
|
|
32
|
+
|
|
33
|
+
initialize(): void {
|
|
34
|
+
// Use explicit AWS credentials if provided (local dev or self hosting)
|
|
35
|
+
// Otherwise, use IAM role credentials (EC2 production)
|
|
36
|
+
const s3Config: {
|
|
37
|
+
region: string;
|
|
38
|
+
credentials?: { accessKeyId: string; secretAccessKey: string };
|
|
39
|
+
} = {
|
|
40
|
+
region: this.region,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
if (process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY) {
|
|
44
|
+
s3Config.credentials = {
|
|
45
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
46
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
this.s3Client = new S3Client(s3Config);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private getS3Key(bucket: string, key: string): string {
|
|
54
|
+
return `${this.appKey}/${bucket}/${key}`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async putObject(bucket: string, key: string, file: Express.Multer.File): Promise<void> {
|
|
58
|
+
if (!this.s3Client) {
|
|
59
|
+
throw new Error('S3 client not initialized');
|
|
60
|
+
}
|
|
61
|
+
const s3Key = this.getS3Key(bucket, key);
|
|
62
|
+
|
|
63
|
+
const command = new PutObjectCommand({
|
|
64
|
+
Bucket: this.s3Bucket,
|
|
65
|
+
Key: s3Key,
|
|
66
|
+
Body: file.buffer,
|
|
67
|
+
ContentType: file.mimetype || 'application/octet-stream',
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
await this.s3Client.send(command);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
logger.error('S3 Upload error', {
|
|
74
|
+
error: error instanceof Error ? error.message : String(error),
|
|
75
|
+
bucket,
|
|
76
|
+
key: s3Key,
|
|
77
|
+
});
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async getObject(bucket: string, key: string): Promise<Buffer | null> {
|
|
83
|
+
if (!this.s3Client) {
|
|
84
|
+
throw new Error('S3 client not initialized');
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
const command = new GetObjectCommand({
|
|
88
|
+
Bucket: this.s3Bucket,
|
|
89
|
+
Key: this.getS3Key(bucket, key),
|
|
90
|
+
});
|
|
91
|
+
const response = await this.s3Client.send(command);
|
|
92
|
+
const chunks: Uint8Array[] = [];
|
|
93
|
+
// Type assertion for readable stream
|
|
94
|
+
const body = response.Body as AsyncIterable<Uint8Array>;
|
|
95
|
+
for await (const chunk of body) {
|
|
96
|
+
chunks.push(chunk);
|
|
97
|
+
}
|
|
98
|
+
return Buffer.concat(chunks);
|
|
99
|
+
} catch {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async deleteObject(bucket: string, key: string): Promise<void> {
|
|
105
|
+
if (!this.s3Client) {
|
|
106
|
+
throw new Error('S3 client not initialized');
|
|
107
|
+
}
|
|
108
|
+
const command = new DeleteObjectCommand({
|
|
109
|
+
Bucket: this.s3Bucket,
|
|
110
|
+
Key: this.getS3Key(bucket, key),
|
|
111
|
+
});
|
|
112
|
+
await this.s3Client.send(command);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async createBucket(_bucket: string): Promise<void> {
|
|
116
|
+
// In S3 with multi-tenant, we don't create actual buckets
|
|
117
|
+
// We just use folders under the app key
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async deleteBucket(bucket: string): Promise<void> {
|
|
121
|
+
if (!this.s3Client) {
|
|
122
|
+
throw new Error('S3 client not initialized');
|
|
123
|
+
}
|
|
124
|
+
// List and delete all objects in the "bucket" (folder)
|
|
125
|
+
const prefix = `${this.appKey}/${bucket}/`;
|
|
126
|
+
|
|
127
|
+
let continuationToken: string | undefined;
|
|
128
|
+
do {
|
|
129
|
+
const listCommand = new ListObjectsV2Command({
|
|
130
|
+
Bucket: this.s3Bucket,
|
|
131
|
+
Prefix: prefix,
|
|
132
|
+
ContinuationToken: continuationToken,
|
|
133
|
+
});
|
|
134
|
+
const listResponse = await this.s3Client.send(listCommand);
|
|
135
|
+
|
|
136
|
+
if (listResponse.Contents && listResponse.Contents.length) {
|
|
137
|
+
const deleteCommand = new DeleteObjectsCommand({
|
|
138
|
+
Bucket: this.s3Bucket,
|
|
139
|
+
Delete: {
|
|
140
|
+
Objects: listResponse.Contents.filter((obj) => obj.Key !== undefined).map((obj) => ({
|
|
141
|
+
Key: obj.Key as string,
|
|
142
|
+
})),
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
await this.s3Client.send(deleteCommand);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
continuationToken = listResponse.NextContinuationToken;
|
|
149
|
+
} while (continuationToken);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// S3 supports presigned URLs
|
|
153
|
+
supportsPresignedUrls(): boolean {
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async getUploadStrategy(
|
|
158
|
+
bucket: string,
|
|
159
|
+
key: string,
|
|
160
|
+
metadata: { contentType?: string; size?: number }
|
|
161
|
+
): Promise<UploadStrategyResponse> {
|
|
162
|
+
if (!this.s3Client) {
|
|
163
|
+
throw new Error('S3 client not initialized');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const s3Key = this.getS3Key(bucket, key);
|
|
167
|
+
const expiresIn = ONE_HOUR_IN_SECONDS; // 1 hour
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
// Generate presigned POST URL for multipart form upload
|
|
171
|
+
const { url, fields } = await createPresignedPost(this.s3Client, {
|
|
172
|
+
Bucket: this.s3Bucket,
|
|
173
|
+
Key: s3Key,
|
|
174
|
+
Conditions: [
|
|
175
|
+
['content-length-range', 0, metadata.size || DEFAULT_MAX_UPLOAD_SIZE_BYTES], // Max 10MB by default
|
|
176
|
+
],
|
|
177
|
+
Expires: expiresIn,
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
method: 'presigned',
|
|
182
|
+
uploadUrl: url,
|
|
183
|
+
fields,
|
|
184
|
+
key,
|
|
185
|
+
confirmRequired: true,
|
|
186
|
+
confirmUrl: `/api/storage/buckets/${bucket}/objects/${encodeURIComponent(key)}/confirm-upload`,
|
|
187
|
+
expiresAt: new Date(Date.now() + expiresIn * 1000),
|
|
188
|
+
};
|
|
189
|
+
} catch (error) {
|
|
190
|
+
logger.error('Failed to generate presigned upload URL', {
|
|
191
|
+
error: error instanceof Error ? error.message : String(error),
|
|
192
|
+
bucket,
|
|
193
|
+
key,
|
|
194
|
+
});
|
|
195
|
+
throw error;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async getDownloadStrategy(
|
|
200
|
+
bucket: string,
|
|
201
|
+
key: string,
|
|
202
|
+
expiresIn: number = ONE_HOUR_IN_SECONDS,
|
|
203
|
+
isPublic: boolean = false
|
|
204
|
+
): Promise<DownloadStrategyResponse> {
|
|
205
|
+
if (!this.s3Client) {
|
|
206
|
+
throw new Error('S3 client not initialized');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const s3Key = this.getS3Key(bucket, key);
|
|
210
|
+
// Public files get longer expiration (7 days), private files get shorter (1 hour default)
|
|
211
|
+
const actualExpiresIn = isPublic ? SEVEN_DAYS_IN_SECONDS : expiresIn; // 604800 = 7 days
|
|
212
|
+
const cloudFrontUrl = process.env.AWS_CLOUDFRONT_URL;
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
// If CloudFront URL is configured, use CloudFront for downloads
|
|
216
|
+
if (cloudFrontUrl) {
|
|
217
|
+
const cloudFrontKeyPairId = process.env.AWS_CLOUDFRONT_KEY_PAIR_ID;
|
|
218
|
+
const cloudFrontPrivateKey = process.env.AWS_CLOUDFRONT_PRIVATE_KEY;
|
|
219
|
+
|
|
220
|
+
if (!cloudFrontKeyPairId || !cloudFrontPrivateKey) {
|
|
221
|
+
logger.warn(
|
|
222
|
+
'CloudFront URL configured but missing key pair ID or private key, falling back to S3'
|
|
223
|
+
);
|
|
224
|
+
} else {
|
|
225
|
+
try {
|
|
226
|
+
// Generate CloudFront signed URL
|
|
227
|
+
// IMPORTANT: URL-encode the S3 key to match what CloudFront receives
|
|
228
|
+
// This ensures the signature matches for files with spaces, parentheses, etc.
|
|
229
|
+
const encodedS3Key = s3Key
|
|
230
|
+
.split('/')
|
|
231
|
+
.map((segment) => encodeURIComponent(segment))
|
|
232
|
+
.join('/');
|
|
233
|
+
const cloudFrontObjectUrl = `${cloudFrontUrl.replace(/\/$/, '')}/${encodedS3Key}`;
|
|
234
|
+
|
|
235
|
+
// Convert escaped newlines to actual newlines in the private key
|
|
236
|
+
const formattedPrivateKey = cloudFrontPrivateKey.replace(/\\n/g, '\n');
|
|
237
|
+
|
|
238
|
+
// dateLessThan can be string | number | Date - using Date object directly
|
|
239
|
+
const dateLessThan = new Date(Date.now() + actualExpiresIn * 1000);
|
|
240
|
+
|
|
241
|
+
const signedUrl = getCloudFrontSignedUrl({
|
|
242
|
+
url: cloudFrontObjectUrl,
|
|
243
|
+
keyPairId: cloudFrontKeyPairId,
|
|
244
|
+
privateKey: formattedPrivateKey,
|
|
245
|
+
dateLessThan,
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
logger.info('CloudFront signed URL generated successfully.');
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
method: 'presigned',
|
|
252
|
+
url: signedUrl,
|
|
253
|
+
expiresAt: dateLessThan,
|
|
254
|
+
};
|
|
255
|
+
} catch (cfError) {
|
|
256
|
+
logger.error('Failed to generate CloudFront signed URL, falling back to S3', {
|
|
257
|
+
error: cfError instanceof Error ? cfError.message : String(cfError),
|
|
258
|
+
bucket,
|
|
259
|
+
key,
|
|
260
|
+
});
|
|
261
|
+
// Fall through to S3 signed URL generation
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Note: isPublic here refers to the application-level setting,
|
|
267
|
+
// not the actual S3 bucket policy. In a multi-tenant setup,
|
|
268
|
+
// we're using a single S3 bucket with folder-based isolation,
|
|
269
|
+
// so we always use presigned URLs for security.
|
|
270
|
+
// The "public" setting only affects the URL expiration time.
|
|
271
|
+
|
|
272
|
+
// Always generate presigned URL for security in multi-tenant environment
|
|
273
|
+
const command = new GetObjectCommand({
|
|
274
|
+
Bucket: this.s3Bucket,
|
|
275
|
+
Key: s3Key,
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
const url = await getSignedUrl(this.s3Client, command, { expiresIn: actualExpiresIn });
|
|
279
|
+
|
|
280
|
+
return {
|
|
281
|
+
method: 'presigned',
|
|
282
|
+
url,
|
|
283
|
+
expiresAt: new Date(Date.now() + actualExpiresIn * 1000),
|
|
284
|
+
};
|
|
285
|
+
} catch (error) {
|
|
286
|
+
logger.error('Failed to generate download URL', {
|
|
287
|
+
error: error instanceof Error ? error.message : String(error),
|
|
288
|
+
bucket,
|
|
289
|
+
key,
|
|
290
|
+
});
|
|
291
|
+
throw error;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
async verifyObjectExists(bucket: string, key: string): Promise<boolean> {
|
|
296
|
+
if (!this.s3Client) {
|
|
297
|
+
throw new Error('S3 client not initialized');
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const s3Key = this.getS3Key(bucket, key);
|
|
301
|
+
|
|
302
|
+
try {
|
|
303
|
+
const command = new HeadObjectCommand({
|
|
304
|
+
Bucket: this.s3Bucket,
|
|
305
|
+
Key: s3Key,
|
|
306
|
+
});
|
|
307
|
+
await this.s3Client.send(command);
|
|
308
|
+
return true;
|
|
309
|
+
} catch {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
package/backend/src/server.ts
CHANGED
|
@@ -5,29 +5,25 @@ import dotenv from 'dotenv';
|
|
|
5
5
|
import path from 'path';
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
7
7
|
import fs from 'fs';
|
|
8
|
-
import authRouter from '@/api/routes/auth.js';
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
import
|
|
17
|
-
import
|
|
18
|
-
import {
|
|
19
|
-
import { openAPIRouter } from '@/api/routes/openapi.js';
|
|
20
|
-
import { agentDocsRouter } from '@/api/routes/agent.js';
|
|
21
|
-
import { aiRouter } from '@/api/routes/ai.js';
|
|
22
|
-
import { errorMiddleware } from '@/api/middleware/error.js';
|
|
8
|
+
import authRouter from '@/api/routes/auth/index.routes.js';
|
|
9
|
+
import databaseRouter from '@/api/routes/database/index.routes.js';
|
|
10
|
+
import { storageRouter } from '@/api/routes/storage/index.routes.js';
|
|
11
|
+
import { metadataRouter } from '@/api/routes/metadata/index.routes.js';
|
|
12
|
+
import { logsRouter } from '@/api/routes/logs/index.routes.js';
|
|
13
|
+
import { docsRouter } from '@/api/routes/docs/index.routes.js';
|
|
14
|
+
import functionsRouter from '@/api/routes/functions/index.routes.js';
|
|
15
|
+
import secretsRouter from '@/api/routes/secrets/index.routes.js';
|
|
16
|
+
import { usageRouter } from '@/api/routes/usage/index.routes.js';
|
|
17
|
+
import { aiRouter } from '@/api/routes/ai/index.routes.js';
|
|
18
|
+
import { errorMiddleware } from '@/api/middlewares/error.js';
|
|
23
19
|
import fetch, { HeadersInit } from 'node-fetch';
|
|
24
|
-
import { DatabaseManager } from '@/
|
|
25
|
-
import {
|
|
26
|
-
import { StorageService } from '@/
|
|
27
|
-
import {
|
|
20
|
+
import { DatabaseManager } from '@/infra/database/database.manager.js';
|
|
21
|
+
import { LogService } from '@/services/logs/log.service.js';
|
|
22
|
+
import { StorageService } from '@/services/storage/storage.service.js';
|
|
23
|
+
import { SocketManager } from '@/infra/socket/socket.manager.js';
|
|
28
24
|
import { seedBackend } from '@/utils/seed.js';
|
|
29
25
|
import logger from '@/utils/logger.js';
|
|
30
|
-
import { isProduction } from './utils/environment';
|
|
26
|
+
import { isProduction } from './utils/environment.js';
|
|
31
27
|
import packageJson from '../../package.json';
|
|
32
28
|
|
|
33
29
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -51,11 +47,9 @@ export async function createApp() {
|
|
|
51
47
|
const storageService = StorageService.getInstance();
|
|
52
48
|
await storageService.initialize(); // create data/storage
|
|
53
49
|
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
//
|
|
57
|
-
const analyticsManager = AnalyticsManager.getInstance();
|
|
58
|
-
await analyticsManager.initialize(); // connect to _insforge database
|
|
50
|
+
// Initialize logs service
|
|
51
|
+
const logService = LogService.getInstance();
|
|
52
|
+
await logService.initialize(); // connect to CloudWatch
|
|
59
53
|
|
|
60
54
|
const app = express();
|
|
61
55
|
|
|
@@ -87,25 +81,47 @@ export async function createApp() {
|
|
|
87
81
|
let responseSize = 0;
|
|
88
82
|
|
|
89
83
|
// Override send method
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
84
|
+
res.send = function (
|
|
85
|
+
data: string | Buffer | Record<string, unknown> | unknown[] | number | boolean
|
|
86
|
+
) {
|
|
87
|
+
if (data !== undefined && data !== null) {
|
|
88
|
+
if (typeof data === 'string') {
|
|
89
|
+
responseSize = Buffer.byteLength(data);
|
|
90
|
+
} else if (Buffer.isBuffer(data)) {
|
|
91
|
+
responseSize = data.length;
|
|
92
|
+
} else if (typeof data === 'number' || typeof data === 'boolean') {
|
|
93
|
+
responseSize = Buffer.byteLength(String(data));
|
|
94
|
+
} else {
|
|
95
|
+
try {
|
|
96
|
+
responseSize = Buffer.byteLength(JSON.stringify(data));
|
|
97
|
+
} catch {
|
|
98
|
+
// Handle circular references or unstringifiable objects
|
|
99
|
+
responseSize = 0;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
94
102
|
}
|
|
95
103
|
return originalSend.call(this, data);
|
|
96
104
|
};
|
|
105
|
+
|
|
97
106
|
// Override json method
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
107
|
+
res.json = function (
|
|
108
|
+
data: Record<string, unknown> | unknown[] | string | number | boolean | null
|
|
109
|
+
) {
|
|
110
|
+
if (data !== undefined) {
|
|
111
|
+
try {
|
|
112
|
+
responseSize = Buffer.byteLength(JSON.stringify(data));
|
|
113
|
+
} catch {
|
|
114
|
+
// Handle circular references or unstringifiable objects
|
|
115
|
+
responseSize = 0;
|
|
116
|
+
}
|
|
102
117
|
}
|
|
103
118
|
return originalJson.call(this, data);
|
|
104
119
|
};
|
|
120
|
+
|
|
105
121
|
// Log after response is finished
|
|
106
122
|
res.on('finish', () => {
|
|
107
|
-
// Skip logging for
|
|
108
|
-
if (req.path.includes('/
|
|
123
|
+
// Skip logging for logs endpoints to avoid infinite loops
|
|
124
|
+
if (req.path.includes('/logs/')) {
|
|
109
125
|
return;
|
|
110
126
|
}
|
|
111
127
|
|
|
@@ -121,6 +137,7 @@ export async function createApp() {
|
|
|
121
137
|
timestamp: new Date().toISOString(),
|
|
122
138
|
});
|
|
123
139
|
});
|
|
140
|
+
|
|
124
141
|
next();
|
|
125
142
|
});
|
|
126
143
|
|
|
@@ -142,11 +159,9 @@ export async function createApp() {
|
|
|
142
159
|
});
|
|
143
160
|
});
|
|
144
161
|
|
|
145
|
-
// Mount
|
|
162
|
+
// Mount all routes
|
|
146
163
|
apiRouter.use('/auth', authRouter);
|
|
147
|
-
apiRouter.use('/database
|
|
148
|
-
apiRouter.use('/database/records', databaseRecordsRouter);
|
|
149
|
-
apiRouter.use('/database/advance', databaseAdvanceRouter);
|
|
164
|
+
apiRouter.use('/database', databaseRouter);
|
|
150
165
|
apiRouter.use('/storage', storageRouter);
|
|
151
166
|
apiRouter.use('/metadata', metadataRouter);
|
|
152
167
|
apiRouter.use('/logs', logsRouter);
|
|
@@ -154,37 +169,11 @@ export async function createApp() {
|
|
|
154
169
|
apiRouter.use('/functions', functionsRouter);
|
|
155
170
|
apiRouter.use('/secrets', secretsRouter);
|
|
156
171
|
apiRouter.use('/usage', usageRouter);
|
|
157
|
-
apiRouter.use('/openapi', openAPIRouter);
|
|
158
|
-
apiRouter.use('/agent-docs', agentDocsRouter);
|
|
159
172
|
apiRouter.use('/ai', aiRouter);
|
|
160
173
|
|
|
161
174
|
// Mount all API routes under /api prefix
|
|
162
175
|
app.use('/api', apiRouter);
|
|
163
176
|
|
|
164
|
-
// Add direct OpenAPI route at /openapi
|
|
165
|
-
app.get('/openapi', async (_req: Request, res: Response) => {
|
|
166
|
-
try {
|
|
167
|
-
const { OpenAPIService } = await import('@/core/documentation/openapi.js');
|
|
168
|
-
const openAPIService = OpenAPIService.getInstance();
|
|
169
|
-
const openAPIDocument = await openAPIService.generateOpenAPIDocument();
|
|
170
|
-
res.json(openAPIDocument);
|
|
171
|
-
} catch {
|
|
172
|
-
res.status(500).json({ error: 'Failed to generate OpenAPI document' });
|
|
173
|
-
}
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
// Add direct AI agent documentation route at /agent-docs
|
|
177
|
-
app.get('/agent-docs', async (_req: Request, res: Response) => {
|
|
178
|
-
try {
|
|
179
|
-
const { AgentAPIDocService } = await import('@/core/documentation/agent.js');
|
|
180
|
-
const agentAPIDocService = AgentAPIDocService.getInstance();
|
|
181
|
-
const agentDocs = await agentAPIDocService.generateAgentDocumentation();
|
|
182
|
-
res.json(agentDocs);
|
|
183
|
-
} catch {
|
|
184
|
-
res.status(500).json({ error: 'Failed to generate agent API documentation' });
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
|
|
188
177
|
// Proxy function execution to Deno runtime
|
|
189
178
|
app.all('/functions/:slug', async (req: Request, res: Response) => {
|
|
190
179
|
try {
|
|
@@ -224,19 +213,26 @@ export async function createApp() {
|
|
|
224
213
|
}
|
|
225
214
|
});
|
|
226
215
|
|
|
227
|
-
//
|
|
228
|
-
const
|
|
216
|
+
// Serve auth app
|
|
217
|
+
const authAppPath = path.join(__dirname, 'auth');
|
|
218
|
+
if (fs.existsSync(authAppPath)) {
|
|
219
|
+
app.use('/auth', express.static(authAppPath));
|
|
220
|
+
app.get('/auth*', (_req: Request, res: Response) => {
|
|
221
|
+
res.sendFile(path.join(authAppPath, 'index.html'));
|
|
222
|
+
});
|
|
223
|
+
} else if (!isProduction()) {
|
|
224
|
+
const authAppUrl = process.env.AUTH_APP_URL || 'http://localhost:7132';
|
|
225
|
+
logger.info('Auth app not built, proxying to development server', { authAppUrl });
|
|
226
|
+
}
|
|
229
227
|
|
|
230
|
-
//
|
|
228
|
+
// Serve main frontend if it exists
|
|
229
|
+
const frontendPath = path.join(__dirname, 'frontend');
|
|
231
230
|
if (fs.existsSync(frontendPath)) {
|
|
231
|
+
app.use(express.static(frontendPath, { index: false }));
|
|
232
232
|
// Catch all handler for SPA routes
|
|
233
|
-
app.get('/cloud*', (_req: Request, res: Response) => {
|
|
234
|
-
res.sendFile(path.join(frontendPath, 'index.html'));
|
|
235
|
-
});
|
|
236
|
-
app.get('/dashboard*', (_req: Request, res: Response) => {
|
|
233
|
+
app.get(['/cloud*', '/dashboard*'], (_req: Request, res: Response) => {
|
|
237
234
|
res.sendFile(path.join(frontendPath, 'index.html'));
|
|
238
235
|
});
|
|
239
|
-
app.use(express.static(frontendPath));
|
|
240
236
|
} else {
|
|
241
237
|
// Catch-all for 404 errors - Traditional REST format
|
|
242
238
|
app.use('*', (req: Request, res: Response) => {
|
|
@@ -266,7 +262,7 @@ async function initializeServer() {
|
|
|
266
262
|
});
|
|
267
263
|
|
|
268
264
|
// Initialize Socket.IO service
|
|
269
|
-
const socketService =
|
|
265
|
+
const socketService = SocketManager.getInstance();
|
|
270
266
|
socketService.initialize(server);
|
|
271
267
|
} catch (error) {
|
|
272
268
|
logger.error('Failed to initialize server', {
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import { Pool } from 'pg';
|
|
2
|
-
import { DatabaseManager } from '@/
|
|
2
|
+
import { DatabaseManager } from '@/infra/database/database.manager.js';
|
|
3
3
|
import logger from '@/utils/logger.js';
|
|
4
4
|
import { AIConfigurationSchema, AIConfigurationWithUsageSchema } from '@insforge/shared-schemas';
|
|
5
5
|
|
|
6
6
|
export class AIConfigService {
|
|
7
|
+
private static instance: AIConfigService;
|
|
7
8
|
private pool: Pool | null = null;
|
|
8
9
|
|
|
10
|
+
private constructor() {}
|
|
11
|
+
|
|
12
|
+
public static getInstance(): AIConfigService {
|
|
13
|
+
if (!AIConfigService.instance) {
|
|
14
|
+
AIConfigService.instance = new AIConfigService();
|
|
15
|
+
}
|
|
16
|
+
return AIConfigService.instance;
|
|
17
|
+
}
|
|
18
|
+
|
|
9
19
|
private getPool(): Pool {
|
|
10
20
|
if (!this.pool) {
|
|
11
21
|
this.pool = DatabaseManager.getInstance().getPool();
|
|
@@ -20,9 +30,8 @@ export class AIConfigService {
|
|
|
20
30
|
modelId: string,
|
|
21
31
|
systemPrompt?: string
|
|
22
32
|
): Promise<{ id: string }> {
|
|
23
|
-
const client = await this.getPool().connect();
|
|
24
33
|
try {
|
|
25
|
-
const result = await
|
|
34
|
+
const result = await this.getPool().query(
|
|
26
35
|
`INSERT INTO _ai_configs (input_modality, output_modality, provider, model_id, system_prompt)
|
|
27
36
|
VALUES ($1, $2, $3, $4, $5)
|
|
28
37
|
RETURNING id`,
|
|
@@ -34,17 +43,14 @@ export class AIConfigService {
|
|
|
34
43
|
} catch (error) {
|
|
35
44
|
logger.error('Failed to create AI configuration', { error });
|
|
36
45
|
throw new Error('Failed to create AI configuration');
|
|
37
|
-
} finally {
|
|
38
|
-
client.release();
|
|
39
46
|
}
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
async findAll(): Promise<AIConfigurationWithUsageSchema[]> {
|
|
43
|
-
const client = await this.getPool().connect();
|
|
44
50
|
try {
|
|
45
51
|
// Use a single query with aggregation to get configs with usage stats
|
|
46
|
-
const result = await
|
|
47
|
-
`SELECT
|
|
52
|
+
const result = await this.getPool().query(
|
|
53
|
+
`SELECT
|
|
48
54
|
c.id,
|
|
49
55
|
c.input_modality as "inputModality",
|
|
50
56
|
c.output_modality as "outputModality",
|
|
@@ -63,7 +69,7 @@ export class AIConfigService {
|
|
|
63
69
|
);
|
|
64
70
|
|
|
65
71
|
return result.rows.map((row) => ({
|
|
66
|
-
id: row.id,
|
|
72
|
+
id: row.id, // UUID
|
|
67
73
|
inputModality: row.inputModality,
|
|
68
74
|
outputModality: row.outputModality,
|
|
69
75
|
provider: row.provider,
|
|
@@ -80,16 +86,13 @@ export class AIConfigService {
|
|
|
80
86
|
} catch (error) {
|
|
81
87
|
logger.error('Failed to fetch AI configurations with usage', { error });
|
|
82
88
|
throw new Error('Failed to fetch AI configurations');
|
|
83
|
-
} finally {
|
|
84
|
-
client.release();
|
|
85
89
|
}
|
|
86
90
|
}
|
|
87
91
|
|
|
88
92
|
async update(id: string, systemPrompt: string | null): Promise<boolean> {
|
|
89
|
-
const client = await this.getPool().connect();
|
|
90
93
|
try {
|
|
91
|
-
const result = await
|
|
92
|
-
`UPDATE _ai_configs
|
|
94
|
+
const result = await this.getPool().query(
|
|
95
|
+
`UPDATE _ai_configs
|
|
93
96
|
SET system_prompt = $1, updated_at = NOW()
|
|
94
97
|
WHERE id = $2`,
|
|
95
98
|
[systemPrompt, id]
|
|
@@ -103,15 +106,12 @@ export class AIConfigService {
|
|
|
103
106
|
} catch (error) {
|
|
104
107
|
logger.error('Failed to update AI configuration', { error, id });
|
|
105
108
|
throw new Error('Failed to update AI configuration');
|
|
106
|
-
} finally {
|
|
107
|
-
client.release();
|
|
108
109
|
}
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
async delete(id: string): Promise<boolean> {
|
|
112
|
-
const client = await this.getPool().connect();
|
|
113
113
|
try {
|
|
114
|
-
const result = await
|
|
114
|
+
const result = await this.getPool().query('DELETE FROM _ai_configs WHERE id = $1', [id]);
|
|
115
115
|
|
|
116
116
|
const success = (result.rowCount ?? 0) > 0;
|
|
117
117
|
if (success) {
|
|
@@ -121,15 +121,12 @@ export class AIConfigService {
|
|
|
121
121
|
} catch (error) {
|
|
122
122
|
logger.error('Failed to delete AI configuration', { error, id });
|
|
123
123
|
throw new Error('Failed to delete AI configuration');
|
|
124
|
-
} finally {
|
|
125
|
-
client.release();
|
|
126
124
|
}
|
|
127
125
|
}
|
|
128
126
|
|
|
129
127
|
async findByModelId(modelId: string): Promise<AIConfigurationSchema | null> {
|
|
130
|
-
const client = await this.getPool().connect();
|
|
131
128
|
try {
|
|
132
|
-
const result = await
|
|
129
|
+
const result = await this.getPool().query(
|
|
133
130
|
`SELECT id, input_modality as "inputModality", output_modality as "outputModality", provider, model_id as "modelId", system_prompt as "systemPrompt", created_at, updated_at
|
|
134
131
|
FROM _ai_configs
|
|
135
132
|
WHERE model_id = $1`,
|
|
@@ -155,8 +152,6 @@ export class AIConfigService {
|
|
|
155
152
|
modelId,
|
|
156
153
|
});
|
|
157
154
|
throw new Error('Failed to fetch AI configuration');
|
|
158
|
-
} finally {
|
|
159
|
-
client.release();
|
|
160
155
|
}
|
|
161
156
|
}
|
|
162
157
|
|