mirror of
https://github.com/screentinker/screentinker.git
synced 2026-06-14 18:22:46 -06:00
fix(workspaces): use APP_URL env var for invite-accept URL generation
Slice 1+3 (c4fbd2b) introduced PUBLIC_URL as the env var name for the
public-facing origin used to construct invite-accept URLs. The README
has long documented APP_URL as the canonical name for this concept
(used for Stripe callbacks in the existing codebase). The new code
should have read APP_URL from the start; PUBLIC_URL was unintentional
naming drift.
Caught during prod-deploy survey on 2026-05-17: APP_URL was set on the
production systemd unit and documented in the README, but read by no
code path on origin/main. PUBLIC_URL was read by slice-1 code but set
nowhere. The bug was masked in 99% of cases by the request-derived
fallback (${req.protocol}://${req.get('host')}) which produces the
correct URL when invites are triggered from browsers behind Cloudflare.
It would have manifested for any future non-browser-triggered invite
path.
README updated to note APP_URL covers both Stripe callbacks and
invite-accept URL generation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
caa9fd0f40
commit
159a36ed99
|
|
@ -111,7 +111,7 @@ Schema migrations run automatically on first boot — no manual migration comman
|
|||
| `SELF_HOSTED` | First user gets all features unlocked | `false` |
|
||||
| `DISABLE_REGISTRATION` | Block new account creation (including OAuth auto-signup). First-user setup on an empty DB is still allowed. | `false` |
|
||||
| `DISABLE_HOMEPAGE` | Redirect `/` to `/app` instead of serving the marketing landing page. For internal-only self-hosted deployments. | `false` |
|
||||
| `APP_URL` | Your public URL (used for Stripe callbacks) | _(none)_ |
|
||||
| `APP_URL` | Your public URL (used for Stripe callbacks and invite-accept URLs in emailed invites) | _(none)_ |
|
||||
| `JWT_SECRET` | JWT signing key (auto-generated if not set) | _(auto)_ |
|
||||
| `SSL_CERT` | Path to SSL certificate | `server/certs/cert.pem` |
|
||||
| `SSL_KEY` | Path to SSL private key | `server/certs/key.pem` |
|
||||
|
|
|
|||
|
|
@ -259,16 +259,18 @@ router.post('/:id/invites', async (req, res) => {
|
|||
return res.status(409).json({ error: 'An invite for this email is already pending' });
|
||||
}
|
||||
|
||||
// Build accept URL. PUBLIC_URL env var (when set) pins the public-facing
|
||||
// Build accept URL. APP_URL env var (when set) pins the public-facing
|
||||
// origin regardless of how the request arrived - recommended in prod so
|
||||
// invites triggered from non-browser sources (curl, future API automation)
|
||||
// always carry the canonical origin. Falls back to request-derived for
|
||||
// local dev and when PUBLIC_URL isn't set; with trust proxy on, req.protocol
|
||||
// + req.get('host') reflect Cloudflare-forwarded X-Forwarded-Proto + Host.
|
||||
// Path is /app#/accept-invite/<id> - the SPA lives at /app, so a bare
|
||||
// /#/accept-invite/<id> would land on the marketing landing page in dev
|
||||
// (and rely on the DISABLE_HOMEPAGE redirect in prod). /app is explicit.
|
||||
const publicBase = process.env.PUBLIC_URL || `${req.protocol}://${req.get('host')}`;
|
||||
// always carry the canonical origin. Same env var the rest of the codebase
|
||||
// uses for Stripe callbacks (see README env-var table). Falls back to
|
||||
// request-derived for local dev and when APP_URL isn't set; with trust
|
||||
// proxy on, req.protocol + req.get('host') reflect Cloudflare-forwarded
|
||||
// X-Forwarded-Proto + Host. Path is /app#/accept-invite/<id> - the SPA
|
||||
// lives at /app, so a bare /#/accept-invite/<id> would land on the
|
||||
// marketing landing page in dev (and rely on the DISABLE_HOMEPAGE
|
||||
// redirect in prod). /app is explicit.
|
||||
const publicBase = process.env.APP_URL || `${req.protocol}://${req.get('host')}`;
|
||||
const acceptUrl = `${publicBase}/app#/accept-invite/${inviteId}`;
|
||||
const org = db.prepare('SELECT name FROM organizations WHERE id = ?').get(ws.organization_id);
|
||||
const { subject, text } = buildInviteEmail({
|
||||
|
|
|
|||
Loading…
Reference in a new issue