frontend: Add form to create addressbook

This commit is contained in:
Lennart
2025-06-08 21:54:03 +02:00
parent 573781310a
commit e58973d366
4 changed files with 124 additions and 46 deletions

View File

@@ -108,6 +108,7 @@
</ul> </ul>
{% endif %} {% endif %}
<section>
<h3>Create calendar</h3> <h3>Create calendar</h3>
<form action="/frontend/user/{{ user.id }}/calendar" method="POST"> <form action="/frontend/user/{{ user.id }}/calendar" method="POST">
<label> <label>
@@ -152,6 +153,8 @@
</form> </form>
</section> </section>
</section>
<section> <section>
<h2>Addressbooks</h2> <h2>Addressbooks</h2>
<ul> <ul>
@@ -186,6 +189,29 @@
{% endfor %} {% endfor %}
</ul> </ul>
{% endif %} {% endif %}
<section>
<h3>Create addressbook</h3>
<form action="/frontend/user/{{ user.id }}/addressbook" method="POST">
<label>
href
<input type="text" name="id" />
</label>
<br>
<label>
Displayname
<input type="text" name="displayname" />
</label>
<br>
<label>
Description
<input type="text" name="description" />
</label>
<br>
<button type="submit">Create</button>
</form>
</section>
</section> </section>
{% endblock %} {% endblock %}

View File

@@ -32,7 +32,7 @@ use oidc_user_store::OidcUserStore;
use crate::{ use crate::{
assets::{Assets, EmbedService}, assets::{Assets, EmbedService},
routes::{ routes::{
addressbook::{route_addressbook, route_addressbook_restore}, addressbook::{route_addressbook, route_addressbook_restore, route_create_addressbook},
app_token::{route_delete_app_token, route_post_app_token}, app_token::{route_delete_app_token, route_post_app_token},
calendar::{route_calendar, route_calendar_restore, route_create_calendar}, calendar::{route_calendar, route_calendar_restore, route_create_calendar},
login::{route_get_login, route_post_login, route_post_logout}, login::{route_get_login, route_post_login, route_post_logout},
@@ -76,6 +76,10 @@ pub fn frontend_router<
post(route_calendar_restore::<CS>), post(route_calendar_restore::<CS>),
) )
// Addressbook // Addressbook
.route(
"/user/{user}/addressbook",
post(route_create_addressbook::<AS>),
)
.route( .route(
"/user/{user}/addressbook/{addressbook}", "/user/{user}/addressbook/{addressbook}",
get(route_addressbook::<AS>), get(route_addressbook::<AS>),

View File

@@ -3,7 +3,7 @@ use std::sync::Arc;
use askama::Template; use askama::Template;
use askama_web::WebTemplate; use askama_web::WebTemplate;
use axum::{ use axum::{
Extension, Extension, Form,
extract::Path, extract::Path,
response::{IntoResponse, Redirect, Response}, response::{IntoResponse, Redirect, Response},
}; };
@@ -11,6 +11,7 @@ use axum_extra::TypedHeader;
use headers::Referer; use headers::Referer;
use http::StatusCode; use http::StatusCode;
use rustical_store::{Addressbook, AddressbookStore, auth::User}; use rustical_store::{Addressbook, AddressbookStore, auth::User};
use serde::{Deserialize, Deserializer};
#[derive(Template, WebTemplate)] #[derive(Template, WebTemplate)]
#[template(path = "pages/addressbook.html")] #[template(path = "pages/addressbook.html")]
@@ -32,6 +33,53 @@ pub async fn route_addressbook<AS: AddressbookStore>(
.into_response()) .into_response())
} }
fn empty_to_none<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
where
D: Deserializer<'de>,
{
let val: Option<String> = Deserialize::deserialize(deserializer)?;
Ok(val.filter(|val| !val.is_empty()))
}
#[derive(Deserialize, Clone)]
pub struct PutAddressbookForm {
id: String,
#[serde(deserialize_with = "empty_to_none")]
displayname: Option<String>,
#[serde(deserialize_with = "empty_to_none")]
description: Option<String>,
}
pub async fn route_create_addressbook<AS: AddressbookStore>(
Path(owner): Path<String>,
Extension(store): Extension<Arc<AS>>,
user: User,
Form(PutAddressbookForm {
id,
displayname,
description,
}): Form<PutAddressbookForm>,
) -> Result<Response, rustical_store::Error> {
if !user.is_principal(&owner) {
return Ok(StatusCode::UNAUTHORIZED.into_response());
}
assert!(!id.is_empty());
let addressbook = Addressbook {
id: id.to_owned(),
displayname,
description,
principal: user.id.to_owned(),
synctoken: 0,
deleted_at: None,
push_topic: uuid::Uuid::new_v4().to_string(),
};
store.insert_addressbook(addressbook).await?;
Ok(Redirect::to(&format!("/frontend/user/{}/addressbook/{}", user.id, id)).into_response())
}
pub async fn route_addressbook_restore<AS: AddressbookStore>( pub async fn route_addressbook_restore<AS: AddressbookStore>(
Path((owner, addressbook_id)): Path<(String, String)>, Path((owner, addressbook_id)): Path<(String, String)>,
Extension(store): Extension<Arc<AS>>, Extension(store): Extension<Arc<AS>>,

View File

@@ -96,7 +96,7 @@ pub async fn route_create_calendar<C: CalendarStore>(
description, description,
color, color,
subscription_url, subscription_url,
principal: user.id, principal: user.id.to_owned(),
components: comps, components: comps,
order: 0, order: 0,
timezone_id: None, timezone_id: None,
@@ -107,7 +107,7 @@ pub async fn route_create_calendar<C: CalendarStore>(
}; };
store.insert_calendar(cal).await?; store.insert_calendar(cal).await?;
Ok(Redirect::to(&id).into_response()) Ok(Redirect::to(&format!("/frontend/user/{}/calendar/{}", user.id, id)).into_response())
} }
pub async fn route_calendar_restore<CS: CalendarStore>( pub async fn route_calendar_restore<CS: CalendarStore>(