insforge 0.3.1
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/.dockerignore +58 -0
- package/.env.example +49 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +83 -0
- package/.github/ISSUE_TEMPLATE/config.yml +11 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +79 -0
- package/.github/copilot-instructions.md +147 -0
- package/.github/workflows/build-image.yml +65 -0
- package/.github/workflows/ci-premerge-check.yml +24 -0
- package/.github/workflows/deploy-aws.yml +130 -0
- package/.github/workflows/lint-and-format.yml +33 -0
- package/.prettierignore +65 -0
- package/.prettierrc +9 -0
- package/CHANGELOG.md +3 -0
- package/CONTRIBUTING.md +126 -0
- package/Dockerfile +27 -0
- package/GITHUB_OAUTH_SETUP.md +49 -0
- package/GOOGLE_OAUTH_SETUP.md +148 -0
- package/LICENSE +201 -0
- package/README.md +134 -0
- package/assets/Dark.svg +23 -0
- package/assets/archDiagram.png +0 -0
- package/assets/banner.png +0 -0
- package/assets/mcpInstallv2.png +0 -0
- package/assets/sampleResponse.png +0 -0
- package/assets/signin.png +0 -0
- package/assets/userflow.png +0 -0
- package/backend/migrations/000_create-base-tables.sql +142 -0
- package/backend/migrations/001_create-helper-functions.sql +41 -0
- package/backend/migrations/002_rename-auth-tables.sql +30 -0
- package/backend/migrations/003_create-users-table.sql +56 -0
- package/backend/migrations/004_add-reload-postgrest-func.sql +24 -0
- package/backend/migrations/005_enable-project-admin-modify-users.sql +30 -0
- package/backend/migrations/006_modify-ai-usage-table.sql +25 -0
- package/backend/migrations/007_drop-metadata-table.sql +2 -0
- package/backend/migrations/008_add-system-tables.sql +77 -0
- package/backend/migrations/009_add-function-secrets.sql +24 -0
- package/backend/migrations/010_modify-ai-config-modalities.sql +93 -0
- package/backend/migrations/011_refactor-secrets-table.sql +15 -0
- package/backend/migrations/012_add-storage-uploaded-by.sql +8 -0
- package/backend/package.json +75 -0
- package/backend/src/api/middleware/auth.ts +240 -0
- package/backend/src/api/middleware/error.ts +231 -0
- package/backend/src/api/middleware/upload.ts +59 -0
- package/backend/src/api/routes/agent.ts +29 -0
- package/backend/src/api/routes/ai.ts +472 -0
- package/backend/src/api/routes/auth.oauth.ts +482 -0
- package/backend/src/api/routes/auth.ts +386 -0
- package/backend/src/api/routes/database.advance.ts +275 -0
- package/backend/src/api/routes/database.records.ts +246 -0
- package/backend/src/api/routes/database.tables.ts +161 -0
- package/backend/src/api/routes/docs.ts +66 -0
- package/backend/src/api/routes/functions.ts +183 -0
- package/backend/src/api/routes/logs.ts +150 -0
- package/backend/src/api/routes/metadata.ts +160 -0
- package/backend/src/api/routes/openapi.ts +82 -0
- package/backend/src/api/routes/secrets.ts +199 -0
- package/backend/src/api/routes/storage.ts +547 -0
- package/backend/src/api/routes/usage.ts +96 -0
- package/backend/src/core/ai/chat.ts +207 -0
- package/backend/src/core/ai/client.ts +242 -0
- package/backend/src/core/ai/config.ts +187 -0
- package/backend/src/core/ai/image.ts +156 -0
- package/backend/src/core/ai/model.ts +117 -0
- package/backend/src/core/ai/usage.ts +290 -0
- package/backend/src/core/auth/auth.ts +781 -0
- package/backend/src/core/auth/oauth.ts +398 -0
- package/backend/src/core/database/advance.ts +1074 -0
- package/backend/src/core/database/manager.ts +178 -0
- package/backend/src/core/database/table.ts +772 -0
- package/backend/src/core/documentation/agent.ts +689 -0
- package/backend/src/core/documentation/openapi.ts +856 -0
- package/backend/src/core/functions/functions.ts +310 -0
- package/backend/src/core/logs/analytics.ts +76 -0
- package/backend/src/core/logs/audit.ts +255 -0
- package/backend/src/core/logs/providers/base.provider.ts +83 -0
- package/backend/src/core/logs/providers/cloudwatch.provider.ts +510 -0
- package/backend/src/core/logs/providers/localdb.provider.ts +246 -0
- package/backend/src/core/secrets/encryption.ts +58 -0
- package/backend/src/core/secrets/secrets.ts +410 -0
- package/backend/src/core/socket/socket.ts +388 -0
- package/backend/src/core/socket/types.ts +79 -0
- package/backend/src/core/storage/storage.ts +923 -0
- package/backend/src/server.ts +288 -0
- package/backend/src/types/ai.ts +46 -0
- package/backend/src/types/auth.ts +90 -0
- package/backend/src/types/database.ts +136 -0
- package/backend/src/types/error-constants.ts +86 -0
- package/backend/src/types/logs.ts +47 -0
- package/backend/src/types/profile.ts +55 -0
- package/backend/src/types/storage.ts +23 -0
- package/backend/src/utils/cloud-token.ts +39 -0
- package/backend/src/utils/constants.ts +1 -0
- package/backend/src/utils/environment.ts +35 -0
- package/backend/src/utils/helpers.ts +49 -0
- package/backend/src/utils/logger.ts +13 -0
- package/backend/src/utils/response.ts +62 -0
- package/backend/src/utils/seed.ts +205 -0
- package/backend/src/utils/sql-parser.ts +63 -0
- package/backend/src/utils/uuid.ts +9 -0
- package/backend/src/utils/validations.ts +129 -0
- package/backend/tests/README.md +134 -0
- package/backend/tests/cleanup-all-test-data.sh +231 -0
- package/backend/tests/cloud/test-s3-multitenant.sh +132 -0
- package/backend/tests/local/comprehensive-curl-tests.sh +156 -0
- package/backend/tests/local/test-auth-router.sh +144 -0
- package/backend/tests/local/test-database-router.sh +222 -0
- package/backend/tests/local/test-e2e.sh +241 -0
- package/backend/tests/local/test-fk-errors.sh +97 -0
- package/backend/tests/local/test-id-field.sh +201 -0
- package/backend/tests/local/test-public-bucket.sh +265 -0
- package/backend/tests/local/test-secrets.sh +248 -0
- package/backend/tests/local/test-serverless-functions.sh.disabled +325 -0
- package/backend/tests/local/test-traditional-rest.sh +209 -0
- package/backend/tests/manual/README.md +51 -0
- package/backend/tests/manual/create-large-table-simple.sql +11 -0
- package/backend/tests/manual/seed-large-table.sql +101 -0
- package/backend/tests/manual/setup-large-table-extras.sql +34 -0
- package/backend/tests/manual/test-better-auth.sh +303 -0
- package/backend/tests/manual/test-bulk-upsert.sh +410 -0
- package/backend/tests/manual/test-database-advance.sh +297 -0
- package/backend/tests/manual/test-postgrest-stability.sh +192 -0
- package/backend/tests/manual/test-rawsql-export-import.sh +412 -0
- package/backend/tests/manual/test-universal-storage.sh +264 -0
- package/backend/tests/manual/test-users.sql +18 -0
- package/backend/tests/run-all-tests.sh +140 -0
- package/backend/tests/setup.ts +22 -0
- package/backend/tests/test-config.sh +303 -0
- package/backend/tsconfig.json +23 -0
- package/backend/tsup.config.ts +18 -0
- package/backend/vitest.config.ts +22 -0
- package/docker-compose.prod.yml +145 -0
- package/docker-compose.yml +167 -0
- package/docker-init/db/db-init.sql +125 -0
- package/docker-init/db/jwt.sql +5 -0
- package/docker-init/db/logs.sql +9 -0
- package/docker-init/db/postgresql.conf +17 -0
- package/docs/deprecated/insforge-auth-api.md +215 -0
- package/docs/deprecated/insforge-auth-sdk.md +100 -0
- package/docs/deprecated/insforge-db-api.md +359 -0
- package/docs/deprecated/insforge-db-sdk.md +140 -0
- package/docs/deprecated/insforge-debug-sdk.md +157 -0
- package/docs/deprecated/insforge-debug.md +65 -0
- package/docs/deprecated/insforge-instructions.md +124 -0
- package/docs/deprecated/insforge-project.md +118 -0
- package/docs/deprecated/insforge-storage-api.md +279 -0
- package/docs/deprecated/insforge-storage-sdk.md +159 -0
- package/docs/insforge-instructions-sdk.md +407 -0
- package/eslint.config.js +317 -0
- package/examples/oauth/frontend-oauth-example.html +251 -0
- package/examples/response-examples.md +444 -0
- package/frontend/README.md +112 -0
- package/frontend/components.json +17 -0
- package/frontend/index.html +13 -0
- package/frontend/package.json +63 -0
- package/frontend/public/favicon.ico +0 -0
- package/frontend/src/App.tsx +106 -0
- package/frontend/src/assets/icons/checkbox_checked.svg +6 -0
- package/frontend/src/assets/icons/checkbox_undetermined.svg +6 -0
- package/frontend/src/assets/icons/checked.svg +3 -0
- package/frontend/src/assets/icons/error.svg +3 -0
- package/frontend/src/assets/icons/pencil.svg +4 -0
- package/frontend/src/assets/icons/refresh.svg +4 -0
- package/frontend/src/assets/icons/step_active.svg +3 -0
- package/frontend/src/assets/icons/step_inactive.svg +11 -0
- package/frontend/src/assets/icons/warning.svg +3 -0
- package/frontend/src/assets/logos/amazon.svg +1 -0
- package/frontend/src/assets/logos/claude_code.svg +3 -0
- package/frontend/src/assets/logos/cline.svg +6 -0
- package/frontend/src/assets/logos/cursor.svg +20 -0
- package/frontend/src/assets/logos/discord.svg +9 -0
- package/frontend/src/assets/logos/gemini.svg +19 -0
- package/frontend/src/assets/logos/github.svg +5 -0
- package/frontend/src/assets/logos/google.svg +13 -0
- package/frontend/src/assets/logos/grok.svg +10 -0
- package/frontend/src/assets/logos/insforge_dark.svg +15 -0
- package/frontend/src/assets/logos/insforge_light.svg +15 -0
- package/frontend/src/assets/logos/openai.svg +10 -0
- package/frontend/src/assets/logos/roo_code.svg +9 -0
- package/frontend/src/assets/logos/trae.svg +3 -0
- package/frontend/src/assets/logos/windsurf.svg +10 -0
- package/frontend/src/components/ButtonWithLoading.tsx +27 -0
- package/frontend/src/components/Checkbox.tsx +61 -0
- package/frontend/src/components/CodeBlock.tsx +32 -0
- package/frontend/src/components/ConfirmDialog.tsx +96 -0
- package/frontend/src/components/CopyButton.tsx +69 -0
- package/frontend/src/components/DeleteActionButton.tsx +42 -0
- package/frontend/src/components/EmptyState.tsx +41 -0
- package/frontend/src/components/ErrorState.tsx +35 -0
- package/frontend/src/components/FeatureSidebar.tsx +126 -0
- package/frontend/src/components/FeatureSidebarItem.tsx +101 -0
- package/frontend/src/components/JsonHighlight.tsx +61 -0
- package/frontend/src/components/LoadingState.tsx +16 -0
- package/frontend/src/components/PaginationControls.tsx +54 -0
- package/frontend/src/components/PromptDialog.tsx +68 -0
- package/frontend/src/components/SearchInput.tsx +90 -0
- package/frontend/src/components/SelectionClearButton.tsx +26 -0
- package/frontend/src/components/Stepper.tsx +139 -0
- package/frontend/src/components/ThemeToggle.tsx +58 -0
- package/frontend/src/components/TypeBadge.tsx +20 -0
- package/frontend/src/components/datagrid/DataGrid.tsx +264 -0
- package/frontend/src/components/datagrid/DefaultCellRenderer.tsx +114 -0
- package/frontend/src/components/datagrid/IdCell.tsx +44 -0
- package/frontend/src/components/datagrid/SortableHeader.tsx +74 -0
- package/frontend/src/components/datagrid/cell-editors/BooleanCellEditor.tsx +54 -0
- package/frontend/src/components/datagrid/cell-editors/DateCellEditor.tsx +483 -0
- package/frontend/src/components/datagrid/cell-editors/JsonCellEditor.tsx +362 -0
- package/frontend/src/components/datagrid/cell-editors/TextCellEditor.tsx +38 -0
- package/frontend/src/components/datagrid/cell-editors/index.ts +14 -0
- package/frontend/src/components/datagrid/cell-editors/types.ts +43 -0
- package/frontend/src/components/datagrid/datagridTypes.tsx +72 -0
- package/frontend/src/components/datagrid/index.tsx +20 -0
- package/frontend/src/components/index.ts +39 -0
- package/frontend/src/components/layout/AppHeader.tsx +146 -0
- package/frontend/src/components/layout/AppSidebar.tsx +190 -0
- package/frontend/src/components/layout/CloudLayout.tsx +95 -0
- package/frontend/src/components/layout/Layout.tsx +43 -0
- package/frontend/src/components/radix/Alert.tsx +45 -0
- package/frontend/src/components/radix/AlertDialog.tsx +115 -0
- package/frontend/src/components/radix/Avatar.tsx +45 -0
- package/frontend/src/components/radix/Badge.tsx +33 -0
- package/frontend/src/components/radix/Button.tsx +50 -0
- package/frontend/src/components/radix/Card.tsx +58 -0
- package/frontend/src/components/radix/Dialog.tsx +98 -0
- package/frontend/src/components/radix/DropdownMenu.tsx +185 -0
- package/frontend/src/components/radix/Form.tsx +167 -0
- package/frontend/src/components/radix/Input.tsx +22 -0
- package/frontend/src/components/radix/Label.tsx +19 -0
- package/frontend/src/components/radix/Popover.tsx +29 -0
- package/frontend/src/components/radix/ScrollArea.tsx +44 -0
- package/frontend/src/components/radix/Select.tsx +151 -0
- package/frontend/src/components/radix/Separator.tsx +26 -0
- package/frontend/src/components/radix/Sheet.tsx +119 -0
- package/frontend/src/components/radix/Skeleton.tsx +7 -0
- package/frontend/src/components/radix/Switch.tsx +29 -0
- package/frontend/src/components/radix/Tabs.tsx +50 -0
- package/frontend/src/components/radix/Textarea.tsx +21 -0
- package/frontend/src/components/radix/Tooltip.tsx +28 -0
- package/frontend/src/features/ai/components/AIConfigCard.tsx +154 -0
- package/frontend/src/features/ai/components/AIConfigDialog.tsx +76 -0
- package/frontend/src/features/ai/components/AIConfigForm.tsx +222 -0
- package/frontend/src/features/ai/components/AIEmptyState.tsx +18 -0
- package/frontend/src/features/ai/components/fields/ModalityField.tsx +87 -0
- package/frontend/src/features/ai/components/fields/ModelSelectionField.tsx +134 -0
- package/frontend/src/features/ai/components/fields/SystemPromptField.tsx +33 -0
- package/frontend/src/features/ai/helpers.ts +155 -0
- package/frontend/src/features/ai/hooks/useAIConfigs.ts +221 -0
- package/frontend/src/features/ai/hooks/useAIUsage.ts +77 -0
- package/frontend/src/features/ai/page/AIPage.tsx +178 -0
- package/frontend/src/features/ai/services/ai.service.ts +148 -0
- package/frontend/src/features/auth/components/AddOAuthDialog.tsx +106 -0
- package/frontend/src/features/auth/components/AuthMethodTab.tsx +238 -0
- package/frontend/src/features/auth/components/OAuthConfigDialog.tsx +303 -0
- package/frontend/src/features/auth/components/OAuthEmptyState.tsx +15 -0
- package/frontend/src/features/auth/components/UserFormDialog.tsx +248 -0
- package/frontend/src/features/auth/components/UsersDataGrid.tsx +183 -0
- package/frontend/src/features/auth/components/UsersTab.tsx +114 -0
- package/frontend/src/features/auth/hooks/useOAuthConfig.ts +129 -0
- package/frontend/src/features/auth/hooks/useUsers.ts +57 -0
- package/frontend/src/features/auth/index.ts +9 -0
- package/frontend/src/features/auth/page/AuthenticationPage.tsx +169 -0
- package/frontend/src/features/auth/services/auth.service.ts +112 -0
- package/frontend/src/features/auth/services/oauth.service.ts +49 -0
- package/frontend/src/features/dashboard/page/DashboardPage.tsx +194 -0
- package/frontend/src/features/database/components/ColumnTypeSelect.tsx +64 -0
- package/frontend/src/features/database/components/DatabaseDataGrid.tsx +282 -0
- package/frontend/src/features/database/components/ForeignKeyCell.tsx +187 -0
- package/frontend/src/features/database/components/ForeignKeyPopover.tsx +378 -0
- package/frontend/src/features/database/components/LinkRecordModal.tsx +288 -0
- package/frontend/src/features/database/components/RecordFormDialog.tsx +164 -0
- package/frontend/src/features/database/components/RecordFormField.tsx +568 -0
- package/frontend/src/features/database/components/TableEmptyState.tsx +21 -0
- package/frontend/src/features/database/components/TableForm.tsx +656 -0
- package/frontend/src/features/database/components/TableFormColumn.tsx +137 -0
- package/frontend/src/features/database/components/TableListSkeleton.tsx +9 -0
- package/frontend/src/features/database/components/TableSidebar.tsx +47 -0
- package/frontend/src/features/database/constants.ts +26 -0
- package/frontend/src/features/database/helpers.ts +125 -0
- package/frontend/src/features/database/hooks/UseLinkModal.tsx +78 -0
- package/frontend/src/features/database/index.ts +12 -0
- package/frontend/src/features/database/page/DatabasePage.tsx +626 -0
- package/frontend/src/features/database/schema.ts +25 -0
- package/frontend/src/features/database/services/database.service.ts +216 -0
- package/frontend/src/features/functions/components/FunctionEmptyState.tsx +15 -0
- package/frontend/src/features/functions/components/FunctionRow.tsx +71 -0
- package/frontend/src/features/functions/components/FunctionViewer.tsx +46 -0
- package/frontend/src/features/functions/components/FunctionsContent.tsx +88 -0
- package/frontend/src/features/functions/components/FunctionsSidebar.tsx +56 -0
- package/frontend/src/features/functions/components/SecretEmptyState.tsx +23 -0
- package/frontend/src/features/functions/components/SecretRow.tsx +68 -0
- package/frontend/src/features/functions/components/SecretsContent.tsx +120 -0
- package/frontend/src/features/functions/hooks/useFunctions.ts +106 -0
- package/frontend/src/features/functions/page/FunctionsPage.tsx +28 -0
- package/frontend/src/features/functions/services/functions.service.ts +48 -0
- package/frontend/src/features/login/components/AuthErrorBoundary.tsx +87 -0
- package/frontend/src/features/login/components/PrivateRoute.tsx +24 -0
- package/frontend/src/features/login/page/CloudLoginPage.tsx +93 -0
- package/frontend/src/features/login/page/LoginPage.tsx +174 -0
- package/frontend/src/features/logs/components/AnalyticsLogsTable.tsx +313 -0
- package/frontend/src/features/logs/components/LogsTable.tsx +199 -0
- package/frontend/src/features/logs/hooks/useAuditLogs.ts +39 -0
- package/frontend/src/features/logs/index.ts +5 -0
- package/frontend/src/features/logs/page/AnalyticsLogsPage.tsx +530 -0
- package/frontend/src/features/logs/page/AuditsPage.tsx +192 -0
- package/frontend/src/features/logs/services/log.service.ts +171 -0
- package/frontend/src/features/metadata/hooks/useMetadata.ts +53 -0
- package/frontend/src/features/metadata/index.ts +0 -0
- package/frontend/src/features/metadata/page/MetadataPage.tsx +136 -0
- package/frontend/src/features/metadata/services/metadata.service.ts +17 -0
- package/frontend/src/features/onboard/components/CompletionCard.tsx +41 -0
- package/frontend/src/features/onboard/components/OnboardButton.tsx +84 -0
- package/frontend/src/features/onboard/components/StepContent.tsx +91 -0
- package/frontend/src/features/onboard/components/TestConnectionStep.tsx +53 -0
- package/frontend/src/features/onboard/components/mcp/CursorDeeplinkGenerator.tsx +35 -0
- package/frontend/src/features/onboard/components/mcp/McpInstallation.tsx +144 -0
- package/frontend/src/features/onboard/components/mcp/index.ts +4 -0
- package/frontend/src/features/onboard/components/mcp/mcp-helper.tsx +98 -0
- package/frontend/src/features/onboard/index.ts +3 -0
- package/frontend/src/features/onboard/page/OnBoardPage.tsx +104 -0
- package/frontend/src/features/onboard/types.ts +8 -0
- package/frontend/src/features/secrets/hooks/useSecrets.ts +139 -0
- package/frontend/src/features/secrets/services/secrets.service.ts +57 -0
- package/frontend/src/features/storage/components/BucketEmptyState.tsx +19 -0
- package/frontend/src/features/storage/components/BucketFormDialog.tsx +194 -0
- package/frontend/src/features/storage/components/BucketListSkeleton.tsx +17 -0
- package/frontend/src/features/storage/components/FilePreviewDialog.tsx +287 -0
- package/frontend/src/features/storage/components/StorageDataGrid.tsx +239 -0
- package/frontend/src/features/storage/components/StorageManager.tsx +236 -0
- package/frontend/src/features/storage/components/StorageSidebar.tsx +44 -0
- package/frontend/src/features/storage/components/UploadToast.tsx +46 -0
- package/frontend/src/features/storage/index.ts +3 -0
- package/frontend/src/features/storage/page/StoragePage.tsx +553 -0
- package/frontend/src/features/storage/services/storage.service.ts +144 -0
- package/frontend/src/features/visualizer/components/AuthNode.tsx +107 -0
- package/frontend/src/features/visualizer/components/BucketNode.tsx +34 -0
- package/frontend/src/features/visualizer/components/SchemaVisualizer.tsx +359 -0
- package/frontend/src/features/visualizer/components/TableNode.tsx +152 -0
- package/frontend/src/features/visualizer/components/VisualizerSkeleton.tsx +24 -0
- package/frontend/src/features/visualizer/components/index.ts +5 -0
- package/frontend/src/features/visualizer/page/VisualizerPage.tsx +127 -0
- package/frontend/src/index.css +248 -0
- package/frontend/src/lib/api/client.ts +163 -0
- package/frontend/src/lib/contexts/AuthContext.tsx +157 -0
- package/frontend/src/lib/contexts/OnboardStepContext.tsx +68 -0
- package/frontend/src/lib/contexts/SocketContext.tsx +303 -0
- package/frontend/src/lib/contexts/ThemeContext.tsx +125 -0
- package/frontend/src/lib/hooks/useAuth.ts +4 -0
- package/frontend/src/lib/hooks/useConfirm.ts +55 -0
- package/frontend/src/lib/hooks/useInterval.ts +27 -0
- package/frontend/src/lib/hooks/useMediaQuery.ts +59 -0
- package/frontend/src/lib/hooks/useOnboardingCompletion.ts +29 -0
- package/frontend/src/lib/hooks/usePagination.ts +27 -0
- package/frontend/src/lib/hooks/useTimeout.ts +27 -0
- package/frontend/src/lib/hooks/useToast.tsx +229 -0
- package/frontend/src/lib/utils/constants.ts +38 -0
- package/frontend/src/lib/utils/utils.ts +165 -0
- package/frontend/src/lib/utils/validation-schemas.ts +126 -0
- package/frontend/src/main.tsx +16 -0
- package/frontend/src/rdg.css +194 -0
- package/frontend/src/vite-env.d.ts +12 -0
- package/frontend/tailwind.config.js +97 -0
- package/frontend/tsconfig.json +26 -0
- package/frontend/tsconfig.node.json +10 -0
- package/frontend/vite.config.ts +37 -0
- package/frontend/vitest.config.ts +36 -0
- package/functions/deno.json +25 -0
- package/functions/server.ts +290 -0
- package/functions/worker-template.js +126 -0
- package/openapi/ai.yaml +689 -0
- package/openapi/auth.yaml +563 -0
- package/openapi/functions.yaml +476 -0
- package/openapi/health.yaml +30 -0
- package/openapi/logs.yaml +224 -0
- package/openapi/metadata.yaml +178 -0
- package/openapi/records.yaml +382 -0
- package/openapi/secrets.yaml +371 -0
- package/openapi/storage.yaml +876 -0
- package/openapi/tables.yaml +464 -0
- package/package.json +88 -0
- package/shared-schemas/package.json +31 -0
- package/shared-schemas/src/ai-api.schema.ts +167 -0
- package/shared-schemas/src/ai.schema.ts +54 -0
- package/shared-schemas/src/auth-api.schema.ts +193 -0
- package/shared-schemas/src/auth.schema.ts +94 -0
- package/shared-schemas/src/database-api.schema.ts +259 -0
- package/shared-schemas/src/database.schema.ts +69 -0
- package/shared-schemas/src/functions-api.schema.ts +25 -0
- package/shared-schemas/src/functions.schema.ts +16 -0
- package/shared-schemas/src/index.ts +13 -0
- package/shared-schemas/src/logs-api.schema.ts +49 -0
- package/shared-schemas/src/logs.schema.ts +14 -0
- package/shared-schemas/src/metadata.schema.ts +56 -0
- package/shared-schemas/src/storage-api.schema.ts +65 -0
- package/shared-schemas/src/storage.schema.ts +19 -0
- package/shared-schemas/tsconfig.json +21 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { ConvertedValue } from '@/components/datagrid/datagridTypes';
|
|
2
|
+
import { apiClient } from '@/lib/api/client';
|
|
3
|
+
import {
|
|
4
|
+
ColumnSchema,
|
|
5
|
+
GetTableSchemaResponse,
|
|
6
|
+
CreateTableRequest,
|
|
7
|
+
UpdateTableSchemaResponse,
|
|
8
|
+
UpdateTableSchemaRequest,
|
|
9
|
+
} from '@insforge/shared-schemas';
|
|
10
|
+
|
|
11
|
+
export class DatabaseService {
|
|
12
|
+
// Table operations
|
|
13
|
+
async listTables(): Promise<string[]> {
|
|
14
|
+
const data = await apiClient.request('/database/tables', {
|
|
15
|
+
headers: apiClient.withAccessToken(),
|
|
16
|
+
});
|
|
17
|
+
// data is already unwrapped by request method and should be an array
|
|
18
|
+
return Array.isArray(data) ? data : [];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
getTableSchema(tableName: string): Promise<GetTableSchemaResponse> {
|
|
22
|
+
return apiClient.request(`/database/tables/${tableName}/schema`, {
|
|
23
|
+
headers: apiClient.withAccessToken(),
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
createTable(tableName: string, columns: ColumnSchema[]) {
|
|
28
|
+
const body: CreateTableRequest = { tableName: tableName, columns, rlsEnabled: true };
|
|
29
|
+
return apiClient.request('/database/tables', {
|
|
30
|
+
method: 'POST',
|
|
31
|
+
headers: apiClient.withAccessToken({
|
|
32
|
+
'Content-Type': 'application/json',
|
|
33
|
+
}),
|
|
34
|
+
body: JSON.stringify(body),
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
deleteTable(tableName: string) {
|
|
39
|
+
return apiClient.request(`/database/tables/${tableName}`, {
|
|
40
|
+
method: 'DELETE',
|
|
41
|
+
headers: apiClient.withAccessToken(),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
updateTableSchema(
|
|
46
|
+
tableName: string,
|
|
47
|
+
operations: UpdateTableSchemaRequest
|
|
48
|
+
): Promise<UpdateTableSchemaResponse | void> {
|
|
49
|
+
return apiClient.request(`/database/tables/${tableName}/schema`, {
|
|
50
|
+
method: 'PATCH',
|
|
51
|
+
headers: apiClient.withAccessToken({
|
|
52
|
+
'Content-Type': 'application/json',
|
|
53
|
+
}),
|
|
54
|
+
body: JSON.stringify(operations),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Record operations
|
|
59
|
+
/**
|
|
60
|
+
* Data fetching method with built-in search, sorting, and pagination for UI components.
|
|
61
|
+
*
|
|
62
|
+
* @param tableName - Name of the table
|
|
63
|
+
* @param limit - Number of records to fetch
|
|
64
|
+
* @param offset - Number of records to skip
|
|
65
|
+
* @param searchQuery - Search term to filter text columns
|
|
66
|
+
* @param sortColumns - Sorting configuration
|
|
67
|
+
* @returns Structured response with records and pagination info
|
|
68
|
+
*/
|
|
69
|
+
async getTableRecords(
|
|
70
|
+
tableName: string,
|
|
71
|
+
limit = 10,
|
|
72
|
+
offset = 0,
|
|
73
|
+
searchQuery?: string,
|
|
74
|
+
sortColumns?: { columnKey: string; direction: string }[]
|
|
75
|
+
) {
|
|
76
|
+
const params = new URLSearchParams();
|
|
77
|
+
params.set('limit', limit.toString());
|
|
78
|
+
params.set('offset', offset.toString());
|
|
79
|
+
|
|
80
|
+
// Construct PostgREST filter directly in frontend if search query is provided
|
|
81
|
+
if (searchQuery && searchQuery.trim()) {
|
|
82
|
+
const searchValue = searchQuery.trim();
|
|
83
|
+
|
|
84
|
+
// Get table schema to identify text columns
|
|
85
|
+
const schema = await this.getTableSchema(tableName);
|
|
86
|
+
const textColumns = schema.columns
|
|
87
|
+
.filter((col: ColumnSchema) => {
|
|
88
|
+
const type = col.type.toLowerCase();
|
|
89
|
+
return type === 'string';
|
|
90
|
+
})
|
|
91
|
+
.map((col: ColumnSchema) => col.columnName);
|
|
92
|
+
|
|
93
|
+
if (textColumns.length > 0) {
|
|
94
|
+
// Create PostgREST OR filter for text columns
|
|
95
|
+
const orFilters = textColumns
|
|
96
|
+
.map((column: string) => `${column}.ilike.*${searchValue}*`)
|
|
97
|
+
.join(',');
|
|
98
|
+
params.set('or', `(${orFilters})`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Add sorting if provided - PostgREST uses "order" parameter
|
|
103
|
+
if (sortColumns && sortColumns.length > 0) {
|
|
104
|
+
const orderParam = sortColumns
|
|
105
|
+
.map((col) => `${col.columnKey}.${col.direction.toLowerCase()}`)
|
|
106
|
+
.join(',');
|
|
107
|
+
params.set('order', orderParam);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const response: {
|
|
111
|
+
data: { [key: string]: ConvertedValue }[];
|
|
112
|
+
pagination: { offset: number; limit: number; total: number };
|
|
113
|
+
} = await apiClient.request(`/database/records/${tableName}?${params.toString()}`, {
|
|
114
|
+
headers: {
|
|
115
|
+
Prefer: 'count=exact',
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
records: response.data,
|
|
121
|
+
pagination: response.pagination,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get a single record by foreign key value.
|
|
127
|
+
* Specifically designed for foreign key lookups.
|
|
128
|
+
*
|
|
129
|
+
* @param tableName - Name of the table to search in
|
|
130
|
+
* @param columnName - Name of the column to filter by
|
|
131
|
+
* @param value - Value to match
|
|
132
|
+
* @returns Single record or null if not found
|
|
133
|
+
*/
|
|
134
|
+
async getRecordByForeignKeyValue(tableName: string, columnName: string, value: string) {
|
|
135
|
+
const queryParams = `${columnName}=eq.${encodeURIComponent(value)}&limit=1`;
|
|
136
|
+
const response = await this.getRecords(tableName, queryParams);
|
|
137
|
+
|
|
138
|
+
// Return the first record if found, or null if not found
|
|
139
|
+
if (response.records && response.records.length > 0) {
|
|
140
|
+
return response.records[0];
|
|
141
|
+
}
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async getRecords(tableName: string, queryParams: string = '') {
|
|
146
|
+
const url = `/database/records/${tableName}${queryParams ? `?${queryParams}` : ''}`;
|
|
147
|
+
const response = await apiClient.request(url, {
|
|
148
|
+
headers: apiClient.withAccessToken(),
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// Traditional REST: check if response is array (direct data) or wrapped
|
|
152
|
+
if (Array.isArray(response)) {
|
|
153
|
+
return {
|
|
154
|
+
records: response,
|
|
155
|
+
total: response.length,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// If backend returns wrapped format for this endpoint
|
|
160
|
+
if (response.data && Array.isArray(response.data)) {
|
|
161
|
+
return {
|
|
162
|
+
records: response.data,
|
|
163
|
+
total: response.data.length,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
records: response,
|
|
169
|
+
total: response.length,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
createRecords(table: string, records: { [key: string]: ConvertedValue }[]) {
|
|
174
|
+
// if data is json and data[id] == "" then remove id from data, because can't assign '' to uuid
|
|
175
|
+
records = records.map((record) => {
|
|
176
|
+
if (typeof record === 'object' && record.id === '') {
|
|
177
|
+
delete record.id;
|
|
178
|
+
}
|
|
179
|
+
return record;
|
|
180
|
+
});
|
|
181
|
+
return apiClient.request(`/database/records/${table}`, {
|
|
182
|
+
method: 'POST',
|
|
183
|
+
headers: apiClient.withAccessToken({
|
|
184
|
+
'Content-Type': 'application/json',
|
|
185
|
+
}),
|
|
186
|
+
body: JSON.stringify(records),
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
createRecord(table: string, data: { [key: string]: ConvertedValue }) {
|
|
191
|
+
if (typeof data === 'object' && data.id === '') {
|
|
192
|
+
// can't assign '' to uuid, so we need to remove it
|
|
193
|
+
delete data.id;
|
|
194
|
+
}
|
|
195
|
+
return this.createRecords(table, [data]);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
updateRecord(table: string, id: string, data: { [key: string]: ConvertedValue }) {
|
|
199
|
+
return apiClient.request(`/database/records/${table}?id=eq.${id}`, {
|
|
200
|
+
method: 'PATCH',
|
|
201
|
+
headers: apiClient.withAccessToken({
|
|
202
|
+
'Content-Type': 'application/json',
|
|
203
|
+
}),
|
|
204
|
+
body: JSON.stringify(data),
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
deleteRecord(table: string, id: string) {
|
|
209
|
+
return apiClient.request(`/database/records/${table}?id=eq.${id}`, {
|
|
210
|
+
method: 'DELETE',
|
|
211
|
+
headers: apiClient.withAccessToken(),
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export const databaseService = new DatabaseService();
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Code2 } from 'lucide-react';
|
|
2
|
+
|
|
3
|
+
export default function FunctionEmptyState() {
|
|
4
|
+
return (
|
|
5
|
+
<div className="flex flex-col items-center justify-center py-8 text-center gap-3 rounded-[8px] bg-neutral-100 dark:bg-[#333333]">
|
|
6
|
+
<Code2 size={40} className="text-neutral-400 dark:text-neutral-600" />
|
|
7
|
+
<div className="flex flex-col items-center justify-center gap-1">
|
|
8
|
+
<p className="text-sm font-medium text-zinc-950 dark:text-white">No functions available</p>
|
|
9
|
+
<p className="text-neutral-500 dark:text-neutral-400 text-xs">
|
|
10
|
+
No edge functions have been created yet
|
|
11
|
+
</p>
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { CopyButton } from '@/components/CopyButton';
|
|
2
|
+
import { type EdgeFunction } from '../services/functions.service';
|
|
3
|
+
import { cn, getBackendUrl } from '@/lib/utils/utils';
|
|
4
|
+
import { format, formatDistance } from 'date-fns';
|
|
5
|
+
interface FunctionRowProps {
|
|
6
|
+
function: EdgeFunction;
|
|
7
|
+
onClick: () => void;
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function FunctionRow({ function: func, onClick, className }: FunctionRowProps) {
|
|
12
|
+
const functionUrl = `${getBackendUrl()}/functions/${func.slug}`;
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<div
|
|
16
|
+
className={cn(
|
|
17
|
+
'group h-14 px-3 bg-white hover:bg-neutral-100 dark:bg-[#333333] dark:hover:bg-neutral-700 rounded-[8px] transition-all cursor-pointer',
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
onClick={onClick}
|
|
21
|
+
>
|
|
22
|
+
<div className="grid grid-cols-12 h-full items-center">
|
|
23
|
+
{/* Name Column */}
|
|
24
|
+
<div className="col-span-2 min-w-0 px-3 py-1.5">
|
|
25
|
+
<p className="text-sm text-zinc-950 dark:text-white truncate" title={func.name}>
|
|
26
|
+
{func.name}
|
|
27
|
+
</p>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
{/* URL Column */}
|
|
31
|
+
<div className="col-span-6 min-w-0 px-3 py-1.5">
|
|
32
|
+
<div className="flex items-center gap-3">
|
|
33
|
+
<span
|
|
34
|
+
className="text-sm text-muted-foreground dark:text-white truncate"
|
|
35
|
+
title={functionUrl}
|
|
36
|
+
>
|
|
37
|
+
{functionUrl}
|
|
38
|
+
</span>
|
|
39
|
+
<CopyButton
|
|
40
|
+
showText={false}
|
|
41
|
+
text={functionUrl}
|
|
42
|
+
className="h-7 w-7 dark:hover:bg-neutral-500 dark:data-[copied=true]:group-hover:bg-neutral-700 dark:data-[copied=true]:hover:bg-neutral-700"
|
|
43
|
+
/>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
{/* Created Column */}
|
|
48
|
+
<div className="col-span-2 px-3 py-1.5">
|
|
49
|
+
<span
|
|
50
|
+
className="text-sm text-muted-foreground dark:text-white truncate"
|
|
51
|
+
title={func.created_at}
|
|
52
|
+
>
|
|
53
|
+
{format(new Date(func.created_at), 'MMM dd, yyyy HH:mm')}
|
|
54
|
+
</span>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
{/* Last Update Column */}
|
|
58
|
+
<div className="col-span-2 px-3 py-1.5">
|
|
59
|
+
<span
|
|
60
|
+
className="text-sm text-muted-foreground dark:text-white truncate"
|
|
61
|
+
title={func.deployed_at}
|
|
62
|
+
>
|
|
63
|
+
{func.deployed_at
|
|
64
|
+
? formatDistance(new Date(func.deployed_at), new Date(), { addSuffix: true })
|
|
65
|
+
: 'Never'}
|
|
66
|
+
</span>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
interface CodeEditorProps {
|
|
2
|
+
code: string;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function CodeEditor({ code }: CodeEditorProps) {
|
|
6
|
+
// Split code into lines for line numbers
|
|
7
|
+
const lines = code.split('\n');
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<div className="h-full bg-white dark:bg-neutral-900 overflow-auto">
|
|
11
|
+
<div className="flex min-h-full">
|
|
12
|
+
{/* Line Numbers */}
|
|
13
|
+
<div className="flex-shrink-0 bg-gray-50 dark:bg-neutral-800 px-3 py-4 font-mono text-sm text-gray-500 dark:text-gray-400 select-none">
|
|
14
|
+
{lines.map((_, index) => (
|
|
15
|
+
<div key={index} className="leading-6 text-right min-w-[2rem]">
|
|
16
|
+
{index + 1}
|
|
17
|
+
</div>
|
|
18
|
+
))}
|
|
19
|
+
{/* Extra line numbers for blank lines */}
|
|
20
|
+
{Array.from({ length: 6 }, (_, i) => (
|
|
21
|
+
<div key={`extra-${i}`} className="leading-6 text-right min-w-[2rem]">
|
|
22
|
+
{lines.length + 1 + i}
|
|
23
|
+
</div>
|
|
24
|
+
))}
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
{/* Code Area */}
|
|
28
|
+
<div className="flex-1">
|
|
29
|
+
<pre className="font-mono text-sm leading-6 p-4 m-0 bg-transparent text-gray-900 dark:text-white">
|
|
30
|
+
{lines.map((line, index) => (
|
|
31
|
+
<div key={index} className="min-h-[1.5rem]">
|
|
32
|
+
{line || <span> </span>}
|
|
33
|
+
</div>
|
|
34
|
+
))}
|
|
35
|
+
{/* Some blank lines at the end */}
|
|
36
|
+
{Array.from({ length: 6 }, (_, i) => (
|
|
37
|
+
<div key={`blank-${i}`} className="min-h-[1.5rem]">
|
|
38
|
+
|
|
39
|
+
</div>
|
|
40
|
+
))}
|
|
41
|
+
</pre>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { ChevronRight } from 'lucide-react';
|
|
2
|
+
import { Skeleton } from '@/components/radix/Skeleton';
|
|
3
|
+
import { FunctionRow } from './FunctionRow';
|
|
4
|
+
import { CodeEditor } from './FunctionViewer';
|
|
5
|
+
import FunctionEmptyState from './FunctionEmptyState';
|
|
6
|
+
import { useFunctions } from '../hooks/useFunctions';
|
|
7
|
+
import { useToast } from '@/lib/hooks/useToast';
|
|
8
|
+
import { useEffect, useRef } from 'react';
|
|
9
|
+
|
|
10
|
+
export function FunctionsContent() {
|
|
11
|
+
const toastShownRef = useRef(false);
|
|
12
|
+
const { showToast } = useToast();
|
|
13
|
+
const {
|
|
14
|
+
functions,
|
|
15
|
+
isRuntimeAvailable,
|
|
16
|
+
selectedFunction,
|
|
17
|
+
isLoading: loading,
|
|
18
|
+
selectFunction,
|
|
19
|
+
clearSelection,
|
|
20
|
+
} = useFunctions();
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (!isRuntimeAvailable && !toastShownRef.current) {
|
|
24
|
+
toastShownRef.current = true;
|
|
25
|
+
showToast('Function container is unhealthy.', 'error');
|
|
26
|
+
}
|
|
27
|
+
}, [isRuntimeAvailable, showToast]);
|
|
28
|
+
|
|
29
|
+
// If a function is selected, show the detail view
|
|
30
|
+
if (selectedFunction) {
|
|
31
|
+
return (
|
|
32
|
+
<div className="flex flex-col h-full">
|
|
33
|
+
<div className="flex items-center gap-2.5 p-4 border-b border-border-gray dark:border-neutral-600">
|
|
34
|
+
<button
|
|
35
|
+
onClick={clearSelection}
|
|
36
|
+
className="text-xl text-zinc-500 dark:text-neutral-400 hover:text-zinc-950 dark:hover:text-white transition-colors"
|
|
37
|
+
>
|
|
38
|
+
Functions
|
|
39
|
+
</button>
|
|
40
|
+
<ChevronRight className="w-5 h-5 text-muted-foreground dark:text-neutral-400" />
|
|
41
|
+
<p className="text-xl text-zinc-950 dark:text-white">{selectedFunction.name}</p>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div className="flex-1 min-h-0">
|
|
45
|
+
<CodeEditor code={selectedFunction.code || '// No code available'} />
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Default list view
|
|
52
|
+
return (
|
|
53
|
+
<div className="flex flex-col gap-6 p-4">
|
|
54
|
+
<p className="h-7 text-xl text-zinc-950 dark:text-white">Functions</p>
|
|
55
|
+
<div className="flex flex-col gap-2">
|
|
56
|
+
{/* Table Header */}
|
|
57
|
+
<div className="grid grid-cols-12 px-3 text-sm text-muted-foreground dark:text-neutral-400">
|
|
58
|
+
<div className="col-span-2 py-1 px-3">Name</div>
|
|
59
|
+
<div className="col-span-6 py-1 px-3">URL</div>
|
|
60
|
+
<div className="col-span-2 py-1 px-3">Created</div>
|
|
61
|
+
<div className="col-span-2 py-1 px-3">Last Update</div>
|
|
62
|
+
</div>
|
|
63
|
+
{loading ? (
|
|
64
|
+
<>
|
|
65
|
+
{[...Array(4)].map((_, i) => (
|
|
66
|
+
<Skeleton key={i} className="h-14 rounded-[8px] cols-span-full" />
|
|
67
|
+
))}
|
|
68
|
+
</>
|
|
69
|
+
) : functions.length >= 1 ? (
|
|
70
|
+
<>
|
|
71
|
+
{functions.map((func) => (
|
|
72
|
+
<FunctionRow
|
|
73
|
+
key={func.id}
|
|
74
|
+
function={func}
|
|
75
|
+
onClick={() => void selectFunction(func)}
|
|
76
|
+
className="cols-span-full"
|
|
77
|
+
/>
|
|
78
|
+
))}
|
|
79
|
+
</>
|
|
80
|
+
) : (
|
|
81
|
+
<div className="cols-span-full">
|
|
82
|
+
<FunctionEmptyState />
|
|
83
|
+
</div>
|
|
84
|
+
)}
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { cn } from '@/lib/utils/utils';
|
|
2
|
+
import { useFunctions } from '../hooks/useFunctions';
|
|
3
|
+
|
|
4
|
+
interface FunctionsSidebarProps {
|
|
5
|
+
selectedSection: 'functions' | 'secrets';
|
|
6
|
+
onSectionSelect: (section: 'functions' | 'secrets') => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function FunctionsSidebar({ selectedSection, onSectionSelect }: FunctionsSidebarProps) {
|
|
10
|
+
const { clearSelection } = useFunctions();
|
|
11
|
+
const sections = [
|
|
12
|
+
{
|
|
13
|
+
id: 'functions' as const,
|
|
14
|
+
name: 'Functions',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: 'secrets' as const,
|
|
18
|
+
name: 'Secrets',
|
|
19
|
+
},
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div className="w-60 px-3 py-4 flex flex-col h-full bg-white dark:bg-neutral-800 border-r border-border-gray dark:border-neutral-700">
|
|
24
|
+
<div className="mb-4 w-full">
|
|
25
|
+
<p className="text-base text-zinc-950 dark:text-neutral-400">Edge Functions</p>
|
|
26
|
+
</div>
|
|
27
|
+
<div className="flex-1 overflow-y-auto">
|
|
28
|
+
<div className="space-y-2">
|
|
29
|
+
{sections.map((section) => {
|
|
30
|
+
const isSelected = selectedSection === section.id;
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<button
|
|
34
|
+
key={section.id}
|
|
35
|
+
onClick={() => {
|
|
36
|
+
if (section.id === 'functions') {
|
|
37
|
+
clearSelection();
|
|
38
|
+
}
|
|
39
|
+
onSectionSelect(section.id);
|
|
40
|
+
}}
|
|
41
|
+
className={cn(
|
|
42
|
+
'h-9 w-full flex items-center justify-between pl-3 py-1 pr-1 rounded text-left transition-colors',
|
|
43
|
+
isSelected
|
|
44
|
+
? 'bg-zinc-100 dark:bg-neutral-700 text-zinc-950 dark:text-white'
|
|
45
|
+
: 'hover:bg-zinc-50 dark:hover:bg-neutral-700/50 text-zinc-700 dark:text-zinc-300'
|
|
46
|
+
)}
|
|
47
|
+
>
|
|
48
|
+
<p className="text-sm text-zinc-950 dark:text-white">{section.name}</p>
|
|
49
|
+
</button>
|
|
50
|
+
);
|
|
51
|
+
})}
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Key } from 'lucide-react';
|
|
2
|
+
|
|
3
|
+
interface SecretEmptyStateProps {
|
|
4
|
+
searchQuery: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export default function SecretEmptyState({ searchQuery }: SecretEmptyStateProps) {
|
|
8
|
+
return (
|
|
9
|
+
<div className="flex flex-col items-center justify-center py-8 rounded-[8px] bg-neutral-100 dark:bg-[#333333]">
|
|
10
|
+
<Key className="w-12 h-12 mx-auto mb-4 text-muted-foreground" />
|
|
11
|
+
<div className="flex flex-col items-center justify-center gap-1">
|
|
12
|
+
<p className="text-sm font-medium text-zinc-950 dark:text-white">
|
|
13
|
+
{searchQuery ? 'No matching secrets found' : 'No secrets configured'}
|
|
14
|
+
</p>
|
|
15
|
+
<p className="text-neutral-500 dark:text-neutral-400 text-sm">
|
|
16
|
+
{searchQuery
|
|
17
|
+
? 'Try adjusting your search terms'
|
|
18
|
+
: 'Create environment variables for your edge functions'}
|
|
19
|
+
</p>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Trash2 } from 'lucide-react';
|
|
2
|
+
import { Button } from '@/components/radix/Button';
|
|
3
|
+
import { type Secret } from '@/features/secrets/services/secrets.service';
|
|
4
|
+
import { cn } from '@/lib/utils/utils';
|
|
5
|
+
import { formatDistance } from 'date-fns';
|
|
6
|
+
|
|
7
|
+
interface SecretRowProps {
|
|
8
|
+
secret: Secret;
|
|
9
|
+
onDelete: (secret: Secret) => void;
|
|
10
|
+
className?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function SecretRow({ secret, onDelete, className }: SecretRowProps) {
|
|
14
|
+
const handleDeleteClick = (e: React.MouseEvent) => {
|
|
15
|
+
e.stopPropagation();
|
|
16
|
+
onDelete(secret);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div
|
|
21
|
+
className={cn(
|
|
22
|
+
'group h-14 px-3 bg-white hover:bg-neutral-100 dark:bg-[#333333] dark:hover:bg-neutral-700 rounded-[8px] transition-all',
|
|
23
|
+
className
|
|
24
|
+
)}
|
|
25
|
+
>
|
|
26
|
+
<div className="grid grid-cols-12 h-full items-center">
|
|
27
|
+
{/* Name Column */}
|
|
28
|
+
<div className="col-span-8 min-w-0 px-3 py-1.5">
|
|
29
|
+
<p
|
|
30
|
+
className="text-sm text-zinc-950 dark:text-white truncate font-medium"
|
|
31
|
+
title={secret.key}
|
|
32
|
+
>
|
|
33
|
+
{secret.key}
|
|
34
|
+
</p>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
{/* Digest Column */}
|
|
38
|
+
{/* <div className="col-span-5 min-w-0 px-3 py-1.5">
|
|
39
|
+
<span className="text-sm text-zinc-950 dark:text-white font-mono truncate">
|
|
40
|
+
</span>
|
|
41
|
+
</div> */}
|
|
42
|
+
|
|
43
|
+
{/* Updated at Column */}
|
|
44
|
+
<div className="col-span-3 px-3 py-1.5">
|
|
45
|
+
<span className="text-sm text-zinc-950 dark:text-white truncate">
|
|
46
|
+
{secret.updatedAt
|
|
47
|
+
? formatDistance(new Date(secret.updatedAt), new Date(), { addSuffix: true })
|
|
48
|
+
: 'Never'}
|
|
49
|
+
</span>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
{/* Delete Button Column */}
|
|
53
|
+
<div className="col-span-1 flex justify-end px-3 py-1.5">
|
|
54
|
+
<Button
|
|
55
|
+
variant="ghost"
|
|
56
|
+
size="sm"
|
|
57
|
+
onClick={handleDeleteClick}
|
|
58
|
+
disabled={secret.isReserved}
|
|
59
|
+
className="h-7 w-7 p-1 text-neutral-500 dark:text-neutral-400 hover:text-black dark:hover:text-white hover:bg-neutral-200 dark:hover:bg-neutral-600"
|
|
60
|
+
title={secret.isReserved ? 'Cannot delete reserved secrets' : 'Delete secret'}
|
|
61
|
+
>
|
|
62
|
+
<Trash2 className="w-5 h-5" />
|
|
63
|
+
</Button>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|