8.3 KiB
Analiza repozytorium: bin (Pastebin)
Data analizy: 2026-04-21
1. Cel projektu
Bin to minimalistyczny, samodzielny serwis pastebin akceptujący zarówno tekst, jak i pliki binarne (obrazy, PDF-y itp.). Filozofia projektu opiera się na prostocie, łatwości wdrożenia i minimalizmie. W odróżnieniu od tradycyjnych pastebinów nie wymaga bazy danych — wszystkie pasty przechowywane są w płaskim systemie plików. Projekt zawiera klienty: interfejs webowy, CLI i integrację z Vimem.
Live demo: https://basedbin.fly.dev
Docker: wantguns/bin (multi-arch: amd64 + arm64)
2. Stack technologiczny
| Warstwa | Technologia |
|---|---|
| Język | Rust (Edition 2021) |
| Framework web | Rocket 0.5.0-rc.1 (async) |
| Szablony HTML | Tera (via rocket_dyn_templates) |
| Kolorowanie składni | Syntect 4.6.0 + motyw Ayu Dark |
| Frontend | Vanilla JavaScript, HTML, CSS (brak frameworków) |
| Detekcja MIME | tree_magic 0.2.3 |
| CLI args | clap 3.0.9 |
| Kryptografia | sha256 (ETag) |
| Obsadzanie zasobów | rust-embed 6.3.0 |
| Konteneryzacja | Docker (obraz scratch) |
| Deployment | Fly.io |
3. Struktura katalogów
bin/
├── src/ # Kod źródłowy Rust
│ ├── main.rs # Punkt wejścia, setup serwera, parsowanie CLI
│ ├── routes/ # Handlery endpointów HTTP
│ │ ├── mod.rs
│ │ ├── index.rs # GET / – strona główna
│ │ ├── upload.rs # POST / – upload pliku binarnego
│ │ ├── submit.rs # POST /submit – formularz tekstowy
│ │ ├── retrieve.rs # GET /<id> – surowe pobranie pasty
│ │ ├── pretty_retrieve.rs # GET /p/<id> – wyświetlanie z podświetlaniem
│ │ └── static_files.rs # GET /static/<file> – zasoby statyczne
│ └── models/
│ ├── mod.rs
│ ├── paste_id.rs # Generowanie i walidacja ID pasty
│ ├── pretty_syntax.rs # Parsowanie rozszerzenia z URL
│ ├── pretty.rs # Logika podświetlania składni
│ └── response_wrapper.rs # Abstrakcja odpowiedzi HTTP
│
├── templates/ # Szablony Tera
│ ├── base.html.tera
│ ├── index.html.tera
│ └── pretty.html.tera
│
├── static/ # Zasoby osadzone w binarce
│ ├── css/
│ ├── js/
│ ├── fonts/ # Iosevka (ttf + woff2)
│ └── media/ # Favicony
│
├── resources/ # Zasoby binarne (syntaksy, motyw)
├── contrib/cli/client # Skrypt bash CLI
├── Cargo.toml # Manifest projektu
├── build.rs # Wstrzykiwanie git hash do binarki
├── Dockerfile # Wielostopniowy build
├── docker-compose.yml
└── fly.toml # Konfiguracja Fly.io
4. Endpointy API
| Metoda | Ścieżka | Opis |
|---|---|---|
| GET | / |
Strona główna z formularzem |
| GET | /static/<file> |
Zasoby statyczne (CSS, JS, czcionki) |
| POST | / |
Upload pliku binarnego → zwraca ID |
| POST | /submit |
Formularz tekstowy → redirect do /p/<id>.<ext> |
| GET | /<id> |
Surowa treść pasty |
| GET | /<id>.<ext> |
Surowa treść z rozszerzeniem (rank=1) |
| GET | /p/<id> |
Pasta z podświetlaniem składni (HTML) |
| GET | /p/<id>.<ext> |
Pasta z wymuszonym językiem (rank=1) |
Priorytet routingu: trasy z rozszerzeniem (rank=1) są dopasowywane przed trasami generycznymi (rank=2).
5. Modele danych
PasteId
pub struct PasteId<'a>(Cow<'a, str>)
- Generuje losowe 6-znakowe ID alfanumeryczne (36^6 ≈ 2,1 mld kombinacji)
- Implementuje
FromParamdla automatycznej walidacji URL w Rocket
PasteIdSyntax
pub struct PasteIdSyntax<'a> { syn_id: Cow<'a, str> }
- Parsuje URL typu
/p/abc123.cppna nazwę pliku i rozszerzenie get_fname()→"abc123",get_ext()→"cpp"
ResponseWrapper
enum ResponseWrapper<R> {
MetaInterfaceResponse(R),
PrettyPasteContentResponse(R, SystemTime),
RawPasteContentResponse(R, SystemTime),
Redirect(Box<Redirect>),
NotFound(String),
ServerError(String),
}
Centralnie zarządza nagłówkami HTTP:
Server: bin v.<VERSION> (<GIT_HASH>)ETag,Last-Modified,Cache-Control
6. Zależności (Cargo.toml)
| Crate | Wersja | Zastosowanie |
|---|---|---|
rand |
0.8.4 | Generowanie ID |
rocket |
0.5.0-rc.1 | Framework web |
tree_magic |
0.2.3 | Detekcja MIME |
syntect |
4.6.0 | Podświetlanie składni |
rust-embed |
6.3.0 | Osadzanie zasobów |
clap |
3.0.9 | Parsowanie CLI |
once_cell |
1 | Lazy static |
sha256 |
1 | ETag |
time |
0.3 | Formatowanie czasu |
rocket_dyn_templates |
0.1.0-rc.1 | Szablony Tera |
7. System budowania
build.rs
Przechwytuje hash commita git i wstrzykuje go jako stałą czasu kompilacji (GIT_HASH).
.cargo/config.toml
- Kompilacja statyczna:
-C target-feature=+crt-static - Domyślny target:
x86_64-unknown-linux-gnu - Cross-kompilacja ARM64:
aarch64-linux-gnu-gcc
Docker (wielostopniowy)
- Builder: obraz Rust →
cargo build --release - Runner: obraz
scratch(pusty) + tylko binarka - Rezultat: minimalistyczny obraz (~20-30 MB)
8. Konfiguracja serwera
Argumenty CLI
| Flag | Domyślnie | Opis |
|---|---|---|
-a |
127.0.0.1 |
Adres nasłuchu |
-p |
6162 |
Port |
-u |
./upload |
Katalog przechowywania plików |
-b |
100 MiB |
Limit rozmiaru uploadu binarnego |
-c |
off | Pokaż opis klienta CLI na stronie głównej |
Zmienne środowiskowe (prefiks BIN_)
BIN_PORT=6163
BIN_ADDRESS=0.0.0.0
BIN_LIMITS={form="16 MiB"}
BIN_WORKERS=8
BIN_IDENT=false
9. Architektura i wzorce projektowe
-
Embedded Resources — wszystkie zasoby (CSS, JS, czcionki, definicje syntaksy) skompilowane w binarce (
rust-embed). Zero zależności zewnętrznych. -
Response Wrapper — generyczny wrapper abstrakcji odpowiedzi z centralizowaną logiką nagłówków HTTP.
-
Type-Safe URL Params —
PasteIdiPasteIdSyntaximplementują traitFromParam— Rocket automatycznie waliduje parametry URL. -
Lazy Static —
BINARY_ETAGobliczany raz przy starcie (SHA256 wersji) viaonce_cell::sync::Lazy. -
Build-Time Version Injection — hash git osadzony w czasie kompilacji, widoczny w nagłówku
Server. -
Flat Filesystem Storage — brak bazy danych; pasty jako pliki w katalogu
./upload.
10. Frontend (JavaScript)
index.js (~150 linii)
- Drag-and-drop upload plików
- Wklejanie obrazów ze schowka (
pasteevent) - Tab jako 4 spacje (nie nawigacja formularza)
- Ctrl+Enter → submit
- Dynamiczne pokazywanie/ukrywanie UI
- Fork przez localStorage
pretty.js
- Przełącznik zawijania: 3 stany (brak → auto → 80 znaków)
- Fork: kopiuje treść do localStorage, przekierowuje na główną
- Raw: przełącza
/p/<id>→/<id> - New: przejście do strony głównej
11. Strategia cachowania
| Typ odpowiedzi | Cache-Control |
|---|---|
| Pasta pretty | max-age=604800, stale-while-revalidate=86400 |
| Pasta raw | max-age=604800, immutable |
| Meta/Statyczne | ETag (SHA256 wersji) → 304 Not Modified |
12. Statystyki kodu
| Metryka | Wartość |
|---|---|
| Pliki źródłowe Rust | 13 |
| Linie kodu Rust | ~600 |
| Szablony HTML | 3 |
| Pliki JavaScript | 2 |
| Arkusze CSS | 2 |
| Obsługiwane języki (syntaksy) | 100+ |
| Endpointy API | 7 |
| Typy odpowiedzi HTTP | 6 |
13. Algorytmy kluczowe
Generowanie ID
1. rand::thread_rng() → thread-safe RNG
2. Alphanumeric distribution (a-z, A-Z, 0-9)
3. Pobierz 6 próbek → String
4. Opakuj w Cow::Owned
Detekcja MIME (upload binarny)
1. Wczytaj bajty pliku
2. tree_magic::from_filepath() → MIME
3. MIME zawiera "text" → /p/<id> (kolorowanie)
4. Inaczej → /<id> (surowe pobranie)
Renderowanie składni
1. Parsuj URL /p/abc123.cpp
2. Wyodrębnij rozszerzenie ("cpp")
3. Znajdź definicję syntaxu w syntect
4. Fallback → plain_text
5. Renderuj HTML z motywem Ayu Dark