Compare commits
3 Commits
c2263e6bf1
...
d1c41e8fc5
| Author | SHA1 | Date | |
|---|---|---|---|
| d1c41e8fc5 | |||
| 5b6cb7d796 | |||
| 593c0322bc |
@ -89,9 +89,10 @@ SSR_CACHE_SECS=3600
|
|||||||
|
|
||||||
# HTTP 响应压缩算法。逗号分隔,大小写不敏感。
|
# HTTP 响应压缩算法。逗号分隔,大小写不敏感。
|
||||||
# 支持:gzip、brotli(可简写为 br)、deflate、zstd。
|
# 支持:gzip、brotli(可简写为 br)、deflate、zstd。
|
||||||
# 示例值(四项全开)等价于 "all",也是不设环境变量时的默认。
|
# 默认关闭压缩(不设环境变量时等同 "off")。
|
||||||
|
# 设为 "all" 启用全部算法,或按需组合,例如 "gzip, brotli, zstd"。
|
||||||
# 用 "none" 或 "off" 关闭压缩。
|
# 用 "none" 或 "off" 关闭压缩。
|
||||||
COMPRESSION_ALGORITHMS=gzip,brotli,deflate,zstd
|
COMPRESSION_ALGORITHMS=off
|
||||||
|
|
||||||
# ─────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────
|
||||||
# 图片响应缓存头(硬编码默认值,非环境变量)
|
# 图片响应缓存头(硬编码默认值,非环境变量)
|
||||||
|
|||||||
30
src/main.rs
30
src/main.rs
@ -35,9 +35,9 @@ pub mod infra;
|
|||||||
mod highlight;
|
mod highlight;
|
||||||
// middleware:Axum 中间件与启动期纯函数(cache-control / admin 守卫 / 压缩层),
|
// middleware:Axum 中间件与启动期纯函数(cache-control / admin 守卫 / 压缩层),
|
||||||
// server-only。从 main.rs 抽出以便独立测试,路由组装处以 crate::middleware::xxx 调用。
|
// server-only。从 main.rs 抽出以便独立测试,路由组装处以 crate::middleware::xxx 调用。
|
||||||
|
mod hooks;
|
||||||
#[cfg(feature = "server")]
|
#[cfg(feature = "server")]
|
||||||
mod middleware;
|
mod middleware;
|
||||||
mod hooks;
|
|
||||||
mod models;
|
mod models;
|
||||||
mod pages;
|
mod pages;
|
||||||
mod router;
|
mod router;
|
||||||
@ -154,6 +154,25 @@ fn main() {
|
|||||||
eprintln!("HINT: check the logs above; verify DATABASE_URL and that PostgreSQL is healthy.");
|
eprintln!("HINT: check the logs above; verify DATABASE_URL and that PostgreSQL is healthy.");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 端口预探测:dioxus::server::serve() 内部对
|
||||||
|
// `TcpListener::bind(addr).await...unwrap()`(dioxus-server 0.7.9 launch.rs:143)
|
||||||
|
// 失败会直接 panic(SIGABRT)。这里在交接给 serve() 之前先探测同一地址,
|
||||||
|
// 失败则走统一的 exit(1) 路径,输出可操作的提示,而不是丢一个裸 panic 栈。
|
||||||
|
// 探测用的 listener 立即 drop,由 serve() 重新绑定(同进程内快速 rebind,
|
||||||
|
// 不经过 TIME_WAIT,无窗口问题)。
|
||||||
|
let addr = dioxus::cli_config::fullstack_address_or_localhost();
|
||||||
|
if let Err(e) = tokio::net::TcpListener::bind(addr).await {
|
||||||
|
tracing::error!("无法绑定监听地址 {addr}: {e}");
|
||||||
|
eprintln!("ERROR: 无法绑定监听地址 {addr}: {e}");
|
||||||
|
eprintln!(
|
||||||
|
"HINT: 端口 {} 可能已被占用。用 `lsof -i :{}` 查看占用进程,\
|
||||||
|
或设置 PORT 环境变量换一个端口。",
|
||||||
|
addr.port(),
|
||||||
|
addr.port()
|
||||||
|
);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
// 迁移 runtime 用完即弃,显式 drop 以在 serve() 前释放其线程资源。
|
// 迁移 runtime 用完即弃,显式 drop 以在 serve() 前释放其线程资源。
|
||||||
drop(migrate_rt);
|
drop(migrate_rt);
|
||||||
@ -330,7 +349,9 @@ fn main() {
|
|||||||
// layer 顺序:后加的最外层先执行。CSRF 最外层先拦截非法来源。
|
// layer 顺序:后加的最外层先执行。CSRF 最外层先拦截非法来源。
|
||||||
let mut app_routes = dioxus_app
|
let mut app_routes = dioxus_app
|
||||||
.layer(axum::middleware::from_fn(ssr_generation_middleware))
|
.layer(axum::middleware::from_fn(ssr_generation_middleware))
|
||||||
.layer(axum::middleware::from_fn(crate::middleware::add_cache_control))
|
.layer(axum::middleware::from_fn(
|
||||||
|
crate::middleware::add_cache_control,
|
||||||
|
))
|
||||||
.layer(axum::middleware::from_fn(crate::api::csrf::csrf_middleware));
|
.layer(axum::middleware::from_fn(crate::api::csrf::csrf_middleware));
|
||||||
if let Some(layer) = crate::middleware::compression_layer_from_env() {
|
if let Some(layer) = crate::middleware::compression_layer_from_env() {
|
||||||
app_routes = app_routes.layer(layer);
|
app_routes = app_routes.layer(layer);
|
||||||
@ -341,9 +362,8 @@ fn main() {
|
|||||||
));
|
));
|
||||||
// admin_guard 置于最外层(最后添加 = 最先执行):未登录的 /admin* 请求
|
// admin_guard 置于最外层(最后添加 = 最先执行):未登录的 /admin* 请求
|
||||||
// 在 CSRF / cache / SSR 渲染之前就被 302 短路,零渲染开销。
|
// 在 CSRF / cache / SSR 渲染之前就被 302 短路,零渲染开销。
|
||||||
let app_routes = app_routes.layer(axum::middleware::from_fn(
|
let app_routes =
|
||||||
crate::middleware::admin_guard,
|
app_routes.layer(axum::middleware::from_fn(crate::middleware::admin_guard));
|
||||||
));
|
|
||||||
|
|
||||||
// 静态资源路由:图片文件服务。
|
// 静态资源路由:图片文件服务。
|
||||||
// 注意:`dioxus::server::serve()` 接管了 listener 与 `into_make_service`
|
// 注意:`dioxus::server::serve()` 接管了 listener 与 `into_make_service`
|
||||||
|
|||||||
@ -78,7 +78,8 @@ pub(crate) fn parse_compression_algorithms(env: &str) -> Option<CompressionAlgor
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 根据 COMPRESSION_ALGORITHMS 环境变量构造 CompressionLayer。
|
/// 根据 COMPRESSION_ALGORITHMS 环境变量构造 CompressionLayer。
|
||||||
/// 未设置或设置为 "all" 时启用全部算法;设置为 ""、"none" 或 "off" 时禁用。
|
/// 默认(未设置时)关闭压缩;显式设为 "all" 启用全部算法,设为 "gzip,brotli,..."
|
||||||
|
/// 按需选择;设为 ""、"none" 或 "off" 等价于不启用。
|
||||||
///
|
///
|
||||||
/// CompressionLayer 使用 tower-http 的 `DefaultPredicate`,开箱即用即:
|
/// CompressionLayer 使用 tower-http 的 `DefaultPredicate`,开箱即用即:
|
||||||
/// - 跳过 `image/*` content-type(WebP/PNG/JPEG/GIF 等已是压缩格式,再压浪费 CPU,
|
/// - 跳过 `image/*` content-type(WebP/PNG/JPEG/GIF 等已是压缩格式,再压浪费 CPU,
|
||||||
@ -91,7 +92,7 @@ pub(crate) fn parse_compression_algorithms(env: &str) -> Option<CompressionAlgor
|
|||||||
pub(crate) fn compression_layer_from_env() -> Option<tower_http::compression::CompressionLayer> {
|
pub(crate) fn compression_layer_from_env() -> Option<tower_http::compression::CompressionLayer> {
|
||||||
use tower_http::compression::CompressionLayer;
|
use tower_http::compression::CompressionLayer;
|
||||||
|
|
||||||
let env = std::env::var("COMPRESSION_ALGORITHMS").unwrap_or_else(|_| "all".to_string());
|
let env = std::env::var("COMPRESSION_ALGORITHMS").unwrap_or_else(|_| "off".to_string());
|
||||||
let algorithms = parse_compression_algorithms(&env)?;
|
let algorithms = parse_compression_algorithms(&env)?;
|
||||||
|
|
||||||
Some(
|
Some(
|
||||||
@ -303,12 +304,9 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn compression_default_env_is_all() {
|
fn compression_default_env_is_off() {
|
||||||
// 模拟未设置环境变量时的默认值
|
// 模拟未设置环境变量时的默认值
|
||||||
assert_eq!(
|
assert_eq!(parse_compression_algorithms("off"), None);
|
||||||
parse_compression_algorithms("all"),
|
|
||||||
Some(CompressionAlgorithms::all_enabled())
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
use crate::components::ui::{BTN_OUTLINE, BTN_TEXT_AMBER, BTN_TEXT_RED, LoadingButton};
|
use crate::components::ui::{LoadingButton, BTN_OUTLINE, BTN_TEXT_AMBER, BTN_TEXT_RED};
|
||||||
|
|
||||||
use super::format_bytes;
|
use super::format_bytes;
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user