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/
|
||||
/.github/
|
||||
/upload/
|
||||
/shuttle/
|
||||
|
||||
# Files
|
||||
.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"]
|
||||
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]
|
||||
actix-web = { version = "4.3.1", features = ["rustls"] }
|
||||
actix-multipart = "0.6.0"
|
||||
|
@ -21,7 +30,10 @@ env_logger = "0.10.0"
|
|||
log = "0.4.17"
|
||||
serde = "1.0.163"
|
||||
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"
|
||||
dotenvy = "0.15.7"
|
||||
url = "2.3.1"
|
||||
|
@ -34,6 +46,10 @@ humantime-serde = "1.1.1"
|
|||
glob = "0.3.1"
|
||||
ring = "0.16.20"
|
||||
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]
|
||||
version = "0.13.3"
|
||||
|
@ -54,7 +70,6 @@ actix-rt = "2.8.0"
|
|||
[profile.dev]
|
||||
opt-level = 0
|
||||
debug = true
|
||||
panic = "abort"
|
||||
|
||||
[profile.test]
|
||||
opt-level = 0
|
||||
|
|
|
@ -12,6 +12,8 @@ $ curl https://paste.site.com/safe-toad.txt
|
|||
some text
|
||||
```
|
||||
|
||||
The public instance is available at [https://rustypaste.shuttleapp.rs](https://rustypaste.shuttleapp.rs) 🚀
|
||||
|
||||
## Features
|
||||
|
||||
- File upload & URL shortening & upload from URL
|
||||
|
|
|
@ -3,6 +3,7 @@ refresh_rate = "1s"
|
|||
|
||||
[server]
|
||||
address = "127.0.0.1:8000"
|
||||
#url = "https://rustypaste.shuttleapp.rs"
|
||||
#workers=4
|
||||
max_content_length = "10MB"
|
||||
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::web::Data;
|
||||
#[cfg(not(feature = "shuttle"))]
|
||||
use actix_web::{App, HttpServer};
|
||||
use awc::ClientBuilder;
|
||||
use hotwatch::{Event, Hotwatch};
|
||||
use rustypaste::config::Config;
|
||||
use rustypaste::config::{Config, ServerConfig};
|
||||
use rustypaste::paste::PasteType;
|
||||
use rustypaste::server;
|
||||
use rustypaste::util;
|
||||
|
@ -11,17 +12,28 @@ use rustypaste::CONFIG_ENV;
|
|||
use std::env;
|
||||
use std::fs;
|
||||
use std::io::Result as IoResult;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{mpsc, RwLock};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
#[cfg(feature = "shuttle")]
|
||||
use {
|
||||
actix_web::web::{self, ServiceConfig},
|
||||
shuttle_actix_web::ShuttleActixWeb,
|
||||
};
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> IoResult<()> {
|
||||
/// Sets up the application.
|
||||
///
|
||||
/// * 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.
|
||||
dotenvy::dotenv().ok();
|
||||
|
||||
// Initialize logger.
|
||||
#[cfg(not(feature = "shuttle"))]
|
||||
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
|
||||
|
||||
// Parse configuration.
|
||||
|
@ -30,7 +42,7 @@ async fn main() -> IoResult<()> {
|
|||
env::remove_var(CONFIG_ENV);
|
||||
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");
|
||||
log::trace!("{:#?}", config);
|
||||
|
@ -83,6 +95,7 @@ async fn main() -> IoResult<()> {
|
|||
.unwrap_or_else(|_| panic!("failed to watch {config_path:?}"));
|
||||
|
||||
// Create a thread for cleaning up expired files.
|
||||
let upload_path = server_config.upload_path.clone();
|
||||
thread::spawn(move || loop {
|
||||
let mut enabled = false;
|
||||
if let Some(ref cleanup_config) = paste_config
|
||||
|
@ -92,7 +105,7 @@ async fn main() -> IoResult<()> {
|
|||
{
|
||||
if cleanup_config.enabled {
|
||||
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) {
|
||||
Ok(()) => log::info!("Removed expired file: {:?}", file),
|
||||
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.
|
||||
let mut http_server = HttpServer::new(move || {
|
||||
let http_client = ClientBuilder::new()
|
||||
|
@ -144,3 +166,33 @@ async fn main() -> IoResult<()> {
|
|||
// Run the server.
|
||||
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