mirror of https://github.com/raftario/filite.git
Backend support for code highlighting
This commit is contained in:
parent
768082f180
commit
2fbd134743
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE texts
|
||||||
|
DROP COLUMN highlight;
|
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE texts
|
||||||
|
ADD highlight BOOLEAN NOT NULL DEFAULT false;
|
|
@ -0,0 +1,47 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>{{ title }}</title>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.10/styles/{{ theme }}.min.css"
|
||||||
|
/>
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
pre {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo,
|
||||||
|
monospace;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<pre><code>{{ contents }}</code></pre>
|
||||||
|
<script
|
||||||
|
src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.10/highlight.min.js"
|
||||||
|
integrity="sha256-1zu+3BnLYV9LdiY85uXMzii3bdrkelyp37e0ZyTAQh0="
|
||||||
|
crossorigin="anonymous"
|
||||||
|
></script>
|
||||||
|
{{ languages }}
|
||||||
|
<script>
|
||||||
|
hljs.initHighlightingOnLoad();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -231,7 +231,7 @@ for (const group in inputs) {
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({ contents }),
|
body: JSON.stringify({ contents, highlight: true }),
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
status = response.status;
|
status = response.status;
|
||||||
|
|
|
@ -65,6 +65,8 @@ pub mod texts {
|
||||||
pub contents: String,
|
pub contents: String,
|
||||||
/// Creation date and time as a UNIX timestamp
|
/// Creation date and time as a UNIX timestamp
|
||||||
pub created: i32,
|
pub created: i32,
|
||||||
|
/// Whether to enable code highlighting or not for that text
|
||||||
|
pub highlight: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A new entry to the `texts` table
|
/// A new entry to the `texts` table
|
||||||
|
@ -75,5 +77,7 @@ pub mod texts {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
/// Text contents
|
/// Text contents
|
||||||
pub contents: &'a str,
|
pub contents: &'a str,
|
||||||
|
/// Whether to enable code highlighting or not for that text
|
||||||
|
pub highlight: bool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -175,11 +175,17 @@ pub mod texts {
|
||||||
find!(texts, Text);
|
find!(texts, Text);
|
||||||
|
|
||||||
/// REPLACE a text entry
|
/// REPLACE a text entry
|
||||||
pub fn replace(r_id: i32, r_contents: &str, pool: Data<Pool>) -> QueryResult<Text> {
|
pub fn replace(
|
||||||
|
r_id: i32,
|
||||||
|
r_contents: &str,
|
||||||
|
r_highlight: bool,
|
||||||
|
pool: Data<Pool>,
|
||||||
|
) -> QueryResult<Text> {
|
||||||
let conn: &SqliteConnection = &pool.get().unwrap();
|
let conn: &SqliteConnection = &pool.get().unwrap();
|
||||||
let new_text = NewText {
|
let new_text = NewText {
|
||||||
id: r_id,
|
id: r_id,
|
||||||
contents: r_contents,
|
contents: r_contents,
|
||||||
|
highlight: r_highlight,
|
||||||
};
|
};
|
||||||
diesel::replace_into(table)
|
diesel::replace_into(table)
|
||||||
.values(&new_text)
|
.values(&new_text)
|
||||||
|
|
|
@ -116,6 +116,13 @@ fn timestamp_to_last_modified(timestamp: i32) -> String {
|
||||||
datetime.format("%a, %d %b %Y %H:%M:%S GMT").to_string()
|
datetime.format("%a, %d %b %Y %H:%M:%S GMT").to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Escapes text to be inserted in a HTML element
|
||||||
|
fn escape_html(text: &str) -> String {
|
||||||
|
text.replace("&", "&")
|
||||||
|
.replace("<", "<")
|
||||||
|
.replace(">", ">")
|
||||||
|
}
|
||||||
|
|
||||||
/// GET multiple entries
|
/// GET multiple entries
|
||||||
macro_rules! select {
|
macro_rules! select {
|
||||||
($m:ident) => {
|
($m:ident) => {
|
||||||
|
@ -196,6 +203,9 @@ lazy_static! {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static HIGHLIGHT_CONTENTS: &str = include_str!("../resources/highlight.html");
|
||||||
|
const HIGHLIGHT_LANGUAGE: &str = r#"<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.10/languages/{{ language }}.min.js"></script>"#;
|
||||||
|
|
||||||
/// Index page letting users upload via a UI
|
/// Index page letting users upload via a UI
|
||||||
pub async fn index(
|
pub async fn index(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
|
@ -406,6 +416,7 @@ pub mod links {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod texts {
|
pub mod texts {
|
||||||
|
use crate::routes::escape_html;
|
||||||
use crate::{
|
use crate::{
|
||||||
queries::{self, SelectQuery},
|
queries::{self, SelectQuery},
|
||||||
routes::{
|
routes::{
|
||||||
|
@ -413,6 +424,10 @@ pub mod texts {
|
||||||
},
|
},
|
||||||
Pool,
|
Pool,
|
||||||
};
|
};
|
||||||
|
use crate::{
|
||||||
|
routes::{HIGHLIGHT_CONTENTS, HIGHLIGHT_LANGUAGE},
|
||||||
|
setup::Config,
|
||||||
|
};
|
||||||
use actix_identity::Identity;
|
use actix_identity::Identity;
|
||||||
use actix_web::{web, Error, HttpRequest, HttpResponse};
|
use actix_web::{web, Error, HttpRequest, HttpResponse};
|
||||||
|
|
||||||
|
@ -420,14 +435,38 @@ pub mod texts {
|
||||||
|
|
||||||
/// GET a text entry and display it
|
/// GET a text entry and display it
|
||||||
pub async fn get(
|
pub async fn get(
|
||||||
|
config: web::Data<Config>,
|
||||||
path: web::Path<String>,
|
path: web::Path<String>,
|
||||||
pool: web::Data<Pool>,
|
pool: web::Data<Pool>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let id = parse_id(&path)?;
|
let id = parse_id(&path)?;
|
||||||
match web::block(move || queries::texts::find(id, pool)).await {
|
match web::block(move || queries::texts::find(id, pool)).await {
|
||||||
Ok(text) => Ok(HttpResponse::Ok()
|
Ok(text) => {
|
||||||
.header("Last-Modified", timestamp_to_last_modified(text.created))
|
let last_modified = timestamp_to_last_modified(text.created);
|
||||||
.body(text.contents)),
|
if text.highlight {
|
||||||
|
let languages: Vec<String> = config
|
||||||
|
.highlight
|
||||||
|
.languages
|
||||||
|
.iter()
|
||||||
|
.map(|l| HIGHLIGHT_LANGUAGE.replace("{{ language }}", l))
|
||||||
|
.collect();
|
||||||
|
let languages = languages.join("\n");
|
||||||
|
let contents = HIGHLIGHT_CONTENTS
|
||||||
|
.replace("{{ title }}", &path)
|
||||||
|
.replace("{{ theme }}", &config.highlight.theme)
|
||||||
|
.replace("{{ contents }}", &escape_html(&text.contents))
|
||||||
|
.replace("{{ languages }}", &languages);
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok()
|
||||||
|
.header("Last-Modified", last_modified)
|
||||||
|
.header("Content-Type", "text/html")
|
||||||
|
.body(contents))
|
||||||
|
} else {
|
||||||
|
Ok(HttpResponse::Ok()
|
||||||
|
.header("Last-Modified", last_modified)
|
||||||
|
.body(text.contents))
|
||||||
|
}
|
||||||
|
}
|
||||||
Err(e) => match_find_error(e),
|
Err(e) => match_find_error(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -436,6 +475,7 @@ pub mod texts {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct PutText {
|
pub struct PutText {
|
||||||
pub contents: String,
|
pub contents: String,
|
||||||
|
pub highlight: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PUT a new text entry
|
/// PUT a new text entry
|
||||||
|
@ -451,7 +491,8 @@ pub mod texts {
|
||||||
|
|
||||||
let id = parse_id(&path)?;
|
let id = parse_id(&path)?;
|
||||||
match_replace_result(
|
match_replace_result(
|
||||||
web::block(move || queries::texts::replace(id, &body.contents, pool)).await,
|
web::block(move || queries::texts::replace(id, &body.contents, body.highlight, pool))
|
||||||
|
.await,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ table! {
|
||||||
id -> Integer,
|
id -> Integer,
|
||||||
contents -> Text,
|
contents -> Text,
|
||||||
created -> Integer,
|
created -> Integer,
|
||||||
|
highlight -> Bool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
26
src/setup.rs
26
src/setup.rs
|
@ -88,6 +88,17 @@ pub struct Config {
|
||||||
pub files_dir: PathBuf,
|
pub files_dir: PathBuf,
|
||||||
/// Maximum allowed file size
|
/// Maximum allowed file size
|
||||||
pub max_filesize: usize,
|
pub max_filesize: usize,
|
||||||
|
/// Highlight.js configuration
|
||||||
|
pub highlight: HighlightConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
#[cfg_attr(not(feature = "dev"), serde(default))]
|
||||||
|
pub struct HighlightConfig {
|
||||||
|
/// Theme to use
|
||||||
|
pub theme: String,
|
||||||
|
/// Additional languages to include
|
||||||
|
pub languages: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "dev"))]
|
#[cfg(not(feature = "dev"))]
|
||||||
|
@ -104,12 +115,22 @@ impl Default for Config {
|
||||||
let files_dir = get_data_dir().join("files");
|
let files_dir = get_data_dir().join("files");
|
||||||
let max_filesize = 10_000_000;
|
let max_filesize = 10_000_000;
|
||||||
|
|
||||||
Config {
|
Self {
|
||||||
port,
|
port,
|
||||||
database_url,
|
database_url,
|
||||||
pool_size,
|
pool_size,
|
||||||
files_dir,
|
files_dir,
|
||||||
max_filesize,
|
max_filesize,
|
||||||
|
highlight: HighlightConfig::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for HighlightConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
theme: "github".to_owned(),
|
||||||
|
languages: vec!["rust".to_owned()],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -192,12 +213,13 @@ impl Config {
|
||||||
};
|
};
|
||||||
let max_filesize = parse_env!("MAX_FILESIZE");
|
let max_filesize = parse_env!("MAX_FILESIZE");
|
||||||
|
|
||||||
Config {
|
Self {
|
||||||
port,
|
port,
|
||||||
database_url,
|
database_url,
|
||||||
pool_size,
|
pool_size,
|
||||||
files_dir,
|
files_dir,
|
||||||
max_filesize,
|
max_filesize,
|
||||||
|
highlight: HighlightConfig::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue