lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README | LICENSE

service.rs (4628B)


      1 use serde::{Deserialize, Serialize};
      2 #[cfg(feature = "cli")]
      3 use std::path::PathBuf;
      4 
      5 #[cfg(feature = "cli")]
      6 use clap::{ArgAction, Args, ValueHint};
      7 use radroots_runtime_paths::{
      8     DEFAULT_SERVICE_IDENTITY_FILE_NAME, RadrootsBootstrapPaths, RadrootsPathOverrides,
      9     RadrootsPathProfile, RadrootsPathResolver, RadrootsRuntimeNamespace, RadrootsRuntimePathsError,
     10     default_namespaced_bootstrap_paths,
     11 };
     12 
     13 pub const DEFAULT_SERVICE_IDENTITY_PATH: &str = DEFAULT_SERVICE_IDENTITY_FILE_NAME;
     14 
     15 pub fn service_bootstrap_paths_for(
     16     resolver: &RadrootsPathResolver,
     17     profile: RadrootsPathProfile,
     18     overrides: &RadrootsPathOverrides,
     19     runtime_id: &str,
     20 ) -> Result<RadrootsBootstrapPaths, RadrootsRuntimePathsError> {
     21     let namespace = RadrootsRuntimeNamespace::service(runtime_id)?;
     22     default_namespaced_bootstrap_paths(
     23         resolver,
     24         profile,
     25         overrides,
     26         &namespace,
     27         DEFAULT_SERVICE_IDENTITY_PATH,
     28     )
     29 }
     30 
     31 #[cfg(feature = "cli")]
     32 #[derive(Args, Debug, Clone)]
     33 pub struct RadrootsServiceCliArgs {
     34     #[arg(
     35         long,
     36         value_name = "PATH",
     37         value_hint = ValueHint::FilePath,
     38         help = "Path to the daemon configuration file; no implicit cwd-rooted default is used"
     39     )]
     40     pub config: Option<PathBuf>,
     41 
     42     #[arg(
     43         long,
     44         value_name = "PATH",
     45         value_hint = ValueHint::FilePath,
     46         help = "Path to the daemon encrypted identity envelope; callers may resolve a canonical namespaced default ending in identity.secret.json with a sibling .key wrapping key file"
     47     )]
     48     pub identity: Option<PathBuf>,
     49 
     50     #[arg(
     51         long,
     52         action = ArgAction::SetTrue,
     53         help = "Allow generating a new encrypted identity envelope when the configured path is missing; if not set and the identity is absent, the daemon will fail"
     54     )]
     55     pub allow_generate_identity: bool,
     56 }
     57 
     58 #[derive(Debug, Serialize, Deserialize, Clone)]
     59 pub struct RadrootsNostrServiceConfig {
     60     pub logs_dir: String,
     61     #[serde(default)]
     62     pub relays: Vec<String>,
     63     #[serde(default)]
     64     pub nip89_identifier: Option<String>,
     65     #[serde(default)]
     66     pub nip89_extra_tags: Vec<Vec<String>>,
     67 }
     68 
     69 #[cfg(test)]
     70 mod tests {
     71     use std::path::PathBuf;
     72 
     73     use radroots_runtime_paths::{
     74         RadrootsHostEnvironment, RadrootsPathOverrides, RadrootsPathProfile, RadrootsPathResolver,
     75         RadrootsPlatform,
     76     };
     77 
     78     use super::{RadrootsNostrServiceConfig, service_bootstrap_paths_for};
     79 
     80     #[test]
     81     fn service_config_defaults_optional_fields() {
     82         let cfg: RadrootsNostrServiceConfig = toml::from_str(
     83             r#"
     84 logs_dir = "logs"
     85 "#,
     86         )
     87         .expect("service config should parse");
     88 
     89         assert_eq!(cfg.logs_dir, "logs");
     90         assert!(cfg.relays.is_empty());
     91         assert_eq!(cfg.nip89_identifier, None);
     92         assert!(cfg.nip89_extra_tags.is_empty());
     93     }
     94 
     95     #[test]
     96     fn service_bootstrap_paths_follow_runtime_paths_contract() {
     97         let resolver = RadrootsPathResolver::new(
     98             RadrootsPlatform::Linux,
     99             RadrootsHostEnvironment {
    100                 home_dir: Some(PathBuf::from("/home/treesap")),
    101                 ..RadrootsHostEnvironment::default()
    102             },
    103         );
    104 
    105         let paths = service_bootstrap_paths_for(
    106             &resolver,
    107             RadrootsPathProfile::InteractiveUser,
    108             &RadrootsPathOverrides::default(),
    109             "radrootsd",
    110         )
    111         .expect("service bootstrap paths should resolve");
    112 
    113         assert_eq!(
    114             paths.config_path,
    115             PathBuf::from("/home/treesap/.radroots/config/services/radrootsd/config.toml")
    116         );
    117         assert_eq!(
    118             paths.logs_dir,
    119             PathBuf::from("/home/treesap/.radroots/logs/services/radrootsd")
    120         );
    121         assert_eq!(
    122             paths.identity_path,
    123             PathBuf::from(
    124                 "/home/treesap/.radroots/secrets/services/radrootsd/identity.secret.json"
    125             )
    126         );
    127     }
    128 
    129     #[test]
    130     fn service_bootstrap_paths_reject_invalid_runtime_ids() {
    131         let resolver = RadrootsPathResolver::new(
    132             RadrootsPlatform::Linux,
    133             RadrootsHostEnvironment {
    134                 home_dir: Some(PathBuf::from("/home/treesap")),
    135                 ..RadrootsHostEnvironment::default()
    136             },
    137         );
    138 
    139         let err = service_bootstrap_paths_for(
    140             &resolver,
    141             RadrootsPathProfile::InteractiveUser,
    142             &RadrootsPathOverrides::default(),
    143             "",
    144         )
    145         .expect_err("empty runtime ids must fail");
    146 
    147         assert!(!err.to_string().is_empty());
    148     }
    149 }