rust-nex/rnex-core/src/nex/datastore.rs

785 lines
No EOL
34 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use cfg_if::cfg_if;
cfg_if! {
if #[cfg(feature = "datastore")] {
use crate::define_rmc_proto;
use macros::rmc_struct;
use rnex_core::prudp::socket_addr::PRUDPSockAddr;
use std::sync::{Weak};
use chrono::Utc;
use sqlx::types::time;
use sqlx::types::time::PrimitiveDateTime;
use rnex_core::PID;
use rnex_core::nex::remote_console::RemoteConsole;
use rnex_core::nex::s3presigner::S3Presigner;
use rnex_core::rmc::response::ErrorCode;
use rnex_core::rmc::protocols::secure::{Secure, RawSecure, RawSecureInfo, RemoteSecure};
use rnex_core::rmc::protocols::datastore::{BufferQueueParam, CompletePostParam, DataStoreCustomRankingResult, DataStoreGetCustomRankingByDataIDParam, DataStorePrepareGetParam, DataStoreReqGetInfo, DataStoreSearchParam, GetMetaInfo, GetMetaParam, KeyValue, Permission, PersistenceTarget, RateCustomRankingParam, RatingInfo, RatingInfoWithSlot};
use rnex_core::rmc::protocols::datastore::{DataStore, RawDataStore, RawDataStoreInfo, RemoteDataStore, PreparePostParam, ReqPostInfo};
use crate::nex::user::User;
use rnex_core::executables::common::{RNEX_DATASTORE_S3_BUCKET, RNEX_DATASTORE_S3_ENDPOINT, get_db};
use rnex_core::rmc::structures::qbuffer::QBuffer;
use sqlx::types::chrono::DateTime;
use rnex_core::kerberos::KerberosDateTime;
use rnex_core::rmc::structures::qresult::QResult;
fn map_row_to_meta_info(
row_data_id: i64,
row_owner: i32,
row_size: i32,
row_name: String,
row_data_type: i16,
row_meta_binary: Vec<u8>,
row_permission: i16,
row_permission_recipients: Vec<i64>,
row_delete_permission: i16,
row_delete_permission_recipients: Vec<i64>,
row_period: i16,
row_refer_data_id: i64,
row_flag: i32,
row_tags: Vec<String>,
row_creation_date: chrono::NaiveDateTime,
row_update_date: chrono::NaiveDateTime,
ratings: Vec<RatingInfoWithSlot>
) -> GetMetaInfo {
GetMetaInfo {
dataid: row_data_id as u64,
owner: row_owner as u32,
size: row_size as u32,
name: row_name,
data_type: row_data_type as u16,
meta_binary: QBuffer(row_meta_binary),
permission: Permission {
permission: row_permission as u8,
recipient_ids: row_permission_recipients.into_iter().map(|id| id as u32).collect(),
},
del_permission: Permission {
permission: row_delete_permission as u8,
recipient_ids: row_delete_permission_recipients.into_iter().map(|id| id as u32).collect(),
},
period: row_period as u16,
status: 0,
referred_count: 0,
refer_dat_id: row_refer_data_id as u32,
flag: row_flag as u32,
tags: row_tags,
expire_time: KerberosDateTime::from_u64(0x9C3F3E0000),
created_time: KerberosDateTime::from_naive(row_creation_date),
updated_time: KerberosDateTime::from_naive(row_update_date),
referred_time: KerberosDateTime::from_naive(row_creation_date),
ratings,
}
}
async fn check_object_availability(data_id: u64, password: u64) -> Result<(), ErrorCode> {
let row = sqlx::query!(
r#"
SELECT under_review, access_password
FROM datastore.objects
WHERE data_id = $1 AND upload_completed = TRUE AND deleted = FALSE
"#,
data_id as i64
)
.fetch_optional(get_db())
.await
.map_err(|e| {
eprintln!("Availability check DB error: {:?}", e);
ErrorCode::DataStore_SystemFileError
})?
.ok_or(ErrorCode::DataStore_NotFound)?;
let access_password = row.access_password as u64;
if access_password != 0 && access_password != password {
return Err(ErrorCode::DataStore_InvalidPassword);
}
if row.under_review {
return Err(ErrorCode::DataStore_UnderReviewing);
}
Ok(())
}
async fn get_object_ratings(data_id: u64, password: u64) -> Result<Vec<RatingInfoWithSlot>, ErrorCode> {
check_object_availability(data_id, password).await?;
let rows = sqlx::query!(
r#"
SELECT slot, total_value, count, initial_value
FROM datastore.object_ratings
WHERE data_id = $1
"#,
data_id as i64
)
.fetch_all(get_db())
.await
.map_err(|e| {
eprintln!("Ratings fetch error: {:?}", e);
ErrorCode::DataStore_SystemFileError
})?;
let ratings = rows.into_iter().map(|row| {
RatingInfoWithSlot {
slot: row.slot as i8,
rating: RatingInfo {
total_value: row.total_value.unwrap_or(0),
count: row.count as u32,
initial_value: row.initial_value.unwrap_or(0),
},
}
}).collect();
Ok(ratings)
}
async fn get_object_info_by_data_id(data_id: u64, password: u64) -> Result<GetMetaInfo, ErrorCode> {
check_object_availability(data_id, password).await?;
let row = sqlx::query!(
r#"SELECT data_id, owner, size, name, data_type, meta_binary,
permission, permission_recipients, delete_permission, delete_permission_recipients,
period, refer_data_id, flag, tags, creation_date, update_date
FROM datastore.objects WHERE data_id = $1"#,
data_id as i64
)
.fetch_optional(get_db())
.await
.map_err(|_| ErrorCode::DataStore_SystemFileError)?
.ok_or(ErrorCode::DataStore_NotFound)?;
let ratings = get_object_ratings(data_id, password).await?;
Ok(map_row_to_meta_info(
row.data_id,
row.owner.unwrap_or(0),
row.size.unwrap_or(0),
row.name.unwrap_or_default(),
row.data_type.unwrap_or(0) as i16,
row.meta_binary.unwrap_or_default(),
row.permission.unwrap_or(0) as i16,
row.permission_recipients.unwrap_or_default().into_iter().map(|id| id as i64).collect(),
row.delete_permission.unwrap_or(0) as i16,
row.delete_permission_recipients.unwrap_or_default().into_iter().map(|id| id as i64).collect(),
row.period.unwrap_or(0) as i16,
row.refer_data_id.unwrap_or(0),
row.flag.unwrap_or(0),
row.tags.unwrap_or_default(),
row.creation_date.map(|dt| chrono::NaiveDateTime::new(
chrono::NaiveDate::from_ymd_opt(dt.year(), dt.month() as u32, dt.day() as u32).unwrap(),
chrono::NaiveTime::from_hms_opt(dt.hour() as u32, dt.minute() as u32, dt.second() as u32).unwrap()
)).unwrap_or_default(),
row.update_date.map(|dt| chrono::NaiveDateTime::new(
chrono::NaiveDate::from_ymd_opt(dt.year(), dt.month() as u32, dt.day() as u32).unwrap(),
chrono::NaiveTime::from_hms_opt(dt.hour() as u32, dt.minute() as u32, dt.second() as u32).unwrap()
)).unwrap_or_default(),
ratings
))
}
async fn get_object_info_by_persistence_target(target: PersistenceTarget, password: u64) -> Result<GetMetaInfo, ErrorCode> {
let row = sqlx::query!(
r#"SELECT data_id, owner, size, name, data_type, meta_binary,
permission, permission_recipients, delete_permission, delete_permission_recipients,
period, refer_data_id, flag, tags, creation_date, update_date,
access_password, under_review
FROM datastore.objects
WHERE owner = $1 AND persistence_slot_id = $2
AND upload_completed = TRUE AND deleted = FALSE"#,
target.owner as i32,
target.persistence_slot_id as i16
)
.fetch_optional(get_db())
.await
.map_err(|_| ErrorCode::DataStore_SystemFileError)?
.ok_or(ErrorCode::DataStore_NotFound)?;
let db_password = row.access_password as u64;
if db_password != 0 && db_password != password {
return Err(ErrorCode::DataStore_InvalidPassword);
}
if row.under_review {
return Err(ErrorCode::DataStore_UnderReviewing);
}
let ratings = get_object_ratings(row.data_id as u64, password).await?;
Ok(map_row_to_meta_info(
row.data_id,
row.owner.unwrap_or(0),
row.size.unwrap_or(0),
row.name.unwrap_or_default(),
row.data_type.unwrap_or(0) as i16,
row.meta_binary.unwrap_or_default(),
row.permission.unwrap_or(0) as i16,
row.permission_recipients.unwrap_or_default().into_iter().map(|id| id as i64).collect(),
row.delete_permission.unwrap_or(0) as i16,
row.delete_permission_recipients.unwrap_or_default().into_iter().map(|id| id as i64).collect(),
row.period.unwrap_or(0) as i16,
row.refer_data_id.unwrap_or(0),
row.flag.unwrap_or(0),
row.tags.unwrap_or_default(),
row.creation_date.map(|dt| chrono::NaiveDateTime::new(
chrono::NaiveDate::from_ymd_opt(dt.year(), dt.month() as u32, dt.day() as u32).unwrap(),
chrono::NaiveTime::from_hms_opt(dt.hour() as u32, dt.minute() as u32, dt.second() as u32).unwrap()
)).unwrap_or_default(),
row.update_date.map(|dt| chrono::NaiveDateTime::new(
chrono::NaiveDate::from_ymd_opt(dt.year(), dt.month() as u32, dt.day() as u32).unwrap(),
chrono::NaiveTime::from_hms_opt(dt.hour() as u32, dt.minute() as u32, dt.second() as u32).unwrap()
)).unwrap_or_default(),
ratings
))
}
async fn get_buffer_queues_by_data_id_and_slot(
data_id: u64,
slot: u32
) -> Result<Vec<QBuffer>, ErrorCode> {
check_object_availability(data_id, 0).await?;
let rows = sqlx::query!(
r#"
SELECT buffer
FROM datastore.buffer_queues
WHERE data_id = $1 AND slot = $2
ORDER BY creation_date ASC
"#,
data_id as i64,
slot as i32
)
.fetch_all(get_db())
.await
.map_err(|e| {
log::error!("Buffer queue fetch error: {:?}", e);
ErrorCode::DataStore_SystemFileError
})?;
let buffer_queues = rows
.into_iter()
.map(|row| QBuffer(row.buffer))
.collect();
Ok(buffer_queues)
}
fn verify_object_permission(
owner_id: u32,
viewer_id: u32,
permission: &Permission,
) -> Result<(), ErrorCode> {
if owner_id == viewer_id {
return Ok(());
}
match permission.permission {
0 => Ok(()), // All can read
1 => Err(ErrorCode::DataStore_PermissionDenied), // Friends only, unimplemented
2 => {
// Recipient IDs can read
if permission.recipient_ids.contains(&viewer_id) {
Ok(())
} else {
Err(ErrorCode::DataStore_PermissionDenied)
}
}
3 => Err(ErrorCode::DataStore_PermissionDenied), // Owner only, redundant
_ => Err(ErrorCode::DataStore_InvalidArgument), // ??? haxx0r
}
}
fn filter_properties_by_result_option(
meta_info: &mut GetMetaInfo,
result_option: u8,
) {
if (result_option & 0x01) == 0 {
meta_info.meta_binary = QBuffer(Vec::new());
}
if (result_option & 0x04) == 0 {
meta_info.ratings = Vec::new();
}
// No idea what the other things do. :shrug:
}
// Dawg...
async fn get_custom_rankings_by_data_ids(
application_id: u32,
data_ids: Vec<u64>
) -> Vec<DataStoreCustomRankingResult> {
let mut results = Vec::with_capacity(data_ids.len());
let rows = sqlx::query!(
r#"
SELECT
rankings.data_id,
rankings.value
FROM datastore.object_custom_rankings rankings
JOIN UNNEST($1::bigint[]) WITH ORDINALITY AS rows(data_id, ord)
ON rankings.data_id = rows.data_id
AND rankings.application_id = $2
ORDER BY rows.ord
"#,
&data_ids.iter().map(|&id| id as i64).collect::<Vec<i64>>(),
application_id as i32
)
.fetch_all(get_db())
.await;
let rows = match rows {
Ok(r) => r,
Err(e) => {
log::error!("Custom ranking query error: {:?}", e);
return results;
}
};
for row in rows {
let data_id = row.data_id as u64;
let score = row.value.unwrap_or(0) as u32;
if let Ok(meta) = get_object_info_by_data_id(data_id, 0).await {
results.push(DataStoreCustomRankingResult {
order: 0,
score,
meta_info: meta,
});
} else {
log::warn!("Could not find metadata for ranked object {}", data_id);
}
}
results
}
async fn get_user_course_object_ids(owner_pid: u32) -> Result<Vec<u64>, ErrorCode> {
let rows = sqlx::query!(
r#"
SELECT data_id
FROM datastore.objects
WHERE owner = $1 AND data_type > 2 AND data_type < 50
"#,
owner_pid as i64
)
.fetch_all(get_db())
.await
.map_err(|e| {
log::error!("error fetching course IDs for PID {}: {:?}", owner_pid, e);
ErrorCode::DataStore_SystemFileError
})?;
let mut valid_ids = Vec::new();
for row in rows {
let data_id = row.data_id as u64;
// always check avail
if check_object_availability(data_id, 0).await.is_ok() {
valid_ids.push(data_id);
}
}
Ok(valid_ids)
}
fn get_blacklist_1() -> Vec<String> {
vec![
"けされ", "消され", "削除され", "リセットされ", "BANされ", "BANされ",
"キミのコース", "君のコース", "きみのコース", "い い ね", "遊びます", "地震",
"震災", "被災", "津波", "バンされ", "い~ね", "震度", "じしん", "banされ",
"くわしくは", "詳しくは", "ちんちん", "ち0こ", "bicth", "い.い.ね",
"ナイ~ス", "い&い", "い-いね", "いぃね", "nigger", "ngger", "star if u",
"Star if u", "Star if you", "star if you", "PENlS", "マンコ", "butthole",
"LILI", "vagina", "vagyna", "うんち", "うんこ", "ウンコ", "",
"EENE", "まんこ", "ウンチ", "niglet", "nigglet", "please like", "きんたま",
"Butthole", "llね", "iいね", "give a star", "ちんぽ", "亀頭", "penis",
"ウンコ", "plz more stars", "star plz", "い()ね", "PLEASE star", "Bitte Sterne",
].into_iter().map(String::from).collect()
}
fn get_blacklist_2() -> Vec<String> {
vec![
"ゼロから", "0から", "0から", "い  い  ね", "いい", "東日本", "大震",
].into_iter().map(String::from).collect()
}
fn get_blacklist_3() -> Vec<String> {
vec![
"いいね", "下さい", "ください", "押して", "おして", "返す", "かえす",
"", "してくれ", "するよ", "☆くれたら", "☆あげます", "★くれたら",
"★あげます", "しね", "ころす", "ころされた", "アナル", "ファック",
"キンタマ", "○ね", "キチガイ", "うんこ", "KITIGAI", "金玉", "おっぱい",
"☆おす", "☆押す", "★おす", "★押す", "いいする", "いいよ", "イイネ",
"ケツ", "うんち", "かくせいざい", "覚せい剤", "シャブ", "きんたま",
"ちんちん", "おしっこ", "ちんぽこ", "ころして", "グッド", "グット",
"レ●プ", "バーカ", "きちがい", "ちんげ", "マンコ", "まんこ", "チンポ",
"クズ", "ウンコ", "ナイスおねがいします", "penis", "イイね", "☆よろ",
"ナイス!して", "ま/んこ", "まん/こ",
].into_iter().map(String::from).collect()
}
impl DataStore for User {
async fn get_meta(&self, mut metaparam: GetMetaParam) -> Result<GetMetaInfo, ErrorCode> {
let mut meta_info = if metaparam.dataid != 0 {
get_object_info_by_data_id(metaparam.dataid, metaparam.access_password).await?
} else {
get_object_info_by_persistence_target(metaparam.persistence_target, metaparam.access_password).await?
};
let current_pid = self.pid;
verify_object_permission(meta_info.owner, current_pid, &meta_info.permission)?;
filter_properties_by_result_option(&mut meta_info, metaparam.result_option);
Ok(meta_info)
}
async fn prepare_post_object(&self, postparam: PreparePostParam) -> Result<ReqPostInfo, ErrorCode> {
let recipient_ids: Vec<i32> = postparam.permission.recipient_ids.iter().map(|&id| id as i32).collect();
let del_recipient_ids: Vec<i32> = postparam.del_permission.recipient_ids.iter().map(|&id| id as i32).collect();
let now = time::OffsetDateTime::now_utc();
let row = sqlx::query!(
r#"
INSERT INTO datastore.objects (
owner, size, name, data_type, meta_binary,
permission, permission_recipients,
delete_permission, delete_permission_recipients,
flag, period, refer_data_id, tags,
persistence_slot_id, extra_data, creation_date, update_date
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17
) RETURNING data_id
"#,
self.pid as i32,
postparam.size as i32,
postparam.name,
postparam.data_type as i32,
&postparam.meta_binary.0,
postparam.permission.permission as i32,
&recipient_ids,
postparam.del_permission.permission as i32,
&del_recipient_ids,
postparam.flag as i32,
postparam.period as i32,
postparam.refer_data_id as i64,
&postparam.tags,
postparam.persistence_init_param.persistence_slot_id as i32,
&postparam.extra_data,
time::PrimitiveDateTime::new(now.date(), now.time()),
time::PrimitiveDateTime::new(now.date(), now.time())
)
.fetch_one(get_db())
.await
.map_err(|e| {
log::error!("DB Error: {:?}", e);
ErrorCode::DataStore_SystemFileError
})?;
let data_id = row.data_id as u64;
let presigner = S3Presigner::new(
&format!("https://{}", *RNEX_DATASTORE_S3_ENDPOINT),
format!("{}", *RNEX_DATASTORE_S3_BUCKET)
).await;
let key = format!("data/{}.bin", data_id);
let (upload_url, fields) = presigner.generate_presigned_post(&key).await;
let form_fields = fields.into_iter().map(|(k, v)| {
KeyValue { key: k, value: v }
}).collect();
Ok(ReqPostInfo {
dataid: data_id,
url: upload_url,
request_headers: vec![],
form_fields,
root_ca_cert: vec![],
})
}
async fn complete_post_object(&self, completeparam: CompletePostParam) -> Result<(), ErrorCode> {
log::info!("Data ID: {:?}", completeparam.dataid);
log::info!("Success: {:?}", completeparam.success);
let record = sqlx::query!(
r#"SELECT owner, under_review FROM datastore.objects WHERE data_id = $1"#,
completeparam.dataid as i64
)
.fetch_optional(get_db())
.await
.map_err(|e| {
eprintln!("select error: {:?}", e);
ErrorCode::DataStore_SystemFileError
})?;
let record = record.ok_or(ErrorCode::DataStore_NotFound)?;
if record.under_review {
return Err(ErrorCode::DataStore_UnderReviewing);
}
if record.owner.unwrap_or(0) as u32 != self.pid {
return Err(ErrorCode::DataStore_PermissionDenied);
}
if completeparam.success {
sqlx::query!(
r#"UPDATE datastore.objects SET upload_completed = true WHERE data_id = $1"#,
completeparam.dataid as i64
)
.execute(get_db())
.await
.map_err(|e| {
eprintln!("update error: {:?}", e);
ErrorCode::DataStore_SystemFileError
})?;
} else {
return Err(ErrorCode::Transport_TemporaryServerError);
}
Ok(())
}
async fn rate_custom_ranking(&self, rankingparam: Vec<RateCustomRankingParam>) -> Result<(), ErrorCode> {
for abcparam in rankingparam {
let exists = sqlx::query_scalar!(
r#"SELECT EXISTS(SELECT 1 FROM datastore.objects WHERE data_id = $1)"#,
abcparam.dataid as i64
)
.fetch_one(get_db())
.await
.map_err(|_| ErrorCode::DataStore_SystemFileError)?;
if !exists.unwrap_or(false) {
return Err(ErrorCode::DataStore_NotFound);
}
sqlx::query!(
r#"
INSERT INTO datastore.object_custom_rankings (data_id, application_id, value)
VALUES ($1, $2, $3)
ON CONFLICT (data_id, application_id)
DO UPDATE SET value = datastore.object_custom_rankings.value + EXCLUDED.value
"#,
abcparam.dataid as i64,
abcparam.appid as i32,
abcparam.score as i32
)
.execute(get_db())
.await
.map_err(|e| {
log::error!("update/insert error: {:?}", e);
ErrorCode::DataStore_SystemFileError
})?;
}
Ok(())
}
async fn get_application_config(&self, appid: u32) -> Result<Vec<i32>, ErrorCode> {
const MAX_COURSE_UPLOADS: i32 = 100;
let config = match appid {
0 => vec![
0x00000001, 0x00000032, 0x00000096, 0x0000012c, 0x000001f4,
0x00000320, 0x00000514, 0x000007d0, 0x00000bb8, 0x00001388,
MAX_COURSE_UPLOADS, 0x00000014, 0x0000001e, 0x00000028, 0x00000032,
0x0000003c, 0x00000046, 0x00000050, 0x0000005a, 0x00000064,
0x00000023, 0x0000004b, 0x00000023, 0x0000004b, 0x00000032,
0x00000000, 0x00000003, 0x00000003, 0x00000064, 0x00000006,
0x00000001, 0x00000060, 0x00000005, 0x00000060, 0x00000000,
0x000007e4, 0x00000001, 0x00000001, 0x0000000c, 0x00000000,
],
1 => vec![
2,
1770179696,
1770179664,
1770179640,
1770180827,
1770180777,
1770180745,
1770177625,
1770177590,
],
2 => vec![0x000007df, 0x0000000c, 0x00000016, 0x00000005, 0x00000000],
10 => vec![35, 75, 96, 40, 5, 6],
_ => {
log::error!("unknown SMM app id: {}", appid);
return Err(ErrorCode::DataStore_Unknown);
}
};
Ok(config)
}
async fn get_custom_ranking_by_data_id(
&self,
custom_ranking_param: DataStoreGetCustomRankingByDataIDParam
) -> Result<(Vec<DataStoreCustomRankingResult>, Vec<QResult>), ErrorCode> {
println!("appid: {:?}", custom_ranking_param.application_id);
println!("dataid list: {:?}", custom_ranking_param.data_id_list);
println!("result option: {:?}", custom_ranking_param.result_option);
let mut ranking_results = get_custom_rankings_by_data_ids(custom_ranking_param.application_id, custom_ranking_param.data_id_list).await;
let mut q_results = Vec::with_capacity(ranking_results.len());
for result in &mut ranking_results {
if (custom_ranking_param.result_option & 0x01) == 0 {
result.meta_info.tags = Vec::new();
}
if (custom_ranking_param.result_option & 0x02) == 0 {
result.meta_info.ratings = Vec::new();
}
if (custom_ranking_param.result_option & 0x04) == 0 {
result.meta_info.meta_binary = QBuffer(Vec::new());
}
if (custom_ranking_param.result_option & 0x20) == 0 {
result.score = 0;
}
q_results.push(QResult::success(ErrorCode::Core_Unknown));
}
Ok((ranking_results, q_results))
}
async fn get_buffer_queue(&self, bufferparam: BufferQueueParam) -> Result<Vec<QBuffer>, ErrorCode> {
// log::info!("GetBufferQueue: dataid={}, slot={}", param.dataid, param.slot);
let buffers = get_buffer_queues_by_data_id_and_slot(bufferparam.dataid, bufferparam.slot).await?;
Ok(buffers)
}
async fn prepare_get_object(&self, prepare_get_param: DataStorePrepareGetParam) -> Result<DataStoreReqGetInfo, ErrorCode> {
let meta_info = if prepare_get_param.dataid != 0 {
get_object_info_by_data_id(prepare_get_param.dataid, prepare_get_param.access_password).await?
} else {
get_object_info_by_persistence_target(prepare_get_param.persistence_target, prepare_get_param.access_password).await?
};
verify_object_permission(meta_info.owner, self.pid, &meta_info.permission)?;
let presigner = S3Presigner::new(
&format!("https://{}", *RNEX_DATASTORE_S3_ENDPOINT),
format!("{}", *RNEX_DATASTORE_S3_BUCKET)
).await;
let key = format!("data/{}.bin", meta_info.dataid);
let download_url = presigner.generate_presigned_get(&key);
Ok(DataStoreReqGetInfo {
url: download_url,
request_headers: vec![],
size: meta_info.size,
root_ca_cert: vec![],
dataid: meta_info.dataid,
})
}
async fn followings_latest_course_search_object(
&self,
course_search_param: DataStoreSearchParam,
_extra_data: Vec<String>
) -> Result<Vec<DataStoreCustomRankingResult>, ErrorCode> {
let mut all_results = Vec::new();
for &owner_pid in &course_search_param.owner_ids {
let course_ids = get_user_course_object_ids(owner_pid).await?;
if course_ids.is_empty() {
continue;
}
let mut results = get_custom_rankings_by_data_ids(0, course_ids).await;
// Flag 0x1: Return Tags
// Flag 0x2: Return Ratings
// Flag 0x4: Return MetaBinary
// Flag 0x20: Return Score
for res in &mut results {
if course_search_param.result_option & 0x1 == 0 {
res.meta_info.tags = Vec::new();
}
if course_search_param.result_option & 0x2 == 0 {
res.meta_info.ratings = Vec::new();
}
if course_search_param.result_option & 0x4 == 0 {
res.meta_info.meta_binary = rnex_core::rmc::structures::qbuffer::QBuffer(Vec::new());
}
if course_search_param.result_option & 0x20 == 0 {
res.score = 0;
}
}
all_results.extend(results);
}
// note: we assume the client sorts the data lol
Ok(all_results)
}
async fn get_application_config_string(&self, application_id: u32) -> Result<Vec<String>, ErrorCode> {
let config = match application_id {
128 => get_blacklist_1(),
129 => get_blacklist_2(),
130 => get_blacklist_3(),
_ => {
log::warn!("unsupported application_id in GetApplicationConfigString: {}", application_id);
Vec::new()
}
};
Ok(config)
}
async fn get_metas_multiple_param(
&self,
params: Vec<GetMetaParam>
) -> Result<(Vec<GetMetaInfo>, Vec<QResult>), ErrorCode> {
let mut metas = Vec::with_capacity(params.len());
let mut results = Vec::with_capacity(params.len());
for param in params {
let info_result = if param.dataid != 0 {
get_object_info_by_data_id(param.dataid, param.access_password).await
} else {
get_object_info_by_persistence_target(param.persistence_target, param.access_password).await
};
match info_result {
Ok(mut meta) => {
if let Err(e) = verify_object_permission(meta.owner, self.pid, &meta.permission) {
metas.push(GetMetaInfo::default());
results.push(QResult::error(e));
} else {
if param.result_option & 0x1 == 0 {
meta.tags = Vec::new();
}
if param.result_option & 0x2 == 0 {
meta.ratings = Vec::new();
}
if param.result_option & 0x4 == 0 {
meta.meta_binary = rnex_core::rmc::structures::qbuffer::QBuffer(Vec::new());
}
metas.push(meta);
results.push(QResult::success(ErrorCode::Core_Unknown));
}
}
Err(e) => {
metas.push(GetMetaInfo::default());
results.push(QResult::error(e));
}
}
}
Ok((metas, results))
}
}
}
}