Update all modules to use registry - part 3

This commit is contained in:
mdecimus
2026-02-11 21:30:17 +00:00
parent c4064082ea
commit f7c2fe10c1
86 changed files with 1010 additions and 1037 deletions

View File

@@ -0,0 +1,212 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use super::WEBADMIN_KEY;
use crate::Core;
use ahash::AHashMap;
use arc_swap::ArcSwap;
use std::{
borrow::Cow,
io::{self, Cursor, Read},
path::PathBuf,
};
use store::BlobStore;
pub struct WebApplicationManager {
bundle_path: TempDir,
routes: ArcSwap<AHashMap<String, Resource<PathBuf>>>,
}
#[derive(Default, Clone)]
pub struct Resource<T> {
pub content_type: Cow<'static, str>,
pub contents: T,
}
impl<T> Resource<T> {
pub fn new(content_type: impl Into<Cow<'static, str>>, contents: T) -> Self {
Self {
content_type: content_type.into(),
contents,
}
}
}
impl WebApplicationManager {
pub fn new(base_path: PathBuf) -> Self {
Self {
bundle_path: TempDir::new(base_path),
routes: ArcSwap::from_pointee(Default::default()),
}
}
pub async fn get(&self, path: &str) -> trc::Result<Resource<Vec<u8>>> {
let routes = self.routes.load();
if let Some(resource) = routes.get(path).or_else(|| routes.get("index.html")) {
tokio::fs::read(&resource.contents)
.await
.map(|contents| Resource {
content_type: resource.content_type.clone(),
contents,
})
.map_err(|err| {
trc::ResourceEvent::Error
.reason(err)
.ctx(trc::Key::Path, path.to_string())
.caused_by(trc::location!())
})
} else {
Ok(Resource::default())
}
}
pub async fn unpack(&self, blob_store: &BlobStore) -> trc::Result<()> {
// Delete any existing bundles
self.bundle_path.clean().await.map_err(unpack_error)?;
// Obtain webadmin bundle
let bundle = blob_store
.get_blob(WEBADMIN_KEY, 0..usize::MAX)
.await?
.ok_or_else(|| {
trc::ResourceEvent::NotFound
.caused_by(trc::location!())
.details("Webadmin bundle not found")
})?;
// Uncompress
let mut bundle = zip::ZipArchive::new(Cursor::new(bundle)).map_err(|err| {
trc::ResourceEvent::Error
.caused_by(trc::location!())
.reason(err)
.details("Failed to decompress webadmin bundle")
})?;
let mut routes = AHashMap::new();
for i in 0..bundle.len() {
let (file_name, contents) = {
let mut file = bundle.by_index(i).map_err(|err| {
trc::ResourceEvent::Error
.caused_by(trc::location!())
.reason(err)
.details("Failed to read file from webadmin bundle")
})?;
if file.is_dir() {
continue;
}
let mut contents = Vec::new();
file.read_to_end(&mut contents).map_err(unpack_error)?;
(file.name().to_string(), contents)
};
let path = self.bundle_path.path.join(format!("{i:02}"));
tokio::fs::write(&path, contents)
.await
.map_err(unpack_error)?;
let resource = Resource {
content_type: match file_name
.rsplit_once('.')
.map(|(_, ext)| ext)
.unwrap_or_default()
{
"html" => "text/html",
"css" => "text/css",
"wasm" => "application/wasm",
"js" => "application/javascript",
"json" => "application/json",
"png" => "image/png",
"svg" => "image/svg+xml",
"ico" => "image/x-icon",
_ => "application/octet-stream",
}
.into(),
contents: path,
};
routes.insert(file_name, resource);
}
// Update routes
self.routes.store(routes.into());
trc::event!(
Resource(trc::ResourceEvent::WebadminUnpacked),
Path = self.bundle_path.path.to_string_lossy().into_owned(),
);
Ok(())
}
pub async fn update(&self, core: &Core) -> trc::Result<()> {
todo!()
/* let bytes = core
.storage
.config
.fetch_resource("webadmin")
.await
.map_err(|err| {
trc::ResourceEvent::Error
.caused_by(trc::location!())
.reason(err)
.details("Failed to download webadmin")
})?;
core.storage.blob.put_blob(WEBADMIN_KEY, &bytes).await*/
}
pub async fn update_and_unpack(&self, core: &Core) -> trc::Result<()> {
self.update(core).await?;
self.unpack(&core.storage.blob).await
}
}
impl Resource<Vec<u8>> {
pub fn is_empty(&self) -> bool {
self.content_type.is_empty() && self.contents.is_empty()
}
}
pub struct TempDir {
pub path: PathBuf,
}
impl TempDir {
pub fn new(path: PathBuf) -> TempDir {
TempDir {
path: path.join(std::str::from_utf8(WEBADMIN_KEY).unwrap()),
}
}
pub async fn clean(&self) -> io::Result<()> {
if tokio::fs::metadata(&self.path).await.is_ok() {
let _ = tokio::fs::remove_dir_all(&self.path).await;
}
tokio::fs::create_dir(&self.path).await
}
}
fn unpack_error(err: std::io::Error) -> trc::Error {
trc::ResourceEvent::Error
.reason(err)
.details("Failed to unpack webadmin bundle")
}
impl Default for WebApplicationManager {
fn default() -> Self {
Self::new(std::env::temp_dir())
}
}
impl Default for TempDir {
fn default() -> Self {
Self::new(std::env::temp_dir())
}
}
impl Drop for TempDir {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.path);
}
}