#前言
wordpress由于其中心化和插件化为管理带来了不少的便捷,但是面对一些奇奇怪怪的要求时候我们有可能找不到对应的插件,比如在一个全新的页面展示网站的运行状态(如下)

#教程
可以新建一个php页面来写,但为了效率建议在根目录放置css和js,优化体验。
#代码
:root {
--bg: #06090f;
--fg: #e6fffa;
--muted: #8aa5b7;
--card: rgba(10, 12, 16, 0.62);
--card-border: rgba(0, 255, 170, 0.08);
--shadow: 0 18px 48px rgba(0, 0, 0, 0.55);
--accent: #00ffaa;
--accent-soft: rgba(0, 255, 170, 0.14);
--grid-rgb: 0, 255, 170;
}
html,
body {
height: 100%;
margin: 0;
}
body {
background: var(--bg);
color: var(--fg);
font: 16px/1.6 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial;
transition: background 0.6s ease;
}
.status-wrapper {
position: relative;
min-height: 100%;
overflow: hidden;
}
.status-canvas {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
display: block;
z-index: 0;
}
.status-container {
position: relative;
z-index: 1;
max-width: 1100px;
margin: 64px auto;
padding: 0 20px 60px;
}
.status-header {
margin-bottom: 18px;
}
.status-title {
font-size: 30px;
margin: 0 0 6px 0;
font-weight: 600;
}
.theme-switcher {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.theme-switcher .label {
font-size: 13px;
color: var(--muted);
}
.theme-button {
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.06);
color: var(--fg);
padding: 6px 14px;
border-radius: 999px;
font-size: 13px;
cursor: pointer;
transition: all 0.2s ease;
backdrop-filter: blur(6px);
}
.theme-button:hover {
transform: translateY(-1px);
border-color: rgba(var(--grid-rgb), 0.4);
box-shadow: 0 0 18px rgba(var(--grid-rgb), 0.15);
}
.theme-button.active {
background: var(--accent-soft);
color: var(--accent);
border-color: rgba(var(--grid-rgb), 0.55);
box-shadow: 0 0 22px rgba(var(--grid-rgb), 0.2);
}
.custom-panel {
display: none;
gap: 14px;
padding: 14px 18px;
margin-bottom: 22px;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 18px;
backdrop-filter: blur(12px);
flex-wrap: wrap;
align-items: flex-end;
}
.custom-panel.active {
display: flex;
}
.custom-panel .field {
display: flex;
flex-direction: column;
min-width: 150px;
}
.custom-panel label {
font-size: 12px;
color: var(--muted);
margin-bottom: 6px;
}
.custom-panel input[type="color"] {
width: 52px;
height: 32px;
border: none;
border-radius: 8px;
cursor: pointer;
background: none;
padding: 0;
}
.custom-panel select,
.custom-panel input[type="range"],
.custom-panel input[type="number"] {
background: rgba(0, 0, 0, 0.25);
color: var(--fg);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 8px;
padding: 6px 10px;
font-size: 13px;
}
.custom-panel input[type="range"] {
width: 160px;
height: 30px;
padding: 0;
margin-top: 4px;
}
.custom-panel .hint {
font-size: 12px;
color: var(--muted);
flex: 1 1 100%;
margin-top: 4px;
}
.status-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 18px;
}
.status-card {
background: var(--card);
backdrop-filter: blur(14px);
border-radius: 16px;
padding: 18px 20px;
box-shadow: var(--shadow);
border: 1px solid var(--card-border);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.status-card:hover {
transform: translateY(-4px);
box-shadow: 0 22px 55px rgba(0, 0, 0, 0.35);
}
.status-label {
font-size: 13px;
color: var(--muted);
}
.status-value {
font-size: 22px;
font-weight: 500;
margin-top: 8px;
}
.status-bar {
height: 6px;
background: rgba(255, 255, 255, 0.08);
border-radius: 999px;
overflow: hidden;
margin-top: 12px;
}
.status-bar-fill {
height: 100%;
background: linear-gradient(90deg, var(--accent), rgba(var(--grid-rgb), 0.45));
}
.status-badge {
display: inline-block;
padding: 3px 10px;
border-radius: 999px;
background: var(--accent-soft);
color: var(--accent);
font-size: 12px;
font-weight: 500;
letter-spacing: 0.02em;
}
.status-footer {
margin-top: 28px;
color: var(--muted);
font-size: 13px;
text-align: right;
}
body::before,
.status-wrapper::before {
content: "";
position: absolute;
inset: 0;
background: radial-gradient(1200px 580px at 50% 18%, rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.82));
z-index: 0;
transition: background 0.6s ease;
}
body[data-theme="cyber"] {
--bg: #06090f;
--fg: #e6fffa;
--muted: #8aa5b7;
--card: rgba(10, 12, 16, 0.62);
--card-border: rgba(0, 255, 170, 0.08);
--shadow: 0 18px 48px rgba(0, 0, 0, 0.55);
--accent: #00ffaa;
--accent-soft: rgba(0, 255, 170, 0.14);
--grid-rgb: 0, 255, 170;
}
body[data-theme="aurora"] {
--bg: #050720;
--fg: #f5f7ff;
--muted: #a9b8ff;
--card: rgba(18, 20, 45, 0.68);
--card-border: rgba(138, 165, 255, 0.12);
--shadow: 0 22px 50px rgba(12, 18, 48, 0.5);
--accent: #7af5ff;
--accent-soft: rgba(122, 245, 255, 0.16);
--grid-rgb: 122, 245, 255;
}
body[data-theme="dusk"] {
--bg: #050308;
--fg: #fff5ff;
--muted: #d8b0ff;
--card: rgba(26, 12, 30, 0.7);
--card-border: rgba(255, 86, 201, 0.14);
--shadow: 0 22px 55px rgba(12, 6, 18, 0.55);
--accent: #ff56c9;
--accent-soft: rgba(255, 86, 201, 0.16);
--grid-rgb: 255, 86, 201;
}
body[data-theme="ocean"] {
--bg: #02090f;
--fg: #e6fbff;
--muted: #8ac4d9;
--card: rgba(10, 30, 45, 0.64);
--card-border: rgba(90, 200, 255, 0.16);
--shadow: 0 22px 55px rgba(0, 20, 30, 0.52);
--accent: #00b3ff;
--accent-soft: rgba(0, 179, 255, 0.18);
--grid-rgb: 0, 179, 255;
}
body[data-theme="sunset"] {
--bg: #120207;
--fg: #fff4f1;
--muted: #ffbdb3;
--card: rgba(45, 11, 24, 0.68);
--card-border: rgba(255, 120, 120, 0.16);
--shadow: 0 24px 58px rgba(28, 6, 12, 0.55);
--accent: #ff6b6b;
--accent-soft: rgba(255, 107, 107, 0.18);
--grid-rgb: 255, 107, 107;
}
body[data-theme="matrix"] {
--bg: #020409;
--fg: #dfffee;
--muted: #7cd9ba;
--card: rgba(8, 18, 18, 0.7);
--card-border: rgba(0, 255, 170, 0.18);
--shadow: 0 24px 60px rgba(0, 20, 20, 0.55);
--accent: #00f0a8;
--accent-soft: rgba(0, 240, 168, 0.18);
--grid-rgb: 0, 240, 168;
}
body[data-theme="starfire"] {
--bg: #03051a;
--fg: #f4faff;
--muted: #9ecbff;
--card: rgba(12, 18, 45, 0.7);
--card-border: rgba(100, 170, 255, 0.14);
--shadow: 0 24px 60px rgba(3, 8, 30, 0.55);
--accent: #6fe0ff;
--accent-soft: rgba(111, 224, 255, 0.2);
--grid-rgb: 111, 224, 255;
}
body[data-theme="custom"] {
--bg: #050810;
--fg: #e7faff;
--muted: #a0bfd1;
--card: rgba(12, 18, 28, 0.7);
--card-border: rgba(255, 255, 255, 0.12);
--shadow: 0 24px 58px rgba(5, 10, 16, 0.55);
}
.status-wrapper::before {
pointer-events: none;
}
.status-footer a {
color: inherit;
}
@media (max-width: 720px) {
.status-container {
margin: 32px auto;
padding: 0 16px 40px;
}
.status-title {
font-size: 26px;
}
}
(function () {
const CONFIG_ELEMENT_ID = 'status-theme-config';
const CANVAS_ID = 'statusCanvas';
const STORAGE_THEME_KEY = 'statusTheme';
const STORAGE_CUSTOM_KEY = 'statusCustom';
const themePayload = document.getElementById(CONFIG_ELEMENT_ID);
if (!themePayload) {
console.warn('[status-theme] missing config payload');
return;
}
let config;
try {
config = JSON.parse(themePayload.textContent || '{}');
} catch (err) {
console.error('[status-theme] failed to parse config', err);
return;
}
const canvas = document.getElementById(CANVAS_ID);
if (!canvas) {
console.warn('[status-theme] missing canvas element');
return;
}
const ctx = canvas.getContext('2d');
const themeButtons = Array.from(document.querySelectorAll('.theme-button'));
const customPanel = document.getElementById('customPanel');
const customInputs = {
accent: document.getElementById('customAccent'),
mode: document.getElementById('customMode'),
density: document.getElementById('customDensity'),
speed: document.getElementById('customSpeed')
};
const presets = config.presets || {};
const customDefaults = config.customDefaults || {
accent: '#00ffaa',
mode: 'pixelFlow',
density: 72,
speed: 100
};
const state = {
themeId: config.activeTheme || 'cyber',
preset: null,在·
mode: 'pixelFlow',
dotSize: 2,
density: 72,
speed: 1,
amplitude: 0.6,
accentRGB: '0,255,170',
custom: loadCustomSettings(),
animating: false,
frame: 0,
elements: [],
extra: {}
};
let animationId = null;
let dpr = window.devicePixelRatio || 1;
// -------------------- Utility --------------------
function loadCustomSettings() {
try {
const stored = localStorage.getItem(STORAGE_CUSTOM_KEY);
if (stored) {
return Object.assign({}, customDefaults, JSON.parse(stored));
}
} catch (err) {
console.warn('[status-theme] unable to read custom settings', err);
}
return Object.assign({}, customDefaults);
}
function persistCustomSettings() {
try {
localStorage.setItem(STORAGE_CUSTOM_KEY, JSON.stringify(state.custom));
} catch (err) {
console.warn('[status-theme] unable to persist custom settings', err);
}
}
function persistTheme(themeId) {
try {
localStorage.setItem(STORAGE_THEME_KEY, themeId);
} catch (err) {
console.warn('[status-theme] unable to persist theme', err);
}
}
function hexToRgb(hex) {
const cleaned = (hex || '').replace('#', '').trim();
if (cleaned.length === 3) {
return cleaned.split('').map(ch => parseInt(ch + ch, 16));
}
if (cleaned.length === 6) {
return [cleaned.slice(0, 2), cleaned.slice(2, 4), cleaned.slice(4, 6)].map(part => parseInt(part, 16));
}
return [0, 255, 170];
}
function computeAccentRGB() {
const style = getComputedStyle(document.body);
let rgb = style.getPropertyValue('--grid-rgb').trim();
if (rgb) {
return rgb;
}
const accent = style.getPropertyValue('--accent').trim();
if (accent && accent.startsWith('#')) {
const [r, g, b] = hexToRgb(accent);
return `${r},${g},${b}`;
}
return '0,255,170';
}
function setCustomVariables(settings) {
const [r, g, b] = hexToRgb(settings.accent || '#00ffaa');
document.body.style.setProperty('--accent', settings.accent || '#00ffaa');
document.body.style.setProperty('--accent-soft', `rgba(${r},${g},${b},0.16)`);
document.body.style.setProperty('--grid-rgb', `${r},${g},${b}`);
}
function clearCustomVariables() {
['--accent', '--accent-soft', '--grid-rgb'].forEach(prop => document.body.style.removeProperty(prop));
}
function refreshCanvasSize() {
dpr = window.devicePixelRatio || 1;
canvas.width = Math.round(window.innerWidth * dpr);
canvas.height = Math.round(window.innerHeight * dpr);
rebuildElements();
}
function rebuildElements() {
const width = canvas.width;
const height = canvas.height;
const densityScale = (state.density || 70) / 70;
state.elements = [];
state.extra = {};
if (state.mode === 'pixelFlow') {
const spacing = Math.max(8, 28 - densityScale * 10);
const cols = Math.ceil(width / spacing);
const rows = Math.ceil(height / spacing);
for (let y = 0; y <= rows; y++) {
for (let x = 0; x <= cols; x++) {
state.elements.push({
x: x * spacing,
y: y * spacing,
seedX: Math.random() * 1000,
seedY: Math.random() * 1000
});
}
}
} else if (state.mode === 'matrix') {
const columns = Math.max(28, Math.round(50 * densityScale));
const columnWidth = width / columns;
state.extra.columns = [];
for (let i = 0; i < columns; i++) {
state.extra.columns.push({
x: i * columnWidth + columnWidth * 0.5,
y: -Math.random() * height,
length: (Math.random() * 0.5 + 0.4) * height,
speed: (Math.random() * 0.5 + 0.6) * state.speed * 60
});
}
} else if (state.mode === 'stars') {
const count = Math.round(180 * densityScale);
for (let i = 0; i < count; i++) {
state.elements.push({
x: Math.random() * width,
y: Math.random() * height,
size: (Math.random() * 1.6 + 0.5) * dpr,
speed: (Math.random() * 0.4 + 0.15) * state.speed * 40,
phase: Math.random() * Math.PI * 2
});
}
} else if (state.mode === 'waves') {
const waveCount = Math.max(2, Math.round(3 * densityScale));
state.elements = new Array(waveCount).fill(null).map((_, index) => ({
amplitude: (40 + 20 * index) * state.amplitude,
wavelength: 0.0015 + index * 0.0006,
speed: (0.3 + index * 0.12) * state.speed,
phase: Math.random() * Math.PI * 2,
alpha: 0.18 + index * 0.12
}));
} else { // dots fallback
const spacing = Math.max(10, 32 - densityScale * 12);
const cols = Math.ceil(width / spacing);
const rows = Math.ceil(height / spacing);
for (let y = 0; y <= rows; y++) {
for (let x = 0; x <= cols; x++) {
state.elements.push({
x: x * spacing,
y: y * spacing,
offset: Math.random() * 100
});
}
}
}
}
// -------------------- Noise --------------------
function FlowNoise(seed) {
const perm = new Uint8Array(512);
const gradX = new Float32Array(512);
const gradY = new Float32Array(512);
let s = seed || Date.now();
function random() {
s = (s * 1664525 + 1013904223) % 4294967296;
return s / 4294967296;
}
const gradients = [
[1, 0], [-1, 0], [0, 1], [0, -1],
[Math.SQRT1_2, Math.SQRT1_2], [-Math.SQRT1_2, Math.SQRT1_2],
[Math.SQRT1_2, -Math.SQRT1_2], [-Math.SQRT1_2, -Math.SQRT1_2]
];
for (let i = 0; i < 256; i++) {
const g = gradients[i % gradients.length];
gradX[i] = g[0];
gradY[i] = g[1];
perm[i] = i;
}
for (let i = 255; i > 0; i--) {
const j = Math.floor(random() * (i + 1));
const tmp = perm[i];
perm[i] = perm[j];
perm[j] = tmp;
}
for (let i = 0; i < 256; i++) {
perm[i + 256] = perm[i];
gradX[i + 256] = gradX[i];
gradY[i + 256] = gradY[i];
}
function fade(t) {
return t * t * t * (t * (t * 6 - 15) + 10);
}
function lerp(a, b, t) {
return a + t * (b - a);
}
function grad(hash, x, y) {
return gradX[hash] * x + gradY[hash] * y;
}
this.noise = function (x, y) {
const ix = Math.floor(x) & 255;
const iy = Math.floor(y) & 255;
const fx = x - Math.floor(x);
const fy = y - Math.floor(y);
const u = fade(fx);
const v = fade(fy);
const aa = perm[ix + perm[iy]];
const ab = perm[ix + perm[iy + 1]];
const ba = perm[ix + 1 + perm[iy]];
const bb = perm[ix + 1 + perm[iy + 1]];
const x1 = lerp(grad(aa, fx, fy), grad(ba, fx - 1, fy), u);
const x2 = lerp(grad(ab, fx, fy - 1), grad(bb, fx - 1, fy - 1), u);
return lerp(x1, x2, v);
};
}
const flowNoise = new FlowNoise(config.noiseSeed || 20251107);
// -------------------- Animation --------------------
function renderFrame(timestamp) {
const width = canvas.width;
const height = canvas.height;
const accent = state.accentRGB;
ctx.clearRect(0, 0, width, height);
const time = timestamp * 0.001 * state.speed;
if (state.mode === 'pixelFlow') {
const dotSize = (state.dotSize || 2) * dpr;
const amplitude = Math.max(12, state.amplitude * 35);
for (let i = 0; i < state.elements.length; i++) {
const dot = state.elements[i];
const flow = flowNoise.noise(dot.seedX + time * 0.35, dot.seedY + time * 0.35);
const flow2 = flowNoise.noise(dot.seedX - time * 0.28, dot.seedY + time * 0.22);
const px = dot.x * dpr + flow * amplitude;
const py = dot.y * dpr + flow2 * amplitude;
const brightness = 0.35 + 0.45 * Math.sin(time * 2 + dot.seedX);
ctx.fillStyle = `rgba(${accent}, ${0.08 + 0.45 * brightness})`;
ctx.fillRect(px, py, dotSize, dotSize);
}
} else if (state.mode === 'matrix' && state.extra.columns) {
ctx.fillStyle = 'rgba(0,0,0,0.12)';
ctx.fillRect(0, 0, width, height);
const colWidth = width / (state.extra.columns.length || 1);
for (let i = 0; i < state.extra.columns.length; i++) {
const column = state.extra.columns[i];
column.y += column.speed;
if (column.y - column.length > height) {
column.y = -Math.random() * height * 0.4;
column.length = (Math.random() * 0.4 + 0.3) * height;
column.speed = (Math.random() * 0.5 + 0.6) * state.speed * 60;
}
const x = column.x * dpr;
const grad = ctx.createLinearGradient(x, (column.y - column.length) * dpr, x, column.y * dpr);
grad.addColorStop(0, `rgba(${accent},0)`);
grad.addColorStop(0.2, `rgba(${accent},0.15)`);
grad.addColorStop(0.6, `rgba(${accent},0.55)`);
grad.addColorStop(1, `rgba(${accent},0)`);
ctx.fillStyle = grad;
ctx.fillRect(
x,
(column.y - column.length) * dpr,
Math.max(2, state.dotSize * dpr * 1.4),
column.length * dpr
);
}
} else if (state.mode === 'stars') {
for (let i = 0; i < state.elements.length; i++) {
const star = state.elements[i];
star.phase += 0.02 * state.speed;
const flick = 0.45 + 0.5 * Math.sin(time * 2 + star.phase);
ctx.beginPath();
ctx.fillStyle = `rgba(${accent}, ${0.15 + flick * 0.6})`;
ctx.arc(star.x * dpr, star.y * dpr, star.size, 0, Math.PI * 2);
ctx.fill();
star.y += star.speed * 0.016;
if (star.y > height + 10) {
star.y = -Math.random() * 60;
star.x = Math.random() * width;
}
}
} else if (state.mode === 'waves') {
ctx.lineWidth = Math.max(1.2 * dpr, 0.8);
for (let i = 0; i < state.elements.length; i++) {
const wave = state.elements[i];
wave.phase += 0.6 * wave.speed * 0.016;
ctx.beginPath();
ctx.strokeStyle = `rgba(${accent},${wave.alpha})`;
const segment = 6 * dpr;
for (let x = 0; x <= width; x += segment) {
const y = height * 0.5 + Math.sin(x * wave.wavelength + time * wave.speed + wave.phase) * wave.amplitude;
const px = x;
const py = y * dpr;
if (x === 0) ctx.moveTo(px, py);
else ctx.lineTo(px, py);
}
ctx.stroke();
}
} else { // dots fallback
const dotSize = (state.dotSize || 2) * dpr;
const amplitude = Math.max(10, state.amplitude * 30);
for (let i = 0; i < state.elements.length; i++) {
const dot = state.elements[i];
const nx = dot.x * dpr + Math.sin(time + dot.offset) * amplitude;
const ny = dot.y * dpr + Math.cos(time * 1.2 + dot.offset) * amplitude;
const flick = 0.4 + 0.4 * Math.sin(time * 2 + dot.offset);
ctx.fillStyle = `rgba(${accent}, ${0.08 + 0.4 * flick})`;
ctx.fillRect(nx, ny, dotSize, dotSize);
}
}
animationId = requestAnimationFrame(renderFrame);
}
function stopAnimation() {
if (animationId) {
cancelAnimationFrame(animationId);
animationId = null;
}
}
function startAnimation() {
stopAnimation();
animationId = requestAnimationFrame(renderFrame);
}
function applyTheme(themeId, persist = false) {
if (!presets[themeId]) {
themeId = 'cyber';
}
const preset = Object.assign({}, presets[themeId]);
if (preset.customizable) {
customPanel?.classList.add('active');
setCustomVariables(state.custom);
syncCustomInputs();
preset.mode = state.custom.mode || preset.mode;
preset.density = Number(state.custom.density || preset.density || 70);
const speedFactor = (state.custom.speed || 100) / 100;
preset.speed = (preset.speed || 1) * speedFactor;
} else {
customPanel?.classList.remove('active');
clearCustomVariables();
}
state.themeId = themeId;
state.preset = preset;
state.mode = preset.mode || 'pixelFlow';
state.density = preset.density || 70;
state.speed = preset.speed || 1;
state.dotSize = preset.dotSize || 2;
state.amplitude = preset.amplitude || 0.6;
state.accentRGB = computeAccentRGB();
themeButtons.forEach(btn => btn.classList.toggle('active', btn.dataset.theme === themeId));
if (persist) {
persistTheme(themeId);
}
refreshCanvasSize();
startAnimation();
}
function syncCustomInputs() {
if (!customPanel) return;
if (customInputs.accent) customInputs.accent.value = state.custom.accent || customDefaults.accent;
if (customInputs.mode) customInputs.mode.value = state.custom.mode || customDefaults.mode;
if (customInputs.density) customInputs.density.value = state.custom.density || customDefaults.density;
if (customInputs.speed) customInputs.speed.value = state.custom.speed || customDefaults.speed;
}
function handleCustomChange() {
if (!customPanel) return;
if (customInputs.accent) state.custom.accent = customInputs.accent.value;
if (customInputs.mode) state.custom.mode = customInputs.mode.value;
if (customInputs.density) state.custom.density = Number(customInputs.density.value);
if (customInputs.speed) state.custom.speed = Number(customInputs.speed.value);
persistCustomSettings();
if (state.themeId === 'custom') {
setCustomVariables(state.custom);
applyTheme('custom', true);
}
}
// -------------------- Init --------------------
themeButtons.forEach(button => {
button.addEventListener('click', () => {
applyTheme(button.dataset.theme, true);
});
});
if (customPanel) {
Object.values(customInputs).forEach(input => {
if (!input) return;
input.addEventListener('change', handleCustomChange);
if (input.type === 'range' || input.type === 'color') {
input.addEventListener('input', handleCustomChange);
}
});
}
window.addEventListener('resize', () => {
clearTimeout(state.resizeTimer);
state.resizeTimer = setTimeout(refreshCanvasSize, 120);
});
let initialTheme = state.themeId;
try {
const stored = localStorage.getItem(STORAGE_THEME_KEY);
if (stored && presets[stored]) {
initialTheme = stored;
}
} catch (err) {
console.warn('[status-theme] unable to read stored theme', err);
}
applyTheme(initialTheme, false);
})();
<?php
// 独立网站状态页(不依赖插件)。将本文件放在 WordPress 根目录,并通过 https://你的域名/site-status.php 访问。
// 可选:加载 WordPress 环境以便读取数据库信息与版本(若不需要可注释掉)。
$wp_bootstrap_loaded = false;
$wp_root = __DIR__ . DIRECTORY_SEPARATOR;
if (file_exists($wp_root . 'wp-load.php')) {
require_once $wp_root . 'wp-load.php';
$wp_bootstrap_loaded = true;
}
// ------------------------- 指标采集 -------------------------
function ss_safe_shell($cmd) {
if (!function_exists('shell_exec') || ini_get('safe_mode')) return null;
return @shell_exec($cmd);
}
function ss_path_allowed($path) {
$restrictions = ini_get('open_basedir');
if (empty($restrictions)) return true;
$path_real = @realpath($path);
$path_check = $path_real ? $path_real : $path;
$paths = preg_split('/[:;]/', $restrictions);
foreach ($paths as $allowed) {
$allowed = rtrim($allowed);
if ($allowed === '') continue;
$allowed_real = @realpath($allowed);
if ($allowed_real && strncasecmp($path_check, $allowed_real, strlen($allowed_real)) === 0) {
return true;
}
}
return false;
}
function ss_format_bytes($bytes, $precision = 2) {
$units = array('B','KB','MB','GB','TB');
for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) {
$bytes /= 1024;
}
return round($bytes, $precision).' '.$units[$i];
}
function ss_cpu_usage() {
$os = strtolower(PHP_OS);
if (strpos($os, 'linux') !== false && ss_path_allowed('/proc/stat') && is_readable('/proc/stat')) {
$stat1 = @file('/proc/stat');
usleep(200000);
$stat2 = @file('/proc/stat');
if ($stat1 && $stat2) {
$info1 = preg_split('/\s+/', trim($stat1[0]));
$info2 = preg_split('/\s+/', trim($stat2[0]));
$idle1 = (int)$info1[4];
$total1 = array_sum(array_map('intval', array_slice($info1, 1)));
$idle2 = (int)$info2[4];
$total2 = array_sum(array_map('intval', array_slice($info2, 1)));
$diff_total = max(1, $total2 - $total1);
$diff_idle = max(0, $idle2 - $idle1);
$usage = (1 - $diff_idle / $diff_total) * 100;
return round($usage, 1);
}
}
$out = ss_safe_shell('wmic cpu get LoadPercentage');
if ($out) {
foreach (explode("\n", $out) as $line) {
$line = trim($line);
if (is_numeric($line)) return (float)$line;
}
}
return null;
}
function ss_loadavg() {
if (function_exists('sys_getloadavg')) {
$l = sys_getloadavg();
return implode(', ', $l);
}
$out = ss_safe_shell('wmic cpu get LoadPercentage');
if ($out) {
foreach (explode("\n", $out) as $line) {
$line = trim($line);
if (is_numeric($line)) {
$c = 4;
$v = (float)$line / 100 * $c;
return sprintf('%.2f, %.2f, %.2f', $v, $v, $v);
}
}
}
return 'N/A';
}
function ss_memory() {
$os = strtolower(PHP_OS);
if (strpos($os, 'linux') !== false && ss_path_allowed('/proc/meminfo') && is_readable('/proc/meminfo')) {
$meminfo = file('/proc/meminfo');
$vals = array();
foreach ($meminfo as $row) {
if (preg_match('/^(\w+):\s+(\d+)/', $row, $m)) {
$vals[$m[1]] = (int)$m[2];
}
}
if (isset($vals['MemTotal']) && isset($vals['MemAvailable'])) {
$total = $vals['MemTotal'] * 1024;
$avail = $vals['MemAvailable'] * 1024;
$used = $total - $avail;
$percent = $total > 0 ? round($used / $total * 100) : 0;
return array(
'total' => $total,
'used' => $used,
'percent' => $percent,
'total_f' => ss_format_bytes($total),
'used_f' => ss_format_bytes($used)
);
}
}
$out = ss_safe_shell('wmic OS get FreePhysicalMemory,TotalVisibleMemorySize /Value');
if ($out && preg_match('/TotalVisibleMemorySize=(\d+)/', $out, $t) && preg_match('/FreePhysicalMemory=(\d+)/', $out, $f)) {
$total = $t[1] * 1024;
$free = $f[1] * 1024;
$used = $total - $free;
$p = $total > 0 ? round($used / $total * 100) : 0;
return array(
'total' => $total,
'used' => $used,
'percent' => $p,
'total_f' => ss_format_bytes($total),
'used_f' => ss_format_bytes($used)
);
}
$limit = ini_get('memory_limit');
$limit_bytes = 0;
if ($limit && $limit !== '-1') {
$unit = strtolower(substr($limit, -1));
$value = (int)$limit;
switch ($unit) {
case 'g': $value *= 1024;
case 'm': $value *= 1024;
case 'k': $value *= 1024; break;
}
$limit_bytes = $value;
}
$used_bytes = function_exists('memory_get_usage') ? memory_get_usage(true) : 0;
$percent = $limit_bytes > 0 ? round($used_bytes / $limit_bytes * 100) : 0;
return array(
'total' => $limit_bytes,
'used' => $used_bytes,
'percent' => max(0, min(100, $percent)),
'total_f' => $limit_bytes > 0 ? ss_format_bytes($limit_bytes) : 'N/A',
'used_f' => $used_bytes > 0 ? ss_format_bytes($used_bytes) : 'N/A'
);
}
function ss_disk() {
$root = __DIR__;
$total = @disk_total_space($root);
$free = @disk_free_space($root);
if ($total !== false && $free !== false) {
$used = $total - $free;
$p = $total > 0 ? round($used / $total * 100) : 0;
return array(
'total' => $total,
'used' => $used,
'percent' => $p,
'total_f' => ss_format_bytes($total),
'used_f' => ss_format_bytes($used)
);
}
return array('total' => 0, 'used' => 0, 'percent' => 0, 'total_f' => 'N/A', 'used_f' => 'N/A');
}
function ss_db() {
$res = array('connected' => false, 'size_f' => 'N/A', 'version' => 'N/A');
if (!defined('DB_NAME')) return $res;
global $wpdb;
try {
if (isset($wpdb) && $wpdb->check_connection()) {
$res['connected'] = true;
$size = $wpdb->get_var($wpdb->prepare("SELECT SUM(data_length+index_length) FROM information_schema.TABLES WHERE table_schema=%s", DB_NAME));
if ($size) $res['size_f'] = ss_format_bytes((int)$size);
$res['version'] = $wpdb->db_version();
}
} catch (Exception $e) {}
return $res;
}
$metrics = array(
'cpu' => ss_cpu_usage(),
'load' => ss_loadavg(),
'mem' => ss_memory(),
'disk' => ss_disk(),
'db' => $wp_bootstrap_loaded ? ss_db() : array('connected' => false, 'size_f' => 'N/A', 'version' => 'N/A'),
'wp_version' => $wp_bootstrap_loaded ? get_bloginfo('version') : 'N/A',
'php_version' => PHP_VERSION,
);
$theme_presets = array(
'cyber' => array('label' => '赛博矩阵', 'mode' => 'pixelFlow', 'speed' => 1.1, 'dotSize' => 2, 'amplitude' => 0.65, 'density' => 72),
'aurora' => array('label' => '极光幻境', 'mode' => 'pixelFlow', 'speed' => 0.85, 'dotSize' => 2.4, 'amplitude' => 0.9, 'density' => 64),
'matrix' => array('label' => '霓虹矩阵', 'mode' => 'matrix', 'speed' => 1.35, 'dotSize' => 2.3, 'amplitude' => 0.75, 'density' => 88),
'starfire' => array('label' => '星火宇宙', 'mode' => 'stars', 'speed' => 0.7, 'dotSize' => 2.6, 'amplitude' => 0.45, 'density' => 82),
'ocean' => array('label' => '深海蓝潮', 'mode' => 'waves', 'speed' => 0.95, 'amplitude' => 1.1, 'density' => 68),
'custom' => array('label' => '自定义', 'mode' => 'pixelFlow', 'speed' => 1, 'dotSize' => 2, 'amplitude' => 0.6, 'density' => 72, 'customizable' => true),
);
$requested_theme = isset($_GET['style']) ? preg_replace('/[^a-z0-9_-]/i', '', $_GET['style']) : '';
$active_theme = $requested_theme && isset($theme_presets[$requested_theme]) ? $requested_theme : 'cyber';
$theme_config = array(
'presets' => $theme_presets,
'activeTheme' => $active_theme,
'customDefaults' => array(
'accent' => '#00ffaa',
'mode' => 'pixelFlow',
'density' => 72,
'speed' => 100
),
'noiseSeed' => 20251107
);
$theme_config_json = json_encode($theme_config, JSON_UNESCAPED_UNICODE);
?>
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>网站运行状态</title>
<link rel="stylesheet" href="site-status-assets/css/status-themes.css?v=1.0">
</head>
<body data-theme="<?php echo htmlspecialchars($active_theme, ENT_QUOTES, 'UTF-8'); ?>">
<div class="status-wrapper">
<canvas class="status-canvas" id="statusCanvas"></canvas>
<div class="status-container">
<div class="status-header">
<h1 class="status-title">Site Status Dashboard</h1>
<div class="status-label">Last updated: <?php echo date('Y-m-d H:i:s'); ?></div>
</div>
<?php if (count($theme_presets) > 1): ?>
<div class="theme-switcher" role="group" aria-label="Theme selector">
<span class="label">主题风格:</span>
<?php foreach ($theme_presets as $theme_id => $theme_info): ?>
<button type="button" class="theme-button<?php echo $theme_id === $active_theme ? ' active' : ''; ?>" data-theme="<?php echo htmlspecialchars($theme_id, ENT_QUOTES, 'UTF-8'); ?>">
<?php echo htmlspecialchars($theme_info['label'], ENT_QUOTES, 'UTF-8'); ?>
</button>
<?php endforeach; ?>
</div>
<?php endif; ?>
<div id="customPanel" class="custom-panel">
<div class="field">
<label for="customAccent">主题主色</label>
<input type="color" id="customAccent" value="#00ffaa">
</div>
<div class="field">
<label for="customMode">背景动效</label>
<select id="customMode">
<option value="pixelFlow">像素流场</option>
<option value="matrix">霓虹下落</option>
<option value="stars">星轨闪烁</option>
<option value="waves">流光曲线</option>
<option value="dots">柔和粒子</option>
</select>
</div>
<div class="field">
<label for="customDensity">动效密度</label>
<input type="range" id="customDensity" min="40" max="120" value="72">
</div>
<div class="field" style="min-width:200px;">
<label for="customSpeed">速度倍数</label>
<input type="range" id="customSpeed" min="40" max="160" value="100">
</div>
<div class="hint">提示:切换到“自定义”主题后,上述参数将立即生效并保存。</div>
</div>
<div class="status-cards">
<div class="status-card">
<div class="status-label">Server Load</div>
<div class="status-value"><?php echo htmlspecialchars($metrics['load']); ?></div>
<div class="status-label" style="margin-top:8px">CPU</div>
<div class="status-bar"><div class="status-bar-fill" style="width: <?php echo (float)($metrics['cpu'] ?? 0); ?>%"></div></div>
<div class="status-label"><?php echo (float)($metrics['cpu'] ?? 0); ?>%</div>
</div>
<div class="status-card">
<div class="status-label">Memory Usage</div>
<div class="status-value"><?php echo $metrics['mem']['used_f']; ?> / <?php echo $metrics['mem']['total_f']; ?></div>
<div class="status-bar"><div class="status-bar-fill" style="width: <?php echo (int)$metrics['mem']['percent']; ?>%"></div></div>
</div>
<div class="status-card">
<div class="status-label">Disk Usage</div>
<div class="status-value"><?php echo $metrics['disk']['used_f']; ?> / <?php echo $metrics['disk']['total_f']; ?></div>
<div class="status-bar"><div class="status-bar-fill" style="width: <?php echo (int)$metrics['disk']['percent']; ?>%"></div></div>
</div>
<div class="status-card">
<div class="status-label">Database</div>
<div class="status-value"><?php echo $metrics['db']['connected'] ? '<span class="status-badge">Connected</span>' : '<span class="status-badge" style="background:#402728;color:#ffd0d0">Disconnected</span>'; ?></div>
<div class="status-label" style="margin-top:6px">Size: <?php echo htmlspecialchars($metrics['db']['size_f']); ?> | MySQL: <?php echo htmlspecialchars($metrics['db']['version']); ?></div>
</div>
<div class="status-card">
<div class="status-label">Environment</div>
<div class="status-value">PHP <?php echo htmlspecialchars($metrics['php_version']); ?><?php if ($metrics['wp_version'] !== 'N/A') { echo ' · WordPress ' . $metrics['wp_version']; } ?></div>
</div>
</div>
<div class="status-footer">
Powered by standalone status page · <?php echo $_SERVER['HTTP_HOST']; ?> |
<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener">苏ICP备2025217910号-1</a>
</div>
</div>
</div>
<script type="application/json" id="status-theme-config"><?php echo $theme_config_json; ?></script>
<script src="site-status-assets/js/status-themes.js?v=1.0" defer></script>
</body>
</html>
Comments NOTHING