Initial commit: OpenFUT Core + Bridge scaffold

Two-repo monorepo for an offline FUT backend inspired by SPT.

openfut-core: game-independent REST API backend (Axum, SQLite, SQLx)
- 19 API endpoints: auth, profiles, clubs, cards, packs, squads,
  objectives, SBCs, match rewards, NPC market, statistics
- JSON-driven card/pack/objective/SBC data (fully moddable)
- SQLx migrations, weighted pack generator, SBC validation engine
- 5 integration tests passing

openfut-bridge: FIFA 23 traffic proxy + reverse-engineering scaffold
- Catch-all HTTP proxy with request capture to captures/
- Known-route mapper (FUT paths → Core API calls)
- Placeholder responses for unknown endpoints
- Admin endpoints: captures, unknown endpoint list
- 4 unit tests passing

cargo fmt ✓  cargo clippy -D warnings ✓  cargo test ✓

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
funman300
2026-06-25 14:42:16 -07:00
commit 6ef4c40e29
79 changed files with 9158 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>;