mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-12-20 21:50:10 +08:00
feat: 支持禁用webui 速率配置 禁用外网访问 禁用webui
This commit is contained in:
parent
e97f3e1283
commit
58332dad24
@ -160,4 +160,55 @@ export default class WebUIManager {
|
|||||||
|
|
||||||
return eventSource
|
return eventSource
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取WebUI基础配置
|
||||||
|
public static async getWebUIConfig() {
|
||||||
|
const { data } = await serverRequest.get<ServerResponse<WebUIConfig>>(
|
||||||
|
'/WebUIConfig/GetConfig'
|
||||||
|
)
|
||||||
|
return data.data
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新WebUI基础配置
|
||||||
|
public static async updateWebUIConfig(config: Partial<WebUIConfig>) {
|
||||||
|
const { data } = await serverRequest.post<ServerResponse<boolean>>(
|
||||||
|
'/WebUIConfig/UpdateConfig',
|
||||||
|
config
|
||||||
|
)
|
||||||
|
return data.data
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取是否禁用WebUI
|
||||||
|
public static async getDisableWebUI() {
|
||||||
|
const { data } = await serverRequest.get<ServerResponse<boolean>>(
|
||||||
|
'/WebUIConfig/GetDisableWebUI'
|
||||||
|
)
|
||||||
|
return data.data
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新是否禁用WebUI
|
||||||
|
public static async updateDisableWebUI(disable: boolean) {
|
||||||
|
const { data } = await serverRequest.post<ServerResponse<boolean>>(
|
||||||
|
'/WebUIConfig/UpdateDisableWebUI',
|
||||||
|
{ disable }
|
||||||
|
)
|
||||||
|
return data.data
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取是否禁用非局域网访问
|
||||||
|
public static async getDisableNonLANAccess() {
|
||||||
|
const { data } = await serverRequest.get<ServerResponse<boolean>>(
|
||||||
|
'/WebUIConfig/GetDisableNonLANAccess'
|
||||||
|
)
|
||||||
|
return data.data
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新是否禁用非局域网访问
|
||||||
|
public static async updateDisableNonLANAccess(disable: boolean) {
|
||||||
|
const { data } = await serverRequest.post<ServerResponse<boolean>>(
|
||||||
|
'/WebUIConfig/UpdateDisableNonLANAccess',
|
||||||
|
{ disable }
|
||||||
|
)
|
||||||
|
return data.data
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { useNavigate, useSearchParams } from 'react-router-dom'
|
|||||||
import ChangePasswordCard from './change_password'
|
import ChangePasswordCard from './change_password'
|
||||||
import LoginConfigCard from './login'
|
import LoginConfigCard from './login'
|
||||||
import OneBotConfigCard from './onebot'
|
import OneBotConfigCard from './onebot'
|
||||||
|
import ServerConfigCard from './server'
|
||||||
import ThemeConfigCard from './theme'
|
import ThemeConfigCard from './theme'
|
||||||
import WebUIConfigCard from './webui'
|
import WebUIConfigCard from './webui'
|
||||||
|
|
||||||
@ -67,6 +68,11 @@ export default function ConfigPage() {
|
|||||||
<OneBotConfigCard />
|
<OneBotConfigCard />
|
||||||
</ConfingPageItem>
|
</ConfingPageItem>
|
||||||
</Tab>
|
</Tab>
|
||||||
|
<Tab title="服务器配置" key="server">
|
||||||
|
<ConfingPageItem>
|
||||||
|
<ServerConfigCard />
|
||||||
|
</ConfingPageItem>
|
||||||
|
</Tab>
|
||||||
<Tab title="WebUI配置" key="webui">
|
<Tab title="WebUI配置" key="webui">
|
||||||
<ConfingPageItem>
|
<ConfingPageItem>
|
||||||
<WebUIConfigCard />
|
<WebUIConfigCard />
|
||||||
|
|||||||
185
napcat.webui/src/pages/dashboard/config/server.tsx
Normal file
185
napcat.webui/src/pages/dashboard/config/server.tsx
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
import { Input } from '@heroui/input'
|
||||||
|
import { Switch } from '@heroui/switch'
|
||||||
|
import { useRequest } from 'ahooks'
|
||||||
|
import { useEffect } from 'react'
|
||||||
|
import { Controller, useForm } from 'react-hook-form'
|
||||||
|
import toast from 'react-hot-toast'
|
||||||
|
|
||||||
|
import SaveButtons from '@/components/button/save_buttons'
|
||||||
|
import PageLoading from '@/components/page_loading'
|
||||||
|
|
||||||
|
import WebUIManager from '@/controllers/webui_manager'
|
||||||
|
|
||||||
|
const ServerConfigCard = () => {
|
||||||
|
const {
|
||||||
|
data: configData,
|
||||||
|
loading: configLoading,
|
||||||
|
error: configError,
|
||||||
|
refreshAsync: refreshConfig
|
||||||
|
} = useRequest(WebUIManager.getWebUIConfig)
|
||||||
|
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
handleSubmit: handleConfigSubmit,
|
||||||
|
formState: { isSubmitting },
|
||||||
|
setValue: setConfigValue
|
||||||
|
} = useForm<{
|
||||||
|
host: string
|
||||||
|
port: number
|
||||||
|
loginRate: number
|
||||||
|
disableWebUI: boolean
|
||||||
|
disableNonLANAccess: boolean
|
||||||
|
}>({
|
||||||
|
defaultValues: {
|
||||||
|
host: '0.0.0.0',
|
||||||
|
port: 6099,
|
||||||
|
loginRate: 10,
|
||||||
|
disableWebUI: false,
|
||||||
|
disableNonLANAccess: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const reset = () => {
|
||||||
|
if (configData) {
|
||||||
|
setConfigValue('host', configData.host)
|
||||||
|
setConfigValue('port', configData.port)
|
||||||
|
setConfigValue('loginRate', configData.loginRate)
|
||||||
|
setConfigValue('disableWebUI', configData.disableWebUI)
|
||||||
|
setConfigValue('disableNonLANAccess', configData.disableNonLANAccess)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSubmit = handleConfigSubmit(async (data) => {
|
||||||
|
try {
|
||||||
|
await WebUIManager.updateWebUIConfig(data)
|
||||||
|
toast.success('保存成功')
|
||||||
|
} catch (error) {
|
||||||
|
const msg = (error as Error).message
|
||||||
|
toast.error(`保存失败: ${msg}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const onRefresh = async () => {
|
||||||
|
try {
|
||||||
|
await refreshConfig()
|
||||||
|
toast.success('刷新成功')
|
||||||
|
} catch (error) {
|
||||||
|
const msg = (error as Error).message
|
||||||
|
toast.error(`刷新失败: ${msg}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
reset()
|
||||||
|
}, [configData])
|
||||||
|
|
||||||
|
if (configLoading) return <PageLoading loading={true} />
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<title>服务器配置 - NapCat WebUI</title>
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div className="flex-shrink-0 w-full">服务器配置</div>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="host"
|
||||||
|
render={({ field }) => (
|
||||||
|
<Input
|
||||||
|
{...field}
|
||||||
|
label="监听地址"
|
||||||
|
placeholder="请输入监听地址"
|
||||||
|
description="服务器监听的IP地址,0.0.0.0表示监听所有网卡"
|
||||||
|
isDisabled={!!configError}
|
||||||
|
errorMessage={configError ? '获取配置失败' : undefined}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="port"
|
||||||
|
render={({ field }) => (
|
||||||
|
<Input
|
||||||
|
{...field}
|
||||||
|
type="number"
|
||||||
|
value={field.value?.toString() || ''}
|
||||||
|
label="监听端口"
|
||||||
|
placeholder="请输入监听端口"
|
||||||
|
description="服务器监听的端口号,范围1-65535"
|
||||||
|
isDisabled={!!configError}
|
||||||
|
errorMessage={configError ? '获取配置失败' : undefined}
|
||||||
|
onChange={(e) => field.onChange(parseInt(e.target.value) || 0)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="loginRate"
|
||||||
|
render={({ field }) => (
|
||||||
|
<Input
|
||||||
|
{...field}
|
||||||
|
type="number"
|
||||||
|
value={field.value?.toString() || ''}
|
||||||
|
label="登录速率限制"
|
||||||
|
placeholder="请输入登录速率限制"
|
||||||
|
description="每小时允许的登录尝试次数"
|
||||||
|
isDisabled={!!configError}
|
||||||
|
errorMessage={configError ? '获取配置失败' : undefined}
|
||||||
|
onChange={(e) => field.onChange(parseInt(e.target.value) || 0)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div className="flex-shrink-0 w-full">安全配置</div>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="disableWebUI"
|
||||||
|
render={({ field }) => (
|
||||||
|
<Switch
|
||||||
|
isSelected={field.value}
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
isDisabled={!!configError}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span>禁用WebUI</span>
|
||||||
|
<span className="text-sm text-default-400">
|
||||||
|
启用后将完全禁用WebUI服务,需要重启生效
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</Switch>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="disableNonLANAccess"
|
||||||
|
render={({ field }) => (
|
||||||
|
<Switch
|
||||||
|
isSelected={field.value}
|
||||||
|
onValueChange={field.onChange}
|
||||||
|
isDisabled={!!configError}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span>禁用非局域网访问</span>
|
||||||
|
<span className="text-sm text-default-400">
|
||||||
|
启用后只允许局域网内的设备访问WebUI,提高安全性
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</Switch>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<SaveButtons
|
||||||
|
onSubmit={onSubmit}
|
||||||
|
reset={reset}
|
||||||
|
isSubmitting={isSubmitting || configLoading}
|
||||||
|
refresh={onRefresh}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ServerConfigCard
|
||||||
8
napcat.webui/src/types/server.d.ts
vendored
8
napcat.webui/src/types/server.d.ts
vendored
@ -181,3 +181,11 @@ interface ThemeConfig {
|
|||||||
dark: ThemeConfigItem
|
dark: ThemeConfigItem
|
||||||
light: ThemeConfigItem
|
light: ThemeConfigItem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface WebUIConfig {
|
||||||
|
host: string
|
||||||
|
port: number
|
||||||
|
loginRate: number
|
||||||
|
disableWebUI: boolean
|
||||||
|
disableNonLANAccess: boolean
|
||||||
|
}
|
||||||
|
|||||||
@ -61,7 +61,15 @@ async function checkCertificates(logger: LogWrapper): Promise<{ key: string, cer
|
|||||||
export async function InitWebUi(logger: LogWrapper, pathWrapper: NapCatPathWrapper) {
|
export async function InitWebUi(logger: LogWrapper, pathWrapper: NapCatPathWrapper) {
|
||||||
webUiPathWrapper = pathWrapper;
|
webUiPathWrapper = pathWrapper;
|
||||||
WebUiConfig = new WebUiConfigWrapper();
|
WebUiConfig = new WebUiConfigWrapper();
|
||||||
const [host, port, token] = await InitPort(await WebUiConfig.GetWebUIConfig());
|
const config = await WebUiConfig.GetWebUIConfig();
|
||||||
|
|
||||||
|
// 检查是否禁用WebUI
|
||||||
|
if (config.disableWebUI) {
|
||||||
|
logger.log('[NapCat] [WebUi] WebUI is disabled by configuration.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [host, port, token] = await InitPort(config);
|
||||||
webUiRuntimePort = port;
|
webUiRuntimePort = port;
|
||||||
if (port == 0) {
|
if (port == 0) {
|
||||||
logger.log('[NapCat] [WebUi] Current WebUi is not run.');
|
logger.log('[NapCat] [WebUi] Current WebUi is not run.');
|
||||||
|
|||||||
127
src/webui/src/api/WebUIConfig.ts
Normal file
127
src/webui/src/api/WebUIConfig.ts
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
import { RequestHandler } from 'express';
|
||||||
|
import { WebUiConfig } from '@/webui';
|
||||||
|
import { sendError, sendSuccess } from '@webapi/utils/response';
|
||||||
|
import { isEmpty } from '@webapi/utils/check';
|
||||||
|
|
||||||
|
// 获取WebUI基础配置
|
||||||
|
export const GetWebUIConfigHandler: RequestHandler = async (_, res) => {
|
||||||
|
try {
|
||||||
|
const config = await WebUiConfig.GetWebUIConfig();
|
||||||
|
return sendSuccess(res, {
|
||||||
|
host: config.host,
|
||||||
|
port: config.port,
|
||||||
|
loginRate: config.loginRate,
|
||||||
|
disableWebUI: config.disableWebUI,
|
||||||
|
disableNonLANAccess: config.disableNonLANAccess
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
const msg = (error as Error).message;
|
||||||
|
return sendError(res, `获取WebUI配置失败: ${msg}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取是否禁用WebUI
|
||||||
|
export const GetDisableWebUIHandler: RequestHandler = async (_, res) => {
|
||||||
|
try {
|
||||||
|
const disable = await WebUiConfig.GetDisableWebUI();
|
||||||
|
return sendSuccess(res, disable);
|
||||||
|
} catch (error) {
|
||||||
|
const msg = (error as Error).message;
|
||||||
|
return sendError(res, `获取WebUI禁用状态失败: ${msg}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 更新是否禁用WebUI
|
||||||
|
export const UpdateDisableWebUIHandler: RequestHandler = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { disable } = req.body;
|
||||||
|
|
||||||
|
if (typeof disable !== 'boolean') {
|
||||||
|
return sendError(res, 'disable参数必须是布尔值');
|
||||||
|
}
|
||||||
|
|
||||||
|
await WebUiConfig.UpdateDisableWebUI(disable);
|
||||||
|
return sendSuccess(res, null);
|
||||||
|
} catch (error) {
|
||||||
|
const msg = (error as Error).message;
|
||||||
|
return sendError(res, `更新WebUI禁用状态失败: ${msg}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取是否禁用非局域网访问
|
||||||
|
export const GetDisableNonLANAccessHandler: RequestHandler = async (_, res) => {
|
||||||
|
try {
|
||||||
|
const disable = await WebUiConfig.GetDisableNonLANAccess();
|
||||||
|
return sendSuccess(res, disable);
|
||||||
|
} catch (error) {
|
||||||
|
const msg = (error as Error).message;
|
||||||
|
return sendError(res, `获取非局域网访问禁用状态失败: ${msg}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 更新是否禁用非局域网访问
|
||||||
|
export const UpdateDisableNonLANAccessHandler: RequestHandler = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { disable } = req.body;
|
||||||
|
|
||||||
|
if (typeof disable !== 'boolean') {
|
||||||
|
return sendError(res, 'disable参数必须是布尔值');
|
||||||
|
}
|
||||||
|
|
||||||
|
await WebUiConfig.UpdateDisableNonLANAccess(disable);
|
||||||
|
return sendSuccess(res, null);
|
||||||
|
} catch (error) {
|
||||||
|
const msg = (error as Error).message;
|
||||||
|
return sendError(res, `更新非局域网访问禁用状态失败: ${msg}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 更新WebUI基础配置
|
||||||
|
export const UpdateWebUIConfigHandler: RequestHandler = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { host, port, loginRate, disableWebUI, disableNonLANAccess } = req.body;
|
||||||
|
|
||||||
|
const updateConfig: any = {};
|
||||||
|
|
||||||
|
if (host !== undefined) {
|
||||||
|
if (isEmpty(host)) {
|
||||||
|
return sendError(res, 'host不能为空');
|
||||||
|
}
|
||||||
|
updateConfig.host = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (port !== undefined) {
|
||||||
|
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
||||||
|
return sendError(res, 'port必须是1-65535之间的整数');
|
||||||
|
}
|
||||||
|
updateConfig.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loginRate !== undefined) {
|
||||||
|
if (!Number.isInteger(loginRate) || loginRate < 1) {
|
||||||
|
return sendError(res, 'loginRate必须是大于0的整数');
|
||||||
|
}
|
||||||
|
updateConfig.loginRate = loginRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disableWebUI !== undefined) {
|
||||||
|
if (typeof disableWebUI !== 'boolean') {
|
||||||
|
return sendError(res, 'disableWebUI必须是布尔值');
|
||||||
|
}
|
||||||
|
updateConfig.disableWebUI = disableWebUI;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disableNonLANAccess !== undefined) {
|
||||||
|
if (typeof disableNonLANAccess !== 'boolean') {
|
||||||
|
return sendError(res, 'disableNonLANAccess必须是布尔值');
|
||||||
|
}
|
||||||
|
updateConfig.disableNonLANAccess = disableNonLANAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
await WebUiConfig.UpdateWebUIConfig(updateConfig);
|
||||||
|
return sendSuccess(res, null);
|
||||||
|
} catch (error) {
|
||||||
|
const msg = (error as Error).message;
|
||||||
|
return sendError(res, `更新WebUI配置失败: ${msg}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -19,6 +19,10 @@ const WebUiConfigSchema = Type.Object({
|
|||||||
autoLoginAccount: Type.String({ default: '' }),
|
autoLoginAccount: Type.String({ default: '' }),
|
||||||
theme: themeType,
|
theme: themeType,
|
||||||
defaultToken: Type.Boolean({ default: true }),
|
defaultToken: Type.Boolean({ default: true }),
|
||||||
|
// 是否关闭WebUI
|
||||||
|
disableWebUI: Type.Boolean({ default: false }),
|
||||||
|
// 是否关闭非局域网访问
|
||||||
|
disableNonLANAccess: Type.Boolean({ default: false }),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type WebUiConfigType = Static<typeof WebUiConfigSchema>;
|
export type WebUiConfigType = Static<typeof WebUiConfigSchema>;
|
||||||
@ -177,4 +181,26 @@ export class WebUiConfigWrapper {
|
|||||||
async UpdateTheme(theme: WebUiConfigType['theme']): Promise<void> {
|
async UpdateTheme(theme: WebUiConfigType['theme']): Promise<void> {
|
||||||
await this.UpdateWebUIConfig({ theme: theme });
|
await this.UpdateWebUIConfig({ theme: theme });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取是否禁用WebUI
|
||||||
|
async GetDisableWebUI(): Promise<boolean> {
|
||||||
|
const config = await this.GetWebUIConfig();
|
||||||
|
return config.disableWebUI;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新是否禁用WebUI
|
||||||
|
async UpdateDisableWebUI(disable: boolean): Promise<void> {
|
||||||
|
await this.UpdateWebUIConfig({ disableWebUI: disable });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取是否禁用非局域网访问
|
||||||
|
async GetDisableNonLANAccess(): Promise<boolean> {
|
||||||
|
const config = await this.GetWebUIConfig();
|
||||||
|
return config.disableNonLANAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新是否禁用非局域网访问
|
||||||
|
async UpdateDisableNonLANAccess(disable: boolean): Promise<void> {
|
||||||
|
await this.UpdateWebUIConfig({ disableNonLANAccess: disable });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,53 @@
|
|||||||
import type { RequestHandler } from 'express';
|
import type { RequestHandler } from 'express';
|
||||||
|
import { WebUiConfig } from '@/webui';
|
||||||
|
|
||||||
|
// 检查是否为局域网IP地址
|
||||||
|
function isLANIP(ip: string): boolean {
|
||||||
|
if (!ip) return false;
|
||||||
|
|
||||||
|
// 移除IPv6的前缀,如果存在
|
||||||
|
const cleanIP = ip.replace(/^::ffff:/, '');
|
||||||
|
|
||||||
|
// 本地回环地址
|
||||||
|
if (cleanIP === '127.0.0.1' || cleanIP === 'localhost' || cleanIP === '::1') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查IPv4私有网络地址
|
||||||
|
const ipv4Regex = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
|
||||||
|
const match = cleanIP.match(ipv4Regex);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
const [, a, b] = match.map(Number);
|
||||||
|
|
||||||
|
// 10.0.0.0/8
|
||||||
|
if (a === 10) return true;
|
||||||
|
|
||||||
|
// 172.16.0.0/12
|
||||||
|
if (a === 172 && b !== undefined && b >= 16 && b <= 31) return true;
|
||||||
|
|
||||||
|
// 192.168.0.0/16
|
||||||
|
if (a === 192 && b === 168) return true;
|
||||||
|
|
||||||
|
// 169.254.0.0/16 (链路本地地址)
|
||||||
|
if (a === 169 && b === 254) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// CORS 中间件,跨域用
|
// CORS 中间件,跨域用
|
||||||
export const cors: RequestHandler = (req, res, next) => {
|
export const cors: RequestHandler = async (req, res, next) => {
|
||||||
|
// 检查是否禁用非局域网访问
|
||||||
|
const config = await WebUiConfig.GetWebUIConfig();
|
||||||
|
if (config.disableNonLANAccess) {
|
||||||
|
const clientIP = req.ip || req.socket.remoteAddress || '';
|
||||||
|
if (!isLANIP(clientIP)) {
|
||||||
|
res.status(403).json({ error: '非局域网访问被禁止' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const origin = req.headers.origin || '*';
|
const origin = req.headers.origin || '*';
|
||||||
res.header('Access-Control-Allow-Origin', origin);
|
res.header('Access-Control-Allow-Origin', origin);
|
||||||
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
||||||
|
|||||||
31
src/webui/src/router/WebUIConfig.ts
Normal file
31
src/webui/src/router/WebUIConfig.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { Router } from 'express';
|
||||||
|
import {
|
||||||
|
GetWebUIConfigHandler,
|
||||||
|
GetDisableWebUIHandler,
|
||||||
|
UpdateDisableWebUIHandler,
|
||||||
|
GetDisableNonLANAccessHandler,
|
||||||
|
UpdateDisableNonLANAccessHandler,
|
||||||
|
UpdateWebUIConfigHandler
|
||||||
|
} from '@webapi/api/WebUIConfig';
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
// 获取WebUI基础配置
|
||||||
|
router.get('/GetConfig', GetWebUIConfigHandler);
|
||||||
|
|
||||||
|
// 更新WebUI基础配置
|
||||||
|
router.post('/UpdateConfig', UpdateWebUIConfigHandler);
|
||||||
|
|
||||||
|
// 获取是否禁用WebUI
|
||||||
|
router.get('/GetDisableWebUI', GetDisableWebUIHandler);
|
||||||
|
|
||||||
|
// 更新是否禁用WebUI
|
||||||
|
router.post('/UpdateDisableWebUI', UpdateDisableWebUIHandler);
|
||||||
|
|
||||||
|
// 获取是否禁用非局域网访问
|
||||||
|
router.get('/GetDisableNonLANAccess', GetDisableNonLANAccessHandler);
|
||||||
|
|
||||||
|
// 更新是否禁用非局域网访问
|
||||||
|
router.post('/UpdateDisableNonLANAccess', UpdateDisableNonLANAccessHandler);
|
||||||
|
|
||||||
|
export { router as WebUIConfigRouter };
|
||||||
@ -13,6 +13,7 @@ import { AuthRouter } from '@webapi/router/auth';
|
|||||||
import { LogRouter } from '@webapi/router/Log';
|
import { LogRouter } from '@webapi/router/Log';
|
||||||
import { BaseRouter } from '@webapi/router/Base';
|
import { BaseRouter } from '@webapi/router/Base';
|
||||||
import { FileRouter } from './File';
|
import { FileRouter } from './File';
|
||||||
|
import { WebUIConfigRouter } from './WebUIConfig';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
@ -35,5 +36,7 @@ router.use('/OB11Config', OB11ConfigRouter);
|
|||||||
router.use('/Log', LogRouter);
|
router.use('/Log', LogRouter);
|
||||||
// file:文件相关路由
|
// file:文件相关路由
|
||||||
router.use('/File', FileRouter);
|
router.use('/File', FileRouter);
|
||||||
|
// router:WebUI配置相关路由
|
||||||
|
router.use('/WebUIConfig', WebUIConfigRouter);
|
||||||
|
|
||||||
export { router as ALLRouter };
|
export { router as ALLRouter };
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user