kittynode_core/infra/
file.rs

1use eyre::{Context, Result};
2use rand::RngCore;
3use std::{fs, path::PathBuf};
4use tracing::info;
5
6fn config_subdir_path(dir_name: &str) -> Result<PathBuf> {
7    home::home_dir()
8        .map(|home| home.join(".config").join(dir_name))
9        .ok_or_else(|| eyre::eyre!("Failed to determine the ~/.config/{} path", dir_name))
10}
11
12pub fn kittynode_path() -> Result<PathBuf> {
13    config_subdir_path("kittynode")
14}
15
16pub fn kittynode_cli_path() -> Result<PathBuf> {
17    config_subdir_path("kittynode-cli")
18}
19
20pub(crate) fn generate_jwt_secret_with_path(path: &PathBuf) -> Result<String> {
21    if !path.exists() {
22        info!("Creating directory at {:?}", path);
23        fs::create_dir_all(path).wrap_err("Failed to create directory")?;
24    }
25
26    info!("Generating JWT secret using a random number generator");
27
28    // Generate 32 random bytes
29    let mut buf = [0u8; 32];
30    rand::rng().fill_bytes(&mut buf);
31
32    // Convert the random bytes to hex
33    let secret = hex::encode(buf);
34
35    // Write the secret to the path
36    fs::write(path.join("jwt.hex"), &secret).wrap_err("Failed to write JWT secret to file")?;
37
38    info!(
39        "JWT secret successfully generated and written to {:?}",
40        path.join("jwt.hex")
41    );
42
43    Ok(secret)
44}
45
46pub(crate) fn generate_jwt_secret() -> Result<String> {
47    let path = kittynode_path()?;
48    generate_jwt_secret_with_path(&path)
49}
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54    use tempfile::tempdir;
55
56    #[test]
57    fn test_generate_jwt_secret() {
58        let temp_dir = tempdir().unwrap();
59        let temp_path = temp_dir.path().to_path_buf();
60
61        let result = generate_jwt_secret_with_path(&temp_path);
62        assert!(result.is_ok(), "Expected OK, got {result:?}");
63
64        let jwt_file_path = temp_path.join("jwt.hex");
65        assert!(jwt_file_path.exists(), "JWT secret file not found");
66
67        let secret = fs::read_to_string(jwt_file_path).unwrap();
68        assert_eq!(secret.len(), 64, "Expected 64 hex characters");
69        assert!(secret.chars().all(|c| c.is_ascii_hexdigit()));
70
71        assert_eq!(result.unwrap(), secret, "Secrets do not match");
72    }
73}