У цій статті автор від першої особи ділиться власним досвідом створення сервісу для обміну файлами на Rust. Він згадує, як колись використовував IRC, де не було простого способу надсилати зображення чи документи, і пояснює, чому вирішив написати свій власний мінімалістичний інструмент rustypaste. Це сервіс, який дозволяє завантажувати файли, скорочувати посилання, створювати одноразові URL та задавати термін дії. Автор крок за кроком показує історію проєкту, його функції та розгортання у хмарі через Shuttle.rs, щоб зробити сервіс публічно доступним. Стаття написана у формі особистої розповіді, що робить її живою та цікавою навіть для тих, хто лише знайомиться з технологіями.
rustypaste » — це власний мінімальний сервіс завантаження/pastebin файлів, написаний на Rust. У цій публікації розповімо про його функції та історію розгортання на shuttle.rs, щоб зробити його загальнодоступним для безкоштовного використання.
Щоб правильно передати ідею, що лежить в основі обміну файлами, мені потрібно розповісти вам історію.
На зорі інтернету спілкування було простішим і, природно, менш безпечним. Однією з речей, якою люди користувалися, був всемогутній Internet Relay Chat (IRC), відома система чату, яка використовується й сьогодні деякими проектами. Колись люди створювали там канали та насолоджувалися незахищеним, менш цензурованим та ніби анонімним спілкуванням. Сьогодні ті часи деякі люди, включаючи мене, вважають золотою ерою інтернету. Все було так мирно, і можна було відчути запах запиленого повітря, що долинає від старого вентилятора ПК, який ледве працює на Windows XP з процесором Intel Pentium 4. *зітхає* Щасливі часи.
Ви не могли ділитися зображеннями в IRC. Принаймні, це було не так просто, оскільки IRC-клієнти були переважно текстовими та розроблені для запуску в терміналі (автор тут говорить про BitchX). Звичайно, це не було так просто та зручно, як сучасні програми для чату, і ми не можемо цього очікувати. Але думаю, що це все ще Cheff’s KISS, що взагалі таке підтримка обміну файлами?
Вона вимагає зберігання файлів десь та їх передачі одержувачу. Є так багато речей, які слід враховувати, такі як обмеження на завантаження, виявлення оголеної людини (якщо вам це важливо), конфіденційність, збереження файлів, додаткові витрати на сервер, дисковий простір тощо. Коротше кажучи, обмін файлами — це РОЗДУМ ! Описувати друзям, як створити кольчужну броню в Minecraft за допомогою виключно текстових повідомлень замість надсилання скріншоту, набагато КРАЩЕ. Або зачекайте, ми можемо використовувати ASCII-зображення, о так, це теж можливо. Правда ж?
На IRC не було простої підтримки обміну файлами, тому людям доводилося вигадувати власні рішення, іншими словами, власні сервери завантаження. За час роботи в IRC я зустрічав багатьох користувачів, які використовують посилання, подібні до наведеного нижче, лише для обміну файлами/зображеннями:
https://paste.xinu.at/jHKy
https://0x0.st/H8hSa.png
У певному сенсі, у сучасному світі робити це здається зайвим і насправді складнішим, ніж просто натискати 3 кнопки на інтерфейсі.
Технології — це, перш за все, контроль . Вважаю, що це особливо актуально сьогодні, оскільки кожен куточок людського мозку легко підкорюється технологіями. Ми глибоко інтегровані з цим світом, і в ситуації, коли ми його не контролюємо, він контролює нас. Кліше, але правда.
У випадку обміну файлами, наявність власного (власного хостингу) сервісу для завантаження ваших файлів – це просто контроль над тим, що ви надаєте цьому віртуальному світу. Те, чим ви хочете поділитися, є там, якщо ви цього хочете, і це там є за обраних вами обставин. Можливо, файли будуть завантажуватися із зовнішнього диска вашого домашнього сервера. Можливо, їх термін дії закінчиться через 2 години. Можливо, ви хочете побачити, хто перейшов за посиланням. Все зводиться до володіння вашими даними.
Якщо врахувати ці моменти, то стає зрозуміло, що недостатні умови часів IRC виявили ідеальне рішення для обміну файлами, що доповнює ідею про те, що централізація — це погано. Досить іронічно, що обмеження IRC призвели до появи самостійно розміщеного обміну файлами.
Спільнота IRC завжди приваблювала користувачів, навіть тих, хто долучився до неї порівняно нещодавно. Спершу для обміну файлами активно використовувались публічні сервіси, однак поступово виникла потреба у власному самохостинговому рішенні. Це привело до створення поста під назвою «Розкрутіть свій власний сервіс хостингу файлів без дурниць з 0x0», опублікованого два роки тому.
Тривалий час як базове рішення застосовувався 0x0, але згодом виникла необхідність перенесення на інший сервер. Переналаштовувати систему було незручно, та й сам факт того, що вона написана на Python, не додавав ентузіазму. Саме тоді з’явилась ідея розробити власний сервіс Pastebin, але вже з використанням мови програмування Rust, яка забезпечує високу швидкість роботи та безпечне управління пам’яттю.
GitHub : https://github.com/orhun/rustypaste
Публічний екземпляр : https://rustypaste.shuttleapp.rs
rustypaste— це мінімальний та простий сервіс завантаження, написаний на Rust та реалізований за допомогою веб-фреймворку Actix. Я обрав Actix тоді, оскільки це був найбільш підходящий фреймворк з точки зору безпеки, продуктивності та простоти. Як постійний користувач rustypasteвже майже 2 роки, можу сказати, що він перебуває у стабільному стані та має купу функцій, що підтримують розширене налаштування.
Найпростіший спосіб взаємодії із rustypasteсервером – це використання , curlале ви також можете скористатися інструментом командного рядка під назвою, rpasteякий написаний на Rust.
Інструмент командного рядка : https://github.com/orhun/rustypaste-cli
Usage:
rpaste [options] <file(s)>
Options:
-h, --help prints help information
-v, --version prints version information
-V, --server-version
retrieves the server version
-o, --oneshot generates one shot links
-p, --pretty prettifies the output
-c, --config CONFIG sets the configuration file
-s, --server SERVER sets the address of the rustypaste server
-a, --auth TOKEN sets the authentication token
-u, --url URL sets the URL to shorten
-r, --remote URL sets the remote URL for uploading
-e, --expire TIME sets the expiration time for the link
Завантажити файл
curl -F "[email protected]" https://rustypaste.shuttleapp.rs rpaste ferris.txt
Скорочення URL-адреси
curl -F "url=https://example.com/some/long/url" https://rustypaste.shuttleapp.rs rpaste -u https://example.com/some/long/url
Завантажити файл з URL-адреси
curl -F "remote=https://example.com/file.png" https://rustypaste.shuttleapp.rs rpaste -r https://example.com/file.png
Термін дії файлу закінчується через 10 хвилин (також працює для URL-адрес)
curl -F "[email protected]" -H "expire:10min" https://rustypaste.shuttleapp.rs rpaste -e 10min ferris.txt
Видалити файл після перегляду один раз
curl -F "[email protected]" https://rustypaste.shuttleapp.rs rpaste -o ferris.txt
Автентифікуватися на сервері
curl -F "[email protected]" -H "Authorization: <auth_token>" https://rustypaste.shuttleapp.rs rpaste -a "<auth_token>" ferris.txt(ви також можете використовувати файл конфігурації)
Для налаштування rustypasteвам потрібен лише один файл конфігурації. Файл за замовчуванням можна переглянути в репозиторії GitHub ( config.toml) .
Ось деякі речі, які ви можете налаштувати та змінити:
Випадкові назви файлів: Кличка домашньої тварини (
capital-mosquito.txt). Буквено-цифровий рядок (yB84D2Dv.txt)
Типи MIME: Підтримує перевизначення та додавання до чорного списку. Підтримує примусове завантаження через
?download=true
Підтримка вимкнення дублікатів завантажень
Автоматичне закінчення терміну дії + автоматичне видалення файлів
Якщо ви хочете розмістити власний rustypasteсервер, є кілька способів зробити це:
Запустіть єдиний бінарний файл (бажано встановлений з менеджера пакетів вашого дистрибутива).
rustypasteдоступний у репозиторіях Arch Linux .
Використовуйте полегшений образ Docker
docker-compose.yml (приклад)
Конфігурація Nginx (приклад)
Або ж ви можете розгорнути його в хмарі!
Shuttle — це хмарна платформа для розробки, створена на Rust, яка дозволяє розгортати ваш застосунок, одночасно піклуючись про всю вашу інфраструктуру.
Shuttle отримує визначення інфраструктури з самого коду Rust за допомогою сигнатур функцій та анотацій . Це чудовий сервіс, який надзвичайно спрощує розгортання хмари та підтримує кілька фреймворків Rust, включаючи Actix. Крім того, ви можете керувати всім (розгортанням, моніторингом, управлінням ресурсами тощо) за допомогою однієї підкоманди cargo, запущеної cargo shuttleз командного рядка. Якщо врахувати все це, не дивно, що вони підтримуються YC😮
Наразі вони підтримують план хобі , який є практично безкоштовним, і ви можете звернутися до них, якщо наступні функції вам не підходять/недостатньо:
Необмежене розгортання
150 тис. запитів на місяць
500 МБ місця для зберігання бази даних
Цього цілком достатньо, rustypasteтому було вирішено скористатися Shuttle.
Однак, у мене виникло одне питання щодо місця для зберігання. Тож я поставив його їм у Discord:
orhun : Як розгортання Shuttle зберігає файли? Чи завантажуються вони всередині ізольованого контейнера? Якщо так, то чи є якісь обмеження щодо дискового простору/пам’яті/процесора?
jonaro00 : Ваш проект працює в контейнері Docker, але запис у файлову систему не рекомендується, оскільки контейнер очищається під час цього
cargo shuttle project restart(що ви робите під час оновлення версії Shuttle або коли щось ламається). Для постійного зберігання вам потрібно використовувати певну форму бази даних. Ви можете перевірити підтримувані бази даних у документації, але також є й інші в розробці.
orhun : Планую автоматично видаляти файли приблизно через годину. Чи рекомендуєте ви використовувати файлову систему в цьому випадку?
jonaro00 : Так, для такого випадку використання, мабуть, все працює добре.
Тим не менш, я не впевнений, чи є якісь обмеження дискового простору для розгортання Shuttle, і ми не змогли з’ясувати це в Discord, тому повідомте мені, якщо вам щось відомо про це. Тепер, коли у нас достатньо інформації про Shuttle, давайте розгорнемо проєкт Rust!
Почніть з встановлення cargo-shuttleта входу в систему:
$ cargo install cargo-shuttle $ cargo shuttle login
Після цього кроку ви можете просто створити проєкт Rust, готовий до розгортання. Для створення шаблону Actix ми можемо виконати:
$ cargo shuttle init -t actix-web
Станом на зараз cargo-shuttleпідтримує такі шаблони: actix-web, axum, poem, poise, rocket, salvo, serenity, tide, , thruster, tower,warp
Після створення проєкт виглядає приблизно так:
#!/usr/bin/env rust-script
//! [dependencies]
//! shuttle-runtime = "0.16.0"
//! actix-web = "4.3.1"
//! shuttle-actix-web = "0.16.0"
//! tokio = "1.28.1"
use actix_web::{get, web::ServiceConfig};
use shuttle_actix_web::ShuttleActixWeb;
// Define a route for the root path ("/") that returns a string "Hello World!"
#[get("/")]
async fn hello_world() -> &'static str {
"Hello World!"
}
// The main entry point for the Shuttle runtime.
#[shuttle_runtime::main]
async fn actix_web(
) -> ShuttleActixWeb<impl FnOnce(&mut ServiceConfig) + Send + Clone + 'static> {
// Define a closure to configure the ServiceConfig of the HttpServer.
let config = move |cfg: &mut ServiceConfig| {
// Register the hello_world function as a service at the root path.
cfg.service(hello_world);
};
// Wrap the closure in a ShuttleActixWeb object and return it.
Ok(config.into())
}
Щоб перевірити це локально:
$ cargo shuttle run
Для розгортання це просто, як виконати таку команду:
$ cargo shuttle deploy
Потім ви можете переглянути розгортання за адресою <app>.shuttleapp.rsта переглянути журнали через cargo shuttle logs.
У випадку з rustypaste, у мене вже є застосунок Actix, тому мені потрібно трохи налаштувати його для розгортання. Головним чином, мені потрібно було змінити головну точку входу застосунку. Було вирішено додати прапорець функції під назвою “shuttle” для обгортання додаткових залежностей Shuttle та активації іншої точки входу. Я не чіпав решту коду, і це чудово, коли зусиль менше.
[features]
default = []
shuttle = [
"dep:shuttle-actix-web",
"dep:shuttle-runtime",
"dep:shuttle-static-folder",
"dep:tokio",
]
[dependencies]
# other dependencies
# ...
shuttle-actix-web = { version = "0.16.0", optional = true }
shuttle-runtime = { version = "0.16.0", optional = true }
shuttle-static-folder = { version = "0.16.0", optional = true }
tokio = { version = "1.28.1", optional = true }
Шатл можна активувати вже --features shuttleзараз. Потім натрапив на ще одну перешкоду.
Бачите, для роботи rustypasteпотрібен config.tomlфайл. А коли ми розгортаємо його як один бінарний файл, поруч із ним не буде файлу конфігурації. Щоб вирішити цю проблему, використавуемо статичну папку для розгортання Shuttle та відредагував точку входу програми наступним чином:
#[cfg(feature = "shuttle")]
#[shuttle_runtime::main]
async fn actix_web(
#[shuttle_static_folder::StaticFolder(folder = "shuttle")] static_folder: PathBuf,
) -> ShuttleActixWeb<impl FnOnce(&mut ServiceConfig) + Send + Clone + 'static>
Отже, якщо я поміщу файл конфігурації в “shuttle/config.toml”, він буде частиною розгортання. Чудово.
Тепер все здається добре. Давайте розгортати!
З : Зачекайте, як увімкнути функцію “шатл” через
cargo shuttle? Іншими словами, чи можна перейти--features shuttleдо неї?A : На момент написання цього допису в блозі це неможливо. Тому було створено цю задачу: https://github.com/shuttle-hq/shuttle/issues/913
З : О, чудово, чи є якийсь спосіб вирішення?
A : Так, було вирішено тимчасово ввімкнути функцію “шатл” за замовчуванням у Cargo.toml, коли я хочу розгорнути.
З : Чудово. Це так працює?
В : Звичайно!
# make "shuttle" feature default
$ sed -i 's|default = \[\]|default = \["shuttle"\]|g' Cargo.toml
# deploy without running tests
$ cargo shuttle deploy --no-test
2023-05-16T13:56:37.027724537Z INFO Entering queued state
2023-05-16T13:56:37.036643575Z DEBUG hyper::client::connect::http: connecting to 10.99.82.119:8001
2023-05-16T13:56:37.039322882Z DEBUG hyper::client::connect::http: connected to 10.99.82.119:8001
2023-05-16T13:56:37.041805834Z DEBUG hyper::client::pool: pooling idle connection for ("http", gateway:8001)
2023-05-16T13:56:37.046562562Z INFO Entering building state
2023-05-16T13:57:08.734972530Z INFO Finished release [optimized] target(s) in 31.63s
2023-05-16T13:57:08.924988890Z INFO Entering built state
2023-05-16T13:57:08.925183822Z INFO Entering loading state
2023-05-16T13:57:08.933992618Z DEBUG shuttle_deployer::runtime_manager: Starting alpha runtime at: /opt/shuttle/shuttle-executables/abe6d63d-8a0f-4d59-9545-8979261889e4
2023-05-16T13:57:10.937504021Z INFO shuttle_proto::runtime: connecting runtime client
2023-05-16T13:57:10.937602528Z DEBUG hyper::client::connect::http: connecting to 127.0.0.1:18369
2023-05-16T13:57:10.940519024Z DEBUG hyper::client::connect::http: connected to 127.0.0.1:18369
2023-05-16T13:57:10.942760924Z DEBUG {service.ready=true} tower::buffer::worker: processing request
2023-05-16T13:57:10.949727188Z DEBUG {service.ready=true} tower::buffer::worker: processing request
2023-05-16T13:57:10.954448150Z INFO shuttle_deployer::deployment::run: loading project from: /opt/shuttle/shuttle-executables/abe6d63d-8a0f-4d59-9545-8979261889e4
2023-05-16T13:57:10.956904512Z DEBUG shuttle_deployer::deployment::run: loading service
2023-05-16T13:57:10.961554512Z DEBUG {service.ready=true} tower::buffer::worker: processing request
2023-05-16T13:57:10.978349558Z INFO {success="true"} shuttle_deployer::deployment::run: loading response
These static folders can be accessed by rustypaste
╭─────────╮
│ Folders │
╞═════════╡
│ shuttle │
╰─────────╯
Service Name: rustypaste
Deployment ID: abe6d63d-8a0f-4d59-9545-8979261889e4
Status: running
Last Updated: 2023-05-16T10:57:10Z
Ура, сервіс вже доступний за посиланням https://rustypaste.shuttleapp.rs !
Як бонус, хотів розгорнути цей сервіс, коли надсилаємо новий тег до репозиторію, тому було створено наступний робочий процес дій GitHub:
name: Deploy on Shuttle
on:
push:
branches:
- master
tags:
- "v*.*.*"
pull_request:
branches:
- master
# allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
build:
name: Build / Deploy
runs-on: ubuntu-22.04
steps:
- name: Checkout the repository
uses: actions/checkout@v3
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
- name: Install cargo-binstall
uses: taiki-e/install-action@cargo-binstall
- name: Install cargo-shuttle
run: cargo binstall -y cargo-shuttle
- name: Prepare for deployment
shell: bash
run: sed -i 's|default = \[\]|default = \["shuttle"\]|g' Cargo.toml
- name: Build
run: cargo build --locked --verbose
- name: Deploy
if: ${{ startsWith(github.event.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch' }}
run: |
cargo shuttle login --api-key ${{ secrets.SHUTTLE_TOKEN }}
cargo shuttle project restart
cargo shuttle deploy --allow-dirty --no-test
Варто зазначити одне, що було б зручніше встановлювати cargo-shuttleчерез, cargo-binstallоскільки це швидше завантажує попередньо зібрані бінарні файли. І нарешті, ось приклад:https://github.com/orhun/rustypaste/commit/29ddef8
Спочатку здається, що розміщувати власний сервер і все налаштовувати складно, але це того варте. Ви rustypasteнавіть можете поділитися дуже секретним документом як одноразовою URL-адресою, щоб він зникав з поверхні Інтернету після першого перегляду.
І хто може заперечувати, що використання власного домену для обміну зображенням/файлом – це супер круто?
До речі, не соромтеся використовувати публічний екземпляр rustypasteі повідомте мені, чи сподобався він вам. Гарантії стабільності для розгортань Shuttle немає, але спробувати варто!
Щасливого завантаження!