transbeam/src/state.rs

136 lines
3.8 KiB
Rust

use jsondb::JsonDb;
pub mod prelude {
pub use std::collections::HashMap;
pub use jsondb::Schema;
pub use serde::{Deserialize, Serialize};
pub use serde_with::serde_as;
pub use serde_with::skip_serializing_none;
pub use time::OffsetDateTime;
pub trait SizedFile {
fn size(&self) -> u64;
fn formatted_size(&self) -> String {
bytesize::to_string(self.size(), false).replace(' ', "")
}
}
}
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,
}
}
}
impl SizedFile for UploadedFile {
fn size(&self) -> u64 {
self.size
}
}
#[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>,
}
impl SizedFile for StoredFile {
fn size(&self) -> u64 {
self.size
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct StoredFileWithPassword {
#[serde(flatten)]
pub file: StoredFile,
/// None password means the admin page can't be accessed
pub password: Option<String>,
}
impl From<super::v0::StoredFile> for StoredFileWithPassword {
fn from(old: super::v0::StoredFile) -> Self {
StoredFileWithPassword {
file: 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, StoredFileWithPassword>);
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, StoredFileWithPassword::from(v)))
.collect(),
)
}
}
}
pub use v1::State;
pub type StateDb = JsonDb<State>;