mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2026-02-06 21:10:23 +00:00
refactor: 整体重构 (#1381)
* feat: pnpm new * Refactor build and release workflows, update dependencies Switch build scripts and workflows from npm to pnpm, update build and artifact paths, and simplify release workflow by removing version detection and changelog steps. Add new dependencies (silk-wasm, express, ws, node-pty-prebuilt-multiarch), update exports in package.json files, and add vite config for napcat-framework. Also, rename manifest.json for framework package and fix static asset copying in shell build config.
This commit is contained in:
50
packages/napcat-webui-backend/src/middleware/auth.ts
Normal file
50
packages/napcat-webui-backend/src/middleware/auth.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { NextFunction, Request, Response } from 'express';
|
||||
|
||||
import { getInitialWebUiToken } from '@/napcat-webui-backend/index';
|
||||
|
||||
import { AuthHelper } from '@/napcat-webui-backend/helper/SignToken';
|
||||
import { sendError } from '@/napcat-webui-backend/utils/response';
|
||||
|
||||
// 鉴权中间件
|
||||
export async function auth (req: Request, res: Response, next: NextFunction) {
|
||||
// 判断当前url是否为/login 如果是跳过鉴权
|
||||
if (req.url === '/auth/login') {
|
||||
return next();
|
||||
}
|
||||
|
||||
// 判断是否有Authorization头
|
||||
if (req.headers?.authorization) {
|
||||
// 切割参数以获取token
|
||||
const authorization = req.headers.authorization.split(' ');
|
||||
// 当Bearer后面没有参数时
|
||||
if (authorization.length < 2) {
|
||||
return sendError(res, 'Unauthorized');
|
||||
}
|
||||
// 获取token
|
||||
const hash = authorization[1];
|
||||
if (!hash) return sendError(res, 'Unauthorized');
|
||||
// 解析token
|
||||
let Credential: WebUiCredentialJson;
|
||||
try {
|
||||
Credential = JSON.parse(Buffer.from(hash, 'base64').toString('utf-8'));
|
||||
} catch (_e) {
|
||||
return sendError(res, 'Unauthorized');
|
||||
}
|
||||
// 使用启动时缓存的token进行验证,而不是动态读取配置文件 因为有可能运行时手动修改了密码
|
||||
const initialToken = getInitialWebUiToken();
|
||||
if (!initialToken) {
|
||||
return sendError(res, 'Server token not initialized');
|
||||
}
|
||||
// 验证凭证在1小时内有效
|
||||
const credentialJson = AuthHelper.validateCredentialWithinOneHour(initialToken, Credential);
|
||||
if (credentialJson) {
|
||||
// 通过验证
|
||||
return next();
|
||||
}
|
||||
// 验证失败
|
||||
return sendError(res, 'Unauthorized');
|
||||
}
|
||||
|
||||
// 没有Authorization头
|
||||
return sendError(res, 'Unauthorized');
|
||||
}
|
||||
62
packages/napcat-webui-backend/src/middleware/cors.ts
Normal file
62
packages/napcat-webui-backend/src/middleware/cors.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import type { RequestHandler } from 'express';
|
||||
import { WebUiConfig } from '@/napcat-webui-backend/index';
|
||||
|
||||
// 检查是否为局域网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 中间件,跨域用
|
||||
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 || '*';
|
||||
res.header('Access-Control-Allow-Origin', 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');
|
||||
res.header('Access-Control-Allow-Credentials', 'true');
|
||||
|
||||
if (req.method === 'OPTIONS') {
|
||||
res.sendStatus(204);
|
||||
return;
|
||||
}
|
||||
next();
|
||||
};
|
||||
Reference in New Issue
Block a user