global: snapshot

This commit is contained in:
k
2024-09-10 23:15:13 +02:00
parent 5edb8111a2
commit ba4021ad73
64 changed files with 2254 additions and 401 deletions

View File

@@ -12,8 +12,10 @@ use serde::Deserialize;
use parser::{log, Date, DateMap, Height, HeightMap, MapChunkId, HEIGHT_MAP_CHUNK_SIZE, OHLC};
use crate::{
chunk::Chunk, headers::add_cors_to_headers, kind::Kind, response::typed_value_to_response,
routes::Route, AppState,
api::structs::{Chunk, Kind, Route},
header_map::HeaderMapUtils,
response::typed_value_to_response,
AppState,
};
#[derive(Deserialize)]
@@ -21,26 +23,26 @@ pub struct Params {
chunk: Option<usize>,
}
pub async fn file_handler(
pub async fn dataset_handler(
headers: HeaderMap,
path: Path<String>,
query: Query<Params>,
State(app_state): State<AppState>,
) -> Response {
match _file_handler(headers, path, query, app_state) {
match _dataset_handler(headers, path, query, app_state) {
Ok(response) => response,
Err(error) => {
let mut response =
(StatusCode::INTERNAL_SERVER_ERROR, error.to_string()).into_response();
add_cors_to_headers(response.headers_mut());
response.headers_mut().insert_cors();
response
}
}
}
fn _file_handler(
fn _dataset_handler(
headers: HeaderMap,
Path(path): Path<String>,
query: Query<Params>,

View File

@@ -0,0 +1,14 @@
use axum::{extract::State, http::HeaderMap, response::Response};
use reqwest::header::HOST;
use crate::{response::generic_to_reponse, AppState};
pub async fn fallback(headers: HeaderMap, State(app_state): State<AppState>) -> Response {
generic_to_reponse(
app_state
.routes
.to_full_paths(headers[HOST].to_str().unwrap().to_string()),
None,
60,
)
}

View File

@@ -0,0 +1,5 @@
mod dataset;
mod fallback;
pub use dataset::*;
pub use fallback::*;

19
server/src/api/mod.rs Normal file
View File

@@ -0,0 +1,19 @@
use axum::{routing::get, Router};
use handlers::{dataset_handler, fallback};
use crate::AppState;
mod handlers;
pub mod structs;
pub trait ApiRoutes {
fn add_api_routes(self) -> Self;
}
impl ApiRoutes for Router<AppState> {
fn add_api_routes(self) -> Self {
self.route("/api/*path", get(dataset_handler))
.route("/api/", get(fallback))
.route("/api", get(fallback))
}
}

View File

@@ -0,0 +1,9 @@
mod chunk;
mod kind;
mod paths;
mod routes;
pub use chunk::*;
pub use kind::*;
pub use paths::*;
pub use routes::*;

View File

@@ -8,7 +8,9 @@ use derive_deref::{Deref, DerefMut};
use itertools::Itertools;
use parser::{Json, Serialization};
use crate::{paths::Paths, Grouped};
use crate::Grouped;
use super::Paths;
#[derive(Clone, Debug)]
pub struct Route {

74
server/src/header_map.rs Normal file
View File

@@ -0,0 +1,74 @@
use std::path::Path;
use axum::http::{header, HeaderMap};
use parser::log;
const STALE_IF_ERROR: u64 = 31_536_000; // 1 Year
pub trait HeaderMapUtils {
fn insert_cors(&mut self);
fn insert_cache_control(&mut self, max_age: u64, stale_while_revalidate: u64);
fn insert_content_type(&mut self, path: &Path);
fn insert_content_type_application_javascript(&mut self);
fn insert_content_type_application_json(&mut self);
fn insert_content_type_text_html(&mut self);
fn insert_content_type_text_plain(&mut self);
fn insert_content_type_font_woff2(&mut self);
}
impl HeaderMapUtils for HeaderMap {
fn insert_cors(&mut self) {
self.insert(header::ACCESS_CONTROL_ALLOW_ORIGIN, "*".parse().unwrap());
self.insert(header::ACCESS_CONTROL_ALLOW_HEADERS, "*".parse().unwrap());
}
fn insert_cache_control(&mut self, max_age: u64, stale_while_revalidate: u64) {
self.insert(
header::CACHE_CONTROL,
format!(
"public, max-age={max_age}, stale-while-revalidate={stale_while_revalidate}, stale-if-error={STALE_IF_ERROR}")
.parse()
.unwrap(),
);
}
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
fn insert_content_type(&mut self, path: &Path) {
match path.extension().unwrap().to_str().unwrap() {
"js" => self.insert_content_type_application_javascript(),
"json" => self.insert_content_type_application_json(),
"html" => self.insert_content_type_text_html(),
"txt" => self.insert_content_type_text_plain(),
"woff2" => self.insert_content_type_font_woff2(),
extension => {
log(&format!("Extension unsupported: {extension}"));
panic!()
}
}
}
fn insert_content_type_application_javascript(&mut self) {
self.insert(
header::CONTENT_TYPE,
"application/javascript".parse().unwrap(),
);
}
fn insert_content_type_application_json(&mut self) {
self.insert(header::CONTENT_TYPE, "application/json".parse().unwrap());
}
fn insert_content_type_text_html(&mut self) {
self.insert(header::CONTENT_TYPE, "text/html".parse().unwrap());
}
fn insert_content_type_text_plain(&mut self) {
self.insert(header::CONTENT_TYPE, "text/plain".parse().unwrap());
}
fn insert_content_type_font_woff2(&mut self) {
self.insert(header::CONTENT_TYPE, "font/woff2".parse().unwrap());
}
}

View File

@@ -1,26 +0,0 @@
use axum::http::{header, HeaderMap};
const STALE_IF_ERROR: u64 = 604800; // 1 Week
pub fn add_cors_to_headers(headers: &mut HeaderMap) {
headers.insert(header::ACCESS_CONTROL_ALLOW_ORIGIN, "*".parse().unwrap());
headers.insert(header::ACCESS_CONTROL_ALLOW_HEADERS, "*".parse().unwrap());
}
pub fn add_json_type_to_headers(headers: &mut HeaderMap) {
headers.insert(header::CONTENT_TYPE, "application/json".parse().unwrap());
}
pub fn add_cache_control_to_headers(
headers: &mut HeaderMap,
max_age: u64,
stale_while_revalidate: u64,
) {
headers.insert(
header::CACHE_CONTROL,
format!(
"public, max-age={max_age}, stale-while-revalidate={stale_while_revalidate}, stale-if-error={STALE_IF_ERROR}")
.parse()
.unwrap(),
);
}

View File

@@ -1,23 +1,17 @@
use std::sync::Arc;
use axum::{extract::State, http::HeaderMap, response::Response, routing::get, serve, Router};
use api::{structs::Routes, ApiRoutes};
use axum::{serve, Router};
use parser::{log, reset_logs};
use reqwest::header::HOST;
use response::generic_to_reponse;
use routes::Routes;
use serde::Serialize;
use tokio::net::TcpListener;
use tower_http::compression::CompressionLayer;
use website::WebsiteRoutes;
mod chunk;
mod handler;
mod headers;
mod kind;
mod paths;
mod api;
mod header_map;
mod response;
mod routes;
use handler::file_handler;
mod website;
#[derive(Clone, Debug, Default, Serialize)]
pub struct Grouped<T> {
@@ -52,28 +46,26 @@ async fn main() -> color_eyre::Result<()> {
.zstd(true);
let router = Router::new()
.route("/*path", get(file_handler))
.route("/", get(fallback))
.add_api_routes()
.add_website_routes()
.with_state(state)
.layer(compression_layer);
let port = 3110;
let mut port = 3110;
let mut listener;
loop {
listener = TcpListener::bind(format!("0.0.0.0:{port}")).await;
if listener.is_ok() {
break;
}
port += 1;
}
log(&format!("Starting server on port {port}..."));
let listener = TcpListener::bind(format!("0.0.0.0:{port}")).await?;
let listener = listener.unwrap();
serve(listener, router).await?;
Ok(())
}
pub async fn fallback(headers: HeaderMap, State(app_state): State<AppState>) -> Response {
generic_to_reponse(
app_state
.routes
.to_full_paths(headers[HOST].to_str().unwrap().to_string()),
None,
60,
)
}

View File

@@ -7,10 +7,8 @@ use serde::de::DeserializeOwned;
use serde::Serialize;
use crate::{
chunk::Chunk,
headers::{add_cache_control_to_headers, add_cors_to_headers, add_json_type_to_headers},
kind::Kind,
routes::Route,
api::structs::{Chunk, Kind, Route},
header_map::HeaderMapUtils,
};
#[derive(Serialize)]
@@ -88,9 +86,9 @@ where
let max_age = cache_time;
let stale_while_revalidate = 2 * max_age;
add_cors_to_headers(headers);
add_json_type_to_headers(headers);
add_cache_control_to_headers(headers, max_age, stale_while_revalidate);
headers.insert_cors();
headers.insert_content_type_application_json();
headers.insert_cache_control(max_age, stale_while_revalidate);
response
}

View File

@@ -0,0 +1,56 @@
use std::{
fs::{self},
path::{Path, PathBuf},
};
use axum::{extract, http::HeaderMap, response::Response};
use parser::log;
use crate::header_map::HeaderMapUtils;
use super::minify_js;
const WEBSITE_PATH: &str = "../website";
pub async fn file_handler(headers: HeaderMap, path: extract::Path<String>) -> Response {
let path = path.0.replace("..", "").replace("\\", "");
let mut path = str_to_path(&path);
if path.extension().is_none() && !path.exists() {
path = str_to_path("index.html");
}
path_to_response(&path)
}
pub async fn index_handler(headers: HeaderMap) -> Response {
path_to_response(&str_to_path("index.html"))
}
fn path_to_response(path: &Path) -> Response {
let mut response;
if path.extension().unwrap() == "js" {
let content = minify_js(path);
response = Response::new(content.into());
} else {
let content = fs::read(path).unwrap_or_else(|error| {
log(&error.to_string());
let path = path.to_str().unwrap();
log(&format!("Can't read file {path}"));
panic!("")
});
response = Response::new(content.into());
}
let headers = response.headers_mut();
headers.insert_cors();
headers.insert_content_type(path);
response
}
fn str_to_path(path: &str) -> PathBuf {
PathBuf::from(&format!("{WEBSITE_PATH}/{path}"))
}

View File

@@ -0,0 +1,29 @@
// Simplified version of: https://github.com/swc-project/swc/blob/main/crates/swc/examples/minify.rs
use std::{path::Path, sync::Arc};
use swc::{config::JsMinifyOptions, try_with_handler, JsMinifyExtras};
use swc_common::{SourceMap, GLOBALS};
pub fn minify_js(path: &Path) -> String {
let cm = Arc::<SourceMap>::default();
let c = swc::Compiler::new(cm.clone());
let output = GLOBALS
.set(&Default::default(), || {
try_with_handler(cm.clone(), Default::default(), |handler| {
let fm = cm.load_file(path).expect("failed to load file");
c.minify(
fm,
handler,
&JsMinifyOptions::default(),
JsMinifyExtras::default(),
)
})
})
.unwrap();
output.code
}

View File

@@ -0,0 +1,3 @@
mod js;
pub use js::*;

View File

@@ -0,0 +1,5 @@
mod file;
mod minify;
pub use file::*;
use minify::*;

18
server/src/website/mod.rs Normal file
View File

@@ -0,0 +1,18 @@
use axum::{routing::get, Router};
mod handlers;
use handlers::{file_handler, index_handler};
use crate::AppState;
pub trait WebsiteRoutes {
fn add_website_routes(self) -> Self;
}
impl WebsiteRoutes for Router<AppState> {
fn add_website_routes(self) -> Self {
self.route("/*path", get(file_handler))
.route("/", get(index_handler))
}
}

View File