iCalendar import: parse_ical + API + MCP tool (#16)

This commit is contained in:
2026-07-12 14:46:15 +02:00
parent 6b0d060945
commit a7fa2ec4aa
9 changed files with 565 additions and 7 deletions

View File

@@ -170,3 +170,57 @@ pub async fn list_schedule_history(
.collect(),
))
}
pub async fn export_ical(
State(state): State<AppState>,
CurrentUser(_user): CurrentUser,
Path(id): Path<uuid::Uuid>,
) -> impl IntoResponse {
let channel_id = ChannelId::from(id);
match state.channel_query.find_by_id(channel_id).await {
Ok(Some(channel)) => {
let ical = domain::generate_ical(
channel.name(),
channel.timezone(),
channel.schedule_config(),
);
(
StatusCode::OK,
[
("content-type", "text/calendar; charset=utf-8"),
(
"content-disposition",
&format!("attachment; filename=\"{}.ics\"", channel.name()),
),
],
ical,
)
.into_response()
}
Ok(None) => StatusCode::NOT_FOUND.into_response(),
Err(e) => AppError::from(e).into_response(),
}
}
pub async fn import_ical(
State(state): State<AppState>,
CurrentUser(user): CurrentUser,
Path(id): Path<uuid::Uuid>,
body: String,
) -> Result<Json<api_types::ChannelResponse>, AppError> {
let config = domain::parse_ical(&body)?;
let cmd = application::channels::UpdateChannelCommand {
channel_id: id.into(),
owner_id: user.id(),
name: None,
description: None,
timezone: None,
schedule_config: Some(config),
rotation_policy: None,
auto_schedule: None,
gap_filler: None,
};
let channel =
application::channels::update::execute(&state.channel_command_deps, cmd).await?;
Ok(Json(api_types::ChannelResponse::from(channel)))
}

View File

@@ -58,6 +58,8 @@ fn channel_router() -> Router<AppState> {
.route("/{id}/snapshots/{snapshot_id}", get(handlers::channels::get_snapshot))
.route("/{id}/snapshots/{snapshot_id}", axum::routing::patch(handlers::channels::patch_snapshot))
.route("/{id}/snapshots/{snapshot_id}/restore", post(handlers::channels::restore_snapshot))
.route("/{id}/export.ics", get(handlers::schedule::export_ical))
.route("/{id}/import", post(handlers::schedule::import_ical))
}
fn admin_router() -> Router<AppState> {