frontend: add carddav

This commit is contained in:
Lennart
2024-11-10 12:44:56 +01:00
parent d576d997c4
commit c199682a46
4 changed files with 79 additions and 14 deletions

View File

@@ -0,0 +1,9 @@
{% extends "layouts/default.html" %}
{% block imports %}
{% endblock %}
{% block content %}
<h1>Test</h1>
<a href="/frontend/user/{{ owner }}">Back</a>
{% endblock %}

View File

@@ -5,7 +5,7 @@
{% block content %} {% block content %}
<style> <style>
li.calendar-list-item { li.collection-list-item {
list-style: none; list-style: none;
display: grid; display: grid;
margin: 12px; margin: 12px;
@@ -31,12 +31,14 @@ li.calendar-list-item {
} }
} }
</style> </style>
<h2>Welcome {{ user_id }}!</h2> <h1>Welcome {{ user_id }}!</h1>
<h2>Calendars</h2>
<ul> <ul>
{% for calendar in calendars %} {% for calendar in calendars %}
{% let color = calendar.color.to_owned().unwrap_or("red".to_owned()) %} {% let color = calendar.color.to_owned().unwrap_or("red".to_owned()) %}
<li class="calendar-list-item" style="--color: {{ color }}"> <li class="collection-list-item" style="--color: {{ color }}">
<a href="/frontend/user/{{ user_id }}/{{ calendar.id}}"> <a href="/frontend/user/{{ user_id }}/calendar/{{ calendar.id}}">
<span class="title">{{ calendar.displayname.to_owned().unwrap_or(calendar.id.to_owned()) }}</span> <span class="title">{{ calendar.displayname.to_owned().unwrap_or(calendar.id.to_owned()) }}</span>
<span class="description"> <span class="description">
{% if let Some(description) = calendar.description %}{{ description }}{% endif %} {% if let Some(description) = calendar.description %}{{ description }}{% endif %}
@@ -46,5 +48,19 @@ li.calendar-list-item {
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
<h2>Addressbooks</h2>
<ul>
{% for addressbook in addressbooks %}
<li class="collection-list-item">
<a href="/frontend/user/{{ user_id }}/addressbook/{{ addressbook.id}}">
<span class="title">{{ addressbook.displayname.to_owned().unwrap_or(addressbook.id.to_owned()) }}</span>
<span class="description">
{% if let Some(description) = addressbook.description %}{{ description }}{% endif %}
</span>
</a>
</li>
{% endfor %}
</ul>
{% endblock %} {% endblock %}

View File

@@ -10,11 +10,12 @@ use actix_web::{
HttpRequest, HttpResponse, Responder, HttpRequest, HttpResponse, Responder,
}; };
use askama::Template; use askama::Template;
use askama_actix::TemplateToResponse;
use assets::{Assets, EmbedService}; use assets::{Assets, EmbedService};
use routes::login::{route_get_login, route_post_login}; use routes::login::{route_get_login, route_post_login};
use rustical_store::{ use rustical_store::{
auth::{AuthenticationMiddleware, AuthenticationProvider, User}, auth::{AuthenticationMiddleware, AuthenticationProvider, User},
Calendar, CalendarStore, Addressbook, AddressbookStore, Calendar, CalendarStore,
}; };
use std::sync::Arc; use std::sync::Arc;
@@ -29,18 +30,27 @@ pub use config::FrontendConfig;
struct UserPage { struct UserPage {
pub user_id: String, pub user_id: String,
pub calendars: Vec<Calendar>, pub calendars: Vec<Calendar>,
pub addressbooks: Vec<Addressbook>,
} }
async fn route_user<C: CalendarStore + ?Sized>( async fn route_user<CS: CalendarStore + ?Sized, AS: AddressbookStore + ?Sized>(
path: Path<String>, path: Path<String>,
store: Data<C>, cal_store: Data<CS>,
addr_store: Data<AS>,
user: User, user: User,
) -> impl Responder { ) -> impl Responder {
// TODO: Check for authorization
let user_id = path.into_inner(); let user_id = path.into_inner();
if user_id != user.id {
return actix_web::HttpResponse::Unauthorized().body("Unauthorized");
}
UserPage { UserPage {
calendars: store.get_calendars(&user.id).await.unwrap(), calendars: cal_store.get_calendars(&user.id).await.unwrap(),
addressbooks: addr_store.get_addressbooks(&user.id).await.unwrap(),
user_id: user.id, user_id: user.id,
} }
.to_response()
} }
#[derive(Template)] #[derive(Template)]
@@ -62,6 +72,25 @@ async fn route_calendar<C: CalendarStore + ?Sized>(
} }
} }
#[derive(Template)]
#[template(path = "pages/addressbook.html")]
struct AddressbookPage {
owner: String,
addressbook: Addressbook,
}
async fn route_addressbook<AS: AddressbookStore + ?Sized>(
path: Path<(String, String)>,
store: Data<AS>,
_user: User,
) -> impl Responder {
let (owner, addrbook_id) = path.into_inner();
AddressbookPage {
owner: owner.to_owned(),
addressbook: store.get_addressbook(&owner, &addrbook_id).await.unwrap(),
}
}
async fn route_root(user: Option<User>, req: HttpRequest) -> impl Responder { async fn route_root(user: Option<User>, req: HttpRequest) -> impl Responder {
let redirect_url = match user { let redirect_url = match user {
Some(user) => req Some(user) => req
@@ -101,10 +130,15 @@ fn unauthorized_handler<B>(res: ServiceResponse<B>) -> actix_web::Result<ErrorHa
Ok(ErrorHandlerResponse::Response(res)) Ok(ErrorHandlerResponse::Response(res))
} }
pub fn configure_frontend<AP: AuthenticationProvider, C: CalendarStore + ?Sized>( pub fn configure_frontend<
AP: AuthenticationProvider,
CS: CalendarStore + ?Sized,
AS: AddressbookStore + ?Sized,
>(
cfg: &mut web::ServiceConfig, cfg: &mut web::ServiceConfig,
auth_provider: Arc<AP>, auth_provider: Arc<AP>,
store: Arc<C>, cal_store: Arc<CS>,
addr_store: Arc<AS>,
frontend_config: FrontendConfig, frontend_config: FrontendConfig,
) { ) {
cfg.service( cfg.service(
@@ -122,17 +156,22 @@ pub fn configure_frontend<AP: AuthenticationProvider, C: CalendarStore + ?Sized>
.build(), .build(),
) )
.app_data(Data::from(auth_provider)) .app_data(Data::from(auth_provider))
.app_data(Data::from(store.clone())) .app_data(Data::from(cal_store.clone()))
.app_data(Data::from(addr_store.clone()))
.service(EmbedService::<Assets>::new("/assets".to_owned())) .service(EmbedService::<Assets>::new("/assets".to_owned()))
.service(web::resource("").route(web::method(Method::GET).to(route_root))) .service(web::resource("").route(web::method(Method::GET).to(route_root)))
.service( .service(
web::resource("/user/{user}") web::resource("/user/{user}")
.route(web::method(Method::GET).to(route_user::<C>)) .route(web::method(Method::GET).to(route_user::<CS, AS>))
.name("frontend_user"), .name("frontend_user"),
) )
.service( .service(
web::resource("/user/{user}/{calendar}") web::resource("/user/{user}/calendar/{calendar}")
.route(web::method(Method::GET).to(route_calendar::<C>)), .route(web::method(Method::GET).to(route_calendar::<CS>)),
)
.service(
web::resource("/user/{user}/addressbook/{calendar}")
.route(web::method(Method::GET).to(route_addressbook::<AS>)),
) )
.service( .service(
web::resource("/login") web::resource("/login")

View File

@@ -44,6 +44,7 @@ pub fn make_app<AS: AddressbookStore + ?Sized, CS: CalendarStore + ?Sized>(
cfg, cfg,
auth_provider.clone(), auth_provider.clone(),
cal_store.clone(), cal_store.clone(),
addr_store.clone(),
frontend_config, frontend_config,
) )
})) }))