make cosmetic changes to state schema, add fileset passwords for forthcoming functionality
This commit is contained in:
parent
073feda920
commit
aef58d133b
134
Cargo.lock
generated
134
Cargo.lock
generated
|
@ -433,6 +433,12 @@ dependencies = [
|
||||||
"alloc-stdlib",
|
"alloc-stdlib",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.4.3"
|
version = "1.4.3"
|
||||||
|
@ -496,6 +502,20 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono"
|
||||||
|
version = "0.4.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6127248204b9aba09a362f6c930ef6a78f2c1b2215f8a7b398c06e1083f17af0"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
"serde",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cipher"
|
name = "cipher"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
@ -578,9 +598,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling"
|
name = "darling"
|
||||||
version = "0.13.4"
|
version = "0.14.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
|
checksum = "4529658bdda7fd6769b8614be250cdcfc3aeb0ee72fe66f9e41e5e5eb73eac02"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core",
|
||||||
"darling_macro",
|
"darling_macro",
|
||||||
|
@ -588,9 +608,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling_core"
|
name = "darling_core"
|
||||||
version = "0.13.4"
|
version = "0.14.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
|
checksum = "649c91bc01e8b1eac09fb91e8dbc7d517684ca6be8ebc75bb9cafc894f9fdb6f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fnv",
|
"fnv",
|
||||||
"ident_case",
|
"ident_case",
|
||||||
|
@ -602,9 +622,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling_macro"
|
name = "darling_macro"
|
||||||
version = "0.13.4"
|
version = "0.14.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
|
checksum = "ddfc69c5bfcbd2fc09a0f38451d2daf0e372e367986a83906d1b0dbc88134fb5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -782,6 +802,12 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hex"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hmac"
|
name = "hmac"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
|
@ -857,6 +883,7 @@ checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -896,6 +923,15 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.58"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27"
|
||||||
|
dependencies = [
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jsondb"
|
name = "jsondb"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
@ -1057,6 +1093,16 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-integer"
|
||||||
|
version = "0.1.45"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
|
@ -1262,12 +1308,6 @@ dependencies = [
|
||||||
"semver",
|
"semver",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustversion"
|
|
||||||
version = "1.0.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.9"
|
version = "1.0.9"
|
||||||
|
@ -1337,20 +1377,25 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_with"
|
name = "serde_with"
|
||||||
version = "1.13.0"
|
version = "2.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b827f2113224f3f19a665136f006709194bdfdcb1fdc1e4b2b5cbac8e0cced54"
|
checksum = "89df7a26519371a3cce44fbb914c2819c84d9b897890987fa3ab096491cc0ea8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustversion",
|
"base64",
|
||||||
|
"chrono",
|
||||||
|
"hex",
|
||||||
|
"indexmap",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"serde_with_macros",
|
"serde_with_macros",
|
||||||
|
"time",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_with_macros"
|
name = "serde_with_macros"
|
||||||
version = "1.5.2"
|
version = "2.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082"
|
checksum = "de337f322382fcdfbb21a014f7c224ee041a23785651db67b9827403178f698f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
@ -1483,6 +1528,7 @@ dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"libc",
|
"libc",
|
||||||
"num_threads",
|
"num_threads",
|
||||||
|
"serde",
|
||||||
"time-macros",
|
"time-macros",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1695,6 +1741,60 @@ version = "0.11.0+wasi-snapshot-preview1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.81"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-backend"
|
||||||
|
version = "0.2.81"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"lazy_static",
|
||||||
|
"log",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.81"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.81"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-backend",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.81"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
|
|
@ -29,7 +29,7 @@ rand = "0.8.5"
|
||||||
sanitise-file-name = "1.0.0"
|
sanitise-file-name = "1.0.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde_with = "1.13.0"
|
serde_with = { version = "2", features = ["time_0_3"] }
|
||||||
thiserror = "1"
|
thiserror = "1"
|
||||||
time = "0.3.9"
|
time = "0.3.9"
|
||||||
tokio = { version = "1.17.0", features = ["full"] }
|
tokio = { version = "1.17.0", features = ["full"] }
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
mod download;
|
mod download;
|
||||||
mod state;
|
mod state;
|
||||||
mod store;
|
mod store;
|
||||||
mod timestamp;
|
|
||||||
mod upload;
|
mod upload;
|
||||||
mod zip;
|
mod zip;
|
||||||
|
|
||||||
|
@ -310,7 +309,9 @@ async fn main() -> std::io::Result<()> {
|
||||||
let cachebuster: String = env_or_else("TRANSBEAM_CACHEBUSTER", String::new);
|
let cachebuster: String = env_or_else("TRANSBEAM_CACHEBUSTER", String::new);
|
||||||
|
|
||||||
let state_file: PathBuf = match std::env::var("TRANSBEAM_STATE_FILE") {
|
let state_file: PathBuf = match std::env::var("TRANSBEAM_STATE_FILE") {
|
||||||
Ok(v) => v.parse().unwrap_or_else(|_| panic!("Invalid value {} for variable TRANSBEAM_STATE_FILE", v)),
|
Ok(v) => v
|
||||||
|
.parse()
|
||||||
|
.unwrap_or_else(|_| panic!("Invalid value {} for variable TRANSBEAM_STATE_FILE", v)),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let legacy_state_file = storage_dir.join("files.json");
|
let legacy_state_file = storage_dir.join("files.json");
|
||||||
if legacy_state_file.is_file() {
|
if legacy_state_file.is_file() {
|
||||||
|
@ -322,7 +323,9 @@ async fn main() -> std::io::Result<()> {
|
||||||
};
|
};
|
||||||
|
|
||||||
let data = web::Data::new(AppData {
|
let data = web::Data::new(AppData {
|
||||||
state: StateDb::load(state_file).await.expect("Failed to load state file"),
|
state: StateDb::load(state_file)
|
||||||
|
.await
|
||||||
|
.expect("Failed to load state file"),
|
||||||
config: Config {
|
config: Config {
|
||||||
base_url,
|
base_url,
|
||||||
max_upload_size,
|
max_upload_size,
|
||||||
|
|
107
src/state.rs
107
src/state.rs
|
@ -1,12 +1,109 @@
|
||||||
use jsondb::JsonDb;
|
use jsondb::JsonDb;
|
||||||
|
|
||||||
mod v0 {
|
mod prelude {
|
||||||
pub type State = crate::store::StoredFiles;
|
pub use std::collections::HashMap;
|
||||||
|
|
||||||
impl jsondb::SchemaV0 for State {
|
pub use jsondb::Schema;
|
||||||
const VERSION_OPTIONAL: bool = true;
|
pub use serde::{Deserialize, Serialize};
|
||||||
|
pub use serde_with::serde_as;
|
||||||
|
pub use serde_with::skip_serializing_none;
|
||||||
|
pub use time::OffsetDateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
mod v0;
|
||||||
|
|
||||||
|
pub mod v1 {
|
||||||
|
//! Schema version 1
|
||||||
|
//!
|
||||||
|
//! Changes:
|
||||||
|
//!
|
||||||
|
//! * Represent times in RFC3339 format instead of epoch seconds
|
||||||
|
//! * Ditch some pre-JsonDb ad-hoc migrations
|
||||||
|
//! * Stored files now have optional passwords for
|
||||||
|
//! deleting/extending them, stored in plaintext because the
|
||||||
|
//! threat model doesn't warrant anything stronger
|
||||||
|
|
||||||
|
use super::prelude::*;
|
||||||
|
|
||||||
|
#[serde_as]
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
|
pub struct UploadedFile {
|
||||||
|
pub name: String,
|
||||||
|
pub size: u64,
|
||||||
|
#[serde_as(as = "time::format_description::well_known::Rfc3339")]
|
||||||
|
pub modtime: OffsetDateTime,
|
||||||
|
}
|
||||||
|
impl From<super::v0::UploadedFile> for UploadedFile {
|
||||||
|
fn from(old: super::v0::UploadedFile) -> Self {
|
||||||
|
UploadedFile {
|
||||||
|
name: old.name,
|
||||||
|
size: old.size,
|
||||||
|
modtime: old.modtime,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use v0::State;
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
|
pub struct FileSet {
|
||||||
|
pub files: Vec<UploadedFile>,
|
||||||
|
// Optional for backwards compatibility only
|
||||||
|
pub directory_name: Option<String>,
|
||||||
|
}
|
||||||
|
impl From<super::v0::FileSet> for FileSet {
|
||||||
|
fn from(old: super::v0::FileSet) -> Self {
|
||||||
|
FileSet {
|
||||||
|
files: old.files.into_iter().map(UploadedFile::from).collect(),
|
||||||
|
directory_name: old.directory_name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[serde_as]
|
||||||
|
#[skip_serializing_none]
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
|
pub struct StoredFile {
|
||||||
|
pub name: String,
|
||||||
|
pub size: u64,
|
||||||
|
#[serde_as(as = "time::format_description::well_known::Rfc3339")]
|
||||||
|
pub modtime: OffsetDateTime,
|
||||||
|
#[serde_as(as = "time::format_description::well_known::Rfc3339")]
|
||||||
|
pub expiry: OffsetDateTime,
|
||||||
|
pub contents: Option<FileSet>,
|
||||||
|
/// None password means the admin page can't be accessed
|
||||||
|
pub password: Option<String>,
|
||||||
|
}
|
||||||
|
impl From<super::v0::StoredFile> for StoredFile {
|
||||||
|
fn from(old: super::v0::StoredFile) -> Self {
|
||||||
|
StoredFile {
|
||||||
|
name: old.name,
|
||||||
|
size: old.size,
|
||||||
|
modtime: old.modtime,
|
||||||
|
expiry: old.expiry,
|
||||||
|
contents: old.contents.map(FileSet::from),
|
||||||
|
password: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize, Serialize)]
|
||||||
|
pub struct StoredFiles(pub HashMap<String, StoredFile>);
|
||||||
|
|
||||||
|
pub type State = StoredFiles;
|
||||||
|
|
||||||
|
impl Schema for State {
|
||||||
|
type Prev = super::v0::State;
|
||||||
|
}
|
||||||
|
impl From<super::v0::State> for State {
|
||||||
|
fn from(old: super::v0::State) -> Self {
|
||||||
|
StoredFiles(
|
||||||
|
old.0
|
||||||
|
.into_iter()
|
||||||
|
.map(|(k, v)| (k, StoredFile::from(v)))
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use v1::State;
|
||||||
pub type StateDb = JsonDb<State>;
|
pub type StateDb = JsonDb<State>;
|
||||||
|
|
92
src/state/v0.rs
Normal file
92
src/state/v0.rs
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
use super::prelude::*;
|
||||||
|
|
||||||
|
use serde_with::{FromInto, PickFirst};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
|
pub struct UploadedFile {
|
||||||
|
pub name: String,
|
||||||
|
pub size: u64,
|
||||||
|
#[serde(with = "timestamp")]
|
||||||
|
pub modtime: OffsetDateTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
|
pub struct FileSet {
|
||||||
|
pub files: Vec<UploadedFile>,
|
||||||
|
// Optional for backwards compatibility only
|
||||||
|
pub directory_name: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<UploadedFile>> for FileSet {
|
||||||
|
fn from(files: Vec<UploadedFile>) -> Self {
|
||||||
|
Self {
|
||||||
|
files,
|
||||||
|
directory_name: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[serde_as]
|
||||||
|
#[skip_serializing_none]
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
|
pub struct StoredFile {
|
||||||
|
pub name: String,
|
||||||
|
pub size: u64,
|
||||||
|
#[serde(with = "timestamp")]
|
||||||
|
pub modtime: OffsetDateTime,
|
||||||
|
#[serde(with = "timestamp")]
|
||||||
|
pub expiry: OffsetDateTime,
|
||||||
|
#[serde_as(as = "Option<PickFirst<(_, FromInto<Vec<UploadedFile>>)>>")]
|
||||||
|
#[serde(default)]
|
||||||
|
pub contents: Option<FileSet>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize, Serialize)]
|
||||||
|
pub struct StoredFiles(pub HashMap<String, StoredFile>);
|
||||||
|
|
||||||
|
pub type State = StoredFiles;
|
||||||
|
|
||||||
|
impl jsondb::SchemaV0 for State {
|
||||||
|
const VERSION_OPTIONAL: bool = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
mod timestamp {
|
||||||
|
use core::fmt;
|
||||||
|
|
||||||
|
use serde::{de::Visitor, Deserializer, Serializer};
|
||||||
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
|
pub(crate) fn serialize<S: Serializer>(
|
||||||
|
time: &OffsetDateTime,
|
||||||
|
ser: S,
|
||||||
|
) -> Result<S::Ok, S::Error> {
|
||||||
|
ser.serialize_i64(time.unix_timestamp())
|
||||||
|
}
|
||||||
|
|
||||||
|
struct I64Visitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for I64Visitor {
|
||||||
|
type Value = i64;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(formatter, "an integer")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E> {
|
||||||
|
Ok(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> {
|
||||||
|
Ok(v as i64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn deserialize<'de, D: Deserializer<'de>>(
|
||||||
|
de: D,
|
||||||
|
) -> Result<OffsetDateTime, D::Error> {
|
||||||
|
Ok(
|
||||||
|
OffsetDateTime::from_unix_timestamp(de.deserialize_i64(I64Visitor)?)
|
||||||
|
.unwrap_or_else(|_| OffsetDateTime::now_utc()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
34
src/store.rs
34
src/store.rs
|
@ -1,7 +1,7 @@
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
|
||||||
io::ErrorKind,
|
io::ErrorKind,
|
||||||
path::{Path, PathBuf}, ops::DerefMut,
|
ops::DerefMut,
|
||||||
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
use log::{debug, error, info};
|
use log::{debug, error, info};
|
||||||
|
@ -9,15 +9,9 @@ use rand::{
|
||||||
distributions::{Alphanumeric, DistString},
|
distributions::{Alphanumeric, DistString},
|
||||||
thread_rng, Rng,
|
thread_rng, Rng,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use serde_with::skip_serializing_none;
|
|
||||||
use serde_with::{serde_as, FromInto, PickFirst};
|
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
use tokio::fs::File;
|
use tokio::fs::File;
|
||||||
|
|
||||||
use crate::upload::UploadedFile;
|
|
||||||
use crate::zip::FileSet;
|
|
||||||
|
|
||||||
const MAX_STORAGE_FILES: usize = 1024;
|
const MAX_STORAGE_FILES: usize = 1024;
|
||||||
|
|
||||||
pub fn gen_storage_code(use_mnemonic: bool) -> String {
|
pub fn gen_storage_code(use_mnemonic: bool) -> String {
|
||||||
|
@ -34,23 +28,7 @@ pub fn is_valid_storage_code(s: &str) -> bool {
|
||||||
.all(|c| c.is_ascii_alphanumeric() || c == &b'-')
|
.all(|c| c.is_ascii_alphanumeric() || c == &b'-')
|
||||||
}
|
}
|
||||||
|
|
||||||
#[serde_as]
|
pub use crate::state::v1::{StoredFile, StoredFiles};
|
||||||
#[skip_serializing_none]
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
|
||||||
pub struct StoredFile {
|
|
||||||
pub name: String,
|
|
||||||
pub size: u64,
|
|
||||||
#[serde(with = "crate::timestamp")]
|
|
||||||
pub modtime: OffsetDateTime,
|
|
||||||
#[serde(with = "crate::timestamp")]
|
|
||||||
pub expiry: OffsetDateTime,
|
|
||||||
#[serde_as(as = "Option<PickFirst<(_, FromInto<Vec<UploadedFile>>)>>")]
|
|
||||||
#[serde(default)]
|
|
||||||
pub contents: Option<FileSet>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Serialize)]
|
|
||||||
pub struct StoredFiles(pub HashMap<String, StoredFile>);
|
|
||||||
|
|
||||||
async fn is_valid_entry(key: &str, info: &StoredFile, storage_dir: &Path) -> bool {
|
async fn is_valid_entry(key: &str, info: &StoredFile, storage_dir: &Path) -> bool {
|
||||||
if info.expiry < OffsetDateTime::now_utc() {
|
if info.expiry < OffsetDateTime::now_utc() {
|
||||||
|
@ -130,11 +108,7 @@ impl crate::AppData {
|
||||||
/// Attempts to add a file to the store. Returns an I/O error if
|
/// Attempts to add a file to the store. Returns an I/O error if
|
||||||
/// something's broken, or a u64 of the maximum allowed file size
|
/// something's broken, or a u64 of the maximum allowed file size
|
||||||
/// if the file was too big, or a unit if everything worked.
|
/// if the file was too big, or a unit if everything worked.
|
||||||
pub async fn add_file(
|
pub async fn add_file(&self, key: String, file: StoredFile) -> Result<(), FileAddError> {
|
||||||
&self,
|
|
||||||
key: String,
|
|
||||||
file: StoredFile,
|
|
||||||
) -> Result<(), FileAddError> {
|
|
||||||
let mut store = self.state.write().await;
|
let mut store = self.state.write().await;
|
||||||
if store.full(self.config.max_storage_size) {
|
if store.full(self.config.max_storage_size) {
|
||||||
return Err(FileAddError::Full);
|
return Err(FileAddError::Full);
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
use core::fmt;
|
|
||||||
|
|
||||||
use serde::{de::Visitor, Deserializer, Serializer};
|
|
||||||
use time::OffsetDateTime;
|
|
||||||
|
|
||||||
pub(crate) fn serialize<S: Serializer>(time: &OffsetDateTime, ser: S) -> Result<S::Ok, S::Error> {
|
|
||||||
ser.serialize_i64(time.unix_timestamp())
|
|
||||||
}
|
|
||||||
|
|
||||||
struct I64Visitor;
|
|
||||||
|
|
||||||
impl<'de> Visitor<'de> for I64Visitor {
|
|
||||||
type Value = i64;
|
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(formatter, "an integer")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E> {
|
|
||||||
Ok(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> {
|
|
||||||
Ok(v as i64)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn deserialize<'de, D: Deserializer<'de>>(de: D) -> Result<OffsetDateTime, D::Error> {
|
|
||||||
Ok(
|
|
||||||
OffsetDateTime::from_unix_timestamp(de.deserialize_i64(I64Visitor)?)
|
|
||||||
.unwrap_or_else(|_| OffsetDateTime::now_utc()),
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -109,13 +109,7 @@ impl Actor for Uploader {
|
||||||
|
|
||||||
type Context = <Uploader as Actor>::Context;
|
type Context = <Uploader as Actor>::Context;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
pub use crate::state::v1::UploadedFile;
|
||||||
pub struct UploadedFile {
|
|
||||||
pub name: String,
|
|
||||||
pub size: u64,
|
|
||||||
#[serde(with = "crate::timestamp")]
|
|
||||||
pub modtime: OffsetDateTime,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UploadedFile {
|
impl UploadedFile {
|
||||||
fn new(name: &str, size: u64, modtime: OffsetDateTime) -> Self {
|
fn new(name: &str, size: u64, modtime: OffsetDateTime) -> Self {
|
||||||
|
@ -328,15 +322,14 @@ impl Uploader {
|
||||||
modtime,
|
modtime,
|
||||||
expiry: OffsetDateTime::now_utc() + lifetime * time::Duration::DAY,
|
expiry: OffsetDateTime::now_utc() + lifetime * time::Duration::DAY,
|
||||||
contents,
|
contents,
|
||||||
|
password: None,
|
||||||
};
|
};
|
||||||
let app_data = self.app_data.clone();
|
let app_data = self.app_data.clone();
|
||||||
let storage_filename = self.storage_filename.clone();
|
let storage_filename = self.storage_filename.clone();
|
||||||
ctx.spawn(
|
ctx.spawn(
|
||||||
actix::fut::wrap_future(async move {
|
actix::fut::wrap_future(async move {
|
||||||
debug!("Spawned future to add entry {} to state", storage_filename);
|
debug!("Spawned future to add entry {} to state", storage_filename);
|
||||||
app_data
|
app_data.add_file(storage_filename, stored_file).await
|
||||||
.add_file(storage_filename, stored_file)
|
|
||||||
.await
|
|
||||||
})
|
})
|
||||||
.map(|res, u: &mut Self, ctx: &mut Context| match res {
|
.map(|res, u: &mut Self, ctx: &mut Context| match res {
|
||||||
Ok(()) => ctx.text(
|
Ok(()) => ctx.text(
|
||||||
|
@ -406,10 +399,7 @@ impl Uploader {
|
||||||
ctx.wait(
|
ctx.wait(
|
||||||
actix::fut::wrap_future(async move {
|
actix::fut::wrap_future(async move {
|
||||||
debug!("Spawned future to remove entry {} from state", filename);
|
debug!("Spawned future to remove entry {} from state", filename);
|
||||||
app_data
|
app_data.remove_file(&filename).await.unwrap();
|
||||||
.remove_file(&filename)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
})
|
})
|
||||||
.map(stop_and_flush),
|
.map(stop_and_flush),
|
||||||
);
|
);
|
||||||
|
|
17
src/zip.rs
17
src/zip.rs
|
@ -2,7 +2,6 @@ use std::io::Write;
|
||||||
|
|
||||||
use crc32fast::Hasher;
|
use crc32fast::Hasher;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
use crate::upload::UploadedFile;
|
use crate::upload::UploadedFile;
|
||||||
|
@ -28,21 +27,7 @@ const EOCD_TOTAL_SIZE: u64 = EOCD64_RECORD_SIZE + EOCD64_LOCATOR_SIZE + EOCD_REC
|
||||||
|
|
||||||
const EMPTY_STRING_CRC32: u32 = 0;
|
const EMPTY_STRING_CRC32: u32 = 0;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
pub use crate::state::v1::FileSet;
|
||||||
pub struct FileSet {
|
|
||||||
pub files: Vec<UploadedFile>,
|
|
||||||
// Optional for backwards compatibility only
|
|
||||||
pub directory_name: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<UploadedFile>> for FileSet {
|
|
||||||
fn from(files: Vec<UploadedFile>) -> Self {
|
|
||||||
Self {
|
|
||||||
files,
|
|
||||||
directory_name: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn full_file_name_len(file: &UploadedFile, directory_name: &Option<String>) -> u64 {
|
fn full_file_name_len(file: &UploadedFile, directory_name: &Option<String>) -> u64 {
|
||||||
file.name.len() as u64
|
file.name.len() as u64
|
||||||
|
|
Loading…
Reference in a new issue