Skip to main content

objectiveai_sdk/filesystem/config/
viewer.rs

1use serde::{Serialize, Deserialize};
2
3/// A generated secret/signature pair for viewer authentication.
4///
5/// The viewer server stores the `secret`. The API client stores the `signature`.
6/// The signature is `sha256=<hex of SHA256(secret)>`. The viewer server validates
7/// by computing SHA256(secret) and comparing against the incoming header value.
8/// Knowing the signature does not reveal the secret (preimage resistance).
9#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
10#[schemars(rename = "filesystem.config.ViewerSecretSignaturePair")]
11pub struct ViewerSecretSignaturePair {
12    /// The secret for the viewer server.
13    pub secret: String,
14    /// The pre-computed signature for the API client (`sha256=<hex>`).
15    pub signature: String,
16}
17
18/// Generates a random secret/signature pair for viewer authentication.
19///
20/// The secret is a random 64-character hex string. The signature is
21/// `sha256=<SHA256(secret)>` — a one-way derivation that cannot be reversed.
22pub fn generate_viewer_secret_signature_pair() -> ViewerSecretSignaturePair {
23    use sha2::{Sha256, Digest};
24
25    let mut bytes = [0u8; 32];
26    rand::fill(&mut bytes);
27    let secret: String = bytes.iter().map(|b| format!("{:02x}", b)).collect();
28
29    let hash = Sha256::digest(secret.as_bytes());
30    let signature = format!("sha256={}", hash.iter().map(|b| format!("{:02x}", b)).collect::<String>());
31
32    ViewerSecretSignaturePair { secret, signature }
33}
34
35#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, schemars::JsonSchema)]
36#[schemars(rename = "filesystem.config.ViewerMode")]
37#[serde(rename_all = "snake_case")]
38pub enum ViewerMode {
39    Remote,
40    #[default]
41    Local,
42}
43
44#[derive(Debug, Clone, Default, Serialize, Deserialize, schemars::JsonSchema)]
45#[schemars(rename = "filesystem.config.ViewerConfig")]
46pub struct ViewerConfig {
47    #[serde(default)]
48    pub mode: ViewerMode,
49    #[serde(skip_serializing_if = "ViewerLocalConfig::is_none")]
50    #[schemars(extend("omitempty" = true))]
51    pub local: Option<ViewerLocalConfig>,
52}
53
54impl ViewerConfig {
55    pub fn is_empty(&self) -> bool {
56        matches!(self.mode, ViewerMode::Local)
57            && self.local.as_ref().is_none_or(|cfg| cfg.is_empty())
58    }
59
60    pub fn is_none(this: &Option<Self>) -> bool {
61        this.as_ref().is_none_or(|cfg| cfg.is_empty())
62    }
63
64    pub fn local(&mut self) -> &mut ViewerLocalConfig {
65        self.local.get_or_insert_with(ViewerLocalConfig::default)
66    }
67
68    pub fn get_mode(&self) -> ViewerMode {
69        self.mode
70    }
71
72    pub fn set_mode(&mut self, mode: ViewerMode) {
73        self.mode = mode;
74    }
75
76    pub fn jq(&self, filter: &str) -> Result<Vec<serde_json::Value>, super::super::Error> {
77        super::super::run_jq(self, filter)
78    }
79}
80
81#[derive(Debug, Clone, Default, Serialize, Deserialize, schemars::JsonSchema)]
82#[schemars(rename = "filesystem.config.ViewerLocalConfig")]
83pub struct ViewerLocalConfig {
84    #[serde(skip_serializing_if = "Option::is_none")]
85    #[schemars(extend("omitempty" = true))]
86    pub secret: Option<String>,
87    #[serde(skip_serializing_if = "Option::is_none")]
88    #[schemars(extend("omitempty" = true))]
89    pub signature: Option<String>,
90}
91
92impl ViewerLocalConfig {
93    pub fn is_empty(&self) -> bool {
94        self.secret.is_none() && self.signature.is_none()
95    }
96
97    pub fn is_none(this: &Option<Self>) -> bool {
98        this.as_ref().is_none_or(|cfg| cfg.is_empty())
99    }
100
101    pub fn get_secret(&self) -> Option<&str> {
102        self.secret.as_deref()
103    }
104
105    pub fn set_secret(&mut self, value: impl Into<String>) {
106        self.secret = Some(value.into());
107    }
108
109    pub fn get_signature(&self) -> Option<&str> {
110        self.signature.as_deref()
111    }
112
113    pub fn set_signature(&mut self, value: impl Into<String>) {
114        self.signature = Some(value.into());
115    }
116
117    pub fn jq(&self, filter: &str) -> Result<Vec<serde_json::Value>, super::super::Error> {
118        super::super::run_jq(self, filter)
119    }
120}