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,780 +0,0 @@
|
|
|
1
|
-
import jwt from 'jsonwebtoken';
|
|
2
|
-
import bcrypt from 'bcryptjs';
|
|
3
|
-
import crypto from 'crypto';
|
|
4
|
-
import axios from 'axios';
|
|
5
|
-
import { OAuth2Client } from 'google-auth-library';
|
|
6
|
-
import dotenv from 'dotenv';
|
|
7
|
-
import { verifyCloudToken } from '@/utils/cloud-token.js';
|
|
8
|
-
import path from 'path';
|
|
9
|
-
import fs from 'fs';
|
|
10
|
-
import { fileURLToPath } from 'url';
|
|
11
|
-
import { DatabaseManager } from '@/core/database/manager.js';
|
|
12
|
-
import logger from '@/utils/logger.js';
|
|
13
|
-
import type {
|
|
14
|
-
UserSchema,
|
|
15
|
-
CreateUserResponse,
|
|
16
|
-
CreateSessionResponse,
|
|
17
|
-
CreateAdminSessionResponse,
|
|
18
|
-
TokenPayloadSchema,
|
|
19
|
-
AuthMetadataSchema,
|
|
20
|
-
} from '@insforge/shared-schemas';
|
|
21
|
-
import { OAuthConfigService } from './oauth';
|
|
22
|
-
import { GitHubEmailInfo, GitHubUserInfo, GoogleUserInfo, UserRecord } from '@/types/auth';
|
|
23
|
-
import { ADMIN_ID } from '@/utils/constants';
|
|
24
|
-
|
|
25
|
-
const JWT_SECRET = () => process.env.JWT_SECRET ?? '';
|
|
26
|
-
const JWT_EXPIRES_IN = '7d';
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Simplified JWT-based auth service
|
|
30
|
-
* Handles all authentication operations including OAuth
|
|
31
|
-
*/
|
|
32
|
-
export class AuthService {
|
|
33
|
-
private static instance: AuthService;
|
|
34
|
-
private adminEmail: string;
|
|
35
|
-
private adminPassword: string;
|
|
36
|
-
private db;
|
|
37
|
-
private processedCodes: Set<string>;
|
|
38
|
-
private tokenCache: Map<string, { access_token: string; id_token: string }>;
|
|
39
|
-
|
|
40
|
-
private constructor() {
|
|
41
|
-
// Load .env file if not already loaded
|
|
42
|
-
if (!process.env.JWT_SECRET) {
|
|
43
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
44
|
-
const __dirname = path.dirname(__filename);
|
|
45
|
-
const envPath = path.resolve(__dirname, '../../../../.env');
|
|
46
|
-
if (fs.existsSync(envPath)) {
|
|
47
|
-
dotenv.config({ path: envPath });
|
|
48
|
-
} else {
|
|
49
|
-
logger.warn('No .env file found, using default environment variables.');
|
|
50
|
-
dotenv.config();
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (!process.env.JWT_SECRET) {
|
|
55
|
-
throw new Error('JWT_SECRET environment variable is required');
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
this.adminEmail = process.env.ADMIN_EMAIL ?? '';
|
|
59
|
-
this.adminPassword = process.env.ADMIN_PASSWORD ?? '';
|
|
60
|
-
|
|
61
|
-
if (!this.adminEmail || !this.adminPassword) {
|
|
62
|
-
throw new Error('ADMIN_EMAIL and ADMIN_PASSWORD environment variables are required');
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const dbManager = DatabaseManager.getInstance();
|
|
66
|
-
this.db = dbManager.getDb();
|
|
67
|
-
|
|
68
|
-
// Initialize OAuth helpers
|
|
69
|
-
this.processedCodes = new Set();
|
|
70
|
-
this.tokenCache = new Map();
|
|
71
|
-
|
|
72
|
-
logger.info('AuthService initialized');
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
public static getInstance(): AuthService {
|
|
76
|
-
if (!AuthService.instance) {
|
|
77
|
-
AuthService.instance = new AuthService();
|
|
78
|
-
}
|
|
79
|
-
return AuthService.instance;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Transform database user to API format (snake_case to camelCase)
|
|
84
|
-
*/
|
|
85
|
-
private dbUserToApiUser(dbUser: UserRecord): UserSchema {
|
|
86
|
-
return {
|
|
87
|
-
id: dbUser.id,
|
|
88
|
-
email: dbUser.email,
|
|
89
|
-
name: dbUser.name,
|
|
90
|
-
emailVerified: dbUser.email_verified,
|
|
91
|
-
createdAt: dbUser.created_at,
|
|
92
|
-
updatedAt: dbUser.updated_at,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Generate JWT token for users and admins
|
|
98
|
-
*/
|
|
99
|
-
generateToken(payload: TokenPayloadSchema): string {
|
|
100
|
-
return jwt.sign(payload, JWT_SECRET(), {
|
|
101
|
-
algorithm: 'HS256',
|
|
102
|
-
expiresIn: JWT_EXPIRES_IN,
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Generate anonymous JWT token (never expires)
|
|
108
|
-
*/
|
|
109
|
-
generateAnonToken(): string {
|
|
110
|
-
const payload = {
|
|
111
|
-
sub: 'anonymous',
|
|
112
|
-
email: 'anon@insforge.com',
|
|
113
|
-
role: 'anon',
|
|
114
|
-
};
|
|
115
|
-
return jwt.sign(payload, JWT_SECRET(), {
|
|
116
|
-
algorithm: 'HS256',
|
|
117
|
-
// No expiresIn means token never expires
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Verify JWT token
|
|
123
|
-
*/
|
|
124
|
-
verifyToken(token: string): TokenPayloadSchema {
|
|
125
|
-
try {
|
|
126
|
-
const decoded = jwt.verify(token, JWT_SECRET()) as TokenPayloadSchema;
|
|
127
|
-
return {
|
|
128
|
-
sub: decoded.sub,
|
|
129
|
-
email: decoded.email,
|
|
130
|
-
role: decoded.role || 'authenticated',
|
|
131
|
-
};
|
|
132
|
-
} catch {
|
|
133
|
-
throw new Error('Invalid token');
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* User registration
|
|
139
|
-
*/
|
|
140
|
-
async register(email: string, password: string, name?: string): Promise<CreateUserResponse> {
|
|
141
|
-
const existingUser = await this.db
|
|
142
|
-
.prepare('SELECT id FROM _accounts WHERE email = ?')
|
|
143
|
-
.get(email);
|
|
144
|
-
|
|
145
|
-
if (existingUser) {
|
|
146
|
-
throw new Error('User already exists');
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const hashedPassword = await bcrypt.hash(password, 10);
|
|
150
|
-
const userId = crypto.randomUUID();
|
|
151
|
-
|
|
152
|
-
await this.db
|
|
153
|
-
.prepare(
|
|
154
|
-
`
|
|
155
|
-
INSERT INTO _accounts (id, email, password, name, email_verified, created_at, updated_at)
|
|
156
|
-
VALUES (?, ?, ?, ?, ?, NOW(), NOW())
|
|
157
|
-
`
|
|
158
|
-
)
|
|
159
|
-
.run(userId, email, hashedPassword, name || null, false);
|
|
160
|
-
|
|
161
|
-
await this.db
|
|
162
|
-
.prepare(
|
|
163
|
-
`
|
|
164
|
-
INSERT INTO users (id, nickname, created_at, updated_at)
|
|
165
|
-
VALUES (?, ?, NOW(), NOW())
|
|
166
|
-
`
|
|
167
|
-
)
|
|
168
|
-
.run(userId, name || null);
|
|
169
|
-
|
|
170
|
-
const dbUser = await this.db
|
|
171
|
-
.prepare(
|
|
172
|
-
'SELECT id, email, name, email_verified, created_at, updated_at FROM _accounts WHERE id = ?'
|
|
173
|
-
)
|
|
174
|
-
.get(userId);
|
|
175
|
-
const user = this.dbUserToApiUser(dbUser);
|
|
176
|
-
const accessToken = this.generateToken({ sub: userId, email, role: 'authenticated' });
|
|
177
|
-
|
|
178
|
-
return { user, accessToken };
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* User login
|
|
183
|
-
*/
|
|
184
|
-
async login(email: string, password: string): Promise<CreateSessionResponse> {
|
|
185
|
-
const dbUser = await this.db.prepare('SELECT * FROM _accounts WHERE email = ?').get(email);
|
|
186
|
-
|
|
187
|
-
if (!dbUser || !dbUser.password) {
|
|
188
|
-
throw new Error('Invalid credentials');
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
const validPassword = await bcrypt.compare(password, dbUser.password);
|
|
192
|
-
if (!validPassword) {
|
|
193
|
-
throw new Error('Invalid credentials');
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const user = this.dbUserToApiUser(dbUser);
|
|
197
|
-
const accessToken = this.generateToken({
|
|
198
|
-
sub: dbUser.id,
|
|
199
|
-
email: dbUser.email,
|
|
200
|
-
role: 'authenticated',
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
return { user, accessToken };
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Admin login (validates against env variables only)
|
|
208
|
-
*/
|
|
209
|
-
adminLogin(email: string, password: string): CreateAdminSessionResponse {
|
|
210
|
-
// Simply validate against environment variables
|
|
211
|
-
if (email !== this.adminEmail || password !== this.adminPassword) {
|
|
212
|
-
throw new Error('Invalid admin credentials');
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Use a fixed admin ID for the system administrator
|
|
216
|
-
|
|
217
|
-
// Return admin user with JWT token - no database interaction
|
|
218
|
-
const accessToken = this.generateToken({ sub: ADMIN_ID, email, role: 'project_admin' });
|
|
219
|
-
|
|
220
|
-
return {
|
|
221
|
-
user: {
|
|
222
|
-
id: ADMIN_ID,
|
|
223
|
-
email: email,
|
|
224
|
-
name: 'Administrator',
|
|
225
|
-
emailVerified: true,
|
|
226
|
-
createdAt: new Date().toISOString(),
|
|
227
|
-
updatedAt: new Date().toISOString(),
|
|
228
|
-
},
|
|
229
|
-
accessToken,
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Admin login with authorization token (validates JWT from external issuer)
|
|
235
|
-
*/
|
|
236
|
-
async adminLoginWithAuthorizationCode(code: string): Promise<CreateAdminSessionResponse> {
|
|
237
|
-
try {
|
|
238
|
-
// Use the helper function to verify cloud token
|
|
239
|
-
const { payload } = await verifyCloudToken(code);
|
|
240
|
-
|
|
241
|
-
// If verification succeeds, extract user info and generate internal token
|
|
242
|
-
const email = payload['email'] || payload['sub'] || 'admin@insforge.local';
|
|
243
|
-
|
|
244
|
-
// Generate internal access token
|
|
245
|
-
const accessToken = this.generateToken({
|
|
246
|
-
sub: ADMIN_ID,
|
|
247
|
-
email: email as string,
|
|
248
|
-
role: 'project_admin',
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
return {
|
|
252
|
-
user: {
|
|
253
|
-
id: ADMIN_ID,
|
|
254
|
-
email: email as string,
|
|
255
|
-
name: 'Administrator',
|
|
256
|
-
emailVerified: true,
|
|
257
|
-
createdAt: new Date().toISOString(),
|
|
258
|
-
updatedAt: new Date().toISOString(),
|
|
259
|
-
},
|
|
260
|
-
accessToken,
|
|
261
|
-
};
|
|
262
|
-
} catch (error) {
|
|
263
|
-
logger.error('Admin token verification failed:', error);
|
|
264
|
-
throw new Error('Invalid admin credentials');
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* Find or create third-party user (main OAuth user handler)
|
|
270
|
-
* Adapted from 3-table to 2-table structure
|
|
271
|
-
*/
|
|
272
|
-
async findOrCreateThirdPartyUser(
|
|
273
|
-
provider: string,
|
|
274
|
-
providerId: string,
|
|
275
|
-
email: string,
|
|
276
|
-
userName: string,
|
|
277
|
-
avatarUrl: string,
|
|
278
|
-
identityData: GoogleUserInfo | GitHubUserInfo | Record<string, unknown>
|
|
279
|
-
): Promise<CreateSessionResponse> {
|
|
280
|
-
// First, try to find existing user by provider ID in _account_providers table
|
|
281
|
-
const account = await this.db
|
|
282
|
-
.prepare('SELECT * FROM _account_providers WHERE provider = ? AND provider_account_id = ?')
|
|
283
|
-
.get(provider, providerId);
|
|
284
|
-
|
|
285
|
-
if (account) {
|
|
286
|
-
// Found existing OAuth user, update last login time
|
|
287
|
-
await this.db
|
|
288
|
-
.prepare(
|
|
289
|
-
'UPDATE _account_providers SET updated_at = CURRENT_TIMESTAMP WHERE provider = ? AND provider_account_id = ?'
|
|
290
|
-
)
|
|
291
|
-
.run(provider, providerId);
|
|
292
|
-
|
|
293
|
-
const dbUser = await this.db
|
|
294
|
-
.prepare(
|
|
295
|
-
'SELECT id, email, name, email_verified, created_at, updated_at FROM _accounts WHERE id = ?'
|
|
296
|
-
)
|
|
297
|
-
.get(account.user_id);
|
|
298
|
-
|
|
299
|
-
const user = this.dbUserToApiUser(dbUser);
|
|
300
|
-
const accessToken = this.generateToken({
|
|
301
|
-
sub: user.id,
|
|
302
|
-
email: user.email,
|
|
303
|
-
role: 'authenticated',
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
return { user, accessToken };
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// If not found by provider_id, try to find by email in _user table
|
|
310
|
-
const existingUser = await this.db
|
|
311
|
-
.prepare('SELECT * FROM _accounts WHERE email = ?')
|
|
312
|
-
.get(email);
|
|
313
|
-
|
|
314
|
-
if (existingUser) {
|
|
315
|
-
// Found existing user by email, create _account_providers record to link OAuth
|
|
316
|
-
await this.db
|
|
317
|
-
.prepare(
|
|
318
|
-
`
|
|
319
|
-
INSERT INTO _account_providers (
|
|
320
|
-
user_id, provider, provider_account_id,
|
|
321
|
-
provider_data, created_at, updated_at
|
|
322
|
-
)
|
|
323
|
-
VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
|
324
|
-
`
|
|
325
|
-
)
|
|
326
|
-
.run(existingUser.id, provider, providerId, JSON.stringify(identityData));
|
|
327
|
-
|
|
328
|
-
const user = this.dbUserToApiUser(existingUser);
|
|
329
|
-
const accessToken = this.generateToken({
|
|
330
|
-
sub: existingUser.id,
|
|
331
|
-
email: existingUser.email,
|
|
332
|
-
role: 'authenticated',
|
|
333
|
-
});
|
|
334
|
-
|
|
335
|
-
return { user, accessToken };
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
// Create new user with OAuth data
|
|
339
|
-
return this.createThirdPartyUser(
|
|
340
|
-
provider,
|
|
341
|
-
userName,
|
|
342
|
-
email,
|
|
343
|
-
providerId,
|
|
344
|
-
identityData,
|
|
345
|
-
avatarUrl
|
|
346
|
-
);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
* Create new third-party user
|
|
351
|
-
*/
|
|
352
|
-
private async createThirdPartyUser(
|
|
353
|
-
provider: string,
|
|
354
|
-
userName: string,
|
|
355
|
-
email: string,
|
|
356
|
-
providerId: string,
|
|
357
|
-
identityData: GoogleUserInfo | GitHubUserInfo | Record<string, unknown>,
|
|
358
|
-
avatarUrl: string
|
|
359
|
-
): Promise<CreateSessionResponse> {
|
|
360
|
-
const userId = crypto.randomUUID();
|
|
361
|
-
|
|
362
|
-
await this.db.exec('BEGIN');
|
|
363
|
-
|
|
364
|
-
try {
|
|
365
|
-
// Create user record (without password for OAuth users)
|
|
366
|
-
await this.db
|
|
367
|
-
.prepare(
|
|
368
|
-
`
|
|
369
|
-
INSERT INTO _accounts (id, email, name, email_verified, created_at, updated_at)
|
|
370
|
-
VALUES (?, ?, ?, true, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
|
371
|
-
`
|
|
372
|
-
)
|
|
373
|
-
.run(userId, email, userName);
|
|
374
|
-
|
|
375
|
-
await this.db
|
|
376
|
-
.prepare(
|
|
377
|
-
`
|
|
378
|
-
INSERT INTO users (id, nickname, avatar_url, created_at, updated_at)
|
|
379
|
-
VALUES (?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
|
380
|
-
`
|
|
381
|
-
)
|
|
382
|
-
.run(userId, userName, avatarUrl);
|
|
383
|
-
|
|
384
|
-
// Create _account_providers record
|
|
385
|
-
await this.db
|
|
386
|
-
.prepare(
|
|
387
|
-
`
|
|
388
|
-
INSERT INTO _account_providers (
|
|
389
|
-
user_id, provider, provider_account_id,
|
|
390
|
-
provider_data, created_at, updated_at
|
|
391
|
-
)
|
|
392
|
-
VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
|
393
|
-
`
|
|
394
|
-
)
|
|
395
|
-
.run(
|
|
396
|
-
userId,
|
|
397
|
-
provider,
|
|
398
|
-
providerId,
|
|
399
|
-
JSON.stringify({ ...identityData, avatar_url: avatarUrl })
|
|
400
|
-
);
|
|
401
|
-
|
|
402
|
-
await this.db.exec('COMMIT');
|
|
403
|
-
|
|
404
|
-
const user: UserSchema = {
|
|
405
|
-
id: userId,
|
|
406
|
-
email,
|
|
407
|
-
name: userName,
|
|
408
|
-
emailVerified: true,
|
|
409
|
-
createdAt: new Date().toISOString(),
|
|
410
|
-
updatedAt: new Date().toISOString(),
|
|
411
|
-
};
|
|
412
|
-
|
|
413
|
-
const accessToken = this.generateToken({
|
|
414
|
-
sub: userId,
|
|
415
|
-
email,
|
|
416
|
-
role: 'authenticated',
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
return { user, accessToken };
|
|
420
|
-
} catch (error) {
|
|
421
|
-
await this.db.exec('ROLLBACK');
|
|
422
|
-
throw error;
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
/**
|
|
427
|
-
* Generate Google OAuth authorization URL
|
|
428
|
-
*/
|
|
429
|
-
async generateGoogleAuthUrl(state?: string): Promise<string | undefined> {
|
|
430
|
-
const oauthConfigService = OAuthConfigService.getInstance();
|
|
431
|
-
const config = await oauthConfigService.getConfigByProvider('google');
|
|
432
|
-
|
|
433
|
-
if (!config) {
|
|
434
|
-
throw new Error('Google OAuth not configured');
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
const selfBaseUrl = process.env.API_BASE_URL || 'http://localhost:7130';
|
|
438
|
-
|
|
439
|
-
if (config?.useSharedKey) {
|
|
440
|
-
if (!state) {
|
|
441
|
-
logger.warn('Shared Google OAuth called without state parameter');
|
|
442
|
-
throw new Error('State parameter is required for shared Google OAuth');
|
|
443
|
-
}
|
|
444
|
-
// Use shared keys if configured
|
|
445
|
-
const cloudBaseUrl = process.env.CLOUD_API_HOST || 'https://api.insforge.dev';
|
|
446
|
-
const redirectUri = `${selfBaseUrl}/api/auth/oauth/shared/callback/${state}`;
|
|
447
|
-
const authUrl = await fetch(
|
|
448
|
-
`${cloudBaseUrl}/auth/v1/shared/google?redirect_uri=${encodeURIComponent(redirectUri)}`,
|
|
449
|
-
{
|
|
450
|
-
method: 'GET',
|
|
451
|
-
headers: {
|
|
452
|
-
'Content-Type': 'application/json',
|
|
453
|
-
},
|
|
454
|
-
}
|
|
455
|
-
);
|
|
456
|
-
if (!authUrl.ok) {
|
|
457
|
-
logger.error('Failed to fetch Google auth URL:', {
|
|
458
|
-
status: authUrl.status,
|
|
459
|
-
statusText: authUrl.statusText,
|
|
460
|
-
});
|
|
461
|
-
throw new Error(`Failed to fetch Google auth URL: ${authUrl.statusText}`);
|
|
462
|
-
}
|
|
463
|
-
const responseData = (await authUrl.json()) as { auth_url?: string; url?: string };
|
|
464
|
-
return responseData.auth_url || responseData.url || '';
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
logger.debug('Google OAuth Config (fresh from DB):', {
|
|
468
|
-
clientId: config.clientId ? 'SET' : 'NOT SET',
|
|
469
|
-
});
|
|
470
|
-
|
|
471
|
-
const authUrl = new URL('https://accounts.google.com/o/oauth2/v2/auth');
|
|
472
|
-
authUrl.searchParams.set('client_id', config.clientId ?? '');
|
|
473
|
-
authUrl.searchParams.set('redirect_uri', `${selfBaseUrl}/api/auth/oauth/google/callback`);
|
|
474
|
-
authUrl.searchParams.set('response_type', 'code');
|
|
475
|
-
authUrl.searchParams.set(
|
|
476
|
-
'scope',
|
|
477
|
-
config.scopes ? config.scopes.join(' ') : 'openid email profile'
|
|
478
|
-
);
|
|
479
|
-
authUrl.searchParams.set('access_type', 'offline');
|
|
480
|
-
if (state) {
|
|
481
|
-
authUrl.searchParams.set('state', state);
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
return authUrl.toString();
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
/**
|
|
488
|
-
* Generate GitHub OAuth authorization URL - ALWAYS reads fresh from DB
|
|
489
|
-
*/
|
|
490
|
-
async generateGitHubAuthUrl(state?: string): Promise<string> {
|
|
491
|
-
const oauthConfigService = OAuthConfigService.getInstance();
|
|
492
|
-
const config = await oauthConfigService.getConfigByProvider('github');
|
|
493
|
-
|
|
494
|
-
if (!config) {
|
|
495
|
-
throw new Error('GitHub OAuth not configured');
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
const selfBaseUrl = process.env.API_BASE_URL || 'http://localhost:7130';
|
|
499
|
-
|
|
500
|
-
if (config?.useSharedKey) {
|
|
501
|
-
if (!state) {
|
|
502
|
-
logger.warn('Shared GitHub OAuth called without state parameter');
|
|
503
|
-
throw new Error('State parameter is required for shared GitHub OAuth');
|
|
504
|
-
}
|
|
505
|
-
// Use shared keys if configured
|
|
506
|
-
const cloudBaseUrl = process.env.CLOUD_API_HOST || 'https://api.insforge.dev';
|
|
507
|
-
const redirectUri = `${selfBaseUrl}/api/auth/oauth/shared/callback/${state}`;
|
|
508
|
-
const authUrl = await fetch(
|
|
509
|
-
`${cloudBaseUrl}/auth/v1/shared/github?redirect_uri=${encodeURIComponent(redirectUri)}`,
|
|
510
|
-
{
|
|
511
|
-
method: 'GET',
|
|
512
|
-
headers: {
|
|
513
|
-
'Content-Type': 'application/json',
|
|
514
|
-
},
|
|
515
|
-
}
|
|
516
|
-
);
|
|
517
|
-
if (!authUrl.ok) {
|
|
518
|
-
logger.error('Failed to fetch GitHub auth URL:', {
|
|
519
|
-
status: authUrl.status,
|
|
520
|
-
statusText: authUrl.statusText,
|
|
521
|
-
});
|
|
522
|
-
throw new Error(`Failed to fetch GitHub auth URL: ${authUrl.statusText}`);
|
|
523
|
-
}
|
|
524
|
-
const responseData = (await authUrl.json()) as { auth_url?: string; url?: string };
|
|
525
|
-
return responseData.auth_url || responseData.url || '';
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
logger.debug('GitHub OAuth Config (fresh from DB):', {
|
|
529
|
-
clientId: config.clientId ? 'SET' : 'NOT SET',
|
|
530
|
-
});
|
|
531
|
-
|
|
532
|
-
const authUrl = new URL('https://github.com/login/oauth/authorize');
|
|
533
|
-
authUrl.searchParams.set('client_id', config.clientId ?? '');
|
|
534
|
-
authUrl.searchParams.set('redirect_uri', `${selfBaseUrl}/api/auth/oauth/github/callback`);
|
|
535
|
-
authUrl.searchParams.set('scope', config.scopes ? config.scopes.join(' ') : 'user:email');
|
|
536
|
-
if (state) {
|
|
537
|
-
authUrl.searchParams.set('state', state);
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
return authUrl.toString();
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
/**
|
|
544
|
-
* Exchange Google code for tokens
|
|
545
|
-
*/
|
|
546
|
-
async exchangeCodeToTokenByGoogle(
|
|
547
|
-
code: string
|
|
548
|
-
): Promise<{ access_token: string; id_token: string }> {
|
|
549
|
-
// Check cache first
|
|
550
|
-
if (this.processedCodes.has(code)) {
|
|
551
|
-
const cachedTokens = this.tokenCache.get(code);
|
|
552
|
-
if (cachedTokens) {
|
|
553
|
-
logger.debug('Returning cached tokens for already processed code.');
|
|
554
|
-
return cachedTokens;
|
|
555
|
-
}
|
|
556
|
-
throw new Error('Authorization code is currently being processed.');
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
const oauthConfigService = OAuthConfigService.getInstance();
|
|
560
|
-
const config = await oauthConfigService.getConfigByProvider('google');
|
|
561
|
-
|
|
562
|
-
if (!config) {
|
|
563
|
-
throw new Error('Google OAuth not configured');
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
try {
|
|
567
|
-
this.processedCodes.add(code);
|
|
568
|
-
|
|
569
|
-
logger.info('Exchanging Google code for tokens', {
|
|
570
|
-
hasCode: !!code,
|
|
571
|
-
clientId: config.clientId?.substring(0, 10) + '...',
|
|
572
|
-
});
|
|
573
|
-
|
|
574
|
-
const clientSecret = await oauthConfigService.getClientSecretByProvider('google');
|
|
575
|
-
const selfBaseUrl = process.env.API_BASE_URL || 'http://localhost:7130';
|
|
576
|
-
const response = await axios.post('https://oauth2.googleapis.com/token', {
|
|
577
|
-
code,
|
|
578
|
-
client_id: config.clientId,
|
|
579
|
-
client_secret: clientSecret,
|
|
580
|
-
redirect_uri: `${selfBaseUrl}/api/auth/oauth/google/callback`,
|
|
581
|
-
grant_type: 'authorization_code',
|
|
582
|
-
});
|
|
583
|
-
|
|
584
|
-
if (!response.data.access_token || !response.data.id_token) {
|
|
585
|
-
throw new Error('Failed to get tokens from Google');
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
const result = {
|
|
589
|
-
access_token: response.data.access_token,
|
|
590
|
-
id_token: response.data.id_token,
|
|
591
|
-
};
|
|
592
|
-
|
|
593
|
-
// Cache the successful token exchange
|
|
594
|
-
this.tokenCache.set(code, result);
|
|
595
|
-
|
|
596
|
-
// Set a timeout to clear the code and cache to prevent memory leaks
|
|
597
|
-
setTimeout(() => {
|
|
598
|
-
this.processedCodes.delete(code);
|
|
599
|
-
this.tokenCache.delete(code);
|
|
600
|
-
}, 60000); // 1 minute timeout
|
|
601
|
-
|
|
602
|
-
return result;
|
|
603
|
-
} catch (error) {
|
|
604
|
-
// If the request fails, remove the code immediately to allow for a retry
|
|
605
|
-
this.processedCodes.delete(code);
|
|
606
|
-
|
|
607
|
-
if (axios.isAxiosError(error) && error.response) {
|
|
608
|
-
logger.error('Google token exchange failed', {
|
|
609
|
-
status: error.response.status,
|
|
610
|
-
error: error.response.data,
|
|
611
|
-
});
|
|
612
|
-
throw new Error(`Google OAuth error: ${JSON.stringify(error.response.data)}`);
|
|
613
|
-
}
|
|
614
|
-
throw error;
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
/**
|
|
619
|
-
* Verify Google ID token and get user info
|
|
620
|
-
*/
|
|
621
|
-
async verifyGoogleToken(idToken: string) {
|
|
622
|
-
const oauthConfigService = OAuthConfigService.getInstance();
|
|
623
|
-
const config = await oauthConfigService.getConfigByProvider('google');
|
|
624
|
-
|
|
625
|
-
if (!config) {
|
|
626
|
-
throw new Error('Google OAuth not configured');
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
const clientSecret = await oauthConfigService.getClientSecretByProvider('google');
|
|
630
|
-
|
|
631
|
-
if (!clientSecret) {
|
|
632
|
-
throw new Error('Google Client Secret not conifgured.');
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
// Create OAuth2Client with fresh config
|
|
636
|
-
const googleClient = new OAuth2Client(config.clientId, clientSecret, config.redirectUri);
|
|
637
|
-
|
|
638
|
-
try {
|
|
639
|
-
// Properly verify the ID token with Google's servers
|
|
640
|
-
const ticket = await googleClient.verifyIdToken({
|
|
641
|
-
idToken,
|
|
642
|
-
audience: config.clientId,
|
|
643
|
-
});
|
|
644
|
-
|
|
645
|
-
const payload = ticket.getPayload();
|
|
646
|
-
if (!payload) {
|
|
647
|
-
throw new Error('Invalid Google token payload');
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
return {
|
|
651
|
-
sub: payload.sub,
|
|
652
|
-
email: payload.email || '',
|
|
653
|
-
email_verified: payload.email_verified || false,
|
|
654
|
-
name: payload.name || '',
|
|
655
|
-
picture: payload.picture || '',
|
|
656
|
-
given_name: payload.given_name || '',
|
|
657
|
-
family_name: payload.family_name || '',
|
|
658
|
-
locale: payload.locale || '',
|
|
659
|
-
};
|
|
660
|
-
} catch (error) {
|
|
661
|
-
logger.error('Google token verification failed:', error);
|
|
662
|
-
throw new Error(`Google token verification failed: ${error}`);
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
/**
|
|
667
|
-
* Find or create Google user
|
|
668
|
-
*/
|
|
669
|
-
async findOrCreateGoogleUser(googleUserInfo: GoogleUserInfo): Promise<CreateSessionResponse> {
|
|
670
|
-
const userName = googleUserInfo.name || googleUserInfo.email.split('@')[0];
|
|
671
|
-
return this.findOrCreateThirdPartyUser(
|
|
672
|
-
'google',
|
|
673
|
-
googleUserInfo.sub,
|
|
674
|
-
googleUserInfo.email,
|
|
675
|
-
userName,
|
|
676
|
-
googleUserInfo.picture || '',
|
|
677
|
-
googleUserInfo
|
|
678
|
-
);
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
/**
|
|
682
|
-
* Exchange GitHub code for access token
|
|
683
|
-
*/
|
|
684
|
-
async exchangeGitHubCodeForToken(code: string): Promise<string> {
|
|
685
|
-
const oauthConfigService = OAuthConfigService.getInstance();
|
|
686
|
-
const config = await oauthConfigService.getConfigByProvider('github');
|
|
687
|
-
|
|
688
|
-
if (!config) {
|
|
689
|
-
throw new Error('GitHub OAuth not configured');
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
const clientSecret = await oauthConfigService.getClientSecretByProvider('github');
|
|
693
|
-
const selfBaseUrl = process.env.API_BASE_URL || 'http://localhost:7130';
|
|
694
|
-
const response = await axios.post(
|
|
695
|
-
'https://github.com/login/oauth/access_token',
|
|
696
|
-
{
|
|
697
|
-
client_id: config.clientId,
|
|
698
|
-
client_secret: clientSecret,
|
|
699
|
-
code,
|
|
700
|
-
redirect_uri: `${selfBaseUrl}/api/auth/oauth/github/callback`,
|
|
701
|
-
},
|
|
702
|
-
{
|
|
703
|
-
headers: {
|
|
704
|
-
Accept: 'application/json',
|
|
705
|
-
},
|
|
706
|
-
}
|
|
707
|
-
);
|
|
708
|
-
|
|
709
|
-
if (!response.data.access_token) {
|
|
710
|
-
throw new Error('Failed to get access token from GitHub');
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
return response.data.access_token;
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
/**
|
|
717
|
-
* Get GitHub user info
|
|
718
|
-
*/
|
|
719
|
-
async getGitHubUserInfo(accessToken: string) {
|
|
720
|
-
const userResponse = await axios.get('https://api.github.com/user', {
|
|
721
|
-
headers: {
|
|
722
|
-
Authorization: `Bearer ${accessToken}`,
|
|
723
|
-
},
|
|
724
|
-
});
|
|
725
|
-
|
|
726
|
-
// GitHub doesn't always return email in user endpoint
|
|
727
|
-
let email = userResponse.data.email;
|
|
728
|
-
|
|
729
|
-
if (!email) {
|
|
730
|
-
const emailResponse = await axios.get('https://api.github.com/user/emails', {
|
|
731
|
-
headers: {
|
|
732
|
-
Authorization: `Bearer ${accessToken}`,
|
|
733
|
-
},
|
|
734
|
-
});
|
|
735
|
-
|
|
736
|
-
const primaryEmail = emailResponse.data.find((e: GitHubEmailInfo) => e.primary);
|
|
737
|
-
email = primaryEmail ? primaryEmail.email : emailResponse.data[0]?.email;
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
return {
|
|
741
|
-
id: userResponse.data.id,
|
|
742
|
-
login: userResponse.data.login,
|
|
743
|
-
name: userResponse.data.name,
|
|
744
|
-
email: email || `${userResponse.data.login}@users.noreply.github.com`,
|
|
745
|
-
avatar_url: userResponse.data.avatar_url,
|
|
746
|
-
};
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
/**
|
|
750
|
-
* Find or create GitHub user
|
|
751
|
-
*/
|
|
752
|
-
async findOrCreateGitHubUser(githubUserInfo: GitHubUserInfo): Promise<CreateSessionResponse> {
|
|
753
|
-
const userName = githubUserInfo.name || githubUserInfo.login;
|
|
754
|
-
const email = githubUserInfo.email || `${githubUserInfo.login}@users.noreply.github.com`;
|
|
755
|
-
|
|
756
|
-
return this.findOrCreateThirdPartyUser(
|
|
757
|
-
'github',
|
|
758
|
-
githubUserInfo.id.toString(),
|
|
759
|
-
email,
|
|
760
|
-
userName,
|
|
761
|
-
githubUserInfo.avatar_url || '',
|
|
762
|
-
githubUserInfo
|
|
763
|
-
);
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
async getMetadata(): Promise<AuthMetadataSchema> {
|
|
767
|
-
const oAuthConfigService = OAuthConfigService.getInstance();
|
|
768
|
-
const oAuthConfigs = await oAuthConfigService.getAllConfigs();
|
|
769
|
-
return {
|
|
770
|
-
oauths: oAuthConfigs,
|
|
771
|
-
};
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
/**
|
|
775
|
-
* Get database instance for direct queries
|
|
776
|
-
*/
|
|
777
|
-
getDb() {
|
|
778
|
-
return this.db;
|
|
779
|
-
}
|
|
780
|
-
}
|