Move airport-editor HTML routes and validate API from ADM to I1+, matching
sweatbox. Config editor stays Administrator-only. Nav/dashboard use
CanAccessAirportEditor; PE tests cover I1 shell, nav, and validate-apt.
Revalidate optionalSession on landing, harden PE assertions for CSRF
rotation and soft-delete cookie clear, add I1 sweatbox API + delete CSRF
tests, fix sweatbox manual/README authz copy, drop dead permanentDisabled
assignment and misleading CSRF re-issue after password change.
- Account page: change password + soft-delete (optional hard-delete via env)
- Session revalidation + claims overlay on HTML and dual-accept API (KD-9)
- Authz: user editor SUP+, sweatbox I1+, config/airport ADM
- Fix pilot rating options: full scale for editors (no actor pilot ceiling)
- Refresh rejects inactive/suspended; DeleteUser for permanent self-delete
- Dashboard/nav as user home with Account + capability-gated tools
Bootstrap data-bs-theme + localStorage toggle in layout chrome; map
console CSS (users/sweatbox/airport editor) onto shared --ofs-console-*
tokens; swap OSM basemap for dark maps. Include PE and webjs coverage,
and the approved sweatbox TWRTrainer P2 command-parity design doc.
Serve a full HTML manual at /sweatbox/manual (Administrator, no FSD
dependency) and link it from the control panel titlebar in a new tab
without changing the dense operator layout.
Vertex circleMarkers now use the surfaceStyle palette (taxi blue-gray,
runway near-black, hold orange, parking green) so types stay distinct
while keeping centered grab handles and drag behavior.
Vertex drag auto-selects a surface; the select tip unhid a banner and
reflowed the layout under the cursor. Skip the tip while dragging and keep
the status strip always one line tall so messages never shove the map.
divIcon knobs were offset because CSS margin:0 !important wiped Leaflet
iconAnchor margins (marginLeft/Top), pinning the disc corner on the lat/lng.
Use L.circleMarker instead so the visual is always centered on the point.
Show 10px centered nodes on all surfaces in Select mode. Capture hit-test
finds the nearest vertex on any surface (no click-to-select first) and
auto-selects that surface. Fix knob offset via border-box + override of
leaflet-div-icon defaults; do not snap vertex to cursor on mousedown.
Root cause: clicking a taxiway selected it (blue highlight) then the same
DOM click re-fired on the map and cleared selection, so handles never stayed
visible and vertex drag could not run.
Leaflet only stops map re-fire when originalEvent._stopped is set; native
stopPropagation alone does not set that flag. Set _stopped, disable path
bubblingMouseEvents, and gate map clicks after feature clicks. Disable boxZoom
(blue rectangle). Cache-bust static module URLs (assets are go:embed).
RCA: Leaflet Map.Drag listens for mousedown on the map container. Vertex
handle DOM hits were unreliable (pane pointer-events, non-interactive
stacking), so the container always received the gesture and panned the map.
Fix: capture-phase listener on the map container hit-tests selected-surface
vertices in pixel space; on hit, stopImmediatePropagation, disable map
dragging, and document-track the pointer while live-updating polyline
setLatLngs. Handles are visual-only. Pure findNearestVertexPx unit-tested.
Replace fragile L.Marker.draggable divIcon handles with document-level
pointer capture, a dedicated high z-index vertex pane, and live
setLatLngs/setLatLng while dragging. Fix CSS that overrode Leaflet
marker position:absolute (which broke hit targets).
Make selected surface vertices reliably draggable with live polyline/marker
updates, single aligned handles, and post-drag map-click suppress that does
not block full refresh on dragend.
Extend serviceapi pilot/ATC DTOs; store PilotRating on LoginData at auth;
populate online_users from session snapshot. Datafeed drops outer
PilotRating/TextATIS, maps info-section to FlightPlan object, zeros
military/QNH, and uses ConfigFsdServerIdent for server (fallback OPENFSD).
Delete handleConn/eventLoop/readLoginPackets, runClassicFSD/listenLoop,
and Deps.Listen/ForceClassicFSD/Server.listen/useClassicFSD. Always run
gnet. Rewrite TestRunServiceHTTPAndListen to use FSDBound select
readiness. Convert verifyperf to gnet-only baseline. Keep shared auth,
broadcast, MOTD helpers and SenderWorker for sweatbox synthetics.
getJwtContext returns nil when missing/wrong type; requireJwtContext
aborts with 500 at every non-optional call site so handlers fail closed
without panic. NewCoalesceOutbound returns (*CoalesceOutbound, error)
instead of panicking on nil writeAsync. Tracks the cleanup design doc.
Pin the enhanced map host to a definite viewport height and invalidate
Leaflet size on layout changes so OSM tiles fill the pane. Remove the
bottom apt/air text backup forms to reclaim console space.
Add config REQUIRE_PILOT_PPL (default false). When enabled, #AP pilot
connections need pilot_rating PPL or higher; ATC is unchanged. Exposed
in admin config editor and /api/v1/config.
I1–I3 may open the Users directory and set network/pilot ratings up to
their own ceilings on any certificate. Create and name/password remain
Supervisor+. Add users.pilot_rating (0–5) via migration.
Replace the three-card user editor with an SSR directory plus create/edit
rail. Filters live in the URL, POST preserves them via dir_* fields, and
users.css provides a dense 16:9 layout. PE tests cover search, sort,
pagination, and rating-ceiling scoping.
Docs point fixtures at pkg/twrfiles/testdata; export DefaultRegistration;
tighten coverage tests; drop dead legacy fixture paths; AGENTS wire checklist.
CI race runs failed intermittently on TestE2E_ATCChatAndITRange and
TestE2E_BeaconAssignAndFPRequest after security/gnet hardening.
- Wait for pilot $FP broadcast before SERVER:FP re-request so the plan is
stored across event loops (empty plan skipped #PC beacon).
- Exchange ATC % positions until peers observe each other before @49999
chat and @94835 IT range fan-out.