scaffold: Backend core — Cargo.toml, main.rs, config, models, panel adapter

Axum server entry point, AppConfig, AppState, ApiError, all model
structs (auth, license, server, wipe), and the PanelAdapter trait
that abstracts AMP/Pterodactyl/companion connections.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Vantz Stockwell
2026-02-14 21:41:58 -05:00
parent 26cbeb5d4c
commit 5c11050eca
11 changed files with 785 additions and 0 deletions

79
backend/src/config/mod.rs Normal file
View File

@@ -0,0 +1,79 @@
use anyhow::{Context, Result};
/// Application configuration loaded from environment variables
#[derive(Clone)]
pub struct AppConfig {
// Database
pub database_url: String,
pub database_max_connections: u32,
// NATS
pub nats_url: String,
// Auth
pub jwt_secret: String,
pub jwt_access_expiry_seconds: i64,
pub jwt_refresh_expiry_seconds: i64,
// Encryption
pub encryption_key: String,
// Cloudflare
pub cloudflare_api_token: String,
pub cloudflare_zone_id: String,
pub base_domain: String,
// Steam
pub steam_api_key: String,
// Email (SMTP)
pub smtp_host: String,
pub smtp_port: u16,
pub smtp_username: String,
pub smtp_password: String,
pub smtp_from: String,
// Server
pub api_port: u16,
pub frontend_url: String,
}
impl AppConfig {
pub fn from_env() -> Result<Self> {
Ok(Self {
database_url: env_required("DATABASE_URL")?,
database_max_connections: env_or_default("DATABASE_MAX_CONNECTIONS", 20),
nats_url: env_or_default_str("NATS_URL", "nats://localhost:4222"),
jwt_secret: env_required("JWT_SECRET")?,
jwt_access_expiry_seconds: env_or_default("JWT_ACCESS_EXPIRY_SECONDS", 900),
jwt_refresh_expiry_seconds: env_or_default("JWT_REFRESH_EXPIRY_SECONDS", 604800),
encryption_key: env_required("ENCRYPTION_KEY")?,
cloudflare_api_token: env_or_default_str("CLOUDFLARE_API_TOKEN", ""),
cloudflare_zone_id: env_or_default_str("CLOUDFLARE_ZONE_ID", ""),
base_domain: env_or_default_str("BASE_DOMAIN", "corrosionmgmt.com"),
steam_api_key: env_or_default_str("STEAM_API_KEY", ""),
smtp_host: env_or_default_str("SMTP_HOST", "localhost"),
smtp_port: env_or_default("SMTP_PORT", 587),
smtp_username: env_or_default_str("SMTP_USERNAME", ""),
smtp_password: env_or_default_str("SMTP_PASSWORD", ""),
smtp_from: env_or_default_str("SMTP_FROM", "noreply@corrosionmgmt.com"),
api_port: env_or_default("API_PORT", 3000),
frontend_url: env_or_default_str("FRONTEND_URL", "http://localhost:5174"),
})
}
}
fn env_required(key: &str) -> Result<String> {
std::env::var(key).with_context(|| format!("Missing required env var: {key}"))
}
fn env_or_default<T: std::str::FromStr>(key: &str, default: T) -> T {
std::env::var(key)
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(default)
}
fn env_or_default_str(key: &str, default: &str) -> String {
std::env::var(key).unwrap_or_else(|_| default.to_string())
}