refactor: webui

This commit is contained in:
pk5ls20 2024-11-15 23:39:19 +08:00
parent 1ec1040e43
commit fe0bda11d3
No known key found for this signature in database
GPG Key ID: 6370ED7A169F493A
33 changed files with 647 additions and 499 deletions

View File

@ -0,0 +1,53 @@
import globals from 'globals';
import ts from 'typescript-eslint';
import vue from 'eslint-plugin-vue';
import prettier from 'eslint-plugin-prettier/recommended';
export default [
{
languageOptions: {
globals: {
...globals.browser,
...globals.node,
},
},
},
...ts.configs.recommended,
{
rules: {
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unused-vars': 'warn',
'@typescript-eslint/no-var-requires': 'warn',
},
},
...vue.configs['flat/base'],
{
files: ['*.vue', '**/*.vue'],
languageOptions: {
parserOptions: {
parser: ts.parser,
},
},
},
{
rules: {
indent: ['error', 4],
semi: ['error', 'always'],
'no-unused-vars': 'off',
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unused-vars': 'warn',
'@typescript-eslint/no-var-requires': 'warn',
'object-curly-spacing': ['error', 'always'],
'vue/v-for-delimiter-style': ['error', 'in'],
'vue/require-name-property': 'warn',
'vue/prefer-true-attribute-shorthand': 'warn',
'prefer-arrow-callback': 'warn',
},
},
prettier,
{
rules: {
'prettier/prettier': 'warn',
},
},
];

View File

@ -4,18 +4,26 @@
"version": "0.0.0", "version": "0.0.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "lint": "eslint . --fix",
"dev": "vite --host 127.0.0.1",
"build": "vue-tsc -b && vite build", "build": "vue-tsc -b && vite build",
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"eslint-plugin-prettier": "^5.2.1",
"qrcode": "^1.5.4", "qrcode": "^1.5.4",
"tdesign-vue-next": "^1.10.3", "tdesign-vue-next": "^1.10.3",
"vue": "^3.5.12", "vue": "^3.5.12",
"vue-router": "^4.4.5" "vue-router": "^4.4.5"
}, },
"devDependencies": { "devDependencies": {
"@eslint/eslintrc": "^3.1.0",
"@eslint/js": "^9.14.0",
"@types/qrcode": "^1.5.5",
"@vitejs/plugin-vue": "^5.1.4", "@vitejs/plugin-vue": "^5.1.4",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-vue": "^9.31.0",
"globals": "^15.12.0",
"typescript": "~5.6.2", "typescript": "~5.6.2",
"vite": "^5.4.10", "vite": "^5.4.10",
"vue-tsc": "^2.1.8" "vue-tsc": "^2.1.8"

View File

@ -4,8 +4,4 @@
</div> </div>
</template> </template>
<script> <script setup lang="ts"></script>
export default {
name: 'App'
};
</script>

View File

@ -1,14 +1,17 @@
export class QQLoginManager { export class QQLoginManager {
private retCredential: string; private retCredential: string;
private apiprefix: string; private readonly apiPrefix: string;
// TODO:
//调试时http://127.0.0.1:6099/api 打包时 ../api //调试时http://127.0.0.1:6099/api 打包时 ../api
constructor(retCredential: string, apiprefix: string = 'http://127.0.0.1:6099/api') { constructor(retCredential: string, apiPrefix: string = 'http://127.0.0.1:6099/api') {
this.retCredential = retCredential; this.retCredential = retCredential;
this.apiprefix = apiprefix; this.apiPrefix = apiPrefix;
} }
// TODO:
public async GetOB11Config(): Promise<any> { public async GetOB11Config(): Promise<any> {
try { try {
const ConfigResponse = await fetch(`${this.apiprefix}/OB11Config/GetConfig`, { const ConfigResponse = await fetch(`${this.apiPrefix}/OB11Config/GetConfig`, {
method: 'POST', method: 'POST',
headers: { headers: {
Authorization: 'Bearer ' + this.retCredential, Authorization: 'Bearer ' + this.retCredential,
@ -22,14 +25,14 @@ export class QQLoginManager {
} }
} }
} catch (error) { } catch (error) {
console.error("Error getting OB11 config:", error); console.error('Error getting OB11 config:', error);
} }
return {}; return {};
} }
public async SetOB11Config(config: any): Promise<boolean> { public async SetOB11Config(config: any): Promise<boolean> {
try { try {
const ConfigResponse = await fetch(`${this.apiprefix}/OB11Config/SetConfig`, { const ConfigResponse = await fetch(`${this.apiPrefix}/OB11Config/SetConfig`, {
method: 'POST', method: 'POST',
headers: { headers: {
Authorization: 'Bearer ' + this.retCredential, Authorization: 'Bearer ' + this.retCredential,
@ -44,137 +47,137 @@ export class QQLoginManager {
} }
} }
} catch (error) { } catch (error) {
console.error("Error setting OB11 config:", error); console.error('Error setting OB11 config:', error);
} }
return false; return false;
} }
public async checkQQLoginStatus(): Promise<boolean> { public async checkQQLoginStatus(): Promise<boolean> {
try { try {
let QQLoginResponse = await fetch(`${this.apiprefix}/QQLogin/CheckLoginStatus`, { const QQLoginResponse = await fetch(`${this.apiPrefix}/QQLogin/CheckLoginStatus`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Authorization': "Bearer " + this.retCredential, Authorization: 'Bearer ' + this.retCredential,
'Content-Type': 'application/json' 'Content-Type': 'application/json',
} },
}); });
if (QQLoginResponse.status == 200) { if (QQLoginResponse.status == 200) {
let QQLoginResponseJson = await QQLoginResponse.json(); const QQLoginResponseJson = await QQLoginResponse.json();
if (QQLoginResponseJson.code == 0) { if (QQLoginResponseJson.code == 0) {
return QQLoginResponseJson.data.isLogin; return QQLoginResponseJson.data.isLogin;
} }
} }
} catch (error) { } catch (error) {
console.error("Error checking QQ login status:", error); console.error('Error checking QQ login status:', error);
} }
return false; return false;
} }
public async checkWebUiLogined(): Promise<boolean> { public async checkWebUiLogined(): Promise<boolean> {
try { try {
let LoginResponse = await fetch(`${this.apiprefix}/auth/check`, { const LoginResponse = await fetch(`${this.apiPrefix}/auth/check`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Authorization': "Bearer " + this.retCredential, Authorization: 'Bearer ' + this.retCredential,
'Content-Type': 'application/json' 'Content-Type': 'application/json',
} },
}); });
if (LoginResponse.status == 200) { if (LoginResponse.status == 200) {
let LoginResponseJson = await LoginResponse.json(); const LoginResponseJson = await LoginResponse.json();
if (LoginResponseJson.code == 0) { if (LoginResponseJson.code == 0) {
return true; return true;
} }
} }
} catch (error) { } catch (error) {
console.error("Error checking web UI login status:", error); console.error('Error checking web UI login status:', error);
} }
return false; return false;
} }
public async loginWithToken(token: string): Promise<string | null> { public async loginWithToken(token: string): Promise<string | null> {
try { try {
let loginResponse = await fetch(`${this.apiprefix}/auth/login`, { const loginResponse = await fetch(`${this.apiPrefix}/auth/login`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json',
}, },
body: JSON.stringify({ token: token }) body: JSON.stringify({ token: token }),
}); });
const loginResponseJson = await loginResponse.json(); const loginResponseJson = await loginResponse.json();
let retCode = loginResponseJson.code; const retCode = loginResponseJson.code;
if (retCode === 0) { if (retCode === 0) {
this.retCredential = loginResponseJson.data.Credential; this.retCredential = loginResponseJson.data.Credential;
return this.retCredential; return this.retCredential;
} }
} catch (error) { } catch (error) {
console.error("Error logging in with token:", error); console.error('Error logging in with token:', error);
} }
return null; return null;
} }
public async getQQLoginQrcode(): Promise<string> { public async getQQLoginQrcode(): Promise<string> {
try { try {
let QQLoginResponse = await fetch(`${this.apiprefix}/QQLogin/GetQQLoginQrcode`, { const QQLoginResponse = await fetch(`${this.apiPrefix}/QQLogin/GetQQLoginQrcode`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Authorization': "Bearer " + this.retCredential, Authorization: 'Bearer ' + this.retCredential,
'Content-Type': 'application/json' 'Content-Type': 'application/json',
} },
}); });
if (QQLoginResponse.status == 200) { if (QQLoginResponse.status == 200) {
let QQLoginResponseJson = await QQLoginResponse.json(); const QQLoginResponseJson = await QQLoginResponse.json();
if (QQLoginResponseJson.code == 0) { if (QQLoginResponseJson.code == 0) {
return QQLoginResponseJson.data.qrcode || ""; return QQLoginResponseJson.data.qrcode || '';
} }
} }
} catch (error) { } catch (error) {
console.error("Error getting QQ login QR code:", error); console.error('Error getting QQ login QR code:', error);
} }
return ""; return '';
} }
public async getQQQuickLoginList(): Promise<string[]> { public async getQQQuickLoginList(): Promise<string[]> {
try { try {
let QQLoginResponse = await fetch(`${this.apiprefix}/QQLogin/GetQuickLoginList`, { const QQLoginResponse = await fetch(`${this.apiPrefix}/QQLogin/GetQuickLoginList`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Authorization': "Bearer " + this.retCredential, Authorization: 'Bearer ' + this.retCredential,
'Content-Type': 'application/json' 'Content-Type': 'application/json',
} },
}); });
if (QQLoginResponse.status == 200) { if (QQLoginResponse.status == 200) {
let QQLoginResponseJson = await QQLoginResponse.json(); const QQLoginResponseJson = await QQLoginResponse.json();
if (QQLoginResponseJson.code == 0) { if (QQLoginResponseJson.code == 0) {
return QQLoginResponseJson.data || []; return QQLoginResponseJson.data || [];
} }
} }
} catch (error) { } catch (error) {
console.error("Error getting QQ quick login list:", error); console.error('Error getting QQ quick login list:', error);
} }
return []; return [];
} }
public async setQuickLogin(uin: string): Promise<{ result: boolean, errMsg: string }> { public async setQuickLogin(uin: string): Promise<{ result: boolean; errMsg: string }> {
try { try {
let QQLoginResponse = await fetch(`${this.apiprefix}/QQLogin/SetQuickLogin`, { const QQLoginResponse = await fetch(`${this.apiPrefix}/QQLogin/SetQuickLogin`, {
method: 'POST', method: 'POST',
headers: { headers: {
'Authorization': "Bearer " + this.retCredential, Authorization: 'Bearer ' + this.retCredential,
'Content-Type': 'application/json' 'Content-Type': 'application/json',
}, },
body: JSON.stringify({ uin: uin }) body: JSON.stringify({ uin: uin }),
}); });
if (QQLoginResponse.status == 200) { if (QQLoginResponse.status == 200) {
let QQLoginResponseJson = await QQLoginResponse.json(); const QQLoginResponseJson = await QQLoginResponse.json();
if (QQLoginResponseJson.code == 0) { if (QQLoginResponseJson.code == 0) {
return { result: true, errMsg: "" }; return { result: true, errMsg: '' };
} else { } else {
return { result: false, errMsg: QQLoginResponseJson.message }; return { result: false, errMsg: QQLoginResponseJson.message };
} }
} }
} catch (error) { } catch (error) {
console.error("Error setting quick login:", error); console.error('Error setting quick login:', error);
} }
return { result: false, errMsg: "接口异常" }; return { result: false, errMsg: '接口异常' };
} }
} }

