From d340db3b51b0d74a8a2eb2ef5ab3de4bd67cbeec Mon Sep 17 00:00:00 2001 From: neri Date: Sun, 27 Feb 2022 01:50:29 +0100 Subject: [PATCH] migrate from chrono to time --- Cargo.lock | 183 +++++++++++++++++++++++++++++++++++++++-------- Cargo.toml | 4 +- init-db.sql | 1 + src/config.rs | 2 +- src/deleter.rs | 23 +++--- src/multipart.rs | 34 +++++---- src/template.rs | 17 +++-- src/upload.rs | 2 +- 8 files changed, 198 insertions(+), 68 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d0254d5..c955d6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -278,6 +278,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "base-x" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" + [[package]] name = "base64" version = "0.13.0" @@ -357,17 +363,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "chrono" -version = "0.4.19" +name = "const_fn" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" -dependencies = [ - "libc", - "num-integer", - "num-traits", - "time 0.1.44", - "winapi", -] +checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" [[package]] name = "convert_case" @@ -440,7 +439,6 @@ dependencies = [ "actix-files", "actix-multipart", "actix-web", - "chrono", "env_logger", "futures-util", "htmlescape", @@ -448,6 +446,7 @@ dependencies = [ "mime", "rand", "sqlx", + "time 0.2.27", "tokio", "tree_magic_mini", "url", @@ -463,7 +462,7 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "rustc_version", + "rustc_version 0.4.0", "syn", ] @@ -506,6 +505,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "discard" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" + [[package]] name = "dotenv" version = "0.15.0" @@ -992,16 +997,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "num-integer" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" -dependencies = [ - "autocfg", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.14" @@ -1130,6 +1125,12 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + [[package]] name = "proc-macro2" version = "1.0.36" @@ -1229,13 +1230,22 @@ dependencies = [ "winapi", ] +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver 0.9.0", +] + [[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver", + "semver 1.0.6", ] [[package]] @@ -1273,12 +1283,27 @@ dependencies = [ "untrusted", ] +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + [[package]] name = "semver" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a3381e03edd24287172047536f20cabde766e2cd3e65e6b00fb3af51c4f38d" +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + [[package]] name = "serde" version = "1.0.136" @@ -1346,6 +1371,21 @@ dependencies = [ "digest 0.10.3", ] +[[package]] +name = "sha1" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" +dependencies = [ + "sha1_smol", +] + +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + [[package]] name = "sha2" version = "0.9.9" @@ -1429,7 +1469,6 @@ dependencies = [ "bitflags", "byteorder", "bytes", - "chrono", "crossbeam-queue", "dirs", "either", @@ -1460,6 +1499,7 @@ dependencies = [ "sqlx-rt", "stringprep", "thiserror", + "time 0.2.27", "tokio-stream", "url", "webpki", @@ -1496,6 +1536,64 @@ dependencies = [ "tokio-rustls", ] +[[package]] +name = "standback" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" +dependencies = [ + "version_check", +] + +[[package]] +name = "stdweb" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" +dependencies = [ + "discard", + "rustc_version 0.2.3", + "stdweb-derive", + "stdweb-internal-macros", + "stdweb-internal-runtime", + "wasm-bindgen", +] + +[[package]] +name = "stdweb-derive" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_derive", + "syn", +] + +[[package]] +name = "stdweb-internal-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" +dependencies = [ + "base-x", + "proc-macro2", + "quote", + "serde", + "serde_derive", + "serde_json", + "sha1", + "syn", +] + +[[package]] +name = "stdweb-internal-runtime" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" + [[package]] name = "stringprep" version = "0.1.2" @@ -1554,12 +1652,16 @@ dependencies = [ [[package]] name = "time" -version = "0.1.44" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" dependencies = [ + "const_fn", "libc", - "wasi", + "standback", + "stdweb", + "time-macros", + "version_check", "winapi", ] @@ -1574,6 +1676,29 @@ dependencies = [ "num_threads", ] +[[package]] +name = "time-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" +dependencies = [ + "proc-macro-hack", + "time-macros-impl", +] + +[[package]] +name = "time-macros-impl" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "standback", + "syn", +] + [[package]] name = "tinyvec" version = "1.5.1" @@ -1807,9 +1932,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" +version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "wasm-bindgen" diff --git a/Cargo.toml b/Cargo.toml index 937485f..c38c274 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" [dependencies] actix-web = { version = "4.0", default-features = false, features = ["macros", "compress-gzip", "compress-zstd"]} -sqlx = { version = "0.5.1", default-features = false, features = [ "runtime-tokio-rustls", "postgres", "chrono" ] } +sqlx = { version = "0.5.1", default-features = false, features = [ "runtime-tokio-rustls", "postgres", "time" ] } env_logger = "0.9.0" log = "0.4.14" actix-files = "0.6.0" @@ -16,7 +16,7 @@ tokio = { version = "1.17.0", features=["rt", "macros", "sync"] } actix-multipart = "0.4.0" futures-util = "0.3" rand = "0.8.3" -chrono = "0.4.19" +time = "0.2.7" htmlescape = "0.3.1" urlencoding = "2.1.0" tree_magic_mini = { version = "3.0.0", features = ["with-gpl-data"] } diff --git a/init-db.sql b/init-db.sql index 55c5ff2..5f2ab73 100644 --- a/init-db.sql +++ b/init-db.sql @@ -9,3 +9,4 @@ CREATE TABLE IF NOT EXISTS files ( ALTER TABLE files ADD COLUMN IF NOT EXISTS delete_on_download boolean; ALTER TABLE files ALTER COLUMN delete_on_download set not null; +ALTER TABLE files ALTER COLUMN valid_till TYPE timestamptz; diff --git a/src/config.rs b/src/config.rs index f382bab..5ec9bd6 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,7 +1,7 @@ use std::env; -use chrono::Duration; use std::path::PathBuf; +use time::Duration; use tokio::fs; #[derive(Clone)] diff --git a/src/deleter.rs b/src/deleter.rs index 193305c..7a77ef1 100644 --- a/src/deleter.rs +++ b/src/deleter.rs @@ -1,7 +1,9 @@ -use chrono::{prelude::*, Duration}; use futures_util::TryStreamExt; +use time::OffsetDateTime; use sqlx::{postgres::PgPool, Row}; +use std::cmp::max; use std::path::{Path, PathBuf}; +use time::ext::NumericalStdDuration; use tokio::fs; use tokio::sync::mpsc::Receiver; use tokio::time::timeout; @@ -10,7 +12,7 @@ pub(crate) async fn delete_old_files(mut receiver: Receiver<()>, db: PgPool, fil loop { wait_for_file_expiry(&mut receiver, &db).await; - let now = Local::now().naive_local(); + let now = OffsetDateTime::now_utc(); let mut rows = sqlx::query("SELECT file_id FROM files WHERE files.valid_till < $1") .bind(now) .fetch(&db); @@ -51,17 +53,18 @@ async fn delete_content(file_id: &str, files_dir: &Path) -> Result<(), std::io:: } async fn wait_for_file_expiry(receiver: &mut Receiver<()>, db: &PgPool) { - let valid_till: (Option,) = + let valid_till: (Option,) = sqlx::query_as("SELECT MIN(valid_till) as min from files") .fetch_one(db) .await .expect("could not fetch expiring files from database"); - let next_timeout = match valid_till.0 { - Some(valid_till) => valid_till.signed_duration_since(Local::now().naive_local()), - None => Duration::days(1), + let next_timeout: std::time::Duration = match valid_till.0 { + Some(valid_till) => (max( + 0, + valid_till.unix_timestamp() - OffsetDateTime::now_utc().unix_timestamp(), + ) as u64) + .std_seconds(), + None => 1_u64.std_days(), }; - let positive_timeout = next_timeout - .to_std() - .unwrap_or_else(|_| std::time::Duration::from_secs(0)); - let _ = timeout(positive_timeout, receiver.recv()).await; + let _ = timeout(next_timeout, receiver.recv()).await; } diff --git a/src/multipart.rs b/src/multipart.rs index 719fd12..16eada6 100644 --- a/src/multipart.rs +++ b/src/multipart.rs @@ -1,17 +1,18 @@ use crate::{config, file_kind::FileKind}; use actix_multipart::{Field, Multipart}; use actix_web::{error, http::header::DispositionParam, Error}; -use chrono::{prelude::*, Duration}; use futures_util::{StreamExt, TryStreamExt}; use std::path::Path; +use time::OffsetDateTime; +use time::{ext::NumericalDuration, Duration}; use tokio::{fs::File, io::AsyncWriteExt}; const MAX_UPLOAD_SECONDS: i64 = 31 * 24 * 60 * 60; -const DEFAULT_UPLOAD_SECONDS: u64 = 30 * 60; +const DEFAULT_UPLOAD_SECONDS: u32 = 30 * 60; pub(crate) struct UploadConfig { pub original_name: String, - pub valid_till: DateTime, + pub valid_till: OffsetDateTime, pub kind: FileKind, pub delete_on_download: bool, } @@ -66,16 +67,13 @@ pub(crate) async fn parse_multipart( let original_name = original_name.ok_or_else(|| error::ErrorBadRequest("no content found"))?; let kind = kind.ok_or_else(|| error::ErrorBadRequest("no content found"))?; - let validated_keep_for: u64 = if let Some(keep_for) = keep_for { - keep_for - .parse() - .map_err(|e| error::ErrorBadRequest(format!("field keep_for is not a number: {}", e)))? - } else { - DEFAULT_UPLOAD_SECONDS - }; - let valid_duration = Duration::seconds(validated_keep_for as i64); - let now = Local::now(); - let valid_till = now + valid_duration; + let keep_for: u32 = keep_for + .map(|k| k.parse()) + .transpose() + .map_err(|e| error::ErrorBadRequest(format!("field keep_for is not a number: {}", e)))? + .unwrap_or(DEFAULT_UPLOAD_SECONDS); + let valid_duration = keep_for.seconds(); + let valid_till = OffsetDateTime::now_utc() + valid_duration; let upload_config = UploadConfig { original_name, @@ -84,7 +82,7 @@ pub(crate) async fn parse_multipart( delete_on_download, }; - check_requirements(&upload_config, size, password, now, config)?; + check_requirements(&upload_config, size, password, &valid_duration, config)?; Ok(upload_config) } @@ -93,14 +91,14 @@ fn check_requirements( upload_config: &UploadConfig, size: u64, password: Option, - now: DateTime, + valid_duration: &Duration, config: &config::Config, ) -> Result<(), error::Error> { if upload_config.original_name.len() > 255 { return Err(error::ErrorBadRequest("filename is too long")); } - let valid_seconds = (upload_config.valid_till - now).num_seconds(); + let valid_seconds = valid_duration.whole_seconds(); if valid_seconds > MAX_UPLOAD_SECONDS { return Err(error::ErrorBadRequest(format!( "maximum allowed validity is {} seconds, but you specified {} seconds", @@ -109,8 +107,8 @@ fn check_requirements( } if let Some(no_auth_limits) = &config.no_auth_limits { - let requires_auth = valid_seconds > no_auth_limits.max_time.num_seconds() - || valid_seconds > no_auth_limits.large_file_max_time.num_seconds() + let requires_auth = valid_seconds > no_auth_limits.max_time.whole_seconds() + || valid_seconds > no_auth_limits.large_file_max_time.whole_seconds() && size > no_auth_limits.large_file_size; // hIGh sECUriTy paSsWoRD CHEck if requires_auth && password.as_ref() != Some(&no_auth_limits.auth_password) { diff --git a/src/template.rs b/src/template.rs index fb57ab4..85cd606 100644 --- a/src/template.rs +++ b/src/template.rs @@ -1,7 +1,7 @@ use std::{cmp, io::ErrorKind}; use actix_web::HttpRequest; -use chrono::Duration; +use time::Duration; use tokio::fs; use crate::config::Config; @@ -82,10 +82,10 @@ fn render_file_size(size: u64) -> String { } fn render_duration(duration: Duration) -> String { - let days = duration.num_days(); - let hours = duration.num_hours() % 24; - let minutes = duration.num_minutes() % 60; - let seconds = duration.num_seconds() % 60; + let days = duration.whole_days(); + let hours = duration.whole_hours() % 24; + let minutes = duration.whole_minutes() % 60; + let seconds = duration.whole_seconds() % 60; let mut elements = vec![]; if let Some(name) = pluralize(days, "tag", "e") { elements.push(name); @@ -115,11 +115,14 @@ fn build_auth_hide_js(config: &Config) -> Option { let auth_hide_js = AUTH_HIDE_JS .replace( "{no_auth_max_time}", - &no_auth_limits.max_time.num_seconds().to_string(), + &no_auth_limits.max_time.whole_seconds().to_string(), ) .replace( "{no_auth_large_file_max_time}", - &no_auth_limits.large_file_max_time.num_seconds().to_string(), + &no_auth_limits + .large_file_max_time + .whole_seconds() + .to_string(), ) .replace( "{no_auth_large_file_size}", diff --git a/src/upload.rs b/src/upload.rs index aae4c37..0c1a389 100644 --- a/src/upload.rs +++ b/src/upload.rs @@ -71,7 +71,7 @@ pub async fn upload( ) .bind(&file_id) .bind(&original_name) - .bind(valid_till.naive_local()) + .bind(valid_till) .bind(kind.to_string()) .bind(delete_on_download) .execute(db.as_ref())