use crate::Pool; use crate::account::account::Auth; use crate::error::{Error, Errors}; use crate::nnid::oauth::generate_token::create_token; use crate::nnid::oauth::generate_token::token_type::NEX_TOKEN; use crate::xml::Xml; use nex_account::grpc::Pid; use reqwest::header::SERVER; use rocket::{State, get}; use serde::Serialize; use sqlx::types::ipnetwork::IpNetwork::V4; use std::net::Ipv4Addr; const NO_IPV4_ERROR: Errors = Errors { error: &[Error { code: "1022", message: "Server is not a valid IPv4 address", }], }; const SERVER_ERROR: Errors = Errors { error: &[Error { code: "9999", message: "Internal Server Error", }], }; const NO_SERVER_ERROR: Errors = Errors { error: &[Error { code: "1021", message: "The requested game server was not found", }], }; const MAINTENANCE_ERROR: Errors = Errors { error: &[Error { code: "2002", message: "The requested game server is under maintenance", }], }; #[derive(Serialize)] #[serde(rename = "nex_token")] pub struct NexToken { host: Ipv4Addr, nex_password: String, pid: i32, port: u16, token: Box, } #[derive(Serialize)] #[serde(rename = "service_token")] pub struct ServiceToken { token: String, } #[get("/v1/api/provider/service_token/@me")] pub async fn get_service_token( pool: &State, auth: Auth, ) -> Result, Option>> { // just gonna put this here as a side note for the future: // we could also be using key derivation to derive the nex token as if it were a key // that way we could reduce the data the database needs to store and also reduce the transfer // cost of sending an entire row from the user table (which is required for the auth code unless // we change the way we read in data to essentially having the user object be a proxy for its // table row) let pool = pool.inner(); let token = create_token(pool, auth.pid, NEX_TOKEN, None).await; Ok(Xml(ServiceToken { token })) } #[get("/v1/api/provider/nex_token/@me?")] pub async fn get_nex_token( pool: &State, auth: Auth, game_server_id: &str, ) -> Result, Option>> { // just gonna put this here as a side note for the future: // we could also be using key derivation to derive the nex token as if it were a key // that way we could reduce the data the database needs to store and also reduce the transfer // cost of sending an entire row from the user table (which is required for the auth code unless // we change the way we read in data to essentially having the user object be a proxy for its // table row) let account_level = auth.account_level; let pool = pool.inner(); let server = match sqlx::query!( "select address, port, maintenance_mode from nex_servers where game_server_id = $1 AND server_account_level <= $2", game_server_id, account_level ) .fetch_optional(pool) .await .expect("database error") { Some(row) => row, None => return Err(Some(NO_SERVER_ERROR)), }; // only crash on db failure (not missing row) if server.maintenance_mode { return Err(Some(MAINTENANCE_ERROR)); } let V4(host) = server.address else { return Err(Some(NO_IPV4_ERROR)); }; let host = host.ip(); let mut client = nex_account::grpc_client().await.unwrap(); let Ok(key) = client.get_nex_key_by_pid(Pid { pid: auth.pid }).await else { println!("account does not exist on nex-account server"); return Err(Some(SERVER_ERROR)); }; let token = nex_account::gen_nexact_token( auth.pid, key.into_inner() .key .try_into() .map_err(|_| Some(SERVER_ERROR))?, ) .expect("NEX_ACCOUNT_KEYPAIR not set"); Ok(Xml(NexToken { host, port: server.port as u16, nex_password: auth.nex_password.clone(), pid: auth.pid, token, })) }