45 lines
1.4 KiB
TypeScript
45 lines
1.4 KiB
TypeScript
/**
|
|
* Invite-System (plan.md Abschnitt 3 und 9).
|
|
*
|
|
* - Token wird zufaellig und nicht erratbar erzeugt (crypto.randomBytes).
|
|
* - In der Datenbank liegt NUR der SHA-256-Hash des Tokens.
|
|
* - Der Klartext-Token wird genau einmal beim Erstellen zurueckgegeben.
|
|
*/
|
|
import { createHash, randomBytes } from 'node:crypto';
|
|
|
|
/** Laenge des rohen Tokens in Bytes (256 Bit Entropie -> nicht erratbar). */
|
|
const TOKEN_BYTE_LENGTH = 32;
|
|
|
|
/** Invite-Links laufen standardmaessig nach 7 Tagen ab. */
|
|
export const INVITE_DEFAULT_TTL_DAYS = 7;
|
|
|
|
/** Erzeugt einen neuen, nicht erratbaren Invite-Token (Klartext). */
|
|
export function generateInviteToken(): string {
|
|
return randomBytes(TOKEN_BYTE_LENGTH).toString('base64url');
|
|
}
|
|
|
|
/** Berechnet den SHA-256-Hash eines Tokens fuer die Datenbank. */
|
|
export function hashInviteToken(token: string): string {
|
|
return createHash('sha256').update(token).digest('hex');
|
|
}
|
|
|
|
/** Standard-Ablaufzeit fuer neue Invites. */
|
|
export function defaultInviteExpiry(now: Date = new Date()): Date {
|
|
const expiry = new Date(now);
|
|
expiry.setDate(expiry.getDate() + INVITE_DEFAULT_TTL_DAYS);
|
|
return expiry;
|
|
}
|
|
|
|
/** Ist ein Invite gueltig (nicht abgelaufen, nicht verwendet)? */
|
|
export function isInviteUsable(
|
|
invite: { expiresAt: Date | null; usedAt: Date | null },
|
|
now: Date = new Date(),
|
|
): boolean {
|
|
if (invite.usedAt !== null) {
|
|
return false;
|
|
}
|
|
if (invite.expiresAt !== null && invite.expiresAt <= now) {
|
|
return false;
|
|
}
|
|
return true;
|
|
} |