1use crate::extensions::distr::{ExtensionDistrFile, MetadataToml};
2use std::path::Path;
3use tokio::io::AsyncWriteExt;
4
5pub static EXTENSION_DIR: &str = "/app/extensions";
6pub static EXTENSION_ROOT_DIR: &str = "/app/repo";
7pub static EXTENSION_LOG: &str = "/tmp/extension_build.log";
8pub static EXTENSION_BUILD_LOCK: &str = "/tmp/extension_build.lock";
9pub static EXTENSION_REBUILD_TRIGGER: &str = "/tmp/rebuild_trigger";
10
11pub async fn is_locked() -> bool {
12 tokio::fs::metadata(EXTENSION_BUILD_LOCK).await.is_ok()
13}
14
15pub async fn trigger_rebuild() -> Result<(), std::io::Error> {
16 let mut file = tokio::fs::File::create(EXTENSION_REBUILD_TRIGGER).await?;
17 file.write_all(b"rebuild").await?;
18
19 Ok(())
20}
21
22pub async fn get_build_logs() -> Box<dyn tokio::io::AsyncRead + Unpin + Send> {
23 match tokio::fs::File::open(EXTENSION_LOG).await {
24 Ok(file) => Box::new(file) as Box<dyn tokio::io::AsyncRead + Unpin + Send>,
25 Err(_) => Box::new(tokio::io::empty()),
26 }
27}
28
29pub async fn write_extension(
30 data: &mut (dyn tokio::io::AsyncRead + Unpin + Send),
31) -> Result<ExtensionDistrFile, anyhow::Error> {
32 let tmp_dir = tempfile::tempdir()?;
33 let tmp_path = tmp_dir.path().join("extension.c7s.zip");
34
35 let mut tmp_file = tokio::fs::File::create_new(&tmp_path).await?;
36 tokio::io::copy(data, &mut tmp_file).await?;
37 let tmp_file = tmp_file.into_std().await;
38
39 let distr =
40 tokio::task::spawn_blocking(move || ExtensionDistrFile::parse_from_reader(tmp_file))
41 .await??;
42
43 let identifier = distr.metadata_toml.get_package_identifier();
44 if !MetadataToml::is_valid_package_identifier(&identifier) {
45 return Err(anyhow::anyhow!("invalid package identifier `{identifier}`"));
46 }
47
48 tokio::fs::copy(
49 tmp_path,
50 Path::new(EXTENSION_DIR).join(format!("{}.c7s.zip", identifier)),
51 )
52 .await?;
53
54 Ok(distr)
55}
56
57pub async fn remove_extension(package_name: &str) -> Result<(), std::io::Error> {
58 let identifier = MetadataToml::convert_package_name_to_identifier(package_name);
59 if !MetadataToml::is_valid_package_identifier(&identifier) {
60 return Err(std::io::Error::new(
61 std::io::ErrorKind::InvalidInput,
62 format!("invalid package identifier `{identifier}`"),
63 ));
64 }
65
66 let path = Path::new(EXTENSION_DIR).join(format!("{}.c7s.zip", identifier));
67
68 tokio::fs::remove_file(path).await?;
69
70 Ok(())
71}
72
73pub async fn list_extensions() -> Result<Vec<ExtensionDistrFile>, anyhow::Error> {
74 let mut entries = tokio::fs::read_dir(EXTENSION_DIR).await?;
75 let mut extensions = Vec::new();
76
77 while let Some(entry) = entries.next_entry().await? {
78 if entry.file_type().await?.is_file() {
79 let path = entry.path();
80 if path.extension().and_then(|s| s.to_str()) == Some("zip") {
81 let file = tokio::fs::File::open(path).await?;
82 let file = file.into_std().await;
83 let distr = match tokio::task::spawn_blocking(move || {
84 ExtensionDistrFile::parse_from_reader(file)
85 })
86 .await
87 {
88 Ok(Ok(d)) => d,
89 _ => continue,
90 };
91
92 extensions.push(distr);
93 }
94 }
95 }
96
97 Ok(extensions)
98}