Checkpoint: Migration to axum

This commit is contained in:
Lennart
2025-06-08 14:10:12 +02:00
parent 790c657b08
commit 95889e3df1
60 changed files with 1476 additions and 2205 deletions

View File

@@ -1,10 +1,12 @@
use crate::Error;
use actix_web::web::Path;
use actix_web::{HttpResponse, web::Data};
use rustical_store::{Addressbook, AddressbookStore, auth::User};
use crate::{Error, addressbook::resource::AddressbookResourceService};
use axum::{
extract::{Path, State},
response::{IntoResponse, Response},
};
use http::StatusCode;
use rustical_store::{Addressbook, AddressbookStore, SubscriptionStore, auth::User};
use rustical_xml::{XmlDeserialize, XmlDocument, XmlRootTag};
use tracing::instrument;
use tracing_actix_web::RootSpan;
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
pub struct Resourcetype {
@@ -39,15 +41,13 @@ struct MkcolRequest {
set: PropElement<MkcolAddressbookProp>,
}
#[instrument(parent = root_span.id(), skip(store, root_span))]
pub async fn route_mkcol<AS: AddressbookStore>(
path: Path<(String, String)>,
body: String,
#[instrument(skip(addr_store))]
pub async fn route_mkcol<AS: AddressbookStore, S: SubscriptionStore>(
Path((principal, addressbook_id)): Path<(String, String)>,
user: User,
store: Data<AS>,
root_span: RootSpan,
) -> Result<HttpResponse, Error> {
let (principal, addressbook_id) = path.into_inner();
State(AddressbookResourceService { addr_store, .. }): State<AddressbookResourceService<AS, S>>,
body: String,
) -> Result<Response, Error> {
if !user.is_principal(&principal) {
return Err(Error::Unauthorized);
}
@@ -65,7 +65,7 @@ pub async fn route_mkcol<AS: AddressbookStore>(
push_topic: uuid::Uuid::new_v4().to_string(),
};
match store
match addr_store
.get_addressbook(&principal, &addressbook_id, true)
.await
{
@@ -74,7 +74,11 @@ pub async fn route_mkcol<AS: AddressbookStore>(
}
Ok(_) => {
// oh no, there's a conflict
return Ok(HttpResponse::Conflict().body("An addressbook already exists at this URI"));
return Ok((
StatusCode::CONFLICT,
"An addressbook already exists at this URI",
)
.into_response());
}
Err(err) => {
// some other error
@@ -82,12 +86,10 @@ pub async fn route_mkcol<AS: AddressbookStore>(
}
}
match store.insert_addressbook(addressbook).await {
match addr_store.insert_addressbook(addressbook).await {
// TODO: The spec says we should return a mkcol-response.
// However, it works without one but breaks on iPadOS when using an empty one :)
Ok(()) => Ok(HttpResponse::Created()
.insert_header(("Cache-Control", "no-cache"))
.body("")),
Ok(()) => Ok(StatusCode::CREATED.into_response()),
Err(err) => {
dbg!(err.to_string());
Err(err.into())

View File

@@ -1,3 +1,3 @@
pub mod mkcol;
pub mod post;
// pub mod post;
pub mod report;

View File

@@ -4,8 +4,6 @@ use crate::{
AddressObjectPropWrapper, AddressObjectPropWrapperName, AddressObjectResource,
},
};
use actix_web::dev::{Path, ResourceDef};
use http::StatusCode;
use rustical_dav::{
resource::{PrincipalUri, Resource},
@@ -32,27 +30,28 @@ pub async fn get_objects_addressbook_multiget<AS: AddressbookStore>(
addressbook_id: &str,
store: &AS,
) -> Result<(Vec<AddressObject>, Vec<String>), Error> {
let resource_def = ResourceDef::prefix(path).join(&ResourceDef::new("/{object_id}.vcf"));
let mut result = vec![];
let mut not_found = vec![];
for href in &addressbook_multiget.href {
let mut path = Path::new(href.as_str());
if !resource_def.capture_match_info(&mut path) {
if let Some(filename) = href.strip_prefix(path) {
if let Some(object_id) = filename.strip_suffix(".vcf") {
match store
.get_object(principal, addressbook_id, object_id, false)
.await
{
Ok(object) => result.push(object),
Err(rustical_store::Error::NotFound) => not_found.push(href.to_owned()),
Err(err) => return Err(err.into()),
};
} else {
not_found.push(href.to_owned());
continue;
}
} else {
not_found.push(href.to_owned());
continue;
};
let object_id = path.get("object_id").unwrap();
match store
.get_object(principal, addressbook_id, object_id, false)
.await
{
Ok(object) => result.push(object),
Err(rustical_store::Error::NotFound) => not_found.push(href.to_owned()),
// TODO: Maybe add error handling on a per-object basis
Err(err) => return Err(err.into()),
};
}
}
Ok((result, not_found))

View File

@@ -1,11 +1,15 @@
use crate::{CardDavPrincipalUri, Error, address_object::resource::AddressObjectPropWrapperName};
use actix_web::{
HttpRequest, Responder,
web::{Data, Path},
use crate::{
CardDavPrincipalUri, Error, address_object::resource::AddressObjectPropWrapperName,
addressbook::resource::AddressbookResourceService,
};
use addressbook_multiget::{AddressbookMultigetRequest, handle_addressbook_multiget};
use axum::{
Extension,
extract::{OriginalUri, Path, State},
response::IntoResponse,
};
use rustical_dav::xml::{PropfindType, sync_collection::SyncCollectionRequest};
use rustical_store::{AddressbookStore, auth::User};
use rustical_store::{AddressbookStore, SubscriptionStore, auth::User};
use rustical_xml::{XmlDeserialize, XmlDocument};
use sync_collection::handle_sync_collection;
use tracing::instrument;
@@ -30,16 +34,15 @@ impl ReportRequest {
}
}
#[instrument(skip(req, addr_store))]
pub async fn route_report_addressbook<AS: AddressbookStore>(
path: Path<(String, String)>,
body: String,
#[instrument(skip(addr_store))]
pub async fn route_report_addressbook<AS: AddressbookStore, S: SubscriptionStore>(
Path((principal, addressbook_id)): Path<(String, String)>,
user: User,
req: HttpRequest,
puri: Data<CardDavPrincipalUri>,
addr_store: Data<AS>,
) -> Result<impl Responder, Error> {
let (principal, addressbook_id) = path.into_inner();
OriginalUri(uri): OriginalUri,
Extension(puri): Extension<CardDavPrincipalUri>,
State(AddressbookResourceService { addr_store, .. }): State<AddressbookResourceService<AS, S>>,
body: String,
) -> Result<impl IntoResponse, Error> {
if !user.is_principal(&principal) {
return Err(Error::Unauthorized);
}
@@ -51,8 +54,8 @@ pub async fn route_report_addressbook<AS: AddressbookStore>(
handle_addressbook_multiget(
addr_multiget,
request.props(),
req.path(),
puri.as_ref(),
uri.path(),
&puri,
&user,
&principal,
&addressbook_id,
@@ -63,8 +66,8 @@ pub async fn route_report_addressbook<AS: AddressbookStore>(
ReportRequest::SyncCollection(sync_collection) => {
handle_sync_collection(
sync_collection,
req.path(),
puri.as_ref(),
uri.path(),
&puri,
&user,
&principal,
&addressbook_id,