mira-app-server
Version:
Mira Server - standalone server application using mira-app-core
116 lines • 4.68 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.canAccessLibrary = canAccessLibrary;
exports.createHttpPermissionMiddleware = createHttpPermissionMiddleware;
// ============================================================
// 权限配置表 — 集中管理,避免逐路由修改
// ============================================================
// 无需认证的公开路由(method + path,相对于 /api mount point)
const PUBLIC_ROUTES = new Set([
'GET /health',
'POST /auth/login',
'POST /auth/register',
'GET /settings',
]);
// 不需要鉴权的路由前缀(支持动态路径参数)
const PUBLIC_PREFIXES = [
'/user/avatar/',
// 服务端日志流(SSE):供本地控制台订阅后端运行日志,GET 只读,仅本地使用
'/logs/stream',
];
// 需要做素材库 allowedRoles 校验的路由前缀
const LIBRARY_SCOPED_PREFIXES = [
'/files/',
'/tags/',
'/folders/',
'/database/',
'/devices/',
'/fs/',
'/statistics/',
];
// ============================================================
// 工具函数
// ============================================================
function canAccessLibrary(config, userRole) {
if (!userRole)
return true;
if (!config?.allowedRoles || config.allowedRoles.length === 0)
return true;
return config.allowedRoles.includes(userRole);
}
function extractLibraryId(req) {
return req.params?.id || req.params?.libraryId || req.query?.libraryId || req.body?.libraryId;
}
function isPublicRoute(method, path) {
if (PUBLIC_ROUTES.has(`${method} ${path}`))
return true;
if (method === 'GET' && PUBLIC_PREFIXES.some(prefix => path.startsWith(prefix)))
return true;
return false;
}
function isLibraryScoped(path) {
return LIBRARY_SCOPED_PREFIXES.some(prefix => path.startsWith(prefix));
}
/**
* 创建统一权限中间件
*
* @param authService 认证服务(来自 AuthRouter.getAuthService())
* @param settingsManager 设置管理器
* @param getLibraryStorage 延迟获取 LibraryStorage(因为 HttpServer 构造时 libraries 尚未初始化)
*/
function createHttpPermissionMiddleware(authService, settingsManager, getLibraryStorage) {
return async (req, res, next) => {
const method = req.method.toUpperCase();
const path = req.path; // mount 在 /api 下,所以是相对路径
// 1. 公开路由直接放行
if (isPublicRoute(method, path)) {
return next();
}
// 2. authRequired=false 时放行
const settings = settingsManager.getSettings();
if (!settings.authRequired) {
return next();
}
// 3. 提取并校验 token(支持 Authorization header 和 query 参数)
const token = req.headers.authorization?.replace('Bearer ', '') || req.query.token;
if (!token) {
return res.status(401).json({ code: 401, message: '未提供认证令牌', data: null });
}
let user;
try {
user = await authService.validateToken(token);
}
catch {
return res.status(500).json({ code: 500, message: '认证服务错误', data: null });
}
if (!user) {
return res.status(401).json({ code: 401, message: '无效或过期的认证令牌', data: null });
}
req.user = authService.getUserInfo(user);
// 4. 库级别权限检查
const libraryStorage = getLibraryStorage();
if (!libraryStorage)
return next(); // libraries 尚未初始化,放行
if (isLibraryScoped(path)) {
const libraryId = extractLibraryId(req);
if (libraryId) {
const libConfig = libraryStorage.getLibraryConfig(libraryId);
if (!canAccessLibrary(libConfig, req.user?.role)) {
return res.status(403).json({ code: 403, message: '权限不足,无法访问该素材库', data: null });
}
}
}
// 5. PUT/DELETE /libraries/:id 也需要库级别检查
if ((method === 'PUT' || method === 'DELETE') && path.match(/^\/libraries\/[^/]+/)) {
const libraryId = req.params?.id;
if (libraryId) {
const libConfig = libraryStorage.getLibraryConfig(libraryId);
if (!canAccessLibrary(libConfig, req.user?.role)) {
return res.status(403).json({ code: 403, message: '权限不足,无法访问该素材库', data: null });
}
}
}
next();
};
}
//# sourceMappingURL=permission.js.map