From db5f57a73c050929fa73a4ae83f2ed3c6a31d0a8 Mon Sep 17 00:00:00 2001 From: zhiyu1998 Date: Sun, 1 Dec 2024 14:21:43 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=B3=20chore:=20rc2-=E5=88=86=E7=A6=BBw?= =?UTF-8?q?ebui=E5=88=B0=E6=96=B0=E7=9A=84=E9=A1=B9=E7=9B=AE=E4=BB=93?= =?UTF-8?q?=E5=BA=93=E5=8F=AF=E8=87=AA=E8=A1=8C=E9=83=A8=E7=BD=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 +- apps/webUI.js | 68 ----- config/tools.yaml | 1 - config/version.yaml | 2 +- constants/constant.js | 6 - index.js | 10 - package.json | 26 +- server/app/layout.jsx | 9 - server/app/page.jsx | 12 - server/app/r/api/bot/route.js | 11 - server/app/r/api/commit/route.js | 30 -- server/app/r/api/config/route.js | 52 ---- server/app/r/api/network/route.js | 25 -- server/app/r/api/network2/route.js | 103 ------- server/app/r/api/resolveControl/route.js | 49 ---- server/app/r/api/system/route.js | 63 ---- server/app/r/api/update/route.js | 241 --------------- server/app/r/api/version/route.js | 43 --- server/components/TagSelector.jsx | 65 ---- server/components/ThemeToggle.jsx | 23 -- server/components/common/ConfigItem.jsx | 47 --- server/components/content.jsx | 13 - server/components/contents/bili.jsx | 156 ---------- server/components/contents/generic.jsx | 359 ----------------------- server/components/contents/home.jsx | 14 - server/components/contents/ncm.jsx | 194 ------------ server/components/contents/tiktok.jsx | 147 ---------- server/components/contents/weekly.jsx | 11 - server/components/contents/youtube.jsx | 126 -------- server/components/header.jsx | 67 ----- server/components/home/bot-config.jsx | 106 ------- server/components/home/bot-info.jsx | 17 -- server/components/home/bot-item.jsx | 63 ---- server/components/home/bot-network.jsx | 105 ------- server/components/home/network.jsx | 147 ---------- server/components/home/system.jsx | 116 -------- server/components/sidebar.jsx | 45 --- server/components/toast.jsx | 9 - server/constants/api.js | 11 - server/constants/redis.js | 5 - server/constants/resolve.js | 78 ----- server/constants/sidebar.js | 130 -------- server/contexts/drawer-context.js | 22 -- server/next.config.js | 5 - server/postcss.config.js | 6 - server/styles/global.css | 41 --- server/tailwind.config.js | 23 -- server/utils/redis.js | 17 -- server/utils/yamlHelper.js | 29 -- utils/network.js | 109 ------- utils/start-nextjs.js | 72 ----- 51 files changed, 7 insertions(+), 3127 deletions(-) delete mode 100644 apps/webUI.js delete mode 100644 server/app/layout.jsx delete mode 100644 server/app/page.jsx delete mode 100644 server/app/r/api/bot/route.js delete mode 100644 server/app/r/api/commit/route.js delete mode 100644 server/app/r/api/config/route.js delete mode 100644 server/app/r/api/network/route.js delete mode 100644 server/app/r/api/network2/route.js delete mode 100644 server/app/r/api/resolveControl/route.js delete mode 100644 server/app/r/api/system/route.js delete mode 100644 server/app/r/api/update/route.js delete mode 100644 server/app/r/api/version/route.js delete mode 100644 server/components/TagSelector.jsx delete mode 100644 server/components/ThemeToggle.jsx delete mode 100644 server/components/common/ConfigItem.jsx delete mode 100644 server/components/content.jsx delete mode 100644 server/components/contents/bili.jsx delete mode 100644 server/components/contents/generic.jsx delete mode 100644 server/components/contents/home.jsx delete mode 100644 server/components/contents/ncm.jsx delete mode 100644 server/components/contents/tiktok.jsx delete mode 100644 server/components/contents/weekly.jsx delete mode 100644 server/components/contents/youtube.jsx delete mode 100644 server/components/header.jsx delete mode 100644 server/components/home/bot-config.jsx delete mode 100644 server/components/home/bot-info.jsx delete mode 100644 server/components/home/bot-item.jsx delete mode 100644 server/components/home/bot-network.jsx delete mode 100644 server/components/home/network.jsx delete mode 100644 server/components/home/system.jsx delete mode 100644 server/components/sidebar.jsx delete mode 100644 server/components/toast.jsx delete mode 100644 server/constants/api.js delete mode 100644 server/constants/redis.js delete mode 100644 server/constants/resolve.js delete mode 100644 server/constants/sidebar.js delete mode 100644 server/contexts/drawer-context.js delete mode 100644 server/next.config.js delete mode 100644 server/postcss.config.js delete mode 100644 server/styles/global.css delete mode 100644 server/tailwind.config.js delete mode 100644 server/utils/redis.js delete mode 100644 server/utils/yamlHelper.js delete mode 100644 utils/network.js delete mode 100644 utils/start-nextjs.js diff --git a/README.md b/README.md index 67c3000..c9f98b6 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,10 @@ sudo apt-get install ffmpeg | 春日野穹OvO | 25 | | MiX | 30 | | AO | 26 | -| Chino | 30 | +| Chino | 80 | +| 辰 | 50 | +| 非酋 | 1杯瑞幸 | +| 白洲梓 | 1杯瑞幸 | diff --git a/apps/webUI.js b/apps/webUI.js deleted file mode 100644 index c3467c1..0000000 --- a/apps/webUI.js +++ /dev/null @@ -1,68 +0,0 @@ -import { REDIS_YUNZAI_WEBUI } from "../constants/constant.js"; -import config from "../model/config.js"; -import { constructPublicIPsMsg } from "../utils/network.js"; -import { redisSetKey } from "../utils/redis-util.js"; -import { buildNextJs } from "../utils/start-nextjs.js"; -import { getBotLoginInfo, getBotStatus, getBotVersionInfo, sendPrivateMsg } from "../utils/yunzai-util.js"; - -export class WebUI extends plugin { - constructor() { - super({ - name: "R插件 WebUI 开关", - dsc: "R插件 WebUI 开关", - event: "message", - priority: 4000, - rule: [ - { - reg: "^#(r|R)wss$", - fnc: "rWebSwitch", - permission: "master", - }, - { - reg: "^#(r|R)ws$", - fnc: "rWebStatus", - permission: "master", - } - ] - }); - // 配置文件 - this.toolsConfig = config.getConfig("tools"); - // 加载WebUI开关 - this.isOpenWebUI = this.toolsConfig.isOpenWebUI; - } - - async initData(e, realIsOpenWebUI) { - if (realIsOpenWebUI) { - Promise.all([getBotStatus(e), getBotVersionInfo(e), getBotLoginInfo(e)]).then(values => { - const status = values[0].data; - const versionInfo = values[1].data; - const loginInfo = values[2].data; - redisSetKey(REDIS_YUNZAI_WEBUI, { - ...status, - ...versionInfo, - ...loginInfo - }); - }); - } - } - - async rWebSwitch(e) { - config.updateField("tools", "isOpenWebUI", !this.isOpenWebUI); - const realIsOpenWebUI = config.getConfig("tools").isOpenWebUI; - if (realIsOpenWebUI) { - // 初始化数据 - await this.initData(e, realIsOpenWebUI); - e.reply(`R插件可视化面板:正在构建中,请稍等...`); - // 动态编译生产环境 - await buildNextJs(); - await sendPrivateMsg(e, constructPublicIPsMsg()); - } - e.reply(`R插件可视化面板:${ realIsOpenWebUI ? "✅已开启" : "❌已关闭" },重启后生效`); - return true; - } - - async rWebStatus(e) { - e.reply(`R插件可视化面板:\n状态:${ this.toolsConfig.isOpenWebUI ? "✅开启" : "❌关闭" }\n地址:******:4016`); - return true; - } -} diff --git a/config/tools.yaml b/config/tools.yaml index 96ed8ee..b86c1f6 100644 --- a/config/tools.yaml +++ b/config/tools.yaml @@ -1,4 +1,3 @@ -isOpenWebUI: false # 是否开启webui defaultPath: './data/rcmp4/' # 保存视频的位置 videoSizeLimit: 70 # 视频大小限制(单位MB),超过大小则转换成群文件上传 proxyAddr: '127.0.0.1' # 魔法地址 diff --git a/config/version.yaml b/config/version.yaml index 0422893..562ab48 100644 --- a/config/version.yaml +++ b/config/version.yaml @@ -1,5 +1,5 @@ - { - version: 1.10.0-rc1, + version: 1.10.0-rc2, data: [ 新增RBS查看哔哩哔哩状态功能, diff --git a/constants/constant.js b/constants/constant.js index 930046f..72dbc91 100644 --- a/constants/constant.js +++ b/constants/constant.js @@ -92,12 +92,6 @@ export const REDIS_YUNZAI_CLOUDSONGLIST = "Yz:rconsole:tools:cloudsonglist"; */ export const REDIS_YUNZAI_WHITELIST = "Yz:rconsole:tools:whitelist"; -/** - * WEBUI需要数据的缓存 - * @type {string} - */ -export const REDIS_YUNZAI_WEBUI = "Yz:rconsole:tools:webui"; - export const TWITTER_BEARER_TOKEN = ""; /** diff --git a/index.js b/index.js index cdc5be4..8a5f3e9 100644 --- a/index.js +++ b/index.js @@ -1,16 +1,12 @@ import fs from "node:fs"; import path from "path"; import config from "./model/config.js"; -import { constructPublicIPsMsg } from "./utils/network.js"; -import { startNextJs } from "./utils/start-nextjs.js"; if (!global.segment) { global.segment = (await import("oicq")).segment } // 加载版本号 const versionData = config.getConfig("version"); -// 加载是否使用WebUI -const isOpenWebUI = config.getConfig("tools").isOpenWebUI; // 加载名称 const packageJsonPath = path.join('./plugins', 'rconsole-plugin', 'package.json'); const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); @@ -40,10 +36,4 @@ for (let i in files) { apps[name] = ret[i].value[Object.keys(ret[i].value)[0]]; } -// 检查是否启动 webui -if (isOpenWebUI) { - startNextJs('start'); - logger.info(constructPublicIPsMsg()); -} - export { apps }; diff --git a/package.json b/package.json index 0a5a716..930fa43 100644 --- a/package.json +++ b/package.json @@ -2,33 +2,11 @@ "name": "rconsole-plugin", "description": "R-Plugin", "type": "module", - "scripts": { - "dev": "cd server && next dev -p 4016", - "dev6": "cd server && HOST=:: next dev -p 4016", - "start": "cd server && next start -p 4016", - "start6": "cd server && HOST=:: next start -p 4016", - "build": "cd server && next build" - }, "dependencies": { "axios": "^1.3.4", - "chart.js": "^4.4.6", "form-data": "^4.0.1", - "ioredis": "^5.4.1", - "js-yaml": "^4.1.0", - "next": "^14.2.16", "node-id3": "^0.2.6", - "node-os-utils": "^1.3.7", - "os-utils": "^0.0.14", - "p-queue": "^8.0.1", "qrcode": "^1.5.3", - "react": "^18.3.1", - "react-chartjs-2": "^5.2.0", - "react-circular-progressbar": "^2.1.0", - "react-dom": "^18.3.1", - "systeminformation": "^5.23.5" - }, - "devDependencies": { - "daisyui": "^4.12.14", - "tailwindcss": "^3.4.14" + "p-queue": "^8.0.1" } -} +} \ No newline at end of file diff --git a/server/app/layout.jsx b/server/app/layout.jsx deleted file mode 100644 index e8acbcc..0000000 --- a/server/app/layout.jsx +++ /dev/null @@ -1,9 +0,0 @@ -import "../styles/global.css"; - -export default function RootLayout({ children }) { - return ( - - {children} - - ) -} diff --git a/server/app/page.jsx b/server/app/page.jsx deleted file mode 100644 index 63aed4c..0000000 --- a/server/app/page.jsx +++ /dev/null @@ -1,12 +0,0 @@ -import Header from "../components/header.jsx"; -import Sidebar from "../components/sidebar.jsx"; -import { DrawerProvider } from "../contexts/drawer-context.js"; - -export default function Page() { - return ( - -
- - - ) -} diff --git a/server/app/r/api/bot/route.js b/server/app/r/api/bot/route.js deleted file mode 100644 index 35d92dc..0000000 --- a/server/app/r/api/bot/route.js +++ /dev/null @@ -1,11 +0,0 @@ -import { REDIS_YUNZAI_WEBUI } from "../../../../../constants/constant.js"; -import { redis } from "../../../../utils/redis.js"; - -export async function GET(req, res) { - const botInfo = JSON.parse(await redis.get(REDIS_YUNZAI_WEBUI)); - - return new Response(JSON.stringify(botInfo), { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }); -} diff --git a/server/app/r/api/commit/route.js b/server/app/r/api/commit/route.js deleted file mode 100644 index 4860c3e..0000000 --- a/server/app/r/api/commit/route.js +++ /dev/null @@ -1,30 +0,0 @@ -async function getLatestCommit(platform = "github") { - // 构建 API URL - const baseUrl = - platform === "github" - ? `https://api.github.com/repos/zhiyu1998/rconsole-plugin/commits` - : `https://gitee.com/api/v5/repos/kyrzy0416/rconsole-plugin/commits`; - - try { - const response = await fetch(baseUrl); - if (!response.ok) throw new Error("获取提交信息失败"); - - const commits = await response.json(); - const latestCommit = commits[0]; // 最新提交 - const { sha, commit, html_url } = latestCommit; - - return { sha, author: commit.author.name, message: commit.message, url: html_url }; - } catch (error) { - console.error("无法获取最新的提交:", error.message); - return null; - } -} - -export async function GET(req, res) { - const latestCommit = await getLatestCommit(); - - return new Response(JSON.stringify(latestCommit), { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }); -} diff --git a/server/app/r/api/config/route.js b/server/app/r/api/config/route.js deleted file mode 100644 index 2e6f69e..0000000 --- a/server/app/r/api/config/route.js +++ /dev/null @@ -1,52 +0,0 @@ -import fs from 'fs'; -import yaml from 'js-yaml'; -import path from 'path'; - -const configPath = path.join(process.cwd(), "../", 'config', 'tools.yaml'); - -export async function GET(req, res) { - try { - const yamlContent = await fs.promises.readFile(configPath, 'utf8'); - const config = yaml.load(yamlContent); - return new Response(JSON.stringify(config), { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }); - } catch (error) { - console.error('读取配置文件失败:', error); - return new Response(JSON.stringify({ error: '读取配置文件失败' }), { - status: 500, - headers: { 'Content-Type': 'application/json' }, - }); - } -} - -export async function POST(req, res) { - try { - const updates = await req.json(); - - const yamlContent = await fs.promises.readFile(configPath, 'utf8'); - const currentConfig = yaml.load(yamlContent); - - // 只更新指定的字段 - const newConfig = { ...currentConfig, ...updates }; - - // 转换回YAML并保存 - const newYamlContent = yaml.dump(newConfig, { - indent: 2, - lineWidth: -1, - quotingType: '"' - }); - await fs.promises.writeFile(configPath, newYamlContent, 'utf8'); - return new Response(JSON.stringify({ success: true }), { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }); - } catch (error) { - console.error('更新配置文件失败:', error); - return new Response(JSON.stringify({ error: '更新配置文件失败' }), { - status: 500, - headers: { 'Content-Type': 'application/json' }, - }); - } -} diff --git a/server/app/r/api/network/route.js b/server/app/r/api/network/route.js deleted file mode 100644 index ba2c6af..0000000 --- a/server/app/r/api/network/route.js +++ /dev/null @@ -1,25 +0,0 @@ -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' }, - }) - } -} diff --git a/server/app/r/api/network2/route.js b/server/app/r/api/network2/route.js deleted file mode 100644 index c8ef821..0000000 --- a/server/app/r/api/network2/route.js +++ /dev/null @@ -1,103 +0,0 @@ -import { unstable_noStore as noStore } from 'next/cache'; -import { promises as fs } from 'fs'; -import os from 'os'; -import si from 'systeminformation'; - -let lastBytesReceived = 0; -let lastBytesSent = 0; -let lastTimestamp = Date.now(); - -async function getLinuxStats() { - const data = await fs.readFile('/proc/net/dev', 'utf8'); - const lines = data.trim().split('\n'); - - let bytesReceived = 0; - let bytesSent = 0; - - for (let i = 2; i < lines.length; i++) { - const line = lines[i].trim(); - const parts = line.split(/\s+/); - - if (parts[0].startsWith('lo:')) continue; - - bytesReceived += parseInt(parts[1], 10); - bytesSent += parseInt(parts[9], 10); - } - - return { bytesReceived, bytesSent }; -} - -async function getWindowsStats() { - const networkStats = await si.networkStats(); - let bytesReceived = 0; - let bytesSent = 0; - - for (const stat of networkStats) { - bytesReceived += stat.rx_bytes || 0; - bytesSent += stat.tx_bytes || 0; - } - - return { bytesReceived, bytesSent }; -} - -async function getNetworkStats() { - try { - const platform = os.platform(); - let bytesReceived = 0; - let bytesSent = 0; - - if (platform === 'linux') { - const stats = await getLinuxStats(); - bytesReceived = stats.bytesReceived; - bytesSent = stats.bytesSent; - } else { - const stats = await getWindowsStats(); - bytesReceived = stats.bytesReceived; - bytesSent = stats.bytesSent; - } - - const now = Date.now(); - const timeDiff = (now - lastTimestamp) / 1000; - - const downloadSpeed = Math.max(0, (bytesReceived - lastBytesReceived) / timeDiff); - const uploadSpeed = Math.max(0, (bytesSent - lastBytesSent) / timeDiff); - - lastBytesReceived = bytesReceived; - lastBytesSent = bytesSent; - lastTimestamp = now; - - return { - downloadSpeed: (downloadSpeed / 1024).toFixed(2), - uploadSpeed: (uploadSpeed / 1024).toFixed(2), - totalReceived: (bytesReceived / (1024 * 1024 * 1024)).toFixed(2), - totalSent: (bytesSent / (1024 * 1024 * 1024)).toFixed(2), - timestamp: now - }; - } catch (error) { - console.error('获取网络统计信息失败:', error); - return { - downloadSpeed: "0", - uploadSpeed: "0", - totalReceived: "0", - totalSent: "0", - timestamp: Date.now() - }; - } -} - -export async function GET() { - // 这个不允许删除,否则无法做到实时获取 - noStore(); - try { - const stats = await getNetworkStats(); - return new Response(JSON.stringify(stats), { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }); - } catch (error) { - return new Response(JSON.stringify({ error: error.message }), { - status: 500, - headers: { 'Content-Type': 'application/json' }, - }); - } -} diff --git a/server/app/r/api/resolveControl/route.js b/server/app/r/api/resolveControl/route.js deleted file mode 100644 index 200e6d9..0000000 --- a/server/app/r/api/resolveControl/route.js +++ /dev/null @@ -1,49 +0,0 @@ -import { REDIS_RESOLVE_CONTROLLER } from "../../../../constants/redis.js"; -import { GLOBAL_RESOLE_CONTROLLER } from "../../../../constants/resolve.js"; -import { redis } from "../../../../utils/redis.js"; - -export async function GET(req, res) { - let resolveList = await redis.get(REDIS_RESOLVE_CONTROLLER); - if (resolveList == null) { - // Redis中不存在就初始化进去 - await redis.set(REDIS_RESOLVE_CONTROLLER, JSON.stringify(GLOBAL_RESOLE_CONTROLLER)); - return new Response(JSON.stringify(GLOBAL_RESOLE_CONTROLLER), { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }); - } - - return new Response(resolveList, { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }); -} - -export async function POST(req) { - try { - const data = await req.json(); - const { selectedTags } = data; - - // 获取所有可能的标签 - const allTags = GLOBAL_RESOLE_CONTROLLER.map(item => item.label); - - // 更新控制器状态 - const updatedController = GLOBAL_RESOLE_CONTROLLER.map(item => ({ - ...item, - value: selectedTags.includes(item.label) ? 1 : 0 - })); - - // 保存到Redis - await redis.set(REDIS_RESOLVE_CONTROLLER, JSON.stringify(updatedController)); - - return new Response(JSON.stringify({ success: true }), { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }); - } catch (error) { - return new Response(JSON.stringify({ success: false, error: error.message }), { - status: 500, - headers: { 'Content-Type': 'application/json' }, - }); - } -} diff --git a/server/app/r/api/system/route.js b/server/app/r/api/system/route.js deleted file mode 100644 index 10b627d..0000000 --- a/server/app/r/api/system/route.js +++ /dev/null @@ -1,63 +0,0 @@ -import { unstable_noStore as noStore } from 'next/cache'; -import si from 'systeminformation'; -import os from 'os'; - -export async function GET(request, { params }) { - // 这个不允许删除,否则无法做到实时获取 - noStore(); - 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 }); - } -} diff --git a/server/app/r/api/update/route.js b/server/app/r/api/update/route.js deleted file mode 100644 index db50ca8..0000000 --- a/server/app/r/api/update/route.js +++ /dev/null @@ -1,241 +0,0 @@ -import { exec } from 'child_process'; -import { promisify } from 'util'; -import fs from 'fs/promises'; -import path from 'path'; -import { REDIS_UPDATE_PATH, REDIS_UPDATE_STATUS } from "../../../../constants/redis.js"; -import { redis } from "../../../../utils/redis.js"; - -const execAsync = promisify(exec); - -// Git错误处理函数 -function handleGitError(error, stderr) { - if (error.message.includes('RPC failed')) { - return '网络连接失败,请检查网络后重试'; - } - if (error.message.includes('early EOF')) { - return '数据传输中断,请重试'; - } - if (error.message.includes('fetch-pack: invalid index-pack output')) { - return '数据包错误,请重试'; - } - if (error.message.includes('Timed out')) { - return '连接超时,请检查网络后重试'; - } - if (error.message.includes('Could not resolveControl host')) { - return '无法解析主机地址,请检查网络'; - } - if (error.message.includes('Permission denied')) { - return '权限被拒绝,请检查git权限配置'; - } - if (error.message.includes('be overwritten by merge')) { - return '存在冲突,请使用强制更新'; - } - - // 如果是其他错误,返回具体错误信息 - return stderr || error.message || '未知错误'; -} - -async function ensureDirectory(dir) { - try { - await fs.access(dir); - } catch { - await fs.mkdir(dir, { recursive: true }); - } -} - -async function copyConfig(src, dest) { - try { - await ensureDirectory(path.dirname(dest)); - await fs.cp(src, dest, { recursive: true }); - console.log(`成功复制配置文件从 ${src} 到 ${dest}`); - return true; - } catch (error) { - console.error(`复制配置文件失败: ${error.message}`); - return false; - } -} - -// 清理更新状态和临时文件 -async function cleanupUpdate(tempDir) { - try { - // 清理临时文件 - await fs.rm(tempDir, { recursive: true, force: true }); - // 清理Redis中的更新状态 - await redis.del(REDIS_UPDATE_STATUS); - await redis.del(REDIS_UPDATE_PATH); - console.log('清理完成'); - } catch (error) { - console.error('清理失败:', error); - } -} - -export async function GET(req) { - try { - const { searchParams } = new URL(req.url); - const isCheck = searchParams.get('check') === 'true'; - const isRestore = searchParams.get('restore') === 'true'; - const isForce = searchParams.get('force') === 'true'; - - // 如果是检查请求 - if (isCheck) { - const updateStatus = await redis.get(REDIS_UPDATE_STATUS); - return new Response(JSON.stringify({ - needsRestore: updateStatus === 'restoring' - }), { - headers: { 'Content-Type': 'application/json' }, - }); - } - - // 如果是恢复请求 - if (isRestore) { - const updateStatus = await redis.get(REDIS_UPDATE_STATUS); - const paths = JSON.parse(await redis.get(REDIS_UPDATE_PATH) || '{}'); - - if (updateStatus === 'restoring' && paths.tempDir && paths.configDir) { - try { - await copyConfig(paths.tempDir, paths.configDir); - await cleanupUpdate(paths.tempDir); - return new Response(JSON.stringify({ - success: true, - message: '配置恢复完成' - }), { - headers: { 'Content-Type': 'application/json' }, - }); - } catch (error) { - return new Response(JSON.stringify({ - success: false, - message: '配置恢复失败:' + error.message - }), { - headers: { 'Content-Type': 'application/json' }, - }); - } - } - - return new Response(JSON.stringify({ - success: true, - message: '无需恢复' - }), { - headers: { 'Content-Type': 'application/json' }, - }); - } - - const projectRoot = path.join(process.cwd(), '..'); - const tempDir = path.join(projectRoot, 'temp', 'update-tmp'); - const configDir = path.join(projectRoot, 'config'); - - // 检查是否有未完成的更新 - const updateStatus = await redis.get(REDIS_UPDATE_STATUS); - if (updateStatus === 'restoring') { - // 如果有未完成的更新,尝试恢复 - const paths = JSON.parse(await redis.get(REDIS_UPDATE_PATH) || '{}'); - if (paths.tempDir && paths.configDir) { - try { - await copyConfig(paths.tempDir, paths.configDir); - console.log('恢复了之前未完成的配置文件更新'); - } finally { - await cleanupUpdate(paths.tempDir); - } - } - } - - console.log('开始新的更新流程'); - - // 保存路径信息到Redis - await redis.set(REDIS_UPDATE_PATH, JSON.stringify({ - tempDir, - configDir - })); - await redis.set(REDIS_UPDATE_STATUS, 'started'); - - // 确保临时目录存在 - await ensureDirectory(tempDir); - - // 备份配置文件 - let configBackedUp = false; - try { - await fs.access(configDir); - await copyConfig(configDir, tempDir); - configBackedUp = true; - console.log('配置文件备份成功'); - await redis.set(REDIS_UPDATE_STATUS, 'backed_up'); - } catch (error) { - console.log('无配置文件需要备份或备份失败:', error.message); - } - - try { - // 执行git操作 - if (isForce) { - console.log('执行强制更新...'); - await execAsync(`git -C "${projectRoot}" checkout .`); - } - - // 标记状态为需要恢复 - await redis.set(REDIS_UPDATE_STATUS, 'restoring'); - - console.log('执行git pull...'); - const { stdout } = await execAsync(`git -C "${projectRoot}" pull --no-rebase`); - - // 恢复配置文件 - if (configBackedUp) { - console.log('开始恢复配置文件...'); - await copyConfig(tempDir, configDir); - } - - // 清理所有临时文件和状态 - await cleanupUpdate(tempDir); - - if (stdout.includes('Already up to date') || stdout.includes('已经是最新')) { - return new Response(JSON.stringify({ - success: true, - message: '已经是最新版本' - }), { - headers: { 'Content-Type': 'application/json' }, - }); - } - - return new Response(JSON.stringify({ - success: true, - message: '更新成功' - }), { - headers: { 'Content-Type': 'application/json' }, - }); - - } catch (error) { - // 如果git操作失败,也尝试恢复配置文件 - if (configBackedUp) { - try { - await copyConfig(tempDir, configDir); - console.log('git操作失败,但配置文件已恢复'); - } catch (restoreError) { - console.error('恢复配置文件失败:', restoreError.message); - } - } - - await cleanupUpdate(tempDir); - - const errorMessage = handleGitError(error, error.stderr); - return new Response(JSON.stringify({ - success: false, - message: errorMessage - }), { - headers: { 'Content-Type': 'application/json' }, - }); - } - - } catch (error) { - console.error('更新过程出错:', error); - - // 确保清理所有临时状态 - const paths = JSON.parse(await redis.get(REDIS_UPDATE_PATH) || '{}'); - if (paths.tempDir) { - await cleanupUpdate(paths.tempDir); - } - - return new Response(JSON.stringify({ - success: false, - message: '更新过程出错:' + (error.message || '未知错误') - }), { - headers: { 'Content-Type': 'application/json' }, - }); - } -} diff --git a/server/app/r/api/version/route.js b/server/app/r/api/version/route.js deleted file mode 100644 index de810b7..0000000 --- a/server/app/r/api/version/route.js +++ /dev/null @@ -1,43 +0,0 @@ -async function getLatestTag() { - // GitHub 和 Gitee 的 API URL - const githubUrl = `https://api.github.com/repos/zhiyu1998/rconsole-plugin/tags`; - const giteeUrl = `https://gitee.com/api/v5/repos/kyrzy0416/rconsole-plugin/tags`; - - // 定义 fetch 请求 - const fetchGitHub = fetch(githubUrl).then(async (response) => { - if (!response.ok) throw new Error("GitHub请求失败"); - const data = await response.json(); - return { source: "GitHub", tag: data }; - }); - - const fetchGitee = fetch(giteeUrl).then(async (response) => { - if (!response.ok) throw new Error("Gitee请求失败"); - const data = await response.json(); - return { source: "Gitee", tag: data }; - }); - - // 使用 Promise.race 竞速 - try { - return await Promise.race([fetchGitHub, fetchGitee]); - } catch (error) { - console.error("无法获取最新的标签:", error.message); - return null; - } -} - -export async function GET(req, res) { - const tags = await getLatestTag(); - console.log(tags); - - let latestTag; - if (tags.source === "Gitee") { - latestTag = tags.tag[tags.length - 1]; - } - latestTag = tags.tag[0]; - console.log(latestTag); - - return new Response(JSON.stringify(latestTag), { - status: 200, - headers: { 'Content-Type': 'application/json' }, - }); -} diff --git a/server/components/TagSelector.jsx b/server/components/TagSelector.jsx deleted file mode 100644 index cd22a68..0000000 --- a/server/components/TagSelector.jsx +++ /dev/null @@ -1,65 +0,0 @@ -import React, { useState, useEffect } from "react"; - -const TagSelector = ({ options = [], initialTags = [], onChange }) => { - const [selectedTags, setSelectedTags] = useState(initialTags); - - useEffect(() => { - setSelectedTags(initialTags); - }, [initialTags]); - - const addTag = (tag) => { - if (!selectedTags.includes(tag)) { - const updatedTags = [...selectedTags, tag]; - setSelectedTags(updatedTags); - if (onChange) onChange(updatedTags); - } - }; - - const removeTag = (tag) => { - const updatedTags = selectedTags.filter((t) => t !== tag); - setSelectedTags(updatedTags); - if (onChange) onChange(updatedTags); - }; - - return ( -
-
- {selectedTags.length > 0 ? ( - selectedTags.map((tag, index) => ( - removeTag(tag)} - > - {tag} - - )) - ) : ( - 暂无标签,请选择一个选项。 - )} -
- - -
- ); -}; - -export default TagSelector; diff --git a/server/components/ThemeToggle.jsx b/server/components/ThemeToggle.jsx deleted file mode 100644 index 67ba3a8..0000000 --- a/server/components/ThemeToggle.jsx +++ /dev/null @@ -1,23 +0,0 @@ -import React, { useState } from 'react'; - -function ThemeToggle() { - // 用于保存主题状态,默认为“light”主题 - const [isDarkTheme, setIsDarkTheme] = useState(false); - - // 切换主题时的处理函数 - const handleThemeChange = () => { - setIsDarkTheme(!isDarkTheme); - }; - - return ( - - ); -} - -export default ThemeToggle; diff --git a/server/components/common/ConfigItem.jsx b/server/components/common/ConfigItem.jsx deleted file mode 100644 index 33b36bf..0000000 --- a/server/components/common/ConfigItem.jsx +++ /dev/null @@ -1,47 +0,0 @@ -import React from 'react'; - -export const ConfigToggle = ({ label, checked, onChange }) => ( -
- -
-); - -export const ConfigInput = ({ label, value, onChange, type = "text", placeholder = "" }) => ( -
- - onChange(type === "number" ? parseInt(e.target.value) : e.target.value)} - placeholder={placeholder} - className="input input-bordered" - /> -
-); - -export const ConfigSelect = ({ label, value, onChange, options }) => ( -
- - -
-); \ No newline at end of file diff --git a/server/components/content.jsx b/server/components/content.jsx deleted file mode 100644 index 135534a..0000000 --- a/server/components/content.jsx +++ /dev/null @@ -1,13 +0,0 @@ -import { SIDEBAR_ITEMS } from "../constants/sidebar.js"; - -export function Content({ activeItem }) { - // 查找当前激活项 - const currentItem = SIDEBAR_ITEMS.find(item => item.name === activeItem); - - // 如果没找到则返回总控制台 - return ( -
- {currentItem?.component || SIDEBAR_ITEMS[0].component} -
- ); -} diff --git a/server/components/contents/bili.jsx b/server/components/contents/bili.jsx deleted file mode 100644 index 44627de..0000000 --- a/server/components/contents/bili.jsx +++ /dev/null @@ -1,156 +0,0 @@ -import { useState, useEffect } from 'react'; -import { BILI_CDN_SELECT_LIST, BILI_DOWNLOAD_METHOD, BILI_RESOLUTION_LIST } from "../../../constants/constant.js"; -import { readYamlConfig, updateYamlConfig } from '../../utils/yamlHelper'; -import Toast from "../toast.jsx"; -import { ConfigToggle, ConfigInput, ConfigSelect } from '../common/ConfigItem'; - -// 定义配置项 -const BILI_CONFIG = { - toggles: [ - { key: 'biliDisplayCover', label: '显示封面' }, - { key: 'biliDisplayInfo', label: '显示视频信息' }, - { key: 'biliDisplayIntro', label: '显示简介' }, - { key: 'biliDisplayOnline', label: '显示在线人数' }, - { key: 'biliDisplaySummary', label: '显示总结' }, - { key: 'biliUseBBDown', label: '使用BBDown' }, - ], - inputs: [ - { key: 'biliSessData', label: 'SESSDATA', type: 'text', placeholder: '请输入Bilibili SESSDATA' }, - { key: 'biliDuration', label: '视频时长限制(秒)', type: 'number' }, - { key: 'biliIntroLenLimit', label: '简介长度限制', type: 'number' }, - ], - selects: [ - { key: 'biliCDN', label: 'CDN选择', options: BILI_CDN_SELECT_LIST }, - { key: 'biliDownloadMethod', label: '下载方式', options: BILI_DOWNLOAD_METHOD }, - { key: 'biliResolution', label: '视频画质', options: BILI_RESOLUTION_LIST }, - ] -}; - -// 默认配置 -const DEFAULT_CONFIG = { - biliSessData: '', - biliDuration: 480, - biliIntroLenLimit: 50, - biliDisplayCover: true, - biliDisplayInfo: true, - biliDisplayIntro: true, - biliDisplayOnline: true, - biliDisplaySummary: false, - biliUseBBDown: false, - biliCDN: 0, - biliDownloadMethod: 0, - biliResolution: 5 -}; - -export default function Bili() { - const [config, setConfig] = useState(DEFAULT_CONFIG); - const [loading, setLoading] = useState(false); - - useEffect(() => { - const loadConfig = async () => { - const yamlConfig = await readYamlConfig(); - if (yamlConfig) { - const newConfig = {}; - Object.keys(DEFAULT_CONFIG).forEach(key => { - newConfig[key] = yamlConfig[key] ?? DEFAULT_CONFIG[key]; - }); - setConfig(newConfig); - } - }; - loadConfig(); - }, []); - - const handleSave = async () => { - setLoading(true); - try { - const success = await updateYamlConfig(config); - if (success) { - document.getElementById('toast-success').classList.remove('hidden'); - setTimeout(() => { - document.getElementById('toast-success').classList.add('hidden'); - }, 3000); - } - } catch (error) { - console.error('保存配置失败:', error); - } finally { - setLoading(false); - } - }; - - const handleConfigChange = (key, value) => { - setConfig(prev => ({ ...prev, [key]: value })); - }; - - return ( -
- - -
-

