Skip to main content

ph_registry/
config.rs

1use serde::Deserialize;
2use anyhow::Context;
3
4#[derive(Debug, Clone, Deserialize)]
5pub struct ServerConfig {
6    #[serde(default = "default_port")]
7    pub port: u16,
8    #[serde(default)]
9    pub read_only: bool,
10}
11
12fn default_port() -> u16 { 8080 }
13
14#[derive(Debug, Clone, Deserialize)]
15#[serde(tag = "type", rename_all = "lowercase")]
16pub enum StorageConfig {
17    S3 {
18        endpoint: String,
19        bucket: String,
20        access_key: String,
21        secret_key: String,
22        #[serde(default = "default_region")]
23        region: String,
24    },
25    Filesystem {
26        path: String,
27    },
28}
29
30fn default_region() -> String { "us-east-1".to_string() }
31
32#[derive(Debug, Clone, Deserialize)]
33pub struct DatabaseConfig {
34    pub path: String,
35}
36
37#[derive(Debug, Clone, Deserialize)]
38pub struct AuthConfig {
39    #[serde(default)]
40    pub pull_requires_auth: bool,
41    pub admin_token: String,
42}
43
44#[derive(Debug, Clone, Deserialize)]
45pub struct LogConfig {
46    #[serde(default = "default_log_level")]
47    pub level: String,
48}
49
50impl Default for LogConfig {
51    fn default() -> Self {
52        Self { level: default_log_level() }
53    }
54}
55
56fn default_log_level() -> String { "info".to_string() }
57
58#[derive(Debug, Clone, Deserialize)]
59pub struct RegistryConfig {
60    pub server: ServerConfig,
61    pub storage: StorageConfig,
62    pub database: DatabaseConfig,
63    pub auth: AuthConfig,
64    #[serde(default)]
65    pub log: LogConfig,
66}
67
68impl RegistryConfig {
69    pub fn load(path: &str) -> anyhow::Result<Self> {
70        let content = std::fs::read_to_string(path)
71            .with_context(|| format!("cannot read config file: {}", path))?;
72        serde_yaml::from_str(&content)
73            .with_context(|| format!("cannot parse config file: {}", path))
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn test_parse_filesystem_config() {
83        let yaml = r#"
84server:
85  port: 9090
86  read_only: false
87storage:
88  type: filesystem
89  path: /tmp/layers
90database:
91  path: /tmp/registry.db
92auth:
93  pull_requires_auth: false
94  admin_token: "phrt_test"
95"#;
96        let cfg: RegistryConfig = serde_yaml::from_str(yaml).unwrap();
97        assert_eq!(cfg.server.port, 9090);
98        assert!(!cfg.server.read_only);
99        match &cfg.storage {
100            StorageConfig::Filesystem { path } => assert_eq!(path, "/tmp/layers"),
101            _ => panic!("expected filesystem storage"),
102        }
103        assert_eq!(cfg.auth.admin_token, "phrt_test");
104    }
105
106    #[test]
107    fn test_parse_s3_config() {
108        let yaml = r#"
109server:
110  port: 8080
111storage:
112  type: s3
113  endpoint: http://minio:9000
114  bucket: prompthub
115  access_key: admin
116  secret_key: secret
117database:
118  path: /data/registry.db
119auth:
120  pull_requires_auth: true
121  admin_token: "phrt_admin"
122"#;
123        let cfg: RegistryConfig = serde_yaml::from_str(yaml).unwrap();
124        match &cfg.storage {
125            StorageConfig::S3 { endpoint, bucket, .. } => {
126                assert_eq!(endpoint, "http://minio:9000");
127                assert_eq!(bucket, "prompthub");
128            }
129            _ => panic!("expected s3 storage"),
130        }
131        assert!(cfg.auth.pull_requires_auth);
132    }
133}