210 lines
5.8 KiB
Rust
210 lines
5.8 KiB
Rust
use crate::{cache, config::Config, repo::ReadRepoMetadataError, templates};
|
|
|
|
use askama::Template;
|
|
use log::error;
|
|
use std::{
|
|
io::{ErrorKind, Read},
|
|
path::PathBuf,
|
|
};
|
|
use trillium::{Conn, Handler};
|
|
use trillium_router::{Router, RouterConnExt};
|
|
|
|
pub fn make_router(config: &'static Config) -> impl Handler {
|
|
let mut hl_registry = giallo::Registry::builtin().unwrap();
|
|
hl_registry.link_grammars();
|
|
let hl_registry: &'static _ = Box::leak(Box::new(hl_registry));
|
|
|
|
let metadata_cache: &'static _ = Box::leak(Box::new(cache::Cache::<
|
|
String,
|
|
templates::Directory,
|
|
>::default()));
|
|
let file_cache: &'static _ =
|
|
Box::leak(Box::new(cache::Cache::<(String, String), String>::default()));
|
|
let client: &'static _ = Box::leak(Box::new(async_lock::Mutex::new(
|
|
crate::api_client::make_client(),
|
|
)));
|
|
|
|
(
|
|
trillium_caching_headers::CachingHeaders::new(),
|
|
//trillium_static_compiled::static_compiled!("./static").with_index_file("index.html"),
|
|
Router::new()
|
|
.get("/", |conn: Conn| async move {
|
|
conn.ok(crate::templates::Home {}.render().unwrap())
|
|
})
|
|
.post("/fetch", move |mut conn: Conn| async move {
|
|
if let Ok(request_body) = conn.request_body().with_max_len(8192).read_bytes().await
|
|
{
|
|
let mut repo_url = None;
|
|
let mut commit_hash = None;
|
|
for (key, val) in form_urlencoded::parse(&request_body) {
|
|
match key.as_ref() {
|
|
"repo-url" => {
|
|
repo_url = Some(val);
|
|
}
|
|
"commit" => {
|
|
commit_hash = Some(val);
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
let (Some(repo_url), Some(commit_hash)) = (repo_url, commit_hash) else {
|
|
return conn.ok("Missing arg");
|
|
};
|
|
|
|
let mut client = client.lock().await;
|
|
let (repo_index, mut repo_metadata) =
|
|
crate::api_client::fetch_repo_tree_index_at_commit(
|
|
&mut client,
|
|
&repo_url,
|
|
&commit_hash,
|
|
None,
|
|
)
|
|
.await
|
|
.expect("todo handle error");
|
|
crate::api_client::fetch_repo_files(
|
|
config,
|
|
&mut client,
|
|
&repo_index,
|
|
&mut repo_metadata,
|
|
None,
|
|
)
|
|
.await
|
|
.expect("todo handle error");
|
|
}
|
|
//let planet = conn.param("planet").unwrap();
|
|
conn.ok(crate::templates::Home {}.render().unwrap())
|
|
})
|
|
.get("/r/:hash/*", move |conn: Conn| {
|
|
async move {
|
|
let Some(repo_hash_str) = conn.param("hash") else {
|
|
return conn.with_status(401);
|
|
};
|
|
|
|
let fetch_metadata = |key| {
|
|
//let mut repo_hash = [0; 24];
|
|
//if dbg!(base64_turbo::URL_SAFE.decode_into(key, &mut repo_hash))
|
|
// != Ok(16)
|
|
//{
|
|
// return None;
|
|
//}
|
|
//let repo_hash = &repo_hash[0..16];
|
|
let repo_dir = PathBuf::from(&config.data_dir)
|
|
.join(crate::SUBDIR_REPOS)
|
|
.join(key);
|
|
let repo_metadata =
|
|
match crate::repo::RepoMetadata::read_from_file(config, &repo_dir) {
|
|
Ok(v) => v,
|
|
Err(e) => {
|
|
if let ReadRepoMetadataError::CannotOpenFile(e) = &e {
|
|
if e.kind() == ErrorKind::NotFound {
|
|
return None;
|
|
}
|
|
}
|
|
error!("Reading repo metadata: {e:?}");
|
|
return None;
|
|
}
|
|
};
|
|
let mut root = templates::Directory::default();
|
|
for entry in repo_metadata.iter_files() {
|
|
match entry {
|
|
Ok(entry) => {
|
|
let Some(name) = entry.file_path.rsplit('/').next() else {
|
|
error!("Entry has no name");
|
|
continue;
|
|
};
|
|
let path = entry.file_path.split('/').peekable();
|
|
|
|
root.insert(
|
|
templates::Entry::File(templates::File {
|
|
name: name.into(),
|
|
path: dbg!(entry.file_path).into(),
|
|
hash: entry.hash.into(),
|
|
}),
|
|
path,
|
|
);
|
|
}
|
|
Err(e) => {
|
|
error!("Reading repo metadata file index: {e:?}")
|
|
}
|
|
}
|
|
}
|
|
Some(root)
|
|
};
|
|
let Some(root) =
|
|
metadata_cache.fetch(repo_hash_str.to_string(), fetch_metadata)
|
|
else {
|
|
return conn.with_status(404);
|
|
};
|
|
|
|
let Some(repo_file) = root.find(conn.path().split('/')) else {
|
|
return conn.with_status(404);
|
|
};
|
|
|
|
let fetch_file = |(repo_hash, file_hash)| {
|
|
let file_path = PathBuf::from(&config.data_dir)
|
|
.join(crate::SUBDIR_REPOS)
|
|
.join(repo_hash)
|
|
.join(file_hash);
|
|
|
|
let mut file = match std::fs::OpenOptions::new().read(true).open(&file_path)
|
|
{
|
|
Ok(file) => file,
|
|
Err(e) => {
|
|
error!("Cannot open file `{file_path:?}`: {e:?}");
|
|
return None;
|
|
}
|
|
};
|
|
|
|
let mut buf = String::new();
|
|
if let Err(e) = file.read_to_string(&mut buf) {
|
|
error!("Error reading file `{file_path:?}`: {e:?}");
|
|
return None;
|
|
}
|
|
|
|
Some(buf)
|
|
};
|
|
let Some(file_content) = file_cache.fetch(
|
|
(repo_hash_str.to_string(), repo_file.hash.clone()),
|
|
fetch_file,
|
|
) else {
|
|
return conn.with_status(500);
|
|
};
|
|
|
|
let file_lang = conn
|
|
.path()
|
|
.rsplit('.')
|
|
.next()
|
|
.unwrap_or(giallo::PLAIN_GRAMMAR_NAME);
|
|
|
|
let hl_options = giallo::HighlightOptions::new(
|
|
file_lang,
|
|
giallo::ThemeVariant::Single("catppuccin-frappe"),
|
|
);
|
|
let highlighted = hl_registry.highlight(&file_content, &hl_options).unwrap_or_else(|_e| {
|
|
// If extension is unknown
|
|
let hl_options = giallo::HighlightOptions::new(
|
|
giallo::PLAIN_GRAMMAR_NAME,
|
|
giallo::ThemeVariant::Single("catppuccin-frappe"),
|
|
);
|
|
hl_registry.highlight(&file_content, &hl_options).unwrap()
|
|
});
|
|
let html = giallo::HtmlRenderer::default().render(
|
|
&highlighted,
|
|
&giallo::RenderOptions {
|
|
show_line_numbers: true,
|
|
..Default::default()
|
|
},
|
|
);
|
|
conn.ok(crate::templates::Repo {
|
|
content: html.clone(),
|
|
root,
|
|
}
|
|
.render()
|
|
.unwrap())
|
|
}
|
|
})
|
|
.get("/e/:secret", |conn: Conn| async move {
|
|
conn.ok(crate::templates::Home {}.render().unwrap())
|
|
}),
|
|
)
|
|
}
|