insforge 0.3.3 → 1.3.0
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/.dockerignore +60 -57
- package/.env.example +84 -49
- package/.github/ISSUE_TEMPLATE/bug_report.yml +36 -83
- package/.github/ISSUE_TEMPLATE/config.yml +11 -11
- package/.github/ISSUE_TEMPLATE/feature_request.yml +26 -79
- package/.github/PULL_REQUEST_TEMPLATE.md +7 -0
- package/.github/copilot-instructions.md +146 -146
- package/.github/workflows/build-image.yml +66 -65
- package/.github/workflows/ci-premerge-check.yml +23 -23
- package/.github/workflows/e2e.yml +63 -0
- package/.github/workflows/lint-and-format.yml +32 -32
- package/.prettierignore +64 -64
- package/CHANGELOG.md +44 -3
- package/CLAUDE_PLUGIN.md +104 -0
- package/CODE_OF_CONDUCT.md +128 -0
- package/CONTRIBUTING.md +125 -125
- package/Dockerfile +30 -27
- package/GITHUB_OAUTH_SETUP.md +49 -49
- package/GOOGLE_OAUTH_SETUP.md +148 -148
- package/LICENSE +201 -201
- package/README.md +182 -134
- package/assets/Dark.svg +23 -23
- 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 +117 -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 +60 -0
- package/auth/src/pages/SignUpPage.tsx +60 -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 +78 -75
- 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} +22 -26
- package/backend/src/api/routes/auth/index.routes.ts +667 -0
- package/backend/src/api/routes/auth/oauth.routes.ts +473 -0
- package/backend/src/api/routes/{database.advance.ts → database/advance.routes.ts} +128 -65
- package/backend/src/api/routes/database/index.routes.ts +90 -0
- package/backend/src/api/routes/{database.records.ts → database/records.routes.ts} +26 -12
- package/backend/src/api/routes/{database.tables.ts → database/tables.routes.ts} +6 -23
- package/backend/src/api/routes/docs/index.routes.ts +75 -0
- package/backend/src/api/routes/email/index.routes.ts +35 -0
- package/backend/src/api/routes/functions/index.routes.ts +194 -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} +33 -31
- package/backend/src/api/routes/realtime/channels.routes.ts +81 -0
- package/backend/src/api/routes/realtime/index.routes.ts +12 -0
- package/backend/src/api/routes/realtime/messages.routes.ts +48 -0
- package/backend/src/api/routes/realtime/permissions.routes.ts +19 -0
- package/backend/src/api/routes/{secrets.ts → secrets/index.routes.ts} +27 -22
- package/backend/src/api/routes/{storage.ts → storage/index.routes.ts} +48 -61
- package/backend/src/api/routes/usage/index.routes.ts +91 -0
- package/backend/src/infra/config/app.config.ts +51 -0
- package/backend/src/infra/database/database.manager.ts +182 -0
- package/backend/{migrations → src/infra/database/migrations}/000_create-base-tables.sql +141 -141
- package/backend/{migrations → src/infra/database/migrations}/001_create-helper-functions.sql +40 -40
- package/backend/{migrations → src/infra/database/migrations}/002_rename-auth-tables.sql +29 -29
- package/backend/{migrations → src/infra/database/migrations}/003_create-users-table.sql +55 -55
- package/backend/{migrations → src/infra/database/migrations}/004_add-reload-postgrest-func.sql +23 -23
- package/backend/{migrations → src/infra/database/migrations}/005_enable-project-admin-modify-users.sql +29 -29
- package/backend/{migrations → src/infra/database/migrations}/006_modify-ai-usage-table.sql +24 -24
- package/backend/{migrations → src/infra/database/migrations}/007_drop-metadata-table.sql +1 -1
- package/backend/{migrations → src/infra/database/migrations}/008_add-system-tables.sql +76 -76
- package/backend/{migrations → src/infra/database/migrations}/009_add-function-secrets.sql +23 -23
- package/backend/{migrations → src/infra/database/migrations}/010_modify-ai-config-modalities.sql +93 -93
- package/backend/{migrations → src/infra/database/migrations}/011_refactor-secrets-table.sql +15 -15
- package/backend/{migrations → src/infra/database/migrations}/012_add-storage-uploaded-by.sql +7 -7
- 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/infra/database/migrations/017_create-realtime-schema.sql +233 -0
- package/backend/src/infra/realtime/realtime.manager.ts +246 -0
- package/backend/src/infra/realtime/webhook-sender.ts +82 -0
- package/backend/src/{core/secrets/encryption.ts → infra/security/encryption.manager.ts} +3 -2
- package/backend/src/infra/security/token.manager.ts +219 -0
- package/backend/src/infra/socket/socket.manager.ts +522 -0
- package/backend/src/providers/ai/openrouter.provider.ts +380 -0
- package/backend/src/providers/email/base.provider.ts +38 -0
- package/backend/src/providers/email/cloud.provider.ts +271 -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/apple.provider.ts +266 -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 +8 -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 +317 -288
- 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 +1150 -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 +802 -0
- package/backend/src/services/database/database.service.ts +127 -0
- package/backend/src/services/email/email.service.ts +73 -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/services/realtime/index.ts +3 -0
- package/backend/src/services/realtime/realtime-auth.service.ts +104 -0
- package/backend/src/services/realtime/realtime-channel.service.ts +237 -0
- package/backend/src/services/realtime/realtime-message.service.ts +260 -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 +77 -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/types/realtime.ts +18 -0
- package/backend/src/{core/socket/types.ts → types/socket.ts} +11 -36
- package/backend/src/utils/cookies.ts +35 -0
- package/backend/src/utils/environment.ts +9 -3
- package/backend/src/utils/logger.ts +20 -2
- package/backend/src/utils/s3-config-loader.ts +64 -0
- package/backend/src/utils/seed.ts +301 -205
- package/backend/src/utils/sql-parser.ts +91 -1
- package/backend/src/utils/utils.ts +114 -0
- package/backend/src/utils/validations.ts +40 -4
- package/backend/tests/README.md +133 -133
- package/backend/tests/cleanup-all-test-data.sh +230 -230
- package/backend/tests/cloud/test-s3-multitenant.sh +131 -131
- package/backend/tests/local/comprehensive-curl-tests.sh +155 -155
- 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 +143 -143
- package/backend/tests/local/test-database-router.sh +222 -222
- package/backend/tests/local/test-e2e.sh +240 -240
- package/backend/tests/local/test-fk-errors.sh +96 -96
- package/backend/tests/local/test-functions.sh +123 -0
- package/backend/tests/local/test-id-field.sh +200 -200
- package/backend/tests/local/test-logs.sh +132 -0
- package/backend/tests/local/test-public-bucket.sh +264 -264
- package/backend/tests/local/test-secrets.sh +249 -247
- package/backend/tests/local/test-serverless-functions.sh.disabled +325 -325
- package/backend/tests/local/test-traditional-rest.sh +208 -208
- package/backend/tests/manual/README.md +50 -50
- package/backend/tests/manual/create-large-table-simple.sql +10 -10
- package/backend/tests/manual/seed-large-table.sql +100 -100
- package/backend/tests/manual/setup-large-table-extras.sql +33 -33
- package/backend/tests/manual/test-bulk-upsert.sh +409 -409
- package/backend/tests/manual/test-database-advance.sh +296 -296
- package/backend/tests/manual/test-postgrest-stability.sh +191 -191
- package/backend/tests/manual/test-rawsql-export-import.sh +411 -411
- package/backend/tests/manual/test-rawsql-modes.sh +244 -0
- package/backend/tests/manual/test-universal-storage.sh +263 -263
- package/backend/tests/manual/test-users.sql +17 -17
- package/backend/tests/run-all-tests.sh +139 -139
- package/backend/tests/setup.ts +0 -0
- package/backend/tests/test-config.sh +338 -302
- package/backend/tests/unit/analyze-query.test.ts +697 -0
- 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 +22 -22
- 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 +204 -144
- package/docker-compose.yml +232 -167
- package/docker-init/db/db-init.sql +97 -125
- package/docker-init/db/jwt.sql +5 -5
- package/docker-init/db/postgresql.conf +16 -16
- package/docker-init/logs/vector.yml +236 -0
- package/docs/README.md +44 -0
- package/docs/agent-docs/real-time.md +269 -0
- package/docs/changelog.mdx +119 -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/email/architecture.mdx +101 -0
- package/docs/core-concepts/email/sdk.mdx +53 -0
- package/docs/core-concepts/functions/architecture.mdx +105 -0
- package/docs/core-concepts/functions/sdk.mdx +184 -0
- package/docs/core-concepts/realtime/architecture.mdx +446 -0
- package/docs/core-concepts/realtime/sdk.mdx +409 -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/deprecated/insforge-auth-api.md +214 -214
- package/docs/deprecated/insforge-auth-sdk.md +99 -99
- package/docs/deprecated/insforge-db-api.md +358 -358
- package/docs/deprecated/insforge-db-sdk.md +139 -139
- package/docs/deprecated/insforge-debug-sdk.md +156 -156
- package/docs/deprecated/insforge-debug.md +64 -64
- package/docs/deprecated/insforge-instructions.md +123 -123
- package/docs/deprecated/insforge-project.md +117 -117
- package/docs/deprecated/insforge-storage-api.md +278 -278
- package/docs/deprecated/insforge-storage-sdk.md +158 -158
- package/docs/docs.json +232 -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/dec-2025/ai-integration.png +0 -0
- package/docs/images/changelog/dec-2025/ai-models.webp +0 -0
- package/docs/images/changelog/dec-2025/alipay-payment.webp +0 -0
- package/docs/images/changelog/dec-2025/apple-login.jpg +0 -0
- package/docs/images/changelog/dec-2025/mcp-installer.png +0 -0
- package/docs/images/changelog/dec-2025/realtime-module.jpg +0 -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 +89 -407
- package/docs/introduction.mdx +45 -0
- package/docs/logo/dark.svg +22 -0
- package/docs/logo/light.svg +20 -0
- package/docs/partnership.mdx +652 -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/examples/oauth/frontend-oauth-example.html +250 -250
- package/examples/response-examples.md +443 -443
- package/frontend/components.json +17 -17
- package/frontend/package.json +69 -63
- package/frontend/src/App.tsx +13 -82
- package/frontend/src/assets/icons/checkbox_checked.svg +6 -6
- package/frontend/src/assets/icons/checkbox_undetermined.svg +6 -6
- package/frontend/src/assets/icons/checked.svg +3 -3
- package/frontend/src/assets/icons/connected.svg +3 -0
- package/frontend/src/assets/icons/error.svg +3 -3
- package/frontend/src/assets/icons/loader.svg +9 -0
- package/frontend/src/assets/icons/pencil.svg +4 -4
- package/frontend/src/assets/icons/refresh.svg +4 -4
- package/frontend/src/assets/icons/step_active.svg +3 -3
- package/frontend/src/assets/icons/step_inactive.svg +11 -11
- package/frontend/src/assets/icons/warning.svg +3 -3
- package/frontend/src/assets/logos/apple.svg +4 -0
- package/frontend/src/assets/logos/claude_code.svg +3 -3
- package/frontend/src/assets/logos/cline.svg +6 -6
- package/frontend/src/assets/logos/cursor.svg +20 -20
- package/frontend/src/assets/logos/discord.svg +8 -8
- package/frontend/src/assets/logos/facebook.svg +3 -0
- package/frontend/src/assets/logos/gemini.svg +19 -19
- package/frontend/src/assets/logos/github.svg +5 -5
- package/frontend/src/assets/logos/google.svg +13 -13
- package/frontend/src/assets/logos/grok.svg +10 -10
- package/frontend/src/assets/logos/insforge_dark.svg +15 -15
- package/frontend/src/assets/logos/insforge_light.svg +15 -15
- 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/openai.svg +10 -10
- package/frontend/src/assets/logos/roo_code.svg +9 -9
- package/frontend/src/assets/logos/spotify.svg +17 -0
- package/frontend/src/assets/logos/tiktok.svg +6 -0
- package/frontend/src/assets/logos/trae.svg +3 -3
- package/frontend/src/assets/logos/windsurf.svg +10 -10
- 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 +131 -91
- 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/pages/AIPage.tsx +166 -0
- 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 +54 -30
- package/frontend/src/features/auth/components/UserFormDialog.tsx +13 -6
- package/frontend/src/features/auth/components/UsersDataGrid.tsx +50 -14
- package/frontend/src/features/auth/components/index.ts +5 -0
- package/frontend/src/features/auth/helpers.tsx +208 -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/pages/AuthMethodsPage.tsx +275 -0
- package/frontend/src/features/auth/pages/ConfigurationPage.tsx +395 -0
- package/frontend/src/features/auth/pages/UsersPage.tsx +257 -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/pages/DashboardPage.tsx +212 -0
- 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/SQLModal.tsx +75 -0
- package/frontend/src/features/database/components/TableEmptyState.tsx +6 -5
- package/frontend/src/features/database/components/TableForm.tsx +28 -19
- 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/useDatabase.ts +66 -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 +135 -0
- package/frontend/src/features/database/index.ts +7 -1
- package/frontend/src/features/database/pages/FunctionsPage.tsx +203 -0
- package/frontend/src/features/database/pages/IndexesPage.tsx +228 -0
- package/frontend/src/features/database/pages/PoliciesPage.tsx +237 -0
- package/frontend/src/features/database/pages/SQLEditorPage.tsx +382 -0
- package/frontend/src/features/database/{page/DatabasePage.tsx → pages/TablesPage.tsx} +168 -209
- package/frontend/src/features/database/pages/TemplatesPage.tsx +39 -0
- package/frontend/src/features/database/pages/TriggersPage.tsx +230 -0
- package/frontend/src/features/database/services/advance.service.ts +40 -0
- package/frontend/src/features/database/services/database.service.ts +33 -194
- package/frontend/src/features/database/services/record.service.ts +219 -0
- package/frontend/src/features/database/services/table.service.ts +58 -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/pages/FunctionsPage.tsx +148 -0
- package/frontend/src/features/functions/{components/SecretsContent.tsx → pages/SecretsPage.tsx} +19 -21
- 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/pages/CloudLoginPage.tsx +118 -0
- package/frontend/src/features/login/{page → pages}/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 +128 -0
- package/frontend/src/features/logs/index.ts +8 -2
- package/frontend/src/features/logs/{page → pages}/AuditsPage.tsx +91 -38
- package/frontend/src/features/logs/pages/LogsPage.tsx +152 -0
- package/frontend/src/features/logs/pages/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/realtime/components/ChannelRow.tsx +83 -0
- package/frontend/src/features/realtime/components/EditChannelModal.tsx +246 -0
- package/frontend/src/features/realtime/components/MessageRow.tsx +85 -0
- package/frontend/src/features/realtime/components/RealtimeEmptyState.tsx +30 -0
- package/frontend/src/features/realtime/hooks/useRealtime.ts +218 -0
- package/frontend/src/features/realtime/index.ts +11 -0
- package/frontend/src/features/realtime/pages/RealtimeChannelsPage.tsx +172 -0
- package/frontend/src/features/realtime/pages/RealtimeMessagesPage.tsx +211 -0
- package/frontend/src/features/realtime/pages/RealtimePermissionsPage.tsx +191 -0
- package/frontend/src/features/realtime/services/realtime.service.ts +107 -0
- 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 → pages}/StoragePage.tsx +41 -143
- 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/pages/VisualizerPage.tsx +97 -0
- 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 +123 -80
- 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 +99 -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 +207 -0
- package/frontend/src/lib/utils/{validation-schemas.ts → schemaValidations.ts} +10 -5
- package/frontend/src/lib/utils/utils.ts +32 -1
- package/frontend/src/vite-env.d.ts +1 -0
- package/frontend/tsconfig.json +25 -25
- package/frontend/tsconfig.node.json +9 -9
- package/frontend/vite.config.ts +5 -3
- package/functions/deno.json +24 -24
- package/functions/server.ts +315 -290
- 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 +715 -688
- package/openapi/auth.yaml +1244 -563
- package/openapi/email.yaml +158 -0
- package/openapi/functions.yaml +475 -475
- package/openapi/health.yaml +29 -29
- package/openapi/logs.yaml +223 -223
- package/openapi/metadata.yaml +177 -177
- package/openapi/realtime.yaml +699 -0
- package/openapi/records.yaml +381 -381
- package/openapi/secrets.yaml +370 -370
- package/openapi/storage.yaml +875 -875
- package/openapi/tables.yaml +463 -463
- package/package.json +97 -88
- package/shared-schemas/package.json +31 -31
- package/shared-schemas/src/ai-api.schema.ts +34 -58
- package/shared-schemas/src/ai.schema.ts +63 -54
- package/shared-schemas/src/auth-api.schema.ts +352 -193
- package/shared-schemas/src/auth.schema.ts +43 -7
- package/shared-schemas/src/cloud-events.schema.ts +57 -0
- package/shared-schemas/src/database-api.schema.ts +35 -4
- package/shared-schemas/src/database.schema.ts +40 -1
- package/shared-schemas/src/docs.schema.ts +26 -0
- package/shared-schemas/src/email-api.schema.ts +30 -0
- package/shared-schemas/src/index.ts +5 -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 +18 -4
- package/shared-schemas/src/realtime-api.schema.ts +111 -0
- package/shared-schemas/src/realtime.schema.ts +143 -0
- package/shared-schemas/tsconfig.json +21 -21
- package/tsconfig.json +7 -7
- 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/manager.ts +0 -178
- 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/socket/socket.ts +0 -388
- 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/ai/page/AIPage.tsx +0 -178
- 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/dashboard/page/DashboardPage.tsx +0 -194
- 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/functions/page/FunctionsPage.tsx +0 -28
- package/frontend/src/features/login/components/AuthErrorBoundary.tsx +0 -87
- package/frontend/src/features/login/components/PrivateRoute.tsx +0 -24
- package/frontend/src/features/login/page/CloudLoginPage.tsx +0 -93
- 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/features/visualizer/page/VisualizerPage.tsx +0 -127
- 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/frontend/src/{features/metadata → lib}/services/metadata.service.ts +0 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { Pool } from 'pg';
|
|
2
|
+
import { DatabaseManager } from '@/infra/database/database.manager.js';
|
|
3
|
+
import logger from '@/utils/logger.js';
|
|
4
|
+
import type { RealtimeMessage, RoleSchema } from '@insforge/shared-schemas';
|
|
5
|
+
import { RealtimeChannelService } from './realtime-channel.service.js';
|
|
6
|
+
import { RealtimeAuthService } from './realtime-auth.service.js';
|
|
7
|
+
|
|
8
|
+
export class RealtimeMessageService {
|
|
9
|
+
private static instance: RealtimeMessageService;
|
|
10
|
+
private pool: Pool | null = null;
|
|
11
|
+
|
|
12
|
+
private constructor() {}
|
|
13
|
+
|
|
14
|
+
static getInstance(): RealtimeMessageService {
|
|
15
|
+
if (!RealtimeMessageService.instance) {
|
|
16
|
+
RealtimeMessageService.instance = new RealtimeMessageService();
|
|
17
|
+
}
|
|
18
|
+
return RealtimeMessageService.instance;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
private getPool(): Pool {
|
|
22
|
+
if (!this.pool) {
|
|
23
|
+
this.pool = DatabaseManager.getInstance().getPool();
|
|
24
|
+
}
|
|
25
|
+
return this.pool;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Insert a message into the channel (client-initiated send).
|
|
30
|
+
* RLS INSERT policy controls who can send to which channels.
|
|
31
|
+
* pg_notify is automatically triggered by database trigger on insert.
|
|
32
|
+
*
|
|
33
|
+
* @returns The inserted message data for broadcasting, or null if RLS denied the insert
|
|
34
|
+
*/
|
|
35
|
+
async insertMessage(
|
|
36
|
+
channelName: string,
|
|
37
|
+
eventName: string,
|
|
38
|
+
payload: Record<string, unknown>,
|
|
39
|
+
userId: string | undefined,
|
|
40
|
+
userRole: RoleSchema
|
|
41
|
+
): Promise<{
|
|
42
|
+
channelId: string;
|
|
43
|
+
channelName: string;
|
|
44
|
+
eventName: string;
|
|
45
|
+
payload: Record<string, unknown>;
|
|
46
|
+
senderId: string | null;
|
|
47
|
+
} | null> {
|
|
48
|
+
// Get channel info
|
|
49
|
+
const channelService = RealtimeChannelService.getInstance();
|
|
50
|
+
const channel = await channelService.getByName(channelName);
|
|
51
|
+
|
|
52
|
+
if (!channel) {
|
|
53
|
+
logger.debug('Channel not found for message insert', { channelName });
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const client = await this.getPool().connect();
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
// Begin transaction to ensure settings persist across queries
|
|
61
|
+
await client.query('BEGIN');
|
|
62
|
+
|
|
63
|
+
// Switch to specified role to enforce RLS policies
|
|
64
|
+
await client.query(`SET LOCAL ROLE ${userRole}`);
|
|
65
|
+
|
|
66
|
+
// Set user context for RLS policy evaluation
|
|
67
|
+
const authService = RealtimeAuthService.getInstance();
|
|
68
|
+
await authService.setUserContext(client, userId, channelName);
|
|
69
|
+
|
|
70
|
+
// Attempt INSERT with sender info - RLS will allow/deny based on policies
|
|
71
|
+
// No RETURNING clause needed - trigger handles pg_notify
|
|
72
|
+
await client.query(
|
|
73
|
+
`INSERT INTO realtime.messages (event_name, channel_id, channel_name, payload, sender_type, sender_id)
|
|
74
|
+
VALUES ($1, $2, $3, $4, 'user', $5)`,
|
|
75
|
+
[eventName, channel.id, channelName, JSON.stringify(payload), userId || null]
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// Commit transaction - insert succeeded
|
|
79
|
+
await client.query('COMMIT');
|
|
80
|
+
|
|
81
|
+
logger.debug('Client message inserted', {
|
|
82
|
+
channelName,
|
|
83
|
+
eventName,
|
|
84
|
+
userId,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
channelId: channel.id,
|
|
89
|
+
channelName,
|
|
90
|
+
eventName,
|
|
91
|
+
payload,
|
|
92
|
+
senderId: userId || null,
|
|
93
|
+
};
|
|
94
|
+
} catch (error) {
|
|
95
|
+
// Rollback transaction on error
|
|
96
|
+
await client.query('ROLLBACK').catch(() => {});
|
|
97
|
+
|
|
98
|
+
// RLS policy denied the INSERT or other error
|
|
99
|
+
logger.debug('Message insert denied or failed', { channelName, eventName, userId, error });
|
|
100
|
+
return null;
|
|
101
|
+
} finally {
|
|
102
|
+
// Reset role back to default before releasing connection
|
|
103
|
+
await client.query('RESET ROLE');
|
|
104
|
+
client.release();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Get a message by ID (used by RealtimeManager after pg_notify)
|
|
110
|
+
*/
|
|
111
|
+
async getById(id: string): Promise<RealtimeMessage | null> {
|
|
112
|
+
const result = await this.getPool().query(
|
|
113
|
+
`SELECT
|
|
114
|
+
id,
|
|
115
|
+
event_name as "eventName",
|
|
116
|
+
channel_id as "channelId",
|
|
117
|
+
channel_name as "channelName",
|
|
118
|
+
payload,
|
|
119
|
+
sender_type as "senderType",
|
|
120
|
+
sender_id as "senderId",
|
|
121
|
+
ws_audience_count as "wsAudienceCount",
|
|
122
|
+
wh_audience_count as "whAudienceCount",
|
|
123
|
+
wh_delivered_count as "whDeliveredCount",
|
|
124
|
+
created_at as "createdAt"
|
|
125
|
+
FROM realtime.messages
|
|
126
|
+
WHERE id = $1`,
|
|
127
|
+
[id]
|
|
128
|
+
);
|
|
129
|
+
return result.rows[0] || null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async list(
|
|
133
|
+
options: {
|
|
134
|
+
channelId?: string;
|
|
135
|
+
eventName?: string;
|
|
136
|
+
limit?: number;
|
|
137
|
+
offset?: number;
|
|
138
|
+
} = {}
|
|
139
|
+
): Promise<RealtimeMessage[]> {
|
|
140
|
+
const { channelId, eventName, limit = 100, offset = 0 } = options;
|
|
141
|
+
|
|
142
|
+
let query = `
|
|
143
|
+
SELECT
|
|
144
|
+
id,
|
|
145
|
+
event_name as "eventName",
|
|
146
|
+
channel_id as "channelId",
|
|
147
|
+
channel_name as "channelName",
|
|
148
|
+
payload,
|
|
149
|
+
sender_type as "senderType",
|
|
150
|
+
sender_id as "senderId",
|
|
151
|
+
ws_audience_count as "wsAudienceCount",
|
|
152
|
+
wh_audience_count as "whAudienceCount",
|
|
153
|
+
wh_delivered_count as "whDeliveredCount",
|
|
154
|
+
created_at as "createdAt"
|
|
155
|
+
FROM realtime.messages
|
|
156
|
+
WHERE 1=1
|
|
157
|
+
`;
|
|
158
|
+
|
|
159
|
+
const params: (string | number)[] = [];
|
|
160
|
+
let paramIndex = 1;
|
|
161
|
+
|
|
162
|
+
if (channelId) {
|
|
163
|
+
query += ` AND channel_id = $${paramIndex++}`;
|
|
164
|
+
params.push(channelId);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (eventName) {
|
|
168
|
+
query += ` AND event_name = $${paramIndex++}`;
|
|
169
|
+
params.push(eventName);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
query += ` ORDER BY created_at DESC LIMIT $${paramIndex++} OFFSET $${paramIndex++}`;
|
|
173
|
+
params.push(limit, offset);
|
|
174
|
+
|
|
175
|
+
const result = await this.getPool().query(query, params);
|
|
176
|
+
return result.rows;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Update message record with delivery statistics
|
|
181
|
+
*/
|
|
182
|
+
async updateDeliveryStats(
|
|
183
|
+
messageId: string,
|
|
184
|
+
stats: {
|
|
185
|
+
wsAudienceCount: number;
|
|
186
|
+
whAudienceCount: number;
|
|
187
|
+
whDeliveredCount: number;
|
|
188
|
+
}
|
|
189
|
+
): Promise<void> {
|
|
190
|
+
await this.getPool().query(
|
|
191
|
+
`UPDATE realtime.messages
|
|
192
|
+
SET
|
|
193
|
+
ws_audience_count = $2,
|
|
194
|
+
wh_audience_count = $3,
|
|
195
|
+
wh_delivered_count = $4
|
|
196
|
+
WHERE id = $1`,
|
|
197
|
+
[messageId, stats.wsAudienceCount, stats.whAudienceCount, stats.whDeliveredCount]
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async getStats(
|
|
202
|
+
options: {
|
|
203
|
+
channelId?: string;
|
|
204
|
+
since?: Date;
|
|
205
|
+
} = {}
|
|
206
|
+
): Promise<{
|
|
207
|
+
totalMessages: number;
|
|
208
|
+
whDeliveryRate: number;
|
|
209
|
+
topEvents: { eventName: string; count: number }[];
|
|
210
|
+
}> {
|
|
211
|
+
const { channelId, since } = options;
|
|
212
|
+
|
|
213
|
+
let whereClause = '1=1';
|
|
214
|
+
const params: (string | Date)[] = [];
|
|
215
|
+
let paramIndex = 1;
|
|
216
|
+
|
|
217
|
+
if (channelId) {
|
|
218
|
+
whereClause += ` AND channel_id = $${paramIndex++}`;
|
|
219
|
+
params.push(channelId);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (since) {
|
|
223
|
+
whereClause += ` AND created_at >= $${paramIndex++}`;
|
|
224
|
+
params.push(since);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const statsResult = await this.getPool().query(
|
|
228
|
+
`SELECT
|
|
229
|
+
COUNT(*) as total_messages,
|
|
230
|
+
SUM(wh_audience_count) as wh_audience_total,
|
|
231
|
+
SUM(wh_delivered_count) as wh_delivered_total
|
|
232
|
+
FROM realtime.messages
|
|
233
|
+
WHERE ${whereClause}`,
|
|
234
|
+
params
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
const topEventsResult = await this.getPool().query(
|
|
238
|
+
`SELECT event_name, COUNT(*) as count
|
|
239
|
+
FROM realtime.messages
|
|
240
|
+
WHERE ${whereClause}
|
|
241
|
+
GROUP BY event_name
|
|
242
|
+
ORDER BY count DESC
|
|
243
|
+
LIMIT 10`,
|
|
244
|
+
params
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
const stats = statsResult.rows[0];
|
|
248
|
+
const whAudienceTotal = parseInt(stats.wh_audience_total) || 0;
|
|
249
|
+
const whDeliveredTotal = parseInt(stats.wh_delivered_total) || 0;
|
|
250
|
+
|
|
251
|
+
return {
|
|
252
|
+
totalMessages: parseInt(stats.total_messages) || 0,
|
|
253
|
+
whDeliveryRate: whAudienceTotal > 0 ? whDeliveredTotal / whAudienceTotal : 0,
|
|
254
|
+
topEvents: topEventsResult.rows.map((row) => ({
|
|
255
|
+
eventName: row.event_name,
|
|
256
|
+
count: parseInt(row.count),
|
|
257
|
+
})),
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Pool } from 'pg';
|
|
2
2
|
import crypto from 'crypto';
|
|
3
|
-
import { DatabaseManager } from '@/
|
|
3
|
+
import { DatabaseManager } from '@/infra/database/database.manager.js';
|
|
4
4
|
import logger from '@/utils/logger.js';
|
|
5
|
-
import {
|
|
5
|
+
import { EncryptionManager } from '@/infra/security/encryption.manager.js';
|
|
6
6
|
|
|
7
7
|
export interface SecretSchema {
|
|
8
8
|
id: string;
|
|
@@ -29,11 +29,19 @@ export interface UpdateSecretInput {
|
|
|
29
29
|
expiresAt?: Date | null;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
export class
|
|
32
|
+
export class SecretService {
|
|
33
|
+
private static instance: SecretService;
|
|
33
34
|
private pool: Pool | null = null;
|
|
34
35
|
|
|
35
|
-
constructor() {
|
|
36
|
-
// Encryption is now handled by the shared
|
|
36
|
+
private constructor() {
|
|
37
|
+
// Encryption is now handled by the shared EncryptionManager
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public static getInstance(): SecretService {
|
|
41
|
+
if (!SecretService.instance) {
|
|
42
|
+
SecretService.instance = new SecretService();
|
|
43
|
+
}
|
|
44
|
+
return SecretService.instance;
|
|
37
45
|
}
|
|
38
46
|
|
|
39
47
|
private getPool(): Pool {
|
|
@@ -47,11 +55,10 @@ export class SecretsService {
|
|
|
47
55
|
* Create a new secret
|
|
48
56
|
*/
|
|
49
57
|
async createSecret(input: CreateSecretInput): Promise<{ id: string }> {
|
|
50
|
-
const client = await this.getPool().connect();
|
|
51
58
|
try {
|
|
52
|
-
const encryptedValue =
|
|
59
|
+
const encryptedValue = EncryptionManager.encrypt(input.value);
|
|
53
60
|
|
|
54
|
-
const result = await
|
|
61
|
+
const result = await this.getPool().query(
|
|
55
62
|
`INSERT INTO _secrets (key, value_ciphertext, is_reserved, expires_at)
|
|
56
63
|
VALUES ($1, $2, $3, $4)
|
|
57
64
|
RETURNING id`,
|
|
@@ -63,8 +70,6 @@ export class SecretsService {
|
|
|
63
70
|
} catch (error) {
|
|
64
71
|
logger.error('Failed to create secret', { error, key: input.key });
|
|
65
72
|
throw new Error('Failed to create secret');
|
|
66
|
-
} finally {
|
|
67
|
-
client.release();
|
|
68
73
|
}
|
|
69
74
|
}
|
|
70
75
|
|
|
@@ -72,9 +77,8 @@ export class SecretsService {
|
|
|
72
77
|
* Get a decrypted secret by ID
|
|
73
78
|
*/
|
|
74
79
|
async getSecretById(id: string): Promise<string | null> {
|
|
75
|
-
const client = await this.getPool().connect();
|
|
76
80
|
try {
|
|
77
|
-
const result = await
|
|
81
|
+
const result = await this.getPool().query(
|
|
78
82
|
`UPDATE _secrets
|
|
79
83
|
SET last_used_at = NOW()
|
|
80
84
|
WHERE id = $1 AND is_active = true
|
|
@@ -83,18 +87,16 @@ export class SecretsService {
|
|
|
83
87
|
[id]
|
|
84
88
|
);
|
|
85
89
|
|
|
86
|
-
if (result.rows.length
|
|
90
|
+
if (!result.rows.length) {
|
|
87
91
|
return null;
|
|
88
92
|
}
|
|
89
93
|
|
|
90
|
-
const decryptedValue =
|
|
94
|
+
const decryptedValue = EncryptionManager.decrypt(result.rows[0].value_ciphertext);
|
|
91
95
|
logger.info('Secret retrieved', { id });
|
|
92
96
|
return decryptedValue;
|
|
93
97
|
} catch (error) {
|
|
94
98
|
logger.error('Failed to get secret', { error, id });
|
|
95
99
|
throw new Error('Failed to get secret');
|
|
96
|
-
} finally {
|
|
97
|
-
client.release();
|
|
98
100
|
}
|
|
99
101
|
}
|
|
100
102
|
|
|
@@ -102,9 +104,8 @@ export class SecretsService {
|
|
|
102
104
|
* Get a decrypted secret by key
|
|
103
105
|
*/
|
|
104
106
|
async getSecretByKey(key: string): Promise<string | null> {
|
|
105
|
-
const client = await this.getPool().connect();
|
|
106
107
|
try {
|
|
107
|
-
const result = await
|
|
108
|
+
const result = await this.getPool().query(
|
|
108
109
|
`UPDATE _secrets
|
|
109
110
|
SET last_used_at = NOW()
|
|
110
111
|
WHERE key = $1 AND is_active = true
|
|
@@ -113,18 +114,16 @@ export class SecretsService {
|
|
|
113
114
|
[key]
|
|
114
115
|
);
|
|
115
116
|
|
|
116
|
-
if (result.rows.length
|
|
117
|
+
if (!result.rows.length) {
|
|
117
118
|
return null;
|
|
118
119
|
}
|
|
119
120
|
|
|
120
|
-
const decryptedValue =
|
|
121
|
+
const decryptedValue = EncryptionManager.decrypt(result.rows[0].value_ciphertext);
|
|
121
122
|
logger.info('Secret retrieved by key', { key });
|
|
122
123
|
return decryptedValue;
|
|
123
124
|
} catch (error) {
|
|
124
125
|
logger.error('Failed to get secret by key', { error, key });
|
|
125
126
|
throw new Error('Failed to get secret');
|
|
126
|
-
} finally {
|
|
127
|
-
client.release();
|
|
128
127
|
}
|
|
129
128
|
}
|
|
130
129
|
|
|
@@ -132,9 +131,8 @@ export class SecretsService {
|
|
|
132
131
|
* List all secrets (without decrypting values)
|
|
133
132
|
*/
|
|
134
133
|
async listSecrets(): Promise<SecretSchema[]> {
|
|
135
|
-
const client = await this.getPool().connect();
|
|
136
134
|
try {
|
|
137
|
-
const result = await
|
|
135
|
+
const result = await this.getPool().query(
|
|
138
136
|
`SELECT
|
|
139
137
|
id,
|
|
140
138
|
key,
|
|
@@ -152,8 +150,6 @@ export class SecretsService {
|
|
|
152
150
|
} catch (error) {
|
|
153
151
|
logger.error('Failed to list secrets', { error });
|
|
154
152
|
throw new Error('Failed to list secrets');
|
|
155
|
-
} finally {
|
|
156
|
-
client.release();
|
|
157
153
|
}
|
|
158
154
|
}
|
|
159
155
|
|
|
@@ -161,14 +157,13 @@ export class SecretsService {
|
|
|
161
157
|
* Update a secret
|
|
162
158
|
*/
|
|
163
159
|
async updateSecret(id: string, input: UpdateSecretInput): Promise<boolean> {
|
|
164
|
-
const client = await this.getPool().connect();
|
|
165
160
|
try {
|
|
166
161
|
const updates: string[] = [];
|
|
167
162
|
const values: (string | boolean | Date | null)[] = [];
|
|
168
163
|
let paramCount = 1;
|
|
169
164
|
|
|
170
165
|
if (input.value !== undefined) {
|
|
171
|
-
const encryptedValue =
|
|
166
|
+
const encryptedValue = EncryptionManager.encrypt(input.value);
|
|
172
167
|
updates.push(`value_ciphertext = $${paramCount++}`);
|
|
173
168
|
values.push(encryptedValue);
|
|
174
169
|
}
|
|
@@ -190,7 +185,7 @@ export class SecretsService {
|
|
|
190
185
|
|
|
191
186
|
values.push(id);
|
|
192
187
|
|
|
193
|
-
const result = await
|
|
188
|
+
const result = await this.getPool().query(
|
|
194
189
|
`UPDATE _secrets
|
|
195
190
|
SET ${updates.join(', ')}
|
|
196
191
|
WHERE id = $${paramCount}`,
|
|
@@ -205,8 +200,6 @@ export class SecretsService {
|
|
|
205
200
|
} catch (error) {
|
|
206
201
|
logger.error('Failed to update secret', { error, id });
|
|
207
202
|
throw new Error('Failed to update secret');
|
|
208
|
-
} finally {
|
|
209
|
-
client.release();
|
|
210
203
|
}
|
|
211
204
|
}
|
|
212
205
|
|
|
@@ -214,34 +207,27 @@ export class SecretsService {
|
|
|
214
207
|
* Check if a secret value matches the stored value
|
|
215
208
|
*/
|
|
216
209
|
async checkSecretByKey(key: string, value: string): Promise<boolean> {
|
|
217
|
-
const client = await this.getPool().connect();
|
|
218
210
|
try {
|
|
219
|
-
|
|
220
|
-
|
|
211
|
+
// Optimized: Single query that retrieves and updates in one operation
|
|
212
|
+
const result = await this.getPool().query(
|
|
213
|
+
`UPDATE _secrets
|
|
214
|
+
SET last_used_at = NOW()
|
|
221
215
|
WHERE key = $1
|
|
222
216
|
AND is_active = true
|
|
223
217
|
AND (expires_at IS NULL OR expires_at > NOW())
|
|
224
|
-
|
|
218
|
+
RETURNING value_ciphertext`,
|
|
225
219
|
[key]
|
|
226
220
|
);
|
|
227
221
|
|
|
228
|
-
if (result.rows.length
|
|
222
|
+
if (!result.rows.length) {
|
|
229
223
|
logger.warn('Secret not found for verification', { key });
|
|
230
224
|
return false;
|
|
231
225
|
}
|
|
232
226
|
|
|
233
|
-
const decryptedValue =
|
|
227
|
+
const decryptedValue = EncryptionManager.decrypt(result.rows[0].value_ciphertext);
|
|
234
228
|
const matches = decryptedValue === value;
|
|
235
229
|
|
|
236
|
-
// Update last_used_at if the check was successful
|
|
237
230
|
if (matches) {
|
|
238
|
-
await client.query(
|
|
239
|
-
`UPDATE _secrets
|
|
240
|
-
SET last_used_at = NOW()
|
|
241
|
-
WHERE key = $1
|
|
242
|
-
AND is_active = true`,
|
|
243
|
-
[key]
|
|
244
|
-
);
|
|
245
231
|
logger.info('Secret check successful', { key });
|
|
246
232
|
} else {
|
|
247
233
|
logger.warn('Secret check failed - value mismatch', { key });
|
|
@@ -251,8 +237,6 @@ export class SecretsService {
|
|
|
251
237
|
} catch (error) {
|
|
252
238
|
logger.error('Failed to check secret', { error, key });
|
|
253
239
|
return false;
|
|
254
|
-
} finally {
|
|
255
|
-
client.release();
|
|
256
240
|
}
|
|
257
241
|
}
|
|
258
242
|
|
|
@@ -260,29 +244,30 @@ export class SecretsService {
|
|
|
260
244
|
* Delete a secret
|
|
261
245
|
*/
|
|
262
246
|
async deleteSecret(id: string): Promise<boolean> {
|
|
263
|
-
const client = await this.getPool().connect();
|
|
264
247
|
try {
|
|
265
|
-
//
|
|
266
|
-
const
|
|
267
|
-
id,
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
if (checkResult.rows.length > 0 && checkResult.rows[0].is_reserved) {
|
|
271
|
-
throw new Error('Cannot delete reserved secret');
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
const result = await client.query('DELETE FROM _secrets WHERE id = $1', [id]);
|
|
248
|
+
// Optimized: Single query with WHERE clause to prevent deleting reserved secrets
|
|
249
|
+
const result = await this.getPool().query(
|
|
250
|
+
'DELETE FROM _secrets WHERE id = $1 AND is_reserved = false',
|
|
251
|
+
[id]
|
|
252
|
+
);
|
|
275
253
|
|
|
276
254
|
const success = (result.rowCount ?? 0) > 0;
|
|
277
255
|
if (success) {
|
|
278
256
|
logger.info('Secret deleted', { id });
|
|
257
|
+
} else {
|
|
258
|
+
// Check if it exists but is reserved
|
|
259
|
+
const checkResult = await this.getPool().query(
|
|
260
|
+
'SELECT is_reserved FROM _secrets WHERE id = $1',
|
|
261
|
+
[id]
|
|
262
|
+
);
|
|
263
|
+
if (checkResult.rows.length && checkResult.rows[0].is_reserved) {
|
|
264
|
+
throw new Error('Cannot delete reserved secret');
|
|
265
|
+
}
|
|
279
266
|
}
|
|
280
267
|
return success;
|
|
281
268
|
} catch (error) {
|
|
282
269
|
logger.error('Failed to delete secret', { error, id });
|
|
283
270
|
throw new Error('Failed to delete secret');
|
|
284
|
-
} finally {
|
|
285
|
-
client.release();
|
|
286
271
|
}
|
|
287
272
|
}
|
|
288
273
|
|
|
@@ -296,7 +281,7 @@ export class SecretsService {
|
|
|
296
281
|
|
|
297
282
|
const oldSecretResult = await client.query(`SELECT key FROM _secrets WHERE id = $1`, [id]);
|
|
298
283
|
|
|
299
|
-
if (oldSecretResult.rows.length
|
|
284
|
+
if (!oldSecretResult.rows.length) {
|
|
300
285
|
throw new Error('Secret not found');
|
|
301
286
|
}
|
|
302
287
|
|
|
@@ -310,7 +295,7 @@ export class SecretsService {
|
|
|
310
295
|
[id]
|
|
311
296
|
);
|
|
312
297
|
|
|
313
|
-
const encryptedValue =
|
|
298
|
+
const encryptedValue = EncryptionManager.encrypt(newValue);
|
|
314
299
|
const newSecretResult = await client.query(
|
|
315
300
|
`INSERT INTO _secrets (key, value_ciphertext)
|
|
316
301
|
VALUES ($1, $2)
|
|
@@ -340,9 +325,8 @@ export class SecretsService {
|
|
|
340
325
|
* Clean up expired secrets
|
|
341
326
|
*/
|
|
342
327
|
async cleanupExpiredSecrets(): Promise<number> {
|
|
343
|
-
const client = await this.getPool().connect();
|
|
344
328
|
try {
|
|
345
|
-
const result = await
|
|
329
|
+
const result = await this.getPool().query(
|
|
346
330
|
`DELETE FROM _secrets
|
|
347
331
|
WHERE expires_at IS NOT NULL
|
|
348
332
|
AND expires_at < NOW()
|
|
@@ -357,8 +341,6 @@ export class SecretsService {
|
|
|
357
341
|
} catch (error) {
|
|
358
342
|
logger.error('Failed to cleanup expired secrets', { error });
|
|
359
343
|
throw new Error('Failed to cleanup expired secrets');
|
|
360
|
-
} finally {
|
|
361
|
-
client.release();
|
|
362
344
|
}
|
|
363
345
|
}
|
|
364
346
|
|