Download
This commit is contained in:
parent
dc13ef503c
commit
5bcc40d573
5 changed files with 105 additions and 12 deletions
10
README.md
10
README.md
|
|
@ -12,7 +12,9 @@ Early development: not usable yet.
|
|||
* [x] Repo caching
|
||||
* [x] Submission form
|
||||
* [x] Config
|
||||
* [ ] Repo serving
|
||||
* [x] Repo serving
|
||||
* [x] Allow download
|
||||
* [x] Markdown rendering
|
||||
* [ ] Replace words
|
||||
* [ ] Abuse report
|
||||
* [ ] Admin tools
|
||||
|
|
@ -21,9 +23,11 @@ Early development: not usable yet.
|
|||
* [ ] Expiration
|
||||
* [ ] Manual removal
|
||||
* [ ] Security tests (zip bomb)
|
||||
* [ ] Allow download
|
||||
* [ ] Markdown rendering
|
||||
* [ ] Security tests (URL encoding in links, HTML, HTTP headers)
|
||||
* [ ] Error pages
|
||||
* [ ] Webdesign
|
||||
* [ ] Compatibility with other forges
|
||||
* [ ] Better logo (contribution welcome)
|
||||
|
||||
## Design choices
|
||||
|
||||
|
|
|
|||
|
|
@ -12,13 +12,23 @@ impl Renderer {
|
|||
hl_registry.link_grammars();
|
||||
Self { hl_registry }
|
||||
}
|
||||
pub fn render(&self, config: &Config, filetype: &str, content: &[u8], pretty: bool, repo_hash_str: &str, file_path: &str) -> String {
|
||||
pub fn render(
|
||||
&self,
|
||||
config: &Config,
|
||||
filetype: &str,
|
||||
content: &[u8],
|
||||
pretty: bool,
|
||||
repo_hash_str: &str,
|
||||
file_path: &str,
|
||||
) -> String {
|
||||
if config.image_extensions.contains_key(filetype) {
|
||||
return format!(r#"<img alt="No description" src="/inline/{repo_hash_str}/{file_path}"/>"#)
|
||||
return format!(
|
||||
r#"<img alt="No description" src="/inline/{repo_hash_str}/{file_path}"/>"#
|
||||
);
|
||||
} else if config.video_extensions.contains_key(filetype) {
|
||||
return format!(r#"<video src="/inline/{repo_hash_str}/{file_path}"></video>"#)
|
||||
return format!(r#"<video src="/inline/{repo_hash_str}/{file_path}"></video>"#);
|
||||
} else if config.audio_extensions.contains_key(filetype) {
|
||||
return format!(r#"<audio src="/inline/{repo_hash_str}/{file_path}"></audio>"#)
|
||||
return format!(r#"<audio src="/inline/{repo_hash_str}/{file_path}"></audio>"#);
|
||||
}
|
||||
let Ok(content) = str::from_utf8(content) else {
|
||||
return String::from("Cannot render file as it is not valid UTF-8.");
|
||||
|
|
|
|||
|
|
@ -161,16 +161,25 @@ pub fn make_router(config: &'static Config) -> impl Handler {
|
|||
.to_string();
|
||||
file_lang.make_ascii_lowercase();
|
||||
|
||||
html = renderer.render(config, &file_lang, &file_content, true, repo_hash_str, &served_file.path);
|
||||
html = renderer.render(
|
||||
config,
|
||||
&file_lang,
|
||||
&file_content,
|
||||
true,
|
||||
repo_hash_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_hash_str.into(),
|
||||
};
|
||||
template.render().unwrap()
|
||||
}
|
||||
|
|
@ -189,12 +198,21 @@ pub fn make_router(config: &'static Config) -> impl Handler {
|
|||
.to_string();
|
||||
file_lang.make_ascii_lowercase();
|
||||
|
||||
let html = renderer.render(config, &file_lang, &file_content, true, repo_hash_str, &served_file.path);
|
||||
let html = renderer.render(
|
||||
config,
|
||||
&file_lang,
|
||||
&file_content,
|
||||
true,
|
||||
repo_hash_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_hash_str.into(),
|
||||
};
|
||||
template.render().unwrap()
|
||||
}
|
||||
|
|
@ -228,9 +246,7 @@ pub fn make_router(config: &'static Config) -> impl Handler {
|
|||
};
|
||||
|
||||
match served_entry {
|
||||
templates::EntryRef::Directory(_) => {
|
||||
conn.with_status(404)
|
||||
}
|
||||
templates::EntryRef::Directory(_) => conn.with_status(404),
|
||||
templates::EntryRef::File(served_file) => {
|
||||
let Some(extension) = served_file.name.rsplit('.').next() else {
|
||||
return conn.with_status(404);
|
||||
|
|
@ -255,6 +271,51 @@ pub fn make_router(config: &'static Config) -> impl Handler {
|
|||
}
|
||||
}
|
||||
})
|
||||
.get("/download/:hash/*", move |conn: Conn| async move {
|
||||
let Some(repo_hash_str) = conn.param("hash") else {
|
||||
return conn.with_status(401);
|
||||
};
|
||||
|
||||
let Some((_repo_metadata, root)) = metadata_cache
|
||||
.fetch(repo_hash_str.to_string(), |key| {
|
||||
fetch_metadata(config, &key)
|
||||
})
|
||||
else {
|
||||
return conn.with_status(404);
|
||||
};
|
||||
|
||||
let Some(served_entry) = root.find(conn.path().split('/')) else {
|
||||
return conn.with_status(404);
|
||||
};
|
||||
|
||||
match served_entry {
|
||||
templates::EntryRef::Directory(_) => conn.with_status(404),
|
||||
templates::EntryRef::File(served_file) => {
|
||||
let Some(file_content) =
|
||||
fetch_file(config, repo_hash_str, &served_file.hash)
|
||||
else {
|
||||
return conn.with_status(500);
|
||||
};
|
||||
let disposition = if served_file.name.is_ascii() {
|
||||
format!(
|
||||
r#"attachment; filename="{}""#,
|
||||
askama::filters::urlencode(&served_file.name).unwrap().0
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"attachment; filename*=UTF-8''{}",
|
||||
askama::filters::urlencode(&served_file.name).unwrap().0
|
||||
)
|
||||
};
|
||||
conn.with_response_header(
|
||||
"Access-Control-Allow-Origin",
|
||||
config.origin.as_str(),
|
||||
)
|
||||
.with_response_header("Content-Disposition", disposition)
|
||||
.ok(file_content)
|
||||
}
|
||||
}
|
||||
})
|
||||
.get("/e/:secret", |conn: Conn| async move {
|
||||
conn.ok(crate::templates::Home {}.render().unwrap())
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -19,6 +19,10 @@ pub struct Repo {
|
|||
pub path: Vec<String>,
|
||||
/// Repository title
|
||||
pub title: String,
|
||||
/// Repository id
|
||||
pub repo_hash: String,
|
||||
/// Served entry
|
||||
pub current_entry: Entry,
|
||||
}
|
||||
|
||||
#[derive(Clone, Template)]
|
||||
|
|
@ -53,6 +57,15 @@ pub enum EntryRef<'a> {
|
|||
File(&'a File),
|
||||
}
|
||||
|
||||
impl<'a> From<EntryRef<'a>> for Entry {
|
||||
fn from(value: EntryRef<'a>) -> Self {
|
||||
match value {
|
||||
EntryRef::Directory(dir) => Entry::Directory(dir.clone()),
|
||||
EntryRef::File(file) => Entry::File(file.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct File {
|
||||
pub name: String,
|
||||
|
|
|
|||
|
|
@ -39,6 +39,11 @@ html, body {
|
|||
<span>
|
||||
{% for element in path %}/{{ element }}{% endfor %}
|
||||
</span>
|
||||
{% match current_entry %}
|
||||
{% when Entry::Directory(dir) %}
|
||||
{% when Entry::File(file) %}
|
||||
<a href="/download/{{ repo_hash|safe }}/{{ file.path }}">Download file</a>
|
||||
{% endmatch %}
|
||||
<div id="content">
|
||||
{{ content|safe }}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue