use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use worker::*;
use crate::claim::SitesStore;
#[derive(Deserialize)]
#[serde(untagged)]
pub enum RepoEntry {
New {
rkey: String,
#[serde(default)]
is_index: bool,
},
Legacy(bool),
}
impl RepoEntry {
pub fn is_index(&self) -> bool {
match self {
RepoEntry::New { is_index, .. } => *is_index,
RepoEntry::Legacy(b) => *b,
}
}
pub fn rkey<'a>(&'a self, name: &'a str) -> &'a str {
match self {
RepoEntry::New { rkey, .. } => rkey.as_str(),
RepoEntry::Legacy(_) => name,
}
}
}
#[derive(Deserialize)]
pub struct DomainMapping {
#[serde(default)]
pub did: String,
#[serde(default)]
pub repos: HashMap<String, RepoEntry>,
}
impl DomainMapping {
pub fn index_repo(&self) -> Option<(&str, &RepoEntry)> {
self.repos.iter().find_map(|(name, entry)| {
if entry.is_index() {
Some((name.as_str(), entry))
} else {
None
}
})
}
}
#[derive(Serialize)]
struct MappingEntry {
rkey: String,
#[serde(default)]
is_index: bool,
}
#[derive(Serialize)]
struct WriteMapping {
did: String,
repos: HashMap<String, MappingEntry>,
}
pub async fn rebuild_for_domain(
kv: &KvStore,
store: &SitesStore,
domain: &str,
) -> Result<(), String> {
match store.mapping_rows_for_domain(domain).await? {
None => {
kv.put(domain, "{}")
.map_err(|e| e.to_string())?
.execute()
.await
.map_err(|e| e.to_string())?;
}
Some((owner_did, sites_list)) => {
let mut repos = HashMap::with_capacity(sites_list.len());
for (name, rkey, is_index) in sites_list {
repos.insert(name, MappingEntry { rkey, is_index });
}
let mapping = WriteMapping {
did: owner_did,
repos,
};
let raw = serde_json::to_string(&mapping).map_err(|e| e.to_string())?;
kv.put(domain, raw)
.map_err(|e| e.to_string())?
.execute()
.await
.map_err(|e| e.to_string())?;
}
}
Ok(())
}
pub async fn delete_r2_prefix(bucket: &Bucket, prefix: &str) -> Result<(), String> {
let mut cursor: Option<String> = None;
loop {
let mut list = bucket.list().prefix(prefix.to_string()).limit(1000);
if let Some(c) = cursor {
list = list.cursor(c);
}
let objects = list.execute().await.map_err(|e| e.to_string())?;
let keys: Vec<String> = objects.objects().iter().map(|o| o.key()).collect();
if !keys.is_empty() {
bucket
.delete_multiple(keys)
.await
.map_err(|e| e.to_string())?;
}
if !objects.truncated() {
break;
}
match objects.cursor() {
Some(c) => cursor = Some(c),
None => break,
}
}
Ok(())
}