feat(ops): put production in demo mode to complete the cutover (#191)
Production could not boot during the cutover to the committed compose file: `DEMO_MODE` is false there, which makes the three PayPal secrets required, and they were not available. Demo mode is the interim the compose file's own header sanctions for exactly this — the whole cart and checkout flow works and nobody is ever charged.
Two things made this cost more than it should have, and both are now written down rather than left to be rediscovered.
`DEMO_MODE` is hardcoded rather than interpolated, so setting a `DEMO_MODE` stack variable in Portainer does nothing at all — there is no `${...}` for it to substitute into and the file's value wins silently. That hardcoding is right: the one value deciding whether the shop takes money should not be flippable from a web UI without a commit anybody can read. But the failure mode reads as "I set it and it ignored me", so the line now says so.
Declaring `PAYPAL_CLIENT_ID=` with an empty value is identical to not declaring it. `isPresent` rejects a blank string deliberately, because set-to-nothing is a mistake rather than a value.
The state is loud in both places that can see it. The compose file leads with a banner saying production is taking no money, and `composeEnvironment.test.ts` asserts `DEMO_MODE` is `true` — that assertion is the guard, not a formality: it fails the moment the file and the expectation disagree, in either direction, so this cannot be switched back quietly and cannot be left on unnoticed.
#191 restores it.
This commit is contained in:
@@ -56,7 +56,13 @@ const DEPLOYMENTS = [
|
||||
},
|
||||
{
|
||||
file: 'docker-compose.prod.yml',
|
||||
demoMode: 'false',
|
||||
// TEMPORARY, and #191 restores it to 'false'. Production is in demo mode
|
||||
// since 2026-08-25 — the stack was brought up during the cutover to this
|
||||
// file before the live PayPal credentials were available, and demo mode is
|
||||
// the sanctioned interim for that. This assertion is what keeps the state
|
||||
// honest: it fails the moment the compose file and this expectation
|
||||
// disagree, so neither can be changed quietly, in either direction.
|
||||
demoMode: 'true',
|
||||
// Deliberately unrestricted. Production has to be able to reach real
|
||||
// customers, and it is the one environment where that is correct.
|
||||
requiresMailAllowlist: false
|
||||
|
||||
+22
-5
@@ -104,15 +104,32 @@ services:
|
||||
- PGPASSWORD=${DB_PASSWORD}
|
||||
- PGDATABASE=redefined
|
||||
|
||||
# ===================================================================
|
||||
# PRODUCTION IS IN DEMO MODE. IT IS TAKING NO MONEY.
|
||||
#
|
||||
# The whole cart and checkout flow works end to end and NOBODY IS EVER
|
||||
# CHARGED. This is the deliberate interim the note below describes, taken
|
||||
# on 2026-08-25 to bring the stack up during the cutover to this file
|
||||
# before the live PayPal credentials were to hand.
|
||||
#
|
||||
# Restoring it is #191. Set this back to `false`, fill the three stack
|
||||
# variables below, and redeploy. Until then every order placed is a
|
||||
# pretend one.
|
||||
# ===================================================================
|
||||
#
|
||||
# Real payments. This is the difference between production and QA, and it
|
||||
# is why the three PayPal secrets are required rather than optional — the
|
||||
# app refuses to start without them when this is false.
|
||||
#
|
||||
# To bring the stack up before PayPal is configured, set this to `true`
|
||||
# and the three PAYPAL_ lines can be removed. The full cart and checkout
|
||||
# flow then works end to end and NOBODY IS EVER CHARGED. That is a
|
||||
# deliberate interim state and a quiet disaster if it is left on.
|
||||
- DEMO_MODE=false
|
||||
# HARDCODED, AND NOT INTERPOLATED, ON PURPOSE. Portainer substitutes stack
|
||||
# variables into this file; it does not hand them to the container. A
|
||||
# `DEMO_MODE` stack variable therefore does NOTHING — there is no `${...}`
|
||||
# here for it to substitute into, and the value below wins silently. That
|
||||
# is deliberate: the one value that decides whether the shop takes money
|
||||
# should not be flippable from a web UI without a commit anybody can read.
|
||||
# It is also exactly what wasted an hour during the cutover, so it is
|
||||
# written down rather than left to be rediscovered.
|
||||
- DEMO_MODE=true
|
||||
- PAYPAL_ENV=live
|
||||
- PAYPAL_CLIENT_ID=${PAYPAL_CLIENT_ID}
|
||||
- PAYPAL_CLIENT_SECRET=${PAYPAL_CLIENT_SECRET}
|
||||
|
||||
Reference in New Issue
Block a user