diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 73d5ea9..d32b39c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -52,33 +52,44 @@ jobs: draft: false prerelease: false - - name: 构建v0版本 - 完整引用版本 + - name: 构建自动版 - 完整引用版本 run: npm run build - - name: 打包v0-${{ steps.determine_version.outputs.version }}-all.zip - run: zip -r v0-${{ steps.determine_version.outputs.version }}-all.zip dist + - name: 打包v${{ steps.determine_version.outputs.version }}-all.zip + run: zip -r v${{ steps.determine_version.outputs.version }}-all.zip dist - - name: 构建v0版本 - JSDeliver引用版本 + - name: 构建自动版 - JSDeliver引用版本 env: VITE_SARASA_TERM_SC_USE_CDN: '1' VITE_USE_CDN: '1' VITE_CDN_LIB_TYPE: 'jsdelivr' run: npm run build - - name: 打包v0-${{ steps.determine_version.outputs.version }}-cdn-jsdelivr.zip - run: zip -r v0-${{ steps.determine_version.outputs.version }}-cdn-jsdelivr.zip dist + - name: 打包v${{ steps.determine_version.outputs.version }}-cdn-jsdelivr.zip + run: zip -r v${{ steps.determine_version.outputs.version }}-cdn-jsdelivr.zip dist - - name: 构建v0版本 - loli(CDNJS)引用版本 + - name: 构建自动版 - loli(CDNJS)引用版本 env: VITE_SARASA_TERM_SC_USE_CDN: '1' VITE_USE_CDN: '1' VITE_CDN_LIB_TYPE: 'loli' run: npm run build - - name: 打包v0-${{ steps.determine_version.outputs.version }}-cdn-loli.zip - run: zip -r v0-${{ steps.determine_version.outputs.version }}-cdn-loli.zip dist + - name: 打包v${{ steps.determine_version.outputs.version }}-cdn-loli.zip + run: zip -r v${{ steps.determine_version.outputs.version }}-cdn-loli.zip dist - - name: 构建哪吒v1内置版本 + - name: 构建哪吒v0版本 + env: + VITE_NEZHA_VERSION: 'v0' + VITE_SARASA_TERM_SC_USE_CDN: '1' + VITE_USE_CDN: '1' + VITE_CDN_LIB_TYPE: 'jsdelivr' + run: npm run build + + - name: 打包v0-dist.zip + run: zip -r v0-dist.zip dist + + - name: 构建哪吒v1版本 env: VITE_NEZHA_VERSION: 'v1' VITE_SARASA_TERM_SC_USE_CDN: '1' @@ -89,34 +100,44 @@ jobs: - name: 打包dist.zip run: zip -r dist.zip dist - - name: Upload v0-${{ steps.determine_version.outputs.version }}-all.zip + - name: Upload v${{ steps.determine_version.outputs.version }}-all.zip uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./v0-${{ steps.determine_version.outputs.version }}-all.zip - asset_name: v0-${{ steps.determine_version.outputs.version }}-all.zip + asset_path: ./v${{ steps.determine_version.outputs.version }}-all.zip + asset_name: v${{ steps.determine_version.outputs.version }}-all.zip asset_content_type: application/zip - - name: Upload v0-${{ steps.determine_version.outputs.version }}-cdn-jsdelivr.zip + - name: Upload v${{ steps.determine_version.outputs.version }}-cdn-jsdelivr.zip uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./v0-${{ steps.determine_version.outputs.version }}-cdn-jsdelivr.zip - asset_name: v0-${{ steps.determine_version.outputs.version }}-cdn-jsdelivr.zip + asset_path: ./v${{ steps.determine_version.outputs.version }}-cdn-jsdelivr.zip + asset_name: v${{ steps.determine_version.outputs.version }}-cdn-jsdelivr.zip asset_content_type: application/zip - - name: Upload v0-${{ steps.determine_version.outputs.version }}-cdn-loli.zip + - name: Upload v${{ steps.determine_version.outputs.version }}-cdn-loli.zip uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./v0-${{ steps.determine_version.outputs.version }}-cdn-loli.zip - asset_name: v0-${{ steps.determine_version.outputs.version }}-cdn-loli.zip + asset_path: ./v${{ steps.determine_version.outputs.version }}-cdn-loli.zip + asset_name: v${{ steps.determine_version.outputs.version }}-cdn-loli.zip + asset_content_type: application/zip + + - name: Upload v0-dist.zip + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./v0-dist.zip + asset_name: v0-dist.zip asset_content_type: application/zip - name: Upload dist.zip @@ -131,14 +152,17 @@ jobs: - name: Add release notes run: | + # 获取最近一次提交的变更内容 + git log $(git describe --tags --abbrev=0)..HEAD --pretty=format:"%s%n%b" > change.txt + # 获取现有的发布说明 gh release view v${{ steps.determine_version.outputs.version }} --json body -q .body > body.txt - # 获取最近一次提交的变更内容 - git log $(git describe --tags --abbrev=0)..HEAD --pretty=format:"%s%n%b" >> body.txt + # 添加最近一次提交的变更内容 + echo -e "\n## 变更内容\n$(cat change.txt)" >> body.txt # 添加其他发布说明 - echo -e "\n哪吒V1请下载dist.zip\n哪吒V0请下载v0-${{ steps.determine_version.outputs.version }}-cdn-jsdelivr.zip\n -all是全量包,-cdn-jsdelivr是jsdelivr引用版,-cdn-loli是cdnjs的loli.net引用版\nv0版本构建物通过修改./config.js指定nezhaVersion的版本号为'v1',v1也可以正常使用" >> body.txt + echo -e "\n哪吒V1请下载dist.zip\n哪吒V0请下载v0-dist.zip\nv${{ steps.determine_version.outputs.version }}-all.zip是全量包\nv${{ steps.determine_version.outputs.version }}-cdn-jsdelivr.zip是jsdelivr引用版\nv${{ steps.determine_version.outputs.version }}-cdn-loli.zip是cdnjs的loli.net引用版" >> body.txt # 更新发布说明 gh release edit v${{ steps.determine_version.outputs.version }} --notes-file body.txt diff --git a/src/App.vue b/src/App.vue index 860ffbf..2948965 100644 --- a/src/App.vue +++ b/src/App.vue @@ -14,7 +14,9 @@ import { } from 'vue'; import { useStore } from 'vuex'; import { useRoute } from 'vue-router'; -import config from '@/config'; +import config, { + init as initConfig, +} from '@/config'; import sleep from '@/utils/sleep'; import LayoutMain from './layout/main.vue'; @@ -76,6 +78,11 @@ async function wsReconnect() { onMounted(async () => { refreshTime(); + // 如果没有配置哪吒版本,尝试载入 v1 版本配置 + if (!config.init) { + await initConfig(); + } + /** * 初始化服务器信息 */ diff --git a/src/config/index.js b/src/config/index.js index 8e7cdd3..8a606d9 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -1,16 +1,20 @@ import { reactive, } from 'vue'; +import { + loadProfile as loadNezhaV1Profile, +} from '@/utils/load-nezha-v1-config'; const defaultNezhaVersion = import.meta.env.VITE_NEZHA_VERSION; const config = reactive({ + init: false, nazhua: { title: '哪吒监控', // 如果打包禁用 Sarasa Term SC 字体,默认为禁用该字体的配置 disableSarasaTermSC: import.meta.env.VITE_DISABLE_SARASA_TERM_SC === '1', - nezhaVersion: ['v0', 'v1'].includes(defaultNezhaVersion) ? defaultNezhaVersion : 'v0', + nezhaVersion: ['v0', 'v1'].includes(defaultNezhaVersion) ? defaultNezhaVersion : null, apiMonitorPath: '/api/v1/monitor/{id}', wsPath: '/ws', nezhaPath: '/nezha/', @@ -25,6 +29,10 @@ const config = reactive({ }, }); +if (config.nazhua.nezhaVersion) { + config.init = true; +} + export function mergeNazhuaConfig(customConfig) { Object.keys(customConfig).forEach((key) => { config.nazhua[key] = customConfig[key]; @@ -34,3 +42,10 @@ export function mergeNazhuaConfig(customConfig) { window.$mergeNazhuaConfig = mergeNazhuaConfig; export default config; + +export const init = async () => { + await loadNezhaV1Profile(true).then((res) => { + config.nazhua.nezhaVersion = res ? 'v1' : 'v0'; + }); + config.init = true; +}; diff --git a/src/layout/components/footer.vue b/src/layout/components/footer.vue index 83e9ec5..a26f927 100644 --- a/src/layout/components/footer.vue +++ b/src/layout/components/footer.vue @@ -6,6 +6,7 @@ 哪吒监控 diff --git a/src/utils/load-nezha-v1-config.js b/src/utils/load-nezha-v1-config.js index 95e287e..8771a83 100644 --- a/src/utils/load-nezha-v1-config.js +++ b/src/utils/load-nezha-v1-config.js @@ -1,126 +1,11 @@ /** * V1版数据加载 */ -import store from '@/store'; import config from '@/config'; import request from '@/utils/request'; -import { Mapping } from '@/utils/object-mapping'; - -/** - * 字段映射 - */ -export const SERVER_FIELD_MAPS = { - ID: 'id', - CreatedAt: undefined, - UpdatedAt: undefined, - DeletedAt: undefined, - Name: 'name', - Tag: '_$function|queryGroup|id', - DisplayIndex: 'display_index', - HideForGuest: undefined, - EnableDDNS: undefined, - Host: '_$mapping|HOST_FIELD_MAPS', - State: '_$mapping|STATE_FIELD_MAPS', - LastActive: 'last_active', -}; -export const HOST_FIELD_MAPS = { - Platform: 'host.platform', - PlatformVersion: 'host.platform_version', - CPU: 'host.cpu', - MemTotal: 'host.mem_total', - DiskTotal: 'host.disk_total', - SwapTotal: 'host.swap_total', - Arch: 'host.arch', - Virtualization: 'host.virtualization', - BootTime: 'host.boot_time', - CountryCode: 'country_code', - Version: 'host.version', - GPU: 'host.gpu', -}; -export const STATE_FIELD_MAPS = { - CPU: 'state.cpu', - MemUsed: 'state.mem_used', - SwapUsed: 'state.swap_used', - DiskUsed: 'state.disk_used', - NetInTransfer: 'state.net_in_transfer', - NetOutTransfer: 'state.net_out_transfer', - NetInSpeed: 'state.net_in_speed', - NetOutSpeed: 'state.net_out_speed', - Uptime: 'state.uptime', - Load1: 'state.load_1', - Load5: 'state.load_5', - Load15: 'state.load_15', - TcpConnCount: 'state.tcp_conn_count', - UdpConnCount: 'state.udp_conn_count', - ProcessCount: 'state.process_count', - Temperatures: 'state.temperatures', - GPU: 'state.gpu', -}; - -/** - * 魔法方法 - */ -const magics = { - HOST_FIELD_MAPS, - STATE_FIELD_MAPS, - queryGroup: (id) => { - const groupItem = store.state.serverGroup?.find?.((i) => { - if (i.servers) { - return i.servers.includes(id); - } - return false; - }); - return groupItem?.name; - }, -}; - -/** - * 处理V1版数据 - * @param {Object} v1Data V1版数据 - * @return {Object} V0版数据 - */ -export const handelV1toV0 = (v1Data) => { - const v0Data = {}; - Object.keys(SERVER_FIELD_MAPS).forEach((key) => { - if (SERVER_FIELD_MAPS[key] === undefined) { - return; - } - if (SERVER_FIELD_MAPS[key].includes('_$')) { - const $magic = SERVER_FIELD_MAPS[key].split('|'); - switch ($magic[0]) { - case '_$function': - if ($magic.length >= 3 && magics[$magic[1]]) { - v0Data[key] = magics[$magic[1]]( - Mapping.mapping(v1Data, $magic[2]), - ); - } else { - v0Data[key] = undefined; - } - break; - case '_$mapping': - v0Data[key] = Mapping.each(magics[$magic[1]], v1Data); - break; - default: - break; - } - return; - } - v0Data[key] = Mapping.mapping(v1Data, SERVER_FIELD_MAPS[key]); - }); - if (v1Data.public_note) { - try { - v0Data.PublicNote = JSON.parse(v1Data.public_note); - } catch (e) { - v1Data.PublicNote = null; - } - } else { - v1Data.PublicNote = null; - } - return v0Data; -}; export const loadServerGroup = async () => request({ - // TODO: v1GroupPath 兼容 v1ApiGroupPath 到v0.6.0 + // DELETE: v1GroupPath 兼容 v1ApiGroupPath 到v0.6.0 url: config.nazhua.v1GroupPath || config.nazhua.v1ApiGroupPath, type: 'GET', }).then((res) => { @@ -156,10 +41,13 @@ export const loadSetting = async () => request({ /** * 加载个人信息 */ -export const loadProfile = async () => request({ +export const loadProfile = async (check) => request({ url: config.nazhua.v1ApiProfilePath, type: 'GET', }).then((res) => { + if (check) { + return res.status === 200; + } if (res.status === 200 && res.data?.success) { return res.data?.data || {}; } diff --git a/src/utils/request.js b/src/utils/request.js index 142826b..a26899b 100644 --- a/src/utils/request.js +++ b/src/utils/request.js @@ -13,11 +13,15 @@ const requestTagMap = {}; * @return {Promise} */ async function axiosRequest(options) { - return axios(options).then((res) => { - if (res.status === 200) { - return res; + return axios(options).then((res) => res).catch((err) => { + if (err.response) { + return err.response; } - throw new CustomError(`网络错误${res.status}`, res.status); + if (err.request) { + // 请求已经成功发起,但没有收到响应 + return null; + } + throw new CustomError(err.message); }); } diff --git a/src/utils/transform-v1-2-v0.js b/src/utils/transform-v1-2-v0.js new file mode 100644 index 0000000..a02b207 --- /dev/null +++ b/src/utils/transform-v1-2-v0.js @@ -0,0 +1,118 @@ +/** + * V1版数据加载 + */ +import store from '@/store'; +import { Mapping } from '@/utils/object-mapping'; + +/** + * 字段映射 + */ +export const SERVER_FIELD_MAPS = { + ID: 'id', + CreatedAt: undefined, + UpdatedAt: undefined, + DeletedAt: undefined, + Name: 'name', + Tag: '_$function|queryGroup|id', + DisplayIndex: 'display_index', + HideForGuest: undefined, + EnableDDNS: undefined, + Host: '_$mapping|HOST_FIELD_MAPS', + State: '_$mapping|STATE_FIELD_MAPS', + LastActive: 'last_active', +}; +export const HOST_FIELD_MAPS = { + Platform: 'host.platform', + PlatformVersion: 'host.platform_version', + CPU: 'host.cpu', + MemTotal: 'host.mem_total', + DiskTotal: 'host.disk_total', + SwapTotal: 'host.swap_total', + Arch: 'host.arch', + Virtualization: 'host.virtualization', + BootTime: 'host.boot_time', + CountryCode: 'country_code', + Version: 'host.version', + GPU: 'host.gpu', +}; +export const STATE_FIELD_MAPS = { + CPU: 'state.cpu', + MemUsed: 'state.mem_used', + SwapUsed: 'state.swap_used', + DiskUsed: 'state.disk_used', + NetInTransfer: 'state.net_in_transfer', + NetOutTransfer: 'state.net_out_transfer', + NetInSpeed: 'state.net_in_speed', + NetOutSpeed: 'state.net_out_speed', + Uptime: 'state.uptime', + Load1: 'state.load_1', + Load5: 'state.load_5', + Load15: 'state.load_15', + TcpConnCount: 'state.tcp_conn_count', + UdpConnCount: 'state.udp_conn_count', + ProcessCount: 'state.process_count', + Temperatures: 'state.temperatures', + GPU: 'state.gpu', +}; + +/** + * 魔法方法 + */ +const magics = { + HOST_FIELD_MAPS, + STATE_FIELD_MAPS, + queryGroup: (id) => { + const groupItem = store.state.serverGroup?.find?.((i) => { + if (i.servers) { + return i.servers.includes(id); + } + return false; + }); + return groupItem?.name; + }, +}; + +/** + * 处理V1版数据 + * @param {Object} v1Data V1版数据 + * @return {Object} V0版数据 + */ +export default function (v1Data) { + const v0Data = {}; + Object.keys(SERVER_FIELD_MAPS).forEach((key) => { + if (SERVER_FIELD_MAPS[key] === undefined) { + return; + } + if (SERVER_FIELD_MAPS[key].includes('_$')) { + const $magic = SERVER_FIELD_MAPS[key].split('|'); + switch ($magic[0]) { + case '_$function': + if ($magic.length >= 3 && magics[$magic[1]]) { + v0Data[key] = magics[$magic[1]]( + Mapping.mapping(v1Data, $magic[2]), + ); + } else { + v0Data[key] = undefined; + } + break; + case '_$mapping': + v0Data[key] = Mapping.each(magics[$magic[1]], v1Data); + break; + default: + break; + } + return; + } + v0Data[key] = Mapping.mapping(v1Data, SERVER_FIELD_MAPS[key]); + }); + if (v1Data.public_note) { + try { + v0Data.PublicNote = JSON.parse(v1Data.public_note); + } catch (e) { + v1Data.PublicNote = null; + } + } else { + v1Data.PublicNote = null; + } + return v0Data; +} diff --git a/src/ws/index.js b/src/ws/index.js index 1d0e009..538d7bf 100644 --- a/src/ws/index.js +++ b/src/ws/index.js @@ -1,8 +1,6 @@ import config from '@/config'; import MessageSubscribe from '@/utils/subscribe'; -import { - handelV1toV0, -} from '@/utils/load-nezha-v1-config'; +import v1TransformV0 from '@/utils/transform-v1-2-v0'; import WSService from './service'; @@ -10,9 +8,9 @@ import WSService from './service'; * 获取不同版本的WebSocket路径 */ function getWsApiPath() { - let url = config.nazhua.wsPath; - if (config.nazhua.nezhaVersion === 'v1') { - url = config.nazhua.v1WsPath; + let url = config?.nazhua?.wsPath; + if (config?.nazhua?.nezhaVersion === 'v1') { + url = config?.nazhua?.v1WsPath; } const a = document.createElement('a'); a.href = url; @@ -38,7 +36,7 @@ const wsService = new WSService({ msg.emit('servers', { now: data.now, servers: data?.servers?.map?.((server) => { - const item = handelV1toV0(server); + const item = v1TransformV0(server); return item; }) || [], });