mirror of
https://github.com/hi2shark/nazhua.git
synced 2026-01-11 22:50:42 +08:00
💥 兼容哪吒V1的数据接口
This commit is contained in:
parent
cc585221c1
commit
1c6107cb07
1
.gitignore
vendored
1
.gitignore
vendored
@ -11,6 +11,7 @@ node_modules
|
|||||||
dist
|
dist
|
||||||
dist-ssr
|
dist-ssr
|
||||||
*.local
|
*.local
|
||||||
|
demo
|
||||||
|
|
||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
.vscode/*
|
.vscode/*
|
||||||
|
|||||||
@ -1,3 +1,8 @@
|
|||||||
|
map $http_upgrade $connection_upgrade {
|
||||||
|
default upgrade;
|
||||||
|
'' close;
|
||||||
|
}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
server_name ${DOMAIN};
|
server_name ${DOMAIN};
|
||||||
@ -7,7 +12,18 @@ server {
|
|||||||
proxy_pass ${NEZHA}ws;
|
proxy_pass ${NEZHA}ws;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
proxy_set_header Connection "upgrade";
|
proxy_set_header Connection $connection_upgrade;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 兼容哪吒V1
|
||||||
|
location /api/v1/ws/server {
|
||||||
|
proxy_pass ${NEZHA}api/v1/ws/server;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection $connection_upgrade;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
|||||||
@ -16,9 +16,13 @@ window.$$nazhuaConfig = {
|
|||||||
// hideFilter: false, // 隐藏筛选
|
// hideFilter: false, // 隐藏筛选
|
||||||
// hideTag: false, // 隐藏标签
|
// hideTag: false, // 隐藏标签
|
||||||
// customCodeMap: {}, // 自定义的地图点信息
|
// customCodeMap: {}, // 自定义的地图点信息
|
||||||
|
// nezhaVersion: 'v1', // 哪吒版本
|
||||||
// apiMonitorPath: '/api/v1/monitor/{id}',
|
// apiMonitorPath: '/api/v1/monitor/{id}',
|
||||||
// wsPath: '/ws',
|
// wsPath: '/ws',
|
||||||
// nezhaPath: '/nezha/',
|
// nezhaPath: '/nezha/',
|
||||||
// nezhaV0ConfigType: 'servers', // 哪吒v0数据读取类型
|
// nezhaV0ConfigType: 'servers', // 哪吒v0数据读取类型
|
||||||
|
// v1ApiMonitorPath: '/api/v1/service/{id}',
|
||||||
|
// v1WsPath: '/api/v1/ws/server',
|
||||||
|
// v1GroupPath: '/api/v1/server-group',
|
||||||
// routeMode: 'h5', // 路由模式
|
// routeMode: 'h5', // 路由模式
|
||||||
};
|
};
|
||||||
|
|||||||
@ -55,7 +55,7 @@ async function wsReconnect() {
|
|||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
handleSystem();
|
handleSystem();
|
||||||
refreshTime();
|
refreshTime();
|
||||||
await store.dispatch('loadServers');
|
await store.dispatch('initServerInfo');
|
||||||
msg.on('close', () => {
|
msg.on('close', () => {
|
||||||
console.log('ws closed');
|
console.log('ws closed');
|
||||||
wsReconnect();
|
wsReconnect();
|
||||||
|
|||||||
@ -12,10 +12,14 @@ const config = {
|
|||||||
},
|
},
|
||||||
nazhua: {
|
nazhua: {
|
||||||
title: '哪吒监控',
|
title: '哪吒监控',
|
||||||
|
nezhaVersion: 'v0',
|
||||||
apiMonitorPath: '/api/v1/monitor/{id}',
|
apiMonitorPath: '/api/v1/monitor/{id}',
|
||||||
wsPath: '/ws',
|
wsPath: '/ws',
|
||||||
nezhaPath: '/nezha/',
|
nezhaPath: '/nezha/',
|
||||||
nezhaV0ConfigType: 'servers',
|
nezhaV0ConfigType: 'servers',
|
||||||
|
v1ApiMonitorPath: '/api/v1/service/{id}',
|
||||||
|
v1WsPath: '/api/v1/ws/server',
|
||||||
|
v1GroupPath: '/api/v1/server-group',
|
||||||
// 解构载入自定义配置
|
// 解构载入自定义配置
|
||||||
...(window.$$nazhuaConfig || {}),
|
...(window.$$nazhuaConfig || {}),
|
||||||
},
|
},
|
||||||
|
|||||||
@ -2,7 +2,13 @@ import {
|
|||||||
createStore,
|
createStore,
|
||||||
} from 'vuex';
|
} from 'vuex';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import loadNezhaConfig from '@/utils/load-nezha-config';
|
import config from '@/config';
|
||||||
|
import loadNezhaV0Config, {
|
||||||
|
loadServerGroup as loadNezhaV0ServerGroup,
|
||||||
|
} from '@/utils/load-nezha-v0-config';
|
||||||
|
import {
|
||||||
|
loadServerGroup as loadNezhaV1ServerGroup,
|
||||||
|
} from '@/utils/load-nezha-v1-config';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
msg,
|
msg,
|
||||||
@ -10,6 +16,7 @@ import {
|
|||||||
|
|
||||||
const defaultState = () => ({
|
const defaultState = () => ({
|
||||||
init: false,
|
init: false,
|
||||||
|
serverGroup: [],
|
||||||
serverList: [],
|
serverList: [],
|
||||||
serverCount: {
|
serverCount: {
|
||||||
total: 0,
|
total: 0,
|
||||||
@ -35,9 +42,13 @@ function handleServerCount(servers) {
|
|||||||
return counts;
|
return counts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let firstSetServers = true;
|
||||||
const store = createStore({
|
const store = createStore({
|
||||||
state: defaultState(),
|
state: defaultState(),
|
||||||
mutations: {
|
mutations: {
|
||||||
|
SET_SERVER_GROUP(state, serverGroup) {
|
||||||
|
state.serverGroup = serverGroup;
|
||||||
|
},
|
||||||
SET_SERVERS(state, servers) {
|
SET_SERVERS(state, servers) {
|
||||||
const newServers = [...servers];
|
const newServers = [...servers];
|
||||||
newServers.sort((a, b) => b.DisplayIndex - a.DisplayIndex);
|
newServers.sort((a, b) => b.DisplayIndex - a.DisplayIndex);
|
||||||
@ -58,8 +69,6 @@ const store = createStore({
|
|||||||
};
|
};
|
||||||
if (oldItem?.PublicNote) {
|
if (oldItem?.PublicNote) {
|
||||||
serverItem.PublicNote = oldItem.PublicNote;
|
serverItem.PublicNote = oldItem.PublicNote;
|
||||||
} else {
|
|
||||||
serverItem.PublicNote = {};
|
|
||||||
}
|
}
|
||||||
return serverItem;
|
return serverItem;
|
||||||
});
|
});
|
||||||
@ -74,8 +83,20 @@ const store = createStore({
|
|||||||
/**
|
/**
|
||||||
* 加载服务器列表
|
* 加载服务器列表
|
||||||
*/
|
*/
|
||||||
async loadServers({ commit }) {
|
async initServerInfo({ commit }) {
|
||||||
const serverResult = await loadNezhaConfig();
|
firstSetServers = true;
|
||||||
|
// 如果是v1版本的话,加载v1版本的数据
|
||||||
|
if (config.nazhua.nezhaVersion === 'v1') {
|
||||||
|
loadNezhaV1ServerGroup().then((res) => {
|
||||||
|
if (res) {
|
||||||
|
commit('SET_SERVER_GROUP', res);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 如果是v0版本的话,加载v0版本的数据
|
||||||
|
// 加载初始化的服务器列表,需要其中的公开备注字段
|
||||||
|
const serverResult = await loadNezhaV0Config();
|
||||||
if (!serverResult) {
|
if (!serverResult) {
|
||||||
console.error('load server config failed');
|
console.error('load server config failed');
|
||||||
return;
|
return;
|
||||||
@ -87,6 +108,11 @@ const store = createStore({
|
|||||||
};
|
};
|
||||||
return item;
|
return item;
|
||||||
}) || [];
|
}) || [];
|
||||||
|
const res = loadNezhaV0ServerGroup(servers);
|
||||||
|
if (res) {
|
||||||
|
commit('SET_SERVER_GROUP', res);
|
||||||
|
}
|
||||||
|
firstSetServers = false;
|
||||||
commit('SET_SERVERS', servers);
|
commit('SET_SERVERS', servers);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@ -104,7 +130,12 @@ const store = createStore({
|
|||||||
};
|
};
|
||||||
return item;
|
return item;
|
||||||
}) || [];
|
}) || [];
|
||||||
commit('UPDATE_SERVERS', servers);
|
if (firstSetServers) {
|
||||||
|
firstSetServers = false;
|
||||||
|
commit('SET_SERVERS', servers);
|
||||||
|
} else {
|
||||||
|
commit('UPDATE_SERVERS', servers);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@ -38,3 +38,30 @@ export default async () => fetch(config.nazhua.nezhaPath).then((res) => res.text
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}).catch(() => null);
|
}).catch(() => null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取标签列表
|
||||||
|
*/
|
||||||
|
export const loadServerGroup = (services) => {
|
||||||
|
const tagMap = {};
|
||||||
|
services.forEach((i) => {
|
||||||
|
if (i.Tag) {
|
||||||
|
if (!tagMap[i.Tag]) {
|
||||||
|
tagMap[i.Tag] = [];
|
||||||
|
}
|
||||||
|
tagMap[i.Tag].push(i);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const tagList = [];
|
||||||
|
Object.entries(tagMap).forEach(([tag, serviceIds]) => {
|
||||||
|
tagList.push({
|
||||||
|
name: tag,
|
||||||
|
count: serviceIds.length,
|
||||||
|
servers: serviceIds,
|
||||||
|
group: {
|
||||||
|
name: tag,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return tagList;
|
||||||
|
};
|
||||||
138
src/utils/load-nezha-v1-config.js
Normal file
138
src/utils/load-nezha-v1-config.js
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
/**
|
||||||
|
* 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({
|
||||||
|
url: config.nazhua.v1GroupPath,
|
||||||
|
type: 'GET',
|
||||||
|
}).then((res) => {
|
||||||
|
if (res.status === 200) {
|
||||||
|
const list = res.data?.data || [];
|
||||||
|
return list.map((i) => {
|
||||||
|
const item = {
|
||||||
|
...i,
|
||||||
|
name: i?.group?.name,
|
||||||
|
count: i?.servers?.length,
|
||||||
|
};
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}).catch(() => null);
|
||||||
99
src/utils/object-mapping.js
Normal file
99
src/utils/object-mapping.js
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
/**
|
||||||
|
* 对象映射封装
|
||||||
|
*/
|
||||||
|
class Mapping {
|
||||||
|
/**
|
||||||
|
* 字符串映射对象
|
||||||
|
*
|
||||||
|
* @param {Record<string, any>} obj 查找的对象
|
||||||
|
* @param {string} key 查找的属性
|
||||||
|
*
|
||||||
|
* @return {any}
|
||||||
|
*/
|
||||||
|
static mapping(obj, key) {
|
||||||
|
// 检查 obj 是否为对象,如果不是,返回 undefined
|
||||||
|
if (!obj || typeof obj !== 'object') {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
// 检查 key 是否为字符串,如果不是,返回 undefined
|
||||||
|
if (typeof key !== 'string') {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
// 检查 key 是否包含非法字符,如果包含,返回 undefined
|
||||||
|
if (key.includes('..') || key.startsWith('.') || key.endsWith('.')) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
// 如果 key 包含 '.',使用 reduce 方法递归获取嵌套属性值
|
||||||
|
if (key.includes('.')) {
|
||||||
|
return key.split('.').reduce((val, k) => (val !== undefined ? Mapping.get(val, k) : undefined), obj);
|
||||||
|
}
|
||||||
|
// 如果 key 不包含 '.',直接获取属性值
|
||||||
|
return Mapping.get(obj, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取数据
|
||||||
|
* 支持处理数组指针
|
||||||
|
* @param {Record<string, any> | any[]} obj 属性对象
|
||||||
|
* @param {string} key 属性名称
|
||||||
|
* @return {any}
|
||||||
|
*/
|
||||||
|
static get(obj, key) {
|
||||||
|
if (!obj || typeof obj !== 'object' || !key) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const indexMatch = key.match(/\[(\d+)\]/);
|
||||||
|
if (indexMatch) {
|
||||||
|
const [fullMatch, indexStr] = indexMatch;
|
||||||
|
const index = Number(indexStr);
|
||||||
|
const matchIndex = key.indexOf(fullMatch);
|
||||||
|
if (matchIndex === 0) {
|
||||||
|
if (Array.isArray(obj) && index < obj.length) {
|
||||||
|
const val = obj[index];
|
||||||
|
const restKey = key.slice(fullMatch.length);
|
||||||
|
return restKey ? Mapping.get(val, restKey) : val;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const pre = key.slice(0, matchIndex);
|
||||||
|
const list = obj[pre];
|
||||||
|
if (Array.isArray(list) && index < list.length) {
|
||||||
|
const val = list[index];
|
||||||
|
const restKey = key.slice(matchIndex + fullMatch.length);
|
||||||
|
return restKey ? Mapping.get(val, restKey) : val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return obj[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据根据key的映射进行组装
|
||||||
|
*
|
||||||
|
* @param {KeyMap} keys 映射对象
|
||||||
|
* @param {Record<string, unknown>} data 数据对象
|
||||||
|
*
|
||||||
|
* @return {Record<string, unknown>}
|
||||||
|
*/
|
||||||
|
static each(keys, data) {
|
||||||
|
// 检查 keys 是否为对象,如果不是,返回 undefined
|
||||||
|
if (!keys || typeof keys !== 'object') {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
// 检查 data 是否为对象,如果不是,返回 undefined
|
||||||
|
if (!data || typeof data !== 'object') {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return Object.entries(keys).reduce((acc, [key, value]) => {
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
acc[key] = Mapping.mapping(data, value);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const { mapping } = Mapping;
|
||||||
|
|
||||||
|
export { Mapping, mapping };
|
||||||
|
|
||||||
|
export default mapping;
|
||||||
@ -1,39 +1,21 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import uuid from '@/utils/uuid';
|
import uuid from '@/utils/uuid';
|
||||||
import validate from '@/utils/validate';
|
|
||||||
|
|
||||||
import config from '@/config';
|
|
||||||
import CustomError from './custom-error';
|
import CustomError from './custom-error';
|
||||||
|
|
||||||
const {
|
const limit = 10;
|
||||||
codeField,
|
|
||||||
dataField,
|
|
||||||
msgField,
|
|
||||||
okCode,
|
|
||||||
limit = 10,
|
|
||||||
} = config.request;
|
|
||||||
|
|
||||||
const requestTagMap = {};
|
const requestTagMap = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* axios请求
|
* axios请求
|
||||||
* @param {object} options 请求参数
|
* @param {object} options 请求参数
|
||||||
* @param {boolean} noFormat 不进行返回数据的格式化处理 网络状态200即为成功
|
|
||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
async function axiosRequest(options, noFormat) {
|
async function axiosRequest(options) {
|
||||||
return axios(options).then((res) => {
|
return axios(options).then((res) => {
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
if (noFormat) {
|
return res;
|
||||||
return res;
|
|
||||||
}
|
|
||||||
if (validate.isSet(res.data[codeField]) && `${res.data[codeField]}` === `${okCode}`) {
|
|
||||||
return res.data[dataField];
|
|
||||||
}
|
|
||||||
if (typeof res.data[codeField] !== 'undefined') {
|
|
||||||
throw new CustomError(res.data[msgField], res.data[codeField]);
|
|
||||||
}
|
|
||||||
throw new CustomError('服务器返回内容不规范', -99);
|
|
||||||
}
|
}
|
||||||
throw new CustomError(`网络错误${res.status}`, res.status);
|
throw new CustomError(`网络错误${res.status}`, res.status);
|
||||||
});
|
});
|
||||||
@ -69,7 +51,6 @@ class NetworkRequest {
|
|||||||
* @param {string} type 请求的Method
|
* @param {string} type 请求的Method
|
||||||
* @param {object} headers Header请求参数
|
* @param {object} headers Header请求参数
|
||||||
* @param {object} data 请求参数
|
* @param {object} data 请求参数
|
||||||
* @param {boolean} noFormat 不进行返回数据的格式化处理 网络状态200即为成功
|
|
||||||
* @param {boolean} defaultContentType 默认的请求方式
|
* @param {boolean} defaultContentType 默认的请求方式
|
||||||
* @param {Boolean} priority 优先调用请求
|
* @param {Boolean} priority 优先调用请求
|
||||||
*
|
*
|
||||||
@ -85,7 +66,6 @@ class NetworkRequest {
|
|||||||
type,
|
type,
|
||||||
headers,
|
headers,
|
||||||
data,
|
data,
|
||||||
noFormat = false,
|
|
||||||
defaultContentType = true,
|
defaultContentType = true,
|
||||||
requestTag = undefined,
|
requestTag = undefined,
|
||||||
responseType,
|
responseType,
|
||||||
@ -100,9 +80,7 @@ class NetworkRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const defaultHeaders = {
|
const defaultHeaders = {};
|
||||||
...config.request.headers,
|
|
||||||
};
|
|
||||||
if (defaultContentType === false) {
|
if (defaultContentType === false) {
|
||||||
if (NetworkRequest.FormRequest(defaultHeaders)) {
|
if (NetworkRequest.FormRequest(defaultHeaders)) {
|
||||||
defaultHeaders['content-type'] = 'application/json';
|
defaultHeaders['content-type'] = 'application/json';
|
||||||
@ -122,7 +100,6 @@ class NetworkRequest {
|
|||||||
signal: abortController?.signal ?? undefined,
|
signal: abortController?.signal ?? undefined,
|
||||||
responseType,
|
responseType,
|
||||||
},
|
},
|
||||||
noFormat,
|
|
||||||
(res) => {
|
(res) => {
|
||||||
resolve(res);
|
resolve(res);
|
||||||
},
|
},
|
||||||
@ -153,13 +130,13 @@ class NetworkRequest {
|
|||||||
if (this.tasks.length === 0) {
|
if (this.tasks.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const [options, onFormat, success, fail, tag] = this.tasks.pop();
|
const [options, success, fail, tag] = this.tasks.pop();
|
||||||
// 请求未执行已被中止
|
// 请求未执行已被中止
|
||||||
if (options?.signal?.aborted) {
|
if (options?.signal?.aborted) {
|
||||||
this.overTask();
|
this.overTask();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
requestTagMap[tag] = axiosRequest(options, onFormat);
|
requestTagMap[tag] = axiosRequest(options);
|
||||||
requestTagMap[tag].finally(() => {
|
requestTagMap[tag].finally(() => {
|
||||||
this.overTask();
|
this.overTask();
|
||||||
// 一秒内请求不重复
|
// 一秒内请求不重复
|
||||||
|
|||||||
@ -131,10 +131,13 @@ function switchPeakShaving() {
|
|||||||
|
|
||||||
async function loadMonitor() {
|
async function loadMonitor() {
|
||||||
await request({
|
await request({
|
||||||
url: config.nazhua.apiMonitorPath.replace('{id}', props.info.ID),
|
url: (
|
||||||
|
config.nazhua.nezhaVersion === 'v1' ? config.nazhua.v1ApiMonitorPath : config.nazhua.apiMonitorPath
|
||||||
|
).replace('{id}', props.info.ID),
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
if (Array.isArray(res)) {
|
const list = config.nazhua.nezhaVersion === 'v1' ? res.data?.data : res.data?.result;
|
||||||
monitorData.value = res;
|
if (Array.isArray(list)) {
|
||||||
|
monitorData.value = list;
|
||||||
}
|
}
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|||||||
@ -45,8 +45,10 @@
|
|||||||
>
|
>
|
||||||
<span class="text">
|
<span class="text">
|
||||||
<span class="text-item value-text">{{ billAndPlan.billing.value }}</span>
|
<span class="text-item value-text">{{ billAndPlan.billing.value }}</span>
|
||||||
<span class="text-item">/</span>
|
<template v-if="!billAndPlan.billing.isFree">
|
||||||
<span class="text-item label-text">{{ billAndPlan.billing.cycleLabel }}</span>
|
<span class="text-item">/</span>
|
||||||
|
<span class="text-item label-text">{{ billAndPlan.billing.cycleLabel }}</span>
|
||||||
|
</template>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@ -64,6 +64,7 @@ export default (params) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (validate.isSet(billingDataMod?.amount)) {
|
if (validate.isSet(billingDataMod?.amount)) {
|
||||||
|
let isFree = false;
|
||||||
let amountValue = billingDataMod.amount;
|
let amountValue = billingDataMod.amount;
|
||||||
let label;
|
let label;
|
||||||
if (billingDataMod.amount.toString() === '-1') {
|
if (billingDataMod.amount.toString() === '-1') {
|
||||||
@ -71,6 +72,7 @@ export default (params) => {
|
|||||||
label = `每${cycleLabel}`;
|
label = `每${cycleLabel}`;
|
||||||
} else if (billingDataMod.amount.toString() === '0') {
|
} else if (billingDataMod.amount.toString() === '0') {
|
||||||
amountValue = config.nazhua.freeAmount || '免费';
|
amountValue = config.nazhua.freeAmount || '免费';
|
||||||
|
isFree = true;
|
||||||
} else {
|
} else {
|
||||||
label = `${cycleLabel}付`;
|
label = `${cycleLabel}付`;
|
||||||
}
|
}
|
||||||
@ -79,6 +81,7 @@ export default (params) => {
|
|||||||
value: amountValue,
|
value: amountValue,
|
||||||
cycleLabel,
|
cycleLabel,
|
||||||
months,
|
months,
|
||||||
|
isFree,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// 剩余时间
|
// 剩余时间
|
||||||
|
|||||||
@ -63,12 +63,12 @@ export default (params) => {
|
|||||||
case 'cpu':
|
case 'cpu':
|
||||||
return {
|
return {
|
||||||
type: 'cpu',
|
type: 'cpu',
|
||||||
used: (props.info.State.CPU).toFixed(1) * 1,
|
used: (props.info.State?.CPU || 0).toFixed(1) * 1,
|
||||||
colors: {
|
colors: {
|
||||||
used: '#0088ff',
|
used: '#0088ff',
|
||||||
total: 'rgba(255, 255, 255, 0.2)',
|
total: 'rgba(255, 255, 255, 0.2)',
|
||||||
},
|
},
|
||||||
valText: `${(props.info.State.CPU).toFixed(1) * 1}%`,
|
valText: `${(props.info.State?.CPU || 0).toFixed(1) * 1}%`,
|
||||||
label: 'CPU',
|
label: 'CPU',
|
||||||
content: {
|
content: {
|
||||||
default: cpuInfo.value?.core,
|
default: cpuInfo.value?.core,
|
||||||
|
|||||||
@ -87,35 +87,10 @@ const serverList = computed(() => store.state.serverList);
|
|||||||
// 服务器总数
|
// 服务器总数
|
||||||
const serverCount = computed(() => store.state.serverCount);
|
const serverCount = computed(() => store.state.serverCount);
|
||||||
|
|
||||||
/**
|
const tagOptions = computed(() => store.state.serverGroup.map((i) => ({
|
||||||
* 解构数据
|
|
||||||
*/
|
|
||||||
const serverListData = computed(() => {
|
|
||||||
const tagMap = {};
|
|
||||||
serverList.value.forEach((i) => {
|
|
||||||
if (i.Tag) {
|
|
||||||
if (!tagMap[i.Tag]) {
|
|
||||||
tagMap[i.Tag] = 0;
|
|
||||||
}
|
|
||||||
tagMap[i.Tag] += 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const tags = [];
|
|
||||||
Object.entries(tagMap).forEach(([tag, count]) => {
|
|
||||||
tags.push({
|
|
||||||
tag,
|
|
||||||
count,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
tags,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const tagOptions = computed(() => (serverListData.value?.tags || []).map((i) => ({
|
|
||||||
key: uuid(),
|
key: uuid(),
|
||||||
label: i.tag,
|
label: i.name,
|
||||||
value: i.tag,
|
value: i.name,
|
||||||
})));
|
})));
|
||||||
|
|
||||||
const onlineOptions = computed(() => {
|
const onlineOptions = computed(() => {
|
||||||
|
|||||||
@ -1,10 +1,20 @@
|
|||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import MessageSubscribe from '@/utils/subscribe';
|
import MessageSubscribe from '@/utils/subscribe';
|
||||||
|
import {
|
||||||
|
handelV1toV0,
|
||||||
|
} from '@/utils/load-nezha-v1-config';
|
||||||
import WSService from './service';
|
import WSService from './service';
|
||||||
|
|
||||||
|
function getWsApiPath() {
|
||||||
|
if (config.nazhua.nezhaVersion === 'v1') {
|
||||||
|
return config.nazhua.v1WsPath;
|
||||||
|
}
|
||||||
|
return config.nazhua.wsPath;
|
||||||
|
}
|
||||||
|
|
||||||
const msg = new MessageSubscribe();
|
const msg = new MessageSubscribe();
|
||||||
const wsService = new WSService({
|
const wsService = new WSService({
|
||||||
wsUrl: config?.nazhua?.wsPath,
|
wsUrl: getWsApiPath(),
|
||||||
onConnect: () => {
|
onConnect: () => {
|
||||||
msg.emit('connect');
|
msg.emit('connect');
|
||||||
},
|
},
|
||||||
@ -16,7 +26,17 @@ const wsService = new WSService({
|
|||||||
},
|
},
|
||||||
onMessage: (data) => {
|
onMessage: (data) => {
|
||||||
if (data?.now) {
|
if (data?.now) {
|
||||||
msg.emit('servers', data);
|
if (config.nazhua.nezhaVersion === 'v1') {
|
||||||
|
msg.emit('servers', {
|
||||||
|
now: data.now,
|
||||||
|
servers: data?.servers?.map?.((server) => {
|
||||||
|
const item = handelV1toV0(server);
|
||||||
|
return item;
|
||||||
|
}) || [],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
msg.emit('servers', data);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
msg.emit('message', data);
|
msg.emit('message', data);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,6 +29,11 @@ export default defineConfig({
|
|||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
ws: true,
|
ws: true,
|
||||||
},
|
},
|
||||||
|
'/api/v1/ws/server': {
|
||||||
|
target: process.env.WS_HOST,
|
||||||
|
changeOrigin: true,
|
||||||
|
ws: true,
|
||||||
|
},
|
||||||
'/nezha/': {
|
'/nezha/': {
|
||||||
target: process.env.NEZHA_HOST,
|
target: process.env.NEZHA_HOST,
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user