mirror of
https://git.ethanthesleepy.one/ethanaobrien/ew
synced 2026-07-12 00:32:20 +08:00
Add client side header check for if client supports custom songs
This commit is contained in:
@@ -230,8 +230,29 @@ async fn get_clearrate_json() -> JsonValue {
|
|||||||
rv
|
rv
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn strip_custom_clear_rates(mut data: JsonValue) -> JsonValue {
|
||||||
|
let ids = data["master_music_ids"].clone();
|
||||||
|
let rates = data["all_user_clear_rate"].clone();
|
||||||
|
let mut kept_ids = array![];
|
||||||
|
let mut kept_rates = array![];
|
||||||
|
for (i, id) in ids.members().enumerate() {
|
||||||
|
if id.as_i64().unwrap_or(0) >= crate::database::custom_song::FIRST_MUSIC_ID {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
kept_ids.push(id.clone()).unwrap();
|
||||||
|
kept_rates.push(rates[i].clone()).unwrap();
|
||||||
|
}
|
||||||
|
data["master_music_ids"] = kept_ids;
|
||||||
|
data["all_user_clear_rate"] = kept_rates;
|
||||||
|
data
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn clearrate(req: HttpRequest) -> impl Responder {
|
pub async fn clearrate(req: HttpRequest) -> impl Responder {
|
||||||
global::api(&req, Some(get_clearrate_json().await))
|
let mut data = get_clearrate_json().await;
|
||||||
|
if !crate::router::custom_song::client_supports_custom_songs(&req) {
|
||||||
|
data = strip_custom_clear_rates(data);
|
||||||
|
}
|
||||||
|
global::api(&req, Some(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn ranking(req: HttpRequest, body: String) -> impl Responder {
|
pub async fn ranking(req: HttpRequest, body: String) -> impl Responder {
|
||||||
|
|||||||
@@ -80,6 +80,19 @@ pub fn disabled() -> bool {
|
|||||||
args.hidden || !args.enable_custom_songs
|
args.hidden || !args.enable_custom_songs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A client that understands custom songs advertises it with this exact header
|
||||||
|
// on its API requests. Old / official clients don't send it, so we must NOT
|
||||||
|
// inject custom-song data (custom master_music_ids) into the shared /api/user
|
||||||
|
// response for them - the unresolvable ids would break the account
|
||||||
|
const SUPPORT_HEADER: &str = "X-Custom-Songs";
|
||||||
|
|
||||||
|
pub fn client_supports_custom_songs(req: &HttpRequest) -> bool {
|
||||||
|
req.headers()
|
||||||
|
.get(SUPPORT_HEADER)
|
||||||
|
.and_then(|v| v.to_str().ok())
|
||||||
|
== Some("1")
|
||||||
|
}
|
||||||
|
|
||||||
// The catalog is filtered per requesting user: private songs only show for
|
// The catalog is filtered per requesting user: private songs only show for
|
||||||
// their owner, shared songs for the owner plus their shared-user list
|
// their owner, shared songs for the owner plus their shared-user list
|
||||||
async fn list(req: HttpRequest, body: String) -> impl Responder {
|
async fn list(req: HttpRequest, body: String) -> impl Responder {
|
||||||
@@ -1247,4 +1260,43 @@ mod tests {
|
|||||||
});
|
});
|
||||||
jzon::parse(&String::from_utf8_lossy(&body)).unwrap()
|
jzon::parse(&String::from_utf8_lossy(&body)).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// With the feature enabled, custom unlock ids are appended to /api/user ONLY
|
||||||
|
// for clients that send X-Custom-Songs: 1. An old/official client (no
|
||||||
|
// header, or a different value) gets its official unlock list untouched.
|
||||||
|
#[test]
|
||||||
|
fn unlock_ids_gated_on_support_header() {
|
||||||
|
use actix_web::test::TestRequest;
|
||||||
|
let _lock = crate::runtime::lock_test_data_path();
|
||||||
|
|
||||||
|
let owner = 4242;
|
||||||
|
let music_id = database::next_music_id();
|
||||||
|
database::insert_song(music_id, owner, &object!{music_id: music_id}, "public", &array![], false);
|
||||||
|
// Sanity: the feature is on, so the id is visible to get_music_ids
|
||||||
|
assert!(get_music_ids(owner).contains(music_id));
|
||||||
|
|
||||||
|
// Exactly what the /api/user handler appends to master_music_ids
|
||||||
|
let appended = |req: &HttpRequest| -> JsonValue {
|
||||||
|
if client_supports_custom_songs(req) {
|
||||||
|
get_music_ids(owner)
|
||||||
|
} else {
|
||||||
|
array![]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// No header -> unsupported -> nothing appended
|
||||||
|
let without = TestRequest::default().to_http_request();
|
||||||
|
assert!(!client_supports_custom_songs(&without));
|
||||||
|
assert!(appended(&without).is_empty());
|
||||||
|
|
||||||
|
// Correct header -> supported -> custom id appended
|
||||||
|
let with = TestRequest::default().insert_header(("X-Custom-Songs", "1")).to_http_request();
|
||||||
|
assert!(client_supports_custom_songs(&with));
|
||||||
|
assert!(appended(&with).contains(music_id));
|
||||||
|
|
||||||
|
// Any other value is treated as an unsupporting client
|
||||||
|
let wrong = TestRequest::default().insert_header(("X-Custom-Songs", "true")).to_http_request();
|
||||||
|
assert!(!client_supports_custom_songs(&wrong));
|
||||||
|
assert!(appended(&wrong).is_empty());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,9 +65,10 @@ async fn user(req: HttpRequest) -> impl Responder {
|
|||||||
|
|
||||||
user["lottery_list"] = array![];
|
user["lottery_list"] = array![];
|
||||||
|
|
||||||
// Custom songs visible to this user are unlocked
|
if crate::router::custom_song::client_supports_custom_songs(&req) {
|
||||||
for id in crate::router::custom_song::get_music_ids(user["user"]["id"].as_i64().unwrap()).members() {
|
for id in crate::router::custom_song::get_music_ids(user["user"]["id"].as_i64().unwrap()).members() {
|
||||||
user["master_music_ids"].push(id.as_i64().unwrap()).unwrap();
|
user["master_music_ids"].push(id.as_i64().unwrap()).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
global::api(&req, Some(user))
|
global::api(&req, Some(user))
|
||||||
|
|||||||
Reference in New Issue
Block a user