Authelia generates broken URLs (missing /reset-password/step2, has
%2F encoding). Instead of chaining replaces, hardcode the known-good
base URL and extract just the JWT token via split.
Made-with: Cursor
Authelia URL-encodes the /login path in authelia_url when building
the JWT reset link, producing bc.a250.ca%2Flogin instead of
bc.a250.ca/login. Single replace fixes it without any other mangling.
Made-with: Cursor
The replace hacks in email templates were double-prepending
/reset-password/step2 since Authelia already generates the correct
URL. Removed the Traefik redirectregex middleware too since it's
no longer needed.
Made-with: Cursor
- triggerPasswordReset now logs the full URL, status, and response body
- Detects Authelia "KO" status responses as errors
- Forwards real client IP instead of 127.0.0.1
- Sets displayName=email on LDAP user creation for friendly email greetings
- Backfills displayName for existing users on re-provision
Made-with: Cursor
When a subscription is deleted, all Docker volumes for the customer
stack are tarred to /data/archives/{stackName}/ before the stack is
removed and volumes pruned. On resubscribe or reactivation, volumes
are restored from the archive before deploying the stack.
Made-with: Cursor
Add gitea_config volume for /etc/gitea so app.ini survives restarts.
Set INSTALL_LOCK=true to skip the setup wizard since all config is
provided via environment variables.
Made-with: Cursor
- Update dashboard buttons to use correct Authelia paths:
/settings/two-factor-authentication and /settings/security
- Change customer stack ACL from one_factor to two_factor so Authelia
enables the 2FA registration UI (passkeys, TOTP)
Made-with: Cursor
Replaces the generic "Account Settings" button with a dedicated card
containing passkey, TOTP, and password links plus a warning about
enabling 2FA to prevent account compromise.
Made-with: Cursor
The Remote-Groups header reflects groups at login time. If a user was
added to 'customers' after logging in (via /activate), the dashboard
would show "No Active Subscription". Now checks LDAP directly as
fallback.
Made-with: Cursor
If a logged-in user has a Stripe customer ID but isn't in the customers
group yet, they've paid but haven't activated. Send them to /activate
instead of showing "No Active Subscription".
Made-with: Cursor
The welcome page button was linking to Authelia's reset page which
requires an active login session. Now it POSTs to /resend-reset which
calls the Authelia API server-side and sends the email immediately.
Button text updated from "Reset Password" to "Set Password".
Made-with: Cursor
Previously, users already in LDAP but not yet activated (e.g. webhook
created the user, or lapsed sub) were redirected to the auth-gated
dashboard. Now only active customers (in 'customers' group) skip the
welcome page — everyone else sees onboarding with password reset.
Made-with: Cursor
The webhook was provisioning the user before the success page loaded,
causing IsNew=false and skipping the welcome/onboarding page entirely.
Now:
- Webhook only ensures user+stripe ID as a backstop (no password email)
- Success page is the sole owner of password reset + welcome flow
- Uses group membership (not IsNew) to distinguish new vs returning:
if already in 'customers' group -> dashboard, otherwise -> welcome
Made-with: Cursor
The triggerPasswordReset function existed but was never called.
New users now receive a set-password email immediately after their
Stripe checkout completes.
Made-with: Cursor
Replace per-form onsubmit handlers with a single script that handles
all data-stack-action forms identically: confirm if needed, then
disable the button and show a contextual loading label.
Made-with: Cursor
- Inverted condition was showing 'being provisioned' when stack not deployed
- Actions block was gated on StackDeployed so no Start button after destroy
- Start button now always shown when not running
- Destroy button only shown when stack is deployed
- 'Being provisioned' message replaced with accurate 'stopped' message
Made-with: Cursor
Dashboard was auto-deploying any missing stack on every page load.
This stomped on the Destroy action. Stack creation only happens at
activation and via explicit Start — not on dashboard render.
Made-with: Cursor
Each customer now receives a dedicated Uptime Kuma monitoring instance
at their subdomain. Drops the unused Redis sidecar from the template.
Made-with: Cursor
- stack-template.yml: prominent comment explaining this is the product
being sold and how to swap in the real application image
- deploy-stack-dev.sh: force-update locally-built images after stack
deploy so swarm always runs the freshly built container
Made-with: Cursor
- Also check sub.CancelAt > 0 (handles explicit cancel_at date, not just period-end)
- Fall back to item CurrentPeriodEnd for the display date since current_period_end
moved off the top-level Subscription object in stripe-go v84
Made-with: Cursor
- Remove public/private toggle — all customer stacks now always deploy
behind authelia-auth middleware, no exceptions
- Remove ALLOW_CUSTOMER_STACK_AUTH_TOGGLE and CUSTOMER_STACK_REQUIRE_AUTH_DEFAULT
config, env vars, routes, and UI
- Replace docker-compose.dev.yml + docker-compose.swarm-dev.yml with
unified stack.yml for swarm deployment
- Various handler, ldap, stripe, swarm, and template additions from
prior work sessions
Made-with: Cursor
- Add internal/version package with ldflags-injected Commit/BuildTime
- Dockerfile accepts BUILD_COMMIT/BUILD_TIME args, passes via -ldflags
- Log version on startup, expose GET /version endpoint
- Show commit hash badge in bottom-right of landing + dashboard pages
- Deploy scripts gate on clean git tree and pass commit to build
- Remove staging files, misc config updates
Made-with: Cursor
- Removed duplicate user_verification from top-level webauthn config
- user_verification is now properly configured only under selection_criteria
- Passkey authentication remains enabled and properly configured