Harden auth/upload, fix PR-delete cascade and sync backfill
- OIDC: require signed short-lived state on login callback; reject missing userinfo sub (account-takeover guard); validate token exchange + userinfo responses - Upload: safe zip extraction (path-traversal + zip-bomb cap), streamed size-capped writes, sanitised filenames - Garmin: increasing lookback resets last_sync_at for one-time backfill - Activities: delete/reprocess remove PersonalRecord rows (no FK cascade) - Profile: validate /weight limit; sync lookback UI copy - Dashboard: sleep shading uses same day as charted body battery Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -168,8 +168,11 @@ export default function DashboardPage() {
|
||||
date: rows[0]?.date ? rows[0].date.slice(0, 10) : null, // intraday endpoint wants YYYY-MM-DD
|
||||
resting_hr: pick('resting_hr'),
|
||||
sleep_duration_s: pick('sleep_duration_s'),
|
||||
sleep_start: pick('sleep_start'),
|
||||
sleep_end: pick('sleep_end'),
|
||||
// Sleep window must come from the SAME day as `date` (the day whose intraday
|
||||
// body battery we chart), not the latest non-null — otherwise the sleep
|
||||
// shading is aligned to a different night. Null here just means "no shading".
|
||||
sleep_start: rows[0]?.sleep_start ?? null,
|
||||
sleep_end: rows[0]?.sleep_end ?? null,
|
||||
hrv_nightly_avg: pick('hrv_nightly_avg'),
|
||||
sleep_score: pick('sleep_score'),
|
||||
steps: pick('steps'),
|
||||
|
||||
@@ -8,6 +8,8 @@ const AUTH_ERRORS = {
|
||||
not_authorized: "Your account isn't permitted to access MileVault — ask the admin to add you to the allowed group.",
|
||||
passkey_in_use: "That passkey is already linked to another account. Sign in to that account, or have an admin remove it on the Users page, then try linking again.",
|
||||
link_failed: "Couldn't link the passkey. Please try again.",
|
||||
invalid_state: "Your sign-in link expired or was invalid. Please try signing in again.",
|
||||
no_identity: "Couldn't read your identity from the provider. Please try again.",
|
||||
}
|
||||
|
||||
export default function LoginPage() {
|
||||
|
||||
@@ -363,7 +363,7 @@ export default function ProfilePage() {
|
||||
))}
|
||||
</div>
|
||||
|
||||
<Field label="Initial sync lookback days" hint="How far back to pull on the FIRST sync only (-1 = all history back to 2010). After that, scheduled syncs just refresh the last few days. To re-pull old history later, disconnect and reconnect.">
|
||||
<Field label="Sync lookback days" hint="How far back to pull on the first sync (-1 = all history back to 2010). After that, scheduled syncs only refresh the last day or two. Increasing this value triggers a one-time backfill of the extra history on the next sync.">
|
||||
<Input type="number" value={gcForm.sync_lookback_days} min={-1}
|
||||
onChange={e => setGcForm(f => ({ ...f, sync_lookback_days: e.target.value }))} />
|
||||
{(() => { const n = parseInt(gcForm.sync_lookback_days, 10); return n > 365 && n !== -1 })() && (
|
||||
|
||||
Reference in New Issue
Block a user