const crypto = require("crypto"); const baseModel = require("../../middleware/baseModel"); const { op } = baseModel; const MAX_TOKENS_PER_USER = 5; function hashPlainToken(plain) { return crypto.createHash("sha256").update(plain, "utf8").digest("hex"); } function generatePlainToken() { return `waw_${crypto.randomBytes(24).toString("hex")}`; } /** 当前时间在 [start,end] 内且 status=active 的订阅 */ async function findActiveSubscriptionForUser(userId) { const now = new Date(); return baseModel.biz_subscription.findOne({ where: { user_id: userId, status: "active", start_time: { [op.lte]: now }, end_time: { [op.gte]: now }, }, order: [["id", "DESC"]], }); } async function createToken(body) { const { user_id, token_name, expire_at } = body; if (!user_id || !expire_at) throw new Error("缺少 user_id 或 expire_at"); const u = await baseModel.biz_user.findByPk(user_id); if (!u) throw new Error("用户不存在"); if (u.status !== "active") throw new Error("用户已禁用"); const activeCount = await baseModel.biz_api_token.count({ where: { user_id, status: "active" }, }); if (activeCount >= MAX_TOKENS_PER_USER) { throw new Error(`单用户最多 ${MAX_TOKENS_PER_USER} 个有效 Token`); } const sub = await findActiveSubscriptionForUser(user_id); const plan_id = sub ? sub.plan_id : null; const plain = generatePlainToken(); const token_hash = hashPlainToken(plain); const row = await baseModel.biz_api_token.create({ user_id, plan_id, token_name: token_name || "default", token_hash, status: "active", expire_at, }); return { row, plain_token: plain, warn: sub ? null : "当前无生效中的订阅,鉴权将失败", }; } async function revokeToken(body) { const id = body.id; if (id == null) throw new Error("缺少 id"); const row = await baseModel.biz_api_token.findByPk(id); if (!row) throw new Error("Token 不存在"); await row.update({ status: "revoked" }); return row; } async function revokeAllForUser(userId) { if (userId == null) throw new Error("缺少 user_id"); const [n] = await baseModel.biz_api_token.update( { status: "revoked" }, { where: { user_id: userId, status: "active" } } ); return n; } module.exports = { hashPlainToken, createToken, revokeToken, revokeAllForUser, findActiveSubscriptionForUser, MAX_TOKENS_PER_USER, };