diff --git a/crates/frontend/Cargo.toml b/crates/frontend/Cargo.toml
index 26cc44d..d100300 100644
--- a/crates/frontend/Cargo.toml
+++ b/crates/frontend/Cargo.toml
@@ -1,15 +1,19 @@
[package]
name = "rustical_frontend"
-version = "0.1.0"
-edition = "2021"
+version.workspace = true
+edition.workspace = true
+description.workspace = true
+repository.workspace = true
+publish = false
[dependencies]
-actix-web = "4.9"
-askama = { version = "0.12", features = ["serde", "with-actix-web"] }
-askama_actix = "0.14"
-tokio = { version = "1.40", features = ["sync", "full"] }
-rustical_store = { path = "../store/" }
-anyhow = "1.0"
-thiserror = "1.0"
-actix-session = { version = "0.10", features = ["cookie-session"] }
-serde = { version = "1.0", features = ["derive", "rc"] }
+askama = { workspace = true }
+askama_actix = { workspace = true }
+actix-session = { workspace = true }
+serde = { workspace = true }
+anyhow = { workspace = true }
+thiserror = { workspace = true }
+tokio = { workspace = true }
+actix-web = { workspace = true }
+rustical_store = { workspace = true }
+actix-files = "0.6.6"
diff --git a/crates/frontend/public/asd.html b/crates/frontend/public/asd.html
new file mode 100644
index 0000000..21c82e4
--- /dev/null
+++ b/crates/frontend/public/asd.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+ ok wow!
+
+
+
diff --git a/crates/frontend/public/style.css b/crates/frontend/public/style.css
new file mode 100644
index 0000000..e69de29
diff --git a/crates/frontend/src/config.rs b/crates/frontend/src/config.rs
new file mode 100644
index 0000000..b03f3b1
--- /dev/null
+++ b/crates/frontend/src/config.rs
@@ -0,0 +1,6 @@
+use serde::{Deserialize, Serialize};
+
+#[derive(Deserialize, Serialize, Debug, Clone)]
+pub struct FrontendConfig {
+ secret_key: String,
+}
diff --git a/crates/frontend/src/lib.rs b/crates/frontend/src/lib.rs
index 76eb90c..d02f48b 100644
--- a/crates/frontend/src/lib.rs
+++ b/crates/frontend/src/lib.rs
@@ -1,31 +1,93 @@
+use actix_session::{storage::CookieSessionStore, Session, SessionMiddleware};
use actix_web::{
+ cookie::Key,
get,
http::Method,
- web::{self, Data},
+ web::{self, Data, Path},
+ Responder,
};
use askama::Template;
-use rustical_store::CalendarStore;
+use login::{route_get_login, route_post_login};
+use rustical_store::{
+ auth::{AuthenticationMiddleware, AuthenticationProvider, User},
+ model::Calendar,
+ CalendarStore,
+};
use std::sync::Arc;
use tokio::sync::RwLock;
+mod config;
+mod login;
+
+pub use config::FrontendConfig;
+
#[derive(Template)]
#[template(path = "index.html")]
struct IndexTemplate {}
#[get("")]
-async fn route_index() -> IndexTemplate {
+async fn route_index(session: Session) -> IndexTemplate {
+ if let Some(user) = session.get::("user").unwrap() {
+ dbg!(user);
+ } else {
+ session.insert("user", "lennart").unwrap();
+ }
+ dbg!(session.status());
IndexTemplate {}
}
-async fn route_user(store: Data>) -> IndexTemplate {
- IndexTemplate {}
+#[derive(Template)]
+#[template(path = "components/calendar_list.html")]
+struct CalendarList {
+ pub owner: String,
+ pub calendars: Vec,
}
-pub fn configure_frontend(
+#[derive(Template)]
+#[template(path = "layouts/default.html")]
+struct DefaultTemplate {
+ pub body: Body,
+}
+
+async fn route_user(
+ path: Path,
+ store: Data>,
+) -> impl Responder {
+ let store = store.read().await;
+ let owner = path.into_inner();
+ DefaultTemplate {
+ body: CalendarList {
+ owner: owner.to_owned(),
+ calendars: store.get_calendars(&owner).await.unwrap(),
+ },
+ }
+}
+
+pub fn configure_frontend(
cfg: &mut web::ServiceConfig,
+ auth_provider: Arc,
store: Arc>,
) {
- cfg.app_data(Data::from(store.clone()))
- .service(route_index)
- .service(web::resource("/user/{user}").route(web::method(Method::GET).to(route_user::)));
+ cfg.service(
+ web::scope("")
+ .wrap(AuthenticationMiddleware::new(auth_provider.clone()))
+ .wrap(
+ SessionMiddleware::builder(CookieSessionStore::default(), Key::from(&[0; 64]))
+ .cookie_secure(true)
+ .cookie_content_security(actix_session::config::CookieContentSecurity::Private)
+ .build(),
+ )
+ .app_data(Data::from(auth_provider))
+ .app_data(Data::from(store.clone()))
+ .service(actix_files::Files::new("/public", "crates/frontend/public").prefer_utf8(true))
+ .service(route_index)
+ .service(
+ web::resource("/user/{user}").route(web::method(Method::GET).to(route_user::)),
+ )
+ .service(
+ web::resource("/login")
+ .route(web::method(Method::GET).to(route_get_login))
+ .route(web::method(Method::POST).to(route_post_login::)),
+ ),
+ );
}
diff --git a/crates/frontend/src/login.rs b/crates/frontend/src/login.rs
new file mode 100644
index 0000000..7633f3f
--- /dev/null
+++ b/crates/frontend/src/login.rs
@@ -0,0 +1,47 @@
+use actix_session::Session;
+use actix_web::{
+ error::ErrorUnauthorized,
+ web::{Data, Form, Redirect},
+ HttpRequest, HttpResponse, Responder,
+};
+use askama::Template;
+use rustical_store::auth::AuthenticationProvider;
+use serde::Deserialize;
+
+use crate::DefaultTemplate;
+
+#[derive(Template)]
+#[template(path = "components/login.html")]
+struct LoginForm;
+
+pub async fn route_get_login() -> impl Responder {
+ DefaultTemplate { body: LoginForm }
+}
+
+#[derive(Deserialize)]
+pub struct PostLoginForm {
+ username: String,
+ password: String,
+}
+
+pub async fn route_post_login(
+ req: HttpRequest,
+ form: Form,
+ session: Session,
+ auth_provider: Data,
+) -> HttpResponse {
+ // TODO: implement auth check
+ dbg!(&form.username, &form.password);
+ if let Ok(Some(user)) = auth_provider
+ .validate_user_token(&form.username, &form.password)
+ .await
+ {
+ session.insert("user", user).unwrap();
+ Redirect::to(format!("/frontend/user/{}", &form.username))
+ .see_other()
+ .respond_to(&req)
+ .map_into_boxed_body()
+ } else {
+ ErrorUnauthorized("Unauthorized").error_response()
+ }
+}
diff --git a/crates/frontend/templates/components/calendar_list.html b/crates/frontend/templates/components/calendar_list.html
new file mode 100644
index 0000000..da88e35
--- /dev/null
+++ b/crates/frontend/templates/components/calendar_list.html
@@ -0,0 +1,12 @@
+Welcome {{ owner }}!
+
+ {% for calendar in calendars %}
+ -
+ {% if let Some(name) = calendar.displayname %}
+ {{ name }}
+ {% else %}
+ {{ calendar.id }}
+ {% endif %}
+
+ {% endfor %}
+
diff --git a/crates/frontend/templates/components/login.html b/crates/frontend/templates/components/login.html
new file mode 100644
index 0000000..0f0112b
--- /dev/null
+++ b/crates/frontend/templates/components/login.html
@@ -0,0 +1,7 @@
+
diff --git a/crates/frontend/templates/layouts/default.html b/crates/frontend/templates/layouts/default.html
new file mode 100644
index 0000000..918a7ae
--- /dev/null
+++ b/crates/frontend/templates/layouts/default.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+ RustiCal
+
+
+
+
+ {{ body|safe }}
+
+
+