Bilibili 配置

- -
-
-

基础配置

- - {/* 输入框配置 */} -
- {BILI_CONFIG.inputs.map(item => ( - handleConfigChange(item.key, value)} - placeholder={item.placeholder} - /> - ))} -
- - {/* 开关配置 */} -
- {BILI_CONFIG.toggles.map(item => ( - handleConfigChange(item.key, value)} - /> - ))} -
- - {/* 选择框配置 */} -
- {BILI_CONFIG.selects.map(item => ( - handleConfigChange(item.key, value)} - options={item.options} - /> - ))} -
-
-
- - {/* 操作按钮 */} -
- - -
-
-
- ); -} diff --git a/server/components/contents/generic.jsx b/server/components/contents/generic.jsx deleted file mode 100644 index 53d2746..0000000 --- a/server/components/contents/generic.jsx +++ /dev/null @@ -1,359 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { readYamlConfig, updateYamlConfig } from '../../utils/yamlHelper'; -import { ConfigInput, ConfigSelect, ConfigToggle } from '../common/ConfigItem'; -import TagSelector from "../TagSelector.jsx"; -import Toast from "../toast.jsx"; - -// 定义配置项 -const GENERIC_CONFIG = { - basicInputs: [ - { - key: 'defaultPath', - label: '视频保存路径', - type: 'text', - placeholder: '请输入视频保存路径...', - defaultValue: './data/rcmp4/' - }, - { - key: 'videoSizeLimit', - label: '视频大小限制(MB)', - type: 'number', - hint: '超过限制转为群文件', - defaultValue: 70 - } - ], - proxyInputs: [ - { - key: 'proxyAddr', - label: '魔法地址', - type: 'text', - placeholder: '请输入代理地址...', - defaultValue: '127.0.0.1' - }, - { - key: 'proxyPort', - label: '魔法端口', - type: 'text', - placeholder: '请输入代理端口...', - defaultValue: '7890' - } - ], - streamInputs: [ - { - key: 'identifyPrefix', - label: '识别前缀', - type: 'text', - placeholder: '请输入识别前缀...', - defaultValue: '' - }, - { - key: 'streamDuration', - label: '视频最大时长(秒)', - type: 'number', - defaultValue: 10 - } - ], - concurrencyInputs: [ - { - key: 'queueConcurrency', - label: '队列并发数', - type: 'number', - hint: '仅影响B站下载', - defaultValue: 1 - }, - { - key: 'videoDownloadConcurrency', - label: '视频下载并发数', - type: 'number', - defaultValue: 1 - } - ], - textareas: [ - { - key: 'deeplApiUrls', - label: 'DeepL API地址', - placeholder: '请输入DeepL API地址,多个地址用逗号分隔...', - defaultValue: '' - } - ], - toggles: [ - { - key: 'streamCompatibility', - label: '兼容模式', - hint: 'NCQQ不用开启,其他ICQQ、LLO需要开启', - defaultValue: false - } - ], - otherInputs: [ - { - key: 'xiaohongshuCookie', - label: '小红书Cookie', - type: 'text', - placeholder: '请输入小红书的Cookie...', - defaultValue: '' - }, - { - key: 'autoclearTrashtime', - label: '自动清理时间', - type: 'text', - placeholder: '请输入Cron表达式...', - hint: 'Cron表达式', - defaultValue: '0 0 8 * * ?' - } - ], - aiInputs: [ - { - key: 'aiBaseURL', - label: 'AI接口地址', - type: 'text', - placeholder: '请输入AI接口地址...', - defaultValue: '', - hint: '用于识图的接口,kimi默认接口为:https://api.moonshot.cn,其他服务商自己填写' - }, - { - key: 'aiApiKey', - label: 'API Key', - type: 'text', - placeholder: '请输入API Key...', - defaultValue: '', - hint: '用于识图的api key,kimi接口申请:https://platform.moonshot.cn/console/api-keys' - } - ], - aiSelects: [ - { - key: 'aiModel', - label: 'AI模型', - options: [ - { value: 'moonshot-v1-8k', label: 'Moonshot V1 8K' }, - { value: 'moonshot-v1-32k', label: 'Moonshot V1 32K' }, - { value: 'moonshot-v1-128k', label: 'Moonshot V1 128K' }, - // 可以根据需要添加更多模型选项 - ], - defaultValue: 'moonshot-v1-8k', - hint: '模型,使用kimi不用填写,其他要填写' - } - ] -}; - -// 生成默认配置 -const DEFAULT_CONFIG = Object.values(GENERIC_CONFIG).reduce((acc, group) => { - group.forEach(item => { - acc[item.key] = item.defaultValue; - }); - return acc; -}, {}); - -export default function Generic() { - const [config, setConfig] = useState(DEFAULT_CONFIG); - const [loading, setLoading] = useState(false); - const [resolveOptions, setResolveOptions] = useState([]); - const [selectedResolveTags, setSelectedResolveTags] = useState([]); - - useEffect(() => { - const loadConfig = async () => { - const yamlConfig = await readYamlConfig(); - if (yamlConfig) { - const newConfig = {}; - Object.keys(DEFAULT_CONFIG).forEach(key => { - newConfig[key] = yamlConfig[key] ?? DEFAULT_CONFIG[key]; - }); - setConfig(newConfig); - } - }; - loadConfig(); - }, []); - - useEffect(() => { - // 获取解析控制器配置 - const fetchResolveControl = async () => { - try { - const response = await fetch('/r/api/resolveControl'); - const data = await response.json(); - const enabledTags = data - .filter(item => item.value === 1) - .map(item => item.label); - setSelectedResolveTags(enabledTags); - setResolveOptions(data.map(item => item.label)); - } catch (error) { - console.error('获取解析控制器配置失败:', error); - } - }; - - fetchResolveControl(); - }, []); - - const handleSave = async () => { - setLoading(true); - try { - const success = await updateYamlConfig(config); - if (success) { - document.getElementById('generic-toast-success').classList.remove('hidden'); - setTimeout(() => { - document.getElementById('generic-toast-success').classList.add('hidden'); - }, 3000); - } - } catch (error) { - console.error('保存配置失败:', error); - } finally { - setLoading(false); - } - }; - - const handleConfigChange = (key, value) => { - setConfig(prev => ({ ...prev, [key]: value })); - }; - - const handleResolveTagsChange = async (tags) => { - try { - const response = await fetch('/r/api/resolveControl', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ selectedTags: tags }), - }); - - if (response.ok) { - setSelectedResolveTags(tags); - } - } catch (error) { - console.error('更新解析控制器配置失败:', error); - } - }; - - // 渲染输入框组 - const renderInputGroup = (inputs, title) => ( -
- {inputs.map(item => ( -
- handleConfigChange(item.key, value)} - placeholder={item.placeholder} - /> - {item.hint && ( - - {item.hint} - - )} -
- ))} -
- ); - - return ( -
- - -
-

通用配置

- -
-
-

基础配置

- - {/* 基础配置 */} - {renderInputGroup(GENERIC_CONFIG.basicInputs)} - - {/* 解析控制 */} -

全局解析控制

- - - {/* 代理配置 */} -

代理设置

- {renderInputGroup(GENERIC_CONFIG.proxyInputs)} - - {/* 流媒体配置 */} -

流媒体设置

- {renderInputGroup(GENERIC_CONFIG.streamInputs)} - - {/* 并发配置 */} -

并发设置

- {renderInputGroup(GENERIC_CONFIG.concurrencyInputs)} - - {/* DeepL API配置 */} -

API设置

- {GENERIC_CONFIG.textareas.map(item => ( -
- -