Public server: separate in/out IP quotas, web dashboard scaffold, test intervals
3 agents worked in parallel: 1. DB schema (user_db.rs): - ip_usage: inbound_bytes/outbound_bytes columns (renamed from tx/rx) - test_intervals table for per-second graphing data - Directional methods: get_ip_daily_inbound/outbound, record_ip_inbound/outbound - Query methods: get_session_intervals, get_ip_sessions, get_ip_stats - New structs: IntervalData, SessionSummary, IpStats 2. Quota (quota.rs): - Direction enum (Inbound/Outbound/Both) - 6 new directional IP limits (daily/weekly/monthly × in/out) - check_ip() now takes direction parameter - record_usage() takes (inbound_bytes, outbound_bytes) 3. Web dashboard (web/): - Stub router with axum (will be expanded) - Templates: index.html + dashboard.html with Chart.js - Dependencies: axum, tower-http, serde, serde_json, askama (optional, pro feature) CLI additions: --ip-daily-in, --ip-daily-out, --web-port, --shared-password 64 tests, all passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@ mod user_db;
|
||||
mod quota;
|
||||
mod enforcer;
|
||||
mod server_loop;
|
||||
mod web;
|
||||
mod ldap_auth;
|
||||
|
||||
use clap::Parser;
|
||||
@@ -88,10 +89,26 @@ struct Cli {
|
||||
#[arg(long = "max-duration", default_value_t = 300)]
|
||||
max_duration: u64,
|
||||
|
||||
/// Daily inbound (client→server) limit per IP in bytes (0 = unlimited)
|
||||
#[arg(long = "ip-daily-in", default_value_t = 0)]
|
||||
ip_daily_in: u64,
|
||||
|
||||
/// Daily outbound (server→client) limit per IP in bytes (0 = unlimited)
|
||||
#[arg(long = "ip-daily-out", default_value_t = 0)]
|
||||
ip_daily_out: u64,
|
||||
|
||||
/// How often to check quotas during a test in seconds
|
||||
#[arg(long = "quota-check-interval", default_value_t = 10)]
|
||||
quota_check_interval: u64,
|
||||
|
||||
/// Web dashboard port (0 = disabled)
|
||||
#[arg(long = "web-port", default_value_t = 8080)]
|
||||
web_port: u16,
|
||||
|
||||
/// Shared password for public mode (all users use this password)
|
||||
#[arg(long = "shared-password")]
|
||||
shared_password: Option<String>,
|
||||
|
||||
/// Use EC-SRP5 authentication
|
||||
#[arg(long = "ecsrp5")]
|
||||
ecsrp5: bool,
|
||||
@@ -242,6 +259,8 @@ async fn main() -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
// Initialize quota manager
|
||||
// Directional IP quotas default to 0 (unlimited) unless the combined
|
||||
// quota is set, in which case the same value is used for each direction.
|
||||
let quota_mgr = quota::QuotaManager::new(
|
||||
db.clone(),
|
||||
cli.daily_quota,
|
||||
@@ -250,6 +269,12 @@ async fn main() -> anyhow::Result<()> {
|
||||
cli.ip_daily,
|
||||
cli.ip_weekly,
|
||||
cli.ip_monthly,
|
||||
cli.ip_daily, // ip_daily_inbound
|
||||
cli.ip_daily, // ip_daily_outbound
|
||||
cli.ip_weekly, // ip_weekly_inbound
|
||||
cli.ip_weekly, // ip_weekly_outbound
|
||||
cli.ip_monthly, // ip_monthly_inbound
|
||||
cli.ip_monthly, // ip_monthly_outbound
|
||||
cli.max_conn_per_ip,
|
||||
cli.max_duration,
|
||||
);
|
||||
@@ -268,6 +293,22 @@ async fn main() -> anyhow::Result<()> {
|
||||
cli.max_conn_per_ip, cli.max_duration,
|
||||
);
|
||||
|
||||
// Start web dashboard if port > 0
|
||||
if cli.web_port > 0 {
|
||||
let web_db = db.clone();
|
||||
let web_port = cli.web_port;
|
||||
tokio::spawn(async move {
|
||||
tracing::info!("Web dashboard starting on http://0.0.0.0:{}", web_port);
|
||||
let app = web::create_router(web_db);
|
||||
let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", web_port))
|
||||
.await
|
||||
.expect("Failed to bind web dashboard port");
|
||||
if let Err(e) = axum::serve(listener, app).await {
|
||||
tracing::error!("Web dashboard error: {}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
tracing::info!("btest-server-pro starting on port {}", cli.port);
|
||||
|
||||
let v4 = if cli.listen_addr.eq_ignore_ascii_case("none") { None } else { Some(cli.listen_addr) };
|
||||
|
||||
Reference in New Issue
Block a user