26 Commits

Author SHA1 Message Date
Jordan Doyle
b095d2464c Fix build after updating dependencies & make scripts posix-compliant 2020-04-12 16:44:59 +01:00
Jordan Doyle
3c76d4fd1a Remove FreeBSD build as it currently fails 2020-04-12 16:44:57 +01:00
Jordan Doyle
06b90b8271 Do a cargo check instead of duplicating the build 2020-04-12 16:44:55 +01:00
Jordan Doyle
21b04b88ae Add rel="help" link to this repo (sorry! feel free to patch this out) 2020-04-12 16:44:50 +01:00
Jordan Doyle
53d97709fa Bump package versions 2020-04-12 16:44:37 +01:00
Jordan Doyle
ff3d193734 Update README.md 2020-04-12 15:42:37 +01:00
Ron Nazarov
2b53d0a34c Fixed "snippits" to "snippets" 2020-04-12 15:40:26 +01:00
Jordan Doyle
2502cc7f5c Update .travis.yml 2020-04-12 14:39:54 +01:00
Jordan Doyle
4bd924b423 Bump version to 1.0.3 2020-04-05 14:51:03 +01:00
j_dickert
be88df22f7 Add linenumbering
Fix rocket dependencies
2020-04-05 14:48:42 +01:00
Olgierd "Allgreed" Kasprowicz
275ace8356 Add Dockerfile 2019-05-23 19:47:45 +01:00
Jordan Johnson-Doyle
41900eaeee Fix deploy script on arm & windows builds 2019-02-21 18:12:43 +00:00
Jordan Doyle
1115bbc937 We're maturing like a fine cheese 2019-02-21 16:53:14 +00:00
Jordan Johnson-Doyle
5b7868080f Prevent cloning paste on show where not necessary 2019-02-21 16:41:39 +00:00
Jordan Johnson-Doyle
c2219e15f4 Avoid cloning generated id when submitting paste 2019-02-21 11:51:36 +00:00
Jordan Johnson-Doyle
26b21d7b39 Bump version to 1.0.2 2019-02-16 19:32:14 +00:00
Jordan Johnson-Doyle
fda03cfe21 Add config to Cargo.toml for crates.io 2019-02-16 19:25:39 +00:00
Jordan Johnson-Doyle
e466683062 Unnecessary fully qualified struct reference 2019-02-16 19:00:54 +00:00
Jordan Johnson-Doyle
2fad96945e Strip debug symbols on release builds from Travis 2019-02-16 18:56:18 +00:00
Jordan Johnson-Doyle
c3b886c196 Remove dependency on ring 2019-02-16 18:55:37 +00:00
Jordan Johnson-Doyle
caa8c3568d Remove unused dependencies, slightly optimise release binary size 2019-02-16 18:05:06 +00:00
Jordan Johnson-Doyle
22f5e257a9 Bump to 1.0.1 2019-02-16 11:51:14 +00:00
Jordan Johnson-Doyle
f675f75b3b Rename render -> show_paste 2019-02-16 11:51:14 +00:00
Jordan Johnson-Doyle
cd3d911ca0 Use overflow: auto instead of overflow: scroll 2019-02-16 11:51:13 +00:00
Jordan Doyle
4c9f1af0ab Update README.md 2019-02-16 11:04:23 +00:00
Jordan Doyle
1fe4db7420 Update README.md 2019-02-15 23:26:52 +00:00
14 changed files with 775 additions and 557 deletions

1
.dockerignore Normal file
View File

@@ -0,0 +1 @@
target

View File

