Skip to content

Document storage + auto-sync, propagation deletes, quotas + Profile tab#53

Merged
TheAngryRaven merged 2 commits into
BETAfrom
claude/cloud-document-storage
May 25, 2026
Merged

Document storage + auto-sync, propagation deletes, quotas + Profile tab#53
TheAngryRaven merged 2 commits into
BETAfrom
claude/cloud-document-storage

Conversation

@TheAngryRaven
Copy link
Copy Markdown
Owner

@TheAngryRaven TheAngryRaven commented May 25, 2026

Summary

Turns garage data into a free, auto-synced "documents" storage type — separate
from log blobs — with server-enforced limits and a new Profile tab to see usage.

Naming: these are storage types (documents / logs), not "tiers" —
"tier" is reserved for future subscription levels that will scale these limits.

Auto-sync (documents storage type)

  • New host pub/sub src/lib/garageEvents.ts: the doc-store modules
    (vehicleStorage, setupStorage, templateStorage, noteStorage) emit
    { store, key, put|delete } after each write.
  • cloud-sync/autoSync.ts subscribes (when signed in), debounces, and
    incrementally upserts (put) / deletes (delete) the single changed cloud
    record; reconciles (pull docs → push docs) on sign-in. Started in the
    plugin setup via a dynamic import, so the sync engine stays off the initial
    bundle
    (verified).

