insforge 0.3.2 → 1.2.10
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/.cursor/rules/cursor-rules.mdc +94 -0
- package/.dockerignore +3 -0
- package/.env.example +33 -4
- package/.github/ISSUE_TEMPLATE/bug_report.yml +13 -60
- package/.github/ISSUE_TEMPLATE/config.yml +2 -2
- package/.github/ISSUE_TEMPLATE/feature_request.yml +10 -63
- package/.github/PULL_REQUEST_TEMPLATE.md +7 -0
- package/.github/workflows/build-image.yml +2 -1
- package/.github/workflows/e2e.yml +63 -0
- package/CHANGELOG.md +41 -0
- package/CLAUDE_PLUGIN.md +104 -0
- package/CODE_OF_CONDUCT.md +128 -0
- package/CONTRIBUTING.md +1 -1
- package/Dockerfile +4 -1
- package/README.md +66 -18
- 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 +115 -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 +57 -0
- package/auth/src/pages/SignUpPage.tsx +57 -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 +9 -9
- 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} +20 -24
- package/backend/src/api/routes/auth/index.routes.ts +570 -0
- package/backend/src/api/routes/auth/oauth.routes.ts +448 -0
- package/backend/src/api/routes/{database.advance.ts → database/advance.routes.ts} +107 -65
- package/backend/src/api/routes/database/index.routes.ts +13 -0
- package/backend/src/api/routes/{database.records.ts → database/records.routes.ts} +22 -8
- package/backend/src/api/routes/{database.tables.ts → database/tables.routes.ts} +20 -23
- package/backend/src/api/routes/docs/index.routes.ts +76 -0
- package/backend/src/api/routes/functions/index.routes.ts +188 -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} +21 -31
- package/backend/src/api/routes/{secrets.ts → secrets/index.routes.ts} +27 -22
- package/backend/src/api/routes/{storage.ts → storage/index.routes.ts} +34 -53
- package/backend/src/api/routes/usage/index.routes.ts +89 -0
- package/backend/src/infra/config/app.config.ts +51 -0
- package/backend/src/{core/database/manager.ts → infra/database/database.manager.ts} +76 -85
- 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/{core/secrets/encryption.ts → infra/security/encryption.manager.ts} +3 -2
- package/backend/src/infra/security/token.manager.ts +125 -0
- package/backend/src/{core/socket/socket.ts → infra/socket/socket.manager.ts} +15 -15
- package/backend/src/providers/ai/openrouter.provider.ts +377 -0
- package/backend/src/providers/email/base.provider.ts +41 -0
- package/backend/src/providers/email/cloud.provider.ts +187 -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/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 +7 -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 +70 -74
- 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 +1136 -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 +811 -0
- package/backend/src/services/email/email.service.ts +75 -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/{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 +66 -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/{core/socket/types.ts → types/socket.ts} +5 -6
- package/backend/src/utils/environment.ts +9 -3
- package/backend/src/utils/logger.ts +20 -2
- package/backend/src/utils/seed.ts +150 -57
- package/backend/src/utils/sql-parser.ts +1 -1
- package/backend/src/utils/utils.ts +114 -0
- package/backend/src/utils/validations.ts +40 -4
- 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 +1 -1
- package/backend/tests/local/test-e2e.sh +1 -1
- package/backend/tests/local/test-functions.sh +123 -0
- package/backend/tests/local/test-logs.sh +132 -0
- package/backend/tests/local/test-public-bucket.sh +3 -3
- package/backend/tests/local/test-secrets.sh +14 -12
- package/backend/tests/local/test-traditional-rest.sh +2 -2
- package/backend/tests/manual/test-rawsql-modes.sh +244 -0
- package/backend/tests/test-config.sh +37 -1
- 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 +1 -1
- 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 +60 -4
- package/docker-compose.yml +65 -4
- package/docker-init/db/db-init.sql +6 -34
- package/docker-init/logs/vector.yml +236 -0
- package/docs/README.md +44 -0
- package/docs/changelog.mdx +67 -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/functions/architecture.mdx +105 -0
- package/docs/core-concepts/functions/sdk.mdx +184 -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/docs.json +210 -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/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 +55 -374
- package/docs/introduction.mdx +45 -0
- package/docs/logo/dark.svg +22 -0
- package/docs/logo/light.svg +20 -0
- package/docs/partnership.mdx +647 -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/frontend/package.json +10 -4
- package/frontend/src/App.tsx +13 -82
- package/frontend/src/assets/icons/connected.svg +3 -0
- package/frontend/src/assets/icons/loader.svg +9 -0
- package/frontend/src/assets/logos/apple.svg +4 -0
- package/frontend/src/assets/logos/discord.svg +1 -1
- package/frontend/src/assets/logos/facebook.svg +3 -0
- 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/spotify.svg +17 -0
- package/frontend/src/assets/logos/tiktok.svg +6 -0
- 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 +133 -92
- 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/page/AIPage.tsx +67 -79
- 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 +53 -30
- package/frontend/src/features/auth/components/UserFormDialog.tsx +13 -6
- package/frontend/src/features/auth/components/UsersDataGrid.tsx +44 -14
- package/frontend/src/features/auth/components/index.ts +5 -0
- package/frontend/src/features/auth/helpers.tsx +200 -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/page/AuthMethodsPage.tsx +275 -0
- package/frontend/src/features/auth/page/ConfigurationPage.tsx +395 -0
- package/frontend/src/features/auth/page/UsersPage.tsx +285 -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/page/DashboardPage.tsx +187 -169
- 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/TableEmptyState.tsx +6 -5
- package/frontend/src/features/database/components/TableForm.tsx +28 -15
- 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/useFullMetadata.ts +18 -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 +131 -0
- package/frontend/src/features/database/index.ts +6 -1
- package/frontend/src/features/database/page/FunctionsPage.tsx +211 -0
- package/frontend/src/features/database/page/IndexesPage.tsx +240 -0
- package/frontend/src/features/database/page/PoliciesPage.tsx +248 -0
- package/frontend/src/features/database/page/SQLEditorPage.tsx +382 -0
- package/frontend/src/features/database/page/{DatabasePage.tsx → TablesPage.tsx} +186 -185
- package/frontend/src/features/database/page/TemplatesPage.tsx +39 -0
- package/frontend/src/features/database/page/TriggersPage.tsx +242 -0
- package/frontend/src/features/database/services/advance.service.ts +66 -0
- package/frontend/src/features/database/services/{database.service.ts → record.service.ts} +67 -64
- package/frontend/src/features/database/services/table.service.ts +64 -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/page/FunctionsPage.tsx +160 -17
- package/frontend/src/features/functions/{components/SecretsContent.tsx → page/SecretsPage.tsx} +8 -12
- 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/page/CloudLoginPage.tsx +79 -54
- package/frontend/src/features/login/page/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 +181 -0
- package/frontend/src/features/logs/index.ts +8 -2
- package/frontend/src/features/logs/page/AuditsPage.tsx +91 -38
- package/frontend/src/features/logs/page/LogsPage.tsx +152 -0
- package/frontend/src/features/logs/page/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/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/StoragePage.tsx +41 -115
- 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/page/VisualizerPage.tsx +33 -29
- 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 +5 -6
- 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 +84 -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 +183 -0
- package/frontend/src/lib/utils/{validation-schemas.ts → schemaValidations.ts} +10 -5
- package/frontend/src/lib/utils/utils.ts +19 -1
- package/frontend/src/vite-env.d.ts +1 -0
- package/frontend/vite.config.ts +5 -3
- package/functions/server.ts +28 -3
- 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 +31 -4
- package/openapi/auth.yaml +827 -146
- package/package.json +16 -7
- package/shared-schemas/package.json +1 -1
- package/shared-schemas/src/ai-api.schema.ts +34 -58
- package/shared-schemas/src/ai.schema.ts +5 -0
- package/shared-schemas/src/auth-api.schema.ts +154 -8
- package/shared-schemas/src/auth.schema.ts +42 -6
- package/shared-schemas/src/cloud-events.schema.ts +57 -0
- package/shared-schemas/src/database-api.schema.ts +3 -3
- package/shared-schemas/src/database.schema.ts +1 -1
- package/shared-schemas/src/index.ts +1 -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 +9 -4
- package/test-gemini.sh +35 -0
- package/test-usage-admin.sh +57 -0
- package/test-usage.sh +50 -0
- 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 -781
- 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/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/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/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/login/components/AuthErrorBoundary.tsx +0 -87
- package/frontend/src/features/login/components/PrivateRoute.tsx +0 -24
- 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/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/backend/{migrations → src/infra/database/migrations}/000_create-base-tables.sql +0 -0
- /package/backend/{migrations → src/infra/database/migrations}/001_create-helper-functions.sql +0 -0
- /package/backend/{migrations → src/infra/database/migrations}/002_rename-auth-tables.sql +0 -0
- /package/backend/{migrations → src/infra/database/migrations}/003_create-users-table.sql +0 -0
- /package/backend/{migrations → src/infra/database/migrations}/004_add-reload-postgrest-func.sql +0 -0
- /package/backend/{migrations → src/infra/database/migrations}/005_enable-project-admin-modify-users.sql +0 -0
- /package/backend/{migrations → src/infra/database/migrations}/006_modify-ai-usage-table.sql +0 -0
- /package/backend/{migrations → src/infra/database/migrations}/007_drop-metadata-table.sql +0 -0
- /package/backend/{migrations → src/infra/database/migrations}/008_add-system-tables.sql +0 -0
- /package/backend/{migrations → src/infra/database/migrations}/009_add-function-secrets.sql +0 -0
- /package/backend/{migrations → src/infra/database/migrations}/010_modify-ai-config-modalities.sql +0 -0
- /package/backend/{migrations → src/infra/database/migrations}/011_refactor-secrets-table.sql +0 -0
- /package/backend/{migrations → src/infra/database/migrations}/012_add-storage-uploaded-by.sql +0 -0
- /package/frontend/src/{features/metadata → lib}/services/metadata.service.ts +0 -0
|
@@ -0,0 +1,1032 @@
|
|
|
1
|
+
# yaml-language-server: $schema=https://schema.zeabur.app/template.json
|
|
2
|
+
apiVersion: zeabur.com/v1
|
|
3
|
+
kind: Template
|
|
4
|
+
metadata:
|
|
5
|
+
name: InsForge
|
|
6
|
+
spec:
|
|
7
|
+
description: InsForge is the Agent-Native Supabase Alternative, enabling AI agents to build and manage full-stack applications autonomously.
|
|
8
|
+
coverImage: https://cdn.zeabur.com/insforge.png
|
|
9
|
+
icon: https://avatars.githubusercontent.com/u/198419463?s=96&v=4
|
|
10
|
+
variables:
|
|
11
|
+
- key: PUBLIC_DOMAIN
|
|
12
|
+
type: DOMAIN
|
|
13
|
+
name: InsForge Domain
|
|
14
|
+
description: The domain for accessing your InsForge application.
|
|
15
|
+
- key: ADMIN_EMAIL
|
|
16
|
+
type: STRING
|
|
17
|
+
name: Admin Email
|
|
18
|
+
description: Email address for the admin user.
|
|
19
|
+
- key: ADMIN_PASSWORD
|
|
20
|
+
type: STRING
|
|
21
|
+
name: Admin Password
|
|
22
|
+
description: Password for admin. Must be at least 8 characters.
|
|
23
|
+
- key: OPENROUTER_API_KEY
|
|
24
|
+
type: STRING
|
|
25
|
+
name: OpenRouter API Key
|
|
26
|
+
description: API key for OpenRouter LLM services (optional).
|
|
27
|
+
tags:
|
|
28
|
+
- Development
|
|
29
|
+
- Database
|
|
30
|
+
- API
|
|
31
|
+
- Platform
|
|
32
|
+
readme: |-
|
|
33
|
+
# InsForge
|
|
34
|
+
|
|
35
|
+
InsForge is the Agent-Native Supabase Alternative, enabling AI agents to build and manage full-stack applications autonomously.
|
|
36
|
+
|
|
37
|
+
## Features
|
|
38
|
+
|
|
39
|
+
- **Authentication System** - User management and secure authentication
|
|
40
|
+
- **Database Storage** - Flexible PostgreSQL database with automatic schema management
|
|
41
|
+
- **File Management** - Secure file upload and storage capabilities
|
|
42
|
+
- **PostgREST API** - Automatic REST API generation from database schema
|
|
43
|
+
- **Serverless Functions** - Edge functions for custom business logic
|
|
44
|
+
- **AI Agent Integration** - Connect AI agents like Claude or GPT to manage your backend
|
|
45
|
+
|
|
46
|
+
## Quick Start
|
|
47
|
+
|
|
48
|
+
This template provides **one-click deployment** of the complete InsForge platform:
|
|
49
|
+
|
|
50
|
+
1. **Deploy** - Click deploy and bind a domain
|
|
51
|
+
2. **Login** - Use your admin credentials to access the dashboard
|
|
52
|
+
3. **Connect AI Agent** - Link your AI agent (Claude, GPT, etc.) through the dashboard
|
|
53
|
+
4. **Build Apps** - Use natural language prompts to create applications:
|
|
54
|
+
- "Build a todo app with user authentication"
|
|
55
|
+
- "Create an Instagram clone with image upload"
|
|
56
|
+
- "Build a blog with comments and likes"
|
|
57
|
+
|
|
58
|
+
## Use Cases
|
|
59
|
+
|
|
60
|
+
- **AI-Generated Frontends** - Rapidly create backends for AI-generated frontend projects
|
|
61
|
+
- **Agent-Driven Development** - Let AI agents manage your entire backend infrastructure
|
|
62
|
+
- **Rapid Prototyping** - Build full-stack applications using natural language
|
|
63
|
+
|
|
64
|
+
## Community
|
|
65
|
+
|
|
66
|
+
- [Discord](https://discord.gg/MPxwj5xVvW) - Join our community
|
|
67
|
+
- [GitHub](https://github.com/InsForge/InsForge) - Contribute to the project
|
|
68
|
+
- Email: info@insforge.dev
|
|
69
|
+
|
|
70
|
+
services:
|
|
71
|
+
- name: postgres
|
|
72
|
+
icon: https://raw.githubusercontent.com/zeabur/service-icons/main/marketplace/postgresql.svg
|
|
73
|
+
template: PREBUILT
|
|
74
|
+
spec:
|
|
75
|
+
source:
|
|
76
|
+
image: postgres:15.13
|
|
77
|
+
command:
|
|
78
|
+
- docker-entrypoint.sh
|
|
79
|
+
- -c
|
|
80
|
+
- config_file=/etc/postgresql/postgresql.conf
|
|
81
|
+
ports:
|
|
82
|
+
- id: database
|
|
83
|
+
port: 5432
|
|
84
|
+
type: TCP
|
|
85
|
+
volumes:
|
|
86
|
+
- id: data
|
|
87
|
+
dir: /var/lib/postgresql/data
|
|
88
|
+
instructions:
|
|
89
|
+
- title: Connection String
|
|
90
|
+
content: postgres://postgres:${POSTGRES_PASSWORD}@${PORT_FORWARDED_HOSTNAME}:${DATABASE_PORT_FORWARDED_PORT}/insforge
|
|
91
|
+
- title: PostgreSQL Connect Command
|
|
92
|
+
content: psql "postgres://postgres:${POSTGRES_PASSWORD}@${PORT_FORWARDED_HOSTNAME}:${DATABASE_PORT_FORWARDED_PORT}/insforge"
|
|
93
|
+
- title: PostgreSQL username
|
|
94
|
+
content: postgres
|
|
95
|
+
- title: PostgresSQL password
|
|
96
|
+
content: ${POSTGRES_PASSWORD}
|
|
97
|
+
- title: PostgresSQL database
|
|
98
|
+
content: insforge
|
|
99
|
+
- title: PostgreSQL host
|
|
100
|
+
content: ${PORT_FORWARDED_HOSTNAME}
|
|
101
|
+
- title: PostgreSQL port
|
|
102
|
+
content: ${DATABASE_PORT_FORWARDED_PORT}
|
|
103
|
+
env:
|
|
104
|
+
PGDATA:
|
|
105
|
+
default: /var/lib/postgresql/data/pgdata
|
|
106
|
+
POSTGRES_CONNECTION_STRING:
|
|
107
|
+
default: postgres://postgres:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/insforge
|
|
108
|
+
expose: true
|
|
109
|
+
POSTGRES_DATABASE:
|
|
110
|
+
default: insforge
|
|
111
|
+
expose: true
|
|
112
|
+
POSTGRES_DB:
|
|
113
|
+
default: insforge
|
|
114
|
+
POSTGRES_HOST:
|
|
115
|
+
default: ${CONTAINER_HOSTNAME}
|
|
116
|
+
expose: true
|
|
117
|
+
POSTGRES_PASSWORD:
|
|
118
|
+
default: ${PASSWORD}
|
|
119
|
+
expose: true
|
|
120
|
+
readonly: true
|
|
121
|
+
POSTGRES_PORT:
|
|
122
|
+
default: ${DATABASE_PORT}
|
|
123
|
+
expose: true
|
|
124
|
+
POSTGRES_URI:
|
|
125
|
+
default: ${POSTGRES_CONNECTION_STRING}
|
|
126
|
+
expose: true
|
|
127
|
+
POSTGRES_USER:
|
|
128
|
+
default: postgres
|
|
129
|
+
POSTGRES_USERNAME:
|
|
130
|
+
default: postgres
|
|
131
|
+
expose: true
|
|
132
|
+
JWT_SECRET:
|
|
133
|
+
default: ${PASSWORD}
|
|
134
|
+
expose: true
|
|
135
|
+
readonly: true
|
|
136
|
+
JWT_EXP:
|
|
137
|
+
default: "3600"
|
|
138
|
+
configs:
|
|
139
|
+
- path: /etc/postgresql/postgresql.conf
|
|
140
|
+
template: |
|
|
141
|
+
# PostgreSQL configuration for InsForge
|
|
142
|
+
# Enable logical replication for Logflare
|
|
143
|
+
|
|
144
|
+
# Listen on all interfaces to allow container connections
|
|
145
|
+
listen_addresses = '*'
|
|
146
|
+
|
|
147
|
+
# Set WAL level to logical for Logflare replication
|
|
148
|
+
wal_level = logical
|
|
149
|
+
|
|
150
|
+
# Set max replication slots (needed for logical replication)
|
|
151
|
+
max_replication_slots = 10
|
|
152
|
+
|
|
153
|
+
# Set max WAL senders
|
|
154
|
+
max_wal_senders = 10
|
|
155
|
+
|
|
156
|
+
# Shared preload libraries (if needed)
|
|
157
|
+
# shared_preload_libraries = 'pg_stat_statements'
|
|
158
|
+
- path: /docker-entrypoint-initdb.d/01-init.sql
|
|
159
|
+
template: |
|
|
160
|
+
-- init.sql
|
|
161
|
+
-- Create role for anonymous user
|
|
162
|
+
CREATE ROLE anon NOLOGIN;
|
|
163
|
+
|
|
164
|
+
-- Create role for authenticator
|
|
165
|
+
CREATE ROLE authenticated NOLOGIN;
|
|
166
|
+
|
|
167
|
+
-- Create project admin role for admin users
|
|
168
|
+
CREATE ROLE project_admin NOLOGIN;
|
|
169
|
+
|
|
170
|
+
GRANT USAGE ON SCHEMA public TO anon;
|
|
171
|
+
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO anon;
|
|
172
|
+
GRANT USAGE ON SCHEMA public TO authenticated;
|
|
173
|
+
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO authenticated;
|
|
174
|
+
GRANT USAGE ON SCHEMA public TO project_admin;
|
|
175
|
+
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO project_admin;
|
|
176
|
+
|
|
177
|
+
-- Grant permissions to roles
|
|
178
|
+
-- NOTICE: The anon role is intended for unauthenticated users, so it should only have read access.
|
|
179
|
+
GRANT SELECT ON ALL TABLES IN SCHEMA public TO anon;
|
|
180
|
+
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
|
181
|
+
GRANT SELECT ON TABLES TO anon;
|
|
182
|
+
|
|
183
|
+
GRANT SELECT ON ALL TABLES IN SCHEMA public TO authenticated;
|
|
184
|
+
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
|
185
|
+
GRANT SELECT ON TABLES TO authenticated;
|
|
186
|
+
|
|
187
|
+
GRANT INSERT ON ALL TABLES IN SCHEMA public TO authenticated;
|
|
188
|
+
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
|
189
|
+
GRANT INSERT ON TABLES TO authenticated;
|
|
190
|
+
|
|
191
|
+
GRANT UPDATE ON ALL TABLES IN SCHEMA public TO authenticated;
|
|
192
|
+
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
|
193
|
+
GRANT UPDATE ON TABLES TO authenticated;
|
|
194
|
+
|
|
195
|
+
GRANT DELETE ON ALL TABLES IN SCHEMA public TO authenticated;
|
|
196
|
+
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
|
197
|
+
GRANT DELETE ON TABLES TO authenticated;
|
|
198
|
+
|
|
199
|
+
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO project_admin;
|
|
200
|
+
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
|
201
|
+
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO project_admin;
|
|
202
|
+
|
|
203
|
+
-- Create function to automatically create RLS policies for new tables
|
|
204
|
+
CREATE OR REPLACE FUNCTION public.create_default_policies()
|
|
205
|
+
RETURNS event_trigger AS $$
|
|
206
|
+
DECLARE
|
|
207
|
+
obj record;
|
|
208
|
+
table_schema text;
|
|
209
|
+
table_name text;
|
|
210
|
+
has_rls boolean;
|
|
211
|
+
BEGIN
|
|
212
|
+
FOR obj IN SELECT * FROM pg_event_trigger_ddl_commands() WHERE command_tag = 'CREATE TABLE'
|
|
213
|
+
LOOP
|
|
214
|
+
-- Extract schema and table name from object_identity
|
|
215
|
+
-- Handle quoted identifiers by removing quotes
|
|
216
|
+
SELECT INTO table_schema, table_name
|
|
217
|
+
split_part(obj.object_identity, '.', 1),
|
|
218
|
+
trim(both '"' from split_part(obj.object_identity, '.', 2));
|
|
219
|
+
-- Check if RLS is enabled on the table
|
|
220
|
+
SELECT INTO has_rls
|
|
221
|
+
rowsecurity
|
|
222
|
+
FROM pg_tables
|
|
223
|
+
WHERE schemaname = table_schema
|
|
224
|
+
AND tablename = table_name;
|
|
225
|
+
-- Only create policies if RLS is enabled
|
|
226
|
+
IF has_rls THEN
|
|
227
|
+
-- Create policies for each role
|
|
228
|
+
-- anon: read-only access
|
|
229
|
+
EXECUTE format('CREATE POLICY "anon_policy" ON %s FOR SELECT TO anon USING (true)', obj.object_identity);
|
|
230
|
+
-- authenticated: full access
|
|
231
|
+
EXECUTE format('CREATE POLICY "authenticated_policy" ON %s FOR ALL TO authenticated USING (true) WITH CHECK (true)', obj.object_identity);
|
|
232
|
+
-- project_admin: full access
|
|
233
|
+
EXECUTE format('CREATE POLICY "project_admin_policy" ON %s FOR ALL TO project_admin USING (true) WITH CHECK (true)', obj.object_identity);
|
|
234
|
+
END IF;
|
|
235
|
+
END LOOP;
|
|
236
|
+
END;
|
|
237
|
+
$$ LANGUAGE plpgsql;
|
|
238
|
+
|
|
239
|
+
-- Create event trigger to run the function when new tables are created
|
|
240
|
+
CREATE EVENT TRIGGER create_policies_on_table_create
|
|
241
|
+
ON ddl_command_end
|
|
242
|
+
WHEN TAG IN ('CREATE TABLE')
|
|
243
|
+
EXECUTE FUNCTION public.create_default_policies();
|
|
244
|
+
|
|
245
|
+
-- Create function to handle RLS enablement
|
|
246
|
+
CREATE OR REPLACE FUNCTION public.create_policies_after_rls()
|
|
247
|
+
RETURNS event_trigger AS $$
|
|
248
|
+
DECLARE
|
|
249
|
+
obj record;
|
|
250
|
+
table_schema text;
|
|
251
|
+
table_name text;
|
|
252
|
+
BEGIN
|
|
253
|
+
FOR obj IN SELECT * FROM pg_event_trigger_ddl_commands() WHERE command_tag = 'ALTER TABLE'
|
|
254
|
+
LOOP
|
|
255
|
+
-- Extract schema and table name
|
|
256
|
+
-- Handle quoted identifiers by removing quotes
|
|
257
|
+
SELECT INTO table_schema, table_name
|
|
258
|
+
split_part(obj.object_identity, '.', 1),
|
|
259
|
+
trim(both '"' from split_part(obj.object_identity, '.', 2));
|
|
260
|
+
-- Check if table has RLS enabled and no policies yet
|
|
261
|
+
IF EXISTS (
|
|
262
|
+
SELECT 1 FROM pg_tables
|
|
263
|
+
WHERE schemaname = table_schema
|
|
264
|
+
AND tablename = table_name
|
|
265
|
+
AND rowsecurity = true
|
|
266
|
+
) AND NOT EXISTS (
|
|
267
|
+
SELECT 1 FROM pg_policies
|
|
268
|
+
WHERE schemaname = table_schema
|
|
269
|
+
AND tablename = table_name
|
|
270
|
+
) THEN
|
|
271
|
+
-- Create default policies
|
|
272
|
+
EXECUTE format('CREATE POLICY "anon_policy" ON %s FOR SELECT TO anon USING (true)', obj.object_identity);
|
|
273
|
+
EXECUTE format('CREATE POLICY "authenticated_policy" ON %s FOR ALL TO authenticated USING (true) WITH CHECK (true)', obj.object_identity);
|
|
274
|
+
EXECUTE format('CREATE POLICY "project_admin_policy" ON %s FOR ALL TO project_admin USING (true) WITH CHECK (true)', obj.object_identity);
|
|
275
|
+
END IF;
|
|
276
|
+
END LOOP;
|
|
277
|
+
END;
|
|
278
|
+
$$ LANGUAGE plpgsql;
|
|
279
|
+
|
|
280
|
+
-- Create event trigger for ALTER TABLE commands
|
|
281
|
+
CREATE EVENT TRIGGER create_policies_on_rls_enable
|
|
282
|
+
ON ddl_command_end
|
|
283
|
+
WHEN TAG IN ('ALTER TABLE')
|
|
284
|
+
EXECUTE FUNCTION public.create_policies_after_rls();
|
|
285
|
+
- path: /docker-entrypoint-initdb.d/02-jwt.sql
|
|
286
|
+
template: |
|
|
287
|
+
\set jwt_secret `echo "$JWT_SECRET"`
|
|
288
|
+
\set jwt_exp `echo "$JWT_EXP"`
|
|
289
|
+
|
|
290
|
+
ALTER DATABASE insforge SET "app.settings.jwt_secret" TO :'jwt_secret';
|
|
291
|
+
ALTER DATABASE insforge SET "app.settings.jwt_exp" TO :'jwt_exp';
|
|
292
|
+
healthCheck:
|
|
293
|
+
type: TCP
|
|
294
|
+
port: database
|
|
295
|
+
|
|
296
|
+
- name: postgrest
|
|
297
|
+
icon: https://avatars.githubusercontent.com/u/15115011?s=96&v=4
|
|
298
|
+
dependencies:
|
|
299
|
+
- postgres
|
|
300
|
+
template: PREBUILT
|
|
301
|
+
spec:
|
|
302
|
+
source:
|
|
303
|
+
image: postgrest/postgrest:v12.2.12
|
|
304
|
+
ports:
|
|
305
|
+
- id: api
|
|
306
|
+
port: 3000
|
|
307
|
+
type: HTTP
|
|
308
|
+
env:
|
|
309
|
+
PGHOST:
|
|
310
|
+
default: postgres
|
|
311
|
+
PGPORT:
|
|
312
|
+
default: "5432"
|
|
313
|
+
PGDATABASE:
|
|
314
|
+
default: insforge
|
|
315
|
+
PGUSER:
|
|
316
|
+
default: postgres
|
|
317
|
+
PGPASSWORD:
|
|
318
|
+
default: ${POSTGRES_PASSWORD}
|
|
319
|
+
expose: true
|
|
320
|
+
PGRST_OPENAPI_SERVER_PROXY_URI:
|
|
321
|
+
default: http://localhost:3000
|
|
322
|
+
PGRST_DB_SCHEMA:
|
|
323
|
+
default: public
|
|
324
|
+
PGRST_DB_ANON_ROLE:
|
|
325
|
+
default: anon
|
|
326
|
+
PGRST_JWT_SECRET:
|
|
327
|
+
default: ${JWT_SECRET}
|
|
328
|
+
expose: true
|
|
329
|
+
readonly: true
|
|
330
|
+
PGRST_DB_CHANNEL_ENABLED:
|
|
331
|
+
default: "true"
|
|
332
|
+
PGRST_DB_CHANNEL:
|
|
333
|
+
default: pgrst
|
|
334
|
+
healthCheck:
|
|
335
|
+
type: HTTP
|
|
336
|
+
port: api
|
|
337
|
+
http:
|
|
338
|
+
path: /
|
|
339
|
+
|
|
340
|
+
- name: deno
|
|
341
|
+
icon: https://avatars.githubusercontent.com/u/42048915?s=96&v=4
|
|
342
|
+
dependencies:
|
|
343
|
+
- postgres
|
|
344
|
+
- postgrest
|
|
345
|
+
template: PREBUILT
|
|
346
|
+
spec:
|
|
347
|
+
source:
|
|
348
|
+
image: denoland/deno:alpine-2.0.6
|
|
349
|
+
command:
|
|
350
|
+
- sh
|
|
351
|
+
- -c
|
|
352
|
+
- |
|
|
353
|
+
cd /app &&
|
|
354
|
+
echo 'Downloading Deno dependencies...' &&
|
|
355
|
+
deno cache functions/server.ts &&
|
|
356
|
+
echo 'Starting Deno server on port 7133...' &&
|
|
357
|
+
deno run --allow-net --allow-env --allow-read=./functions/worker-template.js --watch functions/server.ts
|
|
358
|
+
ports:
|
|
359
|
+
- id: runtime
|
|
360
|
+
port: 7133
|
|
361
|
+
type: HTTP
|
|
362
|
+
volumes:
|
|
363
|
+
- id: cache
|
|
364
|
+
dir: /deno-dir
|
|
365
|
+
env:
|
|
366
|
+
PORT:
|
|
367
|
+
default: "7133"
|
|
368
|
+
DENO_ENV:
|
|
369
|
+
default: development
|
|
370
|
+
DENO_DIR:
|
|
371
|
+
default: /deno-dir
|
|
372
|
+
POSTGRES_HOST:
|
|
373
|
+
default: postgres
|
|
374
|
+
POSTGRES_PORT:
|
|
375
|
+
default: "5432"
|
|
376
|
+
POSTGRES_DB:
|
|
377
|
+
default: insforge
|
|
378
|
+
POSTGRES_USER:
|
|
379
|
+
default: postgres
|
|
380
|
+
POSTGRES_PASSWORD:
|
|
381
|
+
default: ${PGPASSWORD}
|
|
382
|
+
POSTGREST_BASE_URL:
|
|
383
|
+
default: http://postgrest:3000
|
|
384
|
+
WORKER_TIMEOUT_MS:
|
|
385
|
+
default: "30000"
|
|
386
|
+
ENCRYPTION_KEY:
|
|
387
|
+
default: ${PASSWORD}
|
|
388
|
+
JWT_SECRET:
|
|
389
|
+
default: ${PGRST_JWT_SECRET}
|
|
390
|
+
configs:
|
|
391
|
+
- path: /app/functions/server.ts
|
|
392
|
+
template: |
|
|
393
|
+
import { Client } from 'https://deno.land/x/postgres@v0.17.0/mod.ts';
|
|
394
|
+
import { join, dirname, fromFileUrl } from 'https://deno.land/std@0.224.0/path/mod.ts';
|
|
395
|
+
|
|
396
|
+
/* eslint-disable no-console */
|
|
397
|
+
const port = parseInt(Deno.env.get('PORT') ?? '7133');
|
|
398
|
+
|
|
399
|
+
console.log(`Deno serverless runtime running on port ${port}`);
|
|
400
|
+
|
|
401
|
+
// Configuration
|
|
402
|
+
const WORKER_TIMEOUT_MS = parseInt(Deno.env.get('WORKER_TIMEOUT_MS') ?? '30000');
|
|
403
|
+
|
|
404
|
+
// Worker template code - loaded on first use
|
|
405
|
+
let workerTemplateCode: string | null = null;
|
|
406
|
+
|
|
407
|
+
async function getWorkerTemplateCode(): Promise<string> {
|
|
408
|
+
if (!workerTemplateCode) {
|
|
409
|
+
const currentDir = dirname(fromFileUrl(import.meta.url));
|
|
410
|
+
workerTemplateCode = await Deno.readTextFile(join(currentDir, 'worker-template.js'));
|
|
411
|
+
}
|
|
412
|
+
return workerTemplateCode;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Decrypt function for Deno (compatible with Node.js encryption)
|
|
416
|
+
async function decryptSecret(ciphertext: string, key: string): Promise<string> {
|
|
417
|
+
try {
|
|
418
|
+
const parts = ciphertext.split(':');
|
|
419
|
+
if (parts.length !== 3) {
|
|
420
|
+
throw new Error('Invalid ciphertext format');
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Get the encryption key by hashing the JWT secret
|
|
424
|
+
const keyData = new TextEncoder().encode(key);
|
|
425
|
+
const hashBuffer = await crypto.subtle.digest('SHA-256', keyData);
|
|
426
|
+
const cryptoKey = await crypto.subtle.importKey(
|
|
427
|
+
'raw',
|
|
428
|
+
hashBuffer,
|
|
429
|
+
{ name: 'AES-GCM' },
|
|
430
|
+
false,
|
|
431
|
+
['decrypt']
|
|
432
|
+
);
|
|
433
|
+
|
|
434
|
+
// Extract IV, auth tag, and encrypted data
|
|
435
|
+
const iv = Uint8Array.from(parts[0].match(/.{2}/g)!.map(byte => parseInt(byte, 16)));
|
|
436
|
+
const authTag = Uint8Array.from(parts[1].match(/.{2}/g)!.map(byte => parseInt(byte, 16)));
|
|
437
|
+
const encrypted = Uint8Array.from(parts[2].match(/.{2}/g)!.map(byte => parseInt(byte, 16)));
|
|
438
|
+
|
|
439
|
+
// Combine encrypted data and auth tag (GCM expects them together)
|
|
440
|
+
const cipherData = new Uint8Array(encrypted.length + authTag.length);
|
|
441
|
+
cipherData.set(encrypted);
|
|
442
|
+
cipherData.set(authTag, encrypted.length);
|
|
443
|
+
|
|
444
|
+
// Decrypt
|
|
445
|
+
const decryptedBuffer = await crypto.subtle.decrypt(
|
|
446
|
+
{ name: 'AES-GCM', iv },
|
|
447
|
+
cryptoKey,
|
|
448
|
+
cipherData
|
|
449
|
+
);
|
|
450
|
+
|
|
451
|
+
return new TextDecoder().decode(decryptedBuffer);
|
|
452
|
+
} catch (error) {
|
|
453
|
+
console.error('Failed to decrypt secret:', error);
|
|
454
|
+
throw error;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Database connection
|
|
459
|
+
const dbConfig = {
|
|
460
|
+
user: Deno.env.get('POSTGRES_USER') || 'postgres',
|
|
461
|
+
password: Deno.env.get('POSTGRES_PASSWORD') || 'postgres',
|
|
462
|
+
database: Deno.env.get('POSTGRES_DB') || 'insforge',
|
|
463
|
+
hostname: Deno.env.get('POSTGRES_HOST') || 'postgres',
|
|
464
|
+
port: parseInt(Deno.env.get('POSTGRES_PORT') || '5432', 10),
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
// Get function code from database
|
|
468
|
+
async function getFunctionCode(slug: string): Promise<string | null> {
|
|
469
|
+
const client = new Client(dbConfig);
|
|
470
|
+
|
|
471
|
+
try {
|
|
472
|
+
await client.connect();
|
|
473
|
+
|
|
474
|
+
const result = await client.queryObject<{ code: string }>`
|
|
475
|
+
SELECT code FROM _functions
|
|
476
|
+
WHERE slug = ${slug} AND status = 'active'
|
|
477
|
+
`;
|
|
478
|
+
|
|
479
|
+
if (!result.rows.length) {
|
|
480
|
+
return null;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
return result.rows[0].code;
|
|
484
|
+
} catch (error) {
|
|
485
|
+
console.error(`Error fetching function ${slug}:`, error);
|
|
486
|
+
return null;
|
|
487
|
+
} finally {
|
|
488
|
+
await client.end();
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// Get all secrets from main secrets table and decrypt them
|
|
493
|
+
async function getFunctionSecrets(): Promise<Record<string, string>> {
|
|
494
|
+
const client = new Client(dbConfig);
|
|
495
|
+
|
|
496
|
+
try {
|
|
497
|
+
await client.connect();
|
|
498
|
+
|
|
499
|
+
// Get the encryption key from environment
|
|
500
|
+
const encryptionKey = Deno.env.get('ENCRYPTION_KEY') || Deno.env.get('JWT_SECRET');
|
|
501
|
+
if (!encryptionKey) {
|
|
502
|
+
console.error('No encryption key available for decrypting secrets');
|
|
503
|
+
return {};
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// Fetch all active secrets from _secrets table
|
|
507
|
+
const result = await client.queryObject<{
|
|
508
|
+
key: string;
|
|
509
|
+
value_ciphertext: string;
|
|
510
|
+
}>`
|
|
511
|
+
SELECT key, value_ciphertext
|
|
512
|
+
FROM _secrets
|
|
513
|
+
WHERE is_active = true
|
|
514
|
+
AND (expires_at IS NULL OR expires_at > NOW())
|
|
515
|
+
`;
|
|
516
|
+
|
|
517
|
+
const secrets: Record<string, string> = {};
|
|
518
|
+
|
|
519
|
+
// Decrypt each secret
|
|
520
|
+
for (const row of result.rows) {
|
|
521
|
+
try {
|
|
522
|
+
secrets[row.key] = await decryptSecret(row.value_ciphertext, encryptionKey);
|
|
523
|
+
} catch (error) {
|
|
524
|
+
console.error(`Failed to decrypt secret ${row.key}:`, error);
|
|
525
|
+
// Skip this secret if decryption fails
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
return secrets;
|
|
530
|
+
} catch (error) {
|
|
531
|
+
console.error('Error fetching secrets:', error);
|
|
532
|
+
return {};
|
|
533
|
+
} finally {
|
|
534
|
+
await client.end();
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// Execute function in isolated worker
|
|
539
|
+
async function executeInWorker(code: string, request: Request): Promise<Response> {
|
|
540
|
+
// Get worker template
|
|
541
|
+
const template = await getWorkerTemplateCode();
|
|
542
|
+
|
|
543
|
+
// Fetch all function secrets
|
|
544
|
+
const secrets = await getFunctionSecrets();
|
|
545
|
+
|
|
546
|
+
// Create blob for worker
|
|
547
|
+
const workerBlob = new Blob([template], { type: 'application/javascript' });
|
|
548
|
+
const workerUrl = URL.createObjectURL(workerBlob);
|
|
549
|
+
|
|
550
|
+
return new Promise(async (resolve) => {
|
|
551
|
+
const worker = new Worker(workerUrl, { type: 'module' });
|
|
552
|
+
|
|
553
|
+
// Set timeout for worker execution
|
|
554
|
+
const timeout = setTimeout(() => {
|
|
555
|
+
worker.terminate();
|
|
556
|
+
URL.revokeObjectURL(workerUrl);
|
|
557
|
+
resolve(
|
|
558
|
+
new Response(JSON.stringify({ error: 'Function timeout' }), {
|
|
559
|
+
status: 504,
|
|
560
|
+
headers: { 'Content-Type': 'application/json' },
|
|
561
|
+
})
|
|
562
|
+
);
|
|
563
|
+
}, WORKER_TIMEOUT_MS);
|
|
564
|
+
|
|
565
|
+
// Handle worker response
|
|
566
|
+
worker.onmessage = (e) => {
|
|
567
|
+
clearTimeout(timeout);
|
|
568
|
+
worker.terminate();
|
|
569
|
+
URL.revokeObjectURL(workerUrl);
|
|
570
|
+
|
|
571
|
+
if (e.data.success) {
|
|
572
|
+
const { response } = e.data;
|
|
573
|
+
// The worker now properly sends null for bodyless responses
|
|
574
|
+
resolve(
|
|
575
|
+
new Response(response.body, {
|
|
576
|
+
status: response.status,
|
|
577
|
+
statusText: response.statusText,
|
|
578
|
+
headers: response.headers,
|
|
579
|
+
})
|
|
580
|
+
);
|
|
581
|
+
} else {
|
|
582
|
+
resolve(
|
|
583
|
+
new Response(JSON.stringify({ error: e.data.error }), {
|
|
584
|
+
status: e.data.status || 500,
|
|
585
|
+
headers: { 'Content-Type': 'application/json' },
|
|
586
|
+
})
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
|
|
591
|
+
// Handle worker errors
|
|
592
|
+
worker.onerror = (error) => {
|
|
593
|
+
clearTimeout(timeout);
|
|
594
|
+
worker.terminate();
|
|
595
|
+
URL.revokeObjectURL(workerUrl);
|
|
596
|
+
console.error('Worker error:', error);
|
|
597
|
+
resolve(
|
|
598
|
+
new Response(JSON.stringify({ error: 'Worker execution error' }), {
|
|
599
|
+
status: 500,
|
|
600
|
+
headers: { 'Content-Type': 'application/json' },
|
|
601
|
+
})
|
|
602
|
+
);
|
|
603
|
+
};
|
|
604
|
+
|
|
605
|
+
// Prepare request data
|
|
606
|
+
const body = request.body ? await request.text() : null;
|
|
607
|
+
const requestData = {
|
|
608
|
+
url: request.url,
|
|
609
|
+
method: request.method,
|
|
610
|
+
headers: Object.fromEntries(request.headers),
|
|
611
|
+
body,
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
// Send message with code, request data, and secrets
|
|
615
|
+
worker.postMessage({ code, requestData, secrets });
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
Deno.serve({ port }, async (req: Request) => {
|
|
620
|
+
const url = new URL(req.url);
|
|
621
|
+
const pathname = url.pathname;
|
|
622
|
+
|
|
623
|
+
// Health check
|
|
624
|
+
if (pathname === '/health') {
|
|
625
|
+
return new Response(
|
|
626
|
+
JSON.stringify({
|
|
627
|
+
status: 'ok',
|
|
628
|
+
runtime: 'deno',
|
|
629
|
+
version: Deno.version.deno,
|
|
630
|
+
typescript: Deno.version.typescript,
|
|
631
|
+
v8: Deno.version.v8,
|
|
632
|
+
}),
|
|
633
|
+
{
|
|
634
|
+
headers: { 'Content-Type': 'application/json' },
|
|
635
|
+
}
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// Function execution - match ONLY exact slug, no subpaths
|
|
640
|
+
const slugMatch = pathname.match(/^\/([a-zA-Z0-9_-]+)$/);
|
|
641
|
+
if (slugMatch) {
|
|
642
|
+
const slug = slugMatch[1];
|
|
643
|
+
const startTime = Date.now();
|
|
644
|
+
|
|
645
|
+
// Get function code from database
|
|
646
|
+
const code = await getFunctionCode(slug);
|
|
647
|
+
|
|
648
|
+
if (!code) {
|
|
649
|
+
return new Response(JSON.stringify({ error: 'Function not found or not active' }), {
|
|
650
|
+
status: 404,
|
|
651
|
+
headers: { 'Content-Type': 'application/json' },
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// Execute in worker with original request
|
|
656
|
+
try {
|
|
657
|
+
const response = await executeInWorker(code, req);
|
|
658
|
+
const duration = Date.now() - startTime;
|
|
659
|
+
|
|
660
|
+
// Log completed invocations only
|
|
661
|
+
console.log(
|
|
662
|
+
JSON.stringify({
|
|
663
|
+
timestamp: new Date().toISOString(),
|
|
664
|
+
level: 'info',
|
|
665
|
+
slug,
|
|
666
|
+
method: req.method,
|
|
667
|
+
status: response.status,
|
|
668
|
+
duration: `${duration}ms`,
|
|
669
|
+
})
|
|
670
|
+
);
|
|
671
|
+
|
|
672
|
+
return response;
|
|
673
|
+
} catch (error) {
|
|
674
|
+
const duration = Date.now() - startTime;
|
|
675
|
+
console.error(
|
|
676
|
+
JSON.stringify({
|
|
677
|
+
timestamp: new Date().toISOString(),
|
|
678
|
+
level: 'error',
|
|
679
|
+
slug,
|
|
680
|
+
error: error instanceof Error ? error.message : String(error),
|
|
681
|
+
duration: `${duration}ms`,
|
|
682
|
+
})
|
|
683
|
+
);
|
|
684
|
+
return new Response(JSON.stringify({ error: 'Function execution failed' }), {
|
|
685
|
+
status: 500,
|
|
686
|
+
headers: { 'Content-Type': 'application/json' },
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// Runtime info
|
|
692
|
+
if (pathname === '/info') {
|
|
693
|
+
return new Response(
|
|
694
|
+
JSON.stringify({
|
|
695
|
+
runtime: 'deno',
|
|
696
|
+
version: Deno.version,
|
|
697
|
+
env: Deno.env.get('DENO_ENV') || 'production',
|
|
698
|
+
database: {
|
|
699
|
+
host: dbConfig.hostname,
|
|
700
|
+
database: dbConfig.database,
|
|
701
|
+
},
|
|
702
|
+
}),
|
|
703
|
+
{
|
|
704
|
+
headers: { 'Content-Type': 'application/json' },
|
|
705
|
+
}
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// 404
|
|
710
|
+
return new Response('Not Found', { status: 404 });
|
|
711
|
+
});
|
|
712
|
+
- path: /app/functions/deno.json
|
|
713
|
+
template: |
|
|
714
|
+
{
|
|
715
|
+
"compilerOptions": {
|
|
716
|
+
"lib": ["deno.window", "deno.worker"],
|
|
717
|
+
"strict": true
|
|
718
|
+
},
|
|
719
|
+
"lint": {
|
|
720
|
+
"files": {
|
|
721
|
+
"include": ["./**/*.ts", "./**/*.js"]
|
|
722
|
+
},
|
|
723
|
+
"rules": {
|
|
724
|
+
"tags": ["recommended"]
|
|
725
|
+
}
|
|
726
|
+
},
|
|
727
|
+
"fmt": {
|
|
728
|
+
"files": {
|
|
729
|
+
"include": ["./**/*.ts", "./**/*.js"]
|
|
730
|
+
},
|
|
731
|
+
"options": {
|
|
732
|
+
"useTabs": false,
|
|
733
|
+
"lineWidth": 100,
|
|
734
|
+
"indentWidth": 2,
|
|
735
|
+
"singleQuote": true
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
- path: /app/functions/worker-template.js
|
|
740
|
+
template: |
|
|
741
|
+
/**
|
|
742
|
+
* Worker Template for Serverless Functions
|
|
743
|
+
*
|
|
744
|
+
* This code runs inside a Web Worker environment created by Deno.
|
|
745
|
+
* Each worker is created fresh for a single request, executes once, and terminates.
|
|
746
|
+
*/
|
|
747
|
+
/* eslint-env worker */
|
|
748
|
+
/* global self, Request, Deno */
|
|
749
|
+
|
|
750
|
+
// Import SDK at worker level - this will be available to all functions
|
|
751
|
+
import { createClient } from 'npm:@insforge/sdk';
|
|
752
|
+
// Import base64 utilities for encoding/decoding
|
|
753
|
+
import { encodeBase64, decodeBase64 } from 'https://deno.land/std@0.224.0/encoding/base64.ts';
|
|
754
|
+
|
|
755
|
+
// Handle the single message with code, request data, and secrets
|
|
756
|
+
self.onmessage = async (e) => {
|
|
757
|
+
const { code, requestData, secrets = {} } = e.data;
|
|
758
|
+
|
|
759
|
+
try {
|
|
760
|
+
/**
|
|
761
|
+
* MOCK DENO OBJECT EXPLANATION:
|
|
762
|
+
*
|
|
763
|
+
* Why we need a mock Deno object:
|
|
764
|
+
* - Edge functions run in isolated Web Workers (sandboxed environments)
|
|
765
|
+
* - Web Workers don't have access to the real Deno global object for security
|
|
766
|
+
* - We need to provide Deno.env functionality so functions can access secrets
|
|
767
|
+
*
|
|
768
|
+
* How it works:
|
|
769
|
+
* 1. The main server (server.ts) fetches all active secrets from the _secrets table
|
|
770
|
+
* 2. Only active (is_active=true) and non-expired secrets are included
|
|
771
|
+
* 3. Secrets are decrypted and passed to this worker via the 'secrets' object
|
|
772
|
+
* 4. We create a mock Deno object that provides Deno.env.get()
|
|
773
|
+
* 5. When user code calls Deno.env.get('MY_SECRET'), it reads from our secrets object
|
|
774
|
+
*
|
|
775
|
+
* This allows edge functions to use familiar Deno.env syntax while maintaining security
|
|
776
|
+
* Secrets are managed via the /api/secrets endpoint
|
|
777
|
+
*/
|
|
778
|
+
const mockDeno = {
|
|
779
|
+
// Mock the Deno.env API - only get() is needed for reading secrets
|
|
780
|
+
env: {
|
|
781
|
+
get: (key) => secrets[key] || undefined,
|
|
782
|
+
},
|
|
783
|
+
};
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* FUNCTION WRAPPING EXPLANATION:
|
|
787
|
+
*
|
|
788
|
+
* Here we create a wrapper function that will execute the user's code.
|
|
789
|
+
* The user's function expects to have access to:
|
|
790
|
+
* - module.exports (to export their function)
|
|
791
|
+
* - createClient (the Insforge SDK)
|
|
792
|
+
* - Deno (for Deno.env.get() etc.)
|
|
793
|
+
* - encodeBase64, decodeBase64 (base64 encoding utilities)
|
|
794
|
+
*
|
|
795
|
+
* We inject our mockDeno as the 'Deno' parameter, so when the user's code
|
|
796
|
+
* calls Deno.env.get('MY_SECRET'), it's actually calling mockDeno.env.get('MY_SECRET')
|
|
797
|
+
*/
|
|
798
|
+
const wrapper = new Function(
|
|
799
|
+
'exports',
|
|
800
|
+
'module',
|
|
801
|
+
'createClient',
|
|
802
|
+
'Deno',
|
|
803
|
+
'encodeBase64',
|
|
804
|
+
'decodeBase64',
|
|
805
|
+
code
|
|
806
|
+
);
|
|
807
|
+
const exports = {};
|
|
808
|
+
const module = { exports };
|
|
809
|
+
|
|
810
|
+
// Execute the wrapper, passing mockDeno as the Deno global and utility functions
|
|
811
|
+
// This makes Deno.env.get(), encodeBase64(), and decodeBase64() available inside the user's function
|
|
812
|
+
wrapper(exports, module, createClient, mockDeno, encodeBase64, decodeBase64);
|
|
813
|
+
|
|
814
|
+
// Get the exported function
|
|
815
|
+
const functionHandler = module.exports || exports.default || exports;
|
|
816
|
+
|
|
817
|
+
if (typeof functionHandler !== 'function') {
|
|
818
|
+
throw new Error(
|
|
819
|
+
'No function exported. Expected: module.exports = async function(req) { ... }'
|
|
820
|
+
);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// Create Request object from data
|
|
824
|
+
const request = new Request(requestData.url, {
|
|
825
|
+
method: requestData.method,
|
|
826
|
+
headers: requestData.headers,
|
|
827
|
+
body: requestData.body,
|
|
828
|
+
});
|
|
829
|
+
|
|
830
|
+
// Execute the function
|
|
831
|
+
const response = await functionHandler(request);
|
|
832
|
+
|
|
833
|
+
// Serialize and send response
|
|
834
|
+
// Properly handle responses with no body
|
|
835
|
+
let body = null;
|
|
836
|
+
|
|
837
|
+
// Only read body if response has content
|
|
838
|
+
// Status codes 204, 205, and 304 should not have a body
|
|
839
|
+
if (![204, 205, 304].includes(response.status)) {
|
|
840
|
+
body = await response.text();
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
const responseData = {
|
|
844
|
+
status: response.status,
|
|
845
|
+
statusText: response.statusText,
|
|
846
|
+
headers: Object.fromEntries(response.headers),
|
|
847
|
+
body: body,
|
|
848
|
+
};
|
|
849
|
+
|
|
850
|
+
self.postMessage({ success: true, response: responseData });
|
|
851
|
+
} catch (error) {
|
|
852
|
+
// Check if the error is actually a Response object (thrown by the function)
|
|
853
|
+
if (error instanceof Response) {
|
|
854
|
+
// Handle error responses the same way
|
|
855
|
+
let body = null;
|
|
856
|
+
|
|
857
|
+
if (![204, 205, 304].includes(error.status)) {
|
|
858
|
+
body = await error.text();
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
const responseData = {
|
|
862
|
+
status: error.status,
|
|
863
|
+
statusText: error.statusText,
|
|
864
|
+
headers: Object.fromEntries(error.headers),
|
|
865
|
+
body: body,
|
|
866
|
+
};
|
|
867
|
+
self.postMessage({ success: true, response: responseData });
|
|
868
|
+
} else {
|
|
869
|
+
// For actual errors, include status if available
|
|
870
|
+
self.postMessage({
|
|
871
|
+
success: false,
|
|
872
|
+
error: error.message || 'Unknown error',
|
|
873
|
+
status: error.status || 500,
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
};
|
|
878
|
+
healthCheck:
|
|
879
|
+
type: HTTP
|
|
880
|
+
port: runtime
|
|
881
|
+
http:
|
|
882
|
+
path: /health
|
|
883
|
+
|
|
884
|
+
- name: insforge
|
|
885
|
+
icon: https://avatars.githubusercontent.com/u/198419463?s=96&v=4
|
|
886
|
+
dependencies:
|
|
887
|
+
- postgres
|
|
888
|
+
- postgrest
|
|
889
|
+
- deno
|
|
890
|
+
template: PREBUILT
|
|
891
|
+
spec:
|
|
892
|
+
source:
|
|
893
|
+
image: ghcr.io/insforge/insforge-oss:v1.2.6
|
|
894
|
+
ports:
|
|
895
|
+
- id: web
|
|
896
|
+
port: 7130
|
|
897
|
+
type: HTTP
|
|
898
|
+
- id: dev
|
|
899
|
+
port: 7131
|
|
900
|
+
type: HTTP
|
|
901
|
+
env:
|
|
902
|
+
PORT:
|
|
903
|
+
default: "7130"
|
|
904
|
+
PROJECT_ROOT:
|
|
905
|
+
default: /app
|
|
906
|
+
API_BASE_URL:
|
|
907
|
+
default: ${ZEABUR_WEB_URL}
|
|
908
|
+
VITE_API_BASE_URL:
|
|
909
|
+
default: ${ZEABUR_WEB_URL}
|
|
910
|
+
JWT_SECRET:
|
|
911
|
+
default: ${PGRST_JWT_SECRET}
|
|
912
|
+
ADMIN_EMAIL:
|
|
913
|
+
default: ${ADMIN_EMAIL}
|
|
914
|
+
ADMIN_PASSWORD:
|
|
915
|
+
default: ${ADMIN_PASSWORD}
|
|
916
|
+
# PostgreSQL connection
|
|
917
|
+
POSTGRES_HOST:
|
|
918
|
+
default: postgres
|
|
919
|
+
POSTGRES_PORT:
|
|
920
|
+
default: "5432"
|
|
921
|
+
POSTGRES_DB:
|
|
922
|
+
default: insforge
|
|
923
|
+
POSTGRES_USER:
|
|
924
|
+
default: postgres
|
|
925
|
+
POSTGRESDB_PASSWORD:
|
|
926
|
+
default: ${PGPASSWORD}
|
|
927
|
+
DATABASE_URL:
|
|
928
|
+
default: postgres://postgres:${PGPASSWORD}@postgres:5432/insforge
|
|
929
|
+
POSTGREST_BASE_URL:
|
|
930
|
+
default: http://postgrest:3000
|
|
931
|
+
# Deno Runtime URL for serverless functions
|
|
932
|
+
DENO_RUNTIME_URL:
|
|
933
|
+
default: http://deno:7133
|
|
934
|
+
# OAuth Configuration (optional)
|
|
935
|
+
GOOGLE_CLIENT_ID:
|
|
936
|
+
default: ""
|
|
937
|
+
GOOGLE_CLIENT_SECRET:
|
|
938
|
+
default: ""
|
|
939
|
+
GOOGLE_REDIRECT_URI:
|
|
940
|
+
default: ${ZEABUR_WEB_URL}/api/auth/v1/callback
|
|
941
|
+
GITHUB_CLIENT_ID:
|
|
942
|
+
default: ""
|
|
943
|
+
GITHUB_CLIENT_SECRET:
|
|
944
|
+
default: ""
|
|
945
|
+
GITHUB_REDIRECT_URI:
|
|
946
|
+
default: ${ZEABUR_WEB_URL}/api/auth/v1/callback
|
|
947
|
+
# Multi-tenant Cloud Configuration
|
|
948
|
+
DEPLOYMENT_ID:
|
|
949
|
+
default: ${ZEABUR_SERVICE_ID}
|
|
950
|
+
PROJECT_ID:
|
|
951
|
+
default: ${ZEABUR_PROJECT_ID}
|
|
952
|
+
APP_KEY:
|
|
953
|
+
default: ""
|
|
954
|
+
ACCESS_API_KEY:
|
|
955
|
+
default: ""
|
|
956
|
+
OPENROUTER_API_KEY:
|
|
957
|
+
default: ${OPENROUTER_API_KEY}
|
|
958
|
+
healthCheck:
|
|
959
|
+
type: HTTP
|
|
960
|
+
port: web
|
|
961
|
+
http:
|
|
962
|
+
path: /
|
|
963
|
+
domainKey: PUBLIC_DOMAIN
|
|
964
|
+
|
|
965
|
+
localization:
|
|
966
|
+
zh-CN:
|
|
967
|
+
description: InsForge 是 Agent-Native 的 Supabase 替代方案,让 AI 智能体能够自主构建和管理全栈应用程序。
|
|
968
|
+
readme: |
|
|
969
|
+
# InsForge
|
|
970
|
+
|
|
971
|
+
InsForge 是 Agent-Native 的 Supabase 替代方案,让 AI 智能体能够自主构建和管理全栈应用程序。
|
|
972
|
+
|
|
973
|
+
## 功能特性
|
|
974
|
+
|
|
975
|
+
- **身份验证系统** - 用户管理和安全认证
|
|
976
|
+
- **数据库存储** - 灵活的 PostgreSQL 数据库,支持自动模式管理
|
|
977
|
+
- **文件管理** - 安全的文件上传和存储功能
|
|
978
|
+
- **PostgREST API** - 从数据库模式自动生成 REST API
|
|
979
|
+
- **无服务器函数** - 用于自定义业务逻辑的边缘函数
|
|
980
|
+
- **AI 智能体集成** - 连接 Claude 或 GPT 等 AI 智能体来管理后端
|
|
981
|
+
|
|
982
|
+
## 快速开始
|
|
983
|
+
|
|
984
|
+
此模板提供 InsForge 平台的**一键部署**:
|
|
985
|
+
|
|
986
|
+
1. **部署** - 点击部署并绑定域名
|
|
987
|
+
2. **登录** - 使用管理员凭据访问控制台
|
|
988
|
+
3. **连接 AI 智能体** - 通过控制台连接您的 AI 智能体(Claude、GPT 等)
|
|
989
|
+
4. **构建应用** - 使用自然语言提示创建应用程序:
|
|
990
|
+
- "构建一个带用户认证的待办事项应用"
|
|
991
|
+
- "创建一个带图片上传的 Instagram 克隆"
|
|
992
|
+
- "构建一个带评论和点赞的博客"
|
|
993
|
+
|
|
994
|
+
## 使用场景
|
|
995
|
+
|
|
996
|
+
- **AI 生成的前端** - 为 AI 生成的前端项目快速创建后端
|
|
997
|
+
- **智能体驱动开发** - 让 AI 智能体管理您的整个后端基础设施
|
|
998
|
+
- **快速原型设计** - 使用自然语言构建全栈应用程序
|
|
999
|
+
|
|
1000
|
+
zh-TW:
|
|
1001
|
+
description: InsForge 是 Agent-Native 的 Supabase 替代方案,讓 AI 智慧體能夠自主建構和管理全端應用程式。
|
|
1002
|
+
readme: |
|
|
1003
|
+
# InsForge
|
|
1004
|
+
|
|
1005
|
+
InsForge 是 Agent-Native 的 Supabase 替代方案,讓 AI 智慧體能夠自主建構和管理全端應用程式。
|
|
1006
|
+
|
|
1007
|
+
## 功能特色
|
|
1008
|
+
|
|
1009
|
+
- **身份驗證系統** - 使用者管理和安全認證
|
|
1010
|
+
- **資料庫儲存** - 靈活的 PostgreSQL 資料庫,支援自動模式管理
|
|
1011
|
+
- **檔案管理** - 安全的檔案上傳和儲存功能
|
|
1012
|
+
- **PostgREST API** - 從資料庫模式自動產生 REST API
|
|
1013
|
+
- **無伺服器函數** - 用於自訂業務邏輯的邊緣函數
|
|
1014
|
+
- **AI 智慧體整合** - 連接 Claude 或 GPT 等 AI 智慧體來管理後端
|
|
1015
|
+
|
|
1016
|
+
## 快速開始
|
|
1017
|
+
|
|
1018
|
+
此模版提供 InsForge 平台的**一鍵部署**:
|
|
1019
|
+
|
|
1020
|
+
1. **部署** - 點擊部署並綁定網域
|
|
1021
|
+
2. **登入** - 使用管理員憑證存取控制台
|
|
1022
|
+
3. **連接 AI 智慧體** - 透過控制台連接您的 AI 智慧體(Claude、GPT 等)
|
|
1023
|
+
4. **建構應用** - 使用自然語言提示建立應用程式:
|
|
1024
|
+
- "建構一個帶使用者認證的待辦事項應用"
|
|
1025
|
+
- "建立一個帶圖片上傳的 Instagram 複製品"
|
|
1026
|
+
- "建構一個帶評論和按讚的部落格"
|
|
1027
|
+
|
|
1028
|
+
## 使用情境
|
|
1029
|
+
|
|
1030
|
+
- **AI 產生的前端** - 為 AI 產生的前端專案快速建立後端
|
|
1031
|
+
- **智慧體驅動開發** - 讓 AI 智慧體管理您的整個後端基礎設施
|
|
1032
|
+
- **快速原型設計** - 使用自然語言建構全端應用程式
|