/* 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) 2023 metamuffin */ #![feature(file_create_new)] pub mod migrate; use anyhow::anyhow; use base64::Engine; use clap::{Parser, Subcommand, ValueEnum}; use jellyclient::Instance; use jellycommon::{ config::GlobalConfig, user::CreateSessionParams, Node, NodeKind, NodePrivate, NodePublic, }; use log::{info, warn}; use migrate::migrate; use rand::random; use std::{fmt::Debug, fs::File, io::Write, path::PathBuf}; #[derive(Parser)] struct Args { /// Path to global jellything config #[arg(short = 'C', long)] config: Option, #[clap(subcommand)] action: Action, } #[derive(Subcommand)] enum Action { /// Initialize a new jellything instance Init { /// Base path of the instance, must either be absolute or relative to the servers pwd base_path: PathBuf, #[arg(short, long)] brand: String, #[arg(short, long)] hostname: String, }, Migrate { database: PathBuf, mode: MigrateMode, save_location: PathBuf, }, Reimport { /// Custom hostname, the config's is used by default #[arg(long)] hostname: Option, /// Disable TLS. Dont use this. #[arg(long)] no_tls: bool, }, } #[derive(Debug, Clone, Copy, PartialEq, ValueEnum)] enum MigrateMode { Import, Export, } fn main() -> anyhow::Result<()> { env_logger::builder() .filter_level(log::LevelFilter::Info) .parse_env("LOG") .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!("

My very own jellything instance

"))?; // 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())), ), admin_username: "admin".to_string(), admin_password: "hackme".to_string(), 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 change the admin password."); Ok(()) } a @ Action::Migrate { .. } => migrate(a), Action::Reimport { hostname, no_tls } => tokio::runtime::Builder::new_multi_thread() .enable_all() .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); info!("login"); let session = inst .login(CreateSessionParams { drop_permissions: None, expire: None, password: config.admin_password, username: config.admin_username, }) .await?; session.reimport().await?; Ok(()) }), } } // fn ok_or_warn(r: Result) -> Option { // match r { // Ok(t) => Some(t), // Err(e) => { // warn!("{e:?}"); // None // } // } // }