forked from Maurice/pastabble
Initial commit
This commit is contained in:
commit
1d32fb0365
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
/data
|
1161
Cargo.lock
generated
Normal file
1161
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "pastabble"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
kv = { version = "0.24.0", features = ["json-value"] }
|
||||||
|
serde = { version = "1.0.145", features = ["derive"] }
|
||||||
|
chrono = { version = "0.4.22", features = ["serde"] }
|
||||||
|
rand = "0.8.5"
|
||||||
|
rouille = "3.6.1"
|
7
Dockerfile
Normal file
7
Dockerfile
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
FROM alpine:latest
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY ./target/x86_64-unknown-linux-musl/release/ /app
|
||||||
|
COPY ./about.html /app/about.html
|
||||||
|
|
||||||
|
ENTRYPOINT [ "./pastabble" ]
|
21
README.md
Normal file
21
README.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# PASTABBLE
|
||||||
|
|
||||||
|
Welcome at PASTABBLE!
|
||||||
|
|
||||||
|
## About
|
||||||
|
Pastable is a lightweight and fast pastebin alternative and URL-shortener made by Plabble and written in Rust.
|
||||||
|
|
||||||
|
## Environment variables
|
||||||
|
| Variable | Default |
|
||||||
|
| -------- | ------- |
|
||||||
|
| PORT | 8080 |
|
||||||
|
| DATA_DIR | ./data |
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
Place the about.html file from this folder in the same folder as the binary
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
Rust and musl toolchain should be installed
|
||||||
|
```
|
||||||
|
rustup target add x86_64-unknown-linux-musl
|
||||||
|
```
|
28
about.html
Normal file
28
about.html
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>About</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<pre>
|
||||||
|
Welcome at PASTABBLE!
|
||||||
|
|
||||||
|
ABOUT
|
||||||
|
Pastable is a lightweight and fast pastebin alternative, and URL-shortener made by Plabble and written in Rust.
|
||||||
|
|
||||||
|
USAGE
|
||||||
|
Get /: Show this page
|
||||||
|
Get /{id}: Get paste by ID and return in plain text
|
||||||
|
Post /: Create new paste (plain text raw body or form parameter 'content') and return random generated key
|
||||||
|
Post /{id}: Create new paste (plain text raw body) and return requested key if available, else random generated key
|
||||||
|
|
||||||
|
FROM TERMINAL
|
||||||
|
Pipe your input into: curl -F 'content=<-' paste.plabble.org -w "\n"
|
||||||
|
Or post it as raw data like: curl paste.plabble.org -H "Content-Type: text/plain" -d @- -w "\n"
|
||||||
|
You can create an alias for this! To get the result: curl paste.plabble.org/YOURID
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
5
build.sh
Executable file
5
build.sh
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
RUNNER="podman"
|
||||||
|
|
||||||
|
cargo build --target x86_64-unknown-linux-musl --release
|
||||||
|
$RUNNER build -t pastabble:latest .
|
93
src/main.rs
Normal file
93
src/main.rs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
use std::{env, fs};
|
||||||
|
|
||||||
|
use chrono::Utc;
|
||||||
|
use kv::{Config, Store, Json};
|
||||||
|
use paste::Paste;
|
||||||
|
use rand::{Rng, distributions::Alphanumeric};
|
||||||
|
use rouille::{Response, router, try_or_400, post_input};
|
||||||
|
|
||||||
|
mod paste;
|
||||||
|
|
||||||
|
// Generate random key function
|
||||||
|
fn new_key() -> String {
|
||||||
|
rand::thread_rng()
|
||||||
|
.sample_iter(&Alphanumeric)
|
||||||
|
.take(5)
|
||||||
|
.map(char::from)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Get settings
|
||||||
|
let port: u16 = env::var("PORT").unwrap_or(String::from("8080")).parse().expect("Failed to parse PORT variable");
|
||||||
|
let data_dir: String = env::var("DATA_DIR").unwrap_or(String::from("./data"));
|
||||||
|
|
||||||
|
let config = Config::new(data_dir);
|
||||||
|
let store = Store::new(config).expect("Failed to initialize database store");
|
||||||
|
let about = fs::read_to_string("./about.html").expect("about.html not found!!");
|
||||||
|
|
||||||
|
// Get pastes bucket (default)
|
||||||
|
let pastes = store.bucket::<String, Json<Paste>>(Some("pastes"))
|
||||||
|
.expect("Failed to open default bucket");
|
||||||
|
|
||||||
|
// Start server
|
||||||
|
println!("Started PASTABBLE server on port {}", port);
|
||||||
|
rouille::start_server(format!("0.0.0.0:{}", port), move |req| {
|
||||||
|
router!(req,
|
||||||
|
(GET) (/) => {
|
||||||
|
Response::html(&about)
|
||||||
|
},
|
||||||
|
(GET) (/{id: String}) => {
|
||||||
|
// Get note from database, if exists
|
||||||
|
match pastes.get(&id).expect("Failed to access DB") {
|
||||||
|
Some(paste) => {
|
||||||
|
Response::text(paste.0.content)
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
Response::text(format!("Note with ID '{}' not found", &id))
|
||||||
|
.with_status_code(404)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(POST) (/{id: String}) => {
|
||||||
|
// Use provided key or generate new one
|
||||||
|
let key = if !id.is_empty() && !pastes.contains(&id).unwrap() {
|
||||||
|
id
|
||||||
|
} else {
|
||||||
|
loop {
|
||||||
|
let new_key = new_key();
|
||||||
|
if !pastes.contains(&new_key).expect("Could not access store") {
|
||||||
|
break new_key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Try read body from form data, and if not present from request body
|
||||||
|
let body = match post_input!(&req, {
|
||||||
|
content: String
|
||||||
|
}) {
|
||||||
|
Ok(f) => {
|
||||||
|
f.content
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
try_or_400!(rouille::input::plain_text_body(&req))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create and save new note
|
||||||
|
let paste = Json(Paste {
|
||||||
|
content: body,
|
||||||
|
expires: None,
|
||||||
|
language: None,
|
||||||
|
created: Utc::now()
|
||||||
|
});
|
||||||
|
|
||||||
|
pastes.set(&key, &paste).expect("Failed to save note");
|
||||||
|
|
||||||
|
// Return key
|
||||||
|
Response::text(key)
|
||||||
|
},
|
||||||
|
_ => Response::empty_404()
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
13
src/paste.rs
Normal file
13
src/paste.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
use chrono::serde::ts_seconds_option;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||||
|
pub struct Paste {
|
||||||
|
pub content: String,
|
||||||
|
pub language: Option<String>,
|
||||||
|
|
||||||
|
#[serde(with = "ts_seconds_option")]
|
||||||
|
pub expires: Option<DateTime<Utc>>,
|
||||||
|
pub created: DateTime<Utc>
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user