diff options
author | metamuffin <metamuffin@disroot.org> | 2025-04-30 10:47:54 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2025-04-30 10:47:54 +0200 |
commit | a2ef3f6ec4c830611fde1a2e935588ccbbc61c03 (patch) | |
tree | ddcc1cb501e6c7237edd491aa7136d02150d03d3 /server | |
parent | 212a0f23bc894faf88e159560c113f504349cc05 (diff) | |
download | jellything-a2ef3f6ec4c830611fde1a2e935588ccbbc61c03.tar jellything-a2ef3f6ec4c830611fde1a2e935588ccbbc61c03.tar.bz2 jellything-a2ef3f6ec4c830611fde1a2e935588ccbbc61c03.tar.zst |
config works
Diffstat (limited to 'server')
-rw-r--r-- | server/Cargo.toml | 24 | ||||
-rw-r--r-- | server/src/compat/jellyfin/mod.rs | 10 | ||||
-rw-r--r-- | server/src/config.rs | 43 | ||||
-rw-r--r-- | server/src/logic/stream.rs | 3 | ||||
-rw-r--r-- | server/src/main.rs | 48 | ||||
-rw-r--r-- | server/src/routes.rs | 13 | ||||
-rw-r--r-- | server/src/ui/admin/mod.rs | 23 | ||||
-rw-r--r-- | server/src/ui/assets.rs | 59 | ||||
-rw-r--r-- | server/src/ui/error.rs | 2 | ||||
-rw-r--r-- | server/src/ui/mod.rs | 3 | ||||
-rw-r--r-- | server/src/ui/player.rs | 3 |
11 files changed, 132 insertions, 99 deletions
diff --git a/server/Cargo.toml b/server/Cargo.toml index 57d0c29..be8abdb 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -13,25 +13,23 @@ jellycache = { path = "../cache" } jellyui = { path = "../ui" } jellylogic = { path = "../logic" } -serde = { version = "1.0.217", features = ["derive", "rc"] } -bincode = { version = "2.0.0-rc.3", features = ["serde", "derive"] } -serde_json = "1.0.138" - -log = { workspace = true } anyhow = { workspace = true } -env_logger = "0.11.6" -rand = "0.9.0" +async-recursion = "1.1.1" base64 = "0.22.1" -chrono = { version = "0.4.39", features = ["serde"] } +bincode = { version = "2.0.0-rc.3", features = ["serde", "derive"] } chashmap = "2.2.2" - -async-recursion = "1.1.1" +chrono = { version = "0.4.39", features = ["serde"] } +env_logger = "0.11.6" futures = "0.3.31" -tokio = { workspace = true } -tokio-util = { version = "0.7.13", features = ["io", "io-util"] } - +log = { workspace = true } +rand = "0.9.0" rocket = { workspace = true, features = ["secrets", "json"] } rocket_ws = { workspace = true } +serde = { version = "1.0.217", features = ["derive", "rc"] } +serde_json = "1.0.138" +serde_yml = "0.0.12" +tokio = { workspace = true } +tokio-util = { version = "0.7.13", features = ["io", "io-util"] } [build-dependencies] glob = "0.3.2" diff --git a/server/src/compat/jellyfin/mod.rs b/server/src/compat/jellyfin/mod.rs index f999060..1c602ca 100644 --- a/server/src/compat/jellyfin/mod.rs +++ b/server/src/compat/jellyfin/mod.rs @@ -7,7 +7,7 @@ pub mod models; use crate::{helper::A, ui::error::MyResult}; use anyhow::{anyhow, Context}; -use jellybase::{database::Database, CONF}; +use jellybase::database::Database; use jellycommon::{ api::{FilterProperty, NodeFilterSort, SortOrder, SortProperty}, routes::{u_asset, u_node_slug_backdrop, u_node_slug_poster}, @@ -19,7 +19,7 @@ use jellylogic::{ filter_sort::filter_and_sort_nodes, login::login_logic, node::DatabaseNodeUserDataExt, session::Session, }; -use jellyui::node_page::aspect_class; +use jellyui::{get_brand, get_slogan, node_page::aspect_class}; use models::*; use rocket::{ get, @@ -47,7 +47,7 @@ pub fn r_jellyfin_system_info_public_case() -> Json<Value> { pub fn r_jellyfin_system_info_public() -> Json<Value> { Json(json!({ "LocalAddress": LOCAL_ADDRESS, - "ServerName": CONF.brand.clone(), + "ServerName": get_brand(), "Version": VERSION, "ProductName": "Jellything", "OperatingSystem": "", @@ -59,7 +59,7 @@ pub fn r_jellyfin_system_info_public() -> Json<Value> { #[get("/Branding/Configuration")] pub fn r_jellyfin_branding_configuration() -> Json<Value> { Json(json!({ - "LoginDisclaimer": format!("{} - {}", CONF.brand, CONF.slogan), + "LoginDisclaimer": format!("{} - {}", get_brand(), get_slogan()), "CustomCss": "", "SplashscreenEnabled": false, })) @@ -122,7 +122,7 @@ pub fn r_jellyfin_system_info(_session: A<Session>) -> Json<Value> { "EncoderLocation": "System", "SystemArchitecture": "X64", "LocalAddress": LOCAL_ADDRESS, - "ServerName": CONF.brand, + "ServerName": get_brand(), "Version": VERSION, "OperatingSystem": "", "Id": SERVER_ID diff --git a/server/src/config.rs b/server/src/config.rs new file mode 100644 index 0000000..27074b4 --- /dev/null +++ b/server/src/config.rs @@ -0,0 +1,43 @@ +/* + 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) 2025 metamuffin <metamuffin.org> +*/ + +use anyhow::{anyhow, Context, Result}; +use serde::Deserialize; +use std::env::{args, var}; +use tokio::fs::read_to_string; + +#[derive(Debug, Deserialize)] +struct Config { + transcoder: jellytranscoder::Config, + ui: jellyui::Config, + stream: jellystream::Config, + cache: jellycache::Config, + server: crate::Config, + base: jellybase::Config, + logic: jellylogic::Config, +} + +pub async fn load_config() -> Result<()> { + let path = args() + .nth(1) + .or_else(|| var("JELLYTHING_CONFIG").ok()) + .ok_or(anyhow!( + "No config supplied. Use first argument or JELLYTHING_CONFIG environment variable." + ))?; + + let config_raw = read_to_string(path).await.context("reading main config")?; + let config: Config = serde_yml::from_str(&config_raw).context("parsing main config")?; + + *jellystream::CONF_PRELOAD.lock().unwrap() = Some(config.stream); + *jellytranscoder::CONF_PRELOAD.lock().unwrap() = Some(config.transcoder); + *jellycache::CONF_PRELOAD.lock().unwrap() = Some(config.cache); + *jellylogic::CONF_PRELOAD.lock().unwrap() = Some(config.logic); + *jellybase::CONF_PRELOAD.lock().unwrap() = Some(config.base); + *crate::CONF_PRELOAD.lock().unwrap() = Some(config.server); + *jellyui::CONF_PRELOAD.lock().unwrap() = Some(config.ui); + + Ok(()) +} diff --git a/server/src/logic/stream.rs b/server/src/logic/stream.rs index 9d4db6d..89589c7 100644 --- a/server/src/logic/stream.rs +++ b/server/src/logic/stream.rs @@ -5,7 +5,7 @@ */ use crate::{database::Database, helper::A, ui::error::MyError}; use anyhow::{anyhow, Result}; -use jellybase::{assetfed::AssetInner, federation::Federation}; +use jellybase::assetfed::AssetInner; use jellycommon::{stream::StreamSpec, TrackSource}; use jellylogic::session::Session; use jellystream::SMediaInfo; @@ -43,7 +43,6 @@ pub async fn r_stream_head( #[get("/n/<id>/stream?<spec..>")] pub async fn r_stream( _session: A<Session>, - _federation: &State<Federation>, db: &State<Database>, id: &str, range: Option<RequestRange>, diff --git a/server/src/main.rs b/server/src/main.rs index 94a53c7..ea75208 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -8,45 +8,65 @@ #![recursion_limit = "4096"] use anyhow::Context; +use config::load_config; use database::Database; -use jellybase::{federation::Federation, CONF, SECRETS}; -use jellylogic::{admin::log::enable_logging, login::hash_password}; +use jellylogic::{admin::log::enable_logging, login::create_admin_account}; use log::{error, info, warn}; use routes::build_rocket; -use tokio::fs::create_dir_all; +use serde::{Deserialize, Serialize}; +use std::sync::Mutex; +use std::{path::PathBuf, process::exit, sync::LazyLock}; pub use jellybase::database; pub mod api; pub mod compat; +pub mod config; pub mod helper; pub mod locale; pub mod logic; pub mod routes; pub mod ui; +#[rustfmt::skip] +#[derive(Debug, Deserialize, Serialize, Default)] +pub struct Config { + database_path: PathBuf, + asset_path: PathBuf, + cookie_key: Option<String>, + tls:bool, + hostname: String, +} + +pub static CONF_PRELOAD: Mutex<Option<Config>> = Mutex::new(None); +static CONF: LazyLock<Config> = LazyLock::new(|| { + CONF_PRELOAD + .lock() + .unwrap() + .take() + .expect("cache config not preloaded. logic error") +}); + #[rocket::main] async fn main() { enable_logging(); + info!("loading config..."); + if let Err(e) = load_config().await { + error!("error {e:?}"); + exit(1); + } + #[cfg(feature = "bypass-auth")] log::warn!("authentification bypass enabled"); - create_dir_all(&CONF.cache_path).await.unwrap(); let database = Database::open(&CONF.database_path) .context("opening database") .unwrap(); - let federation = Federation::initialize(); - if let Some(username) = &CONF.admin_username - && let Some(password) = &SECRETS.admin_password - { - database - .create_admin_user(username, hash_password(username, password)) - .unwrap(); - } else { - info!("admin account disabled") + if let Err(e) = create_admin_account(&database) { + error!("failed to create admin account: {e:?}"); } - let r = build_rocket(database, federation).launch().await; + let r = build_rocket(database).launch().await; match r { Ok(_) => warn!("server shutdown"), Err(e) => error!("server exited: {e}"), diff --git a/server/src/routes.rs b/server/src/routes.rs index ef7b067..da3a389 100644 --- a/server/src/routes.rs +++ b/server/src/routes.rs @@ -13,8 +13,8 @@ use crate::ui::{ }, admin::{ log::{r_admin_log, r_admin_log_stream}, - r_admin_dashboard, r_admin_delete_cache, r_admin_import, r_admin_invite, - r_admin_remove_invite, r_admin_transcode_posters, r_admin_update_search, + r_admin_dashboard, r_admin_import, r_admin_invite, r_admin_remove_invite, + r_admin_transcode_posters, r_admin_update_search, user::{r_admin_remove_user, r_admin_user, r_admin_user_permission, r_admin_users}, }, assets::{r_asset, r_item_backdrop, r_item_poster, r_node_thumbnail, r_person_asset}, @@ -28,6 +28,7 @@ use crate::ui::{ stats::r_stats, style::{r_assets_font, r_assets_js, r_assets_js_map, r_assets_style}, }; +use crate::CONF; use crate::{ api::{ r_api_account_login, r_api_asset_token_raw, r_api_nodes_modified_since, r_api_root, @@ -61,7 +62,6 @@ use crate::{ }, }; use base64::Engine; -use jellybase::{federation::Federation, CONF, SECRETS}; use log::warn; use rand::random; use rocket::{ @@ -76,7 +76,7 @@ macro_rules! uri { }; } -pub fn build_rocket(database: Database, federation: Federation) -> Rocket<Build> { +pub fn build_rocket(database: Database) -> Rocket<Build> { rocket::build() .configure(Config { address: std::env::var("BIND_ADDR") @@ -86,8 +86,7 @@ pub fn build_rocket(database: Database, federation: Federation) -> Rocket<Build> .map(|e| e.parse().unwrap()) .unwrap_or(8000), secret_key: SecretKey::derive_from( - SECRETS - .cookie_key + CONF.cookie_key .clone() .unwrap_or_else(|| { warn!("cookie_key not configured, generating a random one."); @@ -99,7 +98,6 @@ pub fn build_rocket(database: Database, federation: Federation) -> Rocket<Build> ..Default::default() }) .manage(database) - .manage(federation) .manage(PlayersyncChannels::default()) .attach(AdHoc::on_response("set server header", |_req, res| { res.set_header(Header::new("server", "jellything")); @@ -129,7 +127,6 @@ pub fn build_rocket(database: Database, federation: Federation) -> Rocket<Build> r_account_settings_post, r_account_settings, r_admin_dashboard, - r_admin_delete_cache, r_admin_import, r_admin_invite, r_admin_log_stream, diff --git a/server/src/ui/admin/mod.rs b/server/src/ui/admin/mod.rs index 3a9e4e2..62c5940 100644 --- a/server/src/ui/admin/mod.rs +++ b/server/src/ui/admin/mod.rs @@ -12,7 +12,7 @@ use super::{ }; use crate::{database::Database, helper::A, locale::AcceptLanguage}; use anyhow::{anyhow, Context}; -use jellybase::{assetfed::AssetInner, federation::Federation, CONF}; +use jellybase::assetfed::AssetInner; use jellycommon::routes::u_admin_dashboard; use jellyimport::{import_wrap, is_importing, IMPORT_ERRORS}; use jellylogic::session::AdminSession; @@ -103,7 +103,6 @@ pub async fn r_admin_remove_invite( pub async fn r_admin_import( session: A<AdminSession>, database: &State<Database>, - _federation: &State<Federation>, incremental: bool, ) -> MyResult<Redirect> { drop(session); @@ -139,26 +138,6 @@ pub async fn r_admin_update_search( Ok(Redirect::temporary(u_admin_dashboard())) } -#[post("/admin/delete_cache")] -pub async fn r_admin_delete_cache( - session: A<AdminSession>, - database: &State<Database>, -) -> MyResult<Redirect> { - drop(session); - let t = Instant::now(); - let r = tokio::fs::remove_dir_all(&CONF.cache_path).await; - tokio::fs::create_dir(&CONF.cache_path).await?; - // admin_dashboard( - // database, - // Some( - // r.map_err(|e| e.into()) - // .map(|_| format!("Cache deleted; took {:?}", t.elapsed())), - // ), - // ) - // .await - Ok(Redirect::temporary(u_admin_dashboard())) -} - static SEM_TRANSCODING: Semaphore = Semaphore::const_new(1); fn is_transcoding() -> bool { SEM_TRANSCODING.available_permits() == 0 diff --git a/server/src/ui/assets.rs b/server/src/ui/assets.rs index 3b9319e..596661a 100644 --- a/server/src/ui/assets.rs +++ b/server/src/ui/assets.rs @@ -4,11 +4,9 @@ Copyright (C) 2025 metamuffin <metamuffin.org> */ use super::error::MyResult; -use crate::helper::{cache::CacheControlFile, A}; +use crate::{helper::{cache::CacheControlFile, A}, CONF}; use anyhow::{anyhow, bail, Context}; -use base64::Engine; -use jellybase::{assetfed::AssetInner, database::Database, federation::Federation, CONF}; -use jellycache::async_cache_file; +use jellybase::{assetfed::AssetInner, database::Database}; use jellycommon::{LocalTrack, NodeID, PeopleGroup, SourceTrackKind, TrackSource}; use jellylogic::session::Session; use log::info; @@ -21,22 +19,23 @@ pub const AVIF_SPEED: u8 = 5; #[get("/asset/<token>?<width>")] pub async fn r_asset( _session: A<Session>, - fed: &State<Federation>, token: &str, width: Option<usize>, ) -> MyResult<(ContentType, CacheControlFile)> { let width = width.unwrap_or(2048); let asset = AssetInner::deser(token)?; - let path = if let AssetInner::Federated { host, asset } = asset { - let session = fed.get_session(&host).await?; + let path = + // if let AssetInner::Federated { host, asset } = asset { + // let session = fed.get_session(&host).await?; - let asset = base64::engine::general_purpose::URL_SAFE.encode(asset); - async_cache_file("fed-asset", &asset, |out| async { - session.asset(out, &asset, width).await - }) - .await? - } else { + // let asset = base64::engine::general_purpose::URL_SAFE.encode(asset); + // async_cache_file("fed-asset", &asset, |out| async { + // session.asset(out, &asset, width).await + // }) + // .await? + // } else + { let source = resolve_asset(asset).await.context("resolving asset")?; // fit the resolution into a finite set so the maximum cache is finite too. @@ -56,7 +55,7 @@ pub async fn resolve_asset(asset: AssetInner) -> anyhow::Result<PathBuf> { match asset { AssetInner::Cache(c) => Ok(c.abs()), AssetInner::Assets(c) => Ok(CONF.asset_path.join(c)), - AssetInner::Media(c) => Ok(CONF.media_path.join(c)), + AssetInner::Media(c) => Ok(c), _ => bail!("wrong asset type"), } } @@ -138,7 +137,6 @@ pub async fn r_person_asset( pub async fn r_node_thumbnail( _session: A<Session>, db: &State<Database>, - fed: &State<Federation>, id: A<NodeID>, t: f64, width: Option<usize>, @@ -146,7 +144,7 @@ pub async fn r_node_thumbnail( let node = db.get_node(id.0)?.ok_or(anyhow!("node does not exist"))?; let media = node.media.as_ref().ok_or(anyhow!("no media"))?; - let (thumb_track_index, thumb_track) = media + let (thumb_track_index, _thumb_track) = media .tracks .iter() .enumerate() @@ -171,23 +169,24 @@ pub async fn r_node_thumbnail( return Err(anyhow!("track set to wrong asset type").into()); }; // the track selected might be different from thumb_track - jellytranscoder::thumbnail::create_thumbnail(&CONF.media_path.join(path), t).await? + jellytranscoder::thumbnail::create_thumbnail(&path, t).await? } TrackSource::Remote(_) => { - // TODO in the new system this is preferrably a property of node ext for regular fed - let session = fed - .get_session( - thumb_track - .federated - .last() - .ok_or(anyhow!("federation broken"))?, - ) - .await?; + // // TODO in the new system this is preferrably a property of node ext for regular fed + // let session = fed + // .get_session( + // thumb_track + // .federated + // .last() + // .ok_or(anyhow!("federation broken"))?, + // ) + // .await?; - async_cache_file("fed-thumb", (id.0, t as i64), |out| { - session.node_thumbnail(out, id.0.into(), 2048, t) - }) - .await? + // async_cache_file("fed-thumb", (id.0, t as i64), |out| { + // session.node_thumbnail(out, id.0.into(), 2048, t) + // }) + // .await? + todo!() } }; diff --git a/server/src/ui/error.rs b/server/src/ui/error.rs index 0ea1a8d..05249af 100644 --- a/server/src/ui/error.rs +++ b/server/src/ui/error.rs @@ -3,7 +3,7 @@ which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2025 metamuffin <metamuffin.org> */ -use jellybase::CONF; +use crate::CONF; use log::info; use rocket::{ catch, diff --git a/server/src/ui/mod.rs b/server/src/ui/mod.rs index 17a8b6f..041dadc 100644 --- a/server/src/ui/mod.rs +++ b/server/src/ui/mod.rs @@ -3,10 +3,9 @@ which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2025 metamuffin <metamuffin.org> */ -use crate::{helper::A, locale::AcceptLanguage}; +use crate::{helper::A, locale::AcceptLanguage, CONF}; use error::MyResult; use home::rocket_uri_macro_r_home; -use jellybase::CONF; use jellylogic::session::Session; use jellyui::{render_page, scaffold::RenderInfo, CustomPage}; use rocket::{ diff --git a/server/src/ui/player.rs b/server/src/ui/player.rs index 94ca6ac..300e9d2 100644 --- a/server/src/ui/player.rs +++ b/server/src/ui/player.rs @@ -4,8 +4,7 @@ Copyright (C) 2025 metamuffin <metamuffin.org> */ use super::error::MyResult; -use crate::{database::Database, helper::A, locale::AcceptLanguage}; -use jellybase::CONF; +use crate::{database::Database, helper::A, locale::AcceptLanguage, CONF}; use jellycommon::{ api::NodeFilterSort, stream::{StreamContainer, StreamSpec}, |