Skip to content

Commit 2935d14

Browse files
committed
refactor: update WebUIServer configuration onlyLocalHost
1 parent bbd664c commit 2935d14

File tree

7 files changed

+135
-60
lines changed

7 files changed

+135
-60
lines changed

package-dist.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"name":"llonebot","version":"6.3.0","type":"module","description":"","main":"llonebot.js","author":"linyuchen"}
1+
{"name":"llonebot","version":"6.3.1","type":"module","description":"","main":"llonebot.js","author":"linyuchen"}

src/common/utils/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,8 @@ export * from './legacyLog'
44
export * from './misc'
55
export * from './upgrade'
66
export { getVideoInfo, checkFfmpeg } from './video'
7-
export { encodeSilk } from './audio'
7+
export { encodeSilk } from './audio'
8+
9+
export async function sleep(ms: number) {
10+
return new Promise(resolve => setTimeout(resolve, ms))
11+
}

src/main/main.ts

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ async function onLoad() {
119119
ctx.parallel('llob/config-updated', c)
120120
})
121121
loadPluginAfterLogin()
122+
ctx.webuiServer.setConfig(config)
122123
}
123124
else {
124125
config = defaultConfig
@@ -134,21 +135,16 @@ async function onLoad() {
134135
const uin = await ctx.ntUserApi.getUinByUid(data[2])
135136
selfInfo.uin = uin
136137
const configUtil = getConfigUtil(true)
138+
config = configUtil.getConfig()
139+
ctx.parallel('llob/config-updated', config)
140+
configUtil.listenChange(c => {
141+
ctx.parallel('llob/config-updated', c)
142+
})
137143
loadPluginAfterLogin()
138144
// this.ctx.database.config.path = path.join(dbDir, `${uin}.db`)
139145
ctx.ntUserApi.getSelfNick().then(nick => {
140146
ctx.logger.info(`获取登录号${uin}昵称成功`, nick)
141-
Object.assign(selfInfo, {
142-
uin,
143-
nick: nick,
144-
online: true,
145-
})
146-
const config = configUtil.getConfig()
147-
configUtil.setConfig(config)
148-
ctx.parallel('llob/config-updated', config)
149-
configUtil.listenChange(c => {
150-
ctx.parallel('llob/config-updated', c)
151-
})
147+
selfInfo.nick = nick
152148
}).catch(e => {
153149
ctx.logger.warn('获取登录号昵称失败', e)
154150
})

src/ntqqapi/core.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ class Core extends Service {
6868
public start() {
6969
this.startupTime = Math.trunc(Date.now() / 1000)
7070
this.registerListener()
71-
this.ctx.logger.info(`LLTwoBot/${version}`)
7271
this.ctx.on('llob/config-updated', input => {
7372
Object.assign(this.config, input)
7473
setFFMpegPath(input.ffmpeg || '')

src/version.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import fs from 'fs'
22
import packageJson from '../package-dist.json'
33

4-
export const version = '6.3.0'
4+
export const version = '6.3.1'
55

66
export const writeVersion = ()=>{
77
const pkgJsonPath = './package-dist.json'

src/webui/BE/server.ts

Lines changed: 97 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ import { fileURLToPath } from 'url'
55
import { getConfigUtil, webuiTokenUtil } from '@/common/config'
66
import { Config, WebUIConfig } from '@/common/types'
77
import { Server } from 'http'
8+
import { Socket } from 'net'
89
import { Context, Service } from 'cordis'
910
import { selfInfo, LOG_DIR } from '@/common/globalVars'
1011
import { getAvailablePort } from '@/common/utils/port'
1112
import { pmhq } from '@/ntqqapi/native/pmhq'
1213
import { ReqConfig, ResConfig } from './types'
1314
import { appendFileSync } from 'node:fs'
15+
import { sleep } from '@/common/utils'
1416

1517
const __filename = fileURLToPath(import.meta.url)
1618
const __dirname = path.dirname(__filename)
@@ -72,12 +74,34 @@ setInterval(() => {
7274
}
7375
}, 60 * 60 * 1000)
7476

75-
abstract class WebUIServerBase extends Service {
76-
protected server: Server | null = null
77-
protected app: Express = express()
78-
abstract appName: string
77+
export class WebUIServer extends Service {
78+
private server: Server | null = null
79+
private app: Express = express()
80+
private connections = new Set<Socket>()
81+
private currentPort?: number
82+
public port?: number = undefined
83+
static inject = ['ntLoginApi']
84+
85+
constructor(ctx: Context, public config: WebUIServerConfig) {
86+
super(ctx, 'webuiServer', true)
87+
// 初始化服务器路由
88+
this.initServer()
89+
// 监听 config 更新事件
90+
ctx.on('llob/config-updated', (newConfig: Config) => {
91+
const oldConfig = { ...this.config }
92+
this.setConfig(newConfig)
93+
const forcePort = (oldConfig.port === newConfig.webui?.port) ? this.currentPort : undefined
94+
if (oldConfig.onlyLocalhost != newConfig.onlyLocalhost
95+
|| oldConfig.enable != newConfig.webui?.enable
96+
|| oldConfig.port != newConfig.webui?.port
97+
) {
98+
this.ctx.logger.info('WebUI 配置已更新:', this.config)
99+
setTimeout(() => this.restart(forcePort), 1000)
100+
}
101+
})
102+
}
79103

80-
protected initServer() {
104+
private initServer() {
81105
this.app.use(express.static(feDistPath))
82106
this.app.use(express.json())
83107
this.app.use(cors())
@@ -252,62 +276,81 @@ abstract class WebUIServerBase extends Service {
252276
})
253277
}
254278

255-
abstract getHostPort(): { host: string, port: number }
279+
private getHostPort(): { host: string; port: number } {
280+
const host = this.config.onlyLocalhost ? '127.0.0.1' : ''
281+
return { host, port: this.config.port }
282+
}
256283

257-
async startServer() {
284+
private async startServer(forcePort?: number) {
258285
const { host, port } = this.getHostPort()
259-
console.log(`Starting server: ${host}:${port}`)
260-
const availablePort = await getAvailablePort(port)
261-
this.server = this.app.listen(availablePort, host, () => {
262-
this.ctx.logger.info(`${this.appName} 端口: ${port}`)
286+
const targetPort = forcePort !== undefined ? forcePort : await getAvailablePort(port)
287+
this.server = this.app.listen(targetPort, host, () => {
288+
this.currentPort = targetPort
289+
this.ctx.logger.info(`Webui 服务器已启动 ${host}:${targetPort}`)
290+
})
291+
292+
// 跟踪所有连接,以便在停止时能够关闭它们
293+
this.server.on('connection', (conn) => {
294+
this.connections.add(conn)
295+
conn.on('close', () => {
296+
this.connections.delete(conn)
297+
})
263298
})
299+
264300
this.server.on('error', (err: any) => {
265301
if (err.code === 'EADDRINUSE') {
266-
this.ctx.logger.error(`${this.appName} 端口 ${port} 被占用,启动失败!`)
302+
this.ctx.logger.error(`Webui 端口 ${targetPort} 被占用,启动失败!`)
267303
}
268304
else {
269-
this.ctx.logger.error(`${this.appName} 启动失败:`, err)
305+
this.ctx.logger.error(`Webui 启动失败:`, err)
270306
}
271307
})
272-
return availablePort
308+
return targetPort
273309
}
274310

275311
stop() {
276-
this.server?.close()
277-
}
278-
279-
restart() {
280-
this.stop()
281-
this.start()
282-
}
283-
}
284-
285-
export class WebUIServer extends WebUIServerBase {
286-
appName = 'Webui'
287-
public port?: number = undefined
288-
static inject = ['ntLoginApi']
312+
return new Promise<void>((resolve) => {
313+
if (this.server) {
314+
// 先关闭所有现有连接
315+
if (this.connections.size > 0) {
316+
this.ctx.logger.info(`Webui 正在关闭 ${this.connections.size} 个连接...`)
317+
for (const conn of this.connections) {
318+
conn.destroy()
319+
}
320+
this.connections.clear()
321+
}
289322

290-
constructor(ctx: Context, public config: WebUIServerConfig) {
291-
super(ctx, 'webuiServer', true)
292-
// 获取配置接口
293-
this.initServer()
294-
// 监听 config 更新事件
295-
ctx.on('llob/config-updated', (newConfig: Config) => {
296-
const oldConfig = { ...this.config }
297-
this.config = { onlyLocalhost: newConfig.onlyLocalhost, ...newConfig.webui }
298-
if (oldConfig.onlyLocalhost != newConfig.onlyLocalhost
299-
|| oldConfig.enable != newConfig.webui?.enable
300-
|| oldConfig.port != newConfig.webui?.port
301-
) {
302-
this.ctx.logger.info('WebUI 配置已更新:', this.config)
303-
this.restart()
323+
// 然后关闭服务器
324+
this.server.close((err) => {
325+
if (err) {
326+
this.ctx.logger.error(`Webui 停止时出错:`, err)
327+
}
328+
else {
329+
this.ctx.logger.info(`Webui 服务器已停止`)
330+
}
331+
this.server = null
332+
// 不清空 currentPort,以便 restart 时复用
333+
resolve()
334+
})
335+
}
336+
else {
337+
this.ctx.logger.info(`Webui 服务器未运行`)
338+
resolve()
304339
}
305340
})
306341
}
307342

308-
getHostPort(): { host: string; port: number } {
309-
const host = this.config.onlyLocalhost ? '127.0.0.1' : ''
310-
return { host, port: this.config.port }
343+
async restart(forcePort?: number) {
344+
await this.stop()
345+
// 等待端口完全释放(Windows 上需要)
346+
await new Promise(resolve => setTimeout(resolve, 1000))
347+
await this.startWithPort(forcePort)
348+
}
349+
350+
private setConfig(newConfig: Config) {
351+
const oldConfig = { ...this.config }
352+
this.config = { onlyLocalhost: newConfig.onlyLocalhost, ...newConfig.webui }
353+
311354
}
312355

313356
async start() {
@@ -319,5 +362,15 @@ export class WebUIServer extends WebUIServerBase {
319362
this.ctx.logger.error('记录 WebUI 端口失败:', err)
320363
})
321364
}
365+
366+
private async startWithPort(forcePort?: number): Promise<void> {
367+
if (!this.config?.enable) {
368+
return
369+
}
370+
this.port = await this.startServer(forcePort)
371+
pmhq.tellPort(this.port).catch((err: Error) => {
372+
this.ctx.logger.error('记录 WebUI 端口失败:', err)
373+
})
374+
}
322375
}
323376

src/webui/FE/components/OtherConfig.tsx

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react'
22
import { Config } from '../types'
3-
import { Globe, FileText, Trash2, Music, Lock, Clock, Shield, Edit } from 'lucide-react'
3+
import { Globe, FileText, Trash2, Music, Lock, Clock, Shield, Edit, Paperclip } from 'lucide-react'
44

55
interface OtherConfigProps {
66
config: Config;
@@ -57,6 +57,29 @@ const OtherConfig: React.FC<OtherConfigProps> = ({ config, onChange, onOpenChang
5757
className='flex items-center justify-between p-4 bg-gray-50/50 rounded-xl hover:bg-gray-100/50 transition-colors'>
5858
<div className='flex items-center gap-3'>
5959
<Globe size={20} className='text-blue-600' />
60+
<div>
61+
<div className='text-sm font-medium text-gray-800'>只监听本地地址</div>
62+
<div className='text-xs text-gray-500 mt-0.5'>取消后可能会暴露在公网</div>
63+
</div>
64+
</div>
65+
<input
66+
type='checkbox'
67+
checked={config.onlyLocalhost}
68+
onChange={(e) => handleChange('onlyLocalhost', e.target.checked)}
69+
className="w-12 h-6 rounded-full bg-gray-300 relative cursor-pointer appearance-none
70+
checked:bg-gradient-to-r checked:from-blue-500 checked:to-purple-600
71+
transition-colors duration-200 ease-in-out
72+
before:content-[''] before:absolute before:top-0.5 before:left-0.5
73+
before:w-5 before:h-5 before:rounded-full before:bg-white
74+
before:transition-transform before:duration-200
75+
checked:before:translate-x-6"
76+
/>
77+
</div>
78+
<div
79+
className='flex items-center justify-between p-4 bg-gray-50/50 rounded-xl hover:bg-gray-100/50 transition-colors'>
80+
<div className='flex items-center gap-3'>
81+
{/*<FileText size={20} className='text-blue-600' />*/}
82+
<Paperclip size={20} className='text-blue-600'/>
6083
<div>
6184
<div className='text-sm font-medium text-gray-800'>本地文件转URL</div>
6285
<div className='text-xs text-gray-500 mt-0.5'>启用后可将本地文件转换为URL链接</div>

0 commit comments

Comments
 (0)