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<BeamoutRequest>, state: Data<AppState>) -> 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<Sha256> = Hmac::new_from_slice(CONFIG.jwt_signing_secret.as_bytes()).unwrap();
let token: BTreeMap<String, String> = 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 {})
}