mirror of
https://github.com/Buriburizaem0n/nezha-dash-v1.git
synced 2026-11-04 01:00:20 +00:00
* feat(server-detail): add online history tab Render per-server online periods from GET /api/v1/server/:id/online-history: summary card (current status, online rate, total in window, last active), daily online-time bars, and the raw period list. Window selector offers 1d/7d/30d and locks options beyond the server's max_days; guests hitting the 1d cap see a login hint. i18n keys added for all locales (zh-CN/zh-TW translated, others fall back to English for Weblate). * fix(online-history): keep selector pills above the sliding indicator The active-indicator overlay sits at z-10 and intercepted clicks on the day pills; wrap pills at z-20 like the existing PeriodSelector. * feat(online-history): render daily online as 24h timeline with hover tooltips 每日在线卡片从整日占比进度条改为 0:00–24:00 整轴时间条: 每个在线时段按当日时刻定位为高亮分段,hover 显示起止时间与时长; 进行中时段以亮色高亮。卡片顶部新增共享刻度(0:00 / 12:00 / 24:00)。 * feat(online-history): regroup periods by day as expandable chip wall Rework the Online tab per the chip-wall design: periods are grouped by panel-timezone day with per-day totals, defaulting to the latest day with "Expand more" revealing up to ten more groups per click, plus an Online/Offline switch that derives offline chips as the window complement. - respond to the backend panel timezone (timezone/tz_offset): dayjs 1.11 .tz(input, zone) ignores explicit offsets, so offset-bearing timestamps are parsed first and converted afterwards; naive date strings keep the zone-parse path (panelDay) - guests falling back automatically: a rejected days>1 request locks 7d/30d and retries at 1d instead of surfacing only a login prompt - fix day-selector indicator landing on the wrong pill: useActiveIndicator refs are stored by index, pass the map index not the day value - disable Node experimental webstorage in vitest workers: Node >=25 ships a broken global localStorage that shadows the jsdom one - 14 locales: tabSwitch.Online + serverOnlineHistory.* * feat(online-history): boxed summary stats with desktop/mobile layouts * fix(online-history): align offline gaps, DST coordinates and ongoing chips - extract pure timeline/grouping helpers into src/lib/online-history.ts - clamp offline complements to day and window boundaries, so a gap can no longer run into a later day's first online session - use wall-clock coordinates and true next-midnight boundaries on DST days - label only the segment ending at the live session end as Ongoing - keep group totals equal to the visible chips for cross-day sessions - keep period chips on one line and render next-midnight as 24:00 - add regression tests for multi-day offline gaps, window_start/window_end, spring-forward/fall-back geometry and ongoing clipping * style(online-history): lighten summary metrics and shorten rate bar * feat(online-history): refine UI and unify daily history rendering --------- Co-authored-by: Sam5440 <Sam5440@users.noreply.github.com> Co-authored-by: hamster1963 <1410514192@qq.com>
134 lines
3.1 KiB
TypeScript
134 lines
3.1 KiB
TypeScript
import { execSync } from "node:child_process";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import react from "@vitejs/plugin-react";
|
|
import { defineConfig } from "vitest/config";
|
|
|
|
// Node ≥25 默认启用实验性 webstorage:其全局 localStorage 在无有效存储文件时
|
|
// 会遮蔽 jsdom 注入的同名全局(localStorage.clear 变为非函数)。测试 worker
|
|
// 进程统一禁用,保证 jsdom 的 storage 生效。
|
|
process.env.NODE_OPTIONS = [
|
|
process.env.NODE_OPTIONS,
|
|
"--no-experimental-webstorage",
|
|
]
|
|
.filter(Boolean)
|
|
.join(" ");
|
|
|
|
// Get git commit hash
|
|
const getGitHash = () => {
|
|
try {
|
|
return execSync("git rev-parse --short HEAD").toString().trim();
|
|
} catch (e) {
|
|
console.log(e);
|
|
return "unknown";
|
|
}
|
|
};
|
|
|
|
const getVendorChunkName = (moduleId: string) => {
|
|
const normalizedId = moduleId.replace(/\\/g, "/");
|
|
|
|
if (!normalizedId.includes("/node_modules/")) {
|
|
return null;
|
|
}
|
|
|
|
const packagePath = normalizedId.split("/node_modules/").pop();
|
|
if (!packagePath) {
|
|
return null;
|
|
}
|
|
|
|
const packageName = packagePath.split("/")[0];
|
|
return packageName || null;
|
|
};
|
|
|
|
// https://vite.dev/config/
|
|
export default defineConfig({
|
|
base: "/",
|
|
define: {
|
|
"import.meta.env.VITE_GIT_HASH": JSON.stringify(getGitHash()),
|
|
},
|
|
plugins: [react()],
|
|
resolve: {
|
|
alias: {
|
|
"@": path.resolve(__dirname, "./src"),
|
|
},
|
|
},
|
|
server: {
|
|
https: {
|
|
key: fs.readFileSync("./.cert/key.pem"),
|
|
cert: fs.readFileSync("./.cert/cert.pem"),
|
|
},
|
|
proxy: {
|
|
"/api/v1/ws/server": {
|
|
target: "ws://localhost:8008",
|
|
changeOrigin: true,
|
|
ws: true,
|
|
},
|
|
"/api/v1/": {
|
|
target: "http://localhost:8008",
|
|
changeOrigin: true,
|
|
},
|
|
},
|
|
headers: {
|
|
"Cache-Control": "no-store",
|
|
Pragma: "no-cache",
|
|
},
|
|
},
|
|
test: {
|
|
environment: "jsdom",
|
|
environmentOptions: {
|
|
jsdom: {
|
|
url: "https://localhost/",
|
|
},
|
|
},
|
|
globals: true,
|
|
setupFiles: ["./src/test/setup.ts"],
|
|
css: true,
|
|
include: ["src/test/**/*.{test,spec}.{ts,tsx}"],
|
|
coverage: {
|
|
provider: "v8",
|
|
reporter: ["text", "html", "lcov"],
|
|
reportsDirectory: "./coverage",
|
|
thresholds: {
|
|
statements: 86,
|
|
branches: 74,
|
|
functions: 86,
|
|
lines: 86,
|
|
},
|
|
include: ["src/**/*.{ts,tsx}"],
|
|
exclude: [
|
|
"src/**/*.test.{ts,tsx}",
|
|
"src/**/*.spec.{ts,tsx}",
|
|
"src/test/**",
|
|
"src/types/**",
|
|
"src/main.tsx",
|
|
"src/i18n.js",
|
|
"src/vite-env.d.ts",
|
|
],
|
|
},
|
|
},
|
|
build: {
|
|
// Target older Safari versions (iOS 15/16) explicitly, since Vite's
|
|
// default "widely-available browsers" target only covers the latest
|
|
// two major Safari releases and would otherwise emit syntax that
|
|
// crashes on older WebKit engines (white screen on load).
|
|
target: ["es2020", "safari15"],
|
|
cssTarget: ["safari15"],
|
|
rolldownOptions: {
|
|
output: {
|
|
entryFileNames: `assets/[name].[hash].js`,
|
|
chunkFileNames: `assets/[name].[hash].js`,
|
|
assetFileNames: `assets/[name].[hash].[ext]`,
|
|
codeSplitting: {
|
|
groups: [
|
|
{
|
|
name: getVendorChunkName,
|
|
test: /[\\/]node_modules[\\/]/,
|
|
},
|
|
],
|
|
},
|
|
},
|
|
},
|
|
chunkSizeWarningLimit: 1500,
|
|
},
|
|
});
|