mirror of
https://github.com/hi2shark/nazhua.git
synced 2026-01-19 10:40:43 +08:00
Compare commits
7 Commits
d85eeb9a44
...
0606ba918d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0606ba918d | ||
|
|
249a1463b3 | ||
|
|
7d424ffa78 | ||
|
|
057e0f5f11 | ||
|
|
247115c3c3 | ||
|
|
bd6b3c8b44 | ||
|
|
a23fbe4546 |
2
.github/workflows/docker-build.yml
vendored
2
.github/workflows/docker-build.yml
vendored
@ -12,7 +12,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
|
||||
33
.github/workflows/release.yml
vendored
33
.github/workflows/release.yml
vendored
@ -12,22 +12,19 @@ on:
|
||||
|
||||
jobs:
|
||||
build-and-release:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 10
|
||||
fetch-depth: 20
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Get version from package.json
|
||||
id: get_version
|
||||
run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
|
||||
@ -41,6 +38,16 @@ jobs:
|
||||
echo "version=${{ steps.get_version.outputs.version }}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Generate release notes
|
||||
id: release_notes
|
||||
run: |
|
||||
echo "#### Changes" > release_notes.md
|
||||
git log -20 --pretty=format:"- %s" >> release_notes.md
|
||||
echo -e "\n-----------\n哪吒V1请下载dist.zip\n哪吒V0请下载v0-dist.zip\n哪吒V0/nazhua/子目录需求请下载v0-nazhua.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引用版" >> release_notes.md
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
@ -49,7 +56,7 @@ jobs:
|
||||
with:
|
||||
tag_name: v${{ steps.determine_version.outputs.version }}
|
||||
release_name: Release v${{ steps.determine_version.outputs.version }}
|
||||
draft: false
|
||||
draft: true
|
||||
prerelease: false
|
||||
|
||||
- name: 构建自动版 - 完整引用版本
|
||||
@ -174,19 +181,7 @@ 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
|
||||
|
||||
# 添加最近一次提交的变更内容
|
||||
echo -e "\n## 变更内容\n$(cat change.txt)" >> body.txt
|
||||
|
||||
# 添加其他发布说明
|
||||
echo -e "\n哪吒V1请下载dist.zip\n哪吒V0请下载v0-dist.zip\n哪吒V0/nazhua/子目录需求请下载v0-nazhua.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
|
||||
gh release edit v${{ steps.determine_version.outputs.version }} --notes-file release_notes.md
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "nazhua",
|
||||
"version": "0.4.13",
|
||||
"version": "0.4.14",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@ -19,7 +19,6 @@ window.$$nazhuaConfig = {
|
||||
// hideTag: false, // 隐藏标签
|
||||
// hideDotBG: true, // 隐藏框框里面的点点背景
|
||||
// monitorRefreshTime: 10, // 监控刷新时间间隔,单位s(秒), 0为不刷新,为保证不频繁请求源站,最低生效值为10s
|
||||
// filterWeirdGPU: true, // 过滤奇怪的GPU
|
||||
// filterGPUKeywords: ['Virtual Display'], // 如果GPU名称中包含这些关键字,则过滤掉
|
||||
// customCodeMap: {}, // 自定义的地图点信息
|
||||
// nezhaVersion: 'v1', // 哪吒版本
|
||||
|
||||
12
readme.md
12
readme.md
@ -84,7 +84,9 @@ Nazhua对这个支持大概在90%左右,参与数据处理了的字段如下
|
||||
4-1. 分组数据,v1来自公开的api接口,`/api/v1/server-group`。
|
||||
|
||||
## 部署
|
||||
Nazhua主题是一个纯前端项目,可以部署在纯静态服务器上,但需要解决`/api/v1/monitor/${id}`监控数据、`/ws`WS服务和`/`主页的跨域访问。
|
||||
Nazhua主题是一个纯前端项目,可以部署在纯静态服务器上;
|
||||
v0需要解决`/api/v1/monitor/${id}`监控数据、`/ws`WS服务和`/`主页的跨域访问。
|
||||
v1需要解决`/api/xxx`等数据接口、`/api/v1/ws/server`WS服务的跨域访问。
|
||||
通常来说,你需要一个nginx或者caddy反代请求解决跨域问题。
|
||||
|
||||
### Docker Compose + Cloudflare Tunnels部署
|
||||
@ -118,8 +120,8 @@ services:
|
||||
### Nginx配置示例
|
||||
```nginx
|
||||
map $http_upgrade $connection_upgrade {
|
||||
default upgrade;
|
||||
'' close;
|
||||
default upgrade;
|
||||
'' close;
|
||||
}
|
||||
|
||||
server {
|
||||
@ -172,7 +174,6 @@ server {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 自定义配置
|
||||
可以通过修改根目录下的`config.js`文件来自定义配置
|
||||
例如:(*参考内容在文档上不一定是最新,具体参考public/config.js或者[Nazhua配置生成器](https://hi2shark.github.io/nazhua-generator/)*)
|
||||
@ -183,6 +184,7 @@ window.$$nazhuaConfig = {
|
||||
infinityCycle: '无限', // 无限周期名称
|
||||
buyBtnText: '购买', // 购买按钮文案
|
||||
listServerStatusType: 'progress', // 服务器状态类型--列表
|
||||
listServerRealTimeShowLoad: false, // 列表显示服务器实时负载
|
||||
detailServerStatusType: 'progress', // 服务器状态类型--详情页
|
||||
disableSarasaTermSC: false, // 禁用Sarasa Term SC字体
|
||||
hideWorldMap: false, // 隐藏地图
|
||||
@ -196,6 +198,8 @@ window.$$nazhuaConfig = {
|
||||
hideFilter: false, // 隐藏筛选
|
||||
hideTag: false, // 隐藏标签
|
||||
hideDotBG: false, // 隐藏框框里面的点点背景
|
||||
monitorRefreshTime: 10, // 监控刷新时间间隔,单位s(秒), 0为不刷新,为保证不频繁请求源站,最低生效值为10s
|
||||
filterGPUKeywords: ['Virtual Display'], // 如果GPU名称中包含这些关键字,则过滤掉
|
||||
customCodeMap: {}, // 自定义的地图点信息
|
||||
nezhaVersion: 'v1', // 哪吒版本
|
||||
apiMonitorPath: '/api/v1/monitor/{id}',
|
||||
|
||||
@ -11,6 +11,21 @@
|
||||
</span>
|
||||
</div>
|
||||
<div class="right-box">
|
||||
<div
|
||||
class="refresh-data-group"
|
||||
title="是否自动刷新"
|
||||
@click="switchRefresh"
|
||||
>
|
||||
<div
|
||||
class="switch-box"
|
||||
:class="{
|
||||
active: refreshData,
|
||||
}"
|
||||
>
|
||||
<span class="switch-dot" />
|
||||
</div>
|
||||
<span class="label-text">刷新</span>
|
||||
</div>
|
||||
<div
|
||||
class="peak-shaving-group"
|
||||
title="过滤太高或太低的数据"
|
||||
@ -62,16 +77,37 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
const refreshData = ref(true);
|
||||
const peakShaving = ref(false);
|
||||
|
||||
const monitorData = ref([]);
|
||||
|
||||
const monitorChartData = computed(() => {
|
||||
/**
|
||||
* 处理监控数据以生成分类的平均延迟随时间变化的列表。
|
||||
*
|
||||
* @returns {Object} 返回一个对象,包含:
|
||||
* - cateList {Array}: 唯一监控名称的列表。
|
||||
* - dateList {Array}: 排序后的唯一时间戳列表。
|
||||
* - valueList {Array}: 包含以下内容的对象列表:
|
||||
* - name {String}: 监控名称。
|
||||
* - data {Array}: [时间戳, 平均延迟] 对的数组。
|
||||
*
|
||||
* 该函数执行以下步骤:
|
||||
* 1. 遍历监控数据以分类和过滤平均延迟。
|
||||
* 2. 如果启用了削峰,则应用削峰以过滤异常值。
|
||||
* 3. 构建监控名称到其各自时间戳和平均延迟的映射。
|
||||
* 4. 将映射转换为监控名称、时间戳和平均延迟数据的列表。
|
||||
* 5. 删除重复的时间戳并对其进行排序。
|
||||
*/
|
||||
const cateMap = {};
|
||||
const dateMap = {};
|
||||
monitorData.value.forEach((i) => {
|
||||
const dateMap = {};
|
||||
if (!cateMap[i.monitor_name]) {
|
||||
cateMap[i.monitor_name] = [];
|
||||
cateMap[i.monitor_name] = {
|
||||
dateMap,
|
||||
avgs: [],
|
||||
};
|
||||
}
|
||||
const {
|
||||
threshold,
|
||||
@ -80,49 +116,47 @@ const monitorChartData = computed(() => {
|
||||
min,
|
||||
} = peakShaving.value ? getThreshold(i.avg_delay, 2) : {};
|
||||
i.created_at.forEach((o, index) => {
|
||||
if (!dateMap[o]) {
|
||||
dateMap[o] = [];
|
||||
if (dateMap[o]) {
|
||||
return;
|
||||
}
|
||||
const avgDelay = i.avg_delay[index];
|
||||
if (peakShaving.value) {
|
||||
if (Math.abs(avgDelay - mean) > threshold && max / min > 2) {
|
||||
return;
|
||||
}
|
||||
if (avgDelay === 0) {
|
||||
return;
|
||||
}
|
||||
if (Math.abs(avgDelay - mean) > threshold && max / min > 2) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
dateMap[o].push({
|
||||
name: i.monitor_name,
|
||||
value: (avgDelay).toFixed(2) * 1,
|
||||
});
|
||||
dateMap[o] = (avgDelay).toFixed(2) * 1;
|
||||
});
|
||||
});
|
||||
const dateList = [];
|
||||
Object.keys(dateMap).forEach((i) => {
|
||||
if (dateMap[i]?.length) {
|
||||
const time = parseInt(i, 10);
|
||||
dateList.push(time);
|
||||
dateMap[i].forEach((o) => {
|
||||
cateMap[o.name].push([time, o.value]);
|
||||
});
|
||||
}
|
||||
});
|
||||
dateList.sort((a, b) => a - b);
|
||||
let dateList = [];
|
||||
const cateList = [];
|
||||
const valueList = [];
|
||||
Object.keys(cateMap).forEach((i) => {
|
||||
if (cateMap[i]?.length) {
|
||||
cateList.push(i);
|
||||
}
|
||||
const {
|
||||
dateMap,
|
||||
avgs,
|
||||
} = cateMap[i];
|
||||
Object.entries(dateMap).forEach(([key, value]) => {
|
||||
const time = parseInt(key, 10);
|
||||
avgs.push([time, value]);
|
||||
dateList.push(time);
|
||||
});
|
||||
valueList.push({
|
||||
name: i,
|
||||
data: cateMap[i],
|
||||
data: avgs,
|
||||
});
|
||||
if (avgs.length) {
|
||||
cateList.push(i);
|
||||
}
|
||||
});
|
||||
// 去重
|
||||
dateList = Array.from(new Set(dateList)).sort((a, b) => a - b);
|
||||
return {
|
||||
cateList,
|
||||
dateList,
|
||||
cateList,
|
||||
valueList,
|
||||
};
|
||||
});
|
||||
@ -131,6 +165,10 @@ function switchPeakShaving() {
|
||||
peakShaving.value = !peakShaving.value;
|
||||
}
|
||||
|
||||
function switchRefresh() {
|
||||
refreshData.value = !refreshData.value;
|
||||
}
|
||||
|
||||
async function loadMonitor() {
|
||||
await request({
|
||||
url: (
|
||||
@ -147,19 +185,21 @@ async function loadMonitor() {
|
||||
}
|
||||
|
||||
let loadMonitorTimer = null;
|
||||
async function setTimeLoadMonitor() {
|
||||
async function setTimeLoadMonitor(force = false) {
|
||||
if (loadMonitorTimer) {
|
||||
clearTimeout(loadMonitorTimer);
|
||||
}
|
||||
await loadMonitor();
|
||||
if (refreshData.value || force) {
|
||||
await loadMonitor();
|
||||
}
|
||||
let monitorRefreshTime = parseInt(config.nazhua.monitorRefreshTime, 10);
|
||||
// 0 为不刷新
|
||||
if (monitorRefreshTime === 0) {
|
||||
return;
|
||||
}
|
||||
// 非数字 强制为 10
|
||||
// 非数字 强制为30
|
||||
if (Number.isNaN(monitorRefreshTime)) {
|
||||
monitorRefreshTime = 10;
|
||||
monitorRefreshTime = 30;
|
||||
}
|
||||
// 最小 10 秒
|
||||
const sTime = Math.min(monitorRefreshTime, 10);
|
||||
@ -169,7 +209,7 @@ async function setTimeLoadMonitor() {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
setTimeLoadMonitor();
|
||||
setTimeLoadMonitor(true);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
@ -197,7 +237,14 @@ onUnmounted(() => {
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
.peak-shaving-group {
|
||||
.right-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.peak-shaving-group,
|
||||
.refresh-data-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
|
||||
@ -18,7 +18,10 @@
|
||||
<span
|
||||
class="server-flag"
|
||||
>
|
||||
<span :class="platformLogoIconClassName" />
|
||||
<span
|
||||
class="fi"
|
||||
:class="'fi-' + (info?.Host?.CountryCode || 'un')"
|
||||
/>
|
||||
</span>
|
||||
<span class="server-name">
|
||||
{{ info.Name }}
|
||||
@ -29,10 +32,7 @@
|
||||
v-if="cpuAndMemAndDisk"
|
||||
class="cpu-mem-group"
|
||||
>
|
||||
<span
|
||||
v-if="info?.Host?.Platform"
|
||||
:class="'fl-' + info?.Host?.Platform"
|
||||
/>
|
||||
<span :class="platformLogoIconClassName" />
|
||||
<span class="core-mem">{{ cpuAndMemAndDisk }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -8,13 +8,21 @@
|
||||
* @property {number} mean - 数据的平均值
|
||||
*/
|
||||
export function getThreshold(data, tolerance = 2) {
|
||||
// 计算数据的平均值
|
||||
const mean = data.reduce((sum, value) => sum + value, 0) / data.length;
|
||||
// 计算数据的方差
|
||||
const variance = data.reduce((sum, value) => sum + (value - mean) ** 2, 0) / data.length;
|
||||
// 计算标准差
|
||||
const stdDev = Math.sqrt(variance);
|
||||
// 计算阈值
|
||||
const threshold = tolerance * stdDev;
|
||||
// 过滤掉值为0的数据
|
||||
const filteredData = data.filter((value) => value !== 0);
|
||||
// 计算过滤后数据的最小值
|
||||
const min = Math.min(...filteredData);
|
||||
// 计算过滤后数据的最大值
|
||||
const max = Math.max(...filteredData);
|
||||
// 返回包含阈值、平均值、最小值和最大值的对象
|
||||
return {
|
||||
threshold,
|
||||
mean,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user