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,41 @@
|
|
|
1
|
+
import { EmailTemplate } from '@/types/email.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Email provider interface
|
|
5
|
+
* Defines the contract that all email providers must implement
|
|
6
|
+
*/
|
|
7
|
+
export interface EmailProvider {
|
|
8
|
+
/**
|
|
9
|
+
* Initialize the email provider (optional)
|
|
10
|
+
*/
|
|
11
|
+
initialize?(): void | Promise<void>;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Send email using predefined template
|
|
15
|
+
* @param email - Recipient email address
|
|
16
|
+
* @param name - Recipient name
|
|
17
|
+
* @param template - Template type
|
|
18
|
+
* @param variables - Variables to use in the email template
|
|
19
|
+
*/
|
|
20
|
+
sendWithTemplate(
|
|
21
|
+
email: string,
|
|
22
|
+
name: string,
|
|
23
|
+
template: EmailTemplate,
|
|
24
|
+
variables?: Record<string, string>
|
|
25
|
+
): Promise<void>;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Send raw email with custom subject and body
|
|
29
|
+
* Optional - not all providers may support this
|
|
30
|
+
* @param to - Recipient email address
|
|
31
|
+
* @param subject - Email subject
|
|
32
|
+
* @param html - HTML email body
|
|
33
|
+
* @param text - Plain text email body (optional)
|
|
34
|
+
*/
|
|
35
|
+
sendRaw?(to: string, subject: string, html: string, text?: string): Promise<void>;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Check if provider supports template-based emails
|
|
39
|
+
*/
|
|
40
|
+
supportsTemplates(): boolean;
|
|
41
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import jwt from 'jsonwebtoken';
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
import { config } from '@/infra/config/app.config.js';
|
|
4
|
+
import logger from '@/utils/logger.js';
|
|
5
|
+
import { AppError } from '@/api/middlewares/error.js';
|
|
6
|
+
import { ERROR_CODES } from '@/types/error-constants.js';
|
|
7
|
+
import { EmailTemplate } from '@/types/email.js';
|
|
8
|
+
import { EmailProvider } from './base.provider.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Cloud email provider for sending emails via Insforge cloud backend
|
|
12
|
+
*/
|
|
13
|
+
export class CloudEmailProvider implements EmailProvider {
|
|
14
|
+
/**
|
|
15
|
+
* Generate JWT sign token for cloud API authentication
|
|
16
|
+
* @returns JWT token signed with project secret
|
|
17
|
+
*/
|
|
18
|
+
private generateSignToken(): string {
|
|
19
|
+
const projectId = config.cloud.projectId;
|
|
20
|
+
const jwtSecret = config.app.jwtSecret;
|
|
21
|
+
|
|
22
|
+
if (!projectId || projectId === 'local') {
|
|
23
|
+
throw new AppError(
|
|
24
|
+
'PROJECT_ID is not configured. Cannot send emails without cloud project setup.',
|
|
25
|
+
500,
|
|
26
|
+
ERROR_CODES.INTERNAL_ERROR
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!jwtSecret) {
|
|
31
|
+
throw new AppError(
|
|
32
|
+
'JWT_SECRET is not configured. Cannot generate sign token.',
|
|
33
|
+
500,
|
|
34
|
+
ERROR_CODES.INTERNAL_ERROR
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const payload = {
|
|
39
|
+
sub: projectId,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return jwt.sign(payload, jwtSecret, {
|
|
43
|
+
expiresIn: '10m', // Short-lived token for API request
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Check if provider supports templates
|
|
49
|
+
*/
|
|
50
|
+
supportsTemplates(): boolean {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Send email using predefined template
|
|
56
|
+
* @param email - Recipient email address
|
|
57
|
+
* @param name - Recipient name
|
|
58
|
+
* @param template - Template type (email-verification or reset-password)
|
|
59
|
+
* @param variables - Variables to use in the email template
|
|
60
|
+
* @returns Promise that resolves when email is sent successfully
|
|
61
|
+
*/
|
|
62
|
+
async sendWithTemplate(
|
|
63
|
+
email: string,
|
|
64
|
+
name: string,
|
|
65
|
+
template: EmailTemplate,
|
|
66
|
+
variables?: Record<string, string>
|
|
67
|
+
): Promise<void> {
|
|
68
|
+
try {
|
|
69
|
+
const projectId = config.cloud.projectId;
|
|
70
|
+
const apiHost = config.cloud.apiHost;
|
|
71
|
+
const signToken = this.generateSignToken();
|
|
72
|
+
|
|
73
|
+
// Validate inputs
|
|
74
|
+
if (!email || !name || !template) {
|
|
75
|
+
throw new AppError(
|
|
76
|
+
'Missing required parameters for sending email',
|
|
77
|
+
400,
|
|
78
|
+
ERROR_CODES.INVALID_INPUT
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const validTemplates: EmailTemplate[] = [
|
|
83
|
+
'email-verification-code',
|
|
84
|
+
'email-verification-link',
|
|
85
|
+
'reset-password-code',
|
|
86
|
+
'reset-password-link',
|
|
87
|
+
];
|
|
88
|
+
if (!validTemplates.includes(template)) {
|
|
89
|
+
throw new AppError(
|
|
90
|
+
`Invalid template type: ${template}. Must be one of: ${validTemplates.join(', ')}`,
|
|
91
|
+
400,
|
|
92
|
+
ERROR_CODES.INVALID_INPUT
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const url = `${apiHost}/email/v1/${projectId}/send-with-template`;
|
|
97
|
+
const response = await axios.post(
|
|
98
|
+
url,
|
|
99
|
+
{
|
|
100
|
+
email,
|
|
101
|
+
name,
|
|
102
|
+
template,
|
|
103
|
+
variables,
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
headers: {
|
|
107
|
+
'Content-Type': 'application/json',
|
|
108
|
+
sign: signToken,
|
|
109
|
+
},
|
|
110
|
+
timeout: 10000, // 10 second timeout
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
if (response.data?.success) {
|
|
115
|
+
logger.info('Email sent successfully', {
|
|
116
|
+
projectId,
|
|
117
|
+
template,
|
|
118
|
+
});
|
|
119
|
+
} else {
|
|
120
|
+
throw new AppError(
|
|
121
|
+
'Email service returned unsuccessful response',
|
|
122
|
+
500,
|
|
123
|
+
ERROR_CODES.INTERNAL_ERROR
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
} catch (error) {
|
|
127
|
+
// Handle axios errors
|
|
128
|
+
if (axios.isAxiosError(error)) {
|
|
129
|
+
const status = error.response?.status;
|
|
130
|
+
const message = error.response?.data?.message || error.message;
|
|
131
|
+
|
|
132
|
+
logger.error('Failed to send email via cloud backend', {
|
|
133
|
+
projectId: config.cloud.projectId,
|
|
134
|
+
template,
|
|
135
|
+
status,
|
|
136
|
+
message,
|
|
137
|
+
error: error.response?.data,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Provide more specific error messages
|
|
141
|
+
if (status === 401 || status === 403) {
|
|
142
|
+
throw new AppError(
|
|
143
|
+
'Authentication failed with cloud email service. Check PROJECT_ID and JWT_SECRET.',
|
|
144
|
+
status,
|
|
145
|
+
ERROR_CODES.AUTH_UNAUTHORIZED
|
|
146
|
+
);
|
|
147
|
+
} else if (status === 429) {
|
|
148
|
+
throw new AppError(
|
|
149
|
+
'Email rate limit exceeded. Free plans are limited to 3000 emails per month.',
|
|
150
|
+
status,
|
|
151
|
+
ERROR_CODES.RATE_LIMITED
|
|
152
|
+
);
|
|
153
|
+
} else if (status === 400) {
|
|
154
|
+
throw new AppError(
|
|
155
|
+
`Invalid email request: ${message}`,
|
|
156
|
+
status,
|
|
157
|
+
ERROR_CODES.INVALID_INPUT
|
|
158
|
+
);
|
|
159
|
+
} else {
|
|
160
|
+
throw new AppError(
|
|
161
|
+
`Failed to send email: ${message}`,
|
|
162
|
+
status || 500,
|
|
163
|
+
ERROR_CODES.INTERNAL_ERROR
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Re-throw AppError
|
|
169
|
+
if (error instanceof AppError) {
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Handle other errors
|
|
174
|
+
logger.error('Unexpected error sending email', {
|
|
175
|
+
projectId: config.cloud.projectId,
|
|
176
|
+
template,
|
|
177
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
throw new AppError(
|
|
181
|
+
`Failed to send email: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
182
|
+
500,
|
|
183
|
+
ERROR_CODES.INTERNAL_ERROR
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { LogSchema, LogSourceSchema, LogStatsSchema } from '@insforge/shared-schemas';
|
|
2
2
|
|
|
3
|
-
export interface
|
|
3
|
+
export interface LogProvider {
|
|
4
4
|
initialize(): Promise<void>;
|
|
5
5
|
|
|
6
|
-
getLogSources(): Promise<
|
|
6
|
+
getLogSources(): Promise<LogSourceSchema[]>;
|
|
7
7
|
|
|
8
8
|
getLogsBySource(
|
|
9
9
|
sourceName: string,
|
|
10
10
|
limit?: number,
|
|
11
11
|
beforeTimestamp?: string
|
|
12
12
|
): Promise<{
|
|
13
|
-
logs:
|
|
13
|
+
logs: LogSchema[];
|
|
14
14
|
total: number;
|
|
15
15
|
tableName: string;
|
|
16
16
|
}>;
|
|
17
17
|
|
|
18
|
-
getLogSourceStats(): Promise<
|
|
18
|
+
getLogSourceStats(): Promise<LogStatsSchema[]>;
|
|
19
19
|
|
|
20
20
|
searchLogs(
|
|
21
21
|
query: string,
|
|
@@ -23,7 +23,7 @@ export interface AnalyticsProvider {
|
|
|
23
23
|
limit?: number,
|
|
24
24
|
offset?: number
|
|
25
25
|
): Promise<{
|
|
26
|
-
logs: (
|
|
26
|
+
logs: (LogSchema & { source: string })[];
|
|
27
27
|
total: number;
|
|
28
28
|
}>;
|
|
29
29
|
|
|
@@ -31,7 +31,7 @@ export interface AnalyticsProvider {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
// Base class with common functionality
|
|
34
|
-
export abstract class
|
|
34
|
+
export abstract class BaseLogProvider implements LogProvider {
|
|
35
35
|
// Source name mapping for user-friendly display
|
|
36
36
|
protected sourceNameMap: Record<string, string> = {
|
|
37
37
|
'cloudflare.logs.prod': 'insforge.logs',
|
|
@@ -59,24 +59,24 @@ export abstract class BaseAnalyticsProvider implements AnalyticsProvider {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
abstract initialize(): Promise<void>;
|
|
62
|
-
abstract getLogSources(): Promise<
|
|
62
|
+
abstract getLogSources(): Promise<LogSourceSchema[]>;
|
|
63
63
|
abstract getLogsBySource(
|
|
64
64
|
sourceName: string,
|
|
65
65
|
limit?: number,
|
|
66
66
|
beforeTimestamp?: string
|
|
67
67
|
): Promise<{
|
|
68
|
-
logs:
|
|
68
|
+
logs: LogSchema[];
|
|
69
69
|
total: number;
|
|
70
70
|
tableName: string;
|
|
71
71
|
}>;
|
|
72
|
-
abstract getLogSourceStats(): Promise<
|
|
72
|
+
abstract getLogSourceStats(): Promise<LogStatsSchema[]>;
|
|
73
73
|
abstract searchLogs(
|
|
74
74
|
query: string,
|
|
75
75
|
sourceName?: string,
|
|
76
76
|
limit?: number,
|
|
77
77
|
offset?: number
|
|
78
78
|
): Promise<{
|
|
79
|
-
logs: (
|
|
79
|
+
logs: (LogSchema & { source: string })[];
|
|
80
80
|
total: number;
|
|
81
81
|
}>;
|
|
82
82
|
abstract close(): Promise<void>;
|
|
@@ -4,23 +4,23 @@ import {
|
|
|
4
4
|
FilterLogEventsCommand,
|
|
5
5
|
StartQueryCommand,
|
|
6
6
|
GetQueryResultsCommand,
|
|
7
|
+
CreateLogGroupCommand,
|
|
8
|
+
ResourceAlreadyExistsException,
|
|
7
9
|
} from '@aws-sdk/client-cloudwatch-logs';
|
|
8
|
-
import { LogSource, AnalyticsLogRecord, LogSourceStats } from '@/types/logs.js';
|
|
9
10
|
import logger from '@/utils/logger.js';
|
|
10
|
-
import {
|
|
11
|
+
import { BaseLogProvider } from './base.provider.js';
|
|
12
|
+
import { AppError } from '@/api/middlewares/error.js';
|
|
13
|
+
import { ERROR_CODES } from '@/types/error-constants.js';
|
|
14
|
+
import { LogSchema, LogSourceSchema, LogStatsSchema } from '@insforge/shared-schemas';
|
|
11
15
|
|
|
12
|
-
export class CloudWatchProvider extends
|
|
16
|
+
export class CloudWatchProvider extends BaseLogProvider {
|
|
13
17
|
private cwClient: CloudWatchLogsClient | null = null;
|
|
14
18
|
private cwLogGroup: string | null = null;
|
|
15
19
|
private cwRegion: string | null = null;
|
|
16
20
|
|
|
17
|
-
initialize(): Promise<void> {
|
|
21
|
+
async initialize(): Promise<void> {
|
|
18
22
|
this.cwRegion = process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION || 'us-east-2';
|
|
19
|
-
this.cwLogGroup = process.env.CLOUDWATCH_LOG_GROUP ||
|
|
20
|
-
|
|
21
|
-
if (!this.cwLogGroup) {
|
|
22
|
-
throw new Error('CLOUDWATCH_LOG_GROUP is required when using CloudWatch analytics');
|
|
23
|
-
}
|
|
23
|
+
this.cwLogGroup = process.env.CLOUDWATCH_LOG_GROUP || '/insforge/local';
|
|
24
24
|
|
|
25
25
|
const cloudwatchOpts: {
|
|
26
26
|
region: string;
|
|
@@ -34,8 +34,18 @@ export class CloudWatchProvider extends BaseAnalyticsProvider {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
this.cwClient = new CloudWatchLogsClient({ ...cloudwatchOpts });
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
|
|
38
|
+
// Create log group if it doesn't exist
|
|
39
|
+
try {
|
|
40
|
+
await this.cwClient.send(new CreateLogGroupCommand({ logGroupName: this.cwLogGroup }));
|
|
41
|
+
logger.info(`Created CloudWatch log group: ${this.cwLogGroup}`);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
if (error instanceof ResourceAlreadyExistsException) {
|
|
44
|
+
logger.info(`CloudWatch log group already exists: ${this.cwLogGroup}`);
|
|
45
|
+
} else {
|
|
46
|
+
logger.warn(`Could not create CloudWatch log group: ${error}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
39
49
|
}
|
|
40
50
|
|
|
41
51
|
private getSuffixMapping(): Record<string, string> {
|
|
@@ -47,9 +57,13 @@ export class CloudWatchProvider extends BaseAnalyticsProvider {
|
|
|
47
57
|
};
|
|
48
58
|
}
|
|
49
59
|
|
|
50
|
-
async getLogSources(): Promise<
|
|
60
|
+
async getLogSources(): Promise<LogSourceSchema[]> {
|
|
51
61
|
if (!this.cwLogGroup || !this.cwClient) {
|
|
52
|
-
throw new
|
|
62
|
+
throw new AppError(
|
|
63
|
+
'AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY not found in environment variables',
|
|
64
|
+
500,
|
|
65
|
+
ERROR_CODES.LOGS_AWS_NOT_CONFIGURED
|
|
66
|
+
);
|
|
53
67
|
}
|
|
54
68
|
const logGroup = this.cwLogGroup;
|
|
55
69
|
const client = this.cwClient;
|
|
@@ -59,13 +73,13 @@ export class CloudWatchProvider extends BaseAnalyticsProvider {
|
|
|
59
73
|
const result = await client.send(cmd);
|
|
60
74
|
const streams = result.logStreams || [];
|
|
61
75
|
|
|
62
|
-
const available:
|
|
76
|
+
const available: LogSourceSchema[] = [];
|
|
63
77
|
let idCounter = 1;
|
|
64
78
|
|
|
65
79
|
for (const [displayName, suffix] of Object.entries(suffixMapping)) {
|
|
66
80
|
const have = streams.some((s) => (s.logStreamName || '').includes(suffix));
|
|
67
81
|
if (have) {
|
|
68
|
-
available.push({ id: idCounter
|
|
82
|
+
available.push({ id: String(idCounter++), name: displayName, token: suffix });
|
|
69
83
|
}
|
|
70
84
|
}
|
|
71
85
|
|
|
@@ -77,12 +91,16 @@ export class CloudWatchProvider extends BaseAnalyticsProvider {
|
|
|
77
91
|
limit: number = 100,
|
|
78
92
|
beforeTimestamp?: string
|
|
79
93
|
): Promise<{
|
|
80
|
-
logs:
|
|
94
|
+
logs: LogSchema[];
|
|
81
95
|
total: number;
|
|
82
96
|
tableName: string;
|
|
83
97
|
}> {
|
|
84
98
|
if (!this.cwLogGroup || !this.cwClient) {
|
|
85
|
-
throw new
|
|
99
|
+
throw new AppError(
|
|
100
|
+
'AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY not found in environment variables',
|
|
101
|
+
500,
|
|
102
|
+
ERROR_CODES.LOGS_AWS_NOT_CONFIGURED
|
|
103
|
+
);
|
|
86
104
|
}
|
|
87
105
|
const client = this.cwClient;
|
|
88
106
|
const logGroup = this.cwLogGroup;
|
|
@@ -127,7 +145,7 @@ export class CloudWatchProvider extends BaseAnalyticsProvider {
|
|
|
127
145
|
const fle = await client.send(
|
|
128
146
|
new FilterLogEventsCommand({
|
|
129
147
|
logGroupName: logGroup,
|
|
130
|
-
logStreamNames: streams.length
|
|
148
|
+
logStreamNames: streams.length ? streams.slice(0, 100) : undefined,
|
|
131
149
|
startTime: startMs,
|
|
132
150
|
endTime: endMs,
|
|
133
151
|
nextToken,
|
|
@@ -185,7 +203,7 @@ export class CloudWatchProvider extends BaseAnalyticsProvider {
|
|
|
185
203
|
await sleep(300);
|
|
186
204
|
}
|
|
187
205
|
|
|
188
|
-
if (results && results.length
|
|
206
|
+
if (results && results.length) {
|
|
189
207
|
// Convert Insights results to our format
|
|
190
208
|
events = results.map((row) => {
|
|
191
209
|
const obj = Object.fromEntries(row.map((c) => [c.field || '', c.value || '']));
|
|
@@ -225,7 +243,7 @@ export class CloudWatchProvider extends BaseAnalyticsProvider {
|
|
|
225
243
|
const fle = await client.send(
|
|
226
244
|
new FilterLogEventsCommand({
|
|
227
245
|
logGroupName: logGroup,
|
|
228
|
-
logStreamNames: streams.length
|
|
246
|
+
logStreamNames: streams.length ? streams.slice(0, 100) : undefined,
|
|
229
247
|
startTime: fallbackStartMs,
|
|
230
248
|
endTime: beforeMs,
|
|
231
249
|
limit: limit * 2, // Get a bit more to ensure we have enough
|
|
@@ -238,7 +256,7 @@ export class CloudWatchProvider extends BaseAnalyticsProvider {
|
|
|
238
256
|
}
|
|
239
257
|
}
|
|
240
258
|
// Keep CloudWatch's default order (oldest first, newest last)
|
|
241
|
-
const logs:
|
|
259
|
+
const logs: LogSchema[] = events.map((e) => {
|
|
242
260
|
const message = e.message || '';
|
|
243
261
|
let parsed: Record<string, unknown> = {};
|
|
244
262
|
try {
|
|
@@ -251,7 +269,7 @@ export class CloudWatchProvider extends BaseAnalyticsProvider {
|
|
|
251
269
|
id: e.eventId || `${e.logStreamName || ''}-${e.timestamp || ''}`,
|
|
252
270
|
// CloudWatch timestamp is in milliseconds
|
|
253
271
|
timestamp: e.timestamp ? new Date(e.timestamp).toISOString() : new Date().toISOString(),
|
|
254
|
-
|
|
272
|
+
eventMessage:
|
|
255
273
|
typeof parsed === 'object' && parsed && (parsed as Record<string, unknown>).msg
|
|
256
274
|
? String((parsed as Record<string, unknown>).msg)
|
|
257
275
|
: typeof message === 'string'
|
|
@@ -268,14 +286,18 @@ export class CloudWatchProvider extends BaseAnalyticsProvider {
|
|
|
268
286
|
};
|
|
269
287
|
}
|
|
270
288
|
|
|
271
|
-
async getLogSourceStats(): Promise<
|
|
289
|
+
async getLogSourceStats(): Promise<LogStatsSchema[]> {
|
|
272
290
|
if (!this.cwLogGroup || !this.cwClient) {
|
|
273
|
-
throw new
|
|
291
|
+
throw new AppError(
|
|
292
|
+
'AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY not found in environment variables',
|
|
293
|
+
500,
|
|
294
|
+
ERROR_CODES.LOGS_AWS_NOT_CONFIGURED
|
|
295
|
+
);
|
|
274
296
|
}
|
|
275
297
|
const client = this.cwClient;
|
|
276
298
|
const logGroup = this.cwLogGroup;
|
|
277
299
|
const sources = await this.getLogSources();
|
|
278
|
-
const stats:
|
|
300
|
+
const stats: LogStatsSchema[] = [];
|
|
279
301
|
const suffixMapping = this.getSuffixMapping();
|
|
280
302
|
|
|
281
303
|
const dls = await client.send(new DescribeLogStreamsCommand({ logGroupName: logGroup }));
|
|
@@ -289,7 +311,7 @@ export class CloudWatchProvider extends BaseAnalyticsProvider {
|
|
|
289
311
|
|
|
290
312
|
let lastActivity = '';
|
|
291
313
|
|
|
292
|
-
if (sourceStreams.length
|
|
314
|
+
if (sourceStreams.length) {
|
|
293
315
|
try {
|
|
294
316
|
// Use EXACTLY the same approach as getLogsBySource to get consistent results
|
|
295
317
|
const endMs = Date.now();
|
|
@@ -297,16 +319,13 @@ export class CloudWatchProvider extends BaseAnalyticsProvider {
|
|
|
297
319
|
|
|
298
320
|
// Use CloudWatch Insights to efficiently get the latest timestamp
|
|
299
321
|
try {
|
|
300
|
-
const end = Date.now();
|
|
301
|
-
const start = end - 24 * 60 * 60 * 1000; // Last 24 hours
|
|
302
|
-
|
|
303
322
|
const insights = `fields @timestamp | filter @logStream like /${suffix}/ | sort @timestamp desc | limit 1`;
|
|
304
323
|
|
|
305
324
|
const startQuery = await client.send(
|
|
306
325
|
new StartQueryCommand({
|
|
307
326
|
logGroupName: logGroup,
|
|
308
|
-
startTime: Math.floor(
|
|
309
|
-
endTime: Math.floor(
|
|
327
|
+
startTime: Math.floor(startMs / 1000),
|
|
328
|
+
endTime: Math.floor(endMs / 1000),
|
|
310
329
|
queryString: insights,
|
|
311
330
|
limit: 1,
|
|
312
331
|
})
|
|
@@ -326,7 +345,7 @@ export class CloudWatchProvider extends BaseAnalyticsProvider {
|
|
|
326
345
|
await sleep(200);
|
|
327
346
|
}
|
|
328
347
|
|
|
329
|
-
if (results && results.length
|
|
348
|
+
if (results && results.length) {
|
|
330
349
|
const row = results[0];
|
|
331
350
|
const timestampField = row.find((field) => field.field === '@timestamp');
|
|
332
351
|
if (timestampField && timestampField.value) {
|
|
@@ -357,7 +376,7 @@ export class CloudWatchProvider extends BaseAnalyticsProvider {
|
|
|
357
376
|
const fle = await client.send(
|
|
358
377
|
new FilterLogEventsCommand({
|
|
359
378
|
logGroupName: logGroup,
|
|
360
|
-
logStreamNames: sourceStreams.length
|
|
379
|
+
logStreamNames: sourceStreams.length ? sourceStreams.slice(0, 100) : undefined,
|
|
361
380
|
startTime: startMs,
|
|
362
381
|
endTime: endMs,
|
|
363
382
|
limit: 1000, // Reasonable limit for fallback
|
|
@@ -365,7 +384,7 @@ export class CloudWatchProvider extends BaseAnalyticsProvider {
|
|
|
365
384
|
);
|
|
366
385
|
|
|
367
386
|
const events = fle.events || [];
|
|
368
|
-
if (events.length
|
|
387
|
+
if (events.length) {
|
|
369
388
|
const latestEvent = events[events.length - 1];
|
|
370
389
|
if (latestEvent.timestamp) {
|
|
371
390
|
lastActivity = new Date(latestEvent.timestamp).toISOString();
|
|
@@ -409,11 +428,15 @@ export class CloudWatchProvider extends BaseAnalyticsProvider {
|
|
|
409
428
|
limit: number = 100,
|
|
410
429
|
_offset = 0 // CloudWatch doesn't support offset-based pagination
|
|
411
430
|
): Promise<{
|
|
412
|
-
logs: (
|
|
431
|
+
logs: (LogSchema & { source: string })[];
|
|
413
432
|
total: number;
|
|
414
433
|
}> {
|
|
415
434
|
if (!this.cwLogGroup || !this.cwClient) {
|
|
416
|
-
throw new
|
|
435
|
+
throw new AppError(
|
|
436
|
+
'AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY not found in environment variables',
|
|
437
|
+
500,
|
|
438
|
+
ERROR_CODES.LOGS_AWS_NOT_CONFIGURED
|
|
439
|
+
);
|
|
417
440
|
}
|
|
418
441
|
const client = this.cwClient;
|
|
419
442
|
const logGroup = this.cwLogGroup;
|
|
@@ -462,7 +485,7 @@ export class CloudWatchProvider extends BaseAnalyticsProvider {
|
|
|
462
485
|
const toObj = (row: Array<{ field?: string; value?: string }>) =>
|
|
463
486
|
Object.fromEntries(row.map((c) => [c.field || '', c.value || '']));
|
|
464
487
|
|
|
465
|
-
const mapped: (
|
|
488
|
+
const mapped: (LogSchema & { source: string })[] = rows.map((r) => {
|
|
466
489
|
const o = toObj(r);
|
|
467
490
|
const msg = o['@message'] || '';
|
|
468
491
|
let parsed: Record<string, unknown> = {};
|
|
@@ -487,7 +510,7 @@ export class CloudWatchProvider extends BaseAnalyticsProvider {
|
|
|
487
510
|
timestamp: o['@timestamp']
|
|
488
511
|
? new Date(parseInt(o['@timestamp'])).toISOString()
|
|
489
512
|
: new Date().toISOString(),
|
|
490
|
-
|
|
513
|
+
eventMessage:
|
|
491
514
|
typeof parsed === 'object' && (parsed as Record<string, unknown>).msg
|
|
492
515
|
? String((parsed as Record<string, unknown>).msg)
|
|
493
516
|
: typeof msg === 'string'
|