Initial commit: Kalendartool (Next.js, Prisma, Docker)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Kühn
2026-10-08 14:38:04 +02:00
commit bc49c3074e
94 changed files with 12812 additions and 0 deletions

45
lib/invites/tokens.ts Normal file
View File

@@ -0,0 +1,45 @@
/**
* 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;
}