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