perf,feat(events),hardening: second review pass

Fleet overview N+1:
- InstanceResourceService gains a 30s TTL cache with single-flight
  coalescing; the overview no longer fires six live upstream requests per
  device on every render (plus the re-login storm), while the per-device
  detail route probes live via force:true

Event journal becomes live:
- job terminal transitions (manual executions and the interrupted sweep)
  now append to the journal, so /api/v1/events SSE feeds the frontend's
  invalidation controller that was built but never received events
- journal pruning moves off the append hot path (was an unindexable
  full-table json_extract scan per insert) onto the retention timer

Console auth hardening:
- scrypt upgraded from N=16384 to N=2^16 (OWASP interactive guidance);
  a new password_kdf column records the derivation per row and legacy
  hashes rehash transparently on the next successful login without
  invalidating sessions (migration 18)

Legacy stack:
- instance URL validation blocks IPv4-compatible IPv6 after WHATWG
  canonicalization (::a9fe:a9fe metadata, ::7f00:1 loopback slipped past)
- status polls cool down auto-login for 60s after a failed attempt so a
  stale saved password cannot hammer the device into an account lockout

Build hygiene:
- web bundle splits app (410kB) from vendor (212kB) so framework code
  stays cacheable across releases; stale root package-lock.json removed
  (pnpm is the only lockfile)
This commit is contained in:
chick
2026-09-07 02:20:41 +08:00
parent 29a0eee854
commit c1be714ba4
14 changed files with 268 additions and 3859 deletions
+4 -1
View File
@@ -14,7 +14,10 @@ export function normalizeBaseUrl(rawUrl) {
(ipv4[0] === 169 && ipv4[1] === 254) ||
(ipv4[0] >= 224)
)
const prohibitedIpv6 = hostname === '::' || hostname === '::1' || /^fe[89ab][0-9a-f]:/.test(hostname) || hostname.startsWith('ff') || hostname.startsWith('::ffff:') || hostname.startsWith('64:ff9b:')
// WHATWG canonicalization turns ::127.0.0.1 into ::7f00:1 and ::169.254.169.254 into
// ::a9fe:a9fe; the IPv4-compatible form (:: <1-2 groups>) must be matched after
// canonicalization or it smuggles loopback/metadata targets past the list.
const prohibitedIpv6 = hostname === '::' || hostname === '::1' || /^::[0-9a-f]{1,4}(:[0-9a-f]{1,4})?$/.test(hostname) || /^fe[89ab][0-9a-f]:/.test(hostname) || hostname.startsWith('ff') || hostname.startsWith('::ffff:') || hostname.startsWith('64:ff9b:')
const prohibitedMetadata = hostname === '100.100.100.200' || hostname === '168.63.129.16'
const prohibitedTransition = hostname.startsWith('2002:')
const isIpLiteral = isIP(hostname) !== 0
+9
View File
@@ -190,6 +190,7 @@ export function createSimAdminClient(instance, { fetchImpl = fetch, timeoutMs =
}
let ephemeralSecret = ''
let lastFailedAutoLoginAt = 0
async function ensureAuthenticated({ credential } = {}) {
const status = await fetchJson('/api/auth/status')
const auth = apiData(status.data) || {}
@@ -203,12 +204,20 @@ export function createSimAdminClient(instance, { fetchImpl = fetch, timeoutMs =
return { configured: auth.configured ?? null, authenticated: false, loginAttempted: false, statusCode: status.status, reason: 'password_required' }
}
if (credential !== undefined) ephemeralSecret = ''
// Status polls auto-login with the saved password; without a cooldown a
// stale password turns every refresh into a fresh login attempt, which
// some devices punish with account lockouts.
if (credential === undefined && !ephemeralSecret && Date.now() - lastFailedAutoLoginAt < 60_000) {
return { configured: auth.configured ?? null, authenticated: false, loginAttempted: false, statusCode: status.status, reason: 'login_cooldown' }
}
const login = await request('/api/auth/login', {
method: 'POST',
headers: { 'content-type': 'application/json', accept: 'application/json' },
body: JSON.stringify({ password }),
})
if (credential !== undefined && login.ok) ephemeralSecret = password
if (login.ok) lastFailedAutoLoginAt = 0
else if (credential === undefined) lastFailedAutoLoginAt = Date.now()
return { configured: auth.configured ?? null, authenticated: login.ok, loginAttempted: true, statusCode: login.status, reason: login.ok ? null : 'login_failed' }
}