@@ -25,17 +25,15 @@ matrix:
os: osx
# *BSD
- env: TARGET=x86_64-unknown-freebsd DISABLE_TESTS=1
# - env: TARGET=x86_64-unknown-freebsd DISABLE_TESTS=1
- env: TARGET=x86_64-unknown-netbsd DISABLE_TESTS=1
# Windows
- env: TARGET=x86_64-pc-windows-gnu
allow_failures:
- env: TARGET=x86_64-unknown-linux-gnu
before_install:
- set -e
- command -v apt-get && sudo apt-get install -y libclang-dev
- rustup self update
install:
@@ -56,7 +54,7 @@ deploy:
# - Encrypt it: `travis encrypt 0123456789012345678901234567890123456789
# - Paste the output down here
api_key:
secure: "VRWAaqZi3ttaVfvgaSK0hQAIYDFUvPdOhhHxQESNO5qoexjc9wioDe7qfi2PcIKEMcEnXgVlmqKEyd0K4Cur9gWKDCfVPUEDTlph+Xdx+p5ViB+9Bz1z9qFSJchZtqqPGqfaq4Fk5HjBr6V31JFV0pjDfy2dHJzjQke+CGG8Zk/1n4H0dYqxZ4EyxcmuIxAnplyUDNdOrbwkuRKEGXLg94Ayk/OmLqHwvCJGYsa3jSgS2ySDT3nRaM9CG5WxKaEQMW4uEWJLbgmqFaxCWh1sOYt+brDWXc9aIJIFsR4b8wdHdHVL0C34dW/0xjxSQPN3PK3jKZYduiu49XgHtpKF9fvsFQSeQwMsAizucJtVKaJr1LfjKk3WwKPuiRotrPVwggdm9/zjXn1POerLR1TMNZr2fxI2iqkXA2sjpRJiDL7sBDg+glY09lGZazyCRIMszxuthoxjw38ZqwYzU4iM7mCkQQHFzVObZdanDtsCH6sM0FDfb7LmJ9uAdDHnzKwTKk23w0hEmdQJHkYimwCQR8Yp7jAhRyxNxYuWtdpuWP4WXDRmLypqMszNqFkZymvVFmMAm0hcSRxwkup4eUWY4yVuHch8/sl51zKXAFk35P2/gZyfoD7+yhiBY5j6dN4s6VBuePc9knRAUP3vWxdTjVg+PoQuWFEC5pHUvsUm/yk="
secure: "I1pMvhljeuqXkQDtaktLAcC0VFwlHZIXiM81f+FI+m8pWBV6eAy4alD9+tBSl808g8VIyv1nTbw/UGxVlKALqhR0iT9eCCxvVrgJuWKawJYuzrKxYnKf82t3RjTO1qh6Uf3LeCuZ+ReGbFeR4wTlxC9CQLwdBg3xUA+8bNNJigrEECmNjrUigmRdnKb1R1KXCNW9AwXMlVv+4I1me50/dFLdnhlaAjpsWFXKb2vAk/xoyob1WMEZbInrD/NX1kOz0UPiIX/K8Qjsgp6SAFxjhWGu9jk7VnzvEGPUmTS7OJ4tols2Lhde1AC0y/pElt3YFjA3qfKJfk4B8bUAizomeR+GwJ5YD/CEqhopa8b34SuxeHUOkX9GxPoba08qAmqPAeWcO2hlS0aBiiIUMIOgUqjGDy3MiMqg7VdalkiNtRsr2iCXXSe9p2FPWFsYW1bjGCbIYGBrmWZZEIVYJk2laSmIng27MBvlJFdDkX5Nf3d75Y5U/ZAUwfhpA8ZxwVbddKjYs1S8x09s5yhVOjxJzK2G0aJIg94wMflYEsygrZxjpMkOI79HMZrKArY2S6N6wqgKIdFM2kCmk0ps9DM9pn2o9h91EpnAi7PUd8RIB8yOh9g/K4K9ClvScUqagLR+DvjPoMh68F7Y5XnbuZkIgQ7fKB0cBtB+JnrIE3b3icI="
file_glob: true
file: $CRATE_NAME-$TRAVIS_TAG-$TARGET.*
on:

