refactor: forget epoch and going down to a 4char hex string
This commit is contained in:
parent
a7c74241a0
commit
b3f5f403aa
7 changed files with 123 additions and 69 deletions
84
Cargo.lock
generated
84
Cargo.lock
generated
|
|
@ -443,6 +443,18 @@ dependencies = [
|
||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"r-efi 5.3.0",
|
||||||
|
"wasip2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.4.2"
|
version = "0.4.2"
|
||||||
|
|
@ -451,7 +463,7 @@ checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"r-efi",
|
"r-efi 6.0.0",
|
||||||
"wasip2",
|
"wasip2",
|
||||||
"wasip3",
|
"wasip3",
|
||||||
]
|
]
|
||||||
|
|
@ -465,6 +477,7 @@ dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
"http-body-util",
|
"http-body-util",
|
||||||
|
"rand",
|
||||||
"serde",
|
"serde",
|
||||||
"teloxide",
|
"teloxide",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
|
|
@ -474,7 +487,6 @@ dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"urlencoding",
|
"urlencoding",
|
||||||
"uuid",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1072,6 +1084,15 @@ dependencies = [
|
||||||
"zerovec",
|
"zerovec",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prettyplease"
|
name = "prettyplease"
|
||||||
version = "0.2.37"
|
version = "0.2.37"
|
||||||
|
|
@ -1124,12 +1145,47 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r-efi"
|
||||||
|
version = "5.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "r-efi"
|
name = "r-efi"
|
||||||
version = "6.0.0"
|
version = "6.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
|
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.9.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||||
|
dependencies = [
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.3.4",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rc-box"
|
name = "rc-box"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
|
|
@ -1588,7 +1644,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fastrand",
|
"fastrand",
|
||||||
"getrandom",
|
"getrandom 0.4.2",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
|
|
@ -1876,7 +1932,7 @@ version = "1.23.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9"
|
checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.4.2",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
@ -2387,6 +2443,26 @@ dependencies = [
|
||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.8.48"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.8.48"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.117",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerofrom"
|
name = "zerofrom"
|
||||||
version = "0.1.7"
|
version = "0.1.7"
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ teloxide = { version = "0.13", features = ["macros"], optional = true }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
toml = "0.8"
|
toml = "0.8"
|
||||||
dotenvy = "0.15"
|
dotenvy = "0.15"
|
||||||
uuid = { version = "1", features = ["v4"] }
|
rand = "0.9"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
base64 = "0.22"
|
base64 = "0.22"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
|
|
|
||||||
|
|
@ -50,12 +50,6 @@ impl Entry {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "telegram", test))]
|
|
||||||
/// Return the short ID (UUID portion after the underscore).
|
|
||||||
pub fn short_id(&self) -> &str {
|
|
||||||
self.id.split('_').last().unwrap_or(&self.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Serialize entry back to file format.
|
/// Serialize entry back to file format.
|
||||||
pub fn to_file_contents(&self) -> String {
|
pub fn to_file_contents(&self) -> String {
|
||||||
let frontmatter = toml::to_string_pretty(&self.meta).unwrap();
|
let frontmatter = toml::to_string_pretty(&self.meta).unwrap();
|
||||||
|
|
@ -106,20 +100,11 @@ pub fn read_approved(dir: &Path) -> Vec<Entry> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "telegram", test))]
|
#[cfg(any(feature = "telegram", test))]
|
||||||
/// Find a single entry by short ID (the UUID portion after the underscore).
|
/// Find a single entry by ID.
|
||||||
pub fn find_entry(dir: &Path, short_id: &str) -> Result<Entry, String> {
|
pub fn find_entry(dir: &Path, id: &str) -> Result<Entry, String> {
|
||||||
let read_dir = std::fs::read_dir(dir).map_err(|e| e.to_string())?;
|
let path = dir.join(format!("{id}.txt"));
|
||||||
for item in read_dir {
|
let contents = std::fs::read_to_string(&path).map_err(|_| "Not found.".to_string())?;
|
||||||
let Ok(item) = item else { continue };
|
Entry::parse(id, &contents)
|
||||||
let path = item.path();
|
|
||||||
let fname = path.file_name().and_then(|s| s.to_str()).unwrap_or("");
|
|
||||||
if fname.contains(short_id) && fname.ends_with(".txt") {
|
|
||||||
let contents = std::fs::read_to_string(&path).map_err(|e| e.to_string())?;
|
|
||||||
let id = path.file_stem().and_then(|s| s.to_str()).unwrap_or("").to_string();
|
|
||||||
return Entry::parse(&id, &contents);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err("Not found.".into())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "telegram", test))]
|
#[cfg(any(feature = "telegram", test))]
|
||||||
|
|
@ -334,9 +319,9 @@ Hello!"#;
|
||||||
fn test_find_entry() {
|
fn test_find_entry() {
|
||||||
let dir = tempfile::tempdir().unwrap();
|
let dir = tempfile::tempdir().unwrap();
|
||||||
let contents = "+++\nname = \"alice\"\ndate = \"2026-04-10\"\nstatus = \"pending\"\n+++\nhello";
|
let contents = "+++\nname = \"alice\"\ndate = \"2026-04-10\"\nstatus = \"pending\"\n+++\nhello";
|
||||||
std::fs::write(dir.path().join("1744300800_abcd1234.txt"), contents).unwrap();
|
std::fs::write(dir.path().join("ab12.txt"), contents).unwrap();
|
||||||
|
|
||||||
let entry = find_entry(dir.path(), "abcd1234").unwrap();
|
let entry = find_entry(dir.path(), "ab12").unwrap();
|
||||||
assert_eq!(entry.meta.name, "alice");
|
assert_eq!(entry.meta.name, "alice");
|
||||||
|
|
||||||
assert!(find_entry(dir.path(), "nonexistent").is_err());
|
assert!(find_entry(dir.path(), "nonexistent").is_err());
|
||||||
|
|
@ -352,16 +337,16 @@ Hello!"#;
|
||||||
std::fs::create_dir_all(&drawings_dir).unwrap();
|
std::fs::create_dir_all(&drawings_dir).unwrap();
|
||||||
std::fs::create_dir_all(&vn_dir).unwrap();
|
std::fs::create_dir_all(&vn_dir).unwrap();
|
||||||
|
|
||||||
let contents = "+++\nname = \"alice\"\ndate = \"2026-04-10\"\nstatus = \"denied\"\ndrawing = \"1744300800_abcd1234.png\"\nvoice_note = \"1744300800_abcd1234.webm\"\n+++\nhello";
|
let contents = "+++\nname = \"alice\"\ndate = \"2026-04-10\"\nstatus = \"denied\"\ndrawing = \"ab12.png\"\nvoice_note = \"ab12.webm\"\n+++\nhello";
|
||||||
std::fs::write(entries_dir.join("1744300800_abcd1234.txt"), contents).unwrap();
|
std::fs::write(entries_dir.join("ab12.txt"), contents).unwrap();
|
||||||
std::fs::write(drawings_dir.join("1744300800_abcd1234.png"), b"fake png").unwrap();
|
std::fs::write(drawings_dir.join("ab12.png"), b"fake png").unwrap();
|
||||||
std::fs::write(vn_dir.join("1744300800_abcd1234.webm"), b"fake webm").unwrap();
|
std::fs::write(vn_dir.join("ab12.webm"), b"fake webm").unwrap();
|
||||||
|
|
||||||
let name = delete_entry(dir.path(), "abcd1234").unwrap();
|
let name = delete_entry(dir.path(), "ab12").unwrap();
|
||||||
assert_eq!(name, "alice");
|
assert_eq!(name, "alice");
|
||||||
assert!(!entries_dir.join("1744300800_abcd1234.txt").exists());
|
assert!(!entries_dir.join("ab12.txt").exists());
|
||||||
assert!(!drawings_dir.join("1744300800_abcd1234.png").exists());
|
assert!(!drawings_dir.join("ab12.png").exists());
|
||||||
assert!(!vn_dir.join("1744300800_abcd1234.webm").exists());
|
assert!(!vn_dir.join("ab12.webm").exists());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -371,17 +356,4 @@ Hello!"#;
|
||||||
assert!(delete_entry(dir.path(), "nonexistent").is_err());
|
assert!(delete_entry(dir.path(), "nonexistent").is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_short_id() {
|
|
||||||
let contents = "+++\nname = \"a\"\ndate = \"2026-04-10\"\nstatus = \"pending\"\n+++\nhi";
|
|
||||||
let entry = Entry::parse("1744300800_abcd1234", contents).unwrap();
|
|
||||||
assert_eq!(entry.short_id(), "abcd1234");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_short_id_no_underscore() {
|
|
||||||
let contents = "+++\nname = \"a\"\ndate = \"2026-04-10\"\nstatus = \"pending\"\n+++\nhi";
|
|
||||||
let entry = Entry::parse("plainid", contents).unwrap();
|
|
||||||
assert_eq!(entry.short_id(), "plainid");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -257,7 +257,10 @@ fn escape_html(s: &str) -> String {
|
||||||
|
|
||||||
fn render_entries(entries: &[Entry], config: &Config) -> String {
|
fn render_entries(entries: &[Entry], config: &Config) -> String {
|
||||||
let mut html = String::new();
|
let mut html = String::new();
|
||||||
for entry in entries {
|
for (i, entry) in entries.iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
html.push_str("<hr class=\"entry-separator\">");
|
||||||
|
}
|
||||||
html.push_str(&render_entry(entry, config));
|
html.push_str(&render_entry(entry, config));
|
||||||
}
|
}
|
||||||
html
|
html
|
||||||
|
|
@ -304,7 +307,8 @@ fn render_entry(entry: &Entry, config: &Config) -> String {
|
||||||
String::new()
|
String::new()
|
||||||
};
|
};
|
||||||
format!(
|
format!(
|
||||||
"<article class=\"entry\">{header}{drawing_html}{voice_note_html}<div class=\"entry-body\">{body}</div></article>"
|
"<article class=\"entry\" id=\"{id}\">{header}{drawing_html}{voice_note_html}<div class=\"entry-body\">{body}</div></article>",
|
||||||
|
id = escape_html(&entry.id)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -397,7 +401,7 @@ mod tests {
|
||||||
assert!(html.contains("entry-header"));
|
assert!(html.contains("entry-header"));
|
||||||
assert!(html.contains("entry-name"));
|
assert!(html.contains("entry-name"));
|
||||||
assert!(html.contains("entry-body"));
|
assert!(html.contains("entry-body"));
|
||||||
assert!(html.contains("<article class=\"entry\">"));
|
assert!(html.contains("<article class=\"entry\" id=\"test\">"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ fn format_entry_list(entries: &[Entry], status_label: &str) -> String {
|
||||||
let ellipsis = if entry.body.chars().count() > 30 { "..." } else { "" };
|
let ellipsis = if entry.body.chars().count() > 30 { "..." } else { "" };
|
||||||
lines.push(format!(
|
lines.push(format!(
|
||||||
"- {} ({}) \"{}{}\"\n /view_{}",
|
"- {} ({}) \"{}{}\"\n /view_{}",
|
||||||
entry.meta.name, entry.meta.date, preview, ellipsis, entry.short_id()
|
entry.meta.name, entry.meta.date, preview, ellipsis, entry.id
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
lines.join("\n")
|
lines.join("\n")
|
||||||
|
|
@ -23,10 +23,9 @@ fn format_entry_list(entries: &[Entry], status_label: &str) -> String {
|
||||||
|
|
||||||
/// Send a notification to Telegram about a new entry.
|
/// Send a notification to Telegram about a new entry.
|
||||||
async fn notify(bot: &Bot, chat_id: ChatId, entry: &Entry) {
|
async fn notify(bot: &Bot, chat_id: ChatId, entry: &Entry) {
|
||||||
let short_id = entry.short_id();
|
|
||||||
let text = format!(
|
let text = format!(
|
||||||
"New guestbook entry:\n\nName: {}\nWebsite: {}\n\n{}\n\n/allow_{}\n/deny_{}",
|
"New guestbook entry:\n\nName: {}\nWebsite: {}\n\n{}\n\n/allow_{}\n/deny_{}",
|
||||||
entry.meta.name, entry.meta.website, entry.body, short_id, short_id
|
entry.meta.name, entry.meta.website, entry.body, entry.id, entry.id
|
||||||
);
|
);
|
||||||
if let Err(e) = bot.send_message(chat_id, &text).await {
|
if let Err(e) = bot.send_message(chat_id, &text).await {
|
||||||
tracing::error!("failed to send telegram message: {e}");
|
tracing::error!("failed to send telegram message: {e}");
|
||||||
|
|
@ -109,11 +108,10 @@ pub async fn bot_task(bot: Bot, chat_id: ChatId, data_dir: PathBuf) {
|
||||||
} else if let Some(id) = text.strip_prefix("/view_") {
|
} else if let Some(id) = text.strip_prefix("/view_") {
|
||||||
match entries::find_entry(&entries_dir, id) {
|
match entries::find_entry(&entries_dir, id) {
|
||||||
Ok(entry) => {
|
Ok(entry) => {
|
||||||
let short_id = entry.short_id();
|
|
||||||
let text = format!(
|
let text = format!(
|
||||||
"Entry ({:?}):\n\nName: {}\nWebsite: {}\nDate: {}\n\n{}\n\n/allow_{}\n/deny_{}",
|
"Entry ({:?}):\n\nName: {}\nWebsite: {}\nDate: {}\n\n{}\n\n/allow_{}\n/deny_{}",
|
||||||
entry.meta.status, entry.meta.name, entry.meta.website,
|
entry.meta.status, entry.meta.name, entry.meta.website,
|
||||||
entry.meta.date, entry.body, short_id, short_id
|
entry.meta.date, entry.body, entry.id, entry.id
|
||||||
);
|
);
|
||||||
bot.send_message(msg.chat.id, &text).await?;
|
bot.send_message(msg.chat.id, &text).await?;
|
||||||
|
|
||||||
|
|
|
||||||
25
src/web.rs
25
src/web.rs
|
|
@ -11,7 +11,7 @@ use axum::{
|
||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use uuid::Uuid;
|
use rand::Rng;
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::entries::{self, Entry, EntryMeta, Status};
|
use crate::entries::{self, Entry, EntryMeta, Status};
|
||||||
|
|
@ -49,6 +49,17 @@ pub fn router(state: Arc<AppState>) -> Router {
|
||||||
.with_state(state)
|
.with_state(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_id(entries_dir: &std::path::Path) -> String {
|
||||||
|
const CHARS: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyz";
|
||||||
|
let mut rng = rand::rng();
|
||||||
|
loop {
|
||||||
|
let id: String = (0..4).map(|_| CHARS[rng.random_range(0..36)] as char).collect();
|
||||||
|
if !entries_dir.join(format!("{id}.txt")).exists() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn security_headers(req: axum::extract::Request, next: Next) -> Response {
|
async fn security_headers(req: axum::extract::Request, next: Next) -> Response {
|
||||||
let is_static = req.uri().path().starts_with("/drawings/")
|
let is_static = req.uri().path().starts_with("/drawings/")
|
||||||
|| req.uri().path().starts_with("/voice_notes/");
|
|| req.uri().path().starts_with("/voice_notes/");
|
||||||
|
|
@ -260,15 +271,13 @@ async fn submit(
|
||||||
};
|
};
|
||||||
|
|
||||||
let now = chrono::Utc::now();
|
let now = chrono::Utc::now();
|
||||||
let epoch = now.timestamp();
|
|
||||||
let short_id = &Uuid::new_v4().to_string()[..8];
|
|
||||||
let prefix = format!("{epoch}_{short_id}");
|
|
||||||
let date = now.format("%Y-%m-%dT%H:%M:%S").to_string();
|
let date = now.format("%Y-%m-%dT%H:%M:%S").to_string();
|
||||||
let filename = format!("{prefix}.txt");
|
let id = generate_id(&state.config.data_dir.join("entries"));
|
||||||
|
let filename = format!("{id}.txt");
|
||||||
|
|
||||||
// Save drawing with the same prefix as the entry
|
// Save drawing with the same ID as the entry
|
||||||
let drawing_filename = if let Some(ref bytes) = drawing_bytes {
|
let drawing_filename = if let Some(ref bytes) = drawing_bytes {
|
||||||
let drawing_name = format!("{prefix}.png");
|
let drawing_name = format!("{id}.png");
|
||||||
let drawings_dir = state.config.data_dir.join("drawings");
|
let drawings_dir = state.config.data_dir.join("drawings");
|
||||||
if let Err(e) = std::fs::create_dir_all(&drawings_dir) {
|
if let Err(e) = std::fs::create_dir_all(&drawings_dir) {
|
||||||
tracing::error!("failed to create drawings directory: {e}");
|
tracing::error!("failed to create drawings directory: {e}");
|
||||||
|
|
@ -284,7 +293,7 @@ async fn submit(
|
||||||
};
|
};
|
||||||
|
|
||||||
let voice_note_filename = if let Some(ref bytes) = voice_note_bytes {
|
let voice_note_filename = if let Some(ref bytes) = voice_note_bytes {
|
||||||
let vn_name = format!("{prefix}.webm");
|
let vn_name = format!("{id}.webm");
|
||||||
let vn_dir = state.config.data_dir.join("voice_notes");
|
let vn_dir = state.config.data_dir.join("voice_notes");
|
||||||
if let Err(e) = std::fs::create_dir_all(&vn_dir) {
|
if let Err(e) = std::fs::create_dir_all(&vn_dir) {
|
||||||
tracing::error!("failed to create voice notes directory: {e}");
|
tracing::error!("failed to create voice notes directory: {e}");
|
||||||
|
|
|
||||||
|
|
@ -108,11 +108,6 @@ audio {
|
||||||
/* Entries */
|
/* Entries */
|
||||||
.entry {
|
.entry {
|
||||||
margin: 0.5em 0;
|
margin: 0.5em 0;
|
||||||
padding-bottom: 0.5em;
|
|
||||||
border-bottom: 1px solid #ccc;
|
|
||||||
}
|
|
||||||
.entry:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
}
|
||||||
.entry-header {
|
.entry-header {
|
||||||
margin-bottom: 0.2em;
|
margin-bottom: 0.2em;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue