diff options
| author | metamuffin <metamuffin@disroot.org> | 2026-03-14 22:00:01 +0100 |
|---|---|---|
| committer | metamuffin <metamuffin@disroot.org> | 2026-03-14 22:00:01 +0100 |
| commit | 1f3bcb47bc27768c67b05305f528e01ed491a6d3 (patch) | |
| tree | a0b7d515112e7ac755bec8e77c5aa2edbbdb7803 | |
| parent | 577227d69fbc619c4d30a262174dbf2e173c49de (diff) | |
| download | jellything-1f3bcb47bc27768c67b05305f528e01ed491a6d3.tar jellything-1f3bcb47bc27768c67b05305f528e01ed491a6d3.tar.bz2 jellything-1f3bcb47bc27768c67b05305f528e01ed491a6d3.tar.zst | |
default for every config field
| -rw-r--r-- | common/src/internal.rs | 6 | ||||
| -rw-r--r-- | import/src/lib.rs | 43 | ||||
| -rw-r--r-- | server/src/main.rs | 124 | ||||
| -rw-r--r-- | stream/src/lib.rs | 2 | ||||
| -rw-r--r-- | ui/src/lib.rs | 30 |
5 files changed, 162 insertions, 43 deletions
diff --git a/common/src/internal.rs b/common/src/internal.rs index 3f2c622..864eed4 100644 --- a/common/src/internal.rs +++ b/common/src/internal.rs @@ -10,8 +10,10 @@ use jellyobject::fields; use serde::{Deserialize, Serialize}; fields! { - IM_PATH: str = b"Ipth"; - IM_MTIME: u64 = b"Imtm"; + IMPORT_PATH: str = b"Ipth"; + IMPORT_MTIME: u64 = b"Imtm"; + + SESSION_KEY: str = b"Gskx"; } #[derive(Serialize, Deserialize, Clone)] diff --git a/import/src/lib.rs b/import/src/lib.rs index 97cd373..d138f75 100644 --- a/import/src/lib.rs +++ b/import/src/lib.rs @@ -17,7 +17,7 @@ use crate::{ use anyhow::{Context, Result, anyhow}; use jellycache::{Cache, HashKey}; use jellycommon::{ - internal::{IM_MTIME, IM_PATH}, + internal::{IMPORT_MTIME, IMPORT_PATH}, jellyobject::{self, OBB, Object, Path as TagPath, Tag}, *, }; @@ -44,11 +44,30 @@ use tokio::{runtime::Handle, sync::Semaphore, task::spawn_blocking}; pub use jellyimport_fallback_generator::generate_person_fallback; -#[derive(Debug, Deserialize, Serialize, Default, Clone)] +#[derive(Debug, Deserialize, Serialize, Clone)] +#[rustfmt::skip] pub struct Config { - media_path: PathBuf, - api: ApiSecrets, - num_threads: usize, + #[serde(default = "defaults::media_path")] media_path: PathBuf, + #[serde(default)] api: ApiSecrets, + #[serde(default = "defaults::num_threads")] num_threads: usize, +} +mod defaults { + use std::path::PathBuf; + impl Default for super::Config { + fn default() -> Self { + Self { + api: Default::default(), + media_path: media_path(), + num_threads: num_threads(), + } + } + } + pub fn num_threads() -> usize { + rayon::current_num_threads() + } + pub fn media_path() -> PathBuf { + "media".into() + } } #[derive(Serialize, Deserialize, Debug, Default, Clone)] @@ -451,7 +470,7 @@ fn compare_mtime(dba: &ImportConfig, path: &Path) -> Result<bool> { dba.db.transaction(&mut |txn| { match txn.query_single(Query { filter: Filter::Match( - TagPath(vec![IM_PATH.0]), + TagPath(vec![IMPORT_PATH.0]), path.to_string_lossy().to_string().into(), ), ..Default::default() @@ -459,7 +478,7 @@ fn compare_mtime(dba: &ImportConfig, path: &Path) -> Result<bool> { None => was_changed = true, Some(row) => { let meta = txn.get(row)?.unwrap(); - let prev_mtime = meta.get(IM_MTIME).unwrap_or_default(); + let prev_mtime = meta.get(IMPORT_MTIME).unwrap_or_default(); was_changed = mtime > prev_mtime } } @@ -474,17 +493,21 @@ fn update_mtime(dba: &ImportConfig, path: &Path) -> Result<()> { dba.db.transaction(&mut |txn| { let row = match txn.query_single(Query { filter: Filter::Match( - TagPath(vec![IM_PATH.0]), + TagPath(vec![IMPORT_PATH.0]), path.to_string_lossy().to_string().into(), ), ..Default::default() })? { Some(row) => row, - None => txn.insert(OBB::new().with(IM_PATH, &path.to_string_lossy()).finish())?, + None => txn.insert( + OBB::new() + .with(IMPORT_PATH, &path.to_string_lossy()) + .finish(), + )?, }; let mut ob = txn.get(row)?.unwrap(); - ob = ob.insert(IM_MTIME, mtime); + ob = ob.insert(IMPORT_MTIME, mtime); txn.update(row, ob)?; Ok(()) diff --git a/server/src/main.rs b/server/src/main.rs index 564f320..e2f162e 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -3,7 +3,7 @@ which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2026 metamuffin <metamuffin.org> */ -#![feature(never_type)] +#![feature(never_type, exit_status_error)] #![allow(clippy::needless_borrows_for_generic_args)] #![recursion_limit = "4096"] @@ -12,16 +12,25 @@ use crate::{ logger::setup_logger, }; use anyhow::{Result, anyhow}; +use base64::{Engine, prelude::BASE64_STANDARD}; use jellycache::Cache; use jellycommon::{ - USER_ADMIN, USER_LOGIN, USER_PASSWORD, - jellyobject::{OBB, Path}, + USER_ADMIN, USER_LOGIN, USER_PASSWORD, USER_PASSWORD_REQUIRE_CHANGE, + internal::SESSION_KEY, + jellyobject::{EMPTY, OBB, Path}, }; use jellydb::{Database, Filter, Query}; use log::{error, info}; +use rand::random; use routes::build_rocket; use serde::Deserialize; -use std::{env::args, fs::read_to_string, path::PathBuf, process::exit, sync::Arc}; +use std::{ + env::args, + fs::read_to_string, + path::PathBuf, + process::{Command, exit}, + sync::Arc, +}; pub mod auth; pub mod logger; @@ -57,33 +66,65 @@ pub struct State { } #[derive(Debug, Deserialize)] +#[rustfmt::skip] pub struct Config { pub import: jellyimport::Config, - pub ui: jellyui::Config, - pub stream: Arc<jellystream::Config>, - pub session_key: String, - pub admin_password: String, - pub asset_path: PathBuf, - pub database_path: PathBuf, - pub cache_path: PathBuf, - pub max_memory_cache_size: usize, - pub tls: bool, - pub hostname: String, + #[serde(default)] pub ui: jellyui::Config, + #[serde(default)] pub stream: Arc<jellystream::Config>, + #[serde(default = "defaults::base_path")] pub base_path: PathBuf, + #[serde(default = "defaults::asset_path")] pub asset_path: PathBuf, + #[serde(default = "defaults::database_path")] pub database_path: PathBuf, + #[serde(default = "defaults::cache_path")] pub cache_path: PathBuf, + #[serde(default = "defaults::admin_password")] pub admin_password: String, + #[serde(default = "defaults::max_memory_cache_size")] pub max_memory_cache_size: usize, + pub session_key: Option<String>, +} +mod defaults { + use std::path::PathBuf; + pub fn base_path() -> PathBuf { + ".".into() + } + pub fn asset_path() -> PathBuf { + "assets".into() + } + pub fn database_path() -> PathBuf { + "db".into() + } + pub fn cache_path() -> PathBuf { + "cachedb".into() + } + pub fn max_memory_cache_size() -> usize { + 100_000_000 + } + pub fn admin_password() -> String { + "admin".to_string() + } } pub fn create_state() -> Result<Arc<State>> { let config_path = args() .nth(1) .ok_or(anyhow!("first argument (config path) missing"))?; - let config: Config = serde_yaml_ng::from_str(&read_to_string(config_path)?)?; + let mut config: Config = serde_yaml_ng::from_str(&read_to_string(config_path)?)?; + config.cache_path = config.base_path.join(config.cache_path); + config.asset_path = config.base_path.join(config.asset_path); + config.database_path = config.base_path.join(config.database_path); + + init_asset_dir(&config.asset_path)?; let cache_storage = jellykv::rocksdb::new(&config.cache_path)?; let db_storage = jellykv::rocksdb::new(&config.database_path)?; + let session_key = config + .session_key + .clone() + .map(Ok) + .unwrap_or_else(|| session_key_from_db(&db_storage))?; + let state = Arc::new(State { cache: Cache::new(Box::new(cache_storage), config.max_memory_cache_size).into(), database: Arc::new(db_storage), - session_key: SessionKey::parse(&config.session_key)?, + session_key: SessionKey::parse(&session_key)?, config, }); @@ -101,16 +142,53 @@ fn create_admin_user(state: &State) -> Result<()> { if admin_row.is_none() { info!("Creating new admin user"); let pwhash = hash_password("admin", &state.config.admin_password); - txn.insert( - OBB::new() - .with(USER_LOGIN, "admin") - .with(USER_PASSWORD, &pwhash) - .with(USER_ADMIN, ()) - .finish(), - )?; + let mut user = OBB::new() + .with(USER_LOGIN, "admin") + .with(USER_PASSWORD, &pwhash) + .with(USER_ADMIN, ()) + .finish(); + if state.config.admin_password == "admin" { + user = user.insert(USER_PASSWORD_REQUIRE_CHANGE, ()); + } + txn.insert(user)?; } Ok(()) })?; Ok(()) } + +fn session_key_from_db(db: &dyn Database) -> Result<String> { + let mut sk = None; + db.transaction(&mut |txn| match txn.query_single(Query { + filter: Filter::Has(Path(vec![SESSION_KEY.0])), + ..Default::default() + })? { + Some(r) => { + sk = Some(txn.get(r)?.unwrap().get(SESSION_KEY).unwrap().to_string()); + Ok(()) + } + None => { + let k = BASE64_STANDARD.encode([(); 32].map(|()| random())); + txn.insert(EMPTY.insert(SESSION_KEY, &k))?; + sk = Some(k); + Ok(()) + } + })?; + Ok(sk.unwrap()) +} + +fn init_asset_dir(path: &std::path::Path) -> Result<()> { + if path.exists() { + return Ok(()); + } + info!("git-cloning assets repo to {path:?}"); + Command::new("git") + .arg("clone") + .arg("https://codeberg.org/metamuffin/jellything-assets.git") + .arg(path) + .output()? + .exit_ok()?; + info!("done"); + Ok(()) +} diff --git a/stream/src/lib.rs b/stream/src/lib.rs index 9171595..0070860 100644 --- a/stream/src/lib.rs +++ b/stream/src/lib.rs @@ -40,7 +40,7 @@ pub struct Config { #[serde(default)] pub offer_vp8: bool, #[serde(default)] pub offer_vp9: bool, #[serde(default)] pub offer_av1: bool, - pub transcoder: jellytranscoder::Config, + #[serde(default)] pub transcoder: jellytranscoder::Config, } pub struct SMediaInfo { diff --git a/ui/src/lib.rs b/ui/src/lib.rs index b4f8d68..7afdd28 100644 --- a/ui/src/lib.rs +++ b/ui/src/lib.rs @@ -7,23 +7,39 @@ pub mod components; pub(crate) mod format; mod scaffold; -use std::borrow::Cow; - pub use jellyui_client_scripts::*; pub use jellyui_client_style::*; pub use jellyui_locale::tr; +pub use scaffold::Scaffold; use jellycommon::jellyobject::Object; use markup::DynRender; -pub use scaffold::Scaffold; use serde::{Deserialize, Serialize}; +use std::borrow::Cow; #[rustfmt::skip] -#[derive(Debug, Deserialize, Serialize, Default)] +#[derive(Debug, Deserialize, Serialize)] pub struct Config { - pub brand: String, - pub slogan: String, - pub logo: bool, + #[serde(default = "defaults::brand")] pub brand: String, + #[serde(default = "defaults::slogan")] pub slogan: String, + #[serde(default)] pub logo: bool, +} +mod defaults { + impl Default for super::Config { + fn default() -> Self { + Self { + brand: brand(), + slogan: slogan(), + logo: false, + } + } + } + pub fn brand() -> String { + "Jellything".to_string() + } + pub fn slogan() -> String { + "Somebody put a nice slogan here".to_string() + } } pub trait Page { |