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,313 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Clock, ChevronRight, ChevronDown, ArrowDown } from 'lucide-react';
|
|
3
|
+
import { AnalyticsLogRecord } from '@/features/logs/services/log.service';
|
|
4
|
+
import { Badge } from '@/components/radix/Badge';
|
|
5
|
+
import { JsonHighlight } from '@/components/JsonHighlight';
|
|
6
|
+
import { cn } from '@/lib/utils/utils';
|
|
7
|
+
|
|
8
|
+
interface AnalyticsLogsTableProps {
|
|
9
|
+
logs: AnalyticsLogRecord[];
|
|
10
|
+
loading: boolean;
|
|
11
|
+
source: string;
|
|
12
|
+
onRefresh: () => void;
|
|
13
|
+
showSource?: boolean;
|
|
14
|
+
onScroll: (e: React.UIEvent<HTMLDivElement>) => void;
|
|
15
|
+
scrollRef: React.RefObject<HTMLDivElement | null>;
|
|
16
|
+
hasMore: boolean;
|
|
17
|
+
isLoadingMore: boolean;
|
|
18
|
+
autoRefresh: boolean;
|
|
19
|
+
onScrollToBottom: () => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function AnalyticsLogsTable({
|
|
23
|
+
logs,
|
|
24
|
+
loading,
|
|
25
|
+
source,
|
|
26
|
+
onRefresh: _onRefresh,
|
|
27
|
+
showSource = false,
|
|
28
|
+
onScroll,
|
|
29
|
+
scrollRef,
|
|
30
|
+
hasMore,
|
|
31
|
+
isLoadingMore,
|
|
32
|
+
autoRefresh,
|
|
33
|
+
onScrollToBottom,
|
|
34
|
+
}: AnalyticsLogsTableProps) {
|
|
35
|
+
const [expandedRows, setExpandedRows] = useState<Set<string>>(new Set());
|
|
36
|
+
|
|
37
|
+
const formatTimestamp = (timestamp: string) => {
|
|
38
|
+
const date = new Date(timestamp);
|
|
39
|
+
const months = [
|
|
40
|
+
'Jan',
|
|
41
|
+
'Feb',
|
|
42
|
+
'Mar',
|
|
43
|
+
'Apr',
|
|
44
|
+
'May',
|
|
45
|
+
'Jun',
|
|
46
|
+
'Jul',
|
|
47
|
+
'Aug',
|
|
48
|
+
'Sep',
|
|
49
|
+
'Oct',
|
|
50
|
+
'Nov',
|
|
51
|
+
'Dec',
|
|
52
|
+
];
|
|
53
|
+
const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
|
54
|
+
|
|
55
|
+
const weekday = days[date.getDay()];
|
|
56
|
+
const month = months[date.getMonth()];
|
|
57
|
+
const day = date.getDate();
|
|
58
|
+
const year = date.getFullYear();
|
|
59
|
+
const time = date.toLocaleTimeString('en-US', { hour12: false });
|
|
60
|
+
|
|
61
|
+
return `${weekday} ${month} ${day} ${year} ${time}`;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const toggleRowExpansion = (id: string) => {
|
|
65
|
+
const newExpanded = new Set(expandedRows);
|
|
66
|
+
if (newExpanded.has(id)) {
|
|
67
|
+
newExpanded.delete(id);
|
|
68
|
+
} else {
|
|
69
|
+
newExpanded.add(id);
|
|
70
|
+
}
|
|
71
|
+
setExpandedRows(newExpanded);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const getStatusCodeColor = (statusCode?: number) => {
|
|
75
|
+
if (!statusCode) {
|
|
76
|
+
return 'text-gray-600';
|
|
77
|
+
}
|
|
78
|
+
if (statusCode >= 200 && statusCode < 300) {
|
|
79
|
+
return 'text-green-600';
|
|
80
|
+
}
|
|
81
|
+
if (statusCode >= 400 && statusCode < 500) {
|
|
82
|
+
return 'text-yellow-600';
|
|
83
|
+
}
|
|
84
|
+
if (statusCode >= 500) {
|
|
85
|
+
return 'text-red-600';
|
|
86
|
+
}
|
|
87
|
+
return 'text-gray-600';
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
if (loading) {
|
|
91
|
+
return (
|
|
92
|
+
<div className="flex-1 flex items-center justify-center">
|
|
93
|
+
<div className="text-center">
|
|
94
|
+
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600 dark:border-blue-400 mx-auto mb-4" />
|
|
95
|
+
<p className="text-sm text-gray-500 dark:text-zinc-400">Loading logs...</p>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!logs?.length) {
|
|
102
|
+
return (
|
|
103
|
+
<div className="flex-1 flex items-center justify-center">
|
|
104
|
+
<div className="text-center">
|
|
105
|
+
<Clock className="mx-auto h-12 w-12 text-gray-400 mb-4" />
|
|
106
|
+
<h3 className="text-lg font-medium text-gray-900 dark:text-white mb-2">No Logs Found</h3>
|
|
107
|
+
<p className="text-sm text-gray-500 dark:text-zinc-400">
|
|
108
|
+
{source === 'search'
|
|
109
|
+
? 'No logs match your search criteria'
|
|
110
|
+
: `No logs available for ${source}`}
|
|
111
|
+
</p>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<div className="flex-1 flex flex-col overflow-hidden bg-white dark:bg-neutral-800">
|
|
119
|
+
{/* Status bar */}
|
|
120
|
+
<div className="px-4 py-2 bg-gray-50 dark:bg-neutral-800 border-b text-xs text-gray-600 dark:text-zinc-400 flex justify-between items-center">
|
|
121
|
+
<div className="flex items-center space-x-4">
|
|
122
|
+
<span className="text-zinc-900 dark:text-zinc-400">{logs.length} logs loaded</span>
|
|
123
|
+
{hasMore && (
|
|
124
|
+
<div className="flex items-center space-x-2">
|
|
125
|
+
<button
|
|
126
|
+
onClick={onScrollToBottom}
|
|
127
|
+
className="flex items-center space-x-1 text-blue-600 dark:text-blue-400 hover:text-blue-800 hover:bg-blue-300 dark:hover:bg-blue-600/50 px-2 py-1 rounded transition-colors"
|
|
128
|
+
title="Go to bottom"
|
|
129
|
+
>
|
|
130
|
+
<ArrowDown className="h-3 w-3 text-zinc-900 dark:text-zinc-400" />
|
|
131
|
+
<span>Go to bottom</span>
|
|
132
|
+
</button>
|
|
133
|
+
<span className="text-zinc-900 dark:text-zinc-400">•</span>
|
|
134
|
+
<span className="text-blue-600 dark:text-blue-400">Scroll up to load older logs</span>
|
|
135
|
+
</div>
|
|
136
|
+
)}
|
|
137
|
+
{autoRefresh && (
|
|
138
|
+
<span className="text-green-600 dark:text-green-400 flex items-center">
|
|
139
|
+
<div className="w-2 h-2 bg-green-500 rounded-full mr-1 animate-pulse" />
|
|
140
|
+
Auto-refreshing
|
|
141
|
+
</span>
|
|
142
|
+
)}
|
|
143
|
+
</div>
|
|
144
|
+
{isLoadingMore && (
|
|
145
|
+
<span className="text-gray-500 dark:text-zinc-400">Loading older logs...</span>
|
|
146
|
+
)}
|
|
147
|
+
</div>
|
|
148
|
+
|
|
149
|
+
{/* Logs List - Scrollable with infinite scroll */}
|
|
150
|
+
<div ref={scrollRef} className="flex-1 overflow-auto" onScroll={onScroll}>
|
|
151
|
+
<div className="font-mono text-sm">
|
|
152
|
+
{/* Loading indicator at top for older logs */}
|
|
153
|
+
{isLoadingMore && (
|
|
154
|
+
<div className="px-4 py-2 text-center text-gray-500 dark:text-zinc-400 text-xs bg-gray-50 dark:bg-neutral-700 border-b dark:border-neutral-700">
|
|
155
|
+
<div className="inline-flex items-center">
|
|
156
|
+
<div className="animate-spin rounded-full h-3 w-3 border-b-2 border-gray-400 dark:border-gray-400 mr-2" />
|
|
157
|
+
Loading older logs...
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
)}
|
|
161
|
+
|
|
162
|
+
{logs.map((log, index) => {
|
|
163
|
+
const uniqueKey = `${log.id}-${log.timestamp}-${index}`;
|
|
164
|
+
const isExpanded = expandedRows.has(log.id);
|
|
165
|
+
const hasDetails = log.body && Object.keys(log.body).length > 0;
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<div
|
|
169
|
+
key={uniqueKey}
|
|
170
|
+
className="border-b border-gray-100 dark:border-neutral-700 hover:bg-gray-50 dark:hover:bg-neutral-700 dark:hover:text-white"
|
|
171
|
+
>
|
|
172
|
+
<div
|
|
173
|
+
className={cn(
|
|
174
|
+
'flex items-start px-4 py-2',
|
|
175
|
+
hasDetails ? 'cursor-pointer' : 'cursor-default'
|
|
176
|
+
)}
|
|
177
|
+
onClick={() => hasDetails && toggleRowExpansion(log.id)}
|
|
178
|
+
>
|
|
179
|
+
{/* Timestamp column - gray background */}
|
|
180
|
+
<div className="flex-shrink-0 w-48 bg-gray-100 dark:bg-neutral-600 px-3 py-1 rounded mr-3 text-gray-700 dark:text-white text-xs">
|
|
181
|
+
{formatTimestamp(log.timestamp)}
|
|
182
|
+
</div>
|
|
183
|
+
|
|
184
|
+
{/* Message content */}
|
|
185
|
+
<div className="flex-1 min-w-0">
|
|
186
|
+
<div className="flex items-center space-x-2">
|
|
187
|
+
{/* Expand/collapse icon */}
|
|
188
|
+
{hasDetails && (
|
|
189
|
+
<div className="flex-shrink-0">
|
|
190
|
+
{isExpanded ? (
|
|
191
|
+
<ChevronDown className="h-3 w-3 text-zinc-900 dark:text-zinc-400" />
|
|
192
|
+
) : (
|
|
193
|
+
<ChevronRight className="h-3 w-3 text-zinc-900 dark:text-zinc-400" />
|
|
194
|
+
)}
|
|
195
|
+
</div>
|
|
196
|
+
)}
|
|
197
|
+
|
|
198
|
+
{/* Log message */}
|
|
199
|
+
<span className="text-gray-900 dark:text-white break-all">
|
|
200
|
+
{log.event_message}
|
|
201
|
+
</span>
|
|
202
|
+
|
|
203
|
+
{/* Status code badge */}
|
|
204
|
+
{log.body?.status_code && (
|
|
205
|
+
<Badge
|
|
206
|
+
variant="outline"
|
|
207
|
+
className={cn(
|
|
208
|
+
'text-xs border-0',
|
|
209
|
+
getStatusCodeColor(log.body.status_code)
|
|
210
|
+
)}
|
|
211
|
+
>
|
|
212
|
+
{log.body.status_code}
|
|
213
|
+
</Badge>
|
|
214
|
+
)}
|
|
215
|
+
|
|
216
|
+
{/* Source name for search results */}
|
|
217
|
+
{showSource && log.source && (
|
|
218
|
+
<span className="text-blue-600 dark:text-blue-400 text-xs font-medium bg-blue-50 dark:bg-blue-900 px-2 py-1 rounded">
|
|
219
|
+
{log.source}
|
|
220
|
+
</span>
|
|
221
|
+
)}
|
|
222
|
+
|
|
223
|
+
{/* Duration */}
|
|
224
|
+
{log.body?.duration && (
|
|
225
|
+
<span className="text-gray-600 dark:text-zinc-400 text-xs bg-gray-50 dark:bg-neutral-700 px-2 py-1 rounded">
|
|
226
|
+
{log.body.duration}
|
|
227
|
+
</span>
|
|
228
|
+
)}
|
|
229
|
+
|
|
230
|
+
{/* Size */}
|
|
231
|
+
{log.body?.size && (
|
|
232
|
+
<span className="text-gray-500 dark:text-zinc-400 text-xs">
|
|
233
|
+
{log.body.size}b
|
|
234
|
+
</span>
|
|
235
|
+
)}
|
|
236
|
+
</div>
|
|
237
|
+
</div>
|
|
238
|
+
</div>
|
|
239
|
+
|
|
240
|
+
{/* Expanded details */}
|
|
241
|
+
{isExpanded && hasDetails && (
|
|
242
|
+
<div className="px-4 pb-3 bg-gray-50 dark:bg-neutral-800 dark:hover:bg-neutral-700 border-t border-gray-200 dark:border-neutral-700">
|
|
243
|
+
<div className="ml-48 pl-3">
|
|
244
|
+
{/* Request details */}
|
|
245
|
+
{log.body?.log_type === 'request' && (
|
|
246
|
+
<div className="space-y-1 text-xs text-gray-600 dark:text-zinc-400 mb-3">
|
|
247
|
+
{log.body.ip && (
|
|
248
|
+
<div>
|
|
249
|
+
<span className="font-medium text-gray-700 dark:text-white">IP:</span>{' '}
|
|
250
|
+
{log.body.ip}
|
|
251
|
+
</div>
|
|
252
|
+
)}
|
|
253
|
+
{log.body.user_agent && (
|
|
254
|
+
<div>
|
|
255
|
+
<span className="font-medium text-gray-700 dark:text-white">
|
|
256
|
+
User Agent:
|
|
257
|
+
</span>{' '}
|
|
258
|
+
{log.body.user_agent}
|
|
259
|
+
</div>
|
|
260
|
+
)}
|
|
261
|
+
{log.body.path && (
|
|
262
|
+
<div>
|
|
263
|
+
<span className="font-medium text-gray-700 dark:text-white">
|
|
264
|
+
Path:
|
|
265
|
+
</span>{' '}
|
|
266
|
+
{log.body.path}
|
|
267
|
+
</div>
|
|
268
|
+
)}
|
|
269
|
+
</div>
|
|
270
|
+
)}
|
|
271
|
+
|
|
272
|
+
{/* Error details */}
|
|
273
|
+
{log.body?.error && (
|
|
274
|
+
<div className="mb-3 p-2 bg-red-50 dark:bg-red-900 border border-red-200 dark:border-red-800 rounded text-xs">
|
|
275
|
+
<div className="text-red-800 dark:text-red-200 font-medium mb-1">
|
|
276
|
+
Error:
|
|
277
|
+
</div>
|
|
278
|
+
<div className="text-red-700 dark:text-red-300">{log.body.error}</div>
|
|
279
|
+
{log.body.stack && (
|
|
280
|
+
<pre className="mt-2 text-red-600 dark:text-red-400 text-xs whitespace-pre-wrap overflow-x-auto">
|
|
281
|
+
{log.body.stack}
|
|
282
|
+
</pre>
|
|
283
|
+
)}
|
|
284
|
+
</div>
|
|
285
|
+
)}
|
|
286
|
+
|
|
287
|
+
{/* Full JSON body */}
|
|
288
|
+
<details className="text-xs">
|
|
289
|
+
<summary className="cursor-pointer text-zinc-600 dark:text-zinc-400 hover:text-zinc-800 font-medium mb-2">
|
|
290
|
+
Event Body
|
|
291
|
+
</summary>
|
|
292
|
+
<div className="bg-white dark:bg-neutral-700 p-2 rounded border overflow-x-auto">
|
|
293
|
+
<JsonHighlight json={JSON.stringify(log.body, null, 2)} />
|
|
294
|
+
</div>
|
|
295
|
+
</details>
|
|
296
|
+
</div>
|
|
297
|
+
</div>
|
|
298
|
+
)}
|
|
299
|
+
</div>
|
|
300
|
+
);
|
|
301
|
+
})}
|
|
302
|
+
|
|
303
|
+
{/* End indicator */}
|
|
304
|
+
{!hasMore && logs.length > 0 && (
|
|
305
|
+
<div className="px-4 py-3 text-center text-gray-500 dark:text-zinc-400 text-xs bg-gray-50 dark:bg-neutral-700">
|
|
306
|
+
No more logs to load
|
|
307
|
+
</div>
|
|
308
|
+
)}
|
|
309
|
+
</div>
|
|
310
|
+
</div>
|
|
311
|
+
</div>
|
|
312
|
+
);
|
|
313
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import React, { useState, useMemo } from 'react';
|
|
2
|
+
import { ChevronRight, ChevronDown } from 'lucide-react';
|
|
3
|
+
import { Button } from '@/components/radix/Button';
|
|
4
|
+
import { PaginationControls } from '@/components/PaginationControls';
|
|
5
|
+
import type { AuditLogSchema } from '@insforge/shared-schemas';
|
|
6
|
+
|
|
7
|
+
interface LogsTableProps {
|
|
8
|
+
logs: AuditLogSchema[];
|
|
9
|
+
schema?: Record<string, unknown>;
|
|
10
|
+
loading?: boolean;
|
|
11
|
+
searchQuery?: string;
|
|
12
|
+
onRefresh?: () => void;
|
|
13
|
+
onConfirm?: (options: {
|
|
14
|
+
title: string;
|
|
15
|
+
description: string;
|
|
16
|
+
confirmText?: string;
|
|
17
|
+
destructive?: boolean;
|
|
18
|
+
}) => Promise<boolean>;
|
|
19
|
+
currentPage?: number;
|
|
20
|
+
totalRecords?: number;
|
|
21
|
+
pageSize?: number;
|
|
22
|
+
onPageChange?: (page: number) => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function LogsTable({
|
|
26
|
+
logs,
|
|
27
|
+
loading,
|
|
28
|
+
searchQuery = '',
|
|
29
|
+
currentPage = 1,
|
|
30
|
+
totalRecords = 0,
|
|
31
|
+
pageSize = 50,
|
|
32
|
+
onPageChange,
|
|
33
|
+
}: LogsTableProps) {
|
|
34
|
+
const [expandedRows, setExpandedRows] = useState<Set<string>>(new Set());
|
|
35
|
+
|
|
36
|
+
// Filter logs based on search (client-side filtering on current page)
|
|
37
|
+
const filteredLogs = useMemo(() => {
|
|
38
|
+
if (!searchQuery || !logs) {
|
|
39
|
+
return logs || [];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const lowerQuery = searchQuery.toLowerCase();
|
|
43
|
+
return logs.filter(
|
|
44
|
+
(log) =>
|
|
45
|
+
log.action?.toLowerCase().includes(lowerQuery) ||
|
|
46
|
+
log.actor?.toLowerCase().includes(lowerQuery) ||
|
|
47
|
+
log.module?.toLowerCase().includes(lowerQuery) ||
|
|
48
|
+
(log.details && JSON.stringify(log.details).toLowerCase().includes(lowerQuery))
|
|
49
|
+
);
|
|
50
|
+
}, [logs, searchQuery]);
|
|
51
|
+
|
|
52
|
+
// Calculate total pages
|
|
53
|
+
const totalPages = Math.ceil(totalRecords / pageSize);
|
|
54
|
+
|
|
55
|
+
const toggleRowExpansion = (id: string) => {
|
|
56
|
+
const newExpanded = new Set(expandedRows);
|
|
57
|
+
if (newExpanded.has(id)) {
|
|
58
|
+
newExpanded.delete(id);
|
|
59
|
+
} else {
|
|
60
|
+
newExpanded.add(id);
|
|
61
|
+
}
|
|
62
|
+
setExpandedRows(newExpanded);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Logs are audit records and should not be individually deletable
|
|
66
|
+
// They can only be cleared in bulk by admins if needed
|
|
67
|
+
|
|
68
|
+
const formatDate = (dateString: string) => {
|
|
69
|
+
const date = new Date(dateString);
|
|
70
|
+
return new Intl.DateTimeFormat('en-US', {
|
|
71
|
+
month: 'short',
|
|
72
|
+
day: 'numeric',
|
|
73
|
+
year: 'numeric',
|
|
74
|
+
hour: '2-digit',
|
|
75
|
+
minute: '2-digit',
|
|
76
|
+
}).format(date);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
if (loading) {
|
|
80
|
+
return (
|
|
81
|
+
<div className="flex-1 flex items-center justify-center">
|
|
82
|
+
<div className="text-gray-500 dark:text-neutral-400">Loading logs...</div>
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<div className="relative flex-1 flex flex-col overflow-hidden">
|
|
89
|
+
<div className="flex-1 overflow-auto relative">
|
|
90
|
+
<table className="w-full bg-white dark:bg-neutral-900">
|
|
91
|
+
<thead className="sticky top-0 z-20">
|
|
92
|
+
<tr className="h-12.5 bg-gray-50 dark:bg-neutral-800 border-b border-gray-200 dark:border-neutral-700">
|
|
93
|
+
<th className="px-4 text-left text-[13px] font-semibold text-gray-600 dark:text-neutral-400 min-w-45">
|
|
94
|
+
Time
|
|
95
|
+
</th>
|
|
96
|
+
<th className="px-4 text-left text-[13px] font-semibold text-gray-600 dark:text-neutral-400 min-w-30">
|
|
97
|
+
Actor
|
|
98
|
+
</th>
|
|
99
|
+
<th className="px-4 text-left text-[13px] font-semibold text-gray-600 dark:text-neutral-400 min-w-25">
|
|
100
|
+
Action
|
|
101
|
+
</th>
|
|
102
|
+
<th className="px-4 text-left text-[13px] font-semibold text-gray-600 dark:text-neutral-400 min-w-30">
|
|
103
|
+
Module
|
|
104
|
+
</th>
|
|
105
|
+
<th className="px-4 text-left text-[13px] font-semibold text-gray-600 dark:text-neutral-400 min-w-37.5">
|
|
106
|
+
Details
|
|
107
|
+
</th>
|
|
108
|
+
<th className="px-4 text-left text-[13px] font-semibold text-gray-600 dark:text-neutral-400 min-w-30">
|
|
109
|
+
IP Address
|
|
110
|
+
</th>
|
|
111
|
+
</tr>
|
|
112
|
+
</thead>
|
|
113
|
+
<tbody>
|
|
114
|
+
{filteredLogs.length === 0 ? (
|
|
115
|
+
<tr>
|
|
116
|
+
<td
|
|
117
|
+
colSpan={6}
|
|
118
|
+
className="px-4 py-12 text-center text-gray-500 dark:text-neutral-400"
|
|
119
|
+
>
|
|
120
|
+
{searchQuery ? 'No logs found matching your search' : 'No audit logs yet'}
|
|
121
|
+
</td>
|
|
122
|
+
</tr>
|
|
123
|
+
) : (
|
|
124
|
+
filteredLogs.map((log) => {
|
|
125
|
+
const isExpanded = expandedRows.has(String(log.id));
|
|
126
|
+
const details = log.details;
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<React.Fragment key={log.id}>
|
|
130
|
+
<tr className="h-14 border-b border-gray-100 dark:border-neutral-800 transition-colors hover:bg-gray-50/50 dark:hover:bg-neutral-800/50">
|
|
131
|
+
<td className="px-4 text-[13px] text-gray-600 dark:text-neutral-400">
|
|
132
|
+
{formatDate(log.createdAt)}
|
|
133
|
+
</td>
|
|
134
|
+
<td className="px-4 text-sm font-medium text-gray-800 dark:text-neutral-200">
|
|
135
|
+
{log.actor}
|
|
136
|
+
</td>
|
|
137
|
+
<td className="px-4 text-gray-700 dark:text-neutral-300">{log.action}</td>
|
|
138
|
+
<td className="px-4 text-sm text-gray-700 dark:text-neutral-300">
|
|
139
|
+
{log.module}
|
|
140
|
+
</td>
|
|
141
|
+
<td className="px-4">
|
|
142
|
+
{details && typeof details === 'object' ? (
|
|
143
|
+
<Button
|
|
144
|
+
variant="ghost"
|
|
145
|
+
size="sm"
|
|
146
|
+
onClick={() => toggleRowExpansion(String(log.id))}
|
|
147
|
+
className="h-8 px-3 text-[13px] text-gray-700 dark:text-neutral-300 hover:bg-gray-100 dark:hover:bg-neutral-700"
|
|
148
|
+
>
|
|
149
|
+
{isExpanded ? (
|
|
150
|
+
<ChevronDown className="h-3.5 w-3.5 mr-1 text-gray-600 dark:text-neutral-400" />
|
|
151
|
+
) : (
|
|
152
|
+
<ChevronRight className="h-3.5 w-3.5 mr-1 text-gray-600 dark:text-neutral-400" />
|
|
153
|
+
)}
|
|
154
|
+
View Details
|
|
155
|
+
</Button>
|
|
156
|
+
) : (
|
|
157
|
+
<span className="text-[13px] text-gray-500 dark:text-neutral-500">-</span>
|
|
158
|
+
)}
|
|
159
|
+
</td>
|
|
160
|
+
<td className="px-4">
|
|
161
|
+
<span className="font-mono text-[12px] text-gray-500 dark:text-neutral-500">
|
|
162
|
+
{log.ipAddress || '-'}
|
|
163
|
+
</span>
|
|
164
|
+
</td>
|
|
165
|
+
</tr>
|
|
166
|
+
{isExpanded && details && typeof details === 'object' && (
|
|
167
|
+
<tr>
|
|
168
|
+
<td
|
|
169
|
+
colSpan={6}
|
|
170
|
+
className="px-4 py-3 bg-gray-50 dark:bg-neutral-800 border-b border-gray-100 dark:border-neutral-700"
|
|
171
|
+
>
|
|
172
|
+
<pre className="text-xs text-gray-700 dark:text-neutral-300 whitespace-pre-wrap font-mono bg-white dark:bg-neutral-900 p-3 rounded border border-border-gray dark:border-neutral-700">
|
|
173
|
+
{JSON.stringify(details, null, 2)}
|
|
174
|
+
</pre>
|
|
175
|
+
</td>
|
|
176
|
+
</tr>
|
|
177
|
+
)}
|
|
178
|
+
</React.Fragment>
|
|
179
|
+
);
|
|
180
|
+
})
|
|
181
|
+
)}
|
|
182
|
+
</tbody>
|
|
183
|
+
</table>
|
|
184
|
+
</div>
|
|
185
|
+
|
|
186
|
+
{/* Pagination */}
|
|
187
|
+
{!loading && totalRecords > pageSize && (
|
|
188
|
+
<PaginationControls
|
|
189
|
+
currentPage={currentPage}
|
|
190
|
+
totalPages={totalPages}
|
|
191
|
+
totalRecords={totalRecords}
|
|
192
|
+
pageSize={pageSize}
|
|
193
|
+
onPageChange={onPageChange}
|
|
194
|
+
recordLabel="logs"
|
|
195
|
+
/>
|
|
196
|
+
)}
|
|
197
|
+
</div>
|
|
198
|
+
);
|
|
199
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
2
|
+
import { logsService } from '../services/log.service';
|
|
3
|
+
import { GetAuditLogsRequest } from '@insforge/shared-schemas';
|
|
4
|
+
import { useToast } from '@/lib/hooks/useToast';
|
|
5
|
+
|
|
6
|
+
export const useAuditLogs = (filters?: Partial<GetAuditLogsRequest>) => {
|
|
7
|
+
return useQuery({
|
|
8
|
+
queryKey: ['audit-logs', filters],
|
|
9
|
+
queryFn: () => logsService.getAuditLogs({ limit: 100, offset: 0, ...filters }),
|
|
10
|
+
staleTime: 30 * 1000,
|
|
11
|
+
gcTime: 5 * 60 * 1000,
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const useAuditLogStats = (days = 7) => {
|
|
16
|
+
return useQuery({
|
|
17
|
+
queryKey: ['audit-log-stats', days],
|
|
18
|
+
queryFn: () => logsService.getAuditLogStats(days),
|
|
19
|
+
staleTime: 60 * 1000,
|
|
20
|
+
gcTime: 10 * 60 * 1000,
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const useClearAuditLogs = () => {
|
|
25
|
+
const queryClient = useQueryClient();
|
|
26
|
+
const { showToast } = useToast();
|
|
27
|
+
|
|
28
|
+
return useMutation({
|
|
29
|
+
mutationFn: (daysToKeep?: number) => logsService.clearAuditLogs(daysToKeep),
|
|
30
|
+
onSuccess: (data) => {
|
|
31
|
+
void queryClient.invalidateQueries({ queryKey: ['audit-logs'] });
|
|
32
|
+
void queryClient.invalidateQueries({ queryKey: ['audit-log-stats'] });
|
|
33
|
+
showToast(`Cleared ${data.deleted} audit logs`, 'success');
|
|
34
|
+
},
|
|
35
|
+
onError: (error: Error) => {
|
|
36
|
+
showToast(`Failed to clear audit logs: ${error.message}`, 'error');
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
};
|