mirror of
https://github.com/amigan/rustypaste-pretty.git
synced 2025-01-31 04:52:37 -05:00
feat(deploy): deploy on shuttle.rs (#36)
* feat(deploy): deploy on shuttle.rs * chore(deploy): add automated shuttle deploy workflow * style(readme): update the formatting in README.md * chore(deploy): optimize shuttle workflow * fix(deploy): start the project * fix(deploy): remove start step This reverts commit 4f25921aeb2a064952dd9bc88895e446cac134e6. * chore(deploy): expose the version for the public instance * docs(lib): update the comment for shuttle entry-point * chore(deploy): run the shuttle deployment on new tag
This commit is contained in:
parent
019d1556da
commit
29ddef8df0
8 changed files with 1466 additions and 11 deletions
|
@ -2,6 +2,7 @@
|
||||||
/.git/
|
/.git/
|
||||||
/.github/
|
/.github/
|
||||||
/upload/
|
/upload/
|
||||||
|
/shuttle/
|
||||||
|
|
||||||
# Files
|
# Files
|
||||||
.gitignore
|
.gitignore
|
||||||
|
|
31
.github/workflows/shuttle.yml
vendored
Normal file
31
.github/workflows/shuttle.yml
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
name: Deploy on Shuttle
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*.*.*"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
name: 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: Login
|
||||||
|
run: cargo shuttle login --api-key ${{ secrets.SHUTTLE_TOKEN }}
|
||||||
|
- name: Deploy
|
||||||
|
run: cargo shuttle deploy --allow-dirty --no-test
|
1306
Cargo.lock
generated
1306
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
19
Cargo.toml
19
Cargo.toml
|
@ -12,6 +12,15 @@ keywords = ["paste", "pastebin", "upload"]
|
||||||
categories = ["web-programming::http-server"]
|
categories = ["web-programming::http-server"]
|
||||||
include = ["src/**/*", "Cargo.*", "LICENSE", "README.md", "CHANGELOG.md"]
|
include = ["src/**/*", "Cargo.*", "LICENSE", "README.md", "CHANGELOG.md"]
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
shuttle = [
|
||||||
|
"dep:shuttle-actix-web",
|
||||||
|
"dep:shuttle-runtime",
|
||||||
|
"dep:shuttle-static-folder",
|
||||||
|
"dep:tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-web = { version = "4.3.1", features = ["rustls"] }
|
actix-web = { version = "4.3.1", features = ["rustls"] }
|
||||||
actix-multipart = "0.6.0"
|
actix-multipart = "0.6.0"
|
||||||
|
@ -21,7 +30,10 @@ env_logger = "0.10.0"
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
serde = "1.0.163"
|
serde = "1.0.163"
|
||||||
futures-util = "0.3.28"
|
futures-util = "0.3.28"
|
||||||
petname = { version = "1.1.3", default-features = false, features = ["std_rng", "default_dictionary"] }
|
petname = { version = "1.1.3", default-features = false, features = [
|
||||||
|
"std_rng",
|
||||||
|
"default_dictionary",
|
||||||
|
] }
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
dotenvy = "0.15.7"
|
dotenvy = "0.15.7"
|
||||||
url = "2.3.1"
|
url = "2.3.1"
|
||||||
|
@ -34,6 +46,10 @@ humantime-serde = "1.1.1"
|
||||||
glob = "0.3.1"
|
glob = "0.3.1"
|
||||||
ring = "0.16.20"
|
ring = "0.16.20"
|
||||||
hotwatch = "0.4.5"
|
hotwatch = "0.4.5"
|
||||||
|
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 }
|
||||||
|
|
||||||
[dependencies.config]
|
[dependencies.config]
|
||||||
version = "0.13.3"
|
version = "0.13.3"
|
||||||
|
@ -54,7 +70,6 @@ actix-rt = "2.8.0"
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
opt-level = 0
|
opt-level = 0
|
||||||
debug = true
|
debug = true
|
||||||
panic = "abort"
|
|
||||||
|
|
||||||
[profile.test]
|
[profile.test]
|
||||||
opt-level = 0
|
opt-level = 0
|
||||||
|
|
|
@ -12,6 +12,8 @@ $ curl https://paste.site.com/safe-toad.txt
|
||||||
some text
|
some text
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The public instance is available at [https://rustypaste.shuttleapp.rs](https://rustypaste.shuttleapp.rs) 🚀
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- File upload & URL shortening & upload from URL
|
- File upload & URL shortening & upload from URL
|
||||||
|
|
|
@ -3,6 +3,7 @@ refresh_rate = "1s"
|
||||||
|
|
||||||
[server]
|
[server]
|
||||||
address = "127.0.0.1:8000"
|
address = "127.0.0.1:8000"
|
||||||
|
#url = "https://rustypaste.shuttleapp.rs"
|
||||||
#workers=4
|
#workers=4
|
||||||
max_content_length = "10MB"
|
max_content_length = "10MB"
|
||||||
upload_path = "./upload"
|
upload_path = "./upload"
|
||||||
|
|
53
shuttle/config.toml
Normal file
53
shuttle/config.toml
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
[config]
|
||||||
|
refresh_rate = "1s"
|
||||||
|
|
||||||
|
[server]
|
||||||
|
address = "127.0.0.1:8000"
|
||||||
|
url = "https://rustypaste.shuttleapp.rs"
|
||||||
|
#workers=4
|
||||||
|
max_content_length = "10MB"
|
||||||
|
upload_path = "./upload"
|
||||||
|
timeout = "30s"
|
||||||
|
expose_version = true
|
||||||
|
landing_page = """
|
||||||
|
┬─┐┬ ┬┌─┐┌┬┐┬ ┬┌─┐┌─┐┌─┐┌┬┐┌─┐
|
||||||
|
├┬┘│ │└─┐ │ └┬┘├─┘├─┤└─┐ │ ├┤
|
||||||
|
┴└─└─┘└─┘ ┴ ┴ ┴ ┴ ┴└─┘ ┴ └─┘
|
||||||
|
|
||||||
|
Submit files via HTTP POST here:
|
||||||
|
|
||||||
|
curl -F 'file=@example.txt' https://rustypaste.shuttleapp.rs
|
||||||
|
|
||||||
|
This will return the URL of the uploaded file.
|
||||||
|
|
||||||
|
Pastes expire every hour. Uploaded files might not be persistent.
|
||||||
|
|
||||||
|
Check out the GitHub repository: https://github.com/orhun/rustypaste
|
||||||
|
Command line tool is available : https://github.com/orhun/rustypaste-cli
|
||||||
|
|
||||||
|
If you liked this, consider supporting me: https://donate.orhun.dev <3
|
||||||
|
|
||||||
|
🦀
|
||||||
|
"""
|
||||||
|
|
||||||
|
[paste]
|
||||||
|
# random_url = { enabled = true, type = "petname", words = 2, separator = "-" }
|
||||||
|
random_url = { enabled = true, type = "alphanumeric", length = 6 }
|
||||||
|
default_extension = "txt"
|
||||||
|
mime_override = [
|
||||||
|
{ mime = "image/jpeg", regex = "^.*\\.jpg$" },
|
||||||
|
{ mime = "image/png", regex = "^.*\\.png$" },
|
||||||
|
{ mime = "image/svg+xml", regex = "^.*\\.svg$" },
|
||||||
|
{ mime = "video/webm", regex = "^.*\\.webm$" },
|
||||||
|
{ mime = "video/x-matroska", regex = "^.*\\.mkv$" },
|
||||||
|
{ mime = "application/octet-stream", regex = "^.*\\.bin$" },
|
||||||
|
{ mime = "text/plain", regex = "^.*\\.(log|txt|diff|sh|kt|rs|toml)$" },
|
||||||
|
]
|
||||||
|
mime_blacklist = [
|
||||||
|
"application/x-dosexec",
|
||||||
|
"application/java-archive",
|
||||||
|
"application/java-vm",
|
||||||
|
]
|
||||||
|
duplicate_files = true
|
||||||
|
default_expiry = "1h"
|
||||||
|
delete_expired_files = { enabled = true, interval = "1h" }
|
64
src/main.rs
64
src/main.rs
|
@ -1,9 +1,10 @@
|
||||||
use actix_web::middleware::Logger;
|
use actix_web::middleware::Logger;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
|
#[cfg(not(feature = "shuttle"))]
|
||||||
use actix_web::{App, HttpServer};
|
use actix_web::{App, HttpServer};
|
||||||
use awc::ClientBuilder;
|
use awc::ClientBuilder;
|
||||||
use hotwatch::{Event, Hotwatch};
|
use hotwatch::{Event, Hotwatch};
|
||||||
use rustypaste::config::Config;
|
use rustypaste::config::{Config, ServerConfig};
|
||||||
use rustypaste::paste::PasteType;
|
use rustypaste::paste::PasteType;
|
||||||
use rustypaste::server;
|
use rustypaste::server;
|
||||||
use rustypaste::util;
|
use rustypaste::util;
|
||||||
|
@ -11,17 +12,28 @@ use rustypaste::CONFIG_ENV;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::Result as IoResult;
|
use std::io::Result as IoResult;
|
||||||
use std::path::PathBuf;
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::{mpsc, RwLock};
|
use std::sync::{mpsc, RwLock};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
#[cfg(feature = "shuttle")]
|
||||||
|
use {
|
||||||
|
actix_web::web::{self, ServiceConfig},
|
||||||
|
shuttle_actix_web::ShuttleActixWeb,
|
||||||
|
};
|
||||||
|
|
||||||
#[actix_web::main]
|
/// Sets up the application.
|
||||||
async fn main() -> IoResult<()> {
|
///
|
||||||
|
/// * loads the configuration
|
||||||
|
/// * initializes the logger
|
||||||
|
/// * creates the necessary directories
|
||||||
|
/// * spawns the threads
|
||||||
|
fn setup(config_folder: &Path) -> IoResult<(Data<RwLock<Config>>, ServerConfig, Hotwatch)> {
|
||||||
// Load the .env file.
|
// Load the .env file.
|
||||||
dotenvy::dotenv().ok();
|
dotenvy::dotenv().ok();
|
||||||
|
|
||||||
// Initialize logger.
|
// Initialize logger.
|
||||||
|
#[cfg(not(feature = "shuttle"))]
|
||||||
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
|
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
|
||||||
|
|
||||||
// Parse configuration.
|
// Parse configuration.
|
||||||
|
@ -30,7 +42,7 @@ async fn main() -> IoResult<()> {
|
||||||
env::remove_var(CONFIG_ENV);
|
env::remove_var(CONFIG_ENV);
|
||||||
PathBuf::from(path)
|
PathBuf::from(path)
|
||||||
}
|
}
|
||||||
None => PathBuf::from("config.toml"),
|
None => config_folder.join("config.toml"),
|
||||||
};
|
};
|
||||||
let config = Config::parse(&config_path).expect("failed to parse config");
|
let config = Config::parse(&config_path).expect("failed to parse config");
|
||||||
log::trace!("{:#?}", config);
|
log::trace!("{:#?}", config);
|
||||||
|
@ -83,6 +95,7 @@ async fn main() -> IoResult<()> {
|
||||||
.unwrap_or_else(|_| panic!("failed to watch {config_path:?}"));
|
.unwrap_or_else(|_| panic!("failed to watch {config_path:?}"));
|
||||||
|
|
||||||
// Create a thread for cleaning up expired files.
|
// Create a thread for cleaning up expired files.
|
||||||
|
let upload_path = server_config.upload_path.clone();
|
||||||
thread::spawn(move || loop {
|
thread::spawn(move || loop {
|
||||||
let mut enabled = false;
|
let mut enabled = false;
|
||||||
if let Some(ref cleanup_config) = paste_config
|
if let Some(ref cleanup_config) = paste_config
|
||||||
|
@ -92,7 +105,7 @@ async fn main() -> IoResult<()> {
|
||||||
{
|
{
|
||||||
if cleanup_config.enabled {
|
if cleanup_config.enabled {
|
||||||
log::debug!("Running cleanup...");
|
log::debug!("Running cleanup...");
|
||||||
for file in util::get_expired_files(&server_config.upload_path) {
|
for file in util::get_expired_files(&upload_path) {
|
||||||
match fs::remove_file(&file) {
|
match fs::remove_file(&file) {
|
||||||
Ok(()) => log::info!("Removed expired file: {:?}", file),
|
Ok(()) => log::info!("Removed expired file: {:?}", file),
|
||||||
Err(e) => log::error!("Cannot remove expired file: {}", e),
|
Err(e) => log::error!("Cannot remove expired file: {}", e),
|
||||||
|
@ -118,6 +131,15 @@ async fn main() -> IoResult<()> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Ok((config, server_config, hotwatch))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "shuttle"))]
|
||||||
|
#[actix_web::main]
|
||||||
|
async fn main() -> IoResult<()> {
|
||||||
|
// Set up the application.
|
||||||
|
let (config, server_config, _) = setup(&PathBuf::new())?;
|
||||||
|
|
||||||
// Create an HTTP server.
|
// Create an HTTP server.
|
||||||
let mut http_server = HttpServer::new(move || {
|
let mut http_server = HttpServer::new(move || {
|
||||||
let http_client = ClientBuilder::new()
|
let http_client = ClientBuilder::new()
|
||||||
|
@ -144,3 +166,33 @@ async fn main() -> IoResult<()> {
|
||||||
// Run the server.
|
// Run the server.
|
||||||
http_server.run().await
|
http_server.run().await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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> {
|
||||||
|
// Set up the application.
|
||||||
|
let (config, server_config, _) = setup(&static_folder)?;
|
||||||
|
|
||||||
|
// Create the service.
|
||||||
|
let service_config = move |cfg: &mut ServiceConfig| {
|
||||||
|
let http_client = ClientBuilder::new()
|
||||||
|
.timeout(
|
||||||
|
server_config
|
||||||
|
.timeout
|
||||||
|
.unwrap_or_else(|| Duration::from_secs(30)),
|
||||||
|
)
|
||||||
|
.disable_redirects()
|
||||||
|
.finish();
|
||||||
|
cfg.service(
|
||||||
|
web::scope("")
|
||||||
|
.app_data(Data::clone(&config))
|
||||||
|
.app_data(Data::new(http_client))
|
||||||
|
.wrap(Logger::default())
|
||||||
|
.configure(server::configure_routes),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(service_config.into())
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue