feat(landing): self-serve registration page via Drasl API (B1)
Add a static /register page (en/es/eu) that calls the Drasl REST API v2 directly from the browser: register -> login -> optional skin upload, all in the pixel design. Guests never touch Drasl's own web UI. Drasl config gains the pieces this needs: - CORSAllowOrigins scoped to the apex (the API has no CORS until set; never "*"). - [RateLimit] for the now public-facing anonymous POST /users. - [RegistrationNewPlayer] with RequireInvite driven by a new REGISTRATION_MODE (invite|open) .env flag. REGISTRATION_MODE has two consumers from one key: render-config.sh derives the TOML boolean for Drasl (drasl is TOML-only, no env), and the landing build reads it to show/hide the invite-code field. render-config.sh halts on any value other than invite/open. Security verified against drasl source: anonymous POST /users cannot set privileged fields (isAdmin/isLocked/chosenUuid/maxPlayerCount are gated on callerIsAdmin in CreateUser), so browser-direct registration is safe. Docs: plan/16-landing-registration.md captures the design + the B1 vs fork decision; build-order, deploy, and the deploy skill wire REGISTRATION_MODE and the landing-rebuild requirement (www/ is gitignored, not updated by a git pull). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -588,6 +588,64 @@ section { position: relative; z-index: 1; }
|
||||
.reveal.anim { animation: reveal-in .6s cubic-bezier(.2,.7,.3,1) both; }
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
REGISTER PAGE
|
||||
============================================================ */
|
||||
.reg-wrap { width: min(560px, calc(100% - 48px)); margin-inline: auto; }
|
||||
.reg-card {
|
||||
background: var(--surface);
|
||||
padding: clamp(24px, 4vw, 40px);
|
||||
box-shadow: inset 2px 2px 0 var(--bevel-hi), inset -2px -2px 0 var(--bevel-lo), 0 8px 24px oklch(0 0 0 / 0.4);
|
||||
}
|
||||
.reg-card .eyebrow { display: block; margin-bottom: 12px; }
|
||||
.reg-card h1 { font-size: clamp(28px, 4vw, 40px); }
|
||||
.reg-card .lead { margin: 14px 0 0; font-size: 17px; }
|
||||
|
||||
.reg-form { display: flex; flex-direction: column; gap: 18px; margin-top: 28px; }
|
||||
.reg-field { display: flex; flex-direction: column; gap: 7px; }
|
||||
.reg-field label {
|
||||
font-family: var(--font-pixel); font-size: 9px; letter-spacing: 0.1em;
|
||||
color: var(--dim); text-transform: uppercase; line-height: 1.8;
|
||||
}
|
||||
.reg-input {
|
||||
font-family: var(--font-body); font-size: 16px; color: var(--text);
|
||||
background: var(--slot); border: 0; padding: 13px 14px;
|
||||
box-shadow: inset 2px 2px 0 var(--bevel-lo), inset -2px -2px 0 var(--bevel-hi);
|
||||
}
|
||||
.reg-input::placeholder { color: var(--dim); }
|
||||
.reg-input:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
|
||||
.reg-field .hint { color: var(--dim); font-size: 13px; }
|
||||
|
||||
.reg-models { display: flex; gap: 10px; }
|
||||
.reg-models label {
|
||||
display: inline-flex; align-items: center; gap: 8px;
|
||||
font-family: var(--font-body); font-size: 15px; letter-spacing: 0; text-transform: none;
|
||||
color: var(--muted); cursor: pointer;
|
||||
background: var(--slot); padding: 9px 14px;
|
||||
box-shadow: inset 2px 2px 0 var(--bevel-lo), inset -2px -2px 0 var(--bevel-hi);
|
||||
}
|
||||
.reg-models input { accent-color: var(--accent); }
|
||||
|
||||
.reg-msg { font-size: 15px; padding: 12px 14px; box-shadow: inset 2px 2px 0 var(--bevel-lo), inset -2px -2px 0 var(--bevel-hi); }
|
||||
.reg-msg[hidden] { display: none; }
|
||||
.reg-msg.err { background: oklch(0.30 0.10 30); color: oklch(0.92 0.04 40); }
|
||||
.reg-msg.ok { background: var(--slot); color: var(--accent); }
|
||||
|
||||
.reg-foot { margin-top: 22px; color: var(--dim); font-size: 14.5px; }
|
||||
.reg-foot a { color: var(--accent); }
|
||||
|
||||
.reg-success[hidden], .reg-skin[hidden] { display: none; }
|
||||
.reg-success { margin-top: 26px; display: flex; flex-direction: column; gap: 16px; }
|
||||
.reg-uuid {
|
||||
font-family: var(--font-mono); font-size: 17px; color: var(--gold);
|
||||
background: var(--slot); padding: 8px 12px; word-break: break-all;
|
||||
box-shadow: inset 2px 2px 0 var(--bevel-lo), inset -2px -2px 0 var(--bevel-hi);
|
||||
}
|
||||
.reg-skin { margin-top: 8px; display: flex; flex-direction: column; gap: 16px; border-top: 1px solid var(--line); padding-top: 22px; }
|
||||
.reg-skin h3 { font-size: 22px; }
|
||||
.reg-skin .lead { font-size: 15px; }
|
||||
.reg-file-name { color: var(--muted); font-size: 14px; }
|
||||
|
||||
/* ============================================================
|
||||
RESPONSIVE
|
||||
============================================================ */
|
||||
|
||||
Reference in New Issue
Block a user