Add server config route

This commit is contained in:
2025-11-03 19:13:42 +01:00
parent 602edaae79
commit 830f47b61f
7 changed files with 141 additions and 7 deletions

View File

@@ -68,6 +68,17 @@ dependencies = [
"syn",
]
[[package]]
name = "actix-remote-ip"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7629b357d4705cf3f1e31f989f48ecd56027112f7d52dcf06dd96ee197065f8e"
dependencies = [
"actix-web",
"futures-util",
"log",
]
[[package]]
name = "actix-router"
version = "0.5.3"
@@ -1405,6 +1416,7 @@ checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
name = "matrixgw_backend"
version = "0.1.0"
dependencies = [
"actix-remote-ip",
"actix-session",
"actix-web",
"anyhow",

View File

@@ -13,4 +13,5 @@ serde = { version = "1.0.228", features = ["derive"] }
rust-s3 = { version = "0.37.0", features = ["tokio"] }
tokio = { version = "1.48.0", features = ["full"] }
actix-web = "4.11.0"
actix-session = { version = "0.11.0", features = ["redis-session"] }
actix-session = { version = "0.11.0", features = ["redis-session"] }
actix-remote-ip = "0.1.0"

View File

@@ -18,6 +18,11 @@ pub struct AppConfig {
#[clap(short, long, env)]
pub proxy_ip: Option<String>,
/// Unsecure : for development, bypass authentication, using the account with the given
/// email address by default
#[clap(long, env)]
unsecure_auto_login_email: Option<String>,
/// Secret key, used to secure some resources. Must be randomly generated
#[clap(short = 'S', long, env, default_value = "")]
secret: String,
@@ -54,6 +59,10 @@ pub struct AppConfig {
)]
pub oidc_configuration_url: String,
/// OpenID provider name
#[arg(long, env, default_value = "3rd party provider")]
pub oidc_provider_name: String,
/// OpenID client ID
#[arg(long, env, default_value = "foo")]
pub oidc_client_id: String,
@@ -103,6 +112,14 @@ impl AppConfig {
&ARGS
}
/// Get auto login email (if not empty)
pub fn unsecure_auto_login_email(&self) -> Option<&str> {
match self.unsecure_auto_login_email.as_deref() {
None | Some("") => None,
s => s,
}
}
/// Get app secret
pub fn secret(&self) -> &str {
let mut secret = self.secret.as_str();
@@ -118,6 +135,11 @@ impl AppConfig {
secret
}
/// Check if auth is disabled
pub fn is_auth_disabled(&self) -> bool {
self.unsecure_auto_login_email().is_some()
}
/// Get Redis connection configuration
pub fn redis_connection_string(&self) -> String {
format!(
@@ -136,6 +158,7 @@ impl AppConfig {
client_id: self.oidc_client_id.as_str(),
client_secret: self.oidc_client_secret.as_str(),
configuration_url: self.oidc_configuration_url.as_str(),
name: self.oidc_provider_name.as_str(),
redirect_url: self
.oidc_redirect_url
.replace("APP_ORIGIN", &self.website_origin),
@@ -169,6 +192,7 @@ impl AppConfig {
#[derive(Debug, Clone, serde::Serialize)]
pub struct OIDCProvider<'a> {
pub name: &'a str,
pub client_id: &'a str,
pub client_secret: &'a str,
pub configuration_url: &'a str,

View File

@@ -0,0 +1 @@
pub mod server_controller;

View File

@@ -0,0 +1,74 @@
use crate::app_config::AppConfig;
use actix_web::HttpResponse;
/// Serve robots.txt (disallow ranking)
pub async fn robots_txt() -> HttpResponse {
HttpResponse::Ok()
.content_type("text/plain")
.body("User-agent: *\nDisallow: /\n")
}
#[derive(serde::Serialize)]
pub struct LenConstraints {
min: usize,
max: usize,
}
impl LenConstraints {
pub fn new(min: usize, max: usize) -> Self {
Self { min, max }
}
pub fn not_empty(max: usize) -> Self {
Self { min: 1, max }
}
pub fn max_only(max: usize) -> Self {
Self { min: 0, max }
}
pub fn check_str(&self, s: &str) -> bool {
s.len() >= self.min && s.len() <= self.max
}
pub fn check_u32(&self, v: u32) -> bool {
v >= self.min as u32 && v <= self.max as u32
}
}
#[derive(serde::Serialize)]
pub struct ServerConstraints {
pub token_name: LenConstraints,
pub token_ip_net: LenConstraints,
pub token_max_inactivity: LenConstraints,
}
impl Default for ServerConstraints {
fn default() -> Self {
Self {
token_name: LenConstraints::new(5, 255),
token_ip_net: LenConstraints::max_only(44),
token_max_inactivity: LenConstraints::new(3600, 3600 * 24 * 365),
}
}
}
#[derive(serde::Serialize)]
struct ServerConfig {
auth_disabled: bool,
oidc_provider_name: &'static str,
constraints: ServerConstraints,
}
impl Default for ServerConfig {
fn default() -> Self {
Self {
auth_disabled: AppConfig::get().is_auth_disabled(),
oidc_provider_name: AppConfig::get().openid_provider().name,
constraints: Default::default(),
}
}
}
/// Get server static configuration
pub async fn config() -> HttpResponse {
HttpResponse::Ok().json(ServerConfig::default())
}

View File

@@ -1 +1,2 @@
pub mod app_config;
pub mod controllers;

View File

@@ -1,7 +1,11 @@
use actix_remote_ip::RemoteIPConfig;
use actix_session::SessionMiddleware;
use actix_session::config::SessionLifecycle;
use actix_session::storage::RedisSessionStore;
use actix_web::cookie::Key;
use actix_web::{App, HttpServer};
use actix_web::{App, HttpServer, web};
use matrixgw_backend::app_config::AppConfig;
use matrixgw_backend::controllers::server_controller;
#[tokio::main]
async fn main() -> std::io::Result<()> {
@@ -19,9 +23,26 @@ async fn main() -> std::io::Result<()> {
AppConfig::get().website_origin
);
HttpServer::new(move || App::new())
.workers(4)
.bind(&AppConfig::get().listen_address)?
.run()
.await
HttpServer::new(move || {
App::new()
.wrap(
SessionMiddleware::builder(redis_store.clone(), secret_key.clone())
.cookie_name("matrixgw-session".to_string())
.session_lifecycle(SessionLifecycle::BrowserSession(Default::default()))
.build(),
)
.app_data(web::Data::new(RemoteIPConfig {
proxy: AppConfig::get().proxy_ip.clone(),
}))
// Server controller
.route("/robots.txt", web::get().to(server_controller::robots_txt))
.route(
"/api/server/config",
web::get().to(server_controller::config),
)
})
.workers(4)
.bind(&AppConfig::get().listen_address)?
.run()
.await
}