use crate::config::CONFIG; use crate::error::{APIError, APIErrorsResponse}; use crate::AppState; use actix_web::web::{Data, Json}; use actix_web::{post, HttpResponse}; use hmac::digest::KeyInit; use hmac::Hmac; use jwt::VerifyWithKey; use log::error; use sea_orm::{ActiveModelTrait, IntoActiveModel}; use serde::{Deserialize, Serialize}; use sha2::Sha256; use starkingdoms_protocol::api::APISavedPlayerData; use std::collections::BTreeMap; use std::time::{SystemTime, UNIX_EPOCH}; use ulid::Ulid; #[derive(Serialize, Deserialize)] pub struct BeamoutRequest { pub api_token: String, pub user_auth_realm_id: String, pub user_auth_token: String, pub data: APISavedPlayerData, } #[derive(Serialize, Deserialize)] pub struct BeamoutResponse {} #[post("/beamout")] pub async fn beam_out(data: Json, state: Data) -> HttpResponse { if !CONFIG.internal_tokens.contains(&data.api_token) { return HttpResponse::Unauthorized().json(APIErrorsResponse { errors: vec![APIError { code: "ERR_BAD_TOKEN".to_string(), message: "Missing or invalid api token".to_string(), path: None, }], }); } let key: Hmac = Hmac::new_from_slice(CONFIG.jwt_signing_secret.as_bytes()).unwrap(); let token: BTreeMap = match data.user_auth_token.verify_with_key(&key) { Ok(t) => t, Err(e) => { error!("verifying error: {}", e); return HttpResponse::Unauthorized().json(APIErrorsResponse { errors: vec![APIError { code: "ERR_BAD_TOKEN".to_string(), message: "Missing or invalid user token".to_string(), path: None, }], }); } }; if !token.contains_key("user") || !token.contains_key("nonce") { return HttpResponse::Unauthorized().json(APIErrorsResponse { errors: vec![APIError { code: "ERR_BAD_TOKEN".to_string(), message: "Missing or invalid user token (missing scopes)".to_string(), path: None, }], }); } let saved_data = toml::to_string(&data.data).unwrap(); let savefile_model = starkingdoms_api_entities::entity::user_savefile::Model { id: format!("save-{}", Ulid::new().to_string()), user: token.get("user").unwrap().clone(), data: saved_data, timestamp: SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_secs() as i64, }; let savefile_active_model = savefile_model.into_active_model(); match savefile_active_model.insert(&state.conn).await { Ok(_) => (), Err(e) => { error!("database error: {}", e); return HttpResponse::InternalServerError().json(APIErrorsResponse { errors: vec![APIError { code: "ERR_DB_ERROR".to_string(), message: "database failure".to_string(), path: None, }], }); } } HttpResponse::Ok().json(BeamoutResponse {}) }