diff --git a/package.json b/package.json
index 8c73196..a75ac9f 100644
--- a/package.json
+++ b/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"
}
}
diff --git a/server/app/layout.jsx b/server/app/layout.jsx
new file mode 100644
index 0000000..e8acbcc
--- /dev/null
+++ b/server/app/layout.jsx
@@ -0,0 +1,9 @@
+import "../styles/global.css";
+
+export default function RootLayout({ children }) {
+ return (
+
+
{children}
+
+ )
+}
diff --git a/server/app/page.jsx b/server/app/page.jsx
new file mode 100644
index 0000000..4e3a190
--- /dev/null
+++ b/server/app/page.jsx
@@ -0,0 +1,3 @@
+export default function Page() {
+ return
+}
diff --git a/server/app/r/api/napcat/[pid]/route.js b/server/app/r/api/napcat/[pid]/route.js
new file mode 100644
index 0000000..0fd7fc0
--- /dev/null
+++ b/server/app/r/api/napcat/[pid]/route.js
@@ -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' },
+ });
+}
diff --git a/server/app/r/api/network/route.js b/server/app/r/api/network/route.js
new file mode 100644
index 0000000..ba2c6af
--- /dev/null
+++ b/server/app/r/api/network/route.js
@@ -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' },
+ })
+ }
+}
diff --git a/server/app/r/api/system/route.js b/server/app/r/api/system/route.js
new file mode 100644
index 0000000..af08533
--- /dev/null
+++ b/server/app/r/api/system/route.js
@@ -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 });
+ }
+}
diff --git a/server/app/r/page.js b/server/app/r/page.js
new file mode 100644
index 0000000..3bc2d81
--- /dev/null
+++ b/server/app/r/page.js
@@ -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 (
+
+
+
+
+ )
+}
diff --git a/server/assets/fonts/FiraCode-VF.woff2 b/server/assets/fonts/FiraCode-VF.woff2
new file mode 100644
index 0000000..e755a9d
Binary files /dev/null and b/server/assets/fonts/FiraCode-VF.woff2 differ
diff --git a/server/assets/fonts/SourceHanSerifCN-VF.woff2 b/server/assets/fonts/SourceHanSerifCN-VF.woff2
new file mode 100644
index 0000000..87e20c4
Binary files /dev/null and b/server/assets/fonts/SourceHanSerifCN-VF.woff2 differ
diff --git a/server/components/ThemeToggle.jsx b/server/components/ThemeToggle.jsx
new file mode 100644
index 0000000..67ba3a8
--- /dev/null
+++ b/server/components/ThemeToggle.jsx
@@ -0,0 +1,23 @@
+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/content.jsx b/server/components/content.jsx
new file mode 100644
index 0000000..39ae729
--- /dev/null
+++ b/server/components/content.jsx
@@ -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 = {
+ "总控制台": ,
+ "哔哩哔哩控制台": ,
+ "抖音控制台": ,
+ "周刊预览":
+ };
+
+ return (
+
+ { contentMap[activeItem] || contentMap["总控制台"] }
+
+ );
+}
diff --git a/server/components/contents/bili.jsx b/server/components/contents/bili.jsx
new file mode 100644
index 0000000..e98ba6e
--- /dev/null
+++ b/server/components/contents/bili.jsx
@@ -0,0 +1,7 @@
+export default function Bili() {
+
+
+ return (
+ Bili
+ )
+}
diff --git a/server/components/contents/home.jsx b/server/components/contents/home.jsx
new file mode 100644
index 0000000..eff1a96
--- /dev/null
+++ b/server/components/contents/home.jsx
@@ -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 (
+
+
+
+
+ {/* 监控卡片 */ }
+
+
+
监控
+
+
+
上传: 0.87 KB/s
+
下载: 3.21 KB/s
+
+
+
总发送: 21.17 GB
+
总接收: 90.46 GB
+
+
+
+
+
+
+ );
+}
diff --git a/server/components/contents/tiktok.jsx b/server/components/contents/tiktok.jsx
new file mode 100644
index 0000000..95bcac4
--- /dev/null
+++ b/server/components/contents/tiktok.jsx
@@ -0,0 +1,5 @@
+export default function Tiktok() {
+ return (
+ Tiktok
+ )
+}
diff --git a/server/components/contents/weekly.jsx b/server/components/contents/weekly.jsx
new file mode 100644
index 0000000..c3767d5
--- /dev/null
+++ b/server/components/contents/weekly.jsx
@@ -0,0 +1,11 @@
+export default function Weekly() {
+ return (
+
+
+
+ )
+}
diff --git a/server/components/header.jsx b/server/components/header.jsx
new file mode 100644
index 0000000..1ede9ec
--- /dev/null
+++ b/server/components/header.jsx
@@ -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 (
+
+
+
+
+
+
+
+
+
+

+
+
+
+
{user.nickname || "未获取"}
+
{user.user_id || "NaN"}
+
+
+
+
+ )
+};
diff --git a/server/components/home/bot-config.jsx b/server/components/home/bot-config.jsx
new file mode 100644
index 0000000..a62013c
--- /dev/null
+++ b/server/components/home/bot-config.jsx
@@ -0,0 +1,61 @@
+export function BotConfig() {
+ return (
+
+
+
🔥热门快捷配置
+
+
+
+
通用
+
R 插件一些通用配置:魔法配置、线程配置等
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/server/components/home/bot-info.jsx b/server/components/home/bot-info.jsx
new file mode 100644
index 0000000..ded27b8
--- /dev/null
+++ b/server/components/home/bot-info.jsx
@@ -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 (
+
+
+ {/* 机器人信息卡片 */ }
+
+
+
+
+
+ )
+}
diff --git a/server/components/home/bot-item.jsx b/server/components/home/bot-item.jsx
new file mode 100644
index 0000000..67557ee
--- /dev/null
+++ b/server/components/home/bot-item.jsx
@@ -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 (
+
+
+
🐔状态
+
+
+
+

+
+
+
+
+
昵称:{ user.nickname || "未获取" }
+
QQ号:{ user.user_id || "NaN" }
+
+
+
协议信息:
+
+
{ versionInfo.app_name }
+
{ versionInfo.app_version }
+
{ versionInfo.protocol_version }
+
+
+
+
+
+
+ )
+}
diff --git a/server/components/home/bot-network.jsx b/server/components/home/bot-network.jsx
new file mode 100644
index 0000000..6fe1563
--- /dev/null
+++ b/server/components/home/bot-network.jsx
@@ -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 (
+
+
+
🐔网速
+
+
+
+
{ linksTime[0] }
+
+
+
+
{ linksTime[1] }
+
+
+
+
{ linksTime[2] }
+
+
+
+
{ linksTime[3] }
+
+
+
+
+ );
+}
diff --git a/server/components/home/system.jsx b/server/components/home/system.jsx
new file mode 100644
index 0000000..b0be871
--- /dev/null
+++ b/server/components/home/system.jsx
@@ -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 (
+
+
+ {/* 状态卡片 */ }
+
+
+
状态
+
+
+
+
+
+
CPU
+
{ systemInfo ? `( ${ systemInfo.cpuCoresUsed } / ${ systemInfo.totalCpuCores } ) 核` : "" }
+
+
+
+
+
+
内存
+
{systemInfo ? `${systemInfo.usedMemory} / ${systemInfo.totalMemory}` : ""}
+
+
+
+
+
+
磁盘使用
+
{systemInfo ? `${systemInfo.usedDisk} / ${systemInfo.totalDisk}` : ""}
+
+
+
+
+
+
+ {/* 系统信息卡片 */ }
+
+
+
系统信息
+
主机名称: {systemInfo ? systemInfo.hostname : ""}
+
发行版本: {systemInfo ? systemInfo.distro : ""}
+
内核版本: {systemInfo ? systemInfo.kernelVersion : ""}
+
系统类型: {systemInfo ? systemInfo.arch : ""}
+
主机地址: {systemInfo ? systemInfo.ipAddress : ""}
+
运行时间: {systemInfo ? systemInfo.uptime : ""}
+
+
+
+
+ );
+}
diff --git a/server/components/sidebar.jsx b/server/components/sidebar.jsx
new file mode 100644
index 0000000..a1287d5
--- /dev/null
+++ b/server/components/sidebar.jsx
@@ -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 (
+
+ );
+}
diff --git a/server/constants/napcat.js b/server/constants/napcat.js
new file mode 100644
index 0000000..3cd8565
--- /dev/null
+++ b/server/constants/napcat.js
@@ -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`;
diff --git a/server/constants/sidebar.js b/server/constants/sidebar.js
new file mode 100644
index 0000000..ffb496f
--- /dev/null
+++ b/server/constants/sidebar.js
@@ -0,0 +1,63 @@
+export const SIDEBAR_ITEMS = [
+ {
+ name: "总控制台",
+ icon: ,
+ theme: "light"
+ },
+ {
+ name: "哔哩哔哩控制台",
+ icon: ,
+ theme: "valentine"
+ },
+ {
+ name: "抖音控制台", icon: ,
+ theme: "dark"
+ },
+ {
+ name: "周刊预览",
+ icon: ,
+ theme: "dim"
+ }
+];
diff --git a/server/constants/system.js b/server/constants/system.js
new file mode 100644
index 0000000..648c8b1
--- /dev/null
+++ b/server/constants/system.js
@@ -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=`;
diff --git a/server/contexts/drawer-context.js b/server/contexts/drawer-context.js
new file mode 100644
index 0000000..ced0fed
--- /dev/null
+++ b/server/contexts/drawer-context.js
@@ -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 (
+
+ {children}
+
+ );
+};
+
+export const useDrawer = () => useContext(DrawerContext);
+
+
diff --git a/server/postcss.config.js b/server/postcss.config.js
new file mode 100644
index 0000000..28c1b6d
--- /dev/null
+++ b/server/postcss.config.js
@@ -0,0 +1,6 @@
+// postcss.config.js
+export default {
+ plugins: {
+ tailwindcss: {},
+ },
+};
diff --git a/server/styles/global.css b/server/styles/global.css
new file mode 100644
index 0000000..d250bfb
--- /dev/null
+++ b/server/styles/global.css
@@ -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;
+ /* 滑块悬停时的颜色 */
+}
diff --git a/server/tailwind.config.js b/server/tailwind.config.js
new file mode 100644
index 0000000..5421ba9
--- /dev/null
+++ b/server/tailwind.config.js
@@ -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;
diff --git a/server/utils/axiosInstance.js b/server/utils/axiosInstance.js
new file mode 100644
index 0000000..e446da2
--- /dev/null
+++ b/server/utils/axiosInstance.js
@@ -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;
diff --git a/server/utils/napact.js b/server/utils/napact.js
new file mode 100644
index 0000000..7f3735d
--- /dev/null
+++ b/server/utils/napact.js
@@ -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;
+}