mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-13 20:32:48 +00:00
Extend the app state
This commit is contained in:
@@ -6,6 +6,7 @@ use error::Error;
|
|||||||
use routes::{calendar, event, principal, root};
|
use routes::{calendar, event, principal, root};
|
||||||
use rustical_store::calendar::CalendarStore;
|
use rustical_store::calendar::CalendarStore;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use std::sync::Arc;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
pub mod depth_extractor;
|
pub mod depth_extractor;
|
||||||
@@ -14,11 +15,20 @@ pub mod namespace;
|
|||||||
mod propfind;
|
mod propfind;
|
||||||
pub mod routes;
|
pub mod routes;
|
||||||
|
|
||||||
|
pub struct Context<C: CalendarStore> {
|
||||||
|
pub prefix: String,
|
||||||
|
pub store: Arc<RwLock<C>>,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn configure_well_known(cfg: &mut web::ServiceConfig, caldav_root: String) {
|
pub fn configure_well_known(cfg: &mut web::ServiceConfig, caldav_root: String) {
|
||||||
cfg.service(web::redirect("/caldav", caldav_root).permanent());
|
cfg.service(web::redirect("/caldav", caldav_root).permanent());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn configure_dav<C: CalendarStore>(cfg: &mut web::ServiceConfig, store: Data<RwLock<C>>) {
|
pub fn configure_dav<C: CalendarStore>(
|
||||||
|
cfg: &mut web::ServiceConfig,
|
||||||
|
prefix: String,
|
||||||
|
store: Arc<RwLock<C>>,
|
||||||
|
) {
|
||||||
let propfind_method = || Method::from_str("PROPFIND").unwrap();
|
let propfind_method = || Method::from_str("PROPFIND").unwrap();
|
||||||
let report_method = || Method::from_str("REPORT").unwrap();
|
let report_method = || Method::from_str("REPORT").unwrap();
|
||||||
|
|
||||||
@@ -31,7 +41,8 @@ pub fn configure_dav<C: CalendarStore>(cfg: &mut web::ServiceConfig, store: Data
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
cfg.app_data(store)
|
// cfg.app_data(store)
|
||||||
|
cfg.app_data(Data::new(Context { prefix, store }))
|
||||||
.service(
|
.service(
|
||||||
web::resource("{path:.*}")
|
web::resource("{path:.*}")
|
||||||
// Without the guard this service would handle all requests
|
// Without the guard this service would handle all requests
|
||||||
|
|||||||
42
crates/dav/src/propfind_extractor.rs
Normal file
42
crates/dav/src/propfind_extractor.rs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
use actix_web::{http::StatusCode, Either, FromRequest, HttpRequest, ResponseError};
|
||||||
|
use derive_more::Display;
|
||||||
|
use futures_util::{
|
||||||
|
future::{err, ok, Ready},
|
||||||
|
Future,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Display)]
|
||||||
|
pub struct BadPropfindRequest {}
|
||||||
|
|
||||||
|
impl ResponseError for BadPropfindRequest {
|
||||||
|
fn status_code(&self) -> actix_web::http::StatusCode {
|
||||||
|
StatusCode::BAD_REQUEST
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct Propfind(Vec<String>);
|
||||||
|
|
||||||
|
impl FromRequest for Propfind {
|
||||||
|
type Error = BadPropfindRequest;
|
||||||
|
type Future = Either<PropfindExtractFut, Ready<Result<Self, Self::Error>>>;
|
||||||
|
|
||||||
|
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PropfindExtractFut {
|
||||||
|
body_fut: HttpMessageBody,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Future for PropfindExtractFut {
|
||||||
|
type Output = Result<Propfind, BadPropfindRequest>;
|
||||||
|
|
||||||
|
fn poll(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut std::task::Context<'_>,
|
||||||
|
) -> std::task::Poll<Self::Output> {
|
||||||
|
Pin::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ use crate::propfind::{
|
|||||||
generate_multistatus, parse_propfind, write_invalid_props_response, write_propstat_response,
|
generate_multistatus, parse_propfind, write_invalid_props_response, write_propstat_response,
|
||||||
write_resourcetype,
|
write_resourcetype,
|
||||||
};
|
};
|
||||||
use crate::Error;
|
use crate::{Context, Error};
|
||||||
use actix_web::http::header::ContentType;
|
use actix_web::http::header::ContentType;
|
||||||
use actix_web::http::StatusCode;
|
use actix_web::http::StatusCode;
|
||||||
use actix_web::web::{Data, Path};
|
use actix_web::web::{Data, Path};
|
||||||
@@ -17,7 +17,6 @@ use rustical_store::calendar::{Calendar, CalendarStore, Event};
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::RwLock;
|
|
||||||
|
|
||||||
async fn handle_report_calendar_query(
|
async fn handle_report_calendar_query(
|
||||||
query_node: Node<'_, '_>,
|
query_node: Node<'_, '_>,
|
||||||
@@ -71,7 +70,7 @@ async fn handle_report_calendar_query(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn route_report_calendar<C: CalendarStore>(
|
pub async fn route_report_calendar<C: CalendarStore>(
|
||||||
store: Data<RwLock<C>>,
|
context: Data<Context<C>>,
|
||||||
body: String,
|
body: String,
|
||||||
path: Path<(String, String)>,
|
path: Path<(String, String)>,
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
@@ -80,7 +79,7 @@ pub async fn route_report_calendar<C: CalendarStore>(
|
|||||||
|
|
||||||
let doc = roxmltree::Document::parse(&body).map_err(|_e| Error::InternalError)?;
|
let doc = roxmltree::Document::parse(&body).map_err(|_e| Error::InternalError)?;
|
||||||
let query_node = doc.root_element();
|
let query_node = doc.root_element();
|
||||||
let events = store.read().await.get_events(&cid).await.unwrap();
|
let events = context.store.read().await.get_events(&cid).await.unwrap();
|
||||||
|
|
||||||
// TODO: implement filtering
|
// TODO: implement filtering
|
||||||
match query_node.tag_name().name() {
|
match query_node.tag_name().name() {
|
||||||
@@ -173,10 +172,11 @@ pub async fn route_propfind_calendar<C: CalendarStore>(
|
|||||||
body: String,
|
body: String,
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
auth: BasicAuth,
|
auth: BasicAuth,
|
||||||
store: Data<RwLock<C>>,
|
context: Data<Context<C>>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let (_principal, cid) = path.into_inner();
|
let (_principal, cid) = path.into_inner();
|
||||||
let calendar = store
|
let calendar = context
|
||||||
|
.store
|
||||||
.read()
|
.read()
|
||||||
.await
|
.await
|
||||||
.get_calendar(&cid)
|
.get_calendar(&cid)
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
use crate::Error;
|
use crate::{Context, Error};
|
||||||
use actix_web::web::{Data, Path};
|
use actix_web::web::{Data, Path};
|
||||||
use actix_web::HttpResponse;
|
use actix_web::HttpResponse;
|
||||||
use rustical_store::calendar::CalendarStore;
|
use rustical_store::calendar::CalendarStore;
|
||||||
use tokio::sync::RwLock;
|
|
||||||
|
|
||||||
pub async fn delete_event<C: CalendarStore>(
|
pub async fn delete_event<C: CalendarStore>(
|
||||||
store: Data<RwLock<C>>,
|
context: Data<Context<C>>,
|
||||||
path: Path<(String, String, String)>,
|
path: Path<(String, String, String)>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let (_principal, mut cid, uid) = path.into_inner();
|
let (_principal, mut cid, uid) = path.into_inner();
|
||||||
if cid.ends_with(".ics") {
|
if cid.ends_with(".ics") {
|
||||||
cid.truncate(cid.len() - 4);
|
cid.truncate(cid.len() - 4);
|
||||||
}
|
}
|
||||||
store
|
context
|
||||||
|
.store
|
||||||
.write()
|
.write()
|
||||||
.await
|
.await
|
||||||
.delete_event(&uid)
|
.delete_event(&uid)
|
||||||
@@ -23,14 +23,15 @@ pub async fn delete_event<C: CalendarStore>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_event<C: CalendarStore>(
|
pub async fn get_event<C: CalendarStore>(
|
||||||
store: Data<RwLock<C>>,
|
context: Data<Context<C>>,
|
||||||
path: Path<(String, String, String)>,
|
path: Path<(String, String, String)>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let (_principal, mut cid, uid) = path.into_inner();
|
let (_principal, mut cid, uid) = path.into_inner();
|
||||||
if cid.ends_with(".ics") {
|
if cid.ends_with(".ics") {
|
||||||
cid.truncate(cid.len() - 4);
|
cid.truncate(cid.len() - 4);
|
||||||
}
|
}
|
||||||
let event = store
|
let event = context
|
||||||
|
.store
|
||||||
.read()
|
.read()
|
||||||
.await
|
.await
|
||||||
.get_event(&uid)
|
.get_event(&uid)
|
||||||
@@ -43,7 +44,7 @@ pub async fn get_event<C: CalendarStore>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn put_event<C: CalendarStore>(
|
pub async fn put_event<C: CalendarStore>(
|
||||||
store: Data<RwLock<C>>,
|
context: Data<Context<C>>,
|
||||||
path: Path<(String, String, String)>,
|
path: Path<(String, String, String)>,
|
||||||
body: String,
|
body: String,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
@@ -53,7 +54,8 @@ pub async fn put_event<C: CalendarStore>(
|
|||||||
cid.truncate(cid.len() - 4);
|
cid.truncate(cid.len() - 4);
|
||||||
}
|
}
|
||||||
dbg!(&body);
|
dbg!(&body);
|
||||||
store
|
context
|
||||||
|
.store
|
||||||
.write()
|
.write()
|
||||||
.await
|
.await
|
||||||
.upsert_event(uid, body)
|
.upsert_event(uid, body)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use crate::{
|
|||||||
generate_multistatus, parse_propfind, write_invalid_props_response,
|
generate_multistatus, parse_propfind, write_invalid_props_response,
|
||||||
write_propstat_response, write_resourcetype,
|
write_propstat_response, write_resourcetype,
|
||||||
},
|
},
|
||||||
Error,
|
Context, Error,
|
||||||
};
|
};
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
http::{header::ContentType, StatusCode},
|
http::{header::ContentType, StatusCode},
|
||||||
@@ -20,7 +20,6 @@ use quick_xml::{
|
|||||||
Writer,
|
Writer,
|
||||||
};
|
};
|
||||||
use rustical_store::calendar::CalendarStore;
|
use rustical_store::calendar::CalendarStore;
|
||||||
use tokio::sync::RwLock;
|
|
||||||
|
|
||||||
// Executes the PROPFIND request and returns a XML string to be written into a <mulstistatus> object.
|
// Executes the PROPFIND request and returns a XML string to be written into a <mulstistatus> object.
|
||||||
pub async fn generate_propfind_principal_response(
|
pub async fn generate_propfind_principal_response(
|
||||||
@@ -90,7 +89,7 @@ pub async fn route_propfind_principal<C: CalendarStore>(
|
|||||||
body: String,
|
body: String,
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
auth: BasicAuth,
|
auth: BasicAuth,
|
||||||
store: Data<RwLock<C>>,
|
context: Data<Context<C>>,
|
||||||
depth: Depth,
|
depth: Depth,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let props = parse_propfind(&body).map_err(|_e| Error::BadRequest)?;
|
let props = parse_propfind(&body).map_err(|_e| Error::BadRequest)?;
|
||||||
@@ -98,7 +97,8 @@ pub async fn route_propfind_principal<C: CalendarStore>(
|
|||||||
let mut responses = Vec::new();
|
let mut responses = Vec::new();
|
||||||
// also get calendars:
|
// also get calendars:
|
||||||
if depth != Depth::Zero {
|
if depth != Depth::Zero {
|
||||||
let cals = store
|
let cals = context
|
||||||
|
.store
|
||||||
.read()
|
.read()
|
||||||
.await
|
.await
|
||||||
.get_calendars()
|
.get_calendars()
|
||||||
|
|||||||
8
crates/davfs/Cargo.toml
Normal file
8
crates/davfs/Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "davfs"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
3
crates/davfs/src/main.rs
Normal file
3
crates/davfs/src/main.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
|
}
|
||||||
11
src/main.rs
11
src/main.rs
@@ -9,6 +9,7 @@ use clap::Parser;
|
|||||||
use config::{CalendarStoreConfig, JsonCalendarStoreConfig};
|
use config::{CalendarStoreConfig, JsonCalendarStoreConfig};
|
||||||
use rustical_api::configure_api;
|
use rustical_api::configure_api;
|
||||||
use rustical_dav::{configure_dav, configure_well_known};
|
use rustical_dav::{configure_dav, configure_well_known};
|
||||||
|
use rustical_frontend::configure_frontend;
|
||||||
use rustical_store::calendar::JsonCalendarStore;
|
use rustical_store::calendar::JsonCalendarStore;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
@@ -45,9 +46,9 @@ async fn main() -> Result<()> {
|
|||||||
App::new()
|
App::new()
|
||||||
.wrap(Logger::new("[%s] %r"))
|
.wrap(Logger::new("[%s] %r"))
|
||||||
.wrap(NormalizePath::trim())
|
.wrap(NormalizePath::trim())
|
||||||
.service(
|
.service(web::scope("/caldav").configure(|cfg| {
|
||||||
web::scope("/dav").configure(|cfg| configure_dav(cfg, cal_store.clone().into())),
|
configure_dav(cfg, "/caldav".to_string(), cal_store.clone().into())
|
||||||
)
|
}))
|
||||||
.service(
|
.service(
|
||||||
web::scope("/.well-known")
|
web::scope("/.well-known")
|
||||||
.configure(|cfg| configure_well_known(cfg, "/dav".to_string())),
|
.configure(|cfg| configure_well_known(cfg, "/dav".to_string())),
|
||||||
@@ -55,6 +56,10 @@ async fn main() -> Result<()> {
|
|||||||
.service(
|
.service(
|
||||||
web::scope("/api").configure(|cfg| configure_api(cfg, cal_store.clone().into())),
|
web::scope("/api").configure(|cfg| configure_api(cfg, cal_store.clone().into())),
|
||||||
)
|
)
|
||||||
|
.service(
|
||||||
|
web::scope("/frontend")
|
||||||
|
.configure(|cfg| configure_frontend(cfg, cal_store.clone().into())),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.bind(("0.0.0.0", 4000))?
|
.bind(("0.0.0.0", 4000))?
|
||||||
.run()
|
.run()
|
||||||
|
|||||||
Reference in New Issue
Block a user