# CHANGELOG — Corrosion Admin Panel All notable changes to this project will be documented in this file. ## [Unreleased] ### Fixed (Safe Formatting Utilities — 2026-02-15) **Frontend:** - `AnalyticsView.vue` — Replaced unsafe `.toFixed()` calls with `safeFixed()` for avg_players and uptime_percentage (2 occurrences) - `WipeAnalyticsView.vue` — Replaced unsafe `.toFixed()` calls with `safeFixed()` for all metrics properties: success_rate_percent, population_curve stats, wipe durations, and CSV export (5 occurrences) - `PlayerRetentionView.vue` — Replaced unsafe `.toFixed()` calls with `safeFixed()` for retention percentages and session durations (5 occurrences in template + 1 in tooltip formatter) - `MapAnalyticsView.vue` — Replaced unsafe `.toFixed()` calls with `safeFixed()` for rotation effectiveness, map performance metrics, and table display (6 occurrences) **Purpose:** Prevents runtime errors from calling `.toFixed()` on null/undefined values in analytics views. Uses the safe formatting utilities from `@/utils/formatters.ts` with optional chaining for all numeric display operations. ### Fixed (NestJS Entity Alignment — 2026-02-15) **Backend (NestJS):** - `NotificationsConfig` entity — Renamed `email_alerts_enabled` → `email_enabled`, added `email_address` column - `NotificationsConfig` entity — Renamed notification columns to match service expectations: `notify_on_start`, `notify_on_stop`, `notify_on_crash`, `notify_on_wipe_start`, `notify_on_wipe_complete`, `notify_on_wipe_failure`, `notify_on_player_threshold`, `player_threshold` - `ScheduledTask` entity — Renamed `is_active` → `is_enabled`, added `last_run` column - `TeamMember` entity — Renamed `accepted_at` → `joined_at` to match service expectations - `Role` entity — Added `description` column - `JwtStrategy` — Updated to reference `joined_at` instead of `accepted_at` - Resolved all 12 TypeScript compilation errors caused by entity/service column mismatches ### Added (Frontend Gap Closure — 2026-02-15) **New Views:** - `SchedulesView.vue` — Scheduled task management with CRUD operations for server automation (restart, announcement, command, plugin reload tasks) - `MigrationView.vue` — Data export/import interface with export history and file upload for server migration - `ChangelogView.vue` — Paginated platform changelog feed with category badges and version display - `ForgotPasswordView.vue` — Password reset flow with email submission and success state - `AlertsView.vue` — Alert configuration dashboard with threshold sliders, notification channel toggles, and alert history table **Component Updates:** - `ErrorBoundary.vue` — Global error handler component with retry functionality - `DashboardLayout.vue` — Mobile responsive sidebar with hamburger menu, permission-based nav visibility, and new nav items (Schedules, Alerts, Changelog) - `ServerInfoView.vue` — Complete rewrite for public server info page with header image, MOTD, wipe schedule, mods list, and Discord integration **Store & API Integration:** - `plugins.ts` — Implemented all stubbed methods with real API calls (fetchPlugins, installPlugin, uninstallPlugin, reloadPlugin, updatePluginConfig, searchPlugins) - `useApi.ts` — Token refresh interceptor with automatic retry on 401, prevents infinite refresh loops - `auth.ts` — Added `hasPermission()` helper with basic permission checking **Router:** - Added routes: `/schedules`, `/migration`, `/changelog`, `/alerts`, `/forgot-password` - Added catch-all route redirecting to home - All new routes under authenticated dashboard layout **App Structure:** - Wrapped root `` with `ErrorBoundary` for global error handling **Purpose:** Closes frontend implementation gaps identified during Phase 4. Implements critical missing views (scheduled tasks, alerts, migration tools), hardens auth flow with token refresh, adds permission-based UI visibility, and improves mobile UX with responsive sidebar. ### Added (NestJS Backend — Core Modules) **Auth Module (`modules/auth/`):** - Complete authentication system with JWT and refresh tokens - DTOs: `LoginDto`, `RegisterDto`, `RefreshTokenDto`, `VerifyTotpDto`, `UpdateProfileDto`, `ForgotPasswordDto`, `ResetPasswordDto` - `JwtStrategy` — Passport strategy with user lookup, license resolution, and role permissions injection - `AuthService` — Full auth lifecycle: - `register()` — User creation with auto-generated license key (CORR-XXXX-XXXX-XXXX format) - `login()` — Credential validation, TOTP verification, token generation - `refresh()` — Access token refresh from valid refresh token - `setupTotp()` — TOTP secret generation with QR code (otpauth + qrcode libraries) - `verifyTotp()` — TOTP code validation and 2FA enablement - `getProfile()` / `updateProfile()` — User profile management - `forgotPassword()` / `resetPassword()` — Password recovery stubs (SMTP integration pending) - `AuthController` — 9 REST endpoints: - `POST /auth/login` — Email/password login with optional TOTP - `POST /auth/register` — New user registration with auto-license creation - `POST /auth/refresh` — Token refresh - `POST /auth/2fa/setup` — Generate TOTP QR code (authenticated) - `POST /auth/2fa/verify` — Enable 2FA (authenticated) - `GET /auth/me` — Current user profile (authenticated) - `PUT /auth/profile` — Update profile (authenticated) - `POST /auth/forgot-password` — Request password reset (public) - `POST /auth/reset-password` — Reset with token (public) - Password hashing via argon2, TOTP via otpauth with 30-second window validation - License key auto-generation on registration (random hex parts) - JWT payload includes: sub (user ID), email, username, is_super_admin, license_id, permissions - Strategy enriches JWT with license context (owner or team member lookup) and role permissions **Users Module (`modules/users/`):** - Simple CRUD wrapper around User repository - `UsersService` — `findById()`, `findByEmail()`, `findAll()` with password_hash excluded from select - `UsersController` — Admin-only endpoints: - `GET /users` — List all users (requires `users.view` permission) - `GET /users/:id` — Get user by ID (requires `users.view` permission) - Password fields excluded from all query results **Licenses Module (`modules/licenses/`):** - License management with owner authorization - DTO: `ValidateKeyDto` — License key validation input - `LicensesService`: - `findById()` — License lookup with owner/super admin authorization check - `findByKey()` — Key-based lookup - `findByOwner()` — All licenses owned by user - `create()` — New license generation with CORR-XXXX-XXXX-XXXX format - `validateKey()` — Public key validation returning status and metadata - `LicensesController`: - `GET /licenses/:id` — Get license (owner or super admin only) - `POST /licenses/validate-key` — Public key validation endpoint - License key format: `CORR-{4-hex}-{4-hex}-{4-hex}` (e.g., CORR-A1B2-C3D4-E5F6) - Ownership enforced: non-super-admin users can only access their own licenses **Patterns Applied:** - All DTOs use class-validator decorators (@IsEmail, @IsString, @MinLength, etc.) - All controllers use @ApiTags and @ApiBearerAuth for Swagger documentation - All routes use @ApiOperation for endpoint descriptions - Custom decorators: @Public(), @CurrentUser(), @CurrentTenant(), @RequirePermission() - Entity imports from `../../entities/` directory - ConfigService for environment variables (JWT_SECRET, JWT_ACCESS_EXPIRY_SECONDS, JWT_REFRESH_EXPIRY_SECONDS) - Multi-tenant isolation: License lookup respects ownership unless super admin - JwtStrategy enriches request.user with license_id and permissions for downstream guards **Security:** - Argon2 password hashing (not bcrypt — more resistant to GPU attacks) - TOTP 6-digit codes with ±1 period window validation - Refresh tokens with separate expiry (default 7 days vs 1 hour access token) - Password fields never returned in API responses - License access requires ownership or super admin flag **Status:** Core auth, users, and licenses modules operational. Registration creates user + license atomically. Login returns JWT with license context. TOTP 2FA flow complete. Password reset stubbed pending SMTP integration. All endpoints documented via Swagger. ### Added (Phase 4 — Module Licensing Backend) **Backend Infrastructure:** - Migration `009_module_licensing.sql` — Module marketplace database schema: - `modules` table — Registry of available modules (slug, name, description, category, price, features, version, plugin URL) - `module_purchases` table — License-module ownership tracking with transaction logging - `module_installations` table — Deployment status tracking (pending, installing, installed, failed) - Seed data: Loot Manager module ($9.99) with features array - `backend/src/models/modules.rs` — Domain models: - `Module` struct with rust_decimal pricing support - `ModuleWithOwnership` — Catalog display with is_purchased flag - `ModulePurchase`, `ModuleInstallation` — Purchase and deployment records - `PurchasedModule` — Combined view for user's module library - `backend/src/db/modules.rs` — Data access layer (11 query functions): - `get_module_catalog()` — All available modules - `get_catalog_with_ownership(license_id)` — Annotated catalog with purchase status - `get_purchased_modules(license_id)` — User's module library with installation status - `is_module_purchased(license_id, module_id)` — Ownership validation - `record_module_purchase()` — Transaction logging with PayPal ID support - `get_module_installation_status()` / `update_installation_status()` — Deployment tracking - `get_module_by_id()` / `get_module_by_slug()` — Module lookup - `backend/src/api/modules.rs` — REST endpoints with auth middleware: - `GET /api/modules/catalog` — Returns modules with is_purchased flag for current license - `GET /api/modules/my-modules` — Purchased modules with installation details - `POST /api/modules/purchase` — Records purchase (stub transaction for Phase 4 MVP — payment integration deferred to XO's direct touch) - `POST /api/modules/install` — Triggers module installation via ModuleInstaller service - `GET /api/modules/:module_id/installation-status` — Real-time deployment status polling - Router integration in `main.rs` at `/api/modules` with JWT auth requirement - `Cargo.toml` dependency: `rust_decimal` for DECIMAL field support **Multi-Tenancy Enforcement:** - All queries scoped by `license_id` from JWT claims - Foreign key constraints enforce license-module binding - Purchase validation prevents cross-tenant access - Installation status isolated per license **Payment Integration Strategy:** - Purchase endpoint stubs transaction with "STUB_TRANSACTION" ID - PayPal integration deferred to XO's direct implementation - `transaction_id` and `amount_paid` fields ready for real gateway **Status:** Module licensing backend operational. Catalog queryable, purchases recordable, ownership enforceable, installation status trackable. Payment gateway integration pending. ### Added (Phase 4 — Loot Manager Plugin Skeleton) **Plugin Skeleton:** - `plugin/modules/LootManager.cs` — First paid module (skeleton implementation) - Configuration: Loot profiles with container multipliers + custom loot tables - Hooks: `OnLootSpawn()` and `OnEntitySpawned()` for container loot modification - Profile switching: Multiplier-based (2x, 5x, 10x) and full custom loot table support - Container types: Normal crate, elite, mine, barrel, food, military, default fallback - Chat command: `/loot.profile [name]` — In-game profile switching for admins - Item support: Shortname, min/max amount, spawn chance, skin ID - `plugin/modules/README.md` — Module documentation - Price: $9.99 - Features: Visual loot table editor (dashboard integration TBD), profile switching, skin support - Installation: Auto-deploy via module store (implementation TBD) **Database:** - Migration 009 already includes Loot Manager seed data in `modules` table **Status:** Skeleton complete. Hooks functional. Profile switching works via chat command. Dashboard UI integration and deployment automation pending future iteration. ### Added (Phase 4 — Module Auto-Installation Pipeline) **Backend Service:** - `backend/src/services/module_installer.rs` — Automated module deployment orchestrator: - `ModuleInstaller::install_module(license_id, module_id)` — Main entry point - Purchase verification against `module_purchases` table - Module metadata fetch (plugin_file_url, slug) - Server connection detection (AMP, Pterodactyl, bare metal) - Multi-adapter dispatch with automatic failover - Installation status tracking (pending → installing → installed/failed) - Background task spawning for async installation - Panel adapter integration: - `install_via_amp()` — Downloads plugin, uploads to `oxide/plugins/`, executes `oxide.reload *` - `install_via_pterodactyl()` — Same flow using Pterodactyl client API - `install_via_companion()` — Publishes NATS command to bare metal agent - HTTP client integration: `reqwest` for plugin file download from CDN - Encryption support: Decrypts panel API keys using `services::encryption::decrypt()` - Error handling: Comprehensive context wrapping with installation failure logging **NATS Integration:** - New subject pattern: `corrosion.{license_id}.cmd.module.install` - Request/reply timeout: 60 seconds for companion agent response - Expected payload: ```json { "module_id": "loot-manager", "download_url": "https://cdn.corrosionmgmt.com/modules/LootManager.cs", "filename": "LootManager.cs", "target_path": "oxide/plugins/" } ``` - Expected response: ```json { "module_id": "loot-manager", "success": true|false, "error": "optional error message" } ``` - Subject pattern already covered by existing `corrosion.*.cmd.>` wildcard in STREAM_AGENT_COMMANDS **API Updates:** - `backend/src/api/modules.rs`: - Updated `POST /api/modules/install` — Replaced stub with real ModuleInstaller invocation - Spawn background task for async installation - Return immediately with "installing" status - `GET /api/modules/:module_id/installation-status` — Already existed, now returns real data from `module_installations` table - ModuleInstaller instantiation with encryption key from AppConfig **Documentation:** - `docs/COMPANION_AGENT_MODULE_INSTALL.md` — Companion agent NATS contract specification: - Subject patterns and payload schemas - Expected agent behavior (download, install, reload, respond) - Error handling requirements - Example pseudocode implementation (Go) - Testing procedures and failure scenarios **Dependencies:** - `Cargo.toml`: Added `rust_decimal` feature to `sqlx` for DECIMAL field support **Status:** Backend pipeline fully operational. Modules install automatically to AMP/Pterodactyl servers. Companion agent NATS contract documented. Companion agent implementation (Go) pending future iteration. ### Added (Phase 4 — Module Store Frontend) **Frontend:** - Complete `ModuleStoreView.vue` implementation — Customer-facing module marketplace with: - **Catalog Tab:** - Module grid with preview images, prices, category badges, purchase status - Search functionality (name/description) - Category filter (Loot, Events, Economy, Kits, Admin, PVP, PVE, Building) - Hover animations and professional card layout - **My Modules Tab:** - Purchased modules with installation status tracking - "Install" button for purchased-but-not-installed modules - Empty state prompting catalog browsing - **Module Detail Modal:** - Full-screen module preview with screenshots gallery - Expanded description and complete features list - Version display and pricing details - Direct purchase/install CTA from modal - **Purchase Confirmation Modal:** - Shows module name, license binding, total price - Error handling with inline error display - Non-refundable disclaimer - Processing state during purchase flow - **Payment Flow:** - Instant purchase confirmation (MVP) - External payment URL redirect support (Stripe/PayPal) - State refresh after successful purchase - TypeScript types (`types/index.ts`): - `Module` interface with full marketplace metadata (id, slug, name, description, price, category, images, features, version, purchase/install status) - `PurchaseRequest` interface for API integration - **API Integration:** - `GET /api/modules/catalog` — Browse all available modules - `GET /api/modules/my-modules` — Fetch purchased modules for current license - `POST /api/modules/purchase` — Initiate module purchase (returns payment URL or instant confirmation) - `POST /api/modules/install` — Trigger deployment to game server **Design Details:** - Professional marketplace UI using existing Tailwind patterns - Color-coded category badges (8 categories supported) - Preview image with hover scale effect - "Purchased" badge overlay on owned modules - Three-state purchase flow: Not Purchased → Purchased → Installed - Mobile-responsive grid (1/2/3 columns) - Empty states for zero results and zero purchases - Price display prominently in catalog cards and modals **Purpose:** Enables server admins to browse, preview, purchase, and install premium gameplay modules (Loot systems, Events, Economy plugins, Kits) directly from the dashboard. Customers pay real money here — UI polish critical. ### Added (Phase 2 — Alerting System) **Backend:** - Migration 008: Alert configuration and history tables - `alert_config` table with threshold settings per license (population drop %, FPS threshold) - `alert_history` table logging all triggered alerts with metadata - Default alert config created for all existing licenses - Alert service (`services/alerting.rs`): - `check_population_anomaly()` — Detects player count drops exceeding threshold - `check_fps_degradation()` — Monitors server performance degradation - Spam prevention (30-minute duplicate suppression) - Multi-channel notifications (Discord + Pushbullet + Email) - Severity levels: Info, Warning, Critical - Alert database layer (`db/alerts.rs`): - `get_alert_config()` / `update_alert_config()` — Threshold configuration - `insert_alert()` / `mark_alert_notified()` — Alert history tracking - `check_recent_alert()` — Duplicate detection - `cleanup_old_alerts()` — 90-day retention cleanup - Updated `db/notifications.rs` — Notification config retrieval with webhook/API key support **Alert Types:** - Population Drop — Triggers when player count drops >X% in 1 hour - FPS Degradation — Triggers when FPS falls below configurable threshold - Server Crash — Critical alert for auto-recovery failures - Wipe Failed — Alert when wipe execution fails **Purpose:** Proactive monitoring for server health issues. Alerts server admins via Discord/Pushbullet when anomalies detected (population crashes, performance degradation). Configurable thresholds per license. ### Added (Phase 2 — Wipe Performance Analytics) **Backend:** - `backend/src/db/wipes.rs` — Comprehensive wipe analytics query layer: - `get_wipe_success_rate()` — Success vs failure rate over time range - `get_average_wipe_duration()` — Average execution time for successful wipes - `get_wipe_to_peak_population()` — Hours from wipe completion to peak player count (24h window) - `get_population_curve_by_cycle()` — Day 1 vs Day 2 vs Day 3 average player counts post-wipe - `get_optimal_wipe_timing()` — Recommends best day of week + hour based on historical peak populations - `get_wipe_analytics_entries()` — Detailed per-wipe records for charting (duration, peak pop, success) - All queries use hourly aggregates (`server_stats_hourly`) with 90-day retention - `backend/src/api/analytics.rs` — Wipe performance endpoint: - `GET /api/analytics/wipes/performance?range=90d` — Returns full wipe performance metrics - Supports range params: `6d`, `12d`, `90d`, `all` (converted to wipe count estimates) - Response includes: success rate, avg duration, population curve, optimal timing, individual wipe entries **Frontend:** - `WipeAnalyticsView.vue` — Complete wipe performance dashboard: - **ECharts Visualizations:** - Wipe success timeline (scatter plot: green = success, red = failed) - Population curve bar chart (Day 1/Day 2/Day 3 average players post-wipe) - Wipe duration trend (line chart showing execution time evolution) - **Insight Cards:** - Success rate percentage with total wipe count - Average wipe duration (formatted as minutes:seconds) - Peak population day identifier - Optimal wipe timing recommendation (day + hour) - **Actionable Recommendations Banner:** - Optimal wipe day/hour based on post-wipe player peaks - Weekly vs bi-weekly wipe suggestion (if Day 1 >> Day 2 population) - Duration optimization alerts (if avg > 10 minutes) - Rollback protection warnings (if failures detected) - Time range selector: Last 6 wipes / Last 12 wipes / All time - CSV export functionality - Added route `/wipes/analytics` to router - TypeScript interfaces: `WipePerformanceMetrics`, `WipeAnalyticsEntry`, `PopulationCurve` **Purpose:** Answers critical questions: "How long do wipes take? When do players peak post-wipe? What's my success rate? When should I schedule wipes for max population?" Enables data-driven wipe timing optimization and operational insights. ### Added (Phase 3 — Public Status Page) **Backend:** - Migration 007: Added `status_page_description` TEXT column to `public_site_config` - Public API models (`models/public.rs`): - `PublicServerStatus` — Server status with live stats for public display - `PlatformHealth` — Platform-wide health metrics (total servers, online count, total players, uptime) - `StatusPageResponse` — Complete status page data structure - `PublicSiteConfig` — Full public site configuration model - Public database queries (`db/public.rs`): - `get_public_servers()` — Retrieves all opted-in servers with current stats, uptime percentages (24h/7d/30d), wipe schedules - `get_platform_health()` — Calculates platform-wide aggregate metrics - `calculate_uptime_percentage()` — Uptime calculation from hourly stats - `format_cron_expression()` — Human-readable wipe schedule formatting - `get_public_site_config()` / `create_public_site_config()` / `update_public_site_config()` — Config management - Public API endpoint (`api/public.rs`): - `GET /api/public/status` — Public status page data (no auth required) - Settings API (`api/settings.rs`): - `GET /api/settings/public-site` — Fetch public site config (auth required) - `PUT /api/settings/public-site` — Update status page opt-in and description (auth required) **Frontend:** - `StatusPageView.vue` — Complete public status page with: - Platform health header (total servers, online now, total players, platform uptime) - Server grid with status indicators (green/yellow/red), player counts, uptime badges (24h/7d/30d) - Wipe schedule display with countdown timers - Server search/filter functionality - Auto-refresh every 10 seconds via polling - Mobile-responsive grid layout - "Powered by Corrosion" footer with panel link - Settings dashboard integration (`SettingsView.vue`): - New "Public Status" tab with toggle for `show_on_status_page` - Text area for `status_page_description` - Save endpoint integration **Infrastructure:** - nginx already configured for `status.corrosionmgmt.com` routing - Router already configured with `/status` route on both panel and marketing domains **Purpose:** Public-facing marketing page showcasing all Corrosion servers. Drives platform visibility and attracts new customers ("I want this for my server too"). ### Added (Phase 2.2 — Player Retention Analytics) **Backend:** - Migration `004_player_sessions.sql` — Player session tracking table with indexes for retention queries - `backend/src/db/player_sessions.rs` — Complete player session tracking and retention analysis: - `track_player_join()` / `track_player_leave()` — Record individual player sessions - `calculate_retention_after_wipe()` — Calculate 24h/48h/72h return rates per wipe - `get_unique_player_count()` / `get_avg_session_duration()` — Session metrics - `get_new_vs_returning_ratio()` — New vs returning player analysis - `get_recent_wipe_retention_metrics()` — Multi-wipe retention trends - `cleanup_old_player_sessions()` — 90-day retention cleanup - `backend/src/api/plugin.rs` — Plugin event endpoints: - `POST /api/plugin/player-event` — Track player join/leave events - `POST /api/plugin/checkin` — Plugin registration on server start - Extended `backend/src/api/analytics.rs` with retention endpoints: - `GET /api/analytics/retention?wipe_count=6` — Multi-wipe retention metrics - `GET /api/analytics/retention/export` — CSV export of retention data **Frontend:** - `PlayerRetentionView.vue` — Complete retention analytics dashboard: - ECharts retention curve (24h/48h/72h lines across multiple wipes) - Summary cards: unique players, avg session duration, new vs returning ratio - Wipe selector (last 3/6/10/20 wipes) - Detailed wipe table with retention percentages - CSV export functionality - Added route `/retention` to router - TypeScript interfaces: `WipeRetentionMetric`, `SessionSummary`, `RetentionResponse` **Plugin:** - Updated `CorrosionCompanion.cs` to track player events via `/api/plugin/player-event` - Modified `OnPlayerConnected` / `OnPlayerDisconnected` hooks with license_key authentication **Purpose:** Answers critical question: "What percentage of players return 24h/48h/72h after a wipe?" Enables data-driven wipe timing optimization and player retention analysis. ### Added (Phase 2.2 — Map Analytics System) **Backend:** - Migration 005: Added `map_id` FK to `server_stats` and `wipe_history` for map effectiveness tracking - Stats consumer now captures `current_map_id` from `server_config` when persisting stats - Map analytics database queries (`db/maps.rs`): - `get_map_analytics()` — Returns performance metrics per map (avg/peak players, times used, effectiveness score) - `get_map_population_trends()` — Player count trends per map over wipe cycles - Effectiveness scoring algorithm: (avg_players / peak_players) * 100 - Analytics API endpoint (`api/analytics.rs`): - `GET /api/analytics/maps?range=90d` — Map performance summary with rotation effectiveness **Frontend:** - `MapAnalyticsView.vue` — Complete map effectiveness dashboard with: - Summary cards: Best performing map, rotation effectiveness %, total maps tracked - ECharts bar chart comparing avg vs peak players per map - Sortable performance table with effectiveness color coding (green ≥80%, yellow ≥60%, red <60%) - Actionable insights section recommending rotation improvements - CSV export functionality - Time range selector (30d/90d/all) - TypeScript types: `MapPerformanceMetrics`, `MapAnalyticsSummary` - Router: Added `/maps/analytics` route under admin dashboard **Purpose:** Answers "Which maps drive the most players? Is my rotation working?" Enables data-driven map selection for wipe day. ### Added (Phase 2 — Data Aggregation Pipeline) **Backend:** - Stats ingestion consumer service (`stats_consumer.rs`) subscribing to `corrosion.*.stats` NATS subject - Complete stats database queries (`db/stats.rs`) with support for: - Raw stats insertion and retrieval - Hourly aggregation queries - Analytics summary calculations (peak/avg players, uptime) - Data retention cleanup (7 days raw, 90 days hourly) - Hourly stats aggregation scheduler job (runs at :05 past every hour) - Daily cleanup scheduler job (runs at 03:00 UTC) - Analytics API endpoints (`api/analytics.rs`): - `GET /api/analytics/summary` — Peak/avg players, uptime percentage - `GET /api/analytics/timeseries` — Time-series data for charting (hourly/raw granularity) - `GET /api/analytics/export` — CSV export of server stats - Background service initialization in main.rs (stats consumer + scheduler) **Frontend:** - Analytics TypeScript types (`AnalyticsSummary`, `TimeseriesData`, `HourlyStats`) - Complete `AnalyticsView.vue` implementation with: - Real-time data fetching from analytics API - Apache ECharts integration for Player Count and Server Performance charts - Time range selector (24h/7d/30d) - CSV export functionality - Loading states and responsive layout **Infrastructure:** - Made `NatsBridge.jetstream` public for service consumer access ### Added (Sovereign Infrastructure Stack) **Services Deployed:** - **Gitea** (git.corrosionmgmt.com) — Self-hosted Git with Actions support - Container: `corrosion-gitea` on port 8090 (HTTP) and 8095 (SSH) - SQLite database (self-contained, persistent) - Replaces GitHub dependency for source control - Gitea Actions enabled for CI/CD - **SeaweedFS** (cdn.corrosionmgmt.com) — S3-compatible object storage and CDN - Container: `corrosion-cdn` with integrated Master/Volume/Filer/S3 - Filer UI at port 8091 (cdn.corrosionmgmt.com) - Master UI at port 8093 (admin.cdn.corrosionmgmt.com) - S3 API at port 8092 (internal access) - Purpose: Map hosting, plugin packages, companion binaries, backups - **Gitea Act Runner** (asgard build server) — CI/CD execution environment - Runs on Ryzen 9 7945HX (16C/32T, 64GB DDR5) - Docker-based job execution - Go 1.21+ and Rust toolchains available - Connects to public Gitea instance remotely **CI/CD Workflows:** - `test-runner.yml` — Runner capability validation (hostname, resources, toolchains) - `build-companion.yml` — Production companion agent build pipeline: - Triggers on version tags (v*.*.*) - Cross-compiles for Linux AMD64 and Windows AMD64 - Generates SHA256 checksums - Creates Gitea release with auto-generated installation instructions - Uploads binaries and checksums as release assets **Documentation:** - `infra/docker-compose.yml` — Infrastructure stack definition - `infra/README.md` — Deployment guide and architecture overview - `infra/NPM-CONFIG.md` — Nginx Proxy Manager configuration - `infra/ASGARD-RUNNER.md` — Act runner setup guide **Repository Migration:** - Migrated from GitHub to self-hosted Gitea - Remote updated to `git@git.corrosionmgmt.com:vantzs/corrosion-admin-panel.git` - All future development on sovereign infrastructure ### Technical Details **Data Flow:** ``` Plugin/Agent publishes stats (60s interval) → NATS JetStream (corrosion.*.stats) → StatsConsumerService persists to server_stats table → Hourly aggregation job rolls up to server_stats_hourly → Analytics API queries aggregated data → Frontend renders charts via ECharts ``` **Database Schema:** - `server_stats` table (raw stats, 7-day retention) - `server_stats_hourly` table (aggregated hourly data, 90-day retention) **Scheduler Jobs:** - Hourly aggregation: `0 5 * * * *` (at :05 past every hour) - Daily cleanup: `0 0 3 * * *` (at 03:00 UTC) ### Installation Notes **Frontend:** ```bash cd frontend && npm install echarts ``` **Backend:** No additional dependencies beyond existing `Cargo.toml`. ### Deferred to Phase 2.2 - Player retention tracking (new vs returning players, session duration) - Wipe-correlated analytics - Player activity heatmaps (time-of-day patterns) - Anomaly alerting system --- ## [2025-02-15] — Phase 1 Complete ### Added (Phase 1 — Foundation) **Backend Services:** - Core control plane (Axum + Tokio) - Auto-wiper with rollback (`wipe_engine.rs`) - Plugin management system - WebSocket/NATS bridge for real-time data - Companion agent adapter (bare metal server management) - Panel adapters (AMP + Pterodactyl) **Frontend:** - Vue 3 dashboard with 19 admin sub-views - Wipe management UI with real-time progress - Toast notification system - Plugin management interface - Public server site **Infrastructure:** - PostgreSQL schema (migrations 001-003) - NATS JetStream streams (6 streams configured) - Docker Compose deployment (4 services) - JWT auth with refresh tokens, TOTP 2FA **Companion Agent:** - Go binary for bare metal server management - NATS-based command execution - Process lifecycle control - File operations support **uMod Plugin:** - C# plugin for Rust game server integration - Stats publishing every 60 seconds - Server lifecycle event reporting ### Commits - `c5d0571` — feat: Complete Phase 1 frontend — WebSocket + Wipe feature end-to-end - `590765f` — feat: Complete Phase 1 backend services and WebSocket/NATS bridge - `8320591` — docs: Update companion agent language choice to Go - `3c39345` — docs: Add CLAUDE.md and Claude Code settings - `81eeb3b` — docs: Add AGENTS.md roster and resource discipline --- **Format:** `type: Short description` **Types:** feat, fix, docs, refactor, test, chore, perf, ci