mirror of
https://github.com/NetworkCats/rustyip.git
synced 2026-11-13 16:17:46 +00:00
feat: add security headers, caching headers, and static file versioning
This commit is contained in:
@@ -10,6 +10,7 @@ use crate::cli_detect::is_cli_user_agent;
|
||||
use crate::db::{self, SharedDb};
|
||||
use crate::error::AppError;
|
||||
use crate::models::{IpInfo, get_en_name};
|
||||
use crate::static_assets;
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
pub struct IpQuery {
|
||||
@@ -28,6 +29,7 @@ struct IndexTemplate {
|
||||
ip: String,
|
||||
query: String,
|
||||
site_domain: Arc<str>,
|
||||
css_version: u64,
|
||||
asn: String,
|
||||
org: String,
|
||||
country: String,
|
||||
@@ -125,6 +127,7 @@ pub async fn root(
|
||||
ip: info.ip.clone(),
|
||||
query: query.ip.unwrap_or_default(),
|
||||
site_domain: state.site_domain.clone(),
|
||||
css_version: static_assets::asset_version("style.css"),
|
||||
asn: format_asn(&info),
|
||||
org: format_org(&info).to_owned(),
|
||||
country: format_country(&info).to_owned(),
|
||||
|
||||
@@ -3,6 +3,8 @@ pub mod config;
|
||||
pub mod db;
|
||||
pub mod error;
|
||||
pub mod handlers;
|
||||
pub mod middleware;
|
||||
pub mod models;
|
||||
pub mod routes;
|
||||
pub mod static_assets;
|
||||
pub mod updater;
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
use axum::extract::Request;
|
||||
use axum::http::header::{self, HeaderValue};
|
||||
use axum::middleware::Next;
|
||||
use axum::response::Response;
|
||||
|
||||
pub async fn security_headers(request: Request, next: Next) -> Response {
|
||||
let mut response = next.run(request).await;
|
||||
let headers = response.headers_mut();
|
||||
|
||||
headers.insert(
|
||||
header::X_CONTENT_TYPE_OPTIONS,
|
||||
HeaderValue::from_static("nosniff"),
|
||||
);
|
||||
headers.insert(header::X_FRAME_OPTIONS, HeaderValue::from_static("DENY"));
|
||||
headers.insert(
|
||||
header::REFERRER_POLICY,
|
||||
HeaderValue::from_static("strict-origin-when-cross-origin"),
|
||||
);
|
||||
headers.insert(
|
||||
header::STRICT_TRANSPORT_SECURITY,
|
||||
HeaderValue::from_static("max-age=63072000; includeSubDomains; preload"),
|
||||
);
|
||||
headers.insert(
|
||||
header::HeaderName::from_static("x-permitted-cross-domain-policies"),
|
||||
HeaderValue::from_static("none"),
|
||||
);
|
||||
headers.insert(
|
||||
header::HeaderName::from_static("permissions-policy"),
|
||||
HeaderValue::from_static(
|
||||
"camera=(), microphone=(), geolocation=(), interest-cohort=()",
|
||||
),
|
||||
);
|
||||
headers.insert(
|
||||
header::HeaderName::from_static("cross-origin-opener-policy"),
|
||||
HeaderValue::from_static("same-origin"),
|
||||
);
|
||||
headers.insert(
|
||||
header::HeaderName::from_static("cross-origin-resource-policy"),
|
||||
HeaderValue::from_static("same-origin"),
|
||||
);
|
||||
|
||||
response
|
||||
}
|
||||
+6
-3
@@ -1,8 +1,10 @@
|
||||
use axum::Router;
|
||||
use axum::middleware;
|
||||
use axum::routing::get;
|
||||
use tower_http::services::ServeDir;
|
||||
use axum::Router;
|
||||
|
||||
use crate::handlers::{self, AppState};
|
||||
use crate::middleware::security_headers;
|
||||
use crate::static_assets;
|
||||
|
||||
pub fn build_router(state: AppState) -> Router {
|
||||
Router::new()
|
||||
@@ -20,6 +22,7 @@ pub fn build_router(state: AppState) -> Router {
|
||||
.route("/vpn", get(handlers::vpn_handler))
|
||||
.route("/hosting", get(handlers::hosting_handler))
|
||||
.route("/tor", get(handlers::tor_handler))
|
||||
.nest_service("/static", ServeDir::new("static"))
|
||||
.route("/static/{*path}", get(static_assets::serve_static))
|
||||
.layer(middleware::from_fn(security_headers))
|
||||
.with_state(state)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use axum::extract::Path;
|
||||
use axum::http::header::{self, HeaderValue};
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::{IntoResponse, Response};
|
||||
|
||||
use static_files::Resource;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/generated.rs"));
|
||||
|
||||
static ASSETS: LazyLock<HashMap<&'static str, Resource>> = LazyLock::new(generate);
|
||||
|
||||
pub fn asset_version(filename: &str) -> u64 {
|
||||
ASSETS
|
||||
.get(filename)
|
||||
.map(|r| r.modified)
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
pub async fn serve_static(Path(path): Path<String>) -> Response {
|
||||
let Some(resource) = ASSETS.get(path.as_str()) else {
|
||||
return StatusCode::NOT_FOUND.into_response();
|
||||
};
|
||||
|
||||
(
|
||||
[
|
||||
(header::CONTENT_TYPE, HeaderValue::from_static(resource.mime_type)),
|
||||
(
|
||||
header::CACHE_CONTROL,
|
||||
HeaderValue::from_static("public, max-age=31536000, immutable"),
|
||||
),
|
||||
],
|
||||
resource.data,
|
||||
)
|
||||
.into_response()
|
||||
}
|
||||
Reference in New Issue
Block a user