Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | 1x 1x 1x 5x 5x 5x 5x 5x 1x 1x 1x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 3x 3x 3x 1x 3x 2x 2x 3x 1x 1x | import { SignJWT, jwtVerify, type JWTPayload } from 'jose';
import bcrypt from 'bcryptjs';
const SESSION_TTL_SECONDS = Number(process.env.SESSION_TTL_SECONDS ?? 28800);
function getSecret(): Uint8Array {
const secret = process.env.JWT_SECRET;
if (!secret || secret.length < 32) {
throw new Error('JWT_SECRET is missing or too short (need at least 32 chars).');
}
return new TextEncoder().encode(secret);
}
export type SessionPayload = {
sub: string;
username: string;
role: 'MASTER' | 'STAFF';
};
export async function hashPassword(plain: string): Promise<string> {
return bcrypt.hash(plain, 10);
}
export async function verifyPassword(plain: string, hash: string): Promise<boolean> {
return bcrypt.compare(plain, hash);
}
export async function signSession(payload: SessionPayload): Promise<string> {
return new SignJWT(payload as unknown as JWTPayload)
.setProtectedHeader({ alg: 'HS256' })
.setIssuedAt()
.setExpirationTime(`${SESSION_TTL_SECONDS}s`)
.sign(getSecret());
}
export async function verifySession(token: string): Promise<SessionPayload | null> {
try {
const { payload } = await jwtVerify(token, getSecret());
return payload as unknown as SessionPayload;
} catch {
return null;
}
}
export const SESSION_COOKIE = 'seo_session';
export const SESSION_MAX_AGE = SESSION_TTL_SECONDS;
|