mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-13 15:52:38 +00:00
frontend: embed assets into binary
This commit is contained in:
102
Cargo.lock
generated
102
Cargo.lock
generated
@@ -19,29 +19,6 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "actix-files"
|
|
||||||
version = "0.6.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0773d59061dedb49a8aed04c67291b9d8cf2fe0b60130a381aab53c6dd86e9be"
|
|
||||||
dependencies = [
|
|
||||||
"actix-http",
|
|
||||||
"actix-service",
|
|
||||||
"actix-utils",
|
|
||||||
"actix-web",
|
|
||||||
"bitflags",
|
|
||||||
"bytes",
|
|
||||||
"derive_more 0.99.18",
|
|
||||||
"futures-core",
|
|
||||||
"http-range",
|
|
||||||
"log",
|
|
||||||
"mime",
|
|
||||||
"mime_guess",
|
|
||||||
"percent-encoding",
|
|
||||||
"pin-project-lite",
|
|
||||||
"v_htmlescape",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "actix-http"
|
name = "actix-http"
|
||||||
version = "3.9.0"
|
version = "3.9.0"
|
||||||
@@ -1483,12 +1460,6 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "http-range"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "httparse"
|
name = "httparse"
|
||||||
version = "1.9.5"
|
version = "1.9.5"
|
||||||
@@ -2486,6 +2457,40 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-embed"
|
||||||
|
version = "8.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0"
|
||||||
|
dependencies = [
|
||||||
|
"rust-embed-impl",
|
||||||
|
"rust-embed-utils",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-embed-impl"
|
||||||
|
version = "8.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rust-embed-utils",
|
||||||
|
"syn",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-embed-utils"
|
||||||
|
version = "8.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d"
|
||||||
|
dependencies = [
|
||||||
|
"sha2",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.24"
|
version = "0.1.24"
|
||||||
@@ -2605,12 +2610,15 @@ dependencies = [
|
|||||||
name = "rustical_frontend"
|
name = "rustical_frontend"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-files",
|
|
||||||
"actix-session",
|
"actix-session",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"askama",
|
"askama",
|
||||||
"askama_actix",
|
"askama_actix",
|
||||||
|
"futures-core",
|
||||||
|
"hex",
|
||||||
|
"mime_guess",
|
||||||
|
"rust-embed",
|
||||||
"rustical_store",
|
"rustical_store",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
@@ -2679,6 +2687,15 @@ version = "1.0.18"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "same-file"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
@@ -3573,12 +3590,6 @@ dependencies = [
|
|||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "v_htmlescape"
|
|
||||||
version = "0.15.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4e8257fbc510f0a46eb602c10215901938b5c2a7d5e70fc11483b1d3c9b5b18c"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "valuable"
|
name = "valuable"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -3597,6 +3608,16 @@ version = "0.9.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "walkdir"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||||
|
dependencies = [
|
||||||
|
"same-file",
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "want"
|
name = "want"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@@ -3709,6 +3730,15 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-util"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-x86_64-pc-windows-gnu"
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
|||||||
@@ -54,6 +54,10 @@ quick-xml = { version = "0.37", features = [
|
|||||||
"serde-types",
|
"serde-types",
|
||||||
"serialize",
|
"serialize",
|
||||||
] }
|
] }
|
||||||
|
rust-embed = "8.5"
|
||||||
|
futures-core = "0.3.31"
|
||||||
|
hex = "0.4.3"
|
||||||
|
mime_guess = "2.0.5"
|
||||||
itertools = "0.13"
|
itertools = "0.13"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
strum = { version = "0.26", features = ["strum_macros", "derive"] }
|
strum = { version = "0.26", features = ["strum_macros", "derive"] }
|
||||||
|
|||||||
@@ -16,4 +16,7 @@ thiserror = { workspace = true }
|
|||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
actix-web = { workspace = true }
|
actix-web = { workspace = true }
|
||||||
rustical_store = { workspace = true }
|
rustical_store = { workspace = true }
|
||||||
actix-files = "0.6"
|
rust-embed.workspace = true
|
||||||
|
futures-core.workspace = true
|
||||||
|
hex.workspace = true
|
||||||
|
mime_guess.workspace = true
|
||||||
|
|||||||
113
crates/frontend/src/assets.rs
Normal file
113
crates/frontend/src/assets.rs
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
use actix_web::{
|
||||||
|
body::BoxBody,
|
||||||
|
dev::{
|
||||||
|
HttpServiceFactory, ResourceDef, Service, ServiceFactory, ServiceRequest, ServiceResponse,
|
||||||
|
},
|
||||||
|
http::{header, Method},
|
||||||
|
HttpResponse,
|
||||||
|
};
|
||||||
|
use futures_core::future::LocalBoxFuture;
|
||||||
|
use rust_embed::RustEmbed;
|
||||||
|
|
||||||
|
#[derive(RustEmbed)]
|
||||||
|
#[folder = "frontend/dist/assets"]
|
||||||
|
pub struct Assets;
|
||||||
|
|
||||||
|
pub struct EmbedService<E>
|
||||||
|
where
|
||||||
|
E: 'static + RustEmbed,
|
||||||
|
{
|
||||||
|
_embed: PhantomData<E>,
|
||||||
|
prefix: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E> EmbedService<E>
|
||||||
|
where
|
||||||
|
E: 'static + RustEmbed,
|
||||||
|
{
|
||||||
|
pub fn new(prefix: String) -> Self {
|
||||||
|
Self {
|
||||||
|
prefix,
|
||||||
|
_embed: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E> HttpServiceFactory for EmbedService<E>
|
||||||
|
where
|
||||||
|
E: 'static + RustEmbed,
|
||||||
|
{
|
||||||
|
fn register(self, config: &mut actix_web::dev::AppService) {
|
||||||
|
let resource_def = if config.is_root() {
|
||||||
|
ResourceDef::root_prefix(&self.prefix)
|
||||||
|
} else {
|
||||||
|
ResourceDef::prefix(&self.prefix)
|
||||||
|
};
|
||||||
|
config.register_service(resource_def, None, self, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E> ServiceFactory<ServiceRequest> for EmbedService<E>
|
||||||
|
where
|
||||||
|
E: 'static + RustEmbed,
|
||||||
|
{
|
||||||
|
type Response = ServiceResponse;
|
||||||
|
type Error = actix_web::Error;
|
||||||
|
type Config = ();
|
||||||
|
type Service = EmbedService<E>;
|
||||||
|
type InitError = ();
|
||||||
|
type Future = LocalBoxFuture<'static, Result<Self::Service, Self::InitError>>;
|
||||||
|
|
||||||
|
fn new_service(&self, _: ()) -> Self::Future {
|
||||||
|
let prefix = self.prefix.clone();
|
||||||
|
Box::pin(async move {
|
||||||
|
Ok(Self {
|
||||||
|
prefix,
|
||||||
|
_embed: PhantomData,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E> Service<ServiceRequest> for EmbedService<E>
|
||||||
|
where
|
||||||
|
E: 'static + RustEmbed,
|
||||||
|
{
|
||||||
|
type Response = ServiceResponse<BoxBody>;
|
||||||
|
type Error = actix_web::Error;
|
||||||
|
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||||
|
|
||||||
|
actix_web::dev::always_ready!();
|
||||||
|
|
||||||
|
fn call(&self, req: ServiceRequest) -> Self::Future {
|
||||||
|
Box::pin(async move {
|
||||||
|
if req.method() != Method::GET && req.method() != Method::HEAD {
|
||||||
|
return Ok(req.into_response(HttpResponse::MethodNotAllowed()));
|
||||||
|
}
|
||||||
|
let path = req.match_info().unprocessed().trim_start_matches('/');
|
||||||
|
|
||||||
|
match E::get(path) {
|
||||||
|
Some(file) => {
|
||||||
|
let data = file.data;
|
||||||
|
let hash = hex::encode(file.metadata.sha256_hash());
|
||||||
|
let mime = mime_guess::from_path(path).first_or_octet_stream();
|
||||||
|
|
||||||
|
let body = if req.method() == Method::HEAD {
|
||||||
|
Default::default()
|
||||||
|
} else {
|
||||||
|
data
|
||||||
|
};
|
||||||
|
Ok(req.into_response(
|
||||||
|
HttpResponse::Ok()
|
||||||
|
.content_type(mime)
|
||||||
|
.insert_header((header::ETAG, hash))
|
||||||
|
.body(body),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
None => Ok(req.into_response(HttpResponse::NotFound())),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ use actix_web::{
|
|||||||
Responder,
|
Responder,
|
||||||
};
|
};
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
|
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},
|
||||||
@@ -13,6 +14,7 @@ use rustical_store::{
|
|||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
mod assets;
|
||||||
mod config;
|
mod config;
|
||||||
mod routes;
|
mod routes;
|
||||||
|
|
||||||
@@ -72,11 +74,7 @@ pub fn configure_frontend<AP: AuthenticationProvider, C: CalendarStore + ?Sized>
|
|||||||
)
|
)
|
||||||
.app_data(Data::from(auth_provider))
|
.app_data(Data::from(auth_provider))
|
||||||
.app_data(Data::from(store.clone()))
|
.app_data(Data::from(store.clone()))
|
||||||
.service(
|
.service(EmbedService::<Assets>::new("/assets".to_owned()))
|
||||||
// TODO: Bundle assets in a neat way
|
|
||||||
actix_files::Files::new("/assets", "crates/frontend/frontend/dist/assets")
|
|
||||||
.prefer_utf8(true),
|
|
||||||
)
|
|
||||||
.service(
|
.service(
|
||||||
web::resource("/user/{user}").route(web::method(Method::GET).to(route_user::<C>)),
|
web::resource("/user/{user}").route(web::method(Method::GET).to(route_user::<C>)),
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user