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
|
@@ -1,772 +0,0 @@
|
|
|
1
|
-
import { DatabaseManager } from '@/core/database/manager.js';
|
|
2
|
-
import { AppError } from '@/api/middleware/error.js';
|
|
3
|
-
import { ERROR_CODES } from '@/types/error-constants.js';
|
|
4
|
-
import {
|
|
5
|
-
COLUMN_TYPES,
|
|
6
|
-
ForeignKeyRow,
|
|
7
|
-
ColumnInfo,
|
|
8
|
-
PrimaryKeyInfo,
|
|
9
|
-
ForeignKeyInfo,
|
|
10
|
-
} from '@/types/database.js';
|
|
11
|
-
import {
|
|
12
|
-
ColumnSchema,
|
|
13
|
-
ColumnType,
|
|
14
|
-
CreateTableResponse,
|
|
15
|
-
GetTableSchemaResponse,
|
|
16
|
-
UpdateTableSchemaRequest,
|
|
17
|
-
UpdateTableSchemaResponse,
|
|
18
|
-
DeleteTableResponse,
|
|
19
|
-
OnDeleteActionSchema,
|
|
20
|
-
OnUpdateActionSchema,
|
|
21
|
-
ForeignKeySchema,
|
|
22
|
-
} from '@insforge/shared-schemas';
|
|
23
|
-
import { validateIdentifier } from '@/utils/validations.js';
|
|
24
|
-
import { convertSqlTypeToColumnType } from '@/utils/helpers';
|
|
25
|
-
|
|
26
|
-
const reservedColumns = {
|
|
27
|
-
id: ColumnType.UUID,
|
|
28
|
-
created_at: ColumnType.DATETIME,
|
|
29
|
-
updated_at: ColumnType.DATETIME,
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const userTableFrozenColumns = ['nickname', 'avatar_url'];
|
|
33
|
-
|
|
34
|
-
const SAFE_FUNCS = new Set(['now()', 'gen_random_uuid()']);
|
|
35
|
-
|
|
36
|
-
function getSafeDollarQuotedLiteral(s: string) {
|
|
37
|
-
let tag = 'val';
|
|
38
|
-
while (s.includes(`$${tag}$`)) {
|
|
39
|
-
tag += '_';
|
|
40
|
-
}
|
|
41
|
-
return `$${tag}$${s}$${tag}$`;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function getSystemDefault(columnType?: ColumnType, isNullable?: boolean): string | null {
|
|
45
|
-
if (!columnType || isNullable) {
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
const fieldType = COLUMN_TYPES[columnType];
|
|
49
|
-
if (!fieldType?.defaultValue) {
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const def = fieldType.defaultValue.trim().toLowerCase();
|
|
54
|
-
if (SAFE_FUNCS.has(def)) {
|
|
55
|
-
return `DEFAULT ${def}`;
|
|
56
|
-
}
|
|
57
|
-
return `DEFAULT ${getSafeDollarQuotedLiteral(def)}`;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export function formatDefaultValue(
|
|
61
|
-
input: string | null | undefined,
|
|
62
|
-
columnType?: ColumnType,
|
|
63
|
-
isNullable?: boolean
|
|
64
|
-
): string {
|
|
65
|
-
if (!input) {
|
|
66
|
-
return getSystemDefault(columnType, isNullable) ?? '';
|
|
67
|
-
}
|
|
68
|
-
const value = input.trim();
|
|
69
|
-
const lowered = value.toLowerCase();
|
|
70
|
-
|
|
71
|
-
if (SAFE_FUNCS.has(lowered)) {
|
|
72
|
-
return `DEFAULT ${lowered}`;
|
|
73
|
-
}
|
|
74
|
-
return `DEFAULT ${getSafeDollarQuotedLiteral(value)}`;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export class DatabaseTableService {
|
|
78
|
-
private dbManager: DatabaseManager;
|
|
79
|
-
|
|
80
|
-
constructor() {
|
|
81
|
-
this.dbManager = DatabaseManager.getInstance();
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* List all tables
|
|
86
|
-
*/
|
|
87
|
-
async listTables(): Promise<string[]> {
|
|
88
|
-
const db = this.dbManager.getDb();
|
|
89
|
-
const tables = await db
|
|
90
|
-
.prepare(
|
|
91
|
-
`
|
|
92
|
-
SELECT table_name as name
|
|
93
|
-
FROM information_schema.tables
|
|
94
|
-
WHERE table_schema = 'public'
|
|
95
|
-
AND table_type = 'BASE TABLE'
|
|
96
|
-
AND table_name NOT LIKE '\\_%'
|
|
97
|
-
AND table_name != 'jwks'
|
|
98
|
-
`
|
|
99
|
-
)
|
|
100
|
-
.all();
|
|
101
|
-
|
|
102
|
-
return tables.map((t: { name: string }) => t.name);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Create a new table
|
|
107
|
-
*/
|
|
108
|
-
async createTable(
|
|
109
|
-
table_name: string,
|
|
110
|
-
columns: ColumnSchema[],
|
|
111
|
-
use_RLS = true
|
|
112
|
-
): Promise<CreateTableResponse> {
|
|
113
|
-
// Validate table name
|
|
114
|
-
validateIdentifier(table_name, 'table');
|
|
115
|
-
// Prevent creation of system tables
|
|
116
|
-
if (table_name.startsWith('_')) {
|
|
117
|
-
throw new AppError(
|
|
118
|
-
'Cannot create system tables',
|
|
119
|
-
403,
|
|
120
|
-
ERROR_CODES.FORBIDDEN,
|
|
121
|
-
'Table names starting with underscore are reserved for system tables'
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Filter out reserved fields with matching types, throw error for mismatched types
|
|
126
|
-
const validatedColumns = this.validateReservedFields(columns);
|
|
127
|
-
|
|
128
|
-
// Validate remaining columns - only need to validate column names since Zod handles type validation
|
|
129
|
-
validatedColumns.forEach((col: ColumnSchema, index: number) => {
|
|
130
|
-
// Validate column name
|
|
131
|
-
try {
|
|
132
|
-
validateIdentifier(col.columnName, 'column');
|
|
133
|
-
} catch (error) {
|
|
134
|
-
if (error instanceof AppError) {
|
|
135
|
-
throw new AppError(
|
|
136
|
-
`Invalid column name at index ${index}: ${error.message}`,
|
|
137
|
-
error.statusCode,
|
|
138
|
-
error.code,
|
|
139
|
-
error.nextActions
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
throw error;
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
const db = this.dbManager.getDb();
|
|
147
|
-
|
|
148
|
-
// Check if table exists
|
|
149
|
-
const tableExists = await db
|
|
150
|
-
.prepare(
|
|
151
|
-
`
|
|
152
|
-
SELECT EXISTS (
|
|
153
|
-
SELECT FROM information_schema.tables
|
|
154
|
-
WHERE table_schema = 'public'
|
|
155
|
-
AND table_name = ?
|
|
156
|
-
) as exists
|
|
157
|
-
`
|
|
158
|
-
)
|
|
159
|
-
.get(table_name);
|
|
160
|
-
|
|
161
|
-
if (tableExists?.exists) {
|
|
162
|
-
throw new AppError(
|
|
163
|
-
`table ${table_name} already exists`,
|
|
164
|
-
400,
|
|
165
|
-
ERROR_CODES.DATABASE_DUPLICATE,
|
|
166
|
-
`table ${table_name} already exists. Please check the table name, it must be a unique table name.`
|
|
167
|
-
);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Map columns to SQL with proper type conversion
|
|
171
|
-
const columnDefs = validatedColumns
|
|
172
|
-
.map((col: ColumnSchema) => {
|
|
173
|
-
const fieldType = COLUMN_TYPES[col.type as ColumnType];
|
|
174
|
-
const sqlType = fieldType.sqlType;
|
|
175
|
-
|
|
176
|
-
// Handle default values
|
|
177
|
-
const defaultClause = formatDefaultValue(
|
|
178
|
-
col.defaultValue,
|
|
179
|
-
col.type as ColumnType,
|
|
180
|
-
col.isNullable
|
|
181
|
-
);
|
|
182
|
-
|
|
183
|
-
const nullable = col.isNullable ? '' : 'NOT NULL';
|
|
184
|
-
const unique = col.isUnique ? 'UNIQUE' : '';
|
|
185
|
-
|
|
186
|
-
return `${this.quoteIdentifier(col.columnName)} ${sqlType} ${nullable} ${unique} ${defaultClause}`.trim();
|
|
187
|
-
})
|
|
188
|
-
.join(', ');
|
|
189
|
-
|
|
190
|
-
// Prepare foreign key constraints
|
|
191
|
-
const foreignKeyConstraints = validatedColumns
|
|
192
|
-
.filter((col) => col.foreignKey)
|
|
193
|
-
.map((col) => this.generateFkeyConstraintStatement(col, true))
|
|
194
|
-
.join(', ');
|
|
195
|
-
|
|
196
|
-
// Create table with auto fields and foreign keys
|
|
197
|
-
const tableDefinition = [
|
|
198
|
-
'id UUID PRIMARY KEY DEFAULT gen_random_uuid()',
|
|
199
|
-
columnDefs,
|
|
200
|
-
'created_at TIMESTAMPTZ DEFAULT now()',
|
|
201
|
-
'updated_at TIMESTAMPTZ DEFAULT now()',
|
|
202
|
-
foreignKeyConstraints,
|
|
203
|
-
]
|
|
204
|
-
.filter(Boolean)
|
|
205
|
-
.join(', ');
|
|
206
|
-
|
|
207
|
-
await db
|
|
208
|
-
.prepare(
|
|
209
|
-
`
|
|
210
|
-
CREATE TABLE ${this.quoteIdentifier(table_name)} (
|
|
211
|
-
${tableDefinition}
|
|
212
|
-
);
|
|
213
|
-
NOTIFY pgrst, 'reload schema';
|
|
214
|
-
`
|
|
215
|
-
)
|
|
216
|
-
.exec();
|
|
217
|
-
|
|
218
|
-
if (use_RLS) {
|
|
219
|
-
// Enable RLS policies
|
|
220
|
-
await db
|
|
221
|
-
.prepare(
|
|
222
|
-
`
|
|
223
|
-
ALTER TABLE ${this.quoteIdentifier(table_name)} ENABLE ROW LEVEL SECURITY;
|
|
224
|
-
`
|
|
225
|
-
)
|
|
226
|
-
.exec();
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Create trigger for updated_at
|
|
230
|
-
await db
|
|
231
|
-
.prepare(
|
|
232
|
-
`
|
|
233
|
-
CREATE TRIGGER ${this.quoteIdentifier(table_name + '_update_timestamp')}
|
|
234
|
-
BEFORE UPDATE ON ${this.quoteIdentifier(table_name)}
|
|
235
|
-
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
|
236
|
-
`
|
|
237
|
-
)
|
|
238
|
-
.exec();
|
|
239
|
-
|
|
240
|
-
// Update metadata
|
|
241
|
-
// Metadata is now updated on-demand
|
|
242
|
-
|
|
243
|
-
return {
|
|
244
|
-
message: 'table created successfully',
|
|
245
|
-
tableName: table_name,
|
|
246
|
-
columns: validatedColumns.map((col) => ({
|
|
247
|
-
...col,
|
|
248
|
-
sqlType: COLUMN_TYPES[col.type as ColumnType].sqlType,
|
|
249
|
-
})),
|
|
250
|
-
autoFields: ['id', 'created_at', 'updated_at'],
|
|
251
|
-
nextActions: 'you can now use the table with the POST /api/database/tables/{table} endpoint',
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Get table schema
|
|
257
|
-
*/
|
|
258
|
-
/**
|
|
259
|
-
* Parse PostgreSQL default value format
|
|
260
|
-
* Extracts the actual value from formats like 'abc'::text or 123::integer
|
|
261
|
-
*/
|
|
262
|
-
private parseDefaultValue(defaultValue: string | null): string | undefined {
|
|
263
|
-
if (!defaultValue) {
|
|
264
|
-
return undefined;
|
|
265
|
-
}
|
|
266
|
-
// Handle string literals with type casting (e.g., 'abc'::text)
|
|
267
|
-
const stringMatch = defaultValue.match(/^'([^']*)'::[\w\s]+$/);
|
|
268
|
-
if (stringMatch) {
|
|
269
|
-
return stringMatch[1];
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// Handle numeric/boolean values with type casting (e.g., 123::integer, true::boolean)
|
|
273
|
-
const typeCastMatch = defaultValue.match(/^(.+?)::[\w\s]+$/);
|
|
274
|
-
if (typeCastMatch) {
|
|
275
|
-
return typeCastMatch[1];
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// Return as-is if no type casting pattern found
|
|
279
|
-
return defaultValue;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
async getTableSchema(table: string): Promise<GetTableSchemaResponse> {
|
|
283
|
-
const db = this.dbManager.getDb();
|
|
284
|
-
|
|
285
|
-
// Get column information from information_schema
|
|
286
|
-
const columns = await db
|
|
287
|
-
.prepare(
|
|
288
|
-
`
|
|
289
|
-
SELECT
|
|
290
|
-
column_name,
|
|
291
|
-
data_type,
|
|
292
|
-
is_nullable,
|
|
293
|
-
column_default,
|
|
294
|
-
character_maximum_length
|
|
295
|
-
FROM information_schema.columns
|
|
296
|
-
WHERE table_schema = 'public'
|
|
297
|
-
AND table_name = ?
|
|
298
|
-
ORDER BY ordinal_position
|
|
299
|
-
`
|
|
300
|
-
)
|
|
301
|
-
.all(table);
|
|
302
|
-
|
|
303
|
-
if (columns.length === 0) {
|
|
304
|
-
throw new AppError(
|
|
305
|
-
'table not found',
|
|
306
|
-
404,
|
|
307
|
-
ERROR_CODES.DATABASE_NOT_FOUND,
|
|
308
|
-
'table not found. Please check the table name, it must be a valid table name, or you can create a new table with the POST /api/database/tables endpoint'
|
|
309
|
-
);
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
// Get foreign key information
|
|
313
|
-
const foreignKeyMap = await this.getFkeyConstraints(table);
|
|
314
|
-
|
|
315
|
-
// Get primary key information
|
|
316
|
-
const primaryKeys = await db
|
|
317
|
-
.prepare(
|
|
318
|
-
`
|
|
319
|
-
SELECT column_name
|
|
320
|
-
FROM information_schema.key_column_usage
|
|
321
|
-
WHERE table_schema = 'public'
|
|
322
|
-
AND table_name = ?
|
|
323
|
-
AND constraint_name = (
|
|
324
|
-
SELECT constraint_name
|
|
325
|
-
FROM information_schema.table_constraints
|
|
326
|
-
WHERE table_schema = 'public'
|
|
327
|
-
AND table_name = ?
|
|
328
|
-
AND constraint_type = 'PRIMARY KEY'
|
|
329
|
-
)
|
|
330
|
-
`
|
|
331
|
-
)
|
|
332
|
-
.all(table, table);
|
|
333
|
-
|
|
334
|
-
const pkSet = new Set(primaryKeys.map((pk: PrimaryKeyInfo) => pk.column_name));
|
|
335
|
-
|
|
336
|
-
// Get unique columns
|
|
337
|
-
const uniqueColumns = await db
|
|
338
|
-
.prepare(
|
|
339
|
-
`
|
|
340
|
-
SELECT DISTINCT kcu.column_name
|
|
341
|
-
FROM information_schema.table_constraints tc
|
|
342
|
-
JOIN information_schema.key_column_usage kcu
|
|
343
|
-
ON tc.constraint_name = kcu.constraint_name
|
|
344
|
-
AND tc.table_schema = kcu.table_schema
|
|
345
|
-
WHERE tc.table_schema = 'public'
|
|
346
|
-
AND tc.table_name = ?
|
|
347
|
-
AND tc.constraint_type = 'UNIQUE'
|
|
348
|
-
`
|
|
349
|
-
)
|
|
350
|
-
.all(table);
|
|
351
|
-
|
|
352
|
-
const uniqueSet = new Set(uniqueColumns.map((u: { column_name: string }) => u.column_name));
|
|
353
|
-
|
|
354
|
-
// Get row count
|
|
355
|
-
const { row_count } = await db.prepare(`SELECT COUNT(*) as row_count FROM "${table}"`).get();
|
|
356
|
-
|
|
357
|
-
return {
|
|
358
|
-
tableName: table,
|
|
359
|
-
columns: columns.map((col: ColumnInfo) => ({
|
|
360
|
-
columnName: col.column_name,
|
|
361
|
-
type: convertSqlTypeToColumnType(col.data_type),
|
|
362
|
-
isNullable: col.is_nullable === 'YES',
|
|
363
|
-
isPrimaryKey: pkSet.has(col.column_name),
|
|
364
|
-
isUnique: pkSet.has(col.column_name) || uniqueSet.has(col.column_name),
|
|
365
|
-
defaultValue: this.parseDefaultValue(col.column_default),
|
|
366
|
-
...(foreignKeyMap.has(col.column_name) && {
|
|
367
|
-
foreignKey: foreignKeyMap.get(col.column_name),
|
|
368
|
-
}),
|
|
369
|
-
})),
|
|
370
|
-
recordCount: row_count,
|
|
371
|
-
};
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* Update table schema
|
|
376
|
-
*/
|
|
377
|
-
async updateTableSchema(
|
|
378
|
-
tableName: string,
|
|
379
|
-
operations: UpdateTableSchemaRequest
|
|
380
|
-
): Promise<UpdateTableSchemaResponse> {
|
|
381
|
-
const { addColumns, dropColumns, updateColumns, addForeignKeys, dropForeignKeys, renameTable } =
|
|
382
|
-
operations;
|
|
383
|
-
|
|
384
|
-
// Prevent modification of system tables
|
|
385
|
-
if (tableName.startsWith('_')) {
|
|
386
|
-
throw new AppError(
|
|
387
|
-
'System tables cannot be modified',
|
|
388
|
-
403,
|
|
389
|
-
ERROR_CODES.DATABASE_FORBIDDEN,
|
|
390
|
-
'System tables cannot be modified. System tables are prefixed with underscore.'
|
|
391
|
-
);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
const db = this.dbManager.getDb();
|
|
395
|
-
|
|
396
|
-
// Check if table exists
|
|
397
|
-
const tableExists = await db
|
|
398
|
-
.prepare(
|
|
399
|
-
`
|
|
400
|
-
SELECT EXISTS (
|
|
401
|
-
SELECT FROM information_schema.tables
|
|
402
|
-
WHERE table_schema = 'public'
|
|
403
|
-
AND table_name = ?
|
|
404
|
-
) as exists
|
|
405
|
-
`
|
|
406
|
-
)
|
|
407
|
-
.get(tableName);
|
|
408
|
-
|
|
409
|
-
if (!tableExists?.exists) {
|
|
410
|
-
throw new AppError(
|
|
411
|
-
'table not found',
|
|
412
|
-
404,
|
|
413
|
-
ERROR_CODES.DATABASE_NOT_FOUND,
|
|
414
|
-
'Please check the table name, it must be a valid table name, or you can create a new table with the POST /api/database/tables endpoint'
|
|
415
|
-
);
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
const safeTableName = this.quoteIdentifier(tableName);
|
|
419
|
-
const foreignKeyMap = await this.getFkeyConstraints(tableName);
|
|
420
|
-
const completedOperations: string[] = [];
|
|
421
|
-
|
|
422
|
-
// Execute operations
|
|
423
|
-
|
|
424
|
-
// Drop foreign key constraints
|
|
425
|
-
if (dropForeignKeys && Array.isArray(dropForeignKeys)) {
|
|
426
|
-
for (const col of dropForeignKeys) {
|
|
427
|
-
const constraintName = foreignKeyMap.get(col)?.constraint_name;
|
|
428
|
-
if (constraintName) {
|
|
429
|
-
await db
|
|
430
|
-
.prepare(
|
|
431
|
-
`
|
|
432
|
-
ALTER TABLE ${safeTableName}
|
|
433
|
-
DROP CONSTRAINT ${this.quoteIdentifier(constraintName)}
|
|
434
|
-
`
|
|
435
|
-
)
|
|
436
|
-
.exec();
|
|
437
|
-
|
|
438
|
-
completedOperations.push(`Dropped foreign key constraint on column: ${col}`);
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
// Drop columns first (to avoid conflicts with renames)
|
|
444
|
-
if (dropColumns && Array.isArray(dropColumns)) {
|
|
445
|
-
for (const col of dropColumns) {
|
|
446
|
-
if (Object.keys(reservedColumns).includes(col)) {
|
|
447
|
-
throw new AppError(
|
|
448
|
-
'cannot drop system columns',
|
|
449
|
-
404,
|
|
450
|
-
ERROR_CODES.DATABASE_FORBIDDEN,
|
|
451
|
-
`You cannot drop the system column '${col}'`
|
|
452
|
-
);
|
|
453
|
-
}
|
|
454
|
-
if (tableName === 'users' && userTableFrozenColumns.includes(col)) {
|
|
455
|
-
throw new AppError(
|
|
456
|
-
'cannot drop frozen users columns',
|
|
457
|
-
403,
|
|
458
|
-
ERROR_CODES.FORBIDDEN,
|
|
459
|
-
`You cannot drop the frozen users column '${col}'`
|
|
460
|
-
);
|
|
461
|
-
}
|
|
462
|
-
await db
|
|
463
|
-
.prepare(
|
|
464
|
-
`
|
|
465
|
-
ALTER TABLE ${safeTableName}
|
|
466
|
-
DROP COLUMN ${this.quoteIdentifier(col)}
|
|
467
|
-
`
|
|
468
|
-
)
|
|
469
|
-
.exec();
|
|
470
|
-
|
|
471
|
-
completedOperations.push(`Dropped column: ${col}`);
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
// Update columns
|
|
476
|
-
if (updateColumns && Array.isArray(updateColumns)) {
|
|
477
|
-
for (const column of updateColumns) {
|
|
478
|
-
if (Object.keys(reservedColumns).includes(column.columnName)) {
|
|
479
|
-
throw new AppError(
|
|
480
|
-
'cannot update system columns',
|
|
481
|
-
404,
|
|
482
|
-
ERROR_CODES.DATABASE_FORBIDDEN,
|
|
483
|
-
`You cannot update the system column '${column.columnName}'`
|
|
484
|
-
);
|
|
485
|
-
}
|
|
486
|
-
if (tableName === 'users' && userTableFrozenColumns.includes(column.columnName)) {
|
|
487
|
-
throw new AppError(
|
|
488
|
-
'cannot update frozen user columns',
|
|
489
|
-
403,
|
|
490
|
-
ERROR_CODES.FORBIDDEN,
|
|
491
|
-
`You cannot update the frozen users column '${column.columnName}'`
|
|
492
|
-
);
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
// Handle default value changes
|
|
496
|
-
if (column.defaultValue !== undefined) {
|
|
497
|
-
if (column.defaultValue === '') {
|
|
498
|
-
// Drop default
|
|
499
|
-
await db
|
|
500
|
-
.prepare(
|
|
501
|
-
`
|
|
502
|
-
ALTER TABLE ${safeTableName}
|
|
503
|
-
ALTER COLUMN ${this.quoteIdentifier(column.columnName)} DROP DEFAULT
|
|
504
|
-
`
|
|
505
|
-
)
|
|
506
|
-
.exec();
|
|
507
|
-
} else {
|
|
508
|
-
// Set default
|
|
509
|
-
await db
|
|
510
|
-
.prepare(
|
|
511
|
-
`
|
|
512
|
-
ALTER TABLE ${safeTableName}
|
|
513
|
-
ALTER COLUMN ${this.quoteIdentifier(column.columnName)} SET ${formatDefaultValue(column.defaultValue)}
|
|
514
|
-
`
|
|
515
|
-
)
|
|
516
|
-
.exec();
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
// Handle column rename - do this last to avoid issues with other operations
|
|
521
|
-
if (column.newColumnName) {
|
|
522
|
-
await db
|
|
523
|
-
.prepare(
|
|
524
|
-
`
|
|
525
|
-
ALTER TABLE ${safeTableName}
|
|
526
|
-
RENAME COLUMN ${this.quoteIdentifier(column.columnName)} TO ${this.quoteIdentifier(column.newColumnName as string)}
|
|
527
|
-
`
|
|
528
|
-
)
|
|
529
|
-
.exec();
|
|
530
|
-
}
|
|
531
|
-
completedOperations.push(`Updated column: ${column.columnName}`);
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
// Add new columns
|
|
536
|
-
if (addColumns && Array.isArray(addColumns)) {
|
|
537
|
-
// Validate and filter reserved fields
|
|
538
|
-
const columnsToAdd = this.validateReservedFields(addColumns);
|
|
539
|
-
|
|
540
|
-
for (const col of columnsToAdd) {
|
|
541
|
-
const fieldType = COLUMN_TYPES[col.type as ColumnType];
|
|
542
|
-
let sqlType = fieldType.sqlType;
|
|
543
|
-
if (col.type === ColumnType.UUID) {
|
|
544
|
-
sqlType = 'UUID';
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
const nullable = col.isNullable !== false ? '' : 'NOT NULL';
|
|
548
|
-
const unique = col.isUnique ? 'UNIQUE' : '';
|
|
549
|
-
const defaultClause = formatDefaultValue(
|
|
550
|
-
col.defaultValue,
|
|
551
|
-
col.type as ColumnType,
|
|
552
|
-
col.isNullable
|
|
553
|
-
);
|
|
554
|
-
|
|
555
|
-
await db
|
|
556
|
-
.prepare(
|
|
557
|
-
`
|
|
558
|
-
ALTER TABLE ${safeTableName}
|
|
559
|
-
ADD COLUMN ${this.quoteIdentifier(col.columnName)} ${sqlType} ${nullable} ${unique} ${defaultClause}
|
|
560
|
-
`
|
|
561
|
-
)
|
|
562
|
-
.exec();
|
|
563
|
-
|
|
564
|
-
completedOperations.push(`Added column: ${col.columnName}`);
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
// Add foreign key constraints
|
|
569
|
-
if (addForeignKeys && Array.isArray(addForeignKeys)) {
|
|
570
|
-
for (const col of addForeignKeys) {
|
|
571
|
-
if (Object.keys(reservedColumns).includes(col.columnName)) {
|
|
572
|
-
throw new AppError(
|
|
573
|
-
'cannot add foreign key on system columns',
|
|
574
|
-
404,
|
|
575
|
-
ERROR_CODES.DATABASE_FORBIDDEN,
|
|
576
|
-
`You cannot add foreign key on the system column '${col.columnName}'`
|
|
577
|
-
);
|
|
578
|
-
}
|
|
579
|
-
const fkeyConstraint = this.generateFkeyConstraintStatement(col, true);
|
|
580
|
-
await db
|
|
581
|
-
.prepare(
|
|
582
|
-
`
|
|
583
|
-
ALTER TABLE ${safeTableName}
|
|
584
|
-
ADD ${fkeyConstraint}
|
|
585
|
-
`
|
|
586
|
-
)
|
|
587
|
-
.exec();
|
|
588
|
-
|
|
589
|
-
completedOperations.push(`Added foreign key constraint on column: ${col.columnName}`);
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
if (renameTable && renameTable.newTableName) {
|
|
594
|
-
if (tableName === 'users') {
|
|
595
|
-
throw new AppError('Cannot rename users table', 403, ERROR_CODES.FORBIDDEN);
|
|
596
|
-
}
|
|
597
|
-
// Prevent renaming to system tables
|
|
598
|
-
if (renameTable.newTableName.startsWith('_')) {
|
|
599
|
-
throw new AppError(
|
|
600
|
-
'Cannot rename to system table',
|
|
601
|
-
403,
|
|
602
|
-
ERROR_CODES.FORBIDDEN,
|
|
603
|
-
'Table names starting with underscore are reserved for system tables'
|
|
604
|
-
);
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
const safeNewTableName = this.quoteIdentifier(renameTable.newTableName);
|
|
608
|
-
// Rename the table
|
|
609
|
-
await db
|
|
610
|
-
.prepare(
|
|
611
|
-
`
|
|
612
|
-
ALTER TABLE ${safeTableName}
|
|
613
|
-
RENAME TO ${safeNewTableName}
|
|
614
|
-
`
|
|
615
|
-
)
|
|
616
|
-
.exec();
|
|
617
|
-
|
|
618
|
-
completedOperations.push(`Renamed table from ${tableName} to ${renameTable.newTableName}`);
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
// Update metadata after schema changes
|
|
622
|
-
// Metadata is now updated on-demand
|
|
623
|
-
|
|
624
|
-
// enable postgrest to query this table
|
|
625
|
-
await db
|
|
626
|
-
.prepare(
|
|
627
|
-
`
|
|
628
|
-
NOTIFY pgrst, 'reload schema';
|
|
629
|
-
`
|
|
630
|
-
)
|
|
631
|
-
.exec();
|
|
632
|
-
|
|
633
|
-
return {
|
|
634
|
-
message: 'table schema updated successfully',
|
|
635
|
-
tableName,
|
|
636
|
-
operations: completedOperations,
|
|
637
|
-
};
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
/**
|
|
641
|
-
* Delete a table
|
|
642
|
-
*/
|
|
643
|
-
async deleteTable(table: string): Promise<DeleteTableResponse> {
|
|
644
|
-
// Prevent deletion of system tables
|
|
645
|
-
if (table.startsWith('_')) {
|
|
646
|
-
throw new AppError(
|
|
647
|
-
'System tables cannot be deleted',
|
|
648
|
-
403,
|
|
649
|
-
ERROR_CODES.DATABASE_FORBIDDEN,
|
|
650
|
-
'System tables cannot be deleted. System tables are prefixed with underscore.'
|
|
651
|
-
);
|
|
652
|
-
}
|
|
653
|
-
if (table === 'users') {
|
|
654
|
-
throw new AppError('Cannot delete users table', 403, ERROR_CODES.DATABASE_FORBIDDEN);
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
const db = this.dbManager.getDb();
|
|
658
|
-
await db.prepare(`DROP TABLE IF EXISTS ${this.quoteIdentifier(table)} CASCADE`).run();
|
|
659
|
-
|
|
660
|
-
// Update metadata
|
|
661
|
-
// Metadata is now updated on-demand
|
|
662
|
-
|
|
663
|
-
// enable postgrest to query this table
|
|
664
|
-
await db
|
|
665
|
-
.prepare(
|
|
666
|
-
`
|
|
667
|
-
NOTIFY pgrst, 'reload schema';
|
|
668
|
-
`
|
|
669
|
-
)
|
|
670
|
-
.exec();
|
|
671
|
-
|
|
672
|
-
return {
|
|
673
|
-
message: 'table deleted successfully',
|
|
674
|
-
tableName: table,
|
|
675
|
-
nextActions:
|
|
676
|
-
'table deleted successfully, you can create a new table with the POST /api/database/tables endpoint',
|
|
677
|
-
};
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
// Helper methods
|
|
681
|
-
private quoteIdentifier(identifier: string): string {
|
|
682
|
-
return `"${identifier.replace(/"/g, '""')}"`;
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
private validateReservedFields(columns: ColumnSchema[]): ColumnSchema[] {
|
|
686
|
-
return columns.filter((col: ColumnSchema) => {
|
|
687
|
-
const reservedType = reservedColumns[col.columnName as keyof typeof reservedColumns];
|
|
688
|
-
if (reservedType) {
|
|
689
|
-
// If it's a reserved field name
|
|
690
|
-
if (col.type === reservedType) {
|
|
691
|
-
// Type matches - silently ignore this column
|
|
692
|
-
return false;
|
|
693
|
-
} else {
|
|
694
|
-
// Type doesn't match - throw error
|
|
695
|
-
throw new AppError(
|
|
696
|
-
`Column '${col.columnName}' is a reserved field that requires type '${reservedType}', but got '${col.type}'`,
|
|
697
|
-
400,
|
|
698
|
-
ERROR_CODES.DATABASE_VALIDATION_ERROR,
|
|
699
|
-
'Please check the column name and type, id/created_at/updated_at are reserved fields and cannot be used as column names'
|
|
700
|
-
);
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
return true;
|
|
704
|
-
});
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
private generateFkeyConstraintStatement(
|
|
708
|
-
col: { columnName: string; foreignKey?: ForeignKeySchema },
|
|
709
|
-
include_source_column: boolean = true
|
|
710
|
-
) {
|
|
711
|
-
if (!col.foreignKey) {
|
|
712
|
-
return '';
|
|
713
|
-
}
|
|
714
|
-
// Store foreign_key in a const to avoid repeated non-null assertions
|
|
715
|
-
const fk = col.foreignKey;
|
|
716
|
-
const constraintName = `fk_${col.columnName}_${fk.referenceTable}_${fk.referenceColumn}`;
|
|
717
|
-
const onDelete = fk.onDelete || 'RESTRICT';
|
|
718
|
-
const onUpdate = fk.onUpdate || 'RESTRICT';
|
|
719
|
-
|
|
720
|
-
if (include_source_column) {
|
|
721
|
-
return `CONSTRAINT ${this.quoteIdentifier(constraintName)} FOREIGN KEY (${this.quoteIdentifier(col.columnName)}) REFERENCES ${this.quoteIdentifier(fk.referenceTable)}(${this.quoteIdentifier(fk.referenceColumn)}) ON DELETE ${onDelete} ON UPDATE ${onUpdate}`;
|
|
722
|
-
} else {
|
|
723
|
-
return `CONSTRAINT ${this.quoteIdentifier(constraintName)} REFERENCES ${this.quoteIdentifier(fk.referenceTable)}(${this.quoteIdentifier(fk.referenceColumn)}) ON DELETE ${onDelete} ON UPDATE ${onUpdate}`;
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
private async getFkeyConstraints(table: string): Promise<Map<string, ForeignKeyInfo>> {
|
|
728
|
-
const db = this.dbManager.getDb();
|
|
729
|
-
const foreignKeys = await db
|
|
730
|
-
.prepare(
|
|
731
|
-
`
|
|
732
|
-
SELECT
|
|
733
|
-
tc.constraint_name,
|
|
734
|
-
kcu.column_name as from_column,
|
|
735
|
-
ccu.table_name AS foreign_table,
|
|
736
|
-
ccu.column_name AS foreign_column,
|
|
737
|
-
rc.delete_rule as on_delete,
|
|
738
|
-
rc.update_rule as on_update
|
|
739
|
-
FROM information_schema.table_constraints AS tc
|
|
740
|
-
JOIN information_schema.key_column_usage AS kcu
|
|
741
|
-
ON tc.constraint_name = kcu.constraint_name
|
|
742
|
-
AND tc.table_schema = kcu.table_schema
|
|
743
|
-
JOIN information_schema.constraint_column_usage AS ccu
|
|
744
|
-
ON ccu.constraint_name = tc.constraint_name
|
|
745
|
-
AND ccu.table_schema = tc.table_schema
|
|
746
|
-
JOIN information_schema.referential_constraints AS rc
|
|
747
|
-
ON rc.constraint_name = tc.constraint_name
|
|
748
|
-
AND rc.constraint_schema = tc.table_schema
|
|
749
|
-
WHERE tc.constraint_type = 'FOREIGN KEY'
|
|
750
|
-
AND tc.table_name = ?
|
|
751
|
-
`
|
|
752
|
-
)
|
|
753
|
-
.all(table);
|
|
754
|
-
|
|
755
|
-
// Create a map of column names to their foreign key info
|
|
756
|
-
const foreignKeyMap = new Map<string, ForeignKeyInfo>();
|
|
757
|
-
foreignKeys.forEach((fk: ForeignKeyRow) => {
|
|
758
|
-
if (fk.foreign_table.startsWith('_')) {
|
|
759
|
-
// hiden internal table.
|
|
760
|
-
return;
|
|
761
|
-
}
|
|
762
|
-
foreignKeyMap.set(fk.from_column, {
|
|
763
|
-
constraint_name: fk.constraint_name,
|
|
764
|
-
referenceTable: fk.foreign_table,
|
|
765
|
-
referenceColumn: fk.foreign_column,
|
|
766
|
-
onDelete: fk.on_delete as OnDeleteActionSchema,
|
|
767
|
-
onUpdate: fk.on_update as OnUpdateActionSchema,
|
|
768
|
-
});
|
|
769
|
-
});
|
|
770
|
-
return foreignKeyMap;
|
|
771
|
-
}
|
|
772
|
-
}
|