@@ -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
5656DB_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
7480REFRESH_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.
81151REDIS_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.
106176ROADMAP_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
0 commit comments