diff --git a/package.json b/package.json
index a75ac9f..254174c 100644
--- a/package.json
+++ b/package.json
@@ -5,6 +5,7 @@
"dependencies": {
"axios": "^1.3.4",
"form-data": "^4.0.1",
+ "js-yaml": "^4.1.0",
"next": "^14.2.16",
"node-id3": "^0.2.6",
"p-queue": "^8.0.1",
diff --git a/server/app/r/api/config/route.js b/server/app/r/api/config/route.js
new file mode 100644
index 0000000..bfe1a14
--- /dev/null
+++ b/server/app/r/api/config/route.js
@@ -0,0 +1,52 @@
+import fs from 'fs';
+import yaml from 'js-yaml';
+import path from 'path';
+
+export async function GET(req, res) {
+ const configPath = path.join(process.cwd(), "../", 'config', 'tools.yaml');
+ 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) {
+ const configPath = path.join(process.cwd(), "../", 'config', 'tools.yaml');
+ 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/components/content.jsx b/server/components/content.jsx
index 39ae729..8b18e1a 100644
--- a/server/components/content.jsx
+++ b/server/components/content.jsx
@@ -1,15 +1,21 @@
import Bili from "./contents/bili.jsx";
+import Generic from "./contents/generic.jsx";
import Home from "./contents/home.jsx";
+import Ncm from "./contents/ncm.jsx";
import Tiktok from "./contents/tiktok.jsx";
import Weekly from "./contents/weekly.jsx";
+import Youtube from "./contents/youtube.jsx";
export function Content({ activeItem }) {
// 使用对象映射内容,以便于后期扩展和维护
const contentMap = {
"总控制台": ,
+ "通用及杂项": ,
"哔哩哔哩控制台": ,
"抖音控制台": ,
+ "网易云控制台": ,
+ "油管控制台": ,
"周刊预览":
};
diff --git a/server/components/contents/bili.jsx b/server/components/contents/bili.jsx
index e98ba6e..2184832 100644
--- a/server/components/contents/bili.jsx
+++ b/server/components/contents/bili.jsx
@@ -1,7 +1,297 @@
-export default function Bili() {
+import { useState, useEffect } from 'react';
+import { readYamlConfig, updateYamlConfig } from '../../utils/yamlHelper';
+export default function Bili() {
+ const [config, setConfig] = useState({
+ biliSessData: '',
+ biliDuration: 480,
+ biliIntroLenLimit: 50,
+ biliDisplayCover: true,
+ biliDisplayInfo: true,
+ biliDisplayIntro: true,
+ biliDisplayOnline: true,
+ biliDisplaySummary: false,
+ biliUseBBDown: false,
+ biliCDN: 0,
+ biliDownloadMethod: 0,
+ biliResolution: 5
+ });
+
+ const [loading, setLoading] = useState(false);
+
+ // 读取配置
+ useEffect(() => {
+ const loadConfig = async () => {
+ const yamlConfig = await readYamlConfig();
+ if (yamlConfig) {
+ setConfig({
+ biliSessData: yamlConfig.biliSessData || '',
+ biliDuration: yamlConfig.biliDuration || 480,
+ biliIntroLenLimit: yamlConfig.biliIntroLenLimit || 50,
+ biliDisplayCover: yamlConfig.biliDisplayCover ?? true,
+ biliDisplayInfo: yamlConfig.biliDisplayInfo ?? true,
+ biliDisplayIntro: yamlConfig.biliDisplayIntro ?? true,
+ biliDisplayOnline: yamlConfig.biliDisplayOnline ?? true,
+ biliDisplaySummary: yamlConfig.biliDisplaySummary ?? false,
+ biliUseBBDown: yamlConfig.biliUseBBDown ?? false,
+ biliCDN: yamlConfig.biliCDN || 0,
+ biliDownloadMethod: yamlConfig.biliDownloadMethod || 0,
+ biliResolution: yamlConfig.biliResolution || 5
+ });
+ }
+ };
+
+ loadConfig();
+ }, []);
+
+ // 保存配置
+ const handleSave = async () => {
+ setLoading(true);
+ try {
+ const success = await updateYamlConfig({
+ biliSessData: config.biliSessData,
+ biliDuration: config.biliDuration,
+ biliIntroLenLimit: config.biliIntroLenLimit,
+ biliDisplayCover: config.biliDisplayCover,
+ biliDisplayInfo: config.biliDisplayInfo,
+ biliDisplayIntro: config.biliDisplayIntro,
+ biliDisplayOnline: config.biliDisplayOnline,
+ biliDisplaySummary: config.biliDisplaySummary,
+ biliUseBBDown: config.biliUseBBDown,
+ biliCDN: config.biliCDN,
+ biliDownloadMethod: config.biliDownloadMethod,
+ biliResolution: config.biliResolution
+ });
+
+ if (success) {
+ // 使用 daisyUI 的 toast 提示
+ 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 handleReset = async () => {
+ const yamlConfig = await readYamlConfig();
+ if (yamlConfig) {
+ setConfig({
+ biliSessData: yamlConfig.biliSessData || '',
+ biliDuration: yamlConfig.biliDuration || 480,
+ biliIntroLenLimit: yamlConfig.biliIntroLenLimit || 50,
+ biliDisplayCover: yamlConfig.biliDisplayCover ?? true,
+ biliDisplayInfo: yamlConfig.biliDisplayInfo ?? true,
+ biliDisplayIntro: yamlConfig.biliDisplayIntro ?? true,
+ biliDisplayOnline: yamlConfig.biliDisplayOnline ?? true,
+ biliDisplaySummary: yamlConfig.biliDisplaySummary ?? false,
+ biliUseBBDown: yamlConfig.biliUseBBDown ?? false,
+ biliCDN: yamlConfig.biliCDN || 0,
+ biliDownloadMethod: yamlConfig.biliDownloadMethod || 0,
+ biliResolution: yamlConfig.biliResolution || 5
+ });
+ }
+ };
return (
-
Bili
+
+ {/* 成功提示 */}
+
+
+
+
Bilibili 配置
+
+ {/* 基础配置部分 */}
+
+
+
基础配置
+
+ {/* SESSDATA配置 */}
+
+
+ setConfig({ ...config, biliSessData: e.target.value })}
+ placeholder="请输入Bilibili SESSDATA"
+ className="input input-bordered w-full"
+ />
+
+
+ {/* 数值配置部分 */}
+
+
+ {/* 开关配置部分 */}
+
+
+ {/* 下拉选择配置部分 */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* 保存按钮 */}
+
+
+
+
+
+
)
}
diff --git a/server/components/contents/generic.jsx b/server/components/contents/generic.jsx
new file mode 100644
index 0000000..bf08997
--- /dev/null
+++ b/server/components/contents/generic.jsx
@@ -0,0 +1,289 @@
+import { useState, useEffect } from 'react';
+import { readYamlConfig, updateYamlConfig } from '../../utils/yamlHelper';
+
+export default function Generic() {
+ const [config, setConfig] = useState({
+ defaultPath: './data/rcmp4/',
+ videoSizeLimit: 70,
+ proxyAddr: '127.0.0.1',
+ proxyPort: '7890',
+ identifyPrefix: '',
+ streamDuration: 10,
+ streamCompatibility: false,
+ queueConcurrency: 1,
+ videoDownloadConcurrency: 1,
+ autoclearTrashtime: '0 0 8 * * ?',
+ deeplApiUrls: ''
+ });
+
+ const [loading, setLoading] = useState(false);
+
+ // 读取配置
+ useEffect(() => {
+ const loadConfig = async () => {
+ const yamlConfig = await readYamlConfig();
+ if (yamlConfig) {
+ setConfig({
+ defaultPath: yamlConfig.defaultPath || './data/rcmp4/',
+ videoSizeLimit: yamlConfig.videoSizeLimit || 70,
+ proxyAddr: yamlConfig.proxyAddr || '127.0.0.1',
+ proxyPort: yamlConfig.proxyPort || '7890',
+ identifyPrefix: yamlConfig.identifyPrefix || '',
+ streamDuration: yamlConfig.streamDuration || 10,
+ streamCompatibility: yamlConfig.streamCompatibility ?? false,
+ queueConcurrency: yamlConfig.queueConcurrency || 1,
+ videoDownloadConcurrency: yamlConfig.videoDownloadConcurrency || 1,
+ autoclearTrashtime: yamlConfig.autoclearTrashtime || '0 0 8 * * ?',
+ deeplApiUrls: yamlConfig.deeplApiUrls || ''
+ });
+ }
+ };
+
+ loadConfig();
+ }, []);
+
+ // 保存配置
+ const handleSave = async () => {
+ setLoading(true);
+ try {
+ const success = await updateYamlConfig({
+ defaultPath: config.defaultPath,
+ videoSizeLimit: config.videoSizeLimit,
+ proxyAddr: config.proxyAddr,
+ proxyPort: config.proxyPort,
+ identifyPrefix: config.identifyPrefix,
+ streamDuration: config.streamDuration,
+ streamCompatibility: config.streamCompatibility,
+ queueConcurrency: config.queueConcurrency,
+ videoDownloadConcurrency: config.videoDownloadConcurrency,
+ autoclearTrashtime: config.autoclearTrashtime,
+ deeplApiUrls: config.deeplApiUrls
+ });
+
+ 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 handleReset = async () => {
+ const yamlConfig = await readYamlConfig();
+ if (yamlConfig) {
+ setConfig({
+ defaultPath: yamlConfig.defaultPath || './data/rcmp4/',
+ videoSizeLimit: yamlConfig.videoSizeLimit || 70,
+ proxyAddr: yamlConfig.proxyAddr || '127.0.0.1',
+ proxyPort: yamlConfig.proxyPort || '7890',
+ identifyPrefix: yamlConfig.identifyPrefix || '',
+ streamDuration: yamlConfig.streamDuration || 10,
+ streamCompatibility: yamlConfig.streamCompatibility ?? false,
+ queueConcurrency: yamlConfig.queueConcurrency || 1,
+ videoDownloadConcurrency: yamlConfig.videoDownloadConcurrency || 1,
+ autoclearTrashtime: yamlConfig.autoclearTrashtime || '0 0 8 * * ?',
+ deeplApiUrls: yamlConfig.deeplApiUrls || ''
+ });
+ }
+ };
+
+ return (
+
+ {/* 成功提示 */}
+
+
+
+
通用配置
+
+ {/* 基础配置部分 */}
+
+
+
基础配置
+
+ {/* 路径和大小限制配置 */}
+
+
+ {/* 代理配置 */}
+
+
+ {/* 其他基础配置 */}
+
+
+ {/* 并发和定时配置 */}
+
+
+ {/* DeepL API配置 */}
+
+
+
+
+ {/* 开关配置 */}
+
+
+
+ NCQQ不用开启,其他ICQQ、LLO需要开启
+
+
+
+ {/* 定时清理配置 */}
+
+
+ setConfig({ ...config, autoclearTrashtime: e.target.value })}
+ placeholder="请输入Cron表达式..."
+ className="input input-bordered"
+ />
+
+
+
+
+ {/* 保存按钮 */}
+
+
+
+
+
+
+ );
+}
diff --git a/server/components/contents/ncm.jsx b/server/components/contents/ncm.jsx
new file mode 100644
index 0000000..acbd154
--- /dev/null
+++ b/server/components/contents/ncm.jsx
@@ -0,0 +1,242 @@
+import { useState, useEffect } from 'react';
+import { readYamlConfig, updateYamlConfig } from '../../utils/yamlHelper';
+
+export default function Ncm() {
+ const [config, setConfig] = useState({
+ useLocalNeteaseAPI: false,
+ useNeteaseSongRequest: false,
+ isSendVocal: true,
+ songRequestMaxList: 10,
+ neteaseCookie: '',
+ neteaseCloudAPIServer: '',
+ neteaseCloudAudioQuality: 'exhigh',
+ neteaseUserId: ''
+ });
+
+ const [loading, setLoading] = useState(false);
+
+ // 读取配置
+ useEffect(() => {
+ const loadConfig = async () => {
+ const yamlConfig = await readYamlConfig();
+ if (yamlConfig) {
+ setConfig({
+ useLocalNeteaseAPI: yamlConfig.useLocalNeteaseAPI ?? false,
+ useNeteaseSongRequest: yamlConfig.useNeteaseSongRequest ?? false,
+ isSendVocal: yamlConfig.isSendVocal ?? true,
+ songRequestMaxList: yamlConfig.songRequestMaxList || 10,
+ neteaseCookie: yamlConfig.neteaseCookie || '',
+ neteaseCloudAPIServer: yamlConfig.neteaseCloudAPIServer || '',
+ neteaseCloudAudioQuality: yamlConfig.neteaseCloudAudioQuality || 'exhigh',
+ neteaseUserId: yamlConfig.neteaseUserId || ''
+ });
+ }
+ };
+
+ loadConfig();
+ }, []);
+
+ // 保存配置
+ const handleSave = async () => {
+ setLoading(true);
+ try {
+ const success = await updateYamlConfig({
+ useLocalNeteaseAPI: config.useLocalNeteaseAPI,
+ useNeteaseSongRequest: config.useNeteaseSongRequest,
+ isSendVocal: config.isSendVocal,
+ songRequestMaxList: config.songRequestMaxList,
+ neteaseCookie: config.neteaseCookie,
+ neteaseCloudAPIServer: config.neteaseCloudAPIServer,
+ neteaseCloudAudioQuality: config.neteaseCloudAudioQuality,
+ neteaseUserId: config.neteaseUserId
+ });
+
+ if (success) {
+ document.getElementById('ncm-toast-success').classList.remove('hidden');
+ setTimeout(() => {
+ document.getElementById('ncm-toast-success').classList.add('hidden');
+ }, 3000);
+ }
+ } catch (error) {
+ console.error('保存配置失败:', error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ // 重置配置
+ const handleReset = async () => {
+ const yamlConfig = await readYamlConfig();
+ if (yamlConfig) {
+ setConfig({
+ useLocalNeteaseAPI: yamlConfig.useLocalNeteaseAPI ?? false,
+ useNeteaseSongRequest: yamlConfig.useNeteaseSongRequest ?? false,
+ isSendVocal: yamlConfig.isSendVocal ?? true,
+ songRequestMaxList: yamlConfig.songRequestMaxList || 10,
+ neteaseCookie: yamlConfig.neteaseCookie || '',
+ neteaseCloudAPIServer: yamlConfig.neteaseCloudAPIServer || '',
+ neteaseCloudAudioQuality: yamlConfig.neteaseCloudAudioQuality || 'exhigh',
+ neteaseUserId: yamlConfig.neteaseUserId || ''
+ });
+ }
+ };
+
+ const audioQualityOptions = [
+ { value: 'standard', label: '标准' },
+ { value: 'higher', label: '较高' },
+ { value: 'exhigh', label: '极高' },
+ { value: 'lossless', label: '无损' },
+ { value: 'hires', label: 'Hi-Res' },
+ { value: 'jyeffect', label: '高清环绕声' },
+ { value: 'sky', label: '沉浸环绕声' },
+ { value: 'dolby', label: '杜比全景声' },
+ { value: 'jymaster', label: '超清母带' }
+ ];
+
+ return (
+
+ {/* 成功提示 */}
+
+
+
+
网易云音乐配置
+
+ {/* 基础配置部分 */}
+
+
+
基础配置
+
+ {/* 文本输入配置 */}
+
+
+
+
+
+
+ setConfig({ ...config, neteaseCloudAPIServer: e.target.value })}
+ placeholder="请输入API服务器地址..."
+ className="input input-bordered w-full"
+ />
+
+
+
+ setConfig({ ...config, neteaseUserId: e.target.value })}
+ placeholder="网易云用户ID"
+ className="input input-bordered w-full"
+ />
+
+
+
+ {/* 开关配置部分 */}
+
+
+ {/* 其他配置 */}
+
+
+
+ setConfig({ ...config, songRequestMaxList: parseInt(e.target.value) })}
+ className="input input-bordered"
+ />
+
+
+
+
+
+
+
+
+
+ {/* 保存按钮 */}
+
+
+
+
+
+
+ );
+}
diff --git a/server/components/contents/tiktok.jsx b/server/components/contents/tiktok.jsx
index 95bcac4..38ea112 100644
--- a/server/components/contents/tiktok.jsx
+++ b/server/components/contents/tiktok.jsx
@@ -1,5 +1,151 @@
+import { useState, useEffect } from 'react';
+import { readYamlConfig, updateYamlConfig } from '../../utils/yamlHelper';
+
export default function Tiktok() {
+ const [config, setConfig] = useState({
+ douyinCookie: '',
+ douyinCompression: true,
+ douyinComments: false
+ });
+
+ const [loading, setLoading] = useState(false);
+
+ // 读取配置
+ useEffect(() => {
+ const loadConfig = async () => {
+ const yamlConfig = await readYamlConfig();
+ if (yamlConfig) {
+ setConfig({
+ douyinCookie: yamlConfig.douyinCookie || '',
+ douyinCompression: yamlConfig.douyinCompression ?? true,
+ douyinComments: yamlConfig.douyinComments ?? false
+ });
+ }
+ };
+
+ loadConfig();
+ }, []);
+
+ // 保存配置
+ const handleSave = async () => {
+ setLoading(true);
+ try {
+ const success = await updateYamlConfig({
+ douyinCookie: config.douyinCookie,
+ douyinCompression: config.douyinCompression,
+ douyinComments: config.douyinComments
+ });
+
+ if (success) {
+ document.getElementById('tiktok-toast-success').classList.remove('hidden');
+ setTimeout(() => {
+ document.getElementById('tiktok-toast-success').classList.add('hidden');
+ }, 3000);
+ }
+ } catch (error) {
+ console.error('保存配置失败:', error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ // 重置配置
+ const handleReset = async () => {
+ const yamlConfig = await readYamlConfig();
+ if (yamlConfig) {
+ setConfig({
+ douyinCookie: yamlConfig.douyinCookie || '',
+ douyinCompression: yamlConfig.douyinCompression ?? true,
+ douyinComments: yamlConfig.douyinComments ?? false
+ });
+ }
+ };
+
return (
- Tiktok
- )
+
+ {/* 成功提示 */}
+
+
+
+
抖音配置
+
+ {/* 基础配置部分 */}
+
+
+
基础配置
+
+ {/* Cookie配置 */}
+
+
+
+
+ {/* 开关配置部分 */}
+
+
+
+
+ {/* 保存按钮 */}
+
+
+
+
+
+
+ );
}
diff --git a/server/components/contents/youtube.jsx b/server/components/contents/youtube.jsx
new file mode 100644
index 0000000..d460af7
--- /dev/null
+++ b/server/components/contents/youtube.jsx
@@ -0,0 +1,164 @@
+import { useState, useEffect } from 'react';
+import { readYamlConfig, updateYamlConfig } from '../../utils/yamlHelper';
+
+export default function Youtube() {
+ const [config, setConfig] = useState({
+ youtubeGraphicsOptions: 720,
+ youtubeClipTime: 0,
+ youtubeDuration: 480,
+ youtubeCookiePath: ''
+ });
+
+ const [loading, setLoading] = useState(false);
+
+ // 读取配置
+ useEffect(() => {
+ const loadConfig = async () => {
+ const yamlConfig = await readYamlConfig();
+ if (yamlConfig) {
+ setConfig({
+ youtubeGraphicsOptions: yamlConfig.youtubeGraphicsOptions || 720,
+ youtubeClipTime: yamlConfig.youtubeClipTime || 0,
+ youtubeDuration: yamlConfig.youtubeDuration || 480,
+ youtubeCookiePath: yamlConfig.youtubeCookiePath || ''
+ });
+ }
+ };
+
+ loadConfig();
+ }, []);
+
+ // 保存配置
+ const handleSave = async () => {
+ setLoading(true);
+ try {
+ const success = await updateYamlConfig({
+ youtubeGraphicsOptions: config.youtubeGraphicsOptions,
+ youtubeClipTime: config.youtubeClipTime,
+ youtubeDuration: config.youtubeDuration,
+ youtubeCookiePath: config.youtubeCookiePath
+ });
+
+ if (success) {
+ document.getElementById('youtube-toast-success').classList.remove('hidden');
+ setTimeout(() => {
+ document.getElementById('youtube-toast-success').classList.add('hidden');
+ }, 3000);
+ }
+ } catch (error) {
+ console.error('保存配置失败:', error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ // 重置配置
+ const handleReset = async () => {
+ const yamlConfig = await readYamlConfig();
+ if (yamlConfig) {
+ setConfig({
+ youtubeGraphicsOptions: yamlConfig.youtubeGraphicsOptions || 720,
+ youtubeClipTime: yamlConfig.youtubeClipTime || 0,
+ youtubeDuration: yamlConfig.youtubeDuration || 480,
+ youtubeCookiePath: yamlConfig.youtubeCookiePath || ''
+ });
+ }
+ };
+
+ return (
+
+ {/* 成功提示 */}
+
+
+
+
YouTube 配置
+
+ {/* 基础配置部分 */}
+
+
+
基础配置
+
+ {/* Cookie路径配置 */}
+
+
+ setConfig({ ...config, youtubeCookiePath: e.target.value })}
+ placeholder="请输入Cookie.txt文件路径..."
+ className="input input-bordered w-full"
+ />
+
+
+ {/* 数值配置部分 */}
+
+
+
+
+
+
+
+ setConfig({ ...config, youtubeClipTime: parseInt(e.target.value) })}
+ className="input input-bordered"
+ />
+
+
+
+ setConfig({ ...config, youtubeDuration: parseInt(e.target.value) })}
+ className="input input-bordered"
+ />
+
+
+
+
+
+ {/* 保存按钮 */}
+
+
+
+
+
+
+ );
+}
diff --git a/server/components/home/bot-config.jsx b/server/components/home/bot-config.jsx
index a62013c..5622e5d 100644
--- a/server/components/home/bot-config.jsx
+++ b/server/components/home/bot-config.jsx
@@ -2,55 +2,26 @@ export function BotConfig() {
return (
-
🔥热门快捷配置
+
🔥快捷更新
-
通用
-
R 插件一些通用配置:魔法配置、线程配置等
-
-
-
-
-
-
-
哔哩哔哩
-
哔哩哔哩相关配置
+
检查更新
+
当前最新版本为:v0.0.0
-
抖音
-
抖音相关配置
+
非强制更新
+
R 插件的非强制更新,力度小
+
+
+
+
diff --git a/server/constants/sidebar.js b/server/constants/sidebar.js
index ffb496f..b070bec 100644
--- a/server/constants/sidebar.js
+++ b/server/constants/sidebar.js
@@ -2,26 +2,39 @@ export const SIDEBAR_ITEMS = [
{
name: "总控制台",
icon:
,
theme: "light"
},
+ {
+ name: "通用及杂项",
+ icon:
,
+ theme: "cupcake"
+ },
{
name: "哔哩哔哩控制台",
icon: