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::{ColumnTrait, EntityTrait, QueryFilter, QueryOrder};
use serde::{Deserialize, Serialize};
use sha2::Sha256;
use starkingdoms_protocol::api::APISavedPlayerData;
use std::collections::BTreeMap;
#[derive(Serialize, Deserialize)]
pub struct BeaminRequest {
pub api_token: String,
pub user_auth_realm_id: String,
pub user_auth_token: String,
}
#[derive(Serialize, Deserialize)]
pub struct BeaminResponse {
pub save_id: String,
pub save: APISavedPlayerData,
}
#[post("/beamin")]
pub async fn beam_in(data: Json<BeaminRequest>, 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 user_id = token.get("user").unwrap();
let user_savefile: Vec<starkingdoms_api_entities::entity::user_savefile::Model> =
match starkingdoms_api_entities::entity::user_savefile::Entity::find()
.filter(starkingdoms_api_entities::entity::user_savefile::Column::User.eq(user_id))
.order_by_desc(starkingdoms_api_entities::entity::user_savefile::Column::Timestamp)
.all(&state.conn)
.await
{
Ok(sf) => sf,
Err(e) => {
error!("database error: {}", e);
return HttpResponse::InternalServerError().json(APIErrorsResponse {
errors: vec![APIError {
code: "ERR_DB_ERROR".to_string(),
message: "Unable to fetch user savefiles".to_string(),
path: None,
}],
});
}
};
if user_savefile.is_empty() {
return HttpResponse::NoContent().json(APIErrorsResponse {
errors: vec![APIError {
code: "ERR_NO_SAVES".to_string(),
message: "This user has no savefiles".to_string(),
path: None,
}],
});
}
let save = &user_savefile[0];
let save_id = &save.id;
let save_data_str = &save.data;
let save_data: APISavedPlayerData =
toml::from_str(save_data_str).expect("database contained corrupted player save data");
HttpResponse::Ok().json(BeaminResponse {
save_id: save_id.clone(),
save: save_data,
})
}