diff options
-rw-r--r-- | base/src/federation.rs | 18 | ||||
-rw-r--r-- | base/src/lib.rs | 13 | ||||
-rw-r--r-- | common/src/config.rs | 49 | ||||
-rw-r--r-- | import/src/lib.rs | 8 | ||||
-rw-r--r-- | server/src/main.rs | 6 | ||||
-rw-r--r-- | server/src/routes/mod.rs | 5 | ||||
-rw-r--r-- | server/src/routes/stream.rs | 9 | ||||
-rw-r--r-- | server/src/routes/ui/account/session/token.rs | 4 | ||||
-rw-r--r-- | tool/src/main.rs | 122 |
9 files changed, 130 insertions, 104 deletions
diff --git a/base/src/federation.rs b/base/src/federation.rs index 9cd04ae..0041e26 100644 --- a/base/src/federation.rs +++ b/base/src/federation.rs @@ -3,10 +3,10 @@ which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2023 metamuffin <metamuffin.org> */ -use crate::CONF; +use crate::SECRETS; use anyhow::anyhow; use jellyclient::{Instance, Session}; -use jellycommon::user::CreateSessionParams; +use jellycommon::{config::FederationAccount, user::CreateSessionParams}; use std::{collections::HashMap, sync::Arc}; use tokio::sync::RwLock; @@ -17,10 +17,12 @@ pub struct Federation { impl Federation { pub fn initialize() -> Self { - let instances = CONF - .remote_credentials + let instances = SECRETS + .federation .iter() - .map(|(k, (_, _, tls))| (k.to_owned(), Instance::new(k.to_owned(), *tls))) + .map(|(k, FederationAccount { tls, .. })| { + (k.to_owned(), Instance::new(k.to_owned(), *tls)) + }) .collect::<HashMap<_, _>>(); Self { @@ -40,8 +42,10 @@ impl Federation { if let Some(s) = w.get(host) { Ok(s.to_owned()) } else { - let (username, password, _) = CONF - .remote_credentials + let FederationAccount { + username, password, .. + } = SECRETS + .federation .get(host) .ok_or(anyhow!("no credentials of the remote server"))?; let s = Arc::new( diff --git a/base/src/lib.rs b/base/src/lib.rs index a7b15c5..0001caa 100644 --- a/base/src/lib.rs +++ b/base/src/lib.rs @@ -10,7 +10,10 @@ pub mod federation; pub mod permission; pub mod temp; -use jellycommon::{config::GlobalConfig, AssetLocation}; +use jellycommon::{ + config::{GlobalConfig, SecretsConfig}, + AssetLocation, +}; use std::{fs::File, path::PathBuf, sync::LazyLock}; pub static CONF: LazyLock<GlobalConfig> = LazyLock::new(|| { @@ -20,9 +23,13 @@ pub static CONF: LazyLock<GlobalConfig> = LazyLock::new(|| { "First argument or JELLYTHING_CONFIG must specify the configuration to use.", ) })) - .unwrap(), + .expect("config cannot be read"), ) - .unwrap() + .expect("config invalid") +}); +pub static SECRETS: LazyLock<SecretsConfig> = LazyLock::new(|| { + serde_yaml::from_reader(File::open(&CONF.secrets_path).expect("secrets file missing")) + .expect("secrets config invalid") }); pub trait AssetLocationExt { diff --git a/common/src/config.rs b/common/src/config.rs index 7328d6e..1c1439e 100644 --- a/common/src/config.rs +++ b/common/src/config.rs @@ -5,6 +5,7 @@ */ use crate::{jhls::EncodingProfile, user::PermissionSet}; +use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use std::{collections::HashMap, path::PathBuf}; @@ -20,16 +21,47 @@ pub struct GlobalConfig { #[serde(default = "default::temp_path")] pub temp_path: PathBuf, #[serde(default = "default::cache_path")] pub cache_path: PathBuf, #[serde(default = "default::media_path")] pub media_path: PathBuf, + #[serde(default = "default::secrets_path")] pub secrets_path: PathBuf, #[serde(default = "default::transcoding_profiles")] pub transcoding_profiles: Vec<EncodingProfile>, #[serde(default = "default::max_in_memory_cache_size")] pub max_in_memory_cache_size: usize, #[serde(default)] pub admin_username: Option<String>, - #[serde(default)] pub admin_password: Option<String>, - #[serde(default)] pub cookie_key: Option<String>, - #[serde(default)] pub session_key: Option<String>, #[serde(default = "default::login_expire")] pub login_expire: i64, - #[serde(default)] pub remote_credentials: HashMap<String, (String, String, bool)>, #[serde(default)] pub default_permission_set: PermissionSet, - #[serde(default)] pub tmdb_api_key: Option<String>, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct SecretsConfig { + pub federation: HashMap<String, FederationAccount>, + pub api: ApiSecrets, + #[serde(default)] + pub cookie_key: Option<String>, + #[serde(default)] + pub session_key: Option<String>, + #[serde(default)] + pub admin_password: Option<String>, +} +#[derive(Serialize, Deserialize, Debug)] +pub struct FederationAccount { + pub username: String, + pub password: String, + #[serde(default = "return_true")] + pub tls: bool, +} +#[derive(Serialize, Deserialize, Debug)] +pub struct ApiSecrets { + pub tmdb: Option<String>, + pub imdb: Option<String>, + pub omdb: Option<String>, + pub fanart_tv: Option<String>, + pub trakt: Option<TraktApiSecrets>, +} +#[derive(Serialize, Deserialize, Debug)] +pub struct TraktApiSecrets { + pub client_id: String, + pub client_secret: String, + pub expire: DateTime<Utc>, + pub access_token: String, + pub refresh_token: String, } mod default { @@ -54,6 +86,9 @@ mod default { pub fn media_path() -> PathBuf { "data/media".into() } + pub fn secrets_path() -> PathBuf { + "data/secrets.yaml".into() + } pub fn temp_path() -> PathBuf { "/tmp".into() } @@ -98,3 +133,7 @@ mod default { ] } } + +fn return_true() -> bool { + true +} diff --git a/import/src/lib.rs b/import/src/lib.rs index abaf7fd..1d8efd8 100644 --- a/import/src/lib.rs +++ b/import/src/lib.rs @@ -12,10 +12,7 @@ use anyhow::{anyhow, bail, Context, Ok}; use async_recursion::async_recursion; use futures::{executor::block_on, stream::FuturesUnordered, StreamExt}; use jellybase::{ - cache::{async_cache_file, cache_memory}, - database::{DataAcid, ReadableTable, Ser, T_NODE, T_NODE_IMPORT}, - federation::Federation, - AssetLocationExt, CONF, + cache::{async_cache_file, cache_memory}, database::{DataAcid, ReadableTable, Ser, T_NODE, T_NODE_IMPORT}, federation::Federation, AssetLocationExt, CONF, SECRETS }; use jellyclient::Session; use jellycommon::{ @@ -241,8 +238,7 @@ async fn process_source( ImportSource::Override(n) => insert_node(&id, n)?, ImportSource::Tmdb { id: tid } => { info!("tmdb lookup {id}"); - let key = CONF - .tmdb_api_key + let key = SECRETS.api.tmdb .as_ref() .ok_or(anyhow!("no tmdb api key"))?; diff --git a/server/src/main.rs b/server/src/main.rs index 6862a98..fbfbba6 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -10,9 +10,7 @@ use crate::routes::ui::{account::hash_password, admin::log::enable_logging}; use database::DataAcid; use jellybase::{ - database::{ReadableTable, Ser, T_USER}, - federation::Federation, - CONF, + database::{ReadableTable, Ser, T_USER}, federation::Federation, CONF, SECRETS }; use jellycommon::user::{PermissionSet, Theme, User}; use log::{error, info, warn}; @@ -33,7 +31,7 @@ async fn main() { let federation = Federation::initialize(); if let Some(username) = &CONF.admin_username - && let Some(password) = &CONF.admin_password + && let Some(password) = &SECRETS.admin_password { let txn = database.begin_write().unwrap(); let mut users = txn.open_table(T_USER).unwrap(); diff --git a/server/src/routes/mod.rs b/server/src/routes/mod.rs index 6bc5127..d6c1e9f 100644 --- a/server/src/routes/mod.rs +++ b/server/src/routes/mod.rs @@ -6,7 +6,7 @@ use crate::{database::DataAcid, routes::ui::error::MyResult}; use api::{r_api_account_login, r_api_node_raw, r_api_root, r_api_version}; use base64::Engine; -use jellybase::{federation::Federation, CONF}; +use jellybase::{federation::Federation, CONF, SECRETS}; use log::warn; use rand::random; use rocket::{ @@ -59,7 +59,8 @@ pub fn build_rocket(database: DataAcid, federation: Federation) -> Rocket<Build> .map(|e| e.parse().unwrap()) .unwrap_or(8000), secret_key: SecretKey::derive_from( - CONF.cookie_key + SECRETS + .cookie_key .clone() .unwrap_or_else(|| { warn!("cookie_key not configured, generating a random one."); diff --git a/server/src/routes/stream.rs b/server/src/routes/stream.rs index c033bda..5c21a5a 100644 --- a/server/src/routes/stream.rs +++ b/server/src/routes/stream.rs @@ -10,9 +10,10 @@ use jellybase::{ database::{TableExt, T_NODE}, federation::Federation, permission::{NodePermissionExt, PermissionSetExt}, - CONF, + SECRETS, }; use jellycommon::{ + config::FederationAccount, stream::StreamSpec, user::{CreateSessionParams, UserPermission}, TrackSource, @@ -77,8 +78,10 @@ pub async fn r_stream( .last() .ok_or(anyhow!("federation inconsistent"))?; - let (username, password, _) = CONF - .remote_credentials + let FederationAccount { + password, username, .. + } = SECRETS + .federation .get(host) .ok_or(anyhow!("no credentials on the server-side"))?; diff --git a/server/src/routes/ui/account/session/token.rs b/server/src/routes/ui/account/session/token.rs index 969207d..9cc0c4f 100644 --- a/server/src/routes/ui/account/session/token.rs +++ b/server/src/routes/ui/account/session/token.rs @@ -11,13 +11,13 @@ use aes_gcm_siv::{ use anyhow::anyhow; use base64::Engine; use chrono::{Duration, Utc}; -use jellybase::CONF; +use jellybase::SECRETS; use jellycommon::user::PermissionSet; use log::warn; use std::sync::LazyLock; static SESSION_KEY: LazyLock<[u8; 32]> = LazyLock::new(|| { - if let Some(sk) = &CONF.session_key { + if let Some(sk) = &SECRETS.session_key { let r = base64::engine::general_purpose::STANDARD .decode(sk) .expect("key invalid; should be valid base64"); diff --git a/tool/src/main.rs b/tool/src/main.rs index c220a08..58559dc 100644 --- a/tool/src/main.rs +++ b/tool/src/main.rs @@ -6,16 +6,13 @@ pub mod migrate; use anyhow::anyhow; -use base64::Engine; use clap::{Parser, Subcommand, ValueEnum}; +use jellybase::{CONF, SECRETS}; use jellyclient::Instance; -use jellycommon::{ - config::GlobalConfig, user::CreateSessionParams, Node, NodeKind, NodePrivate, NodePublic, -}; -use log::{info, warn}; +use jellycommon::user::CreateSessionParams; +use log::{error, info}; use migrate::migrate; -use rand::random; -use std::{fmt::Debug, fs::File, io::Write, path::PathBuf}; +use std::{fmt::Debug, path::PathBuf}; #[derive(Parser)] struct Args { @@ -65,69 +62,49 @@ fn main() -> anyhow::Result<()> { .init(); let args = Args::parse(); - let config = args - .config - .or(std::env::var_os("JELLYTHING_CONFIG").map(|p| PathBuf::from(p))) - .map(|path| { - Ok::<_, anyhow::Error>(serde_yaml::from_reader::<_, GlobalConfig>(File::open( - path, - )?)?) - }) - .transpose()?; - match args.action { - Action::Init { - base_path: path, - brand, - hostname, - } => { - info!("creating new instance..."); - std::fs::create_dir_all(path.join("library"))?; - std::fs::create_dir_all(path.join("cache"))?; - std::fs::create_dir_all(path.join("assets"))?; - std::fs::create_dir_all(path.join("media"))?; - File::create_new(path.join("assets/front.htm"))? - .write_fmt(format_args!("<h1>My very own jellything instance</h1>"))?; + Action::Init { .. } => { + // info!("creating new instance..."); + // std::fs::create_dir_all(path.join("library"))?; + // std::fs::create_dir_all(path.join("cache"))?; + // std::fs::create_dir_all(path.join("assets"))?; + // std::fs::create_dir_all(path.join("media"))?; + // File::create_new(path.join("assets/front.htm"))? + // .write_fmt(format_args!("<h1>My very own jellything instance</h1>"))?; + + // // TODO: dont fill that + // serde_yaml::to_writer( + // File::create_new(path.join("config.yaml"))?, + // &GlobalConfig { + // brand: brand.clone(), + // hostname, + // slogan: "Creative slogan here".to_string(), + // asset_path: path.join("assets"), + // cache_path: path.join("cache"), + // library_path: path.join("library"), + // database_path: path.join("database"), + // temp_path: "/tmp".into(), - // TODO: dont fill that - serde_yaml::to_writer( - File::create_new(path.join("config.yaml"))?, - &GlobalConfig { - brand: brand.clone(), - hostname, - slogan: "Creative slogan here".to_string(), - asset_path: path.join("assets"), - cache_path: path.join("cache"), - library_path: path.join("library"), - database_path: path.join("database"), - temp_path: "/tmp".into(), - cookie_key: Some( - base64::engine::general_purpose::STANDARD - .encode([(); 32].map(|_| random())), - ), - session_key: Some( - base64::engine::general_purpose::STANDARD - .encode([(); 32].map(|_| random())), - ), - login_expire: 10, - ..Default::default() - }, - )?; - serde_json::to_writer( - File::create_new(path.join("library/directory.json"))?, - &Node { - public: NodePublic { - kind: Some(NodeKind::Collection), - title: Some("My Library".to_string()), - ..Default::default() - }, - private: NodePrivate { - ..Default::default() - }, - }, - )?; - info!("{brand:?} is ready!"); - warn!("please add an admin password to login."); + // login_expire: 10, + // ..Default::default() + // }, + // )?; + // serde_json::to_writer( + // File::create_new(path.join("library/directory.json"))?, + // &Node { + // public: NodePublic { + // kind: Some(NodeKind::Collection), + // title: Some("My Library".to_string()), + // ..Default::default() + // }, + // private: NodePrivate { + // ..Default::default() + // }, + // }, + // )?; + // info!("{brand:?} is ready!"); + // warn!("please add an admin password to login."); + error!("init is currently disabled"); Ok(()) } a @ Action::Migrate { .. } => migrate(a), @@ -136,18 +113,19 @@ fn main() -> anyhow::Result<()> { .build() .unwrap() .block_on(async move { - let config = config.ok_or(anyhow!("this action requires the config"))?; - let inst = Instance::new(hostname.unwrap_or(config.hostname.clone()), !no_tls); + let inst = Instance::new(hostname.unwrap_or(CONF.hostname.clone()), !no_tls); info!("login"); let session = inst .login(CreateSessionParams { drop_permissions: None, expire: None, - password: config + password: SECRETS .admin_password + .clone() .ok_or(anyhow!("admin account required"))?, - username: config + username: CONF .admin_username + .clone() .ok_or(anyhow!("admin account required"))?, }) .await?; |