pub mod api_keys;
pub mod auth;
pub mod export;
pub mod profile;
pub mod settings;
pub mod sync;
#[cfg(feature = "portal")]
pub mod db;
#[cfg(feature = "portal")]
pub mod auth_db;
#[cfg(feature = "portal")]
pub mod profile_db;
#[cfg(feature = "portal")]
pub mod settings_db;
#[cfg(feature = "portal")]
pub mod api_keys_db;
#[cfg(feature = "portal")]
pub mod rate_limiting;
#[cfg(feature = "portal")]
pub mod usage_tracking;
#[cfg(feature = "portal")]
pub mod export_db;
#[cfg(feature = "portal")]
pub mod sync_db;
#[cfg(feature = "portal")]
pub mod sync_ws;
pub mod middleware;
use axum::{
routing::{delete, get, post, put},
Router,
};
pub use api_keys::{ApiKey, ApiKeyScope};
pub use auth::{AuthConfig, AuthService, Claims, TokenPair};
pub use export::{ExportConfig, ExportFormat, ExportJob, ExportService};
pub use profile::{Profile, ProfileService, ProfileUpdate};
pub use settings::{SettingsService, UserSettings};
pub use sync::{SyncConfig, SyncEvent, SyncService};
#[cfg(feature = "portal")]
pub use db::{DatabasePool, DbError};
#[cfg(feature = "portal")]
pub use auth_db::PortalState;
#[cfg(feature = "portal")]
pub use sync_ws::{SyncWsState, WsState};
#[cfg(feature = "portal")]
pub use sync_db::{
DbDeviceSession, DeviceSessionRepository, SyncConflict, SyncConflictRepository, SyncQueueItem,
SyncQueueRepository, SyncState, SyncStateRepository,
};
pub use middleware::{optional_auth, require_auth, AuthClaims};
pub fn portal_router() -> Router {
Router::new()
.route("/auth/register", post(auth::handlers::register))
.route("/auth/login", post(auth::handlers::login))
.route("/auth/logout", post(auth::handlers::logout))
.route("/auth/refresh", post(auth::handlers::refresh_token))
.route(
"/auth/password/reset",
post(auth::handlers::request_password_reset),
)
.route(
"/auth/password/reset/:token",
post(auth::handlers::reset_password),
)
.route("/auth/2fa/setup", post(auth::handlers::setup_2fa))
.route("/auth/2fa/verify", post(auth::handlers::verify_2fa))
.route("/profile", get(profile::handlers::get_profile))
.route("/profile", put(profile::handlers::update_profile))
.route("/profile/avatar", post(profile::handlers::upload_avatar))
.route("/profile/delete", delete(profile::handlers::delete_account))
.route("/settings", get(settings::handlers::get_settings))
.route("/settings", put(settings::handlers::update_settings))
.route("/settings/sync", post(settings::handlers::sync_settings))
.route("/api-keys", get(api_keys::handlers::list_keys))
.route("/api-keys", post(api_keys::handlers::create_key))
.route("/api-keys/:id", delete(api_keys::handlers::revoke_key))
.route("/api-keys/:id/rotate", post(api_keys::handlers::rotate_key))
.route("/export", post(export::handlers::create_export))
.route("/export/:id", get(export::handlers::get_export_status))
.route(
"/export/:id/download",
get(export::handlers::download_export),
)
.route("/export/schedule", post(export::handlers::schedule_export))
.route("/export/history", get(export::handlers::export_history))
}
#[derive(Debug, Clone)]
pub struct PortalConfig {
pub jwt_secret: String,
pub token_expiry_secs: u64,
pub refresh_token_expiry_secs: u64,
pub require_2fa_for_sensitive: bool,
pub max_api_keys_per_user: usize,
pub export_storage_path: String,
pub export_retention_days: u32,
}
impl Default for PortalConfig {
fn default() -> Self {
Self {
jwt_secret: std::env::var("JWT_SECRET")
.unwrap_or_else(|_| "change-me-in-production".to_string()),
token_expiry_secs: 3600, refresh_token_expiry_secs: 604800, require_2fa_for_sensitive: false,
max_api_keys_per_user: 10,
export_storage_path: "/var/lib/reasonkit/exports".to_string(),
export_retention_days: 30,
}
}
}
#[cfg(feature = "portal")]
pub fn portal_router_with_db(state: PortalState) -> Router {
use axum::middleware as axum_middleware;
let public_routes = Router::new()
.route("/auth/register", post(auth_db::register))
.route("/auth/login", post(auth_db::login))
.route("/auth/refresh", post(auth_db::refresh_token))
.route(
"/auth/password/reset",
post(auth::handlers::request_password_reset),
)
.route(
"/auth/password/reset/:token",
post(auth::handlers::reset_password),
);
let protected_routes = Router::new()
.route("/auth/logout", post(auth_db::logout))
.route("/auth/2fa/setup", post(auth::handlers::setup_2fa))
.route("/auth/2fa/verify", post(auth::handlers::verify_2fa))
.route("/profile", get(profile_db::get_profile))
.route("/profile", put(profile_db::update_profile))
.route("/profile/avatar", post(profile_db::upload_avatar))
.route("/profile/delete", delete(profile_db::delete_account))
.route("/settings", get(settings_db::get_settings))
.route("/settings", put(settings_db::update_settings))
.route("/settings/:key", get(settings_db::get_setting))
.route("/settings/:key", put(settings_db::set_setting))
.route("/settings/:key", delete(settings_db::delete_setting))
.route("/settings/sync", post(settings_db::sync_settings))
.route("/api-keys", get(api_keys_db::list_keys))
.route("/api-keys", post(api_keys_db::create_key))
.route("/api-keys/:id", delete(api_keys_db::revoke_key))
.route("/api-keys/:id/rotate", post(api_keys_db::rotate_key))
.route("/api-keys/:id/usage", get(usage_tracking::get_usage_stats))
.route(
"/api-keys/:id/usage/daily",
get(usage_tracking::get_daily_usage),
)
.route(
"/api-keys/:id/usage/endpoints",
get(usage_tracking::get_endpoint_usage),
)
.route("/export", post(export_db::create_export))
.route("/export/:id", get(export_db::get_export_status))
.route("/export/:id/download", get(export_db::download_export))
.route("/export/history", get(export_db::export_history))
.route_layer(axum_middleware::from_fn(middleware::require_auth));
Router::new()
.merge(public_routes)
.merge(protected_routes)
.with_state(state)
}
#[cfg(feature = "portal")]
pub fn portal_router_with_sync(portal_state: PortalState, ws_state: WsState) -> Router {
use axum::middleware as axum_middleware;
let sync_state = SyncWsState {
portal: portal_state.clone(),
ws: ws_state,
};
let public_routes = Router::new()
.route("/auth/register", post(auth_db::register))
.route("/auth/login", post(auth_db::login))
.route("/auth/refresh", post(auth_db::refresh_token))
.route(
"/auth/password/reset",
post(auth::handlers::request_password_reset),
)
.route(
"/auth/password/reset/:token",
post(auth::handlers::reset_password),
)
.with_state(portal_state.clone());
let ws_routes = Router::new()
.route("/sync/ws", get(sync_ws::ws_handler))
.with_state(sync_state.clone());
let sync_protected = Router::new()
.route("/sync/status", get(sync_ws::get_sync_status))
.route("/sync/conflicts", get(sync_ws::get_conflicts))
.route(
"/sync/conflicts/:id/resolve",
post(sync_ws::resolve_conflict),
)
.route_layer(axum_middleware::from_fn(middleware::require_auth))
.with_state(sync_state);
let protected_routes = Router::new()
.route("/auth/logout", post(auth_db::logout))
.route("/auth/2fa/setup", post(auth::handlers::setup_2fa))
.route("/auth/2fa/verify", post(auth::handlers::verify_2fa))
.route("/profile", get(profile_db::get_profile))
.route("/profile", put(profile_db::update_profile))
.route("/profile/avatar", post(profile_db::upload_avatar))
.route("/profile/delete", delete(profile_db::delete_account))
.route("/settings", get(settings_db::get_settings))
.route("/settings", put(settings_db::update_settings))
.route("/settings/:key", get(settings_db::get_setting))
.route("/settings/:key", put(settings_db::set_setting))
.route("/settings/:key", delete(settings_db::delete_setting))
.route("/settings/sync", post(settings_db::sync_settings))
.route("/api-keys", get(api_keys_db::list_keys))
.route("/api-keys", post(api_keys_db::create_key))
.route("/api-keys/:id", delete(api_keys_db::revoke_key))
.route("/api-keys/:id/rotate", post(api_keys_db::rotate_key))
.route("/api-keys/:id/usage", get(usage_tracking::get_usage_stats))
.route(
"/api-keys/:id/usage/daily",
get(usage_tracking::get_daily_usage),
)
.route(
"/api-keys/:id/usage/endpoints",
get(usage_tracking::get_endpoint_usage),
)
.route("/export", post(export_db::create_export))
.route("/export/:id", get(export_db::get_export_status))
.route("/export/:id/download", get(export_db::download_export))
.route("/export/history", get(export_db::export_history))
.route_layer(axum_middleware::from_fn(middleware::require_auth))
.with_state(portal_state);
Router::new()
.merge(public_routes)
.merge(ws_routes)
.merge(sync_protected)
.merge(protected_routes)
}