Skip to content

Commit

Permalink
Merge pull request #39 from PocketRelay/35-on-demand-dashboard-updates
Browse files Browse the repository at this point in the history
Modified public serving middleware to allow serving from filesystem
  • Loading branch information
jacobtread authored Aug 31, 2023
2 parents 986a235 + 2c06de2 commit c5ff7e6
Showing 1 changed file with 62 additions and 36 deletions.
98 changes: 62 additions & 36 deletions src/routes/public.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ use axum::{
use embeddy::Embedded;
use hyper::{header::CONTENT_TYPE, StatusCode};
use std::{
borrow::Cow,
convert::Infallible,
future::{ready, Ready},
path::Path,
path::{Path, PathBuf},
task::{Context, Poll},
};
use tower::Service;

use crate::utils::types::BoxFuture;

/// Resources embedded from the public data folder such as the
/// dashboard static assets and the content for the ingame store.
///
Expand All @@ -25,63 +25,89 @@ use tower::Service;
#[folder = "src/resources/public"]
pub struct PublicContent;

fn find_local_path(path: &str) -> Option<PathBuf> {
let data_path = Path::new("data/public").canonicalize().ok()?;
let file_path = data_path.join(path).canonicalize().ok()?;
// Folders outside of the data path should be ignored
if !file_path.starts_with(data_path) {
return None;
}

Some(file_path)
}

impl<T> Service<Request<T>> for PublicContent {
type Response = Response;
type Error = Infallible;
type Future = Ready<Result<Self::Response, Self::Error>>;
type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;

fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}

fn call(&mut self, req: Request<T>) -> Self::Future {
let mut path = req.uri().path();
let std_path = Path::new(path);
let path = req.uri().path();

// Strip the leading slash in order to match paths correctly
let mut path = match path.strip_prefix('/') {
Some(value) => value.to_string(),
None => path.to_string(),
};

let std_path = Path::new(&path);

// Determine type using extension
let extension: Cow<'_, str> = match std_path.extension() {
let extension: String = match std_path.extension() {
// Extract the extension lossily
Some(value) => value.to_string_lossy(),
Some(value) => value.to_string_lossy().to_string(),
// Use the index file when responding to paths (For SPA dashboard support)
None => {
path = "index.html";
Cow::Borrowed("html")
path = "index.html".to_string();
"html".to_string()
}
};

// Strip the leading slash in order to match paths correctly
let path = match path.strip_prefix('/') {
Some(value) => value,
None => path,
};
Box::pin(async move {
let path = path;

// Create the response message
let response = match Self::get(path) {
// File exists, serve it with its known extension
Some(file) => {
// Guess mime type from file extension
let mime_type: &'static str = match extension.as_ref() {
"html" => "text/html",
"js" | "mjs" => "text/javascript",
"json" => "application/json",
"woff" => "font/woff",
"woff2" => "font/woff2",
"webp" => "image/webp",
"css" => "text/css",
_ => "text/plain",
};
// Guess mime type from file extension
let mime_type: &'static str = match extension.as_ref() {
"html" => "text/html",
"js" | "mjs" => "text/javascript",
"json" => "application/json",
"woff" => "font/woff",
"woff2" => "font/woff2",
"webp" => "image/webp",
"css" => "text/css",
_ => "text/plain",
};

// File exists in public data folder server try serve that and fallback to next on failure
if let Some(local_path) = find_local_path(&path) {
if local_path.exists() && local_path.is_file() {
if let Ok(contents) = tokio::fs::read(local_path).await {
// Create byte reponse from the embedded file
let mut response = Full::from(contents).into_response();
response
.headers_mut()
.insert(CONTENT_TYPE, HeaderValue::from_static(mime_type));
return Ok(response);
}
}
}

// File exists within binary serve that
if let Some(contents) = Self::get(&path) {
// Create byte reponse from the embedded file
let mut response = Full::from(file).into_response();
let mut response = Full::from(contents).into_response();
response
.headers_mut()
.insert(CONTENT_TYPE, HeaderValue::from_static(mime_type));
response
return Ok(response);
}
// File not found 404
None => StatusCode::NOT_FOUND.into_response(),
};

ready(Ok(response))
// All above failed server 404
Ok(StatusCode::NOT_FOUND.into_response())
})
}
}

0 comments on commit c5ff7e6

Please sign in to comment.