This commit is contained in:
Pascal Engélibert 2026-08-15 18:07:12 +02:00
commit 4f58cea6ad
2 changed files with 141 additions and 2 deletions

View file

@ -1,12 +1,11 @@
//! Client for downloading repositories from a forge's API.
use std::{collections::HashSet, io::Write, path::PathBuf};
use crate::{
config::Config,
repo::{RepoMetadata, WriteRepoMetadataError},
};
use std::{collections::HashSet, io::Write, path::PathBuf};
use rand::RngExt;
use serde::Deserialize;
use sha2::Digest;

View file

@ -11,6 +11,7 @@ use std::{
};
use trillium::{Conn, Handler};
use trillium_router::{Router, RouterConnExt};
use sha2::Digest;
#[derive(Clone)]
struct PageCacheEntry {
@ -317,6 +318,145 @@ pub fn make_router(config: &'static Config) -> impl Handler {
.ok(file_content)
}
}
})
.get("/e/:secret/*", move |conn: Conn| {
async move {
let Some(secret) = conn.param("secret") else {
return conn.with_status(401);
};
let mut hasher = sha2::Sha256::default();
let mut repo_key = [0; 32];
if base64_turbo::URL_SAFE_NO_PAD.decode_into(secret, &mut repo_key) != Ok(32) {
return conn.with_status(401);
}
hasher.update(repo_key);
let repo_id: [u8; 32] = hasher.finalize_reset().into();
let mut repo_id_str = [0; 22];
base64_turbo::URL_SAFE_NO_PAD
.encode_into(&repo_id[0..16], &mut repo_id_str)
.expect("unreachable");
let repo_id_str = str::from_utf8(&repo_id_str).expect("unreachable");
let Some((repo_metadata, root)) = metadata_cache
.fetch(repo_id_str.to_string(), |key| {
fetch_metadata(config, &key)
})
else {
return conn.with_status(404);
};
let invalidate_page_cache = |entry: &PageCacheEntry| {
// strict cmp because it's better to spend more on useless invalidations
// than to serve outdated pages
// (e.g. privacy issues if the author thinks sensible data are removed but we still serve them)
entry.time > repo_metadata.time_updated
};
if let Some(rendered_page) = page_cache.fetch_cond_only(
(repo_id_str.to_string(), conn.path().to_string()),
invalidate_page_cache,
) {
conn.ok(rendered_page.content)
} else {
let Some(served_entry) = root.find(conn.path().split('/')) else {
page_cache
.update((repo_id_str.to_string(), conn.path().to_string()), None);
return conn.with_status(404);
};
let rendered_page = match served_entry {
templates::EntryRef::Directory(served_dir) => {
let mut served_file = None;
for default_filename in config.default_files.split(':') {
if let Some(templates::Entry::File(f)) =
served_dir.entries.get(default_filename)
{
served_file = Some(f);
break;
}
}
let mut html = String::new();
if let Some(served_file) = served_file {
let Some(file_content) =
fetch_file(config, repo_id_str, &served_file.hash)
else {
return conn.with_status(500);
};
let mut file_lang = served_file
.name
.rsplit('.')
.next()
.unwrap_or(giallo::PLAIN_GRAMMAR_NAME)
.to_string();
file_lang.make_ascii_lowercase();
html = renderer.render(
config,
&file_lang,
&file_content,
true,
repo_id_str,
&served_file.path,
);
} else {
// TODO something?
}
let template = crate::templates::Repo {
current_entry: served_entry.into(),
content: html.clone(),
root,
path: conn.path().split('/').map(|s| s.to_string()).collect(),
title: repo_metadata.title,
repo_hash: repo_id_str.into(),
};
template.render().unwrap()
}
templates::EntryRef::File(served_file) => {
let Some(file_content) =
fetch_file(config, repo_id_str, &served_file.hash)
else {
return conn.with_status(500);
};
let mut file_lang = served_file
.name
.rsplit('.')
.next()
.unwrap_or(giallo::PLAIN_GRAMMAR_NAME)
.to_string();
file_lang.make_ascii_lowercase();
let html = renderer.render(
config,
&file_lang,
&file_content,
true,
repo_id_str,
&served_file.path,
);
let template = crate::templates::Repo {
current_entry: served_entry.into(),
content: html.clone(),
root,
path: conn.path().split('/').map(|s| s.to_string()).collect(),
title: repo_metadata.title,
repo_hash: repo_id_str.into(),
};
template.render().unwrap()
}
};
page_cache.update(
(repo_id_str.to_string(), conn.path().to_string()),
Some(PageCacheEntry {
time: std::time::UNIX_EPOCH.elapsed().unwrap().as_secs(),
content: rendered_page.clone(),
}),
);
conn.ok(rendered_page)
}
}
}),
)
}