Compare commits

...

3 commits

7 changed files with 102 additions and 7 deletions

4
.dockerignore Normal file
View file

@ -0,0 +1,4 @@
.git/
.env
target
.DS_Store

33
Cargo.lock generated
View file

@ -871,6 +871,17 @@ dependencies = [
"syn",
]
[[package]]
name = "diesel_migrations"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a73ce704bad4231f001bff3314d91dce4aba0770cee8b233991859abc15c1f6"
dependencies = [
"diesel",
"migrations_internals",
"migrations_macros",
]
[[package]]
name = "diesel_table_macro_syntax"
version = "0.2.0"
@ -1783,6 +1794,27 @@ version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "migrations_internals"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd01039851e82f8799046eabbb354056283fb265c8ec0996af940f4e85a380ff"
dependencies = [
"serde",
"toml",
]
[[package]]
name = "migrations_macros"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffb161cc72176cb37aa47f1fc520d3ef02263d67d661f44f05d05a079e1237fd"
dependencies = [
"migrations_internals",
"proc-macro2",
"quote",
]
[[package]]
name = "mime"
version = "0.3.17"
@ -2602,6 +2634,7 @@ dependencies = [
"console_error_panic_hook",
"deadpool-diesel",
"diesel",
"diesel_migrations",
"dotenvy",
"futures",
"lettre",

View file

@ -33,3 +33,4 @@ tower = "0.5.2"
regex = "1.11.1"
lettre = { version = "0.11.12", features = ["tokio1", "serde", "tracing", "tokio1-native-tls"] }
clap = { version = "4.5.31", features = ["derive"] }
diesel_migrations = { version = "2.2.0", features = ["postgres"] }

22
Dockerfile Normal file
View file

@ -0,0 +1,22 @@
FROM lukemathwalker/cargo-chef:latest-rust-1.85.0 AS chef
WORKDIR /app
FROM chef AS planner
COPY . .
RUN cargo chef prepare --recipe-path recipe.json
FROM chef AS builder
COPY --from=planner /app/recipe.json recipe.json
# Build dependencies - this is the caching Docker layer!
RUN cargo chef cook --release --recipe-path recipe.json
# Build application
COPY . .
RUN cargo build --release --bin shoutdotdev
# We do not need the Rust toolchain to run the binary!
FROM debian:bookworm-slim AS runtime
RUN apt-get update && apt-get install -y libpq-dev
WORKDIR /app
COPY --from=builder /app/target/release/shoutdotdev /usr/local/bin
COPY ./static ./static
ENTRYPOINT ["/usr/local/bin/shoutdotdev"]

View file

@ -6,6 +6,8 @@ AUTH.REDIRECT_URL=http://localhost:3000/auth/callback
AUTH.AUTH_URL=https://example.com/authorize
AUTH.TOKEN_URL=https://example.com/token
AUTH.USERINFO_URL=https://example.com/userinfo
# The .env parser (dotenvy) requires quotes around any value with spaces. Note
# that in this regard it is incompatible with Docker's --env-file parser.
EMAIL.VERIFICATION_FROM=no-reply@shout.dev
EMAIL.MESSAGE_FROM=no-reply@shout.dev
EMAIL.SMTP.SERVER=smtp.example.com

View file

@ -25,6 +25,7 @@ use std::process::exit;
use chrono::{TimeDelta, Utc};
use clap::{Parser, Subcommand};
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
use email::SmtpOptions;
use tokio::time::sleep;
use tracing_subscriber::EnvFilter;
@ -57,12 +58,12 @@ enum Commands {
#[tokio::main]
async fn main() {
let settings = Settings::load().unwrap();
tracing_subscriber::fmt()
.with_env_filter(EnvFilter::from_default_env())
.init();
let settings = Settings::load().unwrap();
let cli = Cli::parse();
let database_url = settings.database_url.clone();
@ -71,6 +72,7 @@ async fn main() {
let db_pool = deadpool_diesel::postgres::Pool::builder(manager)
.build()
.unwrap();
let session_store = PgStore::new(db_pool.clone());
let reqwest_client = reqwest::ClientBuilder::new()
.https_only(true)
@ -94,6 +96,16 @@ async fn main() {
exit(1);
};
if settings.run_database_migrations == Some(1) {
const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations/");
// Run migrations on server startup
let conn = db_pool.get().await.unwrap();
conn.interact(|conn| conn.run_pending_migrations(MIGRATIONS).map(|_| ()))
.await
.unwrap()
.unwrap();
}
let app_state = AppState {
db_pool: db_pool.clone(),
mailer,

View file

@ -1,3 +1,4 @@
use anyhow::{Context as _, Result};
use axum::extract::FromRef;
use config::{Config, ConfigError, Environment};
use dotenvy::dotenv;
@ -12,6 +13,11 @@ pub struct Settings {
pub database_url: String,
/**
* When set to 1, embedded Diesel migrations will be run on startup.
*/
pub run_database_migrations: Option<u8>,
#[serde(default = "default_host")]
pub host: String,
@ -78,14 +84,29 @@ pub struct SlackSettings {
}
impl Settings {
pub fn load() -> Result<Self, ConfigError> {
if let Err(err) = dotenv() {
tracing::warn!("Couldn't load .env file: {:?}", err);
pub fn load() -> Result<Self> {
match dotenv() {
Err(err) => {
if err.not_found() {
tracing::info!("no .env file found");
} else {
return Err(err).context("dotenvy error");
}
}
Ok(pathbuf) => {
tracing::info!(
"using env file {}",
pathbuf
.to_str()
.ok_or(anyhow::anyhow!("pathbuf is not valid unicode"))?
);
}
}
let s = Config::builder()
.add_source(Environment::default())
.build()?;
s.try_deserialize()
.build()
.context("config error")?;
Ok(s.try_deserialize().context("deserialize error")?)
}
}