atlas_cli/storage/
database.rs1use crate::error::{Error, Result};
2use crate::manifest::utils::{determine_manifest_type, manifest_type_to_string};
3use crate::storage::traits::{ManifestMetadata, ManifestType, StorageBackend};
4use atlas_c2pa_lib::manifest::Manifest;
5use reqwest::blocking::Client;
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use std::time::Duration;
9
10#[derive(Debug, Clone)]
11pub struct DatabaseStorage {
12 base_url: String,
13 client: Client,
14}
15
16impl DatabaseStorage {
17 pub fn print_manifest_structure(value: &Value, indent: usize) {
18 let spaces = " ".repeat(indent);
19 match value {
20 Value::Object(map) => {
21 for (key, value) in map {
22 println!("{spaces}{key}: ");
23 Self::print_manifest_structure(value, indent + 2);
24 }
25 }
26 Value::Array(arr) => {
27 for value in arr {
28 Self::print_manifest_structure(value, indent + 2);
29 }
30 }
31 _ => println!("{spaces}{value}"),
32 }
33 }
34}
35
36#[derive(Debug, Serialize, Deserialize)]
37struct StoredManifest {
38 #[serde(skip_serializing_if = "Option::is_none")]
39 _id: Option<serde_json::Value>,
40 manifest_id: String,
41 manifest_type: String,
42 manifest: Value,
43 created_at: String,
44}
45
46#[derive(Debug, Serialize, Deserialize)]
47struct ManifestWrapper {
48 manifest_id: String,
49 manifest_type: String,
50 #[serde(flatten)]
51 manifest: Manifest,
52 created_at: String,
53}
54
55impl DatabaseStorage {
56 pub fn new(url: String) -> Result<Self> {
57 let client = Client::builder()
58 .timeout(Duration::from_secs(30))
59 .build()
60 .map_err(|e| Error::Storage(format!("Failed to create HTTP client: {e}")))?;
61
62 Ok(Self {
63 base_url: url.trim_end_matches('/').to_string(),
64 client,
65 })
66 }
67
68 fn manifest_url(&self, id: Option<&str>) -> String {
69 match id {
70 Some(id) => format!("{}/manifests/{}", self.base_url, id),
71 None => format!("{}/manifests", self.base_url),
72 }
73 }
74}
75
76impl StorageBackend for DatabaseStorage {
77 fn get_base_uri(&self) -> String {
78 self.base_url.clone()
79 }
80
81 fn store_manifest(&self, manifest: &Manifest) -> Result<String> {
82 let existing = self
84 .client
85 .get(format!(
86 "{}/manifests/{}",
87 self.base_url, &manifest.instance_id
88 ))
89 .send()
90 .map_err(|e| Error::Storage(format!("Failed to check existing manifest: {e}")))?;
91
92 if existing.status().is_success() {
93 let parts: Vec<&str> = manifest.instance_id.split(':').collect();
97 let uuid_part = if parts.len() >= 3 {
98 parts[2].to_string() } else {
100 manifest.instance_id.clone() };
102
103 let claim_generator = manifest.claim_generator.replace('/', "_");
105
106 let all_manifests_response = self
108 .client
109 .get(format!("{}/manifests", self.base_url))
110 .send()
111 .map_err(|e| Error::Storage(format!("Failed to list manifests: {e}")))?;
112
113 let all_manifests: Vec<serde_json::Value> = all_manifests_response
114 .json()
115 .map_err(|e| Error::Storage(format!("Failed to parse manifests list: {e}")))?;
116
117 let mut max_version = 0;
119 for manifest_entry in all_manifests {
120 if let Some(id) = manifest_entry.get("manifest_id").and_then(|v| v.as_str()) {
121 if id.starts_with(&format!("urn:c2pa:{uuid_part}:")) {
122 let id_parts: Vec<&str> = id.split(':').collect();
123 if id_parts.len() >= 5 {
124 if let Some(version_reason) = id_parts.get(4) {
125 if let Some(version_str) = version_reason.split('_').next() {
126 if let Ok(version) = version_str.parse::<i32>() {
127 max_version = max_version.max(version);
128 }
129 }
130 }
131 }
132 }
133 }
134 }
135
136 let versioned_id = format!(
139 "urn:c2pa:{}:{}:{}_{}",
140 uuid_part,
141 claim_generator,
142 max_version + 1,
143 1
144 );
145
146 let mut updated_manifest = manifest.clone();
148 updated_manifest.instance_id = versioned_id.clone();
149
150 let manifest_type = manifest_type_to_string(&determine_manifest_type(manifest));
152
153 let stored_manifest = StoredManifest {
154 _id: None,
155 manifest_id: versioned_id.clone(),
156 manifest_type,
157 manifest: serde_json::to_value(&updated_manifest)
158 .map_err(|e| Error::Serialization(e.to_string()))?,
159 created_at: time::OffsetDateTime::now_utc().to_string(),
160 };
161
162 self.client
163 .post(self.manifest_url(Some(&versioned_id)))
164 .json(&stored_manifest)
165 .send()
166 .map_err(|e| Error::Storage(format!("Failed to store manifest: {e}")))?;
167
168 Ok(versioned_id)
169 } else {
170 let manifest_type = manifest_type_to_string(&determine_manifest_type(manifest));
172
173 let stored_manifest = StoredManifest {
174 _id: None,
175 manifest_id: manifest.instance_id.clone(),
176 manifest_type,
177 manifest: serde_json::to_value(manifest)
178 .map_err(|e| Error::Serialization(e.to_string()))?,
179 created_at: time::OffsetDateTime::now_utc().to_string(),
180 };
181
182 self.client
183 .post(self.manifest_url(Some(&manifest.instance_id)))
184 .json(&stored_manifest)
185 .send()
186 .map_err(|e| Error::Storage(format!("Failed to store manifest: {e}")))?;
187
188 Ok(manifest.instance_id.clone())
189 }
190 }
191
192 fn retrieve_manifest(&self, id: &str) -> Result<Manifest> {
193 let parts: Vec<&str> = id.split(':').collect();
195 let uuid_part = if parts.len() >= 3 && parts[0] == "urn" && parts[1] == "c2pa" {
196 parts[2] } else {
198 id };
200
201 let response = self
203 .client
204 .get(format!("{}/manifests/{}", self.base_url, id))
205 .send()
206 .map_err(|e| Error::Storage(format!("Failed to retrieve manifest: {e}")))?;
207
208 if response.status().is_success() {
209 let stored_manifest: StoredManifest = response
211 .json()
212 .map_err(|e| Error::Storage(format!("Failed to parse manifest: {e}")))?;
213
214 let manifest_value = stored_manifest
216 .manifest
217 .get("manifest")
218 .ok_or_else(|| Error::Storage("Invalid manifest structure".to_string()))?;
219
220 return serde_json::from_value(manifest_value.clone())
221 .map_err(|e| Error::Storage(format!("Failed to parse manifest data: {e}")));
222 }
223
224 let list_response = self
226 .client
227 .get(format!("{}/manifests", self.base_url))
228 .send()
229 .map_err(|e| Error::Storage(format!("Failed to list manifests: {e}")))?;
230
231 if !list_response.status().is_success() {
232 return Err(Error::Storage(format!(
233 "Failed to list manifests. Status: {}",
234 list_response.status()
235 )));
236 }
237
238 let manifests: Vec<StoredManifest> = list_response
240 .json()
241 .map_err(|e| Error::Storage(format!("Failed to parse manifests list: {e}")))?;
242
243 let mut versions: Vec<StoredManifest> = manifests
245 .into_iter()
246 .filter(|m| m.manifest_id.contains(&format!("urn:c2pa:{uuid_part}:")))
247 .collect();
248
249 if versions.is_empty() {
250 return Err(Error::Storage(format!("Manifest not found for ID: {id}")));
251 }
252
253 versions.sort_by(|a, b| b.created_at.cmp(&a.created_at));
255
256 let latest = &versions[0];
258
259 let manifest_value = latest
261 .manifest
262 .get("manifest")
263 .ok_or_else(|| Error::Storage("Invalid manifest structure".to_string()))?;
264
265 serde_json::from_value(manifest_value.clone())
266 .map_err(|e| Error::Storage(format!("Failed to parse manifest data: {e}")))
267 }
268
269 fn list_manifests(&self) -> Result<Vec<ManifestMetadata>> {
270 let response = self
271 .client
272 .get(self.manifest_url(None))
273 .send()
274 .map_err(|e| Error::Storage(format!("Failed to list manifests: {e}")))?;
275
276 if !response.status().is_success() {
277 return Err(Error::Storage(format!(
278 "Failed to list manifests. Status: {}",
279 response.status()
280 )));
281 }
282
283 let stored_manifests: Vec<StoredManifest> = response
284 .json()
285 .map_err(|e| Error::Storage(format!("Failed to parse manifests list: {e}")))?;
286
287 Ok(stored_manifests
288 .into_iter()
289 .map(|m| {
290 let title = m
291 .manifest
292 .get("manifest")
293 .and_then(|inner| inner.get("manifest"))
294 .and_then(|manifest| manifest.get("title"))
295 .and_then(|t| t.as_str())
296 .unwrap_or("Unknown")
297 .to_string();
298
299 ManifestMetadata {
300 id: m.manifest_id,
301 name: title,
302 manifest_type: match m.manifest_type.as_str() {
303 "dataset" => ManifestType::Dataset,
304 _ => ManifestType::Model,
305 },
306 created_at: m.created_at,
307 }
308 })
309 .collect())
310 }
311
312 fn delete_manifest(&self, id: &str) -> Result<()> {
313 let response = self
314 .client
315 .delete(self.manifest_url(Some(id)))
316 .send()
317 .map_err(|e| Error::Storage(format!("Failed to delete manifest: {e}")))?;
318
319 if !response.status().is_success() {
320 return Err(Error::Storage(format!(
321 "Failed to delete manifest. Status: {}",
322 response.status()
323 )));
324 }
325
326 Ok(())
327 }
328
329 fn as_any(&self) -> &dyn std::any::Any {
331 self
332 }
333}