1
1
mirror of https://github.com/ZeroCatDev/ClassworksKV.git synced 2025-12-07 13:03:09 +00:00
Sunwuyuan 7a010faa54
1
2025-11-23 16:48:27 +08:00

161 lines
5.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 设备管理中间件
*
* 提供统一的设备UUID处理逻辑
* 1. deviceMiddleware - 自动获取或创建设备将设备信息存储到res.locals.device
* 2. deviceInfoMiddleware - 仅获取设备信息,不创建新设备
* 3. passwordMiddleware - 验证设备密码
*/
import {PrismaClient} from "@prisma/client";
import errors from "../utils/errors.js";
import {verifyDevicePassword} from "../utils/crypto.js";
import {analyzeDevice} from "../utils/deviceDetector.js";
const prisma = new PrismaClient();
/**
* 为新设备创建默认的自动登录配置
* @param {number} deviceId - 设备ID
*/
async function createDefaultAutoAuth(deviceId) {
try {
// 创建默认的自动授权配置不需要密码、类型是classroom一体机
await prisma.autoAuth.create({
data: {
deviceId: deviceId,
password: null, // 无密码
deviceType: "classroom", // 一体机类型
isReadOnly: false, // 非只读
},
});
} catch (error) {
console.error('创建默认自动登录配置失败:', error);
// 这里不抛出错误,避免影响设备创建流程
}
}
/**
* 设备中间件 - 统一处理设备UUID
*
* 从req.params.deviceUuid或req.body.deviceUuid获取UUID
* 如果设备不存在则自动创建,并智能生成设备名称
* 将设备信息存储到res.locals.device
*
* 使用方式:
* router.post('/path', deviceMiddleware, handler)
* router.get('/path/:deviceUuid', deviceMiddleware, handler)
*/
export const deviceMiddleware = errors.catchAsync(async (req, res, next) => {
const deviceUuid = req.params.deviceUuid || req.body.deviceUuid;
if (!deviceUuid) {
return next(errors.createError(400, "缺少设备UUID"));
}
// 查找或创建设备
let device = await prisma.device.findUnique({
where: {uuid: deviceUuid},
});
if (!device) {
// 设备不存在,自动创建并生成智能设备名称
const userAgent = req.headers['user-agent'];
const customDeviceType = req.body.deviceType || req.query.deviceType;
const note = req.body.note || req.query.note;
// 生成设备名称,确保不为空
const deviceName = analyzeDevice(userAgent, req.headers, customDeviceType, note).generatedName;
device = await prisma.device.create({
data: {
uuid: deviceUuid,
name: deviceName,
password: null,
passwordHint: null,
accountId: null,
},
});
// 为新创建的设备添加默认的自动登录配置
await createDefaultAutoAuth(device.id);
// 将设备分析结果添加到响应中
res.locals.deviceAnalysis = deviceAnalysis;
}
// 将设备信息存储到res.locals
res.locals.device = device;
next();
});
/**
* 设备信息中间件 - 仅获取设备信息,不创建新设备
*
* 从req.params.deviceUuid获取UUID
* 如果设备不存在则返回404错误
* 将设备信息存储到res.locals.device
*
* 使用方式:
* router.get('/path/:deviceUuid', deviceInfoMiddleware, handler)
*/
export const deviceInfoMiddleware = errors.catchAsync(async (req, res, next) => {
const deviceUuid = req.params.deviceUuid;
if (!deviceUuid) {
return next(errors.createError(400, "缺少设备UUID"));
}
// 查找设备
const device = await prisma.device.findUnique({
where: {uuid: deviceUuid},
});
if (!device) {
return next(errors.createError(404, "设备不存在"));
}
// 将设备信息存储到res.locals
res.locals.device = device;
next();
});
/**
* 密码验证中间件 - 验证设备密码
*
* 前置条件必须先使用deviceMiddleware或deviceInfoMiddleware
* 从req.body.password获取密码
* 如果设备有密码但未提供或密码错误则返回401错误
*
* 特殊规则如果设备绑定了账户且req.account存在且匹配则跳过密码验证
*
* 使用方式:
* router.post('/path', deviceMiddleware, passwordMiddleware, handler)
*/
export const passwordMiddleware = errors.catchAsync(async (req, res, next) => {
const device = res.locals.device;
const {password} = req.body;
if (!device) {
return next(errors.createError(500, "设备信息未加载请先使用deviceMiddleware"));
}
// 如果设备绑定了账户,且请求中有账户信息且匹配,则跳过密码验证
if (device.accountId && req.account && req.account.id === device.accountId) {
return next();
}
// 如果设备有密码,验证密码
if (device.password) {
if (!password) {
return next(errors.createError(401, "设备需要密码"));
}
const isValid = await verifyDevicePassword(password, device.password);
if (!isValid) {
return next(errors.createError(401, "密码错误"));
}
}
next();
});