1 Commits
0.1 ... 0.2

9 changed files with 757 additions and 25 deletions

7
Cargo.lock generated
View File

@@ -556,6 +556,7 @@ name = "hoyo-sdk"
version = "0.1.0"
dependencies = [
"axum",
"http-body-util",
"password-hash",
"pbkdf2",
"rand",
@@ -595,12 +596,12 @@ dependencies = [
[[package]]
name = "http-body-util"
version = "0.1.2"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
dependencies = [
"bytes",
"futures-util",
"futures-core",
"http",
"http-body",
"pin-project-lite",

View File

@@ -22,6 +22,7 @@ thiserror = "2.0.11"
regex = "1.11.1"
tracing = "0.1.41"
tracing-subscriber = "0.3.19"
http-body-util = "0.1.3"
[profile.release]
strip = true # Automatically strip symbols from the binary.

View File

@@ -0,0 +1,98 @@
use std::collections::HashMap;
use axum::extract::Query;
use tracing::{error, info};
use super::*;
pub fn routes() -> Router<AppStateRef> {
Router::new()
.route("/combo/box/api/config/sdk/combo", get(config_sdk_combo))
.route("/combo/box/api/config/sw/precache", get(config_sw_precache))
}
#[derive(Deserialize, Debug)]
#[allow(unused)]
struct ConfigSDKComboRequest {
pub biz_key: String,
pub client_type: u32,
}
#[derive(Serialize)]
struct ConfigSDKComboResponseData {
pub vals: HashMap<String, String>,
}
async fn config_sdk_combo(
query: Query<ConfigSDKComboRequest>,
) -> Json<Response<ConfigSDKComboResponseData>> {
info!("GET /combo/box/api/config/sdk/combo - {:?}", &query);
match query.client_type {
3 => Json(Response::new(ConfigSDKComboResponseData {
vals: HashMap::<_, _>::from([
(
String::from("httpdns_plus_config"),
String::from(
"{ \"enable\": 1, \"cache_expire_time\": 300, \"ip_report_enable\":1}",
),
),
(
String::from("login_record_config"),
String::from("{\"is_checked\":true}"),
),
(
String::from("network_report_config"),
String::from(
"{ \"enable\": 1, \"status_codes\": [206], \"url_paths\": [\"dataUpload\"] }",
),
),
(String::from("list_goods_work_mode"), String::from("1")),
(
String::from("kibana_pc_config"),
String::from(
"{ \"enable\": 1, \"level\": \"Info\",\"modules\": [\"download\"] }",
),
),
(
String::from("h5log_filter_config"),
String::from(
"{\n\t\"function\": {\n\t\t\"event_name\": [\"info_get_cps\", \"notice_close_notice\", \"info_get_uapc\", \"report_set_info\", \"info_get_channel_id\", \"info_get_sub_channel_id\"]\n\t}\n}",
),
),
(
String::from("webview_apm_config"),
String::from("{ \"crash_capture_enable\": true }"),
),
(
String::from("webview_rendermethod_config"),
String::from("{ \"useLegacy\": false }"),
),
(String::from("enable_web_dpi"), String::from("true")),
]),
})),
_ => {
error!("Unknown client_type");
Json(Response::error(7, "RetCode_NoConfig"))
}
}
}
#[derive(Serialize)]
struct ConfigSWPrecacheResponseData {
pub vals: HashMap<String, String>,
}
async fn config_sw_precache() -> Json<Response<ConfigSWPrecacheResponseData>> {
info!("GET /combo/box/api/config/sw/precache");
Json(Response::new(ConfigSWPrecacheResponseData {
vals: HashMap::<_, _>::from([
(
String::from("url"),
String::from("https://sdk.mihoyo.com/sw.html"),
),
(String::from("enable"), String::from("true")),
]),
}))
}

View File

@@ -1,25 +1,39 @@
use std::collections::HashMap;
use axum::extract::Query;
use tracing::{error, info};
use super::*;
#[derive(Deserialize)]
struct RequestData {
struct GranterLoginRequestData {
pub uid: String,
pub token: String,
}
#[derive(Deserialize)]
struct GranterTokenRequest {
struct GranterLoginRequest {
pub data: String,
}
pub fn routes() -> Router<AppStateRef> {
Router::new().route(
"/{product_name}/combo/granter/login/v2/login",
post(login_v2),
)
Router::new()
.route(
"/{product_name}/combo/granter/login/v2/login",
post(login_v2),
)
.route(
"/{product_name}/combo/granter/api/getConfig",
get(api_get_config),
)
.route(
"/{product_name}/combo/granter/api/compareProtocolVersion",
post(compare_protocol_version),
)
}
#[derive(Serialize)]
struct ResponseData {
struct GranterLoginResponseData {
pub account_type: u32,
pub combo_id: String,
pub combo_token: String,
@@ -30,26 +44,37 @@ struct ResponseData {
async fn login_v2(
state: State<AppStateRef>,
request: Json<GranterTokenRequest>,
) -> Json<Response<ResponseData>> {
let Ok(data) = serde_json::from_str::<RequestData>(&request.data) else {
request: Json<GranterLoginRequest>,
) -> Json<Response<GranterLoginResponseData>> {
info!(
"POST /{{product_name}}/combo/granter/login/v2/login - {}",
&request.data
);
let Ok(data) = serde_json::from_str::<GranterLoginRequestData>(&request.data) else {
error!("Couldn't convert data to GranterLoginRequestData");
return Json(Response::error(-101, "Account token error"));
};
let Ok(uid) = data.uid.parse() else {
error!("Couldn't convert parse uid as int");
return Json(Response::error(-101, "Account token error"));
};
match state.db.get_account_by_uid(uid).await {
Ok(Some(account)) if account.token == data.token => {
info!("Logged in to account \"{}\"", account.username.as_str());
success_rsp(data.uid.clone(), account.token)
}
_ => Json(Response::error(-101, "Account token error")),
_ => {
error!("Couldn't find account with specified uid");
Json(Response::error(-101, "Account token error"))
}
}
}
fn success_rsp(uid: String, token: String) -> Json<Response<ResponseData>> {
Json(Response::new(ResponseData {
fn success_rsp(uid: String, token: String) -> Json<Response<GranterLoginResponseData>> {
Json(Response::new(GranterLoginResponseData {
account_type: 1,
combo_id: uid.clone(),
combo_token: token,
@@ -58,3 +83,127 @@ fn success_rsp(uid: String, token: String) -> Json<Response<ResponseData>> {
open_id: uid,
}))
}
#[derive(Deserialize, Debug)]
#[allow(unused)]
struct GranterApiGetConfigRequest {
pub app_id: u32,
pub channel_id: u32,
pub client_type: u32,
}
#[derive(Serialize)]
struct GranterApiGetConfigResponseData {
pub protocol: bool,
pub qr_enabled: bool,
pub log_level: String,
pub announce_url: String,
pub push_alias_type: u32,
pub disable_ysdk_guard: bool,
pub enable_announce_pic_popup: bool,
pub app_name: String,
pub qr_enabled_apps: Option<HashMap<String, bool>>,
pub qr_app_icons: Option<HashMap<String, String>>,
pub qr_cloud_display_name: String,
pub enable_user_center: bool,
pub functional_switch_configs: HashMap<String, String>,
pub ugc_protocol: bool,
}
async fn api_get_config(
query: Query<GranterApiGetConfigRequest>,
) -> Json<Response<GranterApiGetConfigResponseData>> {
info!(
"GET {{productName}}/combo/granter/api/getConfig - {:?}",
query
);
Json(Response::new(GranterApiGetConfigResponseData {
protocol: false,
qr_enabled: true,
log_level: String::from("INFO"),
announce_url: String::from(
"https://sdk.mihoyo.com/nap/btannouncement/index.html?sdk_presentation_style=fullscreen&sdk_screen_transparent=true&auth_appid=announcement&game=nap&game_biz=nap_cn&authkey_ver=1&sign_type=2&version=2.30#/",
),
push_alias_type: 0,
disable_ysdk_guard: false,
enable_announce_pic_popup: true,
app_name: String::from("Zenless Zone Zero"),
qr_enabled_apps: (query.client_type == 3).then_some(HashMap::<_, _>::from([
(String::from("bbs"), true),
(String::from("cloud"), false),
])),
qr_app_icons: (query.client_type == 3).then_some(HashMap::<_, _>::from([
(String::from("app"), String::new()),
(String::from("bbs"), String::new()),
(String::from("cloud"), String::new()),
])),
qr_cloud_display_name: String::from("Zenless Zone Zero Cloud"),
enable_user_center: true,
functional_switch_configs: HashMap::<_, _>::new(),
ugc_protocol: true,
}))
}
#[derive(Deserialize, Debug)]
#[allow(unused)]
struct GranterCompareProtocolVersionRequest {
pub app_id: u32,
pub channel_id: u32,
pub language: String,
pub major: String,
pub minimum: String,
}
#[derive(Serialize)]
struct GranterCompareProtocolVersionResponseData {
pub modified: bool,
pub protocol: Option<GranterCompareProtocolVersionProtocolResponseData>,
}
#[derive(Serialize)]
struct GranterCompareProtocolVersionProtocolResponseData {
pub id: u32,
pub app_id: u32,
pub language: String,
pub user_proto: String,
pub priv_proto: String,
pub major: u32,
pub minimum: u32,
pub create_time: String,
pub teenager_proto: String,
pub third_proto: String,
pub full_priv_proto: String,
}
async fn compare_protocol_version(
body: Json<GranterCompareProtocolVersionRequest>,
) -> Json<Response<GranterCompareProtocolVersionResponseData>> {
info!(
"POST /{{product_name}}/combo/granter/api/compareProtocolVersion - {:?}",
body
);
Json(Response::new(GranterCompareProtocolVersionResponseData {
modified: true,
protocol: Some(GranterCompareProtocolVersionProtocolResponseData {
id: 0,
app_id: body.app_id,
language: body.language.clone(),
user_proto: String::new(),
priv_proto: String::new(),
major: body.major.parse().unwrap_or(match body.app_id {
11 => 0,
_ => 6,
}),
minimum: body.minimum.parse().unwrap_or(match body.app_id {
11 => 14,
_ => 1,
}),
create_time: String::from("0"),
teenager_proto: String::new(),
third_proto: String::new(),
full_priv_proto: String::new(),
}),
}))
}

91
src/handlers/device_fp.rs Normal file
View File

@@ -0,0 +1,91 @@
use axum::extract::Query;
use tracing::{error, info};
use super::*;
pub fn routes() -> Router<AppStateRef> {
Router::new()
.route("/device-fp/api/getExtList", get(get_ext_list))
.route("/device-fp/api/getFp", post(get_fp))
.route(
"/data_abtest_api/config/experiment/list",
post(data_abtest_api),
)
}
#[derive(Deserialize, Debug)]
struct DeviceGetExtListRequestData {
pub platform: u32,
}
#[derive(Serialize)]
struct DeviceGetExtListResponseData {
pub code: u32,
pub msg: String,
pub ext_list: Vec<&'static str>,
pub pkg_list: Vec<String>,
pub pkg_str: String,
}
async fn get_ext_list(
query: Query<DeviceGetExtListRequestData>,
) -> Json<Response<DeviceGetExtListResponseData>> {
info!("GET /device-fp/api/getExtList - {:?}", &query);
match query.platform {
3 => Json(Response::new(DeviceGetExtListResponseData {
code: 200,
msg: String::from("ok"),
ext_list: vec![
"cpuName",
"deviceModel",
"deviceName",
"deviceType",
"deviceUID",
"gpuID",
"gpuName",
"gpuAPI",
"gpuVendor",
"gpuVersion",
"gpuMemory",
"osVersion",
"cpuCores",
"cpuFrequency",
"gpuVendorID",
"isGpuMultiTread",
"memorySize",
"screenSize",
"engineName",
"addressMAC",
"packageVersion",
],
pkg_list: vec![],
pkg_str: String::from("/vK5WTh5SS3SAj8Zm0qPWg=="),
})),
_ => {
error!("Unsupported platform");
Json(Response::new(DeviceGetExtListResponseData {
code: 401,
msg: String::from("Unsupported platform"),
ext_list: vec![],
pkg_list: vec![],
pkg_str: String::new(),
}))
}
}
}
#[derive(Serialize)]
struct DeviceGetFPResponseData {}
async fn get_fp(body: String) -> Json<Response<DeviceGetFPResponseData>> {
info!("POST /device-fp/api/getFp - {:?}", &body);
Json(Response::new(DeviceGetFPResponseData {}))
}
async fn data_abtest_api(body: String) -> &'static str {
info!("POST /data_abtest_api/config/experiment/list - {:?}", &body);
r#"{"retcode":0,"success":true,"message":"","data":[{"code":1000,"type":1,"config_id":"6486","period_id":"6486_691","version":"1","configs":{"webViewRenderMethod":"none"},"sceneWhiteList":false,"experimentWhiteList":false},{"code":1000,"type":2,"config_id":"145","period_id":"6486_691","version":"1","configs":{"webViewRenderMethod":"none"},"sceneWhiteList":false,"experimentWhiteList":false}]}"#
}

237
src/handlers/ma_passport.rs Normal file
View File

@@ -0,0 +1,237 @@
use tracing::{error, info};
use super::*;
pub fn routes() -> Router<AppStateRef> {
Router::new()
.route(
"/{product_name}/account/ma-passport/api/appLoginByPassword",
post(app_login_by_password),
)
.route(
"/{product_name}/account/ma-passport/token/verifySToken",
post(verify_s_token),
)
}
#[derive(Deserialize, Debug)]
struct LoginRequest {
pub account: String,
pub password: String,
}
#[derive(Serialize)]
struct LoginResponseData {
pub token: TokenData,
pub user_info: UserInfoData,
pub ext_user_info: ExtUserInfoData,
pub reactivate_action_ticket: String,
pub bind_email_action_ticket: String,
}
#[derive(Serialize)]
struct TokenData {
pub token_type: u32,
pub token: String,
}
#[derive(Serialize)]
struct UserInfoData {
pub aid: String,
pub mid: String,
pub account_name: String,
pub email: String,
pub is_email_verify: u32,
pub area_code: String,
pub mobile: String,
pub safe_area_code: String,
pub safe_mobile: String,
pub realname: String,
pub identity_code: String,
pub rebind_area_code: String,
pub rebind_mobile: String,
pub rebind_mobile_time: String,
pub links: Vec<LinkData>,
pub country: String,
pub password_time: String,
pub is_adult: u32,
pub unmasked_email: String,
pub unmasked_email_type: u32,
}
#[derive(Serialize, Default)]
struct LinkData {
pub thirdparty: String,
pub union_id: String,
pub nickname: String,
pub email: String,
#[serde(rename = "subType")]
pub sub_type: String,
pub sub_union_id: String,
}
#[derive(Serialize, Default)]
struct ExtUserInfoData {
pub guardian_email: String,
pub birth: String,
}
async fn app_login_by_password(
state: State<AppStateRef>,
request: Json<LoginRequest>,
) -> Json<Response<LoginResponseData>> {
info!(
"POST /{{product_name}}/account/ma-passport/api/appLoginByPassword - {:?}",
&request
);
let Ok(login) = crate::util::rsa_decrypt(&request.account) else {
error!("Couldn't decrypt account (login)");
return Json(Response::error(
-10,
"Your patch is outdated, get a new one at https://discord.gg/reversedrooms (Password decryption failed)",
));
};
let Ok(password) = crate::util::rsa_decrypt(&request.password) else {
error!("Couldn't decrypt password");
return Json(Response::error(
-10,
"Your patch is outdated, get a new one at https://discord.gg/reversedrooms (Password decryption failed)",
));
};
let account = match state.db.get_account_by_name(login.clone()).await {
Ok(Some(account)) => account,
Ok(None) => {
error!("Couldn't find account with specified login \"{login}\"");
return Json(Response::error(-101, "Account or password error"));
}
Err(err) => {
error!("database error: {err}");
return Json(Response::error(-1, "Internal server error"));
}
};
if !account.password.verify(&password) {
error!("Password doesn't match");
return Json(Response::error(-101, "Account or password error"));
}
info!("Logged in to account \"{}\"", account.username.as_str());
Json(Response::new(LoginResponseData {
token: TokenData {
token_type: 1,
token: account.token,
},
user_info: UserInfoData {
aid: account.uid.to_string(),
mid: account.uid.to_string(),
account_name: String::new(),
email: account.username.as_str().to_string(),
is_email_verify: 0,
area_code: String::from("**"),
mobile: String::new(),
safe_area_code: String::new(),
safe_mobile: String::new(),
realname: String::new(),
identity_code: String::new(),
rebind_area_code: String::new(),
rebind_mobile: String::new(),
rebind_mobile_time: String::from("315532800"),
links: Vec::new(),
country: String::from("RU"),
password_time: String::from("1762297200"),
is_adult: 0,
unmasked_email: String::new(),
unmasked_email_type: 0,
},
ext_user_info: ExtUserInfoData {
guardian_email: String::new(),
birth: String::from("0"),
},
reactivate_action_ticket: String::new(),
bind_email_action_ticket: String::new(),
}))
}
#[derive(Deserialize, Debug)]
struct VerifySTokenRequest {
pub mid: String,
pub stoken: String,
}
#[derive(Serialize)]
struct VerifySTokenResponseData {
pub user_info: UserInfoData,
pub tokens: Vec<TokenData>,
pub ext_user_info: ExtUserInfoData,
}
async fn verify_s_token(
state: State<AppStateRef>,
request: Json<VerifySTokenRequest>,
) -> Json<Response<VerifySTokenResponseData>> {
info!(
"POST /{{product_name}}/account/ma-passport/token/verifySToken - {:?}",
&request
);
let Ok(uid) = request.mid.parse() else {
error!("Couldn't convert parse mid as int");
return Json(Response::error(-101, "Account cache error"));
};
let account = match state.db.get_account_by_uid(uid).await {
Ok(Some(account)) => account,
Ok(None) => {
error!("Couldn't find account with specified uid");
return Json(Response::error(-101, "Account cache error"));
}
Err(err) => {
tracing::error!("database error: {err}");
return Json(Response::error(-1, "Internal server error"));
}
};
if account.token == request.stoken {
info!("Logged in to account \"{}\"", account.username.as_str());
Json(Response::new(VerifySTokenResponseData {
user_info: UserInfoData {
aid: account.uid.to_string(),
mid: account.uid.to_string(),
account_name: String::new(),
email: account.username.as_str().to_string(),
is_email_verify: 0,
area_code: String::from("**"),
mobile: String::new(),
safe_area_code: String::new(),
safe_mobile: String::new(),
realname: String::new(),
identity_code: String::new(),
rebind_area_code: String::new(),
rebind_mobile: String::new(),
rebind_mobile_time: String::from("315532800"),
links: Vec::new(),
country: String::from("RU"),
password_time: String::from("1762297200"),
is_adult: 0,
unmasked_email: String::new(),
unmasked_email_type: 0,
},
tokens: vec![TokenData {
token_type: 1,
token: account.token,
}],
ext_user_info: ExtUserInfoData {
guardian_email: String::new(),
birth: String::from("0"),
},
}))
} else {
error!("Token doesn't match");
Json(Response::error(
-101,
"For account safety, please log in again",
))
}
}

View File

@@ -1,24 +1,39 @@
use std::collections::HashMap;
use axum::extract::Query;
use tracing::{error, info};
use super::*;
pub fn routes() -> Router<AppStateRef> {
Router::new()
.route("/{product_name}/mdk/shield/api/login", post(login))
.route("/{product_name}/mdk/shield/api/verify", post(verify))
.route(
"/{product_name}/mdk/shield/api/loadConfig",
get(load_config),
)
}
#[derive(Deserialize)]
#[derive(Deserialize, Debug)]
struct LoginRequest {
pub account: String,
pub password: String,
pub is_crypto: bool,
}
#[derive(Deserialize)]
#[derive(Deserialize, Debug)]
struct VerifyRequest {
pub uid: String,
pub token: String,
}
#[derive(Deserialize, Debug)]
struct LoadConfigRequest {
pub client: u32,
pub game_key: String,
}
#[derive(Serialize, Default)]
struct ResponseData {
pub account: ResponseAccountData,
@@ -38,11 +53,54 @@ struct ResponseAccountData {
pub uid: String,
}
#[derive(Serialize, Default)]
struct ResponseLoadConfigData {
pub id: u32,
pub game_key: String,
pub client: String,
pub identity: String,
pub guest: bool,
pub ignore_version: String,
pub scene: String,
pub name: String,
pub disable_regist: bool,
pub enable_email_captcha: bool,
pub thirdparty: Vec<String>,
pub disable_mmt: bool,
pub server_guest: bool,
pub thirdparty_ignore: HashMap<String, String>,
pub enable_ps_bind_account: bool,
pub thirdparty_login_configs: HashMap<String, String>,
pub initialize_firebase: bool,
pub bbs_auth_login: bool,
pub bbs_auth_login_ignore: Vec<String>,
pub fetch_instance_id: bool,
pub enable_flash_login: bool,
pub enable_logo_18: bool,
pub logo_height: String,
pub logo_width: String,
pub enable_cx_bind_account: bool,
pub firebase_blacklist_devices_switch: bool,
pub firebase_blacklist_devices_version: u32,
pub hoyolab_auth_login: bool,
pub hoyolab_auth_login_ignore: Vec<String>,
pub hoyoplay_auth_login: bool,
pub enable_douyin_flash_login: bool,
pub enable_age_gate: bool,
pub enable_age_gate_ignore: Vec<String>,
}
async fn login(
state: State<AppStateRef>,
request: Json<LoginRequest>,
) -> Json<Response<ResponseData>> {
info!(
"POST /{{product_name}}/mdk/shield/api/login - {:?}",
&request
);
if !request.is_crypto {
error!("Unencrypted passwords are disabled by SDK security policy");
return Json(Response::error(
-10,
"Invalid account format: unencrypted passwords are disabled by SDK security policy",
@@ -50,22 +108,31 @@ async fn login(
}
let Ok(password) = crate::util::rsa_decrypt(&request.password) else {
return Json(Response::error(-10, "Your patch is outdated, get a new one at https://discord.gg/reversedrooms (Password decryption failed)"));
error!("Couldn't decrypt password");
return Json(Response::error(
-10,
"Your patch is outdated, get a new one at https://discord.gg/reversedrooms (Password decryption failed)",
));
};
let account = match state.db.get_account_by_name(request.account.clone()).await {
Ok(Some(account)) => account,
Ok(None) => return Json(Response::error(-101, "Account or password error")),
Ok(None) => {
error!("Couldn't find account with specified login");
return Json(Response::error(-101, "Account or password error"));
}
Err(err) => {
tracing::error!("database error: {err}");
error!("database error: {err}");
return Json(Response::error(-1, "Internal server error"));
}
};
if !account.password.verify(&password) {
error!("Password doesn't match");
return Json(Response::error(-101, "Account or password error"));
}
info!("Logged in to account \"{}\"", account.username.as_str());
Json(Response::new(ResponseData {
account: ResponseAccountData {
area_code: String::from("**"),
@@ -83,20 +150,30 @@ async fn verify(
state: State<AppStateRef>,
request: Json<VerifyRequest>,
) -> Json<Response<ResponseData>> {
info!(
"POST /{{product_name}}/mdk/shield/api/verify - {:?}",
&request
);
let Ok(uid) = request.uid.parse() else {
error!("Couldn't convert parse uid as int");
return Json(Response::error(-101, "Account cache error"));
};
let account = match state.db.get_account_by_uid(uid).await {
Ok(Some(account)) => account,
Ok(None) => return Json(Response::error(-101, "Account cache error")),
Ok(None) => {
error!("Couldn't find account with specified uid");
return Json(Response::error(-101, "Account cache error"));
}
Err(err) => {
tracing::error!("database error: {err}");
error!("database error: {err}");
return Json(Response::error(-1, "Internal server error"));
}
};
if account.token == request.token {
info!("Logged in to account \"{}\"", account.username.as_str());
Json(Response::new(ResponseData {
account: ResponseAccountData {
area_code: String::from("**"),
@@ -109,9 +186,63 @@ async fn verify(
..Default::default()
}))
} else {
error!("Token doesn't match");
Json(Response::error(
-101,
"For account safety, please log in again",
))
}
}
async fn load_config(query: Query<LoadConfigRequest>) -> Json<Response<ResponseLoadConfigData>> {
info!(
"GET /{{product_name}}/mdk/shield/api/loadConfig - {:?}",
&query
);
match query.client {
1..=3 => Json(Response::new(ResponseLoadConfigData {
id: 28 + query.client,
game_key: query.game_key.clone(),
client: String::from(match query.client {
1 => "IOS",
2 => "Android",
3 => "PC",
_ => unreachable!("Invalid query client"),
}),
identity: String::from("I_IDENTITY"),
guest: false,
ignore_version: String::new(),
scene: String::from("S_NORMAL"),
name: String::from("Nap"),
disable_regist: false,
enable_email_captcha: false,
thirdparty: Vec::<_>::new(),
disable_mmt: false,
server_guest: (query.client == 1),
thirdparty_ignore: HashMap::<_, _>::new(),
enable_ps_bind_account: false,
thirdparty_login_configs: HashMap::<_, _>::new(),
initialize_firebase: false,
bbs_auth_login: (query.client != 3),
bbs_auth_login_ignore: Vec::<_>::new(),
fetch_instance_id: false,
enable_flash_login: (query.client != 3),
enable_logo_18: false,
logo_height: String::from("0"),
logo_width: String::from("0"),
enable_cx_bind_account: false,
firebase_blacklist_devices_switch: false,
firebase_blacklist_devices_version: 0,
hoyolab_auth_login: false,
hoyolab_auth_login_ignore: Vec::<_>::new(),
hoyoplay_auth_login: (query.client == 3),
enable_douyin_flash_login: false,
enable_age_gate: false,
enable_age_gate_ignore: Vec::<_>::new(),
})),
_ => {
error!("Unknown client type");
Json(Response::error(-104, "Missing configuration"))
}
}
}

View File

@@ -1,13 +1,16 @@
use crate::AppStateRef;
use axum::{
Form, Json, Router,
extract::State,
response::Html,
routing::{get, post},
Form, Json, Router,
};
use serde::{Deserialize, Serialize};
pub mod combo_box_api;
pub mod combo_granter;
pub mod device_fp;
pub mod ma_passport;
pub mod mdk_shield_api;
pub mod register;
pub mod risky_api;

View File

@@ -6,7 +6,9 @@ use std::{
use axum::Router;
use config::SdkConfig;
use database::DbContext;
use handlers::{combo_granter, mdk_shield_api, register, risky_api};
use handlers::{
combo_box_api, combo_granter, device_fp, ma_passport, mdk_shield_api, register, risky_api,
};
use tokio::net::TcpListener;
use tracing::error;
@@ -51,6 +53,10 @@ async fn main() -> ExitCode {
.merge(register::routes())
.merge(mdk_shield_api::routes())
.merge(combo_granter::routes())
.merge(combo_box_api::routes())
.merge(device_fp::routes())
.merge(ma_passport::routes())
.fallback(handle_404)
.with_state(STATE.get().unwrap());
let listener = TcpListener::bind(&CONFIG.http_addr)
@@ -65,3 +71,18 @@ async fn main() -> ExitCode {
fn init_tracing() {
tracing_subscriber::fmt().without_time().init();
}
async fn handle_404(request: axum::extract::Request) -> (axum::http::StatusCode, String) {
let uri = request.uri().clone();
error!("404 - request: {} {}", request.method(), request.uri());
error!("404 - headers: {:?}", request.headers());
if request.method() == axum::http::Method::POST {
use http_body_util::BodyExt;
let body = request.collect().await.unwrap().to_bytes();
error!("404 - body: {:?}", body);
}
(
axum::http::StatusCode::NOT_FOUND,
format!("No route for {}", uri),
)
}