feat: add user-agent arg, moved actions in file
This commit is contained in:
parent
1808e98450
commit
0f310896d7
4 changed files with 91 additions and 58 deletions
1
src/admin/mod.rs
Normal file
1
src/admin/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod user;
|
63
src/admin/user.rs
Normal file
63
src/admin/user.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
use std::collections::HashSet;
|
||||
|
||||
use reqwest::header::USER_AGENT;
|
||||
use sea_orm::{DatabaseConnection, DbErr, EntityTrait, ActiveValue::NotSet, Set};
|
||||
|
||||
use crate::{entities::{user, property}, proto};
|
||||
|
||||
|
||||
pub async fn user_fetch_skin(db: &DatabaseConnection, user: &user::Model, agent: &str) -> Result<(), DbErr> {
|
||||
tracing::info!("Updating skin for user {} ({})", user.name, user.uuid);
|
||||
let url = format!("https://sessionserver.mojang.com/session/minecraft/profile/{}?unsigned=false", user.uuid.simple());
|
||||
let res = reqwest::Client::new()
|
||||
.get(url.clone()) //TODO: needs trimmed uuid, is it trimmed by default?
|
||||
.header(USER_AGENT, agent)
|
||||
.send().await
|
||||
.map_err(|_| DbErr::Custom(format!("could not fetch profile for user {}", user.uuid)))?
|
||||
.text().await
|
||||
.expect("invalid body on response");
|
||||
|
||||
let doc = serde_json::from_str::<proto::SessionUser>(&res)
|
||||
.map_err(|_| DbErr::Json(format!("invalid texture response: {}", res)))?;
|
||||
|
||||
let mut skin = proto::Property::default_skin();
|
||||
|
||||
for s in doc.properties.expect("missing properties field") {
|
||||
if s.name == "textures" {
|
||||
skin = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
property::Entity::insert(
|
||||
property::ActiveModel {
|
||||
id: NotSet,
|
||||
name: Set("textures".into()),
|
||||
user_id: Set(user.id),
|
||||
value: Set(skin.value),
|
||||
signature: Set(skin.signature),
|
||||
}
|
||||
).exec(db).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn user_fetch_skin_all(db: &DatabaseConnection, agent: &str) -> Result<(), DbErr> {
|
||||
let mut users_with_skins = HashSet::new();
|
||||
|
||||
for skin in property::Entity::find().all(db).await? {
|
||||
users_with_skins.insert(skin.user_id);
|
||||
}
|
||||
|
||||
for user in user::Entity::find().all(db).await? {
|
||||
if users_with_skins.contains(&user.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Err(e) = user_fetch_skin(db, &user, agent).await {
|
||||
tracing::error!("could not fetch skin for '{}': {}", user.name, e);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
16
src/main.rs
16
src/main.rs
|
@ -1,6 +1,7 @@
|
|||
mod proto;
|
||||
mod entities;
|
||||
mod routes;
|
||||
mod admin;
|
||||
mod persistence;
|
||||
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
@ -8,7 +9,7 @@ use std::{collections::HashMap, sync::Arc};
|
|||
use chrono::{DateTime, Utc, Duration};
|
||||
use clap::{Parser, Subcommand};
|
||||
use axum::{Router, routing::{get, post}, response::IntoResponse, Json, http::StatusCode};
|
||||
use routes::register::fill_missing_skins;
|
||||
use admin::user::user_fetch_skin_all;
|
||||
use sea_orm::{DatabaseConnection, Database};
|
||||
use tokio::sync::Mutex;
|
||||
use tracing_subscriber::filter::filter_fn;
|
||||
|
@ -31,6 +32,10 @@ struct CliArgs {
|
|||
/// Action to run
|
||||
#[clap(subcommand)]
|
||||
action: CliAction,
|
||||
|
||||
/// User agent to send when fetching minecraft data
|
||||
#[arg(short, long)]
|
||||
user_agent: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Clone, Debug)]
|
||||
|
@ -88,6 +93,7 @@ pub struct AppState {
|
|||
token_duration: u32,
|
||||
time_window: u32,
|
||||
fallback: bool,
|
||||
user_agent: String,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
|
@ -102,12 +108,16 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
|
||||
let db = Database::connect(cfg.database.clone()).await?;
|
||||
|
||||
let user_agent = cfg.user_agent.unwrap_or(
|
||||
format!("yggdrasil/{}", env!("CARGO_PKG_VERSION"))
|
||||
);
|
||||
|
||||
match cfg.action {
|
||||
CliAction::Clean { token_lifetime } => {
|
||||
purge_expired_tokens(&db, Duration::hours(token_lifetime.into())).await?;
|
||||
},
|
||||
CliAction::Skins { } => {
|
||||
fill_missing_skins(&db).await?;
|
||||
user_fetch_skin_all(&db, &user_agent).await?;
|
||||
},
|
||||
CliAction::Serve { bind_addr, token_duration, time_window, fallback } => {
|
||||
let secret = load_secret(&db).await?;
|
||||
|
@ -128,7 +138,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
// CUSTOM
|
||||
.route("/register/unmigrated", post(register_unmigrated))
|
||||
.fallback(fallback_route)
|
||||
.with_state(AppState { store, db, token_duration, time_window, fallback, secret });
|
||||
.with_state(AppState { store, db, token_duration, time_window, fallback, secret, user_agent });
|
||||
|
||||
info!(target: "MAIN", "serving Yggdrasil on {}", &addr);
|
||||
|
||||
|
|
|
@ -1,58 +1,12 @@
|
|||
use std::{time::Duration, collections::HashSet};
|
||||
use std::time::Duration;
|
||||
|
||||
use axum::{extract::State, Json};
|
||||
use reqwest::StatusCode;
|
||||
use sea_orm::{EntityTrait, QueryFilter, ColumnTrait, Set, ActiveValue::NotSet, DatabaseConnection, DbErr};
|
||||
use reqwest::{StatusCode, header::USER_AGENT};
|
||||
use sea_orm::{EntityTrait, QueryFilter, ColumnTrait, Set, ActiveValue::NotSet};
|
||||
use tracing::info;
|
||||
|
||||
use crate::{AppState, proto::{self, Response}, entities};
|
||||
|
||||
// TODO shouldn't be here probably
|
||||
pub async fn fill_missing_skins(db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||
let mut users_with_skins = HashSet::new();
|
||||
|
||||
for skin in entities::property::Entity::find().all(db).await? {
|
||||
users_with_skins.insert(skin.user_id);
|
||||
}
|
||||
|
||||
for user in entities::user::Entity::find().all(db).await? {
|
||||
if users_with_skins.contains(&user.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
info!("Updating skin for user {} ({})", user.name, user.uuid);
|
||||
let url = format!("https://sessionserver.mojang.com/session/minecraft/profile/{}?unsigned=false", user.uuid.simple());
|
||||
let res = reqwest::Client::new().get(url.clone()) //TODO: needs trimmed uuid, is it trimmed by default?
|
||||
.json(&()).send()
|
||||
.await.map_err(|_| DbErr::Custom(format!("could not fetch profile for user {}", user.uuid)))?
|
||||
.text().await.expect("invalid body on response");
|
||||
|
||||
let doc = serde_json::from_str::<proto::SessionUser>(&res)
|
||||
.map_err(|_| DbErr::Json("invalid texture response".into()))?;
|
||||
|
||||
let mut skin = proto::Property::default_skin();
|
||||
|
||||
for s in doc.properties.expect("missing properties field") {
|
||||
if s.name == "textures" {
|
||||
skin = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
entities::property::Entity::insert(
|
||||
entities::property::ActiveModel {
|
||||
id: NotSet,
|
||||
name: Set("textures".into()),
|
||||
user_id: Set(user.id),
|
||||
value: Set(skin.value),
|
||||
signature: Set(skin.signature),
|
||||
}
|
||||
).exec(db).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
//TODO: replace format! with axum's own
|
||||
pub async fn register_unmigrated(State(state): State<AppState>, Json(payload): Json<proto::RegisterRequest>) -> Response<proto::RegisterResponse> {
|
||||
info!(target: "REGISTER", "[UNMIGRATED] called with {:?}", payload);
|
||||
|
@ -60,11 +14,13 @@ pub async fn register_unmigrated(State(state): State<AppState>, Json(payload): J
|
|||
let form = proto::RefreshRequest {
|
||||
accessToken: payload.token.accessToken,
|
||||
clientToken: payload.token.clientToken,
|
||||
selectedProfile: Some(payload.token.selectedProfile.clone()),
|
||||
selectedProfile: None,
|
||||
requestUser: Some(true),
|
||||
};
|
||||
|
||||
let response = reqwest::Client::new().post("https://authserver.mojang.com/refresh")
|
||||
let response = reqwest::Client::new()
|
||||
.post("https://authserver.mojang.com/refresh")
|
||||
.header(USER_AGENT, &state.user_agent)
|
||||
.json(&form)
|
||||
.send().await
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, Json(proto::Error::simple(format!("mojang error : {:?}", e)))))?
|
||||
|
@ -99,10 +55,13 @@ pub async fn register_unmigrated(State(state): State<AppState>, Json(payload): J
|
|||
tokio::time::sleep(Duration::from_millis(500)).await; // avoid errors fetching skin due to timings
|
||||
|
||||
let url = format!("https://sessionserver.mojang.com/session/minecraft/profile/{}?unsigned=false", uuid.simple());
|
||||
let res = reqwest::Client::new().get(url.clone()) //TODO: needs trimmed uuid, is it trimmed by default?
|
||||
.json(&()).send()
|
||||
.await.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, Json(proto::Error::simple("internal server error"))))?
|
||||
.text().await.expect("invalid body on response");
|
||||
let res = reqwest::Client::new()
|
||||
.get(url.clone()) //TODO: needs trimmed uuid, is it trimmed by default?
|
||||
.header(USER_AGENT, &state.user_agent)
|
||||
.send().await
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, Json(proto::Error::simple("internal server error"))))?
|
||||
.text().await
|
||||
.expect("invalid body on response");
|
||||
|
||||
info!(target:"REGISTER", "Mojang response to texture fetch [{}]: {}", url, &res);
|
||||
let doc = serde_json::from_str::<proto::SessionUser>(&res)
|
||||
|
|
Loading…
Reference in a new issue