Skip to content

Commit 60f8699

Browse files
committed
Refactor environment configuration and enhance application security checks
- Updated `.env.example` to include new environment variables for email delivery and site metadata, improving configuration clarity for deployment. - Added a new function in `lib.rs` to enforce production environment checks, ensuring critical secrets and configurations are set before application startup. - Removed outdated Docker and Railway configuration files to streamline the project structure and reduce maintenance overhead. These changes enhance the security and clarity of the application's configuration, ensuring safer deployments.
1 parent 1dc050d commit 60f8699

11 files changed

Lines changed: 134 additions & 331 deletions

File tree

.env.example

Lines changed: 86 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ LOG_LEVEL=debug
2424
# Directory used for daily-rotated log files. File logging is opt-in.
2525
# - Set to a writable path (e.g. `/var/log/portfolio-backend`) to enable
2626
# rotating `app.log.<date>` and `error.log.<date>` files.
27-
# - Leave unset on ephemeral container platforms (Railway, Fly.io,
28-
# Kubernetes, etc.) — logs stream to stdout and the platform aggregates
27+
# - Leave unset on ephemeral container platforms (Fly.io, Kubernetes,
28+
# etc.) — logs stream to stdout and the platform aggregates
2929
# them. Writing to disk inside an ephemeral / read-only container will
3030
# just panic the process at startup.
3131
# - Local development defaults to `./logs` when this variable is unset.
32-
# LOG_DIR=logs
32+
LOG_DIR=logs
3333

3434
# ----------------------------------------------------------------------------
3535
# Database
@@ -55,6 +55,12 @@ DB_CONNECT_TIMEOUT=30
5555
# Seconds before an idle connection is dropped from the pool
5656
DB_IDLE_TIMEOUT=600
5757

58+
# Total time budget (in seconds) the app will keep retrying the initial DB
59+
# connection at startup before giving up. Defaults to 60 — enough to absorb
60+
# docker-compose / Railway DNS races without letting a misconfiguration
61+
# hang the container indefinitely.
62+
# DB_CONNECT_RETRY_SECS=60
63+
5864
# ----------------------------------------------------------------------------
5965
# CORS
6066
# ----------------------------------------------------------------------------
@@ -73,11 +79,75 @@ JWT_SECRET=change_me_to_a_long_random_secret
7379
# Secret used to sign refresh tokens — must differ from JWT_SECRET
7480
REFRESH_TOKEN_SECRET=change_me_to_another_long_random_secret
7581

