mirror of
https://github.com/ZeroCatDev/ClassworksKV.git
synced 2025-10-22 10:23:12 +00:00
200 lines
4.1 KiB
JavaScript
200 lines
4.1 KiB
JavaScript
/**
|
||
* Device Code Store - 内存存储
|
||
*
|
||
* 用于存储设备授权流程中的临时代码和令牌
|
||
* 格式如: 1234-ABCD
|
||
*/
|
||
|
||
class DeviceCodeStore {
|
||
constructor() {
|
||
// 存储结构: { deviceCode: { token: string, expiresAt: number, createdAt: number } }
|
||
this.store = new Map();
|
||
|
||
// 默认过期时间: 15分钟
|
||
this.expirationTime = 15 * 60 * 1000;
|
||
|
||
// 定期清理过期数据 (每5分钟)
|
||
this.cleanupInterval = setInterval(() => {
|
||
this.cleanup();
|
||
}, 5 * 60 * 1000);
|
||
}
|
||
|
||
/**
|
||
* 生成设备代码 (格式: 1234-ABCD)
|
||
*/
|
||
generateDeviceCode() {
|
||
const part1 = Math.floor(1000 + Math.random() * 9000).toString(); // 4位数字
|
||
const part2 = Math.random().toString(36).substring(2, 6).toUpperCase(); // 4位字母
|
||
return `${part1}-${part2}`;
|
||
}
|
||
|
||
/**
|
||
* 创建新的设备代码
|
||
* @returns {string} 生成的设备代码
|
||
*/
|
||
create() {
|
||
let deviceCode;
|
||
|
||
// 确保生成的代码不重复
|
||
do {
|
||
deviceCode = this.generateDeviceCode();
|
||
} while (this.store.has(deviceCode));
|
||
|
||
const now = Date.now();
|
||
this.store.set(deviceCode, {
|
||
token: null,
|
||
expiresAt: now + this.expirationTime,
|
||
createdAt: now,
|
||
});
|
||
|
||
return deviceCode;
|
||
}
|
||
|
||
/**
|
||
* 绑定令牌到设备代码
|
||
* @param {string} deviceCode - 设备代码
|
||
* @param {string} token - 令牌
|
||
* @returns {boolean} 是否成功绑定
|
||
*/
|
||
bindToken(deviceCode, token) {
|
||
const entry = this.store.get(deviceCode);
|
||
|
||
if (!entry) {
|
||
return false;
|
||
}
|
||
|
||
// 检查是否过期
|
||
if (Date.now() > entry.expiresAt) {
|
||
this.store.delete(deviceCode);
|
||
return false;
|
||
}
|
||
|
||
// 绑定令牌
|
||
entry.token = token;
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 获取设备代码对应的令牌(获取后删除)
|
||
* @param {string} deviceCode - 设备代码
|
||
* @returns {string|null} 令牌,如果不存在或未绑定返回null
|
||
*/
|
||
getAndRemove(deviceCode) {
|
||
const entry = this.store.get(deviceCode);
|
||
|
||
if (!entry) {
|
||
return null;
|
||
}
|
||
|
||
// 检查是否过期
|
||
if (Date.now() > entry.expiresAt) {
|
||
this.store.delete(deviceCode);
|
||
return null;
|
||
}
|
||
|
||
// 如果令牌未绑定,返回null但不删除代码
|
||
if (!entry.token) {
|
||
return null;
|
||
}
|
||
|
||
// 获取令牌后删除条目
|
||
const token = entry.token;
|
||
this.store.delete(deviceCode);
|
||
return token;
|
||
}
|
||
|
||
/**
|
||
* 检查设备代码是否存在且未过期
|
||
* @param {string} deviceCode - 设备代码
|
||
* @returns {boolean}
|
||
*/
|
||
exists(deviceCode) {
|
||
const entry = this.store.get(deviceCode);
|
||
|
||
if (!entry) {
|
||
return false;
|
||
}
|
||
|
||
if (Date.now() > entry.expiresAt) {
|
||
this.store.delete(deviceCode);
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 获取设备代码的状态信息(不删除)
|
||
* @param {string} deviceCode - 设备代码
|
||
* @returns {object|null} 状态信息
|
||
*/
|
||
getStatus(deviceCode) {
|
||
const entry = this.store.get(deviceCode);
|
||
|
||
if (!entry) {
|
||
return null;
|
||
}
|
||
|
||
if (Date.now() > entry.expiresAt) {
|
||
this.store.delete(deviceCode);
|
||
return null;
|
||
}
|
||
|
||
return {
|
||
hasToken: !!entry.token,
|
||
expiresAt: entry.expiresAt,
|
||
createdAt: entry.createdAt,
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 清理过期的条目
|
||
*/
|
||
cleanup() {
|
||
const now = Date.now();
|
||
let cleanedCount = 0;
|
||
|
||
for (const [deviceCode, entry] of this.store.entries()) {
|
||
if (now > entry.expiresAt) {
|
||
this.store.delete(deviceCode);
|
||
cleanedCount++;
|
||
}
|
||
}
|
||
|
||
if (cleanedCount > 0) {
|
||
console.log(`清理了 ${cleanedCount} 个过期的设备代码`);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取当前存储的条目数量
|
||
*/
|
||
size() {
|
||
return this.store.size;
|
||
}
|
||
|
||
/**
|
||
* 清理定时器(用于优雅关闭)
|
||
*/
|
||
destroy() {
|
||
if (this.cleanupInterval) {
|
||
clearInterval(this.cleanupInterval);
|
||
}
|
||
this.store.clear();
|
||
}
|
||
}
|
||
|
||
// 导出单例
|
||
const deviceCodeStore = new DeviceCodeStore();
|
||
|
||
// 优雅关闭处理
|
||
process.on('SIGTERM', () => {
|
||
deviceCodeStore.destroy();
|
||
});
|
||
|
||
process.on('SIGINT', () => {
|
||
deviceCodeStore.destroy();
|
||
});
|
||
|
||
export default deviceCodeStore;
|