Merge pull request #42 from zer0bin-dev/main

This commit is contained in:
Kainoa Kanter 2022-03-23 23:30:44 -07:00 committed by GitHub
commit c49cc4191a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 783 additions and 1278 deletions

View File

@ -5,6 +5,6 @@
- Your code must be formatted with the formatters we use
- The frontend must use our `prettier` config
- The backend must use `rustfmt`
- The frontend must be installed and compiled with `yarn` and `parcel`
- The frontend must be installed with `yarn` and compiled with `parcel`
- Additional frontend packages must not be considered bloated
- All commits must follow [Conventional Commits](https://marketplace.visualstudio.com/items?itemName=vivaxy.vscode-conventional-commits)

121
README.md
View File

@ -10,127 +10,64 @@
<br>
<p align="center">
<a href="https://github.com/zer0bin-dev/zer0bin/stargazers">
<img alt="Stargazers" src="https://custom-icon-badges.herokuapp.com/github/stars/zer0bin-dev/zer0bin?style=for-the-badge&logo=star&color=f6c177&logoColor=ebbcba&labelColor=191724"></a>
<img alt="Stargazers" src="https://custom-icon-badges.herokuapp.com/github/stars/zer0bin-dev/zer0bin?style=for-the-badge&logo=star&color=f6c177&logoColor=eb6f92&labelColor=191724"></a>
<a href="https://github.com/zer0bin-dev/zer0bin/wiki">
<img alt="Wiki" src="https://custom-icon-badges.herokuapp.com/badge/read_the-wiki-ebbcba?style=for-the-badge&logo=repo&logoColor=eb6f92&labelColor=191724"></a>
<a href="https://github.com/zer0bin-dev/zer0bin/releases/latest">
<img alt="Releases" src="https://img.shields.io/github/release/zer0bin-dev/zer0bin?style=for-the-badge&logo=github&color=31748f&logoColor=ebbcba&labelColor=191724"/></a>
<img alt="Releases" src="https://img.shields.io/github/release/zer0bin-dev/zer0bin?style=for-the-badge&logo=github&color=31748f&logoColor=eb6f92&labelColor=191724"/></a>
<a href="https://github.com/zer0bin-dev/zer0bin/blob/main/LICENSE">
<img alt="License" src="https://custom-icon-badges.herokuapp.com/github/license/zer0bin-dev/zer0bin?style=for-the-badge&logo=law&color=c4a7e7&logoColor=ebbcba&labelColor=191724"></a>
<img alt="License" src="https://custom-icon-badges.herokuapp.com/github/license/zer0bin-dev/zer0bin?style=for-the-badge&logo=law&color=c4a7e7&logoColor=eb6f92&labelColor=191724"></a>
<a href="https://github.com/zer0bin-dev/zer0bin/issues">
<img alt="Issues" src="https://custom-icon-badges.herokuapp.com/github/issues/zer0bin-dev/zer0bin?style=for-the-badge&logo=issue-opened&color=9ccfd8&logoColor=eb6f92&labelColor=191724"></a>
</p>
<br>
</div>
# Features
- ✨ Code highlighting and line numbers (default)
- 📖 Markdown rendering (add `---` or `md` to the start of your paste)
- ‍🧑‍💻 [CLI Client](https://github.com/zer0bin-dev/zer0)
- 🚀 Easily selfhostable
- 👀 View counter
- ⌨️ Keybinds (<kbd>Ctrl</kbd> + <kbd>S</kbd>: save paste, <kbd>Ctrl</kbd> + <kbd>N</kbd>: new paste, <kbd>Ctrl</kbd> + <kbd>D</kbd>: duplicate paste)
- 📱 Mobile-friendly UI
- 🧈 Super smooth scrolling
- 🖼️ Badge generation for stats (seen below)
# Public instances
Submit your public instance [here](https://github.com/Domterion/zer0bin/issues/new?assignees=&labels=&template=03_public_instance.md&title=%F0%9F%9A%80+)!
| Website | Expiration | Max paste size | Version | Country |
| ---------------------------------------------- | ---------- | -------------- | ------- | ------- |
| [zer0b.in](https://zer0b.in) | 90 days | 50,000 chars | v0.7.1 | 🇺🇸 USA |
| [stepbro.voring.me](https://stepbro.voring.me) | 365 days | 69,000 chars | v0.7.1 | 🇺🇸 USA |
| [tinker.nz](https://tinker.nz/) | 28 days | 69,420 chars | v0.7.1 | 🇳🇿 NZ |
| URL | Expiration | Max paste size | Total Pastes | Version | Country |
| ---------------------------------------------- | ---------- | -------------- | -------------------------------------------- | --------------------------------------------- | ------- |
| [zer0b.in](https://zer0b.in) | 90 days | 50,000 chars | ![pastes](https://zer0b.in/api/b/t) | ![version](https://zer0b.in/api/b/v) | 🇺🇸 USA |
| [stepbro.voring.me](https://stepbro.voring.me) | 365 days | 69,000 chars | ![pastes](https://stepbro.voring.me/api/b/t) | ![version](https://stepbro.voring.me/api/b/v) | 🇺🇸 USA |
| [tinker.nz](https://tinker.nz/) | 28 days | 69,420 chars | ![pastes](https://tinker.nz/api/b/t) | ![version](https://tinker.nz/api/b/v) | 🇳🇿 NZ |
# Technologies used
### Frontend:
<a href="https://www.typescriptlang.org/"><img src="https://github.com/tandpfun/skill-icons/raw/main/icons/TypeScript.svg" height=40/></a> <a href="https://sass-lang.com/"><img src="https://github.com/tandpfun/skill-icons/raw/main/icons/Sass.svg" height=40/></a> <a href="https://pugjs.org/"><img src="https://github.com/tandpfun/skill-icons/raw/main/icons/Pug-Dark.svg" height=40/></a> <a href="https://rosepinetheme.com/"><img src="https://cdn.discordapp.com/attachments/810799100940255260/953176309444542464/RosePine.svg" height=40/></a> <a href="https://highlightjs.org/"><img src="https://cdn.discordapp.com/attachments/810799100940255260/953177926688464936/HLJS.svg" height=40/></a> <a href="https://github.com/ant-design/ant-design-icons"><img src="https://cdn.discordapp.com/attachments/810799100940255260/953181625259266059/AntIcons-Dark.svg" height=40/></a> <a href="https://github.com/idiotWu/smooth-scrollbar"><img src="https://cdn.discordapp.com/attachments/810799100940255260/953564432628322364/SmoothScrollJS.svg" height=40/></a><br><a href="https://atomiks.github.io/tippyjs/"><img src="https://cdn.discordapp.com/attachments/872332549777666078/955624715521769522/Tippy.svg" height=40/></a> <a href="https://github.com/loonywizard/js-confetti"><img src="https://cdn.discordapp.com/attachments/810799100940255260/955609316042362930/JSConfetti.svg" height=40/></a> <a href="https://github.com/hadialqattan/no-darkreader"><img src="https://cdn.discordapp.com/attachments/810799100940255260/955869669535907870/NoDarkReader.svg" height=40/></a> <a href="https://parceljs.org/"><img src="https://user-images.githubusercontent.com/44733677/158683062-17ac3b62-cacd-4add-babb-1f74f36020d8.svg" height=40/></a> <a href="https://prettier.io/"><img src="https://cdn.discordapp.com/attachments/810799100940255260/953339670538887318/Prettier.svg" height=40/></a> <a href="https://yarnpkg.org"><img src="https://cdn.discordapp.com/attachments/810799100940255260/954823377493852170/Yarn.svg" height=40/></a> <a href="https://transfonter.org"><img src="https://user-images.githubusercontent.com/44733677/159066877-234f68ba-e95c-439d-b5fe-74def49dc762.svg" height=40></a>
<a href="https://www.typescriptlang.org/"><img src="https://github.com/tandpfun/skill-icons/raw/main/icons/TypeScript.svg" height=40/></a> <a href="https://definitelytyped.org/"><img src="https://cdn.discordapp.com/attachments/810799100940255260/953567495321710602/DefinitelyTyped.svg" height=40/></a> <a href="https://sass-lang.com/"><img src="https://github.com/tandpfun/skill-icons/raw/main/icons/Sass.svg" height=40/></a> <a href="https://pugjs.org/"><img src="https://github.com/tandpfun/skill-icons/raw/main/icons/Pug-Dark.svg" height=40/></a> <a href="https://rosepinetheme.com/"><img src="https://cdn.discordapp.com/attachments/810799100940255260/953176309444542464/RosePine.svg" height=40/></a> <a href="https://highlightjs.org/"><img src="https://cdn.discordapp.com/attachments/810799100940255260/956227499229061140/hljs.svg" height=40/></a> <a href="https://marked.js.org/"><img src="https://cdn.discordapp.com/attachments/810799100940255260/956263178961047612/MarkedJS.svg" height=40/></a> <a href="https://github.com/ant-design/ant-design-icons"><img src="https://cdn.discordapp.com/attachments/810799100940255260/956227498985799690/anticons.svg" height=40/></a><br><a href="https://github.com/idiotWu/smooth-scrollbar"><img src="https://cdn.discordapp.com/attachments/810799100940255260/953564432628322364/SmoothScrollJS.svg" height=40/></a> <a href="https://atomiks.github.io/tippyjs/"><img src="https://cdn.discordapp.com/attachments/872332549777666078/955624715521769522/Tippy.svg" height=40/></a> <a href="https://github.com/loonywizard/js-confetti"><img src="https://cdn.discordapp.com/attachments/810799100940255260/955609316042362930/JSConfetti.svg" height=40/></a> <a href="https://github.com/hadialqattan/no-darkreader"><img src="https://cdn.discordapp.com/attachments/810799100940255260/955869669535907870/NoDarkReader.svg" height=40/></a> <a href="https://parceljs.org/"><img src="https://user-images.githubusercontent.com/44733677/158683062-17ac3b62-cacd-4add-babb-1f74f36020d8.svg" height=40/></a> <a href="https://prettier.io/"><img src="https://cdn.discordapp.com/attachments/810799100940255260/953339670538887318/Prettier.svg" height=40/></a> <a href="https://yarnpkg.org"><img src="https://cdn.discordapp.com/attachments/810799100940255260/954823377493852170/Yarn.svg" height=40/></a> <a href="https://transfonter.org"><img src="https://user-images.githubusercontent.com/44733677/159066877-234f68ba-e95c-439d-b5fe-74def49dc762.svg" height=40></a>
### Backend:
<a href="https://www.rust-lang.org/"><img src="https://github.com/tandpfun/skill-icons/raw/main/icons/Rust.svg" height=40/></a> <a href="https://actix.rs/"><img src="https://user-images.githubusercontent.com/44733677/158648238-0586f185-4e0c-43bc-b6a9-effd18b3b1ac.svg" height=40/></a> <a href="https://github.com/serde-rs/serde"><img src="https://cdn.discordapp.com/attachments/810799100940255260/953176309964627978/Serde-Dark.svg" height=40/></a> <a href="https://github.com/launchbadge/sqlx"><img src="https://cdn.discordapp.com/attachments/810799100940255260/953176310354673704/SQLX-Dark.svg" height=40/></a> <a href="https://github.com/chronotope/chrono"><img src="https://cdn.discordapp.com/attachments/810799100940255260/953178919169835018/NPM-svg.png" height=40/></a> <a href="https://www.postgresql.org/"><img src="https://github.com/tandpfun/skill-icons/raw/main/icons/PostgreSQL-Dark.svg" height=40/></a> <a href="https://github.com/nikolay-govorov/nanoid"><img src="https://cdn.discordapp.com/attachments/810799100940255260/953176309629067354/NanoID-Dark.svg" height=40/></a>
<a href="https://www.rust-lang.org/"><img src="https://github.com/tandpfun/skill-icons/raw/main/icons/Rust.svg" height=40/></a> <a href="https://actix.rs/"><img src="https://user-images.githubusercontent.com/44733677/158648238-0586f185-4e0c-43bc-b6a9-effd18b3b1ac.svg" height=40/></a> <a href="https://github.com/serde-rs/serde"><img src="https://cdn.discordapp.com/attachments/810799100940255260/956227498792849418/serde.svg" height=40/></a> <a href="https://github.com/launchbadge/sqlx"><img src="https://cdn.discordapp.com/attachments/810799100940255260/956227498608320562/sqlx.svg" height=40/></a> <a href="https://github.com/chronotope/chrono"><img src="https://cdn.discordapp.com/attachments/810799100940255260/953178919169835018/NPM-svg.png" height=40/></a> <a href="https://www.postgresql.org/"><img src="https://github.com/tandpfun/skill-icons/raw/main/icons/PostgreSQL-Dark.svg" height=40/></a> <a href="https://github.com/nikolay-govorov/nanoid"><img src="https://cdn.discordapp.com/attachments/810799100940255260/953176309629067354/NanoID-Dark.svg" height=40/></a> <a href="https://github.com/cgburgess/badge-maker"><img src="https://cdn.discordapp.com/attachments/810799100940255260/956244924930617385/RustBadgeMaker.svg" height=40/></a>
### Misc:
<a href="https://nginx.com/"><img src="https://github.com/tandpfun/skill-icons/raw/main/icons/Nginx.svg" height=40/></a> <!-- <a href="https://docker.com/"><img src="https://github.com/tandpfun/skill-icons/raw/main/icons/Docker.svg" height=40/></a> --> <a href="https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax"><img src="https://github.com/tandpfun/skill-icons/raw/main/icons/Markdown-Dark.svg" height=40/></a> <a href="https://git-scm.com/"><img src="https://github.com/tandpfun/skill-icons/raw/main/icons/Git.svg" height=40/></a> <a href="https://www.conventionalcommits.org/"><img src="https://cdn.discordapp.com/attachments/810799100940255260/954930714007191582/ConventionalCommits.svg" height=40/></a>
<a href="https://nginx.com/"><img src="https://github.com/tandpfun/skill-icons/raw/main/icons/Nginx.svg" height=40/></a> <!-- <a href="https://docker.com/"><img src="https://github.com/tandpfun/skill-icons/raw/main/icons/Docker.svg" height=40/></a> --> <a href="https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax"><img src="https://github.com/tandpfun/skill-icons/raw/main/icons/Markdown-Dark.svg" height=40/></a> <a href="https://git-scm.com/"><img src="https://github.com/tandpfun/skill-icons/raw/main/icons/Git.svg" height=40/></a> <a href="https://www.conventionalcommits.org/"><img src="https://cdn.discordapp.com/attachments/810799100940255260/955896756095320074/ConventionalCommits.svg" height=40/></a>
###### (Sorta) made with [Skill Icons](https://skillicons.dev/)
# API
# More info
- **GET** `/api/p/:id` - Get a paste by ID
- **POST** `/api/p/n` - Post a new paste
- **GET** `/api/s` - Get stats about the instance
### Looking for API reference, self-hosting instructions, and/or benchmarks?
# Self-host instructions
<a href="https://github.com/zer0bin-dev/zer0bin/wiki">
<img alt="Stargazers" src="https://custom-icon-badges.herokuapp.com/badge/read_the-wiki-ebbcba?style=for-the-badge&logo=repo&logoColor=eb6f92&labelColor=191724" height=50></a>
### Requirements
- 🦀 [Rust](https://www.rust-lang.org/) ≥ 1.58.0
- 🐈 [Yarn](https://yarnpkg.com/) ≥ 1.0.0
- 🐘 [PostgreSQL](https://www.postgresql.org/) ≥ 9.6
- 🦝 [Nginx](https://www.nginx.com/) ≥ 1.18.0
- 🌾 [Brotli plugin](https://github.com/google/ngx_brotli) recommended
- 🐧 [Linux](https://kernel.org/) or 😈 [FreeBSD](https://freebsd.org/)
- 🌄 Domain with [SSL](https://letsencrypt.org/)
<details>
<summary><h3>Steps</h3></summary>
Please run each command one at a time!
```bash
# export EDITOR=nano
git clone https://github.com/zer0bin-dev/zer0bin && cd zer0bin
$EDITOR example.nginx # Edit as appropriate
mv example.nginx yourdomain.tld
sudo cp ./yourdomain.tld /etc/nginx/sites-available
sudo cp ./yourdomain.tld /etc/nginx/sites-enabled
systemctl nginx restart # Or whichever process manager you use
cd frontend
cp config.example.json config.json
$EDITOR config.json # Edit as appropriate
yarn && yarn build
cd ../backend
psql -f schema.sql -U postgres zer0bin
cp config.example.json config.json
$EDITOR config.json # Edit as appropriate
cargo build --release
./target/release/zer0bin-bin # Preferably in a tmux session or as a service
```
</details>
<details>
<summary><h3>Configuration</h3></summary>
| Key | Values | Description |
| ------------------------------------------ | ------------------------ | ------------------------------------------------------------------------------ |
| server.backend_host | 127.0.0.1 or 0.0.0.0 | The host to run the backend on |
| server.backend_port | Any open port | The port to run the backend on |
| pastes.character_limit | Number up to 2^64 - 1 | The amount of characters allowed in a single paste |
| pastes.days_til_expiration | Number up to 2^63 or -1 | The days till a paste is to expire. If set to -1 then pastes will never expire |
| pastes.id_length | Number up to 2^64 - 1 | The length of the ID for each paste |
| databases.postgres_uri | PostreSQL Connection URI | The URI to use when connecting to a PostgreSQL database |
| ratelimits.seconds_in_between_pastes | Number up to 2^64 - 1 | The seconds between paste uploads |
| ratelimits.allowed_pastes_before_ratelimit | Number up to 2^32 - 1 | Amount of requests that can be made before they are blocked and have to wait |
| logging.on_post_paste | true or false | Log on new paste is made |
| logging.on_get_paste | true or false | Log on paste get |
</details>
# Benchmarks
###### Measured with Lighthouse and Firefox Network Performance Analysis
<details>
<summary><h3>Results</h3></summary>
### Homepage
![image](https://user-images.githubusercontent.com/44733677/159143364-a6e06aee-256e-4998-83f2-541c77e85184.png)
![image](https://user-images.githubusercontent.com/44733677/159143316-c8e95ed6-ec32-44b3-abcc-18bd204a004d.png)
![image](https://user-images.githubusercontent.com/44733677/159143321-aa8a5f6f-53d2-40d1-bff9-71d72347e7ea.png)
### 20 line paste
![image](https://user-images.githubusercontent.com/44733677/159143373-6e8787d8-61ef-44dc-9d66-52cd74e2a495.png)
![image](https://user-images.githubusercontent.com/44733677/159143298-f8c7420d-ff76-4db7-a98e-03ff3615315e.png)
![image](https://user-images.githubusercontent.com/44733677/159143303-efb71e15-623a-42cc-abf6-7e602843d801.png)
</details>
# Contributing
See [CONTRIBUTING.md](./CONTRIBUTING.md)

54
backend/Cargo.lock generated Executable file → Normal file
View File

@ -71,7 +71,7 @@ dependencies = [
"http",
"httparse",
"httpdate",
"itoa",
"itoa 1.0.1",
"language-tags",
"local-channel",
"log",
@ -182,7 +182,7 @@ dependencies = [
"encoding_rs",
"futures-core",
"futures-util",
"itoa",
"itoa 1.0.1",
"language-tags",
"log",
"mime",
@ -268,12 +268,13 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "backend"
version = "0.7.1"
version = "1.0.1"
dependencies = [
"actix-cors",
"actix-governor",
"actix-rt",
"actix-web",
"badge-maker",
"chrono",
"nanoid",
"serde",
@ -281,12 +282,37 @@ dependencies = [
"sqlx",
]
[[package]]
name = "badge-maker"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "383647c1ae7388c2801ef995f6de2f9551a361ad637f2a0e4fea41c51dd37b81"
dependencies = [
"aho-corasick",
"bincode",
"itoa 0.4.8",
"lazy_static",
"regex",
"seahash",
"serde",
"thiserror",
]
[[package]]
name = "base64"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]]
name = "bitflags"
version = "1.3.2"
@ -819,7 +845,7 @@ checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03"
dependencies = [
"bytes",
"fnv",
"itoa",
"itoa 1.0.1",
]
[[package]]
@ -873,6 +899,12 @@ dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
[[package]]
name = "itoa"
version = "1.0.1"
@ -1364,6 +1396,12 @@ dependencies = [
"untrusted",
]
[[package]]
name = "seahash"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
[[package]]
name = "semver"
version = "1.0.6"
@ -1396,7 +1434,7 @@ version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
dependencies = [
"itoa",
"itoa 1.0.1",
"ryu",
"serde",
]
@ -1408,7 +1446,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [
"form_urlencoded",
"itoa",
"itoa 1.0.1",
"ryu",
"serde",
]
@ -1533,7 +1571,7 @@ dependencies = [
"hex",
"hmac",
"indexmap",
"itoa",
"itoa 1.0.1",
"libc",
"log",
"md-5",
@ -1652,7 +1690,7 @@ version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "004cbc98f30fa233c61a38bc77e96a9106e65c88f2d3bef182ae952027e5753d"
dependencies = [
"itoa",
"itoa 1.0.1",
"libc",
"num_threads",
"time-macros",

View File

@ -1,6 +1,6 @@
[package]
name = "backend"
version = "0.7.1"
version = "1.0.1"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -14,6 +14,8 @@ actix-web = "4.0.0-rc.1"
actix-cors = "0.6.0"
actix-governor = "0.3.0"
badge-maker = "0.2.1"
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "postgres", "chrono"] }
chrono = { version = "0.4.19", features = ["serde"] }

View File

@ -14,7 +14,7 @@ use config::Config;
use sqlx::{postgres::PgPoolOptions, PgPool};
use crate::routes::{get_paste, get_stats, new_paste};
use crate::routes::{get_paste, get_stats, get_total_pastes_badge, get_version_badge, new_paste};
#[derive(Clone)]
pub struct AppState {
@ -62,8 +62,12 @@ async fn main() -> io::Result<()> {
web::scope("/p")
.wrap(Governor::new(&paste_governor))
.service(get_paste)
.service(new_paste)
// .service(get_raw_paste),
.service(new_paste), // .service(get_raw_paste),
)
.service(
web::scope("/b")
.service(get_version_badge)
.service(get_total_pastes_badge),
)
})
.bind(address)?

View File

@ -4,8 +4,9 @@ use actix_web::{
HttpResponse, Responder,
};
use chrono::Duration;
use badge_maker::{BadgeBuilder, Style};
use chrono::Duration;
use nanoid::nanoid;
use sqlx::{postgres::PgRow, types::chrono::Utc, Row};
@ -17,35 +18,7 @@ use crate::{
AppState,
};
#[get("/s")]
pub async fn get_stats(state: web::Data<AppState>) -> impl Responder {
let version = env!("CARGO_PKG_VERSION").to_string();
// TODO: Maybe there's a less hacky way to do this..?
let count: Result<i64, sqlx::Error> = sqlx::query(r#"SELECT COUNT(*) FROM pastes"#)
.try_map(|row: PgRow| row.try_get::<i64, _>("count"))
.fetch_one(&state.pool)
.await;
if let Err(e) = count {
eprintln!("Error occurred while retrieving paste count: {:?}", e);
return HttpResponse::InternalServerError().json(ApiResponse {
success: false,
data: ApiError {
message: "Error occurred while retrieving paste count, please try again."
.to_string(),
},
});
}
HttpResponse::Ok().json(ApiResponse {
success: true,
data: GetStatsResponse {
count: count.unwrap(),
version
},
})
}
// Pastes
#[get("/{id}")]
pub async fn get_paste(state: web::Data<AppState>, id: web::Path<String>) -> impl Responder {
@ -186,14 +159,11 @@ pub async fn new_paste(
if state.config.logging.on_post_paste {
println!("[POST] id={} length={}", id, content.len());
}
HttpResponse::Ok().json(ApiResponse {
success: true,
data: NewPasteResponse {
id,
content,
},
})},
success: true,
data: NewPasteResponse { id, content },
})
}
Err(e) => {
eprintln!("Error occurred while creating paste: {:?}", e);
@ -206,3 +176,86 @@ pub async fn new_paste(
}
}
}
// Stats
#[get("/s")]
pub async fn get_stats(state: web::Data<AppState>) -> impl Responder {
let version = env!("CARGO_PKG_VERSION").to_string();
// TODO: Maybe there's a less hacky way to do this..?
let count: Result<i64, sqlx::Error> = sqlx::query(r#"SELECT COUNT(*) FROM pastes"#)
.try_map(|row: PgRow| row.try_get::<i64, _>("count"))
.fetch_one(&state.pool)
.await;
if let Err(e) = count {
eprintln!("Error occurred while retrieving paste count: {:?}", e);
return HttpResponse::InternalServerError().json(ApiResponse {
success: false,
data: ApiError {
message: "Error occurred while retrieving paste count, please try again."
.to_string(),
},
});
}
HttpResponse::Ok().json(ApiResponse {
success: true,
data: GetStatsResponse {
count: count.unwrap(),
version,
},
})
}
// Badges
#[get("/v")]
pub async fn get_version_badge() -> impl Responder {
let version = env!("CARGO_PKG_VERSION").to_string();
let badge = BadgeBuilder::new()
.label("version")
.message(&version)
.color_parse("#31748f")
.label_color_parse("#191724")
.style(Style::FlatSquare)
.build()
.expect("Failed to build badge")
.svg();
HttpResponse::Ok().content_type("image/svg+xml").body(badge)
}
#[get("/t")]
pub async fn get_total_pastes_badge(state: web::Data<AppState>) -> impl Responder {
let count: Result<i64, sqlx::Error> = sqlx::query(r#"SELECT COUNT(*) FROM pastes"#)
.try_map(|row: PgRow| row.try_get::<i64, _>("count"))
.fetch_one(&state.pool)
.await;
if let Err(e) = count {
eprintln!("Error occurred while retrieving paste count: {:?}", e);
return HttpResponse::InternalServerError().json(ApiResponse {
success: false,
data: ApiError {
message: "Error occurred while retrieving paste count, please try again."
.to_string(),
},
});
}
let badge = BadgeBuilder::new()
.label("total pastes")
.message(&count.unwrap().to_string())
.color_parse("#ebbcba")
.label_color_parse("#191724")
.style(Style::FlatSquare)
.build()
.expect("Failed to build badge")
.svg();
HttpResponse::Ok().content_type("image/svg+xml").body(badge)
}

View File

@ -1,18 +1,18 @@
# replace `example.tld` with your domain
# replace `/location/of/zer0bin` with the location of the zer0bin folder
# uncomment the `brotli on;` lines if you have the nginx brotoli plugin
# uncomment the `ssl_certificate` lines if you have letsencrypt certs
# comment out line 15 and uncomment line 16 if you have the nginx brotoli plugin
# uncomment lines 51 and 52 if you have letsencrypt certs
server {
#listen 80 is default
server_name example.tld;
return 301 https://example.tld$request_uri;
# brotli on;
}
server {
listen 443;
server_name example.tld;
gzip_static on;
# brotli on;
root /location/of/zer0bin/frontend/dist;
@ -21,10 +21,14 @@ server {
location / {
index index.html;
expires 30d;
add_header Cache-Control "public, no-transform";
}
location ~ \.(css|js|html) {
try_files $uri =404;
expires 30d;
add_header Cache-Control "public, no-transform";
}
location ^~ /.well-known/ {
@ -37,11 +41,13 @@ server {
proxy_pass http://localhost:8000/;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
add_header Last-Modified $date_gmt;
add_header Cache-Control 'no-store, no-cache';
if_modified_since off;
expires off;
etag off;
}
expires 30d;
add_header Cache-Control "public, no-transform";
# ssl_certificate /etc/letsencrypt/live/example.tld/fullchain.pem;
# ssl_certificate_key /etc/letsencrypt/live/example.tld/privkey.pem;
}

3
frontend/config.example.json Executable file → Normal file
View File

@ -1,3 +1,4 @@
{
"api_url": "https://example.tld/api"
"api_url": "https://yourdomain.tld/api",
"confetti_chance": "10"
}

View File

@ -1,32 +1,30 @@
{
"name": "zer0bin",
"source": "index.pug",
"version": "0.7.1",
"version": "1.0.1",
"browserslist": "> 0.5%, last 2 versions, not dead",
"license": "MIT",
"scripts": {
"start": "parcel index.pug",
"build": "parcel build index.pug"
"build": "parcel build index.pug --public-url /"
},
"devDependencies": {
"@parcel/compressor-brotli": "^2.3.2",
"@parcel/compressor-gzip": "^2.3.2",
"@parcel/config-default": "^2.3.2",
"@parcel/transformer-pug": "2.3.2",
"@parcel/transformer-sass": "^2.3.2",
"@types/core-js": "^2.5.5",
"@types/node": "^17.0.21",
"parcel": "^2.3.2"
"@types/node": "^17.0.21"
},
"description": "just a place to paste",
"repository": "https://github.com/Domterion/zer0bin",
"repository": "https://github.com/zer0bin-dev/zer0bin",
"author": "Domterion, ThatOneCalculator",
"dependencies": {
"@ant-design/icons": "^4.7.0",
"@ant-design/icons-svg": "^4.2.1",
"@parcel/core": "^2.4.0",
"@parcel/transformer-pug": "^2.4.0",
"highlight.js": "^11.4.0",
"js-confetti": "^0.10.2",
"marked": "^4.0.12",
"no-darkreader": "^1.0.1",
"parcel": "^2.4.0",
"smooth-scrollbar": "^8.7.4",
"tippy.js": "^6.3.7"
}

View File

@ -38,6 +38,7 @@ githubButton.innerHTML += renderIconDefinitionToSVGElement(GithubOutlined, {
tippy("#save-button", {
content: "Save paste<br><span class='keybind'>Ctrl + S</span>",
placement: "bottom",
animation: "scale",
theme: "rosepine",
allowHTML: true,
@ -45,6 +46,7 @@ tippy("#save-button", {
tippy("#new-button", {
content: "New paste<br><span class='keybind'>Ctrl + N</span>",
placement: "bottom",
animation: "scale",
theme: "rosepine",
allowHTML: true,
@ -52,6 +54,7 @@ tippy("#new-button", {
tippy("#copy-button", {
content: "Duplicate paste<br><span class='keybind'>Ctrl + D</span>",
placement: "bottom",
animation: "scale",
theme: "rosepine",
allowHTML: true,
@ -66,7 +69,29 @@ tippy("#github-button", {
})} ${renderIconDefinitionToSVGElement(HeartOutlined, {
extraSVGAttrs: extraSVGAttrs,
})}</span>`,
placement: "bottom",
animation: "scale",
theme: "rosepine",
allowHTML: true,
})
const observer = new MutationObserver(callback)
function callback() {
let theme = ""
if (window.location.pathname == "/") {
theme = "rosepine"
} else {
theme = "rosepine-extended"
}
document.querySelectorAll("button").forEach(function (btn) {
//@ts-ignore
btn._tippy.setProps({ theme: theme })
})
}
observer.observe(document.getElementById("code-view-pre"), {
attributes: true,
})

View File

@ -1,11 +1,13 @@
import "no-darkreader"
import hljs from "highlight.js"
import { marked } from "marked"
import JSConfetti from "js-confetti"
import Scrollbar from "smooth-scrollbar"
import config from "../config.json"
const apiUrl = config.api_url
const confettiChance = parseInt(config.confetti_chance)
let rawContent = ""
const jsConfetti = new JSConfetti()
@ -125,10 +127,18 @@ function addMessage(message: string) {
function viewPaste(content: string, views: string) {
lineNumbers.innerHTML = ""
for (let i = 1; i <= content.split("\n").length; i++) {
lineNumbers.innerHTML = lineNumbers.innerHTML + `${i}<br>`
if (
content.startsWith("---") ||
content.startsWith("md ") ||
content.startsWith("md\n")
) {
codeView.innerHTML = marked.parse(content.substring(3))
} else {
for (let i = 1; i <= content.split("\n").length; i++) {
lineNumbers.innerHTML = lineNumbers.innerHTML + `${i}<br>`
}
codeView.innerHTML = hljs.highlightAuto(content).value
}
codeView.innerHTML = hljs.highlightAuto(content).value
disable(saveButton)
enable(newButton)
@ -163,7 +173,7 @@ async function savePaste() {
rawContent = res["data"]["content"]
viewPaste(rawContent, "0")
const rand = Math.floor(Math.random() * 40)
const rand = Math.floor(Math.random() * confettiChance * 6)
if (rand < 5) {
jsConfetti.addConfetti({

View File

@ -1,17 +1,17 @@
@font-face {
font-family: "Cartograph CF";
src: url("../fonts/CartographCF-Regular.woff2") format("woff2"),
url("../fonts/CartographCF-Regular.woff") format("woff"),
url("../fonts/CartographCF-Regular.ttf") format("truetype");
src: local("../fonts/CartographCF-Regular.woff2") format("woff2"),
local("../fonts/CartographCF-Regular.woff") format("woff"),
local("../fonts/CartographCF-Regular.ttf") format("truetype");
font-weight: normal;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Cartograph CF";
src: url("../fonts/CartographCF-RegularItalic.woff2") format("woff2"),
url("../fonts/CartographCF-RegularItalic.woff") format("woff"),
url("../fonts/CartographCF-RegularItalic.ttf") format("truetype");
src: local("../fonts/CartographCF-RegularItalic.woff2") format("woff2"),
local("../fonts/CartographCF-RegularItalic.woff") format("woff"),
local("../fonts/CartographCF-RegularItalic.ttf") format("truetype");
font-weight: normal;
font-style: italic;
font-display: swap;

View File

@ -4,11 +4,11 @@ $font-mono: "Cartograph CF", ui-monospace, SFMono-Regular, Menlo, Monaco,
Consolas, "Liberation Mono", "Courier New", monospace;
@import "./font.scss";
.tippy-box[data-theme~="rosepine"] {
.rosepine-parent {
color: $text;
background-color: $bg;
opacity: 0.8;
margin-top: 10px;
padding: 10px;
font-family: $font-mono;
> .tippy-backdrop {
@ -18,3 +18,11 @@ $font-mono: "Cartograph CF", ui-monospace, SFMono-Regular, Menlo, Monaco,
fill: $bg;
}
}
.tippy-box[data-theme~="rosepine"] {
@extend .rosepine-parent;
margin-top: 0.3rem;
}
.tippy-box[data-theme~="rosepine-extended"] {
@extend .rosepine-parent;
margin-top: 2rem;
}

File diff suppressed because it is too large Load Diff