diff --git a/Cargo.lock b/Cargo.lock index df8e300..81ed917 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -838,7 +838,7 @@ dependencies = [ [[package]] name = "lonk" -version = "1.0.1" +version = "2.0.1" dependencies = [ "argh", "async-object-pool", diff --git a/Cargo.toml b/Cargo.toml index 5b1f7fa..f511ccc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lonk" -version = "1.0.1" +version = "2.0.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/data/config.json b/data/config.json index ae640d4..8bd4a3b 100644 --- a/data/config.json +++ b/data/config.json @@ -1,5 +1,5 @@ { - "version": 1, + "version": 2, "db": { "address": "redis://redis:6379", "expire_seconds": 259200 @@ -10,7 +10,8 @@ }, "log_rules": { "error_log_file": "/data/log/error.log", - "access_log_file": "/data/log/access.log" + "access_log_file": "/data/log/access.log", + "link_log_file": "/data/log/link.log" }, "serve_rules": { "dir": { diff --git a/src/main.rs b/src/main.rs index 345f97c..0e70a0b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -142,8 +142,10 @@ mod conf { pub struct LogRules { /// Where to write error logs to. The file will be appended to. pub error_log_file: PathBuf, - /// Where to write access ogs to. The file will be appended to. + /// Where to write access logs to. The file will be appended to. pub access_log_file: PathBuf, + /// Where to write link shortening logs to. The file will be appended to. + pub link_log_file: PathBuf, } #[derive(Deserialize, Serialize, Debug, Clone)] @@ -183,6 +185,7 @@ mod conf { ServeDirNotExists(PathBuf), AccessLogDirectoryNotExists(PathBuf), ErrorLogDirectoryNotExists(PathBuf), + LinkLogDirectoryNotExists(PathBuf), } impl Config { @@ -223,24 +226,27 @@ mod conf { } // Check access and error log parent directories + fn check_exists(path: &PathBuf, to_err: impl FnOnce(PathBuf) -> E) -> Result<(), E> { + let weak_canonical = normalize_path(path); + if let Some(parent) = weak_canonical.parent() { + if !parent.exists() { + return Err(to_err(parent.to_path_buf())); + } + } + Ok(()) + } // - Access log file - let weak_canonical = normalize_path(&self.log_rules.access_log_file); - if let Some(parent) = weak_canonical.parent() { - if !parent.exists() { - return Err(ConfigParseError::AccessLogDirectoryNotExists( - parent.to_path_buf(), - )); - } - } + check_exists(&self.log_rules.access_log_file, |path| { + ConfigParseError::AccessLogDirectoryNotExists(path) + })?; // - Error log file - let weak_canonical = normalize_path(&self.log_rules.error_log_file); - if let Some(parent) = weak_canonical.parent() { - if !parent.exists() { - return Err(ConfigParseError::ErrorLogDirectoryNotExists( - parent.to_path_buf(), - )); - } - } + check_exists(&self.log_rules.error_log_file, |path| { + ConfigParseError::ErrorLogDirectoryNotExists(path) + })?; + // - Link log file + check_exists(&self.log_rules.link_log_file, |path| { + ConfigParseError::LinkLogDirectoryNotExists(path) + })?; Ok(self) } @@ -342,6 +348,9 @@ mod conf { ConfigParseError::ErrorLogDirectoryNotExists(dir) => { eprintln!("Error log file should have parent directory {}, but this directory does not exist.", dir.to_string_lossy()) } + ConfigParseError::LinkLogDirectoryNotExists(dir) => { + eprintln!("Link registration log file should have parent directory {}, but this directory does not exist.", dir.to_string_lossy()) + } } std::process::exit(1); } @@ -406,6 +415,7 @@ mod conf { Self { error_log_file: "/etc/lonk/log/error.log".into(), access_log_file: "/etc/lonk/log/access.log".into(), + link_log_file: "/etc/lonk/log/links.log".into(), } } } @@ -924,6 +934,7 @@ mod service { pub struct Logger { access_tx: sync::mpsc::UnboundedSender, error_tx: sync::mpsc::UnboundedSender, + link_tx: sync::mpsc::UnboundedSender, } impl Logger { @@ -931,6 +942,7 @@ mod service { // Create the communication channels let (access_tx, access_rx) = sync::mpsc::unbounded_channel::(); let (error_tx, error_rx) = sync::mpsc::unbounded_channel::(); + let (link_tx, link_rx) = sync::mpsc::unbounded_channel::(); // Start the logging tasks tokio::spawn(Self::logging_task( @@ -938,11 +950,13 @@ mod service { config.access_log_file.clone(), )); tokio::spawn(Self::logging_task(error_rx, config.error_log_file.clone())); + tokio::spawn(Self::logging_task(link_rx, config.link_log_file.clone())); // Done Logger { access_tx, error_tx, + link_tx, } } @@ -965,6 +979,16 @@ mod service { pub fn error(&self, msg: String) -> Result<(), ()> { self.error_tx.send(msg).map_err(|_| ()) } + + /// Log a message into the link log file. + /// + /// Returns a Result with empty type; if posting the log message + /// failed for any reason, it's unlikely to recover, and the user + /// should decide either to stop logging, ignore these errors, or + /// halt the program. + pub fn link(&self, msg: String) -> Result<(), ()> { + self.link_tx.send(msg).map_err(|_| ()) + } /// The task responsible for receiving the log messages and actually /// writing them into the corresponding files. One task is created @@ -1068,7 +1092,7 @@ async fn shorten( Ok(result) => match result { service::db::AddResult::Success(slug) => { logger - .access(format!("{} -> {}\n", slug.inner_str(), url)) + .link(format!("{} -> {}\n", slug.inner_str(), url)) .ok(); Ok(slug) } @@ -1237,8 +1261,7 @@ async fn serve() { // GET / // This should be the last thing matched, so that anything that doesn't // match another filter will try to match a file. - let homepage = warp::get() - .and(config.serve_rules.dir.to_filter()); + let homepage = warp::get().and(config.serve_rules.dir.to_filter()); let get_routes = warp::get().and(link.or(homepage)); let post_routes = warp::post().and(shorten);