Initial commit: OpenFUT Core

Offline Ultimate Team backend — game-independent REST API.

- 19 API endpoints: auth, profiles, clubs, cards, packs, squads,
  objectives, SBCs, match rewards, NPC market, statistics
- Axum + SQLite + SQLx with full migrations
- Weighted pack generator, SBC validation engine
- JSON-driven mod data (cards, packs, objectives, SBCs)
- 5 integration tests passing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
funman300
2026-06-25 14:52:06 -07:00
commit 1ffe0ffa9f
60 changed files with 6152 additions and 0 deletions
+62
View File
@@ -0,0 +1,62 @@
use axum::{
http::StatusCode,
response::{IntoResponse, Response},
Json,
};
use serde_json::json;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum AppError {
#[error("not found: {0}")]
NotFound(String),
#[error("bad request: {0}")]
BadRequest(String),
#[error("conflict: {0}")]
Conflict(String),
#[error("database error: {0}")]
Database(#[from] sqlx::Error),
#[error("internal error: {0}")]
Internal(#[from] anyhow::Error),
#[error("io error: {0}")]
Io(#[from] std::io::Error),
#[error("json error: {0}")]
Json(#[from] serde_json::Error),
}
impl IntoResponse for AppError {
fn into_response(self) -> Response {
let (status, message) = match &self {
AppError::NotFound(msg) => (StatusCode::NOT_FOUND, msg.clone()),
AppError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg.clone()),
AppError::Conflict(msg) => (StatusCode::CONFLICT, msg.clone()),
AppError::Database(e) => {
tracing::error!("Database error: {e}");
(StatusCode::INTERNAL_SERVER_ERROR, "database error".into())
}
AppError::Internal(e) => {
tracing::error!("Internal error: {e}");
(
StatusCode::INTERNAL_SERVER_ERROR,
"internal server error".into(),
)
}
AppError::Io(e) => {
tracing::error!("IO error: {e}");
(StatusCode::INTERNAL_SERVER_ERROR, "io error".into())
}
AppError::Json(e) => (StatusCode::BAD_REQUEST, format!("json parse error: {e}")),
};
let body = Json(json!({ "error": message }));
(status, body).into_response()
}
}
pub type AppResult<T> = Result<T, AppError>;