mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2026-03-03 09:10:25 +00:00
Add flexible IP access control to WebUI config
Replaces the 'disableNonLANAccess' option with a more flexible access control system supporting 'none', 'whitelist', and 'blacklist' modes, along with IP list and X-Forwarded-For support. Updates backend API, config schema, middleware, and frontend UI to allow configuration of access control mode, IP whitelist/blacklist, and X-Forwarded-For handling. Removes legacy LAN-only access logic and updates types accordingly.
This commit is contained in:
@@ -12,7 +12,10 @@ export const GetWebUIConfigHandler: RequestHandler = async (_, res) => {
|
||||
port: config.port,
|
||||
loginRate: config.loginRate,
|
||||
disableWebUI: config.disableWebUI,
|
||||
disableNonLANAccess: config.disableNonLANAccess,
|
||||
accessControlMode: config.accessControlMode || 'none',
|
||||
ipWhitelist: config.ipWhitelist || [],
|
||||
ipBlacklist: config.ipBlacklist || [],
|
||||
enableXForwardedFor: config.enableXForwardedFor || false,
|
||||
});
|
||||
} catch (error) {
|
||||
const msg = (error as Error).message;
|
||||
@@ -48,38 +51,47 @@ export const UpdateDisableWebUIHandler: RequestHandler = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 获取是否禁用非局域网访问
|
||||
export const GetDisableNonLANAccessHandler: RequestHandler = async (_, res) => {
|
||||
// 获取当前客户端IP
|
||||
export const GetClientIPHandler: RequestHandler = async (req, res) => {
|
||||
try {
|
||||
const disable = await WebUiConfig.GetDisableNonLANAccess();
|
||||
return sendSuccess(res, disable);
|
||||
} catch (error) {
|
||||
const msg = (error as Error).message;
|
||||
return sendError(res, `获取非局域网访问禁用状态失败: ${msg}`);
|
||||
}
|
||||
};
|
||||
const config = await WebUiConfig.GetWebUIConfig();
|
||||
|
||||
// 更新是否禁用非局域网访问
|
||||
export const UpdateDisableNonLANAccessHandler: RequestHandler = async (req, res) => {
|
||||
try {
|
||||
const { disable } = req.body;
|
||||
|
||||
if (typeof disable !== 'boolean') {
|
||||
return sendError(res, 'disable参数必须是布尔值');
|
||||
// 根据配置决定如何获取客户端IP(与 CORS 中间件逻辑一致)
|
||||
let clientIP: string;
|
||||
if (config.enableXForwardedFor) {
|
||||
const forwardedFor = req.headers['x-forwarded-for'];
|
||||
if (typeof forwardedFor === 'string') {
|
||||
clientIP = forwardedFor.split(',')[0]?.trim() || '';
|
||||
} else if (Array.isArray(forwardedFor) && forwardedFor.length > 0) {
|
||||
clientIP = forwardedFor[0] || '';
|
||||
} else {
|
||||
clientIP = req.ip || req.socket.remoteAddress || '';
|
||||
}
|
||||
} else {
|
||||
clientIP = req.ip || req.socket.remoteAddress || '';
|
||||
}
|
||||
|
||||
await WebUiConfig.UpdateDisableNonLANAccess(disable);
|
||||
return sendSuccess(res, null);
|
||||
// 标准化 IP(移除 IPv4-mapped IPv6 前缀,但保留纯 IPv6)
|
||||
let normalizedIP = clientIP;
|
||||
if (clientIP.startsWith('::ffff:')) {
|
||||
const ipv4 = clientIP.substring(7);
|
||||
// 检查是否是有效的 IPv4
|
||||
if (/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(ipv4)) {
|
||||
normalizedIP = ipv4;
|
||||
}
|
||||
}
|
||||
|
||||
return sendSuccess(res, { ip: normalizedIP });
|
||||
} catch (error) {
|
||||
const msg = (error as Error).message;
|
||||
return sendError(res, `更新非局域网访问禁用状态失败: ${msg}`);
|
||||
return sendError(res, `获取客户端IP失败: ${msg}`);
|
||||
}
|
||||
};
|
||||
|
||||
// 更新WebUI基础配置
|
||||
export const UpdateWebUIConfigHandler: RequestHandler = async (req, res) => {
|
||||
try {
|
||||
const { host, port, loginRate, disableWebUI, disableNonLANAccess } = req.body;
|
||||
const { host, port, loginRate, disableWebUI, accessControlMode, ipWhitelist, ipBlacklist, enableXForwardedFor } = req.body;
|
||||
|
||||
const updateConfig: any = {};
|
||||
|
||||
@@ -111,11 +123,32 @@ export const UpdateWebUIConfigHandler: RequestHandler = async (req, res) => {
|
||||
updateConfig.disableWebUI = disableWebUI;
|
||||
}
|
||||
|
||||
if (disableNonLANAccess !== undefined) {
|
||||
if (typeof disableNonLANAccess !== 'boolean') {
|
||||
return sendError(res, 'disableNonLANAccess必须是布尔值');
|
||||
if (accessControlMode !== undefined) {
|
||||
if (!['none', 'whitelist', 'blacklist'].includes(accessControlMode)) {
|
||||
return sendError(res, 'accessControlMode必须是none、whitelist或blacklist');
|
||||
}
|
||||
updateConfig.disableNonLANAccess = disableNonLANAccess;
|
||||
updateConfig.accessControlMode = accessControlMode;
|
||||
}
|
||||
|
||||
if (ipWhitelist !== undefined) {
|
||||
if (!Array.isArray(ipWhitelist)) {
|
||||
return sendError(res, 'ipWhitelist必须是数组');
|
||||
}
|
||||
updateConfig.ipWhitelist = ipWhitelist;
|
||||
}
|
||||
|
||||
if (ipBlacklist !== undefined) {
|
||||
if (!Array.isArray(ipBlacklist)) {
|
||||
return sendError(res, 'ipBlacklist必须是数组');
|
||||
}
|
||||
updateConfig.ipBlacklist = ipBlacklist;
|
||||
}
|
||||
|
||||
if (enableXForwardedFor !== undefined) {
|
||||
if (typeof enableXForwardedFor !== 'boolean') {
|
||||
return sendError(res, 'enableXForwardedFor必须是布尔值');
|
||||
}
|
||||
updateConfig.enableXForwardedFor = enableXForwardedFor;
|
||||
}
|
||||
|
||||
await WebUiConfig.UpdateWebUIConfig(updateConfig);
|
||||
|
||||
Reference in New Issue
Block a user