Add prettyness.

This commit is contained in:
Dan Ponte 2023-02-28 23:52:38 -05:00
parent 9ead53097e
commit 3789caf68f
7 changed files with 70 additions and 19 deletions

7
Cargo.lock generated
View File

@ -2385,6 +2385,7 @@ dependencies = [
"shuttle-actix-web", "shuttle-actix-web",
"shuttle-runtime", "shuttle-runtime",
"shuttle-static-folder", "shuttle-static-folder",
"text-template",
"tokio", "tokio",
"url", "url",
] ]
@ -2778,6 +2779,12 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "text-template"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51bcc3e9514cba67c087126b18600010c7c29b0cb51a6f6bc75e5058c2369bba"
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.40" version = "1.0.40"

View File

@ -52,6 +52,7 @@ shuttle-actix-web = { version = "0.16.0", optional = true }
shuttle-runtime = { version = "0.16.0", optional = true } shuttle-runtime = { version = "0.16.0", optional = true }
shuttle-static-folder = { version = "0.16.0", optional = true } shuttle-static-folder = { version = "0.16.0", optional = true }
tokio = { version = "1.28.1", optional = true } tokio = { version = "1.28.1", optional = true }
text-template = "0.1.0"
[dependencies.config] [dependencies.config]
version = "0.13.3" version = "0.13.3"
@ -72,6 +73,7 @@ actix-rt = "2.8.0"
[profile.dev] [profile.dev]
opt-level = 0 opt-level = 0
debug = true debug = true
panic = "abort"
[profile.test] [profile.test]
opt-level = 0 opt-level = 0

View File