1082
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,17 +1,30 @@
[package]
name = "bin"
version = "1.0.0"
version = "1.0.3"
description = "a paste bin."
repository = "https://github.com/w4/bin"
license = "WTFPL OR 0BSD"
authors = ["Jordan Doyle <jordan@doyle.la>"]
edition = "2018"
[dependencies]
owning_ref = "0.4"
linked-hash-map = "0.5"
askama = { git = "https://github.com/djc/askama", rev = "fc5addc4", features = ["with-rocket"] }
askama_escape = { git = "https://github.com/djc/askama", rev = "fc5addc4" }
rocket = "0.4"
lazy_static = "1.2"
rand = "0.6"
rocket = { version = "0.4.4", default-features = false }
askama = "0.9"
lazy_static = "1.4"
rand = { version = "0.7", features = ["nightly"] }
gpw = "0.1"
syntect = "3.0"
chashmap = { git = "https://github.com/redox-os/tfs", rev = "b3e7cae1" }
serde = { version = "1.0", features = ["derive"] }
syntect = "4.1"
serde_derive = "1.0"
[profile.release]
lto = true
codegen-units = 1
incremental = false
[badges]
travis-ci = { repository = "w4/bin" }
is-it-maintained-issue-resolution = { repository = "w4/bin" }
is-it-maintained-open-issues = { repository = "w4/bin" }
maintenance = { status = "passively-maintained" }

15
Dockerfile Normal file
View File

@@ -0,0 +1,15 @@
FROM rust:1.34.2-slim-stretch AS builder
RUN rustup install nightly-x86_64-unknown-linux-gnu
COPY . /sources
WORKDIR /sources
RUN cargo +nightly build --release
RUN chown nobody:nogroup /sources/target/release/bin
FROM debian:stretch-slim
COPY --from=builder /sources/target/release/bin /pastebin
USER nobody
EXPOSE 8000
ENTRYPOINT ["/pastebin"]

View File

