mirror of
https://codeberg.org/icewind/ugc-scaper.git
synced 2026-06-03 18:24:10 +02:00
api server
This commit is contained in:
parent
8dc91f8bc3
commit
5617493524
4 changed files with 2165 additions and 1 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,3 +1,3 @@
|
|||
/target
|
||||
target
|
||||
result*
|
||||
.direnv
|
||||
2025
api-server/Cargo.lock
generated
Normal file
2025
api-server/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
14
api-server/Cargo.toml
Normal file
14
api-server/Cargo.toml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "ugc-api-server"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1.34.0", features = ["macros", "rt-multi-thread", "rt"] }
|
||||
main_error = "0.1.2"
|
||||
ugc-scraper = { path = "../", version = "0.1" }
|
||||
axum = "0.6.20"
|
||||
steamid-ng = "1.0.0"
|
||||
thiserror = "1.0.50"
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
125
api-server/src/main.rs
Normal file
125
api-server/src/main.rs
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
use axum::extract::{Path, State};
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::{IntoResponse, Response};
|
||||
use axum::{response::Html, routing::get, Json, Router};
|
||||
use main_error::MainResult;
|
||||
use std::env::var;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
use steamid_ng::{SteamID, SteamIDError};
|
||||
use thiserror::Error;
|
||||
use tracing::{debug, error, instrument};
|
||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
||||
use ugc_scraper::{ScrapeError, UgcClient};
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
struct AppState {
|
||||
client: Arc<UgcClient>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
enum ApiError {
|
||||
#[error(transparent)]
|
||||
SteamId(#[from] SteamIDError),
|
||||
#[error(transparent)]
|
||||
Scrape(#[from] ScrapeError),
|
||||
}
|
||||
|
||||
impl IntoResponse for ApiError {
|
||||
fn into_response(self) -> Response {
|
||||
error!(error = ?self, "error while handling request");
|
||||
match self {
|
||||
Self::SteamId(err) => {
|
||||
(StatusCode::UNPROCESSABLE_ENTITY, format!("{:#}", err)).into_response()
|
||||
}
|
||||
Self::Scrape(err) => {
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, format!("{:#}", err)).into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> MainResult {
|
||||
tracing_subscriber::registry()
|
||||
.with(tracing_subscriber::EnvFilter::new(
|
||||
std::env::var("RUST_LOG").unwrap_or_else(|_| "ugc_api_server=debug".into()),
|
||||
))
|
||||
.with(tracing_subscriber::fmt::layer())
|
||||
.init();
|
||||
|
||||
let port = var("PORT")?.parse()?;
|
||||
// build our application with a route
|
||||
let app = Router::new()
|
||||
.route("/", get(handler))
|
||||
.route("/player/:id", get(player))
|
||||
.route("/player/:id/history", get(player_history))
|
||||
.route("/team/:id", get(team))
|
||||
.route("/team/:id/roster", get(team_roster))
|
||||
.route("/team/:id/matches", get(team_matches))
|
||||
.with_state(AppState::default());
|
||||
|
||||
// run it
|
||||
let addr = SocketAddr::from(([127, 0, 0, 1], port));
|
||||
tracing::info!("listening on {}", addr);
|
||||
axum::Server::bind(&addr)
|
||||
.serve(app.into_make_service())
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handler() -> Html<&'static str> {
|
||||
Html("<h1>Hello, World!</h1>")
|
||||
}
|
||||
|
||||
#[instrument(skip(state))]
|
||||
async fn player(
|
||||
Path(id): Path<String>,
|
||||
State(state): State<AppState>,
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
let steam_id = SteamID::try_from(id.as_str())?;
|
||||
debug!(player = steam_id.steam3(), "requesting player");
|
||||
let response = state.client.player(steam_id).await?;
|
||||
Ok(Json(response))
|
||||
}
|
||||
|
||||
#[instrument(skip(state))]
|
||||
async fn player_history(
|
||||
Path(id): Path<String>,
|
||||
State(state): State<AppState>,
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
let steam_id = SteamID::try_from(id.as_str())?;
|
||||
debug!(player = steam_id.steam3(), "requesting player history");
|
||||
let response = state.client.player_team_history(steam_id).await?;
|
||||
Ok(Json(response))
|
||||
}
|
||||
|
||||
#[instrument(skip(state))]
|
||||
async fn team(
|
||||
Path(id): Path<u32>,
|
||||
State(state): State<AppState>,
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
debug!(team = id, "requesting team");
|
||||
let response = state.client.team(id).await?;
|
||||
Ok(Json(response))
|
||||
}
|
||||
|
||||
#[instrument(skip(state))]
|
||||
async fn team_roster(
|
||||
Path(id): Path<u32>,
|
||||
State(state): State<AppState>,
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
debug!(team = id, "requesting team roster");
|
||||
let response = state.client.team_roster_history(id).await?;
|
||||
Ok(Json(response))
|
||||
}
|
||||
|
||||
#[instrument(skip(state))]
|
||||
async fn team_matches(
|
||||
Path(id): Path<u32>,
|
||||
State(state): State<AppState>,
|
||||
) -> Result<impl IntoResponse, ApiError> {
|
||||
debug!(team = id, "requesting team matches");
|
||||
let response = state.client.team_matches(id).await?;
|
||||
Ok(Json(response))
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue