feat: add CalDAV source config to admin dashboard (#112)

Add source definition for aelis.caldav with server URL, username,
password, look-ahead days, and timezone fields.

Route per-user credentials through /api/sources/:id/credentials
instead of the admin provider config endpoint, controlled by a
perUserCredentials flag on the source definition.

Co-authored-by: Ona <no-reply@ona.com>
This commit is contained in:
2026-04-12 12:02:15 +01:00
committed by GitHub
parent 400055ab8c
commit 0a8243c55b
2 changed files with 68 additions and 2 deletions

View File

@@ -20,7 +20,13 @@ import {
import { Separator } from "@/components/ui/separator"
import { Switch } from "@/components/ui/switch"
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"
import { fetchSourceConfig, pushLocation, replaceSource, updateProviderConfig } from "@/lib/api"
import {
fetchSourceConfig,
pushLocation,
replaceSource,
updateProviderConfig,
updateSourceCredentials,
} from "@/lib/api"
interface SourceConfigPanelProps {
source: SourceDefinition
@@ -83,7 +89,11 @@ export function SourceConfigPanel({ source, onUpdate }: SourceConfigPanelProps)
(v) => typeof v === "string" && v.length > 0,
)
if (hasCredentials) {
promises.push(updateProviderConfig(source.id, { credentials: credentialFields }))
if (source.perUserCredentials) {
promises.push(updateSourceCredentials(source.id, credentialFields))
} else {
promises.push(updateProviderConfig(source.id, { credentials: credentialFields }))
}
}
await Promise.all(promises)

View File

@@ -23,6 +23,8 @@ export interface SourceDefinition {
name: string
description: string
alwaysEnabled?: boolean
/** When true, secret fields are stored as per-user credentials via /api/sources/:id/credentials. */
perUserCredentials?: boolean
fields: Record<string, ConfigFieldDef>
}
@@ -78,6 +80,44 @@ const sourceDefinitions: SourceDefinition[] = [
},
},
},
{
id: "aelis.caldav",
name: "CalDAV",
description: "Calendar events from any CalDAV server (Nextcloud, Radicale, Baikal, etc.).",
perUserCredentials: true,
fields: {
serverUrl: {
type: "string",
label: "Server URL",
required: true,
secret: false,
description: "CalDAV server URL (e.g. https://nextcloud.example.com/remote.php/dav)",
},
username: {
type: "string",
label: "Username",
required: true,
secret: false,
},
password: {
type: "string",
label: "Password",
required: true,
secret: true,
},
lookAheadDays: {
type: "number",
label: "Look-ahead Days",
defaultValue: 0,
description: "Number of additional days beyond today to fetch events for",
},
timeZone: {
type: "string",
label: "Timezone",
description: "IANA timezone for determining \"today\" (e.g. Europe/London). Defaults to UTC.",
},
},
},
{
id: "aelis.tfl",
name: "TfL",
@@ -164,6 +204,22 @@ export async function updateProviderConfig(
}
}
export async function updateSourceCredentials(
sourceId: string,
credentials: Record<string, unknown>,
): Promise<void> {
const res = await fetch(`${serverBase()}/sources/${sourceId}/credentials`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
credentials: "include",
body: JSON.stringify(credentials),
})
if (!res.ok) {
const data = (await res.json()) as { error?: string }
throw new Error(data.error ?? `Failed to update credentials: ${res.status}`)
}
}
export interface LocationInput {
lat: number
lng: number