@@ -1,33 +1,47 @@
# bin.
a pastebin.
# bin
a paste bin.
A paste bin that's actually minimalist. No database requirement, no commenting functionality, no self-destructing or time bomb messages and no social media integration—just an application to quickly send snippets of text to people.
[bin](https://bin.gy/) is written in Rust in around 200 lines of code. It's fast, it's simple, there's code highlighting and you can ⌘+A without going to the 'plain' page. It's revolutionary in the paste bin industry, disrupting markets and pushing boundaries never seen before.
There's no good open source pastebin solutions. I'm sorry to everyone who has one on GitHub but I have to say it. We try to cram as many little features as humanly possible into them and still try and call them minimalist. I don't want to run Redis, I don't want commenting functionality, I don't want self-destructing or time bomb messages and I especially don't want social media integration—I don't know about you but normally I just need to send a quick little snippit of code to someone, it doesn't need a title and I don't *really* mind when it disappears as long as its around long enough for them to see. Honestly, [I'm guilty of it myself](https://github.com/w4/hidden-note), we've all made a pastebin at one point or another but when it comes to making one to release to the public we create abominations.
##### so how do you get bin?
[bin.](https://bin.doyle.la/) is written in Rust in around 100 lines of code. It's fast, it's simple, there's code highlighting and you can ⌘+A without going to the 'plain' page. Revolutionary in the pastebin industry, disrupting markets and pushing boundaries never seen before.
##### curl support?
Download the latest version from the [releases](https://github.com/w4/bin/releases) page, extract it and run the `./bin` executable. You can also compile it from source using Cargo if you swing that way:
```bash
$ curl -X PUT --data 'hello world' bin.doyle.la
https://bin.doyle.la/cateettary
$ curl https://bin.doyle.la/cateettary
hello world
# nix-shell provides an environment with rust/cargo installed
$ nix-shell
[nix-shell:~/Code/bin]$ cargo build --release
Compiling bin v1.0.0 (/Users/jordanjd/Code/bin)
Finished release [optimized] target(s) in 3.61s
[nix-shell:~/Code/bin]$ ./target/release/bin
...
```
##### how do you run bin?
##### how do you run it?
```bash
$ ./bin
```
##### good one, what settings are there?
##### funny, what settings are there?
bin. uses [rocket](https://rocket.rs) so you can add a [rocket config file](https://api.rocket.rs/v0.3/rocket/config/) if you like. You can set `ROCKET_PORT` in your environment if you want to change the default port (8820).
bin uses [rocket](https://rocket.rs) so you can add a [rocket config file](https://api.rocket.rs/v0.3/rocket/config/) if you like. You can set `ROCKET_PORT` in your environment if you want to change the default port (8820).
bin's only configuration value is `BIN_BUFFER_SIZE` which defaults to 2000. Change this value if you want your bin to hold more pastes.
##### is there curl support?
```bash
$ curl -X PUT --data 'hello world' https://bin.gy
https://bin.gy/cateettary
$ curl https://bin.gy/cateettary
hello world
```
##### how does syntax highlighting work?
To get syntax highlighting you need to add the file extension at the end of your paste URL.
To get syntax highlighting you need to add the file extension at the end of your paste URL.

View File

@@ -19,7 +19,8 @@ main() {
cross rustc --bin bin --target $TARGET --release -- -C lto
cp target/$TARGET/release/bin $stage/
[ "$TARGET" = "arm-unknown-linux-gnueabi" ] || [ "$TARGET" = "x86_64-pc-windows-gnu" ] || strip target/$TARGET/release/bin
cp target/$TARGET/release/* $stage/
cd $stage
tar czf $src/$CRATE_NAME-$TRAVIS_TAG-$TARGET.tar.gz *

View File

@@ -4,7 +4,7 @@ set -ex
# TODO This is the "test phase", tweak it as you see fit
main() {
cross build --target $TARGET
cross check
cross build --target $TARGET --release
if [ ! -z $DISABLE_TESTS ]; then
@@ -12,7 +12,6 @@ main() {
fi
cross test --target $TARGET
cross test --target $TARGET --release
}
# we don't run the "test phase" when doing deploys

View File

@@ -2,8 +2,8 @@ with import <nixpkgs> {};
let src = fetchFromGitHub {
owner = "mozilla";
repo = "nixpkgs-mozilla";
# commited 12/2/2019
rev = "37f7f33ae3ddd70506cd179d9718621b5686c48d";
# commited 19/2/2020
rev = "e912ed483e980dfb4666ae0ed17845c4220e5e7c";
sha256 = "0cmvc9fnr38j3n0m4yf0k6s2x589w1rdby1qry1vh435v79gp95j";
};
in
@@ -14,6 +14,9 @@ stdenv.mkDerivation {
buildInputs = [
latest.rustChannels.nightly.cargo
latest.rustChannels.nightly.rust
stdenv.cc.libc
] ++
pkgs.lib.optional pkgs.stdenv.isDarwin pkgs.darwin.apple_sdk.frameworks.Security;
LIBCLANG_PATH="${llvmPackages.libclang}/lib";
}

View File

@@ -1,5 +1,6 @@
extern crate gpw;
extern crate linked_hash_map;
extern crate owning_ref;
extern crate rand;
use rand::distributions::Alphanumeric;
@@ -7,9 +8,11 @@ use rand::{thread_rng, Rng};
use linked_hash_map::LinkedHashMap;
use owning_ref::RwLockReadGuardRef;
use std::cell::RefCell;
use std::env;
use std::sync::RwLock;
use std::sync::{PoisonError, RwLock};
lazy_static! {
static ref ENTRIES: RwLock<LinkedHashMap<String, String>> = RwLock::new(LinkedHashMap::new());
@@ -25,12 +28,12 @@ lazy_static! {
///
/// During the purge, `ENTRIES` is locked and the current thread will block.
fn purge_old() {
let entries_len = ENTRIES.read().unwrap_or_else(|p| p.into_inner()).len();
let entries_len = ENTRIES.read().unwrap_or_else(PoisonError::into_inner).len();
if entries_len > *BUFFER_SIZE {
let to_remove = entries_len - *BUFFER_SIZE;
let mut entries = ENTRIES.write().unwrap_or_else(|p| p.into_inner());
let mut entries = ENTRIES.write().unwrap_or_else(PoisonError::into_inner);
for _ in 0..to_remove {
entries.pop_front();
@@ -41,13 +44,11 @@ fn purge_old() {
/// Generates a 'pronounceable' random ID using gpw
pub fn generate_id() -> String {
thread_local!(static KEYGEN: RefCell<gpw::PasswordGenerator> = RefCell::new(gpw::PasswordGenerator::default()));
KEYGEN.with(|k| {
k.borrow_mut().next().unwrap_or_else(|| {
thread_rng()
.sample_iter(&Alphanumeric)
.take(6)
.collect::<String>()
})
KEYGEN.with(|k| k.borrow_mut().next()).unwrap_or_else(|| {
thread_rng()
.sample_iter(&Alphanumeric)
.take(6)
.collect::<String>()
})
}
@@ -56,17 +57,19 @@ pub fn store_paste(id: String, content: String) {
purge_old();
ENTRIES
.write()
.unwrap_or_else(|p| p.into_inner())
.unwrap_or_else(PoisonError::into_inner)
.insert(id, content);
}
/// Get a paste by id.
///
/// Returns `None` if the paste doesn't exist.
pub fn get_paste(id: &str) -> Option<String> {
ENTRIES
.read()
.unwrap_or_else(|p| p.into_inner())
.get(id)
.cloned()
pub fn get_paste(id: &str) -> Option<RwLockReadGuardRef<LinkedHashMap<String, String>, String>> {
let or = RwLockReadGuardRef::new(ENTRIES.read().unwrap_or_else(PoisonError::into_inner));
if or.contains_key(id) {
Some(or.map(|x| x.get(id).unwrap()))
} else {
None
}
}

View File

@@ -1,5 +1,4 @@
#![feature(proc_macro_hygiene, decl_macro)]
#![feature(type_alias_enum_variants)]
#[macro_use]
extern crate lazy_static;
@@ -8,7 +7,6 @@ extern crate lazy_static;
extern crate rocket;
extern crate askama;
extern crate askama_escape;
mod highlight;
mod io;
@@ -18,15 +16,15 @@ use highlight::highlight;
use io::{generate_id, get_paste, store_paste};
use params::{HostHeader, IsPlaintextRequest};
use askama::Template;
use askama_escape::{Html, MarkupDisplay};
use askama::{Html as AskamaHtml, MarkupDisplay, Template};
use rocket::http::{ContentType, Status};
use rocket::http::{ContentType, RawStr, Status};
use rocket::request::Form;
use rocket::response::content::Content;
use rocket::response::content::{Content, Html};
use rocket::response::Redirect;
use rocket::Data;
use std::borrow::Cow;
use std::io::Read;
///
@@ -35,11 +33,14 @@ use std::io::Read;
#[derive(Template)]
#[template(path = "index.html")]
struct Index {}
struct Index;
#[get("/")]
fn index() -> Index {
Index {}
fn index() -> Result<Html<String>, Status> {
Index
.render()
.map(Html)
.map_err(|_| Status::InternalServerError)
}
///
@@ -54,8 +55,9 @@ struct IndexForm {
#[post("/", data = "<input>")]
fn submit(input: Form<IndexForm>) -> Redirect {
let id = generate_id();
store_paste(id.clone(), input.into_inner().val);
Redirect::to(uri!(render: id))
let uri = uri!(show_paste: &id);
store_paste(id, input.into_inner().val);
Redirect::to(uri)
}
#[put("/", data = "<input>")]
@@ -64,11 +66,13 @@ fn submit_raw(input: Data, host: HostHeader) -> std::io::Result<String> {
input.open().take(1024 * 1000).read_to_string(&mut data)?;
let id = generate_id();
store_paste(id.clone(), data);
let uri = uri!(show_paste: &id);
store_paste(id, data);
match *host {
Some(host) => Ok(format!("https://{}/{}", host, id)),
None => Ok(id),
Some(host) => Ok(format!("https://{}{}", host, uri)),
None => Ok(format!("{}", uri)),
}
}
@@ -78,41 +82,49 @@ fn submit_raw(input: Data, host: HostHeader) -> std::io::Result<String> {
#[derive(Template)]
#[template(path = "paste.html")]
struct Render {
content: MarkupDisplay<Html, String>,
struct ShowPaste<'a> {
content: MarkupDisplay<AskamaHtml, Cow<'a, String>>,
}
#[get("/<key>")]
fn render(key: String, plaintext: IsPlaintextRequest) -> Result<Content<String>, Status> {
fn show_paste(key: String, plaintext: IsPlaintextRequest) -> Result<Content<String>, Status> {
let mut splitter = key.splitn(2, '.');
let key = splitter.next().ok_or_else(|| Status::NotFound)?;
let ext = splitter.next();
// get() returns a read-only lock, we're not going to be writing to this key
// again so we can hold this for as long as we want
let entry = get_paste(key).ok_or_else(|| Status::NotFound)?;
let entry = &*get_paste(key).ok_or_else(|| Status::NotFound)?;
if *plaintext {
Ok(Content(ContentType::Plain, entry))
Ok(Content(ContentType::Plain, entry.to_string()))
} else {
let template = Render {
content: match ext {
None => MarkupDisplay::new_unsafe(entry, Html),
Some(extension) => highlight(&entry, extension)
.map(|h| MarkupDisplay::new_safe(h, Html))
.ok_or_else(|| Status::NotFound)?,
let code_highlighted = match ext {
Some(extension) => match highlight(&entry, extension) {
Some(html) => html,
None => return Err(Status::NotFound),
},
None => String::from(RawStr::from_str(entry).html_escape()),
};
template
.render()
.map(|html| Content(ContentType::HTML, html))
.map_err(|_| Status::InternalServerError)
// Add <code> tags to enable line numbering with CSS
let html = format!(
"<code>{}</code>",
code_highlighted.replace("\n", "</code><code>")
);
let content = MarkupDisplay::new_safe(Cow::Borrowed(&html), AskamaHtml);
let template = ShowPaste { content };
match template.render() {
Ok(html) => Ok(Content(ContentType::HTML, html)),
Err(_) => Err(Status::InternalServerError),
}
}
}
fn main() {
rocket::ignite()
.mount("/", routes![index, submit, submit_raw, render])
.mount("/", routes![index, submit, submit_raw, show_paste])
.launch();
}

View File

@@ -4,6 +4,8 @@
<meta charset="UTF-8">
<title>bin.</title>
<link rel="help" href="https://github.com/w4/bin">
<style>
* { box-sizing: border-box; }
@@ -25,4 +27,4 @@
</style>
</head>
<body>{% block content %}{% endblock content %}</body>
</html>
</html>

View File

@@ -6,10 +6,14 @@
textarea {
height: 100%;
width: 100%;
background: none;
border: none;
color: inherit;
resize: none;
overflow: auto;
color: inherit;
font-family: inherit;
font-size: 1rem;
line-height: inherit;
@@ -24,7 +28,6 @@
width: 3rem;
border: none;
border-radius: 50%;
background: #2196F3;
color: white;

View File

@@ -5,11 +5,27 @@
height: 100%;
width: 100%;
margin: 0;
overflow: scroll;
overflow: auto;
font-family: inherit;
font-size: 1rem;
line-height: inherit;
counter-reset: line;
}
code {
counter-increment: line;
}
code::before {
content: counter(line);
display: inline-block;
width: 2em; /* Fixed width */
padding: 0 1em 0.3em 0;
margin-right: .5em;
color: #888;
-webkit-user-select: none;
}
{% endblock styles %}
{% block content %}<pre><code>{{ content|safe }}</code></pre>{% endblock content %}
{% block content %}<pre>{{ content|safe }}</pre>{% endblock content %}