mirror of
https://github.com/ZeroCatDev/ClassworksKV.git
synced 2025-12-07 13:03:09 +00:00
规范代码格式
This commit is contained in:
parent
4ec10acfcf
commit
c545612c9c
12
app.js
12
app.js
@ -1,8 +1,8 @@
|
||||
import "./utils/instrumentation.js";
|
||||
// import createError from "http-errors";
|
||||
import express from "express";
|
||||
import { join, dirname } from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import {dirname, join} from "path";
|
||||
import {fileURLToPath} from "url";
|
||||
// import cookieParser from "cookie-parser";
|
||||
import logger from "morgan";
|
||||
import bodyParser from "body-parser";
|
||||
@ -15,11 +15,11 @@ import deviceRouter from "./routes/device.js";
|
||||
import deviceAuthRouter from "./routes/device-auth.js";
|
||||
import accountsRouter from "./routes/accounts.js";
|
||||
import autoAuthRouter from "./routes/auto-auth.js";
|
||||
import { register } from "./utils/metrics.js";
|
||||
import {register} from "./utils/metrics.js";
|
||||
import cors from "cors";
|
||||
|
||||
var app = express();
|
||||
|
||||
import cors from "cors";
|
||||
app.options("/{*path}", cors());
|
||||
app.use(
|
||||
cors({
|
||||
@ -36,11 +36,11 @@ const __dirname = dirname(__filename);
|
||||
// view engine setup
|
||||
app.set("views", join(__dirname, "views"));
|
||||
app.set("view engine", "ejs");
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
app.use(bodyParser.urlencoded({extended: true}));
|
||||
app.use(bodyParser.json());
|
||||
app.use(logger("dev"));
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: false }));
|
||||
app.use(express.urlencoded({extended: false}));
|
||||
// app.use(cookieParser());
|
||||
app.use(express.static(join(__dirname, "public")));
|
||||
|
||||
|
||||
6
bin/www
6
bin/www
@ -5,9 +5,9 @@
|
||||
*/
|
||||
|
||||
import app from '../app.js';
|
||||
import { createServer } from 'http';
|
||||
import { initSocket } from '../utils/socket.js';
|
||||
import { initializeMetrics } from '../utils/metrics.js';
|
||||
import {createServer} from 'http';
|
||||
import {initSocket} from '../utils/socket.js';
|
||||
import {initializeMetrics} from '../utils/metrics.js';
|
||||
|
||||
/**
|
||||
* Get port from environment and store in Express.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
import { execSync } from "child_process";
|
||||
import {execSync} from "child_process";
|
||||
import dotenv from "dotenv";
|
||||
|
||||
dotenv.config();
|
||||
@ -9,7 +9,7 @@ dotenv.config();
|
||||
function runDatabaseMigration() {
|
||||
try {
|
||||
console.log("🔄 执行数据库迁移...");
|
||||
execSync("npx prisma migrate deploy", { stdio: "inherit" });
|
||||
execSync("npx prisma migrate deploy", {stdio: "inherit"});
|
||||
console.log("✅ 数据库迁移完成");
|
||||
} catch (error) {
|
||||
console.error("❌ 数据库迁移失败:", error.message);
|
||||
@ -33,8 +33,8 @@ function buildLocal() {
|
||||
try {
|
||||
// 确保数据库迁移已执行
|
||||
runDatabaseMigration();
|
||||
execSync("npm install", { stdio: "inherit" }); // 安装依赖
|
||||
execSync("npx prisma generate", { stdio: "inherit" }); // 生成 Prisma 客户端
|
||||
execSync("npm install", {stdio: "inherit"}); // 安装依赖
|
||||
execSync("npx prisma generate", {stdio: "inherit"}); // 生成 Prisma 客户端
|
||||
console.log("✅ 构建完成");
|
||||
} catch (error) {
|
||||
console.error("❌ 构建失败:", error.message);
|
||||
@ -45,7 +45,7 @@ function buildLocal() {
|
||||
// 🚀 启动服务函数
|
||||
function startServer() {
|
||||
try {
|
||||
execSync("npm run start", { stdio: "inherit" }); // 启动项目
|
||||
execSync("npm run start", {stdio: "inherit"}); // 启动项目
|
||||
} catch (error) {
|
||||
console.error("❌ 服务启动失败:", error.message);
|
||||
process.exit(1);
|
||||
@ -56,7 +56,7 @@ function startServer() {
|
||||
function runPrismaCommand(args) {
|
||||
try {
|
||||
const command = `npx prisma ${args.join(" ")}`;
|
||||
execSync(command, { stdio: "inherit" });
|
||||
execSync(command, {stdio: "inherit"});
|
||||
} catch (error) {
|
||||
console.error("❌ Prisma 命令执行失败:", error.message);
|
||||
process.exit(1);
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
|
||||
import http from 'http';
|
||||
import url from 'url';
|
||||
import { randomBytes } from 'crypto';
|
||||
import {randomBytes} from 'crypto';
|
||||
|
||||
// 配置
|
||||
const CONFIG = {
|
||||
@ -128,11 +128,11 @@ function createCallbackServer(state) {
|
||||
const parsedUrl = url.parse(req.url, true);
|
||||
|
||||
if (parsedUrl.pathname === CONFIG.callbackPath) {
|
||||
const { token, error, state: returnedState } = parsedUrl.query;
|
||||
const {token, error, state: returnedState} = parsedUrl.query;
|
||||
|
||||
// 验证状态参数
|
||||
if (returnedState !== state) {
|
||||
res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
|
||||
res.writeHead(400, {'Content-Type': 'text/html; charset=utf-8'});
|
||||
res.end(`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
@ -158,7 +158,7 @@ function createCallbackServer(state) {
|
||||
}
|
||||
|
||||
if (error) {
|
||||
res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
|
||||
res.writeHead(400, {'Content-Type': 'text/html; charset=utf-8'});
|
||||
res.end(`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
@ -184,7 +184,7 @@ function createCallbackServer(state) {
|
||||
}
|
||||
|
||||
if (token) {
|
||||
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
||||
res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'});
|
||||
res.end(`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
@ -212,7 +212,7 @@ function createCallbackServer(state) {
|
||||
}
|
||||
|
||||
// 如果没有token和error参数
|
||||
res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
|
||||
res.writeHead(400, {'Content-Type': 'text/html; charset=utf-8'});
|
||||
res.end(`
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
@ -236,7 +236,7 @@ function createCallbackServer(state) {
|
||||
reject(new Error('缺少必要的参数'));
|
||||
} else {
|
||||
// 404 for other paths
|
||||
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
||||
res.writeHead(404, {'Content-Type': 'text/plain'});
|
||||
res.end('Not Found');
|
||||
}
|
||||
};
|
||||
@ -271,7 +271,7 @@ function createCallbackServer(state) {
|
||||
|
||||
// 打开浏览器
|
||||
async function openBrowser(url) {
|
||||
const { spawn } = await import('child_process');
|
||||
const {spawn} = await import('child_process');
|
||||
|
||||
let command;
|
||||
let args;
|
||||
@ -288,7 +288,7 @@ async function openBrowser(url) {
|
||||
}
|
||||
|
||||
try {
|
||||
spawn(command, args, { detached: true, stdio: 'ignore' });
|
||||
spawn(command, args, {detached: true, stdio: 'ignore'});
|
||||
logSuccess('已尝试打开浏览器');
|
||||
} catch (error) {
|
||||
logWarning('无法自动打开浏览器,请手动打开授权链接');
|
||||
@ -323,7 +323,7 @@ async function saveToken(token) {
|
||||
try {
|
||||
// 确保目录存在
|
||||
if (!fs.existsSync(tokenDir)) {
|
||||
fs.mkdirSync(tokenDir, { recursive: true });
|
||||
fs.mkdirSync(tokenDir, {recursive: true});
|
||||
}
|
||||
|
||||
// 写入令牌
|
||||
|
||||
@ -10,8 +10,6 @@
|
||||
* 或配置为可执行:chmod +x cli/get-token.js && ./cli/get-token.js
|
||||
*/
|
||||
|
||||
import readline from 'readline';
|
||||
|
||||
// 配置
|
||||
const CONFIG = {
|
||||
// API服务器地址
|
||||
@ -166,7 +164,7 @@ async function saveToken(token) {
|
||||
try {
|
||||
// 确保目录存在
|
||||
if (!fs.existsSync(tokenDir)) {
|
||||
fs.mkdirSync(tokenDir, { recursive: true });
|
||||
fs.mkdirSync(tokenDir, {recursive: true});
|
||||
}
|
||||
|
||||
// 写入令牌
|
||||
@ -191,7 +189,7 @@ async function main() {
|
||||
}
|
||||
|
||||
// 1. 生成设备代码
|
||||
const { device_code, expires_in } = await generateDeviceCode();
|
||||
const {device_code, expires_in} = await generateDeviceCode();
|
||||
logSuccess('设备授权码生成成功!');
|
||||
|
||||
// 2. 显示设备代码和授权链接
|
||||
|
||||
@ -7,9 +7,9 @@
|
||||
* 3. passwordMiddleware - 验证设备密码
|
||||
*/
|
||||
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import {PrismaClient} from "@prisma/client";
|
||||
import errors from "../utils/errors.js";
|
||||
import { verifyDevicePassword } from "../utils/crypto.js";
|
||||
import {verifyDevicePassword} from "../utils/crypto.js";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
@ -54,7 +54,7 @@ export const deviceMiddleware = errors.catchAsync(async (req, res, next) => {
|
||||
|
||||
// 查找或创建设备
|
||||
let device = await prisma.device.findUnique({
|
||||
where: { uuid: deviceUuid },
|
||||
where: {uuid: deviceUuid},
|
||||
});
|
||||
|
||||
if (!device) {
|
||||
@ -89,7 +89,7 @@ export const deviceMiddleware = errors.catchAsync(async (req, res, next) => {
|
||||
* router.get('/path/:deviceUuid', deviceInfoMiddleware, handler)
|
||||
*/
|
||||
export const deviceInfoMiddleware = errors.catchAsync(async (req, res, next) => {
|
||||
const deviceUuid = req.params.deviceUuid ;
|
||||
const deviceUuid = req.params.deviceUuid;
|
||||
|
||||
if (!deviceUuid) {
|
||||
return next(errors.createError(400, "缺少设备UUID"));
|
||||
@ -97,7 +97,7 @@ export const deviceInfoMiddleware = errors.catchAsync(async (req, res, next) =>
|
||||
|
||||
// 查找设备
|
||||
const device = await prisma.device.findUnique({
|
||||
where: { uuid: deviceUuid },
|
||||
where: {uuid: deviceUuid},
|
||||
});
|
||||
|
||||
if (!device) {
|
||||
@ -123,7 +123,7 @@ export const deviceInfoMiddleware = errors.catchAsync(async (req, res, next) =>
|
||||
*/
|
||||
export const passwordMiddleware = errors.catchAsync(async (req, res, next) => {
|
||||
const device = res.locals.device;
|
||||
const { password } = req.body;
|
||||
const {password} = req.body;
|
||||
|
||||
if (!device) {
|
||||
return next(errors.createError(500, "设备信息未加载,请先使用deviceMiddleware"));
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { isDevelopment } from "../utils/config.js";
|
||||
import {isDevelopment} from "../utils/config.js";
|
||||
|
||||
const errorHandler = (err, req, res, next) => {
|
||||
// 判断响应是否已经发送
|
||||
|
||||
@ -6,9 +6,9 @@
|
||||
* 适用于只需要账户验证的接口
|
||||
*/
|
||||
|
||||
import { verifyAccessToken, validateAccountToken, generateAccessToken } from "../utils/tokenManager.js";
|
||||
import { verifyToken } from "../utils/jwt.js";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import {generateAccessToken, validateAccountToken, verifyAccessToken} from "../utils/tokenManager.js";
|
||||
import {verifyToken} from "../utils/jwt.js";
|
||||
import {PrismaClient} from "@prisma/client";
|
||||
import errors from "../utils/errors.js";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
@ -56,7 +56,7 @@ export const jwtAuth = async (req, res, next) => {
|
||||
|
||||
// 从数据库获取账户信息
|
||||
const account = await prisma.account.findUnique({
|
||||
where: { id: decoded.accountId },
|
||||
where: {id: decoded.accountId},
|
||||
});
|
||||
|
||||
if (!account) {
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
* 适用于所有KV相关的接口
|
||||
*/
|
||||
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import {PrismaClient} from "@prisma/client";
|
||||
import errors from "../utils/errors.js";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
@ -25,7 +25,7 @@ export const kvTokenAuth = async (req, res, next) => {
|
||||
|
||||
// 查找token对应的应用安装信息
|
||||
const appInstall = await prisma.appInstall.findUnique({
|
||||
where: { token },
|
||||
where: {token},
|
||||
include: {
|
||||
device: true,
|
||||
},
|
||||
|
||||
@ -56,7 +56,6 @@ export const prepareTokenForRateLimit = (req, res, next) => {
|
||||
};
|
||||
|
||||
|
||||
|
||||
// 认证相关路由限速器(防止暴力破解)
|
||||
export const authLimiter = rateLimit({
|
||||
windowMs: 30 * 60 * 1000, // 30分钟
|
||||
|
||||
@ -6,10 +6,10 @@
|
||||
* 3. 适用于需要设备上下文的接口
|
||||
*/
|
||||
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import {PrismaClient} from "@prisma/client";
|
||||
import errors from "../utils/errors.js";
|
||||
import { verifyToken as verifyAccountJWT } from "../utils/jwt.js";
|
||||
import { verifyDevicePassword } from "../utils/crypto.js";
|
||||
import {verifyToken as verifyAccountJWT} from "../utils/jwt.js";
|
||||
import {verifyDevicePassword} from "../utils/crypto.js";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
@ -26,7 +26,7 @@ export const uuidAuth = async (req, res, next) => {
|
||||
|
||||
// 2. 查找设备并存储到locals
|
||||
const device = await prisma.device.findUnique({
|
||||
where: { uuid },
|
||||
where: {uuid},
|
||||
});
|
||||
|
||||
if (!device) {
|
||||
@ -46,11 +46,11 @@ export const uuidAuth = async (req, res, next) => {
|
||||
try {
|
||||
const accountPayload = await verifyAccountJWT(jwt);
|
||||
const account = await prisma.account.findUnique({
|
||||
where: { id: accountPayload.accountId },
|
||||
where: {id: accountPayload.accountId},
|
||||
include: {
|
||||
devices: {
|
||||
where: { uuid },
|
||||
select: { id: true }
|
||||
where: {uuid},
|
||||
select: {id: true}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -93,14 +93,14 @@ export const uuidAuth = async (req, res, next) => {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
export const extractDeviceInfo = async (req,res,next) => {
|
||||
var uuid= extractUuid(req);
|
||||
export const extractDeviceInfo = async (req, res, next) => {
|
||||
var uuid = extractUuid(req);
|
||||
|
||||
if (!uuid) {
|
||||
throw errors.createError(400, "需要提供设备UUID");
|
||||
}
|
||||
const device = await prisma.device.findUnique({
|
||||
where: { uuid },
|
||||
where: {uuid},
|
||||
});
|
||||
if (!device) {
|
||||
throw errors.createError(404, "设备不存在");
|
||||
@ -109,6 +109,7 @@ export const extractDeviceInfo = async (req,res,next) => {
|
||||
res.locals.deviceId = device.id;
|
||||
next();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求中提取UUID
|
||||
*/
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
||||
<title>登录失败</title>
|
||||
<style>
|
||||
* {
|
||||
@ -50,9 +50,15 @@
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
0%, 100% { transform: translateX(0); }
|
||||
10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
|
||||
20%, 40%, 60%, 80% { transform: translateX(5px); }
|
||||
0%, 100% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
10%, 30%, 50%, 70%, 90% {
|
||||
transform: translateX(-5px);
|
||||
}
|
||||
20%, 40%, 60%, 80% {
|
||||
transform: translateX(5px);
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
@ -120,10 +126,10 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="container">
|
||||
<div class="error-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||
<path d="M6 18L18 6M6 6l12 12" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
@ -134,7 +140,7 @@
|
||||
<div class="error-code" id="errorCode"></div>
|
||||
</div>
|
||||
|
||||
<a href="javascript:history.back()" class="retry-btn">返回重试</a>
|
||||
<a class="retry-btn" href="javascript:history.back()">返回重试</a>
|
||||
<button class="close-btn" onclick="window.close()">关闭窗口</button>
|
||||
|
||||
<div class="help-text">
|
||||
@ -143,9 +149,9 @@
|
||||
• 回调URL是否已添加到OAuth应用中<br>
|
||||
• 环境变量是否配置正确
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
<script>
|
||||
// 从URL获取错误信息
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const error = params.get('error');
|
||||
@ -161,6 +167,6 @@
|
||||
document.getElementById('errorMsg').textContent = errorMsg;
|
||||
document.getElementById('errorCode').textContent = `错误代码: ${error}`;
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -2,7 +2,7 @@
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
||||
<title>登录成功</title>
|
||||
<style>
|
||||
* {
|
||||
@ -132,10 +132,10 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="container">
|
||||
<div class="success-icon">
|
||||
<svg fill="none" viewBox="0 0 24 24">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" />
|
||||
<path d="M5 13l4 4L19 7" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
@ -152,9 +152,9 @@
|
||||
<div class="auto-close">
|
||||
窗口将在 <span class="countdown" id="countdown">10</span> 秒后自动关闭
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
<script>
|
||||
// 从URL获取参数
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const token = params.get('token');
|
||||
@ -249,6 +249,6 @@
|
||||
clearInterval(timer);
|
||||
document.querySelector('.auto-close').style.display = 'none';
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,9 +1,9 @@
|
||||
import { Router } from "express";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import {Router} from "express";
|
||||
import {PrismaClient} from "@prisma/client";
|
||||
import crypto from "crypto";
|
||||
import { oauthProviders, getCallbackURL, generateState } from "../config/oauth.js";
|
||||
import { generateAccountToken, generateTokenPair, refreshAccessToken, revokeAllTokens, revokeRefreshToken } from "../utils/jwt.js";
|
||||
import { jwtAuth } from "../middleware/jwt-auth.js";
|
||||
import {generateState, getCallbackURL, oauthProviders} from "../config/oauth.js";
|
||||
import {generateTokenPair, refreshAccessToken, revokeAllTokens, revokeRefreshToken} from "../utils/jwt.js";
|
||||
import {jwtAuth} from "../middleware/jwt-auth.js";
|
||||
import errors from "../utils/errors.js";
|
||||
|
||||
const router = Router();
|
||||
@ -27,7 +27,7 @@ function generatePkcePair() {
|
||||
.replace(/\+/g, "-")
|
||||
.replace(/\//g, "_")
|
||||
.replace(/=+$/, "");
|
||||
return { codeVerifier, codeChallenge: challenge };
|
||||
return {codeVerifier, codeChallenge: challenge};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -81,8 +81,8 @@ router.get("/oauth/providers", (req, res) => {
|
||||
* - redirect_uri: 前端回调地址(可选)
|
||||
*/
|
||||
router.get("/oauth/:provider", (req, res) => {
|
||||
const { provider } = req.params;
|
||||
const { redirect_uri } = req.query;
|
||||
const {provider} = req.params;
|
||||
const {redirect_uri} = req.query;
|
||||
|
||||
const providerConfig = oauthProviders[provider];
|
||||
if (!providerConfig) {
|
||||
@ -157,8 +157,8 @@ router.get("/oauth/:provider", (req, res) => {
|
||||
* GET /accounts/oauth/:provider/callback
|
||||
*/
|
||||
router.get("/oauth/:provider/callback", async (req, res) => {
|
||||
const { provider } = req.params;
|
||||
const { code, state, error } = req.query;
|
||||
const {provider} = req.params;
|
||||
const {code, state, error} = req.query;
|
||||
|
||||
// 如果OAuth提供者返回错误
|
||||
if (error) {
|
||||
@ -198,12 +198,12 @@ router.get("/oauth/:provider/callback", async (req, res) => {
|
||||
},
|
||||
body: JSON.stringify({
|
||||
client_id: providerConfig.clientId,
|
||||
...(providerConfig.clientSecret ? { client_secret: providerConfig.clientSecret } : {}),
|
||||
...(providerConfig.clientSecret ? {client_secret: providerConfig.clientSecret} : {}),
|
||||
code: code,
|
||||
grant_type: "authorization_code",
|
||||
redirect_uri: getCallbackURL(provider),
|
||||
// PKCE: 携带code_verifier
|
||||
...(stateData?.codeVerifier ? { code_verifier: stateData.codeVerifier } : {}),
|
||||
...(stateData?.codeVerifier ? {code_verifier: stateData.codeVerifier} : {}),
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
@ -215,12 +215,12 @@ router.get("/oauth/:provider/callback", async (req, res) => {
|
||||
},
|
||||
body: new URLSearchParams({
|
||||
client_id: providerConfig.clientId,
|
||||
...(providerConfig.clientSecret ? { client_secret: providerConfig.clientSecret } : {}),
|
||||
...(providerConfig.clientSecret ? {client_secret: providerConfig.clientSecret} : {}),
|
||||
code: code,
|
||||
grant_type: "authorization_code",
|
||||
redirect_uri: getCallbackURL(provider),
|
||||
// PKCE: 携带code_verifier
|
||||
...(stateData?.codeVerifier ? { code_verifier: stateData.codeVerifier } : {}),
|
||||
...(stateData?.codeVerifier ? {code_verifier: stateData.codeVerifier} : {}),
|
||||
}),
|
||||
});
|
||||
}
|
||||
@ -237,7 +237,7 @@ router.get("/oauth/:provider/callback", async (req, res) => {
|
||||
if (provider === 'stcn') {
|
||||
const url = new URL(providerConfig.userInfoURL);
|
||||
url.searchParams.set('accessToken', tokenData.access_token);
|
||||
userResponse = await fetch(url, { headers: { "Accept": "application/json" } });
|
||||
userResponse = await fetch(url, {headers: {"Accept": "application/json"}});
|
||||
} else {
|
||||
userResponse = await fetch(providerConfig.userInfoURL, {
|
||||
headers: {
|
||||
@ -305,7 +305,7 @@ router.get("/oauth/:provider/callback", async (req, res) => {
|
||||
if (account) {
|
||||
// 更新账户信息
|
||||
account = await prisma.account.update({
|
||||
where: { id: account.id },
|
||||
where: {id: account.id},
|
||||
data: {
|
||||
email: normalizedUser.email || account.email,
|
||||
name: normalizedUser.name || account.name,
|
||||
@ -378,7 +378,7 @@ router.get("/profile", jwtAuth, async (req, res, next) => {
|
||||
const accountContext = res.locals.account;
|
||||
|
||||
const account = await prisma.account.findUnique({
|
||||
where: { id: accountContext.id },
|
||||
where: {id: accountContext.id},
|
||||
include: {
|
||||
devices: {
|
||||
select: {
|
||||
@ -439,7 +439,7 @@ router.get("/profile", jwtAuth, async (req, res, next) => {
|
||||
router.post("/devices/bind", jwtAuth, async (req, res, next) => {
|
||||
try {
|
||||
const accountContext = res.locals.account;
|
||||
const { uuid } = req.body;
|
||||
const {uuid} = req.body;
|
||||
|
||||
if (!uuid) {
|
||||
return res.status(400).json({
|
||||
@ -450,7 +450,7 @@ router.post("/devices/bind", jwtAuth, async (req, res, next) => {
|
||||
|
||||
// 查找设备
|
||||
const device = await prisma.device.findUnique({
|
||||
where: { uuid },
|
||||
where: {uuid},
|
||||
});
|
||||
|
||||
if (!device) {
|
||||
@ -470,7 +470,7 @@ router.post("/devices/bind", jwtAuth, async (req, res, next) => {
|
||||
|
||||
// 绑定设备到账户
|
||||
const updatedDevice = await prisma.device.update({
|
||||
where: { uuid },
|
||||
where: {uuid},
|
||||
data: {
|
||||
accountId: accountContext.id,
|
||||
},
|
||||
@ -506,7 +506,7 @@ router.post("/devices/bind", jwtAuth, async (req, res, next) => {
|
||||
router.post("/devices/unbind", jwtAuth, async (req, res, next) => {
|
||||
try {
|
||||
const accountContext = res.locals.account;
|
||||
const { uuid, uuids } = req.body;
|
||||
const {uuid, uuids} = req.body;
|
||||
|
||||
// 支持单个解绑或批量解绑
|
||||
const uuidsToUnbind = uuids || (uuid ? [uuid] : []);
|
||||
@ -521,7 +521,7 @@ router.post("/devices/unbind", jwtAuth, async (req, res, next) => {
|
||||
// 查找所有设备并验证所有权
|
||||
const devices = await prisma.device.findMany({
|
||||
where: {
|
||||
uuid: { in: uuidsToUnbind },
|
||||
uuid: {in: uuidsToUnbind},
|
||||
},
|
||||
});
|
||||
|
||||
@ -547,7 +547,7 @@ router.post("/devices/unbind", jwtAuth, async (req, res, next) => {
|
||||
// 批量解绑设备
|
||||
await prisma.device.updateMany({
|
||||
where: {
|
||||
uuid: { in: uuidsToUnbind },
|
||||
uuid: {in: uuidsToUnbind},
|
||||
accountId: accountContext.id,
|
||||
},
|
||||
data: {
|
||||
@ -577,7 +577,7 @@ router.get("/devices", jwtAuth, async (req, res, next) => {
|
||||
const accountContext = res.locals.account;
|
||||
// 获取账户的设备列表
|
||||
const account = await prisma.account.findUnique({
|
||||
where: { id: accountContext.id },
|
||||
where: {id: accountContext.id},
|
||||
include: {
|
||||
devices: {
|
||||
select: {
|
||||
@ -609,11 +609,11 @@ router.get("/devices", jwtAuth, async (req, res, next) => {
|
||||
*/
|
||||
router.get("/device/:uuid/account", async (req, res, next) => {
|
||||
try {
|
||||
const { uuid } = req.params;
|
||||
const {uuid} = req.params;
|
||||
|
||||
// 查找设备及其关联的账户
|
||||
const device = await prisma.device.findUnique({
|
||||
where: { uuid },
|
||||
where: {uuid},
|
||||
include: {
|
||||
account: {
|
||||
select: {
|
||||
@ -667,7 +667,7 @@ router.get("/device/:uuid/account", async (req, res, next) => {
|
||||
*/
|
||||
router.post("/refresh", async (req, res, next) => {
|
||||
try {
|
||||
const { refresh_token } = req.body;
|
||||
const {refresh_token} = req.body;
|
||||
|
||||
if (!refresh_token) {
|
||||
return res.status(400).json({
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
import { Router } from "express";
|
||||
const router = Router();
|
||||
import { uuidAuth } from "../middleware/uuidAuth.js";
|
||||
import { jwtAuth } from "../middleware/jwt-auth.js";
|
||||
import { kvTokenAuth } from "../middleware/kvTokenAuth.js";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import {Router} from "express";
|
||||
import {uuidAuth} from "../middleware/uuidAuth.js";
|
||||
import {PrismaClient} from "@prisma/client";
|
||||
import crypto from "crypto";
|
||||
import errors from "../utils/errors.js";
|
||||
import { verifyDevicePassword } from "../utils/crypto.js";
|
||||
import {verifyDevicePassword} from "../utils/crypto.js";
|
||||
|
||||
const router = Router();
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
@ -17,11 +16,11 @@ const prisma = new PrismaClient();
|
||||
router.get(
|
||||
"/devices/:uuid/apps",
|
||||
errors.catchAsync(async (req, res, next) => {
|
||||
const { uuid } = req.params;
|
||||
const {uuid} = req.params;
|
||||
|
||||
// 查找设备
|
||||
const device = await prisma.device.findUnique({
|
||||
where: { uuid },
|
||||
where: {uuid},
|
||||
});
|
||||
|
||||
if (!device) {
|
||||
@ -29,7 +28,7 @@ router.get(
|
||||
}
|
||||
|
||||
const installations = await prisma.appInstall.findMany({
|
||||
where: { deviceId: device.id },
|
||||
where: {deviceId: device.id},
|
||||
});
|
||||
|
||||
const apps = installations.map(install => ({
|
||||
@ -56,8 +55,8 @@ router.post(
|
||||
uuidAuth,
|
||||
errors.catchAsync(async (req, res, next) => {
|
||||
const device = res.locals.device;
|
||||
const { appId } = req.params;
|
||||
const { note } = req.body;
|
||||
const {appId} = req.params;
|
||||
const {note} = req.body;
|
||||
|
||||
// 生成token
|
||||
const token = crypto.randomBytes(32).toString("hex");
|
||||
@ -92,10 +91,10 @@ router.delete(
|
||||
uuidAuth,
|
||||
errors.catchAsync(async (req, res, next) => {
|
||||
const device = res.locals.device;
|
||||
const { installId } = req.params;
|
||||
const {installId} = req.params;
|
||||
|
||||
const installation = await prisma.appInstall.findUnique({
|
||||
where: { id: installId },
|
||||
where: {id: installId},
|
||||
});
|
||||
|
||||
if (!installation) {
|
||||
@ -108,7 +107,7 @@ router.delete(
|
||||
}
|
||||
|
||||
await prisma.appInstall.delete({
|
||||
where: { id: installation.id },
|
||||
where: {id: installation.id},
|
||||
});
|
||||
|
||||
return res.status(204).end();
|
||||
@ -122,7 +121,7 @@ router.delete(
|
||||
router.get(
|
||||
"/tokens",
|
||||
errors.catchAsync(async (req, res, next) => {
|
||||
const { uuid } = req.query;
|
||||
const {uuid} = req.query;
|
||||
|
||||
if (!uuid) {
|
||||
return next(errors.createError(400, "需要提供设备UUID"));
|
||||
@ -130,7 +129,7 @@ router.get(
|
||||
|
||||
// 查找设备
|
||||
const device = await prisma.device.findUnique({
|
||||
where: { uuid },
|
||||
where: {uuid},
|
||||
});
|
||||
|
||||
if (!device) {
|
||||
@ -139,8 +138,8 @@ router.get(
|
||||
|
||||
// 获取该设备的所有应用安装记录(即token)
|
||||
const installations = await prisma.appInstall.findMany({
|
||||
where: { deviceId: device.id },
|
||||
orderBy: { installedAt: 'desc' },
|
||||
where: {deviceId: device.id},
|
||||
orderBy: {installedAt: 'desc'},
|
||||
});
|
||||
|
||||
const tokens = installations.map(install => ({
|
||||
@ -168,7 +167,7 @@ router.get(
|
||||
router.post(
|
||||
"/auth/token",
|
||||
errors.catchAsync(async (req, res, next) => {
|
||||
const { namespace, password, appId } = req.body;
|
||||
const {namespace, password, appId} = req.body;
|
||||
|
||||
if (!namespace) {
|
||||
return next(errors.createError(400, "需要提供 namespace"));
|
||||
@ -180,7 +179,7 @@ router.post(
|
||||
|
||||
// 通过 namespace 查找设备
|
||||
const device = await prisma.device.findUnique({
|
||||
where: { namespace },
|
||||
where: {namespace},
|
||||
include: {
|
||||
autoAuths: true,
|
||||
},
|
||||
@ -208,8 +207,8 @@ router.post(
|
||||
|
||||
// 自动迁移:将哈希密码更新为明文密码
|
||||
await prisma.autoAuth.update({
|
||||
where: { id: autoAuth.id },
|
||||
data: { password: password }, // 保存明文密码
|
||||
where: {id: autoAuth.id},
|
||||
data: {password: password}, // 保存明文密码
|
||||
});
|
||||
|
||||
console.log(`AutoAuth ${autoAuth.id} 密码已自动迁移为明文`);
|
||||
@ -217,7 +216,7 @@ router.post(
|
||||
}
|
||||
} catch (err) {
|
||||
// 如果验证失败,继续尝试下一个
|
||||
continue;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -267,8 +266,8 @@ router.post(
|
||||
router.post(
|
||||
"/tokens/:token/set-student-name",
|
||||
errors.catchAsync(async (req, res, next) => {
|
||||
const { token } = req.params;
|
||||
const { name } = req.body;
|
||||
const {token} = req.params;
|
||||
const {name} = req.body;
|
||||
|
||||
if (!name) {
|
||||
return next(errors.createError(400, "需要提供学生名称"));
|
||||
@ -276,7 +275,7 @@ router.post(
|
||||
|
||||
// 查找 token 对应的应用安装记录
|
||||
const appInstall = await prisma.appInstall.findUnique({
|
||||
where: { token },
|
||||
where: {token},
|
||||
include: {
|
||||
device: true,
|
||||
},
|
||||
@ -287,7 +286,7 @@ router.post(
|
||||
}
|
||||
|
||||
// 验证 token 类型是否为 student
|
||||
if (!['student','parent'].includes(appInstall.deviceType)) {
|
||||
if (!['student', 'parent'].includes(appInstall.deviceType)) {
|
||||
return next(errors.createError(403, "只有学生和家长类型的 token 可以设置名称"));
|
||||
}
|
||||
|
||||
@ -325,8 +324,8 @@ router.post(
|
||||
|
||||
// 更新 AppInstall 的 note 字段
|
||||
const updatedInstall = await prisma.appInstall.update({
|
||||
where: { id: appInstall.id },
|
||||
data: { note: appInstall.deviceType === 'parent' ? `${name} 家长` : name },
|
||||
where: {id: appInstall.id},
|
||||
data: {note: appInstall.deviceType === 'parent' ? `${name} 家长` : name},
|
||||
});
|
||||
|
||||
return res.json({
|
||||
@ -347,12 +346,12 @@ router.post(
|
||||
router.put(
|
||||
"/tokens/:token/note",
|
||||
errors.catchAsync(async (req, res, next) => {
|
||||
const { token } = req.params;
|
||||
const { note } = req.body;
|
||||
const {token} = req.params;
|
||||
const {note} = req.body;
|
||||
|
||||
// 查找 token 对应的应用安装记录
|
||||
const appInstall = await prisma.appInstall.findUnique({
|
||||
where: { token },
|
||||
where: {token},
|
||||
});
|
||||
|
||||
if (!appInstall) {
|
||||
@ -361,8 +360,8 @@ router.put(
|
||||
|
||||
// 更新 AppInstall 的 note 字段
|
||||
const updatedInstall = await prisma.appInstall.update({
|
||||
where: { id: appInstall.id },
|
||||
data: { note: note || null },
|
||||
where: {id: appInstall.id},
|
||||
data: {note: note || null},
|
||||
});
|
||||
|
||||
return res.json({
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import { Router } from "express";
|
||||
const router = Router();
|
||||
import { jwtAuth } from "../middleware/jwt-auth.js";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import {Router} from "express";
|
||||
import {jwtAuth} from "../middleware/jwt-auth.js";
|
||||
import {PrismaClient} from "@prisma/client";
|
||||
import errors from "../utils/errors.js";
|
||||
|
||||
const router = Router();
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
/**
|
||||
@ -14,12 +15,12 @@ router.get(
|
||||
"/devices/:uuid/auth-configs",
|
||||
jwtAuth,
|
||||
errors.catchAsync(async (req, res, next) => {
|
||||
const { uuid } = req.params;
|
||||
const {uuid} = req.params;
|
||||
const account = res.locals.account;
|
||||
|
||||
// 查找设备并验证是否属于当前账户
|
||||
const device = await prisma.device.findUnique({
|
||||
where: { uuid },
|
||||
where: {uuid},
|
||||
});
|
||||
|
||||
if (!device) {
|
||||
@ -32,8 +33,8 @@ router.get(
|
||||
}
|
||||
|
||||
const autoAuths = await prisma.autoAuth.findMany({
|
||||
where: { deviceId: device.id },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
where: {deviceId: device.id},
|
||||
orderBy: {createdAt: 'desc'},
|
||||
});
|
||||
|
||||
// 返回配置,智能处理密码显示
|
||||
@ -68,13 +69,13 @@ router.post(
|
||||
"/devices/:uuid/auth-configs",
|
||||
jwtAuth,
|
||||
errors.catchAsync(async (req, res, next) => {
|
||||
const { uuid } = req.params;
|
||||
const {uuid} = req.params;
|
||||
const account = res.locals.account;
|
||||
const { password, deviceType, isReadOnly } = req.body;
|
||||
const {password, deviceType, isReadOnly} = req.body;
|
||||
|
||||
// 查找设备并验证是否属于当前账户
|
||||
const device = await prisma.device.findUnique({
|
||||
where: { uuid },
|
||||
where: {uuid},
|
||||
});
|
||||
|
||||
if (!device) {
|
||||
@ -97,7 +98,7 @@ router.post(
|
||||
|
||||
// 查询该设备的所有自动授权配置,本地检查是否存在相同密码
|
||||
const allAuths = await prisma.autoAuth.findMany({
|
||||
where: { deviceId: device.id },
|
||||
where: {deviceId: device.id},
|
||||
});
|
||||
|
||||
const existingAuth = allAuths.find(auth => auth.password === plainPassword);
|
||||
@ -127,7 +128,8 @@ router.post(
|
||||
},
|
||||
});
|
||||
})
|
||||
);/**
|
||||
);
|
||||
/**
|
||||
* PUT /auto-auth/devices/:uuid/auth-configs/:configId
|
||||
* 更新自动授权配置 (需要 JWT 认证,且设备必须绑定到该账户)
|
||||
* Body: { password?: string, deviceType?: string, isReadOnly?: boolean }
|
||||
@ -136,13 +138,13 @@ router.put(
|
||||
"/devices/:uuid/auth-configs/:configId",
|
||||
jwtAuth,
|
||||
errors.catchAsync(async (req, res, next) => {
|
||||
const { uuid, configId } = req.params;
|
||||
const {uuid, configId} = req.params;
|
||||
const account = res.locals.account;
|
||||
const { password, deviceType, isReadOnly } = req.body;
|
||||
const {password, deviceType, isReadOnly} = req.body;
|
||||
|
||||
// 查找设备并验证是否属于当前账户
|
||||
const device = await prisma.device.findUnique({
|
||||
where: { uuid },
|
||||
where: {uuid},
|
||||
});
|
||||
|
||||
if (!device) {
|
||||
@ -156,7 +158,7 @@ router.put(
|
||||
|
||||
// 查找自动授权配置
|
||||
const autoAuth = await prisma.autoAuth.findUnique({
|
||||
where: { id: configId },
|
||||
where: {id: configId},
|
||||
});
|
||||
|
||||
if (!autoAuth) {
|
||||
@ -183,7 +185,7 @@ router.put(
|
||||
|
||||
// 查询该设备的所有配置,本地检查新密码是否与其他配置冲突
|
||||
const allAuths = await prisma.autoAuth.findMany({
|
||||
where: { deviceId: device.id },
|
||||
where: {deviceId: device.id},
|
||||
});
|
||||
|
||||
const conflictAuth = allAuths.find(auth =>
|
||||
@ -207,7 +209,7 @@ router.put(
|
||||
|
||||
// 更新配置
|
||||
const updatedAuth = await prisma.autoAuth.update({
|
||||
where: { id: configId },
|
||||
where: {id: configId},
|
||||
data: updateData,
|
||||
});
|
||||
|
||||
@ -232,12 +234,12 @@ router.delete(
|
||||
"/devices/:uuid/auth-configs/:configId",
|
||||
jwtAuth,
|
||||
errors.catchAsync(async (req, res, next) => {
|
||||
const { uuid, configId } = req.params;
|
||||
const {uuid, configId} = req.params;
|
||||
const account = res.locals.account;
|
||||
|
||||
// 查找设备并验证是否属于当前账户
|
||||
const device = await prisma.device.findUnique({
|
||||
where: { uuid },
|
||||
where: {uuid},
|
||||
});
|
||||
|
||||
if (!device) {
|
||||
@ -251,7 +253,7 @@ router.delete(
|
||||
|
||||
// 查找自动授权配置
|
||||
const autoAuth = await prisma.autoAuth.findUnique({
|
||||
where: { id: configId },
|
||||
where: {id: configId},
|
||||
});
|
||||
|
||||
if (!autoAuth) {
|
||||
@ -265,7 +267,7 @@ router.delete(
|
||||
|
||||
// 删除配置
|
||||
await prisma.autoAuth.delete({
|
||||
where: { id: configId },
|
||||
where: {id: configId},
|
||||
});
|
||||
|
||||
return res.status(204).end();
|
||||
@ -281,9 +283,9 @@ router.put(
|
||||
"/devices/:uuid/namespace",
|
||||
jwtAuth,
|
||||
errors.catchAsync(async (req, res, next) => {
|
||||
const { uuid } = req.params;
|
||||
const {uuid} = req.params;
|
||||
const account = res.locals.account;
|
||||
const { namespace } = req.body;
|
||||
const {namespace} = req.body;
|
||||
|
||||
if (!namespace) {
|
||||
return next(errors.createError(400, "需要提供 namespace"));
|
||||
@ -298,7 +300,7 @@ router.put(
|
||||
|
||||
// 查找设备并验证是否属于当前账户
|
||||
const device = await prisma.device.findUnique({
|
||||
where: { uuid },
|
||||
where: {uuid},
|
||||
});
|
||||
|
||||
if (!device) {
|
||||
@ -313,7 +315,7 @@ router.put(
|
||||
// 检查新的 namespace 是否已被其他设备使用
|
||||
if (device.namespace !== trimmedNamespace) {
|
||||
const existingDevice = await prisma.device.findUnique({
|
||||
where: { namespace: trimmedNamespace },
|
||||
where: {namespace: trimmedNamespace},
|
||||
});
|
||||
|
||||
if (existingDevice) {
|
||||
@ -323,8 +325,8 @@ router.put(
|
||||
|
||||
// 更新设备的 namespace
|
||||
const updatedDevice = await prisma.device.update({
|
||||
where: { id: device.id },
|
||||
data: { namespace: trimmedNamespace },
|
||||
where: {id: device.id},
|
||||
data: {namespace: trimmedNamespace},
|
||||
});
|
||||
|
||||
return res.json({
|
||||
|
||||
@ -1,13 +1,12 @@
|
||||
import { Router } from "express";
|
||||
import {Router} from "express";
|
||||
import deviceCodeStore from "../utils/deviceCodeStore.js";
|
||||
import errors from "../utils/errors.js";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import {PrismaClient} from "@prisma/client";
|
||||
|
||||
const router = Router();
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* POST /device/code
|
||||
* 生成设备授权码
|
||||
@ -55,7 +54,7 @@ router.post(
|
||||
router.post(
|
||||
"/device/bind",
|
||||
errors.catchAsync(async (req, res, next) => {
|
||||
const { device_code, token } = req.body;
|
||||
const {device_code, token} = req.body;
|
||||
|
||||
if (!device_code || !token) {
|
||||
return next(
|
||||
@ -65,7 +64,7 @@ router.post(
|
||||
|
||||
// 验证token是否有效(检查数据库)
|
||||
const appInstall = await prisma.appInstall.findUnique({
|
||||
where: { token },
|
||||
where: {token},
|
||||
});
|
||||
|
||||
if (!appInstall) {
|
||||
@ -119,7 +118,7 @@ router.post(
|
||||
router.get(
|
||||
"/device/token",
|
||||
errors.catchAsync(async (req, res, next) => {
|
||||
const { device_code } = req.query;
|
||||
const {device_code} = req.query;
|
||||
|
||||
if (!device_code) {
|
||||
return next(errors.createError(400, "请提供 device_code"));
|
||||
@ -174,7 +173,7 @@ router.get(
|
||||
router.get(
|
||||
"/device/status",
|
||||
errors.catchAsync(async (req, res, next) => {
|
||||
const { device_code } = req.query;
|
||||
const {device_code} = req.query;
|
||||
|
||||
if (!device_code) {
|
||||
return next(errors.createError(400, "请提供 device_code"));
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
import { Router } from "express";
|
||||
const router = Router();
|
||||
import { uuidAuth, extractDeviceInfo } from "../middleware/uuidAuth.js";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import crypto from "crypto";
|
||||
import {Router} from "express";
|
||||
import {extractDeviceInfo} from "../middleware/uuidAuth.js";
|
||||
import {PrismaClient} from "@prisma/client";
|
||||
import errors from "../utils/errors.js";
|
||||
import { hashPassword, verifyDevicePassword } from "../utils/crypto.js";
|
||||
import { getOnlineDevices } from "../utils/socket.js";
|
||||
import { registeredDevicesTotal } from "../utils/metrics.js";
|
||||
import {getOnlineDevices} from "../utils/socket.js";
|
||||
import {registeredDevicesTotal} from "../utils/metrics.js";
|
||||
|
||||
const router = Router();
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
@ -38,7 +37,7 @@ async function createDefaultAutoAuth(deviceId) {
|
||||
router.post(
|
||||
"/",
|
||||
errors.catchAsync(async (req, res, next) => {
|
||||
const { uuid, deviceName, namespace } = req.body;
|
||||
const {uuid, deviceName, namespace} = req.body;
|
||||
|
||||
if (!uuid) {
|
||||
return next(errors.createError(400, "设备UUID是必需的"));
|
||||
@ -51,7 +50,7 @@ router.post(
|
||||
try {
|
||||
// 检查UUID是否已存在
|
||||
const existingDevice = await prisma.device.findUnique({
|
||||
where: { uuid },
|
||||
where: {uuid},
|
||||
});
|
||||
|
||||
if (existingDevice) {
|
||||
@ -63,7 +62,7 @@ router.post(
|
||||
|
||||
// 检查 namespace 是否已被使用
|
||||
const existingNamespace = await prisma.device.findUnique({
|
||||
where: { namespace: deviceNamespace },
|
||||
where: {namespace: deviceNamespace},
|
||||
});
|
||||
|
||||
if (existingNamespace) {
|
||||
@ -109,11 +108,11 @@ router.post(
|
||||
router.get(
|
||||
"/:uuid",
|
||||
errors.catchAsync(async (req, res, next) => {
|
||||
const { uuid } = req.params;
|
||||
const {uuid} = req.params;
|
||||
|
||||
// 查找设备,包含绑定的账户信息
|
||||
const device = await prisma.device.findUnique({
|
||||
where: { uuid },
|
||||
where: {uuid},
|
||||
include: {
|
||||
account: {
|
||||
select: {
|
||||
@ -147,7 +146,8 @@ router.get(
|
||||
namespace: device.namespace,
|
||||
});
|
||||
})
|
||||
);/**
|
||||
);
|
||||
/**
|
||||
* PUT /devices/:uuid/name
|
||||
* 设置设备名称 (需要UUID认证)
|
||||
*/
|
||||
@ -155,7 +155,7 @@ router.put(
|
||||
"/:uuid/name",
|
||||
extractDeviceInfo,
|
||||
errors.catchAsync(async (req, res, next) => {
|
||||
const { name } = req.body;
|
||||
const {name} = req.body;
|
||||
const device = res.locals.device;
|
||||
|
||||
if (!name) {
|
||||
@ -163,8 +163,8 @@ router.put(
|
||||
}
|
||||
|
||||
const updatedDevice = await prisma.device.update({
|
||||
where: { id: device.id },
|
||||
data: { name },
|
||||
where: {id: device.id},
|
||||
data: {name},
|
||||
});
|
||||
|
||||
return res.json({
|
||||
@ -181,7 +181,6 @@ router.put(
|
||||
);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* GET /devices/online
|
||||
* 查询在线设备(WebSocket 已连接)
|
||||
@ -193,14 +192,14 @@ router.get(
|
||||
const list = getOnlineDevices();
|
||||
|
||||
if (list.length === 0) {
|
||||
return res.json({ success: true, devices: [] });
|
||||
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 },
|
||||
where: {uuid: {in: uuids}},
|
||||
select: {uuid: true, name: true},
|
||||
});
|
||||
const nameMap = new Map(rows.map((r) => [r.uuid, r.name]));
|
||||
|
||||
@ -210,7 +209,7 @@ router.get(
|
||||
name: nameMap.get(x.uuid) || null,
|
||||
}));
|
||||
|
||||
res.json({ success: true, devices });
|
||||
res.json({success: true, devices});
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { Router } from "express";
|
||||
import {Router} from "express";
|
||||
|
||||
var router = Router();
|
||||
|
||||
/* GET home page. */
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
import { Router } from "express";
|
||||
const router = Router();
|
||||
import {Router} from "express";
|
||||
import kvStore from "../utils/kvStore.js";
|
||||
import { broadcastKeyChanged } from "../utils/socket.js";
|
||||
import { kvTokenAuth } from "../middleware/kvTokenAuth.js";
|
||||
import {broadcastKeyChanged} from "../utils/socket.js";
|
||||
import {kvTokenAuth} from "../middleware/kvTokenAuth.js";
|
||||
import {
|
||||
tokenReadLimiter,
|
||||
tokenWriteLimiter,
|
||||
tokenDeleteLimiter,
|
||||
prepareTokenForRateLimit,
|
||||
tokenBatchLimiter,
|
||||
prepareTokenForRateLimit
|
||||
tokenDeleteLimiter,
|
||||
tokenReadLimiter,
|
||||
tokenWriteLimiter
|
||||
} from "../middleware/rateLimiter.js";
|
||||
import errors from "../utils/errors.js";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import {PrismaClient} from "@prisma/client";
|
||||
|
||||
const router = Router();
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
@ -33,7 +34,7 @@ router.get(
|
||||
|
||||
// 获取设备信息,包含关联的账号
|
||||
const device = await prisma.device.findUnique({
|
||||
where: { id: deviceId },
|
||||
where: {id: deviceId},
|
||||
include: {
|
||||
account: true,
|
||||
},
|
||||
@ -87,7 +88,7 @@ router.get(
|
||||
|
||||
// 查找当前 token 对应的应用安装记录
|
||||
const appInstall = await prisma.appInstall.findUnique({
|
||||
where: { token },
|
||||
where: {token},
|
||||
include: {
|
||||
device: {
|
||||
select: {
|
||||
@ -132,7 +133,7 @@ router.get(
|
||||
tokenReadLimiter,
|
||||
errors.catchAsync(async (req, res) => {
|
||||
const deviceId = res.locals.deviceId;
|
||||
const { sortBy, sortDir, limit, skip } = req.query;
|
||||
const {sortBy, sortDir, limit, skip} = req.query;
|
||||
|
||||
// 构建选项
|
||||
const options = {
|
||||
@ -183,7 +184,7 @@ router.get(
|
||||
tokenReadLimiter,
|
||||
errors.catchAsync(async (req, res) => {
|
||||
const deviceId = res.locals.deviceId;
|
||||
const { sortBy, sortDir, limit, skip } = req.query;
|
||||
const {sortBy, sortDir, limit, skip} = req.query;
|
||||
|
||||
// 构建选项
|
||||
const options = {
|
||||
@ -229,7 +230,7 @@ router.get(
|
||||
tokenReadLimiter,
|
||||
errors.catchAsync(async (req, res, next) => {
|
||||
const deviceId = res.locals.deviceId;
|
||||
const { key } = req.params;
|
||||
const {key} = req.params;
|
||||
|
||||
const value = await kvStore.get(deviceId, key);
|
||||
|
||||
@ -252,7 +253,7 @@ router.get(
|
||||
tokenReadLimiter,
|
||||
errors.catchAsync(async (req, res, next) => {
|
||||
const deviceId = res.locals.deviceId;
|
||||
const { key } = req.params;
|
||||
const {key} = req.params;
|
||||
|
||||
const metadata = await kvStore.getMetadata(deviceId, key);
|
||||
if (!metadata) {
|
||||
@ -352,7 +353,7 @@ router.post(
|
||||
}
|
||||
|
||||
const deviceId = res.locals.deviceId;
|
||||
const { key } = req.params;
|
||||
const {key} = req.params;
|
||||
const value = req.body;
|
||||
|
||||
if (!value || Object.keys(value).length === 0) {
|
||||
@ -403,7 +404,7 @@ router.delete(
|
||||
}
|
||||
|
||||
const deviceId = res.locals.deviceId;
|
||||
const { key } = req.params;
|
||||
const {key} = req.params;
|
||||
|
||||
const result = await kvStore.delete(deviceId, key);
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import dotenv from "dotenv";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
export const siteKey = process.env.SITE_KEY || "";
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import bcrypt from "bcrypt";
|
||||
import { Base64 } from "js-base64";
|
||||
import {Base64} from "js-base64";
|
||||
|
||||
const SALT_ROUNDS = 8;
|
||||
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import "dotenv/config";
|
||||
import { NodeSDK } from "@opentelemetry/sdk-node";
|
||||
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
|
||||
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
|
||||
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
||||
import { resourceFromAttributes } from "@opentelemetry/resources";
|
||||
import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions";
|
||||
import {NodeSDK} from "@opentelemetry/sdk-node";
|
||||
import {getNodeAutoInstrumentations} from "@opentelemetry/auto-instrumentations-node";
|
||||
import {OTLPTraceExporter} from "@opentelemetry/exporter-trace-otlp-proto";
|
||||
import {BatchSpanProcessor} from "@opentelemetry/sdk-trace-base";
|
||||
import {resourceFromAttributes} from "@opentelemetry/resources";
|
||||
import {SemanticResourceAttributes} from "@opentelemetry/semantic-conventions";
|
||||
|
||||
if (process.env.AXIOM_TOKEN && process.env.AXIOM_DATASET) {
|
||||
// Initialize OTLP trace exporter with the endpoint URL and headers
|
||||
// Initialize OTLP trace exporter with the endpoint URL and headers
|
||||
|
||||
12
utils/jwt.js
12
utils/jwt.js
@ -1,11 +1,11 @@
|
||||
import jwt from 'jsonwebtoken';
|
||||
import {
|
||||
generateAccessToken,
|
||||
verifyAccessToken,
|
||||
generateTokenPair,
|
||||
refreshAccessToken,
|
||||
revokeAllTokens,
|
||||
revokeRefreshToken,
|
||||
verifyAccessToken,
|
||||
} from './tokenManager.js';
|
||||
|
||||
// JWT 配置(支持 HS256 与 RS256)
|
||||
@ -24,10 +24,10 @@ function getSignVerifyKeys() {
|
||||
if (!JWT_PRIVATE_KEY || !JWT_PUBLIC_KEY) {
|
||||
throw new Error('RS256 需要同时提供 JWT_PRIVATE_KEY 与 JWT_PUBLIC_KEY');
|
||||
}
|
||||
return { signKey: JWT_PRIVATE_KEY, verifyKey: JWT_PUBLIC_KEY };
|
||||
return {signKey: JWT_PRIVATE_KEY, verifyKey: JWT_PUBLIC_KEY};
|
||||
}
|
||||
// 默认 HS256
|
||||
return { signKey: JWT_SECRET, verifyKey: JWT_SECRET };
|
||||
return {signKey: JWT_SECRET, verifyKey: JWT_SECRET};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -35,7 +35,7 @@ function getSignVerifyKeys() {
|
||||
* @deprecated 建议使用 generateAccessToken
|
||||
*/
|
||||
export function signToken(payload) {
|
||||
const { signKey } = getSignVerifyKeys();
|
||||
const {signKey} = getSignVerifyKeys();
|
||||
return jwt.sign(payload, signKey, {
|
||||
expiresIn: JWT_EXPIRES_IN,
|
||||
algorithm: JWT_ALG,
|
||||
@ -47,8 +47,8 @@ export function signToken(payload) {
|
||||
* @deprecated 建议使用 verifyAccessToken
|
||||
*/
|
||||
export function verifyToken(token) {
|
||||
const { verifyKey } = getSignVerifyKeys();
|
||||
return jwt.verify(token, verifyKey, { algorithms: [JWT_ALG] });
|
||||
const {verifyKey} = getSignVerifyKeys();
|
||||
return jwt.verify(token, verifyKey, {algorithms: [JWT_ALG]});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import { keysTotal } from "./metrics.js";
|
||||
import {PrismaClient} from "@prisma/client";
|
||||
import {keysTotal} from "./metrics.js";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
class KVStore {
|
||||
/**
|
||||
* 通过设备ID和键名获取值
|
||||
@ -76,7 +77,7 @@ class KVStore {
|
||||
},
|
||||
update: {
|
||||
value,
|
||||
...(creatorIp && { creatorIp }),
|
||||
...(creatorIp && {creatorIp}),
|
||||
},
|
||||
create: {
|
||||
deviceId: deviceId,
|
||||
@ -122,7 +123,7 @@ class KVStore {
|
||||
const totalKeys = await prisma.kVStore.count();
|
||||
keysTotal.set(totalKeys);
|
||||
|
||||
return item ? { ...item, deviceId, key } : null;
|
||||
return item ? {...item, deviceId, key} : null;
|
||||
} catch (error) {
|
||||
// 忽略记录不存在的错误
|
||||
if (error.code === "P2025") {
|
||||
@ -139,7 +140,7 @@ class KVStore {
|
||||
* @returns {Array} 键名和元数据数组
|
||||
*/
|
||||
async list(deviceId, options = {}) {
|
||||
const { sortBy = "key", sortDir = "asc", limit = 100, skip = 0 } = options;
|
||||
const {sortBy = "key", sortDir = "asc", limit = 100, skip = 0} = options;
|
||||
|
||||
// 构建排序条件
|
||||
const orderBy = {};
|
||||
@ -182,7 +183,7 @@ class KVStore {
|
||||
* @returns {Array} 键名列表
|
||||
*/
|
||||
async listKeysOnly(deviceId, options = {}) {
|
||||
const { sortBy = "key", sortDir = "asc", limit = 100, skip = 0 } = options;
|
||||
const {sortBy = "key", sortDir = "asc", limit = 100, skip = 0} = options;
|
||||
|
||||
// 构建排序条件
|
||||
const orderBy = {};
|
||||
|
||||
@ -27,7 +27,7 @@ export const keysTotal = new client.Gauge({
|
||||
// 初始化指标数据
|
||||
export async function initializeMetrics() {
|
||||
try {
|
||||
const { PrismaClient } = await import('@prisma/client');
|
||||
const {PrismaClient} = await import('@prisma/client');
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
// 获取已注册设备总数
|
||||
@ -46,4 +46,4 @@ export async function initializeMetrics() {
|
||||
}
|
||||
|
||||
// 导出注册表用于 /metrics 端点
|
||||
export { register };
|
||||
export {register};
|
||||
@ -1,4 +1,4 @@
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import {PrismaClient} from "@prisma/client";
|
||||
import kvStore from "./kvStore.js";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
@ -24,8 +24,8 @@ async function getSystemDeviceId() {
|
||||
if (systemDeviceId) return systemDeviceId;
|
||||
|
||||
let device = await prisma.device.findUnique({
|
||||
where: { uuid: SYSTEM_DEVICE_UUID },
|
||||
select: { id: true },
|
||||
where: {uuid: SYSTEM_DEVICE_UUID},
|
||||
select: {id: true},
|
||||
});
|
||||
|
||||
if (!device) {
|
||||
@ -34,7 +34,7 @@ async function getSystemDeviceId() {
|
||||
uuid: SYSTEM_DEVICE_UUID,
|
||||
name: "系统设备",
|
||||
},
|
||||
select: { id: true },
|
||||
select: {id: true},
|
||||
});
|
||||
}
|
||||
|
||||
@ -65,7 +65,7 @@ export const initReadme = async () => {
|
||||
});
|
||||
|
||||
// 确保在异常情况下也有默认值
|
||||
readmeValue = { ...defaultReadme };
|
||||
readmeValue = {...defaultReadme};
|
||||
}
|
||||
};
|
||||
|
||||
@ -74,7 +74,7 @@ export const initReadme = async () => {
|
||||
* @returns {Object} readme 值对象
|
||||
*/
|
||||
export const getReadmeValue = () => {
|
||||
return readmeValue || { ...defaultReadme };
|
||||
return readmeValue || {...defaultReadme};
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -9,9 +9,9 @@
|
||||
* - 提供广播 KV 键变更的工具方法
|
||||
*/
|
||||
|
||||
import { Server } from "socket.io";
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import { onlineDevicesGauge } from "./metrics.js";
|
||||
import {Server} from "socket.io";
|
||||
import {PrismaClient} from "@prisma/client";
|
||||
import {onlineDevicesGauge} from "./metrics.js";
|
||||
|
||||
// Socket.IO 单例实例
|
||||
let io = null;
|
||||
@ -46,14 +46,16 @@ export function initSocket(server) {
|
||||
// 仅允许通过 query.token/apptoken 加入
|
||||
const qToken = socket.handshake?.query?.token || socket.handshake?.query?.apptoken;
|
||||
if (qToken && typeof qToken === "string") {
|
||||
joinByToken(socket, qToken).catch(() => {});
|
||||
joinByToken(socket, qToken).catch(() => {
|
||||
});
|
||||
}
|
||||
|
||||
// 客户端使用 KV token 加入房间
|
||||
socket.on("join-token", (payload) => {
|
||||
const token = payload?.token || payload?.apptoken;
|
||||
if (typeof token === "string" && token.length > 0) {
|
||||
joinByToken(socket, token).catch(() => {});
|
||||
joinByToken(socket, token).catch(() => {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@ -63,8 +65,8 @@ export function initSocket(server) {
|
||||
const token = payload?.token || payload?.apptoken;
|
||||
if (typeof token !== "string" || token.length === 0) return;
|
||||
const appInstall = await prisma.appInstall.findUnique({
|
||||
where: { token },
|
||||
include: { device: { select: { uuid: true } } },
|
||||
where: {token},
|
||||
include: {device: {select: {uuid: true}}},
|
||||
});
|
||||
const uuid = appInstall?.device?.uuid;
|
||||
if (uuid) {
|
||||
@ -100,10 +102,10 @@ export function initSocket(server) {
|
||||
if (uuids.length === 0) return;
|
||||
|
||||
const at = new Date().toISOString();
|
||||
const payload = { text: safeText, at, senderId: socket.id };
|
||||
const payload = {text: safeText, at, senderId: socket.id};
|
||||
|
||||
uuids.forEach((uuid) => {
|
||||
io.to(uuid).emit("chat:message", { uuid, ...payload });
|
||||
io.to(uuid).emit("chat:message", {uuid, ...payload});
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("chat:send error:", err);
|
||||
@ -142,7 +144,7 @@ function joinDeviceRoom(socket, uuid) {
|
||||
set.add(socket.id);
|
||||
onlineMap.set(uuid, set);
|
||||
// 可选:通知加入
|
||||
io.to(uuid).emit("device-joined", { uuid, connections: set.size });
|
||||
io.to(uuid).emit("device-joined", {uuid, connections: set.size});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -210,7 +212,7 @@ function removeTokenConnection(token, socketId) {
|
||||
*/
|
||||
export function broadcastKeyChanged(uuid, payload) {
|
||||
if (!io || !uuid) return;
|
||||
io.to(uuid).emit("kv-key-changed", { uuid, ...payload });
|
||||
io.to(uuid).emit("kv-key-changed", {uuid, ...payload});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -220,7 +222,7 @@ export function broadcastKeyChanged(uuid, payload) {
|
||||
export function getOnlineDevices() {
|
||||
const list = [];
|
||||
for (const [token, set] of onlineTokens.entries()) {
|
||||
list.push({ token, connections: set.size });
|
||||
list.push({token, connections: set.size});
|
||||
}
|
||||
// 默认按连接数降序
|
||||
return list.sort((a, b) => b.connections - a.connections);
|
||||
@ -240,8 +242,8 @@ export default {
|
||||
*/
|
||||
async function joinByToken(socket, token) {
|
||||
const appInstall = await prisma.appInstall.findUnique({
|
||||
where: { token },
|
||||
include: { device: { select: { uuid: true } } },
|
||||
where: {token},
|
||||
include: {device: {select: {uuid: true}}},
|
||||
});
|
||||
const uuid = appInstall?.device?.uuid;
|
||||
if (uuid) {
|
||||
@ -249,8 +251,8 @@ async function joinByToken(socket, token) {
|
||||
// 跟踪 token 连接用于指标统计
|
||||
trackTokenConnection(socket, token);
|
||||
// 可选:回执
|
||||
socket.emit("joined", { by: "token", uuid, token });
|
||||
socket.emit("joined", {by: "token", uuid, token});
|
||||
} else {
|
||||
socket.emit("join-error", { by: "token", reason: "invalid_token" });
|
||||
socket.emit("join-error", {by: "token", reason: "invalid_token"});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import jwt from 'jsonwebtoken';
|
||||
import crypto from 'crypto';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import {PrismaClient} from '@prisma/client';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
@ -32,19 +32,19 @@ function getKeys(tokenType = 'access') {
|
||||
if (!privateKey || !publicKey) {
|
||||
throw new Error(`RS256 需要同时提供 ${tokenType.toUpperCase()}_TOKEN_PRIVATE_KEY 与 ${tokenType.toUpperCase()}_TOKEN_PUBLIC_KEY`);
|
||||
}
|
||||
return { signKey: privateKey, verifyKey: publicKey };
|
||||
return {signKey: privateKey, verifyKey: publicKey};
|
||||
}
|
||||
|
||||
// 默认 HS256
|
||||
const secret = tokenType === 'access' ? ACCESS_TOKEN_SECRET : REFRESH_TOKEN_SECRET;
|
||||
return { signKey: secret, verifyKey: secret };
|
||||
return {signKey: secret, verifyKey: secret};
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成访问令牌
|
||||
*/
|
||||
export function generateAccessToken(account) {
|
||||
const { signKey } = getKeys('access');
|
||||
const {signKey} = getKeys('access');
|
||||
|
||||
const payload = {
|
||||
type: 'access',
|
||||
@ -68,7 +68,7 @@ export function generateAccessToken(account) {
|
||||
* 生成刷新令牌
|
||||
*/
|
||||
export function generateRefreshToken(account) {
|
||||
const { signKey } = getKeys('refresh');
|
||||
const {signKey} = getKeys('refresh');
|
||||
|
||||
const payload = {
|
||||
type: 'refresh',
|
||||
@ -90,7 +90,7 @@ export function generateRefreshToken(account) {
|
||||
* 验证访问令牌
|
||||
*/
|
||||
export function verifyAccessToken(token) {
|
||||
const { verifyKey } = getKeys('access');
|
||||
const {verifyKey} = getKeys('access');
|
||||
|
||||
try {
|
||||
const decoded = jwt.verify(token, verifyKey, {
|
||||
@ -113,7 +113,7 @@ export function verifyAccessToken(token) {
|
||||
* 验证刷新令牌
|
||||
*/
|
||||
export function verifyRefreshToken(token) {
|
||||
const { verifyKey } = getKeys('refresh');
|
||||
const {verifyKey} = getKeys('refresh');
|
||||
|
||||
try {
|
||||
const decoded = jwt.verify(token, verifyKey, {
|
||||
@ -146,7 +146,7 @@ export async function generateTokenPair(account) {
|
||||
|
||||
// 更新数据库中的刷新令牌
|
||||
await prisma.account.update({
|
||||
where: { id: account.id },
|
||||
where: {id: account.id},
|
||||
data: {
|
||||
refreshToken,
|
||||
refreshTokenExpiry,
|
||||
@ -172,7 +172,7 @@ export async function refreshAccessToken(refreshToken) {
|
||||
|
||||
// 从数据库获取账户信息
|
||||
const account = await prisma.account.findUnique({
|
||||
where: { id: decoded.accountId },
|
||||
where: {id: decoded.accountId},
|
||||
});
|
||||
|
||||
if (!account) {
|
||||
@ -218,9 +218,9 @@ export async function refreshAccessToken(refreshToken) {
|
||||
*/
|
||||
export async function revokeAllTokens(accountId) {
|
||||
await prisma.account.update({
|
||||
where: { id: accountId },
|
||||
where: {id: accountId},
|
||||
data: {
|
||||
tokenVersion: { increment: 1 },
|
||||
tokenVersion: {increment: 1},
|
||||
refreshToken: null,
|
||||
refreshTokenExpiry: null,
|
||||
updatedAt: new Date(),
|
||||
@ -233,7 +233,7 @@ export async function revokeAllTokens(accountId) {
|
||||
*/
|
||||
export async function revokeRefreshToken(accountId) {
|
||||
await prisma.account.update({
|
||||
where: { id: accountId },
|
||||
where: {id: accountId},
|
||||
data: {
|
||||
refreshToken: null,
|
||||
refreshTokenExpiry: null,
|
||||
@ -259,11 +259,16 @@ function parseExpirationToMs(expiresIn) {
|
||||
const unit = match[2];
|
||||
|
||||
switch (unit) {
|
||||
case 's': return value * 1000;
|
||||
case 'm': return value * 60 * 1000;
|
||||
case 'h': return value * 60 * 60 * 1000;
|
||||
case 'd': return value * 24 * 60 * 60 * 1000;
|
||||
default: throw new Error('Invalid time unit');
|
||||
case 's':
|
||||
return value * 1000;
|
||||
case 'm':
|
||||
return value * 60 * 1000;
|
||||
case 'h':
|
||||
return value * 60 * 60 * 1000;
|
||||
case 'd':
|
||||
return value * 24 * 60 * 60 * 1000;
|
||||
default:
|
||||
throw new Error('Invalid time unit');
|
||||
}
|
||||
}
|
||||
|
||||
@ -272,7 +277,7 @@ function parseExpirationToMs(expiresIn) {
|
||||
*/
|
||||
export async function validateAccountToken(decoded) {
|
||||
const account = await prisma.account.findUnique({
|
||||
where: { id: decoded.accountId },
|
||||
where: {id: decoded.accountId},
|
||||
});
|
||||
|
||||
if (!account) {
|
||||
|
||||
@ -8,8 +8,10 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Classworks 服务端</h1>
|
||||
<p>服务运行中</p>
|
||||
<h1>Classworks 服务端</h1>
|
||||
<p>服务运行中</p>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
window.open('https://kv.houlang.cloud')
|
||||
</script>
|
||||
</html>
|
||||
Loading…
x
Reference in New Issue
Block a user