From 2536a3aa885ffb534784601ea6a0f67c771f1524 Mon Sep 17 00:00:00 2001 From: zaunist Date: Mon, 7 Apr 2025 21:38:08 +0800 Subject: [PATCH] fix: call real api --- mobile/app.json | 3 +- mobile/package-lock.json | 396 ++++++++++---- mobile/package.json | 29 +- mobile/src/api/agents.ts | 34 +- mobile/src/assets/adaptive-icon.png | Bin 17547 -> 3132 bytes mobile/src/assets/favicon.png | Bin 1466 -> 532 bytes mobile/src/assets/icon.png | Bin 22380 -> 10275 bytes mobile/src/assets/splash-icon.png | Bin 17547 -> 2885 bytes mobile/src/i18n/locales/en.ts | 31 ++ mobile/src/i18n/locales/zh.ts | 31 ++ .../src/screens/agents/AgentDetailScreen.tsx | 155 +----- mobile/src/screens/agents/AgentsScreen.tsx | 9 +- .../src/screens/agents/CreateAgentScreen.tsx | 448 ++++++++++++---- .../screens/monitors/CreateMonitorScreen.tsx | 486 +++++++++++++++--- .../screens/monitors/MonitorDetailScreen.tsx | 147 ++---- .../src/screens/monitors/MonitorsScreen.tsx | 43 -- mobile/src/screens/status/StatusScreen.tsx | 4 +- 17 files changed, 1176 insertions(+), 640 deletions(-) diff --git a/mobile/app.json b/mobile/app.json index e8963e6..627460f 100644 --- a/mobile/app.json +++ b/mobile/app.json @@ -15,7 +15,8 @@ "**/*" ], "ios": { - "supportsTablet": true + "supportsTablet": true, + "bundleIdentifier": "uk.mdzz.xugou" }, "android": { "adaptiveIcon": { diff --git a/mobile/package-lock.json b/mobile/package-lock.json index 8994b1d..bfcdc1f 100644 --- a/mobile/package-lock.json +++ b/mobile/package-lock.json @@ -9,27 +9,28 @@ "version": "0.0.1", "dependencies": { "@expo/metro-runtime": "~4.0.1", - "@react-native-async-storage/async-storage": "^2.1.2", + "@react-native-async-storage/async-storage": "1.23.1", "@react-navigation/bottom-tabs": "^7.3.3", "@react-navigation/native": "^7.0.19", "@react-navigation/native-stack": "^7.3.3", "async-storage": "^0.1.0", "axios": "^1.8.4", "expo": "~52.0.42", - "expo-dev-client": "~5.0.17", + "expo-clipboard": "~7.0.1", + "expo-dev-client": "~5.0.19", "expo-localization": "^16.0.1", "expo-status-bar": "~2.0.1", "i18next": "^24.2.3", "react": "^18.3.1", "react-dom": "^18.3.1", "react-i18next": "^15.4.1", - "react-native": "0.76.8", + "react-native": "0.76.9", "react-native-chart-kit": "^6.12.0", - "react-native-gesture-handler": "^2.25.0", - "react-native-reanimated": "^3.17.1", - "react-native-safe-area-context": "^5.3.0", - "react-native-screens": "^4.10.0", - "react-native-svg": "^15.11.2", + "react-native-gesture-handler": "~2.20.2", + "react-native-reanimated": "~3.16.1", + "react-native-safe-area-context": "4.12.0", + "react-native-screens": "~4.4.0", + "react-native-svg": "15.8.0", "react-native-web": "~0.19.13", "victory-native": "^41.16.1", "zustand": "^5.0.3" @@ -3226,21 +3227,21 @@ } }, "node_modules/@react-native-async-storage/async-storage": { - "version": "2.1.2", - "resolved": "https://registry.npmmirror.com/@react-native-async-storage/async-storage/-/async-storage-2.1.2.tgz", - "integrity": "sha512-dvlNq4AlGWC+ehtH12p65+17V0Dx7IecOWl6WanF2ja38O1Dcjjvn7jVzkUHJ5oWkQBlyASurTPlTHgKXyYiow==", + "version": "1.23.1", + "resolved": "https://registry.npmmirror.com/@react-native-async-storage/async-storage/-/async-storage-1.23.1.tgz", + "integrity": "sha512-Qd2kQ3yi6Y3+AcUlrHxSLlnBvpdCEMVGFlVBneVOjaFaPU61g1huc38g339ysXspwY1QZA2aNhrk/KlHGO+ewA==", "license": "MIT", "dependencies": { "merge-options": "^3.0.4" }, "peerDependencies": { - "react-native": "^0.0.0-0 || >=0.65 <1.0" + "react-native": "^0.0.0-0 || >=0.60 <1.0" } }, "node_modules/@react-native/assets-registry": { - "version": "0.76.8", - "resolved": "https://registry.npmmirror.com/@react-native/assets-registry/-/assets-registry-0.76.8.tgz", - "integrity": "sha512-vQQi3kabQpj21Iohy2ou3/laCRE5hlW4r122ZivqCIN/UMwr5vT2/fTgPOBQoJ5X3YhZBe58BmifIstZutm0Ew==", + "version": "0.76.9", + "resolved": "https://registry.npmmirror.com/@react-native/assets-registry/-/assets-registry-0.76.9.tgz", + "integrity": "sha512-pN0Ws5xsjWOZ8P37efh0jqHHQmq+oNGKT4AyAoKRpxBDDDmlAmpaYjer9Qz7PpDKF+IUyRjF/+rBsM50a8JcUg==", "license": "MIT", "engines": { "node": ">=18" @@ -3361,13 +3362,13 @@ } }, "node_modules/@react-native/community-cli-plugin": { - "version": "0.76.8", - "resolved": "https://registry.npmmirror.com/@react-native/community-cli-plugin/-/community-cli-plugin-0.76.8.tgz", - "integrity": "sha512-3rs6YehAVEootGpzchmj1ln2BCSTnIDTKqAYykUggEwpplg+CNTiBrvRrnjmlLXk3nOlIE8KOw4zRCd3PpI+bg==", + "version": "0.76.9", + "resolved": "https://registry.npmmirror.com/@react-native/community-cli-plugin/-/community-cli-plugin-0.76.9.tgz", + "integrity": "sha512-08jx8ixCjjd4jNQwNpP8yqrjrDctN2qvPPlf6ebz1OJQk8e1sbUl3wVn1zhhMvWrYcaraDnatPb5uCPq+dn3NQ==", "license": "MIT", "dependencies": { - "@react-native/dev-middleware": "0.76.8", - "@react-native/metro-babel-transformer": "0.76.8", + "@react-native/dev-middleware": "0.76.9", + "@react-native/metro-babel-transformer": "0.76.9", "chalk": "^4.0.0", "execa": "^5.1.1", "invariant": "^2.2.4", @@ -3390,6 +3391,47 @@ } } }, + "node_modules/@react-native/community-cli-plugin/node_modules/@react-native/debugger-frontend": { + "version": "0.76.9", + "resolved": "https://registry.npmmirror.com/@react-native/debugger-frontend/-/debugger-frontend-0.76.9.tgz", + "integrity": "sha512-0Ru72Bm066xmxFuOXhhvrryxvb57uI79yDSFf+hxRpktkC98NMuRenlJhslMrbJ6WjCu1vOe/9UjWNYyxXTRTA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-native/community-cli-plugin/node_modules/@react-native/dev-middleware": { + "version": "0.76.9", + "resolved": "https://registry.npmmirror.com/@react-native/dev-middleware/-/dev-middleware-0.76.9.tgz", + "integrity": "sha512-xkd3C3dRcmZLjFTEAOvC14q3apMLouIvJViCZY/p1EfCMrNND31dgE1dYrLTiI045WAWMt5bD15i6f7dE2/QWA==", + "license": "MIT", + "dependencies": { + "@isaacs/ttlcache": "^1.4.1", + "@react-native/debugger-frontend": "0.76.9", + "chrome-launcher": "^0.15.2", + "chromium-edge-launcher": "^0.2.0", + "connect": "^3.6.5", + "debug": "^2.2.0", + "invariant": "^2.2.4", + "nullthrows": "^1.1.1", + "open": "^7.0.3", + "selfsigned": "^2.4.1", + "serve-static": "^1.13.1", + "ws": "^6.2.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-native/community-cli-plugin/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, "node_modules/@react-native/community-cli-plugin/node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmmirror.com/execa/-/execa-5.1.1.tgz", @@ -3446,6 +3488,12 @@ "node": ">=6" } }, + "node_modules/@react-native/community-cli-plugin/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/@react-native/community-cli-plugin/node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -3491,6 +3539,15 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "license": "ISC" }, + "node_modules/@react-native/community-cli-plugin/node_modules/ws": { + "version": "6.2.3", + "resolved": "https://registry.npmmirror.com/ws/-/ws-6.2.3.tgz", + "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", + "license": "MIT", + "dependencies": { + "async-limiter": "~1.0.0" + } + }, "node_modules/@react-native/debugger-frontend": { "version": "0.76.8", "resolved": "https://registry.npmmirror.com/@react-native/debugger-frontend/-/debugger-frontend-0.76.8.tgz", @@ -3548,31 +3605,31 @@ } }, "node_modules/@react-native/gradle-plugin": { - "version": "0.76.8", - "resolved": "https://registry.npmmirror.com/@react-native/gradle-plugin/-/gradle-plugin-0.76.8.tgz", - "integrity": "sha512-rgocQRydHS8IkcyUs1VFhz+Q0OjL3QYI5/A7pMLRz6nEW8GAmoFnXqwujtPHivVrXbuCLIzGBKnsl2hsZsOopg==", + "version": "0.76.9", + "resolved": "https://registry.npmmirror.com/@react-native/gradle-plugin/-/gradle-plugin-0.76.9.tgz", + "integrity": "sha512-uGzp3dL4GfNDz+jOb8Nik1Vrfq1LHm0zESizrGhHACFiFlUSflVAnWuUAjlZlz5XfLhzGVvunG4Vdrpw8CD2ng==", "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/@react-native/js-polyfills": { - "version": "0.76.8", - "resolved": "https://registry.npmmirror.com/@react-native/js-polyfills/-/js-polyfills-0.76.8.tgz", - "integrity": "sha512-bZzPjpDQaWU+F//N/WZJfaglYgYga4oXl9rzXyKOJP7KcebkOKJbAsG1mo7RCOVIbHYQCKUvQXIpV3IQOWNOEg==", + "version": "0.76.9", + "resolved": "https://registry.npmmirror.com/@react-native/js-polyfills/-/js-polyfills-0.76.9.tgz", + "integrity": "sha512-s6z6m8cK4SMjIX1hm8LT187aQ6//ujLrjzDBogqDCYXRbfjbAYovw5as/v2a2rhUIyJbS3UjokZm3W0H+Oh/RQ==", "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/@react-native/metro-babel-transformer": { - "version": "0.76.8", - "resolved": "https://registry.npmmirror.com/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.76.8.tgz", - "integrity": "sha512-tEjNJo3Wwiig1OmI7H3cLOrAkAxT9lBqs1k+oanRm64o0Cay2ACH0PdXrk45sxENU3iTfxXeQ7mmY1u2XVYDpw==", + "version": "0.76.9", + "resolved": "https://registry.npmmirror.com/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.76.9.tgz", + "integrity": "sha512-HGq11347UHNiO/NvVbAO35hQCmH8YZRs7in7nVq7SL99pnpZK4WXwLdAXmSuwz5uYqOuwnKYDlpadz8fkE94Mg==", "license": "MIT", "dependencies": { "@babel/core": "^7.25.2", - "@react-native/babel-preset": "0.76.8", + "@react-native/babel-preset": "0.76.9", "hermes-parser": "0.23.1", "nullthrows": "^1.1.1" }, @@ -3583,6 +3640,120 @@ "@babel/core": "*" } }, + "node_modules/@react-native/metro-babel-transformer/node_modules/@react-native/babel-plugin-codegen": { + "version": "0.76.9", + "resolved": "https://registry.npmmirror.com/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.76.9.tgz", + "integrity": "sha512-vxL/vtDEIYHfWKm5oTaEmwcnNGsua/i9OjIxBDBFiJDu5i5RU3bpmDiXQm/bJxrJNPRp5lW0I0kpGihVhnMAIQ==", + "license": "MIT", + "dependencies": { + "@react-native/codegen": "0.76.9" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@react-native/metro-babel-transformer/node_modules/@react-native/babel-preset": { + "version": "0.76.9", + "resolved": "https://registry.npmmirror.com/@react-native/babel-preset/-/babel-preset-0.76.9.tgz", + "integrity": "sha512-TbSeCplCM6WhL3hR2MjC/E1a9cRnMLz7i767T7mP90oWkklEjyPxWl+0GGoVGnJ8FC/jLUupg/HvREKjjif6lw==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/plugin-proposal-export-default-from": "^7.24.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-default-from": "^7.24.7", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-transform-arrow-functions": "^7.24.7", + "@babel/plugin-transform-async-generator-functions": "^7.25.4", + "@babel/plugin-transform-async-to-generator": "^7.24.7", + "@babel/plugin-transform-block-scoping": "^7.25.0", + "@babel/plugin-transform-class-properties": "^7.25.4", + "@babel/plugin-transform-classes": "^7.25.4", + "@babel/plugin-transform-computed-properties": "^7.24.7", + "@babel/plugin-transform-destructuring": "^7.24.8", + "@babel/plugin-transform-flow-strip-types": "^7.25.2", + "@babel/plugin-transform-for-of": "^7.24.7", + "@babel/plugin-transform-function-name": "^7.25.1", + "@babel/plugin-transform-literals": "^7.25.2", + "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.8", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", + "@babel/plugin-transform-numeric-separator": "^7.24.7", + "@babel/plugin-transform-object-rest-spread": "^7.24.7", + "@babel/plugin-transform-optional-catch-binding": "^7.24.7", + "@babel/plugin-transform-optional-chaining": "^7.24.8", + "@babel/plugin-transform-parameters": "^7.24.7", + "@babel/plugin-transform-private-methods": "^7.24.7", + "@babel/plugin-transform-private-property-in-object": "^7.24.7", + "@babel/plugin-transform-react-display-name": "^7.24.7", + "@babel/plugin-transform-react-jsx": "^7.25.2", + "@babel/plugin-transform-react-jsx-self": "^7.24.7", + "@babel/plugin-transform-react-jsx-source": "^7.24.7", + "@babel/plugin-transform-regenerator": "^7.24.7", + "@babel/plugin-transform-runtime": "^7.24.7", + "@babel/plugin-transform-shorthand-properties": "^7.24.7", + "@babel/plugin-transform-spread": "^7.24.7", + "@babel/plugin-transform-sticky-regex": "^7.24.7", + "@babel/plugin-transform-typescript": "^7.25.2", + "@babel/plugin-transform-unicode-regex": "^7.24.7", + "@babel/template": "^7.25.0", + "@react-native/babel-plugin-codegen": "0.76.9", + "babel-plugin-syntax-hermes-parser": "^0.25.1", + "babel-plugin-transform-flow-enums": "^0.0.2", + "react-refresh": "^0.14.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@babel/core": "*" + } + }, + "node_modules/@react-native/metro-babel-transformer/node_modules/@react-native/codegen": { + "version": "0.76.9", + "resolved": "https://registry.npmmirror.com/@react-native/codegen/-/codegen-0.76.9.tgz", + "integrity": "sha512-AzlCHMTKrAVC2709V4ZGtBXmGVtWTpWm3Ruv5vXcd3/anH4mGucfJ4rjbWKdaYQJMpXa3ytGomQrsIsT/s8kgA==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.3", + "glob": "^7.1.1", + "hermes-parser": "0.23.1", + "invariant": "^2.2.4", + "jscodeshift": "^0.14.0", + "mkdirp": "^0.5.1", + "nullthrows": "^1.1.1", + "yargs": "^17.6.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@babel/preset-env": "^7.1.6" + } + }, + "node_modules/@react-native/metro-babel-transformer/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@react-native/normalize-colors": { "version": "0.76.8", "resolved": "https://registry.npmmirror.com/@react-native/normalize-colors/-/normalize-colors-0.76.8.tgz", @@ -3590,9 +3761,9 @@ "license": "MIT" }, "node_modules/@react-native/virtualized-lists": { - "version": "0.76.8", - "resolved": "https://registry.npmmirror.com/@react-native/virtualized-lists/-/virtualized-lists-0.76.8.tgz", - "integrity": "sha512-OsB+LoEFH80wL+qhQrNpJR3O3oNDmyZf0go7/MQiayZbT8IkD8aPdHmK4QIVoIwAbFWXw7RkcFdubhSWwi0wAQ==", + "version": "0.76.9", + "resolved": "https://registry.npmmirror.com/@react-native/virtualized-lists/-/virtualized-lists-0.76.9.tgz", + "integrity": "sha512-2neUfZKuqMK2LzfS8NyOWOyWUJOWgDym5fUph6fN9qF+LNPjAvnc4Zr9+o+59qjNu/yXwQgVMWNU4+8WJuPVWw==", "license": "MIT", "dependencies": { "invariant": "^2.2.4", @@ -6128,6 +6299,17 @@ "react-native": "*" } }, + "node_modules/expo-clipboard": { + "version": "7.0.1", + "resolved": "https://registry.npmmirror.com/expo-clipboard/-/expo-clipboard-7.0.1.tgz", + "integrity": "sha512-rqYk0+WoqitPcPKxmMxSpLonX1E5Ije3LBYfnYMbH3xU5Gr8EAH9QnOWOi4BgahUPvcot6nbFEnx+DqARrmxKQ==", + "license": "MIT", + "peerDependencies": { + "expo": "*", + "react": "*", + "react-native": "*" + } + }, "node_modules/expo-constants": { "version": "17.0.8", "resolved": "https://registry.npmmirror.com/expo-constants/-/expo-constants-17.0.8.tgz", @@ -6143,15 +6325,15 @@ } }, "node_modules/expo-dev-client": { - "version": "5.0.17", - "resolved": "https://registry.npmmirror.com/expo-dev-client/-/expo-dev-client-5.0.17.tgz", - "integrity": "sha512-Vgj3DmhChWJpQFGtW4eFMScrrOKkUAdMO/E/3ygp9hoIrrt4FSqiRRctk3eLc6hUzcdR0CaobJtygna0Ho73rQ==", + "version": "5.0.20", + "resolved": "https://registry.npmmirror.com/expo-dev-client/-/expo-dev-client-5.0.20.tgz", + "integrity": "sha512-bLNkHdU7V3I4UefgJbJnIDUBUL0LxIal/xYEx9BbgDd3B7wgQKY//+BpPIxBOKCQ22lkyiHY8y9tLhO903sAgg==", "license": "MIT", "dependencies": { - "expo-dev-launcher": "5.0.32", - "expo-dev-menu": "6.0.23", + "expo-dev-launcher": "5.0.35", + "expo-dev-menu": "6.0.25", "expo-dev-menu-interface": "1.9.3", - "expo-manifests": "~0.15.7", + "expo-manifests": "~0.15.8", "expo-updates-interface": "~1.0.0" }, "peerDependencies": { @@ -6159,36 +6341,24 @@ } }, "node_modules/expo-dev-launcher": { - "version": "5.0.32", - "resolved": "https://registry.npmmirror.com/expo-dev-launcher/-/expo-dev-launcher-5.0.32.tgz", - "integrity": "sha512-Inb8DAx5vRPd/8okx0Auu1jdUJW4f3gEzJwJKbkRl7kL4VTjFBFnkRpt4VEKTb3qAtm8BsqeLhEedW0PAVJmfA==", + "version": "5.0.35", + "resolved": "https://registry.npmmirror.com/expo-dev-launcher/-/expo-dev-launcher-5.0.35.tgz", + "integrity": "sha512-hEQr0ZREnUMxZ6wtQgfK1lzYnbb0zar3HqYZhmANzXmE6UEPbQ4GByLzhpfz/d+xxdBVQZsrHdtiV28KPG2sog==", "license": "MIT", "dependencies": { "ajv": "8.11.0", - "expo-dev-menu": "6.0.22", - "expo-manifests": "~0.15.7", + "expo-dev-menu": "6.0.25", + "expo-manifests": "~0.15.8", "resolve-from": "^5.0.0" }, "peerDependencies": { "expo": "*" } }, - "node_modules/expo-dev-launcher/node_modules/expo-dev-menu": { - "version": "6.0.22", - "resolved": "https://registry.npmmirror.com/expo-dev-menu/-/expo-dev-menu-6.0.22.tgz", - "integrity": "sha512-3NuBgGerUzwLLrz5Y7w/fPDUZ5GfREO9ntvU/b/srPWEOmtUYMIvKTuLX+SbMJBZdQGilfF7xxo93WxIYSp/rw==", - "license": "MIT", - "dependencies": { - "expo-dev-menu-interface": "1.9.3" - }, - "peerDependencies": { - "expo": "*" - } - }, "node_modules/expo-dev-menu": { - "version": "6.0.23", - "resolved": "https://registry.npmmirror.com/expo-dev-menu/-/expo-dev-menu-6.0.23.tgz", - "integrity": "sha512-bO1FhbrQrSVGzAm36xV0SC+vAbhXFswx+w8iAF8osm+zniPNhRv1PQH/GWeet/YVorZoxyiS0juwLqP49rirEw==", + "version": "6.0.25", + "resolved": "https://registry.npmmirror.com/expo-dev-menu/-/expo-dev-menu-6.0.25.tgz", + "integrity": "sha512-K2m4z/I+CPWbMtHlDzU68lHaQs52De0v5gbsjAmA5ig8FrYh4MKZvPxSVANaiKENzgmtglu8qaFh7ua9Gt2TfA==", "license": "MIT", "dependencies": { "expo-dev-menu-interface": "1.9.3" @@ -6262,12 +6432,12 @@ } }, "node_modules/expo-manifests": { - "version": "0.15.7", - "resolved": "https://registry.npmmirror.com/expo-manifests/-/expo-manifests-0.15.7.tgz", - "integrity": "sha512-IVzLcPamzUi4Br96xw6JPHaa1vjYupUnMqYyV1Mtd9VQojS5hJsf5VcVzbAMZE/cFGzWLZ1oJa6QXxYjN39Uww==", + "version": "0.15.8", + "resolved": "https://registry.npmmirror.com/expo-manifests/-/expo-manifests-0.15.8.tgz", + "integrity": "sha512-VuIyaMfRfLZeETNsRohqhy1l7iZ7I+HKMPfZXVL2Yn17TT0WkOhZoq1DzYwPbOHPgp1Uk6phNa86EyaHrD2DLw==", "license": "MIT", "dependencies": { - "@expo/config": "~10.0.10", + "@expo/config": "~10.0.11", "expo-json-utils": "~0.14.0" }, "peerDependencies": { @@ -7094,9 +7264,9 @@ } }, "node_modules/image-size": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/image-size/-/image-size-1.2.0.tgz", - "integrity": "sha512-4S8fwbO6w3GeCVN6OPtA9I5IGKkcDMPcKndtUlpJuCwu7JLjtj7JZpwqLuyY2nrmQT3AWsCJLSKPsc2mPBSl3w==", + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/image-size/-/image-size-1.2.1.tgz", + "integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==", "license": "MIT", "dependencies": { "queue": "6.0.2" @@ -9934,19 +10104,19 @@ "license": "MIT" }, "node_modules/react-native": { - "version": "0.76.8", - "resolved": "https://registry.npmmirror.com/react-native/-/react-native-0.76.8.tgz", - "integrity": "sha512-sVoAd/cDBdVoLjNz8Lp5DAg4l6Iag0l5AiChN2J/NGhM//PW6+MIW50TI8G1iZz4rquAgWtcCHwEp5P5FvMyDA==", + "version": "0.76.9", + "resolved": "https://registry.npmmirror.com/react-native/-/react-native-0.76.9.tgz", + "integrity": "sha512-+LRwecWmTDco7OweGsrECIqJu0iyrREd6CTCgC/uLLYipiHvk+MH9nd6drFtCw/6Blz6eoKTcH9YTTJusNtrWg==", "license": "MIT", "dependencies": { "@jest/create-cache-key-function": "^29.6.3", - "@react-native/assets-registry": "0.76.8", - "@react-native/codegen": "0.76.8", - "@react-native/community-cli-plugin": "0.76.8", - "@react-native/gradle-plugin": "0.76.8", - "@react-native/js-polyfills": "0.76.8", - "@react-native/normalize-colors": "0.76.8", - "@react-native/virtualized-lists": "0.76.8", + "@react-native/assets-registry": "0.76.9", + "@react-native/codegen": "0.76.9", + "@react-native/community-cli-plugin": "0.76.9", + "@react-native/gradle-plugin": "0.76.9", + "@react-native/js-polyfills": "0.76.9", + "@react-native/normalize-colors": "0.76.9", + "@react-native/virtualized-lists": "0.76.9", "abort-controller": "^3.0.0", "anser": "^1.4.9", "ansi-regex": "^5.0.0", @@ -10011,34 +10181,25 @@ } }, "node_modules/react-native-gesture-handler": { - "version": "2.25.0", - "resolved": "https://registry.npmmirror.com/react-native-gesture-handler/-/react-native-gesture-handler-2.25.0.tgz", - "integrity": "sha512-NPjJi6mislXxvjxQPU9IYwBjb1Uejp8GvAbE1Lhh+xMIMEvmgAvVIp5cz1P+xAbV6uYcRRArm278+tEInGOqWg==", + "version": "2.20.2", + "resolved": "https://registry.npmmirror.com/react-native-gesture-handler/-/react-native-gesture-handler-2.20.2.tgz", + "integrity": "sha512-HqzFpFczV4qCnwKlvSAvpzEXisL+Z9fsR08YV5LfJDkzuArMhBu2sOoSPUF/K62PCoAb+ObGlTC83TKHfUd0vg==", "license": "MIT", "dependencies": { "@egjs/hammerjs": "^2.0.17", "hoist-non-react-statics": "^3.3.0", - "invariant": "^2.2.4" + "invariant": "^2.2.4", + "prop-types": "^15.7.2" }, "peerDependencies": { "react": "*", "react-native": "*" } }, - "node_modules/react-native-is-edge-to-edge": { - "version": "1.1.6", - "resolved": "https://registry.npmmirror.com/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.1.6.tgz", - "integrity": "sha512-1pHnFTlBahins6UAajXUqeCOHew9l9C2C8tErnpGC3IyLJzvxD+TpYAixnCbrVS52f7+NvMttbiSI290XfwN0w==", - "license": "MIT", - "peerDependencies": { - "react": ">=18.2.0", - "react-native": ">=0.73.0" - } - }, "node_modules/react-native-reanimated": { - "version": "3.17.1", - "resolved": "https://registry.npmmirror.com/react-native-reanimated/-/react-native-reanimated-3.17.1.tgz", - "integrity": "sha512-ECzLhLxMKLifv34a8799/MHqIazQZV9fLMNSMdixXQlzX71RyL3/ah3cz/h3ERoyhJAYRC2ySLLZho6pXSqMFQ==", + "version": "3.16.7", + "resolved": "https://registry.npmmirror.com/react-native-reanimated/-/react-native-reanimated-3.16.7.tgz", + "integrity": "sha512-qoUUQOwE1pHlmQ9cXTJ2MX9FQ9eHllopCLiWOkDkp6CER95ZWeXhJCP4cSm6AD4jigL5jHcZf/SkWrg8ttZUsw==", "license": "MIT", "dependencies": { "@babel/plugin-transform-arrow-functions": "^7.0.0-0", @@ -10051,8 +10212,7 @@ "@babel/plugin-transform-unicode-regex": "^7.0.0-0", "@babel/preset-typescript": "^7.16.7", "convert-source-map": "^2.0.0", - "invariant": "^2.2.4", - "react-native-is-edge-to-edge": "1.1.6" + "invariant": "^2.2.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0", @@ -10061,9 +10221,9 @@ } }, "node_modules/react-native-safe-area-context": { - "version": "5.3.0", - "resolved": "https://registry.npmmirror.com/react-native-safe-area-context/-/react-native-safe-area-context-5.3.0.tgz", - "integrity": "sha512-glV9bwuozTjf/JDBIBm+ITnukHNaUT3nucgdeADwjtHsfEN3RL5UO6nq99vvdWv5j/O9yCZBvFncM1BBQ+UvpQ==", + "version": "4.12.0", + "resolved": "https://registry.npmmirror.com/react-native-safe-area-context/-/react-native-safe-area-context-4.12.0.tgz", + "integrity": "sha512-ukk5PxcF4p3yu6qMZcmeiZgowhb5AsKRnil54YFUUAXVIS7PJcMHGGC+q44fCiBg44/1AJk5njGMez1m9H0BVQ==", "license": "MIT", "peerDependencies": { "react": "*", @@ -10071,9 +10231,9 @@ } }, "node_modules/react-native-screens": { - "version": "4.10.0", - "resolved": "https://registry.npmmirror.com/react-native-screens/-/react-native-screens-4.10.0.tgz", - "integrity": "sha512-Tw21NGuXm3PbiUGtZd0AnXirUixaAbPXDjNR0baBH7/WJDaDTTELLcQ7QRXuqAWbmr/EVCrKj1348ei1KFIr8A==", + "version": "4.4.0", + "resolved": "https://registry.npmmirror.com/react-native-screens/-/react-native-screens-4.4.0.tgz", + "integrity": "sha512-c7zc7Zwjty6/pGyuuvh9gK3YBYqHPOxrhXfG1lF4gHlojQSmIx2piNbNaV+Uykj+RDTmFXK0e/hA+fucw/Qozg==", "license": "MIT", "dependencies": { "react-freeze": "^1.0.0", @@ -10085,9 +10245,9 @@ } }, "node_modules/react-native-svg": { - "version": "15.11.2", - "resolved": "https://registry.npmmirror.com/react-native-svg/-/react-native-svg-15.11.2.tgz", - "integrity": "sha512-+YfF72IbWQUKzCIydlijV1fLuBsQNGMT6Da2kFlo1sh+LE3BIm/2Q7AR1zAAR6L0BFLi1WaQPLfFUC9bNZpOmw==", + "version": "15.8.0", + "resolved": "https://registry.npmmirror.com/react-native-svg/-/react-native-svg-15.8.0.tgz", + "integrity": "sha512-KHJzKpgOjwj1qeZzsBjxNdoIgv2zNCO9fVcoq2TEhTRsVV5DGTZ9JzUZwybd7q4giT/H3RdtqC3u44dWdO0Ffw==", "license": "MIT", "dependencies": { "css-select": "^5.1.0", @@ -10131,6 +10291,34 @@ "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", "license": "MIT" }, + "node_modules/react-native/node_modules/@react-native/codegen": { + "version": "0.76.9", + "resolved": "https://registry.npmmirror.com/@react-native/codegen/-/codegen-0.76.9.tgz", + "integrity": "sha512-AzlCHMTKrAVC2709V4ZGtBXmGVtWTpWm3Ruv5vXcd3/anH4mGucfJ4rjbWKdaYQJMpXa3ytGomQrsIsT/s8kgA==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.3", + "glob": "^7.1.1", + "hermes-parser": "0.23.1", + "invariant": "^2.2.4", + "jscodeshift": "^0.14.0", + "mkdirp": "^0.5.1", + "nullthrows": "^1.1.1", + "yargs": "^17.6.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@babel/preset-env": "^7.1.6" + } + }, + "node_modules/react-native/node_modules/@react-native/normalize-colors": { + "version": "0.76.9", + "resolved": "https://registry.npmmirror.com/@react-native/normalize-colors/-/normalize-colors-0.76.9.tgz", + "integrity": "sha512-TUdMG2JGk72M9d8DYbubdOlrzTYjw+YMe/xOnLU4viDgWRHsCbtRS9x0IAxRjs3amj/7zmK3Atm8jUPvdAc8qw==", + "license": "MIT" + }, "node_modules/react-native/node_modules/babel-plugin-syntax-hermes-parser": { "version": "0.23.1", "resolved": "https://registry.npmmirror.com/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.23.1.tgz", diff --git a/mobile/package.json b/mobile/package.json index fc9b185..3c2b5fc 100644 --- a/mobile/package.json +++ b/mobile/package.json @@ -10,35 +10,44 @@ }, "dependencies": { "@expo/metro-runtime": "~4.0.1", - "@react-native-async-storage/async-storage": "^2.1.2", + "@react-native-async-storage/async-storage": "1.23.1", "@react-navigation/bottom-tabs": "^7.3.3", "@react-navigation/native": "^7.0.19", "@react-navigation/native-stack": "^7.3.3", "async-storage": "^0.1.0", "axios": "^1.8.4", "expo": "~52.0.42", - "expo-dev-client": "~5.0.17", + "expo-dev-client": "~5.0.19", "expo-localization": "^16.0.1", "expo-status-bar": "~2.0.1", "i18next": "^24.2.3", "react": "^18.3.1", "react-dom": "^18.3.1", "react-i18next": "^15.4.1", - "react-native": "0.76.8", + "react-native": "0.76.9", "react-native-chart-kit": "^6.12.0", - "react-native-gesture-handler": "^2.25.0", - "react-native-reanimated": "^3.17.1", - "react-native-safe-area-context": "^5.3.0", - "react-native-screens": "^4.10.0", - "react-native-svg": "^15.11.2", + "react-native-gesture-handler": "~2.20.2", + "react-native-reanimated": "~3.16.1", + "react-native-safe-area-context": "4.12.0", + "react-native-screens": "~4.4.0", + "react-native-svg": "15.8.0", "react-native-web": "~0.19.13", "victory-native": "^41.16.1", - "zustand": "^5.0.3" + "zustand": "^5.0.3", + "expo-clipboard": "~7.0.1" }, "devDependencies": { "@babel/core": "^7.25.2", "@types/react": "~18.3.12", "typescript": "^5.3.3" }, - "private": true + "private": true, + "expo": { + "doctor": { + "reactNativeDirectoryCheck": { + "exclude": ["i18next", "react-native-chart-kit"], + "listUnknownPackages": false + } + } + } } diff --git a/mobile/src/api/agents.ts b/mobile/src/api/agents.ts index e3b7dc1..c21d7e4 100644 --- a/mobile/src/api/agents.ts +++ b/mobile/src/api/agents.ts @@ -3,32 +3,28 @@ import { API_ENDPOINTS } from '../config/api'; // 客户端类型 export interface Agent { - id: string; + id: string | number; name: string; hostname?: string; ip_address?: string; status: 'online' | 'offline' | 'unknown' | 'active'; // 添加'active'以兼容Dashboard lastSeen?: string; + last_seen?: string; os?: string; - cpu?: { - model: string; - cores: number; - usage: number; - }; - memory?: { - total: number; - used: number; - free: number; - usage: number; - }; - disk?: { - total: number; - used: number; - free: number; - usage: number; - }; + operating_system?: string; + version?: string; created_at: string; - updated_at: string; + updated_at?: string; + // 添加资源监控相关属性 + cpu_usage?: number; + memory_usage?: number; + disk_usage?: number; + memory_total?: number; + memory_used?: number; + disk_total?: number; + disk_used?: number; + network_rx?: number; + network_tx?: number; } // 创建客户端参数 diff --git a/mobile/src/assets/adaptive-icon.png b/mobile/src/assets/adaptive-icon.png index 03d6f6b6c6727954aec1d8206222769afd178d8d..3f6c6e90106ce62b96a70505963710d77a131129 100644 GIT binary patch literal 3132 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 L6Qpq++%xfi(#Q8= literal 17547 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 diff --git a/mobile/src/assets/icon.png b/mobile/src/assets/icon.png index a0b1526fc7b78680fd8d733dbc6113e1af695487..f6d01b2660c9dbd117bc1d17d4f00d307331d2a9 100644 GIT binary patch literal 10275 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

Xf&J z%zN&GnOc>zc#(rJt-;^hZzyp-2A-?Cpx`h}tNe32_?>%NVFygX6iA;zy=_*q$L1cF zs(aY@Y^xHC5M}Asc28@+NmmppmuK>GKbv<34KViDajD{mkJ;h*lne6hHBlJTEjfvw zAtP`@K(^Nbd+S4eI1%6WC2{n-5QoL$o&RW5Cpr3QXy01ri%z#P57pikwu3A>c1$Yq zq3ZQi|0SLbcJghM#w$F|WjO?fS_2An2Nd+z;(Qf(BkC7Jl0k(D< zdo0`iCZdYVsh0%SKNLuZp!VJ( z$wo@zrai_o`om18X)f4j%J0gR-@FAD&Vm1QulPMsm3fh>v^cTIJNn4)O$&qYhMj|BqyF^~?Y%e64gmGiAUg7(I_oQ9$GI~u)+%|$fSiF@&v~$JVXFM6 zi4iJMt($*$_o2OQ#kQ>BT5L{(v}<*@h>sG;5Tt{a^z13iH3d6M60JPF;K$*3?o9g) z5KOat5noRohfk?_WJ5EK^YcEVmsN>m==#1N3MMH-)EgehmHOO5>_J4W>rh*}X1sE! zlm;Rafxip|poPlPdGBv$1Qe4e?QustqLos3mquuvq<&aSxnO(eMj7O! z)CY@`#0|l-%bat!*y;T2L-J_PD6fiNysE`b5Mqghp;pHo@afrP&<~z?aW=Aej^j=j z4wfalgO?Cu0Ek=Esqg&votyjbFHyO#OjGs%ak8zxWkD#WAeIWKPN8gNTG8kIK^#QNP!G!kPLtOz)U3bq+HiO0>k|X}J zZO+(>jfzn{h*tNggUC*RW9gU(APwf+*pPLf6^vIPdVz%w6(GR0@pmf77YBUgd>0T? zmsF2gf+$lQzRT8yf_wqs%M(AjLH?rExA>&^$Vt}k!1f=3(!fV=Ze|^eVrqV9zr>~f z*JQH@aN!Mj%&>=3Zt>E7WmeB{?=70I7wldD=J<{Y{wle$1AJ8i@kg)V?Mq4OCqmo}`jLH6(=2 zf~4*HG`EAjO!_8Rtwg;K8e76TDV!IWQwhkofWKk$%jB6yninwGlETBjcJ@of-BIM4 zt1tVWuYe^p+cDQb<#Z6=T^eayPY8UX8)rrW?WeyDZEo()45+qLD|R)N=!Vk2dYR)# zi?V%j@|FR_#km;&3MXafj(#A(veWc+YOD8A#0Xd zJB#GLqdn5j=n`YR*wBANv@(+VzgqqV0P)u4+pG|$!$*$bs}tFC!Az}9-d%Qk^nU;X CnoO4f literal 22380 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 diff --git a/mobile/src/assets/splash-icon.png b/mobile/src/assets/splash-icon.png index 03d6f6b6c6727954aec1d8206222769afd178d8d..46a9174af04bc91135196217b05f316800dc5bd9 100644 GIT binary patch literal 2885 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 Wa%}B;jqnRR2ACRJ8GO+9O!*HyDzHlc literal 17547 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 => { - return new Promise((resolve) => { - setTimeout(() => { - resolve({ success: true }); - }, 2000); - }); - }, - deleteAgent: (id: string): Promise<{ success: boolean }> => { return Promise.resolve({ success: true }); - }, - - generateToken: (id: string): Promise<{ success: boolean, token: string }> => { - return Promise.resolve({ success: true, token: 'agent_' + Math.random().toString(36).substr(2, 16) }); } }; @@ -121,9 +108,6 @@ const AgentDetailScreen: React.FC = () => { const [resourceHistory, setResourceHistory] = useState([]); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); - const [restarting, setRestarting] = useState(false); - const [showToken, setShowToken] = useState(false); - const [token, setToken] = useState(null); // 加载客户端详情和资源历史 const fetchData = async () => { @@ -148,57 +132,6 @@ const AgentDetailScreen: React.FC = () => { setRefreshing(false); }; - // 重启客户端 - const handleRestart = () => { - Alert.alert( - t('agents.restartTitle', '重启客户端'), - t('agents.restartConfirmation', '您确定要重启此客户端吗?这可能会导致短暂的服务中断。'), - [ - { - text: t('common.cancel', '取消'), - style: 'cancel' - }, - { - text: t('agents.restart', '重启'), - style: 'destructive', - onPress: async () => { - setRestarting(true); - try { - const result = await agentService.restartAgent(agentId); - - if (result.success) { - Alert.alert( - t('agents.restartSuccessTitle', '重启命令已发送'), - t('agents.restartSuccessMessage', '重启命令已成功发送到客户端。客户端将在几分钟内重启。') - ); - } - } catch (error) { - console.error('重启客户端失败', error); - Alert.alert(t('common.error', '错误'), t('agents.restartFailed', '重启客户端失败')); - } finally { - setRestarting(false); - } - } - } - ] - ); - }; - - // 生成新令牌 - const handleGenerateToken = async () => { - try { - const result = await agentService.generateToken(agentId); - - if (result.success) { - setToken(result.token); - setShowToken(true); - } - } catch (error) { - console.error('生成令牌失败', error); - Alert.alert(t('common.error', '错误'), t('agents.generateTokenFailed', '生成新令牌失败')); - } - }; - // 删除客户端 const handleDelete = () => { Alert.alert( @@ -229,11 +162,6 @@ const AgentDetailScreen: React.FC = () => { ); }; - // 编辑客户端 - const handleEdit = () => { - navigation.navigate('EditAgent', { agentId }); - }; - // 获取状态颜色 const getStatusColor = (status: string) => { return status === 'active' ? '#30c85e' : '#888'; @@ -470,58 +398,8 @@ const AgentDetailScreen: React.FC = () => { )} - {/* 令牌信息 */} - {showToken && token && ( - - {t('agents.token', '客户端令牌')} - - {token} - - {t('agents.tokenHelper', '请保管好此令牌,它只会显示一次。您可以使用此令牌重新连接客户端。')} - - - - )} - {/* 操作按钮 */} - - - - {t('common.edit', '编辑')} - - - - - - - {t('agents.generateToken', '生成新令牌')} - - - - - {restarting ? ( - - ) : ( - <> - - - {t('agents.restart', '重启客户端')} - - - )} - - { // 实际项目中这里需要调用API删除 console.log('删除客户端:', id); const numericId = parseInt(id, 10); - const updatedAgents = agents.filter(a => a.id !== numericId); + const updatedAgents = agents.filter(a => String(a.id) !== id); setAgents(updatedAgents); applyFilter(updatedAgents, filter); } @@ -244,13 +244,6 @@ const AgentsScreen: React.FC = () => { - navigation.navigate('EditAgent', { agentId: item.id })} - > - - {t('common.edit', '编辑')} - { const { t } = useTranslation(); const navigation = useNavigation(); - const [name, setName] = useState(''); - const [hostname, setHostname] = useState(''); - const [ipAddress, setIpAddress] = useState(''); - const [isLoading, setIsLoading] = useState(false); + // 状态 + const [isLoading, setIsLoading] = useState(true); + const [token, setToken] = useState(''); + const [serverUrl, setServerUrl] = useState(API_BASE_URL); + const [selectedPlatform, setSelectedPlatform] = useState('linux'); + const [selectedArch, setSelectedArch] = useState('amd64'); + const [copied, setCopied] = useState(false); - const handleSubmit = async () => { - if (!name.trim()) { - Alert.alert( - t('agents.createError', '创建错误'), - t('agents.nameRequired', '请输入客户端名称') - ); - return; - } - + // 页面加载时自动获取注册token + useEffect(() => { + generateToken(); + }, []); + + // 生成注册token + const generateToken = async () => { setIsLoading(true); - - // 模拟创建客户端的 API 调用 - setTimeout(() => { + try { + const tokenResult = await agentService.generateRegistrationToken(); + + if (!tokenResult) { + setIsLoading(false); + Alert.alert( + t('agents.tokenError', 'Token生成错误'), + t('agents.tokenFailed', '无法生成客户端注册Token') + ); + return; + } + + setToken(tokenResult); + setIsLoading(false); + } catch (error) { + console.error('生成Token失败', error); setIsLoading(false); - - // 生成随机令牌 - const token = Math.random().toString(36).substring(2, 15) + - Math.random().toString(36).substring(2, 15); - Alert.alert( - t('agents.createSuccess', '创建成功'), - t('agents.tokenGenerated', '客户端已创建,请复制以下令牌: ') + token, - [ - { - text: 'OK', - onPress: () => navigation.goBack() - } - ] + t('common.error', '错误'), + t('agents.tokenFailed', '无法生成客户端注册Token') ); - }, 1000); + } }; + // 复制命令到剪贴板 + const copyToClipboard = async (text: string) => { + try { + await Clipboard.setStringAsync(text); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + } catch (error) { + console.error('复制到剪贴板失败', error); + } + }; + + // 获取下载URL + const getDownloadUrl = () => { + if (!selectedPlatform || !selectedArch) return ''; + const extension = selectedPlatform === 'windows' ? '.exe' : ''; + return `https://dl.xugou.mdzz.uk/latest/xugou-agent-${selectedPlatform}-${selectedArch}${extension}`; + }; + + // 获取下载命令 + const getDownloadCommand = () => { + if (!selectedPlatform || !selectedArch || selectedPlatform === 'windows') return ''; + return `curl -sSL ${getDownloadUrl()} -o xugou-agent && chmod +x xugou-agent`; + }; + + // 获取运行命令 + const getRunCommand = () => { + return `./xugou-agent start --server ${serverUrl} --token ${token} --interval 60`; + }; + + // 加载中状态 + if (isLoading) { + return ( + + + {t('agents.generatingToken', '正在生成注册Token...')} + + ); + } + + // 显示安装指南 return ( - + - {t('agents.name', '名称')} * - - - {t('agents.hostname', '主机名')} - - - {t('agents.ipAddress', 'IP地址')} - - - - {t('agents.instructions', '安装说明')} - - {t('agents.installInstructions', '1. 创建客户端并获取令牌')} - - - {t('agents.installInstructions2', '2. 在您的服务器上运行安装脚本')} - - - {t('agents.installInstructions3', '3. 使用令牌完成客户端配置')} - - - {t('agents.installInstructions4', '4. 客户端将自动连接到监控服务')} + + {t('agents.addNewAgent', '添加新客户端')} + + {t('agents.followGuide', '请按照以下步骤安装客户端')} - - {isLoading ? ( - - ) : ( - - {t('agents.create', '创建客户端')} + {/* 服务器地址部分 */} + + {t('agents.serverAddress', '服务器地址')} + + {serverUrl} + + copyToClipboard(serverUrl)} + > + + {copied ? t('common.copied', '已复制') : t('common.copy', '复制')} - )} - + + + + {/* 客户端Token部分 */} + + {t('agents.registrationToken', '注册令牌')} + + {token} + + copyToClipboard(token)} + > + + {copied ? t('common.copied', '已复制') : t('common.copy', '复制')} + + + + + {/* 选择平台部分 */} + + {t('agents.installGuide', '安装指南')} + + {t('agents.selectPlatform', '1. 选择操作系统')} + + setSelectedPlatform('linux')} + > + Linux + + + setSelectedPlatform('darwin')} + > + macOS + + + setSelectedPlatform('windows')} + > + Windows + + + + {/* 选择架构部分 */} + {t('agents.selectArch', '2. 选择系统架构')} + + setSelectedArch('amd64')} + > + + {selectedPlatform === 'darwin' ? 'AMD64 (Intel)' : 'AMD64 (x86_64)'} + + + + setSelectedArch('arm64')} + > + + {selectedPlatform === 'darwin' ? 'ARM64 (Apple Silicon)' : 'ARM64'} + + + + + + {/* 下载命令部分 */} + {selectedPlatform === 'windows' ? ( + + {t('agents.downloadInstaller', '3. 下载安装文件')} + Linking.openURL(getDownloadUrl())} + > + + {t('agents.downloadWindows', '下载 Windows 安装程序')} + + + + ) : ( + + {t('agents.downloadRun', '3. 下载并安装')} + + {getDownloadCommand()} + + copyToClipboard(getDownloadCommand())} + > + + {copied ? t('common.copied', '已复制') : t('common.copy', '复制')} + + + + )} + + {/* 运行客户端部分 */} + + {t('agents.runAgent', '4. 运行客户端')} + + {getRunCommand()} + + copyToClipboard(getRunCommand())} + > + + {copied ? t('common.copied', '已复制') : t('common.copy', '复制')} + + + + + {/* 返回按钮 */} + + navigation.goBack()} + > + {t('agents.returnToList', '返回客户端列表')} + + + + {t('agents.generateNewToken', '生成新Token')} + + ); @@ -120,6 +267,20 @@ const styles = StyleSheet.create({ flex: 1, backgroundColor: '#f5f5f5', }, + contentContainer: { + paddingBottom: 100, + }, + loadingContainer: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + backgroundColor: '#f5f5f5', + }, + loadingText: { + marginTop: 16, + fontSize: 16, + color: '#333', + }, formContainer: { padding: 16, }, @@ -129,44 +290,111 @@ const styles = StyleSheet.create({ marginBottom: 8, marginTop: 16, }, - input: { - backgroundColor: '#fff', - borderRadius: 8, - borderWidth: 1, - borderColor: '#ddd', - paddingHorizontal: 12, - paddingVertical: 10, - fontSize: 16, + section: { + marginBottom: 20, }, - infoContainer: { - backgroundColor: '#e6f3ff', - borderRadius: 8, - padding: 16, - marginTop: 24, - marginBottom: 8, - }, - infoTitle: { - fontSize: 16, + sectionTitle: { + fontSize: 20, fontWeight: '600', marginBottom: 8, color: '#0066cc', }, - infoText: { - fontSize: 14, - color: '#333', - marginBottom: 6, + description: { + fontSize: 16, + color: '#555', + marginBottom: 16, }, - submitButton: { + codeBlock: { + backgroundColor: '#f0f0f0', + borderRadius: 8, + padding: 12, + borderWidth: 1, + borderColor: '#ddd', + }, + codeText: { + fontFamily: 'monospace', + fontSize: 14, + }, + copyButton: { + backgroundColor: '#e6e6e6', + borderRadius: 8, + padding: 8, + alignItems: 'center', + marginTop: 8, + alignSelf: 'flex-end', + }, + copyButtonText: { + color: '#333', + fontSize: 14, + fontWeight: '500', + }, + optionsRow: { + flexDirection: 'row', + justifyContent: 'space-between', + marginBottom: 16, + }, + optionButton: { + flex: 1, + backgroundColor: '#e6e6e6', + borderRadius: 8, + padding: 10, + alignItems: 'center', + marginHorizontal: 4, + }, + selectedOption: { + backgroundColor: '#0066cc', + }, + optionText: { + color: '#333', + fontSize: 14, + fontWeight: '500', + }, + selectedOptionText: { + color: '#fff', + }, + downloadButton: { backgroundColor: '#0066cc', borderRadius: 8, - padding: 16, + padding: 12, alignItems: 'center', - marginTop: 24, + marginTop: 8, }, - submitButtonText: { + downloadButtonText: { color: '#fff', fontSize: 16, - fontWeight: '600', + fontWeight: '500', + }, + buttonRow: { + flexDirection: 'row', + justifyContent: 'space-between', + marginTop: 30, + marginBottom: 32, + }, + secondaryButton: { + backgroundColor: '#e6e6e6', + borderRadius: 8, + padding: 12, + alignItems: 'center', + flex: 1, + marginRight: 8, + }, + secondaryButtonText: { + color: '#333', + fontSize: 14, + fontWeight: '500', + }, + resetButton: { + backgroundColor: '#0066cc', + borderRadius: 8, + padding: 12, + alignItems: 'center', + flex: 1, + marginLeft: 8, + }, + resetButtonText: { + color: '#fff', + fontSize: 14, + fontWeight: '500', }, }); diff --git a/mobile/src/screens/monitors/CreateMonitorScreen.tsx b/mobile/src/screens/monitors/CreateMonitorScreen.tsx index 6ae3782..088cc54 100644 --- a/mobile/src/screens/monitors/CreateMonitorScreen.tsx +++ b/mobile/src/screens/monitors/CreateMonitorScreen.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { View, Text, @@ -7,20 +7,144 @@ import { TouchableOpacity, ScrollView, ActivityIndicator, - Alert + Alert, + Modal, + FlatList } from 'react-native'; import { useTranslation } from 'react-i18next'; -import { useNavigation } from '@react-navigation/native'; +import { useNavigation, CommonActions } from '@react-navigation/native'; +import { Ionicons } from '@expo/vector-icons'; +import monitorService from '../../api/monitors'; + +// 请求头键值对类型 +interface Header { + key: string; + value: string; +} + +// 状态码选项类型 +interface StatusCodeOption { + label: string; + value: string; +} const CreateMonitorScreen: React.FC = () => { const { t } = useTranslation(); - const navigation = useNavigation(); + const navigation = useNavigation(); const [name, setName] = useState(''); const [url, setUrl] = useState(''); + const [method, setMethod] = useState<'GET' | 'POST' | 'PUT' | 'DELETE' | 'HEAD'>('GET'); const [interval, setInterval] = useState('60'); - const [monitorType, setMonitorType] = useState('http'); + const [timeoutValue, setTimeoutValue] = useState('30'); + const [expectedStatus, setExpectedStatus] = useState('200'); + const [headers, setHeaders] = useState([{ key: '', value: '' }]); + const [body, setBody] = useState(''); const [isLoading, setIsLoading] = useState(false); + const [shouldNavigate, setShouldNavigate] = useState(false); + const [statusModalVisible, setStatusModalVisible] = useState(false); + + // HTTP方法选项 + const httpMethods = ['GET', 'POST', 'PUT', 'DELETE', 'HEAD'] as const; + + // 状态码选项,包括范围选择 + const statusCodeOptions: StatusCodeOption[] = [ + { label: `200 - ${t('monitors.statusCodes.ok', 'OK')}`, value: '200' }, + { label: `201 - ${t('monitors.statusCodes.created', 'Created')}`, value: '201' }, + { label: `204 - ${t('monitors.statusCodes.noContent', 'No Content')}`, value: '204' }, + { label: `2xx - ${t('monitors.statusCodes.successful', 'Successful')}`, value: '2xx' }, + { label: `301 - ${t('monitors.statusCodes.movedPermanently', 'Moved Permanently')}`, value: '301' }, + { label: `302 - ${t('monitors.statusCodes.found', 'Found')}`, value: '302' }, + { label: `3xx - ${t('monitors.statusCodes.redirection', 'Redirection')}`, value: '3xx' }, + { label: `400 - ${t('monitors.statusCodes.badRequest', 'Bad Request')}`, value: '400' }, + { label: `401 - ${t('monitors.statusCodes.unauthorized', 'Unauthorized')}`, value: '401' }, + { label: `403 - ${t('monitors.statusCodes.forbidden', 'Forbidden')}`, value: '403' }, + { label: `404 - ${t('monitors.statusCodes.notFound', 'Not Found')}`, value: '404' }, + { label: `4xx - ${t('monitors.statusCodes.clientError', 'Client Error')}`, value: '4xx' }, + { label: `500 - ${t('monitors.statusCodes.serverError', 'Internal Server Error')}`, value: '500' }, + { label: `502 - ${t('monitors.statusCodes.badGateway', 'Bad Gateway')}`, value: '502' }, + { label: `503 - ${t('monitors.statusCodes.serviceUnavailable', 'Service Unavailable')}`, value: '503' }, + { label: `504 - ${t('monitors.statusCodes.gatewayTimeout', 'Gateway Timeout')}`, value: '504' }, + { label: `5xx - ${t('monitors.statusCodes.serverErrors', 'Server Error')}`, value: '5xx' }, + ]; + + // 将请求头键值对转换为JSON对象 + const headersToJson = () => { + const result: Record = {}; + + headers.forEach(({ key, value }) => { + if (key.trim()) { + result[key.trim()] = value; + } + }); + + return result; + }; + + // 添加新的请求头行 + const addHeader = () => { + setHeaders([...headers, { key: '', value: '' }]); + }; + + // 删除请求头行 + const removeHeader = (index: number) => { + if (headers.length > 1) { + const newHeaders = [...headers]; + newHeaders.splice(index, 1); + setHeaders(newHeaders); + } + }; + + // 更新请求头键值对 + const updateHeader = (index: number, field: keyof Header, value: string) => { + const newHeaders = [...headers]; + newHeaders[index][field] = value; + + // 如果最后一行有输入内容,自动添加新行 + if (index === headers.length - 1 && (newHeaders[index].key || newHeaders[index].value)) { + newHeaders.push({ key: '', value: '' }); + } + + setHeaders(newHeaders); + }; + + // 获取当前选择的状态码标签 + const getSelectedStatusCodeLabel = () => { + const selected = statusCodeOptions.find(option => option.value === expectedStatus); + return selected ? selected.label : expectedStatus; + }; + + // 处理状态码,将范围状态码(如2xx)转换为前缀数字(如2)以便与后端兼容 + const processStatusCode = (statusCode: string) => { + // 检查是否为范围状态码格式(如2xx、3xx) + if (/^\d+xx$/.test(statusCode)) { + // 仅返回第一个数字 + return statusCode.charAt(0); + } + return statusCode; + }; + + // 使用 useEffect 处理导航 + useEffect(() => { + if (shouldNavigate) { + console.log('尝试使用多种方式导航到监控列表页面'); + + // 方式1: 基本导航 + navigation.navigate('Monitors'); + + // 方式2: 使用CommonActions重置导航 + const timerId = setTimeout(() => { + navigation.dispatch( + CommonActions.reset({ + index: 0, + routes: [{ name: 'Monitors' }], + }) + ); + }, 100); + + return () => clearTimeout(timerId); + } + }, [shouldNavigate, navigation]); const handleSubmit = async () => { if (!name.trim() || !url.trim()) { @@ -33,24 +157,53 @@ const CreateMonitorScreen: React.FC = () => { setIsLoading(true); - // 模拟创建监控的 API 调用 - setTimeout(() => { + try { + // 调用真实的API服务创建监控 + const result = await monitorService.createMonitor({ + name, + url, + method, + interval: Number(interval), + timeout: Number(timeoutValue), + expectedStatus: processStatusCode(expectedStatus), + headers: headersToJson(), + body: ['POST', 'PUT', 'PATCH'].includes(method) ? body : undefined, + type: 'http' + }); + setIsLoading(false); + + if (result.success) { + console.log('创建成功,准备跳转到监控列表页面'); + // 通过状态触发导航 + setShouldNavigate(true); + } else { + console.error('创建失败:', result.message); + // 在Web中Alert可能不显示,使用console.error记录错误 + Alert.alert( + t('monitors.createError', '创建错误'), + result.message || t('monitors.createFailed', '创建监控失败') + ); + } + } catch (error) { + setIsLoading(false); + console.error('创建监控失败', error); + // 在Web中Alert可能不显示,使用console.error记录错误 Alert.alert( - t('monitors.createSuccess', '创建成功'), - t('monitors.monitorCreated', '监控已成功创建'), - [ - { - text: 'OK', - onPress: () => navigation.goBack() - } - ] + t('monitors.createError', '创建错误'), + t('monitors.createFailed', '创建监控失败') ); - }, 1000); + } }; + // 判断是否显示请求体输入框 + const showBodyField = ['POST', 'PUT', 'PATCH'].includes(method); + return ( - + {t('monitors.name', '名称')} * { placeholder={t('monitors.enterUrl', '输入要监控的 URL 或主机')} /> - {t('monitors.type', '监控类型')} - - setMonitorType('http')} - > - - HTTP - - - - setMonitorType('tcp')} - > - - TCP - - - - setMonitorType('ping')} - > - - PING - - + {t('monitors.method', '请求方法')} * + + {httpMethods.map((m) => ( + setMethod(m)} + > + + {m} + + + ))} {t('monitors.interval', '检查间隔 (秒)')} @@ -124,6 +252,68 @@ const CreateMonitorScreen: React.FC = () => { keyboardType="numeric" /> + {t('monitors.timeout', '超时 (秒)')} + + + {t('monitors.expectedStatus', '预期状态码')} * + setStatusModalVisible(true)} + > + + {getSelectedStatusCodeLabel()} + + + + + {t('monitors.headers', '请求头')} + + {headers.map((header, index) => ( + + updateHeader(index, 'key', value)} + placeholder={t('monitors.headerName', '名称')} + /> + updateHeader(index, 'value', value)} + placeholder={t('monitors.headerValue', '值')} + /> + removeHeader(index)} + > + + + + ))} + + + {t('monitors.addHeader', '添加请求头')} + + + + {showBodyField && ( + <> + {t('monitors.body', '请求体')} + + + )} + { )} + + {/* 状态码选择模态框 */} + setStatusModalVisible(false)} + > + + + + {t('monitors.selectStatusCode', '选择状态码')} + setStatusModalVisible(false)} + > + + + + + item.value} + renderItem={({ item }) => ( + { + setExpectedStatus(item.value); + setStatusModalVisible(false); + }} + > + + {item.label} + + {expectedStatus === item.value && ( + + )} + + )} + style={styles.modalList} + /> + + + ); }; @@ -147,6 +389,9 @@ const styles = StyleSheet.create({ flex: 1, backgroundColor: '#f5f5f5', }, + contentContainer: { + paddingBottom: 100, // 添加底部间距,避免被导航栏遮挡 + }, formContainer: { padding: 16, }, @@ -165,40 +410,139 @@ const styles = StyleSheet.create({ paddingVertical: 10, fontSize: 16, }, - typeContainer: { + methodContainer: { flexDirection: 'row', + flexWrap: 'wrap', marginTop: 8, }, - typeButton: { - flex: 1, - padding: 10, + methodButton: { + paddingVertical: 8, + paddingHorizontal: 16, backgroundColor: '#f0f0f0', borderRadius: 8, marginRight: 8, - alignItems: 'center', + marginBottom: 8, }, - typeButtonActive: { + methodButtonActive: { backgroundColor: '#0066cc', }, - typeButtonText: { + methodButtonText: { fontWeight: '500', color: '#333', }, - typeButtonTextActive: { + methodButtonTextActive: { color: '#fff', }, + dropdown: { + backgroundColor: '#fff', + borderRadius: 8, + borderWidth: 1, + borderColor: '#ddd', + paddingHorizontal: 12, + paddingVertical: 12, + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + }, + dropdownText: { + fontSize: 16, + }, + headerContainer: { + marginTop: 8, + }, + headerRow: { + flexDirection: 'row', + alignItems: 'center', + marginBottom: 8, + }, + headerKeyInput: { + flex: 2, + marginRight: 8, + }, + headerValueInput: { + flex: 3, + marginRight: 8, + }, + headerRemoveButton: { + padding: 8, + }, + addHeaderButton: { + flexDirection: 'row', + alignItems: 'center', + paddingVertical: 8, + }, + addHeaderText: { + fontSize: 14, + color: '#0066cc', + marginLeft: 4, + }, + bodyInput: { + minHeight: 100, + textAlignVertical: 'top', + }, submitButton: { backgroundColor: '#0066cc', borderRadius: 8, padding: 16, alignItems: 'center', marginTop: 32, + marginBottom: 32, // 增加底部边距 }, submitButtonText: { color: '#fff', fontSize: 16, fontWeight: '600', }, + // 模态框样式 + modalOverlay: { + flex: 1, + backgroundColor: 'rgba(0, 0, 0, 0.5)', + justifyContent: 'flex-end', + }, + modalContent: { + backgroundColor: 'white', + borderTopLeftRadius: 16, + borderTopRightRadius: 16, + maxHeight: '70%', + }, + modalHeader: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + padding: 16, + borderBottomWidth: 1, + borderBottomColor: '#eee', + }, + modalTitle: { + fontSize: 18, + fontWeight: '600', + }, + modalCloseButton: { + padding: 4, + }, + modalList: { + maxHeight: 300, + }, + statusOption: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingVertical: 12, + paddingHorizontal: 16, + borderBottomWidth: 1, + borderBottomColor: '#f0f0f0', + }, + statusOptionSelected: { + backgroundColor: '#f0f7ff', + }, + statusOptionText: { + fontSize: 16, + color: '#333', + }, + statusOptionTextSelected: { + color: '#0066cc', + fontWeight: '500', + }, }); export default CreateMonitorScreen; \ No newline at end of file diff --git a/mobile/src/screens/monitors/MonitorDetailScreen.tsx b/mobile/src/screens/monitors/MonitorDetailScreen.tsx index d58a99c..412bf26 100644 --- a/mobile/src/screens/monitors/MonitorDetailScreen.tsx +++ b/mobile/src/screens/monitors/MonitorDetailScreen.tsx @@ -14,6 +14,7 @@ import { useTranslation } from 'react-i18next'; import { useNavigation, useRoute } from '@react-navigation/native'; import { RouteProp } from '@react-navigation/native'; import { Ionicons } from '@expo/vector-icons'; +import monitorService from '../../api/monitors'; // 移除 react-native-chart-kit 依赖,使用自定义简单图表 // 简单的自定义图表组件 @@ -85,6 +86,7 @@ interface Monitor { uptime: number; response_time: number; last_check?: string; + last_checked?: string; created_at: string; } @@ -96,64 +98,6 @@ interface MonitorStatusHistory { response_time?: number; } -// 模拟服务 - 实际项目中应该使用真实的API服务 -const monitorService = { - getMonitor: (id: string): Promise => { - return Promise.resolve({ - id, - name: 'API服务', - url: 'https://api.example.com/health', - type: 'http', - method: 'GET', - interval: 60, - timeout: 30, - status: Math.random() > 0.2 ? 'up' : 'down', - active: true, - uptime: 99.8, - response_time: Math.floor(Math.random() * 300) + 50, - last_check: new Date().toISOString(), - created_at: '2023-10-01T00:00:00Z' - }); - }, - - getMonitorHistory: (id: string): Promise => { - const history: MonitorStatusHistory[] = []; - const now = new Date(); - - // 生成过去24小时的历史数据 - for (let i = 0; i < 24; i++) { - const timestamp = new Date(now.getTime() - i * 60 * 60 * 1000).toISOString(); - const status = Math.random() > 0.1 ? 'up' : 'down'; - const response_time = status === 'up' ? Math.floor(Math.random() * 300) + 50 : undefined; - - history.push({ - id: `hist-${id}-${i}`, - status, - timestamp, - response_time - }); - } - - return Promise.resolve(history.reverse()); - }, - - checkNow: (id: string): Promise<{ success: boolean }> => { - return new Promise((resolve) => { - setTimeout(() => { - resolve({ success: true }); - }, 2000); - }); - }, - - toggleActive: (id: string, active: boolean): Promise<{ success: boolean }> => { - return Promise.resolve({ success: true }); - }, - - deleteMonitor: (id: string): Promise<{ success: boolean }> => { - return Promise.resolve({ success: true }); - } -}; - // 路由参数类型 type MonitorDetailRouteProp = RouteProp<{ MonitorDetail: { monitorId: string } }, 'MonitorDetail'>; @@ -174,11 +118,25 @@ const MonitorDetailScreen: React.FC = () => { // 加载监控详情和历史 const fetchData = async () => { try { - const monitorData = await monitorService.getMonitor(monitorId); - const historyData = await monitorService.getMonitorHistory(monitorId); + // 获取监控详情 + const monitorResult = await monitorService.getMonitorById(monitorId); + if (!monitorResult.success || !monitorResult.monitor) { + throw new Error(monitorResult.message || '获取监控详情失败'); + } - setMonitor(monitorData); - setHistory(historyData); + // 获取监控历史 + const historyData = await monitorService.getMonitorHistory(Number(monitorId)); + + // 转换历史数据格式以兼容界面显示 + const formattedHistory: MonitorStatusHistory[] = historyData.map((item: any) => ({ + id: String(item.id || `hist-${monitorId}-${Math.random()}`), + status: item.status, + timestamp: item.timestamp, + response_time: item.response_time + })); + + setMonitor(monitorResult.monitor); + setHistory(formattedHistory); } catch (error) { console.error('获取监控详情失败', error); Alert.alert(t('common.error', '错误'), t('monitors.fetchDetailFailed', '获取监控详情失败')); @@ -198,7 +156,7 @@ const MonitorDetailScreen: React.FC = () => { const handleCheckNow = async () => { setCheckingNow(true); try { - await monitorService.checkNow(monitorId); + await monitorService.checkMonitor(Number(monitorId)); await fetchData(); Alert.alert(t('monitors.checkNowSuccess', '检查成功'), t('monitors.checkNowSuccessMessage', '已成功发起检查,请稍后刷新查看结果')); } catch (error) { @@ -214,31 +172,6 @@ const MonitorDetailScreen: React.FC = () => { navigation.navigate('EditMonitor', { monitorId }); }; - // 切换激活状态 - const handleToggleActive = async () => { - if (!monitor) return; - - try { - await monitorService.toggleActive(monitorId, !monitor.active); - - // 更新本地状态 - setMonitor({ - ...monitor, - active: !monitor.active - }); - - Alert.alert( - t('common.success', '成功'), - monitor.active - ? t('monitors.pauseSuccess', '已暂停监控') - : t('monitors.activateSuccess', '已激活监控') - ); - } catch (error) { - console.error('切换监控状态失败', error); - Alert.alert(t('common.error', '错误'), t('monitors.toggleActiveFailed', '切换监控状态失败')); - } - }; - // 删除监控 const handleDelete = () => { Alert.alert( @@ -254,8 +187,12 @@ const MonitorDetailScreen: React.FC = () => { style: 'destructive', onPress: async () => { try { - await monitorService.deleteMonitor(monitorId); - navigation.goBack(); + const result = await monitorService.deleteMonitor(monitorId); + if (result.success) { + navigation.goBack(); + } else { + Alert.alert(t('common.error', '错误'), result.message || t('monitors.deleteFailed', '删除监控失败')); + } } catch (error) { console.error('删除监控失败', error); Alert.alert(t('common.error', '错误'), t('monitors.deleteFailed', '删除监控失败')); @@ -371,7 +308,7 @@ const MonitorDetailScreen: React.FC = () => { {t('monitors.type', '类型')}: - {monitor.type.toUpperCase()} + {monitor.type?.toUpperCase() || 'HTTP'} {t('monitors.method', '方法')}: @@ -397,7 +334,7 @@ const MonitorDetailScreen: React.FC = () => { {t('monitors.status.lastCheck', '最后检查')} - {formatDate(monitor.last_check)} + {formatDate(monitor.last_check || monitor.last_checked)} {t('monitors.status.uptime', '在线率')} @@ -491,32 +428,6 @@ const MonitorDetailScreen: React.FC = () => { )} - - - - {t('common.edit', '编辑')} - - - - - - - {monitor.active - ? t('monitors.actions.pause', '暂停监控') - : t('monitors.actions.activate', '激活监控')} - - - { case 'down': result = data.filter(m => m.status === 'down'); break; - case 'active': - result = data.filter(m => m.active); - break; - case 'inactive': - result = data.filter(m => !m.active); - break; default: break; } @@ -151,12 +145,6 @@ const MonitorsScreen: React.FC = () => { - - {!item.active && ( - - {t('monitors.inactive', '已禁用')} - - )} ); @@ -202,24 +190,6 @@ const MonitorsScreen: React.FC = () => { {t('monitors.status_down', '故障')} - - handleFilterChange('active')} - > - - {t('monitors.filters.active', '已激活')} - - - - handleFilterChange('inactive')} - > - - {t('monitors.filters.inactive', '已暂停')} - - { } // 如果所有监控器都正常,但有不活跃的客户端,则系统状态为部分故障 - const hasInactiveAgent = agents.some(a => a.status === 'inactive'); + const hasInactiveAgent = agents.some(a => a.status === 'offline' || a.status === 'unknown'); if (hasInactiveAgent) { return 'partial'; @@ -233,7 +233,7 @@ const StatusScreen: React.FC = () => { - {monitor.uptime.toFixed(1)}% {t('status.uptime', '在线率')} + {(monitor.uptime ?? 0).toFixed(1)}% {t('status.uptime', '在线率')} ))}