Anonymous → cloud account migration

  • Handled by the sign-in reconcile: pullDocs (cloud → local) then pushDocs
    (local → cloud). For a new account the cloud is empty, so the pull is a
    no-op and the push uploads all the anon user's local setups/vehicles/templates
    into their new account
    — automatic, no extra step. (Runs on every signed-in
    load, so it self-heals.)
  • Caveat: signing into an existing account that already has a record with the
    same id → cloud wins on the pull (local edit could be overwritten). No
    collisions on a fresh account. Timestamp-merge is the follow-up (tracked on BETA → main [ON HOLD — do not merge] #41).

Propagation deletes

  • Deleting a vehicle/setup while signed in removes the cloud record too. The
    Karts/Setups delete confirm shows a loud "deletes from every device and the
    cloud"
    warning when signed in (normal copy when signed out).

Storage types + server-side enforcement

  • documents = all structured stores (5 MB, free, auto-synced); logs = file
    blobs (20 MB, opt-in). Migration ..._storage_quotas.sql adds:
    • quota_limits table (storage_type, max_bytes) — single source of truth
      (client meter + trigger read it).
    • enforce_sync_quota BEFORE INSERT/UPDATE trigger on sync_records — rejects
      over-limit writes (quota_exceeded); sync_storage_type() helper.
    • sync_storage_usage() RPC — per-type (used, limit) for the meter.
  • storageTypes.ts holds the advisory client mirror + usage math (unit-tested).
    Client checks are advisory; the DB trigger is the real gate.

Profile tab

  • New far-right Profile tab (PanelSlot.Profile, self-gating like Coach).
    cloud-sync contributes StoragePanel — per-type usage meters + an account
    scratch pad (display name/avatar are placeholders for now).

Notes / follow-ups (tracked on #41)

  • Log-blob delete propagation is still not handled (additive).
  • Existing-account id collisions overwrite local on pull (timestamp merge TODO).
  • Over-limit batch push: pushDocs is a single batch upsert, so if local docs
    exceed the limit the whole batch is rejected (saved locally, not synced) —
    could be chunked/partial later.
  • Display name / avatar editing is a placeholder (roadmap).

Test plan

  • npm run lint / npm run typecheck / npm run test:run (328; +6 storage-type tests) / npm run build
  • Sync engine confirmed off the initial index chunk
  • Vehicle/setup delete routes through the emitting storage path (propagation fires)
  • Live pass: sign in → edit auto-syncs; delete → warning + gone from cloud; anon local garage migrates up on first sign-in; Profile meters update; over-limit write rejected server-side

https://claude.ai/code/session_01K4mWVsXnwhtEi92FVBVhB3

…le tab

Garage data (vehicles/setups/templates/types/notes) now auto-syncs to the
cloud while signed in, as its own free "documents" tier (5 MB) separate from
log blobs (20 MB). Limits are enforced server-side.

- garageEvents.ts: host pub/sub; the doc-store modules emit put/delete after
  each write. cloud-sync's autoSync.ts subscribes (lazy, started in setup),
  debounces, and incrementally upserts/deletes the one changed cloud record;
  reconciles (pull→push docs) on sign-in. Stays off the initial bundle.
- Propagation deletes: removing a vehicle/setup while signed in deletes the
  cloud record too; the Karts/Setups delete UI shows a loud "deletes from
  every device + the cloud" warning when signed in.
- Tiers + server enforcement (migration): quota_limits table (single source of
  truth), enforce_sync_quota BEFORE trigger on sync_records, and a
  sync_storage_usage() RPC for the meter. tiers.ts holds the advisory client
  mirror + usage math (unit-tested).
- Profile tab (new PanelSlot.Profile, far right): cloud-sync contributes a
  StoragePanel with per-tier usage meters + an account scratch pad.

https://claude.ai/code/session_01K4mWVsXnwhtEi92FVBVhB3
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 25, 2026

Coverage Summary

Lines: 14.72% (1538/10448) · Statements: 14.02% · Functions: 10.65% · Branches: 10.82%

Per-file coverage
File Lines Functions Branches
src/App.tsx 0% 0% 0%
src/components/AboutDialog.tsx 0% 0% 100%
src/components/admin/BannedIpsTab.tsx 0% 0% 0%
src/components/admin/CoursesTab.tsx 0% 0% 0%
src/components/admin/MessagesTab.tsx 0% 0% 0%
src/components/admin/SubmissionsTab.tsx 0% 0% 0%
src/components/admin/ToolsTab.tsx 0% 0% 0%
src/components/admin/TracksTab.tsx 0% 0% 0%
src/components/BrowserCompatDialog.tsx 0% 0% 0%
src/components/ContactDialog.tsx 0% 0% 0%
src/components/CreditsDialog.tsx 0% 0% 100%
src/components/DataloggerDownload.tsx 0% 0% 0%
src/components/drawer/DeviceSettingsTab.tsx 0% 0% 0%
src/components/drawer/DeviceTracksTab.tsx 0% 0% 0%
src/components/drawer/FilesTab.tsx 0% 0% 0%
src/components/drawer/KartsTab.tsx 0% 0% 0%
src/components/drawer/NotesTab.tsx 0% 0% 0%
src/components/drawer/SetupsTab.tsx 0% 0% 0%
src/components/drawer/TemplateCreator.tsx 0% 0% 0%
src/components/drawer/VehiclesTab.tsx 0% 0% 0%
src/components/ExternalRefBar.tsx 0% 0% 0%
src/components/FileImport.tsx 0% 0% 0%
src/components/FileManagerDrawer.tsx 0% 0% 0%
src/components/graphview/GraphPanel.tsx 0% 0% 0%
src/components/graphview/GraphViewPanel.tsx 0% 0% 0%
src/components/graphview/InfoBox.tsx 0% 0% 0%
src/components/graphview/MiniMap.tsx 0% 0% 0%
src/components/graphview/SingleSeriesChart.tsx 0% 0% 0%
src/components/InstallPrompt.tsx 0% 0% 0%
src/components/LandingPage.tsx 0% 0% 0%
src/components/LapSummaryWidget.tsx 0% 0% 0%
src/components/LapTable.tsx 0% 0% 0%
src/components/LocalWeatherDialog.tsx 0% 0% 0%
src/components/NavLink.tsx 0% 0% 0%
src/components/RaceLineView.tsx 0% 0% 0%
src/components/RangeSlider.tsx 0% 0% 0%
src/components/ResizableSplit.tsx 0% 0% 0%
src/components/SettingsModal.tsx 0% 0% 0%
src/components/SubmitTrackDialog.tsx 0% 0% 0%
src/components/SupportedFilesDialog.tsx 0% 0% 0%
src/components/tabs/CoachTab.tsx 0% 0% 100%
src/components/tabs/GraphViewTab.tsx 0% 0% 100%
src/components/tabs/LabsTab.tsx 0% 0% 100%
src/components/tabs/LapTimesTab.tsx 0% 0% 100%
src/components/tabs/ProfileTab.tsx 0% 0% 100%
src/components/tabs/RaceLineTab.tsx 0% 0% 0%
src/components/TelemetryChart.tsx 0% 0% 0%
src/components/track-editor/AddCourseDialog.tsx 0% 0% 0%
src/components/track-editor/AddTrackDialog.tsx 0% 0% 0%
src/components/track-editor/CourseForm.tsx 0% 0% 0%
src/components/track-editor/EditorModeToggle.tsx 0% 0% 0%
src/components/track-editor/VisualEditor.tsx 0% 0% 0%
src/components/TrackEditor.tsx 0% 0% 0%
src/components/TrackPromptDialog.tsx 0% 0% 0%
src/components/video-overlays/AnalogOverlay.tsx 0% 0% 0%
src/components/video-overlays/BarOverlay.tsx 0% 0% 0%
src/components/video-overlays/BubbleOverlay.tsx 0% 0% 0%
src/components/video-overlays/dataSourceResolver.ts 0% 0% 0%
src/components/video-overlays/DigitalOverlay.tsx 0% 0% 0%
src/components/video-overlays/GraphOverlay.tsx 0% 0% 0%
src/components/video-overlays/LapTimeOverlay.tsx 0% 0% 0%
src/components/video-overlays/MapOverlay.tsx 0% 0% 0%
src/components/video-overlays/OverlaySettingsPanel.tsx 0% 0% 0%
src/components/video-overlays/overlayUtils.ts 0% 0% 0%
src/components/video-overlays/PaceOverlay.tsx 0% 0% 0%
src/components/video-overlays/registry.ts 0% 0% 100%
src/components/video-overlays/SectorOverlay.tsx 0% 0% 0%
src/components/video-overlays/sectorUtils.ts 0% 0% 0%
src/components/video-overlays/themes.ts 0% 0% 0%
src/components/video-overlays/types.ts 0% 100% 100%
src/components/video-overlays/VideoExportDialog.tsx 0% 0% 0%
src/components/VideoPlayer.tsx 0% 0% 0%
src/components/WeatherPanel.tsx 0% 0% 0%
src/contexts/AuthContext.tsx 0% 0% 0%
src/contexts/DeviceContext.tsx 0% 0% 0%
src/contexts/SessionContext.tsx 0% 0% 0%
src/contexts/SettingsContext.tsx 0% 0% 0%
src/hooks/use-mobile.tsx 0% 0% 100%
src/hooks/use-toast.ts 0% 0% 0%
src/hooks/useAuth.ts 100% 100% 100%
src/hooks/useDataLoader.ts 0% 0% 0%
src/hooks/useDocumentHead.ts 0% 0% 0%
src/hooks/useFileManager.ts 0% 0% 0%
src/hooks/useKartManager.ts 100% 100% 100%
src/hooks/useLapManagement.ts 0% 0% 0%
src/hooks/useNoteManager.ts 0% 0% 0%
src/hooks/useOnlineStatus.ts 0% 0% 0%
src/hooks/usePlayback.ts 0% 0% 0%
src/hooks/useReferenceLap.ts 0% 0% 0%
src/hooks/useSessionData.ts 0% 0% 0%
src/hooks/useSessionMetadata.ts 0% 0% 0%
src/hooks/useSettings.ts 0% 0% 0%
src/hooks/useSetupManager.ts 0% 0% 100%
src/hooks/useTemplateManager.ts 0% 0% 0%
src/hooks/useTrackEditorForm.ts 0% 0% 0%
src/hooks/useVehicleManager.ts 0% 0% 100%
src/hooks/useVideoSync.ts 0% 0% 0%
src/integrations/lovable/index.ts 0% 0% 0%
src/lib/aimParser.ts 11.27% 18.18% 3.08%
src/lib/alfanoParser.ts 7.81% 40% 3.93%
src/lib/ble/test/mockBle.ts 100% 100% 50%
src/lib/ble/battery.ts 93.33% 100% 87.5%
src/lib/ble/connection.ts 0% 0% 0%
src/lib/ble/fileTransfer.ts 90.69% 95% 72.91%
src/lib/ble/format.ts 50% 100% 66.66%
src/lib/ble/index.ts 100% 100% 100%
src/lib/ble/internal.ts 100% 100% 50%
src/lib/ble/settings.ts 93.6% 100% 85.29%
src/lib/ble/trackSync.ts 89.69% 90.9% 70.96%
src/lib/ble/types.ts 100% 100% 100%
src/lib/bleDatalogger.ts 100% 100% 100%
src/lib/brakingZones.ts 0% 0% 0%
src/lib/browserCompat.ts 0% 0% 0%
src/lib/channels.ts 97.05% 90.9% 84.61%
src/lib/chartColors.ts 0% 0% 0%
src/lib/chartUtils.ts 0% 0% 0%
src/lib/courseDetection.ts 99.01% 100% 84.14%
src/lib/datalogParser.ts 18.51% 50% 17.39%
src/lib/db/index.ts 0% 0% 0%
src/lib/db/supabaseAdapter.ts 0% 0% 0%
src/lib/db/types.ts 100% 100% 100%
src/lib/dbUtils.ts 3.33% 0% 0%
src/lib/deviceSettingsSchema.ts 0% 0% 0%
src/lib/deviceTrackSync.ts 100% 100% 90%
src/lib/doveParser.ts 82.4% 72.72% 56.36%
src/lib/dovexParser.ts 76.56% 76.92% 47.27%
src/lib/fieldResolver.ts 0% 0% 0%
src/lib/fileStorage.ts 0% 0% 0%
src/lib/garageEvents.ts 0% 0% 100%
src/lib/gforceCalculation.ts 92.85% 100% 86.95%
src/lib/graphPrefsStorage.ts 0% 0% 0%
src/lib/kartStorage.ts 0% 0% 0%
src/lib/lapCalculation.ts 96.12% 100% 90.32%
src/lib/lapDelta.ts 98.96% 100% 82.35%
src/lib/motecParser.ts 4.29% 3.44% 0.69%
src/lib/nmeaParser.ts 84.43% 92.85% 67.62%
src/lib/noteStorage.ts 0% 0% 100%
src/lib/overlayCanvasRenderer.ts 0% 0% 0%
src/lib/parserUtils.ts 100% 100% 98.52%
src/lib/referenceUtils.ts 78.33% 72.72% 46.42%
src/lib/setupStorage.ts 0% 0% 0%
src/lib/speedBounds.ts 0% 0% 0%
src/lib/speedEvents.ts 0% 0% 0%
src/lib/templateStorage.ts 0% 0% 0%
src/lib/trackStorage.ts 0% 0% 0%
src/lib/trackUtils.ts 22.22% 12.5% 22.72%
src/lib/ubxParser.ts 5% 0% 0%
src/lib/utils.ts 0% 0% 100%
src/lib/vboParser.ts 2.4% 9.09% 2.43%
src/lib/vehicleStorage.ts 0% 0% 0%
src/lib/videoExport.ts 0% 0% 0%
src/lib/videoFileStorage.ts 0% 0% 0%
src/lib/videoStorage.ts 0% 0% 0%
src/lib/weatherService.ts 0% 0% 0%
src/pages/Admin.tsx 0% 0% 0%
src/pages/AuthCallback.tsx 0% 0% 0%
src/pages/ForgotPassword.tsx 0% 0% 0%
src/pages/Index.tsx 0% 0% 0%
src/pages/Login.tsx 0% 0% 0%
src/pages/NotFound.tsx 0% 0% 0%
src/pages/Privacy.tsx 0% 0% 0%
src/pages/Register.tsx 0% 0% 0%
src/pages/ResetPassword.tsx 0% 0% 0%
src/plugins/cloud-sync/autoSync.ts 0% 0% 0%
src/plugins/cloud-sync/cloudClient.ts 0% 0% 0%
src/plugins/cloud-sync/CloudFilesSection.tsx 0% 0% 0%
src/plugins/cloud-sync/CloudSyncPanel.tsx 0% 0% 0%
src/plugins/cloud-sync/fileSync.ts 53.84% 27.27% 100%
src/plugins/cloud-sync/FileSyncToggle.tsx 0% 0% 0%
src/plugins/cloud-sync/index.ts 0% 0% 0%
src/plugins/cloud-sync/StoragePanel.tsx 0% 0% 0%
src/plugins/cloud-sync/storageTypes.ts 100% 100% 100%
src/plugins/cloud-sync/syncEngine.ts 0% 0% 0%
src/plugins/cloud-sync/syncStores.ts 100% 100% 100%
src/plugins/index.ts 0% 0% 0%
src/plugins/mounts.ts 100% 100% 100%
src/plugins/panels.ts 100% 100% 100%
src/plugins/PluginMount.tsx 0% 0% 0%
src/plugins/PluginPanelHost.tsx 0% 0% 0%
src/plugins/registry.ts 100% 100% 100%
src/plugins/storage.ts 32.25% 8.33% 33.33%
src/plugins/types.ts 100% 100% 100%
src/types/racing.ts 100% 100% 100%

Reserve "tier" for future subscription tiers. The storage-class concept
(documents vs logs) is now "storage type" throughout: storageTypes.ts
(StorageType/storageTypeForStore/StorageTypeUsage), the quota_limits.storage_type
column + sync_storage_type() helper, and the sync_storage_usage() RPC column.
No behavioural change. Migration is unmerged, so the column rename is safe.

https://claude.ai/code/session_01K4mWVsXnwhtEi92FVBVhB3
@TheAngryRaven TheAngryRaven changed the title Document storage tier: auto-sync, propagation deletes, quotas + Profile tab Document storage + auto-sync, propagation deletes, quotas + Profile tab May 25, 2026
@TheAngryRaven TheAngryRaven merged commit f0eb196 into BETA May 25, 2026
5 checks passed
@TheAngryRaven TheAngryRaven deleted the claude/cloud-document-storage branch May 25, 2026 03:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants