mirror of
https://github.com/Jerryplusy/rc-plugin.git
synced 2025-10-14 08:09:19 +00:00
✨feat: 添加一些网页
This commit is contained in:
parent
0c03a3027f
commit
fddc036832
11
package.json
11
package.json
@ -5,8 +5,17 @@
|
||||
"dependencies": {
|
||||
"axios": "^1.3.4",
|
||||
"form-data": "^4.0.1",
|
||||
"next": "^14.2.16",
|
||||
"node-id3": "^0.2.6",
|
||||
"p-queue": "^8.0.1",
|
||||
"qrcode": "^1.5.3",
|
||||
"p-queue": "^8.0.1"
|
||||
"react": "^18.3.1",
|
||||
"react-circular-progressbar": "^2.1.0",
|
||||
"react-dom": "^18.3.1",
|
||||
"systeminformation": "^5.23.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"daisyui": "^4.12.14",
|
||||
"tailwindcss": "^3.4.14"
|
||||
}
|
||||
}
|
||||
|
9
server/app/layout.jsx
Normal file
9
server/app/layout.jsx
Normal file
@ -0,0 +1,9 @@
|
||||
import "../styles/global.css";
|
||||
|
||||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
)
|
||||
}
|
3
server/app/page.jsx
Normal file
3
server/app/page.jsx
Normal file
@ -0,0 +1,3 @@
|
||||
export default function Page() {
|
||||
return <h1><a href="/r">进入控制面板</a></h1>
|
||||
}
|
10
server/app/r/api/napcat/[pid]/route.js
Normal file
10
server/app/r/api/napcat/[pid]/route.js
Normal file
@ -0,0 +1,10 @@
|
||||
import axiosInstance from "../../../../../utils/axiosInstance.js";
|
||||
|
||||
export async function GET(request, { params }) {
|
||||
const { pid } = params;
|
||||
const napcatResp = await axiosInstance.get(`/${ pid }`);
|
||||
return new Response(JSON.stringify(napcatResp), {
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
25
server/app/r/api/network/route.js
Normal file
25
server/app/r/api/network/route.js
Normal file
@ -0,0 +1,25 @@
|
||||
import axios from "axios";
|
||||
|
||||
export async function GET(request) {
|
||||
const url = new URL(request.url); // 获取请求的 URL
|
||||
const targetUrl = url.searchParams.get("url"); // 从查询参数中获取目标 URL
|
||||
const start = Date.now(); // 记录请求开始时间
|
||||
|
||||
try {
|
||||
await axios.get(targetUrl);
|
||||
// 计算结束时间减去开始时间
|
||||
return new Response(JSON.stringify({
|
||||
time: Date.now() - start
|
||||
}), {
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
} catch (error) {
|
||||
return new Response(JSON.stringify({
|
||||
time: 0
|
||||
}), {
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
})
|
||||
}
|
||||
}
|
60
server/app/r/api/system/route.js
Normal file
60
server/app/r/api/system/route.js
Normal file
@ -0,0 +1,60 @@
|
||||
import si from 'systeminformation';
|
||||
import os from 'os';
|
||||
|
||||
export async function GET(request, { params }) {
|
||||
try {
|
||||
// 获取CPU信息
|
||||
const cpuInfo = await si.cpu();
|
||||
const cpuUsage = await si.currentLoad();
|
||||
const totalCpuCores = cpuInfo.cores;
|
||||
const cpuCoresUsed = ((cpuUsage.currentLoad / 100) * totalCpuCores).toFixed(1); // 使用的核心数
|
||||
|
||||
// 获取内存信息
|
||||
const totalMemory = (os.totalmem() / (1024 ** 3)).toFixed(2); // 转换为 GB
|
||||
const freeMemory = (os.freemem() / (1024 ** 3)).toFixed(2); // 转换为 GB
|
||||
const usedMemory = (totalMemory - freeMemory).toFixed(2);
|
||||
const memoryUsagePercent = ((usedMemory / totalMemory) * 100).toFixed(2);
|
||||
|
||||
// 获取磁盘信息
|
||||
const diskInfo = await si.fsSize();
|
||||
const totalDisk = (diskInfo[0].size / (1024 ** 3)).toFixed(2); // 转换为 GB
|
||||
const usedDisk = (diskInfo[0].used / (1024 ** 3)).toFixed(2); // 转换为 GB
|
||||
const diskUsagePercent = ((usedDisk / totalDisk) * 100).toFixed(2);
|
||||
|
||||
// 获取网络信息
|
||||
const networkInterfaces = os.networkInterfaces();
|
||||
const ipAddress = Object.values(networkInterfaces)
|
||||
.flat()
|
||||
.filter(detail => detail.family === 'IPv4' && !detail.internal)[0].address;
|
||||
|
||||
// 获取系统信息
|
||||
const hostname = os.hostname();
|
||||
const uptime = os.uptime();
|
||||
const osInfo = await si.osInfo();
|
||||
|
||||
return new Response(JSON.stringify({
|
||||
cpuUsage: cpuUsage.currentLoad.toFixed(2),
|
||||
cpuUsageDetail: `${cpuUsage.currentLoad.toFixed(2)}%`,
|
||||
totalCpuCores,
|
||||
cpuCoresUsed,
|
||||
memoryUsage: memoryUsagePercent,
|
||||
usedMemory: `${usedMemory} GB`,
|
||||
totalMemory: `${totalMemory} GB`,
|
||||
diskUsage: diskUsagePercent,
|
||||
usedDisk: `${usedDisk} GB`,
|
||||
totalDisk: `${totalDisk} GB`,
|
||||
loadAverage: cpuUsage.avgLoad.toFixed(2),
|
||||
ipAddress,
|
||||
hostname,
|
||||
uptime: `${Math.floor(uptime / 60 / 60)} hours`,
|
||||
distro: osInfo.distro,
|
||||
kernelVersion: osInfo.kernel,
|
||||
arch: os.arch(),
|
||||
}), {
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
} catch (error) {
|
||||
return new Response(JSON.stringify({ error: error.message }), { status: 500 });
|
||||
}
|
||||
}
|
12
server/app/r/page.js
Normal file
12
server/app/r/page.js
Normal file
@ -0,0 +1,12 @@
|
||||
import Sidebar from "../../components/sidebar.jsx";
|
||||
import Header from "../../components/header.jsx";
|
||||
import { DrawerProvider } from "../../contexts/drawer-context.js";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<DrawerProvider>
|
||||
<Header/>
|
||||
<Sidebar />
|
||||
</DrawerProvider>
|
||||
)
|
||||
}
|
BIN
server/assets/fonts/FiraCode-VF.woff2
Normal file
BIN
server/assets/fonts/FiraCode-VF.woff2
Normal file
Binary file not shown.
BIN
server/assets/fonts/SourceHanSerifCN-VF.woff2
Normal file
BIN
server/assets/fonts/SourceHanSerifCN-VF.woff2
Normal file
Binary file not shown.
23
server/components/ThemeToggle.jsx
Normal file
23
server/components/ThemeToggle.jsx
Normal file
@ -0,0 +1,23 @@
|
||||
import React, { useState } from 'react';
|
||||
|
||||
function ThemeToggle() {
|
||||
// 用于保存主题状态,默认为“light”主题
|
||||
const [isDarkTheme, setIsDarkTheme] = useState(false);
|
||||
|
||||
// 切换主题时的处理函数
|
||||
const handleThemeChange = () => {
|
||||
setIsDarkTheme(!isDarkTheme);
|
||||
};
|
||||
|
||||
return (
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isDarkTheme}
|
||||
onChange={handleThemeChange}
|
||||
className="toggle theme-controller"
|
||||
value={isDarkTheme ? 'dark' : 'light'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default ThemeToggle;
|
21
server/components/content.jsx
Normal file
21
server/components/content.jsx
Normal file
@ -0,0 +1,21 @@
|
||||
import Bili from "./contents/bili.jsx";
|
||||
import Home from "./contents/home.jsx";
|
||||
import Tiktok from "./contents/tiktok.jsx";
|
||||
import Weekly from "./contents/weekly.jsx";
|
||||
|
||||
export function Content({ activeItem }) {
|
||||
|
||||
// 使用对象映射内容,以便于后期扩展和维护
|
||||
const contentMap = {
|
||||
"总控制台": <Home />,
|
||||
"哔哩哔哩控制台": <Bili />,
|
||||
"抖音控制台": <Tiktok />,
|
||||
"周刊预览": <Weekly />
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ contentMap[activeItem] || contentMap["总控制台"] }
|
||||
</div>
|
||||
);
|
||||
}
|
7
server/components/contents/bili.jsx
Normal file
7
server/components/contents/bili.jsx
Normal file
@ -0,0 +1,7 @@
|
||||
export default function Bili() {
|
||||
|
||||
|
||||
return (
|
||||
<div>Bili</div>
|
||||
)
|
||||
}
|
30
server/components/contents/home.jsx
Normal file
30
server/components/contents/home.jsx
Normal file
@ -0,0 +1,30 @@
|
||||
import React from 'react';
|
||||
import BotInfo from "../home/bot-info.jsx";
|
||||
import System from "../home/system.jsx";
|
||||
|
||||
export default function Home({ }) {
|
||||
return (
|
||||
<div className="container mx-auto p-8">
|
||||
<BotInfo />
|
||||
<System />
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
{/* 监控卡片 */ }
|
||||
<div className="card bg-base-100 shadow-xl col-span-1 lg:col-span-3">
|
||||
<div className="card-body">
|
||||
<h2 className="card-title text-lg font-bold">监控</h2>
|
||||
<div className="flex justify-between">
|
||||
<div>
|
||||
<p>上传: 0.87 KB/s</p>
|
||||
<p>下载: 3.21 KB/s</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>总发送: 21.17 GB</p>
|
||||
<p>总接收: 90.46 GB</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
5
server/components/contents/tiktok.jsx
Normal file
5
server/components/contents/tiktok.jsx
Normal file
@ -0,0 +1,5 @@
|
||||
export default function Tiktok() {
|
||||
return (
|
||||
<div>Tiktok</div>
|
||||
)
|
||||
}
|
11
server/components/contents/weekly.jsx
Normal file
11
server/components/contents/weekly.jsx
Normal file
@ -0,0 +1,11 @@
|
||||
export default function Weekly() {
|
||||
return (
|
||||
<div style={ { width: '100%', height: '100vh' } }>
|
||||
<iframe
|
||||
src="https://rrorangeandfriends.site"
|
||||
style={ { width: '100%', height: '100%', border: 'none' } }
|
||||
title="External Website"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
63
server/components/header.jsx
Normal file
63
server/components/header.jsx
Normal file
@ -0,0 +1,63 @@
|
||||
"use client"
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useDrawer } from "../contexts/drawer-context.js";
|
||||
import { getUserInfo } from "../utils/napact.js";
|
||||
import ThemeToggle from "./ThemeToggle.jsx";
|
||||
|
||||
export default function Header () {
|
||||
|
||||
const { toggleDrawer } = useDrawer();
|
||||
|
||||
const [user, setUser] = useState({ user_id: null, nickname: '' });
|
||||
|
||||
useEffect(() => {
|
||||
getUserInfo().then(setUser);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="navbar bg-base-100 p-3">
|
||||
|
||||
<div className="navbar-start">
|
||||
<button className="btn btn-square btn-ghost" onClick={ toggleDrawer }>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
className="inline-block h-5 w-5 stroke-current">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M4 6h16M4 12h16M4 18h16"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div className="navbar-center">
|
||||
<div className="avatar">
|
||||
<div className="w-10 rounded-full">
|
||||
<img
|
||||
alt="Logo"
|
||||
src="https://s2.loli.net/2024/08/19/ty5K6P3hsAaXC47.webp"/>
|
||||
</div>
|
||||
</div>
|
||||
<a className="btn btn-ghost text-xl">R插件控制台</a>
|
||||
</div>
|
||||
<div className="navbar-end">
|
||||
<ThemeToggle />
|
||||
<div className="flex flex-row">
|
||||
<div tabIndex={ 0 } role="button" className="btn btn-ghost btn-circle avatar mr-2">
|
||||
<div className="w-10 rounded-full">
|
||||
<img
|
||||
alt="头像"
|
||||
src={`http://q1.qlogo.cn/g?b=qq&nk=${user.user_id}&s=100`}/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-1.5">
|
||||
<div className="font-bold">{user.nickname || "未获取"}</div>
|
||||
<div className="text-sm opacity-50">{user.user_id || "NaN"}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
};
|
61
server/components/home/bot-config.jsx
Normal file
61
server/components/home/bot-config.jsx
Normal file
@ -0,0 +1,61 @@
|
||||
export function BotConfig() {
|
||||
return (
|
||||
<div className="card bg-base-100 shadow-xl">
|
||||
<div className="card-body">
|
||||
<h2 className="card-title text-lg font-bold">🔥热门快捷配置</h2>
|
||||
<div className="grid grid-cols-1 gap-2">
|
||||
<div className="flex justify-between items-center">
|
||||
<div>
|
||||
<h3 className="font-bold">通用</h3>
|
||||
<p>R 插件一些通用配置:魔法配置、线程配置等</p>
|
||||
</div>
|
||||
<button onClick={()=>document.getElementById('my_modal_5').showModal()} className="btn">配置</button>
|
||||
<dialog id="my_modal_5" className="modal ">
|
||||
<div className="modal-box w-11/12 max-w-2xl">
|
||||
<div className="mockup-browser bg-base-300 border">
|
||||
<div className="mockup-browser-toolbar">
|
||||
<div className="input">https://daisyui.com</div>
|
||||
</div>
|
||||
<div className="bg-base-200 flex justify-center px-4 pt-8">
|
||||
<label className="input input-bordered flex items-center gap-2">
|
||||
魔法地址
|
||||
<input type="text" className="grow"
|
||||
placeholder="例如:http://localhost"/>
|
||||
</label>
|
||||
</div>
|
||||
<div className="bg-base-200 flex justify-center px-4 py-8">
|
||||
<label className="input input-bordered flex items-center gap-2">
|
||||
魔法端口
|
||||
<input type="text" className="grow"
|
||||
placeholder="例如:7890"/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="modal-action">
|
||||
<form method="dialog">
|
||||
{/* if there is a button in form, it will close the modal */ }
|
||||
<button className="btn">Close</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
</div>
|
||||
<div className="flex justify-between items-center">
|
||||
<div>
|
||||
<h3 className="font-bold">哔哩哔哩</h3>
|
||||
<p>哔哩哔哩相关配置</p>
|
||||
</div>
|
||||
<button className="btn btn-warning">🚧施工</button>
|
||||
</div>
|
||||
<div className="flex justify-between items-center">
|
||||
<div>
|
||||
<h3 className="font-bold">抖音</h3>
|
||||
<p>抖音相关配置</p>
|
||||
</div>
|
||||
<button className="btn btn-warning">🚧施工</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
17
server/components/home/bot-info.jsx
Normal file
17
server/components/home/bot-info.jsx
Normal file
@ -0,0 +1,17 @@
|
||||
import { BotConfig } from "./bot-config.jsx";
|
||||
import { BotItem } from "./bot-item.jsx";
|
||||
import { BotNetwork } from "./bot-network.jsx";
|
||||
|
||||
export default function BotInfo() {
|
||||
|
||||
return (
|
||||
<div className="container mx-auto p-8">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
{/* 机器人信息卡片 */ }
|
||||
<BotItem />
|
||||
<BotNetwork />
|
||||
<BotConfig />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
46
server/components/home/bot-item.jsx
Normal file
46
server/components/home/bot-item.jsx
Normal file
@ -0,0 +1,46 @@
|
||||
import { getStatus, getUserInfo, getVersionInfo } from "../../utils/napact.js";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
export function BotItem() {
|
||||
|
||||
const [user, setUser] = useState({ user_id: null, nickname: '' });
|
||||
|
||||
const [status, setStatus] = useState({ online: false, good: false, stat: {} });
|
||||
|
||||
const [versionInfo, setVersionInfo] = useState({ app_name: "", app_version: "", protocol_version: "" });
|
||||
|
||||
useEffect(() => {
|
||||
getUserInfo().then(setUser);
|
||||
getStatus().then(setStatus);
|
||||
getVersionInfo().then(setVersionInfo);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="card bg-base-100 shadow-xl">
|
||||
<div className="card-body">
|
||||
<h2 className="card-title">🐔状态</h2>
|
||||
<div className="flex flex-row pt-5 justify-between items-center">
|
||||
<div className={ `avatar ${ status.online ? "online" : "offline" }` }>
|
||||
<div className="w-24 rounded-full">
|
||||
<img src={ `http://q1.qlogo.cn/g?b=qq&nk=${ user.user_id }&s=100` }/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col ml-12 space-y-2">
|
||||
<div className="space-y-2">
|
||||
<div className="font-bold">昵称:{ user.nickname || "未获取" }</div>
|
||||
<div className="text-sm opacity-50">QQ号:{ user.user_id || "NaN" }</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="font-bold">协议信息:</div>
|
||||
<div className="space-x-1">
|
||||
<div className="badge badge-ghost">{ versionInfo.app_name }</div>
|
||||
<div className="badge badge-ghost">{ versionInfo.app_version }</div>
|
||||
<div className="badge badge-ghost">{ versionInfo.protocol_version }</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
94
server/components/home/bot-network.jsx
Normal file
94
server/components/home/bot-network.jsx
Normal file
@ -0,0 +1,94 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { NETWORK_BASE_URL } from "../../constants/system.js";
|
||||
|
||||
export function BotNetwork() {
|
||||
|
||||
const [linksTime, setLinksTime] = useState(['0 ms', '0 ms', '0 ms', '0 ms']);
|
||||
|
||||
useEffect(() => {
|
||||
const waitingForTestingLink = [
|
||||
"https://kimi.moonshot.cn/",
|
||||
"https://github.com/",
|
||||
"https://youtube.com/",
|
||||
"https://www.google.com/"
|
||||
]
|
||||
async function getNetwork() {
|
||||
for (const value of waitingForTestingLink) {
|
||||
const index = waitingForTestingLink.indexOf(value);
|
||||
const response = await fetch(NETWORK_BASE_URL + value);
|
||||
let tmp = linksTime;
|
||||
tmp[index] = `${ (await response.json()).time }ms`;
|
||||
setLinksTime(tmp);
|
||||
}
|
||||
}
|
||||
// 每隔10秒更新一次系统信息
|
||||
// const intervalId = setInterval(getNetwork, 10000);
|
||||
//
|
||||
// // 清除定时器,避免内存泄漏
|
||||
// return () => clearInterval(intervalId);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="card bg-base-100 shadow-xl">
|
||||
<div className="card-body">
|
||||
<h2 className="card-title">🐔网速</h2>
|
||||
<div className="flex flex-row pt-5 justify-between items-center">
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<svg t="1731510966354" className="icon" viewBox="0 0 1075 1024" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg" p-id="4294" width="48" height="48">
|
||||
<path
|
||||
d="M1049.6 0m0 186.1632l0 651.6736q0 186.1632-186.1632 186.1632l-651.6736 0q-186.1632 0-186.1632-186.1632l0-651.6736q0-186.1632 186.1632-186.1632l651.6736 0q186.1632 0 186.1632 186.1632Z"
|
||||
fill="#000000" p-id="4295"></path>
|
||||
<path
|
||||
d="M605.7984 459.9808c57.1392 1.28 104.1408 52.3264 104.1408 115.1488v232.5504a2.048 2.048 0 0 1-2.048 2.0992h-99.9936a2.048 2.048 0 0 1-2.048-2.048l-1.7408-294.0928c0-1.2288-2.2528-1.4848-2.6112-0.256-13.312 43.9296-52.736 56.32-99.84 56.32H329.984a2.048 2.048 0 0 0-2.048 2.048v235.9296a2.048 2.048 0 0 1-2.0992 2.0992H220.4672a2.048 2.048 0 0 1-2.048-2.048V241.5104a2.048 2.048 0 0 1 2.048-2.048h105.3696a2.048 2.048 0 0 1 2.048 2.048v216.32c0 1.1264 0.9728 2.048 2.0992 2.048h135.2192a2.048 2.048 0 0 0 1.8944-1.2288l96.9216-218.0096a2.048 2.048 0 0 1 1.8944-1.2288h116.7872c1.536 0 2.56 1.5872 1.8944 2.9696l-66.2528 142.2848c-19.7632 36.1984-34.304 61.8496-67.2768 72.8064-1.1776 0.3584-0.9216 2.4576 0.3584 2.4576h54.3744z"
|
||||
fill="#FFFFFF" p-id="4296"></path>
|
||||
<path
|
||||
d="M752.9472 227.9936c-11.776 9.8304-19.456 25.9072-19.456 50.176 0 22.784 7.2704 40.448 18.1248 50.9952-5.632 9.3696-11.4176 15.9744-15.7696 19.456-0.7168 0.6144-0.2048 2.2528 0.7168 2.2016l64.512-4.6592c14.336-1.1264 26.624-6.5536 36.9664-15.7696 12.3904-10.496 19.456-28.5696 19.456-52.224 0-24.2688-7.0656-40.3456-19.456-50.176a64.7168 64.7168 0 0 0-43.008-14.7968c-16.384 0-30.3616 4.9664-42.0864 14.7968z"
|
||||
fill="#007AFF" p-id="4297"></path>
|
||||
</svg>
|
||||
<span className="badge badge-neutral">{ linksTime[0] }</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<svg t="1731511025099" className="icon" viewBox="0 0 1024 1024" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg" p-id="6382" width="48" height="48">
|
||||
<path
|
||||
d="M512 831.904c-19.168 0-38.304-9.92-58.144-29.76-7.808-7.808-7.808-20.48 0-28.288 7.808-7.808 20.48-7.808 28.288 0C494.336 786.08 504.128 792 512 792c7.872 0 17.664-5.92 29.856-18.144 7.808-7.808 20.48-7.808 28.288 0 7.808 7.808 7.808 20.48 0 28.288C550.304 821.984 531.168 831.904 512 831.904zM0 525.504C0 575.392 4.256 620.608 12.8 661.184 21.344 701.728 33.344 736.928 48.8 766.72 64.256 796.512 83.808 822.624 107.456 845.056 131.104 867.488 156.8 885.952 184.544 900.512 212.256 915.04 244 926.784 279.744 935.776 315.456 944.736 351.808 951.04 388.8 954.624 425.792 958.208 466.496 960 510.944 960 555.36 960 596.16 958.208 633.344 954.624 670.496 951.04 706.944 944.736 742.656 935.776 778.4 926.784 810.208 915.04 838.144 900.512 866.048 885.984 891.904 867.488 915.712 845.056 939.552 822.624 959.2 796.512 974.656 766.72 990.144 736.928 1002.208 701.728 1010.944 661.184 1019.648 620.608 1024 575.392 1024 525.504 1024 436.128 996.256 359.136 940.8 294.528 944 285.888 946.752 276.032 949.056 264.896 951.36 253.76 953.6 238.08 955.712 217.792 957.856 197.504 957.056 174.08 953.344 147.52 949.6 120.96 942.592 93.856 932.256 66.24L924.256 64.608C918.944 63.52 910.208 63.872 898.144 65.696 886.048 67.488 871.904 70.72 855.744 75.392 839.552 80.032 818.656 89.28 793.056 103.104 767.456 116.928 740.448 134.432 712 155.584 663.648 140.896 596.992 125.472 512 125.472 427.04 125.472 360.192 140.864 311.456 155.584 283.04 134.432 255.904 117.024 230.144 103.36 204.352 89.728 183.744 80.384 168.256 75.36 152.8 70.336 138.496 67.104 125.344 65.696 112.192 64.256 103.744 63.808 100 64.32 96.256 64.864 93.504 65.504 91.744 66.208 81.408 93.856 74.304 120.96 70.4 147.52 66.496 174.08 65.6 197.408 67.744 217.504 69.856 237.632 72.192 253.408 74.656 264.896 77.152 276.384 80 286.24 83.2 294.528 27.744 358.752 0 435.744 0 525.504ZM136.544 639.392C136.544 581.376 157.856 528.768 200.544 481.536 213.344 467.136 228.192 456.224 245.056 448.832 261.952 441.44 281.152 437.216 302.656 436.224 324.192 435.232 344.704 435.424 364.256 436.832 383.84 438.24 408 440.128 436.8 442.528 465.6 444.928 490.496 446.144 511.456 446.144 532.448 446.144 557.344 444.928 586.144 442.528 614.944 440.128 639.104 438.24 658.656 436.832 678.24 435.424 698.752 435.232 720.288 436.224 741.792 437.248 761.088 441.44 778.144 448.832 795.2 456.224 810.144 467.136 822.944 481.536 865.6 528.768 886.944 581.376 886.944 639.392 886.944 673.408 883.392 703.712 876.288 730.336 869.152 756.96 860.192 779.264 849.344 797.248 838.496 815.264 823.296 830.464 803.744 842.88 784.192 855.296 765.248 864.896 746.944 871.68 728.64 878.496 704.992 883.808 676 887.584 647.04 891.392 621.088 893.696 598.144 894.496 575.2 895.296 546.304 895.712 511.456 895.712 476.64 895.712 447.84 895.296 425.056 894.496 402.304 893.696 376.448 891.392 347.456 887.584 318.496 883.808 294.848 878.496 276.544 871.68 258.24 864.896 239.296 855.296 219.744 842.88 200.192 830.464 184.992 815.264 174.144 797.248 163.296 779.264 154.304 756.96 147.2 730.336 140.096 703.712 136.544 673.408 136.544 639.392ZM256 608A2 3 2520 1 0 384 608 2 3 2520 1 0 256 608zM640 608A2 3 2520 1 0 768 608 2 3 2520 1 0 640 608z"
|
||||
fill="#000000" p-id="6383"></path>
|
||||
</svg>
|
||||
<span className="badge badge-ghost">{ linksTime[1] }</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<svg t="1731513083919" className="icon" viewBox="0 0 1024 1024" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg" p-id="10881" width="48" height="48">
|
||||
<path
|
||||
d="M815.616 0H208.384C93.3 0 0 94.068 0 210.083v603.834C0 929.955 93.3 1024 208.384 1024h607.232C930.7 1024 1024 929.955 1024 813.917V210.083C1024 94.068 930.7 0 815.616 0z"
|
||||
fill="#10A37F" p-id="10882"></path>
|
||||
<path
|
||||
d="M757.364 460.032A142.825 142.825 0 0 0 745.1 342.807a144.407 144.407 0 0 0-155.462-69.26 142.708 142.708 0 0 0-106.729-47.988h-1.257a144.384 144.384 0 0 0-137.355 99.933 142.755 142.755 0 0 0-95.418 69.237 144.43 144.43 0 0 0 17.757 169.262 142.825 142.825 0 0 0 12.241 117.202 144.384 144.384 0 0 0 155.462 69.26A142.755 142.755 0 0 0 541.09 798.44h1.28a144.337 144.337 0 0 0 137.356-100.003 142.732 142.732 0 0 0 95.418-69.237 144.198 144.198 0 0 0-17.78-169.192zM542.022 760.995h-0.163a107.148 107.148 0 0 1-68.562-24.855 69.857 69.857 0 0 0 3.375-1.932l114.06-65.862a18.548 18.548 0 0 0 9.379-16.128v-160.93l48.22 27.833a1.722 1.722 0 0 1 0.932 1.327v133.19a107.497 107.497 0 0 1-107.241 107.357z m-230.68-98.514a107.148 107.148 0 0 1-12.8-71.936l3.398 2.002 114.037 65.885a18.595 18.595 0 0 0 18.758 0l139.264-80.407v55.784a1.745 1.745 0 0 1-0.699 1.374l-115.293 66.56a107.567 107.567 0 0 1-107.334 0 107.497 107.497 0 0 1-39.33-39.285z m-29.998-249.018a106.985 106.985 0 0 1 55.878-47.08l-0.047 3.956v131.84a18.525 18.525 0 0 0 9.356 16.105l139.264 80.407-48.221 27.834a1.745 1.745 0 0 1-1.63 0.14l-115.316-66.63a107.497 107.497 0 0 1-39.284-146.595z m396.102 92.16l-139.24-80.384 48.197-27.834a1.722 1.722 0 0 1 1.629-0.163l115.316 66.583a107.427 107.427 0 0 1-16.593 193.746V521.704a18.525 18.525 0 0 0-9.31-16.057z m47.988-72.215a171.055 171.055 0 0 0-3.374-2.025L608 365.521a18.618 18.618 0 0 0-18.758 0l-139.24 80.384v-55.761c0-0.535 0.232-1.07 0.698-1.396l115.293-66.514a107.38 107.38 0 0 1 159.441 111.174z m-301.638 99.235l-48.22-27.834a1.699 1.699 0 0 1-0.932-1.327V370.316a107.38 107.38 0 0 1 176.059-82.456 97.135 97.135 0 0 0-3.375 1.932l-114.083 65.885a18.572 18.572 0 0 0-9.356 16.105v0.116l-0.093 160.745z m26.205-56.46L512 440.343l62.022 35.817v71.633L512 583.587l-62.022-35.794V476.16z"
|
||||
fill="#FFFFFF" p-id="10883"></path>
|
||||
</svg>
|
||||
<span className="badge badge-ghost">{ linksTime[2] }</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<svg t="1731511054412" className="icon" viewBox="0 0 1024 1024" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg" p-id="7446" width="48" height="48">
|
||||
<path
|
||||
d="M214.101333 512c0-32.512 5.546667-63.701333 15.36-92.928L57.173333 290.218667A491.861333 491.861333 0 0 0 4.693333 512c0 79.701333 18.858667 154.88 52.394667 221.610667l172.202667-129.066667A290.56 290.56 0 0 1 214.101333 512"
|
||||
fill="#FBBC05" p-id="7447"></path>
|
||||
<path
|
||||
d="M516.693333 216.192c72.106667 0 137.258667 25.002667 188.458667 65.962667L854.101333 136.533333C763.349333 59.178667 646.997333 11.392 516.693333 11.392c-202.325333 0-376.234667 113.28-459.52 278.826667l172.373334 128.853333c39.68-118.016 152.832-202.88 287.146666-202.88"
|
||||
fill="#EA4335" p-id="7448"></path>
|
||||
<path
|
||||
d="M516.693333 807.808c-134.357333 0-247.509333-84.864-287.232-202.88l-172.288 128.853333c83.242667 165.546667 257.152 278.826667 459.52 278.826667 124.842667 0 244.053333-43.392 333.568-124.757333l-163.584-123.818667c-46.122667 28.458667-104.234667 43.776-170.026666 43.776"
|
||||
fill="#34A853" p-id="7449"></path>
|
||||
<path
|
||||
d="M1005.397333 512c0-29.568-4.693333-61.44-11.648-91.008H516.650667V614.4h274.602666c-13.696 65.962667-51.072 116.650667-104.533333 149.632l163.541333 123.818667c93.994667-85.418667 155.136-212.650667 155.136-375.850667"
|
||||
fill="#4285F4" p-id="7450"></path>
|
||||
</svg>
|
||||
<span className="badge badge-ghost">{ linksTime[3] }</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
116
server/components/home/system.jsx
Normal file
116
server/components/home/system.jsx
Normal file
@ -0,0 +1,116 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { CircularProgressbar, buildStyles } from 'react-circular-progressbar';
|
||||
import 'react-circular-progressbar/dist/styles.css';
|
||||
import { SYSTEM_BASE_URL } from "../../constants/system.js";
|
||||
|
||||
export default function System() {
|
||||
const [systemInfo, setSystemInfo] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchSystemInfo() {
|
||||
const response = await fetch(SYSTEM_BASE_URL);
|
||||
const data = await response.json();
|
||||
setSystemInfo(data);
|
||||
}
|
||||
|
||||
const intervalId = setInterval(fetchSystemInfo, 5000); // 每隔5秒更新一次系统信息
|
||||
|
||||
return () => clearInterval(intervalId); // 清除定时器,避免内存泄漏
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="container mx-auto p-8">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
{/* 状态卡片 */ }
|
||||
<div className="card bg-base-100 shadow-xl col-span-1 lg:col-span-2">
|
||||
<div className="card-body">
|
||||
<h2 className="card-title text-lg font-bold">状态</h2>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-8 mt-5">
|
||||
<div className="flex flex-col items-center">
|
||||
<div style={ { width: 120, height: 120 } }>
|
||||
<CircularProgressbar
|
||||
value={ systemInfo ? parseFloat(systemInfo.cpuUsage) : 0 }
|
||||
text={ systemInfo ? systemInfo.cpuUsage + "%" : "" }
|
||||
styles={ buildStyles({
|
||||
textSize: '18px',
|
||||
pathColor: `rgba(62, 152, 199, ${ systemInfo ? parseFloat(systemInfo.cpuUsage) / 100 : 0 })`,
|
||||
textColor: '#3b82f6',
|
||||
trailColor: '#d6d6d6',
|
||||
backgroundColor: '#f0f0f0',
|
||||
}) }
|
||||
/>
|
||||
</div>
|
||||
<span className="text mt-4">CPU</span>
|
||||
<span
|
||||
className="text-sm mt-1">{ systemInfo ? `( ${ systemInfo.cpuCoresUsed } / ${ systemInfo.totalCpuCores } ) 核` : "" }</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center">
|
||||
<div style={ { width: 120, height: 120 }}>
|
||||
<CircularProgressbar
|
||||
value={systemInfo ? parseFloat(systemInfo.memoryUsage) : 0}
|
||||
text={systemInfo ? systemInfo.memoryUsage + "%" : ""}
|
||||
styles={buildStyles({
|
||||
textSize: '18px',
|
||||
pathColor: `rgba(62, 152, 199, ${systemInfo ? parseFloat(systemInfo.memoryUsage) / 100 : 0})`,
|
||||
textColor: '#3b82f6',
|
||||
trailColor: '#d6d6d6',
|
||||
backgroundColor: '#f0f0f0',
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
<span className="text mt-4">内存</span>
|
||||
<span className="text-sm mt-1">{systemInfo ? `${systemInfo.usedMemory} / ${systemInfo.totalMemory}` : ""}</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center">
|
||||
<div style={{ width: 120, height: 120 }}>
|
||||
<CircularProgressbar
|
||||
value={systemInfo ? parseFloat(systemInfo.diskUsage) : 0}
|
||||
text={systemInfo ? systemInfo.diskUsage + "%" : ""}
|
||||
styles={buildStyles({
|
||||
textSize: '18px',
|
||||
pathColor: `rgba(62, 152, 199, ${systemInfo ? parseFloat(systemInfo.diskUsage) / 100 : 0})`,
|
||||
textColor: '#3b82f6',
|
||||
trailColor: '#d6d6d6',
|
||||
backgroundColor: '#f0f0f0',
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
<span className="text mt-4">磁盘使用</span>
|
||||
<span className="text-sm mt-1">{systemInfo ? `${systemInfo.usedDisk} / ${systemInfo.totalDisk}` : ""}</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center">
|
||||
<div style={{ width: 120, height: 120 }}>
|
||||
<CircularProgressbar
|
||||
value={systemInfo ? parseFloat(systemInfo.loadAverage) : 0}
|
||||
text={systemInfo ? systemInfo.loadAverage + "%" : ""}
|
||||
styles={buildStyles({
|
||||
textSize: '18px',
|
||||
pathColor: `rgba(62, 152, 199, ${systemInfo ? parseFloat(systemInfo.loadAverage) / 100 : 0})`,
|
||||
textColor: '#3b82f6',
|
||||
trailColor: '#d6d6d6',
|
||||
backgroundColor: '#f0f0f0',
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
<span className="text mt-4">负载</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 系统信息卡片 */ }
|
||||
<div className="card bg-base-100 shadow-xl">
|
||||
<div className="card-body">
|
||||
<h2 className="card-title text-lg font-bold">系统信息</h2>
|
||||
<p className="text">主机名称: {systemInfo ? systemInfo.hostname : ""}</p>
|
||||
<p className="text">发行版本: {systemInfo ? systemInfo.distro : ""}</p>
|
||||
<p className="text">内核版本: {systemInfo ? systemInfo.kernelVersion : ""}</p>
|
||||
<p className="text">系统类型: {systemInfo ? systemInfo.arch : ""}</p>
|
||||
<p className="text">主机地址: {systemInfo ? systemInfo.ipAddress : ""}</p>
|
||||
<p className="text">运行时间: {systemInfo ? systemInfo.uptime : ""}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
45
server/components/sidebar.jsx
Normal file
45
server/components/sidebar.jsx
Normal file
@ -0,0 +1,45 @@
|
||||
"use client"
|
||||
import { useState } from "react";
|
||||
import { SIDEBAR_ITEMS } from "../constants/sidebar.js";
|
||||
import { useDrawer } from "../contexts/drawer-context.js";
|
||||
import { Content } from "./content.jsx";
|
||||
|
||||
export default function Sidebar() {
|
||||
const { isDrawerOpen, toggleDrawer } = useDrawer();
|
||||
|
||||
const [activeItem, setActiveItem] = useState("总控制台");
|
||||
|
||||
// 定义当前主题状态
|
||||
const [theme, setTheme] = useState("light");
|
||||
|
||||
// 切换主题的函数
|
||||
const toggleTheme = (newTheme) => {
|
||||
setTheme(newTheme); // 更新状态
|
||||
document.documentElement.setAttribute("data-theme", newTheme); // 更新主题属性
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="drawer">
|
||||
<input id="my-drawer" type="checkbox" className="drawer-toggle hidden" checked={ isDrawerOpen } readOnly/>
|
||||
|
||||
<div className="drawer-content">
|
||||
<Content activeItem={activeItem} />
|
||||
</div>
|
||||
|
||||
<div className="drawer-side fixed top-16 left-0 h-[calc(100%-4rem)]">
|
||||
<label htmlFor="my-drawer" aria-label="close sidebar" className="drawer-overlay"
|
||||
onClick={ toggleDrawer }></label>
|
||||
<ul className="menu bg-base-200 text-base-content w-80 p-4 h-full overflow-y-auto">
|
||||
{SIDEBAR_ITEMS.map((item) => (
|
||||
<li key={item.name} onClick={() => setActiveItem(item.name)}>
|
||||
<a className={activeItem === item.name ? "active" : ""} onClick={() => toggleTheme(item.theme)}>
|
||||
{item.icon}
|
||||
{item.name}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
11
server/constants/napcat.js
Normal file
11
server/constants/napcat.js
Normal file
@ -0,0 +1,11 @@
|
||||
export const NAPCAT_BASE_URL = "/r/api/napcat";
|
||||
|
||||
/**
|
||||
* 获取登录号信息
|
||||
* @type {string}
|
||||
*/
|
||||
export const NAPCAT_GET_LOGIN_INFO = `${NAPCAT_BASE_URL}/get_login_info`;
|
||||
|
||||
export const NAPCAT_GET_STATUS = `${ NAPCAT_BASE_URL }/get_status`;
|
||||
|
||||
export const NAPCAT_GET_VERSION_INFO = `${ NAPCAT_BASE_URL }/get_version_info`;
|
63
server/constants/sidebar.js
Normal file
63
server/constants/sidebar.js
Normal file
@ -0,0 +1,63 @@
|
||||
export const SIDEBAR_ITEMS = [
|
||||
{
|
||||
name: "总控制台",
|
||||
icon: <svg t="1730947011086" className="icon" viewBox="0 0 1024 1024" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg" p-id="8312" width="32" height="32">
|
||||
<path
|
||||
d="M806.31 1004.13H217.69c-109.1 0-197.83-88.74-197.83-197.81V217.69c0-109.07 88.74-197.82 197.83-197.82h588.62c109.1 0 197.83 88.75 197.83 197.82v588.64c0 109.07-88.73 197.8-197.83 197.8zM217.69 97.57c-66.25 0-120.13 53.88-120.13 120.11v588.64c0 66.22 53.88 120.1 120.13 120.1h588.62c66.25 0 120.13-53.88 120.13-120.1V217.69c0-66.24-53.88-120.11-120.13-120.11H217.69v-0.01z"
|
||||
fill="#333333" p-id="8313"></path>
|
||||
<path d="M494.93 300.08h77.71v420.77h-77.71z" fill="#333333" p-id="8314"></path>
|
||||
<path d="M533.78 324.93m-81.76 0a81.76 81.76 0 1 0 163.52 0 81.76 81.76 0 1 0-163.52 0Z"
|
||||
fill="#C6C6C6" p-id="8315"></path>
|
||||
<path d="M730.78 300.08h77.71v420.77h-77.71z" fill="#333333" p-id="8316"></path>
|
||||
<path d="M769.62 673.98m-81.76 0a81.76 81.76 0 1 0 163.52 0 81.76 81.76 0 1 0-163.52 0Z"
|
||||
fill="#C6C6C6" p-id="8317"></path>
|
||||
<path d="M270.58 300.08h77.71v420.77h-77.71z" fill="#333333" p-id="8318"></path>
|
||||
<path d="M309.44 510.47m-81.76 0a81.76 81.76 0 1 0 163.52 0 81.76 81.76 0 1 0-163.52 0Z"
|
||||
fill="#C6C6C6" p-id="8319"></path>
|
||||
</svg>,
|
||||
theme: "light"
|
||||
},
|
||||
{
|
||||
name: "哔哩哔哩控制台",
|
||||
icon: <svg t="1730946721024" className="icon" viewBox="0 0 1024 1024" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg" p-id="4282" width="32" height="32">
|
||||
<path
|
||||
d="M729.32864 373.94944c-9.79456-5.94432-19.06176-6.784-19.14368-6.784l-1.06496-0.0512c-57.20064-3.8656-121.1648-5.83168-190.12608-5.83168l-13.98784 0.00512c-68.95616 0-132.92544 1.96096-190.12096 5.83168l-1.06496 0.0512c-0.08192 0-9.34912 0.83968-19.14368 6.784-15.04768 9.12896-24.27392 25.94816-27.4176 49.9712-10.07104 76.91264-4.38272 173.64992 0.18944 251.392 2.93888 49.96608 33.408 62.45888 85.04832 67.1488 10.78272 0.98816 69.08928 5.86752 159.50848 5.89312v-0.00512c90.4192-0.02048 148.72576-4.90496 159.5136-5.888 51.64032-4.68992 82.10944-17.18272 85.0432-67.1488 4.57728-77.74208 10.26048-174.47936 0.18944-251.392-3.1488-24.02816-12.37504-40.84736-27.42272-49.97632z m-390.9888 172.71808a23.64928 23.64928 0 0 1-31.68768-10.84416 23.68 23.68 0 0 1 10.84416-31.68768c2.03776-1.00352 50.69312-24.72448 110.5408-43.06432a23.68 23.68 0 1 1 13.88032 45.29152c-56.2944 17.24928-103.11168 40.07424-103.5776 40.30464z m268.89728 35.88608c-0.44032 2.23232-11.26912 54.64064-50.93888 54.64064-21.44256 0-36.10112-14.04928-44.98432-26.77248-8.69376 12.70784-22.80448 26.77248-42.65472 26.77248-35.5328 0-50.13504-48.26624-51.68128-53.77024a11.3664 11.3664 0 0 1 21.87776-6.1696c2.74944 9.6512 14.1312 37.20192 29.7984 37.20192 16.37376 0 28.89216-23.64416 31.98464-31.92832a11.37152 11.37152 0 0 1 10.6496-7.38816h0.06144c4.76672 0.03072 9.0112 3.02592 10.62912 7.50592 0.10752 0.28672 11.96544 31.81568 34.31424 31.81568 20.864 0 28.56448-35.95264 28.64128-36.32128a11.34592 11.34592 0 0 1 13.35808-8.93952 11.36128 11.36128 0 0 1 8.94464 13.35296z m110.11584-46.73536a23.68 23.68 0 0 1-31.68256 10.84416c-0.47104-0.2304-47.47264-23.1168-103.57248-40.30976a23.69024 23.69024 0 0 1-15.70816-29.58336 23.66976 23.66976 0 0 1 29.57824-15.70304c59.84768 18.33984 108.49792 42.0608 110.55104 43.06432a23.68 23.68 0 0 1 10.83392 31.68768z"
|
||||
fill="#F16C8D" p-id="4283"></path>
|
||||
<path
|
||||
d="M849.92 51.2H174.08c-67.8656 0-122.88 55.0144-122.88 122.88v675.84c0 67.87072 55.0144 122.88 122.88 122.88h675.84c67.87072 0 122.88-55.00928 122.88-122.88V174.08c0-67.86048-55.00928-122.88-122.88-122.88z m-36.60288 627.45088c-2.62656 44.57984-21.82144 78.63296-55.51616 98.48832-25.68192 15.13472-54.17472 19.48672-81.13664 21.9392-32.45568 2.94912-92.71808 6.09792-164.66432 6.1184-71.94112-0.02048-132.20864-3.16416-164.66432-6.1184-26.96192-2.45248-55.45472-6.80448-81.13152-21.9392-33.69472-19.85536-52.8896-53.90336-55.51104-98.4832-4.70528-80.13312-10.5728-179.85536 0.19456-262.10816C221.5424 335.16544 280.99072 311.57248 311.5008 310.37952a2482.64192 2482.64192 0 0 1 81.42336-4.08576c-7.53664-8.53504-19.88096-23.3216-28.81536-38.11328-13.73696-22.73792 8.52992-41.68704 8.52992-41.68704s23.68-20.36736 44.52864 5.21216c15.69792 19.26656 38.37952 55.99744 48.61952 72.95488l53.20704-0.21504c13.2608 0 26.33216 0.07168 39.2192 0.21504 10.24-16.95744 32.9216-53.6832 48.61952-72.95488 20.84352-25.57952 44.52864-5.21216 44.52864-5.21216s22.26176 18.94912 8.5248 41.68704c-8.9344 14.79168-21.27872 29.57824-28.81536 38.11328 28.35968 0.97792 55.56224 2.33984 81.42336 4.08064 30.5152 1.19808 89.9584 24.79104 100.61312 106.17344 10.7776 82.24768 4.9152 181.96992 0.20992 262.10304z"
|
||||
fill="#F16C8D" p-id="4284"></path>
|
||||
</svg>,
|
||||
theme: "valentine"
|
||||
},
|
||||
{
|
||||
name: "抖音控制台", icon: <svg t="1730946961123" className="icon" viewBox="0 0 1024 1024" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg" p-id="5496" width="32" height="32">
|
||||
<path
|
||||
d="M0 0m184.32 0l655.36 0q184.32 0 184.32 184.32l0 655.36q0 184.32-184.32 184.32l-655.36 0q-184.32 0-184.32-184.32l0-655.36q0-184.32 184.32-184.32Z"
|
||||
fill="#111111" p-id="5497"></path>
|
||||
<path
|
||||
d="M204.27776 670.59712a246.25152 246.25152 0 0 1 245.97504-245.97504v147.57888a98.49856 98.49856 0 0 0-98.38592 98.38592c0 48.34304 26.14272 100.352 83.54816 100.352 3.81952 0 93.55264-0.88064 93.55264-77.19936V134.35904h157.26592a133.31456 133.31456 0 0 0 133.12 132.99712l-0.13312 147.31264a273.152 273.152 0 0 1-142.62272-38.912l-0.06144 317.98272c0 146.00192-124.24192 224.77824-241.14176 224.77824-131.74784 0.03072-231.1168-106.56768-231.1168-247.92064z"
|
||||
fill="#FF4040" p-id="5498"></path>
|
||||
<path
|
||||
d="M164.92544 631.23456a246.25152 246.25152 0 0 1 245.97504-245.97504v147.57888a98.49856 98.49856 0 0 0-98.38592 98.38592c0 48.34304 26.14272 100.352 83.54816 100.352 3.81952 0 93.55264-0.88064 93.55264-77.19936V94.99648h157.26592a133.31456 133.31456 0 0 0 133.12 132.99712l-0.13312 147.31264a273.152 273.152 0 0 1-142.62272-38.912l-0.06144 317.98272c0 146.00192-124.24192 224.77824-241.14176 224.77824-131.74784 0.03072-231.1168-106.56768-231.1168-247.92064z"
|
||||
fill="#00F5FF" p-id="5499"></path>
|
||||
<path
|
||||
d="M410.91072 427.58144c-158.8224 20.15232-284.44672 222.72-154.112 405.00224 120.40192 98.47808 373.68832 41.20576 380.70272-171.85792l-0.17408-324.1472a280.7296 280.7296 0 0 0 142.88896 38.62528V261.2224a144.98816 144.98816 0 0 1-72.8064-54.82496 135.23968 135.23968 0 0 1-54.70208-72.45824h-123.66848l-0.08192 561.41824c-0.11264 78.46912-130.9696 106.41408-164.18816 30.2592-83.18976-39.77216-64.37888-190.9248 46.31552-192.57344z"
|
||||
fill="#FFFFFF" p-id="5500"></path>
|
||||
</svg>,
|
||||
theme: "dark"
|
||||
},
|
||||
{
|
||||
name: "周刊预览",
|
||||
icon: <svg t="1730970789425" className="icon" viewBox="0 0 1024 1024" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg" p-id="4284" width="32" height="32">
|
||||
<path
|
||||
d="M1024 157.866667v712.533333a80.64 80.64 0 0 0-7.68 14.08c-21.333333 68.693333-66.986667 113.066667-135.68 133.12a116.48 116.48 0 0 0-14.506667 6.4H153.6a58.88 58.88 0 0 0-11.946667-6.826667q-103.68-31.146667-134.4-134.4a58.88 58.88 0 0 0-6.826666-11.946666v-716.8a58.88 58.88 0 0 0 6.826666-11.946667Q38.4 38.4 141.653333 7.68A58.88 58.88 0 0 0 153.6 0.853333h712.533333a62.293333 62.293333 0 0 0 10.24 5.12q111.36 31.146667 142.506667 142.506667A62.293333 62.293333 0 0 0 1024 157.866667zM573.013333 720.213333c13.653333 1.706667 26.026667 4.266667 38.826667 5.12a320 320 0 0 0 44.8 0.426667c32-2.56 47.786667-19.2 47.786667-51.2 0.426667-113.92 0-227.413333 0.426666-341.333333 0-16.64-5.546667-22.613333-22.613333-22.186667q-165.12 0.853333-330.666667 0c-17.92 0-23.466667 6.826667-23.466666 23.893333 0.426667 66.986667 2.133333 133.546667-0.426667 200.533334-1.28 40.96-8.533333 81.493333-14.933333 122.453333-2.986667 18.773333-9.813333 36.693333-15.36 57.173333l40.106666 11.093334c19.2-51.2 30.293333-101.973333 29.866667-155.306667q-0.426667-100.266667 0-200.533333c0-8.533333-2.986667-20.053333 11.946667-20.053334h111.786666v33.28h-102.4v36.266667H490.666667v37.546667H389.12v34.133333h256V456.533333h-112.213333v-37.12h112.213333v-36.693333H533.333333v-32.426667c38.4 0 75.52 0.853333 112.213334-0.426666 16.213333-0.426667 20.48 4.693333 20.48 20.48-0.853333 95.146667-0.426667 190.72-0.426667 285.866666 0 25.173333-6.4 32-32 29.866667s-40.106667-6.4-60.16-9.813333z m-175.36-65.706666h237.653334v-133.12H397.226667z"
|
||||
fill="#19B883" p-id="4285"></path>
|
||||
<path d="M598.186667 557.653333v60.16h-162.56v-60.16z" fill="#22BB88" p-id="4286"></path>
|
||||
</svg>,
|
||||
theme: "dim"
|
||||
}
|
||||
];
|
5
server/constants/system.js
Normal file
5
server/constants/system.js
Normal file
@ -0,0 +1,5 @@
|
||||
const BASE_URL = "/r/api";
|
||||
|
||||
export const SYSTEM_BASE_URL = `${BASE_URL}/system`;
|
||||
|
||||
export const NETWORK_BASE_URL = `${BASE_URL}/network?url=`;
|
22
server/contexts/drawer-context.js
Normal file
22
server/contexts/drawer-context.js
Normal file
@ -0,0 +1,22 @@
|
||||
"use client"
|
||||
import { createContext, useContext, useState } from 'react';
|
||||
|
||||
const DrawerContext = createContext();
|
||||
|
||||
export const DrawerProvider = ({ children }) => {
|
||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
||||
|
||||
const toggleDrawer = () => {
|
||||
setIsDrawerOpen(prev => !prev);
|
||||
};
|
||||
|
||||
return (
|
||||
<DrawerContext.Provider value={{ isDrawerOpen, toggleDrawer }}>
|
||||
{children}
|
||||
</DrawerContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useDrawer = () => useContext(DrawerContext);
|
||||
|
||||
|
6
server/postcss.config.js
Normal file
6
server/postcss.config.js
Normal file
@ -0,0 +1,6 @@
|
||||
// postcss.config.js
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
},
|
||||
};
|
37
server/styles/global.css
Normal file
37
server/styles/global.css
Normal file
@ -0,0 +1,37 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
/** 代码字体 */
|
||||
@font-face {
|
||||
font-family: "FiraCode";
|
||||
src: url("../assets/fonts/FiraCode-VF.woff2");
|
||||
}
|
||||
/** 正文字体 */
|
||||
@font-face {
|
||||
font-family: "SourceHanSerifCN";
|
||||
src: local("SourceHanSerifCN"), url("../assets/fonts/SourceHanSerifCN-VF.woff2");
|
||||
}
|
||||
|
||||
/* 自定义滚动条 */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
/* 滚动条宽度 */
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
/* 滚动条轨道背景色 */
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #888;
|
||||
/* 滚动条滑块颜色 */
|
||||
border-radius: 4px;
|
||||
/* 滑块圆角 */
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #555;
|
||||
/* 滑块悬停时的颜色 */
|
||||
}
|
23
server/tailwind.config.js
Normal file
23
server/tailwind.config.js
Normal file
@ -0,0 +1,23 @@
|
||||
import daisyui from "daisyui"
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
const config = {
|
||||
content: [
|
||||
"./app/**/*.{html,js,jsx}",
|
||||
"./components/**/*.{html,js,jsx}",
|
||||
"./pages/**/*.{html,js,jsx}",
|
||||
"./styles/**/*.{html,js,jsx}"
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
darkMode: "class",
|
||||
plugins: [
|
||||
daisyui,
|
||||
],
|
||||
daisyui: {
|
||||
themes: ["light", "dark", "valentine"],
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
50
server/utils/axiosInstance.js
Normal file
50
server/utils/axiosInstance.js
Normal file
@ -0,0 +1,50 @@
|
||||
import axios from 'axios';
|
||||
|
||||
// 创建 Axios 实例
|
||||
const axiosInstance = axios.create({
|
||||
baseURL: 'http://192.168.31.230:2537', // 基础请求地址
|
||||
timeout: 5000, // 设置请求超时时间,可根据需要调整
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
// 请求拦截器
|
||||
axiosInstance.interceptors.request.use(
|
||||
(config) => {
|
||||
// 这里可以添加请求前的处理逻辑,例如添加 token
|
||||
// const token = localStorage.getItem('token');
|
||||
// if (token) {
|
||||
// config.headers.Authorization = `Bearer ${token}`;
|
||||
// }
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
// 请求错误处理
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// 响应拦截器
|
||||
axiosInstance.interceptors.response.use(
|
||||
(response) => {
|
||||
// 响应成功处理
|
||||
return response.data;
|
||||
},
|
||||
(error) => {
|
||||
// 响应错误处理
|
||||
if (error.response) {
|
||||
// 服务器返回的错误
|
||||
console.error('Error:', error.response.status, error.response.data);
|
||||
} else if (error.request) {
|
||||
// 请求未收到服务器响应
|
||||
console.error('No response received:', error.request);
|
||||
} else {
|
||||
// 设置请求时发生的错误
|
||||
console.error('Request setup error:', error.message);
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export default axiosInstance;
|
17
server/utils/napact.js
Normal file
17
server/utils/napact.js
Normal file
@ -0,0 +1,17 @@
|
||||
import { NAPCAT_GET_LOGIN_INFO, NAPCAT_GET_STATUS, NAPCAT_GET_VERSION_INFO } from "../constants/napcat.js";
|
||||
|
||||
export async function getUserInfo() {
|
||||
const userInfo = await fetch(NAPCAT_GET_LOGIN_INFO).then(resp => resp.json());
|
||||
const { user_id, nickname } = userInfo.data;
|
||||
return { user_id, nickname }
|
||||
}
|
||||
|
||||
export async function getStatus() {
|
||||
const status = await fetch(NAPCAT_GET_STATUS).then(resp => resp.json());
|
||||
return status.data;
|
||||
}
|
||||
|
||||
export async function getVersionInfo() {
|
||||
const versionInfo = await fetch(NAPCAT_GET_VERSION_INFO).then(resp => resp.json());
|
||||
return versionInfo.data;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user