feat: 渲染网络配置

This commit is contained in:
手瓜一十雪 2024-11-15 19:48:27 +08:00
parent a668bfbc13
commit 5c81b60b58
9 changed files with 329 additions and 13 deletions

View File

@ -1,13 +1,57 @@
export class QQLoginManager {
private retCredential: string;
private apiprefix: string;
constructor(retCredential: string) {
constructor(retCredential: string, apiprefix: string = 'http://127.0.0.1:6099/api') {
this.retCredential = retCredential;
this.apiprefix = apiprefix;
}
public async GetOB11Config(): Promise<any> {
try {
const ConfigResponse = await fetch(`${this.apiprefix}/OB11Config/GetConfig`, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + this.retCredential,
'Content-Type': 'application/json',
},
});
if (ConfigResponse.status == 200) {
const ConfigResponseJson = await ConfigResponse.json();
if (ConfigResponseJson.code == 0) {
return ConfigResponseJson?.data;
}
}
} catch (error) {
console.error("Error getting OB11 config:", error);
}
return {};
}
public async SetOB11Config(config: any): Promise<boolean> {
try {
const ConfigResponse = await fetch(`${this.apiprefix}/OB11Config/SetConfig`, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + this.retCredential,
'Content-Type': 'application/json',
},
body: JSON.stringify({ config: JSON.stringify(config) }),
});
if (ConfigResponse.status == 200) {
const ConfigResponseJson = await ConfigResponse.json();
if (ConfigResponseJson.code == 0) {
return true;
}
}
} catch (error) {
console.error("Error setting OB11 config:", error);
}
return false;
}
public async checkQQLoginStatus(): Promise<boolean> {
try {
let QQLoginResponse = await fetch('../api/QQLogin/CheckLoginStatus', {
let QQLoginResponse = await fetch(`${this.apiprefix}/QQLogin/CheckLoginStatus`, {
method: 'POST',
headers: {
'Authorization': "Bearer " + this.retCredential,
@ -28,7 +72,7 @@ export class QQLoginManager {
public async checkWebUiLogined(): Promise<boolean> {
try {
let LoginResponse = await fetch('../api/auth/check', {
let LoginResponse = await fetch(`${this.apiprefix}/auth/check`, {
method: 'POST',
headers: {
'Authorization': "Bearer " + this.retCredential,
@ -37,7 +81,6 @@ export class QQLoginManager {
});
if (LoginResponse.status == 200) {
let LoginResponseJson = await LoginResponse.json();
//console.log(LoginResponseJson);
if (LoginResponseJson.code == 0) {
return true;
}
@ -50,7 +93,7 @@ export class QQLoginManager {
public async loginWithToken(token: string): Promise<string | null> {
try {
let loginResponse = await fetch('../api/auth/login', {
let loginResponse = await fetch(`${this.apiprefix}/auth/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
@ -71,7 +114,7 @@ export class QQLoginManager {
public async getQQLoginQrcode(): Promise<string> {
try {
let QQLoginResponse = await fetch('../api/QQLogin/GetQQLoginQrcode', {
let QQLoginResponse = await fetch(`${this.apiprefix}/QQLogin/GetQQLoginQrcode`, {
method: 'POST',
headers: {
'Authorization': "Bearer " + this.retCredential,
@ -92,7 +135,7 @@ export class QQLoginManager {
public async getQQQuickLoginList(): Promise<string[]> {
try {
let QQLoginResponse = await fetch('../api/QQLogin/GetQuickLoginList', {
let QQLoginResponse = await fetch(`${this.apiprefix}/QQLogin/GetQuickLoginList`, {
method: 'POST',
headers: {
'Authorization': "Bearer " + this.retCredential,
@ -113,7 +156,7 @@ export class QQLoginManager {
public async setQuickLogin(uin: string): Promise<{ result: boolean, errMsg: string }> {
try {
let QQLoginResponse = await fetch('../api/QQLogin/SetQuickLogin', {
let QQLoginResponse = await fetch(`${this.apiprefix}/QQLogin/SetQuickLogin`, {
method: 'POST',
headers: {
'Authorization': "Bearer " + this.retCredential,

View File

@ -20,7 +20,10 @@ import {
Alert as TAlert,
Tag as TTag,
ListItem as TListItem,
Tabs as TTabs,
TabPanel as TTabPanel,
Space as TSpace,
Checkbox as TCheckbox,
} from 'tdesign-vue-next';
import { router } from './router';
import 'tdesign-vue-next/es/style/index.css';
@ -46,4 +49,8 @@ app.use(TList);
app.use(TAlert);
app.use(TTag);
app.use(TListItem);
app.use(TTabs);
app.use(TTabPanel);
app.use(TSpace);
app.use(TCheckbox);
app.mount('#app');

View File

@ -1,6 +1,113 @@
<template>
<div class="network-config">
<h1>网络配置面板</h1>
<p>这里显示面板的网络配置面板</p>
</div>
<t-space>
<t-tabs v-model="value" theme="card" :addable="true" @add="addTab" @remove="removeTab">
<t-tab-panel v-for="data in panelData" :key="data.value" :value="data.value" :label="data.label"
:removable="data.removable">
<component :is="data.component" :config="data.config" />
</t-tab-panel>
</t-tabs>
</t-space>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { defaultOnebotConfig, mergeOnebotConfigs } from '../../../src/onebot/config/config';
import { QQLoginManager } from '../backend/shell';
import HttpServerComponent from './network/HttpServerComponent.vue';
import HttpClientComponent from './network/HttpClientComponent.vue';
import WebsocketServerComponent from './network/WebsocketServerComponent.vue';
import WebsocketClientComponent from './network/WebsocketClientComponent.vue';
let id = 0;
const value = ref('first');
const panelData = ref([]);
const componentMap = {
'httpServers': HttpServerComponent,
'httpClients': HttpClientComponent,
'websocketServers': WebsocketServerComponent,
'websocketClients': WebsocketClientComponent,
};
const getOB11Config = async () => {
const storedCredential = localStorage.getItem('auth');
if (!storedCredential) {
console.error('No stored credential found');
return;
}
const loginManager = new QQLoginManager(storedCredential);
const config = await loginManager.GetOB11Config();
return config;
};
const setOB11Config = async (config) => {
const storedCredential = localStorage.getItem('auth');
if (!storedCredential) {
console.error('No stored credential found');
return false;
}
const loginManager = new QQLoginManager(storedCredential);
const result = await loginManager.SetOB11Config(config);
return result;
};
const loadConfig = async () => {
try {
const userConfig = await getOB11Config();
const mergedConfig = mergeOnebotConfigs(defaultOnebotConfig, userConfig);
const networkConfig = mergedConfig.network;
console.log('networkConfig:', networkConfig); //
const panels = [];
Object.keys(networkConfig).forEach((key) => {
networkConfig[key].forEach((config, index) => {
const component = componentMap[key];
if (!component) {
console.error(`No component found for key: ${key}`);
return;
}
panels.push({
value: `${key}-${index}`,
label: `${config.name}`,
component,
config,
removable: true,
});
});
});
console.log('panels:', panels); //
panelData.value = panels;
if (panels.length > 0) {
value.value = panels[0].value;
}
} catch (error) {
console.error('Error loading config:', error);
}
};
const addTab = () => {
panelData.value.push({
value: `new-${id}`,
label: `新选项卡${id + 1}`,
removable: true,
component: null,
config: {},
});
value.value = `new-${id}`;
id += 1;
};
const removeTab = ({ value: val, index }) => {
if (index < 0) return false;
panelData.value.splice(index, 1);
if (panelData.value.length === 0) return;
if (value.value === val) {
value.value = panelData.value[Math.max(index - 1, 0)].value;
}
};
onMounted(() => {
loadConfig();
});
</script>

View File

@ -0,0 +1,29 @@
<template>
<div>
<h3>HTTP Client 配置</h3>
<t-form>
<t-form-item label="URL">
<t-input v-model="config.url" />
</t-form-item>
<t-form-item label="消息格式">
<t-input v-model="config.messagePostFormat" />
</t-form-item>
<t-form-item label="报告自身消息">
<t-checkbox v-model="config.reportSelfMessage" />
</t-form-item>
<t-form-item label="Token">
<t-input v-model="config.token" />
</t-form-item>
<t-form-item label="调试模式">
<t-checkbox v-model="config.debug" />
</t-form-item>
</t-form>
</div>
</template>
<script setup>
import { defineProps } from 'vue';
defineProps({
config: Object,
});
</script>

View File

@ -0,0 +1,38 @@
<template>
<div>
<h3>HTTP Server 配置</h3>
<t-form>
<t-form-item label="端口">
<t-input v-model.number="config.port" type="number" />
</t-form-item>
<t-form-item label="主机">
<t-input v-model="config.host" type="text" />
</t-form-item>
<t-form-item label="启用 CORS">
<t-checkbox v-model="config.enableCors" />
</t-form-item>
<t-form-item label="启用 WebSocket">
<t-checkbox v-model="config.enableWebsocket" />
</t-form-item>
<t-form-item label="消息格式">
<t-input v-model="config.messagePostFormat" type="text" />
</t-form-item>
<t-form-item label="报告自身消息">
<t-checkbox v-model="config.reportSelfMessage" />
</t-form-item>
<t-form-item label="Token">
<t-input v-model="config.token" type="text" />
</t-form-item>
<t-form-item label="调试模式">
<t-checkbox v-model="config.debug" />
</t-form-item>
</t-form>
</div>
</template>
<script setup>
import { defineProps } from 'vue';
defineProps({
config: Object,
});
</script>

View File

@ -0,0 +1,32 @@
<template>
<div>
<h3>WebSocket Client 配置</h3>
<t-form>
<t-form-item label="URL">
<t-input v-model="config.url" />
</t-form-item>
<t-form-item label="消息格式">
<t-input v-model="config.messagePostFormat" />
</t-form-item>
<t-form-item label="报告自身消息">
<t-checkbox v-model="config.reportSelfMessage" />
</t-form-item>
<t-form-item label="Token">
<t-input v-model="config.token" />
</t-form-item>
<t-form-item label="调试模式">
<t-checkbox v-model="config.debug" />
</t-form-item>
<t-form-item label="心跳间隔">
<t-input v-model.number="config.heartInterval" type="number" />
</t-form-item>
</t-form>
</div>
</template>
<script setup>
import { defineProps } from 'vue';
defineProps({
config: Object,
});
</script>

View File

@ -0,0 +1,38 @@
<template>
<div>
<h3>WebSocket Server 配置</h3>
<t-form>
<t-form-item label="主机">
<t-input v-model="config.host" />
</t-form-item>
<t-form-item label="端口">
<t-input v-model.number="config.port" type="number" />
</t-form-item>
<t-form-item label="消息格式">
<t-input v-model="config.messagePostFormat" />
</t-form-item>
<t-form-item label="报告自身消息">
<t-checkbox v-model="config.reportSelfMessage" />
</t-form-item>
<t-form-item label="Token">
<t-input v-model="config.token" />
</t-form-item>
<t-form-item label="启用推送事件">
<t-checkbox v-model="config.enablePushEvent" />
</t-form-item>
<t-form-item label="调试模式">
<t-checkbox v-model="config.debug" />
</t-form-item>
<t-form-item label="心跳间隔">
<t-input v-model.number="config.heartInterval" type="number" />
</t-form-item>
</t-form>
</div>
</template>
<script setup>
import { defineProps } from 'vue';
defineProps({
config: Object,
});
</script>

View File

@ -36,6 +36,13 @@ export async function InitWebUi(logger: LogWrapper, pathWrapper: NapCatPathWrapp
// 配置静态文件服务,提供./static目录下的文件服务访问路径为/webui
app.use(config.prefix + '/webui', express.static(pathWrapper.staticPath));
//挂载API接口
// 添加CORS支持
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
next();
});
app.use(config.prefix + '/api', ALLRouter);
app.listen(config.port, config.host, async () => {
log(`[NapCat] [WebUi] Current WebUi is running at http://${config.host}:${config.port}${config.prefix}`);

View File

@ -0,0 +1,15 @@
import { RequestHandler } from 'express';
import { WebUiDataRuntime } from '../helper/Data';
export const LogFileListHandler: RequestHandler = async (req, res) => {
res.send({
code: 0,
data: {
uin: 0,
nick: 'NapCat',
avatar: 'https://q1.qlogo.cn/g?b=qq&nk=0&s=640',
status: 'online',
boottime: Date.now()
}
});
};