insforge 1.2.10 → 1.4.8
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 -20
- package/.dockerignore +60 -60
- package/.env.example +83 -77
- package/.github/ISSUE_TEMPLATE/bug_report.yml +36 -36
- package/.github/ISSUE_TEMPLATE/config.yml +11 -11
- package/.github/ISSUE_TEMPLATE/feature_request.yml +26 -26
- package/.github/PULL_REQUEST_TEMPLATE.md +7 -7
- package/.github/copilot-instructions.md +146 -146
- package/.github/workflows/build-image.yml +65 -65
- package/.github/workflows/ci-premerge-check.yml +23 -23
- package/.github/workflows/e2e.yml +63 -63
- package/.github/workflows/lint-and-format.yml +32 -32
- package/.prettierignore +64 -64
- package/CHANGELOG.md +46 -44
- package/CLAUDE_PLUGIN.md +104 -104
- package/CODE_OF_CONDUCT.md +128 -128
- package/CONTRIBUTING.md +125 -125
- package/Dockerfile +30 -30
- package/GITHUB_OAUTH_SETUP.md +49 -49
- package/GOOGLE_OAUTH_SETUP.md +148 -148
- package/LICENSE +201 -201
- package/README.md +182 -182
- package/assets/Dark.svg +23 -23
- package/auth/package.json +30 -28
- package/auth/src/lib/broadcastService.ts +4 -4
- package/auth/src/lib/insforge.ts +8 -0
- package/auth/src/main.tsx +2 -4
- package/auth/src/pages/SignInPage.tsx +5 -2
- package/auth/src/pages/SignUpPage.tsx +5 -2
- package/auth/src/pages/VerifyEmailPage.tsx +18 -0
- package/auth/tsconfig.json +33 -32
- package/auth/tsconfig.node.json +11 -11
- package/backend/package.json +82 -75
- package/backend/src/api/middlewares/rate-limiters.ts +127 -127
- package/backend/src/api/routes/ai/index.routes.ts +475 -468
- package/backend/src/api/routes/auth/index.routes.ts +720 -570
- package/backend/src/api/routes/auth/oauth.routes.ts +478 -448
- package/backend/src/api/routes/database/advance.routes.ts +37 -16
- package/backend/src/api/routes/database/index.routes.ts +80 -1
- package/backend/src/api/routes/database/records.routes.ts +48 -184
- package/backend/src/api/routes/database/rpc.routes.ts +69 -0
- package/backend/src/api/routes/database/tables.routes.ts +0 -14
- package/backend/src/api/routes/deployments/index.routes.ts +192 -0
- package/backend/src/api/routes/docs/index.routes.ts +76 -76
- package/backend/src/api/routes/email/index.routes.ts +35 -0
- package/backend/src/api/routes/functions/index.routes.ts +21 -15
- package/backend/src/api/routes/metadata/index.routes.ts +38 -0
- 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/storage/index.routes.ts +18 -12
- package/backend/src/api/routes/usage/index.routes.ts +6 -4
- package/backend/src/api/routes/webhooks/index.routes.ts +109 -0
- package/backend/src/infra/database/database.manager.ts +14 -11
- package/backend/src/infra/database/migrations/000_create-base-tables.sql +141 -141
- package/backend/src/infra/database/migrations/001_create-helper-functions.sql +40 -40
- package/backend/src/infra/database/migrations/002_rename-auth-tables.sql +29 -29
- package/backend/src/infra/database/migrations/003_create-users-table.sql +55 -55
- package/backend/src/infra/database/migrations/004_add-reload-postgrest-func.sql +23 -23
- package/backend/src/infra/database/migrations/005_enable-project-admin-modify-users.sql +29 -29
- package/backend/src/infra/database/migrations/006_modify-ai-usage-table.sql +24 -24
- package/backend/src/infra/database/migrations/007_drop-metadata-table.sql +1 -1
- package/backend/src/infra/database/migrations/008_add-system-tables.sql +76 -76
- package/backend/src/infra/database/migrations/009_add-function-secrets.sql +23 -23
- package/backend/src/infra/database/migrations/010_modify-ai-config-modalities.sql +93 -93
- package/backend/src/infra/database/migrations/011_refactor-secrets-table.sql +15 -15
- package/backend/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 -44
- package/backend/src/infra/database/migrations/014_add-updated-at-trigger-user-table.sql +7 -7
- package/backend/src/infra/database/migrations/015_create-auth-config-and-email-otp-tables.sql +59 -59
- package/backend/src/infra/database/migrations/016_update-auth-config-and-email-otp.sql +24 -24
- package/backend/src/infra/database/migrations/017_create-realtime-schema.sql +233 -0
- package/backend/src/infra/database/migrations/018_schema-rework.sql +441 -0
- package/backend/src/infra/database/migrations/019_create-deployments-table.sql +36 -0
- package/backend/src/infra/database/migrations/020_add-audio-modality.sql +11 -0
- package/backend/src/infra/database/migrations/bootstrap/bootstrap-migrations.js +103 -0
- package/backend/src/infra/realtime/realtime.manager.ts +246 -0
- package/backend/src/infra/realtime/webhook-sender.ts +82 -0
- package/backend/src/infra/security/token.manager.ts +216 -125
- package/backend/src/infra/socket/socket.manager.ts +198 -64
- package/backend/src/providers/ai/openrouter.provider.ts +24 -12
- package/backend/src/providers/database/base.provider.ts +39 -0
- package/backend/src/providers/database/cloud.provider.ts +159 -0
- package/backend/src/providers/deployments/vercel.provider.ts +516 -0
- package/backend/src/providers/email/base.provider.ts +4 -7
- package/backend/src/providers/email/cloud.provider.ts +84 -0
- package/backend/src/providers/oauth/apple.provider.ts +266 -0
- package/backend/src/providers/oauth/index.ts +1 -0
- package/backend/src/server.ts +329 -284
- package/backend/src/services/ai/ai-config.service.ts +6 -6
- package/backend/src/services/ai/ai-model.service.ts +60 -60
- package/backend/src/services/ai/ai-usage.service.ts +7 -7
- package/backend/src/services/ai/chat-completion.service.ts +415 -220
- package/backend/src/services/ai/helpers.ts +64 -64
- package/backend/src/services/ai/image-generation.service.ts +3 -3
- package/backend/src/services/ai/index.ts +13 -13
- package/backend/src/services/auth/auth-config.service.ts +4 -4
- package/backend/src/services/auth/auth-otp.service.ts +6 -6
- package/backend/src/services/auth/auth.service.ts +148 -74
- package/backend/src/services/auth/index.ts +4 -4
- package/backend/src/services/auth/oauth-config.service.ts +12 -12
- package/backend/src/services/database/database-advance.service.ts +19 -55
- package/backend/src/services/database/database-table.service.ts +38 -94
- package/backend/src/services/database/database.service.ts +127 -0
- package/backend/src/services/database/postgrest-proxy.service.ts +165 -0
- package/backend/src/services/deployments/deployment.service.ts +693 -0
- package/backend/src/services/email/email.service.ts +5 -7
- package/backend/src/services/functions/function.service.ts +61 -41
- package/backend/src/services/logs/audit.service.ts +10 -10
- 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/services/secrets/secret.service.ts +101 -27
- package/backend/src/services/storage/storage.service.ts +30 -30
- package/backend/src/services/usage/usage.service.ts +6 -6
- package/backend/src/types/ai.ts +8 -0
- package/backend/src/types/auth.ts +16 -1
- package/backend/src/types/database.ts +2 -0
- package/backend/src/types/deployments.ts +33 -0
- package/backend/src/types/realtime.ts +18 -0
- package/backend/src/types/socket.ts +7 -31
- package/backend/src/types/storage.ts +1 -1
- package/backend/src/types/webhooks.ts +45 -0
- package/backend/src/utils/cookies.ts +34 -0
- package/backend/src/utils/environment.ts +0 -14
- package/backend/src/utils/s3-config-loader.ts +64 -0
- package/backend/src/utils/seed.ts +79 -43
- package/backend/src/utils/sql-parser.ts +216 -0
- package/backend/src/utils/utils.ts +114 -114
- package/backend/src/utils/validations.ts +10 -10
- 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 -129
- package/backend/tests/local/test-ai-usage.sh +80 -80
- 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 -123
- package/backend/tests/local/test-id-field.sh +200 -200
- package/backend/tests/local/test-logs.sh +132 -132
- package/backend/tests/local/test-public-bucket.sh +264 -264
- package/backend/tests/local/test-rpc.sh +141 -0
- package/backend/tests/local/test-secrets.sh +249 -249
- 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-ai-model-plugins.sh +258 -0
- 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 -244
- 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 -338
- package/backend/tests/unit/analyze-query.test.ts +697 -0
- package/backend/tests/unit/database-advance.test.ts +326 -0
- package/backend/tests/unit/helpers.test.ts +2 -2
- package/backend/tsconfig.json +22 -22
- package/claude-plugin/.claude-plugin/plugin.json +24 -24
- package/claude-plugin/README.md +133 -133
- package/claude-plugin/skills/insforge-schema-patterns/SKILL.md +273 -270
- package/docker-compose.prod.yml +204 -200
- package/docker-compose.yml +232 -228
- package/docker-init/db/db-init.sql +97 -97
- package/docker-init/db/jwt.sql +5 -5
- package/docker-init/db/postgresql.conf +16 -16
- package/docker-init/logs/vector.yml +236 -236
- package/docs/README.md +44 -44
- package/docs/agent-docs/deployment.md +79 -0
- package/docs/agent-docs/real-time.md +269 -0
- package/docs/changelog.mdx +212 -67
- package/docs/core-concepts/ai/architecture.mdx +350 -372
- package/docs/core-concepts/ai/sdk.mdx +238 -213
- package/docs/core-concepts/authentication/architecture.mdx +276 -278
- package/docs/core-concepts/authentication/sdk.mdx +710 -414
- package/docs/core-concepts/authentication/ui-components/customization.mdx +733 -529
- package/docs/core-concepts/authentication/ui-components/nextjs.mdx +247 -221
- package/docs/core-concepts/authentication/ui-components/react-router.mdx +183 -184
- package/docs/core-concepts/authentication/ui-components/react.mdx +136 -129
- package/docs/core-concepts/database/architecture.mdx +292 -255
- package/docs/core-concepts/database/pgvector.mdx +138 -0
- package/docs/core-concepts/database/sdk.mdx +382 -382
- package/docs/core-concepts/deployments/architecture.mdx +152 -0
- package/docs/core-concepts/email/architecture.mdx +103 -0
- package/docs/core-concepts/email/sdk.mdx +53 -0
- package/docs/core-concepts/functions/architecture.mdx +105 -105
- package/docs/core-concepts/functions/sdk.mdx +183 -184
- 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 -243
- package/docs/core-concepts/storage/sdk.mdx +253 -253
- package/docs/deployment/README.md +94 -94
- package/docs/deployment/deploy-to-aws-ec2.md +564 -564
- package/docs/deployment/deploy-to-azure-virtual-machines.md +312 -312
- package/docs/deployment/deploy-to-google-cloud-compute-engine.md +613 -613
- package/docs/deployment/deploy-to-render.md +441 -441
- 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 +240 -210
- package/docs/examples/framework-guides/nextjs.mdx +131 -131
- package/docs/examples/framework-guides/nuxt.mdx +165 -165
- package/docs/examples/framework-guides/react.mdx +165 -165
- package/docs/examples/framework-guides/svelte.mdx +153 -153
- package/docs/examples/framework-guides/vue.mdx +159 -159
- package/docs/examples/overview.mdx +67 -67
- package/docs/favicon.png +0 -0
- package/docs/favicon.svg +4 -19
- 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/apple-oauth.mp4 +0 -0
- package/docs/images/changelog/dec-2025/mcp-installer.png +0 -0
- package/docs/images/changelog/dec-2025/moreModels.png +0 -0
- package/docs/images/changelog/dec-2025/multi-region.webp +0 -0
- package/docs/images/changelog/dec-2025/postgres-connection.webp +0 -0
- package/docs/images/changelog/dec-2025/realtime-module.jpg +0 -0
- package/docs/images/changelog/dec-2025/realtime2.png +0 -0
- package/docs/images/icons/ai.svg +4 -4
- package/docs/images/logos/nextjs.svg +4 -4
- package/docs/images/logos/nuxt.svg +4 -4
- package/docs/images/logos/react.svg +5 -5
- package/docs/images/logos/svelte.svg +4 -4
- package/docs/images/logos/vue.svg +5 -5
- package/docs/images/mcp-setup/CC-MCP-1.mp4 +0 -0
- package/docs/images/mcp-setup/CC-MCP-2.mp4 +0 -0
- package/docs/images/mcp-setup/Cursor-MCP-1.mp4 +0 -0
- package/docs/images/mcp-setup/Cursor-MCP-2.mp4 +0 -0
- package/docs/images/mcp-setup/Cursor-MCP-3.mp4 +0 -0
- package/docs/images/mcp-setup/claude-code-connect.png +0 -0
- package/docs/images/mcp-setup/cline-1.png +0 -0
- package/docs/images/mcp-setup/cline-2.png +0 -0
- package/docs/images/mcp-setup/cline-3.png +0 -0
- package/docs/images/mcp-setup/connect-project.png +0 -0
- package/docs/images/mcp-setup/copilot-1.png +0 -0
- package/docs/images/mcp-setup/copilot-2.png +0 -0
- package/docs/images/mcp-setup/copilot-3.png +0 -0
- package/docs/images/mcp-setup/mcp-json-1.png +0 -0
- package/docs/images/mcp-setup/mcp-json-2.png +0 -0
- package/docs/images/mcp-setup/qoder-1.png +0 -0
- package/docs/images/mcp-setup/qoder-2.png +0 -0
- package/docs/images/mcp-setup/roocode-1.png +0 -0
- package/docs/images/mcp-setup/roocode-2.png +0 -0
- package/docs/images/mcp-setup/trae-1.png +0 -0
- package/docs/images/mcp-setup/trae-2.png +0 -0
- package/docs/images/mcp-setup/trae-3.png +0 -0
- package/docs/images/mcp-setup/trae-4.png +0 -0
- package/docs/images/mcp-setup/trae-5.png +0 -0
- package/docs/images/mcp-setup/windsurf-1.png +0 -0
- package/docs/images/mcp-setup/windsurf-2.png +0 -0
- package/docs/insforge-instructions-sdk.md +93 -88
- package/docs/introduction.mdx +46 -45
- package/docs/logo/dark.svg +22 -22
- package/docs/logo/light.svg +20 -20
- package/docs/mcp-setup.mdx +332 -0
- package/docs/oauth-server.mdx +563 -0
- package/docs/partnership.mdx +720 -646
- package/docs/quickstart.mdx +82 -82
- package/docs/showcase.mdx +52 -52
- package/docs/snippets/sdk-installation.mdx +21 -21
- package/docs/snippets/service-icons.mdx +27 -27
- package/docs/vscode-extension.mdx +74 -0
- package/eslint.config.js +1 -0
- 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 -69
- package/frontend/src/App.tsx +8 -3
- 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 -3
- package/frontend/src/assets/icons/error.svg +3 -3
- package/frontend/src/assets/icons/loader.svg +9 -9
- 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/antigravity.svg +1 -0
- package/frontend/src/assets/logos/apple.svg +3 -3
- package/frontend/src/assets/logos/claude_code.svg +3 -3
- package/frontend/src/assets/logos/cline.svg +6 -6
- package/frontend/src/assets/logos/copilot.svg +10 -0
- package/frontend/src/assets/logos/cursor.svg +20 -20
- package/frontend/src/assets/logos/deepseek.svg +139 -0
- package/frontend/src/assets/logos/discord.svg +8 -8
- package/frontend/src/assets/logos/facebook.svg +3 -3
- 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 +1 -1
- package/frontend/src/assets/logos/kiro.svg +9 -0
- package/frontend/src/assets/logos/linkedin.svg +3 -3
- package/frontend/src/assets/logos/openai.svg +10 -10
- package/frontend/src/assets/logos/qoder.svg +4 -0
- package/frontend/src/assets/logos/qwen.svg +15 -0
- package/frontend/src/assets/logos/roo_code.svg +9 -9
- package/frontend/src/assets/logos/spotify.svg +16 -16
- package/frontend/src/assets/logos/tiktok.svg +5 -5
- 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 -3
- package/frontend/src/components/CodeBlock.tsx +2 -2
- package/frontend/src/components/ConnectCTA.tsx +3 -2
- package/frontend/src/components/datagrid/DataGrid.tsx +90 -62
- package/frontend/src/components/datagrid/datagridTypes.tsx +2 -1
- package/frontend/src/components/datagrid/index.ts +1 -1
- package/frontend/src/components/index.ts +0 -1
- package/frontend/src/components/layout/AppHeader.tsx +13 -37
- package/frontend/src/components/layout/AppSidebar.tsx +85 -100
- package/frontend/src/components/layout/Layout.tsx +34 -32
- package/frontend/src/components/layout/PrimaryMenu.tsx +12 -4
- package/frontend/src/components/radix/Select.tsx +151 -151
- package/frontend/src/features/ai/components/AIConfigCard.tsx +200 -200
- package/frontend/src/features/ai/components/AIEmptyState.tsx +23 -23
- package/frontend/src/features/ai/components/ModalityFilterSidebar.tsx +102 -101
- package/frontend/src/features/ai/components/ModelSelectionDialog.tsx +135 -135
- package/frontend/src/features/ai/components/ModelSelectionGrid.tsx +51 -51
- package/frontend/src/features/ai/components/SystemPromptDialog.tsx +118 -118
- package/frontend/src/features/ai/components/index.ts +6 -6
- package/frontend/src/features/ai/helpers.ts +147 -141
- package/frontend/src/features/ai/{page → pages}/AIPage.tsx +166 -166
- package/frontend/src/features/auth/components/AuthPreview.tsx +96 -96
- package/frontend/src/features/auth/components/OAuthConfigDialog.tsx +1 -0
- package/frontend/src/features/auth/components/UsersDataGrid.tsx +61 -31
- package/frontend/src/features/auth/components/index.ts +5 -5
- package/frontend/src/features/auth/helpers.tsx +8 -0
- package/frontend/src/features/auth/{page → pages}/AuthMethodsPage.tsx +275 -275
- package/frontend/src/features/auth/{page → pages}/UsersPage.tsx +0 -28
- package/frontend/src/features/dashboard/{page → pages}/DashboardPage.tsx +1 -1
- package/frontend/src/features/database/components/DatabaseDataGrid.tsx +0 -2
- package/frontend/src/features/database/components/ForeignKeyCell.tsx +38 -11
- package/frontend/src/features/database/components/ForeignKeyPopover.tsx +18 -8
- package/frontend/src/features/database/components/LinkRecordModal.tsx +61 -13
- package/frontend/src/features/database/components/RecordFormField.tsx +1 -1
- package/frontend/src/features/database/components/SQLModal.tsx +75 -0
- package/frontend/src/features/database/components/TableForm.tsx +0 -4
- package/frontend/src/features/database/components/TableSidebar.tsx +0 -3
- package/frontend/src/features/database/components/TablesEmptyState.tsx +1 -1
- package/frontend/src/features/database/components/TemplatePreview.tsx +1 -2
- package/frontend/src/features/database/constants.ts +16 -28
- package/frontend/src/features/database/hooks/useCSVImport.ts +3 -2
- package/frontend/src/features/database/hooks/useDatabase.ts +66 -0
- package/frontend/src/features/database/hooks/useRawSQL.ts +3 -2
- package/frontend/src/features/database/hooks/useTables.ts +30 -28
- package/frontend/src/features/database/index.ts +1 -0
- package/frontend/src/features/database/{page → pages}/FunctionsPage.tsx +29 -42
- package/frontend/src/features/database/{page → pages}/IndexesPage.tsx +34 -51
- package/frontend/src/features/database/{page → pages}/PoliciesPage.tsx +42 -58
- package/frontend/src/features/database/{page → pages}/SQLEditorPage.tsx +2 -2
- package/frontend/src/features/database/{page → pages}/TablesPage.tsx +0 -42
- package/frontend/src/features/database/{page → pages}/TriggersPage.tsx +34 -51
- package/frontend/src/features/database/services/advance.service.ts +1 -41
- package/frontend/src/features/database/services/database.service.ts +55 -0
- package/frontend/src/features/database/services/record.service.ts +4 -20
- package/frontend/src/features/database/services/table.service.ts +1 -10
- package/frontend/src/features/database/templates/ai-chatbot.ts +6 -6
- package/frontend/src/features/database/templates/ecommerce-platform.ts +2 -2
- package/frontend/src/features/database/templates/instagram-clone.ts +10 -10
- package/frontend/src/features/database/templates/notion-clone.ts +8 -8
- package/frontend/src/features/database/templates/reddit-clone.ts +10 -10
- package/frontend/src/features/deployments/components/DeploymentRow.tsx +93 -0
- package/frontend/src/features/deployments/components/DeploymentsEmptyState.tsx +15 -0
- package/frontend/src/features/deployments/hooks/useDeployments.ts +157 -0
- package/frontend/src/features/deployments/pages/DeploymentsPage.tsx +318 -0
- package/frontend/src/features/deployments/services/deployments.service.ts +63 -0
- package/frontend/src/features/functions/components/FunctionRow.tsx +72 -72
- package/frontend/src/features/functions/components/FunctionsSidebar.tsx +56 -56
- package/frontend/src/features/functions/components/SecretRow.tsx +3 -3
- package/frontend/src/features/functions/components/index.ts +5 -5
- package/frontend/src/features/functions/hooks/useFunctions.ts +5 -4
- package/frontend/src/features/functions/hooks/useSecrets.ts +6 -9
- package/frontend/src/features/functions/{page → pages}/FunctionsPage.tsx +21 -44
- package/frontend/src/features/functions/{page → pages}/SecretsPage.tsx +118 -116
- package/frontend/src/features/functions/services/function.service.ts +8 -25
- package/frontend/src/features/functions/services/secret.service.ts +23 -41
- package/frontend/src/features/login/{page → pages}/CloudLoginPage.tsx +125 -118
- package/frontend/src/features/logs/components/LogDetailPanel.tsx +41 -0
- package/frontend/src/features/logs/components/LogsDataGrid.tsx +32 -1
- package/frontend/src/features/logs/components/index.ts +1 -0
- package/frontend/src/features/logs/hooks/useMcpUsage.ts +13 -66
- package/frontend/src/features/logs/{page → pages}/LogsPage.tsx +36 -6
- package/frontend/src/features/onboard/components/ApiCredentialsSection.tsx +59 -0
- package/frontend/src/features/onboard/components/ConnectionStringSection.tsx +180 -0
- package/frontend/src/features/onboard/components/McpConnectionSection.tsx +159 -0
- package/frontend/src/features/onboard/components/OnboardingController.tsx +68 -0
- package/frontend/src/features/onboard/components/OnboardingModal.tsx +121 -267
- package/frontend/src/features/onboard/components/ShowPasswordButton.tsx +21 -0
- package/frontend/src/features/onboard/components/index.ts +9 -4
- package/frontend/src/features/onboard/components/mcp/CursorDeeplinkGenerator.tsx +1 -1
- package/frontend/src/features/onboard/components/mcp/QoderDeeplinkGenerator.tsx +36 -0
- package/frontend/src/features/onboard/components/mcp/helpers.tsx +123 -98
- package/frontend/src/features/onboard/components/mcp/index.ts +4 -3
- package/frontend/src/features/onboard/index.ts +17 -13
- 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/settings/pages/SettingsPage.tsx +349 -0
- package/frontend/src/features/storage/{page → pages}/StoragePage.tsx +1 -29
- package/frontend/src/features/visualizer/components/AuthNode.tsx +4 -4
- package/frontend/src/features/visualizer/components/SchemaVisualizer.tsx +24 -11
- package/frontend/src/features/visualizer/{page → pages}/VisualizerPage.tsx +11 -36
- package/frontend/src/index.css +249 -249
- package/frontend/src/lib/contexts/ModalContext.tsx +35 -0
- package/frontend/src/lib/contexts/SocketContext.tsx +119 -75
- package/frontend/src/lib/hooks/useMetadata.ts +45 -1
- package/frontend/src/lib/hooks/useModal.tsx +2 -0
- package/frontend/src/lib/routing/AppRoutes.tsx +103 -84
- package/frontend/src/lib/services/metadata.service.ts +20 -3
- package/frontend/src/lib/utils/cloudMessaging.ts +1 -1
- package/frontend/src/lib/utils/menuItems.ts +223 -183
- package/frontend/src/lib/utils/utils.ts +196 -183
- package/frontend/tsconfig.json +25 -25
- package/frontend/tsconfig.node.json +9 -9
- package/functions/deno.json +24 -24
- package/functions/server.ts +6 -6
- package/functions/worker-template.js +1 -1
- package/i18n/README.ar.md +130 -130
- package/i18n/README.de.md +130 -130
- package/i18n/README.es.md +154 -154
- package/i18n/README.fr.md +134 -134
- package/i18n/README.hi.md +129 -129
- package/i18n/README.ja.md +174 -174
- package/i18n/README.ko.md +136 -136
- package/i18n/README.pt-BR.md +131 -131
- package/i18n/README.ru.md +129 -129
- package/i18n/README.zh-CN.md +133 -133
- package/openapi/ai.yaml +825 -715
- package/openapi/auth.yaml +1324 -1244
- package/openapi/email.yaml +158 -0
- package/openapi/functions.yaml +475 -475
- package/openapi/health.yaml +29 -29
- package/openapi/logs.yaml +221 -223
- package/openapi/metadata.yaml +175 -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 +462 -463
- package/package.json +97 -97
- package/shared-schemas/package.json +31 -31
- package/shared-schemas/src/ai-api.schema.ts +251 -143
- package/shared-schemas/src/ai.schema.ts +8 -4
- package/shared-schemas/src/auth-api.schema.ts +380 -339
- package/shared-schemas/src/auth.schema.ts +18 -11
- package/shared-schemas/src/cloud-events.schema.ts +26 -0
- package/shared-schemas/src/database-api.schema.ts +32 -1
- package/shared-schemas/src/database.schema.ts +39 -0
- package/shared-schemas/src/deployments-api.schema.ts +55 -0
- package/shared-schemas/src/deployments.schema.ts +30 -0
- package/shared-schemas/src/docs.schema.ts +32 -0
- package/shared-schemas/src/email-api.schema.ts +30 -0
- package/shared-schemas/src/functions-api.schema.ts +13 -4
- package/shared-schemas/src/functions.schema.ts +1 -1
- package/shared-schemas/src/index.ts +22 -14
- package/shared-schemas/src/metadata.schema.ts +39 -4
- package/shared-schemas/src/realtime-api.schema.ts +111 -0
- package/shared-schemas/src/realtime.schema.ts +143 -0
- package/shared-schemas/src/secrets-api.schema.ts +44 -0
- package/shared-schemas/src/secrets.schema.ts +15 -0
- package/shared-schemas/tsconfig.json +21 -21
- package/tsconfig.json +7 -7
- package/zeabur/README.md +26 -13
- package/zeabur/template.yml +1001 -1032
- package/.cursor/rules/cursor-rules.mdc +0 -94
- package/backend/src/types/profile.ts +0 -55
- package/frontend/src/components/ProjectInfoModal.tsx +0 -128
- package/frontend/src/features/database/hooks/useFullMetadata.ts +0 -18
- package/test-gemini.sh +0 -35
- package/test-usage-admin.sh +0 -57
- package/test-usage.sh +0 -50
- /package/frontend/src/features/auth/{page → pages}/ConfigurationPage.tsx +0 -0
- /package/frontend/src/features/database/{page → pages}/TemplatesPage.tsx +0 -0
- /package/frontend/src/features/login/{page → pages}/LoginPage.tsx +0 -0
- /package/frontend/src/features/logs/{page → pages}/AuditsPage.tsx +0 -0
- /package/frontend/src/features/logs/{page → pages}/MCPLogsPage.tsx +0 -0
|
@@ -13,6 +13,7 @@ import type {
|
|
|
13
13
|
CreateAdminSessionResponse,
|
|
14
14
|
AuthMetadataSchema,
|
|
15
15
|
OAuthProvidersSchema,
|
|
16
|
+
AuthOptions,
|
|
16
17
|
} from '@insforge/shared-schemas';
|
|
17
18
|
import { OAuthConfigService } from '@/services/auth/oauth-config.service.js';
|
|
18
19
|
import { AuthConfigService } from './auth-config.service.js';
|
|
@@ -33,6 +34,7 @@ import {
|
|
|
33
34
|
LinkedInUserInfo,
|
|
34
35
|
DiscordUserInfo,
|
|
35
36
|
XUserInfo,
|
|
37
|
+
AppleUserInfo,
|
|
36
38
|
UserRecord,
|
|
37
39
|
OAuthUserData,
|
|
38
40
|
} from '@/types/auth.js';
|
|
@@ -42,6 +44,7 @@ import { AppError } from '@/api/middlewares/error.js';
|
|
|
42
44
|
import { ERROR_CODES } from '@/types/error-constants.js';
|
|
43
45
|
import { EmailService } from '@/services/email/email.service.js';
|
|
44
46
|
import { XOAuthProvider } from '@/providers/oauth/x.provider.js';
|
|
47
|
+
import { AppleOAuthProvider } from '@/providers/oauth/apple.provider.js';
|
|
45
48
|
|
|
46
49
|
/**
|
|
47
50
|
* Simplified JWT-based auth service
|
|
@@ -62,6 +65,7 @@ export class AuthService {
|
|
|
62
65
|
private facebookOAuthProvider: FacebookOAuthProvider;
|
|
63
66
|
private microsoftOAuthProvider: MicrosoftOAuthProvider;
|
|
64
67
|
private xOAuthProvider: XOAuthProvider;
|
|
68
|
+
private appleOAuthProvider: AppleOAuthProvider;
|
|
65
69
|
|
|
66
70
|
private constructor() {
|
|
67
71
|
this.adminEmail = process.env.ADMIN_EMAIL ?? '';
|
|
@@ -82,6 +86,7 @@ export class AuthService {
|
|
|
82
86
|
this.facebookOAuthProvider = FacebookOAuthProvider.getInstance();
|
|
83
87
|
this.microsoftOAuthProvider = MicrosoftOAuthProvider.getInstance();
|
|
84
88
|
this.xOAuthProvider = XOAuthProvider.getInstance();
|
|
89
|
+
this.appleOAuthProvider = AppleOAuthProvider.getInstance();
|
|
85
90
|
|
|
86
91
|
logger.info('AuthService initialized');
|
|
87
92
|
}
|
|
@@ -105,7 +110,12 @@ export class AuthService {
|
|
|
105
110
|
* User registration
|
|
106
111
|
* Otherwise, returns user with access token for immediate login
|
|
107
112
|
*/
|
|
108
|
-
async register(
|
|
113
|
+
async register(
|
|
114
|
+
email: string,
|
|
115
|
+
password: string,
|
|
116
|
+
name?: string,
|
|
117
|
+
options?: AuthOptions
|
|
118
|
+
): Promise<CreateUserResponse> {
|
|
109
119
|
// Get email auth configuration and validate password
|
|
110
120
|
const authConfigService = AuthConfigService.getInstance();
|
|
111
121
|
const emailAuthConfig = await authConfigService.getAuthConfig();
|
|
@@ -126,16 +136,11 @@ export class AuthService {
|
|
|
126
136
|
try {
|
|
127
137
|
await client.query('BEGIN');
|
|
128
138
|
|
|
139
|
+
const profile = name ? JSON.stringify({ name }) : '{}';
|
|
129
140
|
await client.query(
|
|
130
|
-
`INSERT INTO
|
|
131
|
-
VALUES ($1, $2, $3, $4, $5, NOW(), NOW())`,
|
|
132
|
-
[userId, email, hashedPassword,
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
await client.query(
|
|
136
|
-
`INSERT INTO users (id, nickname, created_at, updated_at)
|
|
137
|
-
VALUES ($1, $2, NOW(), NOW())`,
|
|
138
|
-
[userId, name || null]
|
|
141
|
+
`INSERT INTO auth.users (id, email, password, profile, email_verified, created_at, updated_at)
|
|
142
|
+
VALUES ($1, $2, $3, $4::jsonb, $5, NOW(), NOW())`,
|
|
143
|
+
[userId, email, hashedPassword, profile, false]
|
|
139
144
|
);
|
|
140
145
|
|
|
141
146
|
await client.query('COMMIT');
|
|
@@ -159,7 +164,8 @@ export class AuthService {
|
|
|
159
164
|
if (emailAuthConfig.requireEmailVerification) {
|
|
160
165
|
try {
|
|
161
166
|
if (emailAuthConfig.verifyEmailMethod === 'link') {
|
|
162
|
-
|
|
167
|
+
const redirectTo = emailAuthConfig.signInRedirectTo || options?.emailRedirectTo;
|
|
168
|
+
await this.sendVerificationEmailWithLink(email, redirectTo);
|
|
163
169
|
} else {
|
|
164
170
|
await this.sendVerificationEmailWithCode(email);
|
|
165
171
|
}
|
|
@@ -239,7 +245,7 @@ export class AuthService {
|
|
|
239
245
|
async sendVerificationEmailWithCode(email: string): Promise<void> {
|
|
240
246
|
// Check if user exists
|
|
241
247
|
const pool = this.getPool();
|
|
242
|
-
const result = await pool.query('SELECT * FROM
|
|
248
|
+
const result = await pool.query('SELECT * FROM auth.users WHERE email = $1', [email]);
|
|
243
249
|
const dbUser = result.rows[0];
|
|
244
250
|
if (!dbUser) {
|
|
245
251
|
// Silently succeed to prevent user enumeration
|
|
@@ -257,7 +263,8 @@ export class AuthService {
|
|
|
257
263
|
|
|
258
264
|
// Send email with verification code
|
|
259
265
|
const emailService = EmailService.getInstance();
|
|
260
|
-
|
|
266
|
+
const userName = dbUser.profile?.name || 'User';
|
|
267
|
+
await emailService.sendWithTemplate(email, userName, 'email-verification-code', {
|
|
261
268
|
token: code,
|
|
262
269
|
});
|
|
263
270
|
}
|
|
@@ -267,10 +274,10 @@ export class AuthService {
|
|
|
267
274
|
* Creates a long cryptographic token and sends it via email as a clickable link
|
|
268
275
|
* The link contains only the token (no email) for better privacy and security
|
|
269
276
|
*/
|
|
270
|
-
async sendVerificationEmailWithLink(email: string): Promise<void> {
|
|
277
|
+
async sendVerificationEmailWithLink(email: string, emailRedirectTo?: string): Promise<void> {
|
|
271
278
|
// Check if user exists
|
|
272
279
|
const pool = this.getPool();
|
|
273
|
-
const result = await pool.query('SELECT * FROM
|
|
280
|
+
const result = await pool.query('SELECT * FROM auth.users WHERE email = $1', [email]);
|
|
274
281
|
const dbUser = result.rows[0];
|
|
275
282
|
if (!dbUser) {
|
|
276
283
|
// Silently succeed to prevent user enumeration
|
|
@@ -287,11 +294,13 @@ export class AuthService {
|
|
|
287
294
|
);
|
|
288
295
|
|
|
289
296
|
// Build verification link URL using backend API endpoint
|
|
290
|
-
|
|
297
|
+
// Include redirectTo parameter if provided
|
|
298
|
+
const linkUrl = `${getApiBaseUrl()}/auth/verify-email?token=${token}${emailRedirectTo ? `&redirectTo=${encodeURIComponent(emailRedirectTo)}` : ''}`;
|
|
291
299
|
|
|
292
300
|
// Send email with verification link
|
|
293
301
|
const emailService = EmailService.getInstance();
|
|
294
|
-
|
|
302
|
+
const userName = dbUser.profile?.name || 'User';
|
|
303
|
+
await emailService.sendWithTemplate(email, userName, 'email-verification-link', {
|
|
295
304
|
link: linkUrl,
|
|
296
305
|
});
|
|
297
306
|
}
|
|
@@ -319,7 +328,7 @@ export class AuthService {
|
|
|
319
328
|
|
|
320
329
|
// Update account email verification status
|
|
321
330
|
const result = await client.query(
|
|
322
|
-
`UPDATE
|
|
331
|
+
`UPDATE auth.users
|
|
323
332
|
SET email_verified = true, updated_at = NOW()
|
|
324
333
|
WHERE email = $1
|
|
325
334
|
RETURNING id`,
|
|
@@ -385,7 +394,7 @@ export class AuthService {
|
|
|
385
394
|
|
|
386
395
|
// Update account email verification status
|
|
387
396
|
const result = await client.query(
|
|
388
|
-
`UPDATE
|
|
397
|
+
`UPDATE auth.users
|
|
389
398
|
SET email_verified = true, updated_at = NOW()
|
|
390
399
|
WHERE email = $1
|
|
391
400
|
RETURNING id`,
|
|
@@ -435,7 +444,7 @@ export class AuthService {
|
|
|
435
444
|
async sendResetPasswordEmailWithCode(email: string): Promise<void> {
|
|
436
445
|
// Check if user exists
|
|
437
446
|
const pool = this.getPool();
|
|
438
|
-
const result = await pool.query('SELECT * FROM
|
|
447
|
+
const result = await pool.query('SELECT * FROM auth.users WHERE email = $1', [email]);
|
|
439
448
|
const dbUser = result.rows[0];
|
|
440
449
|
if (!dbUser) {
|
|
441
450
|
// Silently succeed to prevent user enumeration
|
|
@@ -453,7 +462,8 @@ export class AuthService {
|
|
|
453
462
|
|
|
454
463
|
// Send email with reset password code
|
|
455
464
|
const emailService = EmailService.getInstance();
|
|
456
|
-
|
|
465
|
+
const userName = dbUser.profile?.name || 'User';
|
|
466
|
+
await emailService.sendWithTemplate(email, userName, 'reset-password-code', {
|
|
457
467
|
token: code,
|
|
458
468
|
});
|
|
459
469
|
}
|
|
@@ -466,7 +476,7 @@ export class AuthService {
|
|
|
466
476
|
async sendResetPasswordEmailWithLink(email: string): Promise<void> {
|
|
467
477
|
// Check if user exists
|
|
468
478
|
const pool = this.getPool();
|
|
469
|
-
const result = await pool.query('SELECT * FROM
|
|
479
|
+
const result = await pool.query('SELECT * FROM auth.users WHERE email = $1', [email]);
|
|
470
480
|
const dbUser = result.rows[0];
|
|
471
481
|
if (!dbUser) {
|
|
472
482
|
// Silently succeed to prevent user enumeration
|
|
@@ -487,7 +497,8 @@ export class AuthService {
|
|
|
487
497
|
|
|
488
498
|
// Send email with password reset link
|
|
489
499
|
const emailService = EmailService.getInstance();
|
|
490
|
-
|
|
500
|
+
const userName = dbUser.profile?.name || 'User';
|
|
501
|
+
await emailService.sendWithTemplate(email, userName, 'reset-password-link', {
|
|
491
502
|
link: linkUrl,
|
|
492
503
|
});
|
|
493
504
|
}
|
|
@@ -558,7 +569,7 @@ export class AuthService {
|
|
|
558
569
|
|
|
559
570
|
// Update password in the database
|
|
560
571
|
const result = await client.query(
|
|
561
|
-
`UPDATE
|
|
572
|
+
`UPDATE auth.users
|
|
562
573
|
SET password = $1, updated_at = NOW()
|
|
563
574
|
WHERE email = $2
|
|
564
575
|
RETURNING id`,
|
|
@@ -608,10 +619,11 @@ export class AuthService {
|
|
|
608
619
|
user: {
|
|
609
620
|
id: ADMIN_ID,
|
|
610
621
|
email: email,
|
|
611
|
-
name: 'Administrator',
|
|
612
622
|
emailVerified: true,
|
|
613
623
|
createdAt: new Date().toISOString(),
|
|
614
624
|
updatedAt: new Date().toISOString(),
|
|
625
|
+
profile: { name: 'Administrator' },
|
|
626
|
+
metadata: null,
|
|
615
627
|
},
|
|
616
628
|
accessToken,
|
|
617
629
|
};
|
|
@@ -639,10 +651,11 @@ export class AuthService {
|
|
|
639
651
|
user: {
|
|
640
652
|
id: ADMIN_ID,
|
|
641
653
|
email: email as string,
|
|
642
|
-
name: 'Administrator',
|
|
643
654
|
emailVerified: true,
|
|
644
655
|
createdAt: new Date().toISOString(),
|
|
645
656
|
updatedAt: new Date().toISOString(),
|
|
657
|
+
profile: { name: 'Administrator' },
|
|
658
|
+
metadata: null,
|
|
646
659
|
},
|
|
647
660
|
accessToken,
|
|
648
661
|
};
|
|
@@ -670,13 +683,14 @@ export class AuthService {
|
|
|
670
683
|
| MicrosoftUserInfo
|
|
671
684
|
| FacebookUserInfo
|
|
672
685
|
| XUserInfo
|
|
686
|
+
| AppleUserInfo
|
|
673
687
|
| Record<string, unknown>
|
|
674
688
|
): Promise<CreateSessionResponse> {
|
|
675
689
|
const pool = this.getPool();
|
|
676
690
|
|
|
677
|
-
// First, try to find existing user by provider ID in
|
|
691
|
+
// First, try to find existing user by provider ID in auth.user_providers table
|
|
678
692
|
const accountResult = await pool.query(
|
|
679
|
-
'SELECT * FROM
|
|
693
|
+
'SELECT * FROM auth.user_providers WHERE provider = $1 AND provider_account_id = $2',
|
|
680
694
|
[provider, providerId]
|
|
681
695
|
);
|
|
682
696
|
const account = accountResult.rows[0];
|
|
@@ -684,13 +698,13 @@ export class AuthService {
|
|
|
684
698
|
if (account) {
|
|
685
699
|
// Found existing OAuth user, update last login time
|
|
686
700
|
await pool.query(
|
|
687
|
-
'UPDATE
|
|
701
|
+
'UPDATE auth.user_providers SET updated_at = CURRENT_TIMESTAMP WHERE provider = $1 AND provider_account_id = $2',
|
|
688
702
|
[provider, providerId]
|
|
689
703
|
);
|
|
690
704
|
|
|
691
705
|
// Update email_verified to true if not already verified (OAuth login means email is trusted)
|
|
692
706
|
await pool.query(
|
|
693
|
-
'UPDATE
|
|
707
|
+
'UPDATE auth.users SET email_verified = true WHERE id = $1 AND email_verified = false',
|
|
694
708
|
[account.user_id]
|
|
695
709
|
);
|
|
696
710
|
|
|
@@ -710,16 +724,16 @@ export class AuthService {
|
|
|
710
724
|
}
|
|
711
725
|
|
|
712
726
|
// If not found by provider_id, try to find by email in _user table
|
|
713
|
-
const existingUserResult = await pool.query('SELECT * FROM
|
|
727
|
+
const existingUserResult = await pool.query('SELECT * FROM auth.users WHERE email = $1', [
|
|
714
728
|
email,
|
|
715
729
|
]);
|
|
716
730
|
const existingUser = existingUserResult.rows[0];
|
|
717
731
|
|
|
718
732
|
if (existingUser) {
|
|
719
|
-
// Found existing user by email, create
|
|
733
|
+
// Found existing user by email, create auth.user_providers record to link OAuth
|
|
720
734
|
await pool.query(
|
|
721
735
|
`
|
|
722
|
-
INSERT INTO
|
|
736
|
+
INSERT INTO auth.user_providers (
|
|
723
737
|
user_id, provider, provider_account_id,
|
|
724
738
|
provider_data, created_at, updated_at
|
|
725
739
|
)
|
|
@@ -730,7 +744,7 @@ export class AuthService {
|
|
|
730
744
|
|
|
731
745
|
// Update email_verified to true (OAuth login means email is trusted)
|
|
732
746
|
await pool.query(
|
|
733
|
-
'UPDATE
|
|
747
|
+
'UPDATE auth.users SET email_verified = true WHERE id = $1 AND email_verified = false',
|
|
734
748
|
[existingUser.id]
|
|
735
749
|
);
|
|
736
750
|
|
|
@@ -777,6 +791,7 @@ export class AuthService {
|
|
|
777
791
|
| MicrosoftUserInfo
|
|
778
792
|
| FacebookUserInfo
|
|
779
793
|
| XUserInfo
|
|
794
|
+
| AppleUserInfo
|
|
780
795
|
| Record<string, unknown>,
|
|
781
796
|
avatarUrl: string
|
|
782
797
|
): Promise<CreateSessionResponse> {
|
|
@@ -789,26 +804,19 @@ export class AuthService {
|
|
|
789
804
|
await client.query('BEGIN');
|
|
790
805
|
|
|
791
806
|
// Create user record (without password for OAuth users)
|
|
807
|
+
const profile = JSON.stringify({ name: userName, avatar_url: avatarUrl });
|
|
792
808
|
await client.query(
|
|
793
809
|
`
|
|
794
|
-
INSERT INTO
|
|
795
|
-
VALUES ($1, $2, $3, true, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
|
796
|
-
`,
|
|
797
|
-
[userId, email, userName]
|
|
798
|
-
);
|
|
799
|
-
|
|
800
|
-
await client.query(
|
|
801
|
-
`
|
|
802
|
-
INSERT INTO users (id, nickname, avatar_url, created_at, updated_at)
|
|
803
|
-
VALUES ($1, $2, $3, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
|
810
|
+
INSERT INTO auth.users (id, email, profile, email_verified, created_at, updated_at)
|
|
811
|
+
VALUES ($1, $2, $3::jsonb, true, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
|
804
812
|
`,
|
|
805
|
-
[userId,
|
|
813
|
+
[userId, email, profile]
|
|
806
814
|
);
|
|
807
815
|
|
|
808
|
-
// Create
|
|
816
|
+
// Create auth.user_providers record
|
|
809
817
|
await client.query(
|
|
810
818
|
`
|
|
811
|
-
INSERT INTO
|
|
819
|
+
INSERT INTO auth.user_providers (
|
|
812
820
|
user_id, provider, provider_account_id,
|
|
813
821
|
provider_data, created_at, updated_at
|
|
814
822
|
)
|
|
@@ -822,10 +830,11 @@ export class AuthService {
|
|
|
822
830
|
const user: UserSchema = {
|
|
823
831
|
id: userId,
|
|
824
832
|
email,
|
|
825
|
-
name: userName,
|
|
826
833
|
emailVerified: true,
|
|
827
834
|
createdAt: new Date().toISOString(),
|
|
828
835
|
updatedAt: new Date().toISOString(),
|
|
836
|
+
profile: { name: userName, avatar_url: avatarUrl },
|
|
837
|
+
metadata: null,
|
|
829
838
|
};
|
|
830
839
|
|
|
831
840
|
const accessToken = this.tokenManager.generateToken({
|
|
@@ -844,10 +853,15 @@ export class AuthService {
|
|
|
844
853
|
}
|
|
845
854
|
|
|
846
855
|
async getMetadata(): Promise<AuthMetadataSchema> {
|
|
856
|
+
const authConfigService = AuthConfigService.getInstance();
|
|
847
857
|
const oAuthConfigService = OAuthConfigService.getInstance();
|
|
848
|
-
const
|
|
858
|
+
const [oAuthProviders, authConfigs] = await Promise.all([
|
|
859
|
+
oAuthConfigService.getConfiguredProviders(),
|
|
860
|
+
authConfigService.getPublicAuthConfig(),
|
|
861
|
+
]);
|
|
849
862
|
return {
|
|
850
|
-
|
|
863
|
+
oAuthProviders,
|
|
864
|
+
...authConfigs,
|
|
851
865
|
};
|
|
852
866
|
}
|
|
853
867
|
|
|
@@ -870,6 +884,8 @@ export class AuthService {
|
|
|
870
884
|
return this.microsoftOAuthProvider.generateOAuthUrl(state);
|
|
871
885
|
case 'x':
|
|
872
886
|
return this.xOAuthProvider.generateOAuthUrl(state);
|
|
887
|
+
case 'apple':
|
|
888
|
+
return this.appleOAuthProvider.generateOAuthUrl(state);
|
|
873
889
|
default:
|
|
874
890
|
throw new Error(`OAuth provider ${provider} is not implemented yet.`);
|
|
875
891
|
}
|
|
@@ -906,6 +922,9 @@ export class AuthService {
|
|
|
906
922
|
case 'x':
|
|
907
923
|
userData = await this.xOAuthProvider.handleCallback(payload);
|
|
908
924
|
break;
|
|
925
|
+
case 'apple':
|
|
926
|
+
userData = await this.appleOAuthProvider.handleCallback(payload);
|
|
927
|
+
break;
|
|
909
928
|
default:
|
|
910
929
|
throw new Error(`OAuth provider ${provider} is not implemented yet.`);
|
|
911
930
|
}
|
|
@@ -949,6 +968,9 @@ export class AuthService {
|
|
|
949
968
|
case 'x':
|
|
950
969
|
userData = this.xOAuthProvider.handleSharedCallback(payloadData);
|
|
951
970
|
break;
|
|
971
|
+
case 'apple':
|
|
972
|
+
userData = this.appleOAuthProvider.handleSharedCallback(payloadData);
|
|
973
|
+
break;
|
|
952
974
|
case 'microsoft':
|
|
953
975
|
default:
|
|
954
976
|
throw new Error(`OAuth provider ${provider} is not supported for shared callback.`);
|
|
@@ -975,14 +997,17 @@ export class AuthService {
|
|
|
975
997
|
SELECT
|
|
976
998
|
u.id,
|
|
977
999
|
u.email,
|
|
978
|
-
u.
|
|
1000
|
+
u.profile,
|
|
1001
|
+
u.metadata,
|
|
979
1002
|
u.email_verified,
|
|
1003
|
+
u.is_project_admin,
|
|
1004
|
+
u.is_anonymous,
|
|
980
1005
|
u.created_at,
|
|
981
1006
|
u.updated_at,
|
|
982
1007
|
u.password,
|
|
983
1008
|
STRING_AGG(a.provider, ',') as providers
|
|
984
|
-
FROM
|
|
985
|
-
LEFT JOIN
|
|
1009
|
+
FROM auth.users u
|
|
1010
|
+
LEFT JOIN auth.user_providers a ON u.id = a.user_id
|
|
986
1011
|
WHERE u.email = $1
|
|
987
1012
|
GROUP BY u.id
|
|
988
1013
|
`,
|
|
@@ -1003,14 +1028,17 @@ export class AuthService {
|
|
|
1003
1028
|
SELECT
|
|
1004
1029
|
u.id,
|
|
1005
1030
|
u.email,
|
|
1006
|
-
u.
|
|
1031
|
+
u.profile,
|
|
1032
|
+
u.metadata,
|
|
1007
1033
|
u.email_verified,
|
|
1034
|
+
u.is_project_admin,
|
|
1035
|
+
u.is_anonymous,
|
|
1008
1036
|
u.created_at,
|
|
1009
1037
|
u.updated_at,
|
|
1010
1038
|
u.password,
|
|
1011
1039
|
STRING_AGG(a.provider, ',') as providers
|
|
1012
|
-
FROM
|
|
1013
|
-
LEFT JOIN
|
|
1040
|
+
FROM auth.users u
|
|
1041
|
+
LEFT JOIN auth.user_providers a ON u.id = a.user_id
|
|
1014
1042
|
WHERE u.id = $1
|
|
1015
1043
|
GROUP BY u.id
|
|
1016
1044
|
`,
|
|
@@ -1025,36 +1053,29 @@ export class AuthService {
|
|
|
1025
1053
|
* @private
|
|
1026
1054
|
*/
|
|
1027
1055
|
private transformUserRecordToSchema(dbUser: UserRecord): UserSchema {
|
|
1028
|
-
const identities = [];
|
|
1029
1056
|
const providers: string[] = [];
|
|
1030
1057
|
|
|
1031
1058
|
// Add social providers if any
|
|
1032
1059
|
if (dbUser.providers) {
|
|
1033
1060
|
dbUser.providers.split(',').forEach((provider: string) => {
|
|
1034
|
-
identities.push({ provider });
|
|
1035
1061
|
providers.push(provider);
|
|
1036
1062
|
});
|
|
1037
1063
|
}
|
|
1038
1064
|
|
|
1039
1065
|
// Add email provider if password exists
|
|
1040
1066
|
if (dbUser.password) {
|
|
1041
|
-
identities.push({ provider: 'email' });
|
|
1042
1067
|
providers.push('email');
|
|
1043
1068
|
}
|
|
1044
1069
|
|
|
1045
|
-
// Use first provider to determine type: 'email' or 'social'
|
|
1046
|
-
const firstProvider = providers[0];
|
|
1047
|
-
const providerType = firstProvider === 'email' ? 'email' : 'social';
|
|
1048
|
-
|
|
1049
1070
|
return {
|
|
1050
1071
|
id: dbUser.id,
|
|
1051
1072
|
email: dbUser.email,
|
|
1052
|
-
name: dbUser.name,
|
|
1053
1073
|
emailVerified: dbUser.email_verified,
|
|
1054
1074
|
createdAt: dbUser.created_at,
|
|
1055
1075
|
updatedAt: dbUser.updated_at,
|
|
1056
|
-
|
|
1057
|
-
|
|
1076
|
+
providers: providers,
|
|
1077
|
+
profile: dbUser.profile,
|
|
1078
|
+
metadata: dbUser.metadata,
|
|
1058
1079
|
};
|
|
1059
1080
|
}
|
|
1060
1081
|
|
|
@@ -1071,19 +1092,23 @@ export class AuthService {
|
|
|
1071
1092
|
SELECT
|
|
1072
1093
|
u.id,
|
|
1073
1094
|
u.email,
|
|
1074
|
-
u.
|
|
1095
|
+
u.profile,
|
|
1096
|
+
u.metadata,
|
|
1075
1097
|
u.email_verified,
|
|
1098
|
+
u.is_project_admin,
|
|
1099
|
+
u.is_anonymous,
|
|
1076
1100
|
u.created_at,
|
|
1077
1101
|
u.updated_at,
|
|
1078
1102
|
u.password,
|
|
1079
1103
|
STRING_AGG(a.provider, ',') as providers
|
|
1080
|
-
FROM
|
|
1081
|
-
LEFT JOIN
|
|
1104
|
+
FROM auth.users u
|
|
1105
|
+
LEFT JOIN auth.user_providers a ON u.id = a.user_id
|
|
1106
|
+
WHERE u.is_project_admin = false AND u.is_anonymous = false
|
|
1082
1107
|
`;
|
|
1083
1108
|
const params: (string | number)[] = [];
|
|
1084
1109
|
|
|
1085
1110
|
if (search) {
|
|
1086
|
-
query +=
|
|
1111
|
+
query += ` AND (u.email LIKE $1 OR u.profile->>'name' LIKE $2)`;
|
|
1087
1112
|
params.push(`%${search}%`, `%${search}%`);
|
|
1088
1113
|
}
|
|
1089
1114
|
|
|
@@ -1096,11 +1121,12 @@ export class AuthService {
|
|
|
1096
1121
|
// Transform users
|
|
1097
1122
|
const users = dbUsers.map((dbUser) => this.transformUserRecordToSchema(dbUser));
|
|
1098
1123
|
|
|
1099
|
-
// Get total count
|
|
1100
|
-
let countQuery =
|
|
1124
|
+
// Get total count (exclude admins and anonymous users)
|
|
1125
|
+
let countQuery =
|
|
1126
|
+
'SELECT COUNT(*) as count FROM auth.users WHERE is_project_admin = false AND is_anonymous = false';
|
|
1101
1127
|
const countParams: string[] = [];
|
|
1102
1128
|
if (search) {
|
|
1103
|
-
countQuery +=
|
|
1129
|
+
countQuery += ` AND (email LIKE $1 OR profile->>'name' LIKE $2)`;
|
|
1104
1130
|
countParams.push(`%${search}%`, `%${search}%`);
|
|
1105
1131
|
}
|
|
1106
1132
|
const countResult = await pool.query(countQuery, countParams);
|
|
@@ -1123,13 +1149,61 @@ export class AuthService {
|
|
|
1123
1149
|
return this.transformUserRecordToSchema(dbUser);
|
|
1124
1150
|
}
|
|
1125
1151
|
|
|
1152
|
+
/**
|
|
1153
|
+
* Get user profile by ID (public endpoint - returns id and profile)
|
|
1154
|
+
*/
|
|
1155
|
+
async getProfileById(
|
|
1156
|
+
userId: string
|
|
1157
|
+
): Promise<{ id: string; profile: Record<string, unknown> | null } | null> {
|
|
1158
|
+
const pool = this.getPool();
|
|
1159
|
+
const result = await pool.query(`SELECT id, profile FROM auth.users WHERE id = $1`, [userId]);
|
|
1160
|
+
|
|
1161
|
+
if (result.rows.length === 0) {
|
|
1162
|
+
return null;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
return {
|
|
1166
|
+
id: result.rows[0].id,
|
|
1167
|
+
profile: result.rows[0].profile,
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
/**
|
|
1172
|
+
* Update user profile (for authenticated user updating their own profile)
|
|
1173
|
+
*/
|
|
1174
|
+
async updateProfile(
|
|
1175
|
+
userId: string,
|
|
1176
|
+
profile: Record<string, unknown>
|
|
1177
|
+
): Promise<{ id: string; profile: Record<string, unknown> | null }> {
|
|
1178
|
+
const pool = this.getPool();
|
|
1179
|
+
const result = await pool.query(
|
|
1180
|
+
`UPDATE auth.users
|
|
1181
|
+
SET profile = COALESCE(profile, '{}'::jsonb) || $1::jsonb, updated_at = NOW()
|
|
1182
|
+
WHERE id = $2
|
|
1183
|
+
RETURNING id, profile`,
|
|
1184
|
+
[profile, userId]
|
|
1185
|
+
);
|
|
1186
|
+
|
|
1187
|
+
if (result.rows.length === 0) {
|
|
1188
|
+
throw new AppError('User not found', 404, ERROR_CODES.NOT_FOUND);
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
return {
|
|
1192
|
+
id: result.rows[0].id,
|
|
1193
|
+
profile: result.rows[0].profile,
|
|
1194
|
+
};
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1126
1197
|
/**
|
|
1127
1198
|
* Delete multiple users by IDs
|
|
1128
1199
|
*/
|
|
1129
1200
|
async deleteUsers(userIds: string[]): Promise<number> {
|
|
1130
1201
|
const pool = this.getPool();
|
|
1131
1202
|
const placeholders = userIds.map((_, i) => `$${i + 1}`).join(',');
|
|
1132
|
-
const result = await pool.query(
|
|
1203
|
+
const result = await pool.query(
|
|
1204
|
+
`DELETE FROM auth.users WHERE id IN (${placeholders})`,
|
|
1205
|
+
userIds
|
|
1206
|
+
);
|
|
1133
1207
|
|
|
1134
1208
|
return result.rowCount || 0;
|
|
1135
1209
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { AuthService } from './auth.service.js';
|
|
2
|
-
export { AuthConfigService } from './auth-config.service.js';
|
|
3
|
-
export { AuthOTPService } from './auth-otp.service.js';
|
|
4
|
-
export { OAuthConfigService } from './oauth-config.service.js';
|
|
1
|
+
export { AuthService } from './auth.service.js';
|
|
2
|
+
export { AuthConfigService } from './auth-config.service.js';
|
|
3
|
+
export { AuthOTPService } from './auth-otp.service.js';
|
|
4
|
+
export { OAuthConfigService } from './oauth-config.service.js';
|
|
@@ -62,7 +62,7 @@ export class OAuthConfigService {
|
|
|
62
62
|
use_shared_key as "useSharedKey",
|
|
63
63
|
created_at as "createdAt",
|
|
64
64
|
updated_at as "updatedAt"
|
|
65
|
-
FROM
|
|
65
|
+
FROM auth.oauth_configs
|
|
66
66
|
ORDER BY provider ASC`
|
|
67
67
|
);
|
|
68
68
|
|
|
@@ -82,7 +82,7 @@ export class OAuthConfigService {
|
|
|
82
82
|
const result = await this.getPool().query(
|
|
83
83
|
`SELECT
|
|
84
84
|
provider
|
|
85
|
-
FROM
|
|
85
|
+
FROM auth.oauth_configs
|
|
86
86
|
ORDER BY provider ASC`
|
|
87
87
|
);
|
|
88
88
|
|
|
@@ -108,7 +108,7 @@ export class OAuthConfigService {
|
|
|
108
108
|
use_shared_key as "useSharedKey",
|
|
109
109
|
created_at as "createdAt",
|
|
110
110
|
updated_at as "updatedAt"
|
|
111
|
-
FROM
|
|
111
|
+
FROM auth.oauth_configs
|
|
112
112
|
WHERE LOWER(provider) = LOWER($1)
|
|
113
113
|
LIMIT 1`,
|
|
114
114
|
[provider]
|
|
@@ -133,7 +133,7 @@ export class OAuthConfigService {
|
|
|
133
133
|
const result = await this.getPool().query(
|
|
134
134
|
`SELECT
|
|
135
135
|
secret_id as "secretId"
|
|
136
|
-
FROM
|
|
136
|
+
FROM auth.oauth_configs
|
|
137
137
|
WHERE LOWER(provider) = LOWER($1)
|
|
138
138
|
LIMIT 1`,
|
|
139
139
|
[provider]
|
|
@@ -167,7 +167,7 @@ export class OAuthConfigService {
|
|
|
167
167
|
|
|
168
168
|
// Check if config already exists for this provider
|
|
169
169
|
const existingConfig = await client.query(
|
|
170
|
-
'SELECT id FROM
|
|
170
|
+
'SELECT id FROM auth.oauth_configs WHERE LOWER(provider) = LOWER($1)',
|
|
171
171
|
[input.provider]
|
|
172
172
|
);
|
|
173
173
|
|
|
@@ -235,7 +235,7 @@ export class OAuthConfigService {
|
|
|
235
235
|
|
|
236
236
|
// Create new OAuth config
|
|
237
237
|
const result = await client.query(
|
|
238
|
-
`INSERT INTO
|
|
238
|
+
`INSERT INTO auth.oauth_configs (provider, client_id, secret_id, redirect_uri, scopes, use_shared_key)
|
|
239
239
|
VALUES ($1, $2, $3, $4, $5, $6)
|
|
240
240
|
RETURNING
|
|
241
241
|
id,
|
|
@@ -282,7 +282,7 @@ export class OAuthConfigService {
|
|
|
282
282
|
|
|
283
283
|
// Get existing config with secret_id
|
|
284
284
|
const existingResult = await client.query(
|
|
285
|
-
`SELECT id, secret_id as "secretId" FROM
|
|
285
|
+
`SELECT id, secret_id as "secretId" FROM auth.oauth_configs WHERE LOWER(provider) = LOWER($1)`,
|
|
286
286
|
[provider]
|
|
287
287
|
);
|
|
288
288
|
|
|
@@ -306,7 +306,7 @@ export class OAuthConfigService {
|
|
|
306
306
|
value: input.clientSecret,
|
|
307
307
|
});
|
|
308
308
|
// Add secret_id to the update query
|
|
309
|
-
await client.query(`UPDATE
|
|
309
|
+
await client.query(`UPDATE auth.oauth_configs SET secret_id = $1 WHERE id = $2`, [
|
|
310
310
|
secret.id,
|
|
311
311
|
existingConfig.id,
|
|
312
312
|
]);
|
|
@@ -353,7 +353,7 @@ export class OAuthConfigService {
|
|
|
353
353
|
values.push(provider.toLowerCase());
|
|
354
354
|
|
|
355
355
|
const result = await client.query(
|
|
356
|
-
`UPDATE
|
|
356
|
+
`UPDATE auth.oauth_configs
|
|
357
357
|
SET ${updates.join(', ')}
|
|
358
358
|
WHERE LOWER(provider) = $${paramCount}
|
|
359
359
|
RETURNING
|
|
@@ -406,7 +406,7 @@ export class OAuthConfigService {
|
|
|
406
406
|
|
|
407
407
|
// Get existing config with secret_id
|
|
408
408
|
const existingResult = await client.query(
|
|
409
|
-
`SELECT id, secret_id as "secretId" FROM
|
|
409
|
+
`SELECT id, secret_id as "secretId" FROM auth.oauth_configs WHERE LOWER(provider) = LOWER($1)`,
|
|
410
410
|
[provider]
|
|
411
411
|
);
|
|
412
412
|
|
|
@@ -419,13 +419,13 @@ export class OAuthConfigService {
|
|
|
419
419
|
|
|
420
420
|
// Delete OAuth config (secret will be restricted due to foreign key)
|
|
421
421
|
const result = await client.query(
|
|
422
|
-
'DELETE FROM
|
|
422
|
+
'DELETE FROM auth.oauth_configs WHERE LOWER(provider) = LOWER($1)',
|
|
423
423
|
[provider]
|
|
424
424
|
);
|
|
425
425
|
|
|
426
426
|
// Try to delete the associated secret (will fail if still referenced)
|
|
427
427
|
try {
|
|
428
|
-
await client.query('DELETE FROM
|
|
428
|
+
await client.query('DELETE FROM system.secrets WHERE id = $1', [existingConfig.secretId]);
|
|
429
429
|
logger.info('Associated secret deleted', { secretId: existingConfig.secretId });
|
|
430
430
|
} catch {
|
|
431
431
|
logger.warn('Could not delete associated secret, it may be in use elsewhere', {
|