diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..ad32f55 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,19 @@ +# flyctl launch added from .gitignore +# ---> Rust +# Generated by Cargo +# will have compiled files and executables +**/debug +**/target + +# These are backup files generated by rustfmt +**/**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +**/*.pdb + +**/.direnv +**/gs.* +**/result +**/posts +!**/posts/.keep +fly.toml diff --git a/Cargo.lock b/Cargo.lock index f9350db..b2de02a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,6 +68,55 @@ dependencies = [ "winapi", ] +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +dependencies = [ + "anstyle", + "windows-sys", +] + [[package]] name = "async-compression" version = "0.3.15" @@ -296,19 +345,66 @@ dependencies = [ "ansi_term", "atty", "bitflags 1.3.2", - "strsim", + "strsim 0.8.0", "textwrap", "unicode-width", "vec_map", ] +[[package]] +name = "clap" +version = "4.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0827b011f6f8ab38590295339817b0d26f344aa4932c3ced71b45b0c54b4a9" +dependencies = [ + "clap_builder", + "clap_derive", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9441b403be87be858db6a23edb493e7f694761acdc3343d5a0fcaafd304cbc9e" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim 0.10.0", +] + +[[package]] +name = "clap_derive" +version = "4.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.23", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "comrak" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b2d07f7b7e1fd35e70300a9fc5397636650477887dc7b6ad6d29ba7d82e3349" dependencies = [ - "clap", + "clap 2.34.0", "entities", "lazy_static 0.2.11", "regex", @@ -867,6 +963,17 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi 0.3.2", + "rustix 0.38.4", + "windows-sys", +] + [[package]] name = "itertools" version = "0.10.5" @@ -950,6 +1057,12 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +[[package]] +name = "linux-raw-sys" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" + [[package]] name = "lock_api" version = "0.4.10" @@ -1290,6 +1403,7 @@ name = "quick-start" version = "0.1.0" dependencies = [ "axum", + "clap 4.3.17", "comrak", "futures", "hex", @@ -1428,7 +1542,20 @@ dependencies = [ "errno", "io-lifetimes", "libc", - "linux-raw-sys", + "linux-raw-sys 0.3.8", + "windows-sys", +] + +[[package]] +name = "rustix" +version = "0.38.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" +dependencies = [ + "bitflags 2.3.3", + "errno", + "libc", + "linux-raw-sys 0.4.3", "windows-sys", ] @@ -1878,6 +2005,12 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "subtle" version = "2.5.0" @@ -1922,7 +2055,7 @@ dependencies = [ "cfg-if", "fastrand", "redox_syscall", - "rustix", + "rustix 0.37.23", "windows-sys", ] @@ -2206,6 +2339,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcfc827f90e53a02eaef5e535ee14266c1d569214c6aa70133a624d8a3164ba" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "vcpkg" version = "0.2.15" diff --git a/Cargo.toml b/Cargo.toml index bd77681..2b7e1d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,4 +19,5 @@ hex = "0.4" lazy_static = "1.4.0" futures = "0.3.28" comrak = "0.1" -orgize = "0.9" \ No newline at end of file +orgize = "0.9" +clap = { version = "4.0", features = ["derive"] } \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c4041d6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +FROM rust:latest as builder + +WORKDIR /usr/src/app +COPY . . +# Will build and cache the binary and dependent crates in release mode +RUN --mount=type=cache,target=/usr/local/cargo,from=rust:latest,source=/usr/local/cargo \ + --mount=type=cache,target=target \ + cargo build --release && mv ./target/release/quick-start ./gabrielsimmerdotcom + +# Runtime image +FROM debian:bullseye-slim + +RUN apt-get update -y && apt-get install -y ca-certificates fuse3 sqlite3 + +WORKDIR /app + +# Get compiled binaries from builder's cargo install directory +COPY --from=builder /usr/src/app/gabrielsimmerdotcom /app/gabrielsimmerdotcom +COPY litefs.yml /etc +COPY --from=flyio/litefs:0.5 /usr/local/bin/litefs /usr/local/bin/litefs + +ENTRYPOINT litefs mount \ No newline at end of file diff --git a/fly.toml b/fly.toml new file mode 100644 index 0000000..54785f5 --- /dev/null +++ b/fly.toml @@ -0,0 +1,19 @@ +# fly.toml app configuration file generated for gabrielsimmerdotcom on 2023-07-19T21:27:38+01:00 +# +# See https://fly.io/docs/reference/configuration/ for information about how to use this file. +# + +app = "gabrielsimmerdotcom" +primary_region = "lhr" + +[http_service] + internal_port = 8080 + force_https = true + auto_stop_machines = true + auto_start_machines = true + min_machines_running = 0 + processes = ["app"] + +[mounts] + source = "litefs" + destination = "/var/lib/litefs" diff --git a/litefs.yml b/litefs.yml new file mode 100644 index 0000000..0ffc530 --- /dev/null +++ b/litefs.yml @@ -0,0 +1,49 @@ +# The fuse section describes settings for the FUSE file system. This file system +# is used as a thin layer between the SQLite client in your application and the +# storage on disk. It intercepts disk writes to determine transaction boundaries +# so that those transactions can be saved and shipped to replicas. +fuse: + dir: "/litefs" + +# The data section describes settings for the internal LiteFS storage. We'll +# mount a volume to the data directory so it can be persisted across restarts. +# However, this data should not be accessed directly by the user application. +data: + dir: "/var/lib/litefs" + +# This flag ensure that LiteFS continues to run if there is an issue on starup. +# It makes it easy to ssh in and debug any issues you might be having rather +# than continually restarting on initialization failure. +exit-on-error: false + +# This section defines settings for the option HTTP proxy. +# This proxy can handle primary forwarding & replica consistency +# for applications that use a single SQLite database. +proxy: + addr: ":8080" + target: "localhost:8081" + db: "db" + passthrough: + - "*.ico" + - "*.png" + +# This section defines a list of commands to run after LiteFS has connected +# and sync'd with the cluster. You can run multiple commands but LiteFS expects +# the last command to be long-running (e.g. an application server). When the +# last command exits, LiteFS is shut down. +exec: + - cmd: "/app/gabrielsimmerdotcom --bind 0.0.0.0:8081 -d /litefs/db" + +# The lease section specifies how the cluster will be managed. We're using the +# "consul" lease type so that our application can dynamically change the primary. +# +# These environment variables will be available in your Fly.io application. +lease: + type: "consul" + advertise-url: "http://${HOSTNAME}.vm.${FLY_APP_NAME}.internal:20202" + candidate: ${FLY_REGION == PRIMARY_REGION} + promote: true + + consul: + url: "${FLY_CONSUL_URL}" + key: "litefs/${FLY_APP_NAME}" diff --git a/src/main.rs b/src/main.rs index 07ced62..40e954d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,8 +3,8 @@ extern crate lazy_static; use std::fs; use std::str::FromStr; -use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; -use std::{collections::HashMap, time}; +use std::time::{SystemTime, UNIX_EPOCH}; +use std::{collections::HashMap}; use axum::extract::Path; use axum::response::IntoResponse; @@ -20,13 +20,21 @@ use axum::{ use hyper::body::Bytes; use maud::{html, Markup}; use orgize::Org; -use sha2::{Digest, Sha256}; use sqlx::sqlite::{SqliteConnectOptions, SqliteJournalMode, SqlitePoolOptions}; use sqlx::{FromRow, Pool, Sqlite}; use tokio::sync::Mutex; +use clap::Parser; lazy_static! { - static ref CACHE: Mutex> = { Mutex::new(HashMap::new()) }; + static ref CACHE: Mutex> = Mutex::new(HashMap::new()); +} + +#[derive(Parser)] +struct Cli { + #[arg(short, long)] + database_path: String, + #[arg(short, long, default_value_t=("0.0.0.0:3000").to_string())] + bind: String, } #[derive(Clone, Debug)] @@ -43,7 +51,8 @@ struct CachedPage { #[tokio::main] async fn main() -> Result<(), sqlx::Error> { - let opts = SqliteConnectOptions::from_str("sqlite:gs.db")? + let args = Cli::parse(); + let opts = SqliteConnectOptions::from_str(&args.database_path)? .journal_mode(SqliteJournalMode::Wal) .create_if_missing(true); @@ -60,8 +69,8 @@ async fn main() -> Result<(), sqlx::Error> { .layer(middleware::from_fn_with_state(state.clone(), cached_page)) .with_state(state); - println!("Running webserver on port :3000"); - axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) + println!("Running webserver on {}", args.bind); + axum::Server::bind(&args.bind.parse().unwrap()) .serve(app.into_make_service()) .await .unwrap(); @@ -194,7 +203,7 @@ async fn cached_page( .body(Full::from(c.content)) .unwrap(); } else { - let cache_sqlite = sqlx::query("DELETE FROM cached WHERE route = $1") + let _cache_sqlite = sqlx::query("DELETE FROM cached WHERE route = $1") .bind(&path) .execute(&state.database) .await;