Add bearer API shield updates and Preact dashboard#356
Conversation
There was a problem hiding this comment.
Pull request overview
Adds user-level bearer-token support for creating/updating shields via the API host, introduces user API-token management endpoints, and migrates the authenticated dashboard frontend from imperative controllers to a Preact SPA, along with schema migrations and local-dev tooling updates.
Changes:
- Add
Authorization: Bearer <user-token>+X-Shielded-Shield-IDflow to the API host, including last-used stamping. - Add dashboard endpoints and frontend UI to create/list/revoke user API tokens.
- Migrate the dashboard SPA to Preact (new TSX entrypoint + JSX compiler config) and update schema/local-run tooling accordingly.
Reviewed changes
Copilot reviewed 31 out of 38 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tsconfig.json | Switch TS JSX to automatic runtime configured for Preact. |
| ts/model/ShieldsModel.ts | Remove old model/event-emitter dashboard state layer. |
| ts/EventEmitter.ts | Remove old event emitter used by controller-based dashboard. |
| ts/Dashboard.tsx | New Preact dashboard SPA for shields + user token management. |
| ts/Dashboard.ts | Remove old controller-based dashboard bootstrap. |
| ts/Controllers/ShieldController.ts | Remove imperative DOM-based shield editor controller. |
| ts/Controllers/SecretInputContoller.ts | Remove imperative secret input controller. |
| ts/Controllers/MarkdownInputController.ts | Remove imperative markdown input controller. |
| ts/Controllers/ErrorDialogController.ts | Remove imperative error dialog controller. |
| ts/Controllers/DashboardController.ts | Remove imperative dashboard controller wrapper. |
| ts/Controllers/ApiExampleController.ts | Export example generators for reuse by Preact UI. |
| ts/api/tokens.ts | Add browser client for dashboard user API token endpoints. |
| ts/api/shields.ts | Extend shield type with optional APIID. |
| static/style/style.css | Update generated CSS for new dashboard/token UI styling. |
| static/main.js | Update generated JS bundle for Preact dashboard + new APIs. |
| scss/_dashboard.scss | Add styles for API-token dashboard section + validation display. |
| schema/002_shield_api_id.sql | Add nullable api_id and per-user uniqueness constraint. |
| schema/001_user_api_tokens.sql | Add user_api_tokens table for bearer-token storage. |
| schema/000_BASELINE.sql | Provide baseline schema snapshot for clean provisioning. |
| run-local.sh | Add clean local runner (MySQL container + migrations + Caddy). |
| pages/privacy_templ.go | Regenerate templ output (version bump). |
| pages/index_templ.go | Regenerate templ output (version bump). |
| pages/dashboard.templ | Update dashboard page to mount the Preact SPA + add nav. |
| pages/dashboard_templ.go | Regenerate dashboard templ output to match template changes. |
| pages/common_templ.go | Regenerate common templ output (attribute handling/version bump). |
| package.json | Add preact dependency for the dashboard SPA. |
| package-lock.json | Lockfile updates for preact dependency. |
| model/user_api_tokens.go | Add mapper for hashed user API tokens + create/revoke/mark-used. |
| model/shields.go | Persist/load optional api_id and support lookups by (user_id, api_id). |
| dashboardapitokenhandlers.go | Add dashboard handlers for listing/creating/deleting user API tokens. |
| dashboardapihandlers.go | Add/validate shield API IDs and enforce per-user uniqueness. |
| dashboardapihandlers_test.go | Add unit tests for shield API ID validation regex. |
| cmd/shielded/main.go | Wire new token mapper/handlers and update dashboard route path. |
| Caddyfile.local | Add local HTTPS reverse-proxy config for host-based routing. |
| authhandlers.go | Redirect auth flows to /dashboard instead of /dashboard.html. |
| ARCHITECTURE.md | Add high-level architecture and behavior documentation for the system. |
| AGENTS.md | Add contributor guidance and repo working conventions. |
| apihandler.go | Add bearer-token shield updates/creates on API host + field validation. |
Files not reviewed (5)
- pages/common_templ.go: Generated file
- pages/dashboard_templ.go: Generated file
- pages/index_templ.go: Generated file
- pages/privacy_templ.go: Generated file
- static/style/style.css: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| > div:nth-of-type(1) { | ||
| display: flex; | ||
| gap: 0.5rem; | ||
|
|
||
| input { |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 32 out of 39 changed files in this pull request and generated 2 comments.
Files not reviewed (5)
- pages/common_templ.go: Generated file
- pages/dashboard_templ.go: Generated file
- pages/index_templ.go: Generated file
- pages/privacy_templ.go: Generated file
- static/style/style.css: Generated file
78736e9 to
98f9f69
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 32 out of 39 changed files in this pull request and generated 3 comments.
Files not reviewed (5)
- pages/common_templ.go: Generated file
- pages/dashboard_templ.go: Generated file
- pages/index_templ.go: Generated file
- pages/privacy_templ.go: Generated file
- static/style/style.css: Generated file
| func secureStringWithCharset(length int, charset string) (string, error) { | ||
| b := make([]byte, length) | ||
| limit := big.NewInt(int64(len(charset))) | ||
| for i := range b { | ||
| b[i] = charset[seededRand.Intn(len(charset))] | ||
| index, err := cryptorand.Int(cryptorand.Reader, limit) | ||
| if err != nil { | ||
| return "", err | ||
| } | ||
| b[i] = charset[index.Int64()] | ||
| } | ||
| return string(b) | ||
| return string(b), nil |
There was a problem hiding this comment.
No change needed: Go permits any integer type, including int64, as a string index. The current test suite and production build compile this exact code successfully.
| <Input label="Shield key" name="ShieldKey" value={draft.ShieldKey || ""} pattern="[a-z0-9\\-]{5,64}" title="Optional: 5-64 lowercase letters, digits, or hyphens" placeholder="e.g. production-status" autoComplete="off" spellcheck={false} aria-invalid={shieldKeyInvalid} aria-describedby={shieldKeyInvalid ? shieldKeyErrorID : undefined} /> | ||
| {shieldKeyInvalid && <p id={shieldKeyErrorID} class="input-error" role="alert">Shield key must be 5-64 lowercase letters, digits, or hyphens.</p>} | ||
| </section> | ||
| <section class="shield-container"><img src={`https://${env.ImgHost}/s/${draft.PublicID}?break=${imageTick}`} /></section> |
There was a problem hiding this comment.
Addressed in 5f82759. The shield preview now has descriptive alt text based on its current title and text.
| function Input({ label, ...attributes }: JSX.InputHTMLAttributes<HTMLInputElement> & { label: string }) { | ||
| return <div class="input-container"><label>{label}</label><input {...attributes} /></div>; | ||
| } |
There was a problem hiding this comment.
Addressed in 5f82759. The reusable Input component now generates a stable Preact ID and pairs its label with the input using for/id. The related Markdown, shield-token, and newly created user-token fields are also associated with labels.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 32 out of 39 changed files in this pull request and generated 1 comment.
Files not reviewed (5)
- pages/common_templ.go: Generated file
- pages/dashboard_templ.go: Generated file
- pages/index_templ.go: Generated file
- pages/privacy_templ.go: Generated file
- static/style/style.css: Generated file
|
|
||
| const saveShield = async (shield: ShieldInterface) => { | ||
| setError(""); | ||
| try { | ||
| const savedShield = await api.saveShield(shield); | ||
| setShields((current) => (current || []).map((item) => item.ShieldID === savedShield.ShieldID ? savedShield : item)); | ||
| } catch (requestError) { | ||
| setError(errorMessage(requestError)); | ||
| throw requestError; | ||
| } | ||
| }; |
Summary
Verification