/** Anmeldung, Abmeldung und der angemeldete Benutzer. */ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { ApiError, api, isUnauthorized } from "@/lib/api"; import { keys } from "@/lib/queryClient"; import { toast } from "@/store/toast"; import type { MessageResponse, User } from "@/types/api"; export function useMe() { return useQuery({ queryKey: keys.me, queryFn: async () => { try { return await api.get("/me"); } catch (fehler) { // Kein gültiges Token bedeutet schlicht: nicht angemeldet. if (isUnauthorized(fehler)) return null; throw fehler; } }, staleTime: 60_000, retry: false, }); } /** * Wird ausgelöst, wenn die Anmeldedaten stimmen, die Sitzung aber nicht hält. * * Ursache ist praktisch immer ein `Secure`-Cookie über eine HTTP-Verbindung: * Der Browser verwirft es stillschweigend, die Anmeldung meldet Erfolg und die * nächste Anfrage gilt wieder als nicht angemeldet. */ export class SessionNotEstablishedError extends Error { constructor() { super( "Die Anmeldung hat geklappt, aber der Browser hat das Sitzungs-Cookie verworfen. " + "Das passiert bei Zugriff über HTTP, solange COOKIE_SECURE aktiv ist – " + "greife über HTTPS zu oder setze COOKIE_SECURE=false.", ); this.name = "SessionNotEstablishedError"; } } export function useLogin() { const client = useQueryClient(); return useMutation({ mutationFn: async (daten: { username: string; password: string }) => { const benutzer = await api.post("/auth/login", daten); // Gegenprobe: hält die Sitzung wirklich? Ohne sie liefe der Nutzer in eine // stille Schleife zurück zur Anmeldemaske, ohne je einen Grund zu sehen. try { await api.get("/me"); } catch (fehler) { if (isUnauthorized(fehler)) throw new SessionNotEstablishedError(); throw fehler; } return benutzer; }, onSuccess: (benutzer) => { client.setQueryData(keys.me, benutzer); void client.invalidateQueries(); }, }); } export function useLogout() { const client = useQueryClient(); return useMutation({ mutationFn: () => api.post("/auth/logout"), onSuccess: () => { client.setQueryData(keys.me, null); client.clear(); toast.info("Abgemeldet."); }, }); } export function useChangePassword() { const client = useQueryClient(); return useMutation({ mutationFn: (daten: { current_password: string; new_password: string }) => api.post("/auth/change-password", daten), onSuccess: () => { // Der Passwortwechsel beendet alle Sitzungen – es folgt eine neue Anmeldung. client.setQueryData(keys.me, null); client.clear(); toast.success("Passwort geändert.", "Bitte melde dich neu an."); }, }); } /** Übersetzt einen Anmeldefehler in eine Meldung für das Formular. */ export function loginErrorMessage(error: unknown): string { if (error instanceof SessionNotEstablishedError) return error.message; if (error instanceof ApiError) return error.message; return "Der Server ist nicht erreichbar."; }