trigger-rs/crates/dispatch-server/src/query_gateway.rs
traffic95 23fcde33f5 Properties calc port for 1.7beta (#7)
Reviewed-on: #7
Co-authored-by: traffic95 <traffic95@xeondev.com>
Co-committed-by: traffic95 <traffic95@xeondev.com>
2025-03-19 08:52:04 +00:00

201 lines
6.6 KiB
Rust

use axum::{
Json,
extract::{Query, State},
response::IntoResponse,
};
use base64::{Engine, display::Base64Display, engine::general_purpose::STANDARD};
use serde::{Deserialize, Serialize, Serializer};
use tracing::debug;
use trigger_cryptography::rsa;
use trigger_sv::config::RsaSetting;
use crate::{
AppState,
data::{
CdnConfExt, CdnDesignData, CdnGameRes, CdnSilenceData, RegionExtension, RegionSwitchFunc,
ServerDispatchData, ServerGateway,
},
};
pub const ROUTE_ENDPOINT: &str = "/query_gateway";
pub struct Response {
pub data: ServerDispatchData<'static, 'static, 'static>,
pub rsa: Option<&'static RsaSetting>,
}
#[derive(Debug, thiserror::Error)]
pub enum QueryGatewayError {
#[error("invalid rsa version specified: {0}")]
InvalidRsaVer(u32),
#[error("invalid dispatch seed, expected: {0}, got: {1}")]
InvalidDispatchSeed(&'static str, String),
}
impl QueryGatewayError {
pub fn retcode(&self) -> i32 {
match self {
Self::InvalidRsaVer(_) => 74,
Self::InvalidDispatchSeed(_, _) => 75,
}
}
pub fn message(&self) -> String {
String::with_capacity(0)
}
}
#[derive(Serialize)]
struct EncryptedAndSignedData {
#[serde(serialize_with = "to_base64")]
pub content: Vec<u8>,
#[serde(serialize_with = "to_base64")]
pub sign: Vec<u8>,
}
impl IntoResponse for Response {
fn into_response(self) -> axum::response::Response {
let Some(rsa) = self.rsa else {
return Json(self.data).into_response();
};
let json = serde_json::to_string(&self.data).unwrap();
Json(EncryptedAndSignedData {
content: rsa::encrypt(&rsa.client_public_key, json.as_bytes()),
sign: rsa::sign(&rsa.server_private_key, json.as_bytes()).into(),
})
.into_response()
}
}
#[derive(Deserialize)]
pub struct QueryGatewayParam {
pub rsa_ver: u32,
pub seed: String,
}
pub async fn process(
State(state): State<&'static AppState>,
Query(param): Query<QueryGatewayParam>,
) -> Response {
match internal_process(&state, param) {
Ok(response) => response,
Err((rsa, err)) => {
debug!("query_gateway failed: {err}");
Response {
rsa,
data: ServerDispatchData {
retcode: err.retcode(),
msg: err.message(),
..Default::default()
},
}
}
}
}
fn internal_process(
state: &'static AppState,
param: QueryGatewayParam,
) -> Result<Response, (Option<&'static RsaSetting>, QueryGatewayError)> {
use std::borrow::Cow::{Borrowed, Owned};
let rsa = state
.environment
.security
.get_rsa_setting_by_version(param.rsa_ver);
if rsa.is_none() {
return Err((None, QueryGatewayError::InvalidRsaVer(param.rsa_ver)));
};
(param.seed == state.config.bound_server.seed)
.then_some(())
.ok_or((
rsa,
QueryGatewayError::InvalidDispatchSeed(&state.config.bound_server.seed, param.seed),
))?;
let server = &state.config.bound_server;
Ok(Response {
rsa,
data: ServerDispatchData {
retcode: 0,
msg: String::with_capacity(0),
region_name: Borrowed(&server.name),
title: Borrowed(&server.title),
client_secret_key: Owned(
base64::engine::general_purpose::STANDARD
.encode(&state.environment.security.static_key.seed_buf),
),
cdn_check_url: String::with_capacity(0),
gateway: Some(ServerGateway {
ip: Borrowed(&server.addr),
port: server.port,
}),
oaserver_url: String::new(),
force_update_url: String::new(),
stop_jump_url: String::new(),
cdn_conf_ext: Some(CdnConfExt {
// TODO: unhardcode this
design_data: CdnDesignData {
base_url: Borrowed(
"https://autopatchcn.juequling.com/design_data/beta_live/output_7054632_323d17319c/client/",
),
data_revision: Borrowed("7054632"),
md5_files: Borrowed(
r#"[{"fileName": "data_version", "fileSize": 4503, "fileMD5": "419987357302147246"}]"#,
),
},
game_res: CdnGameRes {
audio_revision: Borrowed("7025371"),
base_url: Borrowed(
"https://autopatchcn.juequling.com/game_res/beta_live/output_7054632_323d17319c/client/",
),
branch: Borrowed("beta_live"),
md5_files: Borrowed(
r#"[{"fileName": "res_version", "fileSize": 2379030, "fileMD5": "15840336186912297231"}, {"fileName": "audio_version", "fileSize": 30435, "fileMD5": "15675397132378459243"}, {"fileName": "base_revision", "fileSize": 18, "fileMD5": "18079377284431001248"}]"#,
),
res_revision: Borrowed("7054632"),
},
silence_data: CdnSilenceData {
base_url: Borrowed(
"https://autopatchcn.juequling.com/design_data/beta_live/output_7054632_323d17319c/client_silence/",
),
md5_files: Borrowed(
r#"[{"fileName": "silence_version", "fileSize": 647, "fileMD5": "15019531890587528788"}]"#,
),
silence_revision: Borrowed("7042559"),
},
pre_download: None,
}),
region_ext: Some(RegionExtension {
exchange_url: String::new(),
feedback_url: String::new(),
func_switch: RegionSwitchFunc {
disable_frequent_attempts: 1,
enable_gacha_mobile_console: 1,
enable_notice_mobile_console: 1,
enable_performance_log: 1,
is_kcp: server.is_kcp as i32,
..Default::default()
},
mtr_nap: String::new(),
mtr_sdk: String::new(),
pgc_webview_method: 1,
url_check_nap: String::new(),
url_check_sdk: String::new(),
}),
},
})
}
pub fn to_base64<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.collect_str(&Base64Display::new(bytes, &STANDARD))
}