feat: add TallyNote local reimbursement ledger
TallyNote release / linux-x64 (push) Failing after 2m41s
TallyNote release / linux-x64 (push) Failing after 2m41s
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
import argon2 from "argon2";
|
||||
import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
|
||||
|
||||
const ARGON_OPTIONS = {
|
||||
type: argon2.argon2id,
|
||||
memoryCost: 65_536,
|
||||
timeCost: 3,
|
||||
parallelism: 1,
|
||||
hashLength: 32,
|
||||
} as const;
|
||||
|
||||
export function normalizeUsername(username: string): string {
|
||||
return username.normalize("NFKC").trim().toLocaleLowerCase("und");
|
||||
}
|
||||
|
||||
export function validateNewPassword(password: string): string | null {
|
||||
const length = [...password].length;
|
||||
if (length < 12 || length > 128 || Buffer.byteLength(password, "utf8") > 512) {
|
||||
return "密码长度需要为 12–128 个字符";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function hashPassword(password: string): Promise<string> {
|
||||
return argon2.hash(password, ARGON_OPTIONS);
|
||||
}
|
||||
|
||||
export async function verifyPassword(hash: string, password: string): Promise<boolean> {
|
||||
try {
|
||||
return await argon2.verify(hash, password);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function randomToken(bytes = 32): string {
|
||||
return randomBytes(bytes).toString("base64url");
|
||||
}
|
||||
|
||||
export function sha256(value: string | Buffer): string {
|
||||
return createHash("sha256").update(value).digest("hex");
|
||||
}
|
||||
|
||||
export function constantTimeEqual(left: string, right: string): boolean {
|
||||
const leftBuffer = Buffer.from(left);
|
||||
const rightBuffer = Buffer.from(right);
|
||||
return leftBuffer.length === rightBuffer.length && timingSafeEqual(leftBuffer, rightBuffer);
|
||||
}
|
||||
|
||||
export function temporaryPassword(): string {
|
||||
return `${randomToken(15)}A7!`;
|
||||
}
|
||||
Reference in New Issue
Block a user