@ -8,7 +8,9 @@
[![Docker Builds](https://img.shields.io/github/actions/workflow/status/orhun/rustypaste/docker.yml?style=flat&labelColor=823213&color=2c2c2c&label=docker&logo=Docker&logoColor=white)](https://hub.docker.com/r/orhunp/rustypaste) [![Docker Builds](https://img.shields.io/github/actions/workflow/status/orhun/rustypaste/docker.yml?style=flat&labelColor=823213&color=2c2c2c&label=docker&logo=Docker&logoColor=white)](https://hub.docker.com/r/orhunp/rustypaste)
[![Documentation](https://img.shields.io/docsrs/rustypaste?style=flat&labelColor=823213&color=2c2c2c&logo=Rust&logoColor=white)](https://docs.rs/rustypaste/) [![Documentation](https://img.shields.io/docsrs/rustypaste?style=flat&labelColor=823213&color=2c2c2c&logo=Rust&logoColor=white)](https://docs.rs/rustypaste/)
**Rustypaste** is a minimal file upload/pastebin service. **Rustypaste-pretty** is a minimal file upload/pastebin service with client side highlighting provided by highlight.js.
Just add `?pretty` to the end of a link to highlight!
```sh ```sh
$ echo "some text" > awesome.txt $ echo "some text" > awesome.txt

View File

@ -2,34 +2,18 @@
refresh_rate = "1s" refresh_rate = "1s"
[server] [server]
address = "127.0.0.1:8000" address="127.0.0.1:8020"
#url = "https://rustypaste.shuttleapp.rs" #url = "https://rustypaste.shuttleapp.rs"
#workers=4 #workers=4
max_content_length = "10MB" max_content_length = "10MB"
upload_path = "./upload" upload_path = "./upload"
timeout = "30s" timeout = "30s"
expose_version = false expose_version = false
style="monokai"
landing_page = """ landing_page = """
Submit files via HTTP POST here:
curl -F 'file=@example.txt' <server>
This will return the URL of the uploaded file.
The server administrator might remove any pastes that they do not personally
want to host.
If you are the server administrator and want to change this page, just go
into your config file and change it! If you change the expiry time, it is
recommended that you do.
By default, pastes expire every hour. The server admin may or may not have
changed this.
Check out the GitHub repository at https://github.com/orhun/rustypaste
Command line tool is available at https://github.com/orhun/rustypaste-cli
""" """
[paste] [paste]
@ -50,6 +34,11 @@ mime_blacklist = [
"application/java-archive", "application/java-archive",
"application/java-vm", "application/java-vm",
] ]
duplicate_files = true duplicate_files = true
# default_expiry = "1h" # default_expiry = "1h"
delete_expired_files = { enabled = true, interval = "1h" } delete_expired_files = { enabled = true, interval = "1h" }
[paste.highlight_override]
# For example, to force markdown rather than using highlight.js's autodetection
#"text/markdown" = "markdown"

View File

@ -2,6 +2,7 @@ use crate::mime::MimeMatcher;
use crate::random::RandomURLConfig; use crate::random::RandomURLConfig;
use byte_unit::Byte; use byte_unit::Byte;
use config::{self, ConfigError}; use config::{self, ConfigError};
use std::collections::HashMap;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::time::Duration; use std::time::Duration;
@ -47,6 +48,8 @@ pub struct ServerConfig {
pub landing_page: Option<String>, pub landing_page: Option<String>,
/// Expose version. /// Expose version.
pub expose_version: Option<bool>, pub expose_version: Option<bool>,
/// Highlight.js style
pub style: Option<String>,
} }
/// Paste configuration. /// Paste configuration.
@ -69,6 +72,9 @@ pub struct PasteConfig {
pub default_expiry: Option<Duration>, pub default_expiry: Option<Duration>,
/// Delete expired files. /// Delete expired files.
pub delete_expired_files: Option<CleanupConfig>, pub delete_expired_files: Option<CleanupConfig>,
/// Highlight override.
#[serde(default)]
pub highlight_override: HashMap<String, String>,
} }
/// Cleanup configuration. /// Cleanup configuration.
@ -85,6 +91,7 @@ impl Config {
/// Parses the config file and returns the values. /// Parses the config file and returns the values.
pub fn parse(path: &Path) -> Result<Config, ConfigError> { pub fn parse(path: &Path) -> Result<Config, ConfigError> {
config::Config::builder() config::Config::builder()
.set_default("style", "default").unwrap()
.add_source(config::File::from(path)) .add_source(config::File::from(path))
.add_source(config::Environment::default().separator("__")) .add_source(config::Environment::default().separator("__"))
.build()? .build()?

23
src/pretty.html Normal file
View File

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<title>${file}</title>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/${style}.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
<script>
var xhr= new XMLHttpRequest();
xhr.open('GET', '${file}', true);
xhr.onreadystatechange= function() {
if (this.readyState!==4) return;
if (this.status!==200) return; // or whatever error handling you want
document.getElementById('h').innerHTML= this.responseText;
hljs.highlightAll();
};
xhr.send();
</script>
</head>
<body>
<pre><code id="h" class="${type}">
</code></pre>
</body>
</html>

View File

@ -6,6 +6,7 @@ use crate::mime as mime_util;
use crate::paste::{Paste, PasteType}; use crate::paste::{Paste, PasteType};
use crate::util; use crate::util;
use crate::AUTH_TOKEN_ENV; use crate::AUTH_TOKEN_ENV;
use text_template::*;
use actix_files::NamedFile; use actix_files::NamedFile;
use actix_multipart::Multipart; use actix_multipart::Multipart;
use actix_web::{error, get, post, web, Error, HttpRequest, HttpResponse}; use actix_web::{error, get, post, web, Error, HttpRequest, HttpResponse};
@ -16,7 +17,9 @@ use serde::Deserialize;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::env; use std::env;
use std::fs; use std::fs;
use std::str;
use std::sync::RwLock; use std::sync::RwLock;
use std::collections::HashMap;
/// Shows the landing page. /// Shows the landing page.
#[get("/")] #[get("/")]
@ -53,6 +56,7 @@ async fn serve(
let config = config let config = config
.read() .read()
.map_err(|_| error::ErrorInternalServerError("cannot acquire config"))?; .map_err(|_| error::ErrorInternalServerError("cannot acquire config"))?;
let path = config.server.upload_path.join(&*file); let path = config.server.upload_path.join(&*file);
let mut path = util::glob_match_file(path)?; let mut path = util::glob_match_file(path)?;
let mut paste_type = PasteType::File; let mut paste_type = PasteType::File;
@ -80,6 +84,23 @@ async fn serve(
mime_util::get_mime_type(&config.paste.mime_override, file.to_string()) mime_util::get_mime_type(&config.paste.mime_override, file.to_string())
.map_err(error::ErrorInternalServerError)? .map_err(error::ErrorInternalServerError)?
}; };
if request.query_string() == "pretty" {
let mut values = HashMap::new();
let tmpl_bytes = str::from_utf8(include_bytes!("pretty.html")).unwrap();
let tmpl = Template::from(tmpl_bytes);
values.insert("file", file.as_str());
values.insert("style", match &config.server.style {
Some(style) => style.as_str(),
None => "default",
});
let mime_str = mime_type.to_string();
let overrides = &config.paste.highlight_override;
values.insert("type", if overrides.contains_key(&mime_str) { overrides[&mime_str].as_str() } else { "" });
let rendered = tmpl.fill_in(&values);
return Ok(HttpResponse::Ok().content_type(mime::TEXT_HTML).body(rendered.to_string()))
}
let response = NamedFile::open(&path)? let response = NamedFile::open(&path)?
.disable_content_disposition() .disable_content_disposition()
.set_content_type(mime_type) .set_content_type(mime_type)