Edit wip
This commit is contained in:
parent
d97184cd51
commit
4f58cea6ad
2 changed files with 141 additions and 2 deletions
|
|
@ -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;
|
||||
|
|
|
|||
140
src/server.rs
140
src/server.rs
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue