fix things related to time in KerberosDateTime and datastore

This commit is contained in:
Maple Nebel 2026-06-18 22:36:17 +02:00
commit f2fc26661b
4 changed files with 178 additions and 194 deletions

4
Cargo.lock generated
View file

@ -2871,7 +2871,6 @@ dependencies = [
"sha2 0.10.9", "sha2 0.10.9",
"smallvec", "smallvec",
"thiserror", "thiserror",
"time",
"tokio", "tokio",
"tokio-stream", "tokio-stream",
"tracing", "tracing",
@ -2955,7 +2954,6 @@ dependencies = [
"sqlx-core", "sqlx-core",
"stringprep", "stringprep",
"thiserror", "thiserror",
"time",
"tracing", "tracing",
"whoami", "whoami",
] ]
@ -2994,7 +2992,6 @@ dependencies = [
"sqlx-core", "sqlx-core",
"stringprep", "stringprep",
"thiserror", "thiserror",
"time",
"tracing", "tracing",
"whoami", "whoami",
] ]
@ -3020,7 +3017,6 @@ dependencies = [
"serde_urlencoded", "serde_urlencoded",
"sqlx-core", "sqlx-core",
"thiserror", "thiserror",
"time",
"tracing", "tracing",
"url", "url",
] ]

View file

@ -28,7 +28,7 @@ anyhow = "1.0.100"
ureq = { version = "3.3.0", features = [ "json" ] } ureq = { version = "3.3.0", features = [ "json" ] }
serde = { version = "1.0.228", features = [ "derive" ] } serde = { version = "1.0.228", features = [ "derive" ] }
serde_json = "1.0.149" serde_json = "1.0.149"
sqlx = { version = "0.8.6", optional = true, features = ["postgres", "runtime-tokio", "chrono", "time"] } sqlx = { version = "0.8.6", optional = true, features = ["postgres", "runtime-tokio", "chrono"] }
aws-sdk-s3 = { version = "1.129.0", optional = true } aws-sdk-s3 = { version = "1.129.0", optional = true }
aws-config = { version = "1.8.15", optional = true } aws-config = { version = "1.8.15", optional = true }
base64 = "0.22.1" base64 = "0.22.1"

View file

