From 5ad28caf2b6de913468710fabbdc0c446264bdac Mon Sep 17 00:00:00 2001 From: Sophie <47993817+sdankel@users.noreply.github.com> Date: Fri, 10 Jan 2025 21:36:27 -0800 Subject: [PATCH] move fully to chrono --- .../down.sql | 23 +++++++++++ .../up.sql | 23 +++++++++++ src/api/api_token.rs | 7 ++-- src/api/search.rs | 6 +-- src/db/package_version.rs | 10 ++--- src/db/user_session.rs | 5 ++- src/middleware/session_auth.rs | 3 +- src/middleware/token_auth.rs | 11 +++--- src/models.rs | 39 +++++++++---------- src/schema.rs | 12 +++--- src/upload.rs | 3 +- src/util.rs | 12 ++---- 12 files changed, 99 insertions(+), 55 deletions(-) create mode 100644 migrations/2025-01-11-051241_alter_timestamp_to_tstz/down.sql create mode 100644 migrations/2025-01-11-051241_alter_timestamp_to_tstz/up.sql diff --git a/migrations/2025-01-11-051241_alter_timestamp_to_tstz/down.sql b/migrations/2025-01-11-051241_alter_timestamp_to_tstz/down.sql new file mode 100644 index 0000000..c7d0467 --- /dev/null +++ b/migrations/2025-01-11-051241_alter_timestamp_to_tstz/down.sql @@ -0,0 +1,23 @@ +ALTER TABLE api_tokens +ALTER COLUMN created_at TYPE timestamp +USING created_at AT TIME ZONE 'UTC'; + +ALTER TABLE api_tokens +ALTER COLUMN expires_at TYPE timestamp +USING expires_at AT TIME ZONE 'UTC'; + +ALTER TABLE sessions +ALTER COLUMN expires_at TYPE timestamp +USING expires_at AT TIME ZONE 'UTC'; + +ALTER TABLE sessions +ALTER COLUMN created_at TYPE timestamp +USING created_at AT TIME ZONE 'UTC'; + +ALTER TABLE uploads +ALTER COLUMN created_at TYPE timestamp +USING created_at AT TIME ZONE 'UTC'; + +ALTER TABLE users +ALTER COLUMN created_at TYPE timestamp +USING created_at AT TIME ZONE 'UTC'; diff --git a/migrations/2025-01-11-051241_alter_timestamp_to_tstz/up.sql b/migrations/2025-01-11-051241_alter_timestamp_to_tstz/up.sql new file mode 100644 index 0000000..b14ab40 --- /dev/null +++ b/migrations/2025-01-11-051241_alter_timestamp_to_tstz/up.sql @@ -0,0 +1,23 @@ +ALTER TABLE api_tokens +ALTER COLUMN created_at TYPE timestamptz +USING created_at AT TIME ZONE 'UTC'; + +ALTER TABLE api_tokens +ALTER COLUMN expires_at TYPE timestamptz +USING expires_at AT TIME ZONE 'UTC'; + +ALTER TABLE sessions +ALTER COLUMN expires_at TYPE timestamptz +USING expires_at AT TIME ZONE 'UTC'; + +ALTER TABLE sessions +ALTER COLUMN created_at TYPE timestamptz +USING created_at AT TIME ZONE 'UTC'; + +ALTER TABLE uploads +ALTER COLUMN created_at TYPE timestamptz +USING created_at AT TIME ZONE 'UTC'; + +ALTER TABLE users +ALTER COLUMN created_at TYPE timestamptz +USING created_at AT TIME ZONE 'UTC'; \ No newline at end of file diff --git a/src/api/api_token.rs b/src/api/api_token.rs index 3ed5e0f..758838c 100644 --- a/src/api/api_token.rs +++ b/src/api/api_token.rs @@ -1,4 +1,5 @@ -use crate::{models, util::sys_time_to_epoch}; +use crate::models; +use chrono::{DateTime, Utc}; use rocket::serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug, Clone)] @@ -6,7 +7,7 @@ use rocket::serde::{Deserialize, Serialize}; pub struct Token { pub id: String, pub name: String, - pub created_at: u64, + pub created_at: DateTime, pub token: Option, } @@ -15,7 +16,7 @@ impl From for Token { Token { id: token.id.to_string(), name: token.friendly_name, - created_at: sys_time_to_epoch(token.created_at), + created_at: token.created_at, // We don't return the hashed token, as it's a secret. token: None, } diff --git a/src/api/search.rs b/src/api/search.rs index 0f66536..1805685 100644 --- a/src/api/search.rs +++ b/src/api/search.rs @@ -1,8 +1,8 @@ -use crate::models::RecentPackage; +use crate::models::PackagePreview; use serde::Serialize; #[derive(Serialize, Debug)] pub struct RecentPackagesResponse { - pub recently_created: Vec, - pub recently_updated: Vec, + pub recently_created: Vec, + pub recently_updated: Vec, } diff --git a/src/db/package_version.rs b/src/db/package_version.rs index a864e31..79ee70d 100644 --- a/src/db/package_version.rs +++ b/src/db/package_version.rs @@ -1,7 +1,7 @@ use super::error::DatabaseError; use super::{models, schema, DbConn}; use crate::api::publish::PublishRequest; -use crate::models::{ApiToken, RecentPackage}; +use crate::models::{ApiToken, PackagePreview}; use diesel::prelude::*; use uuid::Uuid; @@ -101,7 +101,7 @@ impl DbConn { } /// Fetch the most recently updated packages. - pub fn get_recently_updated(&mut self) -> Result, DatabaseError> { + pub fn get_recently_updated(&mut self) -> Result, DatabaseError> { let packages = diesel::sql_query( r#"WITH ranked_versions AS ( SELECT @@ -127,14 +127,14 @@ impl DbConn { LIMIT 10; "#, ) - .load::(self.inner()) + .load::(self.inner()) .map_err(|err| DatabaseError::QueryFailed("recently updated".to_string(), err))?; Ok(packages) } /// Fetch the most recently created packages. - pub fn get_recently_created(&mut self) -> Result, DatabaseError> { + pub fn get_recently_created(&mut self) -> Result, DatabaseError> { let packages = diesel::sql_query( r#"WITH ranked_versions AS ( SELECT @@ -160,7 +160,7 @@ impl DbConn { LIMIT 10; "#, ) - .load::(self.inner()) + .load::(self.inner()) .map_err(|err| DatabaseError::QueryFailed("recently created".to_string(), err))?; Ok(packages) diff --git a/src/db/user_session.rs b/src/db/user_session.rs index 43cacaa..e4ea91f 100644 --- a/src/db/user_session.rs +++ b/src/db/user_session.rs @@ -1,5 +1,6 @@ use super::error::DatabaseError; use super::{api, models, schema, DbConn}; +use chrono::{DateTime, Utc}; use diesel::prelude::*; use diesel::upsert::excluded; use std::time::{Duration, SystemTime}; @@ -38,7 +39,9 @@ impl DbConn { let new_session = models::NewSession { user_id: saved_user.id, - expires_at: SystemTime::now() + Duration::from_secs(u64::from(expires_in)), + expires_at: DateTime::::from( + SystemTime::now() + Duration::from_secs(u64::from(expires_in)), + ), }; // Insert new session diff --git a/src/middleware/session_auth.rs b/src/middleware/session_auth.rs index 0cf5b59..c61148c 100644 --- a/src/middleware/session_auth.rs +++ b/src/middleware/session_auth.rs @@ -1,5 +1,6 @@ use crate::db::Database; use crate::models; +use chrono::{DateTime, Utc}; use rocket::http::Status; use rocket::request::{FromRequest, Outcome}; use rocket::Request; @@ -44,7 +45,7 @@ impl<'r> FromRequest<'r> for SessionAuth { { if let Ok(session) = db.get_session(session_id) { if let Ok(user) = db.get_user_for_session(session_id) { - if session.expires_at > SystemTime::now() { + if session.expires_at > DateTime::::from(SystemTime::now()) { return Outcome::Success(SessionAuth { user, session_id }); } } diff --git a/src/middleware/token_auth.rs b/src/middleware/token_auth.rs index cb9f61c..3eaa403 100644 --- a/src/middleware/token_auth.rs +++ b/src/middleware/token_auth.rs @@ -1,12 +1,12 @@ -use std::time::SystemTime; - use crate::db::api_token::PlainToken; use crate::db::Database; use crate::models; +use chrono::{DateTime, Utc}; use rocket::http::hyper::header; use rocket::http::Status; use rocket::request::{FromRequest, Outcome}; use rocket::Request; +use std::time::SystemTime; pub struct TokenAuth { pub token: models::ApiToken, @@ -42,10 +42,9 @@ impl<'r> FromRequest<'r> for TokenAuth { if auth_header.starts_with("Bearer ") { let token = auth_header.trim_start_matches("Bearer "); if let Ok(token) = db.get_token(PlainToken::from(token.to_string())) { - if token - .expires_at - .map_or(true, |expires_at| expires_at > SystemTime::now()) - { + if token.expires_at.map_or(true, |expires_at| { + expires_at > DateTime::::from(SystemTime::now()) + }) { return Outcome::Success(TokenAuth { token }); } return Outcome::Error((Status::Unauthorized, TokenAuthError::Expired)); diff --git a/src/models.rs b/src/models.rs index 088ab06..7d443da 100644 --- a/src/models.rs +++ b/src/models.rs @@ -3,7 +3,6 @@ use diesel::prelude::*; use diesel::sql_types::{Nullable, Text, Timestamptz}; use diesel::QueryableByName; use serde::Serialize; -use std::time::SystemTime; use uuid::Uuid; #[derive(Queryable, Selectable, Debug, Clone)] @@ -17,7 +16,7 @@ pub struct User { pub avatar_url: Option, pub email: Option, pub is_admin: bool, - pub created_at: SystemTime, + pub created_at: DateTime, } #[derive(Insertable)] @@ -37,15 +36,15 @@ pub struct NewUser { pub struct Session { pub id: Uuid, pub user_id: Uuid, - pub expires_at: SystemTime, - pub created_at: SystemTime, + pub expires_at: DateTime, + pub created_at: DateTime, } #[derive(Insertable)] #[diesel(table_name = crate::schema::sessions)] pub struct NewSession { pub user_id: Uuid, - pub expires_at: SystemTime, + pub expires_at: DateTime, } #[derive(Queryable, Selectable, Debug, PartialEq, Eq)] @@ -55,8 +54,8 @@ pub struct ApiToken { pub id: Uuid, pub user_id: Uuid, pub friendly_name: String, - pub expires_at: Option, - pub created_at: SystemTime, + pub expires_at: Option>, + pub created_at: DateTime, } #[derive(Insertable)] @@ -65,7 +64,7 @@ pub struct NewApiToken { pub user_id: Uuid, pub friendly_name: String, pub token: Vec, - pub expires_at: Option, + pub expires_at: Option>, } #[derive(Queryable, Selectable, Debug, Clone)] @@ -77,7 +76,7 @@ pub struct Upload { pub forc_version: String, pub abi_ipfs_hash: Option, pub bytecode_identifier: Option, - pub created_at: SystemTime, + pub created_at: DateTime, } #[derive(Insertable, Debug)] @@ -146,15 +145,15 @@ pub struct NewPackageVersion { #[derive(QueryableByName, Serialize, Debug)] #[serde(rename_all = "camelCase")] -pub struct RecentPackage { - #[sql_type = "Text"] - pub name: String, // Corresponds to `p.package_name as name` - #[sql_type = "Text"] - pub version: String, // Corresponds to `pv.num as version` - #[sql_type = "Nullable"] - pub description: Option, // Corresponds to `pv.package_description as description`, which might be nullable - #[sql_type = "Timestamptz"] - pub created_at: DateTime, // Corresponds to `p.created_at as created_at` - #[sql_type = "Timestamptz"] - pub updated_at: DateTime, // Corresponds to `pv.created_at as updated_at` +pub struct PackagePreview { + #[diesel(sql_type = Text)] + pub name: String, + #[diesel(sql_type = Text)] + pub version: String, + #[diesel(sql_type = Nullable)] + pub description: Option, + #[diesel(sql_type = Timestamptz)] + pub created_at: DateTime, + #[diesel(sql_type = Timestamptz)] + pub updated_at: DateTime, } diff --git a/src/schema.rs b/src/schema.rs index 921cd32..b5513c9 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -6,8 +6,8 @@ diesel::table! { user_id -> Uuid, friendly_name -> Varchar, token -> Bytea, - expires_at -> Nullable, - created_at -> Timestamp, + expires_at -> Nullable, + created_at -> Timestamptz, } } @@ -44,8 +44,8 @@ diesel::table! { sessions (id) { id -> Uuid, user_id -> Uuid, - expires_at -> Timestamp, - created_at -> Timestamp, + expires_at -> Timestamptz, + created_at -> Timestamptz, } } @@ -56,7 +56,7 @@ diesel::table! { forc_version -> Varchar, abi_ipfs_hash -> Nullable, bytecode_identifier -> Nullable, - created_at -> Timestamp, + created_at -> Timestamptz, } } @@ -69,7 +69,7 @@ diesel::table! { avatar_url -> Nullable, email -> Nullable, is_admin -> Bool, - created_at -> Timestamp, + created_at -> Timestamptz, } } diff --git a/src/upload.rs b/src/upload.rs index bbfa456..c1e82df 100644 --- a/src/upload.rs +++ b/src/upload.rs @@ -223,11 +223,12 @@ pub fn install_forc_at_path(forc_version: &str, forc_path: &Path) -> Result<(), #[cfg(test)] mod tests { + use serial_test::serial; use crate::pinata::MockPinataClient; - use super::*; #[tokio::test] + #[serial] async fn handle_project_upload_success() { let upload_id = Uuid::new_v4(); let upload_dir = PathBuf::from("tmp/uploads/").join(upload_id.to_string()); diff --git a/src/util.rs b/src/util.rs index b180fa8..563745e 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,13 +1,5 @@ use semver::Version; -use std::{path::Path, time::SystemTime}; - -pub fn sys_time_to_epoch(sys_time: SystemTime) -> u64 { - sys_time - .duration_since(SystemTime::UNIX_EPOCH) - .expect("convert time to epoch") - .as_secs() - * 1000 -} +use std::path::Path; pub fn validate_or_format_semver(version: &str) -> Option { // Remove the leading 'v' if it exists @@ -40,6 +32,7 @@ mod tests { use super::*; use std::env; use std::fs; + use serial_test::serial; use tempfile::tempdir; #[test] @@ -69,6 +62,7 @@ mod tests { } #[test] + #[serial] fn test_load_env() { // Save the current directory let original_dir = env::current_dir().unwrap();