e49d4c7c9d
EWS-Aufrufe ueber httpntlm bekommen eine eigene 15-s-Zeitgrenze; httpreqs timeout greift waehrend des Verbindungsaufbaus nicht (gemessen 134 s). Netzfehler liefern im Test den Schluessel 'unreachable', das Formular meldet 'nicht erreichbar' statt 'Zugangsdaten pruefen'. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
204 lines
5.2 KiB
TypeScript
204 lines
5.2 KiB
TypeScript
/**
|
|
* Calendar API client functions.
|
|
* Consumes the calendar backend built in 05-03.
|
|
* All calls use credentials: 'include' for cookie-based auth.
|
|
*
|
|
* Anti-pattern (RESEARCH): The browser NEVER fetches external calendars directly.
|
|
* All event data flows through /api/calendar/events which proxies via the backend.
|
|
*/
|
|
|
|
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001';
|
|
|
|
// --- Types mirroring the 05-03 backend contract ---
|
|
|
|
/**
|
|
* Calendar source as returned by GET /calendar/sources.
|
|
* Note: encryptedPassword is NEVER returned (T-05-09).
|
|
*/
|
|
export interface CalendarSource {
|
|
id: string;
|
|
userId: string;
|
|
tenantId: string;
|
|
name: string;
|
|
type: 'caldav' | 'ics' | 'exchange';
|
|
exchangeMode?: string | null;
|
|
domain?: string | null;
|
|
url: string;
|
|
username?: string | null;
|
|
color?: string | null;
|
|
isVisible: boolean;
|
|
syncIntervalMin: number;
|
|
lastSyncAt?: string | null;
|
|
lastSyncError?: string | null;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
/**
|
|
* Normalized calendar event from GET /calendar/events.
|
|
* Mirrors CalendarEvent from calendar.service.ts.
|
|
*/
|
|
export interface CalendarEvent {
|
|
id: string;
|
|
sourceId: string;
|
|
title: string;
|
|
start: string;
|
|
end: string;
|
|
allDay: boolean;
|
|
location?: string;
|
|
description?: string;
|
|
color?: string;
|
|
}
|
|
|
|
/**
|
|
* Payload for creating a new calendar source.
|
|
*/
|
|
export interface CreateSourcePayload {
|
|
name: string;
|
|
type: 'caldav' | 'ics' | 'exchange';
|
|
url: string;
|
|
username?: string;
|
|
password?: string;
|
|
exchangeMode?: string;
|
|
domain?: string;
|
|
color?: string;
|
|
}
|
|
|
|
/**
|
|
* Payload for updating an existing calendar source.
|
|
* All fields are optional.
|
|
*/
|
|
export interface UpdateSourcePayload {
|
|
name?: string;
|
|
type?: 'caldav' | 'ics' | 'exchange';
|
|
url?: string;
|
|
username?: string;
|
|
password?: string;
|
|
exchangeMode?: string;
|
|
domain?: string;
|
|
color?: string;
|
|
isVisible?: boolean;
|
|
}
|
|
|
|
// --- API functions ---
|
|
|
|
/**
|
|
* Fetch all calendar sources for the current user.
|
|
* GET /calendar/sources
|
|
*/
|
|
export async function fetchSources(): Promise<CalendarSource[]> {
|
|
const res = await fetch(`${API_URL}/calendar/sources`, {
|
|
credentials: 'include',
|
|
});
|
|
if (!res.ok) throw new Error('Failed to fetch calendar sources');
|
|
return res.json();
|
|
}
|
|
|
|
/**
|
|
* Add a new calendar source.
|
|
* POST /calendar/sources
|
|
*/
|
|
export async function addSource(
|
|
payload: CreateSourcePayload,
|
|
): Promise<CalendarSource> {
|
|
const res = await fetch(`${API_URL}/calendar/sources`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
credentials: 'include',
|
|
body: JSON.stringify(payload),
|
|
});
|
|
if (!res.ok) throw new Error('Failed to add calendar source');
|
|
return res.json();
|
|
}
|
|
|
|
/**
|
|
* Update an existing calendar source (including isVisible toggle for CAL-02).
|
|
* PATCH /calendar/sources/:id
|
|
*/
|
|
export async function updateSource(
|
|
id: string,
|
|
payload: UpdateSourcePayload,
|
|
): Promise<CalendarSource> {
|
|
const res = await fetch(`${API_URL}/calendar/sources/${id}`, {
|
|
method: 'PATCH',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
credentials: 'include',
|
|
body: JSON.stringify(payload),
|
|
});
|
|
if (!res.ok) throw new Error('Failed to update calendar source');
|
|
return res.json();
|
|
}
|
|
|
|
/**
|
|
* Delete a calendar source.
|
|
* DELETE /calendar/sources/:id
|
|
*/
|
|
export async function deleteSource(id: string): Promise<void> {
|
|
const res = await fetch(`${API_URL}/calendar/sources/${id}`, {
|
|
method: 'DELETE',
|
|
credentials: 'include',
|
|
});
|
|
if (!res.ok) throw new Error('Failed to delete calendar source');
|
|
}
|
|
|
|
/**
|
|
* Test a calendar configuration without saving it.
|
|
* POST /calendar/sources/test-config
|
|
*/
|
|
export async function testSourceConfig(config: {
|
|
type: string;
|
|
url: string;
|
|
username?: string;
|
|
password?: string;
|
|
exchangeMode?: string;
|
|
domain?: string;
|
|
}): Promise<{ success: boolean; error?: string }> {
|
|
const res = await fetch(`${API_URL}/calendar/sources/test-config`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
credentials: 'include',
|
|
body: JSON.stringify(config),
|
|
});
|
|
if (!res.ok) throw new Error('Test request failed');
|
|
return res.json();
|
|
}
|
|
|
|
/**
|
|
* Test connection to a calendar source.
|
|
* POST /calendar/sources/:id/test
|
|
* Returns { success: boolean } from the backend.
|
|
*/
|
|
export async function testSource(
|
|
id: string,
|
|
): Promise<{ success: boolean; error?: string }> {
|
|
const res = await fetch(`${API_URL}/calendar/sources/${id}/test`, {
|
|
method: 'POST',
|
|
credentials: 'include',
|
|
});
|
|
if (!res.ok) throw new Error('Failed to test calendar source');
|
|
return res.json();
|
|
}
|
|
|
|
/**
|
|
* Fetch aggregated events from all visible sources.
|
|
* GET /calendar/events?from=...&to=...
|
|
* Default window: now to now + 30 days (backend default).
|
|
*/
|
|
export async function fetchEvents(
|
|
from?: string,
|
|
to?: string,
|
|
): Promise<CalendarEvent[]> {
|
|
const params = new URLSearchParams();
|
|
if (from) params.set('from', from);
|
|
if (to) params.set('to', to);
|
|
|
|
const queryStr = params.toString();
|
|
const url = `${API_URL}/calendar/events${queryStr ? `?${queryStr}` : ''}`;
|
|
|
|
const res = await fetch(url, {
|
|
credentials: 'include',
|
|
});
|
|
if (!res.ok) throw new Error('Failed to fetch calendar events');
|
|
return res.json();
|
|
}
|