mirror of
https://git.ethanthesleepy.one/ethanaobrien/ew
synced 2026-08-26 15:02:18 +08:00
Render clearrates for private songs for that user
This commit is contained in:
@@ -251,6 +251,14 @@ pub fn non_public_music_ids() -> JsonValue {
|
|||||||
DATABASE.lock_and_select_all("SELECT music_id FROM songs WHERE visibility!='public' ORDER BY music_id", params!()).unwrap_or(array![])
|
DATABASE.lock_and_select_all("SELECT music_id FROM songs WHERE visibility!='public' ORDER BY music_id", params!()).unwrap_or(array![])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn non_public_music_ids_for(user_id: i64) -> JsonValue {
|
||||||
|
DATABASE.lock_and_select_all("
|
||||||
|
SELECT music_id FROM songs
|
||||||
|
WHERE visibility!='public' AND owner_id!=?1
|
||||||
|
AND music_id NOT IN (SELECT music_id FROM shared_users WHERE user_id=?1)
|
||||||
|
ORDER BY music_id", params!(user_id)).unwrap_or(array![])
|
||||||
|
}
|
||||||
|
|
||||||
// Which of these candidate ids no longer exist in the catalog. Only the custom
|
// Which of these candidate ids no longer exist in the catalog. Only the custom
|
||||||
// range is ever considered, so official songs can't come back from this. A song
|
// range is ever considered, so official songs can't come back from this. A song
|
||||||
// that's merely private/shared still has its row - only genuinely deleted ids
|
// that's merely private/shared still has its row - only genuinely deleted ids
|
||||||
|
|||||||
@@ -188,13 +188,9 @@ fn get_pass_percent(failed: i64, pass: i64) -> String {
|
|||||||
|
|
||||||
fn get_json() -> JsonValue {
|
fn get_json() -> JsonValue {
|
||||||
let lives = DATABASE.lock_and_select_all("SELECT live_id FROM lives", params!()).unwrap();
|
let lives = DATABASE.lock_and_select_all("SELECT live_id FROM lives", params!()).unwrap();
|
||||||
let hidden = crate::router::custom_song::hidden_live_ids();
|
|
||||||
let mut rates = array![];
|
let mut rates = array![];
|
||||||
let mut ids = array![];
|
let mut ids = array![];
|
||||||
for id in lives.members() {
|
for id in lives.members() {
|
||||||
if hidden.contains(id.as_i64().unwrap()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let info = DATABASE.get_live_data(id.as_i64().unwrap());
|
let info = DATABASE.get_live_data(id.as_i64().unwrap());
|
||||||
if info.is_err() {
|
if info.is_err() {
|
||||||
continue;
|
continue;
|
||||||
@@ -239,7 +235,24 @@ async fn get_clearrate_json() -> JsonValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
let hidden = crate::router::custom_song::hidden_live_ids_for_user(global::get_uid(req.headers()));
|
||||||
|
if !hidden.is_empty() {
|
||||||
|
let rates = data["all_user_clear_rate"].clone();
|
||||||
|
let ids = data["master_music_ids"].clone();
|
||||||
|
let mut new_rates = array![];
|
||||||
|
let mut new_ids = array![];
|
||||||
|
for (i, rate) in rates.members().enumerate() {
|
||||||
|
if hidden.contains(rate["master_live_id"].as_i64().unwrap()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
new_rates.push(rate.clone()).unwrap();
|
||||||
|
new_ids.push(ids[i].clone()).unwrap();
|
||||||
|
}
|
||||||
|
data["all_user_clear_rate"] = new_rates;
|
||||||
|
data["master_music_ids"] = new_ids;
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
|||||||
@@ -120,6 +120,13 @@ pub fn hidden_live_ids() -> JsonValue {
|
|||||||
database::non_public_music_ids()
|
database::non_public_music_ids()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn hidden_live_ids_for_user(uid: i64) -> JsonValue {
|
||||||
|
if disabled() {
|
||||||
|
return array![];
|
||||||
|
}
|
||||||
|
database::non_public_music_ids_for(uid)
|
||||||
|
}
|
||||||
|
|
||||||
fn song_path(music_id: i64, file: &str) -> String {
|
fn song_path(music_id: i64, file: &str) -> String {
|
||||||
get_data_path(&format!("custom_songs/{}/{}", music_id, file))
|
get_data_path(&format!("custom_songs/{}/{}", music_id, file))
|
||||||
}
|
}
|
||||||
@@ -1464,4 +1471,61 @@ mod tests {
|
|||||||
assert_eq!(call(&chart_md5, "json").status(), actix_web::http::StatusCode::NOT_FOUND);
|
assert_eq!(call(&chart_md5, "json").status(), actix_web::http::StatusCode::NOT_FOUND);
|
||||||
assert_eq!(call(&new_md5, "json").status(), actix_web::http::StatusCode::OK);
|
assert_eq!(call(&new_md5, "json").status(), actix_web::http::StatusCode::OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The JSON clear-rate endpoint filters non-public custom songs per requesting
|
||||||
|
// user: the owner sees all of theirs, a shared user sees the songs shared with
|
||||||
|
// them, everyone else sees only public ones. Official (stock) live ids are
|
||||||
|
// always visible. The parallel master_music_ids array must stay index-aligned
|
||||||
|
// with all_user_clear_rate after filtering.
|
||||||
|
#[test]
|
||||||
|
fn clearrate_hides_custom_songs_per_user() {
|
||||||
|
use actix_web::{test::TestRequest, Responder};
|
||||||
|
use crate::router::clear_rate;
|
||||||
|
let _lock = crate::runtime::lock_test_data_path();
|
||||||
|
crate::runtime::set_enable_custom_songs(true);
|
||||||
|
|
||||||
|
let owner = 5001;
|
||||||
|
let shared_user = 5002;
|
||||||
|
let outsider = 5003;
|
||||||
|
|
||||||
|
let public_id = database::next_music_id();
|
||||||
|
database::insert_song(public_id, owner, &object!{music_id: public_id}, "public", &array![], false);
|
||||||
|
let private_id = database::next_music_id();
|
||||||
|
database::insert_song(private_id, owner, &object!{music_id: private_id}, "private", &array![], false);
|
||||||
|
let shared_id = database::next_music_id();
|
||||||
|
database::insert_song(shared_id, owner, &object!{music_id: shared_id}, "shared", &array![shared_user], false);
|
||||||
|
// A stock live id, outside the custom range - never filtered
|
||||||
|
let stock_id: i64 = 1_500_123;
|
||||||
|
|
||||||
|
for id in [public_id, private_id, shared_id, stock_id] {
|
||||||
|
clear_rate::live_completed(id, 1, false, 100, owner);
|
||||||
|
}
|
||||||
|
clear_rate::invalidate_cache();
|
||||||
|
|
||||||
|
// master_live_ids the endpoint serves to this uid, with an index-alignment guard
|
||||||
|
let visible_to = |uid: i64| -> Vec<i64> {
|
||||||
|
let req = TestRequest::default().insert_header(("aoharu-user-id", uid.to_string())).to_http_request();
|
||||||
|
let body = actix_web::rt::System::new().block_on(async {
|
||||||
|
let resp = clear_rate::clearrate(req.clone()).await.respond_to(&req).map_into_boxed_body();
|
||||||
|
actix_web::body::to_bytes(resp.into_body()).await.unwrap()
|
||||||
|
});
|
||||||
|
let json = jzon::parse(&crate::encryption::decrypt_packet(&String::from_utf8_lossy(&body)).unwrap()).unwrap();
|
||||||
|
let rates = &json["data"]["all_user_clear_rate"];
|
||||||
|
let ids = &json["data"]["master_music_ids"];
|
||||||
|
assert_eq!(rates.len(), ids.len(), "parallel arrays must stay aligned for uid {}", uid);
|
||||||
|
rates.members().map(|r| r["master_live_id"].as_i64().unwrap()).collect()
|
||||||
|
};
|
||||||
|
let sees = |uid: i64, id: i64| visible_to(uid).contains(&id);
|
||||||
|
|
||||||
|
// Owner sees every song of theirs plus the stock id
|
||||||
|
assert!(sees(owner, public_id) && sees(owner, private_id) && sees(owner, shared_id) && sees(owner, stock_id));
|
||||||
|
// Shared user sees public + shared + stock, never the private one
|
||||||
|
assert!(sees(shared_user, public_id) && sees(shared_user, shared_id) && sees(shared_user, stock_id));
|
||||||
|
assert!(!sees(shared_user, private_id));
|
||||||
|
// Outsider and anonymous (uid 0) see public + stock only
|
||||||
|
for uid in [outsider, 0] {
|
||||||
|
assert!(sees(uid, public_id) && sees(uid, stock_id));
|
||||||
|
assert!(!sees(uid, private_id) && !sees(uid, shared_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -855,7 +855,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(asset_gate(current, "Windows", ""), None);
|
assert_eq!(asset_gate(current, "Windows", ""), None);
|
||||||
assert_eq!(asset_gate(current, "android", ""), None);
|
assert_eq!(asset_gate(current, "android", ""), None);
|
||||||
assert_eq!(asset_gate(current, "ios", ""), Some(global::RESULT_GAME_VERSION_UPDATED));
|
assert_eq!(asset_gate(current, "ios", ""), None);
|
||||||
|
|
||||||
assert_eq!(asset_gate(stock, "android", ""), None);
|
assert_eq!(asset_gate(stock, "android", ""), None);
|
||||||
assert_eq!(asset_gate(stock, "ios", ""), None);
|
assert_eq!(asset_gate(stock, "ios", ""), None);
|
||||||
|
|||||||
Reference in New Issue
Block a user