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 store_manifest(&self, manifest: &Manifest) -> Result<String> {
78 let existing = self
80 .client
81 .get(format!(
82 "{}/manifests/{}",
83 self.base_url, &manifest.instance_id
84 ))
85 .send()
86 .map_err(|e| Error::Storage(format!("Failed to check existing manifest: {e}")))?;
87
88 if existing.status().is_success() {
89 let parts: Vec<&str> = manifest.instance_id.split(':').collect();
93 let uuid_part = if parts.len() >= 3 {
94 parts[2].to_string() } else {
96 manifest.instance_id.clone() };
98
99 let claim_generator = manifest.claim_generator.replace('/', "_");
101
102 let all_manifests_response = self
104 .client
105 .get(format!("{}/manifests", self.base_url))
106 .send()
107 .map_err(|e| Error::Storage(format!("Failed to list manifests: {e}")))?;
108
109 let all_manifests: Vec<serde_json::Value> = all_manifests_response
110 .json()
111 .map_err(|e| Error::Storage(format!("Failed to parse manifests list: {e}")))?;
112
113 let mut max_version = 0;
115 for manifest_entry in all_manifests {
116 if let Some(id) = manifest_entry.get("manifest_id").and_then(|v| v.as_str()) {
117 if id.starts_with(&format!("urn:c2pa:{uuid_part}:")) {
118 let id_parts: Vec<&str> = id.split(':').collect();
119 if id_parts.len() >= 5 {
120 if let Some(version_reason) = id_parts.get(4) {
121 if let Some(version_str) = version_reason.split('_').next() {
122 if let Ok(version) = version_str.parse::<i32>() {
123 max_version = max_version.max(version);
124 }
125 }
126 }
127 }
128 }
129 }
130 }
131
132 let versioned_id = format!(
135 "urn:c2pa:{}:{}:{}_{}",
136 uuid_part,
137 claim_generator,
138 max_version + 1,
139 1
140 );
141
142 let mut updated_manifest = manifest.clone();
144 updated_manifest.instance_id = versioned_id.clone();
145
146 let manifest_type = manifest_type_to_string(&determine_manifest_type(manifest));
148
149 let stored_manifest = StoredManifest {
150 _id: None,
151 manifest_id: versioned_id.clone(),
152 manifest_type,
153 manifest: serde_json::to_value(&updated_manifest)
154 .map_err(|e| Error::Serialization(e.to_string()))?,
155 created_at: time::OffsetDateTime::now_utc().to_string(),
156 };
157
158 self.client
159 .post(self.manifest_url(Some(&versioned_id)))
160 .json(&stored_manifest)
161 .send()
162 .map_err(|e| Error::Storage(format!("Failed to store manifest: {e}")))?;
163
164 Ok(versioned_id)
165 } else {
166 let manifest_type = manifest_type_to_string(&determine_manifest_type(manifest));
168
169 let stored_manifest = StoredManifest {
170 _id: None,
171 manifest_id: manifest.instance_id.clone(),
172 manifest_type,
173 manifest: serde_json::to_value(manifest)
174 .map_err(|e| Error::Serialization(e.to_string()))?,
175 created_at: time::OffsetDateTime::now_utc().to_string(),
176 };
177
178 self.client
179 .post(self.manifest_url(Some(&manifest.instance_id)))
180 .json(&stored_manifest)
181 .send()
182 .map_err(|e| Error::Storage(format!("Failed to store manifest: {e}")))?;
183
184 Ok(manifest.instance_id.clone())
185 }
186 }
187
188 fn retrieve_manifest(&self, id: &str) -> Result<Manifest> {
189 let parts: Vec<&str> = id.split(':').collect();
191 let uuid_part = if parts.len() >= 3 && parts[0] == "urn" && parts[1] == "c2pa" {
192 parts[2] } else {
194 id };
196
197 let response = self
199 .client
200 .get(format!("{}/manifests/{}", self.base_url, id))
201 .send()
202 .map_err(|e| Error::Storage(format!("Failed to retrieve manifest: {e}")))?;
203
204 if response.status().is_success() {
205 let stored_manifest: StoredManifest = response
207 .json()
208 .map_err(|e| Error::Storage(format!("Failed to parse manifest: {e}")))?;
209
210 let manifest_value = stored_manifest
212 .manifest
213 .get("manifest")
214 .ok_or_else(|| Error::Storage("Invalid manifest structure".to_string()))?;
215
216 return serde_json::from_value(manifest_value.clone())
217 .map_err(|e| Error::Storage(format!("Failed to parse manifest data: {e}")));
218 }
219
220 let list_response = self
222 .client
223 .get(format!("{}/manifests", self.base_url))
224 .send()
225 .map_err(|e| Error::Storage(format!("Failed to list manifests: {e}")))?;
226
227 if !list_response.status().is_success() {
228 return Err(Error::Storage(format!(
229 "Failed to list manifests. Status: {}",
230 list_response.status()
231 )));
232 }
233
234 let manifests: Vec<StoredManifest> = list_response
236 .json()
237 .map_err(|e| Error::Storage(format!("Failed to parse manifests list: {e}")))?;
238
239 let mut versions: Vec<StoredManifest> = manifests
241 .into_iter()
242 .filter(|m| m.manifest_id.contains(&format!("urn:c2pa:{uuid_part}:")))
243 .collect();
244
245 if versions.is_empty() {
246 return Err(Error::Storage(format!("Manifest not found for ID: {id}")));
247 }
248
249 versions.sort_by(|a, b| b.created_at.cmp(&a.created_at));
251
252 let latest = &versions[0];
254
255 let manifest_value = latest
257 .manifest
258 .get("manifest")
259 .ok_or_else(|| Error::Storage("Invalid manifest structure".to_string()))?;
260
261 serde_json::from_value(manifest_value.clone())
262 .map_err(|e| Error::Storage(format!("Failed to parse manifest data: {e}")))
263 }
264
265 fn list_manifests(&self) -> Result<Vec<ManifestMetadata>> {
266 let response = self
267 .client
268 .get(self.manifest_url(None))
269 .send()
270 .map_err(|e| Error::Storage(format!("Failed to list manifests: {e}")))?;
271
272 if !response.status().is_success() {
273 return Err(Error::Storage(format!(
274 "Failed to list manifests. Status: {}",
275 response.status()
276 )));
277 }
278
279 let stored_manifests: Vec<StoredManifest> = response
280 .json()
281 .map_err(|e| Error::Storage(format!("Failed to parse manifests list: {e}")))?;
282
283 Ok(stored_manifests
284 .into_iter()
285 .map(|m| {
286 let title = m
287 .manifest
288 .get("manifest")
289 .and_then(|inner| inner.get("manifest"))
290 .and_then(|manifest| manifest.get("title"))
291 .and_then(|t| t.as_str())
292 .unwrap_or("Unknown")
293 .to_string();
294
295 ManifestMetadata {
296 id: m.manifest_id,
297 name: title,
298 manifest_type: match m.manifest_type.as_str() {
299 "dataset" => ManifestType::Dataset,
300 _ => ManifestType::Model,
301 },
302 created_at: m.created_at,
303 }
304 })
305 .collect())
306 }
307
308 fn delete_manifest(&self, id: &str) -> Result<()> {
309 let response = self
310 .client
311 .delete(self.manifest_url(Some(id)))
312 .send()
313 .map_err(|e| Error::Storage(format!("Failed to delete manifest: {e}")))?;
314
315 if !response.status().is_success() {
316 return Err(Error::Storage(format!(
317 "Failed to delete manifest. Status: {}",
318 response.status()
319 )));
320 }
321
322 Ok(())
323 }
324
325 fn as_any(&self) -> &dyn std::any::Any {
327 self
328 }
329}