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,233 @@
|
|
|
1
|
+
-- Migration 017: Create Realtime Schema
|
|
2
|
+
--
|
|
3
|
+
-- Creates the realtime schema with:
|
|
4
|
+
-- 1. channels table - Channel definitions with webhook configuration
|
|
5
|
+
-- 2. messages table - All realtime messages with delivery statistics
|
|
6
|
+
-- 3. publish() function - Called by developer triggers to publish events
|
|
7
|
+
--
|
|
8
|
+
-- Permission Model (Supabase pattern):
|
|
9
|
+
-- - SELECT on channels = 'subscribe' permission (subscribe to channel)
|
|
10
|
+
-- - INSERT on messages = 'publish' permission (publish to channel)
|
|
11
|
+
-- Developers define RLS policies on channels/messages table to control access.
|
|
12
|
+
|
|
13
|
+
-- ============================================================================
|
|
14
|
+
-- CREATE SCHEMA
|
|
15
|
+
-- ============================================================================
|
|
16
|
+
|
|
17
|
+
CREATE SCHEMA IF NOT EXISTS realtime;
|
|
18
|
+
|
|
19
|
+
-- ============================================================================
|
|
20
|
+
-- CHANNELS TABLE
|
|
21
|
+
-- ============================================================================
|
|
22
|
+
-- Stores channel definitions with delivery configuration.
|
|
23
|
+
-- RLS policies control subscribe permissions.
|
|
24
|
+
-- - SELECT policy = 'subscribe' permission (can subscribe to channel)
|
|
25
|
+
-- Channel names use : as separator and % for wildcards (LIKE pattern).
|
|
26
|
+
-- Examples: "orders", "order:%", "chat:%:messages"
|
|
27
|
+
|
|
28
|
+
CREATE TABLE IF NOT EXISTS realtime.channels (
|
|
29
|
+
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
|
30
|
+
|
|
31
|
+
-- Channel name pattern (e.g., "orders", "order:%", "chat:%:messages")
|
|
32
|
+
-- Convention: use : as separator, % for wildcards (LIKE pattern)
|
|
33
|
+
pattern TEXT UNIQUE NOT NULL,
|
|
34
|
+
|
|
35
|
+
-- Human-readable description
|
|
36
|
+
description TEXT,
|
|
37
|
+
|
|
38
|
+
-- Webhook URLs to POST events to (NULL or empty array = no webhooks)
|
|
39
|
+
webhook_urls TEXT[],
|
|
40
|
+
|
|
41
|
+
-- Whether this channel is active
|
|
42
|
+
enabled BOOLEAN DEFAULT TRUE NOT NULL,
|
|
43
|
+
|
|
44
|
+
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
45
|
+
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
-- ============================================================================
|
|
49
|
+
-- MESSAGES TABLE
|
|
50
|
+
-- ============================================================================
|
|
51
|
+
-- Stores all realtime messages published through the system.
|
|
52
|
+
-- RLS policies on this table control publish permissions:
|
|
53
|
+
-- - INSERT policy = 'publish' permission (can publish to channel)
|
|
54
|
+
|
|
55
|
+
CREATE TABLE IF NOT EXISTS realtime.messages (
|
|
56
|
+
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
|
57
|
+
|
|
58
|
+
-- Event metadata
|
|
59
|
+
event_name TEXT NOT NULL,
|
|
60
|
+
|
|
61
|
+
-- Channel reference (SET NULL on delete to preserve history)
|
|
62
|
+
channel_id UUID REFERENCES realtime.channels(id) ON DELETE SET NULL,
|
|
63
|
+
channel_name TEXT NOT NULL, -- Denormalized for query convenience after channel deletion
|
|
64
|
+
|
|
65
|
+
-- Event payload (stored for audit/replay purposes)
|
|
66
|
+
payload JSONB DEFAULT '{}'::jsonb NOT NULL,
|
|
67
|
+
|
|
68
|
+
-- Sender information
|
|
69
|
+
-- 'system' = triggered by database trigger (via publish() function)
|
|
70
|
+
-- 'user' = published by client via WebSocket
|
|
71
|
+
sender_type TEXT DEFAULT 'system' NOT NULL CHECK (sender_type IN ('system', 'user')),
|
|
72
|
+
sender_id UUID, -- User ID for 'user' type, NULL for 'system' type
|
|
73
|
+
|
|
74
|
+
-- Delivery statistics for WebSocket
|
|
75
|
+
ws_audience_count INTEGER DEFAULT 0 NOT NULL, -- How many clients were subscribed
|
|
76
|
+
|
|
77
|
+
-- Delivery statistics for Webhooks
|
|
78
|
+
wh_audience_count INTEGER DEFAULT 0 NOT NULL, -- How many webhook URLs configured
|
|
79
|
+
wh_delivered_count INTEGER DEFAULT 0 NOT NULL, -- How many succeeded (2xx response)
|
|
80
|
+
|
|
81
|
+
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
-- ============================================================================
|
|
85
|
+
-- INDEXES
|
|
86
|
+
-- ============================================================================
|
|
87
|
+
|
|
88
|
+
CREATE INDEX IF NOT EXISTS idx_realtime_channels_pattern ON realtime.channels(pattern);
|
|
89
|
+
CREATE INDEX IF NOT EXISTS idx_realtime_channels_enabled ON realtime.channels(enabled);
|
|
90
|
+
CREATE INDEX IF NOT EXISTS idx_realtime_messages_channel_id ON realtime.messages(channel_id);
|
|
91
|
+
CREATE INDEX IF NOT EXISTS idx_realtime_messages_channel_name ON realtime.messages(channel_name);
|
|
92
|
+
CREATE INDEX IF NOT EXISTS idx_realtime_messages_created_at ON realtime.messages(created_at DESC);
|
|
93
|
+
CREATE INDEX IF NOT EXISTS idx_realtime_messages_event_name ON realtime.messages(event_name);
|
|
94
|
+
CREATE INDEX IF NOT EXISTS idx_realtime_messages_sender ON realtime.messages(sender_type, sender_id);
|
|
95
|
+
|
|
96
|
+
-- ============================================================================
|
|
97
|
+
-- UPDATED_AT TRIGGER
|
|
98
|
+
-- ============================================================================
|
|
99
|
+
|
|
100
|
+
CREATE OR REPLACE FUNCTION realtime.update_updated_at()
|
|
101
|
+
RETURNS TRIGGER AS $$
|
|
102
|
+
BEGIN
|
|
103
|
+
NEW.updated_at = NOW();
|
|
104
|
+
RETURN NEW;
|
|
105
|
+
END;
|
|
106
|
+
$$ LANGUAGE plpgsql;
|
|
107
|
+
|
|
108
|
+
DROP TRIGGER IF EXISTS trg_channels_updated_at ON realtime.channels;
|
|
109
|
+
CREATE TRIGGER trg_channels_updated_at
|
|
110
|
+
BEFORE UPDATE ON realtime.channels
|
|
111
|
+
FOR EACH ROW EXECUTE FUNCTION realtime.update_updated_at();
|
|
112
|
+
|
|
113
|
+
-- ============================================================================
|
|
114
|
+
-- ROW LEVEL SECURITY (DISABLED BY DEFAULT)
|
|
115
|
+
-- ============================================================================
|
|
116
|
+
-- RLS is disabled by default for the best developer experience.
|
|
117
|
+
-- Channels and messages are open to all authenticated/anon users out of the box.
|
|
118
|
+
--
|
|
119
|
+
-- To enable access control, developers can:
|
|
120
|
+
-- 1. Enable RLS: ALTER TABLE realtime.channels ENABLE ROW LEVEL SECURITY;
|
|
121
|
+
-- 2. Add policies using the helper function realtime.channel_name()
|
|
122
|
+
--
|
|
123
|
+
-- See documentation for policy examples.
|
|
124
|
+
|
|
125
|
+
-- ============================================================================
|
|
126
|
+
-- HELPER FUNCTIONS FOR RLS POLICIES
|
|
127
|
+
-- ============================================================================
|
|
128
|
+
|
|
129
|
+
-- Returns the channel name being accessed (set by backend during permission checks)
|
|
130
|
+
-- Developers use this in policies instead of writing current_setting directly
|
|
131
|
+
CREATE OR REPLACE FUNCTION realtime.channel_name()
|
|
132
|
+
RETURNS TEXT AS $$
|
|
133
|
+
SELECT current_setting('realtime.channel_name', true);
|
|
134
|
+
$$ LANGUAGE SQL STABLE;
|
|
135
|
+
|
|
136
|
+
-- ============================================================================
|
|
137
|
+
-- PUBLISH FUNCTION
|
|
138
|
+
-- ============================================================================
|
|
139
|
+
-- Called by developer triggers to publish events to channels.
|
|
140
|
+
-- This function can only be executed by the backend (SECURITY DEFINER).
|
|
141
|
+
--
|
|
142
|
+
-- Usage in a trigger:
|
|
143
|
+
-- PERFORM realtime.publish(
|
|
144
|
+
-- 'order:' || NEW.id::text, -- channel name (resolved instance)
|
|
145
|
+
-- 'order_updated', -- event name
|
|
146
|
+
-- jsonb_build_object('id', NEW.id, 'status', NEW.status) -- payload
|
|
147
|
+
-- );
|
|
148
|
+
|
|
149
|
+
CREATE OR REPLACE FUNCTION realtime.publish(
|
|
150
|
+
p_channel_name TEXT,
|
|
151
|
+
p_event_name TEXT,
|
|
152
|
+
p_payload JSONB
|
|
153
|
+
)
|
|
154
|
+
RETURNS UUID AS $$
|
|
155
|
+
DECLARE
|
|
156
|
+
v_channel_id UUID;
|
|
157
|
+
v_message_id UUID;
|
|
158
|
+
BEGIN
|
|
159
|
+
-- Find matching channel: exact match first, then wildcard pattern match
|
|
160
|
+
-- For wildcard patterns like "order:%", check if p_channel_name LIKE pattern
|
|
161
|
+
SELECT id INTO v_channel_id
|
|
162
|
+
FROM realtime.channels
|
|
163
|
+
WHERE enabled = TRUE
|
|
164
|
+
AND (pattern = p_channel_name OR p_channel_name LIKE pattern)
|
|
165
|
+
ORDER BY pattern = p_channel_name DESC
|
|
166
|
+
LIMIT 1;
|
|
167
|
+
|
|
168
|
+
-- If no channel found, raise a warning and return NULL
|
|
169
|
+
IF v_channel_id IS NULL THEN
|
|
170
|
+
RAISE WARNING 'Realtime: No matching channel found for "%"', p_channel_name;
|
|
171
|
+
RETURN NULL;
|
|
172
|
+
END IF;
|
|
173
|
+
|
|
174
|
+
-- Insert message record (system-triggered, so sender_type = 'system')
|
|
175
|
+
INSERT INTO realtime.messages (
|
|
176
|
+
event_name,
|
|
177
|
+
channel_id,
|
|
178
|
+
channel_name,
|
|
179
|
+
payload,
|
|
180
|
+
sender_type
|
|
181
|
+
) VALUES (
|
|
182
|
+
p_event_name,
|
|
183
|
+
v_channel_id,
|
|
184
|
+
p_channel_name,
|
|
185
|
+
p_payload,
|
|
186
|
+
'system'
|
|
187
|
+
)
|
|
188
|
+
RETURNING id INTO v_message_id;
|
|
189
|
+
|
|
190
|
+
RETURN v_message_id;
|
|
191
|
+
END;
|
|
192
|
+
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
|
193
|
+
|
|
194
|
+
-- Revoke execute from public, only backend can call this
|
|
195
|
+
REVOKE ALL ON FUNCTION realtime.publish FROM PUBLIC;
|
|
196
|
+
|
|
197
|
+
-- ============================================================================
|
|
198
|
+
-- TRIGGER FOR PG_NOTIFY
|
|
199
|
+
-- ============================================================================
|
|
200
|
+
-- Trigger function that sends pg_notify for every new message (both system and client).
|
|
201
|
+
|
|
202
|
+
CREATE OR REPLACE FUNCTION realtime.notify_on_message_insert()
|
|
203
|
+
RETURNS TRIGGER AS $$
|
|
204
|
+
BEGIN
|
|
205
|
+
-- Send only message_id to bypass pg_notify 8KB payload limit
|
|
206
|
+
-- Backend will fetch full message from DB
|
|
207
|
+
PERFORM pg_notify('realtime_message', NEW.id::text);
|
|
208
|
+
RETURN NEW;
|
|
209
|
+
END;
|
|
210
|
+
$$ LANGUAGE plpgsql;
|
|
211
|
+
|
|
212
|
+
-- Create trigger on messages table
|
|
213
|
+
DROP TRIGGER IF EXISTS trg_message_notify ON realtime.messages;
|
|
214
|
+
CREATE TRIGGER trg_message_notify
|
|
215
|
+
AFTER INSERT ON realtime.messages
|
|
216
|
+
FOR EACH ROW
|
|
217
|
+
EXECUTE FUNCTION realtime.notify_on_message_insert();
|
|
218
|
+
|
|
219
|
+
-- ============================================================================
|
|
220
|
+
-- GRANTS
|
|
221
|
+
-- ============================================================================
|
|
222
|
+
|
|
223
|
+
-- Grant schema access to both authenticated and anonymous users
|
|
224
|
+
GRANT USAGE ON SCHEMA realtime TO authenticated, anon;
|
|
225
|
+
|
|
226
|
+
-- Grant SELECT on channels table (allows subscribe)
|
|
227
|
+
GRANT SELECT ON realtime.channels TO authenticated, anon;
|
|
228
|
+
|
|
229
|
+
-- Grant INSERT on messages table (allows publish)
|
|
230
|
+
GRANT INSERT ON realtime.messages TO authenticated, anon;
|
|
231
|
+
|
|
232
|
+
-- Grant execution permission on helper function (used when RLS is enabled)
|
|
233
|
+
GRANT EXECUTE ON FUNCTION realtime.channel_name() TO authenticated, anon;
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import type { Client } from 'pg';
|
|
2
|
+
import { SocketManager } from '@/infra/socket/socket.manager.js';
|
|
3
|
+
import { WebhookSender } from './webhook-sender.js';
|
|
4
|
+
import { DatabaseManager } from '@/infra/database/database.manager.js';
|
|
5
|
+
import { RealtimeChannelService } from '@/services/realtime/realtime-channel.service.js';
|
|
6
|
+
import { RealtimeMessageService } from '@/services/realtime/realtime-message.service.js';
|
|
7
|
+
import logger from '@/utils/logger.js';
|
|
8
|
+
import type { RealtimeMessage, RealtimeChannel, WebhookMessage } from '@insforge/shared-schemas';
|
|
9
|
+
import type { DeliveryResult } from '@/types/realtime.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* RealtimeManager - Listens to pg_notify and publishes messages to WebSocket/webhooks
|
|
13
|
+
*
|
|
14
|
+
* This is a singleton that:
|
|
15
|
+
* 1. Maintains a dedicated PostgreSQL connection for LISTEN
|
|
16
|
+
* 2. Receives notifications from realtime.publish() function
|
|
17
|
+
* 3. Publishes messages to WebSocket clients (via Socket.IO rooms)
|
|
18
|
+
* 4. Publishes messages to webhook URLs (via HTTP POST)
|
|
19
|
+
* 5. Updates message records with delivery statistics
|
|
20
|
+
*/
|
|
21
|
+
export class RealtimeManager {
|
|
22
|
+
private static instance: RealtimeManager;
|
|
23
|
+
private listenerClient: Client | null = null;
|
|
24
|
+
private isConnected = false;
|
|
25
|
+
private reconnectTimeout: NodeJS.Timeout | null = null;
|
|
26
|
+
private reconnectAttempts = 0;
|
|
27
|
+
private readonly maxReconnectAttempts = 10;
|
|
28
|
+
private readonly baseReconnectDelay = 5000;
|
|
29
|
+
private webhookSender: WebhookSender;
|
|
30
|
+
|
|
31
|
+
private constructor() {
|
|
32
|
+
this.webhookSender = new WebhookSender();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static getInstance(): RealtimeManager {
|
|
36
|
+
if (!RealtimeManager.instance) {
|
|
37
|
+
RealtimeManager.instance = new RealtimeManager();
|
|
38
|
+
}
|
|
39
|
+
return RealtimeManager.instance;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Initialize the realtime manager and start listening for pg_notify
|
|
44
|
+
*/
|
|
45
|
+
async initialize(): Promise<void> {
|
|
46
|
+
if (this.isConnected) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Create a dedicated client for LISTEN (cannot use pooled connections)
|
|
51
|
+
this.listenerClient = DatabaseManager.getInstance().createClient();
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
await this.listenerClient.connect();
|
|
55
|
+
await this.listenerClient.query('LISTEN realtime_message');
|
|
56
|
+
this.isConnected = true;
|
|
57
|
+
this.reconnectAttempts = 0;
|
|
58
|
+
|
|
59
|
+
this.listenerClient.on('notification', (msg) => {
|
|
60
|
+
if (msg.channel === 'realtime_message' && msg.payload) {
|
|
61
|
+
void this.handlePGNotification(msg.payload);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
this.listenerClient.on('error', (error) => {
|
|
66
|
+
logger.error('RealtimeManager connection error', { error: error.message });
|
|
67
|
+
this.handleDisconnect();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
this.listenerClient.on('end', () => {
|
|
71
|
+
logger.warn('RealtimeManager connection ended');
|
|
72
|
+
this.handleDisconnect();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
logger.info('RealtimeManager initialized and listening');
|
|
76
|
+
} catch (error) {
|
|
77
|
+
logger.error('Failed to initialize RealtimeManager', { error });
|
|
78
|
+
this.handleDisconnect();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Handle incoming pg_notify notification
|
|
84
|
+
* Payload is just the message_id (UUID string) to bypass 8KB limit
|
|
85
|
+
*/
|
|
86
|
+
private async handlePGNotification(messageId: string): Promise<void> {
|
|
87
|
+
try {
|
|
88
|
+
// 1. Fetch message and channel in parallel
|
|
89
|
+
// channelId is guaranteed non-null for fresh messages (publish/insertMessage validate channel)
|
|
90
|
+
const message = await RealtimeMessageService.getInstance().getById(messageId);
|
|
91
|
+
|
|
92
|
+
if (!message || !message.channelId) {
|
|
93
|
+
logger.warn('Message not found or invalid for realtime notification', { messageId });
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 2. Look up channel configuration (for enabled check and webhook URLs)
|
|
98
|
+
const channel = await RealtimeChannelService.getInstance().getById(message.channelId);
|
|
99
|
+
|
|
100
|
+
if (!channel?.enabled) {
|
|
101
|
+
logger.debug('Channel not found or disabled, skipping', { channelId: message.channelId });
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// 3. Publish to WebSocket and/or Webhooks
|
|
106
|
+
const result = await this.publishMessage(message, channel);
|
|
107
|
+
|
|
108
|
+
// 4. Update message record with delivery stats
|
|
109
|
+
await RealtimeMessageService.getInstance().updateDeliveryStats(messageId, result);
|
|
110
|
+
|
|
111
|
+
logger.debug('Realtime message published', {
|
|
112
|
+
messageId,
|
|
113
|
+
channelName: message.channelName,
|
|
114
|
+
eventName: message.eventName,
|
|
115
|
+
...result,
|
|
116
|
+
});
|
|
117
|
+
} catch (error) {
|
|
118
|
+
logger.error('Failed to publish realtime message', { error, messageId });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Publish message to WebSocket clients and webhook URLs
|
|
124
|
+
*/
|
|
125
|
+
private async publishMessage(
|
|
126
|
+
message: RealtimeMessage,
|
|
127
|
+
channel: RealtimeChannel
|
|
128
|
+
): Promise<DeliveryResult> {
|
|
129
|
+
const result: DeliveryResult = {
|
|
130
|
+
wsAudienceCount: 0,
|
|
131
|
+
whAudienceCount: 0,
|
|
132
|
+
whDeliveredCount: 0,
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// Publish to WebSocket clients
|
|
136
|
+
result.wsAudienceCount = this.publishToWebSocket(message);
|
|
137
|
+
|
|
138
|
+
// Publish to Webhook URLs if configured
|
|
139
|
+
if (channel.webhookUrls && channel.webhookUrls.length > 0) {
|
|
140
|
+
const webhookPayload: WebhookMessage = {
|
|
141
|
+
messageId: message.id,
|
|
142
|
+
channel: message.channelName,
|
|
143
|
+
eventName: message.eventName,
|
|
144
|
+
payload: message.payload,
|
|
145
|
+
};
|
|
146
|
+
const whResult = await this.publishToWebhooks(channel.webhookUrls, webhookPayload);
|
|
147
|
+
result.whAudienceCount = whResult.audienceCount;
|
|
148
|
+
result.whDeliveredCount = whResult.deliveredCount;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Publish message to WebSocket clients subscribed to the channel
|
|
156
|
+
* Returns the number of clients in the room (audience count)
|
|
157
|
+
*/
|
|
158
|
+
private publishToWebSocket(message: RealtimeMessage): number {
|
|
159
|
+
const socketManager = SocketManager.getInstance();
|
|
160
|
+
const roomName = `realtime:${message.channelName}`;
|
|
161
|
+
|
|
162
|
+
const audienceCount = socketManager.getRoomSize(roomName);
|
|
163
|
+
|
|
164
|
+
if (audienceCount > 0) {
|
|
165
|
+
socketManager.broadcastToRoom(
|
|
166
|
+
roomName,
|
|
167
|
+
message.eventName,
|
|
168
|
+
message.payload,
|
|
169
|
+
message.senderType,
|
|
170
|
+
message.senderId ?? undefined,
|
|
171
|
+
message.id
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return audienceCount;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Publish message to all configured webhook URLs
|
|
180
|
+
*/
|
|
181
|
+
private async publishToWebhooks(
|
|
182
|
+
urls: string[],
|
|
183
|
+
message: WebhookMessage
|
|
184
|
+
): Promise<{ audienceCount: number; deliveredCount: number }> {
|
|
185
|
+
const audienceCount = urls.length;
|
|
186
|
+
const results = await this.webhookSender.sendToAll(urls, message);
|
|
187
|
+
const deliveredCount = results.filter((r) => r.success).length;
|
|
188
|
+
|
|
189
|
+
return { audienceCount, deliveredCount };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Handle disconnection and attempt reconnection
|
|
194
|
+
*/
|
|
195
|
+
private handleDisconnect(): void {
|
|
196
|
+
this.isConnected = false;
|
|
197
|
+
|
|
198
|
+
if (this.listenerClient) {
|
|
199
|
+
this.listenerClient.removeAllListeners();
|
|
200
|
+
this.listenerClient = null;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Reconnect with exponential backoff
|
|
204
|
+
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
205
|
+
const delay = this.baseReconnectDelay * Math.pow(2, this.reconnectAttempts);
|
|
206
|
+
this.reconnectAttempts++;
|
|
207
|
+
|
|
208
|
+
if (!this.reconnectTimeout) {
|
|
209
|
+
this.reconnectTimeout = setTimeout(() => {
|
|
210
|
+
this.reconnectTimeout = null;
|
|
211
|
+
logger.info(
|
|
212
|
+
`Attempting to reconnect RealtimeManager (attempt ${this.reconnectAttempts})...`
|
|
213
|
+
);
|
|
214
|
+
void this.initialize();
|
|
215
|
+
}, delay);
|
|
216
|
+
}
|
|
217
|
+
} else {
|
|
218
|
+
logger.error('RealtimeManager max reconnect attempts reached');
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Close the realtime manager connection
|
|
224
|
+
*/
|
|
225
|
+
async close(): Promise<void> {
|
|
226
|
+
if (this.reconnectTimeout) {
|
|
227
|
+
clearTimeout(this.reconnectTimeout);
|
|
228
|
+
this.reconnectTimeout = null;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (this.listenerClient) {
|
|
232
|
+
this.listenerClient.removeAllListeners();
|
|
233
|
+
await this.listenerClient.end();
|
|
234
|
+
this.listenerClient = null;
|
|
235
|
+
this.isConnected = false;
|
|
236
|
+
logger.info('RealtimeManager closed');
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Check if the manager is connected and healthy
|
|
242
|
+
*/
|
|
243
|
+
isHealthy(): boolean {
|
|
244
|
+
return this.isConnected;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import axios, { AxiosError } from 'axios';
|
|
2
|
+
import logger from '@/utils/logger.js';
|
|
3
|
+
import type { WebhookMessage } from '@insforge/shared-schemas';
|
|
4
|
+
|
|
5
|
+
export interface WebhookResult {
|
|
6
|
+
url: string;
|
|
7
|
+
success: boolean;
|
|
8
|
+
statusCode?: number;
|
|
9
|
+
error?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* WebhookSender - Handles HTTP delivery of realtime messages to webhook endpoints
|
|
14
|
+
*/
|
|
15
|
+
export class WebhookSender {
|
|
16
|
+
private readonly timeout = 10000; // 10 seconds
|
|
17
|
+
private readonly maxRetries = 2;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Send message to all webhook URLs in parallel
|
|
21
|
+
*/
|
|
22
|
+
async sendToAll(urls: string[], message: WebhookMessage): Promise<WebhookResult[]> {
|
|
23
|
+
const promises = urls.map((url) => this.send(url, message));
|
|
24
|
+
return Promise.all(promises);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Send message to a single webhook URL with retry logic
|
|
29
|
+
*/
|
|
30
|
+
private async send(url: string, message: WebhookMessage): Promise<WebhookResult> {
|
|
31
|
+
let lastError: string | undefined;
|
|
32
|
+
|
|
33
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
34
|
+
try {
|
|
35
|
+
const response = await axios.post(url, message.payload, {
|
|
36
|
+
timeout: this.timeout,
|
|
37
|
+
headers: {
|
|
38
|
+
'Content-Type': 'application/json',
|
|
39
|
+
'X-InsForge-Event': message.eventName,
|
|
40
|
+
'X-InsForge-Channel': message.channel,
|
|
41
|
+
'X-InsForge-Message-Id': message.messageId,
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
url,
|
|
47
|
+
success: response.status >= 200 && response.status < 300,
|
|
48
|
+
statusCode: response.status,
|
|
49
|
+
};
|
|
50
|
+
} catch (error) {
|
|
51
|
+
const axiosError = error as AxiosError;
|
|
52
|
+
lastError = axiosError.message;
|
|
53
|
+
|
|
54
|
+
if (axiosError.response) {
|
|
55
|
+
// Server responded with error status - don't retry
|
|
56
|
+
return {
|
|
57
|
+
url,
|
|
58
|
+
success: false,
|
|
59
|
+
statusCode: axiosError.response.status,
|
|
60
|
+
error: `HTTP ${axiosError.response.status}`,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Network error - retry with backoff
|
|
65
|
+
if (attempt < this.maxRetries) {
|
|
66
|
+
await this.delay(1000 * (attempt + 1)); // 1s, 2s
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
logger.warn('Webhook delivery failed after retries', { url, error: lastError });
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
url,
|
|
74
|
+
success: false,
|
|
75
|
+
error: lastError,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private delay(ms: number): Promise<void> {
|
|
80
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import crypto from 'crypto';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* EncryptionManager - Handles encryption/decryption operations
|
|
5
|
+
* Infrastructure layer for secrets encryption
|
|
5
6
|
*/
|
|
6
|
-
export class
|
|
7
|
+
export class EncryptionManager {
|
|
7
8
|
private static encryptionKey: Buffer | null = null;
|
|
8
9
|
|
|
9
10
|
private static getEncryptionKey(): Buffer {
|