commit
2598efc9ff
@ -0,0 +1,6 @@
|
|||||||
|
[*]
|
||||||
|
indent_style = tab
|
||||||
|
tab_width = 4
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
**/*.rs.bk
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,26 @@
|
|||||||
|
[package]
|
||||||
|
name = "pastebucket"
|
||||||
|
version = "0.7.0"
|
||||||
|
authors = ["rasul <rascul3@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
askama = "0.9"
|
||||||
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
|
colored = "1.9"
|
||||||
|
fern = { version = "0.5", features = ["colored"] }
|
||||||
|
futures = "0.1"
|
||||||
|
getopts = "0.2"
|
||||||
|
gotham = "0.4"
|
||||||
|
gotham_derive = "0.4"
|
||||||
|
harsh = "0.1.6"
|
||||||
|
hyper = "0.12"
|
||||||
|
lazy_static = "1.4"
|
||||||
|
log = "0.4"
|
||||||
|
mime = "0.3"
|
||||||
|
pulldown-cmark = "0.6"
|
||||||
|
serde = "1.0"
|
||||||
|
serde_derive = "1.0"
|
||||||
|
syntect = "3.3"
|
||||||
|
toml = "0.5"
|
||||||
|
url = "1.6"
|
@ -0,0 +1,5 @@
|
|||||||
|
work in progress
|
||||||
|
|
||||||
|
mostly works though but needs more works
|
||||||
|
|
||||||
|
https://p.rascul.xyz
|
@ -0,0 +1,5 @@
|
|||||||
|
max_width = 100
|
||||||
|
hard_tabs = true
|
||||||
|
newline_style = "unix"
|
||||||
|
use_try_shorthand = true
|
||||||
|
edition = "2018"
|
@ -0,0 +1,58 @@
|
|||||||
|
pub mod loglevel;
|
||||||
|
|
||||||
|
use std::default::Default;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use gotham_derive::StateData;
|
||||||
|
use serde_derive::Deserialize;
|
||||||
|
use toml;
|
||||||
|
|
||||||
|
pub use crate::config::loglevel::LogLevel;
|
||||||
|
use crate::result::Result;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, StateData)]
|
||||||
|
pub struct Config {
|
||||||
|
#[serde(default)]
|
||||||
|
pub address: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub url: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub log_level: LogLevel,
|
||||||
|
#[serde(default)]
|
||||||
|
pub log_file: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub data_directory: PathBuf,
|
||||||
|
#[serde(default)]
|
||||||
|
pub template_directory: PathBuf,
|
||||||
|
#[serde(default)]
|
||||||
|
pub static_directory: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
address: "127.0.0.1:9214".into(),
|
||||||
|
url: "http://127.0.0.1:9214".into(),
|
||||||
|
log_level: LogLevel::info,
|
||||||
|
log_file: None,
|
||||||
|
data_directory: PathBuf::from("data"),
|
||||||
|
template_directory: PathBuf::from("templates"),
|
||||||
|
static_directory: PathBuf::from("static"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn load<T: AsRef<Path>>(path: T) -> Result<Config> {
|
||||||
|
if let Ok(mut file) = File::open(path.as_ref()) {
|
||||||
|
let mut buf = String::new();
|
||||||
|
file.read_to_string(&mut buf)?;
|
||||||
|
let config: Config = toml::from_str(&buf)?;
|
||||||
|
Ok(config)
|
||||||
|
} else {
|
||||||
|
Ok(Config::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
use std::default::Default;
|
||||||
|
|
||||||
|
use log::LevelFilter;
|
||||||
|
use serde_derive::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
pub enum LogLevel {
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
error,
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
warn,
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
info,
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
debug,
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
trace,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for LogLevel {
|
||||||
|
fn default() -> LogLevel {
|
||||||
|
LogLevel::info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LogLevel {
|
||||||
|
pub fn level(&self) -> LevelFilter {
|
||||||
|
match &self {
|
||||||
|
LogLevel::error => LevelFilter::Error,
|
||||||
|
LogLevel::warn => LevelFilter::Warn,
|
||||||
|
LogLevel::info => LevelFilter::Info,
|
||||||
|
LogLevel::debug => LevelFilter::Debug,
|
||||||
|
LogLevel::trace => LevelFilter::Trace,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
use chrono::Local;
|
||||||
|
use colored::Colorize;
|
||||||
|
use fern::colors::{Color, ColoredLevelConfig};
|
||||||
|
use fern::Dispatch;
|
||||||
|
|
||||||
|
use crate::config::LogLevel;
|
||||||
|
use crate::result::Result;
|
||||||
|
|
||||||
|
pub fn setup(level: &LogLevel) -> Result<()> {
|
||||||
|
Dispatch::new()
|
||||||
|
.format(|out, message, record| {
|
||||||
|
let colors = ColoredLevelConfig::new()
|
||||||
|
.error(Color::Red)
|
||||||
|
.warn(Color::Magenta)
|
||||||
|
.info(Color::Cyan)
|
||||||
|
.debug(Color::Yellow)
|
||||||
|
.trace(Color::Green);
|
||||||
|
|
||||||
|
out.finish(format_args!(
|
||||||
|
"{} {} {}",
|
||||||
|
Local::now()
|
||||||
|
.format("%Y-%m-%dT%H:%M:%S%.3f%z")
|
||||||
|
.to_string()
|
||||||
|
.white()
|
||||||
|
.bold(),
|
||||||
|
colors.color(record.level()),
|
||||||
|
message
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.level(level.level())
|
||||||
|
.chain(std::io::stdout())
|
||||||
|
.apply()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
mod config;
|
||||||
|
mod logger;
|
||||||
|
mod paste;
|
||||||
|
mod result;
|
||||||
|
mod routes;
|
||||||
|
mod syntax;
|
||||||
|
|
||||||
|
use result::Result;
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
let config = config::Config::load("config.toml")?;
|
||||||
|
logger::setup(&config.log_level)?;
|
||||||
|
gotham::start(config.address.clone(), routes::build(config));
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{BufReader, BufWriter, Error, ErrorKind, Read, Write};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use harsh::HarshBuilder;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use toml;
|
||||||
|
|
||||||
|
use crate::result::Result;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct Paste {
|
||||||
|
pub id: String,
|
||||||
|
pub dt: DateTime<Utc>,
|
||||||
|
pub lang: String,
|
||||||
|
pub text: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Paste {
|
||||||
|
pub fn from_file(path: PathBuf) -> Result<Self> {
|
||||||
|
let file = File::open(path)?;
|
||||||
|
let mut reader = BufReader::new(file);
|
||||||
|
let mut buf = String::new();
|
||||||
|
reader.read_to_string(&mut buf)?;
|
||||||
|
|
||||||
|
let paste: Paste = toml::from_str(&buf)?;
|
||||||
|
|
||||||
|
Ok(paste)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_text(text: String) -> Result<Self> {
|
||||||
|
let dt = Utc::now();
|
||||||
|
let harsh = HarshBuilder::new().salt("salt and pepper").init()?;
|
||||||
|
let encoded = harsh.encode(&[dt.timestamp_millis() as u64]);
|
||||||
|
|
||||||
|
if let Some(id) = encoded {
|
||||||
|
Ok(Paste {
|
||||||
|
id,
|
||||||
|
dt,
|
||||||
|
text,
|
||||||
|
lang: "Plain Text".into(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(Box::new(Error::new(ErrorKind::Other, "couldn't generate hash")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn from_form(form: HashMap<String, String>) -> Result<Self> {
|
||||||
|
let dt = Utc::now();
|
||||||
|
let harsh = HarshBuilder::new().salt("salt and pepper").init()?;
|
||||||
|
let encoded = harsh.encode(&[dt.timestamp_millis() as u64]);
|
||||||
|
|
||||||
|
if let Some(id) = encoded {
|
||||||
|
let text: String = if let Some(t) = form.get("paste") {
|
||||||
|
t.into()
|
||||||
|
} else {
|
||||||
|
"".into()
|
||||||
|
};
|
||||||
|
|
||||||
|
let lang: String = if let Some(l) = form.get("lang") {
|
||||||
|
l.into()
|
||||||
|
} else {
|
||||||
|
"Plain Text".into()
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Paste { id, dt, lang, text })
|
||||||
|
} else {
|
||||||
|
Err(Box::new(Error::new(ErrorKind::Other, "empty hash")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_file(&self, path: PathBuf) -> Result<()> {
|
||||||
|
let file = File::create(path)?;
|
||||||
|
let mut writer = BufWriter::new(file);
|
||||||
|
writer.write_all(toml::to_string_pretty(&self)?.as_bytes())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
|
@ -0,0 +1,45 @@
|
|||||||
|
mod edit;
|
||||||
|
mod index;
|
||||||
|
mod raw;
|
||||||
|
mod submit;
|
||||||
|
mod view;
|
||||||
|
|
||||||
|
use gotham::handler::assets::FileOptions;
|
||||||
|
use gotham::middleware::logger::RequestLogger;
|
||||||
|
use gotham::middleware::state::StateMiddleware;
|
||||||
|
use gotham::pipeline::new_pipeline;
|
||||||
|
use gotham::pipeline::single::single_pipeline;
|
||||||
|
use gotham::router::builder::*;
|
||||||
|
use gotham::router::Router;
|
||||||
|
|
||||||
|
use crate::config::Config;
|
||||||
|
|
||||||
|
pub fn build(config: Config) -> Router {
|
||||||
|
let (chain, pipeline) = single_pipeline(
|
||||||
|
new_pipeline()
|
||||||
|
.add(RequestLogger::new(log::Level::Info))
|
||||||
|
.add(StateMiddleware::new(config.clone()))
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
build_router(chain, pipeline, |route| {
|
||||||
|
route.get("/").to(index::route);
|
||||||
|
route.post("/").to(submit::post);
|
||||||
|
route.put("/").to(submit::put);
|
||||||
|
route
|
||||||
|
.get("/s/*")
|
||||||
|
.to_dir(FileOptions::new(config.static_directory));
|
||||||
|
route
|
||||||
|
.get("/:id")
|
||||||
|
.with_path_extractor::<view::Params>()
|
||||||
|
.to(view::get);
|
||||||
|
route
|
||||||
|
.get("/:id/edit")
|
||||||
|
.with_path_extractor::<edit::Params>()
|
||||||
|
.to(edit::route);
|
||||||
|
route
|
||||||
|
.get("/:id/raw")
|
||||||
|
.with_path_extractor::<raw::Params>()
|
||||||
|
.to(raw::get);
|
||||||
|
})
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
use askama::Template;
|
||||||
|
use futures::future;
|
||||||
|
|
||||||
|
use gotham::handler::{HandlerFuture, IntoHandlerError};
|
||||||
|
use gotham::helpers::http::response::{create_empty_response, create_response};
|
||||||
|
use gotham::state::{FromState, State};
|
||||||
|
use gotham_derive::{StateData, StaticResponseExtender};
|
||||||
|
|
||||||
|
use hyper::StatusCode;
|
||||||
|
use mime;
|
||||||
|
use serde_derive::Deserialize;
|
||||||
|
|
||||||
|
use crate::config::Config;
|
||||||
|
use crate::paste::Paste;
|
||||||
|
use crate::syntax::SYNTAXES;
|
||||||
|
|
||||||
|
#[derive(Deserialize, StateData, StaticResponseExtender)]
|
||||||
|
pub struct Params {
|
||||||
|
id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Template)]
|
||||||
|
#[template(path = "edit.html")]
|
||||||
|
pub struct Edit {
|
||||||
|
id: String,
|
||||||
|
dt: String,
|
||||||
|
text: String,
|
||||||
|
site_url: String,
|
||||||
|
syntaxes: Vec<String>,
|
||||||
|
syntax: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn route(mut state: State) -> Box<HandlerFuture> {
|
||||||
|
Box::new({
|
||||||
|
let config = Config::take_from(&mut state);
|
||||||
|
let Params { id } = Params::take_from(&mut state);
|
||||||
|
|
||||||
|
let mut path = config.data_directory.clone();
|
||||||
|
path.push(id.clone());
|
||||||
|
|
||||||
|
match Paste::from_file(path) {
|
||||||
|
Ok(paste) => {
|
||||||
|
let template = Edit {
|
||||||
|
id,
|
||||||
|
dt: paste.dt.format("%Y-%m-%dT%H:%MZ").to_string(),
|
||||||
|
text: paste.text,
|
||||||
|
site_url: config.url,
|
||||||
|
syntaxes: SYNTAXES.to_vec(),
|
||||||
|
syntax: paste.lang,
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = match template.render() {
|
||||||
|
Ok(content) => create_response(
|
||||||
|
&state,
|
||||||
|
StatusCode::OK,
|
||||||
|
mime::TEXT_HTML_UTF_8,
|
||||||
|
content.into_bytes(),
|
||||||
|
),
|
||||||
|
Err(_) => create_empty_response(&state, StatusCode::INTERNAL_SERVER_ERROR),
|
||||||
|
};
|
||||||
|
|
||||||
|
future::ok((state, res))
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
let io_error = std::io::Error::new(std::io::ErrorKind::Other, e.description());
|
||||||
|
future::err((state, io_error.into_handler_error()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
use askama::Template;
|
||||||
|
use futures::future;
|
||||||
|
|
||||||
|
use gotham::handler::HandlerFuture;
|
||||||
|
use gotham::helpers::http::response::{create_empty_response, create_response};
|
||||||
|
use gotham::state::{FromState, State};
|
||||||
|
|
||||||
|
use hyper::StatusCode;
|
||||||
|
use mime;
|
||||||
|
|
||||||
|
use crate::config::Config;
|
||||||
|
use crate::syntax::SYNTAXES;
|
||||||
|
|
||||||
|
#[derive(Debug, Template)]
|
||||||
|
#[template(path = "index.html")]
|
||||||
|
pub struct Index {
|
||||||
|
syntaxes: Vec<String>,
|
||||||
|
site_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn route(mut state: State) -> Box<HandlerFuture> {
|
||||||
|
Box::new({
|
||||||
|
let config = Config::take_from(&mut state);
|
||||||
|
|
||||||
|
let template = Index {
|
||||||
|
syntaxes: SYNTAXES.to_vec(),
|
||||||
|
site_url: config.url,
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = match template.render() {
|
||||||
|
Ok(content) => create_response(
|
||||||
|
&state,
|
||||||
|
StatusCode::OK,
|
||||||
|
mime::TEXT_HTML_UTF_8,
|
||||||
|
content.into_bytes(),
|
||||||
|
),
|
||||||
|
Err(_) => create_empty_response(&state, StatusCode::INTERNAL_SERVER_ERROR),
|
||||||
|
};
|
||||||
|
|
||||||
|
future::ok((state, res))
|
||||||
|
})
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
use futures::future;
|
||||||
|
|
||||||
|
use gotham::handler::{HandlerFuture, IntoHandlerError};
|
||||||
|
use gotham::helpers::http::response::create_response;
|
||||||
|
use gotham::state::{FromState, State};
|
||||||
|
use gotham_derive::{StateData, StaticResponseExtender};
|
||||||
|
|
||||||
|
use hyper::StatusCode;
|
||||||
|
use mime;
|
||||||
|
use serde_derive::Deserialize;
|
||||||
|
|
||||||
|
use crate::config::Config;
|
||||||
|
use crate::paste::Paste;
|
||||||
|
|
||||||
|
#[derive(Deserialize, StateData, StaticResponseExtender)]
|
||||||
|
pub struct Params {
|
||||||
|
id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(mut state: State) -> Box<HandlerFuture> {
|
||||||
|
Box::new({
|
||||||
|
let config = Config::take_from(&mut state);
|
||||||
|
let Params { id } = Params::take_from(&mut state);
|
||||||
|
|
||||||
|
let mut path = config.data_directory;
|
||||||
|
path.push(id);
|
||||||
|
|
||||||
|
match Paste::from_file(path) {
|
||||||
|
Ok(paste) => {
|
||||||
|
let res = create_response(
|
||||||
|
&state,
|
||||||
|
StatusCode::OK,
|
||||||
|
mime::TEXT_PLAIN,
|
||||||
|
paste.text,
|
||||||
|
);
|
||||||
|
|
||||||
|
future::ok((state, res))
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
let io_error = std::io::Error::new(std::io::ErrorKind::Other, e.description());
|
||||||
|
future::err((state, io_error.into_handler_error()))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::io::{Error, ErrorKind};
|
||||||
|
|
||||||
|
use futures::{future, Future, Stream};
|
||||||
|
|
||||||
|
use gotham::handler::{HandlerFuture, IntoHandlerError};
|
||||||
|
use gotham::helpers::http::response::create_response;
|
||||||
|
use gotham::state::{FromState, State};
|
||||||
|
|
||||||
|
use hyper::Response;
|
||||||
|
use hyper::{Body, StatusCode};
|
||||||
|
use mime;
|
||||||
|
use url::form_urlencoded;
|
||||||
|
|
||||||
|
use crate::config::Config;
|
||||||
|
use crate::paste::Paste;
|
||||||
|
|
||||||
|
pub fn put(mut state: State) -> Box<HandlerFuture> {
|
||||||
|
Box::new({
|
||||||
|
Body::take_from(&mut state).concat2().then(|body| {
|
||||||
|
let config = Config::take_from(&mut state);
|
||||||
|
|
||||||
|
match body {
|
||||||
|
Ok(b) => {
|
||||||
|
let body_content = b.into_bytes();
|
||||||
|
let text = String::from_utf8(body_content.as_ref().to_vec()).unwrap();
|
||||||
|
|
||||||
|
match Paste::from_text(text) {
|
||||||
|
Ok(paste) => {
|
||||||
|
let mut path = config.data_directory.clone();
|
||||||
|
path.push(paste.id.clone());
|
||||||
|
|
||||||
|
if let Err(e) = paste.to_file(path) {
|
||||||
|
let err = Error::new(ErrorKind::Other, e.description());
|
||||||
|
future::err((state, err.into_handler_error()))
|
||||||
|
} else {
|
||||||
|
let res = create_response(
|
||||||
|
&state,
|
||||||
|
StatusCode::OK,
|
||||||
|
mime::TEXT_PLAIN,
|
||||||
|
format!("{}/{}\n", config.url, paste.id),
|
||||||
|
);
|
||||||
|
|
||||||
|
future::ok((state, res))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
let err = Error::new(ErrorKind::Other, e.description());
|
||||||
|
future::err((state, err.into_handler_error()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => future::err((state, e.into_handler_error())),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn post(mut state: State) -> Box<HandlerFuture> {
|
||||||
|
Box::new({
|
||||||
|
Body::take_from(&mut state).concat2().then(|body| {
|
||||||
|
let config = Config::take_from(&mut state);
|
||||||
|
match body {
|
||||||
|
Ok(b) => {
|
||||||
|
let body_content = b.into_bytes();
|
||||||
|
let form_map: HashMap<String, String> = form_urlencoded::parse(&body_content)
|
||||||
|
.into_owned()
|
||||||
|
.map(|x| x)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if let Ok(paste) = Paste::from_form(form_map) {
|
||||||
|
let mut path = config.data_directory;
|
||||||
|
path.push(paste.id.clone());
|
||||||
|
|
||||||
|
if let Err(e) = paste.to_file(path) {
|
||||||
|
let err = Error::new(ErrorKind::Other, e.description());
|
||||||
|
return future::err((state, err.into_handler_error()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = Response::builder()
|
||||||
|
.status(303)
|
||||||
|
.header("Location", format!("/{}", paste.id))
|
||||||
|
.body(Body::empty())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
future::ok((state, res))
|
||||||
|
} else {
|
||||||
|
let res = create_response(
|
||||||
|
&state,
|
||||||
|
StatusCode::OK,
|
||||||
|
mime::TEXT_PLAIN,
|
||||||
|
"ERR"
|
||||||
|
);
|
||||||
|
|
||||||
|
future::ok((state, res))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => future::err((state, e.into_handler_error())),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
use askama::Template;
|
||||||
|
use futures::future;
|
||||||
|
|
||||||
|
use gotham::handler::{HandlerFuture, IntoHandlerError};
|
||||||
|
use gotham::helpers::http::response::{create_empty_response, create_response};
|
||||||
|
use gotham::state::{FromState, State};
|
||||||
|
use gotham_derive::{StateData, StaticResponseExtender};
|
||||||
|
|
||||||
|
use hyper::StatusCode;
|
||||||
|
use mime;
|
||||||
|
use serde_derive::Deserialize;
|
||||||
|
|
||||||
|
use syntect::html::ClassedHTMLGenerator;
|
||||||
|
|
||||||
|
use crate::config::Config;
|
||||||
|
use crate::paste::Paste;
|
||||||
|
use crate::syntax::SYNTAX_SET;
|
||||||
|
|
||||||
|
#[derive(Deserialize, StateData, StaticResponseExtender)]
|
||||||
|
pub struct Params {
|
||||||
|
id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Template)]
|
||||||
|
#[template(path = "view.html")]
|
||||||
|
pub struct View {
|
||||||
|
id: String,
|
||||||
|
dt: String,
|
||||||
|
text_lines: Vec<String>,
|
||||||
|
index_len: usize,
|
||||||
|
site_url: String,
|
||||||
|
syntax: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(mut state: State) -> Box<HandlerFuture> {
|
||||||
|
Box::new({
|
||||||
|
let config = Config::take_from(&mut state);
|
||||||
|
let Params { id } = Params::take_from(&mut state);
|
||||||
|
|
||||||
|
let mut path = config.data_directory.clone();
|
||||||
|
path.push(id.clone());
|
||||||
|
|
||||||
|
match Paste::from_file(path) {
|
||||||
|
Ok(paste) => {
|
||||||
|
let text_lines: Vec<String> = paste.text.lines().map(|s| s.into()).collect();
|
||||||
|
let index_len = format!("{}", text_lines.len()).len();
|
||||||
|
let syntax = SYNTAX_SET
|
||||||
|
.find_syntax_by_name(&paste.lang)
|
||||||
|
.unwrap_or_else(|| SYNTAX_SET.find_syntax_plain_text());
|
||||||
|
let mut high_lines: Vec<String> = Vec::new();
|
||||||
|
|
||||||
|
for line in text_lines {
|
||||||
|
let mut html_generator = ClassedHTMLGenerator::new(&syntax, &SYNTAX_SET);
|
||||||
|
html_generator.parse_html_for_line(&line);
|
||||||
|
high_lines.push(html_generator.finalize());
|
||||||
|
}
|
||||||
|
|
||||||
|
let template = View {
|
||||||
|
id,
|
||||||
|
dt: paste.dt.format("%Y-%m-%dT%H:%MZ").to_string(),
|
||||||
|
text_lines: high_lines,
|
||||||
|
index_len,
|
||||||
|
site_url: config.url,
|
||||||
|
syntax: paste.lang,
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = match template.render() {
|
||||||
|
Ok(content) => create_response(
|
||||||
|
&state,
|
||||||
|
StatusCode::OK,
|
||||||
|
mime::TEXT_HTML_UTF_8,
|
||||||
|
content.into_bytes(),
|
||||||
|
),
|
||||||
|
Err(_) => create_empty_response(&state, StatusCode::INTERNAL_SERVER_ERROR),
|
||||||
|
};
|
||||||
|
|
||||||
|
future::ok((state, res))
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
let io_error = std::io::Error::new(std::io::ErrorKind::Other, e.description());
|
||||||
|
future::err((state, io_error.into_handler_error()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use syntect::parsing::SyntaxSet;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref SYNTAX_SET: SyntaxSet = SyntaxSet::load_defaults_newlines();
|
||||||
|
pub static ref SYNTAXES: Vec<String> = {
|
||||||
|
let syntax_set = SyntaxSet::load_defaults_newlines();
|
||||||
|
let mut syntaxes: Vec<String> = Vec::new();
|
||||||
|
|
||||||
|
for syntax in syntax_set.syntaxes() {
|
||||||
|
if ! syntax.hidden {
|
||||||
|
syntaxes.push(syntax.name.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
syntaxes.sort();
|
||||||
|
syntaxes
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Syntax {
|
||||||
|
pub name: String,
|
||||||
|
pub ext: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Syntax {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.name)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,539 @@
|
|||||||
|
/*
|
||||||
|
* theme "Solarized (light)" generated by syntect
|
||||||
|
*/
|
||||||
|
|
||||||
|
.code {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment {
|
||||||
|
color: #93a1a1;
|
||||||
|
}
|
||||||
|
.meta.documentation {
|
||||||
|
color: #93a1a1;
|
||||||
|
}
|
||||||
|
.string {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
.string.regexp {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
.constant.character.escape {
|
||||||
|
color: #dc322f;
|
||||||
|
}
|
||||||
|
.constant.numeric {
|
||||||
|
color: #6c71c4;
|
||||||
|
}
|
||||||
|
.variable {
|
||||||
|
color: #268bd2;
|
||||||
|
}
|
||||||
|
.variable.function {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.variable.language {
|
||||||
|
color: #d33682;
|
||||||
|
}
|
||||||
|
.keyword {
|
||||||
|
color: #859900;
|
||||||
|
}
|
||||||
|
.meta.import {
|
||||||
|
color: #cb4b16;
|
||||||
|
}
|
||||||
|
.keyword {
|
||||||
|
color: #cb4b16;
|
||||||
|
}
|
||||||
|
.keyword.control.import {
|
||||||
|
color: #cb4b16;
|
||||||
|
}
|
||||||
|
.keyword.control.import.from {
|
||||||
|
color: #cb4b16;
|
||||||
|
}
|
||||||
|
.keyword.other.import {
|
||||||
|
color: #cb4b16;
|
||||||
|
}
|
||||||
|
.keyword.control.at-rule.include {
|
||||||
|
color: #cb4b16;
|
||||||
|
}
|
||||||
|
.keyword.control.at-rule.import {
|
||||||
|
color: #cb4b16;
|
||||||
|
}
|
||||||
|
.keyword.operator.comparison {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
.keyword.operator.assignment {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
.keyword.operator.arithmetic {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
.storage {
|
||||||
|
color: #859900;
|
||||||
|
}
|
||||||
|
.storage.modifier {
|
||||||
|
color: #586e75;
|
||||||
|
}
|
||||||
|
.keyword.control.class {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.entity.name {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.entity.name.class {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.entity.name.type.class {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.entity.other.inherited-class {
|
||||||
|
color: #268bd2;
|
||||||
|
}
|
||||||
|
.entity.other.attribute-name {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.support {
|
||||||
|
color: #859900;
|
||||||
|
}
|
||||||
|
.support.type {
|
||||||
|
color: #859900;
|
||||||
|
}
|
||||||
|
.support.class {
|
||||||
|
color: #859900;
|
||||||
|
}
|
||||||
|
.entity.name.function {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.punctuation.definition.variable {
|
||||||
|
color: #859900;
|
||||||
|
}
|
||||||
|
.constant {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.constant.language {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.meta.preprocessor {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.entity.name.section {
|
||||||
|
color: #cb4b16;
|
||||||
|
}
|
||||||
|
.support.function.construct {
|
||||||
|
color: #dc322f;
|
||||||
|
}
|
||||||
|
.keyword.other.new {
|
||||||
|
color: #dc322f;
|
||||||
|
}
|
||||||
|
.constant.character {
|
||||||
|
color: #cb4b16;
|
||||||
|
}
|
||||||
|
.constant.other {
|
||||||
|
color: #cb4b16;
|
||||||
|
}
|
||||||
|
.entity.name.tag {
|
||||||
|
color: #268bd2;
|
||||||
|
}
|
||||||
|
.punctuation.definition.tag.html {
|
||||||
|
color: #93a1a1;
|
||||||
|
}
|
||||||
|
.punctuation.definition.tag.begin {
|
||||||
|
color: #93a1a1;
|
||||||
|
}
|
||||||
|
.punctuation.definition.tag.end {
|
||||||
|
color: #93a1a1;
|
||||||
|
}
|
||||||
|
.support.function {
|
||||||
|
color: #859900;
|
||||||
|
}
|
||||||
|
.punctuation.separator.continuation {
|
||||||
|
color: #dc322f;
|
||||||
|
}
|
||||||
|
.storage.type {
|
||||||
|
color: #268bd2;
|
||||||
|
}
|
||||||
|
.support.type.exception {
|
||||||
|
color: #cb4b16;
|
||||||
|
}
|
||||||
|
.keyword.other.special-method {
|
||||||
|
color: #cb4b16;
|
||||||
|
}
|
||||||
|
.string.quoted.double {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
.string.quoted.single {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
.punctuation.definition.string {
|
||||||
|
color: #839496;
|
||||||
|
}
|
||||||
|
.meta.brace.square {
|
||||||
|
color: #268bd2;
|
||||||
|
}
|
||||||
|
.punctuation.section.brackets {
|
||||||
|
color: #268bd2;
|
||||||
|
}
|
||||||
|
.meta.brace.round {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
.meta.brace.curly {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
.punctuation.section {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
.punctuation.section.block {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
.punctuation.definition.parameters {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
.punctuation.section.group {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
.support.constant.color {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.invalid.deprecated.color.w3c-non-standard-color-name.scss {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.meta.selector.css {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
.entity.name.tag.css {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.entity.name.tag.scss {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.source.less {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.keyword.control.html.elements {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.source.sass {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.keyword.control.untitled {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.entity.other.attribute-name.class {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.entity.other.attribute-name.id {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.entity.other.attribute-name.pseudo-element {
|
||||||
|
color: #268bd2;
|
||||||
|
}
|
||||||
|
.entity.other.attribute-name.tag.pseudo-element {
|
||||||
|
color: #268bd2;
|
||||||
|
}
|
||||||
|
.entity.other.attribute-name.pseudo-class {
|
||||||
|
color: #268bd2;
|
||||||
|
}
|
||||||
|
.entity.other.attribute-name.tag.pseudo-class {
|
||||||
|
color: #268bd2;
|
||||||
|
}
|
||||||
|
.text.html.basic {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
.meta.tag.other.html {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
.text.html.basic {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
.meta.tag.any.html {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
.text.html.basic {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
.meta.tag.block.any {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
.text.html.basic {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
.meta.tag.inline.any {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
.text.html.basic {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
.meta.tag.structure.any.html {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
.text.html.basic {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
.source.js.embedded.html {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
.punctuation.separator.key-value.html {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
.text.html.basic {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.entity.other.attribute-name.html {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.meta.tag.xml {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.entity.other.attribute-name {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.keyword.other.special-method.ruby {
|
||||||
|
color: #859900;
|
||||||
|
}
|
||||||
|
.variable.other.constant.ruby {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.constant.other.symbol.ruby {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
.keyword.other.special-method.ruby {
|
||||||
|
color: #cb4b16;
|
||||||
|
}
|
||||||
|
.meta.array {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.support.function.construct.php {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.entity.name.function.preprocessor.c {
|
||||||
|
color: #cb4b16;
|
||||||
|
}
|
||||||
|
.meta.preprocessor.c.include {
|
||||||
|
color: #cb4b16;
|
||||||
|
}
|
||||||
|
.meta.preprocessor.macro.c {
|
||||||
|
color: #cb4b16;
|
||||||
|
}
|
||||||
|
.meta.preprocessor.c.include {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
.string.quoted.other.lt-gt.include.c {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
.meta.preprocessor.c.include {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
.punctuation.definition.string.begin.c {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
.meta.preprocessor.c.include {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
.punctuation.definition.string.end.c {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
.other.package.exclude {
|
||||||
|
color: #dc322f;
|
||||||
|
}
|
||||||
|
.other.remove {
|
||||||
|
color: #dc322f;
|
||||||
|
}
|
||||||
|
.other.add {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
.punctuation.section.group.tex {
|
||||||
|
color: #dc322f;
|
||||||
|
}
|
||||||
|
.punctuation.definition.arguments.begin.latex {
|
||||||
|
color: #dc322f;
|
||||||
|
}
|
||||||
|
.punctuation.definition.arguments.end.latex {
|
||||||
|
color: #dc322f;
|
||||||
|
}
|
||||||
|
.punctuation.definition.arguments.latex {
|
||||||
|
color: #dc322f;
|
||||||
|
}
|
||||||
|
.meta.group.braces.tex {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.string.other.math.tex {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.variable.parameter.function.latex {
|
||||||
|
color: #cb4b16;
|
||||||
|
}
|
||||||
|
.punctuation.definition.constant.math.tex {
|
||||||
|
color: #dc322f;
|
||||||
|
}
|
||||||
|
.text.tex.latex {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
.constant.other.math.tex {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
.constant.other.general.math.tex {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
.constant.other.general.math.tex {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
.constant.character.math.tex {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
.string.other.math.tex {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.punctuation.definition.string.begin.tex {
|
||||||
|
color: #dc322f;
|
||||||
|
}
|
||||||
|
.punctuation.definition.string.end.tex {
|
||||||
|
color: #dc322f;
|
||||||
|
}
|
||||||
|
.keyword.control.label.latex {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
.text.tex.latex {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
.constant.other.general.math.tex {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
.variable.parameter.definition.label.latex {
|
||||||
|
color: #dc322f;
|
||||||
|
}
|
||||||
|
.support.function.be.latex {
|
||||||
|
color: #859900;
|
||||||
|
}
|
||||||
|
.support.function.section.latex {
|
||||||
|
color: #cb4b16;
|
||||||
|
}
|
||||||
|
.support.function.general.tex {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
.keyword.control.ref.latex {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
.storage.type.class.python {
|
||||||
|
color: #859900;
|
||||||
|
}
|
||||||
|
.storage.type.function.python {
|
||||||
|
color: #859900;
|
||||||
|
}
|
||||||
|
.storage.modifier.global.python {
|
||||||
|
color: #859900;
|
||||||
|
}
|
||||||
|
.support.type.exception.python {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.meta.scope.for-in-loop.shell {
|
||||||
|
color: #586e75;
|
||||||
|
}
|
||||||
|
.variable.other.loop.shell {
|
||||||
|
color: #586e75;
|
||||||
|
}
|
||||||
|
.meta.scope.case-block.shell {
|
||||||
|
color: #586e75;
|
||||||
|
}
|
||||||
|
.meta.scope.case-body.shell {
|
||||||
|
color: #586e75;
|
||||||
|
}
|
||||||
|
.punctuation.definition.logical-expression.shell {
|
||||||
|
color: #dc322f;
|
||||||
|
}
|
||||||
|
.storage.modifier.c++ {
|
||||||
|
color: #859900;
|
||||||
|
}
|
||||||
|
.support.function.perl {
|
||||||
|
color: #268bd2;
|
||||||
|
}
|
||||||
|
.meta.diff {
|
||||||
|
color: #93a1a1;
|
||||||
|
}
|
||||||
|
.meta.diff.header {
|
||||||
|
color: #93a1a1;
|
||||||
|
}
|
||||||
|
.meta.diff.range {
|
||||||
|
color: #268bd2;
|
||||||
|
}
|
||||||
|
.markup.deleted {
|
||||||
|
color: #dc322f;
|
||||||
|
}
|
||||||
|
.markup.changed {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
.markup.inserted {
|
||||||
|
color: #859900;
|
||||||
|
}
|
||||||
|
.markup.heading {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.punctuation.definition.heading.markdown {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.markup.quote {
|
||||||
|
color: #859900;
|
||||||
|
}
|
||||||
|
.markup.italic {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
.markup.bold {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.markup.underline.link.markdown {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
.meta.link.reference {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
.constant.other.reference.link.markdown {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
.constant.other.reference.link.markdown {
|
||||||
|
color: #6c71c4;
|
||||||
|
}
|
||||||
|
.sublimelinter.notes {
|
||||||
|
color: #eee8d5;
|
||||||
|
}
|
||||||
|
.sublimelinter.outline.illegal {
|
||||||
|
color: #93a1a1;
|
||||||
|
}
|
||||||
|
.sublimelinter.outline.warning {
|
||||||
|
color: #839496;
|
||||||
|
}
|
||||||
|
.sublimelinter.outline.violation {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
.sublimelinter.mark.warning {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.sublimelinter.mark.error {
|
||||||
|
color: #dc322f;
|
||||||
|
}
|
||||||
|
.sublimelinter.gutter-mark {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
.brackethighlighter.all {
|
||||||
|
color: #93a1a1;
|
||||||
|
}
|
||||||
|
.entity.name.filename.find-in-files {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
.constant.numeric.line-number.find-in-files {
|
||||||
|
color: #93a1a1;
|
||||||
|
}
|
||||||
|
.markup.deleted.git_gutter {
|
||||||
|
color: #dc322f;
|
||||||
|
}
|
||||||
|
.markup.inserted.git_gutter {
|
||||||
|
color: #859900;
|
||||||
|
}
|
||||||
|
.markup.changed.git_gutter {
|
||||||
|
color: #b58900;
|
||||||
|
}
|
||||||
|
.variable.other.readwrite.js {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
.variable.other.object.js {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
||||||
|
.variable.other.constant.js {
|
||||||
|
color: #657b83;
|
||||||
|
}
|
@ -0,0 +1,169 @@
|
|||||||
|
body {
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
font-family: monospace;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-select {
|
||||||
|
/*-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
-webkit-user-select: none;*/
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: blue;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: royalblue;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.error_description {
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 90vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.error_description > h2 {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
header > hr {
|
||||||
|
border: 0;
|
||||||
|
border-top: 1px solid lightgray;
|
||||||
|
width: 90vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
header > ul {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
header > ul > li {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
header > ul > li > code {
|
||||||
|
background-color: floralwhite;
|
||||||
|
color: firebrick;
|
||||||
|
padding: 2px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
header > ul > li > code > span {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav > ul {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav > ul > li {
|
||||||
|
display: inline-block;
|
||||||
|
list-style: none;
|
||||||
|
padding: 0 2ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
form > textarea {
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid lightgray;
|
||||||
|
border-radius: .5ch;
|
||||||
|
box-shadow: .1ch .1ch .25ch rgba(0, 0, 0, .45);
|
||||||
|
height: 400px;
|
||||||
|
padding: .5ch;
|
||||||
|
tab-size: 4;
|
||||||
|
-moz-tab-size: 4;
|
||||||
|
-o-tab-size: 4;
|
||||||
|
width: 120ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
form > p > input {
|
||||||
|
background-color: white;
|
||||||
|
border: 1px lightgray solid;
|
||||||
|
border-radius: .5ch;
|
||||||
|
box-shadow: .1ch .1ch .25ch rgba(0, 0, 0, .45);
|
||||||
|
color: blue;
|
||||||
|
padding: .5ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
form > p > input:hover {
|
||||||
|
background-color: #fcfcfc;
|
||||||
|
box-shadow: .1ch .1ch .25ch rgba(0, 0, 128, .45);
|
||||||
|
color: royalblue;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
main > div {
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
background-color: white;
|
||||||
|
border: 1px solid lightgray;
|
||||||
|
border-radius: .5ch;
|
||||||
|
box-shadow: .1ch .1ch .25ch rgba(0, 0, 0, .45);
|
||||||
|
counter-reset: line;
|
||||||
|
display: inline-block;
|
||||||
|
padding: .5rem;
|
||||||
|
text-align: left;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
main > div > div {
|
||||||
|
border-bottom: 1px dashed #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
main > div > div:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
main > div > div:hover {
|
||||||
|
background-color: floralwhite;
|
||||||
|
}
|
||||||
|
|
||||||
|
main > div > div > div {
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 1rem;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
main > div > div > div.index {
|
||||||
|
text-align: right;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
main > div > div > div.index > a {
|
||||||
|
color: lightcoral;
|
||||||
|
}
|
||||||
|
|
||||||
|
main > div > div > div.index > a:hover {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
main > div > div > div.line {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
max-width: 80vw;
|
||||||
|
tab-size: 4;
|
||||||
|
-moz-tab-size: 4;
|
||||||
|
-o-tab-size: 4;
|
||||||
|
padding: 0 !important;
|
||||||
|
padding-left: 1ch !important;
|
||||||
|
border-left: 1px solid lightgray;
|
||||||
|
margin-left: 1ch;
|
||||||
|
min-height: 1rem;
|
||||||
|
background-color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.hljs {
|
||||||
|
display: inline-block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.hljs {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>pastebucket :: {{id}}</title>
|
||||||
|
<link rel="stylesheet" href="/s/pastebucket.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<h1><a href="{{site_url|safe}}">pastebucket</a> :: <a href="/{{id}}">{{id}}</a></h1>
|
||||||
|
<hr>
|
||||||
|
</header>
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/{{id}}/raw">raw</a></li>
|
||||||
|
<li><a href="/{{id}}/edit">edit</a></li>
|
||||||
|
<li>{{syntax}}</li>
|
||||||
|
<li>{{dt}}</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<form action="/" method="post">
|
||||||
|
<textarea id="paste_area" autofocus name="paste" onkeydown="ctrl_enter(event);">{{text|safe}}</textarea>
|
||||||
|
<p>
|
||||||
|
<select name="lang">
|
||||||
|
<option value="{{syntax}}" selected>{{syntax}}
|
||||||
|
<option value="Plain Text">Plain Text
|
||||||
|
{% for s in syntaxes %}
|
||||||
|
<option value="{{s}}">{{s}}
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
|
<p><input id="paste_button" type="submit" value="submit"></p>
|
||||||
|
</form>
|
||||||
|
<script>
|
||||||
|
function ctrl_enter(e) {
|
||||||
|
if (e.keyCode == 13 && e.ctrlKey) {
|
||||||
|
document.getElementById("paste_button").click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,50 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>pastebucket</title>
|
||||||
|
<link rel="stylesheet" href="/s/pastebucket.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<h1><a href="{{site_url|safe}}">pastebucket</a></h1>
|
||||||
|
<hr>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<code>
|
||||||
|
<span class="no-select"><command></span><span> | curl -T- https://p.rascul.xyz</span>
|
||||||
|
</code>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>
|
||||||
|
<span class="no-select">-- or --</span>
|
||||||
|
</code>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>
|
||||||
|
<span>curl -T- https://p.rascul.xyz < </span><span class="no-select"><file></span>
|
||||||
|
</code>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</header>
|
||||||
|
<form action="/" method="post">
|
||||||
|
<textarea id="paste_area" autofocus name="paste" onkeydown="ctrl_enter(event);"></textarea>
|
||||||
|
<p>
|
||||||
|
<select name="lang">
|
||||||
|
<option value="Plain Text">Plain Text
|
||||||
|
{% for syntax in syntaxes %}
|
||||||
|
<option value="{{syntax}}">{{syntax}}
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
|
<p><input id="paste_button" type="submit" value="submit"></p>
|
||||||
|
</form>
|
||||||
|
<script>
|
||||||
|
function ctrl_enter(e) {
|
||||||
|
if (e.keyCode == 13 && e.ctrlKey) {
|
||||||
|
document.getElementById("paste_button").click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,32 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>pastebucket :: {{id}}</title>
|
||||||
|
<link rel="stylesheet" href="/s/pastebucket.css">
|
||||||
|
<link rel="stylesheet" href="/s/code.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<h1><a href="{{site_url|safe}}">pastebucket</a> :: <a href="/{{id}}">{{id}}</a></h1>
|
||||||
|
<hr>
|
||||||
|
</header>
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/{{id}}/raw">raw</a></li>
|
||||||
|
<li><a href="/{{id}}/edit">edit</a></li>
|
||||||
|
<li>{{syntax}}</li>
|
||||||
|
<li>{{dt}}</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<main>
|
||||||
|
<div>
|
||||||
|
{% for line in text_lines %}
|
||||||
|
<div id="{{loop.index}}" onmouseover="document.getElementById('{{loop.index}}').firstElementChild.firstElementChild.style = 'color: red'" onmouseout="document.getElementById('{{loop.index}}').firstElementChild.firstElementChild.style = 'color: lightcoral'">
|
||||||
|
<div class="index no-select" style="width: {{index_len}}ch"><a href="#{{loop.index}}">{{loop.index}}</a></div><div class="line">{{line|safe}}</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in new issue