View File

@ -1,31 +1,30 @@
<template> <template>
<div class="dashboard-container"> <div class="dashboard-container">
<SidebarMenu :menuItems="menuItems" class="sidebar-menu" /> <SidebarMenu :menu-items="menuItems" class="sidebar-menu" />
<div class="content"> <div class="content">
<router-view /> <router-view />
</div> </div>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import { ref } from 'vue';
import SidebarMenu from './webui/Nav.vue'; import SidebarMenu from './webui/Nav.vue';
export default { interface MenuItem {
components: { value: string;
SidebarMenu icon: string;
}, label: string;
data() { route: string;
return { }
menuItems: [
const menuItems = ref<MenuItem[]>([
{ value: 'item1', icon: 'dashboard', label: '基础信息', route: '/dashboard/basic-info' }, { value: 'item1', icon: 'dashboard', label: '基础信息', route: '/dashboard/basic-info' },
{ value: 'item3', icon: 'wifi-1', label: '网络配置', route: '/dashboard/network-config' }, { value: 'item3', icon: 'wifi-1', label: '网络配置', route: '/dashboard/network-config' },
{ value: 'item4', icon: 'setting', label: '其余配置', route: '/dashboard/other-config' }, { value: 'item4', icon: 'setting', label: '其余配置', route: '/dashboard/other-config' },
{ value: 'item5', icon: 'system-log', label: '日志查看', route: '/dashboard/log-view' }, { value: 'item5', icon: 'system-log', label: '日志查看', route: '/dashboard/log-view' },
{ value: 'item6', icon: 'info-circle', label: '关于我们', route: '/dashboard/about-us' } { value: 'item6', icon: 'info-circle', label: '关于我们', route: '/dashboard/about-us' },
] ]);
};
}
}
</script> </script>
<style scoped> <style scoped>

View File

@ -2,67 +2,91 @@
<div class="login-container"> <div class="login-container">
<h2 class="sotheby-font">QQ Login</h2> <h2 class="sotheby-font">QQ Login</h2>
<div class="login-methods"> <div class="login-methods">
<t-button id="quick-login" class="login-method" :class="{ active: loginMethod === 'quick' }" <t-button
@click="loginMethod = 'quick'">Quick Login</t-button> id="quick-login"
<t-button id="qrcode-login" class="login-method" :class="{ active: loginMethod === 'qrcode' }" class="login-method"
@click="loginMethod = 'qrcode'">QR Code</t-button> :class="{ active: loginMethod === 'quick' }"
@click="loginMethod = 'quick'"
>Quick Login</t-button
>
<t-button
id="qrcode-login"
class="login-method"
:class="{ active: loginMethod === 'qrcode' }"
@click="loginMethod = 'qrcode'"
>QR Code</t-button
>
</div> </div>
<div id="quick-login-dropdown" class="login-form" v-show="loginMethod === 'quick'"> <div v-show="loginMethod === 'quick'" id="quick-login-dropdown" class="login-form">
<t-select id="quick-login-select" v-model="selectedAccount" @change="selectAccount" <t-select
placeholder="Select Account"> id="quick-login-select"
v-model="selectedAccount"
placeholder="Select Account"
@change="selectAccount"
>
<t-option v-for="account in quickLoginList" :key="account" :value="account">{{ account }}</t-option> <t-option v-for="account in quickLoginList" :key="account" :value="account">{{ account }}</t-option>
</t-select> </t-select>
</div> </div>
<div id="qrcode" class="qrcode" v-show="loginMethod === 'qrcode'"> <div v-show="loginMethod === 'qrcode'" id="qrcode" class="qrcode">
<canvas ref="qrcodeCanvas"></canvas> <canvas ref="qrcodeCanvas"></canvas>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup lang="ts">
import { ref, onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import QRCode from 'qrcode'; import * as QRCode from 'qrcode';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { MessagePlugin } from 'tdesign-vue-next'; import { MessagePlugin } from 'tdesign-vue-next';
import { QQLoginManager } from '../backend/shell.ts'; import { QQLoginManager } from '@/backend/shell';
const router = useRouter(); const router = useRouter();
const loginMethod = ref('quick'); const loginMethod = ref<'quick' | 'qrcode'>('quick');
const quickLoginList = ref([]); const quickLoginList = ref<string[]>([]);
const selectedAccount = ref(''); const selectedAccount = ref<string>('');
const qrcodeCanvas = ref(null); const qrcodeCanvas = ref<HTMLCanvasElement | null>(null);
const qqLoginManager = new QQLoginManager(localStorage.getItem('auth')); const qqLoginManager = new QQLoginManager(localStorage.getItem('auth') || '');
let heartBeatTimer = null; let heartBeatTimer: number | null = null;
const selectAccount = async (accountName) => { const selectAccount = async (accountName: string): Promise<void> => {
const { result, errMsg } = await qqLoginManager.setQuickLogin(accountName); const { result, errMsg } = await qqLoginManager.setQuickLogin(accountName);
if (result) { if (result) {
MessagePlugin.success("登录成功即将跳转"); await MessagePlugin.success('登录成功即将跳转');
await router.push({ path: '/dashboard/basic-info' }); await router.push({ path: '/dashboard/basic-info' });
} else { } else {
MessagePlugin.error("登录失败," + errMsg); await MessagePlugin.error('登录失败,' + errMsg);
} }
}; };
const generateQrCode = (data, canvas) => { const generateQrCode = (data: string, canvas: HTMLCanvasElement | null): void => {
QRCode.toCanvas(canvas, data, function (error) { if (!canvas) {
if (error) console.log(error); console.error('Canvas element not found');
return;
}
QRCode.toCanvas(canvas, data, function (error: Error | null | undefined) {
if (error) {
console.error('Error generating QR Code:', error);
} else {
console.log('QR Code generated!'); console.log('QR Code generated!');
}
}); });
}; };
const HeartBeat = async () => { const HeartBeat = async (): Promise<void> => {
let isLogined = await qqLoginManager.checkQQLoginStatus(); const isLogined = await qqLoginManager.checkQQLoginStatus();
if (isLogined) { if (isLogined) {
if (heartBeatTimer) {
clearInterval(heartBeatTimer); clearInterval(heartBeatTimer);
}
await router.push({ path: '/dashboard/basic-info' }); await router.push({ path: '/dashboard/basic-info' });
} }
}; };
const InitPages = async () => { const InitPages = async (): Promise<void> => {
quickLoginList.value = await qqLoginManager.getQQQuickLoginList(); quickLoginList.value = await qqLoginManager.getQQQuickLoginList();
generateQrCode(await qqLoginManager.getQQLoginQrcode(), qrcodeCanvas.value); const qrcodeData = await qqLoginManager.getQQLoginQrcode();
heartBeatTimer = setInterval(HeartBeat, 3000); generateQrCode(qrcodeData, qrcodeCanvas.value);
heartBeatTimer = window.setInterval(HeartBeat, 3000);
}; };
onMounted(() => { onMounted(() => {
@ -103,7 +127,7 @@ onMounted(() => {
.login-method.active { .login-method.active {
background-color: #e6f0ff; background-color: #e6f0ff;
color: #007BFF; color: #007bff;
} }
.login-form, .login-form,
@ -125,7 +149,7 @@ onMounted(() => {
font-family: Sotheby, Helvetica, monospace; font-family: Sotheby, Helvetica, monospace;
font-size: 3.125rem; font-size: 3.125rem;
line-height: 1.2; line-height: 1.2;
text-shadow: 0 1px 2px rgba(0, 0, 0, .1); text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
} }
.footer { .footer {

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="login-container"> <div class="login-container">
<h2 class="sotheby-font">WebUi Login</h2> <h2 class="sotheby-font">WebUi Login</h2>
<t-form ref="form" :data="formData" :colon="true" :label-width="0" @submit="onSubmit"> <t-form ref="form" :data="formData" colon :label-width="0" @submit="onSubmit">
<t-form-item name="password"> <t-form-item name="password">
<t-input v-model="formData.token" type="password" clearable placeholder="请输入Token"> <t-input v-model="formData.token" type="password" clearable placeholder="请输入Token">
<template #prefix-icon> <template #prefix-icon>
@ -14,32 +14,34 @@
</t-form-item> </t-form-item>
</t-form> </t-form>
</div> </div>
<div class="footer"> <div class="footer">Power By NapCat.WebUi</div>
Power By NapCat.WebUi
</div>
</template> </template>
<script setup> <script setup lang="ts">
import '../css/style.css'; import '../css/style.css';
import '../css/font.css'; import '../css/font.css';
import { reactive, ref, onMounted } from 'vue'; import { reactive, onMounted } from 'vue';
import { MessagePlugin } from 'tdesign-vue-next'; import { MessagePlugin } from 'tdesign-vue-next';
import { LockOnIcon } from 'tdesign-icons-vue-next'; import { LockOnIcon } from 'tdesign-icons-vue-next';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { QQLoginManager } from '../backend/shell'; import { QQLoginManager } from '@/backend/shell';
const router = useRouter(); const router = useRouter();
const formData = reactive({ interface FormData {
token: string;
}
const formData: FormData = reactive({
token: '', token: '',
}); });
const handleLoginSuccess = async (credential) => { const handleLoginSuccess = async (credential: string) => {
localStorage.setItem('auth', credential); localStorage.setItem('auth', credential);
await checkLoginStatus(); await checkLoginStatus();
}; };
const handleLoginFailure = (message) => { const handleLoginFailure = (message: string) => {
MessagePlugin.error(message); MessagePlugin.error(message);
}; };
@ -63,7 +65,7 @@ const checkLoginStatus = async () => {
} }
}; };
const loginWithToken = async (token) => { const loginWithToken = async (token: string) => {
const loginManager = new QQLoginManager(''); const loginManager = new QQLoginManager('');
const credential = await loginManager.loginWithToken(token); const credential = await loginManager.loginWithToken(token);
if (credential) { if (credential) {
@ -75,15 +77,15 @@ const loginWithToken = async (token) => {
onMounted(() => { onMounted(() => {
const url = new URL(window.location.href); const url = new URL(window.location.href);
const token = url.searchParams.get("token"); const token = url.searchParams.get('token');
if (token) { if (token) {
loginWithToken(token); loginWithToken(token);
} }
checkLoginStatus(); checkLoginStatus();
}); });
const onSubmit = async ({ validateResult }) => { const onSubmit = async ({ validateResult }: { validateResult: boolean }) => {
if (validateResult === true) { if (validateResult) {
await loginWithToken(formData.token); await loginWithToken(formData.token);
} else { } else {
handleLoginFailure('请填写Token'); handleLoginFailure('请填写Token');
@ -131,7 +133,7 @@ const onSubmit = async ({ validateResult }) => {
font-family: Sotheby, Helvetica, monospace; font-family: Sotheby, Helvetica, monospace;
font-size: 3.125rem; font-size: 3.125rem;
line-height: 1.2; line-height: 1.2;
text-shadow: 0 1px 2px rgba(0, 0, 0, .1); text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
} }
.footer { .footer {

View File

@ -1,7 +1,6 @@
<template> <template>
<t-menu theme="light" default-value="2-1" :collapsed="collapsed" class="sidebar-menu"> <t-menu theme="light" default-value="2-1" :collapsed="collapsed" class="sidebar-menu">
<template #logo> <template #logo> </template>
</template>
<router-link v-for="item in menuItems" :key="item.value" :to="item.route"> <router-link v-for="item in menuItems" :key="item.value" :to="item.route">
<t-menu-item :value="item.value" :disabled="item.disabled" class="menu-item"> <t-menu-item :value="item.value" :disabled="item.disabled" class="menu-item">
<template #icon> <template #icon>
@ -18,34 +17,29 @@
</t-menu> </t-menu>
</template> </template>
<script> <script setup lang="ts">
import { defineComponent, ref, onMounted } from 'vue'; import { ref, defineProps } from 'vue';
export default defineComponent({ type MenuItem = {
name: 'SidebarMenu', value: string;
props: { label: string;
menuItems: { route: string;
type: Array, icon?: string;
required: true disabled?: boolean;
} };
},
setup() {
const collapsed = ref(localStorage.getItem('sidebar-collapsed') === 'true');
const iconName = ref(collapsed.value ? 'menu-unfold' : 'menu-fold');
const changeCollapsed = () => { defineProps<{
menuItems: MenuItem[];
}>();
const collapsed = ref<boolean>(localStorage.getItem('sidebar-collapsed') === 'true');
const iconName = ref<string>(collapsed.value ? 'menu-unfold' : 'menu-fold');
const changeCollapsed = (): void => {
collapsed.value = !collapsed.value; collapsed.value = !collapsed.value;
iconName.value = collapsed.value ? 'menu-unfold' : 'menu-fold'; iconName.value = collapsed.value ? 'menu-unfold' : 'menu-fold';
localStorage.setItem('sidebar-collapsed', collapsed.value); localStorage.setItem('sidebar-collapsed', collapsed.value.toString());
}; };
return {
collapsed,
iconName,
changeCollapsed
};
}
});
</script> </script>
<style scoped> <style scoped>

View File

@ -1,5 +1,5 @@
import { createApp } from 'vue' import { createApp } from 'vue';
import App from './App.vue' import App from './App.vue';
import { import {
Button as TButton, Button as TButton,
Input as TInput, Input as TInput,
@ -25,7 +25,7 @@ import {
Space as TSpace, Space as TSpace,
Checkbox as TCheckbox, Checkbox as TCheckbox,
Popup as TPopup, Popup as TPopup,
Dialog as TDialog Dialog as TDialog,
} from 'tdesign-vue-next'; } from 'tdesign-vue-next';
import { router } from './router'; import { router } from './router';
import 'tdesign-vue-next/es/style/index.css'; import 'tdesign-vue-next/es/style/index.css';

View File

@ -3,11 +3,12 @@
<div> <div>
<t-divider content="面板关于信息" align="left" /> <t-divider content="面板关于信息" align="left" />
<t-alert theme="success" message="NapCat.WebUi is running" /> <t-alert theme="success" message="NapCat.WebUi is running" />
<t-list class="list"> <t-list class="list">
<t-list-item class="list-item"> <t-list-item class="list-item">
<span class="item-label">开发人员:</span> <span class="item-label">开发人员:</span>
<span class="item-content"><t-link href="mailto:nanaeonn@outlook.com">Mlikiowa</t-link></span> <span class="item-content">
<t-link href="mailto:nanaeonn@outlook.com">Mlikiowa</t-link>
</span>
</t-list-item> </t-list-item>
<t-list-item class="list-item"> <t-list-item class="list-item">
<span class="item-label">版本信息:</span> <span class="item-label">版本信息:</span>
@ -22,15 +23,11 @@
</div> </div>
</template> </template>
<script> <script setup lang="ts">
export default { const joinQQGroup = () => {
methods: {
joinQQGroup() {
// QQ // QQ
window.open('https://jq.qq.com/?_wv=1027&k=123456789', '_blank'); window.open('https://jq.qq.com/?_wv=1027&k=123456789', '_blank');
} };
}
}
</script> </script>
<style scoped> <style scoped>
@ -59,7 +56,7 @@ export default {
flex: 2; flex: 2;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: flex-end; /* 添加这一行 */ justify-content: flex-end;
} }
.tag-item { .tag-item {

View File

@ -1,18 +1,28 @@
<template> <template>
<t-space> <t-space>
<t-tabs v-model="value" theme="card" :addable="true" @add="showAddTabDialog" @remove="removeTab"> <t-tabs v-model="value" :addable="true" theme="card" @add="showAddTabDialog" @remove="removeTab">
<t-tab-panel v-for="data in panelData" :key="data.value" :value="data.value" :label="data.label" <t-tab-panel
:removable="data.removable"> v-for="data in panelData"
:key="data.value"
:label="data.label"
:removable="data.removable"
:value="data.value"
>
<component :is="data.component" :config="data.config" /> <component :is="data.component" :config="data.config" />
</t-tab-panel> </t-tab-panel>
</t-tabs> </t-tabs>
</t-space> </t-space>
<t-dialog :visible.sync="isDialogVisible" header="添加新选项卡" @confirm="addTab" @close="isDialogVisible = false"> <t-dialog
<t-form :model="newTab" ref="form"> v-model:visible="isDialogVisible"
<t-form-item label="名称" name="name" :rules="[{ required: true, message: '请输入名称' }]"> header="添加新选项卡"
@close="isDialogVisible = false"
@confirm="addTab"
>
<t-form ref="form" :model="newTab">
<t-form-item :rules="[{ required: true, message: '请输入名称' }]" label="名称" name="name">
<t-input v-model="newTab.name" /> <t-input v-model="newTab.name" />
</t-form-item> </t-form-item>
<t-form-item label="类型" name="type" :rules="[{ required: true, message: '请选择类型' }]"> <t-form-item :rules="[{ required: true, message: '请选择类型' }]" label="类型" name="type">
<t-select v-model="newTab.type"> <t-select v-model="newTab.type">
<t-option value="httpServers">HTTP 服务器</t-option> <t-option value="httpServers">HTTP 服务器</t-option>
<t-option value="httpClients">HTTP 客户端</t-option> <t-option value="httpClients">HTTP 客户端</t-option>
@ -24,55 +34,61 @@
</t-dialog> </t-dialog>
</template> </template>
<script setup> <script setup lang="ts">
import { ref, shallowRef, onMounted, watch, nextTick } from 'vue'; import { nextTick, onMounted, ref, shallowRef, watch } from 'vue';
import { defaultOnebotConfig, mergeOnebotConfigs } from '../../../src/onebot/config/config'; import { defaultOnebotConfig, mergeOnebotConfigs } from '../../../src/onebot/config/config';
import { QQLoginManager } from '../backend/shell'; import { QQLoginManager } from '@/backend/shell';
import HttpServerComponent from './network/HttpServerComponent.vue'; import HttpServerComponent from './network/HttpServerComponent.vue';
import HttpClientComponent from './network/HttpClientComponent.vue'; import HttpClientComponent from './network/HttpClientComponent.vue';
import WebsocketServerComponent from './network/WebsocketServerComponent.vue'; import WebsocketServerComponent from './network/WebsocketServerComponent.vue';
import WebsocketClientComponent from './network/WebsocketClientComponent.vue'; import WebsocketClientComponent from './network/WebsocketClientComponent.vue';
let id = 0; interface PanelData {
const value = ref('first'); value: string;
const panelData = ref([]); label: string;
const isDialogVisible = ref(false); removable: boolean;
const newTab = ref({ name: '', type: '' }); component: any;
config: { name: string };
}
const componentMap = { let id = 0;
'httpServers': shallowRef(HttpServerComponent), const value = ref<string>('first');
'httpClients': shallowRef(HttpClientComponent), const panelData = ref<PanelData[]>([]);
'websocketServers': shallowRef(WebsocketServerComponent), const isDialogVisible = ref<boolean>(false);
'websocketClients': shallowRef(WebsocketClientComponent), const newTab = ref<{ name: string; type: string }>({ name: '', type: '' });
const componentMap: Record<string, any> = {
httpServers: shallowRef(HttpServerComponent),
httpClients: shallowRef(HttpClientComponent),
websocketServers: shallowRef(WebsocketServerComponent),
websocketClients: shallowRef(WebsocketClientComponent),
}; };
const getOB11Config = async () => { const getOB11Config = async (): Promise<any | undefined> => {
const storedCredential = localStorage.getItem('auth'); const storedCredential = localStorage.getItem('auth');
if (!storedCredential) { if (!storedCredential) {
console.error('No stored credential found'); console.error('No stored credential found');
return; return;
} }
const loginManager = new QQLoginManager(storedCredential); const loginManager = new QQLoginManager(storedCredential);
const config = await loginManager.GetOB11Config(); return await loginManager.GetOB11Config();
return config;
}; };
const setOB11Config = async (config) => { const setOB11Config = async (config: any): Promise<boolean> => {
const storedCredential = localStorage.getItem('auth'); const storedCredential = localStorage.getItem('auth');
if (!storedCredential) { if (!storedCredential) {
console.error('No stored credential found'); console.error('No stored credential found');
return false; return false;
} }
const loginManager = new QQLoginManager(storedCredential); const loginManager = new QQLoginManager(storedCredential);
const result = await loginManager.SetOB11Config(config); return await loginManager.SetOB11Config(config);
return result;
}; };
const log = (message, data) => { const log = (message: string, data: any) => {
console.log(message, data); console.log(message, data);
}; };
const createPanel = (type, name, id) => { const createPanel = (type: string, name: string, id: number): PanelData => {
return { return {
value: `${type}-${id}`, value: `${type}-${id}`,
label: name, label: name,
@ -82,10 +98,10 @@ const createPanel = (type, name, id) => {
}; };
}; };
const generatePanels = (networkConfig) => { const generatePanels = (networkConfig: any): PanelData[] => {
const panels = []; const panels: PanelData[] = [];
Object.keys(networkConfig).forEach((key) => { Object.keys(networkConfig).forEach((key) => {
networkConfig[key].forEach((config, index) => { networkConfig[key].forEach((config: any, index: number) => {
const component = componentMap[key]; const component = componentMap[key];
if (!component) { if (!component) {
console.error(`No component found for key: ${key}`); console.error(`No component found for key: ${key}`);
@ -100,6 +116,7 @@ const generatePanels = (networkConfig) => {
const loadConfig = async () => { const loadConfig = async () => {
try { try {
const userConfig = await getOB11Config(); const userConfig = await getOB11Config();
if (!userConfig) return;
const mergedConfig = mergeOnebotConfigs(defaultOnebotConfig, userConfig); const mergedConfig = mergeOnebotConfigs(defaultOnebotConfig, userConfig);
const networkConfig = mergedConfig.network; const networkConfig = mergedConfig.network;
log('networkConfig:', networkConfig); log('networkConfig:', networkConfig);
@ -130,7 +147,11 @@ const addTab = async () => {
value.value = newPanel.value; // value.value = newPanel.value; //
}; };
const removeTab = ({ value: val, index }) => { const closeDialog = () => {
isDialogVisible.value = false;
};
const removeTab = ({ value: val, index }: { value: string; index: number }) => {
if (index < 0) return false; if (index < 0) return false;
panelData.value.splice(index, 1); panelData.value.splice(index, 1);
if (panelData.value.length === 0) return; if (panelData.value.length === 0) return;
@ -140,8 +161,8 @@ const removeTab = ({ value: val, index }) => {
}; };
const syncConfig = async () => { const syncConfig = async () => {
const networkConfig = {}; const networkConfig: Record<string, any[]> = {};
panelData.value.forEach(panel => { panelData.value.forEach((panel) => {
const key = panel.value.split('-')[0]; const key = panel.value.split('-')[0];
if (!networkConfig[key]) { if (!networkConfig[key]) {
networkConfig[key] = []; networkConfig[key] = [];
@ -149,6 +170,7 @@ const syncConfig = async () => {
networkConfig[key].push(panel.config); networkConfig[key].push(panel.config);
}); });
const userConfig = await getOB11Config(); const userConfig = await getOB11Config();
if (!userConfig) return;
const mergedConfig = mergeOnebotConfigs(defaultOnebotConfig, userConfig); const mergedConfig = mergeOnebotConfigs(defaultOnebotConfig, userConfig);
mergedConfig.network = networkConfig; mergedConfig.network = networkConfig;
await setOB11Config(mergedConfig); await setOB11Config(mergedConfig);

View File

@ -21,9 +21,22 @@
</div> </div>
</template> </template>
<script setup> <script setup lang="ts">
import { defineProps } from 'vue'; import { ref } from 'vue';
defineProps({
config: Object, interface HttpClientConfig {
url: string;
messagePostFormat: string;
reportSelfMessage: boolean;
token: string;
debug: boolean;
}
const config = ref<HttpClientConfig>({
url: '',
messagePostFormat: '',
reportSelfMessage: false,
token: '',
debug: false,
}); });
</script> </script>

View File

@ -30,9 +30,28 @@
</div> </div>
</template> </template>
<script setup> <script setup lang="ts">
import { defineProps } from 'vue'; import { ref } from 'vue';
defineProps({
config: Object, interface HttpServerConfig {
port: number;
host: string;
enableCors: boolean;
enableWebsocket: boolean;
messagePostFormat: string;
reportSelfMessage: boolean;
token: string;
debug: boolean;
}
const config = ref<HttpServerConfig>({
port: 8080,
host: '',
enableCors: false,
enableWebsocket: false,
messagePostFormat: '',
reportSelfMessage: false,
token: '',
debug: false,
}); });
</script> </script>

View File

@ -24,9 +24,24 @@
</div> </div>
</template> </template>
<script setup> <script setup lang="ts">
import { defineProps } from 'vue'; import { ref } from 'vue';
defineProps({
config: Object, interface WsClientConfig {
url: string;
messagePostFormat: string;
reportSelfMessage: boolean;
token: string;
debug: boolean;
heartInterval: number;
}
const config = ref<WsClientConfig>({
url: '',
messagePostFormat: '',
reportSelfMessage: false,
token: '',
debug: false,
heartInterval: 0,
}); });
</script> </script>

View File

@ -30,9 +30,28 @@
</div> </div>
</template> </template>
<script setup> <script setup lang="ts">
import { defineProps } from 'vue'; import { ref } from 'vue';
defineProps({
config: Object, interface WsServerConfig {
host: string;
port: number;
messagePostFormat: string;
reportSelfMessage: boolean;
token: string;
enablePushEvent: boolean;
debug: boolean;
heartInterval: number;
}
const config = ref<WsServerConfig>({
host: '',
port: 8080,
messagePostFormat: '',
reportSelfMessage: false,
token: '',
enablePushEvent: false,
debug: false,
heartInterval: 0,
}); });
</script> </script>

View File

@ -1,4 +1,4 @@
import { createRouter, createWebHashHistory } from 'vue-router' import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
import Dashboard from '../components/Dashboard.vue'; import Dashboard from '../components/Dashboard.vue';
import BasicInfo from '../pages/BasicInfo.vue'; import BasicInfo from '../pages/BasicInfo.vue';
import AboutUs from '../pages/AboutUs.vue'; import AboutUs from '../pages/AboutUs.vue';
@ -8,7 +8,7 @@ import QQLogin from '../components/QQLogin.vue';
import WebUiLogin from '../components/WebUiLogin.vue'; import WebUiLogin from '../components/WebUiLogin.vue';
import OtherConfig from '../pages/OtherConfig.vue'; import OtherConfig from '../pages/OtherConfig.vue';
const routes = [ const routes: Array<RouteRecordRaw> = [
{ path: '/', redirect: '/webui' }, { path: '/', redirect: '/webui' },
{ path: '/webui', component: WebUiLogin, name: 'WebUiLogin' }, { path: '/webui', component: WebUiLogin, name: 'WebUiLogin' },
{ path: '/qqlogin', component: QQLogin, name: 'QQLogin' }, { path: '/qqlogin', component: QQLogin, name: 'QQLogin' },
@ -21,12 +21,12 @@ const routes = [
{ path: 'network-config', component: NetWork, name: 'NetWork' }, { path: 'network-config', component: NetWork, name: 'NetWork' },
{ path: 'log-view', component: LogView, name: 'LogView' }, { path: 'log-view', component: LogView, name: 'LogView' },
{ path: 'other-config', component: OtherConfig, name: 'OtherConfig' }, { path: 'other-config', component: OtherConfig, name: 'OtherConfig' },
{ path: 'about-us', component: AboutUs, name: 'AboutUs' } { path: 'about-us', component: AboutUs, name: 'AboutUs' },
] ],
} },
] ];
export const router = createRouter({ export const router = createRouter({
history: createWebHashHistory(), history: createWebHashHistory(),
routes, routes,
}) });

View File

@ -1,5 +0,0 @@
declare module '*.vue' {
import { DefineComponent } from 'vue';
const component: DefineComponent<{}, {}, any>;
export default component;
}

View File

@ -1,26 +0,0 @@
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "Bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "preserve",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
}

View File

@ -1,7 +1,34 @@
{ {
"files": [], "compilerOptions": {
"references": [ "target": "ESNext",
{ "path": "./tsconfig.app.json" }, "jsx": "preserve",
{ "path": "./tsconfig.node.json" } "jsxImportSource": "vue",
"lib": [
"DOM",
"DOM.Iterable"
],
"baseUrl": ".",
"module": "esnext",
"moduleResolution": "bundler",
"paths": {
"@/*": [
"src/*"
] ]
},
"resolveJsonModule": true,
"types": [
"vite/client"
],
"strict": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"experimentalDecorators": true,
"useDefineForClassFields": true
},
"include": ["src"],
"exclude": ["node_modules"],
"references": [{"path": "./tsconfig.node.json"}]
} }

View File

@ -1,24 +1,11 @@
{ {
"compilerOptions": { "compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", "composite": true,
"target": "ES2022",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true, "skipLibCheck": true,
"module": "ESNext",
/* Bundler mode */ "moduleResolution": "bundler",
"moduleResolution": "Bundler", "allowSyntheticDefaultImports": true,
"allowImportingTsExtensions": true, "strictNullChecks": true
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
}, },
"include": ["vite.config.ts"] "include": ["vite.config.ts"]
} }

View File

@ -1,8 +1,8 @@
import { defineConfig } from 'vite' import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue';
// https://vite.dev/config/ // https://vite.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [vue()], plugins: [vue()],
base: './' base: './',
}) });

View File

@ -48,9 +48,9 @@
}, },
"dependencies": { "dependencies": {
"express": "^5.0.0", "express": "^5.0.0",
"silk-wasm": "^3.6.1", "fluent-ffmpeg": "^2.1.2",
"ws": "^8.18.0",
"qrcode-terminal": "^0.12.0", "qrcode-terminal": "^0.12.0",
"fluent-ffmpeg": "^2.1.2" "silk-wasm": "^3.6.1",
"ws": "^8.18.0"
} }
} }

View File

@ -45,7 +45,7 @@ export default class GetFriendMsgHistory extends BaseAction<Payload, Response> {
await Promise.all(msgList.map(async msg => { await Promise.all(msgList.map(async msg => {
msg.id = MessageUnique.createUniqueMsgId({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId); msg.id = MessageUnique.createUniqueMsgId({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
})); }));
let network = Object.values(this.obContext.configLoader.configData.network) as Array<typeof this.obContext.configLoader.configData.network[keyof typeof this.obContext.configLoader.configData.network]>; const network = Object.values(this.obContext.configLoader.configData.network) as Array<typeof this.obContext.configLoader.configData.network[keyof typeof this.obContext.configLoader.configData.network]>;
//烘焙消息 //烘焙消息
const ob11MsgList = (await Promise.all( const ob11MsgList = (await Promise.all(
msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg, network.flat().find(e => e.name === adapter)?.messagePostFormat ?? 'array'))) msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg, network.flat().find(e => e.name === adapter)?.messagePostFormat ?? 'array')))

View File

@ -43,9 +43,9 @@ export default class GoCQHTTPGetGroupMsgHistory extends BaseAction<Payload, Resp
await Promise.all(msgList.map(async msg => { await Promise.all(msgList.map(async msg => {
msg.id = MessageUnique.createUniqueMsgId({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId); msg.id = MessageUnique.createUniqueMsgId({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId);
})); }));
let network = Object.values(this.obContext.configLoader.configData.network) as Array<typeof this.obContext.configLoader.configData.network[keyof typeof this.obContext.configLoader.configData.network]>; const network = Object.values(this.obContext.configLoader.configData.network) as Array<typeof this.obContext.configLoader.configData.network[keyof typeof this.obContext.configLoader.configData.network]>;
//烘焙消息 //烘焙消息
let msgFormat = network.flat().find(e => e.name === adapter)?.messagePostFormat ?? 'array'; const msgFormat = network.flat().find(e => e.name === adapter)?.messagePostFormat ?? 'array';
const ob11MsgList = (await Promise.all( const ob11MsgList = (await Promise.all(
msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg, msgFormat))) msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg, msgFormat)))
).filter(msg => msg !== undefined); ).filter(msg => msg !== undefined);

View File

@ -31,8 +31,8 @@ export class GetGroupEssence extends BaseAction<Payload, any> {
} }
async _handle(payload: Payload, adapter: string) { async _handle(payload: Payload, adapter: string) {
let network = Object.values(this.obContext.configLoader.configData.network) as Array<typeof this.obContext.configLoader.configData.network[keyof typeof this.obContext.configLoader.configData.network]>; const network = Object.values(this.obContext.configLoader.configData.network) as Array<typeof this.obContext.configLoader.configData.network[keyof typeof this.obContext.configLoader.configData.network]>;
let msgFormat = network.flat().find(e => e.name === adapter)?.messagePostFormat ?? 'array'; const msgFormat = network.flat().find(e => e.name === adapter)?.messagePostFormat ?? 'array';
const msglist = (await this.core.apis.WebApi.getGroupEssenceMsgAll(payload.group_id.toString())).flatMap((e) => e.data.msg_list); const msglist = (await this.core.apis.WebApi.getGroupEssenceMsgAll(payload.group_id.toString())).flatMap((e) => e.data.msg_list);
if (!msglist) { if (!msglist) {
throw new Error('获取失败'); throw new Error('获取失败');

View File

@ -24,8 +24,8 @@ class GetMsg extends BaseAction<Payload, OB11Message> {
async _handle(payload: Payload, adapter: string) { async _handle(payload: Payload, adapter: string) {
// log("history msg ids", Object.keys(msgHistory)); // log("history msg ids", Object.keys(msgHistory));
let network = Object.values(this.obContext.configLoader.configData.network) as Array<typeof this.obContext.configLoader.configData.network[keyof typeof this.obContext.configLoader.configData.network]>; const network = Object.values(this.obContext.configLoader.configData.network) as Array<typeof this.obContext.configLoader.configData.network[keyof typeof this.obContext.configLoader.configData.network]>;
let msgFormat = network.flat().find(e => e.name === adapter)?.messagePostFormat ?? 'array'; const msgFormat = network.flat().find(e => e.name === adapter)?.messagePostFormat ?? 'array';
if (!payload.message_id) { if (!payload.message_id) {
throw Error('参数message_id不能为空'); throw Error('参数message_id不能为空');
} }

View File

@ -17,9 +17,9 @@ export default class GetRecentContact extends BaseAction<Payload, any> {
async _handle(payload: Payload, adapter: string) { async _handle(payload: Payload, adapter: string) {
const ret = await this.core.apis.UserApi.getRecentContactListSnapShot(+(payload.count || 10)); const ret = await this.core.apis.UserApi.getRecentContactListSnapShot(+(payload.count || 10));
let network = Object.values(this.obContext.configLoader.configData.network) as Array<typeof this.obContext.configLoader.configData.network[keyof typeof this.obContext.configLoader.configData.network]>; const network = Object.values(this.obContext.configLoader.configData.network) as Array<typeof this.obContext.configLoader.configData.network[keyof typeof this.obContext.configLoader.configData.network]>;
//烘焙消息 //烘焙消息
let msgFormat = network.flat().find(e => e.name === adapter)?.messagePostFormat ?? 'array'; const msgFormat = network.flat().find(e => e.name === adapter)?.messagePostFormat ?? 'array';
return await Promise.all(ret.info.changedList.map(async (t) => { return await Promise.all(ret.info.changedList.map(async (t) => {
const FastMsg = await this.core.apis.MsgApi.getMsgsByMsgId({ chatType: t.chatType, peerUid: t.peerUid }, [t.msgId]); const FastMsg = await this.core.apis.MsgApi.getMsgsByMsgId({ chatType: t.chatType, peerUid: t.peerUid }, [t.msgId]);
if (FastMsg.msgList.length > 0) { if (FastMsg.msgList.length > 0) {

View File

@ -85,7 +85,7 @@ export const defaultOnebotConfig = createDefaultConfig<OnebotConfig>({
}, },
musicSignUrl: "", musicSignUrl: "",
enableLocalFile2Url: false enableLocalFile2Url: false
}) });
export const mergeNetworkDefaultConfig = { export const mergeNetworkDefaultConfig = {
httpServers: httpServerDefaultConfigs, httpServers: httpServerDefaultConfigs,
httpClients: httpClientDefaultConfigs, httpClients: httpClientDefaultConfigs,

View File

@ -100,7 +100,7 @@ export class NapCatOneBot11Adapter {
this.context.logger.setLogSelfInfo(selfInfo); this.context.logger.setLogSelfInfo(selfInfo);
}).catch(this.context.logger.logError.bind(this.context.logger)); }).catch(this.context.logger.logError.bind(this.context.logger));
let serviceInfo = await this.creatOneBotLog(ob11Config); const serviceInfo = await this.creatOneBotLog(ob11Config);
this.context.logger.log(`[Notice] [OneBot11] ${serviceInfo}`); this.context.logger.log(`[Notice] [OneBot11] ${serviceInfo}`);
// //创建NetWork服务 // //创建NetWork服务
@ -497,14 +497,14 @@ export class NapCatOneBot11Adapter {
} }
private async emitMsg(message: RawMessage) { private async emitMsg(message: RawMessage) {
let network = Object.values(this.configLoader.configData.network) as Array<typeof this.configLoader.configData.network[keyof typeof this.configLoader.configData.network]>; const network = Object.values(this.configLoader.configData.network) as Array<typeof this.configLoader.configData.network[keyof typeof this.configLoader.configData.network]>;
this.context.logger.logDebug('收到新消息 RawMessage', message); this.context.logger.logDebug('收到新消息 RawMessage', message);
this.apis.MsgApi.parseMessageV2(message).then((ob11Msg) => { this.apis.MsgApi.parseMessageV2(message).then((ob11Msg) => {
if (!ob11Msg) return; if (!ob11Msg) return;
const isSelfMsg = ob11Msg.stringMsg.user_id.toString() == this.core.selfInfo.uin || ob11Msg.arrayMsg.user_id.toString() == this.core.selfInfo.uin; const isSelfMsg = ob11Msg.stringMsg.user_id.toString() == this.core.selfInfo.uin || ob11Msg.arrayMsg.user_id.toString() == this.core.selfInfo.uin;
this.context.logger.logDebug('转化为 OB11Message', ob11Msg); this.context.logger.logDebug('转化为 OB11Message', ob11Msg);
let msgMap: Map<string, OB11Message> = new Map(); const msgMap: Map<string, OB11Message> = new Map();
let enable_client: string[] = []; const enable_client: string[] = [];
network.flat().filter(e => e.enable).map(e => { network.flat().filter(e => e.enable).map(e => {
enable_client.push(e.name); enable_client.push(e.name);
if (e.messagePostFormat == 'string') { if (e.messagePostFormat == 'string') {
@ -518,7 +518,7 @@ export class NapCatOneBot11Adapter {
} }
}); });
let debug_network = network.flat().filter(e => e.enable && e.debug); const debug_network = network.flat().filter(e => e.enable && e.debug);
if (debug_network.length > 0) { if (debug_network.length > 0) {
for (const adapter of debug_network) { for (const adapter of debug_network) {
if (adapter.name) { if (adapter.name) {
@ -534,7 +534,7 @@ export class NapCatOneBot11Adapter {
return; return;
} }
let notreportSelf_network = network.flat().filter(e => e.enable && !e.reportSelfMessage); const notreportSelf_network = network.flat().filter(e => e.enable && !e.reportSelfMessage);
if (isSelfMsg) { if (isSelfMsg) {
for (const adapter of notreportSelf_network) { for (const adapter of notreportSelf_network) {
msgMap.delete(adapter.name); msgMap.delete(adapter.name);

View File

@ -37,6 +37,7 @@ export async function InitWebUi(logger: LogWrapper, pathWrapper: NapCatPathWrapp
app.use(config.prefix + '/webui', express.static(pathWrapper.staticPath)); app.use(config.prefix + '/webui', express.static(pathWrapper.staticPath));
//挂载API接口 //挂载API接口
// 添加CORS支持 // 添加CORS支持
// TODO:
app.use((req, res, next) => { app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
@ -47,22 +48,22 @@ export async function InitWebUi(logger: LogWrapper, pathWrapper: NapCatPathWrapp
app.listen(config.port, config.host, async () => { app.listen(config.port, config.host, async () => {
log(`[NapCat] [WebUi] Current WebUi is running at http://${config.host}:${config.port}${config.prefix}`); log(`[NapCat] [WebUi] Current WebUi is running at http://${config.host}:${config.port}${config.prefix}`);
log(`[NapCat] [WebUi] Login Token is ${config.token}`); log(`[NapCat] [WebUi] Login Token is ${config.token}`);
log(`[NapCat] [WebUi] WebUi User Panel Url: http://${config.host}:${config.port}${config.prefix}/webui?token=${config.token}`); log(
log(`[NapCat] [WebUi] WebUi Local Panel Url: http://127.0.0.1:${config.port}${config.prefix}/webui?token=${config.token}`); `[NapCat] [WebUi] WebUi User Panel Url: http://${config.host}:${config.port}${config.prefix}/webui?token=${config.token}`
);
log(
`[NapCat] [WebUi] WebUi Local Panel Url: http://127.0.0.1:${config.port}${config.prefix}/webui?token=${config.token}`
);
//获取上网Ip //获取上网Ip
//https://www.ip.cn/api/index?ip&type=0 //https://www.ip.cn/api/index?ip&type=0
RequestUtil.HttpGetJson<{ IP: {IP:string} }>( RequestUtil.HttpGetJson<{ IP: { IP: string } }>('https://ip.011102.xyz/', 'GET', {}, {}, true, true)
'https://ip.011102.xyz/', .then((data) => {
'GET', log(
{}, `[NapCat] [WebUi] WebUi Publish Panel Url: http://${data.IP.IP}:${config.port}${config.prefix}/webui/?token=${config.token}`
{}, );
true, })
true .catch((err) => {
).then((data) => {
log(`[NapCat] [WebUi] WebUi Publish Panel Url: http://${data.IP.IP}:${config.port}${config.prefix}/webui/?token=${config.token}`);
}).catch((err) => {
logger.logError.bind(logger)(`[NapCat] [WebUi] Get Publish Panel Url Error: ${err}`); logger.logError.bind(logger)(`[NapCat] [WebUi] Get Publish Panel Url Error: ${err}`);
}); });
}); });
} }