scaffold: Backend API routes, DB queries, and middleware stubs
88 handler stubs across 13 route files, 66 DB query stubs across 11 modules, auth/license extractors, and rate limit middleware. All bodies are todo!() — ready for Phase 1b implementation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
48
backend/src/api/auth.rs
Normal file
48
backend/src/api/auth.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::{
|
||||
routing::post,
|
||||
Router,
|
||||
};
|
||||
|
||||
use crate::models::error::ApiResult;
|
||||
use crate::AppState;
|
||||
|
||||
pub fn router() -> Router<Arc<AppState>> {
|
||||
Router::new()
|
||||
.route("/login", post(login))
|
||||
.route("/register", post(register))
|
||||
.route("/verify-totp", post(verify_totp))
|
||||
.route("/refresh", post(refresh))
|
||||
.route("/setup-totp", post(setup_totp))
|
||||
.route("/backup-codes", post(backup_codes))
|
||||
.route("/logout", post(logout))
|
||||
}
|
||||
|
||||
async fn login() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn register() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn verify_totp() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn refresh() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn setup_totp() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn backup_codes() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn logout() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
38
backend/src/api/license.rs
Normal file
38
backend/src/api/license.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::{
|
||||
routing::{get, post, put},
|
||||
Router,
|
||||
};
|
||||
|
||||
use crate::models::error::ApiResult;
|
||||
use crate::AppState;
|
||||
|
||||
pub fn router() -> Router<Arc<AppState>> {
|
||||
Router::new()
|
||||
.route("/", get(get_license_info))
|
||||
.route("/activate", post(activate_license))
|
||||
.route("/check-in", post(check_in))
|
||||
.route("/subdomain", put(update_subdomain))
|
||||
.route("/custom-domain", put(update_custom_domain))
|
||||
}
|
||||
|
||||
async fn get_license_info() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn activate_license() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn check_in() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn update_subdomain() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn update_custom_domain() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
23
backend/src/api/logs.rs
Normal file
23
backend/src/api/logs.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::{
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
|
||||
use crate::models::error::ApiResult;
|
||||
use crate::AppState;
|
||||
|
||||
pub fn router() -> Router<Arc<AppState>> {
|
||||
Router::new()
|
||||
.route("/", get(query_logs))
|
||||
.route("/export", get(export_logs))
|
||||
}
|
||||
|
||||
async fn query_logs() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn export_logs() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
48
backend/src/api/maps.rs
Normal file
48
backend/src/api/maps.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::{
|
||||
routing::{delete, get, post, put},
|
||||
Router,
|
||||
};
|
||||
|
||||
use crate::models::error::ApiResult;
|
||||
use crate::AppState;
|
||||
|
||||
pub fn router() -> Router<Arc<AppState>> {
|
||||
Router::new()
|
||||
.route("/", get(list_maps))
|
||||
.route("/upload", post(upload_map))
|
||||
.route("/{id}", get(get_map))
|
||||
.route("/{id}", delete(delete_map))
|
||||
.route("/{id}/download", get(download_map))
|
||||
.route("/rotation/{server_id}", get(get_map_rotation))
|
||||
.route("/rotation/{server_id}", put(update_map_rotation))
|
||||
}
|
||||
|
||||
async fn list_maps() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn upload_map() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_map() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn delete_map() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn download_map() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_map_rotation() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn update_map_rotation() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
13
backend/src/api/mod.rs
Normal file
13
backend/src/api/mod.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
pub mod auth;
|
||||
pub mod servers;
|
||||
pub mod wipes;
|
||||
pub mod maps;
|
||||
pub mod plugins;
|
||||
pub mod panels;
|
||||
pub mod schedules;
|
||||
pub mod logs;
|
||||
pub mod public;
|
||||
pub mod team;
|
||||
pub mod notifications;
|
||||
pub mod license;
|
||||
pub mod store;
|
||||
33
backend/src/api/notifications.rs
Normal file
33
backend/src/api/notifications.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::{
|
||||
routing::{get, post, put},
|
||||
Router,
|
||||
};
|
||||
|
||||
use crate::models::error::ApiResult;
|
||||
use crate::AppState;
|
||||
|
||||
pub fn router() -> Router<Arc<AppState>> {
|
||||
Router::new()
|
||||
.route("/", get(get_notification_config))
|
||||
.route("/", put(update_notification_config))
|
||||
.route("/test/discord", post(test_discord))
|
||||
.route("/test/pushbullet", post(test_pushbullet))
|
||||
}
|
||||
|
||||
async fn get_notification_config() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn update_notification_config() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn test_discord() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn test_pushbullet() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
43
backend/src/api/panels.rs
Normal file
43
backend/src/api/panels.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::{
|
||||
routing::{delete, get, post, put},
|
||||
Router,
|
||||
};
|
||||
|
||||
use crate::models::error::ApiResult;
|
||||
use crate::AppState;
|
||||
|
||||
pub fn router() -> Router<Arc<AppState>> {
|
||||
Router::new()
|
||||
.route("/", get(list_panels))
|
||||
.route("/", post(create_panel))
|
||||
.route("/{id}", put(update_panel))
|
||||
.route("/{id}", delete(delete_panel))
|
||||
.route("/{id}/test", post(test_panel))
|
||||
.route("/{id}/discover", get(discover_panel))
|
||||
}
|
||||
|
||||
async fn list_panels() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn create_panel() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn update_panel() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn delete_panel() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn test_panel() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn discover_panel() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
43
backend/src/api/plugins.rs
Normal file
43
backend/src/api/plugins.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::{
|
||||
routing::{delete, get, post, put},
|
||||
Router,
|
||||
};
|
||||
|
||||
use crate::models::error::ApiResult;
|
||||
use crate::AppState;
|
||||
|
||||
pub fn router() -> Router<Arc<AppState>> {
|
||||
Router::new()
|
||||
.route("/", get(list_plugins))
|
||||
.route("/install", post(install_plugin))
|
||||
.route("/{id}", put(update_plugin))
|
||||
.route("/{id}", delete(delete_plugin))
|
||||
.route("/{id}/reload", post(reload_plugin))
|
||||
.route("/search", get(search_umod))
|
||||
}
|
||||
|
||||
async fn list_plugins() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn install_plugin() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn update_plugin() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn delete_plugin() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn reload_plugin() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn search_umod() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
58
backend/src/api/public.rs
Normal file
58
backend/src/api/public.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::{
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
|
||||
use crate::models::error::ApiResult;
|
||||
use crate::AppState;
|
||||
|
||||
pub fn router() -> Router<Arc<AppState>> {
|
||||
Router::new()
|
||||
.route("/servers", get(list_public_servers))
|
||||
.route("/servers/{id}", get(get_public_server))
|
||||
.route("/servers/{id}/wipe-schedule", get(get_wipe_schedule))
|
||||
.route("/servers/{id}/countdown", get(get_countdown))
|
||||
.route("/servers/{id}/map", get(get_current_map))
|
||||
.route("/servers/{id}/players", get(get_players))
|
||||
.route("/servers/{id}/mods", get(get_mods))
|
||||
.route("/servers/{id}/motd", get(get_motd))
|
||||
.route("/servers/{id}/store", get(get_store))
|
||||
}
|
||||
|
||||
async fn list_public_servers() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_public_server() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_wipe_schedule() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_countdown() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_current_map() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_players() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_mods() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_motd() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_store() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
53
backend/src/api/schedules.rs
Normal file
53
backend/src/api/schedules.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::{
|
||||
routing::{delete, get, post, put},
|
||||
Router,
|
||||
};
|
||||
|
||||
use crate::models::error::ApiResult;
|
||||
use crate::AppState;
|
||||
|
||||
pub fn router() -> Router<Arc<AppState>> {
|
||||
Router::new()
|
||||
.route("/", get(list_schedules))
|
||||
.route("/", post(create_schedule))
|
||||
.route("/{id}", get(get_schedule))
|
||||
.route("/{id}", put(update_schedule))
|
||||
.route("/{id}", delete(delete_schedule))
|
||||
.route("/{id}/toggle", post(toggle_schedule))
|
||||
.route("/calendar", get(get_calendar))
|
||||
.route("/conflicts", get(get_conflicts))
|
||||
}
|
||||
|
||||
async fn list_schedules() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn create_schedule() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_schedule() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn update_schedule() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn delete_schedule() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn toggle_schedule() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_calendar() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_conflicts() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
53
backend/src/api/servers.rs
Normal file
53
backend/src/api/servers.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::{
|
||||
routing::{get, post, put},
|
||||
Router,
|
||||
};
|
||||
|
||||
use crate::models::error::ApiResult;
|
||||
use crate::AppState;
|
||||
|
||||
pub fn router() -> Router<Arc<AppState>> {
|
||||
Router::new()
|
||||
.route("/", get(list_servers))
|
||||
.route("/{id}", get(get_server))
|
||||
.route("/{id}", put(update_server))
|
||||
.route("/{id}/command", post(send_command))
|
||||
.route("/{id}/plugins", get(get_server_plugins))
|
||||
.route("/{id}/start", post(start_server))
|
||||
.route("/{id}/stop", post(stop_server))
|
||||
.route("/{id}/restart", post(restart_server))
|
||||
}
|
||||
|
||||
async fn list_servers() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_server() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn update_server() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn send_command() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_server_plugins() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn start_server() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn stop_server() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn restart_server() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
73
backend/src/api/store.rs
Normal file
73
backend/src/api/store.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::{
|
||||
routing::{delete, get, post, put},
|
||||
Router,
|
||||
};
|
||||
|
||||
use crate::models::error::ApiResult;
|
||||
use crate::AppState;
|
||||
|
||||
pub fn router() -> Router<Arc<AppState>> {
|
||||
Router::new()
|
||||
.route("/config", get(get_store_config))
|
||||
.route("/config", put(update_store_config))
|
||||
.route("/categories", get(list_categories))
|
||||
.route("/categories", post(create_category))
|
||||
.route("/categories/{id}", put(update_category))
|
||||
.route("/categories/{id}", delete(delete_category))
|
||||
.route("/items", get(list_items))
|
||||
.route("/items", post(create_item))
|
||||
.route("/items/{id}", put(update_item))
|
||||
.route("/items/{id}", delete(delete_item))
|
||||
.route("/transactions", get(list_transactions))
|
||||
.route("/webhook/paypal", post(paypal_webhook))
|
||||
}
|
||||
|
||||
async fn get_store_config() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn update_store_config() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn list_categories() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn create_category() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn update_category() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn delete_category() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn list_items() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn create_item() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn update_item() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn delete_item() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn list_transactions() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn paypal_webhook() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
53
backend/src/api/team.rs
Normal file
53
backend/src/api/team.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::{
|
||||
routing::{delete, get, post, put},
|
||||
Router,
|
||||
};
|
||||
|
||||
use crate::models::error::ApiResult;
|
||||
use crate::AppState;
|
||||
|
||||
pub fn router() -> Router<Arc<AppState>> {
|
||||
Router::new()
|
||||
.route("/", get(list_members))
|
||||
.route("/invite", post(invite_member))
|
||||
.route("/{id}/role", put(update_member_role))
|
||||
.route("/{id}", delete(remove_member))
|
||||
.route("/roles", get(list_roles))
|
||||
.route("/roles", post(create_role))
|
||||
.route("/roles/{id}", put(update_role))
|
||||
.route("/roles/{id}", delete(delete_role))
|
||||
}
|
||||
|
||||
async fn list_members() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn invite_member() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn update_member_role() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn remove_member() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn list_roles() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn create_role() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn update_role() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn delete_role() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
43
backend/src/api/wipes.rs
Normal file
43
backend/src/api/wipes.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::{
|
||||
routing::{get, post},
|
||||
Router,
|
||||
};
|
||||
|
||||
use crate::models::error::ApiResult;
|
||||
use crate::AppState;
|
||||
|
||||
pub fn router() -> Router<Arc<AppState>> {
|
||||
Router::new()
|
||||
.route("/{server_id}/trigger", post(trigger_wipe))
|
||||
.route("/{server_id}/dry-run", post(dry_run_wipe))
|
||||
.route("/{wipe_id}/rollback", post(rollback_wipe))
|
||||
.route("/{wipe_id}/status", get(get_wipe_status))
|
||||
.route("/history", get(get_wipe_history))
|
||||
.route("/history/{id}", get(get_wipe_history_detail))
|
||||
}
|
||||
|
||||
async fn trigger_wipe() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn dry_run_wipe() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn rollback_wipe() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_wipe_status() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_wipe_history() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_wipe_history_detail() -> ApiResult<impl axum::response::IntoResponse> {
|
||||
todo!()
|
||||
}
|
||||
31
backend/src/db/chat.rs
Normal file
31
backend/src/db/chat.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
use sqlx::PgPool;
|
||||
use uuid::Uuid;
|
||||
use anyhow::Result;
|
||||
|
||||
// TODO: Define ChatMessage struct (id, server_id, steam_id, player_name, message, channel, flagged, timestamp)
|
||||
// TODO: Define PlayerAction struct (id, server_id, steam_id, action_type, reason, performed_by, created_at)
|
||||
|
||||
/// Store an incoming chat message from the game server.
|
||||
pub async fn insert_chat_message(pool: &PgPool, server_id: Uuid, steam_id: &str, player_name: &str, message: &str, channel: &str) -> Result<Uuid> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Retrieve chat messages for a server with pagination.
|
||||
pub async fn get_chat_messages(pool: &PgPool, server_id: Uuid, limit: i64, offset: i64) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Flag a chat message for review (toxic, spam, etc.).
|
||||
pub async fn flag_message(pool: &PgPool, message_id: Uuid, flagged: bool) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Get moderation actions taken against a player.
|
||||
pub async fn get_player_actions(pool: &PgPool, server_id: Uuid, steam_id: &str) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Record a moderation action (kick, ban, mute, warn).
|
||||
pub async fn create_player_action(pool: &PgPool, server_id: Uuid, steam_id: &str, action_type: &str, reason: &str, performed_by: Uuid) -> Result<Uuid> {
|
||||
todo!()
|
||||
}
|
||||
35
backend/src/db/licenses.rs
Normal file
35
backend/src/db/licenses.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
use sqlx::PgPool;
|
||||
use uuid::Uuid;
|
||||
use anyhow::Result;
|
||||
|
||||
// TODO: Define License struct (id, user_id, license_key, status, tier, modules, max_servers, activated_at, expires_at, created_at)
|
||||
|
||||
/// Create a new license record.
|
||||
pub async fn create_license(pool: &PgPool, user_id: Uuid, license_key: &str, tier: &str) -> Result<Uuid> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Look up a license by its key string (used during activation).
|
||||
pub async fn get_license_by_key(pool: &PgPool, license_key: &str) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Fetch a license by its primary key.
|
||||
pub async fn get_license_by_id(pool: &PgPool, license_id: Uuid) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Fetch all licenses belonging to a user.
|
||||
pub async fn get_license_by_user(pool: &PgPool, user_id: Uuid) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Mark a license as activated and record the activation timestamp.
|
||||
pub async fn activate_license(pool: &PgPool, license_id: Uuid) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Update the status of a license (active, suspended, expired, revoked).
|
||||
pub async fn update_license_status(pool: &PgPool, license_id: Uuid, status: &str) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
36
backend/src/db/maps.rs
Normal file
36
backend/src/db/maps.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
use sqlx::PgPool;
|
||||
use uuid::Uuid;
|
||||
use anyhow::Result;
|
||||
|
||||
// TODO: Define Map struct (id, server_id, name, file_path, size_bytes, uploaded_at)
|
||||
// TODO: Define MapRotation struct (id, server_id, map_ids, current_index, auto_rotate)
|
||||
|
||||
/// Upload/register a new custom map.
|
||||
pub async fn create_map(pool: &PgPool, server_id: Uuid, name: &str, file_path: &str, size_bytes: i64) -> Result<Uuid> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Get all maps for a server.
|
||||
pub async fn get_maps(pool: &PgPool, server_id: Uuid) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Fetch a single map by ID.
|
||||
pub async fn get_map_by_id(pool: &PgPool, map_id: Uuid) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Delete a map record (and its associated file reference).
|
||||
pub async fn delete_map(pool: &PgPool, map_id: Uuid) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Get the current map rotation configuration for a server.
|
||||
pub async fn get_map_rotation(pool: &PgPool, server_id: Uuid) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Update the map rotation (set map order, current index, toggle auto-rotate).
|
||||
pub async fn update_map_rotation(pool: &PgPool, server_id: Uuid, map_ids: &[Uuid], auto_rotate: bool) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
14
backend/src/db/mod.rs
Normal file
14
backend/src/db/mod.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
// Database query modules — each corresponds to a domain
|
||||
// Queries use sqlx with compile-time verification against PostgreSQL
|
||||
|
||||
pub mod users;
|
||||
pub mod licenses;
|
||||
pub mod servers;
|
||||
pub mod wipes;
|
||||
pub mod maps;
|
||||
pub mod plugins;
|
||||
pub mod teams;
|
||||
pub mod notifications;
|
||||
pub mod chat;
|
||||
pub mod stats;
|
||||
pub mod store;
|
||||
15
backend/src/db/notifications.rs
Normal file
15
backend/src/db/notifications.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
use sqlx::PgPool;
|
||||
use uuid::Uuid;
|
||||
use anyhow::Result;
|
||||
|
||||
// TODO: Define NotificationConfig struct (id, server_id, discord_webhook_url, events jsonb, enabled, created_at, updated_at)
|
||||
|
||||
/// Fetch the notification configuration for a server.
|
||||
pub async fn get_notification_config(pool: &PgPool, server_id: Uuid) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Insert or update the notification configuration for a server.
|
||||
pub async fn upsert_notification_config(pool: &PgPool, server_id: Uuid, discord_webhook_url: Option<&str>, events: &str, enabled: bool) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
30
backend/src/db/plugins.rs
Normal file
30
backend/src/db/plugins.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use sqlx::PgPool;
|
||||
use uuid::Uuid;
|
||||
use anyhow::Result;
|
||||
|
||||
// TODO: Define Plugin struct (id, server_id, name, version, author, is_enabled, config_json, wipe_on_map, wipe_on_bp, created_at, updated_at)
|
||||
|
||||
/// Insert or update a plugin record (upsert by server_id + name).
|
||||
pub async fn upsert_plugin(pool: &PgPool, server_id: Uuid, name: &str, version: &str, author: Option<&str>) -> Result<Uuid> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Get all plugins for a server.
|
||||
pub async fn get_plugins(pool: &PgPool, server_id: Uuid) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Update the JSON configuration blob for a plugin.
|
||||
pub async fn update_plugin_config(pool: &PgPool, plugin_id: Uuid, config_json: &str) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Update wipe-related settings for a plugin (wipe on map, wipe on BP).
|
||||
pub async fn update_plugin_wipe_settings(pool: &PgPool, plugin_id: Uuid, wipe_on_map: bool, wipe_on_bp: bool) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Remove a plugin record.
|
||||
pub async fn delete_plugin(pool: &PgPool, plugin_id: Uuid) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
52
backend/src/db/servers.rs
Normal file
52
backend/src/db/servers.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
use sqlx::PgPool;
|
||||
use uuid::Uuid;
|
||||
use anyhow::Result;
|
||||
|
||||
// TODO: Define ServerConnection struct (id, license_id, name, host, rcon_port, rcon_password_encrypted, query_port, created_at)
|
||||
// TODO: Define ServerConfig struct (id, server_id, seed, world_size, max_players, hostname, description, header_image_url, etc.)
|
||||
// TODO: Define GameAdmin struct (id, server_id, steam_id, role, added_at)
|
||||
|
||||
/// Register a new server connection (RCON credentials).
|
||||
pub async fn create_server_connection(pool: &PgPool, license_id: Uuid, name: &str, host: &str, rcon_port: i32) -> Result<Uuid> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Fetch a server connection by ID.
|
||||
pub async fn get_server_connection(pool: &PgPool, server_id: Uuid) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Update server connection details.
|
||||
pub async fn update_server_connection(pool: &PgPool, server_id: Uuid, name: Option<&str>, host: Option<&str>, rcon_port: Option<i32>) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Create the initial server configuration record.
|
||||
pub async fn create_server_config(pool: &PgPool, server_id: Uuid) -> Result<Uuid> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Fetch server configuration.
|
||||
pub async fn get_server_config(pool: &PgPool, server_id: Uuid) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Update server configuration fields.
|
||||
pub async fn update_server_config(pool: &PgPool, server_id: Uuid, seed: Option<i32>, world_size: Option<i32>, max_players: Option<i32>) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Get all game admins (moderators/owners) for a server.
|
||||
pub async fn get_game_admins(pool: &PgPool, server_id: Uuid) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Add a game admin by Steam ID.
|
||||
pub async fn create_game_admin(pool: &PgPool, server_id: Uuid, steam_id: &str, role: &str) -> Result<Uuid> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Remove a game admin.
|
||||
pub async fn delete_game_admin(pool: &PgPool, admin_id: Uuid) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
26
backend/src/db/stats.rs
Normal file
26
backend/src/db/stats.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
use sqlx::PgPool;
|
||||
use uuid::Uuid;
|
||||
use anyhow::Result;
|
||||
|
||||
// TODO: Define ServerStats struct (id, server_id, player_count, fps, memory_usage, entities, timestamp)
|
||||
// TODO: Define HourlyStats struct (id, server_id, hour, avg_players, avg_fps, avg_memory, peak_players)
|
||||
|
||||
/// Insert a raw stats snapshot from the game server.
|
||||
pub async fn insert_server_stats(pool: &PgPool, server_id: Uuid, player_count: i32, fps: f64, memory_usage: i64, entities: i32) -> Result<Uuid> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Get the most recent stats snapshots for a server.
|
||||
pub async fn get_recent_stats(pool: &PgPool, server_id: Uuid, limit: i64) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Get hourly aggregated stats for charting.
|
||||
pub async fn get_hourly_stats(pool: &PgPool, server_id: Uuid, hours: i64) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Roll up raw stats into hourly aggregates (called by a scheduled job).
|
||||
pub async fn aggregate_hourly_stats(pool: &PgPool, server_id: Uuid) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
53
backend/src/db/store.rs
Normal file
53
backend/src/db/store.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
use sqlx::PgPool;
|
||||
use uuid::Uuid;
|
||||
use anyhow::Result;
|
||||
|
||||
// TODO: Define WebstoreConfig struct (id, server_id, enabled, currency, stripe_key_encrypted, tebex_secret_encrypted, created_at, updated_at)
|
||||
// TODO: Define Category struct (id, server_id, name, sort_order, created_at)
|
||||
// TODO: Define Item struct (id, category_id, name, description, price, commands, image_url, is_active, created_at)
|
||||
// TODO: Define Transaction struct (id, server_id, steam_id, item_id, amount, currency, status, provider, provider_ref, created_at, completed_at)
|
||||
|
||||
/// Get the webstore configuration for a server.
|
||||
pub async fn get_webstore_config(pool: &PgPool, server_id: Uuid) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Insert or update the webstore configuration.
|
||||
pub async fn upsert_webstore_config(pool: &PgPool, server_id: Uuid, enabled: bool, currency: &str) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Get all item categories for a server's webstore.
|
||||
pub async fn get_categories(pool: &PgPool, server_id: Uuid) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Create a new item category.
|
||||
pub async fn create_category(pool: &PgPool, server_id: Uuid, name: &str, sort_order: i32) -> Result<Uuid> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Get all items, optionally filtered by category.
|
||||
pub async fn get_items(pool: &PgPool, server_id: Uuid, category_id: Option<Uuid>) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Create a new store item.
|
||||
pub async fn create_item(pool: &PgPool, category_id: Uuid, name: &str, description: &str, price: i64, commands: &str) -> Result<Uuid> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Create a new purchase transaction record.
|
||||
pub async fn create_transaction(pool: &PgPool, server_id: Uuid, steam_id: &str, item_id: Uuid, amount: i64, currency: &str, provider: &str) -> Result<Uuid> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Update transaction status (pending -> completed/failed/refunded).
|
||||
pub async fn update_transaction_status(pool: &PgPool, transaction_id: Uuid, status: &str, provider_ref: Option<&str>) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Get transactions for a server with optional status filter.
|
||||
pub async fn get_transactions(pool: &PgPool, server_id: Uuid, status: Option<&str>, limit: i64, offset: i64) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
46
backend/src/db/teams.rs
Normal file
46
backend/src/db/teams.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use sqlx::PgPool;
|
||||
use uuid::Uuid;
|
||||
use anyhow::Result;
|
||||
|
||||
// TODO: Define TeamMember struct (id, license_id, user_id, role_id, invited_at, accepted_at)
|
||||
// TODO: Define Role struct (id, license_id, name, permissions jsonb, created_at)
|
||||
|
||||
/// Get all team members for a license.
|
||||
pub async fn get_team_members(pool: &PgPool, license_id: Uuid) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Add a user to a license team.
|
||||
pub async fn create_team_member(pool: &PgPool, license_id: Uuid, user_id: Uuid, role_id: Uuid) -> Result<Uuid> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Change a team member's role.
|
||||
pub async fn update_team_member_role(pool: &PgPool, member_id: Uuid, role_id: Uuid) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Remove a team member from the license.
|
||||
pub async fn remove_team_member(pool: &PgPool, member_id: Uuid) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Get all roles defined for a license.
|
||||
pub async fn get_roles(pool: &PgPool, license_id: Uuid) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Create a custom role with a permissions set.
|
||||
pub async fn create_role(pool: &PgPool, license_id: Uuid, name: &str, permissions: &str) -> Result<Uuid> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Update a role's name or permissions.
|
||||
pub async fn update_role(pool: &PgPool, role_id: Uuid, name: Option<&str>, permissions: Option<&str>) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Delete a custom role.
|
||||
pub async fn delete_role(pool: &PgPool, role_id: Uuid) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
30
backend/src/db/users.rs
Normal file
30
backend/src/db/users.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use sqlx::PgPool;
|
||||
use uuid::Uuid;
|
||||
use anyhow::Result;
|
||||
|
||||
// TODO: Define User struct (id, email, password_hash, display_name, avatar_url, created_at, updated_at, last_login_at)
|
||||
|
||||
/// Create a new user record.
|
||||
pub async fn create_user(pool: &PgPool, email: &str, password_hash: &str, display_name: &str) -> Result<Uuid> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Fetch a user by their primary key.
|
||||
pub async fn get_user_by_id(pool: &PgPool, user_id: Uuid) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Fetch a user by email address (for login lookups).
|
||||
pub async fn get_user_by_email(pool: &PgPool, email: &str) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Update mutable user profile fields.
|
||||
pub async fn update_user(pool: &PgPool, user_id: Uuid, display_name: Option<&str>, avatar_url: Option<&str>) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Bump the last_login_at timestamp for a user.
|
||||
pub async fn update_last_login(pool: &PgPool, user_id: Uuid) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
42
backend/src/db/wipes.rs
Normal file
42
backend/src/db/wipes.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use sqlx::PgPool;
|
||||
use uuid::Uuid;
|
||||
use anyhow::Result;
|
||||
|
||||
// TODO: Define WipeProfile struct (id, server_id, name, wipe_type, commands, plugin_actions, created_at)
|
||||
// TODO: Define WipeSchedule struct (id, profile_id, cron_expression, next_run_at, enabled)
|
||||
// TODO: Define WipeHistory struct (id, profile_id, started_at, completed_at, status, log)
|
||||
|
||||
/// Create a new wipe profile (template for a wipe operation).
|
||||
pub async fn create_wipe_profile(pool: &PgPool, server_id: Uuid, name: &str, wipe_type: &str) -> Result<Uuid> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Get all wipe profiles for a server.
|
||||
pub async fn get_wipe_profiles(pool: &PgPool, server_id: Uuid) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Create a cron-based schedule for a wipe profile.
|
||||
pub async fn create_wipe_schedule(pool: &PgPool, profile_id: Uuid, cron_expression: &str) -> Result<Uuid> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Get all schedules for a wipe profile.
|
||||
pub async fn get_wipe_schedules(pool: &PgPool, profile_id: Uuid) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Record the start of a wipe execution.
|
||||
pub async fn create_wipe_history(pool: &PgPool, profile_id: Uuid) -> Result<Uuid> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Update a wipe history entry with completion status and log output.
|
||||
pub async fn update_wipe_history(pool: &PgPool, history_id: Uuid, status: &str, log: Option<&str>) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Get wipe history for a profile, ordered by most recent first.
|
||||
pub async fn get_wipe_history(pool: &PgPool, profile_id: Uuid, limit: i64) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
32
backend/src/middleware/auth.rs
Normal file
32
backend/src/middleware/auth.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
use axum::extract::FromRequestParts;
|
||||
use http::request::Parts;
|
||||
use uuid::Uuid;
|
||||
|
||||
/// Extractor that validates the JWT from the Authorization header
|
||||
/// and provides the authenticated user's identity to handlers.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AuthUser {
|
||||
pub user_id: Uuid,
|
||||
pub email: String,
|
||||
pub license_id: Option<Uuid>,
|
||||
pub role: Option<String>,
|
||||
}
|
||||
|
||||
// TODO: Implement JWT validation logic
|
||||
// - Extract Bearer token from Authorization header
|
||||
// - Decode and verify JWT signature using the app's secret
|
||||
// - Check token expiration
|
||||
// - Build AuthUser from claims
|
||||
// - Return 401 Unauthorized on any failure
|
||||
|
||||
#[axum::async_trait]
|
||||
impl<S> FromRequestParts<S> for AuthUser
|
||||
where
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = http::StatusCode;
|
||||
|
||||
async fn from_request_parts(_parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
32
backend/src/middleware/license.rs
Normal file
32
backend/src/middleware/license.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
use axum::extract::FromRequestParts;
|
||||
use http::request::Parts;
|
||||
use uuid::Uuid;
|
||||
|
||||
/// Extractor that ensures the request is associated with a valid, active license.
|
||||
/// Should be used after AuthUser — it reads the license_id from the authenticated user's claims
|
||||
/// and verifies the license exists, is active, and has the required modules enabled.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ValidLicense {
|
||||
pub license_id: Uuid,
|
||||
pub status: String,
|
||||
pub modules: Vec<String>,
|
||||
}
|
||||
|
||||
// TODO: Implement license validation logic
|
||||
// - Retrieve license_id from AuthUser claims (or from request extensions)
|
||||
// - Query the database to confirm the license exists and is active
|
||||
// - Check that the license hasn't expired
|
||||
// - Populate the modules list for downstream permission checks
|
||||
// - Return 403 Forbidden if the license is invalid, expired, or suspended
|
||||
|
||||
#[axum::async_trait]
|
||||
impl<S> FromRequestParts<S> for ValidLicense
|
||||
where
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = http::StatusCode;
|
||||
|
||||
async fn from_request_parts(_parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
3
backend/src/middleware/mod.rs
Normal file
3
backend/src/middleware/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod auth;
|
||||
pub mod license;
|
||||
pub mod rate_limit;
|
||||
17
backend/src/middleware/rate_limit.rs
Normal file
17
backend/src/middleware/rate_limit.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
// TODO: Implement tower-based rate limiting middleware
|
||||
//
|
||||
// Approach options:
|
||||
// 1. Use tower::limit::RateLimitLayer for simple fixed-window rate limiting
|
||||
// 2. Use governor crate for more sophisticated token-bucket / sliding-window limiting
|
||||
// 3. Build a custom tower::Layer + tower::Service that reads client IP + license tier
|
||||
// and applies different rate limits per tier
|
||||
//
|
||||
// Requirements:
|
||||
// - Per-IP rate limiting for unauthenticated endpoints (login, register)
|
||||
// - Per-license rate limiting for authenticated API endpoints
|
||||
// - Higher limits for premium license tiers
|
||||
// - Return 429 Too Many Requests with Retry-After header
|
||||
//
|
||||
// Example integration:
|
||||
// let rate_limit = RateLimitLayer::new(100, Duration::from_secs(60));
|
||||
// Router::new().route("/api/...", get(handler)).layer(rate_limit)
|
||||
Reference in New Issue
Block a user