diff options
| author | metamuffin <metamuffin@disroot.org> | 2026-01-23 17:41:45 +0100 |
|---|---|---|
| committer | metamuffin <metamuffin@disroot.org> | 2026-01-23 17:41:45 +0100 |
| commit | 774f64c0789529884dd7a5232f190e347ad29532 (patch) | |
| tree | 6eb85388837c993a054fba5ca59fdd329f5b5840 /server | |
| parent | 3671a4e07565c86f8071fb2309f463aeaf684ba3 (diff) | |
| download | jellything-774f64c0789529884dd7a5232f190e347ad29532.tar jellything-774f64c0789529884dd7a5232f190e347ad29532.tar.bz2 jellything-774f64c0789529884dd7a5232f190e347ad29532.tar.zst | |
move locale code to own crate
Diffstat (limited to 'server')
| -rw-r--r-- | server/src/api.rs | 50 | ||||
| -rw-r--r-- | server/src/logger.rs | 143 | ||||
| -rw-r--r-- | server/src/main.rs | 6 | ||||
| -rw-r--r-- | server/src/routes.rs | 3 |
4 files changed, 171 insertions, 31 deletions
diff --git a/server/src/api.rs b/server/src/api.rs index 00a8f1d..fe68b1a 100644 --- a/server/src/api.rs +++ b/server/src/api.rs @@ -4,13 +4,9 @@ Copyright (C) 2026 metamuffin <metamuffin.org> */ use super::ui::error::MyResult; -use crate::helper::{accept::AcceptJson, language::AcceptLanguage, A}; -use jellycommon::{user::CreateSessionParams, NodeID}; -use jellylogic::{login::login_logic, node::get_nodes_modified_since, session::Session}; -use jellyui::locale::get_translation_table; -use rocket::{get, post, response::Redirect, serde::json::Json, Either}; -use serde_json::{json, Value}; -use std::collections::HashMap; +use crate::helper::A; +use rocket::{get, post, response::Redirect, serde::json::Json}; +use serde_json::{Value, json}; #[get("/api")] pub fn r_api_root() -> Redirect { @@ -22,26 +18,26 @@ pub fn r_version() -> &'static str { env!("CARGO_PKG_VERSION") } -#[get("/translations")] -pub fn r_translations( - lang: AcceptLanguage, - aj: AcceptJson, -) -> Either<Json<&'static HashMap<&'static str, &'static str>>, String> { - let AcceptLanguage(lang) = lang; - let table = get_translation_table(&lang); - if *aj { - Either::Left(Json(table)) - } else { - let mut s = String::new(); - for (k, v) in table { - s += k; - s += "="; - s += v; - s += "\n"; - } - Either::Right(s) - } -} +// #[get("/translations")] +// pub fn r_translations( +// lang: AcceptLanguage, +// aj: AcceptJson, +// ) -> Either<Json<&'static HashMap<&'static str, &'static str>>, String> { +// let AcceptLanguage(lang) = lang; +// let table = get_translation_table(&lang); +// if *aj { +// Either::Left(Json(table)) +// } else { +// let mut s = String::new(); +// for (k, v) in table { +// s += k; +// s += "="; +// s += v; +// s += "\n"; +// } +// Either::Right(s) +// } +// } #[post("/api/create_session", data = "<data>")] pub fn r_api_account_login(data: Json<CreateSessionParams>) -> MyResult<Value> { diff --git a/server/src/logger.rs b/server/src/logger.rs new file mode 100644 index 0000000..9195b99 --- /dev/null +++ b/server/src/logger.rs @@ -0,0 +1,143 @@ +/* + This file is part of jellything (https://codeberg.org/metamuffin/jellything) + which is licensed under the GNU Affero General Public License (version 3); see /COPYING. + Copyright (C) 2026 metamuffin <metamuffin.org> +*/ + +use chrono::{DateTime, Utc}; +use log::Level; +use serde::{Deserialize, Serialize}; +use std::{ + collections::VecDeque, + sync::{Arc, LazyLock, RwLock}, +}; +use tokio::sync::broadcast; + +const MAX_LOG_LEN: usize = 4096; + +static LOGGER: LazyLock<Log> = LazyLock::new(Log::default); + +pub fn setup_logger() { + log::set_logger(&*LOGGER).unwrap(); + log::set_max_level(log::LevelFilter::Debug); +} + +pub struct LogLine { + pub time: DateTime<Utc>, + pub module: Option<&'static str>, + pub level: LogLevel, + pub message: String, +} + +#[derive(Serialize, Deserialize, Clone, Copy, PartialEq)] +enum LogLevel { + Trace, + Debug, + Info, + Warn, + Error, +} + +type LogBuffer = VecDeque<Arc<LogLine>>; + +pub struct Log { + inner: env_logger::Logger, + stream: ( + broadcast::Sender<Arc<LogLine>>, + broadcast::Sender<Arc<LogLine>>, + ), + log: RwLock<(LogBuffer, LogBuffer)>, +} + +pub fn get_log_buffer(warn: bool) -> VecDeque<Arc<LogLine>> { + if warn { + LOGGER.log.read().unwrap().1.clone() + } else { + LOGGER.log.read().unwrap().0.clone() + } +} +pub fn get_log_stream(warn: bool) -> broadcast::Receiver<Arc<LogLine>> { + if warn { + LOGGER.stream.1.subscribe() + } else { + LOGGER.stream.0.subscribe() + } +} + +impl Default for Log { + fn default() -> Self { + Self { + inner: env_logger::builder() + .filter_level(log::LevelFilter::Warn) + .parse_env("LOG") + .build(), + stream: ( + tokio::sync::broadcast::channel(1024).0, + tokio::sync::broadcast::channel(1024).0, + ), + log: Default::default(), + } + } +} +impl Log { + fn should_log(&self, metadata: &log::Metadata) -> bool { + let level = metadata.level(); + level + <= match metadata.target() { + x if x.starts_with("jelly") => Level::Debug, + x if x.starts_with("rocket::") => Level::Info, + _ => Level::Warn, + } + } + fn do_log(&self, record: &log::Record) { + let time = Utc::now(); + let line = Arc::new(LogLine { + time, + module: record.module_path_static(), + level: match record.level() { + Level::Error => LogLevel::Error, + Level::Warn => LogLevel::Warn, + Level::Info => LogLevel::Info, + Level::Debug => LogLevel::Debug, + Level::Trace => LogLevel::Trace, + }, + message: record.args().to_string(), + }); + let mut w = self.log.write().unwrap(); + w.0.push_back(line.clone()); + let _ = self.stream.0.send(line.clone()); + while w.0.len() > MAX_LOG_LEN { + w.0.pop_front(); + } + if record.level() <= Level::Warn { + let _ = self.stream.1.send(line.clone()); + w.1.push_back(line); + while w.1.len() > MAX_LOG_LEN { + w.1.pop_front(); + } + } + } +} + +impl log::Log for Log { + fn enabled(&self, metadata: &log::Metadata) -> bool { + self.inner.enabled(metadata) || self.should_log(metadata) + } + fn log(&self, record: &log::Record) { + match (record.module_path_static(), record.line()) { + // TODO is there a better way to ignore those? + (Some("rocket::rocket"), Some(670)) => return, + (Some("rocket::server"), Some(401)) => return, + _ => {} + } + if self.inner.enabled(record.metadata()) { + self.inner.log(record); + } + if self.should_log(record.metadata()) { + self.do_log(record) + } + } + fn flush(&self) { + self.inner.flush(); + } +} diff --git a/server/src/main.rs b/server/src/main.rs index 8b0c128..e17083b 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -7,6 +7,7 @@ #![allow(clippy::needless_borrows_for_generic_args)] #![recursion_limit = "4096"] +use crate::logger::setup_logger; use config::load_config; use log::{error, info, warn}; use routes::build_rocket; @@ -18,6 +19,7 @@ pub mod api; pub mod compat; pub mod config; pub mod helper; +pub mod logger; pub mod logic; pub mod routes; pub mod ui; @@ -41,7 +43,7 @@ static CONF: LazyLock<Config> = LazyLock::new(|| { #[rocket::main] async fn main() { - enable_logging(); + setup_logger(); info!("loading config..."); if let Err(e) = load_config().await { error!("error {e:?}"); @@ -49,7 +51,7 @@ async fn main() { } #[cfg(feature = "bypass-auth")] - log::warn!("authentification bypass enabled"); + logger::warn!("authentification bypass enabled"); if let Err(e) = create_admin_account() { error!("failed to create admin account: {e:?}"); diff --git a/server/src/routes.rs b/server/src/routes.rs index c169d5e..2d3e790 100644 --- a/server/src/routes.rs +++ b/server/src/routes.rs @@ -29,7 +29,7 @@ use crate::ui::{ style::{r_assets_font, r_assets_js, r_assets_js_map, r_assets_style}, }; use crate::{ - api::{r_api_account_login, r_api_root, r_nodes_modified_since, r_translations, r_version}, + api::{r_api_account_login, r_api_root, r_nodes_modified_since, r_version}, compat::{ jellyfin::{ r_jellyfin_artists, r_jellyfin_branding_configuration, r_jellyfin_branding_css, @@ -160,7 +160,6 @@ pub fn build_rocket() -> Rocket<Build> { r_nodes_modified_since, r_api_root, r_version, - r_translations, // Compat r_jellyfin_artists, r_jellyfin_branding_configuration, |