1
1
mirror of https://github.com/ZeroCatDev/ClassworksKV.git synced 2025-12-07 13:03:09 +00:00
ClassworksKV/routes/device.js
2025-11-16 16:15:05 +08:00

217 lines
6.1 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.

import {Router} from "express";
import {extractDeviceInfo} from "../middleware/uuidAuth.js";
import {PrismaClient} from "@prisma/client";
import errors from "../utils/errors.js";
import {getOnlineDevices} from "../utils/socket.js";
import {registeredDevicesTotal} from "../utils/metrics.js";
const router = Router();
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);
// 这里不抛出错误,避免影响设备创建流程
}
}
/**
* POST /devices
* 注册新设备
*/
router.post(
"/",
errors.catchAsync(async (req, res, next) => {
const {uuid, deviceName, namespace} = req.body;
if (!uuid) {
return next(errors.createError(400, "设备UUID是必需的"));
}
if (!deviceName) {
return next(errors.createError(400, "设备名称是必需的"));
}
try {
// 检查UUID是否已存在
const existingDevice = await prisma.device.findUnique({
where: {uuid},
});
if (existingDevice) {
return next(errors.createError(409, "设备UUID已存在"));
}
// 处理 namespace如果没有提供则使用 uuid
const deviceNamespace = namespace && namespace.trim() ? namespace.trim() : uuid;
// 检查 namespace 是否已被使用
const existingNamespace = await prisma.device.findUnique({
where: {namespace: deviceNamespace},
});
if (existingNamespace) {
return next(errors.createError(409, "该 namespace 已被使用"));
}
// 创建设备
const device = await prisma.device.create({
data: {
uuid,
name: deviceName,
namespace: deviceNamespace,
},
});
// 为新设备创建默认的自动登录配置
await createDefaultAutoAuth(device.id);
// 更新注册设备总数指标
const totalDevices = await prisma.device.count();
registeredDevicesTotal.set(totalDevices);
return res.status(201).json({
success: true,
device: {
id: device.id,
uuid: device.uuid,
name: device.name,
namespace: device.namespace,
createdAt: device.createdAt,
},
});
} catch (error) {
throw error;
}
})
);
/**
* GET /devices/:uuid
* 获取设备信息 (公开接口,无需认证)
*/
router.get(
"/:uuid",
errors.catchAsync(async (req, res, next) => {
const {uuid} = req.params;
// 查找设备,包含绑定的账户信息
const device = await prisma.device.findUnique({
where: {uuid},
include: {
account: {
select: {
id: true,
name: true,
email: true,
avatarUrl: true,
},
},
},
});
if (!device) {
return next(errors.createError(404, "设备不存在"));
}
return res.json({
id: device.id,
uuid: device.uuid,
name: device.name,
hasPassword: !!device.password,
passwordHint: device.passwordHint,
createdAt: device.createdAt,
account: device.account ? {
id: device.account.id,
name: device.account.name,
email: device.account.email,
avatarUrl: device.account.avatarUrl,
} : null,
isBoundToAccount: !!device.account,
namespace: device.namespace,
});
})
);
/**
* PUT /devices/:uuid/name
* 设置设备名称 (需要UUID认证)
*/
router.put(
"/:uuid/name",
extractDeviceInfo,
errors.catchAsync(async (req, res, next) => {
const {name} = req.body;
const device = res.locals.device;
if (!name) {
return next(errors.createError(400, "设备名称是必需的"));
}
const updatedDevice = await prisma.device.update({
where: {id: device.id},
data: {name},
});
return res.json({
success: true,
device: {
id: updatedDevice.id,
uuid: updatedDevice.uuid,
name: updatedDevice.name,
hasPassword: !!updatedDevice.password,
passwordHint: updatedDevice.passwordHint,
},
});
})
);
/**
* GET /devices/online
* 查询在线设备WebSocket 已连接)
* 返回:[{ uuid, connections, name? }]
*/
router.get(
"/online",
errors.catchAsync(async (req, res) => {
const list = getOnlineDevices();
if (list.length === 0) {
return res.json({success: true, devices: []});
}
// 补充设备名称
const uuids = list.map((x) => x.uuid);
const rows = await prisma.device.findMany({
where: {uuid: {in: uuids}},
select: {uuid: true, name: true},
});
const nameMap = new Map(rows.map((r) => [r.uuid, r.name]));
const devices = list.map((x) => ({
uuid: x.uuid,
connections: x.connections,
name: nameMap.get(x.uuid) || null,
}));
res.json({success: true, devices});
})
);
export default router;