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.
|
//! Client for downloading repositories from a forge's API.
|
||||||
|
|
||||||
use std::{collections::HashSet, io::Write, path::PathBuf};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::Config,
|
config::Config,
|
||||||
repo::{RepoMetadata, WriteRepoMetadataError},
|
repo::{RepoMetadata, WriteRepoMetadataError},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use std::{collections::HashSet, io::Write, path::PathBuf};
|
||||||
use rand::RngExt;
|
use rand::RngExt;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use sha2::Digest;
|
use sha2::Digest;
|
||||||
|
|
|
||||||
140
src/server.rs
140
src/server.rs
|
|
@ -11,6 +11,7 @@ use std::{
|
||||||
};
|
};
|
||||||
use trillium::{Conn, Handler};
|
use trillium::{Conn, Handler};
|
||||||
use trillium_router::{Router, RouterConnExt};
|
use trillium_router::{Router, RouterConnExt};
|
||||||
|
use sha2::Digest;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct PageCacheEntry {
|
struct PageCacheEntry {
|
||||||
|
|
@ -317,6 +318,145 @@ pub fn make_router(config: &'static Config) -> impl Handler {
|
||||||
.ok(file_content)
|
.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