- Rebrand: Kunden-Logo, Joseph-Hinterstein-Portrait, Kontakt-Banner, Texte - Login-Pflicht auf /login (Route-Guard), Gruppen Verwaltung/Mieter - Mieter-Registrierung per einmaligem Registrierungscode - Benutzerverwaltung als 6. Modul (/benutzer, nur Verwaltung) - Demo-Zugänge mit Passwort 0815 (j.hinterstein, a.berger, m.wolf) - Mail-Server-API: Token-Auth (/send, X-Api-Token), Mail-Stack live auf Server - Store-Key hinterstein-mieterportal-v2, Doku (kimi.md, README) nachgeführt
57 lines
1.6 KiB
TypeScript
57 lines
1.6 KiB
TypeScript
/** Authentifizierung + Session, Muster aus dem Schulportal. */
|
|
|
|
import type { Benutzer, BenutzerRolle } from '../types'
|
|
|
|
export const SESSION_KEY = 'mp_benutzer_session'
|
|
export const SESSION_DAUER_MS = 8 * 60 * 60 * 1000 // 8 h
|
|
export const BENUTZER_CHANGED_EVENT = 'mp-benutzer-changed'
|
|
|
|
/** Demo-Passwort aller Demo-Benutzer (wie Schulportal sha256("0815")). */
|
|
export const DEMO_PASSWORT = '0815'
|
|
|
|
export async function sha256(text: string): Promise<string> {
|
|
const daten = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(text))
|
|
return Array.from(new Uint8Array(daten))
|
|
.map((b) => b.toString(16).padStart(2, '0'))
|
|
.join('')
|
|
}
|
|
|
|
export interface Session {
|
|
benutzerId: string
|
|
rolle: BenutzerRolle
|
|
bis: number
|
|
}
|
|
|
|
export function ladeSession(): Session | null {
|
|
try {
|
|
const raw = localStorage.getItem(SESSION_KEY)
|
|
if (!raw) return null
|
|
const s = JSON.parse(raw) as Session
|
|
if (s.bis < Date.now()) {
|
|
localStorage.removeItem(SESSION_KEY)
|
|
return null
|
|
}
|
|
return s
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
export function speichereSession(benutzerId: string, rolle: BenutzerRolle) {
|
|
const s: Session = { benutzerId, rolle, bis: Date.now() + SESSION_DAUER_MS }
|
|
localStorage.setItem(SESSION_KEY, JSON.stringify(s))
|
|
window.dispatchEvent(new Event(BENUTZER_CHANGED_EVENT))
|
|
}
|
|
|
|
export function beendeSession() {
|
|
localStorage.removeItem(SESSION_KEY)
|
|
window.dispatchEvent(new Event(BENUTZER_CHANGED_EVENT))
|
|
}
|
|
|
|
export function aktuellerBenutzer(alle: Benutzer[]): Benutzer | null {
|
|
const s = ladeSession()
|
|
if (!s) return null
|
|
const b = alle.find((x) => x.id === s.benutzerId && x.aktiv)
|
|
return b ?? null
|
|
}
|