v1.0.0 — Hexagonal architecture rewrite
All checks were successful
CI / ci (push) Successful in 7m16s
All checks were successful
CI / ci (push) Successful in 7m16s
Restructure the monolithic 252-line main.rs into a 10-crate workspace with clean hexagonal architecture, swappable adapters, and a production- ready deployment pipeline. Backend architecture: - domain: Canvas, Color/Position/PixelUpdate value objects, port traits (CanvasStore, CanvasPersistence, EventBroadcaster), BroadcastEvent - application: use cases (place_pixel, get_state, save/restore snapshot, connect/disconnect), AppState with Arc snapshot cache - config: AppConfig structs + ConfigSource trait - api-types: shared DTOs, event name constants - adapters: config-env, canvas-file, http-axum (rust-embed), socketio, websocket — all behind port traits, swappable via feature flags - server: composition root with graceful shutdown (SIGTERM/SIGINT) Frontend: - Transport abstraction: Socket.IO and native WebSocket via VITE_TRANSPORT - Canvas zoom/pan with mouse wheel, pinch-to-zoom, and +/- buttons - ImageData rendering (~50x faster than fillRect loop) - Touch support, responsive CSS scaling, mobile-friendly layout - OG/Twitter Card meta tags for rich link previews Production: - Docker: musl static build on scratch — 2.73MB image - CI workflows for Gitea and GitHub Actions (fmt, clippy, test, Docker push) - deploy.sh for private registry - 39 unit tests across domain and application - Zero unwraps, zero unsafe, graceful error handling with tracing - Periodic canvas snapshots with rotation, restored on startup - All config via environment variables with typed defaults
This commit is contained in:
61
crates/config/src/lib.rs
Normal file
61
crates/config/src/lib.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ConfigError {
|
||||
#[error("invalid value for '{field}': {reason}")]
|
||||
InvalidValue { field: String, reason: String },
|
||||
|
||||
#[error("failed to load config: {0}")]
|
||||
LoadFailed(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AppConfig {
|
||||
pub server: ServerConfig,
|
||||
pub canvas: CanvasConfig,
|
||||
pub cooldown: CooldownConfig,
|
||||
pub rate_limit: RateLimitConfig,
|
||||
pub broadcast: BroadcastConfig,
|
||||
pub snapshot: SnapshotConfig,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ServerConfig {
|
||||
pub address: String,
|
||||
pub port: u16,
|
||||
pub enable_cors: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CanvasConfig {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CooldownConfig {
|
||||
pub placement_secs: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RateLimitConfig {
|
||||
pub burst_size: u32,
|
||||
pub per_second: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BroadcastConfig {
|
||||
pub channel_capacity: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SnapshotConfig {
|
||||
pub enabled: bool,
|
||||
pub interval_secs: u64,
|
||||
pub max_snapshots: usize,
|
||||
pub directory: String,
|
||||
}
|
||||
|
||||
pub trait ConfigSource {
|
||||
fn load(&self) -> Result<AppConfig, ConfigError>;
|
||||
}
|
||||
Reference in New Issue
Block a user