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,107 @@
|
|
|
1
|
+
import { Lock, FormInput, Users } from 'lucide-react';
|
|
2
|
+
import GoogleIcon from '@/assets/logos/google.svg';
|
|
3
|
+
import GithubIcon from '@/assets/logos/github.svg';
|
|
4
|
+
import { AuthMetadataSchema } from '@insforge/shared-schemas';
|
|
5
|
+
import { cn } from '@/lib/utils/utils';
|
|
6
|
+
import { useOAuthConfig } from '@/features/auth/hooks/useOAuthConfig';
|
|
7
|
+
|
|
8
|
+
interface AuthNodeProps {
|
|
9
|
+
data: {
|
|
10
|
+
authMetadata: AuthMetadataSchema;
|
|
11
|
+
userCount?: number;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function AuthNode({ data }: AuthNodeProps) {
|
|
16
|
+
const { authMetadata, userCount } = data;
|
|
17
|
+
const { isProviderConfigured } = useOAuthConfig();
|
|
18
|
+
|
|
19
|
+
const enabledCount = authMetadata.oauths.length;
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div className="bg-neutral-900 rounded-lg border border-[#363636] min-w-[280px]">
|
|
23
|
+
{/* Auth Header */}
|
|
24
|
+
<div className="flex items-center justify-between p-2 border-b border-neutral-800">
|
|
25
|
+
<div className="flex items-center gap-2">
|
|
26
|
+
<div className="flex items-center justify-center w-11 h-11 bg-lime-300 rounded p-1.5">
|
|
27
|
+
<Lock className="w-5 h-5 text-neutral-900" />
|
|
28
|
+
</div>
|
|
29
|
+
<div className="flex-1">
|
|
30
|
+
<h3 className="text-sm font-medium text-white">Authentication</h3>
|
|
31
|
+
<p className="text-xs text-neutral-300">
|
|
32
|
+
{enabledCount} provider{enabledCount !== 1 ? 's' : ''} enabled
|
|
33
|
+
</p>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
{/* <div className="p-1.5">
|
|
37
|
+
<ExternalLink className="w-4 h-4 text-neutral-400" />
|
|
38
|
+
</div> */}
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
{/* Auth Providers */}
|
|
42
|
+
<div className="p-2 space-y-2 border-b border-neutral-800">
|
|
43
|
+
{/* Email/Password */}
|
|
44
|
+
<div className="flex items-center justify-between p-2.5 bg-neutral-800 rounded">
|
|
45
|
+
<div className="flex items-center gap-2.5">
|
|
46
|
+
<FormInput className="w-5 h-5 text-neutral-300" />
|
|
47
|
+
<span className="text-sm text-neutral-300">Email/Password</span>
|
|
48
|
+
</div>
|
|
49
|
+
<div className="px-1.5 py-0.5 bg-lime-200 rounded flex items-center">
|
|
50
|
+
<span className="text-xs font-medium text-lime-900">Enabled</span>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
{/* Google OAuth */}
|
|
55
|
+
<div className="flex items-center justify-between p-2.5 bg-neutral-800 rounded">
|
|
56
|
+
<div className="flex items-center gap-2.5">
|
|
57
|
+
<img src={GoogleIcon} alt="google" className="h-5 w-5" />
|
|
58
|
+
<span className="text-sm text-neutral-300">Google OAuth</span>
|
|
59
|
+
</div>
|
|
60
|
+
<div
|
|
61
|
+
className={cn(
|
|
62
|
+
'px-1.5 py-0.5 rounded flex items-center',
|
|
63
|
+
isProviderConfigured('google')
|
|
64
|
+
? 'bg-lime-200 text-lime-900'
|
|
65
|
+
: 'bg-neutral-700 text-neutral-300'
|
|
66
|
+
)}
|
|
67
|
+
>
|
|
68
|
+
<span className="text-xs font-medium">
|
|
69
|
+
{isProviderConfigured('google') ? 'Enabled' : 'Disabled'}
|
|
70
|
+
</span>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
{/* GitHub OAuth */}
|
|
75
|
+
<div className="flex items-center justify-between p-2.5 bg-neutral-800 rounded">
|
|
76
|
+
<div className="flex items-center gap-2.5">
|
|
77
|
+
<img src={GithubIcon} alt="github" className="h-5 w-5" />
|
|
78
|
+
<span className="text-sm text-neutral-300">GitHub OAuth</span>
|
|
79
|
+
</div>
|
|
80
|
+
<div
|
|
81
|
+
className={cn(
|
|
82
|
+
'px-1.5 py-0.5 rounded flex items-center',
|
|
83
|
+
isProviderConfigured('github')
|
|
84
|
+
? 'bg-lime-200 text-lime-900'
|
|
85
|
+
: 'bg-neutral-700 text-neutral-300'
|
|
86
|
+
)}
|
|
87
|
+
>
|
|
88
|
+
<span className="text-xs font-medium">
|
|
89
|
+
{isProviderConfigured('github') ? 'Enabled' : 'Disabled'}
|
|
90
|
+
</span>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
{/* Users Section */}
|
|
96
|
+
<div className="flex items-center justify-between p-3 border-t border-neutral-700">
|
|
97
|
+
<div className="flex items-center gap-2.5">
|
|
98
|
+
<Users className="w-5 h-5 text-neutral-300" />
|
|
99
|
+
<span className="text-sm text-neutral-300">Users</span>
|
|
100
|
+
</div>
|
|
101
|
+
<div className="flex items-center">
|
|
102
|
+
{userCount !== undefined && <span className="text-xs text-neutral-400">{userCount}</span>}
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { HardDrive } from 'lucide-react';
|
|
2
|
+
import { BucketMetadataSchema } from '@insforge/shared-schemas';
|
|
3
|
+
|
|
4
|
+
interface BucketNodeProps {
|
|
5
|
+
data: {
|
|
6
|
+
bucket: BucketMetadataSchema;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function BucketNode({ data }: BucketNodeProps) {
|
|
11
|
+
const { bucket } = data;
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div className="bg-neutral-900 rounded-lg border border-[#363636] min-w-[320px]">
|
|
15
|
+
{/* Bucket Header */}
|
|
16
|
+
<div className="flex items-center justify-between p-2 border-b border-neutral-800">
|
|
17
|
+
<div className="flex items-center gap-2">
|
|
18
|
+
<div className="flex items-center justify-center w-11 h-11 bg-blue-300 rounded p-1.5">
|
|
19
|
+
<HardDrive className="w-5 h-5 text-neutral-900" />
|
|
20
|
+
</div>
|
|
21
|
+
<div className="flex-1">
|
|
22
|
+
<h3 className="text-sm font-medium text-white">{bucket.name}</h3>
|
|
23
|
+
<p className="text-xs text-neutral-300">
|
|
24
|
+
{bucket.objectCount ? `${bucket.objectCount} files` : '0 files'}
|
|
25
|
+
</p>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
{/* <div className="p-1.5">
|
|
29
|
+
<ExternalLink className="w-4 h-4 text-neutral-400" />
|
|
30
|
+
</div> */}
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
ReactFlow,
|
|
4
|
+
Node,
|
|
5
|
+
BuiltInEdge,
|
|
6
|
+
Controls,
|
|
7
|
+
MiniMap,
|
|
8
|
+
useNodesState,
|
|
9
|
+
useEdgesState,
|
|
10
|
+
addEdge,
|
|
11
|
+
Connection,
|
|
12
|
+
ConnectionMode,
|
|
13
|
+
} from '@xyflow/react';
|
|
14
|
+
import '@xyflow/react/dist/style.css';
|
|
15
|
+
import { TableNode, VisualizerTableSchema, VisualizerTableColumnSchema } from './TableNode';
|
|
16
|
+
import { AuthNode } from './AuthNode';
|
|
17
|
+
import { BucketNode } from './BucketNode';
|
|
18
|
+
import {
|
|
19
|
+
AppMetadataSchema,
|
|
20
|
+
StorageBucketSchema,
|
|
21
|
+
AuthMetadataSchema,
|
|
22
|
+
} from '@insforge/shared-schemas';
|
|
23
|
+
|
|
24
|
+
interface SchemaVisualizerProps {
|
|
25
|
+
metadata: AppMetadataSchema;
|
|
26
|
+
userCount?: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type TableNodeData = {
|
|
30
|
+
table: VisualizerTableSchema;
|
|
31
|
+
referencedColumns: string[];
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
type BucketNodeData = {
|
|
35
|
+
bucket: StorageBucketSchema;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
type AuthNodeData = {
|
|
39
|
+
authMetadata: AuthMetadataSchema;
|
|
40
|
+
userCount?: number;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
type CustomNodeData = TableNodeData | BucketNodeData | AuthNodeData;
|
|
44
|
+
|
|
45
|
+
const nodeTypes = {
|
|
46
|
+
tableNode: TableNode,
|
|
47
|
+
authNode: AuthNode,
|
|
48
|
+
bucketNode: BucketNode,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const getLayoutedElements = (nodes: Node<CustomNodeData>[], edges: BuiltInEdge[]) => {
|
|
52
|
+
// Fixed dimensions
|
|
53
|
+
const nodeWidth = 280;
|
|
54
|
+
|
|
55
|
+
// Calculate actual node heights based on content
|
|
56
|
+
const calculateNodeHeight = (node: Node<CustomNodeData>) => {
|
|
57
|
+
if (node.type === 'authNode') {
|
|
58
|
+
// Auth node has fixed content
|
|
59
|
+
return 150;
|
|
60
|
+
} else if (node.type === 'tableNode') {
|
|
61
|
+
// Table node height depends on columns
|
|
62
|
+
const tableData = node.data as TableNodeData;
|
|
63
|
+
const columnCount = tableData.table?.columns?.length || 0;
|
|
64
|
+
const headerHeight = 64; // Header with table name
|
|
65
|
+
const columnHeight = 48; // Each column row height
|
|
66
|
+
const contentHeight = columnCount > 0 ? columnCount * columnHeight : 100; // Empty state height
|
|
67
|
+
return headerHeight + contentHeight;
|
|
68
|
+
} else if (node.type === 'bucketNode') {
|
|
69
|
+
// Bucket node has relatively fixed height
|
|
70
|
+
return 200;
|
|
71
|
+
}
|
|
72
|
+
return 200; // Default
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Layout parameters
|
|
76
|
+
const horizontalGap = 100; // Gap between columns
|
|
77
|
+
const verticalGap = 80; // Gap between nodes in same column
|
|
78
|
+
const canvasMargin = 50;
|
|
79
|
+
|
|
80
|
+
// Group nodes by type
|
|
81
|
+
const authNodes = nodes.filter((node) => node.type === 'authNode');
|
|
82
|
+
const tableNodes = nodes.filter((node) => node.type === 'tableNode');
|
|
83
|
+
const bucketNodes = nodes.filter((node) => node.type === 'bucketNode');
|
|
84
|
+
|
|
85
|
+
// Calculate column X positions
|
|
86
|
+
const authX = canvasMargin;
|
|
87
|
+
const tableStartX = authX + nodeWidth + horizontalGap * 2;
|
|
88
|
+
const bucketX =
|
|
89
|
+
tableStartX +
|
|
90
|
+
Math.ceil(Math.sqrt(tableNodes.length)) * (nodeWidth + horizontalGap) +
|
|
91
|
+
horizontalGap;
|
|
92
|
+
|
|
93
|
+
// Helper function to distribute nodes vertically with dynamic heights
|
|
94
|
+
const distributeVerticallyDynamic = (
|
|
95
|
+
nodesToPosition: Node<CustomNodeData>[],
|
|
96
|
+
startY: number = canvasMargin
|
|
97
|
+
) => {
|
|
98
|
+
const positions: number[] = [];
|
|
99
|
+
let currentY = startY;
|
|
100
|
+
|
|
101
|
+
nodesToPosition.forEach((node) => {
|
|
102
|
+
positions.push(currentY);
|
|
103
|
+
const nodeHeight = calculateNodeHeight(node);
|
|
104
|
+
currentY += nodeHeight + verticalGap;
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
return positions;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Position auth nodes in left column
|
|
111
|
+
const authYPositions = distributeVerticallyDynamic(authNodes);
|
|
112
|
+
const positionedAuthNodes = authNodes.map((node, index) => ({
|
|
113
|
+
...node,
|
|
114
|
+
position: {
|
|
115
|
+
x: authX,
|
|
116
|
+
y: authYPositions[index],
|
|
117
|
+
},
|
|
118
|
+
}));
|
|
119
|
+
|
|
120
|
+
// Position table nodes in a grid in the middle
|
|
121
|
+
let positionedTableNodes: Node<CustomNodeData>[] = [];
|
|
122
|
+
if (tableNodes.length > 0) {
|
|
123
|
+
const cols = Math.ceil(Math.sqrt(tableNodes.length));
|
|
124
|
+
|
|
125
|
+
// Group tables by column for better height calculation
|
|
126
|
+
const tablesByColumn: Node<CustomNodeData>[][] = [];
|
|
127
|
+
for (let col = 0; col < cols; col++) {
|
|
128
|
+
tablesByColumn[col] = [];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
tableNodes.forEach((node, index) => {
|
|
132
|
+
const col = index % cols;
|
|
133
|
+
tablesByColumn[col].push(node);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Calculate Y positions for each column independently
|
|
137
|
+
const columnYPositions: number[][] = tablesByColumn.map((columnNodes) =>
|
|
138
|
+
distributeVerticallyDynamic(columnNodes)
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
positionedTableNodes = tableNodes.map((node, index) => {
|
|
142
|
+
const col = index % cols;
|
|
143
|
+
const rowInColumn = Math.floor(index / cols);
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
...node,
|
|
147
|
+
position: {
|
|
148
|
+
x: tableStartX + col * (nodeWidth + horizontalGap),
|
|
149
|
+
y: columnYPositions[col][rowInColumn],
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Position bucket nodes in right column
|
|
156
|
+
const bucketYPositions = distributeVerticallyDynamic(bucketNodes);
|
|
157
|
+
const positionedBucketNodes = bucketNodes.map((node, index) => ({
|
|
158
|
+
...node,
|
|
159
|
+
position: {
|
|
160
|
+
x: bucketX,
|
|
161
|
+
y: bucketYPositions[index],
|
|
162
|
+
},
|
|
163
|
+
}));
|
|
164
|
+
|
|
165
|
+
// Combine all positioned nodes
|
|
166
|
+
const layoutedNodes = [...positionedAuthNodes, ...positionedTableNodes, ...positionedBucketNodes];
|
|
167
|
+
|
|
168
|
+
return { nodes: layoutedNodes, edges };
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const getNodeColor = (node: Node<CustomNodeData>) => {
|
|
172
|
+
switch (node.type) {
|
|
173
|
+
case 'authNode':
|
|
174
|
+
return '#bef264';
|
|
175
|
+
case 'bucketNode':
|
|
176
|
+
return '#93c5fd';
|
|
177
|
+
default:
|
|
178
|
+
return '#6ee7b7';
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
export function SchemaVisualizer({ metadata, userCount }: SchemaVisualizerProps) {
|
|
183
|
+
// Transform the new metadata structure to the visualizer format
|
|
184
|
+
const tables = useMemo(() => {
|
|
185
|
+
const tablesRecord = metadata.database.tables;
|
|
186
|
+
return Object.entries(tablesRecord).map(([tableName, tableData]): VisualizerTableSchema => {
|
|
187
|
+
// Check for primary key columns from indexes
|
|
188
|
+
const primaryKeyColumns = new Set<string>();
|
|
189
|
+
|
|
190
|
+
tableData.indexes.forEach((index) => {
|
|
191
|
+
if (index.isPrimary) {
|
|
192
|
+
// Extract column names from index definition
|
|
193
|
+
const match = index.indexdef.match(/\(([^)]+)\)/);
|
|
194
|
+
if (match) {
|
|
195
|
+
match[1].split(',').forEach((col) => {
|
|
196
|
+
primaryKeyColumns.add(col.trim().replace(/"/g, ''));
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// Transform columns from the new schema format
|
|
203
|
+
const columns = tableData.schema.map((col) => {
|
|
204
|
+
const column: VisualizerTableColumnSchema = {
|
|
205
|
+
columnName: col.columnName,
|
|
206
|
+
type: col.dataType.toLowerCase().substring(0, 4), // Truncate type to first 4 characters
|
|
207
|
+
isPrimaryKey: primaryKeyColumns.has(col.columnName),
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// Find foreign key info for this column
|
|
211
|
+
const foreignKey = tableData.foreignKeys.find((fk) => fk.columnName === col.columnName);
|
|
212
|
+
if (foreignKey) {
|
|
213
|
+
column.foreignKey = {
|
|
214
|
+
referenceTable: foreignKey.foreignTableName,
|
|
215
|
+
referenceColumn: foreignKey.foreignColumnName,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return column;
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
return {
|
|
223
|
+
tableName,
|
|
224
|
+
columns,
|
|
225
|
+
recordCount: tableData.recordCount ?? 0, // Use actual record count from metadata
|
|
226
|
+
};
|
|
227
|
+
});
|
|
228
|
+
}, [metadata.database.tables]);
|
|
229
|
+
|
|
230
|
+
const initialNodes = useMemo(() => {
|
|
231
|
+
// First, collect all referenced columns for each table
|
|
232
|
+
const referencedColumnsByTable: Record<string, string[]> = {};
|
|
233
|
+
|
|
234
|
+
tables.forEach((table) => {
|
|
235
|
+
table.columns.forEach((column) => {
|
|
236
|
+
if (column.foreignKey) {
|
|
237
|
+
const targetTable = column.foreignKey.referenceTable;
|
|
238
|
+
const targetColumn = column.foreignKey.referenceColumn;
|
|
239
|
+
|
|
240
|
+
if (!referencedColumnsByTable[targetTable]) {
|
|
241
|
+
referencedColumnsByTable[targetTable] = [];
|
|
242
|
+
}
|
|
243
|
+
if (!referencedColumnsByTable[targetTable].includes(targetColumn)) {
|
|
244
|
+
referencedColumnsByTable[targetTable].push(targetColumn);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
const tableNodes: Node<TableNodeData>[] = tables.map((table) => ({
|
|
251
|
+
id: table.tableName,
|
|
252
|
+
type: 'tableNode',
|
|
253
|
+
position: { x: 0, y: 0 },
|
|
254
|
+
data: {
|
|
255
|
+
table,
|
|
256
|
+
referencedColumns: referencedColumnsByTable[table.tableName] || [],
|
|
257
|
+
},
|
|
258
|
+
}));
|
|
259
|
+
|
|
260
|
+
const bucketNodes: Node<BucketNodeData>[] = metadata.storage.buckets.map((bucket) => ({
|
|
261
|
+
id: `bucket-${bucket.name}`,
|
|
262
|
+
type: 'bucketNode',
|
|
263
|
+
position: { x: 0, y: 0 },
|
|
264
|
+
data: { bucket },
|
|
265
|
+
}));
|
|
266
|
+
|
|
267
|
+
const nodes: Node<CustomNodeData>[] = [...tableNodes, ...bucketNodes];
|
|
268
|
+
|
|
269
|
+
// Add authentication node if authData is provided
|
|
270
|
+
nodes.push({
|
|
271
|
+
id: 'authentication',
|
|
272
|
+
type: 'authNode',
|
|
273
|
+
position: { x: 0, y: 0 },
|
|
274
|
+
data: {
|
|
275
|
+
authMetadata: metadata.auth,
|
|
276
|
+
userCount,
|
|
277
|
+
},
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
return nodes;
|
|
281
|
+
}, [metadata, userCount, tables]);
|
|
282
|
+
|
|
283
|
+
const initialEdges = useMemo(() => {
|
|
284
|
+
const edges: BuiltInEdge[] = [];
|
|
285
|
+
|
|
286
|
+
tables.forEach((table) => {
|
|
287
|
+
table.columns.forEach((column) => {
|
|
288
|
+
if (column.foreignKey) {
|
|
289
|
+
const edgeId = `${table.tableName}-${column.columnName}-${column.foreignKey.referenceTable}`;
|
|
290
|
+
edges.push({
|
|
291
|
+
id: edgeId,
|
|
292
|
+
source: table.tableName,
|
|
293
|
+
target: column.foreignKey.referenceTable,
|
|
294
|
+
sourceHandle: `${column.columnName}-source`,
|
|
295
|
+
targetHandle: `${column.foreignKey.referenceColumn}-target`,
|
|
296
|
+
type: 'smoothstep',
|
|
297
|
+
animated: true,
|
|
298
|
+
style: { stroke: 'white', strokeWidth: 2, zIndex: 1000 },
|
|
299
|
+
zIndex: 1000,
|
|
300
|
+
pathOptions: {
|
|
301
|
+
offset: 40,
|
|
302
|
+
},
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
// Add authentication edges if authData exists
|
|
309
|
+
|
|
310
|
+
return edges;
|
|
311
|
+
}, [tables]);
|
|
312
|
+
|
|
313
|
+
const { nodes: layoutedNodes, edges: layoutedEdges } = useMemo(
|
|
314
|
+
() => getLayoutedElements(initialNodes, initialEdges),
|
|
315
|
+
[initialNodes, initialEdges]
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
const [nodes, setNodes, onNodesChange] = useNodesState(layoutedNodes);
|
|
319
|
+
const [edges, setEdges, onEdgesChange] = useEdgesState(layoutedEdges);
|
|
320
|
+
|
|
321
|
+
useEffect(() => {
|
|
322
|
+
setNodes(layoutedNodes);
|
|
323
|
+
setEdges(layoutedEdges);
|
|
324
|
+
}, [layoutedNodes, layoutedEdges, setNodes, setEdges]);
|
|
325
|
+
|
|
326
|
+
const onConnect = useCallback(
|
|
327
|
+
(params: Connection) => setEdges((eds) => addEdge(params, eds)),
|
|
328
|
+
[setEdges]
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
return (
|
|
332
|
+
<div className="w-full h-full">
|
|
333
|
+
<ReactFlow
|
|
334
|
+
nodes={nodes}
|
|
335
|
+
edges={edges}
|
|
336
|
+
onNodesChange={onNodesChange}
|
|
337
|
+
onEdgesChange={onEdgesChange}
|
|
338
|
+
onConnect={onConnect}
|
|
339
|
+
nodeTypes={nodeTypes}
|
|
340
|
+
connectionMode={ConnectionMode.Loose}
|
|
341
|
+
fitView
|
|
342
|
+
fitViewOptions={{ padding: 1, maxZoom: 2, minZoom: 1 }}
|
|
343
|
+
minZoom={0.1}
|
|
344
|
+
maxZoom={2}
|
|
345
|
+
proOptions={{ hideAttribution: true }}
|
|
346
|
+
elevateEdgesOnSelect={true}
|
|
347
|
+
colorMode="dark"
|
|
348
|
+
className="!bg-transparent"
|
|
349
|
+
>
|
|
350
|
+
<Controls
|
|
351
|
+
showInteractive={false}
|
|
352
|
+
className="!border !border-neutral-700 !shadow-lg"
|
|
353
|
+
fitViewOptions={{ padding: 1, duration: 300, maxZoom: 2, minZoom: 1 }}
|
|
354
|
+
/>
|
|
355
|
+
<MiniMap nodeColor={(node: Node<CustomNodeData>) => getNodeColor(node)} />
|
|
356
|
+
</ReactFlow>
|
|
357
|
+
</div>
|
|
358
|
+
);
|
|
359
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { Database, Circle, Key } from 'lucide-react';
|
|
2
|
+
import { Handle, Position } from '@xyflow/react';
|
|
3
|
+
|
|
4
|
+
// Define the table structure expected by this component
|
|
5
|
+
export interface VisualizerTableColumnSchema {
|
|
6
|
+
columnName: string;
|
|
7
|
+
type: string;
|
|
8
|
+
isPrimaryKey?: boolean;
|
|
9
|
+
foreignKey?: {
|
|
10
|
+
referenceTable: string;
|
|
11
|
+
referenceColumn: string;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface VisualizerTableSchema {
|
|
16
|
+
tableName: string;
|
|
17
|
+
columns: VisualizerTableColumnSchema[];
|
|
18
|
+
recordCount?: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface TableNodeProps {
|
|
22
|
+
data: {
|
|
23
|
+
table: VisualizerTableSchema;
|
|
24
|
+
referencedColumns?: string[]; // List of column names that are referenced by other tables
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function TableNode({ data }: TableNodeProps) {
|
|
29
|
+
const { table, referencedColumns = [] } = data;
|
|
30
|
+
|
|
31
|
+
const getColumnIcon = (isReferenced: boolean = false) => {
|
|
32
|
+
// If column is referenced by another table (has incoming connections)
|
|
33
|
+
// Show outer gray diamond with inner white diamond
|
|
34
|
+
if (isReferenced) {
|
|
35
|
+
return (
|
|
36
|
+
<div className="w-4 h-4 flex items-center justify-center relative">
|
|
37
|
+
{/* Outer gray diamond */}
|
|
38
|
+
<div className="w-4 h-4 bg-neutral-800 border border-white absolute transform rotate-45" />
|
|
39
|
+
{/* Inner white diamond */}
|
|
40
|
+
<div className="w-2 h-2 bg-white absolute transform rotate-45" />
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
return (
|
|
45
|
+
<div className="w-4 h-4 flex items-center justify-center relative">
|
|
46
|
+
{/* Outer gray diamond */}
|
|
47
|
+
<div className="w-4 h-4 bg-neutral-800 border border-neutral-700 absolute transform rotate-45" />
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<div className="bg-neutral-900 rounded-lg border border-[#363636] min-w-[320px]">
|
|
54
|
+
{/* Table Header */}
|
|
55
|
+
<div className="flex items-center justify-between p-2 border-b border-neutral-800">
|
|
56
|
+
<div className="flex items-center gap-2">
|
|
57
|
+
<div className="flex items-center justify-center w-11 h-11 bg-teal-300 rounded p-1.5">
|
|
58
|
+
<Database className="w-5 h-5 text-neutral-900" />
|
|
59
|
+
</div>
|
|
60
|
+
<div className="flex-1">
|
|
61
|
+
<h3 className="text-sm font-medium text-white">{table.tableName}</h3>
|
|
62
|
+
<p className="text-xs text-neutral-300">
|
|
63
|
+
{table.recordCount !== undefined
|
|
64
|
+
? `${table.recordCount.toLocaleString()} data`
|
|
65
|
+
: '0 data'}
|
|
66
|
+
</p>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
{/* <div className="p-1.5">
|
|
70
|
+
<ExternalLink className="w-4 h-4 text-neutral-400" />
|
|
71
|
+
</div> */}
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
{/* Columns */}
|
|
75
|
+
<div>
|
|
76
|
+
{table.columns.map((column) => (
|
|
77
|
+
<div
|
|
78
|
+
key={column.columnName}
|
|
79
|
+
className="flex items-center justify-between p-3 border-b border-neutral-800 relative"
|
|
80
|
+
>
|
|
81
|
+
{/* Source handle for foreign key columns - invisible and non-interactive */}
|
|
82
|
+
{column.foreignKey && (
|
|
83
|
+
<Handle
|
|
84
|
+
type="source"
|
|
85
|
+
position={Position.Right}
|
|
86
|
+
id={`${column.columnName}-source`}
|
|
87
|
+
className="!w-3 !h-3 !opacity-0 !border-0 !pointer-events-none"
|
|
88
|
+
style={{
|
|
89
|
+
right: 16,
|
|
90
|
+
top: '50%',
|
|
91
|
+
transform: 'translateY(-50%)',
|
|
92
|
+
pointerEvents: 'none',
|
|
93
|
+
}}
|
|
94
|
+
isConnectable={false}
|
|
95
|
+
/>
|
|
96
|
+
)}
|
|
97
|
+
|
|
98
|
+
{/* Target handle for columns that can be referenced - invisible and non-interactive */}
|
|
99
|
+
{referencedColumns.includes(column.columnName) && (
|
|
100
|
+
<Handle
|
|
101
|
+
type="target"
|
|
102
|
+
position={Position.Left}
|
|
103
|
+
id={`${column.columnName}-target`}
|
|
104
|
+
className="!w-3 !h-3 !opacity-0 !border-0 !pointer-events-none"
|
|
105
|
+
style={{
|
|
106
|
+
left: 16,
|
|
107
|
+
top: '50%',
|
|
108
|
+
transform: 'translateY(-50%)',
|
|
109
|
+
pointerEvents: 'none',
|
|
110
|
+
}}
|
|
111
|
+
isConnectable={false}
|
|
112
|
+
/>
|
|
113
|
+
)}
|
|
114
|
+
|
|
115
|
+
<div className="flex items-center gap-2.5 flex-1">
|
|
116
|
+
{getColumnIcon(referencedColumns.includes(column.columnName))}
|
|
117
|
+
<span className="text-sm text-neutral-300">{column.columnName}</span>
|
|
118
|
+
{column.isPrimaryKey && <Key className="w-3 h-3 text-neutral-400" />}
|
|
119
|
+
</div>
|
|
120
|
+
<div className="flex items-center gap-2.5">
|
|
121
|
+
<div className="px-1.5 py-0.5 bg-neutral-800 rounded flex items-center">
|
|
122
|
+
<span className="text-xs font-medium text-neutral-300">{column.type}</span>
|
|
123
|
+
</div>
|
|
124
|
+
{/* Show white dot with outer circle for foreign key columns, gray circle for others */}
|
|
125
|
+
{column.foreignKey ? (
|
|
126
|
+
<div className="w-5 h-5 flex items-center justify-center relative">
|
|
127
|
+
<Circle
|
|
128
|
+
className="w-5 h-5 text-white fill-none stroke-current"
|
|
129
|
+
strokeWidth={1.5}
|
|
130
|
+
/>
|
|
131
|
+
<div className="w-2 h-2 bg-white rounded-full absolute" />
|
|
132
|
+
</div>
|
|
133
|
+
) : (
|
|
134
|
+
<Circle className="w-5 h-5 text-neutral-700 fill-neutral-800 stroke-current" />
|
|
135
|
+
)}
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
))}
|
|
139
|
+
|
|
140
|
+
{/* Empty state */}
|
|
141
|
+
{table.columns.length === 0 && (
|
|
142
|
+
<div className="flex items-center justify-center p-6">
|
|
143
|
+
<div className="text-center">
|
|
144
|
+
<Database className="w-6 h-6 text-neutral-600 mx-auto mb-2" />
|
|
145
|
+
<p className="text-xs text-neutral-500">No columns defined</p>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
)}
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export function VisualizerSkeleton() {
|
|
2
|
+
return (
|
|
3
|
+
<div className="relative min-h-screen bg-neutral-800 overflow-hidden">
|
|
4
|
+
{/* Dot Matrix Background */}
|
|
5
|
+
<div
|
|
6
|
+
className="absolute inset-0 opacity-50"
|
|
7
|
+
style={{
|
|
8
|
+
backgroundImage: `radial-gradient(circle, #3B3B3B 1px, transparent 1px)`,
|
|
9
|
+
backgroundSize: '12px 12px',
|
|
10
|
+
}}
|
|
11
|
+
/>
|
|
12
|
+
|
|
13
|
+
{/* Loading content */}
|
|
14
|
+
<div className="relative z-10 flex items-center justify-center min-h-screen">
|
|
15
|
+
<div className="text-center">
|
|
16
|
+
<div className="inline-flex items-center gap-3 text-white">
|
|
17
|
+
<div className="w-8 h-8 border-4 border-blue-500 border-t-transparent rounded-full animate-spin" />
|
|
18
|
+
<span className="text-lg font-medium">Loading schema visualization...</span>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
}
|