@ -46,13 +46,14 @@ pub fn derive_key(pid: PID, password: &[u8]) -> [u8; 16] {
key key
} }
#[derive(Pod, Zeroable, Copy, Clone, Debug, Eq, PartialEq, Default)] #[derive(Pod, Zeroable, Copy, Clone, Debug, Eq, PartialEq)]
#[repr(transparent)] #[repr(transparent)]
pub struct KerberosDateTime(pub u64); pub struct KerberosDateTime(pub u64);
impl KerberosDateTime { impl KerberosDateTime {
#[deprecated]
pub fn from_i64(val: i64) -> Self { pub fn from_i64(val: i64) -> Self {
Self(val as u64) Self(bytemuck::cast(val))
} }
pub fn from_naive(dt: chrono::NaiveDateTime) -> Self { pub fn from_naive(dt: chrono::NaiveDateTime) -> Self {
@ -68,7 +69,7 @@ impl KerberosDateTime {
) )
} }
pub fn new(second: u64, minute: u64, hour: u64, day: u64, month: u64, year: u64) -> Self { pub const fn new(second: u64, minute: u64, hour: u64, day: u64, month: u64, year: u64) -> Self {
Self(second | (minute << 6) | (hour << 12) | (day << 17) | (month << 22) | (year << 26)) Self(second | (minute << 6) | (hour << 12) | (day << 17) | (month << 22) | (year << 26))
} }
@ -84,35 +85,26 @@ impl KerberosDateTime {
) )
} }
#[inline] pub const fn get_seconds(&self) -> u8 {
pub fn get_seconds(&self) -> u8 {
(self.0 & 0b111111) as u8 (self.0 & 0b111111) as u8
} }
#[inline] pub const fn get_minutes(&self) -> u8 {
pub fn get_minutes(&self) -> u8 {
((self.0 >> 6) & 0b111111) as u8 ((self.0 >> 6) & 0b111111) as u8
} }
#[inline] pub const fn get_hours(&self) -> u8 {
pub fn get_hours(&self) -> u8 {
((self.0 >> 12) & 0b111111) as u8 ((self.0 >> 12) & 0b111111) as u8
} }
#[inline] pub const fn get_days(&self) -> u8 {
pub fn get_days(&self) -> u8 {
((self.0 >> 17) & 0b111111) as u8 ((self.0 >> 17) & 0b111111) as u8
} }
pub const fn get_month(&self) -> u8 {
#[inline]
pub fn get_month(&self) -> u8 {
((self.0 >> 22) & 0b1111) as u8 ((self.0 >> 22) & 0b1111) as u8
} }
pub const fn get_year(&self) -> u64 {
#[inline]
pub fn get_year(&self) -> u64 {
(self.0 >> 26) & 0xFFFFFFFF (self.0 >> 26) & 0xFFFFFFFF
} }
pub const fn to_regular_time(&self) -> chrono::DateTime<Utc> {
pub fn to_regular_time(&self) -> chrono::DateTime<Utc> {
NaiveDateTime::new( NaiveDateTime::new(
NaiveDate::from_ymd_opt( NaiveDate::from_ymd_opt(
self.get_year() as i32, self.get_year() as i32,
@ -131,6 +123,12 @@ impl KerberosDateTime {
} }
} }
impl Default for KerberosDateTime {
fn default() -> Self {
Self::now()
}
}
impl RmcSerialize for KerberosDateTime { impl RmcSerialize for KerberosDateTime {
fn serialize(&self, writer: &mut impl Write) -> Result<()> { fn serialize(&self, writer: &mut impl Write) -> Result<()> {
Ok(self.0.serialize(writer)?) Ok(self.0.serialize(writer)?)

View file

@ -1,18 +1,28 @@
use crate::rmc::protocols::datastore::{
DataStoreGetCourseRecordParam, DataStoreGetCourseRecordResult, DataStoreUploadCourseRecordParam,
};
use chrono::{NaiveDateTime, Utc};
use futures::TryStreamExt; use futures::TryStreamExt;
use rnex_core::nex::user::User;
use rnex_core::PID; use rnex_core::PID;
use rnex_core::executables::common::{ use rnex_core::executables::common::{
RNEX_DATASTORE_S3_BUCKET, RNEX_DATASTORE_S3_ENDPOINT, get_db, RNEX_DATASTORE_S3_BUCKET, RNEX_DATASTORE_S3_ENDPOINT, get_db,
}; };
use rnex_core::kerberos::KerberosDateTime; use rnex_core::kerberos::KerberosDateTime;
use rnex_core::nex::s3presigner::S3Presigner; use rnex_core::nex::s3presigner::S3Presigner;
use rnex_core::rmc::protocols::datastore::{BufferQueueParam, CompletePostParam, DataStoreChangeMetaParam, DataStoreCustomRankingResult, DataStoreGetCustomRankingByDataIDParam, DataStorePrepareGetParam, DataStoreReqGetInfo, DataStoreSearchParam, GetMetaInfo, GetMetaParam, KeyValue, Permission, PersistenceTarget, RateCustomRankingParam, RatingInfo, RatingInfoWithSlot, RatingInitParamWithSlot}; use rnex_core::nex::user::User;
use rnex_core::rmc::protocols::datastore::{DataStore, PreparePostParam, ReqPostInfo, AttachFileParam, DataStoreRateObjectParam, DataStoreRatingTarget}; use rnex_core::rmc::protocols::datastore::{
AttachFileParam, DataStore, DataStoreRateObjectParam, DataStoreRatingTarget, PreparePostParam,
ReqPostInfo,
};
use rnex_core::rmc::protocols::datastore::{
BufferQueueParam, CompletePostParam, DataStoreChangeMetaParam, DataStoreCustomRankingResult,
DataStoreGetCustomRankingByDataIDParam, DataStorePrepareGetParam, DataStoreReqGetInfo,
DataStoreSearchParam, GetMetaInfo, GetMetaParam, KeyValue, Permission, PersistenceTarget,
RateCustomRankingParam, RatingInfo, RatingInfoWithSlot, RatingInitParamWithSlot,
};
use rnex_core::rmc::response::ErrorCode; use rnex_core::rmc::response::ErrorCode;
use rnex_core::rmc::structures::qbuffer::QBuffer; use rnex_core::rmc::structures::qbuffer::QBuffer;
use rnex_core::rmc::structures::qresult::QResult; use rnex_core::rmc::structures::qresult::QResult;
use sqlx::types::time;
use crate::rmc::protocols::datastore::{DataStoreGetCourseRecordParam, DataStoreGetCourseRecordResult, DataStoreUploadCourseRecordParam};
fn map_row_to_meta_info( fn map_row_to_meta_info(
row_data_id: i64, row_data_id: i64,
@ -68,7 +78,6 @@ fn map_row_to_meta_info(
} }
} }
pub async fn check_object_availability(data_id: i64, password: i64) -> Result<(), ErrorCode> { pub async fn check_object_availability(data_id: i64, password: i64) -> Result<(), ErrorCode> {
let row = sqlx::query!( let row = sqlx::query!(
r#" r#"
@ -134,7 +143,10 @@ pub async fn get_object_ratings(
Ok(ratings) Ok(ratings)
} }
pub async fn get_object_info_by_data_id(data_id: i64, password: i64) -> Result<GetMetaInfo, ErrorCode> { pub async fn get_object_info_by_data_id(
data_id: i64,
password: i64,
) -> Result<GetMetaInfo, ErrorCode> {
check_object_availability(data_id, password).await?; check_object_availability(data_id, password).await?;
let row = sqlx::query!( let row = sqlx::query!(
@ -174,34 +186,8 @@ pub async fn get_object_info_by_data_id(data_id: i64, password: i64) -> Result<G
row.refer_data_id.unwrap_or(0), row.refer_data_id.unwrap_or(0),
row.flag.unwrap_or(0), row.flag.unwrap_or(0),
row.tags.unwrap_or_default(), row.tags.unwrap_or_default(),
row.creation_date row.creation_date.unwrap_or_default(),
.map(|dt| { row.update_date.unwrap_or_default(),
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, ratings,
)) ))
} }
@ -260,34 +246,8 @@ async fn get_object_info_by_persistence_target(
row.refer_data_id.unwrap_or(0), row.refer_data_id.unwrap_or(0),
row.flag.unwrap_or(0), row.flag.unwrap_or(0),
row.tags.unwrap_or_default(), row.tags.unwrap_or_default(),
row.creation_date row.creation_date.unwrap_or_default(),
.map(|dt| { row.update_date.unwrap_or_default(),
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, ratings,
)) ))
} }
@ -360,7 +320,7 @@ fn filter_properties_by_result_option(meta_info: &mut GetMetaInfo, result_option
async fn init_object_rating_slot(data_id: i64, rating_param: RatingInitParamWithSlot) { async fn init_object_rating_slot(data_id: i64, rating_param: RatingInitParamWithSlot) {
log::info!("running init object rating slot"); log::info!("running init object rating slot");
let row = sqlx::query!( let row = sqlx::query!(
r#" r#"
INSERT INTO datastore.object_ratings ( INSERT INTO datastore.object_ratings (
data_id, data_id,
slot, slot,
@ -377,24 +337,24 @@ async fn init_object_rating_slot(data_id: i64, rating_param: RatingInitParamWith
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11 $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11
) )
"#, "#,
data_id, data_id,
rating_param.slot as i16, rating_param.slot as i16,
rating_param.param.flag as i16, rating_param.param.flag as i16,
rating_param.param.internal_flag as i16, rating_param.param.internal_flag as i16,
rating_param.param.lock_type as i16, rating_param.param.lock_type as i16,
rating_param.param.initial_value, rating_param.param.initial_value,
rating_param.param.range_min, rating_param.param.range_min,
rating_param.param.range_max, rating_param.param.range_max,
rating_param.param.period_hour as i16, rating_param.param.period_hour as i16,
rating_param.param.period_duration as i32, rating_param.param.period_duration as i32,
rating_param.param.initial_value, rating_param.param.initial_value,
) )
.execute(get_db()) .execute(get_db())
.await .await
.map_err(|e| { .map_err(|e| {
log::error!("DB Error: {:?}", e); log::error!("DB Error: {:?}", e);
ErrorCode::DataStore_NotFound ErrorCode::DataStore_NotFound
}); });
log::info!("done running"); log::info!("done running");
} }
@ -635,7 +595,12 @@ fn get_blacklist_3() -> Vec<String> {
} }
// couldn't find a better way to do this im going crazyy // couldn't find a better way to do this im going crazyy
async fn rate_object(dataid: i64, slot: i8, rating_value: i32, access_password: i64) -> Result<RatingInfo, ErrorCode> { async fn rate_object(
dataid: i64,
slot: i8,
rating_value: i32,
access_password: i64,
) -> Result<RatingInfo, ErrorCode> {
check_object_availability(dataid, access_password).await?; check_object_availability(dataid, access_password).await?;
let rating = RatingInfo::default(); let rating = RatingInfo::default();
@ -651,12 +616,12 @@ async fn rate_object(dataid: i64, slot: i8, rating_value: i32, access_password:
dataid, dataid,
slot as i16 slot as i16
) )
.fetch_all(get_db()) .fetch_all(get_db())
.await .await
.map_err(|e| { .map_err(|e| {
log::error!("DB Error: {:?}", e); log::error!("DB Error: {:?}", e);
ErrorCode::DataStore_NotFound ErrorCode::DataStore_NotFound
})?; })?;
Ok(rating) Ok(rating)
} }
@ -753,7 +718,7 @@ impl DataStore for User {
.iter() .iter()
.map(|&id| id as i32) .map(|&id| id as i32)
.collect(); .collect();
let now = time::OffsetDateTime::now_utc(); let now = Utc::now().naive_utc();
let row = sqlx::query!( let row = sqlx::query!(
r#" r#"
@ -782,8 +747,8 @@ impl DataStore for User {
&postparam.tags, &postparam.tags,
postparam.persistence_init_param.persistence_slot_id as i32, postparam.persistence_init_param.persistence_slot_id as i32,
&postparam.extra_data, &postparam.extra_data,
time::PrimitiveDateTime::new(now.date(), now.time()), now,
time::PrimitiveDateTime::new(now.date(), now.time()) now
) )
.fetch_one(get_db()) .fetch_one(get_db())
.await .await
@ -803,8 +768,7 @@ impl DataStore for User {
log::info!("RIP len is: {}", postparam.rating_init_params.len()); log::info!("RIP len is: {}", postparam.rating_init_params.len());
for rating_param in &postparam.rating_init_params { for rating_param in &postparam.rating_init_params {
log::info!("running init params"); log::info!("running init params");
init_object_rating_slot(data_id, rating_param.clone()) init_object_rating_slot(data_id, rating_param.clone()).await
.await
} }
let key = format!("data/{}.bin", data_id); let key = format!("data/{}.bin", data_id);
@ -1145,7 +1109,8 @@ impl DataStore for User {
match info_result { match info_result {
Ok(mut meta) => { Ok(mut meta) => {
if let Err(e) = verify_object_permission(meta.owner, self.pid, &meta.permission).await if let Err(e) =
verify_object_permission(meta.owner, self.pid, &meta.permission).await
{ {
metas.push(GetMetaInfo::default()); metas.push(GetMetaInfo::default());
results.push(QResult::error(e)); results.push(QResult::error(e));
@ -1206,8 +1171,7 @@ impl DataStore for User {
.map(|e| e.to_string()) .map(|e| e.to_string())
.collect(); .collect();
let now = time::OffsetDateTime::now_utc(); let now = Utc::now().naive_utc();
let db_now = time::PrimitiveDateTime::new(now.date(), now.time());
let row = sqlx::query!( let row = sqlx::query!(
r#" r#"
@ -1236,29 +1200,28 @@ impl DataStore for User {
&tags, &tags,
param.post_param.persistence_init_param.persistence_slot_id as i32, param.post_param.persistence_init_param.persistence_slot_id as i32,
&extra_data, &extra_data,
db_now, now,
db_now now
) )
.fetch_one(get_db()) .fetch_one(get_db())
.await .await
.map_err(|e| { .map_err(|e| {
log::error!("DB Error: {:?}", e); log::error!("DB Error: {:?}", e);
ErrorCode::DataStore_NotFound ErrorCode::DataStore_NotFound
})?; })?;
let data_id = row.data_id; let data_id = row.data_id;
for rating_param in &param.post_param.rating_init_params { for rating_param in &param.post_param.rating_init_params {
log::info!("running init params"); log::info!("running init params");
init_object_rating_slot(data_id, rating_param.clone()) init_object_rating_slot(data_id, rating_param.clone()).await
.await
} }
let presigner = S3Presigner::new( let presigner = S3Presigner::new(
&format!("https://{}", *RNEX_DATASTORE_S3_ENDPOINT), &format!("https://{}", *RNEX_DATASTORE_S3_ENDPOINT),
format!("{}", *RNEX_DATASTORE_S3_BUCKET), format!("{}", *RNEX_DATASTORE_S3_BUCKET),
) )
.await; .await;
let key = format!("data/{}.jpg", data_id); let key = format!("data/{}.jpg", data_id);
@ -1278,14 +1241,18 @@ impl DataStore for User {
}) })
} }
async fn complete_attach_file(&self, complete_attach_param: CompletePostParam) -> Result<String, ErrorCode> { async fn complete_attach_file(
&self,
complete_attach_param: CompletePostParam,
) -> Result<String, ErrorCode> {
log::info!("Data ID: {:?}", complete_attach_param.dataid); log::info!("Data ID: {:?}", complete_attach_param.dataid);
log::info!("Success: {:?}", complete_attach_param.success); log::info!("Success: {:?}", complete_attach_param.success);
let presigner = S3Presigner::new( let presigner = S3Presigner::new(
&format!("https://{}", *RNEX_DATASTORE_S3_ENDPOINT), &format!("https://{}", *RNEX_DATASTORE_S3_ENDPOINT),
format!("{}", *RNEX_DATASTORE_S3_BUCKET), format!("{}", *RNEX_DATASTORE_S3_BUCKET),
).await; )
.await;
let key = format!("data/{}.jpg", complete_attach_param.dataid); let key = format!("data/{}.jpg", complete_attach_param.dataid);
let download_url = presigner.generate_presigned_get(&key); let download_url = presigner.generate_presigned_get(&key);
@ -1293,13 +1260,19 @@ impl DataStore for User {
Ok(download_url) Ok(download_url)
} }
async fn rate_objects(&self, targets: Vec<DataStoreRatingTarget>, params: Vec<DataStoreRateObjectParam>, _transactional: bool, fetch_ratings: bool) -> Result<(Vec<RatingInfo>, Vec<QResult>), ErrorCode> { async fn rate_objects(
&self,
targets: Vec<DataStoreRatingTarget>,
params: Vec<DataStoreRateObjectParam>,
_transactional: bool,
fetch_ratings: bool,
) -> Result<(Vec<RatingInfo>, Vec<QResult>), ErrorCode> {
let mut ratings: Vec<RatingInfo> = vec![]; let mut ratings: Vec<RatingInfo> = vec![];
let results: Vec<QResult> = vec![]; let results: Vec<QResult> = vec![];
// SMM seems to work fine with this, no clue for other DTSR games // SMM seems to work fine with this, no clue for other DTSR games
if targets.len() != params.len() { if targets.len() != params.len() {
return Err(ErrorCode::DataStore_OperationNotAllowed) return Err(ErrorCode::DataStore_OperationNotAllowed);
} }
for (i, target) in targets.into_iter().enumerate() { for (i, target) in targets.into_iter().enumerate() {
@ -1309,11 +1282,18 @@ impl DataStore for User {
log::info!("Slot: {:?}", target.slot); log::info!("Slot: {:?}", target.slot);
log::info!("Access Password: {:?}", param.access_password); log::info!("Access Password: {:?}", param.access_password);
let object_info = get_object_info_by_data_id(target.dataid, param.access_password).await?; let object_info =
get_object_info_by_data_id(target.dataid, param.access_password).await?;
log::info!("object info get complete"); log::info!("object info get complete");
verify_object_permission(object_info.owner, self.pid, &object_info.permission).await?; verify_object_permission(object_info.owner, self.pid, &object_info.permission).await?;
log::info!("object permission complete"); log::info!("object permission complete");
let rating = rate_object(target.dataid, target.slot, param.rating_value, param.access_password).await?; let rating = rate_object(
target.dataid,
target.slot,
param.rating_value,
param.access_password,
)
.await?;
log::info!("rating complete"); log::info!("rating complete");
if fetch_ratings { if fetch_ratings {
@ -1336,12 +1316,12 @@ impl DataStore for User {
param.period as i16, param.period as i16,
param.dataid param.dataid
) )
.execute(get_db()) .execute(get_db())
.await .await
.map_err(|e| { .map_err(|e| {
eprintln!("update error: {:?}", e); eprintln!("update error: {:?}", e);
ErrorCode::DataStore_NotFound ErrorCode::DataStore_NotFound
})?; })?;
} }
if param.modifies_flag & 0x10 != 0 { if param.modifies_flag & 0x10 != 0 {
@ -1352,12 +1332,12 @@ impl DataStore for User {
param.meta_binary.0, param.meta_binary.0,
param.dataid param.dataid
) )
.execute(get_db()) .execute(get_db())
.await .await
.map_err(|e| { .map_err(|e| {
eprintln!("update error: {:?}", e); eprintln!("update error: {:?}", e);
ErrorCode::DataStore_NotFound ErrorCode::DataStore_NotFound
})?; })?;
} }
if param.modifies_flag & 0x80 != 0 { if param.modifies_flag & 0x80 != 0 {
@ -1368,22 +1348,26 @@ impl DataStore for User {
param.data_type as i16, param.data_type as i16,
param.dataid param.dataid
) )
.execute(get_db()) .execute(get_db())
.await .await
.map_err(|e| { .map_err(|e| {
eprintln!("update error: {:?}", e); eprintln!("update error: {:?}", e);
ErrorCode::DataStore_NotFound ErrorCode::DataStore_NotFound
})?; })?;
} }
Ok(()) Ok(())
} }
async fn recommended_course_search_object(&self, course_search_param: DataStoreSearchParam, extra_data: Vec<String>) -> Result<Vec<DataStoreCustomRankingResult>, ErrorCode> { async fn recommended_course_search_object(
&self,
course_search_param: DataStoreSearchParam,
extra_data: Vec<String>,
) -> Result<Vec<DataStoreCustomRankingResult>, ErrorCode> {
let mut courses = Vec::new(); let mut courses = Vec::new();
let mut stream = sqlx::query!( let mut stream = sqlx::query!(
r#" r#"
SELECT SELECT
object.data_id, object.data_id,
object.owner, object.owner,
@ -1413,14 +1397,13 @@ impl DataStore for User {
ORDER BY RANDOM() ORDER BY RANDOM()
LIMIT 100 LIMIT 100
"# "#
) )
.fetch(get_db()); .fetch(get_db());
while let Some(row) = stream.try_next().await.map_err(|e| { while let Some(row) = stream.try_next().await.map_err(|e| {
eprintln!("stream error: {:?}", e); eprintln!("stream error: {:?}", e);
ErrorCode::DataStore_NotFound ErrorCode::DataStore_NotFound
})? { })? {
let permission = Permission { let permission = Permission {
permission: row.permission.unwrap_or(0) as u8, permission: row.permission.unwrap_or(0) as u8,
recipient_ids: row.permission_recipients.unwrap_or_default(), recipient_ids: row.permission_recipients.unwrap_or_default(),
@ -1431,21 +1414,25 @@ impl DataStore for User {
recipient_ids: row.delete_permission_recipients.unwrap_or_default(), recipient_ids: row.delete_permission_recipients.unwrap_or_default(),
}; };
let meta_binary = row.meta_binary let meta_binary = row
.meta_binary
.map(|bytes| QBuffer(bytes)) .map(|bytes| QBuffer(bytes))
.unwrap_or_default(); .unwrap_or_default();
let created_time = row.creation_date let created_time = row
.map(|t| KerberosDateTime::from_i64(t.assume_utc().unix_timestamp())) .creation_date
.unwrap_or_else(|| KerberosDateTime::from_i64(0)); .map(KerberosDateTime::from_naive)
.unwrap_or_default();
let updated_time = row.update_date let updated_time = row
.map(|t| KerberosDateTime::from_i64(t.assume_utc().unix_timestamp())) .update_date
.unwrap_or_else(|| KerberosDateTime::from_i64(0)); .map(KerberosDateTime::from_naive)
.unwrap_or_default();
let referred_time = row.creation_date let referred_time = row
.map(|t| KerberosDateTime::from_i64(t.assume_utc().unix_timestamp())) .creation_date
.unwrap_or_else(|| KerberosDateTime::from_i64(0)); .map(KerberosDateTime::from_naive)
.unwrap_or_default();
let mut meta_info = GetMetaInfo { let mut meta_info = GetMetaInfo {
dataid: row.data_id, dataid: row.data_id,
@ -1486,9 +1473,11 @@ impl DataStore for User {
Ok(courses) Ok(courses)
} }
async fn upload_course_record(&self, upload_course_record_param: DataStoreUploadCourseRecordParam) -> Result<(), ErrorCode> { async fn upload_course_record(
let now = time::OffsetDateTime::now_utc(); &self,
let db_now = time::PrimitiveDateTime::new(now.date(), now.time()); upload_course_record_param: DataStoreUploadCourseRecordParam,
) -> Result<(), ErrorCode> {
let now = Utc::now().naive_utc();
let row = sqlx::query!( let row = sqlx::query!(
r#" r#"
@ -1518,8 +1507,8 @@ impl DataStore for User {
self.pid, self.pid,
self.pid, self.pid,
upload_course_record_param.score, upload_course_record_param.score,
db_now, now,
db_now now
) )
.execute(get_db()) .execute(get_db())
.await .await
@ -1531,7 +1520,10 @@ impl DataStore for User {
Ok(()) Ok(())
} }
async fn get_course_record(&self, get_course_record_param: DataStoreGetCourseRecordParam) -> Result<DataStoreGetCourseRecordResult, ErrorCode> { async fn get_course_record(
&self,
get_course_record_param: DataStoreGetCourseRecordParam,
) -> Result<DataStoreGetCourseRecordResult, ErrorCode> {
let row = sqlx::query!( let row = sqlx::query!(
r#" r#"
SELECT SELECT
@ -1545,23 +1537,21 @@ impl DataStore for User {
get_course_record_param.dataid, get_course_record_param.dataid,
get_course_record_param.slot as i16 get_course_record_param.slot as i16
) )
.fetch_one(get_db()) .fetch_one(get_db())
.await .await
.map_err(|e| { .map_err(|e| {
log::error!("DB Error: {:?}", e); log::error!("DB Error: {:?}", e);
ErrorCode::DataStore_NotFound ErrorCode::DataStore_NotFound
})?; })?;
Ok( Ok(DataStoreGetCourseRecordResult {
DataStoreGetCourseRecordResult { dataid: get_course_record_param.dataid,
dataid: get_course_record_param.dataid, slot: get_course_record_param.slot,
slot: get_course_record_param.slot, first_pid: row.first_pid as u32,
first_pid: row.first_pid as u32, best_pid: row.best_pid as u32,
best_pid: row.best_pid as u32, best_score: row.best_score,
best_score: row.best_score, created_time: KerberosDateTime::from_i64(0x9C3F3E0000),
created_time: KerberosDateTime::from_i64(0x9C3F3E0000), updated_time: KerberosDateTime::from_i64(0x9C3F3E0000),
updated_time: KerberosDateTime::from_i64(0x9C3F3E0000), })
}
)
} }
} }