Verbinden, Token erneuern und seitenweise abrufen — mit blankem fetch, ohne SDK. Es geht um drei Aufrufe je Anbieter, und die Antworten werden ohnehin mit zod geprüft; ein SDK brächte vor allem Abhängigkeiten und blinde Flecken. Entscheidungen: - Der Zustandswert im OAuth-Ablauf ist mit AUTH_SECRET signiert und trägt die Benutzerkennung. Ohne das ließe sich ein fremdes Kalenderkonto an ein beliebiges Konto hängen. - Die Berechtigungen schließen das Schreiben ein, obwohl M3 nur liest. Sonst müsste ab M4 jedes Konto neu zustimmen — und die Zustimmung ist genau der Schritt, an dem Einrichtung scheitert. - Läuft der Abgleichverweis ab (beide Anbieter antworten mit 410), wird er verworfen und von vorn gelesen. Der Ablauf ist so gebaut, dass ein vollständiger Durchlauf jederzeit möglich ist. - Googles syncToken trägt die Zeitgrenzen nicht mit; Folgeabgleiche liefern also auch Änderungen außerhalb unseres Fensters. Was daneben liegt, wird verworfen — sonst wüchse der Spiegel unbegrenzt. Die Instanzregel greift serverseitig: Google gibt es nur daheim, in der Firmeninstanz liefert die Route 404 statt eines Hinweises. Fehlermeldungen nennen nie den Antwortkörper des Token-Endpunkts — der enthält das Client-Secret oft im Echo. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
53 lines
1.5 KiB
TypeScript
53 lines
1.5 KiB
TypeScript
import { NextResponse } from 'next/server'
|
|
import { notFound } from 'next/navigation'
|
|
import { autorisierungsAdresse } from '@/server/kalender/oauth'
|
|
import {
|
|
alsAnbieter,
|
|
faehigkeitFuer,
|
|
istAnbieter,
|
|
zustandErzeugen,
|
|
} from '@/server/kalender/verbinden'
|
|
import { angemeldet } from '@/server/berechtigung'
|
|
import { erzwingeFaehigkeit } from '@/server/instanz'
|
|
|
|
export const dynamic = 'force-dynamic'
|
|
|
|
/**
|
|
* Startet das Verbinden eines Kalenderkontos und leitet zum Anbieter weiter.
|
|
*
|
|
* Der Riegel sitzt hier serverseitig: Google gibt es nur daheim. In der
|
|
* Firmeninstanz liefert diese Route 404 — nicht etwa einen Hinweis, dass es
|
|
* die Möglichkeit anderswo gäbe.
|
|
*/
|
|
export async function GET(
|
|
_anfrage: Request,
|
|
{ params }: { params: Promise<{ anbieter: string }> },
|
|
) {
|
|
const { anbieter: roh } = await params
|
|
if (!istAnbieter(roh)) notFound()
|
|
|
|
const anbieter = alsAnbieter(roh)
|
|
erzwingeFaehigkeit(faehigkeitFuer(anbieter))
|
|
|
|
const benutzer = await angemeldet()
|
|
if (!benutzer) {
|
|
return NextResponse.redirect(
|
|
new URL('/anmelden?weiter=%2Fkalenderkonten', process.env.APP_URL ?? ''),
|
|
)
|
|
}
|
|
|
|
try {
|
|
const zustand = zustandErzeugen(anbieter, benutzer.id)
|
|
return NextResponse.redirect(autorisierungsAdresse(anbieter, zustand))
|
|
} catch (fehler) {
|
|
const meldung =
|
|
fehler instanceof Error ? fehler.message : 'Der Anbieter ist nicht eingerichtet.'
|
|
return NextResponse.redirect(
|
|
new URL(
|
|
`/kalenderkonten?fehler=${encodeURIComponent(meldung)}`,
|
|
process.env.APP_URL ?? '',
|
|
),
|
|
)
|
|
}
|
|
}
|