82+
# `iss` claim embedded in issued JWTs (and validated on incoming tokens).
83+
# Defaults to "portfolio-backend" when unset.
84+
# JWT_ISSUER=portfolio-backend
85+
86+
# `aud` claim embedded in issued JWTs (and validated on incoming tokens).
87+
# Defaults to "portfolio-frontend" when unset.
88+
# JWT_AUDIENCE=portfolio-frontend
89+
90+
# ----------------------------------------------------------------------------
91+
# Two-Factor Authentication (TOTP)
92+
# ----------------------------------------------------------------------------
93+
# Issuer label shown in authenticator apps (Google Authenticator, 1Password,
94+
# Authy, etc.) when scanning the admin's TOTP QR code. Set this to your
95+
# real domain so users can identify the entry. Defaults to
96+
# "infinitedim.site" when unset.
97+
# TOTP_ISSUER=infinitedim.site
98+
99+
# ----------------------------------------------------------------------------
100+
# Email Delivery (Contact Form)
101+
# ----------------------------------------------------------------------------
102+
# Resend API key used to deliver contact-form messages. When unset, the
103+
# mailer is disabled — contact submissions are still persisted to the DB
104+
# so the admin inbox keeps working, but no notification email is sent.
105+
# Get a key at https://resend.com.
106+
RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
107+
108+
# `from` address on outgoing emails. Must belong to a domain you have
109+
# verified in Resend. Defaults to "noreply@example.com" (which Resend will
110+
# reject).
111+
RESEND_FROM=noreply@yourdomain.com
112+
113+
# Recipient address for contact-form notifications. Defaults to RESEND_FROM
114+
# when unset (i.e. the message is sent to yourself).
115+
CONTACT_EMAIL=you@yourdomain.com
116+
76117
# ----------------------------------------------------------------------------
77-
# Redis
118+
# Site Metadata (RSS feed)
78119
# ----------------------------------------------------------------------------
79-
# Redis connection URL (used for rate limiting and caching)
80-
# For local Docker setup: redis://localhost:6379
120+
# Public URL of the frontend site. Used as the canonical link in the RSS
121+
# feed and as the base URL for blog post links. Defaults to
122+
# "https://infinitedim.vercel.app".
123+
SITE_URL=https://infinitedim.vercel.app
124+
125+
# Title shown at the top of the RSS feed. Defaults to
126+
# "Terminal Portfolio Blog".
127+
SITE_TITLE=Terminal Portfolio Blog
128+
129+
# Description shown in the RSS feed metadata.
130+
SITE_DESCRIPTION=Latest writings on engineering, design, and craft.
131+
132+
# ----------------------------------------------------------------------------
133+
# Uploads
134+
# ----------------------------------------------------------------------------
135+
# On-disk directory used to store images uploaded via the admin panel.
136+
# Defaults to "uploads/blog" (relative to the working directory).
137+
# - On ephemeral container platforms (Fly.io, etc.) the runtime
138+
# filesystem is read-only and any value here will fail with a
139+
# PermissionDenied error on first upload. Mount a persistent volume
140+
# and point UPLOAD_DIR at it, or use object storage (S3/R2/etc.)
141+
# instead.
142+
UPLOAD_DIR=uploads/blog
143+
144+
# ----------------------------------------------------------------------------
145+
# Redis (reserved — not yet integrated)
146+
# ----------------------------------------------------------------------------
147+
# Redis is provisioned in docker-compose for future rate-limiting / caching
148+
# work but the backend code does NOT currently read REDIS_URL. Setting this
149+
# variable today has no effect; it is documented here only so the
150+
# docker-compose stack and forthcoming features stay in sync.
81151
REDIS_URL=redis://localhost:6379
82152

83153
# ----------------------------------------------------------------------------
@@ -104,3 +174,13 @@ ADMIN_HASH_PASSWORD=
104174
# JWT bearer token from roadmap.sh — used server-side to proxy API calls.
105175
# Never expose this to the browser.
106176
ROADMAP_AUTH_TOKEN=Bearer <your_roadmap_jwt_token>
177+
178+
# ----------------------------------------------------------------------------
179+
# API Documentation (Swagger UI)
180+
# ----------------------------------------------------------------------------
181+
# Whether to mount the interactive Swagger UI at `/api/docs`. The raw
182+
# OpenAPI spec is always served at `/api/docs/openapi.json` regardless.
183+
# - Defaults to `true` (UI mounted).
184+
# - Set to `false` (or `0`) to keep the spec accessible while hiding
185+
# the interactive UI from public consumers in production.
186+
ENABLE_SWAGGER_UI=true

.github/workflows/cd-production.yml

Lines changed: 0 additions & 71 deletions
This file was deleted.

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ debug/
1212
target/
1313

1414
# Cargo.lock is intentionally tracked: this crate is a deployable binary, so we
15-
# need reproducible builds for Railway/Docker and supply-chain auditing.
15+
# need reproducible builds for Docker and supply-chain auditing.
1616
# See https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
1717

1818
# These are backup files generated by rustfmt

Cargo.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ tower-http = { version = "0.6", features = [
1818
"fs",
1919
] }
2020
# Production-grade rate limiter. Replaces the in-memory HashMap that was
21-
# broken behind the Railway/Vercel proxy (no X-Forwarded-For parsing) and
21+
# broken behind reverse proxies without X-Forwarded-For parsing and
2222
# allowed unbounded memory growth.
2323
tower_governor = { version = "0.8", default-features = false, features = [
2424
"axum",

RAILWAY_ENV.md

Lines changed: 0 additions & 99 deletions
This file was deleted.

0 commit comments

Comments
 (0)