added caddy
This commit is contained in:
+7
-1
@@ -11,9 +11,14 @@ BACKEND_PORT=8080
|
||||
# 64+ hex chars. Generate: openssl rand -hex 32
|
||||
SESSION_SECRET=please_generate_a_long_random_secret_at_least_64_chars_xxxxxxxxxx
|
||||
# Public URL of the frontend, used to build verification/reset links.
|
||||
# Prod: https://consume.narl.io
|
||||
PUBLIC_APP_URL=http://localhost:5173
|
||||
# Comma-separated allowed CORS origins.
|
||||
# Prod (same-origin behind Caddy): https://consume.narl.io
|
||||
CORS_ORIGINS=http://localhost:5173
|
||||
# Mark the session cookie Secure (HTTPS-only). Auto-on when PUBLIC_APP_URL is
|
||||
# https; override here. Must be true in production, false for plain-http dev.
|
||||
# COOKIE_SECURE=true
|
||||
|
||||
# ── SMTP (mail notifications + verification) ────────────────
|
||||
SMTP_HOST=smtp.example.com
|
||||
@@ -25,5 +30,6 @@ SMTP_FROM="Shopping List <no-reply@example.com>"
|
||||
SMTP_SECURITY=starttls
|
||||
|
||||
# ── Frontend ────────────────────────────────────────────────
|
||||
# Where the SvelteKit app reaches the backend (server-side).
|
||||
# Base origin the browser uses to reach the backend (no trailing /api).
|
||||
# Prod (same-origin behind Caddy): https://consume.narl.io
|
||||
PUBLIC_API_BASE=http://localhost:8080
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
consume.narl.io {
|
||||
# Backend API (routes are served under /api by the Rust app).
|
||||
handle /api/* {
|
||||
reverse_proxy backend:8080
|
||||
}
|
||||
|
||||
# Everything else → SvelteKit (adapter-node) server.
|
||||
handle {
|
||||
reverse_proxy frontend:3000
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
# ── Build ───────────────────────────────────────────────────
|
||||
FROM rust:1-bookworm AS build
|
||||
WORKDIR /app
|
||||
|
||||
# Cache deps: copy manifests, build a stub, then the real source.
|
||||
COPY Cargo.toml Cargo.lock ./
|
||||
RUN mkdir src && echo "fn main() {}" > src/main.rs \
|
||||
&& cargo build --release \
|
||||
&& rm -rf src
|
||||
|
||||
COPY . .
|
||||
# Touch so cargo rebuilds with the real main.rs.
|
||||
RUN touch src/main.rs && cargo build --release
|
||||
|
||||
# ── Runtime ─────────────────────────────────────────────────
|
||||
FROM debian:bookworm-slim AS runtime
|
||||
WORKDIR /app
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
COPY --from=build /app/target/release/shoplist-backend /usr/local/bin/shoplist-backend
|
||||
EXPOSE 8080
|
||||
CMD ["shoplist-backend"]
|
||||
@@ -16,6 +16,8 @@ pub struct Config {
|
||||
pub refetch_min_age_secs: i64,
|
||||
/// Default ISO 4217 currency when an adapter can't determine one.
|
||||
pub default_currency: String,
|
||||
/// Mark the session cookie Secure (HTTPS-only). Enable in production.
|
||||
pub cookie_secure: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -67,6 +69,12 @@ impl Config {
|
||||
refetch_interval_secs: opt("REFETCH_INTERVAL_SECS", "300").parse()?,
|
||||
refetch_min_age_secs: opt("REFETCH_MIN_AGE_SECS", "21600").parse()?,
|
||||
default_currency: opt("DEFAULT_CURRENCY", "EUR").to_uppercase(),
|
||||
// Default Secure when the public URL is HTTPS; override with COOKIE_SECURE.
|
||||
cookie_secure: env::var("COOKIE_SECURE")
|
||||
.map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
|
||||
.unwrap_or_else(|_| {
|
||||
opt("PUBLIC_APP_URL", "http://localhost:5173").starts_with("https://")
|
||||
}),
|
||||
smtp: SmtpConfig {
|
||||
host: opt("SMTP_HOST", "localhost"),
|
||||
port: opt("SMTP_PORT", "587").parse()?,
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ async fn main() -> anyhow::Result<()> {
|
||||
session_store.migrate().await?;
|
||||
|
||||
let session_layer = SessionManagerLayer::new(session_store)
|
||||
.with_secure(false) // set true behind HTTPS in production
|
||||
.with_secure(config.cookie_secure) // true behind HTTPS in production
|
||||
.with_same_site(tower_sessions::cookie::SameSite::Lax)
|
||||
.with_expiry(Expiry::OnInactivity(Duration::days(30)));
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
services:
|
||||
db:
|
||||
image: postgres:17-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: ${POSTGRES_USER}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
POSTGRES_DB: ${POSTGRES_DB}
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
# No published port: db is reachable only on the compose network.
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
backend:
|
||||
build: ./backend
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
|
||||
BACKEND_HOST: 0.0.0.0
|
||||
BACKEND_PORT: 8080
|
||||
SESSION_SECRET: ${SESSION_SECRET}
|
||||
COOKIE_SECURE: "true"
|
||||
PUBLIC_APP_URL: ${PUBLIC_APP_URL}
|
||||
CORS_ORIGINS: ${CORS_ORIGINS}
|
||||
SMTP_HOST: ${SMTP_HOST}
|
||||
SMTP_PORT: ${SMTP_PORT}
|
||||
SMTP_USERNAME: ${SMTP_USERNAME}
|
||||
SMTP_PASSWORD: ${SMTP_PASSWORD}
|
||||
SMTP_FROM: ${SMTP_FROM}
|
||||
SMTP_SECURITY: ${SMTP_SECURITY}
|
||||
# No published port: reached via caddy only.
|
||||
|
||||
frontend:
|
||||
build: ./frontend
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- backend
|
||||
environment:
|
||||
PUBLIC_API_BASE: ${PUBLIC_API_BASE}
|
||||
# adapter-node needs the public origin for CSRF/redirects behind a proxy.
|
||||
ORIGIN: ${PUBLIC_APP_URL}
|
||||
# No published port: reached via caddy only.
|
||||
|
||||
caddy:
|
||||
image: caddy:2-alpine
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- frontend
|
||||
- backend
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
- caddy_data:/data
|
||||
- caddy_config:/config
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
caddy_data:
|
||||
caddy_config:
|
||||
@@ -0,0 +1,20 @@
|
||||
# ── Build ───────────────────────────────────────────────────
|
||||
FROM node:22-bookworm-slim AS build
|
||||
WORKDIR /app
|
||||
RUN corepack enable
|
||||
|
||||
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
COPY . .
|
||||
RUN pnpm build && pnpm prune --prod
|
||||
|
||||
# ── Runtime ─────────────────────────────────────────────────
|
||||
FROM node:22-bookworm-slim AS runtime
|
||||
WORKDIR /app
|
||||
ENV NODE_ENV=production
|
||||
COPY --from=build /app/build ./build
|
||||
COPY --from=build /app/node_modules ./node_modules
|
||||
COPY --from=build /app/package.json ./
|
||||
EXPOSE 3000
|
||||
CMD ["node", "build"]
|
||||
Reference in New Issue
Block a user