aws-runtime-bridge 1.9.172 → 1.9.179
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/CHANGELOG.md +314 -0
- package/README.md +61 -2
- package/dist/adapter/AcodeSdkAdapter.d.ts +374 -1
- package/dist/adapter/AcodeSdkAdapter.js +3 -3
- package/dist/adapter/AdapterRegistry.d.ts +28 -0
- package/dist/adapter/ClaudeSdkAdapter.d.ts +71 -0
- package/dist/adapter/CodexSdkAdapter.d.ts +14 -0
- package/dist/adapter/OpencodeSdkAdapter.d.ts +44 -0
- package/dist/adapter/acode-tool-names.d.ts +9 -0
- package/dist/adapter/idle-poll-loop.d.ts +20 -0
- package/dist/adapter/index.d.ts +3 -0
- package/dist/adapter/instance-ai-config-resolver.d.ts +20 -0
- package/dist/adapter/prompts.d.ts +12 -0
- package/dist/adapter/types.d.ts +184 -0
- package/dist/browser/browser-bootstrap.d.ts +15 -0
- package/dist/browser/browser-connect-token.d.ts +11 -0
- package/dist/browser/browser-frame-encode.d.ts +32 -0
- package/dist/browser/browser-pool.d.ts +129 -0
- package/dist/browser/browser-profile-store.d.ts +41 -0
- package/dist/browser/browser-ws-bridge.d.ts +20 -0
- package/dist/browser/built-in-browser-tools.d.ts +44 -0
- package/dist/browser/element-locator.d.ts +25 -0
- package/dist/browser/element-picker.d.ts +30 -0
- package/dist/browser/frame-encode.d.ts +19 -0
- package/dist/browser/input-dispatcher.d.ts +35 -0
- package/dist/browser/listener-store.d.ts +44 -0
- package/dist/browser/page-listener-manager.d.ts +88 -0
- package/dist/browser/page-listener-types.d.ts +38 -0
- package/dist/browser/screencast-differ.d.ts +10 -0
- package/dist/browser/screencast-manager.d.ts +94 -0
- package/dist/browser/snapshot-serializer.d.ts +56 -0
- package/dist/browser/ssrf-guard.d.ts +44 -0
- package/dist/browser/stealth.d.ts +21 -0
- package/dist/browser/step-archive.d.ts +13 -0
- package/dist/browser/types.d.ts +100 -0
- package/dist/browser/ws-server.d.ts +54 -0
- package/dist/config.d.ts +138 -2
- package/dist/config.js +2 -2
- package/dist/desktop/built-in-desktop-tools.d.ts +68 -0
- package/dist/desktop/built-in-desktop-tools.js +2 -1
- package/dist/desktop/desktop-control.d.ts +142 -0
- package/dist/desktop/desktop-control.js +2 -0
- package/dist/desktop/desktop-mcp-client.d.ts +28 -0
- package/dist/desktop/desktop-staleness.d.ts +66 -0
- package/dist/desktop/desktop-staleness.js +2 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +1 -1
- package/dist/middleware/auth.d.ts +26 -0
- package/dist/middleware/auth.js +1 -1
- package/dist/remote-desktop/computer-control.d.ts +3 -0
- package/dist/remote-desktop/coords.d.ts +6 -0
- package/dist/remote-desktop/frame-backpressure.d.ts +10 -0
- package/dist/remote-desktop/frame-encode.d.ts +3 -0
- package/dist/remote-desktop/index.js +2 -2
- package/dist/remote-desktop/input-dispatcher.d.ts +4 -0
- package/dist/remote-desktop/permission-manager.d.ts +3 -0
- package/dist/remote-desktop/screen-capture.d.ts +91 -0
- package/dist/remote-desktop/session-manager.d.ts +47 -0
- package/dist/remote-desktop/session-manager.js +1 -1
- package/dist/remote-desktop/system-backend.d.ts +64 -0
- package/dist/remote-desktop/types.d.ts +25 -0
- package/dist/remote-desktop/ui-automation.d.ts +11 -0
- package/dist/remote-desktop/win32-api.d.ts +10 -0
- package/dist/routes/acode-todos.d.ts +13 -0
- package/dist/routes/agent-state.d.ts +15 -0
- package/dist/routes/ai-sources.js +1 -1
- package/dist/routes/background-tasks.d.ts +18 -0
- package/dist/routes/background-tasks.js +2 -2
- package/dist/routes/bridge-temp-files.d.ts +1 -0
- package/dist/routes/bridge-temp-files.js +2 -0
- package/dist/routes/browser-listeners.d.ts +17 -0
- package/dist/routes/browser-sessions.d.ts +10 -0
- package/dist/routes/capabilities.js +1 -1
- package/dist/routes/dashboard-icon.d.ts +9 -0
- package/dist/routes/dashboard-icon.js +2 -0
- package/dist/routes/dashboard-theme.d.ts +24 -0
- package/dist/routes/dashboard-theme.js +157 -0
- package/dist/routes/dashboard.d.ts +9 -0
- package/dist/routes/dashboard.js +2021 -628
- package/dist/routes/debug-mount.d.ts +16 -0
- package/dist/routes/desktop-control.d.ts +15 -0
- package/dist/routes/desktop-control.js +2 -0
- package/dist/routes/events.d.ts +4 -0
- package/dist/routes/file-browser.d.ts +15 -10
- package/dist/routes/file-browser.js +1 -1
- package/dist/routes/file-change-diff.d.ts +25 -0
- package/dist/routes/file-diff-rollback.d.ts +28 -0
- package/dist/routes/file-snapshot.d.ts +7 -0
- package/dist/routes/git.d.ts +185 -0
- package/dist/routes/git.js +34 -34
- package/dist/routes/instance-tool-status.d.ts +5 -0
- package/dist/routes/instance.d.ts +8 -0
- package/dist/routes/instance.js +1 -1
- package/dist/routes/jdks.d.ts +4 -0
- package/dist/routes/launch.d.ts +5 -0
- package/dist/routes/lsp-providers.d.ts +11 -0
- package/dist/routes/lsp-providers.js +2 -0
- package/dist/routes/lsp.d.ts +41 -0
- package/dist/routes/lsp.js +4 -2
- package/dist/routes/mcp-extra.d.ts +15 -0
- package/dist/routes/mcp.d.ts +16 -0
- package/dist/routes/mcp.js +1 -1
- package/dist/routes/plugins.d.ts +19 -0
- package/dist/routes/plugins.js +1 -1
- package/dist/routes/processes.d.ts +4 -0
- package/dist/routes/properties.d.ts +4 -0
- package/dist/routes/pty.d.ts +29 -0
- package/dist/routes/runtime-binding.d.ts +3 -0
- package/dist/routes/sessions.d.ts +22 -0
- package/dist/routes/sessions.js +1 -1
- package/dist/routes/skills.d.ts +15 -0
- package/dist/routes/skills.js +1 -1
- package/dist/routes/sub-agents.d.ts +11 -0
- package/dist/routes/sub-agents.js +1 -1
- package/dist/routes/task-context.d.ts +5 -0
- package/dist/routes/tech-stack.d.ts +6 -0
- package/dist/routes/tech-stack.js +2 -0
- package/dist/routes/terminal.d.ts +122 -0
- package/dist/routes/terminal.js +22 -22
- package/dist/routes/workspace-index.d.ts +12 -0
- package/dist/routes/workspace-index.js +2 -0
- package/dist/routes/yml.d.ts +4 -0
- package/dist/scripts/migrate-user-scopes.d.ts +26 -0
- package/dist/scripts/migrate-user-scopes.js +2 -0
- package/dist/services/access-control.d.ts +21 -0
- package/dist/services/acode-bg-manager.d.ts +21 -0
- package/dist/services/acode-package.d.ts +10 -0
- package/dist/services/acode-package.js +1 -1
- package/dist/services/agent-capability-sync.d.ts +26 -0
- package/dist/services/agent-capability-sync.js +1 -1
- package/dist/services/agent-event-store.d.ts +20 -0
- package/dist/services/agent-ownership.d.ts +58 -0
- package/dist/services/agent-ownership.js +3 -0
- package/dist/services/agent-process-manager.d.ts +154 -0
- package/dist/services/agent-pty-manager.d.ts +39 -0
- package/dist/services/agent-pty-manager.js +5 -4
- package/dist/services/agent-startup-memory.d.ts +34 -0
- package/dist/services/agent-startup-memory.js +4 -4
- package/dist/services/ai-config-service.d.ts +26 -52
- package/dist/services/ai-config-service.js +1 -3
- package/dist/services/ai-source-apply.d.ts +7 -15
- package/dist/services/ai-source-apply.js +1 -1
- package/dist/services/auto-register.d.ts +165 -1
- package/dist/services/auto-register.js +1 -1
- package/dist/services/aws-client-agent-mcp.d.ts +14 -0
- package/dist/services/aws-client-agent-mcp.js +2 -2
- package/dist/services/bridge-package-publish-hooks.d.ts +14 -0
- package/dist/services/bridge-pairing.d.ts +82 -0
- package/dist/services/bridge-pairing.js +2 -0
- package/dist/services/bridge-temp-files.d.ts +65 -0
- package/dist/services/bridge-temp-files.js +2 -0
- package/dist/services/browser-session-manager.d.ts +71 -0
- package/dist/services/capability-config.d.ts +60 -15
- package/dist/services/capability-config.js +2 -2
- package/dist/services/change-cache-retention.d.ts +9 -0
- package/dist/services/changelog.d.ts +9 -0
- package/dist/services/cli-commands.d.ts +82 -0
- package/dist/services/cli-commands.js +6 -6
- package/dist/services/connection-key-rotation.d.ts +27 -0
- package/dist/services/connection-key-rotation.js +2 -0
- package/dist/services/debug-mount.d.ts +49 -0
- package/dist/services/desktop-mcp-manager.d.ts +103 -1
- package/dist/services/desktop-mcp-manager.js +1 -1
- package/dist/services/event-delta-pusher.d.ts +15 -0
- package/dist/services/fileSnapshotStore.d.ts +42 -0
- package/dist/services/fileSnapshotStore.js +1 -1
- package/dist/services/gitignore.d.ts +21 -0
- package/dist/services/gitignore.js +2 -0
- package/dist/services/instance-ai-config-service.d.ts +9 -0
- package/dist/services/instance-ai-config-service.js +1 -1
- package/dist/services/instance-features.d.ts +127 -0
- package/dist/services/instance-features.js +1 -1
- package/dist/services/instance-init-service.d.ts +3 -0
- package/dist/services/instance-init-service.js +1 -1
- package/dist/services/instance-state.d.ts +23 -0
- package/dist/services/jdk-scanner.d.ts +15 -0
- package/dist/services/launch-manager.d.ts +138 -0
- package/dist/services/launch-metrics.d.ts +42 -0
- package/dist/services/launch-streams.d.ts +19 -0
- package/dist/services/lifecycle-state.d.ts +5 -0
- package/dist/services/log-retention.d.ts +38 -0
- package/dist/services/log-retention.js +1 -1
- package/dist/services/lsp/install-manager.d.ts +55 -0
- package/dist/services/lsp/install-manager.js +2 -0
- package/dist/services/lsp/provider-registry.d.ts +108 -0
- package/dist/services/lsp/provider-registry.js +2 -0
- package/dist/services/lsp/uri-rewrite.d.ts +44 -0
- package/dist/services/lsp/uri-rewrite.js +2 -0
- package/dist/services/lsp-stdio-bridge.d.ts +41 -4
- package/dist/services/lsp-stdio-bridge.js +3 -3
- package/dist/services/mcp-deploy.d.ts +45 -4
- package/dist/services/mcp-deploy.js +4 -4
- package/dist/services/mcp-env-injector.d.ts +52 -0
- package/dist/services/mcp-launch-binding-queue.d.ts +12 -0
- package/dist/services/mcp-package.d.ts +22 -0
- package/dist/services/mcp-package.js +1 -1
- package/dist/services/mcp-probe-client.d.ts +19 -0
- package/dist/services/mcp-probe-client.js +1 -1
- package/dist/services/open-doc-events.d.ts +43 -0
- package/dist/services/open-doc-events.js +2 -0
- package/dist/services/open-doc-registry.d.ts +59 -0
- package/dist/services/open-doc-registry.js +2 -0
- package/dist/services/orphan-monitor.d.ts +43 -0
- package/dist/services/panel-auth.d.ts +24 -0
- package/dist/services/panel-env.d.ts +28 -0
- package/dist/services/plugin-deployment.d.ts +34 -1
- package/dist/services/plugin-deployment.js +2 -2
- package/dist/services/plugin-event-buffer.d.ts +21 -0
- package/dist/services/plugin-fs.d.ts +11 -0
- package/dist/services/plugin-handler-adapter.d.ts +13 -0
- package/dist/services/plugin-listener-runner.d.ts +10 -0
- package/dist/services/plugin-loader.d.ts +15 -0
- package/dist/services/plugin-manager.d.ts +95 -9
- package/dist/services/plugin-manager.js +4 -4
- package/dist/services/plugin-manifest.d.ts +14 -0
- package/dist/services/plugin-package.d.ts +13 -0
- package/dist/services/plugin-runtime.d.ts +15 -0
- package/dist/services/port-detector.d.ts +15 -0
- package/dist/services/process-detail.d.ts +3 -0
- package/dist/services/process-detector.d.ts +132 -0
- package/dist/services/process-registry.d.ts +120 -0
- package/dist/services/process-top.d.ts +6 -0
- package/dist/services/request-identity.d.ts +77 -0
- package/dist/services/request-identity.js +2 -0
- package/dist/services/request-invalid-dump.d.ts +4 -0
- package/dist/services/request-invalid-dump.js +1 -1
- package/dist/services/ring-buffer.d.ts +33 -0
- package/dist/services/runtime-binding.d.ts +56 -4
- package/dist/services/runtime-binding.js +4 -4
- package/dist/services/runtime-lifecycle-policy.d.ts +17 -0
- package/dist/services/runtime-resources-snapshot.d.ts +26 -0
- package/dist/services/runtime-resources-snapshot.js +2 -2
- package/dist/services/sdk-tool-file-change-recorder.d.ts +9 -0
- package/dist/services/session-output.d.ts +63 -0
- package/dist/services/session-output.js +3 -3
- package/dist/services/skill-manager.d.ts +42 -5
- package/dist/services/skill-manager.js +3 -3
- package/dist/services/snapshot-retention.d.ts +30 -0
- package/dist/services/snapshot-retention.js +2 -0
- package/dist/services/startup-config-wizard.d.ts +7 -1
- package/dist/services/startup-config-wizard.js +10 -10
- package/dist/services/system-metrics.d.ts +3 -0
- package/dist/services/tech-stack-scanner.d.ts +43 -0
- package/dist/services/tech-stack-scanner.js +2 -0
- package/dist/services/terminal-persistence.d.ts +43 -0
- package/dist/services/tool-installer.d.ts +12 -0
- package/dist/services/tunnel-client.d.ts +99 -2
- package/dist/services/tunnel-client.js +1 -1
- package/dist/services/update-log.d.ts +6 -0
- package/dist/services/update-notifier.d.ts +17 -0
- package/dist/services/update-notifier.js +2 -2
- package/dist/services/user-scope.d.ts +115 -0
- package/dist/services/user-scope.js +2 -0
- package/dist/services/windows-service-manager.d.ts +12 -0
- package/dist/services/workspace-files.d.ts +64 -0
- package/dist/services/workspace-index/index-manager.d.ts +46 -0
- package/dist/services/workspace-index/index-manager.js +2 -0
- package/dist/services/workspace-index/indexer.d.ts +15 -0
- package/dist/services/workspace-index/indexer.js +2 -0
- package/dist/services/workspace-index/query.d.ts +74 -0
- package/dist/services/workspace-index/query.js +5 -0
- package/dist/services/workspace-index/resolver.d.ts +26 -0
- package/dist/services/workspace-index/resolver.js +2 -0
- package/dist/services/workspace-index/types.d.ts +89 -0
- package/dist/services/workspace-index/types.js +2 -0
- package/dist/services/workspace-watch.d.ts +4 -0
- package/dist/services/workspaceTaskChangeWatcher.d.ts +49 -0
- package/dist/services/workspaceTaskChangeWatcher.js +2 -2
- package/dist/types.d.ts +9 -0
- package/dist/utils/config-write-queue.d.ts +11 -0
- package/dist/utils/env-safety.d.ts +21 -0
- package/dist/utils/file-utils.d.ts +15 -0
- package/dist/utils/instance-ai-config-debug-logger.d.ts +15 -0
- package/dist/utils/lifecycle-log.d.ts +5 -0
- package/dist/utils/logger.d.ts +13 -0
- package/dist/utils/logger.js +2 -1
- package/dist/utils/npm-version.d.ts +10 -0
- package/dist/utils/path-utils.d.ts +12 -0
- package/dist/utils/scheduler-url.d.ts +18 -0
- package/dist/utils/scheduler-url.js +2 -0
- package/dist/utils/sdk-package-loader.d.ts +13 -0
- package/dist/utils/sdk-package-loader.js +1 -1
- package/dist/utils/target-identity.d.ts +31 -0
- package/dist/utils/target-identity.js +2 -0
- package/dist/utils/validation.d.ts +17 -0
- package/dist/utils/yaml-utils.d.ts +48 -0
- package/package/acode/dist/__tests__/summary-request-helper.d.ts +12 -0
- package/package/acode/dist/__tests__/summary-request-helper.js +2 -0
- package/package/acode/dist/ask-user-question-tool.d.ts +17 -0
- package/package/acode/dist/await-complete-tool.d.ts +12 -0
- package/package/acode/dist/background-command-manager.d.ts +105 -0
- package/package/acode/dist/background-command-store.d.ts +27 -0
- package/package/acode/dist/background-command-tools.d.ts +8 -0
- package/package/acode/dist/background-output-ringbuffer.d.ts +15 -0
- package/package/acode/dist/built-in-file-tools.d.ts +65 -0
- package/package/acode/dist/built-in-symbols-tool.d.ts +3 -0
- package/package/acode/dist/builtins/commandArtifacts.d.ts +3 -0
- package/package/acode/dist/builtins/shellCommandPreparation.d.ts +17 -0
- package/package/acode/dist/builtins/shellCommandRunner.d.ts +4 -0
- package/package/acode/dist/builtins/shellOutputDecoder.d.ts +15 -0
- package/package/acode/dist/cache-control.d.ts +71 -0
- package/package/acode/dist/cache-control.js +2 -0
- package/package/acode/dist/commands/commandLoader.d.ts +3 -0
- package/package/acode/dist/commands/commandParser.d.ts +3 -0
- package/package/acode/dist/commands/commandRegistry.d.ts +3 -0
- package/package/acode/dist/compaction.d.ts +388 -4
- package/package/acode/dist/compaction.js +19 -17
- package/package/acode/dist/config.d.ts +3 -0
- package/package/acode/dist/context-overflow.d.ts +32 -0
- package/package/acode/dist/fs-utils.d.ts +18 -0
- package/package/acode/dist/hooks/hookBus.d.ts +3 -0
- package/package/acode/dist/image-utils.d.ts +35 -0
- package/package/acode/dist/index.d.ts +1 -0
- package/package/acode/dist/index.js +1 -1
- package/package/acode/dist/mcpToolNames.d.ts +32 -0
- package/package/acode/dist/microcompact.d.ts +110 -0
- package/package/acode/dist/microcompact.js +2 -0
- package/package/acode/dist/parsers/ast-cache.d.ts +41 -0
- package/package/acode/dist/parsers/file-index.d.ts +83 -0
- package/package/acode/dist/parsers/file-index.js +4 -0
- package/package/acode/dist/parsers/language-detector.d.ts +4 -0
- package/package/acode/dist/parsers/tree-sitter-loader.d.ts +12 -0
- package/package/acode/dist/parsers/types.d.ts +11 -0
- package/package/acode/dist/paths.d.ts +14 -0
- package/package/acode/dist/plugin-types.d.ts +34 -0
- package/package/acode/dist/read-file-state.d.ts +15 -0
- package/package/acode/dist/runtime-environment.d.ts +16 -0
- package/package/acode/dist/runtime.d.ts +828 -9
- package/package/acode/dist/runtime.js +40 -33
- package/package/acode/dist/skill-tool.d.ts +20 -0
- package/package/acode/dist/sub-agent-manager.d.ts +169 -0
- package/package/acode/dist/sub-agent-manager.js +1 -1
- package/package/acode/dist/sub-agent-store.d.ts +50 -0
- package/package/acode/dist/sub-agent-tools.d.ts +15 -0
- package/package/acode/dist/sub-agent-types.d.ts +13 -0
- package/package/acode/dist/terminal-tool.d.ts +15 -0
- package/package/acode/dist/terminal-types.d.ts +10 -0
- package/package/acode/dist/todo-store.d.ts +15 -0
- package/package/acode/dist/token-estimator.d.ts +32 -0
- package/package/acode/dist/token-estimator.js +2 -0
- package/package/acode/dist/tool-allocation.d.ts +113 -0
- package/package/acode/dist/tool-concurrency/index.d.ts +8 -0
- package/package/acode/dist/tool-concurrency/observability.d.ts +32 -0
- package/package/acode/dist/tool-concurrency/resource-lock.d.ts +30 -0
- package/package/acode/dist/tool-concurrency/scheduler.d.ts +19 -0
- package/package/acode/dist/tool-concurrency/types.d.ts +57 -0
- package/package/acode/dist/tool-group-manager.d.ts +195 -0
- package/package/acode/dist/tool-group-manager.js +4 -4
- package/package/acode/dist/types.d.ts +192 -0
- package/package/aws-client-agent-mcp/README.md +130 -215
- package/package/aws-client-agent-mcp/dist/ability-market-tools.d.ts +57 -0
- package/package/aws-client-agent-mcp/dist/ability-market-tools.js +1 -1
- package/package/aws-client-agent-mcp/dist/agent-client.d.ts +290 -3
- package/package/aws-client-agent-mcp/dist/agent-client.js +1 -1
- package/package/aws-client-agent-mcp/dist/config.d.ts +18 -0
- package/package/aws-client-agent-mcp/dist/constants.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/context-manager.d.ts +70 -0
- package/package/aws-client-agent-mcp/dist/http-client.d.ts +61 -0
- package/package/aws-client-agent-mcp/dist/index.d.ts +10 -0
- package/package/aws-client-agent-mcp/dist/logger.d.ts +24 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +610 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.js +25 -23
- package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts +158 -0
- package/package/aws-client-agent-mcp/dist/mcp-tools.js +38 -22
- package/package/aws-client-agent-mcp/dist/memory-store.d.ts +102 -2
- package/package/aws-client-agent-mcp/dist/memory-store.js +27 -2
- package/package/aws-client-agent-mcp/dist/memory-tools.d.ts +18 -0
- package/package/aws-client-agent-mcp/dist/memory-tools.js +13 -9
- package/package/aws-client-agent-mcp/dist/message-buffer.d.ts +86 -0
- package/package/aws-client-agent-mcp/dist/messageContent.d.ts +24 -0
- package/package/aws-client-agent-mcp/dist/office/office-equation.d.ts +19 -0
- package/package/aws-client-agent-mcp/dist/office/office-image.d.ts +18 -0
- package/package/aws-client-agent-mcp/dist/office/office-workspace.d.ts +29 -0
- package/package/aws-client-agent-mcp/dist/parent-lifecycle.d.ts +18 -0
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.d.ts +6 -0
- package/package/aws-client-agent-mcp/dist/server-memory-store.d.ts +5 -1
- package/package/aws-client-agent-mcp/dist/server-memory-store.js +1 -1
- package/package/aws-client-agent-mcp/dist/status-reporter.d.ts +41 -0
- package/package/aws-client-agent-mcp/dist/tool-groups.d.ts +136 -0
- package/package/aws-client-agent-mcp/dist/types.d.ts +174 -0
- package/package/aws-client-agent-mcp/dist/watchdog.d.ts +7 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.d.ts +108 -0
- package/package.json +1 -1
package/dist/routes/dashboard.js
CHANGED
|
@@ -1,146 +1,480 @@
|
|
|
1
|
-
import{execFile as X}from"node:child_process";import v from"node:fs";import k from"node:os";import O from"node:path";import{Router as Q}from"express";import{getBridgeVersion as Z,getRuntimeHomeDir as ee,getUserHomeDir as te}from"../config.js";import{getAccessControlEntries as ne,isValidIpEntry as se,parseAllowIps as re,refreshAccessControlCache as ae}from"../services/access-control.js";import{getAgentProcessManager as E}from"../services/agent-process-manager.js";import{autoRegister as oe,getRegistrationState as z,getAutoRegisterConfigFilePath as T}from"../services/auto-register.js";import{createSession as ie,getPanelCredentials as L,validateSession as D,destroySession as F,validateCredentials as _,hasConfiguredCredentials as P,isDefaultCredentials as de,setPanelCredentials as le,getAllSessions as ce,getActiveSessionCount as ue,checkLoginAllowed as ge,recordLoginFailure as pe,recordLoginSuccess as fe}from"../services/panel-auth.js";import{getRuntimeBindingPublicState as V,getRuntimePairingCode as me,normalizeSchedulerBaseUrl as be}from"../services/runtime-binding.js";import{collectSystemMetrics as he}from"../services/system-metrics.js";import{loadPersistedSessions as ve}from"../services/terminal-persistence.js";import{runExclusiveConfigWrite as J}from"../utils/config-write-queue.js";import{createLogger as ye,getRecentLogs as xe}from"../utils/logger.js";import{sdkSessions as we}from"./terminal.js";const x=ye("dashboard");let A=null;const Se=1e4;let M=null,R=null;const Ie=3e4;async function j(){he().then(t=>{M={data:t,ts:Date.now()}}).catch(()=>{}),E().discoverAllProcesses().then(t=>{R={data:t,ts:Date.now()}}).catch(()=>{}),E().cleanupStaleRegistry().catch(()=>{})}const ke=12e4;let C=null,w=null;function Ce(){w&&(clearTimeout(w),w=null),w=setTimeout(Be,ke),w.unref?.(),C===null&&(C=setInterval(j,Ie),C.unref?.(),j())}function Be(){C&&(clearInterval(C),C=null),w&&(clearTimeout(w),w=null)}const W=Z();async function Ee(){const t=await import("node:fs/promises"),n=(await import("node:path")).join(ee(),".aws-bridge","lifecycle-state.json");try{const s=await t.readFile(n,"utf8"),i=JSON.parse(s),r=Array.isArray(i.operations)?i.operations:[];return r.length===0?null:(r.sort((a,o)=>new Date(o.updatedAt).getTime()-new Date(a.updatedAt).getTime()),r[0]||null)}catch{return null}}const c=Q(),Te=["/runtime/login","/runtime/logout","/runtime/credentials-status","/runtime/change-credentials","/runtime/server-config","/runtime/access-control","/runtime/node-info","/runtime/logs","/runtime/re-register","/runtime/admin","/runtime/kill-process","/runtime/dashboard","/login","/setup"];function Pe(t){return t==="/"?!0:Te.some(e=>t===e||t.startsWith(`${e}/`))}c.use((t,e,n)=>{Pe(t.path)&&Ce(),n()});const q="panel_sid";function Ae(t){const e={};if(!t)return e;for(const n of t.split(";")){const s=n.indexOf("=");if(s===-1)continue;const i=n.slice(0,s).trim(),r=n.slice(s+1).trim();i&&(e[i]=r)}return e}function N(t){return Ae(t.headers.cookie)[q]}function G(t,e,n){const s=t.secure||t.headers["x-forwarded-proto"]==="https";return`${q}=${e}; HttpOnly; SameSite=Lax; Path=/${s?"; Secure":""}; Max-Age=${n}`}function m(t,e){const n=N(t);return n&&D(n)?!0:t.xhr||t.headers.accept?.includes("application/json")?(e.status(401).json({ok:!1,error:"unauthorized",needLogin:!0}),!1):(e.redirect("/login"),!1)}c.post("/runtime/login",(t,e)=>{const n=t.socket.remoteAddress||"unknown",s=ge(n);if(!s.allowed){e.status(429).set("Retry-After",String(s.retryAfterSec)).json({ok:!1,error:"too many failed login attempts, try again later",retryAfterSec:s.retryAfterSec});return}const{username:i,password:r}=t.body||{};if(!i||!r){e.status(400).json({ok:!1,error:"username and password required"});return}if(!_(String(i),String(r))){pe(n),e.status(401).json({ok:!1,error:"invalid credentials"});return}fe(n);const a=ie(),o=!P();e.setHeader("Set-Cookie",G(t,a,86400)),e.json({ok:!0,needSetup:o})}),c.post("/runtime/logout",(t,e)=>{const n=N(t);n&&F(n),e.setHeader("Set-Cookie",G(t,"",0)),e.json({ok:!0})}),c.get("/runtime/credentials-status",(t,e)=>{if(!m(t,e))return;const n=L();e.json({ok:!0,isDefault:de(),configured:P(),username:n.username})}),c.post("/runtime/change-credentials",async(t,e)=>{if(!m(t,e))return;const{username:n,password:s,passwordConfirm:i,oldPassword:r}=t.body||{};if(s&&String(s).trim()){if(String(s)!==String(i)){e.status(400).json({ok:!1,error:"passwords do not match"});return}if(P()){if(!r){e.status(400).json({ok:!1,error:"old password required"});return}const o=L();if(!_(o.username,String(r))){e.status(403).json({ok:!1,error:"old password is incorrect"});return}}}if(!n||!String(n).trim()){e.status(400).json({ok:!1,error:"username required"});return}const a=s&&String(s).trim()?String(s).trim():L().password;try{await le(String(n).trim(),a),e.json({ok:!0})}catch(o){const l=o;x.error("Failed to change panel credentials:",l.message),e.status(500).json({ok:!1,error:l.message})}});function $(t){const e=typeof t=="string"?t.trim():"";if(e)return e.length<8?"\u2022\u2022\u2022\u2022":"\u2022\u2022\u2022\u2022"+e.slice(-4)}function Y(t){const e=Array.isArray(t.autoRegisterTargets)?t.autoRegisterTargets.filter(s=>!!(s&&typeof s=="object"&&!Array.isArray(s))):[];return e.length>0?e:["serverUrl","schedulerBaseUrl","userKey"].some(s=>!!String(t[s]??"").trim())?[t]:[]}const Re=["enabled","serverUrl","schedulerBaseUrl","tenantId","userKey","connectionKey","password","instanceName","instanceId","projectId","roleName","workspacePath","registerAddr","registerIp","virtualIp","tunnel","preferSameLanIp","maxRetries","retryInterval"];function Ne(t){const e={};for(const n of Re)t[n]!==void 0&&(e[n]=t[n]);return e}function Ue(t,e){const n=String(t.connectionKey??"").trim()||String(e??"").trim();return{serverUrl:String(t.serverUrl??t.schedulerBaseUrl??"").trim(),tenantId:String(t.tenantId??"").trim(),instanceName:String(t.instanceName??"").trim(),instanceId:String(t.instanceId??"").trim()||void 0,registerAddr:String(t.registerAddr??"").trim(),tunnel:typeof t.tunnel=="boolean"?t.tunnel:void 0,userKeyMasked:$(t.userKey),userKeySet:!!String(t.userKey??"").trim(),connectionKeyMasked:$(n),connectionKeySet:!!n}}function U(t){return be(t)??String(t||"").trim()}c.get("/runtime/server-config",(t,e)=>{if(!m(t,e))return;const n=T();let s={};if(v.existsSync(n))try{s=JSON.parse(v.readFileSync(n,"utf-8"))}catch{e.status(500).json({ok:!1,error:"config.json is not valid JSON; fix it manually first"});return}const i=String(s.connectionKey??"").trim()||void 0,r=Y(s).map(a=>Ue(a,i));e.json({ok:!0,targets:r})});function H(t,e){const n=new Error(e);return n.status=t,n}c.post("/runtime/server-config",async(t,e)=>{if(!m(t,e))return;const n=(t.body||{}).targets;if(!Array.isArray(n)){e.status(400).json({ok:!1,error:"targets array is required"});return}const s=[];for(let r=0;r<n.length;r++){const a=n[r];if(!a||typeof a!="object"||Array.isArray(a)){e.status(400).json({ok:!1,error:`targets[${r}] must be an object`});return}const o=a,l=String(o.serverUrl??o.schedulerBaseUrl??"").trim();if(!l){e.status(400).json({ok:!1,error:`targets[${r}].serverUrl is required`});return}try{const d=new URL(l);if(!["http:","https:"].includes(d.protocol))throw new Error("protocol must be http or https")}catch{e.status(400).json({ok:!1,error:`targets[${r}].serverUrl must be a valid http(s) URL`});return}const p=o.tunnel;let f;if(p==="auto"||p===""||p===null||p===void 0)f=void 0;else if(p===!0||p==="true")f=!0;else if(p===!1||p==="false")f=!1;else{e.status(400).json({ok:!1,error:`targets[${r}].tunnel must be auto, true or false`});return}const h=String(o.originalServerUrl??"").trim();s.push({serverUrl:l,originalServerUrl:h&&h!==l?h:"",userKey:String(o.userKey??"").trim(),tenantId:String(o.tenantId??"").trim(),instanceName:String(o.instanceName??"").trim(),connectionKey:String(o.connectionKey??"").trim(),registerAddr:String(o.registerAddr??"").trim(),tunnel:f})}const i=new Set;for(const r of s){const a=U(r.serverUrl);if(i.has(a)){e.status(400).json({ok:!1,error:"duplicate serverUrl in targets"});return}i.add(a)}try{const r=T();await J(r,()=>{let a={};if(v.existsSync(r))try{a=JSON.parse(v.readFileSync(r,"utf-8"))}catch{throw H(500,"config.json is not valid JSON; fix it manually first")}const o=String(a.connectionKey??"").trim(),l=Y(a),p=l.length>0&&l[0]===a,f=new Map(l.map(d=>[U(String(d.serverUrl??d.schedulerBaseUrl??"").trim()),p?Ne(d):d])),h=[];for(const d of s){let b;if(d.originalServerUrl){const S=U(d.originalServerUrl);b=f.get(S),b&&f.delete(S)}if(!b){const S=U(d.serverUrl);b=f.get(S),b&&f.delete(S)}const u=b?{...b}:{};if(u.serverUrl=d.serverUrl,u.tenantId=d.tenantId,u.instanceName=d.instanceName,u.registerAddr=d.registerAddr,d.tunnel===void 0?delete u.tunnel:u.tunnel=d.tunnel,d.userKey)u.userKey=d.userKey;else if(!String(u.userKey??"").trim())throw H(400,`userKey is required for ${d.serverUrl} (new target without inheritable userKey)`);d.connectionKey?u.connectionKey=d.connectionKey:!String(u.connectionKey??"").trim()&&o&&(u.connectionKey=o),h.push(u)}a.autoRegisterTargets=h;for(const d of["enabled","serverUrl","schedulerBaseUrl","tenantId","userKey","connectionKey","password","instanceName","instanceId","registerAddr","registerIp","virtualIp","tunnel"])delete a[d];v.mkdirSync(O.dirname(r),{recursive:!0}),v.writeFileSync(r,JSON.stringify(a,null,2),"utf-8")}),x.info("Server connection config updated via panel",{configPath:r,targetCount:s.length}),e.json({ok:!0})}catch(r){const a=r,o=Number(a.status)||500;o>=500&&x.error("Failed to save server connection config:",a.message),e.status(o).json({ok:!1,error:a.message})}}),c.get("/runtime/access-control",(t,e)=>{m(t,e)&&e.json({ok:!0,allowIps:ne(),defaultAllowIps:["0.0.0.0"]})}),c.post("/runtime/access-control",async(t,e)=>{if(!m(t,e))return;const n=(t.body||{}).allowIps;if(typeof n!="string"&&!Array.isArray(n)){e.status(400).json({ok:!1,error:"allowIps must be a string or an array"});return}const s=re(n),i=s.filter(r=>!se(r));if(i.length>0){e.status(400).json({ok:!1,error:`invalid IP/CIDR entries: ${i.join(", ")}`});return}try{const r=T();await J(r,()=>{let a={};if(v.existsSync(r))try{a=JSON.parse(v.readFileSync(r,"utf-8"))}catch{throw H(500,"config.json is not valid JSON; fix it manually first")}const o=a.accessControl&&typeof a.accessControl=="object"&&!Array.isArray(a.accessControl)?{...a.accessControl}:{};o.allowIps=s,a.accessControl=o,v.mkdirSync(O.dirname(r),{recursive:!0}),v.writeFileSync(r,JSON.stringify(a,null,2),"utf-8"),ae()}),x.info("Access control config updated via panel",{configPath:r,entryCount:s.length}),e.json({ok:!0,allowIps:s})}catch(r){const a=r,o=Number(a.status)||500;o>=500&&x.error("Failed to save access control config:",a.message),e.status(o).json({ok:!1,error:a.message})}}),c.get("/runtime/node-info",(t,e)=>{if(!m(t,e))return;const n=V(),s=z();e.json({ok:!0,hostname:k.hostname(),platform:k.platform(),arch:k.arch(),uptime:Math.floor(process.uptime()),nodeVersion:process.version,bridgeVersion:W,configPath:T(),homeDir:te(),bindingStatus:n.status,paired:n.paired,instanceId:n.instanceId||s.instanceId,schedulerBaseUrl:n.schedulerBaseUrl,registered:s.registered,pairingCode:n.pairingCode,activeSessions:ue()})}),c.get("/runtime/logs",(t,e)=>{if(!m(t,e))return;const n=t.query.level||void 0,s=Math.min(Number(t.query.count)||200,500),r=n&&["debug","info","warn","error"].includes(n)?n:void 0,a=xe(s,r);e.json({ok:!0,logs:a})}),c.post("/runtime/re-register",async(t,e)=>{if(m(t,e))try{const n=await oe();e.json({ok:n})}catch(n){const s=n;e.status(500).json({ok:!1,error:s.message})}}),c.get("/runtime/admin/sessions",(t,e)=>{if(!m(t,e))return;const n=ce().map(s=>({token:s.token.slice(0,8)+"...",createdAt:new Date(s.createdAt).toISOString(),lastUsedAt:new Date(s.lastUsedAt).toISOString(),age:Math.floor((Date.now()-s.createdAt)/1e3)}));e.json({ok:!0,sessions:n})}),c.post("/runtime/admin/sessions/:token/destroy",(t,e)=>{m(t,e)&&(F(t.params.token),e.json({ok:!0}))});function Le(t){return new Promise((e,n)=>{const s=k.platform()==="win32",i=s?"taskkill":"kill",r=s?["/PID",String(t),"/F"]:["-9",String(t)];X(i,r,{timeout:5e3},a=>a?n(a):e())})}c.post("/runtime/kill-process",async(t,e)=>{if(!m(t,e))return;const n=Number(t.body.pid);if(!n||!Number.isInteger(n)||n<=0){e.status(400).json({ok:!1,error:"Invalid PID"});return}const s=E(),i=()=>{const a=new Set,o=R?.data??[];for(const l of o)typeof l.pid=="number"&&a.add(l.pid);for(const l of s.getAllManagedProcesses())typeof l.pid=="number"&&a.add(l.pid);return a};let r=i();if(r.size===0)try{R={data:await s.discoverAllProcesses(),ts:Date.now()},r=i()}catch{}if(!r.has(n)){x.warn("Rejected kill-process for unrecognized PID",{pid:n}),e.status(404).json({ok:!1,error:"PID is not a bridge-discovered or registered agent process"});return}try{await Le(n),x.info("Killed process",{pid:n}),e.json({ok:!0})}catch(a){const o=a;x.warn("Failed to kill process",{pid:n,error:o.message}),e.status(500).json({ok:!1,error:o.message})}}),c.get("/runtime/dashboard",async(t,e)=>{if(m(t,e)){if(A&&Date.now()-A.ts<Se)return void e.json(A.data);try{const n=M?.data??null,s=R?.data??null;M||j();const[i,r,a]=await Promise.all([Promise.resolve(V()),Promise.resolve(z()),ve().catch(()=>[])]),l=E().getAllManagedProcesses(),p=Array.from(we.entries()).map(([I,y])=>({sessionId:I,agentId:y.agentId,workspacePath:y.workspacePath,runtimeStatus:y.currentRuntimeStatus||"running",runtimeActionType:y.currentRuntimeActionType,runtimeActionLabel:y.currentRuntimeActionLabel,providerId:y.providerId}));let f={managed:0,orphan:0,unknown:0},h=[];if(s){f={managed:s.filter(g=>g.managementStatus==="managed").length,orphan:s.filter(g=>g.managementStatus==="orphan").length,unknown:s.filter(g=>g.managementStatus==="unknown").length};const I=new Map(l.map(g=>[g.pid,g]));h=s.map(g=>{const B=I.get(g.pid);return{agentId:g.agentId||B?.agentId,pid:g.pid,managementStatus:g.managementStatus,sessionId:B?.sessionId,state:B?.state,command:g.command,cwd:g.cwd,startedAt:g.startedAt}});const y={managed:0,orphan:1,unknown:2};h.sort((g,B)=>(y[g.managementStatus]??9)-(y[B.managementStatus]??9))}const d=new Set(p.map(I=>I.sessionId)),b=a.filter(I=>!d.has(I.sessionId)),u=i,S=await Ee(),K={ok:!0,collectedAt:new Date().toISOString(),bridge:{version:W,hostname:k.hostname(),platform:k.platform(),arch:k.arch(),uptime:Math.floor(process.uptime()),nodeVersion:process.version},binding:{status:u.status,paired:u.paired,instanceId:u.instanceId,userId:u.userId,schedulerBaseUrl:u.schedulerBaseUrl,pairingCode:me()},registration:{registered:r.registered,instanceId:r.instanceId,lastAttempt:r.lastAttempt?.toISOString(),error:r.error},metrics:n,sessions:{active:p,activeCount:p.length,persisted:b,persistedCount:b.length,total:p.length+b.length},processes:{...f,total:f.managed+f.orphan+f.unknown,list:h,registryCount:l.length},lifecycleOperation:S};A={data:K,ts:Date.now()},e.json(K)}catch(n){const s=n;x.error("Dashboard aggregation failed:",s.message),e.status(500).json({ok:!1,error:s.message})}}}),c.get("/login",(t,e)=>{e.type("text/html; charset=utf-8"),e.send(Me)}),c.get("/",(t,e)=>{const n=N(t);if(!n||!D(n)){e.redirect("/login");return}if(!P()){e.redirect("/setup");return}const s=t.query.lang==="en"?"en":"zh";e.type("text/html; charset=utf-8"),e.send(De.replace("{{LANG}}",s))}),c.get("/setup",(t,e)=>{const n=N(t);if(!n||!D(n)){e.redirect("/login");return}e.type("text/html; charset=utf-8"),e.send(je)});const De=`<!DOCTYPE html>
|
|
1
|
+
import{execFile as ne}from"node:child_process";import y from"node:fs";import B from"node:os";import V from"node:path";import{Router as ae}from"express";import{getBridgeVersion as re,getRuntimeHomeDir as se,getUserHomeDir as oe}from"../config.js";import{getAccessControlEntries as ie,isValidIpEntry as de,parseAllowIps as le,refreshAccessControlCache as ce}from"../services/access-control.js";import{getAgentProcessManager as P}from"../services/agent-process-manager.js";import{autoRegister as ue,getRegistrationState as $,getAutoRegisterConfigFilePath as A}from"../services/auto-register.js";import{createSession as ge,getPanelCredentials as D,validateSession as H,destroySession as W,validateCredentials as J,hasConfiguredCredentials as R,isDefaultCredentials as pe,setPanelCredentials as fe,getAllSessions as me,getActiveSessionCount as he,checkLoginAllowed as be,recordLoginFailure as ve,recordLoginSuccess as ye}from"../services/panel-auth.js";import{getRuntimeBindingPublicState as q,getRuntimePairingCode as xe,normalizeSchedulerBaseUrl as we}from"../services/runtime-binding.js";import{collectSystemMetrics as ke}from"../services/system-metrics.js";import{loadPersistedSessions as Se}from"../services/terminal-persistence.js";import{runExclusiveConfigWrite as G}from"../utils/config-write-queue.js";import{createLogger as Ie,getRecentLogs as Be}from"../utils/logger.js";import{buildTargetKey as T}from"../utils/target-identity.js";import{DASHBOARD_FAVICON_DATA_URI as Y,DASHBOARD_FAVICON_LINK as O}from"./dashboard-icon.js";import{THEME_CSS as j,THEME_BOOT_SCRIPT as z,THEME_FAB_SCRIPT as X}from"./dashboard-theme.js";import{sdkSessions as Ce}from"./terminal.js";const x=Ie("dashboard");let N=null;const Ee=1e4;let M=null,U=null;const Ae=3e4;async function F(){ke().then(t=>{M={data:t,ts:Date.now()}}).catch(()=>{}),P().discoverAllProcesses().then(t=>{U={data:t,ts:Date.now()}}).catch(()=>{}),P().cleanupStaleRegistry().catch(()=>{})}const Te=12e4;let C=null,k=null;function Pe(){k&&(clearTimeout(k),k=null),k=setTimeout(Re,Te),k.unref?.(),C===null&&(C=setInterval(F,Ae),C.unref?.(),F())}function Re(){C&&(clearInterval(C),C=null),k&&(clearTimeout(k),k=null)}const Q=re();async function Ne(){const t=await import("node:fs/promises"),n=(await import("node:path")).join(se(),".aws-bridge","lifecycle-state.json");try{const a=await t.readFile(n,"utf8"),o=JSON.parse(a),r=Array.isArray(o.operations)?o.operations:[];return r.length===0?null:(r.sort((s,d)=>new Date(d.updatedAt).getTime()-new Date(s.updatedAt).getTime()),r[0]||null)}catch{return null}}const f=ae(),Ue=["/runtime/login","/runtime/logout","/runtime/credentials-status","/runtime/change-credentials","/runtime/server-config","/runtime/access-control","/runtime/node-info","/runtime/logs","/runtime/re-register","/runtime/admin","/runtime/kill-process","/runtime/dashboard","/login","/setup"];function Le(t){return t==="/"?!0:Ue.some(e=>t===e||t.startsWith(`${e}/`))}f.use((t,e,n)=>{Le(t.path)&&Pe(),n()});const Z="panel_sid";function Ke(t){const e={};if(!t)return e;for(const n of t.split(";")){const a=n.indexOf("=");if(a===-1)continue;const o=n.slice(0,a).trim(),r=n.slice(a+1).trim();o&&(e[o]=r)}return e}function L(t){return Ke(t.headers.cookie)[Z]}function ee(t,e,n){const a=t.secure||t.headers["x-forwarded-proto"]==="https";return`${Z}=${e}; HttpOnly; SameSite=Lax; Path=/${a?"; Secure":""}; Max-Age=${n}`}function v(t,e){const n=L(t);return n&&H(n)?!0:t.xhr||t.headers.accept?.includes("application/json")?(e.status(401).json({ok:!1,error:"unauthorized",needLogin:!0}),!1):(e.redirect("/login"),!1)}f.post("/runtime/login",(t,e)=>{const n=t.socket.remoteAddress||"unknown",a=be(n);if(!a.allowed){e.status(429).set("Retry-After",String(a.retryAfterSec)).json({ok:!1,error:"too many failed login attempts, try again later",retryAfterSec:a.retryAfterSec});return}const{username:o,password:r}=t.body||{};if(!o||!r){e.status(400).json({ok:!1,error:"username and password required"});return}if(!J(String(o),String(r))){ve(n),e.status(401).json({ok:!1,error:"invalid credentials"});return}ye(n);const s=ge(),d=!R();e.setHeader("Set-Cookie",ee(t,s,86400)),e.json({ok:!0,needSetup:d})}),f.post("/runtime/logout",(t,e)=>{const n=L(t);n&&W(n),e.setHeader("Set-Cookie",ee(t,"",0)),e.json({ok:!0})}),f.get("/runtime/credentials-status",(t,e)=>{if(!v(t,e))return;const n=D();e.json({ok:!0,isDefault:pe(),configured:R(),username:n.username})}),f.post("/runtime/change-credentials",async(t,e)=>{if(!v(t,e))return;const{username:n,password:a,passwordConfirm:o,oldPassword:r}=t.body||{};if(a&&String(a).trim()){if(String(a)!==String(o)){e.status(400).json({ok:!1,error:"passwords do not match"});return}if(R()){if(!r){e.status(400).json({ok:!1,error:"old password required"});return}const d=D();if(!J(d.username,String(r))){e.status(403).json({ok:!1,error:"old password is incorrect"});return}}}if(!n||!String(n).trim()){e.status(400).json({ok:!1,error:"username required"});return}const s=a&&String(a).trim()?String(a).trim():D().password;try{await fe(String(n).trim(),s),e.json({ok:!0})}catch(d){const l=d;x.error("Failed to change panel credentials:",l.message),e.status(500).json({ok:!1,error:l.message})}});function te(t){const e=typeof t=="string"?t.trim():"";if(e)return e.length<8?"\u2022\u2022\u2022\u2022":"\u2022\u2022\u2022\u2022"+e.slice(-4)}function _(t){const e=Array.isArray(t.autoRegisterTargets)?t.autoRegisterTargets.filter(a=>!!(a&&typeof a=="object"&&!Array.isArray(a))):[];return e.length>0?e:["serverUrl","schedulerBaseUrl","userKey"].some(a=>!!String(t[a]??"").trim())?[t]:[]}const De=["enabled","serverUrl","schedulerBaseUrl","tenantId","userKey","connectionKey","password","instanceName","instanceId","projectId","roleName","workspacePath","registerAddr","registerIp","virtualIp","tunnel","preferSameLanIp","maxRetries","retryInterval"];function He(t){const e={};for(const n of De)t[n]!==void 0&&(e[n]=t[n]);return e}function Oe(t,e){const n=String(t.connectionKey??"").trim()||String(e??"").trim();return{serverUrl:String(t.serverUrl??t.schedulerBaseUrl??"").trim(),tenantId:String(t.tenantId??"").trim(),instanceName:String(t.instanceName??"").trim(),instanceId:String(t.instanceId??"").trim()||void 0,registerAddr:String(t.registerAddr??"").trim(),tunnel:typeof t.tunnel=="boolean"?t.tunnel:void 0,userKeyMasked:te(t.userKey),userKeySet:!!String(t.userKey??"").trim(),connectionKeyMasked:te(n),connectionKeySet:!!n,targetKey:T(String(t.serverUrl??t.schedulerBaseUrl??"").trim(),t.userKey)}}function je(t){return we(t)??String(t||"").trim()}f.get("/runtime/server-config",(t,e)=>{if(!v(t,e))return;const n=A();let a={};if(y.existsSync(n))try{a=JSON.parse(y.readFileSync(n,"utf-8"))}catch{e.status(500).json({ok:!1,error:"config.json is not valid JSON; fix it manually first"});return}const o=String(a.connectionKey??"").trim()||void 0,r=_(a).map(s=>Oe(s,o));e.json({ok:!0,targets:r})}),f.get("/runtime/server-config/connection-key",(t,e)=>{if(!v(t,e))return;const n=String((t.query||{}).targetKey??"").trim();if(!n){e.status(400).json({ok:!1,error:"targetKey is required"});return}const a=A();if(!y.existsSync(a)){e.status(404).json({ok:!1,error:"config.json not found"});return}let o;try{o=JSON.parse(y.readFileSync(a,"utf-8"))}catch{e.status(500).json({ok:!1,error:"config.json is not valid JSON"});return}const r=String(o.connectionKey??"").trim()||void 0,s=_(o).find(l=>T(String(l.serverUrl??l.schedulerBaseUrl??"").trim(),l.userKey)===n),d=String(s?.connectionKey??"").trim()||r||"";if(!d){e.status(404).json({ok:!1,error:"\u8BE5\u76EE\u6807\u5C1A\u672A\u914D\u7F6E\u8FDE\u63A5\u5BC6\u94A5\uFF1B\u542F\u52A8\u4E00\u6B21 Bridge \u4F1A\u81EA\u52A8\u751F\u6210\u5E76\u5199\u5165 config.json"});return}e.setHeader("Cache-Control","no-store"),e.json({ok:!0,connectionKey:d})});function K(t,e){const n=new Error(e);return n.status=t,n}f.post("/runtime/server-config",async(t,e)=>{if(!v(t,e))return;const n=(t.body||{}).targets;if(!Array.isArray(n)){e.status(400).json({ok:!1,error:"targets array is required"});return}const a=[];for(let o=0;o<n.length;o++){const r=n[o];if(!r||typeof r!="object"||Array.isArray(r)){e.status(400).json({ok:!1,error:`targets[${o}] must be an object`});return}const s=r,d=String(s.serverUrl??s.schedulerBaseUrl??"").trim();if(!d){e.status(400).json({ok:!1,error:`targets[${o}].serverUrl is required`});return}try{const w=new URL(d);if(!["http:","https:"].includes(w.protocol))throw new Error("protocol must be http or https")}catch{e.status(400).json({ok:!1,error:`targets[${o}].serverUrl must be a valid http(s) URL`});return}const l=s.tunnel;let m;if(l==="auto"||l===""||l===null||l===void 0)m=void 0;else if(l===!0||l==="true")m=!0;else if(l===!1||l==="false")m=!1;else{e.status(400).json({ok:!1,error:`targets[${o}].tunnel must be auto, true or false`});return}const h=String(s.originalServerUrl??"").trim();a.push({serverUrl:d,originalServerUrl:h&&h!==d?h:"",originalTargetKey:String(s.originalTargetKey??"").trim(),userKey:String(s.userKey??"").trim(),tenantId:String(s.tenantId??"").trim(),instanceName:String(s.instanceName??"").trim(),connectionKey:String(s.connectionKey??"").trim(),registerAddr:String(s.registerAddr??"").trim(),tunnel:m})}try{const o=A();await G(o,()=>{let r={};if(y.existsSync(o))try{r=JSON.parse(y.readFileSync(o,"utf-8"))}catch{throw K(500,"config.json is not valid JSON; fix it manually first")}const s=String(r.connectionKey??"").trim(),d=_(r),l=d.length>0&&d[0]===r,m=new Map;for(const i of d){const u=T(String(i.serverUrl??i.schedulerBaseUrl??"").trim(),i.userKey);m.set(u,l?He(i):i)}const h=[];for(const i of a){let u;const b=g=>{const c=m.get(g);return c&&m.delete(g),c},S=(g,c)=>T(g,c),E=g=>{const c=`${je(g)}#`;return[...m.keys()].filter(I=>I.startsWith(c))};if(i.originalTargetKey&&(u=b(i.originalTargetKey)),!u&&i.originalServerUrl&&(u=b(S(i.originalServerUrl,i.userKey)),!u)){const g=E(i.originalServerUrl);g.length===1&&(u=b(g[0]))}if(u||(u=b(S(i.serverUrl,i.userKey))),!u){const g=E(i.serverUrl);g.length>=1&&(u=b(g[0]))}const p=u?{...u}:{};if(p.serverUrl=i.serverUrl,p.tenantId=i.tenantId,p.instanceName=i.instanceName,p.registerAddr=i.registerAddr,i.tunnel===void 0?delete p.tunnel:p.tunnel=i.tunnel,i.userKey)p.userKey=i.userKey;else if(!String(p.userKey??"").trim())throw K(400,`userKey is required for ${i.serverUrl} (new target without inheritable userKey)`);i.connectionKey?p.connectionKey=i.connectionKey:!String(p.connectionKey??"").trim()&&s&&(p.connectionKey=s),h.push(p)}const w=new Map;for(let i=0;i<h.length;i++){const u=h[i],b=T(String(u.serverUrl??u.schedulerBaseUrl??"").trim(),u.userKey),S=w.get(b);if(S!==void 0)throw K(400,`duplicate target: ${String(u.serverUrl??"")} appears in both targets[${S}] and targets[${i}] with the same API key; the same scheduler address can be added multiple times only with different API keys`);w.set(b,i)}r.autoRegisterTargets=h;for(const i of["enabled","serverUrl","schedulerBaseUrl","tenantId","userKey","connectionKey","password","instanceName","instanceId","registerAddr","registerIp","virtualIp","tunnel"])delete r[i];y.mkdirSync(V.dirname(o),{recursive:!0}),y.writeFileSync(o,JSON.stringify(r,null,2),"utf-8")}),x.info("Server connection config updated via panel",{configPath:o,targetCount:a.length}),e.json({ok:!0})}catch(o){const r=o,s=Number(r.status)||500;s>=500&&x.error("Failed to save server connection config:",r.message),e.status(s).json({ok:!1,error:r.message})}}),f.get("/runtime/access-control",(t,e)=>{v(t,e)&&e.json({ok:!0,allowIps:ie(),defaultAllowIps:["0.0.0.0"]})}),f.post("/runtime/access-control",async(t,e)=>{if(!v(t,e))return;const n=(t.body||{}).allowIps;if(typeof n!="string"&&!Array.isArray(n)){e.status(400).json({ok:!1,error:"allowIps must be a string or an array"});return}const a=le(n),o=a.filter(r=>!de(r));if(o.length>0){e.status(400).json({ok:!1,error:`invalid IP/CIDR entries: ${o.join(", ")}`});return}try{const r=A();await G(r,()=>{let s={};if(y.existsSync(r))try{s=JSON.parse(y.readFileSync(r,"utf-8"))}catch{throw K(500,"config.json is not valid JSON; fix it manually first")}const d=s.accessControl&&typeof s.accessControl=="object"&&!Array.isArray(s.accessControl)?{...s.accessControl}:{};d.allowIps=a,s.accessControl=d,y.mkdirSync(V.dirname(r),{recursive:!0}),y.writeFileSync(r,JSON.stringify(s,null,2),"utf-8"),ce()}),x.info("Access control config updated via panel",{configPath:r,entryCount:a.length}),e.json({ok:!0,allowIps:a})}catch(r){const s=r,d=Number(s.status)||500;d>=500&&x.error("Failed to save access control config:",s.message),e.status(d).json({ok:!1,error:s.message})}}),f.get("/runtime/node-info",(t,e)=>{if(!v(t,e))return;const n=q(),a=$();e.json({ok:!0,hostname:B.hostname(),platform:B.platform(),arch:B.arch(),uptime:Math.floor(process.uptime()),nodeVersion:process.version,bridgeVersion:Q,configPath:A(),homeDir:oe(),bindingStatus:n.status,paired:n.paired,instanceId:n.instanceId||a.instanceId,schedulerBaseUrl:n.schedulerBaseUrl,registered:a.registered,pairingCode:n.pairingCode,activeSessions:he()})}),f.get("/runtime/logs",(t,e)=>{if(!v(t,e))return;const n=t.query.level||void 0,a=Math.min(Number(t.query.count)||200,500),r=n&&["debug","info","warn","error"].includes(n)?n:void 0,s=Be(a,r);e.json({ok:!0,logs:s})}),f.post("/runtime/re-register",async(t,e)=>{if(v(t,e))try{const n=await ue();e.json({ok:n})}catch(n){const a=n;e.status(500).json({ok:!1,error:a.message})}}),f.get("/runtime/admin/sessions",(t,e)=>{if(!v(t,e))return;const n=me().map(a=>({token:a.token.slice(0,8)+"...",createdAt:new Date(a.createdAt).toISOString(),lastUsedAt:new Date(a.lastUsedAt).toISOString(),age:Math.floor((Date.now()-a.createdAt)/1e3)}));e.json({ok:!0,sessions:n})}),f.post("/runtime/admin/sessions/:token/destroy",(t,e)=>{v(t,e)&&(W(t.params.token),e.json({ok:!0}))});function ze(t){return new Promise((e,n)=>{const a=B.platform()==="win32",o=a?"taskkill":"kill",r=a?["/PID",String(t),"/F"]:["-9",String(t)];ne(o,r,{timeout:5e3},s=>s?n(s):e())})}f.post("/runtime/kill-process",async(t,e)=>{if(!v(t,e))return;const n=Number(t.body.pid);if(!n||!Number.isInteger(n)||n<=0){e.status(400).json({ok:!1,error:"Invalid PID"});return}const a=P(),o=()=>{const s=new Set,d=U?.data??[];for(const l of d)typeof l.pid=="number"&&s.add(l.pid);for(const l of a.getAllManagedProcesses())typeof l.pid=="number"&&s.add(l.pid);return s};let r=o();if(r.size===0)try{U={data:await a.discoverAllProcesses(),ts:Date.now()},r=o()}catch{}if(!r.has(n)){x.warn("Rejected kill-process for unrecognized PID",{pid:n}),e.status(404).json({ok:!1,error:"PID is not a bridge-discovered or registered agent process"});return}try{await ze(n),x.info("Killed process",{pid:n}),e.json({ok:!0})}catch(s){const d=s;x.warn("Failed to kill process",{pid:n,error:d.message}),e.status(500).json({ok:!1,error:d.message})}}),f.get("/runtime/dashboard",async(t,e)=>{if(v(t,e)){if(N&&Date.now()-N.ts<Ee)return void e.json(N.data);try{const n=M?.data??null,a=U?.data??null;M||F();const[o,r,s]=await Promise.all([Promise.resolve(q()),Promise.resolve($()),Se().catch(()=>[])]),l=P().getAllManagedProcesses(),m=Array.from(Ce.entries()).map(([p,g])=>({sessionId:p,agentId:g.agentId,workspacePath:g.workspacePath,runtimeStatus:g.currentRuntimeStatus||"running",runtimeActionType:g.currentRuntimeActionType,runtimeActionLabel:g.currentRuntimeActionLabel,providerId:g.providerId}));let h={managed:0,orphan:0,unknown:0},w=[];if(a){h={managed:a.filter(c=>c.managementStatus==="managed").length,orphan:a.filter(c=>c.managementStatus==="orphan").length,unknown:a.filter(c=>c.managementStatus==="unknown").length};const p=new Map(l.map(c=>[c.pid,c]));w=a.map(c=>{const I=p.get(c.pid);return{agentId:c.agentId||I?.agentId,pid:c.pid,managementStatus:c.managementStatus,sessionId:I?.sessionId,state:I?.state,command:c.command,cwd:c.cwd,startedAt:c.startedAt}});const g={managed:0,orphan:1,unknown:2};w.sort((c,I)=>(g[c.managementStatus]??9)-(g[I.managementStatus]??9))}const i=new Set(m.map(p=>p.sessionId)),u=s.filter(p=>!i.has(p.sessionId)),b=o,S=await Ne(),E={ok:!0,collectedAt:new Date().toISOString(),bridge:{version:Q,hostname:B.hostname(),platform:B.platform(),arch:B.arch(),uptime:Math.floor(process.uptime()),nodeVersion:process.version},binding:{status:b.status,paired:b.paired,instanceId:b.instanceId,userId:b.userId,schedulerBaseUrl:b.schedulerBaseUrl,pairingCode:xe()},registration:{registered:r.registered,instanceId:r.instanceId,lastAttempt:r.lastAttempt?.toISOString(),error:r.error},metrics:n,sessions:{active:m,activeCount:m.length,persisted:u,persistedCount:u.length,total:m.length+u.length},processes:{...h,total:h.managed+h.orphan+h.unknown,list:w,registryCount:l.length},lifecycleOperation:S};N={data:E,ts:Date.now()},e.json(E)}catch(n){const a=n;x.error("Dashboard aggregation failed:",a.message),e.status(500).json({ok:!1,error:a.message})}}}),f.get("/login",(t,e)=>{e.type("text/html; charset=utf-8"),e.send(Fe)}),f.get("/",(t,e)=>{const n=L(t);if(!n||!H(n)){e.redirect("/login");return}if(!R()){e.redirect("/setup");return}const a=t.query.lang==="en"?"en":"zh";e.type("text/html; charset=utf-8"),e.send(Me.replace("{{LANG}}",a))}),f.get("/setup",(t,e)=>{const n=L(t);if(!n||!H(n)){e.redirect("/login");return}e.type("text/html; charset=utf-8"),e.send(_e)});const Me=`<!DOCTYPE html>
|
|
2
2
|
<html lang="{{LANG}}">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>Bridge Dashboard</title>
|
|
7
|
+
${O}
|
|
8
|
+
${z}
|
|
7
9
|
<style>
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
--orange: #f0a030;
|
|
19
|
-
--green: #43c6a8;
|
|
20
|
-
--green-bg: rgba(67,198,168,0.12);
|
|
21
|
-
--red-bg: rgba(240,84,84,0.12);
|
|
22
|
-
--orange-bg: rgba(240,160,48,0.12);
|
|
23
|
-
--radius: 10px;
|
|
10
|
+
/* \u8BBE\u8BA1\u4EE4\u724C\u89C1 dashboard-theme.ts\uFF1A\u9ED8\u8BA4\u6DF1\u8272\uFF0Chtml.light-theme \u5207\u6D45\u8272 */
|
|
11
|
+
${j}
|
|
12
|
+
|
|
13
|
+
:root { --topbar-h: 56px; }
|
|
14
|
+
|
|
15
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
16
|
+
|
|
17
|
+
html {
|
|
18
|
+
scrollbar-width: thin;
|
|
19
|
+
scrollbar-color: var(--border-strong) transparent;
|
|
24
20
|
}
|
|
25
|
-
|
|
21
|
+
html::-webkit-scrollbar { width: 10px; height: 10px; }
|
|
22
|
+
html::-webkit-scrollbar-track { background: transparent; }
|
|
23
|
+
html::-webkit-scrollbar-thumb {
|
|
24
|
+
background: var(--border-strong);
|
|
25
|
+
border: 2px solid var(--bg);
|
|
26
|
+
border-radius: 5px;
|
|
27
|
+
background-clip: content-box;
|
|
28
|
+
}
|
|
29
|
+
html::-webkit-scrollbar-thumb:hover { background: var(--text-3); background-clip: content-box; }
|
|
30
|
+
|
|
26
31
|
body {
|
|
27
|
-
font-family: -
|
|
32
|
+
font-family: var(--font-sans);
|
|
28
33
|
background: var(--bg);
|
|
29
34
|
color: var(--text);
|
|
30
|
-
padding: 24px;
|
|
31
35
|
min-height: 100vh;
|
|
32
|
-
|
|
33
|
-
|
|
36
|
+
font-size: 14px;
|
|
37
|
+
line-height: 1.5;
|
|
38
|
+
-webkit-font-smoothing: antialiased;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
42
|
+
\u9876\u680F
|
|
43
|
+
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */
|
|
44
|
+
.topbar {
|
|
45
|
+
position: sticky;
|
|
46
|
+
top: 0;
|
|
47
|
+
z-index: 50;
|
|
48
|
+
height: var(--topbar-h);
|
|
34
49
|
display: flex;
|
|
35
50
|
align-items: center;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
51
|
+
gap: 16px;
|
|
52
|
+
padding: 0 24px;
|
|
53
|
+
background: color-mix(in srgb, var(--bg) 82%, transparent);
|
|
54
|
+
backdrop-filter: blur(12px);
|
|
55
|
+
border-bottom: 1px solid var(--border);
|
|
40
56
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
font-weight: 600;
|
|
57
|
+
|
|
58
|
+
.brand {
|
|
44
59
|
display: flex;
|
|
45
60
|
align-items: center;
|
|
46
61
|
gap: 10px;
|
|
62
|
+
font-size: 15px;
|
|
63
|
+
font-weight: 650;
|
|
64
|
+
letter-spacing: -0.01em;
|
|
65
|
+
white-space: nowrap;
|
|
47
66
|
}
|
|
48
|
-
.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
border-radius:
|
|
52
|
-
|
|
53
|
-
|
|
67
|
+
.brand-mark {
|
|
68
|
+
width: 22px;
|
|
69
|
+
height: 22px;
|
|
70
|
+
border-radius: 6px;
|
|
71
|
+
object-fit: contain;
|
|
72
|
+
flex-shrink: 0;
|
|
54
73
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
74
|
+
|
|
75
|
+
.topbar-meta {
|
|
76
|
+
display: flex;
|
|
77
|
+
align-items: center;
|
|
78
|
+
gap: 8px;
|
|
79
|
+
font-size: 12px;
|
|
80
|
+
color: var(--text-3);
|
|
81
|
+
min-width: 0;
|
|
82
|
+
}
|
|
83
|
+
.meta-sep { opacity: 0.4; }
|
|
84
|
+
.meta-host {
|
|
85
|
+
font-family: var(--font-mono);
|
|
86
|
+
font-size: 11.5px;
|
|
87
|
+
overflow: hidden;
|
|
88
|
+
text-overflow: ellipsis;
|
|
89
|
+
white-space: nowrap;
|
|
90
|
+
max-width: 180px;
|
|
58
91
|
}
|
|
59
|
-
|
|
92
|
+
|
|
93
|
+
.topbar-spacer { flex: 1; }
|
|
94
|
+
|
|
95
|
+
.topbar-actions {
|
|
60
96
|
display: flex;
|
|
61
97
|
align-items: center;
|
|
62
|
-
gap:
|
|
98
|
+
gap: 6px;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.version-area {
|
|
102
|
+
display: inline-flex;
|
|
103
|
+
align-items: center;
|
|
104
|
+
gap: 6px;
|
|
105
|
+
font-size: 12px;
|
|
106
|
+
color: var(--text-2);
|
|
107
|
+
font-variant-numeric: tabular-nums;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.icon-btn {
|
|
111
|
+
display: inline-flex;
|
|
112
|
+
align-items: center;
|
|
113
|
+
justify-content: center;
|
|
114
|
+
gap: 6px;
|
|
115
|
+
min-width: 32px;
|
|
116
|
+
height: 32px;
|
|
117
|
+
padding: 0 10px;
|
|
118
|
+
background: transparent;
|
|
119
|
+
border: 1px solid transparent;
|
|
120
|
+
border-radius: var(--radius-sm);
|
|
121
|
+
color: var(--text-2);
|
|
63
122
|
font-size: 13px;
|
|
64
|
-
|
|
123
|
+
font-family: inherit;
|
|
124
|
+
cursor: pointer;
|
|
125
|
+
transition: background .15s, color .15s, border-color .15s;
|
|
65
126
|
}
|
|
66
|
-
.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
border: 1px solid var(--border);
|
|
127
|
+
.icon-btn:hover {
|
|
128
|
+
background: var(--surface-2);
|
|
129
|
+
border-color: var(--border);
|
|
70
130
|
color: var(--text);
|
|
71
|
-
padding: 6px 14px;
|
|
72
|
-
border-radius: 6px;
|
|
73
|
-
cursor: pointer;
|
|
74
|
-
font-size: 13px;
|
|
75
|
-
transition: background .15s;
|
|
76
131
|
}
|
|
77
|
-
.
|
|
78
|
-
|
|
132
|
+
.icon-btn:focus-visible {
|
|
133
|
+
outline: 2px solid var(--accent);
|
|
134
|
+
outline-offset: 1px;
|
|
79
135
|
}
|
|
80
|
-
.
|
|
81
|
-
background: var(--
|
|
82
|
-
border-color: var(--
|
|
136
|
+
.icon-btn.solid {
|
|
137
|
+
background: var(--surface-2);
|
|
138
|
+
border-color: var(--border);
|
|
139
|
+
color: var(--text);
|
|
83
140
|
}
|
|
141
|
+
.icon-btn.solid:hover { background: var(--surface-3); }
|
|
84
142
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
143
|
+
/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
144
|
+
\u4E3B\u5185\u5BB9\u533A
|
|
145
|
+
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */
|
|
146
|
+
.page {
|
|
147
|
+
max-width: 1320px;
|
|
148
|
+
margin: 0 auto;
|
|
149
|
+
padding: 28px 24px 64px;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.page-head {
|
|
153
|
+
display: flex;
|
|
154
|
+
align-items: flex-end;
|
|
155
|
+
justify-content: space-between;
|
|
88
156
|
gap: 16px;
|
|
89
157
|
margin-bottom: 20px;
|
|
158
|
+
flex-wrap: wrap;
|
|
90
159
|
}
|
|
91
|
-
.
|
|
160
|
+
.page-title {
|
|
161
|
+
font-size: 24px;
|
|
162
|
+
font-weight: 680;
|
|
163
|
+
letter-spacing: -0.02em;
|
|
164
|
+
line-height: 1.2;
|
|
165
|
+
}
|
|
166
|
+
.page-sub {
|
|
167
|
+
font-size: 13px;
|
|
168
|
+
color: var(--text-2);
|
|
169
|
+
margin-top: 4px;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
173
|
+
\u5065\u5EB7\u72B6\u6001\u603B\u89C8\uFF08hero\uFF09
|
|
174
|
+
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */
|
|
175
|
+
.hero {
|
|
92
176
|
background: var(--surface);
|
|
93
177
|
border: 1px solid var(--border);
|
|
94
|
-
border-radius: var(--radius);
|
|
95
|
-
|
|
178
|
+
border-radius: var(--radius-lg);
|
|
179
|
+
box-shadow: var(--shadow-1);
|
|
180
|
+
overflow: hidden;
|
|
181
|
+
margin-bottom: 20px;
|
|
96
182
|
}
|
|
97
|
-
|
|
98
|
-
|
|
183
|
+
|
|
184
|
+
.hero-body {
|
|
185
|
+
display: grid;
|
|
186
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
187
|
+
gap: 1px;
|
|
188
|
+
background: var(--border);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.hero-cell {
|
|
192
|
+
background: var(--surface);
|
|
193
|
+
padding: 18px 20px;
|
|
194
|
+
min-width: 0;
|
|
195
|
+
}
|
|
196
|
+
.hero-cell-label {
|
|
197
|
+
display: flex;
|
|
198
|
+
align-items: center;
|
|
199
|
+
gap: 6px;
|
|
200
|
+
font-size: 11px;
|
|
99
201
|
font-weight: 600;
|
|
202
|
+
letter-spacing: 0.06em;
|
|
100
203
|
text-transform: uppercase;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
204
|
+
color: var(--text-3);
|
|
205
|
+
margin-bottom: 8px;
|
|
206
|
+
}
|
|
207
|
+
.hero-cell-value {
|
|
104
208
|
display: flex;
|
|
105
209
|
align-items: center;
|
|
106
210
|
gap: 8px;
|
|
211
|
+
font-size: 17px;
|
|
212
|
+
font-weight: 620;
|
|
213
|
+
letter-spacing: -0.01em;
|
|
214
|
+
line-height: 1.3;
|
|
215
|
+
min-width: 0;
|
|
216
|
+
}
|
|
217
|
+
.hero-cell-value > span:last-child {
|
|
218
|
+
overflow: hidden;
|
|
219
|
+
text-overflow: ellipsis;
|
|
220
|
+
white-space: nowrap;
|
|
221
|
+
}
|
|
222
|
+
.hero-cell-detail {
|
|
223
|
+
margin-top: 6px;
|
|
224
|
+
font-size: 12px;
|
|
225
|
+
color: var(--text-3);
|
|
226
|
+
font-family: var(--font-mono);
|
|
227
|
+
overflow: hidden;
|
|
228
|
+
text-overflow: ellipsis;
|
|
229
|
+
white-space: nowrap;
|
|
107
230
|
}
|
|
108
231
|
|
|
109
|
-
|
|
232
|
+
/* \u72B6\u6001\u70B9 */
|
|
233
|
+
.dot {
|
|
234
|
+
width: 8px;
|
|
235
|
+
height: 8px;
|
|
236
|
+
border-radius: 50%;
|
|
237
|
+
flex-shrink: 0;
|
|
238
|
+
background: var(--text-3);
|
|
239
|
+
}
|
|
240
|
+
.dot.ok { background: var(--ok); box-shadow: 0 0 0 3px var(--ok-soft); }
|
|
241
|
+
.dot.warn { background: var(--warn); box-shadow: 0 0 0 3px var(--warn-soft); }
|
|
242
|
+
.dot.danger { background: var(--danger); box-shadow: 0 0 0 3px var(--danger-soft); }
|
|
243
|
+
.dot.pulse { animation: pulse 2.4s ease-in-out infinite; }
|
|
244
|
+
@keyframes pulse {
|
|
245
|
+
0%, 100% { opacity: 1; }
|
|
246
|
+
50% { opacity: 0.35; }
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/* \u9700\u8981\u5904\u7406\u7684\u4E8B\u9879 */
|
|
250
|
+
.attention {
|
|
110
251
|
display: flex;
|
|
111
|
-
justify-content: space-between;
|
|
112
252
|
align-items: center;
|
|
113
|
-
|
|
253
|
+
gap: 12px;
|
|
254
|
+
padding: 14px 20px;
|
|
255
|
+
background: var(--warn-soft);
|
|
256
|
+
border-top: 1px solid color-mix(in srgb, var(--warn) 30%, transparent);
|
|
114
257
|
font-size: 13px;
|
|
115
|
-
border-bottom: 1px solid var(--border);
|
|
116
258
|
}
|
|
117
|
-
.
|
|
118
|
-
.
|
|
119
|
-
|
|
259
|
+
.attention[hidden] { display: none; }
|
|
260
|
+
.attention-icon {
|
|
261
|
+
width: 20px;
|
|
262
|
+
height: 20px;
|
|
263
|
+
border-radius: 50%;
|
|
264
|
+
background: var(--warn);
|
|
265
|
+
color: #0b0d13;
|
|
266
|
+
display: grid;
|
|
267
|
+
place-items: center;
|
|
268
|
+
font-size: 12px;
|
|
269
|
+
font-weight: 800;
|
|
270
|
+
flex-shrink: 0;
|
|
271
|
+
}
|
|
272
|
+
.attention-text { flex: 1; min-width: 0; color: var(--text); }
|
|
273
|
+
.attention-text b { font-weight: 650; }
|
|
120
274
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
.
|
|
275
|
+
/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
276
|
+
KPI \u78C1\u8D34
|
|
277
|
+
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */
|
|
278
|
+
.kpi-grid {
|
|
279
|
+
display: grid;
|
|
280
|
+
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
281
|
+
gap: 12px;
|
|
282
|
+
margin-bottom: 28px;
|
|
283
|
+
}
|
|
284
|
+
.kpi {
|
|
285
|
+
background: var(--surface);
|
|
286
|
+
border: 1px solid var(--border);
|
|
287
|
+
border-radius: var(--radius);
|
|
288
|
+
padding: 16px 18px;
|
|
289
|
+
min-width: 0;
|
|
290
|
+
transition: border-color .15s;
|
|
291
|
+
}
|
|
292
|
+
.kpi:hover { border-color: var(--border-strong); }
|
|
293
|
+
.kpi-label {
|
|
294
|
+
font-size: 12px;
|
|
295
|
+
color: var(--text-2);
|
|
296
|
+
margin-bottom: 6px;
|
|
297
|
+
display: flex;
|
|
298
|
+
align-items: center;
|
|
299
|
+
gap: 6px;
|
|
300
|
+
}
|
|
301
|
+
.kpi-value {
|
|
302
|
+
font-size: 26px;
|
|
303
|
+
font-weight: 680;
|
|
304
|
+
letter-spacing: -0.03em;
|
|
305
|
+
font-variant-numeric: tabular-nums;
|
|
306
|
+
line-height: 1.1;
|
|
307
|
+
}
|
|
308
|
+
.kpi-value.muted { color: var(--text-3); }
|
|
309
|
+
.kpi-value.ok { color: var(--ok); }
|
|
310
|
+
.kpi-value.warn { color: var(--warn); }
|
|
311
|
+
.kpi-foot {
|
|
312
|
+
font-size: 11.5px;
|
|
313
|
+
color: var(--text-3);
|
|
314
|
+
margin-top: 4px;
|
|
315
|
+
overflow: hidden;
|
|
316
|
+
text-overflow: ellipsis;
|
|
317
|
+
white-space: nowrap;
|
|
318
|
+
}
|
|
125
319
|
|
|
320
|
+
/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
321
|
+
\u533A\u5757\u6807\u9898
|
|
322
|
+
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */
|
|
323
|
+
.section {
|
|
324
|
+
margin-bottom: 28px;
|
|
325
|
+
}
|
|
326
|
+
.section-head {
|
|
327
|
+
display: flex;
|
|
328
|
+
align-items: center;
|
|
329
|
+
gap: 8px;
|
|
330
|
+
margin-bottom: 12px;
|
|
331
|
+
min-height: 24px;
|
|
332
|
+
}
|
|
126
333
|
.section-title {
|
|
127
334
|
font-size: 15px;
|
|
128
|
-
font-weight:
|
|
129
|
-
|
|
335
|
+
font-weight: 640;
|
|
336
|
+
letter-spacing: -0.01em;
|
|
337
|
+
}
|
|
338
|
+
.count {
|
|
339
|
+
font-size: 12px;
|
|
340
|
+
font-weight: 500;
|
|
341
|
+
color: var(--text-3);
|
|
342
|
+
font-variant-numeric: tabular-nums;
|
|
343
|
+
}
|
|
344
|
+
.section-head .spacer { flex: 1; }
|
|
345
|
+
|
|
346
|
+
.toggle-header {
|
|
347
|
+
cursor: pointer;
|
|
348
|
+
user-select: none;
|
|
349
|
+
display: inline-flex;
|
|
350
|
+
align-items: center;
|
|
351
|
+
gap: 6px;
|
|
352
|
+
border-radius: var(--radius-sm);
|
|
353
|
+
padding: 2px 4px;
|
|
354
|
+
margin-left: -4px;
|
|
355
|
+
transition: background .15s;
|
|
356
|
+
}
|
|
357
|
+
.toggle-header:hover { background: var(--surface-2); }
|
|
358
|
+
.toggle-icon {
|
|
359
|
+
display: inline-block;
|
|
360
|
+
font-size: 9px;
|
|
361
|
+
color: var(--text-3);
|
|
362
|
+
transition: transform .2s;
|
|
363
|
+
}
|
|
364
|
+
.toggle-icon.open { transform: rotate(90deg); }
|
|
365
|
+
|
|
366
|
+
/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
367
|
+
\u5361\u7247
|
|
368
|
+
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */
|
|
369
|
+
.card {
|
|
370
|
+
background: var(--surface);
|
|
371
|
+
border: 1px solid var(--border);
|
|
372
|
+
border-radius: var(--radius);
|
|
373
|
+
box-shadow: var(--shadow-1);
|
|
374
|
+
overflow: hidden;
|
|
375
|
+
}
|
|
376
|
+
.card-head {
|
|
130
377
|
display: flex;
|
|
131
378
|
align-items: center;
|
|
132
379
|
gap: 8px;
|
|
380
|
+
padding: 14px 18px;
|
|
381
|
+
border-bottom: 1px solid var(--border);
|
|
133
382
|
}
|
|
134
|
-
.
|
|
383
|
+
.card-title {
|
|
384
|
+
font-size: 13px;
|
|
385
|
+
font-weight: 620;
|
|
386
|
+
letter-spacing: -0.01em;
|
|
387
|
+
}
|
|
388
|
+
.card-title .unit {
|
|
389
|
+
font-size: 11.5px;
|
|
390
|
+
font-weight: 500;
|
|
391
|
+
color: var(--text-3);
|
|
392
|
+
margin-left: 6px;
|
|
393
|
+
font-family: var(--font-mono);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/* \u8D44\u6E90\u4EEA\u8868 */
|
|
397
|
+
.gauges {
|
|
398
|
+
display: grid;
|
|
399
|
+
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
400
|
+
gap: 1px;
|
|
401
|
+
background: var(--border);
|
|
402
|
+
}
|
|
403
|
+
.gauge {
|
|
404
|
+
background: var(--surface);
|
|
405
|
+
padding: 16px 18px;
|
|
406
|
+
min-width: 0;
|
|
407
|
+
}
|
|
408
|
+
.gauge-head {
|
|
409
|
+
display: flex;
|
|
410
|
+
align-items: baseline;
|
|
411
|
+
justify-content: space-between;
|
|
412
|
+
gap: 8px;
|
|
413
|
+
margin-bottom: 10px;
|
|
414
|
+
}
|
|
415
|
+
.gauge-name {
|
|
416
|
+
font-size: 11px;
|
|
417
|
+
font-weight: 620;
|
|
418
|
+
letter-spacing: 0.06em;
|
|
419
|
+
text-transform: uppercase;
|
|
420
|
+
color: var(--text-3);
|
|
421
|
+
}
|
|
422
|
+
.gauge-reading {
|
|
423
|
+
font-size: 19px;
|
|
424
|
+
font-weight: 680;
|
|
425
|
+
letter-spacing: -0.02em;
|
|
426
|
+
font-variant-numeric: tabular-nums;
|
|
427
|
+
line-height: 1;
|
|
428
|
+
}
|
|
429
|
+
.gauge-reading.muted { font-size: 15px; color: var(--text-2); font-weight: 600; }
|
|
430
|
+
|
|
431
|
+
.bar {
|
|
432
|
+
height: 5px;
|
|
433
|
+
background: var(--surface-3);
|
|
434
|
+
border-radius: 3px;
|
|
435
|
+
overflow: hidden;
|
|
436
|
+
margin-bottom: 12px;
|
|
437
|
+
}
|
|
438
|
+
.bar-placeholder { background: transparent; }
|
|
439
|
+
.bar-fill {
|
|
440
|
+
height: 100%;
|
|
441
|
+
border-radius: 3px;
|
|
442
|
+
transition: width .45s cubic-bezier(.4, 0, .2, 1);
|
|
443
|
+
}
|
|
444
|
+
.bar-fill.green { background: var(--ok); }
|
|
445
|
+
.bar-fill.orange { background: var(--warn); }
|
|
446
|
+
.bar-fill.red { background: var(--danger); }
|
|
447
|
+
|
|
448
|
+
.gauge-lines {
|
|
449
|
+
display: flex;
|
|
450
|
+
flex-direction: column;
|
|
451
|
+
gap: 5px;
|
|
452
|
+
}
|
|
453
|
+
.gauge-line {
|
|
454
|
+
display: flex;
|
|
455
|
+
justify-content: space-between;
|
|
456
|
+
gap: 10px;
|
|
135
457
|
font-size: 12px;
|
|
136
|
-
|
|
137
|
-
|
|
458
|
+
min-width: 0;
|
|
459
|
+
}
|
|
460
|
+
.gauge-line dt { color: var(--text-3); flex-shrink: 0; }
|
|
461
|
+
.gauge-line dd {
|
|
462
|
+
color: var(--text-2);
|
|
463
|
+
font-variant-numeric: tabular-nums;
|
|
464
|
+
text-align: right;
|
|
465
|
+
overflow: hidden;
|
|
466
|
+
text-overflow: ellipsis;
|
|
467
|
+
white-space: nowrap;
|
|
138
468
|
}
|
|
139
469
|
|
|
470
|
+
/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
471
|
+
\u8868\u683C
|
|
472
|
+
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */
|
|
140
473
|
.table-wrap {
|
|
141
474
|
background: var(--surface);
|
|
142
475
|
border: 1px solid var(--border);
|
|
143
476
|
border-radius: var(--radius);
|
|
477
|
+
box-shadow: var(--shadow-1);
|
|
144
478
|
overflow-x: auto;
|
|
145
479
|
}
|
|
146
480
|
table {
|
|
@@ -148,251 +482,368 @@ import{execFile as X}from"node:child_process";import v from"node:fs";import k fr
|
|
|
148
482
|
border-collapse: collapse;
|
|
149
483
|
font-size: 13px;
|
|
150
484
|
}
|
|
151
|
-
th {
|
|
485
|
+
thead th {
|
|
152
486
|
text-align: left;
|
|
153
|
-
padding: 10px
|
|
487
|
+
padding: 10px 16px;
|
|
154
488
|
font-size: 11px;
|
|
155
|
-
font-weight:
|
|
489
|
+
font-weight: 620;
|
|
490
|
+
letter-spacing: 0.05em;
|
|
156
491
|
text-transform: uppercase;
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
background: var(--surface2);
|
|
492
|
+
color: var(--text-3);
|
|
493
|
+
background: var(--bg-elevated);
|
|
160
494
|
border-bottom: 1px solid var(--border);
|
|
161
495
|
white-space: nowrap;
|
|
496
|
+
position: sticky;
|
|
497
|
+
top: 0;
|
|
498
|
+
z-index: 1;
|
|
162
499
|
}
|
|
163
|
-
td {
|
|
164
|
-
padding:
|
|
500
|
+
tbody td {
|
|
501
|
+
padding: 11px 16px;
|
|
165
502
|
border-bottom: 1px solid var(--border);
|
|
166
503
|
white-space: nowrap;
|
|
504
|
+
color: var(--text-2);
|
|
505
|
+
}
|
|
506
|
+
tbody tr:last-child td { border-bottom: none; }
|
|
507
|
+
tbody tr { transition: background .12s; }
|
|
508
|
+
tbody tr:hover { background: var(--surface-2); }
|
|
509
|
+
tbody td.strong { color: var(--text); font-weight: 550; }
|
|
510
|
+
code, .mono {
|
|
511
|
+
font-family: var(--font-mono);
|
|
512
|
+
font-size: 12px;
|
|
513
|
+
color: var(--text);
|
|
167
514
|
}
|
|
168
|
-
tr:last-child td { border-bottom: none; }
|
|
169
|
-
tr:hover { background: rgba(255,255,255,0.03); }
|
|
170
515
|
|
|
171
|
-
|
|
516
|
+
/* \u7A7A\u72B6\u6001 */
|
|
517
|
+
.empty {
|
|
172
518
|
display: flex;
|
|
519
|
+
flex-direction: column;
|
|
173
520
|
align-items: center;
|
|
174
|
-
gap:
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
background: var(--surface2);
|
|
180
|
-
border-radius: 3px;
|
|
181
|
-
overflow: hidden;
|
|
182
|
-
}
|
|
183
|
-
.bar-fill {
|
|
184
|
-
height: 100%;
|
|
185
|
-
border-radius: 3px;
|
|
186
|
-
transition: width 0.5s ease;
|
|
521
|
+
gap: 6px;
|
|
522
|
+
padding: 40px 24px;
|
|
523
|
+
text-align: center;
|
|
524
|
+
color: var(--text-3);
|
|
525
|
+
font-size: 13px;
|
|
187
526
|
}
|
|
188
|
-
.
|
|
189
|
-
.
|
|
190
|
-
.bar-fill.red { background: var(--red); }
|
|
191
|
-
.bar-label { font-size: 12px; min-width: 48px; text-align: right; font-variant-numeric: tabular-nums; }
|
|
527
|
+
.empty-icon { font-size: 22px; opacity: 0.5; line-height: 1; }
|
|
528
|
+
.empty-hint { font-size: 12px; color: var(--text-3); opacity: 0.8; }
|
|
192
529
|
|
|
530
|
+
/* \u6807\u7B7E */
|
|
193
531
|
.tag {
|
|
194
|
-
display: inline-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
532
|
+
display: inline-flex;
|
|
533
|
+
align-items: center;
|
|
534
|
+
gap: 5px;
|
|
535
|
+
padding: 2px 8px;
|
|
536
|
+
border-radius: 20px;
|
|
537
|
+
font-size: 11.5px;
|
|
198
538
|
font-weight: 600;
|
|
539
|
+
white-space: nowrap;
|
|
199
540
|
}
|
|
200
|
-
.tag
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
.
|
|
209
|
-
.
|
|
210
|
-
|
|
541
|
+
.tag::before {
|
|
542
|
+
content: "";
|
|
543
|
+
width: 5px;
|
|
544
|
+
height: 5px;
|
|
545
|
+
border-radius: 50%;
|
|
546
|
+
background: currentColor;
|
|
547
|
+
flex-shrink: 0;
|
|
548
|
+
}
|
|
549
|
+
.tag.managed, .tag.green { background: var(--ok-soft); color: var(--ok); }
|
|
550
|
+
.tag.orphan, .tag.orange { background: var(--warn-soft); color: var(--warn); }
|
|
551
|
+
.tag.unknown, .tag.gray { background: var(--surface-3); color: var(--text-2); }
|
|
552
|
+
.tag.red { background: var(--danger-soft); color: var(--danger); }
|
|
553
|
+
.tag.blue { background: var(--info-soft); color: var(--info); }
|
|
554
|
+
.tag.plain::before { display: none; }
|
|
555
|
+
|
|
556
|
+
/* \u72B6\u6001\u6587\u5B57 */
|
|
557
|
+
.status-green { color: var(--ok); font-weight: 550; }
|
|
558
|
+
.status-orange { color: var(--warn); font-weight: 550; }
|
|
559
|
+
.status-red { color: var(--danger); font-weight: 550; }
|
|
560
|
+
.status-muted { color: var(--text-3); }
|
|
561
|
+
|
|
562
|
+
/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
563
|
+
\u5B64\u513F\u8FDB\u7A0B\u544A\u8B66
|
|
564
|
+
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */
|
|
211
565
|
.orphan-banner {
|
|
212
|
-
background: var(--orange-bg);
|
|
213
|
-
border: 1px solid var(--orange);
|
|
214
|
-
border-radius: var(--radius);
|
|
215
|
-
padding: 12px 16px;
|
|
216
|
-
margin-bottom: 12px;
|
|
217
|
-
font-size: 13px;
|
|
218
566
|
display: flex;
|
|
219
567
|
align-items: center;
|
|
220
568
|
gap: 10px;
|
|
569
|
+
padding: 12px 16px;
|
|
570
|
+
margin-bottom: 10px;
|
|
571
|
+
background: var(--warn-soft);
|
|
572
|
+
border: 1px solid color-mix(in srgb, var(--warn) 32%, transparent);
|
|
573
|
+
border-radius: var(--radius);
|
|
574
|
+
font-size: 13px;
|
|
575
|
+
}
|
|
576
|
+
.orphan-banner .icon { font-size: 15px; flex-shrink: 0; }
|
|
577
|
+
.orphan-banner .text { flex: 1; min-width: 0; }
|
|
578
|
+
.orphan-banner .action-hint {
|
|
579
|
+
color: var(--warn);
|
|
580
|
+
font-weight: 620;
|
|
581
|
+
font-size: 12px;
|
|
582
|
+
white-space: nowrap;
|
|
221
583
|
}
|
|
222
|
-
.orphan-banner .icon { font-size: 16px; }
|
|
223
|
-
.orphan-banner .text { flex: 1; }
|
|
224
|
-
.orphan-banner .action-hint { color: var(--orange); font-weight: 600; }
|
|
225
584
|
|
|
226
|
-
|
|
227
|
-
|
|
585
|
+
/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
586
|
+
\u6309\u94AE
|
|
587
|
+
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */
|
|
588
|
+
.btn {
|
|
589
|
+
display: inline-flex;
|
|
590
|
+
align-items: center;
|
|
591
|
+
justify-content: center;
|
|
592
|
+
gap: 6px;
|
|
593
|
+
padding: 7px 14px;
|
|
594
|
+
border-radius: var(--radius-sm);
|
|
228
595
|
border: 1px solid var(--border);
|
|
596
|
+
background: var(--surface-2);
|
|
229
597
|
color: var(--text);
|
|
230
|
-
|
|
231
|
-
|
|
598
|
+
font-family: inherit;
|
|
599
|
+
font-size: 12.5px;
|
|
600
|
+
font-weight: 550;
|
|
232
601
|
cursor: pointer;
|
|
233
|
-
|
|
234
|
-
|
|
602
|
+
transition: background .15s, border-color .15s, color .15s, opacity .15s;
|
|
603
|
+
white-space: nowrap;
|
|
235
604
|
}
|
|
236
|
-
.
|
|
237
|
-
.
|
|
238
|
-
.
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
align-items: center;
|
|
244
|
-
margin-bottom: 12px;
|
|
245
|
-
font-size: 13px;
|
|
246
|
-
flex-wrap: wrap;
|
|
605
|
+
.btn:hover { background: var(--surface-3); border-color: var(--border-strong); }
|
|
606
|
+
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
|
|
607
|
+
.btn:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
608
|
+
.btn-primary {
|
|
609
|
+
background: var(--accent);
|
|
610
|
+
border-color: var(--accent);
|
|
611
|
+
color: #fff;
|
|
247
612
|
}
|
|
248
|
-
.
|
|
249
|
-
.
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
613
|
+
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
|
|
614
|
+
.btn-ghost { background: transparent; border-color: transparent; color: var(--text-2); }
|
|
615
|
+
.btn-ghost:hover { background: var(--surface-2); color: var(--text); border-color: transparent; }
|
|
616
|
+
.btn-danger { border-color: color-mix(in srgb, var(--danger) 45%, transparent); color: var(--danger); background: transparent; }
|
|
617
|
+
.btn-danger:hover { background: var(--danger-soft); border-color: var(--danger); }
|
|
618
|
+
.btn-sm { padding: 3px 10px; font-size: 11.5px; }
|
|
619
|
+
|
|
620
|
+
.btn-kill {
|
|
621
|
+
padding: 3px 10px;
|
|
622
|
+
border-radius: var(--radius-sm);
|
|
623
|
+
font-size: 11.5px;
|
|
624
|
+
font-weight: 600;
|
|
625
|
+
cursor: pointer;
|
|
626
|
+
border: 1px solid color-mix(in srgb, var(--danger) 45%, transparent);
|
|
627
|
+
background: transparent;
|
|
628
|
+
color: var(--danger);
|
|
629
|
+
transition: background .15s, border-color .15s;
|
|
253
630
|
}
|
|
254
|
-
.
|
|
255
|
-
.
|
|
256
|
-
.summary-line .dot.gray { background: var(--text2); }
|
|
631
|
+
.btn-kill:hover { background: var(--danger-soft); border-color: var(--danger); }
|
|
632
|
+
.btn-kill:disabled { opacity: 0.4; cursor: not-allowed; }
|
|
257
633
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
gap: 6px;
|
|
262
|
-
font-size: 13px;
|
|
263
|
-
color: var(--text2);
|
|
264
|
-
}
|
|
634
|
+
/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
635
|
+
\u66F4\u65B0\u6307\u793A\u5668
|
|
636
|
+
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */
|
|
265
637
|
.update-indicator {
|
|
266
638
|
font-size: 12px;
|
|
267
639
|
cursor: pointer;
|
|
268
640
|
white-space: nowrap;
|
|
269
641
|
transition: color .15s;
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
.update-indicator
|
|
274
|
-
.update-indicator.
|
|
275
|
-
.update-indicator.
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
.s-log-list,
|
|
308
|
-
.s-table-wrap {
|
|
309
|
-
scrollbar-width: thin;
|
|
310
|
-
scrollbar-color: color-mix(in srgb, var(--accent) 30%, transparent) transparent;
|
|
311
|
-
}
|
|
312
|
-
.table-wrap::-webkit-scrollbar,
|
|
313
|
-
.settings-content::-webkit-scrollbar,
|
|
314
|
-
.s-log-list::-webkit-scrollbar,
|
|
315
|
-
.s-table-wrap::-webkit-scrollbar {
|
|
316
|
-
width: 6px;
|
|
317
|
-
height: 6px;
|
|
318
|
-
}
|
|
319
|
-
.table-wrap::-webkit-scrollbar-track,
|
|
320
|
-
.settings-content::-webkit-scrollbar-track,
|
|
321
|
-
.s-log-list::-webkit-scrollbar-track,
|
|
322
|
-
.s-table-wrap::-webkit-scrollbar-track {
|
|
323
|
-
background: transparent;
|
|
324
|
-
}
|
|
325
|
-
.table-wrap::-webkit-scrollbar-thumb,
|
|
326
|
-
.settings-content::-webkit-scrollbar-thumb,
|
|
327
|
-
.s-log-list::-webkit-scrollbar-thumb,
|
|
328
|
-
.s-table-wrap::-webkit-scrollbar-thumb {
|
|
329
|
-
background: color-mix(in srgb, var(--accent) 30%, transparent);
|
|
330
|
-
border-radius: 3px;
|
|
331
|
-
}
|
|
332
|
-
.table-wrap::-webkit-scrollbar-thumb:hover,
|
|
333
|
-
.settings-content::-webkit-scrollbar-thumb:hover,
|
|
334
|
-
.s-log-list::-webkit-scrollbar-thumb:hover,
|
|
335
|
-
.s-table-wrap::-webkit-scrollbar-thumb:hover {
|
|
336
|
-
background: color-mix(in srgb, var(--accent) 50%, transparent);
|
|
642
|
+
border-radius: var(--radius-sm);
|
|
643
|
+
padding: 2px 6px;
|
|
644
|
+
}
|
|
645
|
+
.update-indicator:hover { background: var(--surface-2); }
|
|
646
|
+
.update-indicator.available { color: var(--accent); font-weight: 600; }
|
|
647
|
+
.update-indicator.installing { color: var(--warn); cursor: default; }
|
|
648
|
+
.update-indicator.success { color: var(--ok); }
|
|
649
|
+
.update-indicator.failed { color: var(--danger); }
|
|
650
|
+
|
|
651
|
+
/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
652
|
+
\u54CD\u5E94\u5F0F
|
|
653
|
+
\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */
|
|
654
|
+
@media (max-width: 1080px) {
|
|
655
|
+
.gauges { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
@media (max-width: 860px) {
|
|
659
|
+
.hero-body { grid-template-columns: 1fr; }
|
|
660
|
+
.kpi-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
661
|
+
.topbar { padding: 0 16px; gap: 10px; }
|
|
662
|
+
.topbar-meta { display: none; }
|
|
663
|
+
.page { padding: 20px 16px 48px; }
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
@media (max-width: 560px) {
|
|
667
|
+
:root { --topbar-h: 52px; }
|
|
668
|
+
body { font-size: 13.5px; }
|
|
669
|
+
.page { padding: 16px 12px 40px; }
|
|
670
|
+
.page-title { font-size: 19px; }
|
|
671
|
+
.kpi-grid { gap: 8px; }
|
|
672
|
+
.kpi { padding: 13px 14px; }
|
|
673
|
+
.kpi-value { font-size: 21px; }
|
|
674
|
+
.gauges { grid-template-columns: 1fr; }
|
|
675
|
+
.hero-cell { padding: 14px 16px; }
|
|
676
|
+
thead th, tbody td { padding: 9px 12px; }
|
|
677
|
+
.brand { font-size: 14px; }
|
|
678
|
+
.icon-btn { min-width: 30px; height: 30px; padding: 0 8px; }
|
|
337
679
|
}
|
|
338
680
|
</style>
|
|
339
681
|
</head>
|
|
340
682
|
<body>
|
|
341
683
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
<div class="
|
|
345
|
-
<
|
|
346
|
-
<span
|
|
684
|
+
<!-- \u2550\u2550 \u9876\u680F\uFF1A\u54C1\u724C + \u8FD0\u884C\u5143\u4FE1\u606F + \u5168\u5C40\u64CD\u4F5C \u2550\u2550 -->
|
|
685
|
+
<header class="topbar">
|
|
686
|
+
<div class="brand">
|
|
687
|
+
<img class="brand-mark" src="${Y}" alt="">
|
|
688
|
+
<span>AgentsWorkStudio</span>
|
|
689
|
+
</div>
|
|
690
|
+
<div class="topbar-meta">
|
|
691
|
+
<span class="meta-sep">/</span>
|
|
692
|
+
<span>Bridge</span>
|
|
693
|
+
<span class="meta-sep">\xB7</span>
|
|
694
|
+
<span class="meta-host" id="bridgeHostname">-</span>
|
|
695
|
+
</div>
|
|
696
|
+
<div class="topbar-spacer"></div>
|
|
697
|
+
<div class="topbar-actions">
|
|
347
698
|
<span class="version-area">
|
|
348
699
|
<span id="bridgeVersion">v-</span>
|
|
349
700
|
<span id="updateIndicator" style="display:none"></span>
|
|
350
701
|
</span>
|
|
351
|
-
<span class="
|
|
352
|
-
<
|
|
353
|
-
<button onclick="
|
|
354
|
-
<button onclick="refresh()" id="refreshBtn">↻</button>
|
|
702
|
+
<span class="icon-btn" id="themeBtn" onclick="toggleTheme()" title="\u5207\u6362\u4E3B\u9898">☽</span>
|
|
703
|
+
<span class="icon-btn" id="langBtn" onclick="toggleLang()" title="Switch language">\u4E2D\u6587</span>
|
|
704
|
+
<button class="icon-btn solid" onclick="openSettings()" id="settingsBtn" title="Settings / \u8BBE\u7F6E">⚙</button>
|
|
705
|
+
<button class="icon-btn" onclick="refresh()" id="refreshBtn" title="Refresh / \u5237\u65B0">↻</button>
|
|
706
|
+
<button class="icon-btn" onclick="doLogout()" id="logoutBtn" title="Logout / \u9000\u51FA\u767B\u5F55">⏻</button>
|
|
355
707
|
</div>
|
|
356
|
-
</
|
|
357
|
-
|
|
358
|
-
<div class="grid" id="statusCards"></div>
|
|
708
|
+
</header>
|
|
359
709
|
|
|
360
|
-
<
|
|
361
|
-
<div class="grid" id="metricsGrid"></div>
|
|
710
|
+
<main class="page">
|
|
362
711
|
|
|
363
|
-
<!--
|
|
364
|
-
<div class="
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
</div>
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
<div class="table-wrap" id="sessionTable"><div class="empty" id="loadingSessions">Loading...</div></div>
|
|
373
|
-
|
|
374
|
-
<!-- Orphan Processes (shown only when orphans exist) -->
|
|
375
|
-
<div id="orphanContainer" style="display:none">
|
|
376
|
-
<div class="section-title" id="sectionOrphans">Orphan Processes <span class="count" id="orphanCount"></span></div>
|
|
377
|
-
<div class="orphan-banner">
|
|
378
|
-
<span class="icon">⚠</span>
|
|
379
|
-
<span class="text" id="orphanBannerText">These processes are running but no longer managed by bridge</span>
|
|
380
|
-
<span class="action-hint">Terminate to clean up</span>
|
|
712
|
+
<!-- \u2550\u2550 \u9875\u9762\u6807\u9898 + \u5237\u65B0\u65F6\u95F4 \u2550\u2550 -->
|
|
713
|
+
<div class="page-head">
|
|
714
|
+
<div>
|
|
715
|
+
<div class="page-title" id="pageTitle">\u8FD0\u884C\u603B\u89C8</div>
|
|
716
|
+
<div class="page-sub" id="pageSub">\u672C\u673A Bridge \u72B6\u6001\u4E0E Agent \u4F1A\u8BDD</div>
|
|
717
|
+
</div>
|
|
718
|
+
<div class="page-sub">
|
|
719
|
+
<span id="refreshTime">-</span>
|
|
720
|
+
</div>
|
|
381
721
|
</div>
|
|
382
|
-
<div class="table-wrap" id="orphanTable"><div class="empty">No orphans</div></div>
|
|
383
|
-
</div>
|
|
384
722
|
|
|
385
|
-
<!--
|
|
386
|
-
<
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
723
|
+
<!-- \u2550\u2550 \u5065\u5EB7\u603B\u89C8\uFF1A\u4E09\u4E2A\u6700\u5173\u952E\u7684\u300C\u662F\u5426\u53EF\u7528\u300D\u5224\u65AD \u2550\u2550 -->
|
|
724
|
+
<section class="hero" aria-label="Bridge health">
|
|
725
|
+
<div class="hero-body">
|
|
726
|
+
<div class="hero-cell">
|
|
727
|
+
<div class="hero-cell-label" id="heroServiceLabel">Bridge \u670D\u52A1</div>
|
|
728
|
+
<div class="hero-cell-value">
|
|
729
|
+
<span class="dot ok pulse" id="heroServiceDot"></span>
|
|
730
|
+
<span id="heroServiceValue">\u8FD0\u884C\u4E2D</span>
|
|
731
|
+
</div>
|
|
732
|
+
<div class="hero-cell-detail" id="heroServiceDetail">-</div>
|
|
733
|
+
</div>
|
|
734
|
+
<div class="hero-cell">
|
|
735
|
+
<div class="hero-cell-label" id="heroRegLabel">\u8C03\u5EA6\u4E2D\u5FC3\u6CE8\u518C</div>
|
|
736
|
+
<div class="hero-cell-value">
|
|
737
|
+
<span class="dot" id="heroRegDot"></span>
|
|
738
|
+
<span id="heroRegValue">-</span>
|
|
739
|
+
</div>
|
|
740
|
+
<div class="hero-cell-detail" id="heroRegDetail">-</div>
|
|
741
|
+
</div>
|
|
742
|
+
<div class="hero-cell">
|
|
743
|
+
<div class="hero-cell-label" id="heroBindLabel">\u9762\u677F\u7ED1\u5B9A</div>
|
|
744
|
+
<div class="hero-cell-value">
|
|
745
|
+
<span class="dot" id="heroBindDot"></span>
|
|
746
|
+
<span id="heroBindValue">-</span>
|
|
747
|
+
</div>
|
|
748
|
+
<div class="hero-cell-detail" id="heroBindDetail">-</div>
|
|
749
|
+
</div>
|
|
750
|
+
</div>
|
|
751
|
+
<!-- \u9700\u8981\u7528\u6237\u5904\u7406\u7684\u4E8B\u9879\uFF1A\u9ED8\u8BA4\u9690\u85CF\uFF0C\u6709\u5185\u5BB9\u65F6\u624D\u663E\u793A -->
|
|
752
|
+
<div class="attention" id="attentionBar" hidden>
|
|
753
|
+
<span class="attention-icon">!</span>
|
|
754
|
+
<span class="attention-text" id="attentionText"></span>
|
|
755
|
+
<button class="btn btn-sm" id="attentionAction" style="display:none" onclick="runAttentionAction()"></button>
|
|
756
|
+
</div>
|
|
757
|
+
</section>
|
|
758
|
+
|
|
759
|
+
<!-- \u2550\u2550 KPI \u78C1\u8D34\uFF1A\u56DB\u4E2A\u6570\u5B57\uFF0C\u4E00\u773C\u770B\u5B8C \u2550\u2550 -->
|
|
760
|
+
<section class="kpi-grid" id="kpiGrid" aria-label="Key metrics">
|
|
761
|
+
<div class="kpi">
|
|
762
|
+
<div class="kpi-label" id="kpiActiveLabel">\u6D3B\u8DC3\u4F1A\u8BDD</div>
|
|
763
|
+
<div class="kpi-value muted" id="kpiActive">0</div>
|
|
764
|
+
<div class="kpi-foot" id="kpiActiveFoot">-</div>
|
|
765
|
+
</div>
|
|
766
|
+
<div class="kpi">
|
|
767
|
+
<div class="kpi-label" id="kpiPersistedLabel">\u6301\u4E45\u5316\u4F1A\u8BDD</div>
|
|
768
|
+
<div class="kpi-value" id="kpiPersisted">0</div>
|
|
769
|
+
<div class="kpi-foot" id="kpiPersistedFoot">-</div>
|
|
770
|
+
</div>
|
|
771
|
+
<div class="kpi">
|
|
772
|
+
<div class="kpi-label" id="kpiOrphanLabel">\u5B64\u513F\u8FDB\u7A0B</div>
|
|
773
|
+
<div class="kpi-value muted" id="kpiOrphan">0</div>
|
|
774
|
+
<div class="kpi-foot" id="kpiOrphanFoot">-</div>
|
|
775
|
+
</div>
|
|
776
|
+
<div class="kpi">
|
|
777
|
+
<div class="kpi-label" id="kpiUptimeLabel">\u8FD0\u884C\u65F6\u957F</div>
|
|
778
|
+
<div class="kpi-value" id="kpiUptime">-</div>
|
|
779
|
+
<div class="kpi-foot" id="kpiUptimeFoot">-</div>
|
|
780
|
+
</div>
|
|
781
|
+
</section>
|
|
782
|
+
|
|
783
|
+
<!-- \u2550\u2550 \u7CFB\u7EDF\u8D44\u6E90\uFF1A\u56DB\u4E2A\u4EEA\u8868\uFF0C\u6A2A\u5411\u5BF9\u9F50 \u2550\u2550 -->
|
|
784
|
+
<section class="section">
|
|
785
|
+
<div class="section-head">
|
|
786
|
+
<span class="section-title" id="sectionSysRes">\u7CFB\u7EDF\u8D44\u6E90</span>
|
|
787
|
+
<span class="spacer"></span>
|
|
788
|
+
<span class="count" id="sysMetricsCount"></span>
|
|
789
|
+
</div>
|
|
790
|
+
<div class="card" id="metricsCard">
|
|
791
|
+
<div class="gauges" id="metricsGrid"></div>
|
|
792
|
+
</div>
|
|
793
|
+
</section>
|
|
794
|
+
|
|
795
|
+
<!-- \u2550\u2550 \u6D3B\u8DC3\u4F1A\u8BDD \u2550\u2550 -->
|
|
796
|
+
<section class="section">
|
|
797
|
+
<div class="section-head">
|
|
798
|
+
<span class="section-title" id="sectionSessions">\u6D3B\u8DC3\u4F1A\u8BDD</span>
|
|
799
|
+
<span class="count" id="sessionCount"></span>
|
|
800
|
+
<span class="spacer"></span>
|
|
801
|
+
<!-- \u8FDB\u7A0B\u5206\u7C7B\u5C0F\u8BA1\uFF08\u539F summary-line\uFF0C\u6536\u8FDB\u6807\u9898\u884C\u53F3\u4FA7\uFF09 -->
|
|
802
|
+
<span class="count" id="summaryLine">
|
|
803
|
+
<span id="summaryManaged">0 managed</span> \xB7
|
|
804
|
+
<span id="summaryOrphan">0 orphan</span> \xB7
|
|
805
|
+
<span id="summaryUnknown">0 unknown</span>
|
|
806
|
+
</span>
|
|
807
|
+
</div>
|
|
808
|
+
<div class="table-wrap" id="sessionTable"><div class="empty" id="loadingSessions">Loading...</div></div>
|
|
809
|
+
</section>
|
|
810
|
+
|
|
811
|
+
<!-- \u2550\u2550 \u5B64\u513F\u8FDB\u7A0B\uFF08\u6709\u624D\u663E\u793A\uFF09 \u2550\u2550 -->
|
|
812
|
+
<section class="section" id="orphanContainer" style="display:none">
|
|
813
|
+
<div class="section-head">
|
|
814
|
+
<span class="section-title" id="sectionOrphans">\u5B64\u513F\u8FDB\u7A0B</span>
|
|
815
|
+
<span class="count" id="orphanCount"></span>
|
|
816
|
+
</div>
|
|
817
|
+
<div class="orphan-banner">
|
|
818
|
+
<span class="icon">⚠</span>
|
|
819
|
+
<span class="text" id="orphanBannerText">These processes are running but no longer managed by bridge</span>
|
|
820
|
+
<span class="action-hint" id="orphanActionHint">Terminate to clean up</span>
|
|
821
|
+
</div>
|
|
822
|
+
<div class="table-wrap" id="orphanTable"><div class="empty">No orphans</div></div>
|
|
823
|
+
</section>
|
|
824
|
+
|
|
825
|
+
<!-- \u2550\u2550 \u5176\u4ED6\u8FDB\u7A0B\uFF08\u9ED8\u8BA4\u6298\u53E0\uFF09 \u2550\u2550 -->
|
|
826
|
+
<section class="section" id="unknownContainer">
|
|
827
|
+
<div class="section-head">
|
|
828
|
+
<span class="section-title toggle-header" id="sectionUnknown" onclick="toggleUnknown()">
|
|
829
|
+
<span class="toggle-icon" id="unknownIcon">▶</span>
|
|
830
|
+
<span id="unknownLabel"> \u5176\u4ED6\u8FDB\u7A0B</span>
|
|
831
|
+
<span class="count" id="unknownCount"></span>
|
|
832
|
+
</span>
|
|
833
|
+
</div>
|
|
834
|
+
<div class="table-wrap" id="unknownTable" style="display:none"><div class="empty" id="loadingUnknown">Loading...</div></div>
|
|
835
|
+
</section>
|
|
836
|
+
|
|
837
|
+
<!-- \u2550\u2550 \u6301\u4E45\u5316\u4F1A\u8BDD \u2550\u2550 -->
|
|
838
|
+
<section class="section">
|
|
839
|
+
<div class="section-head">
|
|
840
|
+
<span class="section-title" id="sectionPersisted">\u6301\u4E45\u5316\u4F1A\u8BDD</span>
|
|
841
|
+
<span class="count" id="persistedCount"></span>
|
|
842
|
+
</div>
|
|
843
|
+
<div class="table-wrap" id="persistedTable"><div class="empty" id="loadingPersisted">Loading...</div></div>
|
|
844
|
+
</section>
|
|
392
845
|
|
|
393
|
-
|
|
394
|
-
<div class="section-title" id="sectionPersisted">Persisted Sessions <span class="count" id="persistedCount"></span></div>
|
|
395
|
-
<div class="table-wrap" id="persistedTable"><div class="empty" id="loadingPersisted">Loading...</div></div>
|
|
846
|
+
</main>
|
|
396
847
|
|
|
397
848
|
<!-- Settings Modal -->
|
|
398
849
|
<div class="modal-overlay" id="settingsModal" style="display:none" onclick="closeSettings(event)">
|
|
@@ -454,7 +905,7 @@ import{execFile as X}from"node:child_process";import v from"node:fs";import k fr
|
|
|
454
905
|
<div class="s-info-row"><span class="s-info-label" id="lblNodeActiveSessions"></span><span class="s-info-value" id="valNodeActiveSessions">-</span></div>
|
|
455
906
|
</div>
|
|
456
907
|
<div class="s-actions">
|
|
457
|
-
<button class="s-btn s-btn-secondary" onclick="refreshNodeInfo()"
|
|
908
|
+
<button class="s-btn s-btn-secondary s-btn-sm" id="btnRefreshNodeInfo" onclick="refreshNodeInfo()"></button>
|
|
458
909
|
</div>
|
|
459
910
|
</div>
|
|
460
911
|
<!-- Logs page -->
|
|
@@ -473,6 +924,8 @@ import{execFile as X}from"node:child_process";import v from"node:fs";import k fr
|
|
|
473
924
|
</div>
|
|
474
925
|
<!-- Scheduler page -->
|
|
475
926
|
<div class="settings-page" id="pageScheduler">
|
|
927
|
+
<!-- \u4ECE Dashboard\u300C\u5F15\u5BFC\u914D\u7F6E\u300D\u8DF3\u8F6C\u8FC7\u6765\u65F6\u7684\u5E26\u5165\u63D0\u793A\uFF08\u53EA\u56DE\u586B\u975E\u5BC6\u94A5\u5B57\u6BB5\uFF09 -->
|
|
928
|
+
<div class="s-msg info" id="schedPrefillNotice" style="display:none"></div>
|
|
476
929
|
<div class="s-field">
|
|
477
930
|
<label id="lblSchedTargets"></label>
|
|
478
931
|
<div class="s-target-list" id="schedTargetList"></div>
|
|
@@ -481,6 +934,13 @@ import{execFile as X}from"node:child_process";import v from"node:fs";import k fr
|
|
|
481
934
|
<button class="s-btn s-btn-secondary" id="btnAddTarget" onclick="startAddTarget()"></button>
|
|
482
935
|
</div>
|
|
483
936
|
<div class="s-target-form" id="targetForm">
|
|
937
|
+
<!-- \u5B9E\u4F8B\u540D\u79F0\uFF1A\u6700\u5E38\u6539\u7684\u53EF\u9009\u9879\uFF0C\u653E\u5230\u8868\u5355\u6700\u4E0A\u9762 -->
|
|
938
|
+
<div class="s-field">
|
|
939
|
+
<label id="lblSchedInstanceName"></label>
|
|
940
|
+
<input type="text" id="cfgInstanceName" autocomplete="off">
|
|
941
|
+
<span class="s-hint" id="hintSchedInstanceName"></span>
|
|
942
|
+
</div>
|
|
943
|
+
<!-- \u5FC5\u586B\uFF1A\u666E\u901A\u7528\u6237\u53EA\u9700\u8981\u586B\u8FD9\u4E24\u9879 -->
|
|
484
944
|
<div class="s-field">
|
|
485
945
|
<label id="lblSchedUrl"></label>
|
|
486
946
|
<input type="text" id="cfgServerUrl" placeholder="http://192.168.1.100:8080" autocomplete="off">
|
|
@@ -491,38 +951,43 @@ import{execFile as X}from"node:child_process";import v from"node:fs";import k fr
|
|
|
491
951
|
<input type="password" id="cfgUserKey" autocomplete="new-password">
|
|
492
952
|
<span class="s-hint" id="hintSchedUserKey"></span>
|
|
493
953
|
</div>
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
<
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
<
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
<
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
954
|
+
<!-- \u9AD8\u7EA7\u8BBE\u7F6E\uFF1A\u5168\u90E8\u53EF\u9009\u9879\uFF0C\u9ED8\u8BA4\u6536\u7EB3 -->
|
|
955
|
+
<div class="s-advanced" id="schedAdvanced">
|
|
956
|
+
<button type="button" class="s-advanced-head" id="schedAdvancedHead" onclick="toggleSchedAdvanced()" aria-expanded="false" aria-controls="schedAdvancedBody">
|
|
957
|
+
<span class="toggle-icon" id="schedAdvancedIcon">▶</span>
|
|
958
|
+
<span id="lblSchedAdvanced"></span>
|
|
959
|
+
<span class="s-advanced-count" id="schedAdvancedCount"></span>
|
|
960
|
+
</button>
|
|
961
|
+
<div class="s-advanced-body" id="schedAdvancedBody" hidden>
|
|
962
|
+
<div class="s-field">
|
|
963
|
+
<label id="lblSchedTenantId"></label>
|
|
964
|
+
<input type="text" id="cfgTenantId" autocomplete="off">
|
|
965
|
+
<span class="s-hint" id="hintSchedTenantId"></span>
|
|
966
|
+
</div>
|
|
967
|
+
<div class="s-field">
|
|
968
|
+
<label id="lblSchedConnKey"></label>
|
|
969
|
+
<input type="password" id="cfgConnectionKey" autocomplete="new-password">
|
|
970
|
+
<span class="s-hint" id="hintSchedConnKey"></span>
|
|
971
|
+
</div>
|
|
972
|
+
<div class="s-field">
|
|
973
|
+
<label id="lblSchedRegisterAddr"></label>
|
|
974
|
+
<input type="text" id="cfgRegisterAddr" placeholder="192.168.1.25:18081" autocomplete="off">
|
|
975
|
+
<span class="s-hint" id="hintSchedRegisterAddr"></span>
|
|
976
|
+
</div>
|
|
977
|
+
<div class="s-field">
|
|
978
|
+
<label id="lblSchedTunnel"></label>
|
|
979
|
+
<select id="cfgTunnel">
|
|
980
|
+
<option value="auto" id="optTunnelAuto"></option>
|
|
981
|
+
<option value="true" id="optTunnelOn"></option>
|
|
982
|
+
<option value="false" id="optTunnelOff"></option>
|
|
983
|
+
</select>
|
|
984
|
+
<span class="s-hint" id="hintSchedTunnel"></span>
|
|
985
|
+
</div>
|
|
986
|
+
<div class="s-field">
|
|
987
|
+
<div class="s-info-row"><span class="s-info-label" id="lblSchedInstanceId"></span><span class="s-info-value" id="valSchedInstanceId">-</span></div>
|
|
988
|
+
<span class="s-hint" id="hintSchedInstanceId"></span>
|
|
989
|
+
</div>
|
|
990
|
+
</div>
|
|
526
991
|
</div>
|
|
527
992
|
<div class="s-msg" id="schedMsg"></div>
|
|
528
993
|
<div class="s-actions s-actions-row">
|
|
@@ -533,10 +998,6 @@ import{execFile as X}from"node:child_process";import v from"node:fs";import k fr
|
|
|
533
998
|
<div class="s-field">
|
|
534
999
|
<p class="s-desc" id="descSched"></p>
|
|
535
1000
|
</div>
|
|
536
|
-
<div class="s-actions s-actions-row">
|
|
537
|
-
<button class="s-btn s-btn-danger" id="btnSaveRestart" onclick="saveServerConfigAndRestart()"></button>
|
|
538
|
-
<button class="s-btn s-btn-secondary" id="btnReRegister" onclick="doReRegister()"></button>
|
|
539
|
-
</div>
|
|
540
1001
|
</div>
|
|
541
1002
|
<!-- Access control page -->
|
|
542
1003
|
<div class="settings-page" id="pageAccess">
|
|
@@ -569,107 +1030,438 @@ import{execFile as X}from"node:child_process";import v from"node:fs";import k fr
|
|
|
569
1030
|
</div>
|
|
570
1031
|
</div>
|
|
571
1032
|
|
|
1033
|
+
<!-- \u4FDD\u5B58\u6210\u529F\u540E\u7684\u540E\u7EED\u64CD\u4F5C\u5F39\u6846\uFF1A\u91CD\u65B0\u6CE8\u518C\uFF08\u63A8\u8350\uFF09/ \u91CD\u542F Bridge / \u6682\u4E0D -->
|
|
1034
|
+
<div class="modal-overlay" id="afterSaveModal" style="display:none" onclick="closeAfterSave(event)">
|
|
1035
|
+
<div class="settings-panel aftersave-panel" onclick="event.stopPropagation()" role="dialog" aria-modal="true" aria-labelledby="aftersaveTitle">
|
|
1036
|
+
<div class="aftersave-icon" aria-hidden="true">
|
|
1037
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>
|
|
1038
|
+
</div>
|
|
1039
|
+
<div class="aftersave-title" id="aftersaveTitle"></div>
|
|
1040
|
+
<p class="aftersave-desc" id="aftersaveDesc"></p>
|
|
1041
|
+
<div class="aftersave-actions">
|
|
1042
|
+
<button class="s-btn s-btn-primary aftersave-recommended" id="aftersaveBtnRegister" onclick="afterSaveAction('register')"></button>
|
|
1043
|
+
<button class="s-btn s-btn-danger" id="aftersaveBtnRestart" onclick="afterSaveAction('restart')"></button>
|
|
1044
|
+
<button type="button" class="aftersave-skip" id="aftersaveBtnSkip" onclick="afterSaveAction('skip')"></button>
|
|
1045
|
+
</div>
|
|
1046
|
+
</div>
|
|
1047
|
+
</div>
|
|
1048
|
+
|
|
572
1049
|
<style>
|
|
1050
|
+
/* \u2550\u2550 \u8BBE\u7F6E\u5F39\u7A97\uFF08\u590D\u7528\u4E3B\u6837\u5F0F\u8868\u7684\u4EE4\u724C\uFF09 \u2550\u2550 */
|
|
573
1051
|
.modal-overlay {
|
|
574
|
-
position: fixed;
|
|
575
|
-
background: rgba(0,0,0,0.
|
|
1052
|
+
position: fixed; inset: 0;
|
|
1053
|
+
background: rgba(0, 0, 0, 0.5);
|
|
1054
|
+
backdrop-filter: blur(3px);
|
|
1055
|
+
z-index: 1000;
|
|
576
1056
|
display: flex; align-items: center; justify-content: center;
|
|
577
1057
|
padding: 20px;
|
|
1058
|
+
animation: fadeIn .12s ease;
|
|
1059
|
+
}
|
|
1060
|
+
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
1061
|
+
|
|
1062
|
+
.settings-panel {
|
|
1063
|
+
background: var(--surface);
|
|
1064
|
+
border: 1px solid var(--border);
|
|
1065
|
+
border-radius: var(--radius-lg);
|
|
1066
|
+
box-shadow: var(--shadow-2);
|
|
1067
|
+
width: 720px; max-width: 100%;
|
|
1068
|
+
/* \u56FA\u5B9A\u9AD8\u5EA6\uFF1A\u5404\u8BBE\u7F6E\u9875\u5185\u5BB9\u591A\u5C11\u4E0D\u4E00\uFF0C\u82E5\u968F\u5185\u5BB9\u6491\u5F00\uFF0C\u5207\u6362\u9875\u7B7E\u65F6\u9762\u677F\u4F1A\u5FFD\u9AD8\u5FFD\u77EE\u3002
|
|
1069
|
+
\u5916\u6846\u6052\u5B9A 88vh\uFF0C\u957F\u5185\u5BB9\uFF08\u65E5\u5FD7\u3001\u8C03\u5EA6\u8868\u5355\uFF09\u7531 .settings-content \u5185\u90E8\u6EDA\u52A8\u6D88\u5316\u3002 */
|
|
1070
|
+
height: min(88vh, 720px);
|
|
1071
|
+
display: flex; flex-direction: column;
|
|
1072
|
+
overflow: hidden;
|
|
1073
|
+
animation: panelIn .16s cubic-bezier(.2, 0, .2, 1);
|
|
1074
|
+
}
|
|
1075
|
+
@keyframes panelIn {
|
|
1076
|
+
from { opacity: 0; transform: translateY(6px) scale(.99); }
|
|
1077
|
+
to { opacity: 1; transform: none; }
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
.settings-head {
|
|
1081
|
+
display: flex; align-items: center; justify-content: space-between;
|
|
1082
|
+
padding: 15px 20px;
|
|
1083
|
+
border-bottom: 1px solid var(--border);
|
|
1084
|
+
flex-shrink: 0;
|
|
1085
|
+
}
|
|
1086
|
+
.settings-head span {
|
|
1087
|
+
font-size: 15px; font-weight: 650; letter-spacing: -0.01em;
|
|
1088
|
+
}
|
|
1089
|
+
.settings-close {
|
|
1090
|
+
background: none; border: none;
|
|
1091
|
+
color: var(--text-3);
|
|
1092
|
+
font-size: 20px; cursor: pointer;
|
|
1093
|
+
width: 28px; height: 28px;
|
|
1094
|
+
border-radius: var(--radius-sm);
|
|
1095
|
+
display: grid; place-items: center;
|
|
1096
|
+
line-height: 1;
|
|
1097
|
+
transition: background .15s, color .15s;
|
|
1098
|
+
}
|
|
1099
|
+
.settings-close:hover { background: var(--surface-2); color: var(--text); }
|
|
1100
|
+
|
|
1101
|
+
.settings-body { display: flex; flex: 1; min-height: 0; }
|
|
1102
|
+
|
|
1103
|
+
.settings-nav {
|
|
1104
|
+
width: 148px; flex-shrink: 0;
|
|
1105
|
+
border-right: 1px solid var(--border);
|
|
1106
|
+
padding: 10px 8px;
|
|
1107
|
+
display: flex; flex-direction: column; gap: 2px;
|
|
1108
|
+
overflow-y: auto;
|
|
1109
|
+
}
|
|
1110
|
+
.settings-nav-item {
|
|
1111
|
+
padding: 8px 12px;
|
|
1112
|
+
font-size: 13px;
|
|
1113
|
+
border-radius: var(--radius-sm);
|
|
1114
|
+
cursor: pointer;
|
|
1115
|
+
color: var(--text-2);
|
|
1116
|
+
transition: background .15s, color .15s;
|
|
1117
|
+
}
|
|
1118
|
+
.settings-nav-item:hover { color: var(--text); background: var(--surface-2); }
|
|
1119
|
+
.settings-nav-item.active {
|
|
1120
|
+
color: var(--accent);
|
|
1121
|
+
background: var(--accent-soft);
|
|
1122
|
+
font-weight: 600;
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
.settings-content {
|
|
1126
|
+
flex: 1; padding: 22px 24px; overflow-y: auto; min-width: 0;
|
|
1127
|
+
}
|
|
1128
|
+
.settings-page { display: none; }
|
|
1129
|
+
.settings-page.active { display: block; }
|
|
1130
|
+
|
|
1131
|
+
/* \u2500\u2500 \u8868\u5355 \u2500\u2500 */
|
|
1132
|
+
.s-field { margin-bottom: 18px; }
|
|
1133
|
+
.s-field label {
|
|
1134
|
+
display: block; font-size: 12.5px; font-weight: 600;
|
|
1135
|
+
margin-bottom: 7px; color: var(--text-2);
|
|
1136
|
+
}
|
|
1137
|
+
.s-field input,
|
|
1138
|
+
.s-field select,
|
|
1139
|
+
.s-field textarea {
|
|
1140
|
+
width: 100%;
|
|
1141
|
+
padding: 9px 12px;
|
|
1142
|
+
background: var(--bg-elevated);
|
|
1143
|
+
border: 1px solid var(--border);
|
|
1144
|
+
border-radius: var(--radius-sm);
|
|
1145
|
+
color: var(--text);
|
|
1146
|
+
font-size: 13.5px;
|
|
1147
|
+
font-family: inherit;
|
|
1148
|
+
outline: none;
|
|
1149
|
+
transition: border-color .15s, box-shadow .15s;
|
|
1150
|
+
}
|
|
1151
|
+
.s-field input:focus,
|
|
1152
|
+
.s-field select:focus,
|
|
1153
|
+
.s-field textarea:focus {
|
|
1154
|
+
border-color: var(--accent);
|
|
1155
|
+
box-shadow: 0 0 0 3px var(--accent-soft);
|
|
1156
|
+
}
|
|
1157
|
+
.s-field input::placeholder { color: var(--text-3); }
|
|
1158
|
+
.s-field select { cursor: pointer; }
|
|
1159
|
+
.s-field select option { background: var(--bg-elevated); color: var(--text); }
|
|
1160
|
+
.s-field textarea {
|
|
1161
|
+
font-family: var(--font-mono);
|
|
1162
|
+
font-size: 12.5px;
|
|
1163
|
+
resize: vertical;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
.s-hint { display: block; font-size: 11.5px; color: var(--text-3); margin-top: 5px; line-height: 1.5; }
|
|
1167
|
+
.s-desc { font-size: 12.5px; color: var(--text-2); line-height: 1.65; margin: 0; }
|
|
1168
|
+
|
|
1169
|
+
.s-msg {
|
|
1170
|
+
padding: 9px 12px;
|
|
1171
|
+
border-radius: var(--radius-sm);
|
|
1172
|
+
font-size: 12.5px;
|
|
1173
|
+
margin-bottom: 14px;
|
|
1174
|
+
display: none;
|
|
1175
|
+
}
|
|
1176
|
+
.s-msg.error { display: block; background: var(--danger-soft); color: var(--danger); }
|
|
1177
|
+
.s-msg.success { display: block; background: var(--ok-soft); color: var(--ok); }
|
|
1178
|
+
.s-msg.info { display: block; background: var(--info-soft); color: var(--info); }
|
|
1179
|
+
|
|
1180
|
+
.s-actions { margin-top: 14px; }
|
|
1181
|
+
.s-actions-row { display: flex; gap: 8px; flex-wrap: wrap; }
|
|
1182
|
+
.s-actions-col { display: flex; flex-direction: column; gap: 8px; }
|
|
1183
|
+
.s-btn-ghost { background: transparent; border-color: transparent; color: var(--text-2); }
|
|
1184
|
+
.s-btn-ghost:hover { background: var(--surface-2); color: var(--text); border-color: transparent; }
|
|
1185
|
+
|
|
1186
|
+
/* \u2500\u2500 \u4FDD\u5B58\u6210\u529F\u540E\u7684\u540E\u7EED\u64CD\u4F5C\u5F39\u6846 \u2500\u2500 */
|
|
1187
|
+
.settings-panel.aftersave-panel {
|
|
1188
|
+
width: 380px;
|
|
1189
|
+
/* \u8986\u76D6 .settings-panel \u7684\u56FA\u5B9A\u9AD8\u5EA6\uFF1A\u8FD9\u662F\u5185\u5BB9\u5F88\u5C11\u7684\u5C0F\u5F39\u6846\uFF0C
|
|
1190
|
+
\u82E5\u6CBF\u7528 88vh \u4F1A\u88AB\u6491\u51FA\u5927\u7247\u7A7A\u767D\uFF08\u7A84\u5C4F\u5A92\u4F53\u67E5\u8BE2\u540C\u6837\u8986\u76D6\uFF09\u3002 */
|
|
1191
|
+
height: auto;
|
|
1192
|
+
max-height: min(88vh, 720px);
|
|
1193
|
+
padding: 24px 24px 20px;
|
|
1194
|
+
}
|
|
1195
|
+
.aftersave-icon {
|
|
1196
|
+
width: 40px; height: 40px;
|
|
1197
|
+
margin: 0 auto 14px;
|
|
1198
|
+
border-radius: 50%;
|
|
1199
|
+
background: var(--ok-soft);
|
|
1200
|
+
color: var(--ok);
|
|
1201
|
+
display: grid; place-items: center;
|
|
1202
|
+
}
|
|
1203
|
+
.aftersave-icon svg { width: 20px; height: 20px; display: block; }
|
|
1204
|
+
.aftersave-title {
|
|
1205
|
+
font-size: 15px; font-weight: 650; letter-spacing: -0.01em;
|
|
1206
|
+
margin-bottom: 5px;
|
|
1207
|
+
text-align: center;
|
|
1208
|
+
}
|
|
1209
|
+
.aftersave-desc {
|
|
1210
|
+
font-size: 12.5px; color: var(--text-2); line-height: 1.6;
|
|
1211
|
+
margin: 0 0 18px;
|
|
1212
|
+
text-align: center;
|
|
1213
|
+
}
|
|
1214
|
+
.aftersave-actions { display: flex; flex-direction: column; gap: 8px; }
|
|
1215
|
+
.aftersave-skip {
|
|
1216
|
+
margin-top: 2px;
|
|
1217
|
+
background: none;
|
|
1218
|
+
border: none;
|
|
1219
|
+
color: var(--text-3);
|
|
1220
|
+
font-family: inherit;
|
|
1221
|
+
font-size: 12.5px;
|
|
1222
|
+
cursor: pointer;
|
|
1223
|
+
padding: 6px;
|
|
1224
|
+
border-radius: var(--radius-sm);
|
|
1225
|
+
transition: color .15s, background .15s;
|
|
1226
|
+
}
|
|
1227
|
+
.aftersave-skip:hover { color: var(--text); background: var(--surface-2); }
|
|
1228
|
+
.aftersave-recommended {
|
|
1229
|
+
position: relative;
|
|
1230
|
+
}
|
|
1231
|
+
.aftersave-recommended::after {
|
|
1232
|
+
content: attr(data-recommended);
|
|
1233
|
+
position: absolute;
|
|
1234
|
+
top: -8px; right: 10px;
|
|
1235
|
+
padding: 1px 7px;
|
|
1236
|
+
border-radius: 20px;
|
|
1237
|
+
background: var(--accent);
|
|
1238
|
+
color: #fff;
|
|
1239
|
+
font-size: 10px;
|
|
1240
|
+
font-weight: 600;
|
|
1241
|
+
letter-spacing: 0.02em;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
.s-btn {
|
|
1245
|
+
display: inline-flex; align-items: center; justify-content: center; gap: 6px;
|
|
1246
|
+
padding: 8px 18px;
|
|
1247
|
+
border-radius: var(--radius-sm);
|
|
1248
|
+
font-size: 13px; font-weight: 600;
|
|
1249
|
+
font-family: inherit;
|
|
1250
|
+
cursor: pointer;
|
|
1251
|
+
transition: background .15s, border-color .15s, opacity .15s;
|
|
1252
|
+
border: 1px solid transparent;
|
|
1253
|
+
white-space: nowrap;
|
|
1254
|
+
}
|
|
1255
|
+
.s-btn-sm { padding: 5px 12px; font-size: 12px; }
|
|
1256
|
+
.s-btn-primary { background: var(--accent); color: #fff; }
|
|
1257
|
+
.s-btn-primary:hover { background: var(--accent-hover); }
|
|
1258
|
+
.s-btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
1259
|
+
.s-btn-secondary {
|
|
1260
|
+
background: var(--surface-2); color: var(--text); border-color: var(--border);
|
|
1261
|
+
}
|
|
1262
|
+
.s-btn-secondary:hover { background: var(--surface-3); border-color: var(--border-strong); }
|
|
1263
|
+
.s-btn-danger {
|
|
1264
|
+
background: transparent; color: var(--danger);
|
|
1265
|
+
border-color: color-mix(in srgb, var(--danger) 45%, transparent);
|
|
1266
|
+
}
|
|
1267
|
+
.s-btn-danger:hover { background: var(--danger-soft); border-color: var(--danger); }
|
|
1268
|
+
.s-btn-danger:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
1269
|
+
|
|
1270
|
+
/* \u2500\u2500 \u8C03\u5EA6\u4E2D\u5FC3\u76EE\u6807\u5217\u8868 \u2500\u2500 */
|
|
1271
|
+
.s-target-list { display: flex; flex-direction: column; gap: 8px; }
|
|
1272
|
+
.s-target-item {
|
|
1273
|
+
display: flex; align-items: center; justify-content: space-between; gap: 10px;
|
|
1274
|
+
padding: 11px 13px;
|
|
1275
|
+
background: var(--bg-elevated);
|
|
1276
|
+
border: 1px solid var(--border);
|
|
1277
|
+
border-radius: var(--radius-sm);
|
|
1278
|
+
cursor: pointer;
|
|
1279
|
+
transition: border-color .15s, background .15s;
|
|
1280
|
+
}
|
|
1281
|
+
.s-target-item:hover { border-color: var(--border-strong); }
|
|
1282
|
+
.s-target-item.active { border-color: var(--accent); background: var(--accent-soft); }
|
|
1283
|
+
.s-target-main { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
|
|
1284
|
+
.s-target-url {
|
|
1285
|
+
font-size: 13.5px; font-weight: 600; color: var(--text);
|
|
1286
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
1287
|
+
}
|
|
1288
|
+
.s-target-meta {
|
|
1289
|
+
font-size: 11.5px; color: var(--text-3);
|
|
1290
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
1291
|
+
}
|
|
1292
|
+
.s-target-actions { display: flex; gap: 6px; flex-shrink: 0; }
|
|
1293
|
+
.s-target-form {
|
|
1294
|
+
border: 1px solid var(--border);
|
|
1295
|
+
border-radius: var(--radius);
|
|
1296
|
+
padding: 18px;
|
|
1297
|
+
margin-bottom: 18px;
|
|
1298
|
+
background: var(--bg-elevated);
|
|
1299
|
+
}
|
|
1300
|
+
.s-target-empty {
|
|
1301
|
+
padding: 20px 14px;
|
|
1302
|
+
text-align: center;
|
|
1303
|
+
font-size: 12.5px;
|
|
1304
|
+
color: var(--text-3);
|
|
1305
|
+
border: 1px dashed var(--border-strong);
|
|
1306
|
+
border-radius: var(--radius-sm);
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
/* \u2500\u2500 \u9AD8\u7EA7\u8BBE\u7F6E\u6298\u53E0\u533A \u2500\u2500 */
|
|
1310
|
+
.s-advanced {
|
|
1311
|
+
border: 1px solid var(--border);
|
|
1312
|
+
border-radius: var(--radius-sm);
|
|
1313
|
+
background: var(--surface-2);
|
|
1314
|
+
margin: 4px 0 18px;
|
|
1315
|
+
overflow: hidden;
|
|
1316
|
+
}
|
|
1317
|
+
.s-advanced-head {
|
|
1318
|
+
display: flex;
|
|
1319
|
+
align-items: center;
|
|
1320
|
+
gap: 7px;
|
|
1321
|
+
width: 100%;
|
|
1322
|
+
padding: 10px 13px;
|
|
1323
|
+
background: transparent;
|
|
1324
|
+
border: none;
|
|
1325
|
+
color: var(--text-2);
|
|
1326
|
+
font-family: inherit;
|
|
1327
|
+
font-size: 12.5px;
|
|
1328
|
+
font-weight: 600;
|
|
1329
|
+
cursor: pointer;
|
|
1330
|
+
text-align: left;
|
|
1331
|
+
transition: color .15s, background .15s;
|
|
1332
|
+
}
|
|
1333
|
+
.s-advanced-head:hover { color: var(--text); background: var(--surface-3); }
|
|
1334
|
+
.s-advanced-head:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
|
|
1335
|
+
.s-advanced-head .toggle-icon { font-size: 9px; transition: transform .2s; }
|
|
1336
|
+
.s-advanced-head[aria-expanded="true"] .toggle-icon { transform: rotate(90deg); }
|
|
1337
|
+
.s-advanced-count { margin-left: auto; font-weight: 500; color: var(--text-3); font-size: 11.5px; }
|
|
1338
|
+
.s-advanced-body {
|
|
1339
|
+
padding: 14px 13px 4px;
|
|
1340
|
+
background: var(--bg-elevated);
|
|
1341
|
+
border-top: 1px solid var(--border);
|
|
1342
|
+
}
|
|
1343
|
+
.s-advanced-body[hidden] { display: none; }
|
|
1344
|
+
|
|
1345
|
+
/* \u2500\u2500 \u8282\u70B9\u4FE1\u606F \u2500\u2500 */
|
|
1346
|
+
.s-info-grid { display: grid; gap: 0; margin-bottom: 16px; }
|
|
1347
|
+
.s-info-row {
|
|
1348
|
+
display: flex; gap: 16px;
|
|
1349
|
+
padding: 9px 0;
|
|
1350
|
+
border-bottom: 1px solid var(--border);
|
|
1351
|
+
font-size: 13px;
|
|
1352
|
+
min-width: 0;
|
|
578
1353
|
}
|
|
579
|
-
.
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
1354
|
+
.s-info-row:last-child { border-bottom: none; }
|
|
1355
|
+
.s-info-label { color: var(--text-3); min-width: 118px; flex-shrink: 0; }
|
|
1356
|
+
.s-info-value {
|
|
1357
|
+
color: var(--text); word-break: break-all; min-width: 0;
|
|
1358
|
+
font-family: var(--font-mono); font-size: 12.5px;
|
|
583
1359
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
1360
|
+
|
|
1361
|
+
/* \u2500\u2500 \u65E5\u5FD7 \u2500\u2500 */
|
|
1362
|
+
.s-log-toolbar {
|
|
1363
|
+
display: flex; align-items: center; gap: 10px;
|
|
1364
|
+
margin-bottom: 12px; flex-wrap: wrap;
|
|
1365
|
+
}
|
|
1366
|
+
.s-log-toolbar select {
|
|
1367
|
+
background: var(--bg-elevated);
|
|
1368
|
+
border: 1px solid var(--border);
|
|
1369
|
+
color: var(--text);
|
|
1370
|
+
padding: 5px 9px;
|
|
1371
|
+
border-radius: var(--radius-sm);
|
|
1372
|
+
font-size: 12px; outline: none;
|
|
587
1373
|
}
|
|
588
|
-
.settings-head span { font-size: 16px; font-weight: 600; }
|
|
589
|
-
.settings-close {
|
|
590
|
-
background: none; border: none; color: var(--text2);
|
|
591
|
-
font-size: 22px; cursor: pointer; padding: 0 4px; line-height: 1;
|
|
592
|
-
}
|
|
593
|
-
.settings-close:hover { color: var(--text); }
|
|
594
|
-
.settings-body { display: flex; flex: 1; min-height: 320px; }
|
|
595
|
-
.settings-nav { width: 100px; flex-shrink: 0; border-right: 1px solid var(--border); padding: 8px 0; }
|
|
596
|
-
.settings-nav-item { padding: 7px 14px; font-size: 13px; cursor: pointer; color: var(--text2); transition: all .15s; }
|
|
597
|
-
.settings-nav-item:hover { color: var(--text); background: rgba(255,255,255,0.03); }
|
|
598
|
-
.settings-nav-item.active { color: var(--accent); background: rgba(91,141,239,0.1); }
|
|
599
|
-
.settings-content { flex: 1; padding: 20px; overflow-y: auto; }
|
|
600
|
-
.settings-page { display: none; }
|
|
601
|
-
.settings-page.active { display: block; }
|
|
602
|
-
.s-field { margin-bottom: 16px; }
|
|
603
|
-
.s-field label { display: block; font-size: 13px; font-weight: 500; margin-bottom: 6px; color: var(--text2); }
|
|
604
|
-
.s-field input { width: 100%; padding: 9px 12px; background: var(--surface2); border: 1px solid var(--border); border-radius: 6px; color: var(--text); font-size: 14px; outline: none; }
|
|
605
|
-
.s-field input:focus { border-color: var(--accent); }
|
|
606
|
-
.s-field select { width: 100%; padding: 9px 12px; background: var(--surface2); border: 1px solid var(--border); border-radius: 6px; color: var(--text); font-size: 14px; outline: none; cursor: pointer; }
|
|
607
|
-
.s-field select:focus { border-color: var(--accent); }
|
|
608
|
-
.s-field select option { background: var(--surface2); color: var(--text); }
|
|
609
|
-
.s-field textarea { width: 100%; padding: 9px 12px; background: var(--surface2); border: 1px solid var(--border); border-radius: 6px; color: var(--text); font-size: 14px; font-family: "Cascadia Code", "Fira Code", Consolas, monospace; outline: none; resize: vertical; }
|
|
610
|
-
.s-field textarea:focus { border-color: var(--accent); }
|
|
611
|
-
.s-actions-row { display: flex; gap: 10px; flex-wrap: wrap; }
|
|
612
|
-
.s-target-list { display: flex; flex-direction: column; gap: 8px; }
|
|
613
|
-
.s-target-item { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 10px 12px; background: var(--surface2); border: 1px solid var(--border); border-radius: 6px; cursor: pointer; }
|
|
614
|
-
.s-target-item.active { border-color: var(--accent); }
|
|
615
|
-
.s-target-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
|
|
616
|
-
.s-target-url { font-size: 14px; font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
617
|
-
.s-target-meta { font-size: 12px; opacity: 0.7; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
618
|
-
.s-target-actions { display: flex; gap: 6px; flex-shrink: 0; }
|
|
619
|
-
.s-target-form { border: 1px solid var(--border); border-radius: 8px; padding: 16px; margin-bottom: 18px; background: var(--surface); }
|
|
620
|
-
.s-target-empty { padding: 14px; text-align: center; font-size: 13px; opacity: 0.6; border: 1px dashed var(--border); border-radius: 6px; }
|
|
621
|
-
.s-hint { display: block; font-size: 11px; color: var(--text2); margin-top: 4px; }
|
|
622
|
-
.s-msg { padding: 8px 12px; border-radius: 6px; font-size: 13px; margin-bottom: 14px; display: none; }
|
|
623
|
-
.s-msg.error { display: block; background: rgba(240,84,84,0.12); color: var(--red); }
|
|
624
|
-
.s-msg.success { display: block; background: rgba(67,198,168,0.12); color: var(--green); }
|
|
625
|
-
.s-msg.info { display: block; background: rgba(91,141,239,0.12); color: var(--accent); }
|
|
626
|
-
.s-actions { margin-top: 12px; }
|
|
627
|
-
.s-btn { padding: 9px 24px; border-radius: 6px; font-size: 14px; font-weight: 600; cursor: pointer; transition: all .15s; border: none; }
|
|
628
|
-
.s-btn-sm { padding: 5px 12px; font-size: 13px; }
|
|
629
|
-
.s-btn-primary { background: var(--accent); color: #fff; }
|
|
630
|
-
.s-btn-primary:hover { background: var(--accent-hover); }
|
|
631
|
-
.s-btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
632
|
-
.s-btn-secondary { background: var(--surface2); color: var(--text); border: 1px solid var(--border); }
|
|
633
|
-
.s-btn-secondary:hover { background: var(--border); }
|
|
634
|
-
.s-btn-danger { background: var(--red); color: #fff; }
|
|
635
|
-
.s-btn-danger:hover { background: #d94a4a; }
|
|
636
|
-
.s-btn-danger:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
637
|
-
.s-desc { font-size: 13px; color: var(--text2); line-height: 1.6; margin: 0; }
|
|
638
|
-
.s-restart-msg { padding: 8px 12px; border-radius: 6px; font-size: 13px; margin-bottom: 14px; display: none; }
|
|
639
|
-
.s-restart-msg.info { display: block; background: rgba(91,141,239,0.12); color: var(--accent); }
|
|
640
|
-
.s-restart-msg.error { display: block; background: rgba(240,84,84,0.12); color: var(--red); }
|
|
641
|
-
/* Info grid */
|
|
642
|
-
.s-info-grid { display: grid; gap: 2px; margin-bottom: 16px; }
|
|
643
|
-
.s-info-row { display: flex; padding: 7px 0; border-bottom: 1px solid var(--border); font-size: 13px; }
|
|
644
|
-
.s-info-label { color: var(--text2); min-width: 110px; flex-shrink: 0; }
|
|
645
|
-
.s-info-value { color: var(--text); word-break: break-all; }
|
|
646
|
-
/* Log viewer */
|
|
647
|
-
.s-log-toolbar { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; flex-wrap: wrap; }
|
|
648
|
-
.s-log-toolbar select { background: var(--surface2); border: 1px solid var(--border); color: var(--text); padding: 5px 8px; border-radius: 4px; font-size: 12px; outline: none; }
|
|
649
1374
|
.s-log-toolbar select:focus { border-color: var(--accent); }
|
|
650
|
-
.s-log-toggle {
|
|
1375
|
+
.s-log-toggle {
|
|
1376
|
+
font-size: 12px; color: var(--text-2);
|
|
1377
|
+
cursor: pointer; display: flex; align-items: center; gap: 5px;
|
|
1378
|
+
}
|
|
651
1379
|
.s-log-toggle input { accent-color: var(--accent); }
|
|
652
|
-
.s-log-list {
|
|
1380
|
+
.s-log-list {
|
|
1381
|
+
max-height: 380px; overflow-y: auto;
|
|
1382
|
+
font-family: var(--font-mono);
|
|
1383
|
+
font-size: 11.5px; line-height: 1.75;
|
|
1384
|
+
background: var(--bg-elevated);
|
|
1385
|
+
border: 1px solid var(--border);
|
|
1386
|
+
border-radius: var(--radius-sm);
|
|
1387
|
+
padding: 10px;
|
|
1388
|
+
}
|
|
653
1389
|
.s-log-entry { padding: 1px 4px; white-space: pre-wrap; word-break: break-all; }
|
|
654
|
-
.s-log-entry .ll { font-weight:
|
|
1390
|
+
.s-log-entry .ll { font-weight: 700; }
|
|
655
1391
|
.s-log-entry .ll.INFO { color: var(--accent); }
|
|
656
|
-
.s-log-entry .ll.WARN { color: var(--
|
|
657
|
-
.s-log-entry .ll.ERROR { color: var(--
|
|
658
|
-
.s-log-entry .ts { color: var(--
|
|
659
|
-
.s-log-empty { color: var(--
|
|
660
|
-
|
|
1392
|
+
.s-log-entry .ll.WARN { color: var(--warn); }
|
|
1393
|
+
.s-log-entry .ll.ERROR { color: var(--danger); }
|
|
1394
|
+
.s-log-entry .ts { color: var(--text-3); }
|
|
1395
|
+
.s-log-empty { color: var(--text-3); font-size: 12.5px; text-align: center; padding: 28px; }
|
|
1396
|
+
|
|
1397
|
+
/* \u2500\u2500 \u8BBE\u7F6E\u5185\u8868\u683C \u2500\u2500 */
|
|
661
1398
|
.s-table-wrap { max-height: 320px; overflow-y: auto; }
|
|
662
1399
|
.s-table { width: 100%; border-collapse: collapse; font-size: 12px; }
|
|
663
|
-
.s-table th {
|
|
664
|
-
|
|
1400
|
+
.s-table th {
|
|
1401
|
+
text-align: left; padding: 8px 10px;
|
|
1402
|
+
font-size: 11px; font-weight: 620; color: var(--text-3);
|
|
1403
|
+
background: var(--surface-2);
|
|
1404
|
+
border-bottom: 1px solid var(--border);
|
|
1405
|
+
white-space: nowrap;
|
|
1406
|
+
}
|
|
1407
|
+
.s-table td { padding: 7px 10px; border-bottom: 1px solid var(--border); }
|
|
665
1408
|
.s-table tr:last-child td { border-bottom: none; }
|
|
666
|
-
.s-tag {
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
.
|
|
671
|
-
.
|
|
672
|
-
.
|
|
1409
|
+
.s-tag {
|
|
1410
|
+
display: inline-block; padding: 1px 7px;
|
|
1411
|
+
border-radius: 20px; font-size: 11px; font-weight: 600;
|
|
1412
|
+
}
|
|
1413
|
+
.s-tag.green { background: var(--ok-soft); color: var(--ok); }
|
|
1414
|
+
.s-tag.red { background: var(--danger-soft); color: var(--danger); }
|
|
1415
|
+
.s-tag.orange { background: var(--warn-soft); color: var(--warn); }
|
|
1416
|
+
|
|
1417
|
+
.s-restart-msg {
|
|
1418
|
+
padding: 9px 12px; border-radius: var(--radius-sm);
|
|
1419
|
+
font-size: 12.5px; margin-bottom: 14px; display: none;
|
|
1420
|
+
}
|
|
1421
|
+
.s-restart-msg.info { display: block; background: var(--info-soft); color: var(--info); }
|
|
1422
|
+
.s-restart-msg.error { display: block; background: var(--danger-soft); color: var(--danger); }
|
|
1423
|
+
|
|
1424
|
+
/* \u8BBE\u7F6E\u5F39\u7A97\u5185\u7684\u6EDA\u52A8\u5BB9\u5668\uFF1A\u7EC6\u6EDA\u52A8\u6761 */
|
|
1425
|
+
.settings-content,
|
|
1426
|
+
.settings-nav,
|
|
1427
|
+
.s-log-list,
|
|
1428
|
+
.s-table-wrap {
|
|
1429
|
+
scrollbar-width: thin;
|
|
1430
|
+
scrollbar-color: var(--border-strong) transparent;
|
|
1431
|
+
}
|
|
1432
|
+
.settings-content::-webkit-scrollbar,
|
|
1433
|
+
.settings-nav::-webkit-scrollbar,
|
|
1434
|
+
.s-log-list::-webkit-scrollbar,
|
|
1435
|
+
.s-table-wrap::-webkit-scrollbar { width: 6px; height: 6px; }
|
|
1436
|
+
.settings-content::-webkit-scrollbar-track,
|
|
1437
|
+
.settings-nav::-webkit-scrollbar-track,
|
|
1438
|
+
.s-log-list::-webkit-scrollbar-track,
|
|
1439
|
+
.s-table-wrap::-webkit-scrollbar-track { background: transparent; }
|
|
1440
|
+
.settings-content::-webkit-scrollbar-thumb,
|
|
1441
|
+
.settings-nav::-webkit-scrollbar-thumb,
|
|
1442
|
+
.s-log-list::-webkit-scrollbar-thumb,
|
|
1443
|
+
.s-table-wrap::-webkit-scrollbar-thumb {
|
|
1444
|
+
background: var(--border-strong); border-radius: 3px;
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
/* \u7A84\u5C4F\uFF1A\u5BFC\u822A\u6539\u4E3A\u9876\u90E8\u6A2A\u5411\u6EDA\u52A8\uFF08\u9762\u677F\u9AD8\u5EA6\u540C\u6837\u56FA\u5B9A\uFF09 */
|
|
1448
|
+
@media (max-width: 620px) {
|
|
1449
|
+
.settings-panel { height: min(92vh, 640px); }
|
|
1450
|
+
.settings-body { flex-direction: column; }
|
|
1451
|
+
.settings-nav {
|
|
1452
|
+
width: auto;
|
|
1453
|
+
flex-direction: row;
|
|
1454
|
+
border-right: none;
|
|
1455
|
+
border-bottom: 1px solid var(--border);
|
|
1456
|
+
padding: 8px;
|
|
1457
|
+
gap: 4px;
|
|
1458
|
+
overflow-x: auto;
|
|
1459
|
+
overflow-y: hidden;
|
|
1460
|
+
}
|
|
1461
|
+
.settings-nav-item { white-space: nowrap; padding: 6px 12px; }
|
|
1462
|
+
.settings-content { padding: 16px; }
|
|
1463
|
+
.s-info-label { min-width: 92px; }
|
|
1464
|
+
}
|
|
673
1465
|
</style>
|
|
674
1466
|
|
|
675
1467
|
<script>
|
|
@@ -686,6 +1478,32 @@ const T = {
|
|
|
686
1478
|
managed: "\u7BA1\u7406",
|
|
687
1479
|
orphan: "\u5B64\u513F",
|
|
688
1480
|
managedLabel: "\u7BA1\u7406\u4E2D",
|
|
1481
|
+
// \u9875\u9762\u9AA8\u67B6
|
|
1482
|
+
pageTitle: "\u8FD0\u884C\u603B\u89C8",
|
|
1483
|
+
pageSub: "\u672C\u673A Bridge \u72B6\u6001\u4E0E Agent \u4F1A\u8BDD",
|
|
1484
|
+
heroServiceLabel: "Bridge \u670D\u52A1",
|
|
1485
|
+
heroRegLabel: "\u8C03\u5EA6\u4E2D\u5FC3\u6CE8\u518C",
|
|
1486
|
+
heroBindLabel: "\u9762\u677F\u7ED1\u5B9A",
|
|
1487
|
+
heroRunning: "\u8FD0\u884C\u6B63\u5E38",
|
|
1488
|
+
heroRegYes: "\u5DF2\u6CE8\u518C",
|
|
1489
|
+
heroRegNo: "\u672A\u6CE8\u518C",
|
|
1490
|
+
heroBindYes: "\u5DF2\u7ED1\u5B9A",
|
|
1491
|
+
heroBindNo: "\u672A\u7ED1\u5B9A",
|
|
1492
|
+
kpiActiveLabel: "\u6D3B\u8DC3\u4F1A\u8BDD",
|
|
1493
|
+
kpiPersistedLabel: "\u6301\u4E45\u5316\u4F1A\u8BDD",
|
|
1494
|
+
kpiOrphanLabel: "\u5B64\u513F\u8FDB\u7A0B",
|
|
1495
|
+
kpiUptimeLabel: "\u8FD0\u884C\u65F6\u957F",
|
|
1496
|
+
kpiActiveFoot: "\u6B63\u5728\u5DE5\u4F5C\u7684 Agent",
|
|
1497
|
+
kpiPersistedFoot: "\u53EF\u6062\u590D\u7684\u4F1A\u8BDD",
|
|
1498
|
+
kpiOrphanFoot: "\u9700\u6E05\u7406\u7684\u6B8B\u7559\u8FDB\u7A0B",
|
|
1499
|
+
kpiUptimeFoot: "\u81EA\u4E0A\u6B21\u542F\u52A8",
|
|
1500
|
+
// \u5F85\u5904\u7406\u4E8B\u9879
|
|
1501
|
+
attnNoScheduler: "\u8FD8\u672A\u8FDE\u63A5\u8C03\u5EA6\u4E2D\u5FC3",
|
|
1502
|
+
attnNoSchedulerDetail: "\u914D\u7F6E\u670D\u52A1\u5668\u5730\u5740\u540E\uFF0CAgent \u624D\u80FD\u63A5\u6536\u4EFB\u52A1\u3002",
|
|
1503
|
+
attnOrphans: "\u68C0\u51FA {n} \u4E2A\u5B64\u513F\u8FDB\u7A0B",
|
|
1504
|
+
attnOrphansDetail: "\u8FD9\u4E9B\u8FDB\u7A0B\u4ECD\u5728\u8FD0\u884C\uFF0C\u4F46\u5DF2\u4E0D\u53D7 Bridge \u7BA1\u7406\u3002",
|
|
1505
|
+
attnActionScheduler: "\u53BB\u914D\u7F6E",
|
|
1506
|
+
attnActionOrphans: "\u67E5\u770B",
|
|
689
1507
|
// \u72B6\u6001\u5361\u7247
|
|
690
1508
|
cardBridge: "Bridge",
|
|
691
1509
|
cardRegistration: "\u6CE8\u518C",
|
|
@@ -765,6 +1583,7 @@ const T = {
|
|
|
765
1583
|
setRestarting: "\u91CD\u542F\u4E2D...",
|
|
766
1584
|
// \u8282\u70B9\u4FE1\u606F
|
|
767
1585
|
nodeTitle: "\u8282\u70B9\u4FE1\u606F",
|
|
1586
|
+
nodeRefresh: "\u21BB \u5237\u65B0",
|
|
768
1587
|
nodeHostname: "\u4E3B\u673A\u540D",
|
|
769
1588
|
nodePlatform: "\u5E73\u53F0",
|
|
770
1589
|
nodeUptime: "\u8FD0\u884C\u65F6\u957F",
|
|
@@ -794,12 +1613,13 @@ const T = {
|
|
|
794
1613
|
schedAddTarget: "\uFF0B \u6DFB\u52A0\u8C03\u5EA6\u4E2D\u5FC3",
|
|
795
1614
|
schedEditTarget: "\u7F16\u8F91",
|
|
796
1615
|
schedDeleteTarget: "\u5220\u9664",
|
|
1616
|
+
schedCopyConnKey: "\u590D\u5236\u5BC6\u94A5",
|
|
797
1617
|
schedConfirmDelete: "\u5220\u9664\u8BE5\u8C03\u5EA6\u4E2D\u5FC3\u914D\u7F6E\uFF1F",
|
|
798
1618
|
schedFormNew: "\u65B0\u589E\u8C03\u5EA6\u4E2D\u5FC3",
|
|
799
1619
|
schedUrl: "\u670D\u52A1\u5730\u5740 (serverUrl)",
|
|
800
1620
|
schedUrlHint: "\u5FC5\u586B\uFF0C\u4EE5 http:// \u6216 https:// \u5F00\u5934\uFF1B\u5730\u5740\u53D8\u66F4\u65F6 API Key \u4F1A\u81EA\u52A8\u6CBF\u7528",
|
|
801
1621
|
schedUserKey: "\u7528\u6237 API Key (userKey)",
|
|
802
|
-
schedUserKeyHint: "\u5FC5\u586B\uFF1B\u5DF2\u914D\u7F6E\u65F6\u7559\u7A7A\u8868\u793A\u4FDD\u6301\u4E0D\u53D8",
|
|
1622
|
+
schedUserKeyHint: "\u5FC5\u586B\uFF1B\u5DF2\u914D\u7F6E\u65F6\u7559\u7A7A\u8868\u793A\u4FDD\u6301\u4E0D\u53D8\u3002\u540C\u4E00\u5730\u5740\u53EF\u6DFB\u52A0\u591A\u884C\u2014\u2014\u4E0D\u540C API Key \u4EE3\u8868\u4E0D\u540C\u8D26\u53F7/\u79DF\u6237\uFF0C\u4F1A\u5E76\u884C\u6CE8\u518C",
|
|
803
1623
|
schedTenantId: "\u79DF\u6237 ID\uFF08\u53EF\u9009\uFF09",
|
|
804
1624
|
schedTenantIdHint: "\u7559\u7A7A\u7531 server \u6839\u636E userKey \u81EA\u52A8\u63A8\u5BFC",
|
|
805
1625
|
schedInstanceName: "\u5B9E\u4F8B\u540D\u79F0\uFF08\u53EF\u9009\uFF09",
|
|
@@ -815,18 +1635,37 @@ const T = {
|
|
|
815
1635
|
schedTunnelOff: "\u7981\u7528",
|
|
816
1636
|
schedInstanceId: "\u5B9E\u4F8B ID",
|
|
817
1637
|
schedInstanceIdHint: "\u7F16\u8F91\u6539\u5740\u65F6\u81EA\u52A8\u7EE7\u627F\uFF08\u540C\u4E00\u5B9E\u4F8B\u6362\u5730\u5740\uFF09\uFF0C\u65B0\u589E\u76EE\u6807\u6CE8\u518C\u6210\u529F\u540E\u81EA\u52A8\u56DE\u586B",
|
|
1638
|
+
schedAdvanced: "\u9AD8\u7EA7\u8BBE\u7F6E",
|
|
1639
|
+
schedAdvancedSummary: "\u9AD8\u7EA7\u8BBE\u7F6E \xB7 \u5DF2\u586B {n} \u9879\u53EF\u9009\u9879",
|
|
1640
|
+
schedAdvancedEmpty: "\u9AD8\u7EA7\u8BBE\u7F6E \xB7 \u53EF\u9009\u9879\u5747\u4E3A\u7A7A\uFF0C\u4FDD\u6301\u9ED8\u8BA4\u5373\u53EF",
|
|
1641
|
+
schedAdvancedN: "{n} \u9879\u5DF2\u586B",
|
|
818
1642
|
schedSave: "\u4FDD\u5B58\u914D\u7F6E",
|
|
819
1643
|
schedSaving: "\u4FDD\u5B58\u4E2D...",
|
|
820
1644
|
schedSaved: "\u914D\u7F6E\u5DF2\u4FDD\u5B58",
|
|
821
1645
|
schedSavedRegistering: "\u914D\u7F6E\u5DF2\u4FDD\u5B58\uFF0C\u6B63\u5728\u91CD\u65B0\u6CE8\u518C...",
|
|
822
1646
|
schedSaveFail: "\u4FDD\u5B58\u5931\u8D25",
|
|
1647
|
+
schedDupNoKey: "\u8BE5\u5730\u5740\u5DF2\u6709\u914D\u7F6E\u3002\u540C\u4E00\u5730\u5740\u8981\u65B0\u589E\u4E00\u6761\uFF0C\u8BF7\u586B\u5165\u53E6\u4E00\u4E2A API Key\uFF08\u4E0D\u540C\u8D26\u53F7/\u79DF\u6237\uFF09\uFF1B\u82E5\u8981\u4FEE\u6539\u5DF2\u6709\u6761\u76EE\uFF0C\u8BF7\u70B9\u51FB\u5217\u8868\u4E2D\u8BE5\u6761\u76EE\u7684\u300C\u7F16\u8F91\u300D\u3002",
|
|
1648
|
+
schedTenantShort: "\u79DF\u6237",
|
|
823
1649
|
schedSaveAndRestart: "\u4FDD\u5B58\u5E76\u91CD\u542F",
|
|
824
1650
|
schedSaveRestartConfirm: "\u4FDD\u5B58\u914D\u7F6E\u5E76\u91CD\u542F Bridge\uFF1F\u91CD\u542F\u671F\u95F4\u670D\u52A1\u4F1A\u77ED\u6682\u4E2D\u65AD\uFF0C\u8FD0\u884C\u4E2D\u7684 Agent \u4F1A\u88AB\u4FDD\u7559\u3002",
|
|
1651
|
+
aftersaveTitle: "\u914D\u7F6E\u5DF2\u4FDD\u5B58",
|
|
1652
|
+
aftersaveDesc: "\u65B0\u914D\u7F6E\u5DF2\u5199\u5165\u3002\u8981\u7ACB\u5373\u751F\u6548\u5417\uFF1F",
|
|
1653
|
+
aftersaveRegister: "\u7ACB\u5373\u91CD\u65B0\u6CE8\u518C",
|
|
1654
|
+
aftersaveRegisterRec: "\u63A8\u8350",
|
|
1655
|
+
aftersaveRestart: "\u91CD\u542F Bridge \u751F\u6548",
|
|
1656
|
+
aftersaveRestartHint: "\u96A7\u9053\u6A21\u5F0F\u7B49\u53D8\u66F4\u9700\u8981\u91CD\u542F\uFF1B\u91CD\u542F\u671F\u95F4\u670D\u52A1\u77ED\u6682\u4E2D\u65AD\uFF0C\u8FD0\u884C\u4E2D\u7684 Agent \u4F1A\u88AB\u4FDD\u7559",
|
|
1657
|
+
aftersaveSkip: "\u6682\u4E0D\uFF0C\u7A0D\u540E\u518D\u8BF4",
|
|
1658
|
+
aftersaveRegistering: "\u6B63\u5728\u91CD\u65B0\u6CE8\u518C...",
|
|
1659
|
+
aftersaveRestarting: "\u6B63\u5728\u91CD\u542F...",
|
|
1660
|
+
aftersaveRegisterOk: "\u91CD\u65B0\u6CE8\u518C\u6210\u529F\uFF0C\u65B0\u914D\u7F6E\u5DF2\u751F\u6548",
|
|
1661
|
+
aftersaveRegisterFail: "\u91CD\u65B0\u6CE8\u518C\u5931\u8D25",
|
|
1662
|
+
aftersaveSkipDone: "\u5DF2\u4FDD\u5B58\u3002\u914D\u7F6E\u4F1A\u5728\u4E0B\u6B21 Bridge \u542F\u52A8\u65F6\u751F\u6548\uFF1B\u4E5F\u53EF\u968F\u65F6\u624B\u52A8\u91CD\u542F\u3002",
|
|
825
1663
|
schedReRegister: "\u91CD\u65B0\u6CE8\u518C",
|
|
826
1664
|
schedReRegistering: "\u6CE8\u518C\u4E2D...",
|
|
827
1665
|
schedReRegistered: "\u91CD\u65B0\u6CE8\u518C\u6210\u529F",
|
|
828
1666
|
schedReRegisterFail: "\u91CD\u65B0\u6CE8\u518C\u5931\u8D25",
|
|
829
|
-
schedDesc: "\u4FDD\u5B58\u540E\
|
|
1667
|
+
schedDesc: "\u4FDD\u5B58\u6210\u529F\u540E\u4F1A\u8BE2\u95EE\u5982\u4F55\u5E94\u7528\uFF1A\u63A8\u8350\u7ACB\u5373\u91CD\u65B0\u6CE8\u518C\uFF1B\u96A7\u9053\u6A21\u5F0F\u7B49\u90E8\u5206\u53D8\u66F4\u9700\u91CD\u542F Bridge \u751F\u6548\u3002",
|
|
1668
|
+
schedPrefillNotice: "\u5DF2\u4ECE\u9762\u677F\u5E26\u5165\u670D\u52A1\u5668\u5730\u5740 {0}\u3002\u8BF7\u5728\u4E0B\u65B9\u7C98\u8D34\u4F60\u7684 userKey \u540E\u4FDD\u5B58\uFF08\u5BC6\u94A5\u4E0D\u4F1A\u901A\u8FC7\u94FE\u63A5\u4F20\u5165\uFF09\u3002",
|
|
830
1669
|
// \u8BBF\u95EE\u63A7\u5236
|
|
831
1670
|
accTitle: "\u8BBF\u95EE\u63A7\u5236",
|
|
832
1671
|
accAllowIps: "\u5141\u8BB8\u8BBF\u95EE\u7684 IP / \u7F51\u6BB5",
|
|
@@ -859,6 +1698,32 @@ const T = {
|
|
|
859
1698
|
managed: "managed",
|
|
860
1699
|
orphan: "orphan",
|
|
861
1700
|
managedLabel: "managed",
|
|
1701
|
+
// \u9875\u9762\u9AA8\u67B6
|
|
1702
|
+
pageTitle: "Overview",
|
|
1703
|
+
pageSub: "Local bridge status and agent sessions",
|
|
1704
|
+
heroServiceLabel: "Bridge service",
|
|
1705
|
+
heroRegLabel: "Scheduler registration",
|
|
1706
|
+
heroBindLabel: "Panel binding",
|
|
1707
|
+
heroRunning: "Running",
|
|
1708
|
+
heroRegYes: "Registered",
|
|
1709
|
+
heroRegNo: "Not registered",
|
|
1710
|
+
heroBindYes: "Paired",
|
|
1711
|
+
heroBindNo: "Unpaired",
|
|
1712
|
+
kpiActiveLabel: "Active sessions",
|
|
1713
|
+
kpiPersistedLabel: "Persisted sessions",
|
|
1714
|
+
kpiOrphanLabel: "Orphan processes",
|
|
1715
|
+
kpiUptimeLabel: "Uptime",
|
|
1716
|
+
kpiActiveFoot: "Agents currently working",
|
|
1717
|
+
kpiPersistedFoot: "Sessions that can be resumed",
|
|
1718
|
+
kpiOrphanFoot: "Leftover processes to clean up",
|
|
1719
|
+
kpiUptimeFoot: "Since last start",
|
|
1720
|
+
// \u5F85\u5904\u7406\u4E8B\u9879
|
|
1721
|
+
attnNoScheduler: "Not connected to a scheduler",
|
|
1722
|
+
attnNoSchedulerDetail: "Configure a server address so agents can receive tasks.",
|
|
1723
|
+
attnOrphans: "{n} orphan process(es) detected",
|
|
1724
|
+
attnOrphansDetail: "They are still running but no longer managed by Bridge.",
|
|
1725
|
+
attnActionScheduler: "Configure",
|
|
1726
|
+
attnActionOrphans: "Review",
|
|
862
1727
|
cardBridge: "Bridge",
|
|
863
1728
|
cardRegistration: "Registration",
|
|
864
1729
|
cardBinding: "Binding",
|
|
@@ -935,6 +1800,7 @@ const T = {
|
|
|
935
1800
|
setRestarting: "Restarting...",
|
|
936
1801
|
// Node Info
|
|
937
1802
|
nodeTitle: "Node Info",
|
|
1803
|
+
nodeRefresh: "\u21BB Refresh",
|
|
938
1804
|
nodeHostname: "Hostname",
|
|
939
1805
|
nodePlatform: "Platform",
|
|
940
1806
|
nodeUptime: "Uptime",
|
|
@@ -964,12 +1830,13 @@ const T = {
|
|
|
964
1830
|
schedAddTarget: "+ Add scheduler",
|
|
965
1831
|
schedEditTarget: "Edit",
|
|
966
1832
|
schedDeleteTarget: "Delete",
|
|
1833
|
+
schedCopyConnKey: "Copy key",
|
|
967
1834
|
schedConfirmDelete: "Delete this scheduler configuration?",
|
|
968
1835
|
schedFormNew: "New scheduler",
|
|
969
1836
|
schedUrl: "Server URL (serverUrl)",
|
|
970
1837
|
schedUrlHint: "Required, must start with http:// or https://; the API key is kept when the URL changes",
|
|
971
1838
|
schedUserKey: "User API Key (userKey)",
|
|
972
|
-
schedUserKeyHint: "Required; leave blank to keep the configured value",
|
|
1839
|
+
schedUserKeyHint: "Required; leave blank to keep the configured value. The same address can be added multiple times \u2014 a different API key means a different account/tenant, registered in parallel",
|
|
973
1840
|
schedTenantId: "Tenant ID (optional)",
|
|
974
1841
|
schedTenantIdHint: "Leave blank to auto-derive from userKey on the server",
|
|
975
1842
|
schedInstanceName: "Instance name (optional)",
|
|
@@ -985,18 +1852,37 @@ const T = {
|
|
|
985
1852
|
schedTunnelOff: "Off",
|
|
986
1853
|
schedInstanceId: "Instance ID",
|
|
987
1854
|
schedInstanceIdHint: "Inherited automatically when editing the URL (same instance, new address); backfilled after a new target registers successfully",
|
|
1855
|
+
schedAdvanced: "Advanced",
|
|
1856
|
+
schedAdvancedSummary: "Advanced \xB7 {n} optional field(s) filled",
|
|
1857
|
+
schedAdvancedEmpty: "Advanced \xB7 all optional, defaults are fine",
|
|
1858
|
+
schedAdvancedN: "{n} filled",
|
|
988
1859
|
schedSave: "Save config",
|
|
989
1860
|
schedSaving: "Saving...",
|
|
990
1861
|
schedSaved: "Configuration saved",
|
|
991
1862
|
schedSavedRegistering: "Configuration saved, re-registering...",
|
|
992
1863
|
schedSaveFail: "Failed to save",
|
|
1864
|
+
schedDupNoKey: "This address already has an entry. To add another one for the same address, enter a different API key (a different account/tenant); to modify the existing entry, click Edit on it in the list.",
|
|
1865
|
+
schedTenantShort: "tenant",
|
|
993
1866
|
schedSaveAndRestart: "Save & restart",
|
|
994
1867
|
schedSaveRestartConfirm: "Save the configuration and restart the bridge? The service will be briefly interrupted; running agents are preserved.",
|
|
1868
|
+
aftersaveTitle: "Configuration saved",
|
|
1869
|
+
aftersaveDesc: "The new configuration has been written. Apply it now?",
|
|
1870
|
+
aftersaveRegister: "Re-register now",
|
|
1871
|
+
aftersaveRegisterRec: "Recommended",
|
|
1872
|
+
aftersaveRestart: "Restart Bridge to apply",
|
|
1873
|
+
aftersaveRestartHint: "Needed for tunnel-mode changes; restart briefly interrupts the service, running agents are preserved",
|
|
1874
|
+
aftersaveSkip: "Not now",
|
|
1875
|
+
aftersaveRegistering: "Re-registering...",
|
|
1876
|
+
aftersaveRestarting: "Restarting...",
|
|
1877
|
+
aftersaveRegisterOk: "Re-registered successfully, new configuration is live",
|
|
1878
|
+
aftersaveRegisterFail: "Re-register failed",
|
|
1879
|
+
aftersaveSkipDone: "Saved. The configuration applies on next Bridge start; you can also restart manually anytime.",
|
|
995
1880
|
schedReRegister: "Re-register",
|
|
996
1881
|
schedReRegistering: "Registering...",
|
|
997
1882
|
schedReRegistered: "Re-registered successfully",
|
|
998
1883
|
schedReRegisterFail: "Re-registration failed",
|
|
999
|
-
schedDesc: "After saving
|
|
1884
|
+
schedDesc: "After saving you'll be asked how to apply it: re-registering now is recommended; some changes such as tunnel mode require a bridge restart.",
|
|
1885
|
+
schedPrefillNotice: "The server address {0} was pre-filled from the dashboard. Paste your userKey below and save (secrets are never passed via the link).",
|
|
1000
1886
|
// Access control
|
|
1001
1887
|
accTitle: "Access Control",
|
|
1002
1888
|
accAllowIps: "Allowed IPs / networks",
|
|
@@ -1035,11 +1921,22 @@ function t(k) {
|
|
|
1035
1921
|
return ((_a = T[lang]) === null || _a === void 0 ? void 0 : _a[k]) || T.zh[k] || k;
|
|
1036
1922
|
}
|
|
1037
1923
|
|
|
1924
|
+
// \u6309\u9762\u677F\u5F53\u524D\u8BED\u8A00\u683C\u5F0F\u5316\u65F6\u95F4\u3002
|
|
1925
|
+
// \u4E0D\u80FD\u76F4\u63A5\u7528 toLocaleString()\uFF08\u4E0D\u4F20\u53C2\u6570\uFF09\uFF1A\u90A3\u4F1A\u53D6\u6D4F\u89C8\u5668/OS \u7684\u8BED\u8A00\uFF0C\u4E0E\u9762\u677F\u81EA\u5DF1\u7684
|
|
1926
|
+
// \u4E2D\u82F1\u5207\u6362\u6309\u94AE\uFF08lang\uFF09\u8131\u8282\u2014\u2014\u7528\u6237\u5207\u5230 English \u540E\uFF0C\u65E5\u671F\u4ECD\u6309\u7CFB\u7EDF\u8BED\u8A00\u6E32\u67D3\uFF0C\u4E2D\u82F1\u5939\u6742\u3002
|
|
1927
|
+
function fmtTime(value) {
|
|
1928
|
+
var _a;
|
|
1929
|
+
var date = value ? new Date(value) : null;
|
|
1930
|
+
if (!date || isNaN(date.getTime())) return "-";
|
|
1931
|
+
return date.toLocaleString(lang === "en" ? "en-US" : "zh-CN", { hour12: false });
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1038
1934
|
function toggleLang() {
|
|
1039
1935
|
lang = lang === "zh" ? "en" : "zh";
|
|
1040
1936
|
localStorage.setItem("dashboard-lang", lang);
|
|
1041
1937
|
document.documentElement.lang = lang === "en" ? "en" : "zh-CN";
|
|
1042
1938
|
document.getElementById("langBtn").textContent = t("langName");
|
|
1939
|
+
updateThemeBtn();
|
|
1043
1940
|
if (document.getElementById("settingsModal").style.display === "flex") {
|
|
1044
1941
|
renderSettingsUI();
|
|
1045
1942
|
settingsLang = lang;
|
|
@@ -1047,6 +1944,30 @@ function toggleLang() {
|
|
|
1047
1944
|
if (lastData) render(lastData);
|
|
1048
1945
|
}
|
|
1049
1946
|
|
|
1947
|
+
// \u2500\u2500 \u4E3B\u9898\u504F\u597D\uFF08\u9ED8\u8BA4\u6DF1\u8272\uFF1B\u4E0E\u767B\u5F55/\u8BBE\u7F6E\u9875\u5171\u7528 dashboard-theme\uFF09\u2500\u2500
|
|
1948
|
+
function isLightTheme() {
|
|
1949
|
+
return document.documentElement.classList.contains("light-theme");
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
// \u6309\u94AE\u5448\u73B0"\u5373\u5C06\u5207\u6362\u5230\u7684\u76EE\u6807"\uFF1A\u6DF1\u8272\u65F6\u663E\u592A\u9633\uFF08\u70B9\u2192\u6D45\u8272\uFF09\uFF0C\u6D45\u8272\u65F6\u663E\u6708\u4EAE\uFF08\u70B9\u2192\u6DF1\u8272\uFF09
|
|
1953
|
+
function updateThemeBtn() {
|
|
1954
|
+
var btn = document.getElementById("themeBtn");
|
|
1955
|
+
if (!btn) return;
|
|
1956
|
+
var light = isLightTheme();
|
|
1957
|
+
btn.textContent = light ? "\\u263D" : "\\u2600";
|
|
1958
|
+
btn.title = light
|
|
1959
|
+
? (lang === "en" ? "Switch to dark" : "\u5207\u6362\u6DF1\u8272")
|
|
1960
|
+
: (lang === "en" ? "Switch to light" : "\u5207\u6362\u6D45\u8272");
|
|
1961
|
+
}
|
|
1962
|
+
|
|
1963
|
+
function toggleTheme() {
|
|
1964
|
+
var light = document.documentElement.classList.toggle("light-theme");
|
|
1965
|
+
try { localStorage.setItem("dashboard-theme", light ? "light" : "dark"); } catch (e) {}
|
|
1966
|
+
updateThemeBtn();
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1969
|
+
updateThemeBtn();
|
|
1970
|
+
|
|
1050
1971
|
// \u2500\u2500 \u5DE5\u5177\u51FD\u6570 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
1051
1972
|
let lastData = null;
|
|
1052
1973
|
|
|
@@ -1082,7 +2003,20 @@ function escHtml(s) {
|
|
|
1082
2003
|
}
|
|
1083
2004
|
function fmtShortDate(iso) {
|
|
1084
2005
|
if (!iso) return "-";
|
|
1085
|
-
|
|
2006
|
+
// \u4E0D\u80FD\u76F4\u63A5\u5207\u5B57\u7B26\u4E32\uFF1A\u90A3\u6837\u663E\u793A\u7684\u662F UTC \u6302\u949F\u503C\uFF08\u5982 2026-08-18 04:35:45\uFF09\uFF0C
|
|
2007
|
+
// \u4E0E\u6D4F\u89C8\u5668\u6240\u5728\u65F6\u533A\u7684\u672C\u5730\u65F6\u95F4\u76F8\u5DEE\u4E00\u4E2A\u504F\u79FB\u3002\u8FD9\u91CC\u8F6C\u6210 Date \u518D\u6309\u672C\u5730\u65F6\u533A\u6E32\u67D3\uFF0C
|
|
2008
|
+
// \u8BA9\u9762\u677F\u663E\u793A\u7684\u523B\u4E0E\u7528\u6237\u5B9E\u9645\u611F\u77E5\u7684\u4E00\u81F4\u3002
|
|
2009
|
+
var date = new Date(iso);
|
|
2010
|
+
if (isNaN(date.getTime())) return String(iso).slice(0, 19).replace("T", " ");
|
|
2011
|
+
return date.toLocaleString(lang === "en" ? "en-US" : "zh-CN", {
|
|
2012
|
+
year: "numeric",
|
|
2013
|
+
month: "2-digit",
|
|
2014
|
+
day: "2-digit",
|
|
2015
|
+
hour: "2-digit",
|
|
2016
|
+
minute: "2-digit",
|
|
2017
|
+
second: "2-digit",
|
|
2018
|
+
hour12: false,
|
|
2019
|
+
});
|
|
1086
2020
|
}
|
|
1087
2021
|
function fmtShortPath(p) {
|
|
1088
2022
|
if (!p) return "-";
|
|
@@ -1118,105 +2052,237 @@ function barColor(pct) {
|
|
|
1118
2052
|
}
|
|
1119
2053
|
|
|
1120
2054
|
// \u2500\u2500 \u6E32\u67D3 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
2055
|
+
|
|
2056
|
+
/** \u8BBE\u7F6E\u4E00\u4E2A\u72B6\u6001\u70B9\uFF08hero \u7528\uFF09\uFF1Aok / warn / danger / \u7A7A\u3002 */
|
|
2057
|
+
function setDot(id, tone, pulse) {
|
|
2058
|
+
const el = document.getElementById(id);
|
|
2059
|
+
if (!el) return;
|
|
2060
|
+
el.className = "dot" + (tone ? " " + tone : "") + (pulse ? " pulse" : "");
|
|
2061
|
+
}
|
|
2062
|
+
|
|
2063
|
+
/** \u5F85\u5904\u7406\u4E8B\u9879\u7684\u52A8\u4F5C\u76EE\u6807\uFF1A\u70B9\u51FB\u540E\u8DF3\u8F6C\u5230\u5BF9\u5E94\u8BBE\u7F6E\u9875\u3002 */
|
|
2064
|
+
let attentionActionKind = null;
|
|
2065
|
+
|
|
2066
|
+
function runAttentionAction() {
|
|
2067
|
+
if (attentionActionKind === "scheduler") {
|
|
2068
|
+
openSettings();
|
|
2069
|
+
switchSettingsPage("scheduler");
|
|
2070
|
+
} else if (attentionActionKind === "orphans") {
|
|
2071
|
+
const table = document.getElementById("unknownTable");
|
|
2072
|
+
if (table && table.style.display === "none") toggleUnknown();
|
|
2073
|
+
const el = document.getElementById("orphanContainer");
|
|
2074
|
+
if (el) el.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2078
|
+
/**
|
|
2079
|
+
* \u5F85\u5904\u7406\u4E8B\u9879\u6761\uFF1A\u53EA\u5728\u771F\u7684\u9700\u8981\u7528\u6237\u52A8\u624B\u65F6\u51FA\u73B0\u3002
|
|
2080
|
+
* \u4F18\u5148\u7EA7\uFF1A\u672A\u6CE8\u518C\u8C03\u5EA6\u4E2D\u5FC3 > \u5B64\u513F\u8FDB\u7A0B\u3002\u4E24\u8005\u4E92\u4E0D\u8986\u76D6\uFF0C\u4F46\u4E00\u6B21\u53EA\u63D0\u793A\u6700\u7D27\u8981\u7684\u4E00\u6761\uFF0C
|
|
2081
|
+
* \u907F\u514D\u9762\u677F\u53D8\u6210\u4E00\u5806\u9EC4\u6761\u3002
|
|
2082
|
+
*/
|
|
2083
|
+
function renderAttention(data) {
|
|
2084
|
+
const bar = document.getElementById("attentionBar");
|
|
2085
|
+
const textEl = document.getElementById("attentionText");
|
|
2086
|
+
const actionEl = document.getElementById("attentionAction");
|
|
2087
|
+
if (!bar || !textEl || !actionEl) return;
|
|
2088
|
+
|
|
2089
|
+
const registered = Boolean(data.registration?.registered);
|
|
2090
|
+
const orphanCount = data.processes?.orphan ?? 0;
|
|
2091
|
+
|
|
2092
|
+
if (!registered) {
|
|
2093
|
+
textEl.innerHTML =
|
|
2094
|
+
"<b>" + escHtml(t("attnNoScheduler")) + "</b> " +
|
|
2095
|
+
escHtml(t("attnNoSchedulerDetail"));
|
|
2096
|
+
actionEl.textContent = t("attnActionScheduler");
|
|
2097
|
+
actionEl.style.display = "";
|
|
2098
|
+
attentionActionKind = "scheduler";
|
|
2099
|
+
bar.hidden = false;
|
|
2100
|
+
return;
|
|
2101
|
+
}
|
|
2102
|
+
|
|
2103
|
+
if (orphanCount > 0) {
|
|
2104
|
+
textEl.innerHTML =
|
|
2105
|
+
"<b>" + escHtml(t("attnOrphans").replace("{n}", String(orphanCount))) + "</b> " +
|
|
2106
|
+
escHtml(t("attnOrphansDetail"));
|
|
2107
|
+
actionEl.textContent = t("attnActionOrphans");
|
|
2108
|
+
actionEl.style.display = "";
|
|
2109
|
+
attentionActionKind = "orphans";
|
|
2110
|
+
bar.hidden = false;
|
|
2111
|
+
return;
|
|
2112
|
+
}
|
|
2113
|
+
|
|
2114
|
+
bar.hidden = true;
|
|
2115
|
+
attentionActionKind = null;
|
|
2116
|
+
}
|
|
2117
|
+
|
|
2118
|
+
/** \u5065\u5EB7\u603B\u89C8\uFF1A\u4E09\u4E2A\u300C\u662F\u5426\u53EF\u7528\u300D\u7684\u5224\u65AD\uFF0C\u53D6\u4EE3\u539F\u5148 4 \u5F20\u5E73\u94FA\u72B6\u6001\u5361\u3002 */
|
|
2119
|
+
function renderHero(data) {
|
|
2120
|
+
const b = data.binding || {};
|
|
2121
|
+
const r = data.registration || {};
|
|
2122
|
+
|
|
2123
|
+
// Bridge \u670D\u52A1\uFF08\u9875\u9762\u80FD\u6253\u5F00\u5C31\u8BF4\u660E\u8FDB\u7A0B\u6D3B\u7740\uFF0C\u72B6\u6001\u70B9\u6052\u4E3A ok\uFF09
|
|
2124
|
+
document.getElementById("heroServiceLabel").textContent = t("heroServiceLabel");
|
|
2125
|
+
document.getElementById("heroServiceValue").textContent = t("heroRunning");
|
|
2126
|
+
setDot("heroServiceDot", "ok", true);
|
|
2127
|
+
const platform = [data.bridge?.platform, data.bridge?.arch].filter(Boolean).join(" ");
|
|
2128
|
+
document.getElementById("heroServiceDetail").textContent =
|
|
2129
|
+
[platform, data.bridge?.nodeVersion, fmtDuration(data.bridge?.uptime)]
|
|
2130
|
+
.filter(Boolean)
|
|
2131
|
+
.join(" \xB7 ");
|
|
2132
|
+
|
|
2133
|
+
// \u8C03\u5EA6\u4E2D\u5FC3\u6CE8\u518C
|
|
2134
|
+
document.getElementById("heroRegLabel").textContent = t("heroRegLabel");
|
|
2135
|
+
document.getElementById("heroRegValue").textContent =
|
|
2136
|
+
r.registered ? t("heroRegYes") : t("heroRegNo");
|
|
2137
|
+
setDot("heroRegDot", r.registered ? "ok" : "warn", false);
|
|
2138
|
+
document.getElementById("heroRegDetail").textContent =
|
|
2139
|
+
r.error || r.instanceId || r.lastAttempt
|
|
2140
|
+
? r.error || r.instanceId || fmtTime(r.lastAttempt)
|
|
2141
|
+
: "-";
|
|
2142
|
+
|
|
2143
|
+
// \u9762\u677F\u7ED1\u5B9A
|
|
2144
|
+
document.getElementById("heroBindLabel").textContent = t("heroBindLabel");
|
|
2145
|
+
document.getElementById("heroBindValue").textContent =
|
|
2146
|
+
b.paired ? t("heroBindYes") : t("heroBindNo");
|
|
2147
|
+
setDot("heroBindDot", b.paired ? "ok" : "warn", false);
|
|
2148
|
+
document.getElementById("heroBindDetail").textContent =
|
|
2149
|
+
b.schedulerBaseUrl || b.instanceId || "-";
|
|
2150
|
+
}
|
|
2151
|
+
|
|
2152
|
+
/** KPI \u78C1\u8D34\uFF1A\u56DB\u4E2A\u6700\u5173\u952E\u7684\u6570\u5B57\u3002 */
|
|
2153
|
+
function renderKpis(data) {
|
|
2154
|
+
const activeCount = data.sessions?.activeCount ?? 0;
|
|
2155
|
+
const persistedCount = data.sessions?.persistedCount ?? 0;
|
|
2156
|
+
const orphanCount = data.processes?.orphan ?? 0;
|
|
2157
|
+
const managedCount = data.processes?.managed ?? 0;
|
|
2158
|
+
|
|
2159
|
+
document.getElementById("kpiActiveLabel").textContent = t("kpiActiveLabel");
|
|
2160
|
+
document.getElementById("kpiPersistedLabel").textContent = t("kpiPersistedLabel");
|
|
2161
|
+
document.getElementById("kpiOrphanLabel").textContent = t("kpiOrphanLabel");
|
|
2162
|
+
document.getElementById("kpiUptimeLabel").textContent = t("kpiUptimeLabel");
|
|
2163
|
+
|
|
2164
|
+
const activeEl = document.getElementById("kpiActive");
|
|
2165
|
+
activeEl.textContent = String(activeCount);
|
|
2166
|
+
activeEl.className = "kpi-value " + (activeCount > 0 ? "ok" : "muted");
|
|
2167
|
+
document.getElementById("kpiActiveFoot").textContent = t("kpiActiveFoot");
|
|
2168
|
+
|
|
2169
|
+
document.getElementById("kpiPersisted").textContent = String(persistedCount);
|
|
2170
|
+
document.getElementById("kpiPersistedFoot").textContent =
|
|
2171
|
+
t("kpiPersistedFoot") + (managedCount > 0 ? " \xB7 " + managedCount + " " + t("managedLabel") : "");
|
|
2172
|
+
|
|
2173
|
+
const orphanEl = document.getElementById("kpiOrphan");
|
|
2174
|
+
orphanEl.textContent = String(orphanCount);
|
|
2175
|
+
orphanEl.className = "kpi-value " + (orphanCount > 0 ? "warn" : "muted");
|
|
2176
|
+
document.getElementById("kpiOrphanFoot").textContent = t("kpiOrphanFoot");
|
|
2177
|
+
|
|
2178
|
+
document.getElementById("kpiUptime").textContent = fmtDuration(data.bridge?.uptime);
|
|
2179
|
+
document.getElementById("kpiUptimeFoot").textContent = t("kpiUptimeFoot");
|
|
2180
|
+
}
|
|
2181
|
+
|
|
1121
2182
|
function render(data) {
|
|
1122
2183
|
lastData = data;
|
|
1123
|
-
document.getElementById("refreshTime").textContent =
|
|
2184
|
+
document.getElementById("refreshTime").textContent = fmtTime(data.collectedAt);
|
|
1124
2185
|
document.getElementById("bridgeHostname").textContent = data.bridge?.hostname || "-";
|
|
1125
2186
|
document.getElementById("bridgeVersion").textContent = "v" + (data.bridge?.version || "-");
|
|
1126
2187
|
document.getElementById("langBtn").textContent = t("langName");
|
|
1127
|
-
document.getElementById("
|
|
2188
|
+
document.getElementById("pageTitle").textContent = t("pageTitle");
|
|
2189
|
+
document.getElementById("pageSub").textContent = t("pageSub");
|
|
1128
2190
|
|
|
1129
2191
|
// Section titles
|
|
1130
|
-
document.getElementById("sectionSysRes").
|
|
1131
|
-
document.getElementById("sectionSessions").
|
|
2192
|
+
document.getElementById("sectionSysRes").textContent = t("secSysRes");
|
|
2193
|
+
document.getElementById("sectionSessions").textContent = t("secSessions");
|
|
1132
2194
|
document.getElementById("unknownLabel").textContent = " " + t("secUnknown");
|
|
1133
|
-
document.getElementById("sectionOrphans").
|
|
1134
|
-
document.getElementById("sectionPersisted").
|
|
2195
|
+
document.getElementById("sectionOrphans").textContent = t("secOrphans");
|
|
2196
|
+
document.getElementById("sectionPersisted").textContent = t("secPersisted");
|
|
1135
2197
|
document.getElementById("orphanBannerText").textContent = t("orphanBanner");
|
|
1136
2198
|
if (document.getElementById("unknownTable").style.display !== "none") {
|
|
1137
2199
|
document.getElementById("unknownIcon").classList.add("open");
|
|
1138
2200
|
}
|
|
1139
2201
|
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
[t("fldVersion"), data.bridge?.version || "-"],
|
|
1146
|
-
[t("fldHost"), data.bridge?.hostname || "-"],
|
|
1147
|
-
[t("fldPlatform"), (data.bridge?.platform || "") + " " + (data.bridge?.arch || "")],
|
|
1148
|
-
[t("fldUptime"), fmtDuration(data.bridge?.uptime)],
|
|
1149
|
-
[t("fldNode"), data.bridge?.nodeVersion || "-"],
|
|
1150
|
-
]},
|
|
1151
|
-
{ title: t("cardRegistration"), items: [
|
|
1152
|
-
[t("fldStatus"), '<span class="' + statusClass(r.registered) + '">' + (r.registered ? t("stRegistered") : t("stNotRegistered")) + "</span>"],
|
|
1153
|
-
[t("fldInstanceId"), r.instanceId || "-"],
|
|
1154
|
-
[t("fldLastAttempt"), r.lastAttempt ? new Date(r.lastAttempt).toLocaleString() : "-"],
|
|
1155
|
-
[t("fldError"), r.error ? '<span class="status-red">' + r.error + "</span>" : t("stNone")],
|
|
1156
|
-
]},
|
|
1157
|
-
{ title: t("cardBinding"), items: [
|
|
1158
|
-
[t("fldStatus"), '<span class="' + statusClass(b.paired) + '">' + (b.paired ? t("stPaired") : t("stUnpaired")) + "</span>"],
|
|
1159
|
-
[t("fldInstanceId"), b.instanceId || "-"],
|
|
1160
|
-
[t("fldScheduler"), b.schedulerBaseUrl || "-"],
|
|
1161
|
-
[t("fldPairingCode"), b.pairingCode || "-"],
|
|
1162
|
-
]},
|
|
1163
|
-
{ title: t("cardSessions"), items: [
|
|
1164
|
-
[t("fldActiveSessions"), '<span class="' + ((data.sessions?.activeCount ?? 0) > 0 ? "status-green" : "status-muted") + '">' + (data.sessions?.activeCount ?? 0) + "</span>"],
|
|
1165
|
-
[t("fldPersistedSessions"), (data.sessions?.persistedCount ?? 0)],
|
|
1166
|
-
[t("fldManagedProcesses"), data.processes?.managed ?? 0],
|
|
1167
|
-
[t("fldOrphanProcesses"), '<span class="' + ((data.processes?.orphan ?? 0) > 0 ? "status-orange" : "status-muted") + '">' + (data.processes?.orphan ?? 0) + "</span>"],
|
|
1168
|
-
[t("fldRegistryEntries"), data.processes?.registryCount ?? 0],
|
|
1169
|
-
]},
|
|
1170
|
-
];
|
|
1171
|
-
|
|
1172
|
-
document.getElementById("statusCards").innerHTML = cards.map(c => {
|
|
1173
|
-
const rows = c.items.map(([k, v]) =>
|
|
1174
|
-
'<div class="stat-row"><span class="stat-label">' + k + '</span><span class="stat-value">' + v + '</span></div>'
|
|
1175
|
-
).join("");
|
|
1176
|
-
return '<div class="card"><div class="card-title">' + c.title + '</div>' + rows + '</div>';
|
|
1177
|
-
}).join("");
|
|
1178
|
-
|
|
1179
|
-
// Metrics
|
|
2202
|
+
renderHero(data);
|
|
2203
|
+
renderKpis(data);
|
|
2204
|
+
renderAttention(data);
|
|
2205
|
+
|
|
2206
|
+
// Metrics \u2014 \u56DB\u4E2A\u4EEA\u8868\u6A2A\u5411\u5BF9\u9F50\uFF0C\u8BFB\u6570\u7528\u5927\u5B57\uFF0C\u660E\u7EC6\u7528\u5C0F\u5B57
|
|
1180
2207
|
const m = data.metrics;
|
|
1181
2208
|
const metricsGrid = document.getElementById("metricsGrid");
|
|
2209
|
+
const metricsCard = document.getElementById("metricsCard");
|
|
1182
2210
|
if (m && m.ok) {
|
|
1183
2211
|
const cpuPct = m.cpu?.usagePercent ?? 0;
|
|
1184
2212
|
const memPct = m.memory?.usagePercent ?? 0;
|
|
1185
2213
|
const storagePct = m.storage?.usagePercent ?? 0;
|
|
2214
|
+
// \u5B58\u50A8\u8DEF\u5F84\u53EA\u53D6\u6700\u540E\u4E00\u6BB5\uFF08\u5982\u6839\u76D8\u53EA\u663E\u793A\u76D8\u7B26\uFF09\uFF0C\u5B8C\u6574\u8DEF\u5F84\u653E title
|
|
2215
|
+
const storageLabel = m.storage?.path ? fmtShortPath(m.storage.path).split("/").pop() : "";
|
|
2216
|
+
metricsCard.classList.remove("metrics-error");
|
|
1186
2217
|
metricsGrid.innerHTML = [
|
|
1187
|
-
{
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
2218
|
+
{
|
|
2219
|
+
name: "CPU",
|
|
2220
|
+
reading: fmtPercent(cpuPct),
|
|
2221
|
+
usage: cpuPct,
|
|
2222
|
+
lines: [
|
|
2223
|
+
[t("fldCores"), m.cpu?.coreCount ?? "-"],
|
|
2224
|
+
[t("fldModel"), (m.cpu?.model || "-").substring(0, 36)],
|
|
2225
|
+
],
|
|
2226
|
+
},
|
|
2227
|
+
{
|
|
2228
|
+
name: "Memory",
|
|
2229
|
+
reading: fmtPercent(memPct),
|
|
2230
|
+
usage: memPct,
|
|
2231
|
+
lines: [
|
|
2232
|
+
[t("fldUsed"), fmtBytes(m.memory?.usedBytes)],
|
|
2233
|
+
[t("fldTotal"), fmtBytes(m.memory?.totalBytes)],
|
|
2234
|
+
],
|
|
2235
|
+
},
|
|
2236
|
+
{
|
|
2237
|
+
name: "Storage",
|
|
2238
|
+
reading: fmtPercent(storagePct),
|
|
2239
|
+
usage: storagePct,
|
|
2240
|
+
lines: [
|
|
2241
|
+
[t("fldFree"), m.storage ? m.storage.freeGiB + " GiB" : "-"],
|
|
2242
|
+
[t("fldTotal"), m.storage ? m.storage.totalGiB + " GiB" : "-"],
|
|
2243
|
+
],
|
|
2244
|
+
// \u53EA\u663E\u793A\u672B\u4E24\u6BB5\u8DEF\u5F84\uFF0C\u5B8C\u6574\u8DEF\u5F84\u653E title \u5C5E\u6027\uFF08\u65E7\u7248\u628A\u6574\u6761\u7EDD\u5BF9\u8DEF\u5F84\u585E\u8FDB\u6807\u9898\u5BFC\u81F4\u6362\u884C\uFF09
|
|
2245
|
+
foot: storageLabel,
|
|
2246
|
+
footFull: m.storage?.path || "",
|
|
2247
|
+
},
|
|
2248
|
+
{
|
|
2249
|
+
name: "Network",
|
|
2250
|
+
// \u4E0A\u4E0B\u884C\u901F\u7387\u662F\u77AC\u65F6\u503C\uFF0C0 \u4E0E"\u65E0\u6570\u636E"\u8BED\u4E49\u4E0D\u540C\uFF1A0 \u662F\u771F\u5B9E\u8BFB\u6570\uFF0C\u4E0D\u5E94\u663E\u793A\u4E3A -
|
|
2251
|
+
reading: fmtBytes(m.network?.downloadBytesPerSecond ?? 0) + "/s",
|
|
2252
|
+
muted: true,
|
|
2253
|
+
usage: null,
|
|
2254
|
+
lines: [
|
|
2255
|
+
[t("fldUpload"), fmtBytes(m.network?.uploadBytesPerSecond ?? 0) + "/s"],
|
|
2256
|
+
[t("fldTotalDown"), fmtBytes(m.network?.downloadTotalBytes)],
|
|
2257
|
+
],
|
|
2258
|
+
},
|
|
1209
2259
|
].map(c => {
|
|
2260
|
+
// \u65E0\u767E\u5206\u6BD4\u8BFB\u6570\u7684\u4EEA\u8868\uFF08Network\uFF09\u4E5F\u5360\u540C\u6837\u9AD8\u5EA6\u7684\u8FDB\u5EA6\u6761\u4F4D\uFF0C
|
|
2261
|
+
// \u5426\u5219\u56DB\u5217\u9AD8\u5EA6\u4E0D\u9F50\uFF0C\u5E95\u90E8\u5BF9\u9F50\u4F1A\u88AB\u7834\u574F
|
|
1210
2262
|
const barHtml = c.usage != null
|
|
1211
|
-
? '<div class="bar
|
|
1212
|
-
: "";
|
|
1213
|
-
const
|
|
1214
|
-
'<div class="
|
|
2263
|
+
? '<div class="bar"><div class="bar-fill ' + barColor(c.usage) + '" style="width:' + Math.min(c.usage, 100) + '%"></div></div>'
|
|
2264
|
+
: '<div class="bar bar-placeholder"></div>';
|
|
2265
|
+
const lines = c.lines.map(([k, v]) =>
|
|
2266
|
+
'<div class="gauge-line"><dt>' + escHtml(k) + '</dt><dd>' + escHtml(v) + '</dd></div>'
|
|
1215
2267
|
).join("");
|
|
1216
|
-
return '<div class="
|
|
2268
|
+
return '<div class="gauge">' +
|
|
2269
|
+
'<div class="gauge-head">' +
|
|
2270
|
+
'<span class="gauge-name">' + escHtml(c.name) + '</span>' +
|
|
2271
|
+
'<span class="gauge-reading' + (c.muted ? " muted" : "") + '">' + escHtml(c.reading) + '</span>' +
|
|
2272
|
+
'</div>' +
|
|
2273
|
+
barHtml +
|
|
2274
|
+
'<dl class="gauge-lines">' + lines + '</dl>' +
|
|
2275
|
+
(c.foot
|
|
2276
|
+
? '<div class="gauge-line" style="margin-top:6px"><dt style="font-size:11px" title="' + escHtml(c.footFull || c.foot) + '">' + escHtml(c.foot) + '</dt></div>'
|
|
2277
|
+
: '') +
|
|
2278
|
+
'</div>';
|
|
1217
2279
|
}).join("");
|
|
1218
2280
|
} else {
|
|
1219
|
-
metricsGrid.innerHTML =
|
|
2281
|
+
metricsGrid.innerHTML =
|
|
2282
|
+
'<div class="empty" style="grid-column:1/-1">' +
|
|
2283
|
+
'<span class="empty-icon">–</span>' +
|
|
2284
|
+
escHtml(t("metricsUnavailable")) +
|
|
2285
|
+
'</div>';
|
|
1220
2286
|
}
|
|
1221
2287
|
|
|
1222
2288
|
// Build process lookup maps from process list
|
|
@@ -1239,20 +2305,24 @@ function render(data) {
|
|
|
1239
2305
|
|
|
1240
2306
|
// Active sessions (primary table, with PID from process cross-reference)
|
|
1241
2307
|
const sessions = data.sessions?.active || [];
|
|
1242
|
-
document.getElementById("sessionCount").textContent = "(" + sessions.length + ")";
|
|
2308
|
+
document.getElementById("sessionCount").textContent = sessions.length ? "(" + sessions.length + ")" : "";
|
|
1243
2309
|
if (sessions.length === 0) {
|
|
1244
|
-
document.getElementById("sessionTable").innerHTML =
|
|
2310
|
+
document.getElementById("sessionTable").innerHTML =
|
|
2311
|
+
'<div class="empty"><span class="empty-icon">○</span>' + escHtml(t("noSessions")) +
|
|
2312
|
+
'<span class="empty-hint">' + escHtml(t("kpiActiveFoot")) + '</span></div>';
|
|
1245
2313
|
} else {
|
|
1246
2314
|
document.getElementById("sessionTable").innerHTML =
|
|
1247
2315
|
'<table><thead><tr><th>' + t("thAgentId") + '</th><th>' + t("thPid") + '</th><th>' + t("thStatus") + '</th><th>' + t("thAction") + '</th><th>' + t("thWorkspace") + '</th><th>' + t("thProvider") + '</th></tr></thead><tbody>' +
|
|
1248
2316
|
sessions.map(s => {
|
|
1249
2317
|
const pid = pidByAgentId[s.agentId];
|
|
1250
2318
|
const st = s.runtimeStatus || "running";
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
+ '<td>' + (
|
|
1254
|
-
+ '<td
|
|
1255
|
-
+ '<td>' + (s.
|
|
2319
|
+
const ws = s.workspacePath ? s.workspacePath.replace(/\\\\/g, "/").split("/").pop() : "-";
|
|
2320
|
+
return '<tr><td class="strong mono">' + escHtml(s.agentId || "-") + '</td>'
|
|
2321
|
+
+ '<td>' + (pid ? '<code>' + escHtml(String(pid)) + '</code>' : '<span class="status-muted">-</span>') + '</td>'
|
|
2322
|
+
+ '<td><span class="' + statusClass(st) + '">' + escHtml(t("st" + st.charAt(0).toUpperCase() + st.slice(1)) || st) + '</span></td>'
|
|
2323
|
+
+ '<td>' + escHtml(s.runtimeActionLabel || "-") + '</td>'
|
|
2324
|
+
+ '<td title="' + escHtml(s.workspacePath || "") + '">' + escHtml(ws) + '</td>'
|
|
2325
|
+
+ '<td>' + escHtml(s.providerId || "-") + '</td></tr>';
|
|
1256
2326
|
}).join("") +
|
|
1257
2327
|
'</tbody></table>';
|
|
1258
2328
|
}
|
|
@@ -1265,7 +2335,8 @@ function render(data) {
|
|
|
1265
2335
|
document.getElementById("orphanTable").innerHTML =
|
|
1266
2336
|
'<table><thead><tr><th>' + t("thAgentId") + '</th><th>' + t("thPid") + '</th></tr></thead><tbody>' +
|
|
1267
2337
|
orphanProcs.map(p =>
|
|
1268
|
-
'<tr><td>' + (p.agentId
|
|
2338
|
+
'<tr><td class="mono">' + (p.agentId ? escHtml(p.agentId) : '<span class="status-muted">-</span>') + '</td>'
|
|
2339
|
+
+ '<td><code>' + escHtml(String(p.pid)) + '</code></td></tr>'
|
|
1269
2340
|
).join("") +
|
|
1270
2341
|
'</tbody></table>';
|
|
1271
2342
|
} else {
|
|
@@ -1273,33 +2344,42 @@ function render(data) {
|
|
|
1273
2344
|
}
|
|
1274
2345
|
|
|
1275
2346
|
// Unknown processes (collapsible, hidden by default)
|
|
1276
|
-
document.getElementById("unknownCount").textContent = "(" + unknownProcs.length + ")";
|
|
2347
|
+
document.getElementById("unknownCount").textContent = unknownProcs.length ? "(" + unknownProcs.length + ")" : "";
|
|
1277
2348
|
const unknownTable = document.getElementById("unknownTable");
|
|
1278
2349
|
if (unknownProcs.length === 0) {
|
|
1279
|
-
unknownTable.innerHTML =
|
|
2350
|
+
unknownTable.innerHTML =
|
|
2351
|
+
'<div class="empty"><span class="empty-icon">○</span>' + escHtml(t("noProcesses")) + '</div>';
|
|
1280
2352
|
} else {
|
|
1281
2353
|
unknownTable.innerHTML =
|
|
1282
2354
|
'<table><thead><tr><th>' + t("thPid") + '</th><th>' + t("thCommand") + '</th><th>' + t("thDirectory") + '</th><th>' + t("thStarted") + '</th><th>' + t("thAction") + '</th></tr></thead><tbody>' +
|
|
1283
2355
|
unknownProcs.map(p =>
|
|
1284
|
-
'<tr><td><code>' +
|
|
2356
|
+
'<tr><td><code>' + escHtml(String(p.pid)) + '</code></td>'
|
|
2357
|
+
+ '<td class="mono">' + escHtml(p.command || "-") + '</td>'
|
|
2358
|
+
+ '<td title="' + escHtml(p.cwd || "") + '">' + escHtml(fmtShortPath(p.cwd)) + '</td>'
|
|
2359
|
+
+ '<td>' + (p.startedAt ? escHtml(fmtShortDate(p.startedAt)) : "-") + '</td>'
|
|
2360
|
+
+ '<td><button class="btn-kill" data-pid="' + escHtml(String(p.pid)) + '">' + t("btnKill") + '</button></td></tr>'
|
|
1285
2361
|
).join("") +
|
|
1286
2362
|
'</tbody></table>';
|
|
1287
2363
|
}
|
|
1288
2364
|
|
|
1289
2365
|
// Persisted sessions
|
|
1290
2366
|
const persisted = data.sessions?.persisted || [];
|
|
1291
|
-
document.getElementById("persistedCount").textContent = "(" + persisted.length + ")";
|
|
2367
|
+
document.getElementById("persistedCount").textContent = persisted.length ? "(" + persisted.length + ")" : "";
|
|
1292
2368
|
if (persisted.length === 0) {
|
|
1293
|
-
document.getElementById("persistedTable").innerHTML =
|
|
2369
|
+
document.getElementById("persistedTable").innerHTML =
|
|
2370
|
+
'<div class="empty"><span class="empty-icon">○</span>' + escHtml(t("noPersisted")) + '</div>';
|
|
1294
2371
|
} else {
|
|
1295
2372
|
document.getElementById("persistedTable").innerHTML =
|
|
1296
2373
|
'<table><thead><tr><th>' + t("thAgentId") + '</th><th>' + t("thStatus") + '</th><th>' + t("thWorkspace") + '</th><th>' + t("thCommand") + '</th><th>' + t("thStarted") + '</th></tr></thead><tbody>' +
|
|
1297
|
-
persisted.map(s =>
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
2374
|
+
persisted.map(s => {
|
|
2375
|
+
const ws = s.workspacePath ? s.workspacePath.replace(/\\\\/g, "/").split("/").pop() : "-";
|
|
2376
|
+
const status = s.status || "stopped";
|
|
2377
|
+
return '<tr><td class="strong mono">' + escHtml(s.agentId || "-") + '</td>'
|
|
2378
|
+
+ '<td><span class="tag ' + (status === "running" ? "green" : "gray") + '">' + escHtml(status) + '</span></td>'
|
|
2379
|
+
+ '<td title="' + escHtml(s.workspacePath || "") + '">' + escHtml(ws) + '</td>'
|
|
2380
|
+
+ '<td class="mono">' + escHtml(s.command || "-") + '</td>'
|
|
2381
|
+
+ '<td>' + escHtml(fmtTime(s.startedAt)) + '</td></tr>';
|
|
2382
|
+
}).join("") +
|
|
1303
2383
|
'</tbody></table>';
|
|
1304
2384
|
}
|
|
1305
2385
|
}
|
|
@@ -1345,12 +2425,16 @@ const UPDATE_LABELS = {
|
|
|
1345
2425
|
label_installing: "\u5B89\u88C5\u4E2D...",
|
|
1346
2426
|
label_restart: "\u91CD\u542F",
|
|
1347
2427
|
label_failed: "\u66F4\u65B0\u5931\u8D25",
|
|
2428
|
+
confirm_title: "\u786E\u8BA4\u66F4\u65B0\u5E76\u91CD\u542F Bridge",
|
|
2429
|
+
confirm_message: "\u66F4\u65B0\u540E\u5C06\u91CD\u65B0\u542F\u52A8\u5B9E\u4F8B Bridge\uFF0C\u6B63\u5728\u8FD0\u884C\u7684 agent \u5B9E\u4F8B\u4F1A\u505C\u6B62\uFF0C\u8FDB\u884C\u4E2D\u7684\u4EFB\u52A1\u4F1A\u4E2D\u65AD\u3002\u786E\u8BA4\u73B0\u5728\u66F4\u65B0\uFF1F",
|
|
1348
2430
|
},
|
|
1349
2431
|
en: {
|
|
1350
2432
|
label_available: "\u2192 {v}",
|
|
1351
2433
|
label_installing: "installing...",
|
|
1352
2434
|
label_restart: "restart",
|
|
1353
2435
|
label_failed: "failed",
|
|
2436
|
+
confirm_title: "Confirm update and restart Bridge",
|
|
2437
|
+
confirm_message: "The update will restart the Bridge. Running agent instances will stop and in-progress tasks will be interrupted. Update now?",
|
|
1354
2438
|
},
|
|
1355
2439
|
};
|
|
1356
2440
|
|
|
@@ -1389,6 +2473,9 @@ function showUpdateAvailable(data) {
|
|
|
1389
2473
|
|
|
1390
2474
|
async function doInstallUpdate() {
|
|
1391
2475
|
const indicator = document.getElementById("updateIndicator");
|
|
2476
|
+
if (!confirm(ul("confirm_title") + "\\n\\n" + ul("confirm_message"))) {
|
|
2477
|
+
return;
|
|
2478
|
+
}
|
|
1392
2479
|
updateState.installing = true;
|
|
1393
2480
|
indicator.className = "update-indicator installing";
|
|
1394
2481
|
indicator.textContent = ul("label_installing");
|
|
@@ -1473,6 +2560,7 @@ function renderSettingsUI() {
|
|
|
1473
2560
|
document.getElementById("descRestart").textContent = t("setRestartDesc");
|
|
1474
2561
|
document.getElementById("btnRestartBridge").textContent = t("setRestartBridge");
|
|
1475
2562
|
document.getElementById("navNode").textContent = t("nodeTitle");
|
|
2563
|
+
document.getElementById("btnRefreshNodeInfo").textContent = t("nodeRefresh");
|
|
1476
2564
|
document.getElementById("navLogs").textContent = t("logTitle");
|
|
1477
2565
|
document.getElementById("navScheduler").textContent = t("schedTitle");
|
|
1478
2566
|
document.getElementById("lblNodeHostname").textContent = t("nodeHostname");
|
|
@@ -1511,10 +2599,10 @@ function renderSettingsUI() {
|
|
|
1511
2599
|
document.getElementById("optTunnelOff").textContent = t("schedTunnelOff");
|
|
1512
2600
|
document.getElementById("lblSchedInstanceId").textContent = t("schedInstanceId");
|
|
1513
2601
|
document.getElementById("hintSchedInstanceId").textContent = t("schedInstanceIdHint");
|
|
2602
|
+
document.getElementById("lblSchedAdvanced").textContent = t("schedAdvanced");
|
|
2603
|
+
updateSchedAdvancedBadge();
|
|
1514
2604
|
document.getElementById("btnSaveServerConfig").textContent = t("schedSave");
|
|
1515
|
-
document.getElementById("btnSaveRestart").textContent = t("schedSaveAndRestart");
|
|
1516
2605
|
document.getElementById("descSched").textContent = t("schedDesc");
|
|
1517
|
-
document.getElementById("btnReRegister").textContent = t("schedReRegister");
|
|
1518
2606
|
document.getElementById("navAccess").textContent = t("accTitle");
|
|
1519
2607
|
document.getElementById("lblAccessIps").textContent = t("accAllowIps");
|
|
1520
2608
|
document.getElementById("hintAccessIps").textContent = t("accAllowIpsHint");
|
|
@@ -1626,6 +2714,8 @@ function confirmRestartBridge() {
|
|
|
1626
2714
|
// \u2500\u2500 Node Info \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
1627
2715
|
let nodeInfoTimer = null;
|
|
1628
2716
|
function refreshNodeInfo() {
|
|
2717
|
+
var btn = document.getElementById("btnRefreshNodeInfo");
|
|
2718
|
+
if (btn) { btn.disabled = true; btn.textContent = t("logRefreshing"); }
|
|
1629
2719
|
fetch("/runtime/node-info").then(function(r) { return r.json(); }).then(function(d) {
|
|
1630
2720
|
if (!d.ok) return;
|
|
1631
2721
|
document.getElementById("valNodeHostname").textContent = d.hostname;
|
|
@@ -1640,7 +2730,12 @@ function refreshNodeInfo() {
|
|
|
1640
2730
|
document.getElementById("valNodeRegistered").textContent = d.registered ? t("nodeRegYes") : t("nodeRegNo");
|
|
1641
2731
|
document.getElementById("valNodeInstanceId").textContent = d.instanceId || "-";
|
|
1642
2732
|
document.getElementById("valNodeActiveSessions").textContent = d.activeSessions;
|
|
1643
|
-
}).catch(function() {})
|
|
2733
|
+
}).catch(function() {}).then(function() {
|
|
2734
|
+
// \u6210\u529F/\u5931\u8D25\u90FD\u6062\u590D\u6309\u94AE\uFF1B\u6210\u529F\u540E\u77ED\u6682\u663E\u793A\u5B8C\u6210\u6001\u518D\u8FD8\u539F\uFF0C\u7ED9\u7528\u6237"\u786E\u5B9E\u5237\u65B0\u4E86"\u7684\u53CD\u9988
|
|
2735
|
+
if (!btn) return;
|
|
2736
|
+
btn.disabled = false;
|
|
2737
|
+
btn.textContent = t("nodeRefresh");
|
|
2738
|
+
});
|
|
1644
2739
|
}
|
|
1645
2740
|
|
|
1646
2741
|
// \u2500\u2500 Logs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
@@ -1660,7 +2755,7 @@ function refreshLogs() {
|
|
|
1660
2755
|
var html = "";
|
|
1661
2756
|
for (var i = 0; i < d.logs.length; i++) {
|
|
1662
2757
|
var e = d.logs[i];
|
|
1663
|
-
html += '<div class="s-log-entry"><span class="ts">' + escHtml(e.timestamp
|
|
2758
|
+
html += '<div class="s-log-entry"><span class="ts">' + escHtml(fmtShortDate(e.timestamp)) + '</span> <span class="ll ' + e.level.toUpperCase() + '">[' + e.level.toUpperCase() + ']</span>';
|
|
1664
2759
|
if (e.prefix) html += ' [' + escHtml(e.prefix) + ']';
|
|
1665
2760
|
html += ' ' + escHtml(e.message) + '</div>';
|
|
1666
2761
|
}
|
|
@@ -1716,8 +2811,11 @@ function renderSchedTargetList() {
|
|
|
1716
2811
|
var meta = document.createElement("div");
|
|
1717
2812
|
meta.className = "s-target-meta";
|
|
1718
2813
|
var parts = [];
|
|
2814
|
+
// \u540C\u4E00\u5730\u5740\u53EF\u914D\u591A\u884C\uFF08\u4E0D\u540C API Key = \u4E0D\u540C\u8D26\u53F7/\u79DF\u6237\uFF09\uFF0C\u5FC5\u987B\u8BA9\u7528\u6237\u770B\u5F97\u51FA\u662F\u54EA\u4E00\u6761
|
|
2815
|
+
if (item.userKeyMasked) parts.push(item.userKeyMasked);
|
|
1719
2816
|
if (item.instanceName) parts.push(item.instanceName);
|
|
1720
2817
|
if (item.instanceId) parts.push("ID: " + item.instanceId);
|
|
2818
|
+
if (item.tenantId) parts.push(t("schedTenantShort") + ": " + item.tenantId);
|
|
1721
2819
|
if (item.tunnel === true) parts.push("tunnel");
|
|
1722
2820
|
else if (item.tunnel === false && item.registerAddr) parts.push("direct");
|
|
1723
2821
|
meta.textContent = parts.join(" \xB7 ");
|
|
@@ -1725,6 +2823,18 @@ function renderSchedTargetList() {
|
|
|
1725
2823
|
main.appendChild(meta);
|
|
1726
2824
|
var actions = document.createElement("div");
|
|
1727
2825
|
actions.className = "s-target-actions";
|
|
2826
|
+
// \u590D\u5236\u8FDE\u63A5\u5BC6\u94A5\uFF1A\u7528\u6237\u8981\u5728 AwS \u9762\u677F\u767B\u8BB0\u8FD9\u53F0 bridge \u65F6\u5FC5\u987B\u586B\u5B83\uFF0C\u800C\u5217\u8868\u53EA\u663E\u793A\u63A9\u7801\uFF0C
|
|
2827
|
+
// \u6CA1\u6709\u8FD9\u4E2A\u5165\u53E3\u5C31\u53EA\u80FD\u53BB\u673A\u5668\u4E0A cat config.json\u3002\u4EC5\u5728\u5DF2\u914D\u7F6E\u5BC6\u94A5\u65F6\u51FA\u73B0\u3002
|
|
2828
|
+
if (item.connectionKeySet) {
|
|
2829
|
+
var copyBtn = document.createElement("button");
|
|
2830
|
+
copyBtn.className = "s-btn s-btn-sm s-btn-secondary";
|
|
2831
|
+
copyBtn.textContent = t("schedCopyConnKey");
|
|
2832
|
+
copyBtn.onclick = function(ev) {
|
|
2833
|
+
ev.stopPropagation();
|
|
2834
|
+
copyTargetConnectionKey(item.targetKey, copyBtn);
|
|
2835
|
+
};
|
|
2836
|
+
actions.appendChild(copyBtn);
|
|
2837
|
+
}
|
|
1728
2838
|
var editBtn = document.createElement("button");
|
|
1729
2839
|
editBtn.className = "s-btn s-btn-sm s-btn-secondary";
|
|
1730
2840
|
editBtn.textContent = t("schedEditTarget");
|
|
@@ -1738,6 +2848,60 @@ function renderSchedTargetList() {
|
|
|
1738
2848
|
}
|
|
1739
2849
|
}
|
|
1740
2850
|
|
|
2851
|
+
// \u6309 targetKey \u53D6\u8BE5\u76EE\u6807\u7684\u660E\u6587\u8FDE\u63A5\u5BC6\u94A5\u5E76\u590D\u5236\u5230\u526A\u8D34\u677F\u3002
|
|
2852
|
+
// \u53EA\u5728\u9762\u677F\u5DF2\u767B\u5F55\u65F6\u53EF\u7528\uFF08\u540E\u7AEF requirePanelAuth\uFF09\uFF1B\u6309\u94AE\u6587\u6848\u5728\u6210\u529F/\u5931\u8D25\u540E\u77ED\u6682\u53D8\u5316\u7ED9\u51FA\u53CD\u9988\u3002
|
|
2853
|
+
function copyTargetConnectionKey(targetKey, btn) {
|
|
2854
|
+
var original = btn.textContent;
|
|
2855
|
+
btn.disabled = true;
|
|
2856
|
+
fetch("/runtime/server-config/connection-key?targetKey=" + encodeURIComponent(targetKey), {
|
|
2857
|
+
credentials: "same-origin",
|
|
2858
|
+
})
|
|
2859
|
+
.then(function(response) {
|
|
2860
|
+
if (!response.ok) {
|
|
2861
|
+
return response.json().then(function(data) {
|
|
2862
|
+
throw new Error((data && data.error) || ("HTTP " + response.status));
|
|
2863
|
+
});
|
|
2864
|
+
}
|
|
2865
|
+
return response.json();
|
|
2866
|
+
})
|
|
2867
|
+
.then(function(data) {
|
|
2868
|
+
var key = String((data && data.connectionKey) || "");
|
|
2869
|
+
if (!key) throw new Error("empty key");
|
|
2870
|
+
return copyTextToClipboard(key).then(function() {
|
|
2871
|
+
btn.textContent = "\u2713";
|
|
2872
|
+
setTimeout(function() { btn.textContent = original; btn.disabled = false; }, 1500);
|
|
2873
|
+
});
|
|
2874
|
+
})
|
|
2875
|
+
.catch(function(error) {
|
|
2876
|
+
btn.textContent = "\u590D\u5236\u5931\u8D25";
|
|
2877
|
+
console.warn("[Panel] \u590D\u5236\u8FDE\u63A5\u5BC6\u94A5\u5931\u8D25:", error);
|
|
2878
|
+
setTimeout(function() { btn.textContent = original; btn.disabled = false; }, 2000);
|
|
2879
|
+
});
|
|
2880
|
+
}
|
|
2881
|
+
|
|
2882
|
+
// \u4E0E\u6D4F\u89C8\u5668\u526A\u8D34\u677F API \u4EA4\u4E92\uFF1B\u4E0D\u53EF\u7528\u65F6\u56DE\u9000\u5230\u9690\u85CF textarea + execCommand\u3002
|
|
2883
|
+
function copyTextToClipboard(text) {
|
|
2884
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
2885
|
+
return navigator.clipboard.writeText(text);
|
|
2886
|
+
}
|
|
2887
|
+
return new Promise(function(resolve, reject) {
|
|
2888
|
+
try {
|
|
2889
|
+
var area = document.createElement("textarea");
|
|
2890
|
+
area.value = text;
|
|
2891
|
+
area.setAttribute("readonly", "");
|
|
2892
|
+
area.style.position = "fixed";
|
|
2893
|
+
area.style.opacity = "0";
|
|
2894
|
+
document.body.appendChild(area);
|
|
2895
|
+
area.select();
|
|
2896
|
+
var ok = document.execCommand("copy");
|
|
2897
|
+
document.body.removeChild(area);
|
|
2898
|
+
ok ? resolve() : reject(new Error("execCommand copy failed"));
|
|
2899
|
+
} catch (error) {
|
|
2900
|
+
reject(error);
|
|
2901
|
+
}
|
|
2902
|
+
});
|
|
2903
|
+
}
|
|
2904
|
+
|
|
1741
2905
|
// \u5C06\u6307\u5B9A target \u8F7D\u5165\u8868\u5355\uFF08\u7F16\u8F91\u6A21\u5F0F\uFF09\uFF1A\u5BC6\u94A5\u7559\u7A7A=\u4FDD\u6301\u4E0D\u53D8\uFF08placeholder \u663E\u793A\u63A9\u7801\uFF09
|
|
1742
2906
|
function loadTargetToForm(idx) {
|
|
1743
2907
|
schedEditIndex = idx;
|
|
@@ -1756,9 +2920,66 @@ function loadTargetToForm(idx) {
|
|
|
1756
2920
|
ck.placeholder = item.connectionKeyMasked || "";
|
|
1757
2921
|
document.getElementById("valSchedInstanceId").textContent = item.instanceId || "-";
|
|
1758
2922
|
document.getElementById("btnDeleteTarget").style.display = "";
|
|
2923
|
+
// \u7F16\u8F91\u5DF2\u914D\u7F6E\u8FC7\u53EF\u9009\u9879\u7684\u76EE\u6807\u65F6\u81EA\u52A8\u5C55\u5F00\uFF0C\u8BA9\u7528\u6237\u770B\u5230\u73B0\u72B6\uFF1B\u5168\u65B0\u586B\u5199\u4FDD\u6301\u6536\u7EB3
|
|
2924
|
+
setSchedAdvancedOpen(countFilledAdvanced(item) > 0);
|
|
2925
|
+
updateSchedAdvancedBadge();
|
|
1759
2926
|
renderSchedTargetList();
|
|
1760
2927
|
}
|
|
1761
2928
|
|
|
2929
|
+
// \u2500\u2500 \u9AD8\u7EA7\u8BBE\u7F6E\u6298\u53E0\u533A \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
2930
|
+
|
|
2931
|
+
/** \u7EDF\u8BA1\u53EF\u9009\u9879\u91CC\u5DF2\u6709\u5185\u5BB9\u7684\u5B57\u6BB5\u6570\uFF08\u5B9E\u4F8B\u540D\u79F0\u5728\u4E3B\u8868\u5355\u3001\u5B9E\u4F8B ID \u53EA\u8BFB\u56DE\u663E\uFF0C\u5747\u4E0D\u8BA1\u5165\uFF09\u3002 */
|
|
2932
|
+
function countFilledAdvanced(target) {
|
|
2933
|
+
var n = 0;
|
|
2934
|
+
if (target && String(target.tenantId || "").trim()) n += 1;
|
|
2935
|
+
if (target && String(target.connectionKeyMasked || "")) n += 1;
|
|
2936
|
+
if (target && String(target.registerAddr || "").trim()) n += 1;
|
|
2937
|
+
if (target && target.tunnel !== undefined && target.tunnel !== "auto" && target.tunnel !== null) n += 1;
|
|
2938
|
+
return n;
|
|
2939
|
+
}
|
|
2940
|
+
|
|
2941
|
+
function isSchedAdvancedOpen() {
|
|
2942
|
+
var body = document.getElementById("schedAdvancedBody");
|
|
2943
|
+
return Boolean(body && !body.hidden);
|
|
2944
|
+
}
|
|
2945
|
+
|
|
2946
|
+
function setSchedAdvancedOpen(open) {
|
|
2947
|
+
var body = document.getElementById("schedAdvancedBody");
|
|
2948
|
+
var head = document.getElementById("schedAdvancedHead");
|
|
2949
|
+
if (!body || !head) return;
|
|
2950
|
+
body.hidden = !open;
|
|
2951
|
+
head.setAttribute("aria-expanded", open ? "true" : "false");
|
|
2952
|
+
}
|
|
2953
|
+
|
|
2954
|
+
function toggleSchedAdvanced() {
|
|
2955
|
+
setSchedAdvancedOpen(!isSchedAdvancedOpen());
|
|
2956
|
+
}
|
|
2957
|
+
|
|
2958
|
+
// \u9AD8\u7EA7\u533A\u5185\u4EFB\u4F55\u8F93\u5165/\u9009\u62E9\u53D8\u5316\u90FD\u5237\u65B0\u6298\u53E0\u5934\u5FBD\u6807\uFF08\u4E8B\u4EF6\u59D4\u6258\uFF0C\u5B57\u6BB5\u589E\u51CF\u4E0D\u7528\u91CD\u6302\uFF09
|
|
2959
|
+
document.addEventListener("input", function(e) {
|
|
2960
|
+
if (!e.target || !e.target.closest) return;
|
|
2961
|
+
if (e.target.closest("#schedAdvancedBody")) updateSchedAdvancedBadge();
|
|
2962
|
+
});
|
|
2963
|
+
document.addEventListener("change", function(e) {
|
|
2964
|
+
if (!e.target || !e.target.closest) return;
|
|
2965
|
+
if (e.target.closest("#schedAdvancedBody")) updateSchedAdvancedBadge();
|
|
2966
|
+
});
|
|
2967
|
+
|
|
2968
|
+
/** \u6298\u53E0\u5934\u5FBD\u6807\uFF1A\u6536\u7EB3\u72B6\u6001\u4E0B\u63D0\u793A"\u91CC\u9762\u6709\u51E0\u4E2A\u53EF\u9009\u9879\u5DF2\u586B"\uFF0C\u8BA9\u7528\u6237\u4E0D\u5C55\u5F00\u4E5F\u77E5\u9053\u73B0\u72B6\u3002 */
|
|
2969
|
+
function updateSchedAdvancedBadge() {
|
|
2970
|
+
var el = document.getElementById("schedAdvancedCount");
|
|
2971
|
+
if (!el) return;
|
|
2972
|
+
var filled = countFilledAdvanced({
|
|
2973
|
+
tenantId: document.getElementById("cfgTenantId").value,
|
|
2974
|
+
connectionKeyMasked: document.getElementById("cfgConnectionKey").placeholder,
|
|
2975
|
+
registerAddr: document.getElementById("cfgRegisterAddr").value,
|
|
2976
|
+
tunnel: document.getElementById("cfgTunnel").value,
|
|
2977
|
+
});
|
|
2978
|
+
el.textContent = filled > 0
|
|
2979
|
+
? t("schedAdvancedN").replace("{n}", String(filled))
|
|
2980
|
+
: "";
|
|
2981
|
+
}
|
|
2982
|
+
|
|
1762
2983
|
// \u8FDB\u5165\u65B0\u589E\u6A21\u5F0F\uFF1A\u6E05\u7A7A\u8868\u5355
|
|
1763
2984
|
function startAddTarget() {
|
|
1764
2985
|
schedEditIndex = -1;
|
|
@@ -1770,10 +2991,76 @@ function startAddTarget() {
|
|
|
1770
2991
|
document.getElementById("cfgTunnel").value = "auto";
|
|
1771
2992
|
document.getElementById("valSchedInstanceId").textContent = "-";
|
|
1772
2993
|
document.getElementById("btnDeleteTarget").style.display = "none";
|
|
2994
|
+
// \u65B0\u589E\uFF1A\u53EF\u9009\u9879\u5168\u90E8\u4E3A\u7A7A\uFF0C\u9ED8\u8BA4\u6536\u7EB3\uFF08\u9884\u586B\u53EF\u80FD\u968F\u540E\u5C55\u5F00\uFF09
|
|
2995
|
+
setSchedAdvancedOpen(false);
|
|
2996
|
+
updateSchedAdvancedBadge();
|
|
1773
2997
|
hideSchedMsg();
|
|
2998
|
+
applyPrefillIfPending();
|
|
1774
2999
|
renderSchedTargetList();
|
|
1775
3000
|
}
|
|
1776
3001
|
|
|
3002
|
+
/**
|
|
3003
|
+
* Dashboard\u300C\u5F15\u5BFC\u914D\u7F6E\u300D\u8DF3\u8F6C\u53C2\u6570\uFF08URL hash\uFF09\uFF1A
|
|
3004
|
+
* #sched=add&serverUrl=...&instanceName=...&tunnel=auto
|
|
3005
|
+
*
|
|
3006
|
+
* \u5B89\u5168\u7EA6\u675F\uFF1A**\u7EDD\u4E0D\u63A5\u53D7 userKey / connectionKey**\u3002\u5BC6\u94A5\u53EA\u8D70\u7528\u6237\u624B\u8F93\u6216 config.json\uFF0C
|
|
3007
|
+
* \u653E\u8FDB URL \u4F1A\u7559\u5728\u6D4F\u89C8\u5668\u5386\u53F2\u3001\u4EE3\u7406\u65E5\u5FD7\u4E0E\u622A\u56FE\u91CC\u3002
|
|
3008
|
+
*/
|
|
3009
|
+
var schedPrefill = null;
|
|
3010
|
+
|
|
3011
|
+
function readSchedPrefill() {
|
|
3012
|
+
var raw = String(window.location.hash || "").replace(/^#/, "");
|
|
3013
|
+
if (!raw) return null;
|
|
3014
|
+
var params = {};
|
|
3015
|
+
raw.split("&").forEach(function(pair) {
|
|
3016
|
+
var idx = pair.indexOf("=");
|
|
3017
|
+
if (idx < 0) return;
|
|
3018
|
+
var key = decodeURIComponent(pair.slice(0, idx));
|
|
3019
|
+
// \u6CE8\u610F\uFF1A\u672C\u6BB5\u4F4D\u4E8E\u6A21\u677F\u5B57\u7B26\u4E32\u5185\uFF0C\u6B63\u5219\u91CC\u7684\u53CD\u659C\u6760\u5FC5\u987B\u5199\u4E24\u904D\uFF08\u5426\u5219\u4F1A\u88AB\u5F53\u6210\u8F6C\u4E49\u7B26\uFF09\u3002
|
|
3020
|
+
var value = decodeURIComponent(pair.slice(idx + 1).replace(/\\+/g, " "));
|
|
3021
|
+
params[key] = value;
|
|
3022
|
+
});
|
|
3023
|
+
if (params.sched !== "add") return null;
|
|
3024
|
+
var serverUrl = String(params.serverUrl || "").trim();
|
|
3025
|
+
if (!serverUrl) return null;
|
|
3026
|
+
var tunnel = String(params.tunnel || "auto");
|
|
3027
|
+
if (tunnel !== "true" && tunnel !== "false" && tunnel !== "auto") tunnel = "auto";
|
|
3028
|
+
return {
|
|
3029
|
+
serverUrl: serverUrl,
|
|
3030
|
+
instanceName: String(params.instanceName || "").trim(),
|
|
3031
|
+
tunnel: tunnel,
|
|
3032
|
+
};
|
|
3033
|
+
}
|
|
3034
|
+
|
|
3035
|
+
/** \u628A\u5E26\u5165\u503C\u5199\u8FDB\u300C\u65B0\u589E\u300D\u8868\u5355\uFF08\u4EC5\u5728\u7528\u6237\u8FD8\u6CA1\u8F93\u5165\u5185\u5BB9\u65F6\u8986\u76D6\uFF0C\u907F\u514D\u6253\u65AD\u624B\u52A8\u7F16\u8F91\uFF09 */
|
|
3036
|
+
function applyPrefillIfPending() {
|
|
3037
|
+
if (!schedPrefill || schedEditIndex >= 0) return;
|
|
3038
|
+
var serverUrlInput = document.getElementById("cfgServerUrl");
|
|
3039
|
+
if (!serverUrlInput) return;
|
|
3040
|
+
if (!serverUrlInput.value.trim()) serverUrlInput.value = schedPrefill.serverUrl;
|
|
3041
|
+
var nameInput = document.getElementById("cfgInstanceName");
|
|
3042
|
+
if (nameInput && !nameInput.value.trim() && schedPrefill.instanceName) {
|
|
3043
|
+
nameInput.value = schedPrefill.instanceName;
|
|
3044
|
+
}
|
|
3045
|
+
var tunnelSelect = document.getElementById("cfgTunnel");
|
|
3046
|
+
if (tunnelSelect) tunnelSelect.value = schedPrefill.tunnel;
|
|
3047
|
+
updateSchedAdvancedBadge();
|
|
3048
|
+
showPrefillNotice();
|
|
3049
|
+
}
|
|
3050
|
+
|
|
3051
|
+
function showPrefillNotice() {
|
|
3052
|
+
var notice = document.getElementById("schedPrefillNotice");
|
|
3053
|
+
if (!notice || !schedPrefill) return;
|
|
3054
|
+
notice.textContent =
|
|
3055
|
+
t("schedPrefillNotice").replace("{0}", schedPrefill.serverUrl);
|
|
3056
|
+
notice.style.display = "block";
|
|
3057
|
+
}
|
|
3058
|
+
|
|
3059
|
+
function hidePrefillNotice() {
|
|
3060
|
+
var notice = document.getElementById("schedPrefillNotice");
|
|
3061
|
+
if (notice) notice.style.display = "none";
|
|
3062
|
+
}
|
|
3063
|
+
|
|
1777
3064
|
function hideSchedMsg() {
|
|
1778
3065
|
var msg = document.getElementById("schedMsg");
|
|
1779
3066
|
msg.className = "s-msg";
|
|
@@ -1786,14 +3073,16 @@ function showSchedMsg(text, ok) {
|
|
|
1786
3073
|
msg.className = "s-msg " + (ok ? "success" : "error");
|
|
1787
3074
|
}
|
|
1788
3075
|
|
|
1789
|
-
// \u8868\u5355 \u2192 target \u5BF9\u8C61\uFF08\u5BC6\u94A5\u539F\u6837\u643A\u5E26\uFF0C\u7559\u7A7A\u7531\u540E\u7AEF\u6309
|
|
3076
|
+
// \u8868\u5355 \u2192 target \u5BF9\u8C61\uFF08\u5BC6\u94A5\u539F\u6837\u643A\u5E26\uFF0C\u7559\u7A7A\u7531\u540E\u7AEF\u6309 originalTargetKey/\u539F\u5730\u5740\u7EE7\u627F\uFF09
|
|
1790
3077
|
function formToTarget() {
|
|
3078
|
+
var editing = schedEditIndex >= 0 ? schedTargets[schedEditIndex] : null;
|
|
1791
3079
|
return {
|
|
1792
3080
|
serverUrl: document.getElementById("cfgServerUrl").value.trim(),
|
|
1793
|
-
// \u7F16\u8F91\u6A21\u5F0F\u643A\u5E26\u539F\
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
3081
|
+
// \u7F16\u8F91\u6A21\u5F0F\u643A\u5E26\u539F\u8EAB\u4EFD\uFF1A\u540C\u5740\u53EF\u80FD\u6709\u591A\u884C\uFF08\u4E0D\u540C API Key\uFF09\uFF0C
|
|
3082
|
+
// originalTargetKey \u8BA9\u540E\u7AEF\u7CBE\u786E\u77E5\u9053\u8981\u7EE7\u627F\u54EA\u4E00\u6761\u7684 instanceId / \u672A\u66B4\u9732\u5B57\u6BB5
|
|
3083
|
+
originalTargetKey: editing ? (editing.targetKey || "") : "",
|
|
3084
|
+
// \u517C\u5BB9\u65E7\u540E\u7AEF/\u6539\u5730\u5740\u573A\u666F\uFF1A\u5730\u5740\u53D8\u66F4\u65F6\u6309\u539F\u5730\u5740\u7EE7\u627F\uFF08\u540C\u4E00\u5B9E\u4F8B\u6362\u5730\u5740\uFF0C\u65E0\u9700\u91CD\u65B0\u6CE8\u518C\uFF09
|
|
3085
|
+
originalServerUrl: editing ? (editing.serverUrl || "") : "",
|
|
1797
3086
|
userKey: document.getElementById("cfgUserKey").value,
|
|
1798
3087
|
tenantId: document.getElementById("cfgTenantId").value.trim(),
|
|
1799
3088
|
instanceName: document.getElementById("cfgInstanceName").value.trim(),
|
|
@@ -1814,6 +3103,40 @@ function refreshServerConfig() {
|
|
|
1814
3103
|
}).catch(function() {});
|
|
1815
3104
|
}
|
|
1816
3105
|
|
|
3106
|
+
/**
|
|
3107
|
+
* \u5F52\u4E00\u5316\u5730\u5740\u7528\u4E8E\u300C\u65B0\u589E\u65F6\u649E\u5DF2\u6709\u5730\u5740\u300D\u7684\u6BD4\u8F83\uFF08\u4E0E\u540E\u7AEF\u4E00\u81F4\uFF0C\u5FFD\u7565\u5C3E\u659C\u6760/\u534F\u8BAE\u5927\u5C0F\u5199/localhost\uFF09\u3002
|
|
3108
|
+
* \u6CE8\u610F\uFF1A\u672C\u6BB5\u4F4D\u4E8E\u6A21\u677F\u5B57\u7B26\u4E32\u5185\uFF0C\u6B63\u5219\u91CC\u7684\u53CD\u659C\u6760\u5FC5\u987B\u5199\u4E24\u904D\uFF08\u5426\u5219\u4F1A\u88AB\u5F53\u6210\u8F6C\u4E49\u7B26\uFF09\u3002
|
|
3109
|
+
*/
|
|
3110
|
+
function normalizeTargetUrlForCompare(raw) {
|
|
3111
|
+
var value = String(raw || "").trim();
|
|
3112
|
+
if (!value) return "";
|
|
3113
|
+
try {
|
|
3114
|
+
var url = new URL(value);
|
|
3115
|
+
url.protocol = url.protocol.toLowerCase();
|
|
3116
|
+
if (url.hostname.toLowerCase() === "localhost") url.hostname = "127.0.0.1";
|
|
3117
|
+
return url.origin;
|
|
3118
|
+
} catch (e) {
|
|
3119
|
+
return value.replace(/\\/+$/, "");
|
|
3120
|
+
}
|
|
3121
|
+
}
|
|
3122
|
+
|
|
3123
|
+
/**
|
|
3124
|
+
* \u65B0\u589E\u65F6\u67E5\u627E\u300C\u540C\u5730\u5740 + \u540C API Key\u300D\u7684\u5DF2\u6709\u6761\u76EE\uFF08-1 = \u65E0\u51B2\u7A81\uFF09\u3002
|
|
3125
|
+
* \u540C\u5730\u5740\u4E0D\u540C Key \u5C5E\u4E8E\u4E0D\u540C\u8D26\u53F7/\u79DF\u6237\uFF0C\u662F\u5408\u6CD5\u914D\u7F6E\uFF0C\u4E0D\u7B97\u91CD\u590D\uFF1B
|
|
3126
|
+
* \u53EA\u6709\u5730\u5740\u4E0E Key \u90FD\u76F8\u540C\u624D\u662F\u540C\u4E00\u4E2A\u76EE\u6807\u6CE8\u518C\u4E24\u6B21\u3002
|
|
3127
|
+
*/
|
|
3128
|
+
function findDuplicateTargetIndex(serverUrl, userKey) {
|
|
3129
|
+
var urlKey = normalizeTargetUrlForCompare(serverUrl);
|
|
3130
|
+
var keyText = String(userKey || "").trim();
|
|
3131
|
+
if (!urlKey || !keyText) return -1;
|
|
3132
|
+
for (var i = 0; i < schedTargets.length; i++) {
|
|
3133
|
+
if (normalizeTargetUrlForCompare(schedTargets[i].serverUrl) !== urlKey) continue;
|
|
3134
|
+
// \u5DF2\u6709\u6761\u76EE\u53EA\u56DE\u4F20\u63A9\u7801\uFF0C\u65E0\u6CD5\u6BD4\u5BF9\u660E\u6587 Key\uFF1A\u5730\u5740\u76F8\u540C\u5373\u63D0\u793A\uFF08\u7531\u540E\u7AEF\u505A\u6700\u7EC8\u5224\u5B9A\uFF09
|
|
3135
|
+
return i;
|
|
3136
|
+
}
|
|
3137
|
+
return -1;
|
|
3138
|
+
}
|
|
3139
|
+
|
|
1817
3140
|
// \u5168\u91CF\u63D0\u4EA4 targets\uFF1BrestartAfter \u4E3A true \u65F6\u4FDD\u5B58\u6210\u529F\u540E\u91CD\u542F Bridge
|
|
1818
3141
|
function saveServerConfig(restartAfter) {
|
|
1819
3142
|
var form = formToTarget();
|
|
@@ -1822,6 +3145,17 @@ function saveServerConfig(restartAfter) {
|
|
|
1822
3145
|
return Promise.resolve(false);
|
|
1823
3146
|
}
|
|
1824
3147
|
|
|
3148
|
+
// \u65B0\u589E\u6A21\u5F0F\u4E0B\u82E5\u5730\u5740\u5DF2\u5B58\u5728\uFF1A\u540C\u5740\u5141\u8BB8\u65B0\u589E\uFF08\u4E0D\u540C API Key = \u4E0D\u540C\u8D26\u53F7/\u79DF\u6237\uFF09\uFF0C
|
|
3149
|
+
// \u4F46\u540C\u4E00\u4E2A Key \u518D\u5B58\u4E00\u6B21\u6CA1\u6709\u610F\u4E49\u3002\u5DF2\u6709\u6761\u76EE\u53EA\u56DE\u4F20\u63A9\u7801\uFF0C\u65E0\u6CD5\u6BD4\u5BF9\u65B0\u586B\u7684\u660E\u6587 Key\uFF0C
|
|
3150
|
+
// \u56E0\u6B64\u8FD9\u91CC\u53EA\u505A\u63D0\u793A\uFF0C\u6700\u7EC8\u4EE5\u670D\u52A1\u7AEF\u6309\u300C\u5730\u5740 + userKey\u300D\u7684\u8EAB\u4EFD\u6821\u9A8C\u4E3A\u51C6\u3002
|
|
3151
|
+
if (schedEditIndex < 0) {
|
|
3152
|
+
var sameUrlIndex = findDuplicateTargetIndex(form.serverUrl, form.userKey);
|
|
3153
|
+
if (sameUrlIndex >= 0 && !form.userKey) {
|
|
3154
|
+
showSchedMsg(t("schedDupNoKey"), false);
|
|
3155
|
+
return Promise.resolve(false);
|
|
3156
|
+
}
|
|
3157
|
+
}
|
|
3158
|
+
|
|
1825
3159
|
// \u7EC4\u88C5\u5168\u91CF\u5217\u8868\uFF1A\u7F16\u8F91\u6A21\u5F0F\u66FF\u6362\u5BF9\u5E94\u9879\uFF0C\u65B0\u589E\u6A21\u5F0F\u8FFD\u52A0\uFF1B\u5176\u4F59\u9879\u4ECE\u7F13\u5B58\u89C6\u56FE\u539F\u6837\u5E26\u4E0A\uFF08\u5BC6\u94A5\u7559\u7A7A\u7531\u540E\u7AEF\u7EE7\u627F\uFF09
|
|
1826
3160
|
var list = schedTargets.map(function(item) {
|
|
1827
3161
|
return {
|
|
@@ -1853,23 +3187,15 @@ function saveServerConfig(restartAfter) {
|
|
|
1853
3187
|
refreshServerConfig();
|
|
1854
3188
|
schedEditIndex = keepIndex;
|
|
1855
3189
|
if (restartAfter) {
|
|
3190
|
+
// \u300C\u4FDD\u5B58\u5E76\u91CD\u542F\u300D\u5165\u53E3\uFF08\u7EF4\u62A4\u9875\u7B49\uFF09\uFF1A\u4FDD\u6301\u539F\u6709\u7ACB\u5373\u91CD\u542F\u8BED\u4E49
|
|
1856
3191
|
showSchedMsg(t("schedSaved"), true);
|
|
1857
3192
|
doRestartBridge();
|
|
1858
3193
|
return true;
|
|
1859
3194
|
}
|
|
1860
|
-
// \
|
|
1861
|
-
// \
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
return runReRegister().then(function(reg) {
|
|
1865
|
-
if (reg.ok) {
|
|
1866
|
-
showSchedMsg(t("schedReRegistered"), true);
|
|
1867
|
-
refreshNodeInfo();
|
|
1868
|
-
} else {
|
|
1869
|
-
showSchedMsg(t("schedReRegisterFail") + (reg.error ? ": " + reg.error : ""), false);
|
|
1870
|
-
}
|
|
1871
|
-
return reg.ok;
|
|
1872
|
-
});
|
|
3195
|
+
// \u4FDD\u5B58\u6210\u529F\uFF1A\u5F39\u6846\u8BA9\u7528\u6237\u9009\u62E9\u5982\u4F55\u5E94\u7528\uFF08\u91CD\u65B0\u6CE8\u518C\u4E3A\u63A8\u8350\u9879\uFF0C\u4E5F\u53EF\u8DF3\u8FC7\uFF09\uFF0C
|
|
3196
|
+
// \u4E0D\u518D\u81EA\u52A8\u9759\u9ED8\u70ED\u5207\u6362\u2014\u2014\u907F\u514D\u7528\u6237\u8FD8\u6CA1\u770B\u6E05\u63D0\u793A\uFF0C\u6CE8\u518C\u5C31\u5DF2\u7ECF\u53D1\u751F
|
|
3197
|
+
showAfterSaveModal();
|
|
3198
|
+
return true;
|
|
1873
3199
|
}
|
|
1874
3200
|
showSchedMsg(t("schedSaveFail") + (d.error ? ": " + d.error : ""), false);
|
|
1875
3201
|
return false;
|
|
@@ -1885,10 +3211,64 @@ function saveServerConfig(restartAfter) {
|
|
|
1885
3211
|
});
|
|
1886
3212
|
}
|
|
1887
3213
|
|
|
1888
|
-
// \u4FDD\u5B58\
|
|
1889
|
-
function
|
|
1890
|
-
|
|
1891
|
-
|
|
3214
|
+
// \u2500\u2500 \u4FDD\u5B58\u6210\u529F\u540E\u7684\u300C\u5E94\u7528\u65B9\u5F0F\u300D\u5F39\u6846 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3215
|
+
function showAfterSaveModal() {
|
|
3216
|
+
document.getElementById("aftersaveTitle").textContent = t("aftersaveTitle");
|
|
3217
|
+
document.getElementById("aftersaveDesc").textContent = t("aftersaveDesc");
|
|
3218
|
+
var regBtn = document.getElementById("aftersaveBtnRegister");
|
|
3219
|
+
regBtn.textContent = t("aftersaveRegister");
|
|
3220
|
+
regBtn.setAttribute("data-recommended", t("aftersaveRegisterRec"));
|
|
3221
|
+
regBtn.disabled = false;
|
|
3222
|
+
var restartBtn = document.getElementById("aftersaveBtnRestart");
|
|
3223
|
+
restartBtn.textContent = t("aftersaveRestart");
|
|
3224
|
+
restartBtn.title = t("aftersaveRestartHint");
|
|
3225
|
+
restartBtn.disabled = false;
|
|
3226
|
+
document.getElementById("aftersaveBtnSkip").textContent = t("aftersaveSkip");
|
|
3227
|
+
document.getElementById("afterSaveModal").style.display = "flex";
|
|
3228
|
+
}
|
|
3229
|
+
|
|
3230
|
+
function closeAfterSave(e) {
|
|
3231
|
+
// \u70B9\u906E\u7F69\u7B49\u540C\u300C\u6682\u4E0D\u300D\uFF1A\u4FDD\u5B58\u5DF2\u6210\u529F\uFF0C\u6CA1\u6709\u53EF\u53D6\u6D88\u7684\u64CD\u4F5C
|
|
3232
|
+
if (e && e.target !== e.currentTarget) return;
|
|
3233
|
+
afterSaveAction("skip");
|
|
3234
|
+
}
|
|
3235
|
+
|
|
3236
|
+
function afterSaveAction(action) {
|
|
3237
|
+
var modal = document.getElementById("afterSaveModal");
|
|
3238
|
+
var regBtn = document.getElementById("aftersaveBtnRegister");
|
|
3239
|
+
var restartBtn = document.getElementById("aftersaveBtnRestart");
|
|
3240
|
+
var msg = document.getElementById("schedMsg");
|
|
3241
|
+
|
|
3242
|
+
if (action === "register") {
|
|
3243
|
+
regBtn.disabled = true;
|
|
3244
|
+
regBtn.textContent = t("aftersaveRegistering");
|
|
3245
|
+
runReRegister().then(function(reg) {
|
|
3246
|
+
modal.style.display = "none";
|
|
3247
|
+
regBtn.disabled = false;
|
|
3248
|
+
regBtn.textContent = t("aftersaveRegister");
|
|
3249
|
+
if (reg.ok) {
|
|
3250
|
+
showSchedMsg(t("aftersaveRegisterOk"), true);
|
|
3251
|
+
refreshNodeInfo();
|
|
3252
|
+
} else {
|
|
3253
|
+
showSchedMsg(t("aftersaveRegisterFail") + (reg.error ? ": " + reg.error : ""), false);
|
|
3254
|
+
}
|
|
3255
|
+
});
|
|
3256
|
+
return;
|
|
3257
|
+
}
|
|
3258
|
+
|
|
3259
|
+
if (action === "restart") {
|
|
3260
|
+
restartBtn.disabled = true;
|
|
3261
|
+
restartBtn.textContent = t("aftersaveRestarting");
|
|
3262
|
+
modal.style.display = "none";
|
|
3263
|
+
restartBtn.disabled = false;
|
|
3264
|
+
restartBtn.textContent = t("aftersaveRestart");
|
|
3265
|
+
doRestartBridge();
|
|
3266
|
+
return;
|
|
3267
|
+
}
|
|
3268
|
+
|
|
3269
|
+
// skip\uFF1A\u914D\u7F6E\u4FDD\u6301\u843D\u76D8\u72B6\u6001\uFF0C\u4E0B\u6B21 Bridge \u542F\u52A8\u65F6\u751F\u6548
|
|
3270
|
+
modal.style.display = "none";
|
|
3271
|
+
showSchedMsg(t("aftersaveSkipDone"), true);
|
|
1892
3272
|
}
|
|
1893
3273
|
|
|
1894
3274
|
// \u5220\u9664\u5F53\u524D\u7F16\u8F91\u7684 target\uFF1A\u4ECE\u5217\u8868\u79FB\u9664\u540E\u5168\u91CF\u63D0\u4EA4
|
|
@@ -1942,37 +3322,13 @@ function deleteTargetFromList() {
|
|
|
1942
3322
|
});
|
|
1943
3323
|
}
|
|
1944
3324
|
|
|
1945
|
-
// \u53D1\u8D77\u91CD\u65B0\u6CE8\u518C\u8BF7\u6C42\uFF08\u4E0D\u7BA1\u7406\u6309\u94AE\u72B6\u6001\uFF0C\u4F9B\
|
|
3325
|
+
// \u53D1\u8D77\u91CD\u65B0\u6CE8\u518C\u8BF7\u6C42\uFF08\u4E0D\u7BA1\u7406\u6309\u94AE\u72B6\u6001\uFF0C\u4F9B\u5F39\u6846\u4E0E\u5220\u9664\u540E\u7684\u70ED\u5207\u6362\u94FE\u5171\u7528\uFF09
|
|
1946
3326
|
function runReRegister() {
|
|
1947
3327
|
return fetch("/runtime/re-register", { method: "POST", headers: { "Content-Type": "application/json" } })
|
|
1948
3328
|
.then(function(r) { return r.json(); })
|
|
1949
3329
|
.catch(function() { return { ok: false }; });
|
|
1950
3330
|
}
|
|
1951
3331
|
|
|
1952
|
-
function doReRegister() {
|
|
1953
|
-
var btn = document.getElementById("btnReRegister");
|
|
1954
|
-
var msg = document.getElementById("schedMsg");
|
|
1955
|
-
msg.className = "s-msg";
|
|
1956
|
-
msg.style.display = "none";
|
|
1957
|
-
btn.disabled = true;
|
|
1958
|
-
btn.textContent = t("schedReRegistering");
|
|
1959
|
-
runReRegister()
|
|
1960
|
-
.then(function(d) {
|
|
1961
|
-
if (d.ok) {
|
|
1962
|
-
msg.textContent = t("schedReRegistered");
|
|
1963
|
-
msg.className = "s-msg success";
|
|
1964
|
-
refreshNodeInfo();
|
|
1965
|
-
} else {
|
|
1966
|
-
msg.textContent = t("schedReRegisterFail") + (d.error ? ": " + d.error : "");
|
|
1967
|
-
msg.className = "s-msg error";
|
|
1968
|
-
}
|
|
1969
|
-
})
|
|
1970
|
-
.then(function() {
|
|
1971
|
-
btn.disabled = false;
|
|
1972
|
-
btn.textContent = t("schedReRegister");
|
|
1973
|
-
});
|
|
1974
|
-
}
|
|
1975
|
-
|
|
1976
3332
|
// \u2500\u2500 Access Control \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
1977
3333
|
// \u52A0\u8F7D\u5F53\u524D IP \u767D\u540D\u5355\u5230\u8868\u5355\uFF08\u7A7A\u5217\u8868 = \u9ED8\u8BA4\u5168\u653E\u884C\uFF09
|
|
1978
3334
|
function refreshAccessControl() {
|
|
@@ -2059,109 +3415,132 @@ switchSettingsPage = function(page) {
|
|
|
2059
3415
|
else if (page === "logs") { refreshLogs(); startLogAutoRefresh(); }
|
|
2060
3416
|
else if (page === "scheduler") { refreshNodeInfo(); refreshServerConfig(); }
|
|
2061
3417
|
};
|
|
3418
|
+
|
|
3419
|
+
// \u2500\u2500 Dashboard\u300C\u5F15\u5BFC\u914D\u7F6E\u300D\u5E26\u6765\u7684\u8FDE\u63A5\u4FE1\u606F\uFF08#sched=add&serverUrl=...\uFF09\u2500\u2500
|
|
3420
|
+
// \u6253\u5F00\u8BBE\u7F6E\u5E76\u5207\u5230\u300C\u8C03\u5EA6\u4E2D\u5FC3\u300D\u9875\uFF1BuserKey \u5FC5\u987B\u7531\u7528\u6237\u624B\u52A8\u7C98\u8D34\uFF08\u4E0D\u8FDB URL\uFF09\u3002
|
|
3421
|
+
schedPrefill = readSchedPrefill();
|
|
3422
|
+
if (schedPrefill) {
|
|
3423
|
+
openSettings();
|
|
3424
|
+
switchSettingsPage("scheduler");
|
|
3425
|
+
}
|
|
2062
3426
|
</script>
|
|
2063
3427
|
</body>
|
|
2064
|
-
</html>`,
|
|
3428
|
+
</html>`,Fe=`<!DOCTYPE html>
|
|
2065
3429
|
<html lang="zh-CN">
|
|
2066
3430
|
<head>
|
|
2067
3431
|
<meta charset="UTF-8">
|
|
2068
3432
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
2069
3433
|
<title>Bridge Dashboard - Login</title>
|
|
3434
|
+
${O}
|
|
3435
|
+
${z}
|
|
2070
3436
|
<style>
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
--text: #e1e4f0;
|
|
2077
|
-
--text2: #8b90a8;
|
|
2078
|
-
--accent: #5b8def;
|
|
2079
|
-
--accent-hover: #4a7de0;
|
|
2080
|
-
--red: #f05454;
|
|
2081
|
-
--radius: 10px;
|
|
2082
|
-
}
|
|
2083
|
-
* { margin:0; padding:0; box-sizing:border-box; }
|
|
3437
|
+
/* \u4E0E\u4E3B\u9762\u677F\u5171\u7528\u540C\u4E00\u5957\u8BBE\u8BA1\u4EE4\u724C\uFF08\u9ED8\u8BA4\u6DF1\u8272\uFF0Chtml.light-theme \u5207\u6D45\u8272\uFF09 */
|
|
3438
|
+
${j}
|
|
3439
|
+
|
|
3440
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
3441
|
+
|
|
2084
3442
|
body {
|
|
2085
|
-
font-family: -
|
|
3443
|
+
font-family: var(--font-sans);
|
|
2086
3444
|
background: var(--bg);
|
|
2087
3445
|
color: var(--text);
|
|
2088
3446
|
min-height: 100vh;
|
|
2089
3447
|
display: flex;
|
|
2090
3448
|
align-items: center;
|
|
2091
3449
|
justify-content: center;
|
|
3450
|
+
padding: 20px;
|
|
3451
|
+
-webkit-font-smoothing: antialiased;
|
|
2092
3452
|
}
|
|
3453
|
+
|
|
2093
3454
|
.login-card {
|
|
2094
3455
|
background: var(--surface);
|
|
2095
3456
|
border: 1px solid var(--border);
|
|
2096
|
-
border-radius: var(--radius);
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
3457
|
+
border-radius: var(--radius-lg);
|
|
3458
|
+
box-shadow: var(--shadow-2);
|
|
3459
|
+
padding: 36px 32px 32px;
|
|
3460
|
+
width: 384px;
|
|
3461
|
+
max-width: 100%;
|
|
3462
|
+
}
|
|
3463
|
+
|
|
3464
|
+
.login-brand {
|
|
3465
|
+
display: flex;
|
|
3466
|
+
align-items: center;
|
|
3467
|
+
gap: 10px;
|
|
3468
|
+
justify-content: center;
|
|
3469
|
+
margin-bottom: 20px;
|
|
2100
3470
|
}
|
|
3471
|
+
.brand-mark {
|
|
3472
|
+
width: 26px;
|
|
3473
|
+
height: 26px;
|
|
3474
|
+
border-radius: 7px;
|
|
3475
|
+
object-fit: contain;
|
|
3476
|
+
flex-shrink: 0;
|
|
3477
|
+
}
|
|
3478
|
+
.login-brand-name { font-size: 15px; font-weight: 650; letter-spacing: -0.01em; }
|
|
3479
|
+
|
|
2101
3480
|
.login-card h1 {
|
|
2102
|
-
font-size:
|
|
2103
|
-
font-weight:
|
|
2104
|
-
|
|
3481
|
+
font-size: 19px;
|
|
3482
|
+
font-weight: 650;
|
|
3483
|
+
letter-spacing: -0.02em;
|
|
2105
3484
|
text-align: center;
|
|
3485
|
+
margin-bottom: 6px;
|
|
2106
3486
|
}
|
|
2107
3487
|
.login-card p.subtitle {
|
|
2108
3488
|
font-size: 13px;
|
|
2109
|
-
color: var(--
|
|
3489
|
+
color: var(--text-3);
|
|
2110
3490
|
text-align: center;
|
|
2111
|
-
margin-bottom:
|
|
2112
|
-
}
|
|
2113
|
-
.form-group {
|
|
2114
|
-
margin-bottom: 18px;
|
|
3491
|
+
margin-bottom: 26px;
|
|
2115
3492
|
}
|
|
3493
|
+
|
|
3494
|
+
.form-group { margin-bottom: 16px; }
|
|
2116
3495
|
.form-group label {
|
|
2117
3496
|
display: block;
|
|
2118
|
-
font-size:
|
|
2119
|
-
font-weight:
|
|
2120
|
-
margin-bottom:
|
|
2121
|
-
color: var(--
|
|
3497
|
+
font-size: 12.5px;
|
|
3498
|
+
font-weight: 600;
|
|
3499
|
+
margin-bottom: 7px;
|
|
3500
|
+
color: var(--text-2);
|
|
2122
3501
|
}
|
|
2123
3502
|
.form-group input {
|
|
2124
3503
|
width: 100%;
|
|
2125
3504
|
padding: 10px 12px;
|
|
2126
|
-
background: var(--
|
|
3505
|
+
background: var(--bg);
|
|
2127
3506
|
border: 1px solid var(--border);
|
|
2128
|
-
border-radius:
|
|
3507
|
+
border-radius: var(--radius-sm);
|
|
2129
3508
|
color: var(--text);
|
|
2130
|
-
font-size:
|
|
3509
|
+
font-size: 13.5px;
|
|
3510
|
+
font-family: inherit;
|
|
2131
3511
|
outline: none;
|
|
2132
|
-
transition: border-color .15s;
|
|
3512
|
+
transition: border-color .15s, box-shadow .15s;
|
|
2133
3513
|
}
|
|
2134
3514
|
.form-group input:focus {
|
|
2135
3515
|
border-color: var(--accent);
|
|
3516
|
+
box-shadow: 0 0 0 3px var(--accent-soft);
|
|
2136
3517
|
}
|
|
2137
|
-
.form-group input::placeholder {
|
|
2138
|
-
|
|
2139
|
-
opacity: 0.6;
|
|
2140
|
-
}
|
|
3518
|
+
.form-group input::placeholder { color: var(--text-3); }
|
|
3519
|
+
|
|
2141
3520
|
button {
|
|
2142
3521
|
width: 100%;
|
|
2143
3522
|
padding: 10px;
|
|
2144
3523
|
background: var(--accent);
|
|
2145
3524
|
border: none;
|
|
2146
|
-
border-radius:
|
|
3525
|
+
border-radius: var(--radius-sm);
|
|
2147
3526
|
color: #fff;
|
|
2148
|
-
font-size:
|
|
3527
|
+
font-size: 13.5px;
|
|
2149
3528
|
font-weight: 600;
|
|
3529
|
+
font-family: inherit;
|
|
2150
3530
|
cursor: pointer;
|
|
2151
|
-
transition: background .15s;
|
|
2152
|
-
margin-top:
|
|
3531
|
+
transition: background .15s, opacity .15s;
|
|
3532
|
+
margin-top: 6px;
|
|
2153
3533
|
}
|
|
2154
3534
|
button:hover { background: var(--accent-hover); }
|
|
2155
|
-
button:
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
}
|
|
3535
|
+
button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
3536
|
+
button:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
3537
|
+
|
|
2159
3538
|
.error-msg {
|
|
2160
|
-
background:
|
|
2161
|
-
color: var(--
|
|
2162
|
-
padding:
|
|
2163
|
-
border-radius:
|
|
2164
|
-
font-size:
|
|
3539
|
+
background: var(--danger-soft);
|
|
3540
|
+
color: var(--danger);
|
|
3541
|
+
padding: 9px 12px;
|
|
3542
|
+
border-radius: var(--radius-sm);
|
|
3543
|
+
font-size: 12.5px;
|
|
2165
3544
|
margin-bottom: 16px;
|
|
2166
3545
|
display: none;
|
|
2167
3546
|
}
|
|
@@ -2169,23 +3548,40 @@ switchSettingsPage = function(page) {
|
|
|
2169
3548
|
</style>
|
|
2170
3549
|
</head>
|
|
2171
3550
|
<body>
|
|
3551
|
+
<button type="button" class="theme-fab" id="themeFab" onclick="toggleThemeFab()" title="\u5207\u6362\u4E3B\u9898">☽</button>
|
|
2172
3552
|
<div class="login-card">
|
|
2173
|
-
<
|
|
2174
|
-
|
|
3553
|
+
<div class="login-brand">
|
|
3554
|
+
<img class="brand-mark" src="${Y}" alt="AgentsWorkStudio Bridge">
|
|
3555
|
+
<span class="login-brand-name">AgentsWorkStudio</span>
|
|
3556
|
+
</div>
|
|
3557
|
+
<h1>Bridge \u63A7\u5236\u53F0</h1>
|
|
3558
|
+
<p class="subtitle">\u767B\u5F55\u540E\u53EF\u7BA1\u7406\u672C\u673A Bridge</p>
|
|
2175
3559
|
<div class="error-msg" id="errorMsg"></div>
|
|
2176
3560
|
<form id="loginForm" onsubmit="return doLogin(event)">
|
|
2177
3561
|
<div class="form-group">
|
|
2178
|
-
<label for="username"
|
|
2179
|
-
<input type="text" id="username" name="username" placeholder="
|
|
3562
|
+
<label for="username">\u7528\u6237\u540D</label>
|
|
3563
|
+
<input type="text" id="username" name="username" placeholder="\u8BF7\u8F93\u5165\u7528\u6237\u540D" autocomplete="username" autofocus>
|
|
2180
3564
|
</div>
|
|
2181
3565
|
<div class="form-group">
|
|
2182
|
-
<label for="password"
|
|
2183
|
-
<input type="password" id="password" name="password" placeholder="
|
|
3566
|
+
<label for="password">\u5BC6\u7801</label>
|
|
3567
|
+
<input type="password" id="password" name="password" placeholder="\u8BF7\u8F93\u5165\u5BC6\u7801" autocomplete="current-password">
|
|
2184
3568
|
</div>
|
|
2185
|
-
<button type="submit" id="loginBtn"
|
|
3569
|
+
<button type="submit" id="loginBtn">\u767B\u5F55</button>
|
|
2186
3570
|
</form>
|
|
2187
3571
|
</div>
|
|
2188
3572
|
<script>
|
|
3573
|
+
/**
|
|
3574
|
+
* \u767B\u5F55\u540E\u5E26\u7740\u539F hash \u56DE\u4E3B\u9875\u3002
|
|
3575
|
+
*
|
|
3576
|
+
* \u5F15\u5BFC\u914D\u7F6E\u8DF3\u8F6C\u5F62\u5982 /#sched=add&serverUrl=...\uFF1Ahash \u4E0D\u4F1A\u968F\u8BF7\u6C42\u53D1\u7ED9\u670D\u52A1\u7AEF\uFF0C
|
|
3577
|
+
* \u4F46\u670D\u52A1\u7AEF\u628A\u9996\u9875\u91CD\u5B9A\u5411\u5230 /login \u65F6\u5B83\u4ECD\u5728\u5730\u5740\u680F\u91CC\u3002\u6B64\u5904\u82E5\u8DF3 "/" \u4F1A\u628A\u5B83\u4E22\u6389\uFF0C
|
|
3578
|
+
* \u300C\u5E26\u4E86\u914D\u7F6E\u8FDB\u6765\u5374\u4EC0\u4E48\u90FD\u6CA1\u5E26\u5165\u300D\u3002setup \u9875\u540C\u7406\uFF08\u9996\u6B21\u767B\u5F55\u8981\u5148\u8BBE\u7F6E\u8D26\u53F7\u5BC6\u7801\uFF09\u3002
|
|
3579
|
+
*/
|
|
3580
|
+
function continueWithHash(path) {
|
|
3581
|
+
var hash = String(window.location.hash || "");
|
|
3582
|
+
window.location.href = path + hash;
|
|
3583
|
+
}
|
|
3584
|
+
|
|
2189
3585
|
async function doLogin(e) {
|
|
2190
3586
|
e.preventDefault();
|
|
2191
3587
|
const btn = document.getElementById("loginBtn");
|
|
@@ -2207,9 +3603,9 @@ async function doLogin(e) {
|
|
|
2207
3603
|
if (resp.ok) {
|
|
2208
3604
|
const data = await resp.json();
|
|
2209
3605
|
if (data.needSetup) {
|
|
2210
|
-
|
|
3606
|
+
continueWithHash("/setup");
|
|
2211
3607
|
} else {
|
|
2212
|
-
|
|
3608
|
+
continueWithHash("/");
|
|
2213
3609
|
}
|
|
2214
3610
|
return;
|
|
2215
3611
|
}
|
|
@@ -2231,27 +3627,20 @@ document.getElementById("loginForm").addEventListener("keydown", function(e) {
|
|
|
2231
3627
|
if (e.key === "Enter") doLogin(e);
|
|
2232
3628
|
});
|
|
2233
3629
|
</script>
|
|
3630
|
+
${X}
|
|
2234
3631
|
</body>
|
|
2235
|
-
</html>`,
|
|
3632
|
+
</html>`,_e=`<!DOCTYPE html>
|
|
2236
3633
|
<html lang="zh-CN">
|
|
2237
3634
|
<head>
|
|
2238
3635
|
<meta charset="UTF-8">
|
|
2239
3636
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
2240
3637
|
<title>Bridge Dashboard - Setup</title>
|
|
3638
|
+
${O}
|
|
3639
|
+
${z}
|
|
2241
3640
|
<style>
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
--surface2: #232738;
|
|
2246
|
-
--border: #2d3248;
|
|
2247
|
-
--text: #e1e4f0;
|
|
2248
|
-
--text2: #8b90a8;
|
|
2249
|
-
--accent: #5b8def;
|
|
2250
|
-
--accent-hover: #4a7de0;
|
|
2251
|
-
--green: #43c6a8;
|
|
2252
|
-
--red: #f05454;
|
|
2253
|
-
--radius: 10px;
|
|
2254
|
-
}
|
|
3641
|
+
/* \u4E0E\u4E3B\u9762\u677F\u5171\u7528\u8BBE\u8BA1\u4EE4\u724C\uFF08--text2/--surface2/--green/--red \u4E3A\u517C\u5BB9\u522B\u540D\uFF09 */
|
|
3642
|
+
${j}
|
|
3643
|
+
|
|
2255
3644
|
* { margin:0; padding:0; box-sizing:border-box; }
|
|
2256
3645
|
body {
|
|
2257
3646
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans SC", sans-serif;
|
|
@@ -2395,6 +3784,7 @@ document.getElementById("loginForm").addEventListener("keydown", function(e) {
|
|
|
2395
3784
|
</style>
|
|
2396
3785
|
</head>
|
|
2397
3786
|
<body>
|
|
3787
|
+
<button type="button" class="theme-fab" id="themeFab" onclick="toggleThemeFab()" title="\u5207\u6362\u4E3B\u9898">☽</button>
|
|
2398
3788
|
<div class="setup-card" id="setupCard">
|
|
2399
3789
|
<h1>Bridge Dashboard</h1>
|
|
2400
3790
|
<p class="subtitle">First-time setup \u2014 \u9996\u6B21\u767B\u5F55\u8BBE\u7F6E</p>
|
|
@@ -2468,7 +3858,8 @@ async function doSave(e) {
|
|
|
2468
3858
|
document.getElementById("setupForm").style.display = "none";
|
|
2469
3859
|
document.querySelector(".hint").style.display = "none";
|
|
2470
3860
|
document.querySelector(".badge").style.display = "none";
|
|
2471
|
-
|
|
3861
|
+
// \u4FDD\u7559 hash\uFF1A\u5F15\u5BFC\u914D\u7F6E\u662F /#sched=add&serverUrl=... \u2192 /login \u2192 /setup \u4E00\u8DEF\u5E26\u8FC7\u6765\u7684
|
|
3862
|
+
setTimeout(() => { window.location.href = "/" + String(window.location.hash || ""); }, 1500);
|
|
2472
3863
|
} else {
|
|
2473
3864
|
errorEl.textContent = data.error || "Save failed";
|
|
2474
3865
|
errorEl.classList.add("visible");
|
|
@@ -2501,7 +3892,8 @@ async function doSkip() {
|
|
|
2501
3892
|
document.getElementById("setupForm").style.display = "none";
|
|
2502
3893
|
document.querySelector(".hint").style.display = "none";
|
|
2503
3894
|
document.querySelector(".badge").style.display = "none";
|
|
2504
|
-
|
|
3895
|
+
// \u4FDD\u7559 hash\uFF1A\u5F15\u5BFC\u914D\u7F6E\u662F /#sched=add&serverUrl=... \u2192 /login \u2192 /setup \u4E00\u8DEF\u5E26\u8FC7\u6765\u7684
|
|
3896
|
+
setTimeout(() => { window.location.href = "/" + String(window.location.hash || ""); }, 1500);
|
|
2505
3897
|
} else {
|
|
2506
3898
|
errorEl.textContent = data.error || "Skip failed";
|
|
2507
3899
|
errorEl.classList.add("visible");
|
|
@@ -2518,6 +3910,7 @@ async function doSkip() {
|
|
|
2518
3910
|
|
|
2519
3911
|
document.getElementById("setupForm").addEventListener("submit", doSave);
|
|
2520
3912
|
</script>
|
|
3913
|
+
${X}
|
|
2521
3914
|
</body>
|
|
2522
|
-
</html>`;export{
|
|
3915
|
+
</html>`;export{f as dashboardRouter,Pe as noteDashboardActivity};
|
|
2523
3916
|
|