aboutsummaryrefslogtreecommitdiff
path: root/server/src
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2026-01-23 17:41:45 +0100
committermetamuffin <metamuffin@disroot.org>2026-01-23 17:41:45 +0100
commit774f64c0789529884dd7a5232f190e347ad29532 (patch)
tree6eb85388837c993a054fba5ca59fdd329f5b5840 /server/src
parent3671a4e07565c86f8071fb2309f463aeaf684ba3 (diff)
downloadjellything-774f64c0789529884dd7a5232f190e347ad29532.tar
jellything-774f64c0789529884dd7a5232f190e347ad29532.tar.bz2
jellything-774f64c0789529884dd7a5232f190e347ad29532.tar.zst
move locale code to own crate
Diffstat (limited to 'server/src')
-rw-r--r--server/src/api.rs50
-rw-r--r--server/src/logger.rs143
-rw-r--r--server/src/main.rs6
-rw-r--r--server/src/routes.rs3
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,