新增本地认证与管理接口服务

This commit is contained in:
cat-shark
2026-06-20 10:07:44 +08:00
parent a13b804c7a
commit cf82d606aa
10 changed files with 1057 additions and 0 deletions
+63
View File
@@ -0,0 +1,63 @@
const crypto = require("node:crypto");
const { tokenSecret } = require("./config.cjs");
function nowIso() {
return new Date().toISOString();
}
function addYears(date, years) {
const next = new Date(date);
next.setFullYear(next.getFullYear() + years);
return next;
}
function addHours(date, hours) {
return new Date(date.getTime() + Number(hours || 0) * 3600000);
}
function sign(value) {
return crypto.createHmac("sha256", tokenSecret).update(value).digest("base64url");
}
function createToken(userId) {
const payload = Buffer.from(JSON.stringify({ userId, issuedAt: Date.now() })).toString("base64url");
return `${payload}.${sign(payload)}`;
}
function verifyToken(token) {
if (!token || typeof token !== "string" || !token.includes(".")) return null;
const [payload, signature] = token.split(".");
if (!payload || !signature || sign(payload) !== signature) return null;
try {
return JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
} catch {
return null;
}
}
function passwordHash(password, salt = crypto.randomBytes(16).toString("hex")) {
const hash = crypto.pbkdf2Sync(String(password), salt, 120000, 32, "sha256").toString("hex");
return `${salt}:${hash}`;
}
function checkPassword(password, stored) {
const [salt] = String(stored || "").split(":");
if (!salt) return false;
return passwordHash(password, salt) === stored;
}
function daysRemaining(expiresAt) {
return Math.max(0, Math.ceil((new Date(expiresAt).getTime() - Date.now()) / 86400000));
}
module.exports = {
nowIso,
addYears,
addHours,
createToken,
verifyToken,
passwordHash,
checkPassword,
daysRemaining,
};