use std::{net::SocketAddr, sync::Arc}; use anyhow::Context; use axum::{Router, routing::any}; use dotenvy::dotenv; use tower_http::compression::CompressionLayer; use tracing::{error, info, level_filters::LevelFilter}; use tracing_subscriber::{EnvFilter, fmt, prelude::*}; mod crypto; mod proxy; #[derive(Clone)] pub struct AppState { pub client: reqwest::Client, pub target_url: reqwest::Url, pub key: String, pub iv: String, } const DEFAULT_TARGET_URL: &str = "https://cloud.linspirer.com:883"; const DEFAULT_HOST: &str = "0.0.0.0"; const DEFAULT_PORT: &str = "8080"; #[tokio::main] async fn main() -> anyhow::Result<()> { // Load environment variables from .env file dotenv().ok(); // Set up logging tracing_subscriber::registry() .with(fmt::layer()) .with( EnvFilter::builder() .with_default_directive(LevelFilter::INFO.into()) .from_env_lossy(), ) .init(); // Load configuration from environment let key = std::env::var("LINSPIRER_KEY").context("LINSPIRER_KEY must be set")?; let iv = std::env::var("LINSPIRER_IV").context("LINSPIRER_IV must be set")?; let target_url_str = std::env::var("LINSPIRER_TARGET_URL"); let target_url_str = target_url_str.as_deref().unwrap_or(DEFAULT_TARGET_URL); let target_url = reqwest::Url::parse(target_url_str)?; let host_str = std::env::var("LINSPIRER_HOST"); let host = host_str.as_deref().unwrap_or(DEFAULT_HOST); let port_str = std::env::var("LINSPIRER_PORT"); let port = port_str.as_deref().unwrap_or(DEFAULT_PORT); let addr_str = format!("{}:{}", host, port); let addr: SocketAddr = addr_str .parse() .context(format!("Invalid address format: {}", addr_str))?; // Create a reqwest client that ignores SSL certificate verification let client = reqwest::Client::builder() .gzip(true) .deflate(true) .danger_accept_invalid_certs(true) .build()?; // Create shared state let state = Arc::new(AppState { client, target_url, key, iv, }); // Build our application with a single route let app = Router::new() .route("/{*path}", any(proxy::proxy_handler)) .layer(CompressionLayer::new().gzip(true)) .with_state(state); // Run the server info!("Proxy started on {} => {}", addr_str, target_url_str); let listener = tokio::net::TcpListener::bind(&addr).await?; if let Err(e) = axum::serve(listener, app.into_make_service()).await { error!("Server error: {}", e); } Ok(()) }