MyGarage
Self-hosted vehicle maintenance tracking application
What is MyGarage?
MyGarage is a self-hosted vehicle maintenance tracker. Add your cars, log services, track fuel economy, and get automated notifications when maintenance is due. It decodes VINs automatically, checks for recalls, and keeps all your receipts and documents in one place.
The app supports multiple users with OIDC/SSO integration (works with Rauthy, Keycloak, etc.) so your whole household can manage their vehicles. Garage analytics show spending trends across all vehicles, while individual dashboards break down costs per car.
It goes beyond cars: hour-metered gear like ATVs, side-by-sides and generators gets the same treatment as distance-tracked vehicles, a light supplies inventory folds consumable costs into service visits, and LiveLink pulls real-time OBD2 telemetry and GPS trips from a WiCAN device or the Torque Pro app. The interface is fully translatable and ships in seven languages.
Everything runs on your own hardwareβno subscriptions, no cloud accounts, your data stays yours. Take a photo of a receipt and OCR extracts the details. Export reports as PDF or CSV when you need them.
Technology Stack
Backend
- β Python 3.14+ with FastAPI 0.141.1 and Granian 2.8.1 ASGI server
- β SQLAlchemy 2.0.51 + SQLite (WAL mode) via aiosqlite 0.22.1 or PostgreSQL via asyncpg 0.31.0
- β pandas 3.0.5 for CSV import/export and analytics aggregation
- β Pydantic 2.13.4 for data validation and settings management
- β JWT auth with Argon2id hashing, OIDC/SSO with PKCE S256, and joserfc 1.7.4 for token/JWKS verification
- β NHTSA API for VIN decoding and recall checks (httpx 0.28.1)
- β Tesseract OCR for receipt scanning (PyMuPDF 1.28.0)
- β ReportLab 5.0.0 for PDF generation and matplotlib 3.11.1 for analytics charts
- β Multi-service notifications via aiosmtplib 5.1.2 for email delivery
- β APScheduler 3.11.3 for background jobs (maintenance alerts, recall checks, milestones)
- β LiveLink telemetry via aiomqtt 2.5.1 for WiCAN OBD2 devices, plus HTTP ingest from the Torque Pro Android app
- β Rich 15.0+ for pretty container log formatting (opt-in via MYGARAGE_LOG_PRETTY)
Frontend
- β React 19.2.7 + TypeScript 6.0.3 with Bun runtime and Vite 8.1.5 bundler
- β Tailwind CSS 4.3.3 with a shared design system, six selectable accent colours, and light/dark mode
- β React Router 7.18.1 for client-side navigation
- β TanStack Query 5.101.2 for server state management with automatic cache invalidation
- β Recharts 3.9.2 for interactive analytics charts
- β Schedule-X 4.6.1 for calendar UI β native TypeScript with React 19 support
- β date-fns 4.4.0 for date manipulation and formatting
- β Zod 4.4.3 + React Hook Form 7.82.0 for form validation
- β Lucide React 1.25.0 iconography and Sonner 2.0.7 for toasts
- β react-i18next 17.0.10 + i18next 26.3.6 for multi-language support across seven languages
- β OpenAPI-generated TypeScript types via openapi-typescript, eliminating manual type drift
Project Statistics
Version History
Track the evolution of MyGarage through its version releases. Each release includes new features, improvements, and bug fixes.
v3.1.0 2026-08-24
β¨ Added
- β’ Tires: per-position tread / DOT / pressure tracking with wear projection and automatic low-tread reminders (migration 085).
- β’ EV / PHEV charge sessions: SOC start/end, charge level (L1/L2/DCFC), home/public location, and battery SOH on fuel records (migration 086).
- β’ Importers: Fuelio, Drivvo, and Tesla/ABRP charge CSV adapters (
/api/import/vehicles/{vin}/fuel/{fuelio,drivvo,tesla,external}). - β’ Inbound webhooks:
POST /api/v1/webhooks/fuel|odometer|reminders/complete|telegramauthenticated withwebhook_ingest_token(migration 087). Structured Telegram fuel commands (no OCR):fuel <vin|nickname> <odo>[km|mi] <vol>[L|gal|kWh] [price] [cost]. - β’ Vehicle types: Boat, UTV, Snowmobile, Bicycle, and E-Bike (Bicycle hides the fuel log; others remain motorized / hours-friendly where relevant).
- β’ Tow pairing UI for trailer details (
tow_vehicle_vin) plus linked-trailers list on tow vehicles. - β’ Built-in OEM/DIY reminder packs with apply-from-UI and
/api/reminder-packsAPIs. - β’ Matrix notification channel (homeserver + access token + room).
- β’ Quick Entry / PWA deep links:
/quick-entry?action=add-fuel|add-service|odometer. - β’ Opt-in LLM fuel receipt parse (Ollama/OpenAI-compatible; draft only). See docs/tier2-features.md.
- β’ Dashboard **Family & Friends** garage lane: shared household vehicles always get their own section; lightweight reference vehicles (
external_vehiclesAPI) are opt-in via Settings β System (family_friends_enabled, default off). Optional VIN with NHTSA decode on add/edit autofills year/make/model. Contact name and phone are supported for reference vehicles. (migration 088). - β’ Settings β Integrations: inbound webhook token, Telegram fuel-bot enable, and LLM receipt-parse configuration.
- β’ Fuel form: opt-in LLM receipt draft (text or image/PDF) with accept-into-form flow.
- β’ Fuel list: Fuelio / Drivvo / Tesla-ABRP / auto-detect CSV import formats alongside MyGarage CSV.
- β’ Vehicle analytics: configurable Spending Anomalies time range (3m/6m/12m/YTD/all/custom) β #130.
- β’ Reminder packs: type-filtered catalog plus ATV/UTV and snowmobile packs; boats only see winterization.
- β’ Quick Entry: engine-hours action + PWA shortcut (
?action=hours); hours-default vehicle types on create/edit. - β’ Global search (vehicles + reminders) and in-app notification inbox (overdue/upcoming reminders).
- β’ Supply barcode/QR scan on supply create/edit forms (migration 090).
- β’ LiveLink session insights: idle time, harsh accel/brake counts from telemetry (migration 090).
- β’ Sanitized for-sale history PDF (
/api/vehicles/{vin}/reports/sale-history-pdf) plus export-before-delete in Archived Vehicles. - β’ DTC Phase 2 enrichment: common causes, symptoms, and fix guidance on LiveLink DTCs (migration 091).
- β’ UK Imperial gallon standard (US 3.785 L vs UK 4.546 L) in Settings β System; volume and MPG conversions follow it.
- β’ Bulk archive from the dashboard (multi-select) and scheduled auto-archive after N days of inactivity.
- β’ HEIC/HEIF photo uploads convert to JPEG when pillow-heif is available; PostgreSQL full restore is blocked in the UI with restore instructions.
π Changed
- β’ Tire add, edit and reading forms open in a side drawer, matching the rest of the app.
- β’ Tire position is chosen with toggle buttons rather than a dropdown, and positions already tracked stay visible.
- β’ Deleting a tire moved from the tire card to the edit drawer.
- β’ Tire positions display as full names (Front Left, Spare) and are translatable.
- β’ Vehicle type lists are ordered alphabetically by the label shown, in every language.
- β’ Notification service tabs are ordered alphabetically.
- β’ Trailer tow pairing is a summary card that opens a sidecar on click, matching the other Overview cards, instead of an inline form.
- β’ Fuel records carry one canonical fuel type; the free-text field is gone and the value fills in from the vehicle (migration 089).
- β’ CSV export schema v5 drops the duplicate "Fuel Type" column. Imports still read it from older files.
π Fixed
- β’ Reminder pack loader rejects path-traversal
pack_idvalues. - β’ LLM receipt parse is rate-limited and rejects oversized uploads/text.
- β’ Matrix HTML payloads escape title/body and only link http(s) URLs.
- β’ Inbound webhooks no longer return 403 on instances with authentication enabled.
- β’ Webhook ingest token is accepted only via the
X-Webhook-Tokenheader, never in the query string, and is rate limited. - β’ Webhook and CSV-import fill-ups now sync the odometer log and invalidate cached dashboards.
- β’ Reminders completed via webhook or a tire threshold use the
donestatus, so they stay visible and can be reopened. - β’ Telegram inbound bot: replies now reach the user, and a bad command no longer triggers a redelivery loop. (Inbound commands are Telegram-only; the seven outbound notification backends are unaffected.)
- β’ CSV import takes an explicit odometer unit and decimal separator instead of guessing, so imperial and European exports no longer import corrupted values.
- β’ CSV import preserves the charge/fill timestamp, so two same-day sessions are no longer collapsed into one, and re-importing a file you corrected in the source app no longer creates duplicates.
- β’ Saving a tire no longer erases its brand, model, size and DOT code; the tire tab now has separate Add and Edit actions.
- β’ Backdated tire readings no longer overwrite the current tread depth.
- β’ Tire tab now respects the imperial and metric unit preference, and its inputs are properly labelled.
- β’ EV charge level and location can be cleared, and their validation errors now render.
- β’ Adding a fuel record no longer fails when two odometer readings share a date.
- β’ A tire delete that fails now reports the error instead of failing silently.
- β’ Propane tank refills save again; a propane-only record no longer requires an odometer reading.
- β’ Opening a fuel, DEF or propane record no longer rewrites its stored cost, or its volume from the tank size.
- β’ Editing a propane record no longer duplicates the "Vendor:" line in its notes.
- β’ Backups include the fuel type again.
- β’ Unparseable text in a fuel volume field no longer breaks the cost calculation.
- β’ Transferring a vehicle that has no current owner now assigns ownership instead of failing; the history shows the prior owner as Unassigned (migration 092); thanks @sickkick (#150).
v3.0.1 2026-08-15
π Fixed
- β’ Forms now report validation errors on the field that caused them, instead of a bare
Request failed with status code 422(#140). - β’ Numeric fields accept either decimal separator β
528,25and528.25both work. (The window sticker's MSRP fields are not yet covered.) - β’ Optional numeric fields left blank now save as empty instead of being rejected (#140).
- β’ Invalid text typed into a numeric field β like
abc, or a comma decimal on a mismatched keyboard β is now reported instead of being silently discarded on save. - β’ Registering with a weak password now shows the actual reason on the password field, instead of
[object Object],[object Object]. - β’ Error messages across the app no longer show raw text like
Request failed with status code 500; 404s keep the backend's specific reason (e.g. "Recipient user not found") instead of a generic message. - β’ Service history PDF: repaired text overlap, clipping and column alignment, and the Description column now shows the actual service detail instead of "N/A" on every row; thanks @SCDT95 (#145).
- β’ Building the Docker image locally no longer takes ~18 minutes on many-core machines; the frontend build stage moved off Alpine to a glibc base.
v3.0.0 2026-08-14
β¨ Added
- β’ Parts & supplies: light inventory for consumables β track purchases, usage, on-hand quantity, and average unit cost per supply; consume supplies inside a service visit (cost folds into the visit total); a per-vehicle "Supplies used" tab and a full history view. Household-shared catalog (#122).
- β’ Torque Pro: ingest OBD2 telemetry and GPS from the Torque Pro Android app via a per-device upload URL, managed alongside WiCAN devices (#117).
- β’ Trips & location: GPS-breadcrumb trips from Torque with a route map, a last-known-location card, and a per-vehicle location-tracking opt-out (#118).
- β’ CSV exports of fuel and odometer records now follow your unit preference β an imperial account gets miles, gallons and price per gallon instead of metric (#128). The file records which units it uses, so re-importing it converts back correctly.
- β’ The interface is now fully translatable β roughly 1,600 strings extracted across analytics, vehicle detail, wizards, modals, settings, notification setup, record forms, validation messages, dropdown options and server-supplied status values.
- β’ German: complete translation of all six namespaces; thanks @SCDT95 (#125, #126).
- β’ Accent colour: pick from six UI accent colours in Quick Settings, saved to your account.
- β’ ATV is now a selectable vehicle type.
- β’ Engine-hours usage tracking for hour-metered vehicles (ATVs, side-by-sides, equipment) with full parity to distance β an hours history, fuel efficiency as fuel-per-hour and cost-per-hour, hours-based service records and reminders, analytics, the homepage widget, PDF reports and CSV import/export. A vehicle can now track distance and engine hours together (dual tracking).
- β’ French (fr) translation β thanks @roondar (#132).
- β’ Vehicle detail: standard and optional equipment are now editable in a slide-in sidecar (opened from the Equipment buttons next to Edit) β add and remove items, saved to the vehicle β replacing the read-only dropdown cards.
- β’ Vehicle detail: Purchase, Sale and MSRP are combined into one editable Pricing card β edit purchase/sale dates and prices and the MSRP figures from a slide-in sidecar.
π Changed
- β’ Translations: filled 74 keys that were missing from every language (engine-hours tracking, the hours analytics charts, and the vehicle card/sidecar editors) plus the 36 that left the dashboard in English β French is now complete.
- β’ Polish, Russian and Ukrainian: fixed 23 screen titles that read "Title" in the local language (the garage heading showed "TytuΕ"/"ΠΠ°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ" instead of "My Garage").
- β’ Refreshed the interface onto a consistent design system β shared cards, forms, buttons, chips and tables with a single accent-driven theme.
- β’ The vehicle "Maintenance" stat tile is now labelled "Reminders".
- β’ The Fuel tab shows its sub-tab bar only when there's more than one sub-tab (e.g. DEF), instead of a lone "Fuel" entry that just repeated the tab.
- β’ Redesigned the Address Book: category filter chips (with Gas Station and RV Park) replace the dropdown and the separate Gas Stations button, contact cards open an edit sidecar, and Add/Edit are slide-in panels.
- β’ Redesigned the garage Analytics page: Cost by Category is a donut with a per-category breakdown, Running Costs by Vehicle reads cleaner without a scrollbar, and the spending-trend average lines now span the full range.
- β’ Toggle switches now read as the accent colour when on and red when off, everywhere they appear; the Find POI "Use My Location" button is sized to its label.
- β’ Settings β System Configuration: the Debug, "show both units" and mobile quick-entry checkboxes are now toggle switches.
- β’ Settings β File Management: the Window Sticker enable and OCR checkboxes are now toggle switches, and the two cards use a masonry layout so the Window Sticker card sizes to its content instead of stretching.
- β’ Settings β Integrations: enable checkboxes are now toggle switches, the NHTSA card takes a shield icon, provider Edit links use the accent colour, the CarComplaints and LiveLink "About" details moved into a help sidecar (opened by a question-mark button in each card's corner), and the layout stacks CarComplaints and LiveLink beside NHTSA with Shop Finder full-width.
- β’ LiveLink settings now open in a slide-in sidecar instead of a centred modal, with every checkbox replaced by a toggle switch.
- β’ Settings β Notifications: each provider's enable control and every per-event notification are now toggle switches, and the event groups are always expanded instead of hidden behind an accordion.
- β’ Editing a vehicle now opens a slide-in sidecar from the vehicle page instead of a separate full-page form, and saving returns you straight to the vehicle without a page reload.
- β’ The vehicle "Edit" panel is now "Vehicle Settings" and holds only the settings the info cards don't cover β nickname, type, usage tracking, fuel type, DEF, the window sticker and Torque Pro sources; everything else is edited by clicking the card that shows it.
- β’ The window sticker moved off the vehicle Overview into Vehicle Settings, and uploading one now opens a slide-in sidecar instead of a centred modal.
- β’ Torque Pro sources moved off the vehicle Overview into Vehicle Settings, replacing the "Connected Devices" card.
- β’ Vehicle Details, Powertrain and Warranty cards now appear even when empty, so their fields can be filled in on a vehicle whose VIN didn't decode.
- β’ Migration runner: squash (
REPLACES) support plus a create_allβmigrations schema-parity diagnostic; reconciled long-standing schema drift (address_book NOT NULL tightening, dropped dead imperial columns, deduplicated indexes).
ποΈ Removed
- β’ Maintenance templates: the retired feature and its endpoints are gone β application had been a no-op 410 since the schedule system was removed in 2.31.0.
- β’ Dropped 14 unreachable components, schemas and types (~1790 lines) left behind by earlier refactors, along with the translation keys only they used.
- β’ Settings β System Configuration: removed the light/dark theme selector β the theme toggle in the top bar is now the single control.
π Fixed
- β’ Vehicle Analytics: fuel efficiency alerts are now translated and follow your unit preference. They were English prose with L/100km baked in, shown even on imperial accounts.
- β’ Re-importing a CSV you exported no longer multiplies distances by 1.609 and volumes by 3.785. Every export since the extended fuel columns landed was being read back as if it were imperial (#128).
- β’ Imported fuel records showed the wrong price per gallon on imperial accounts β $2.50/gal displayed as $0.66. Imports now record what the price is measured against, and a migration repairs existing rows (#128).
- β’ Torque Pro and WiCAN setup URLs are now absolute, so they can be pasted into the device. They were emitted as bare paths like
/api/v1/torque/<token>/upload(#129). - β’ LiveLink: revoking a Torque source or deleting a WiCAN device no longer fails with a server error once it has reported any data. Its telemetry, sessions and DTCs are kept.
- β’ Vehicle Analytics: spending anomalies now use your configured currency and language. The sentence was composed server-side with a hardcoded "$" and English text, so it ignored both (#131).
- β’ Fuel summary on hours-tracked vehicles no longer shows a distance-based cost-per-mile stat, and the imperial fuel rate now reads GPH.
- β’ OIDC: login failed with "Failed to verify ID token" against providers whose issuer ends in a slash (Rauthy), because the saved issuer URL has it stripped. The
issclaim is now checked against the discovery document. - β’ Theme: the
primarycolour token was never defined, so roughly 560 uses ofbg-primary/text-primary/border-primaryrendered uncoloured. - β’ Dates, numbers and units now follow the selected language rather than the browser's locale.
- β’ Calendar: the month grid and weekday headers rendered in English for every language.
- β’ Nearby places and shop finder: distances and the search radius ignored the unit preference.
- β’ Analytics, vehicle wizard, window sticker and service line items: a hardcoded
$is now formatted in the selected currency. - β’ Vehicle edit: switching language while editing silently discarded everything typed.
- β’ Family management: the multi-user toggle showed the literal text
{t('modal.multiUserMode')} enabledin its confirmation toast. - β’ Fuel defaults: saving a default payment method or trip type reported "Unit preference saved!".
- β’ Pushover setup: the API token field lost the "/ App Token" half of its label.
- β’ Notification and file settings: several field labels rendered in English even though their translations already existed.
- β’ Several settings and fuel labels shipped different text than the code appeared to specify, from stale inline fallbacks.
- β’ Toll transactions: the vendor-name lookup shadowed the translation function.
- β’ Settings: switching auth mode away from "none" silently failed to save.
- β’ Settings: changing an unrelated setting (timezone, debug) no longer rewrites the OIDC configuration.
- β’ Reminders: the notification scheduler could crash on a later run once any reminder had been notified, from a naive vs timezone-aware timestamp comparison.
- β’ Deleting a service visit no longer leaves its auto-synced odometer reading behind as an orphaned record.
- β’ Pop-up notifications raised while a slide-in panel is open are now announced by screen readers and can be dismissed.
- β’ Migration 079 left a stray comma that could break SQLite startup with
near ",": syntax errorand stall the migrations behind it (caught in 3.0.0-rc2); thanks @SCDT95 (#137). - β’ PostgreSQL: migrations 054/055 no longer create duplicate foreign keys against a
create_allbaseline (supported-dialect correctness fix; SQLite unaffected).
π Security
- β’ Settings: sensitive values (OIDC client secret, notification tokens, SMTP password, POI API keys) are masked as
********in API responses; saving a masked value keeps the stored secret. - β’ Bump
cryptography48.0.1 β 50.0.0 (GHSA Bleichenbacher oracle in PKCS#7EnvelopedData; unused here, alert cleanup). - β’ Analytics: sanitize
vinand exception text in the PDF-export error logs (CodeQLpy/log-injection). - β’ Frontend build: drop the
existsSynccheck-then-read in the service-worker font injector (CodeQLjs/file-system-race).
π Build
- β’ Bump backend deps: fastapi 0.138.2 β 0.141.1, granian 2.7.8 β 2.8.1, ruff 0.15.20 β 0.16.1, joserfc, matplotlib, pandas, pandas-stubs, pillow-heif.
v2.31.0 2026-07-15
β¨ Added
- β’ Fuel: optional **Rebate** field (points, discounts, cash back) next to Total Cost; Total Cost then stores the net paid (price Γ volume β rebate), so all cost surfaces and CSV/JSON export/import reflect it (migration 067).
- β’ Settings: Brazilian timezones in the System timezone dropdown β 12 IANA zones covering Brazil's UTC-2/-3/-4/-5 offsets; thanks @sigrist (#112).
- β’ OIDC:
MYGARAGE_TRUSTED_HOSTSallow-lists a self-hosted issuer that resolves to a private/LAN IP (split-horizon DNS), relaxing the SSRF private-IP block for those hosts only. - β’ Address Book: mark/unmark a contact as a gas station in the editor (checkbox); previously only fuel-record quick-add could set it.
- β’ Reverse proxy:
MYGARAGE_ROOT_PATHserves MyGarage under a URL subpath (e.g./mygarage) behind a prefix-stripping proxy β OIDC, PWA, media, and deep-links included; no image rebuild required (#107). - β’ Preferences: per-user 12-hour / 24-hour time format (Settings β System, default 12-hour), applied to every displayed time and the fuel fill-up time entry.
- β’ i18n: Brazilian Portuguese (pt-BR) translation β thanks @FabioCastilho.
- β’ Vehicles:
fuel_type/fuel_type_secondaryare normalized and validated against the canonical vocabulary on create/update; a migration backfills legacy mixed-case/alias values in place. - β’ DEF: diesel-only gates on record create/update, tank capacity, fuel-sync, and CSV import β non-diesel vehicles get a 400 instead of accepting spurious DEF data; full-backup restore stays exempt so a user's own archive always restores completely; the DEF tab renders read-only (history visible, add/edit hidden) for non-diesel vehicles.
- β’ LiveLink: a conservative param-class inference catalog classifies new telemetry parameters at registration and backfills existing rows via migration, activating range/rate-of-change validation for previously-unvalidated PIDs.
- β’ LiveLink: threshold-breach alerts now honor the existing alert-cooldown admin setting instead of firing on every breaching frame.
- β’ Notifications: DEF-low Discord alert β a daily check compares the latest DEF fill level against a configurable threshold (default 25%) with crossing-based dedup; threshold is configurable in Settings > Notifications.
π Changed
- β’ Fuel tracking (fuel, DEF, propane) is now its own primary vehicle tab instead of a Maintenance sub-tab;
?tab=fuel/def/propanedeep-links still work (#116). - β’ Maintenance templates:
POST /api/maintenance-templates/applyreturns 410 Gone β application had been a silent no-op since the schedule system was removed; use Reminders instead.
π Fixed
- β’ Translations: the PWA install prompt, Shop Finder, the notification setup forms, and the add-station dialog showed raw keys (
installPrompt.title) instead of text β 32 strings were missing from every language. - β’ Translations: Polish, Russian, and Ukrainian are complete again β the fuel Rebate field, fill-up date/time, and the gas-station address-book fields were English-only.
- β’ Fuel: editing a fill-up now shows the station it was saved with, and retyping one replaces it (#108).
- β’ Fuel: editing a "one-time visit" station no longer adds it to the address book (#108).
- β’ Export: the fuel CSV "Station" column now shows the station name for address-book stations (#108).
- β’ Quick Entry: no longer shows "No vehicles" on a cold app launch when the account has them β the vehicle list is fetched fresh (no longer service-worker cached) and retries instead of dead-ending (#114).
- β’ Fuel economy: partial fill-ups between two full tanks now count toward the next full tank's L/100km across every surface (record, average, garage card, widget, Analytics) instead of being ignored (#113).
- β’ Mobile layout: the Home and Fuel History action buttons no longer overflow the viewport β both toolbars stack/wrap on narrow screens instead of forcing a single fixed-width row (#115).
- β’ Analytics: the CSV and PDF export buttons are now a single "Export" dropdown, so the header no longer runs off narrow screens.
- β’ Settings: the section tabs now use the same responsive layout as the vehicle sub-tabs (icons-only on mobile) instead of a single row that overflowed.
- β’ PWA: serve
sw.js,manifest.json, and the SPA shell withCache-Control: no-cacheso CDN edge caches (e.g. Cloudflare) can't pin a stale service worker or index across deploys. - β’ Address Book: State/region field accepts non-US codes (e.g. VIC, NSW) up to 50 chars β editor and fuel quick-add now agree (#108).
- β’ Address Book: unify the gas-station tag on
gas_station(FATAL migration 065) so the Gas-Stations filter, autocomplete ranking, and vendor-sync exclusion all match; quick-add-created stations are now correctly excluded from vendor sync (#108). - β’ Fuel: the fill-up time field follows the 12h/24h preference (12-hour has an explicit AM/PM selector) and drops the redundant separate date, replacing the locale-dependent native widget (#109).
- β’ i18n: define 35
commonkeys referenced app-wide but missing from the English source, so form and table labels no longer render as raw keys (all five languages). - β’ Startup: ignore the
MYGARAGE_PORT=tcp://<ip>:<port>service-link variable Kubernetes injects for a Service namedmygarage; the app falls back to the default port instead of crashing on boot (#102). - β’ Vehicle delete: enable SQLite FK enforcement (
PRAGMA foreign_keys=ON) and use an ORM delete so child rows cascade on both engines β deleting a vehicle no longer orphans its history (re-adding the same VIN silently resurrected it). - β’ Vehicle delete: remove the vehicle's photo/document directories and attachment rows+files from disk.
- β’ Backup: full backups snapshot SQLite via the Online Backup API instead of copying the live WAL-mode files (torn-copy risk; db member now self-contained).
- β’ Restore: stale
-wal/-shmsidecars are removed so an old WAL can't replay over the restored database. - β’ Fuel economy: the vehicle-wide L/100km average no longer bridges across missed fill-ups (understated consumption); widget consumption gets the same guard.
- β’ DEF analytics: consumption rate excludes the final unconsumed purchase (rate was overstated ~N/(N-1)).
- β’ Users: admin user-delete pre-cleans share/transfer references so FK-enforcing engines accept it.
- β’ Analytics: the vehicle Analytics page's DEF consumption card uses the same audited calculation as the DEF tab (a duplicate formula asserted a rate from insufficient data).
- β’ Vehicles: clearing
fuel_typeon edit now actually clears the field instead of being silently dropped from the update payload. - β’ Vehicles: VIN-decode prefill uses the server-normalized fuel type instead of the raw NHTSA string.
ποΈ Removed
- β’ Migration 060: drop the dead
tsbsandvincario_*tables (tsbsstill referenced a table dropped in migration 040 β an FK-mismatch landmine under enforcement).
v2.30.0 2026-06-28
β¨ Added
- β’ Widget API: new metric-native
/api/v2/widget/*exposing both metric and imperial units per vehicle (superset of the legacy/api/widget/*). - β’ LiveLink: pull WiCAN SD-card telemetry into MyGarage to backfill offline gaps (auto on device reconnect + manual admin trigger).
π Fixed
- β’ Auth: stop redirecting
auth_mode=noneusers to the login page (#98). - β’ LiveLink: unify telemetry param_key casing across MQTT/HTTPS ingest (uppercase canonical), ending duplicate per-PID streams; migration merges existing rows.
- β’ SQLite: enable WAL mode + 30s busy_timeout to stop "database is locked" errors under concurrent MQTT/scheduler/request writes.
- β’ LiveLink: commit SD backfill inserts in batches (500) so a large pull doesn't hold the SQLite write lock for the entire operation.
- β’ LiveLink: drop WiCAN frame-metadata params (
TS,TIMESTAMP) at every ingest path β they aren't telemetry and flooded storage (existing rows purged).
π Security
- β’ Bump
pydantic-settings2.14.1 β 2.14.2 (GHSA symlink-traversal inNestedSecretsSettingsSource; unused here, alert cleanup). - β’ LiveLink: sanitize
device_idin SD-backfill task logs (CodeQLpy/log-injection).
v2.29.0 2026-06-20
π Fixed
- β’ LiveLink: track-aware WiCAN firmware checks (OBD vs PRO).
β¨ Added
- β’ Docs: LiveLink setup guide covering primary + failover webhook configuration.
π Security
- β’ Frontend: pin transitive deps via
overridesto clearbun audit(brace-expansion, postcss, undici, form-data, js-yaml, @babel/core).
π Build
- β’ Frontend: add
ESNext.Temporalto tsconfiglibfor the temporal-polyfill 1.0 global types.
π Dev Dependencies
- β’ **@playwright/test**: 1.60.0 β 1.61.0
- β’ **@tailwindcss/vite**: 4.3.0 β 4.3.1
- β’ **@types/react**: 19.2.15 β 19.2.17
- β’ **@typescript-eslint/eslint-plugin**: 8.59.4 β 8.61.1
- β’ **@typescript-eslint/parser**: 8.59.4 β 8.61.1
- β’ **@vitest/coverage-v8**: 4.1.7 β 4.1.9
- β’ **@vitest/ui**: 4.1.7 β 4.1.9
- β’ **eslint**: 10.4.0 β 10.5.0
- β’ **eslint-plugin-react-refresh**: 0.5.2 β 0.5.3
- β’ **pandas-stubs**: 3.0.0.260204 β 3.0.3.260530
- β’ **pyright**: 1.1.409 β 1.1.410
- β’ **pytest**: 9.0.3 β 9.1.1
- β’ **ruff**: 0.15.14 β 0.15.18
- β’ **tailwindcss**: 4.3.0 β 4.3.1
- β’ **typescript-eslint**: 8.59.4 β 8.61.1
- β’ **vite**: 8.0.14 β 8.0.16
- β’ **vitest**: 4.1.7 β 4.1.9
π App Dependencies
- β’ **@googlemaps/js-api-loader**: 2.0.2 β 2.1.1
- β’ **@tanstack/react-query**: 5.100.14 β 5.101.0
- β’ **@tanstack/react-query-devtools**: 5.100.14 β 5.101.0
- β’ **aiosmtplib**: 5.1.0 β 5.1.1
- β’ **axios**: 1.16.1 β 1.18.0
- β’ **date-fns**: 4.3.0 β 4.4.0
- β’ **fastapi**: 0.136.3 β 0.138.0
- β’ **cryptography**: 46.0.7 β 48.0.1
- β’ **starlette**: 1.0.1 β 1.3.1
- β’ **i18next**: 26.2.0 β 26.3.1
- β’ **joserfc**: 1.6.8 β 1.7.1
- β’ **lucide-react**: 1.16.0 β 1.21.0
- β’ **matplotlib**: 3.10.9 β 3.11.0
- β’ **pillow-heif**: 1.3.0 β 1.4.0
- β’ **python-multipart**: 0.0.29 β 0.0.32
- β’ **react**: 19.2.6 β 19.2.7
- β’ **react-dom**: 19.2.6 β 19.2.7
- β’ **react-hook-form**: 7.76.1 β 7.79.0
- β’ **react-is**: 19.2.6 β 19.2.7
- β’ **react-router-dom**: 7.15.1 β 7.18.0
- β’ **reportlab**: 4.5.1 β 5.0.0
- β’ **slowapi**: 0.1.9 β 0.1.10
- β’ **sqlalchemy**: 2.0.50 β 2.0.51
- β’ **temporal-polyfill**: 0.3.2 β 1.0.1
π HTTP Servers
- β’ **granian**: 2.7.4 β 2.7.6
v2.28.0 2026-05-30
π Security
- β’ Vehicle delete/transfer, identity-metadata edits, archive/unarchive/visibility, and window-sticker upload/edit/delete are now OWNER-only β a write-share can no longer perform them (D-2/D-3/D-8)
- β’ Child-record writes (trailer, photos, maintenance-template apply/delete, DTC annotate/clear, spot-rental billing) now require a write-share, not just any share (D-4)
- β’ Closed IDORs: transfer-history and spot-rental-billing gate vehicle access before querying
- β’ LiveLink global infra (settings, ingestion token, MQTT, parameter defs, firmware, global device list) is admin-only; per-device ops require ownership of the device's linked vehicle, and relink checks both the current and target VIN (D-5)
- β’
GET /api/dashboardandGET /api/vehicles/archived/list(plus archive/unarchive/visibility) switchoptional_authβrequire_auth, closing the local/oidc no-token fail-open - β’ CSV export fields are sanitized against spreadsheet formula injection (string-only; numeric cells preserved)
- β’ File uploads reject content/declared-type magic-byte mismatches (400) instead of logging and storing; photos are decoded before the disk write; attachment/document configs are strict; window-sticker uploads gain MIME + magic-byte checks
- β’ LiveLink ingest endpoint gains an in-app body-size cap (413) via pure-ASGI middleware
- β’ POI TomTom API key sent as a request header (not a query param) so it can't leak via error-path logs
- β’ MQTT subscriber sanitizes the decoded broker payload before debug logging
π Changed
- β’ Security tripwire rewritten from greps to a stdlib-
astchecker (backend/tools/authz_tripwire.py) inspecting call args, decorators, the service layer, and a one-level call graph - β’ Settings β Integrations: the LiveLink panel is shown only to admins (matches the admin-only infra endpoints)
π Fixed
- β’ Service worker: route document-destination requests (browser/Cloudflare speculative prefetch of SPA routes like
/vehicles/{vin}) through the navigation fallback, so a cancelled/transient prefetch no longer surfaces as an "Uncaught (in promise) Failed to fetch"
v2.27.2 2026-05-27
π Security
- β’ Bump FastAPI floor to >=0.136.3 to pull Starlette >=1.0.1 (CVE-2026-48710 BadHost header auth bypass)
π Fixed
- β’ Restore
console.errorin ErrorBoundary so render failures surface in dev tools - β’ Add backdrop click-to-close on FormModalWrapper to prevent invisible overlay from blocking all clicks
- β’ Add 5-second timeout on service worker navigation fetches to prevent indefinite hangs
v2.27.1 2026-05-25
β¨ Added
- β’
GET /api/auth/oidc/config/adminreturns the canonical admin OIDC config;client_secretis masked with the literal"********"placeholder when stored - β’
PUT /api/auth/oidc/config/adminwrites the OIDC config atomically; emptyclient_secretpreserves the stored value, trailing slashes onissuer_urlare stripped - β’
MYGARAGE_LOG_PRETTY=trueswitches container logs to the Rich-formatted compact[HH:MM:SS] LEVEL messagelayout used by TideWatch and VulnForge
π Changed
- β’
POST /api/auth/oidc/testnow returns the canonical{ok, error, detail, issuer, algorithms_supported}envelope per the homelab OIDC settings contract - β’ OIDC settings modal: Issuer URL helper text corrected (the app appends
/.well-known/openid-configurationitself), Callback URL display has a copy button, test result renders the canonical envelope - β’ Settings save now sends OIDC fields to the dedicated admin endpoint so the
client_secretcannot be inadvertently cleared via the bulk/settings/batchupsert - β’ Granian access-log healthcheck filter now matches on the exact request path and lets failures (HTTP >= 400) through, replacing the loose substring check that previously also swallowed
/health-statusand similar
π Security
- β’ OIDC login flow now uses PKCE S256 (RFC 7636);
code_verifierpersisted onoidc_statesand sent in the token exchange. - β’ ID token verifier explicitly allowlists
EdDSAandRS256algorithms.
v2.27.0 2026-05-24
β¨ Added
- β’ Extended fuel tracking (#69): optional fuel-up time, fueling station with autocomplete and one-time-visit toggle, driver, payment method, trip type, outside temperature, and trip-computer (OBC) values on every fill-up.
- β’ "Auto-fill from last drive" button on the fuel form pulls OBC values from the most recent matching LiveLink drive session.
- β’ Per-user default payment method and trip type under Settings β System.
- β’ "Gas Stations" filter on the Address Book page.
- β’ Vehicles now track a secondary fuel type (PHEV / flex / dual-fuel), populated from NHTSA. Multi-fuel vehicles get a per-fillup fuel-type dropdown.
- β’ Inline "+ Add to address book" action and an X clear button on the station autocomplete.
- β’ Pagination on the fuel records list (50 per page, prev/next).
- β’ Manual address search on the POI Finder, alongside "Use my location".
- β’ VIN duplicate check fires on field blur during vehicle add.
- β’ OBC trip duration field accepts
HH:MMandHH:MM:SSin addition to seconds. - β’ Fuel records CSV export now includes every v2.27.0 column. Schema bumped 3 β 4 (additive).
- β’ CSV fuel import reads the
Fuel Typecolumn with locale-aware normalization.
π Changed
- β’ Fuel record save now runs as a single transaction across station resolution, odometer sync, and DEF sync β no more partial writes on failure.
- β’ Gas-station address-book entries no longer create vendor records.
- β’ Fuel records now require both an odometer reading and a fuel amount.
missed_fillupis the explicit escape hatch for partial entries. - β’
/assets/*static files shipCache-Control: public, max-age=31536000, immutable(Vite hashes the filenames). - β’ Photo and thumbnail endpoints ship
Cache-Control: private, max-age=31536000, immutable. - β’
AuthContextdispatches/settings/publicand/auth/mein parallel viaPromise.allSettledinstead of sequentially. - β’ LiveLink status polling (5s detail / 30s widget) pauses while the tab is hidden.
- β’ README Bun badge now auto-updates from
.bun-versioninstead of hardcoding the version.
π Fixed
- β’ Migration 054 failed on PostgreSQL (#69):
DATETIMEandADD CONSTRAINT IF NOT EXISTSare not valid PG syntax. Now dialect-aware. - β’ Polish/Ukrainian/Russian fuel types (e.g.
Benzyna,ΠΠΈΠ·Π΅Π»Ρ,ΠΠ°Π·) silently mapped tootherinstead of the right canonical value. - β’ Vehicle add/edit form rendered free-text for fuel type instead of a dropdown.
- β’ Stations saved from POI search were not selectable in the fuel-record form (filter mismatch).
- β’ Deleting a fuel record left an orphan synced entry on the mileage timeline. Migration 055 adds a proper FK with cascade and cleans up existing orphans.
- β’ "Per volume" / "per weight" labels, the outside-temperature label (Β°C / Β°F), and the POI search radius now respect the user's unit preference. Imperial users type Fahrenheit; canonical Celsius storage is unchanged.
- β’ Service worker no longer pins to a hardcoded cache name β caches are namespaced by
APP_VERSIONso stale shells are evicted on activate. - β’ Service worker asset fetches retry 3Γ with exponential backoff before surfacing the error, covering the backend cold-start window.
- β’ Service worker no longer precaches
/or/index.html(stale references to old chunk hashes after deploys). - β’ Service worker no longer caches photo/attachment/document/backup/realtime responses β
response.clone()was stalling user fetches behind the CacheStorage write. - β’ Custom middleware (
SecurityHeaders,RequestID,CSRFProtection) rewritten as pure ASGI so streaming responses no longer buffer throughBaseHTTPMiddleware's asyncio queue. - β’ Removed
SlowAPIMiddleware; per-route@limiter.limit(...)decorators still enforce limits, and the globaldefault_limitsfloor is already provided by Traefik'scommon-rateschain. - β’ PG integration tests now run in CI under the docker-compose.test.yml sidecar (#77 β wired via
pg-migrations-pytest-pathcovering bothtests/migrations/andtests/integration/).
π Dockerfile Dependencies
- β’ **oven/bun**: 1.3.12-alpine β 1.3.14-alpine
v2.26.4 2026-04-25
π Fixed
- β’ DEF and propane records displayed canonical liters as gallons on imperial accounts, and the DEF edit form showed canonical $/L in the $/gal field. Same regression as the fuel fix in v2.26.3.
- β’ Propane records were saved with
price_basis='per_tank'while the form's UI and math were per-volume, so per-record price lookups were inconsistent with the rest of the app's metric-canonical storage. New saves now useper_volume; legacy records lazy-migrate on next edit.
v2.26.3 2026-04-25
π Fixed
- β’ Fuel record price/gal displayed and saved as raw $/L on imperial accounts (regression from #67 in v2.26.2).
- β’ Fresh installs failed at migrations 038 and 048:
Base.metadata.create_allproduces the post-#67 canonical schema (odometer_km,due_mileage_km), but those migrations tried to create indexes on the now-removed legacy columns (mileage,due_mileage). Added column-existence guards so a fresh install completes all 53 migrations on both PostgreSQL and SQLite. Existing installs are unaffected (already-applied migrations stay skipped viaschema_migrations).
v2.26.2 2026-04-25
β¨ Added
- β’ Widget API keys and read-only
/api/widget/*endpoints for gethomepage integration. Users can generate per-user keys from **Settings β Integrations β API Keys** and pollsummary,vehicles, andvehicle/{vin}for tile data. Keys are SHA-256 hashed at rest, revocable, and scoped to either all of the user's vehicles or a selected subset (ownership is re-checked at every request). Requiresauth_mode=localoroidc. - β’ Stale badge on API keys that haven't been used in 90+ days.
π Changed
- β’ Login screen now shows the SSO button and a "Continue with password" toggle when OIDC is enabled, instead of rendering both options simultaneously.
- β’ Integrations tab: "Homepage / Widget API Keys" renamed to "API Keys" with consumer-agnostic copy; panel redesigned to match the rest of Settings and spans the full row inside the integrations grid.
π Fixed
- β’ Currency symbol now respects the user's currency preference across forms, lists, analytics charts, and PDF reports (#68).
- β’ Storage flipped to SI-metric canonical (km, L, kg, L/100km). Metric users no longer lose precision on round-trips and fuel cost/volume/price math now agrees end to end (#67). Legacy widget API and v2 backups continue to work.
- β’ About page rendered raw i18n keys (
about.tagline,about.whatIsTitle, etc.) instead of translated strings β added the missingaboutblock tocommon.jsonacross all four locales (en/pl/ru/uk). - β’ Migration 053 (#67) refused to start on databases retaining frozen audit backup tables (
*_records_backup*) left by older migrations. The preflight scan now skips backup-named tables since they're not part of the active schema and their imperial column references are intentional historical snapshots. - β’ Computed fuel economy (L/100 km) was serializing as null on
/api/vehicles/{vin}/fuelresponses β the route was setting the legacympgdict key while the response schema only declaredl_per_100km(#67). - β’ Missing translations on Address Book, Find POI, and the login footer β pages previously rendered raw i18n keys (
addressBook.title,poiFinder.title,auth.tagline). - β’ API-key timestamps showing "just now" on hours-old rows. Timestamps across widget keys, drive sessions, DTCs, photos, archived vehicles, transfers, backups, and telemetry charts now render at the correct wall-clock time regardless of the user's timezone.
- β’ Insurance and warranty expiry filters and family-dashboard service dates were off-by-one day for users west of UTC.
v2.26.1 2026-04-13
π Dockerfile Dependencies
- β’ **oven/bun**: 1.3.11-alpine β 1.3.12-alpine
π Dev Dependencies
- β’ **@typescript-eslint/eslint-plugin**: 8.58.0 β 8.58.2
- β’ **@typescript-eslint/parser**: 8.58.0 β 8.58.2
- β’ **@vitest/coverage-v8**: 4.1.2 β 4.1.4
- β’ **@vitest/ui**: 4.1.2 β 4.1.4
- β’ **autoprefixer**: 10.4.27 β 10.5.0
- β’ **globals**: 17.4.0 β 17.5.0
- β’ **jsdom**: 29.0.1 β 29.0.2
- β’ **pyright**: 1.1.400 β 1.1.408
- β’ **pytest**: 9.0.2 β 9.0.3
- β’ **typescript-eslint**: 8.58.0 β 8.58.2
- β’ **vite**: 8.0.3 β 8.0.8
- β’ **vitest**: 4.1.2 β 4.1.4
π App Dependencies
- β’ **@schedule-x/calendar**: 4.3.1 β 4.4.0
- β’ **@schedule-x/calendar-controls**: 4.3.1 β 4.4.0
- β’ **@schedule-x/events-service**: 4.3.1 β 4.4.0
- β’ **@schedule-x/theme-default**: 4.3.1 β 4.4.0
- β’ **@tanstack/react-query**: 5.96.2 β 5.99.0
- β’ **@tanstack/react-query-devtools**: 5.96.2 β 5.99.0
- β’ **authlib**: 1.6.9 β 1.6.10
- β’ **axios**: 1.14.0 β 1.15.0
- β’ **i18next**: 26.0.3 β 26.0.4
- β’ **lucide-react**: 1.7.0 β 1.8.0
- β’ **pydantic**: 2.12.5 β 2.13.0
- β’ **python-multipart**: 0.0.22 β 0.0.26
- β’ **react**: 19.2.4 β 19.2.5
- β’ **react-dom**: 19.2.4 β 19.2.5
- β’ **react-is**: 19.2.4 β 19.2.5
- β’ **react-router-dom**: 7.14.0 β 7.14.1
π HTTP Servers
- β’ **granian**: 2.7.2 β 2.7.3
v2.26.0 2026-04-05
π Changed
- β’ Replace react-big-calendar with Schedule-X β native TypeScript, React 19 support, zero lodash dependency, resolves all 9 npm audit vulnerabilities
π Fixed
- β’ Add missing i18n translation keys for File Management, Integrations, Notifications, and Backup & Restore settings tabs
- β’ Fix stale closure in calendar event fetching that caused "Failed to load calendar events" on filter changes
π Security
- β’ Restrict all backup API endpoints to admin-only access
- β’ Fix information leakage via raw exception text in HTTP error responses
- β’ Fix broken admin password reset endpoint (ImportError on every call)
- β’ Enforce magic byte validation for photo uploads
- β’ Reject privileged fields (
is_admin,is_active) on self-update endpoint - β’ Reduce JWT session lifetime from 24h to 2h; centralize JWT, cookie, and CSRF expiry from single config value
- β’ Sanitize ~70 logger call sites to prevent log injection via usernames, device IDs, emails, and filenames
- β’ Redact structured data from logs (OCR output, OIDC token responses, settings dicts)
- β’ Document
MYGARAGE_SECRET_KEYenvironment variable override in startup logs and settings UI
π Dev Dependencies
- β’ **@playwright/test**: 1.58.2 β 1.59.1
- β’ **@typescript-eslint/eslint-plugin**: 8.57.2 β 8.58.0
- β’ **@typescript-eslint/parser**: 8.57.2 β 8.58.0
- β’ **@vitest/coverage-v8**: 4.1.1 β 4.1.2
- β’ **@vitest/ui**: 4.1.1 β 4.1.2
- β’ **eslint**: 10.1.0 β 10.2.0
- β’ **ruff**: 0.15.7 β 0.15.9
- β’ **typescript-eslint**: 8.57.2 β 8.58.0
- β’ **vite**: 8.0.2 β 8.0.3
- β’ **vitest**: 4.1.1 β 4.1.2
π App Dependencies
- β’ **@tanstack/react-query**: 5.95.2 β 5.96.2
- β’ **@tanstack/react-query-devtools**: 5.95.2 β 5.96.2
- β’ **axios**: 1.13.6 β 1.14.0
- β’ **fastapi**: 0.135.2 β 0.135.3
- β’ **i18next**: 25.10.9 β 26.0.3
- β’ **i18next-http-backend**: 3.0.2 β 3.0.4
- β’ **lucide-react**: 1.6.0 β 1.7.0
- β’ **pandas**: 3.0.1 β 3.0.2
- β’ **pillow**: 12.1.1 β 12.2.0
- β’ **react-hook-form**: 7.72.0 β 7.72.1
- β’ **react-i18next**: 16.6.6 β 17.0.2
- β’ **react-router-dom**: 7.13.2 β 7.14.0
- β’ **recharts**: 3.8.0 β 3.8.1
- β’ **sqlalchemy**: 2.0.48 β 2.0.49
v2.25.2 2026-03-31
β¨ Added
- β’ Community translations for Polish, Russian, and Ukrainian (thanks @f0rZzZ)
- β’ Interpolation variable validation in
validate-translationsscript
π Fixed
- β’ E2E test fixture to pin English locale and prevent cross-test language contamination
v2.25.1 2026-03-26
π Fixed
- β’ Fix settings i18n namespace mismatch and missing
/localesstatic mount -- Settings.tsx used wrong i18n namespace ('common' instead of 'settings'), causing raw translation keys to render; backend also never mounted/localesas a static path, so non-English translation files were unreachable - β’ Remove deprecated
ignoreDeprecationsfrom tsconfig.json (TypeScript 6 compatibility cleanup)
v2.25.0 2026-03-26
π Dev Dependencies
- β’ **@tailwindcss/vite**: 4.2.1 β 4.2.2
- β’ **@typescript-eslint/eslint-plugin**: 8.57.1 β 8.57.2
- β’ **@typescript-eslint/parser**: 8.57.1 β 8.57.2
- β’ **@vitest/coverage-v8**: 4.1.0 β 4.1.1
- β’ **@vitest/ui**: 4.1.0 β 4.1.1
- β’ **eslint**: 10.0.3 β 10.1.0
- β’ **jsdom**: 29.0.0 β 29.0.1
- β’ **pytest-cov**: 7.0.0 β 7.1.0
- β’ **ruff**: 0.15.4 β 0.15.7
- β’ **tailwindcss**: 4.2.1 β 4.2.2
- β’ **typescript**: 5.9.3 β 6.0.2
- β’ **typescript-eslint**: 8.57.1 β 8.57.2
- β’ **vite**: 8.0.0 β 8.0.2
- β’ **vitest**: 4.1.0 β 4.1.1
π App Dependencies
- β’ **@tanstack/react-query**: 5.90.21 β 5.95.2
- β’ **@tanstack/react-query-devtools**: 5.91.3 β 5.95.2
- β’ **fastapi**: 0.135.1 β 0.135.2
- β’ **lucide-react**: 0.577.0 β 1.6.0
- β’ **pymupdf**: 1.27.2 β 1.27.2.2
- β’ **react-hook-form**: 7.71.2 β 7.72.0
- β’ **react-router-dom**: 7.13.1 β 7.13.2
π Dockerfile Dependencies
- β’ **oven/bun**: 1.3.10-alpine β 1.3.11-alpine
β¨ Added
- β’ Per-user language preference with react-i18next (English, Polish, Ukrainian, Russian)
- β’ Per-user currency preference (16 currencies) with locale-aware formatting
- β’ Language and currency selectors in Settings
- β’ ESLint guards against hardcoded currency/locale strings
- β’ Playwright E2E tests for language/currency switching
- β’ OpenAPI-generated TypeScript types: frontend types are now auto-generated from backend Pydantic schemas via
openapi-typescript, eliminating manual type drift - β’ CI freshness gate (
check-api-typesjob) ensures generated types stay in sync with backend schemas - β’ Per-line-item service categories with category-aware suggestion combobox
- β’ Vehicle reminders system (date, mileage, both, smart modes) with inline creation from service visits
- β’ Reminders sub-tab in Tracking tab with filter views and done/dismiss actions
- β’ Diff-based service visit line item editing (preserves IDs for reminder FKs)
- β’ Calendar and dashboard now powered by reminders instead of maintenance schedule
π Fixed
- β’ Fix service_line_items table schema lost during migration 049 (restore PK, autoincrement, FKs, constraints)
- β’ Fix Vite 8 build failure: convert
manualChunksfrom object to function for Rolldown compatibility
ποΈ Removed
- β’ Maintenance schedule system (tables, routes, services, frontend components)
- β’ Vendor price history system (schedule-dependent)
- β’ Visit-level service category selector (auto-derived from line items)
v2.24.1 2026-03-16
π Fixed
- β’ Standardize all datetime handling to naive UTC, fixing CSRF token insert failures and device offline check crashes
v2.24.0 2026-03-15
π Security
- β’ Enforce vehicle ownership checks on all VIN-scoped routes (65+ routes across 13 files)
- β’ Add bounded
Query()validation to all pagination parameters to prevent DoS
β¨ Added
- β’ TanStack Query v5 for frontend data fetching with automatic caching and cache invalidation
- β’ 9 query/mutation hook files for all vehicle-scoped record types
- β’
FormModalWrappershared component for consistent modal UI - β’
useFormSubmithook for standardized form error handling - β’ 167 new unit tests (16 Zod schema suites, vehicle archive routes, analytics routes)
- β’ 18 new E2E Playwright tests (vehicle lifecycle, fuel records, tab navigation)
- β’ Non-admin user test fixture for multi-user authorization testing
- β’ CI tripwire to catch raw vehicle existence checks in route files
π Changed
- β’ Extract 6 backend service classes (warranty, insurance, tax, toll, recall, spot rental)
- β’ Split
services/oidc.py(939 lines) into 7-file package - β’ Split
services/analytics_service.py(826 lines) into 7-file package - β’ Migrate all 14 list components from manual useState/useEffect to TanStack Query hooks
- β’ Migrate 14 form components from raw
api.post/api.putto TanStack Query mutation hooks for automatic cache invalidation - β’ Adopt
FormModalWrapperin 5 more form components (NoteForm, RecallForm, TollTagForm, TollTransactionForm, ServiceVisitForm) - β’ Expand
FormModalWrapperwith icon, footer, isOpen, and zIndex props; adopt in OIDCModal and LocalAuthModal - β’ Migrate document upload to TanStack Query mutation hook for automatic cache invalidation
- β’ Consolidate
formatDateForInputfrom 9 local copies to shareddateUtils.tsimport
ποΈ Removed
- β’ Delete unused
ServiceAttachmentUpload.tsx(dead code, zero imports)
π Fixed
- β’ Fix PostgreSQL
strftime()error on toll transaction monthly summary (#48) - β’ Fix fuel log edit not saving on diesel/DEF-enabled vehicles due to NaN validation (#49)
- β’ Fix DEF tracking toggle not persisting when disabled (#50)
- β’ Fix PostgreSQL crash on telemetry upsert (hardcoded SQLite dialect import)
- β’ Fix system-info endpoint returning wrong DB size and unredacted URL on PostgreSQL
- β’ Fix backup/restore assuming SQLite file paths on PostgreSQL
- β’ Fix toll transaction response type mismatch (
transaction_datevsdatealias from backend) - β’ Fix 4 PostgreSQL migration failures: transaction-aborting try/except in 011, stale FK in 024, missing table guard in 025, boolean/integer mismatch in 027/034/036/042 (#42)
π App Dependencies
- β’ **@tanstack/react-query**: added (5.90.21)
- β’ **@tanstack/react-query-devtools**: added (5.91.3)
v2.23.2 2026-03-14
π Fixed
- β’ Fix PostgreSQL migrations failing with
UndefinedColumnErrorafter container upgrade (#42) - β’ Fix metric mileage validation error when editing fuel, odometer, and DEF records (#43)
- β’ Fix setting vehicle main photo returning 404 (#44)
- β’ Fix metric units showing imperial labels and values in fuel, DEF, and propane summary cards (#45)
- β’ Fix fuel economy trend chart plotting MPG values while labeled as L/100km (#46)
π Security
- β’ Fix 8 dev dependency vulnerabilities (rollup, undici, flatted) via overrides
π App Dependencies
- β’ **aiomqtt**: 2.5.0 β 2.5.1
- β’ **authlib**: 1.6.8 β 1.6.9
- β’ **fastapi**: 0.134.0 β 0.135.1
- β’ **lucide-react**: 0.575.0 β 0.577.0
- β’ **pymupdf**: 1.27.1 β 1.27.2
- β’ **recharts**: 3.7.0 β 3.8.0
- β’ **sqlalchemy**: 2.0.47 β 2.0.48
v2.23.1 2026-03-11
π Fixed
- β’ Fix local auth login failing over plain HTTP due to cookie
Secureflag being unconditionally set in production (#35)
v2.23.0 2026-02-28
π Dev Dependencies
- β’ **@tailwindcss/vite**: 4.2.0 β 4.2.1
- β’ **@typescript-eslint/eslint-plugin**: 8.56.0 β 8.56.1
- β’ **@typescript-eslint/parser**: 8.56.0 β 8.56.1
- β’ **autoprefixer**: 10.4.24 β 10.4.27
- β’ **eslint**: 10.0.1 β 10.0.2
- β’ **eslint-plugin-react-refresh**: 0.5.0 β 0.5.2
- β’ **ruff**: 0.15.2 β 0.15.4
- β’ **tailwindcss**: 4.2.0 β 4.2.1
- β’ **typescript-eslint**: 8.56.0 β 8.56.1
π App Dependencies
- β’ **axios**: 1.13.5 β 1.13.6
- β’ **fastapi**: 0.129.2 β 0.134.0
- β’ **matplotlib**: 3.10.0 β 3.10.8
- β’ **pillow-heif**: 1.2.1 β 1.3.0
- β’ **react-router-dom**: 7.13.0 β 7.13.1
- β’ **sqlalchemy**: 2.0.46 β 2.0.47
π Dockerfile Dependencies
- β’ **oven/bun**: 1.3.9-alpine β 1.3.10-alpine
π HTTP Servers
- β’ **granian**: 2.7.1 β 2.7.2
β¨ Added
- β’ Mobile Quick Entry β after signing in on a phone, users are redirected to a streamlined Quick Entry page for fast fuel, service, and mileage logging; toggle in Settings β Mobile Experience
- β’ Quick Entry page β vehicle selector (auto-selects if only one), three large action buttons (Fuel Up, Service, Mileage), success toast on submit, Dashboard escape link
- β’ Calendar now shows maintenance schedule items with status badges (overdue, due soon, never done) and miles-remaining indicators in the sidebar
- β’ Never-performed maintenance items with no intervals now appear on today's date as "needs attention" items in the calendar
- β’ Background scheduler for automated notifications β maintenance due/overdue, insurance/warranty expiration, NHTSA recall checks (weekly), and odometer milestone alerts (every 10k miles); enable with
SCHEDULER_ENABLED=trueenvironment variable - β’ Configurable notification thresholds:
notify_service_days(default 30) andnotify_service_miles(default 500) in Settings β Notifications - β’ Odometer milestone notifications β opt-in via Settings β Notifications β Milestones; triggers at every 10,000-mile boundary
π Fixed
- β’ Replaced deprecated
React.FormEventwithSyntheticEvent<HTMLFormElement>across 9 components (React 19 type cleanup)
π Changed
- β’ Vehicle detail page mobile UI overhaul β primary tabs replaced with a 3Γ2 icon grid (no horizontal scrolling), header resized for narrow screens with VIN overflow protection, Share and Transfer added to mobile action menu, overview section switched from CSS columns to grid, LiveLink charts use CSS-driven responsive height
- β’ Service tab mobile improvements β visit cards flex-wrap on narrow screens, search bar full-width on mobile, modal padding reduced, Maintenance Schedule form inputs stack on small screens, action sheet capped at 70vh for landscape usability
ποΈ Removed
- β’ Reminders system β replaced entirely by maintenance schedule items; completed reminders archived as service visits via migration 045, reminder table dropped
v2.22.0 2026-02-23
β¨ Added
- β’ LiveLink session grace period β configurable delay (0-300s, default 60) before ending sessions after WiFi drops, preventing phantom micro-sessions
- β’ LiveLink MQTT command publishing β send
get_vbatt,get_autopid_data, andrebootto WiCAN devices via MQTT - β’ LiveLink telemetry validation β range and rate-of-change checks reject garbage values from partial ECU wakes
- β’ LiveLink grouped PID format support β forward-compatible parsing for community firmware forks
π Fixed
- β’ Database startup crash on retry β migrations 002, 006, 022, 026, 030 now clean up stale temp tables before recreating them, preventing "table already exists" errors after a failed upgrade
- β’ Vehicle sharing modal crash β backend response unwrapping fixed in familyService
- β’ LiveLink token-bound device resolution β global token with multiple devices no longer attaches telemetry to wrong vehicle
- β’ LiveLink DTC ingestion β
DIAGNOSTIC_TROUBLE_CODESstring values now survive schema validation - β’ LiveLink MQTT unknown status β malformed status messages no longer trigger false session transitions
π Tests
- β’ Added migration test harness β 9 tests covering runner behavior (tracking table, discovery order, stop-on-failure, noop second run) and crash-recovery regression for migrations 002, 006, 022, 026, 030 (closes #35)
π Changed
- β’ Simplified top nav from 7 items to 5 β VIN Decoder moved to About page modal, Family merged into Settings
- β’ Unified Family Management into a single view with inline action icons on member cards (replaces 3-tab layout + separate Manage Members modal)
- β’ OIDC users restricted to role and relationship edits only in the user edit modal
ποΈ Removed
- β’ Standalone Family Dashboard page (redirects to Settings)
- β’ FamilyDashboardManageModal (functionality merged into FamilyManagementModal)
v2.21.2 2026-02-22
β¨ Added
- β’ DEF level auto-sync from fuel records β note DEF gauge level on fuel fill-ups to auto-create DEF observation records
- β’ **Redesigned Analytics PDF Reports** β branded layout with charts, KPI cards, and visual hierarchy replacing plain table-only reports β Vehicle report: monthly spending bar chart, service cost donut chart, vendor analysis, seasonal spending cards, cost projections β Garage report: cost breakdown donut, vehicle cost comparison table, monthly trends chart β Bundled DM Sans + JetBrains Mono fonts with graceful fallbacks β Added
matplotlibdependency for chart generation
π Changed
- β’ Streamlined About page β removed duplicated features, tech stack, and statistics (now lives on project website); added links to website and GitHub
π Fixed
- β’ Fuel service write permission checks (added
require_write=Trueto create/update/delete) - β’ Garage PDF export now uses
model_dump()β fixes missingtotal_upgrades,total_inspection,total_collision,total_detailing,total_deffields
π Dev Dependencies
- β’ **@tailwindcss/vite**: 4.1.18 β 4.2.0
- β’ **@typescript-eslint/eslint-plugin**: 8.55.0 β 8.56.0
- β’ **@typescript-eslint/parser**: 8.55.0 β 8.56.0
- β’ **eslint**: 10.0.0 β 10.0.1
- β’ **jsdom**: 28.0.0 β 28.1.0
- β’ **ruff**: 0.15.1 β 0.15.2
- β’ **tailwindcss**: 4.1.18 β 4.2.0
- β’ **typescript-eslint**: 8.55.0 β 8.56.0
π App Dependencies
- β’ **fastapi**: 0.129.0 β 0.129.2
- β’ **lucide-react**: 0.564.0 β 0.575.0
- β’ **pandas**: 3.0.0 β 3.0.1
- β’ **pillow-heif**: 1.2.0 β 1.2.1
- β’ **pydantic-settings**: 2.12.0 β 2.13.1
- β’ **react-hook-form**: 7.71.1 β 7.71.2
v2.21.1 2026-02-14
π Dockerfile Dependencies
- β’ **oven/bun**: 1.3.8-alpine β 1.3.9-alpine
π Dev Dependencies
- β’ **ruff**: 0.15.0 β 0.15.1
π App Dependencies
- β’ **authlib**: 1.6.7 β 1.6.8
- β’ **fastapi**: 0.128.8 β 0.129.0
- β’ **lucide-react**: 0.563.0 β 0.564.0
- β’ **python-dateutil**: 2.9.0 β 2.9.0.post0
- β’ **reportlab**: 4.4.9 β 4.4.10
β¨ Added
- β’ **DEF Tracking** - CRUD, analytics, CSV/JSON export/import for Diesel Exhaust Fluid records
- β’ **DEF in Garage Analytics** - Own cost category in pie chart, vehicle table, monthly trends
- β’ **DEF in Vehicle Analytics** - Dedicated section with spend, gallons, avg cost/gal, consumption rate
- β’ **Inline Analytics Cards** - Added to Fuel and Propane tabs to match DEF tab pattern
- β’ **Migration 039** - Backfills
service_visits.total_costfrom line items + tax/fees for existing records - β’ **
visits_to_dataframe()** - New analytics service function producing one DataFrame row per visit (visit-level totals for financial accuracy) - β’ **Write-path total_cost sync** -
total_costis always recomputed fromcalculated_total_coston every service visit create, update, line item add, and line item delete - β’ **Test Suite Expansion** - Added 120 new tests (85 backend + 35 frontend), up from 1,011 to 1,131 total β Backend: CSRF middleware (20), LiveLink token validation (10), notification dispatcher (30), analytics service (25) β Frontend: ErrorBoundary (6), ThemeContext (8), AuthContext (9), VehicleDetail page (12)
- β’ **Playwright E2E Tests** - 13 end-to-end tests across 5 specs (auth, dashboard, navigation, settings, vehicle) with full frontend-to-backend coverage β Dual web server setup (Granian backend + Vite frontend) with fresh SQLite per run β API-based auth setup with CSRF token handling β CI pipeline integration with Playwright report and test result artifacts
π Changed
- β’ **Analytics migrated to ServiceVisit** - All analytics, dashboard, reports, calendar, and family dashboard now query
service_visits+service_line_itemsinstead of legacyservice_recordstable - β’ **Report CSV columns** - Service history CSV headers changed: "Service Type" β "Category", "Vendor Name" β "Vendor", added "Notes"
- β’ **Schema rename** -
GarageMonthlyTrend.maintenanceβGarageMonthlyTrend.servicein analytics API and frontend - β’ **CSV/JSON export** - Service export now queries
ServiceVisitwith line items and vendor relationships; JSON keeps"service_records"key for backward-compatible re-import - β’ **CSV/JSON import** - Service CSV/JSON import now creates
ServiceVisit+ServiceLineItem+Vendor; accepts both old ("Service Type") and new ("Category") CSV headers - β’ **Attachments route** -
get_attachment_vin()handles bothrecord_type='service'and'service_visit'via ServiceVisit lookup - β’ **TypeScript target** - ES2020 β ES2022
- β’ Removed misleading DEF Level gauge from analytics cards (fill level stays in table)
- β’ **Dependency Updates** - Updated 18 Python dependency floors (fastapi, sqlalchemy, aiosqlite, pydantic, httpx, pandas 3.0, pillow, pillow-heif, reportlab, pymupdf, aiomqtt, authlib security patch, and more)
- β’ **aiosmtplib 3.x to 5.x** - Pin-only update, API fully backwards compatible
- β’ **pydantic-settings 2.6 to 2.12** - Replaced deprecated
class Configwithmodel_config = SettingsConfigDict() - β’ **Dockerfile Alignment** - Aligned pip (26.0.1) and bun (1.3.8) labels with actual versions
- β’ **Ruff target-version** - Changed from
py314topy313to avoid PEP 758 syntax issues, fixed 22 except clauses across 18 files
π Fixed
- β’ **Family Dashboard vehicle images not displaying** - Construct proper API URLs for vehicle photos instead of passing raw DB paths
- β’ **Analytics 500 error for vehicles with 3+ months of cost data** - Convert Decimal values to float before numpy trend calculation
- β’ **Maintenance templates create schedule items instead of reminders** - Apply Template now creates
MaintenanceScheduleItemrecords with duplicate detection, not deprecatedReminderobjects - β’ **MissingGreenlet on service visit creation** -
calculated_total_costproperty triggered lazy load ofline_itemsin async context; now eagerly refreshes relationship first - β’ **LiveLink session duration datetime bug** - Normalized naive
session_started_atto UTC before arithmetic inlivelink_vehicle.py - β’ **ESLint
preserve-caught-error** - Added{ cause }to re-thrown errors in AuthContext - β’ **LiveLink Odometer Unit Conversion** - Standard OBD2 PID A6 (Odometer) reports in kilometers per SAE J1979, but values were stored directly as miles. Now converts km to miles based on the system
distance_unitsetting. Custom PIDs (e.g. Mitsubishi-specific) are left as-is since they already report in the configured unit. - β’ **Fuel/Service Odometer Sync Blocked by LiveLink** - Adding a fuel fill-up or service record on the same date as a LiveLink odometer reading failed to create/update the odometer record. The sync logic only checked notes for
[AUTO-SYNC frommarkers but didn't recognize LiveLink-sourced records as overwritable. Now checks thesourcefield and allows fuel/service data to take priority over LiveLink telemetry. - β’ **Odometer Record Source Field** - Auto-synced odometer records from fuel and service entries were created with
source="manual"instead of the correctsource="fuel"orsource="service". New and updated records now set the source field properly. - β’ **Naive/Aware Datetime Mismatch** - Fixed
TypeError: can't subtract offset-naive and offset-aware datetimesin drive session duration calculation and device offline notification. SQLite strips timezone info on storage; reads are now normalized to UTC before arithmetic. - β’ **VehicleDetail Offline Cache** - Offline cache path incorrectly set error state, making the cached data warning banner unreachable; now correctly shows cached vehicle data with offline warning instead of error page
- β’ **VehicleDetail JSON.parse Safety** - Wrapped localStorage cache parsing in try-catch; corrupted cache is cleared on failure instead of crashing
- β’ **Theme Context Race Condition** - Added
cancelledflag with cleanup in useEffect to prevent stale state updates on unmount - β’ **Dashboard Error State** - Replaced silent catch with user-visible error UI and retry button
- β’ **Auth Timeout** - Replaced unreliable 100ms
setTimeoutin AuthContext with immediate try + 50ms retry pattern
π Improved
- β’ **N+1 Query Fix** -
check_device_offline_status()pre-fetches all vehicle names in a single query instead of N separate queries per device - β’ **Thread Pool Offloading** - File writes, thumbnail creation, and Tesseract OCR calls now run in
asyncio.to_thread()to avoid blocking the event loop - β’ **CSRF Middleware** - Refactored to use
async with get_db_context()context manager, eliminating potential resource leaks from manualanext()/aclose() - β’ **Query Deduplication** - Extracted shared ownership filter in
VehicleService.list_vehicles() - β’ **Exception Handling** - Specific
(ValueError, TypeError)for bcrypt in auth service, token expiry log downgraded to debug level,.is_(True)idiom for SQLAlchemy boolean comparisons - β’ **Currency Formatting** - Consolidated all 12 duplicate
formatCurrencyimplementations into sharedformatUtils.tsutility - β’ **Modal State** - VehicleDetail modal management simplified from 4 boolean
useStatehooks to singleModalTypeunion type - β’ **Auth Page Layout** - Extracted shared
AuthPageLayoutcomponent, removing ~80 lines of duplication from Login and Register pages - β’ **Dead Code Removal** - Removed unused
toll-tags-refreshwindow event listener and unusedonRefreshprop from TollTagList - β’ **Accessibility** - Added ARIA labels to icon-only nav links,
role="status"to loading spinners, progressbar attributes to password strength meter,aria-labelto filter/sort dropdowns - β’ **Pyright Strict Mode** - Reduced
"none"suppressions from 13 to 7 (46% reduction), warnings from 1,317 to 1,296; fixed insurance parser method signatures, constant redefinition patterns, and untyped parameters
ποΈ Removed
- β’ **Legacy ServiceRecord** - Deleted model, routes (
/api/vehicles/{vin}/service), service layer, schemas, and tests. All functionality replaced by ServiceVisit + ServiceLineItem - β’ **Legacy frontend components** - Removed
ServiceRecordForm,ServiceRecordList,types/service.ts,schemas/service.ts(dead code since ServiceVisit migration) - β’ **Legacy attachment endpoints** - Removed
/api/service/{id}/attachmentsupload/list endpoints (replaced by/api/service-visits/{id}/attachments) - β’ **Migration 040** - Drops
service_recordstable and migrates any remainingrecord_type='service'attachments to'service_visit'
v2.21.0 2026-02-05
β¨ Added
- β’ **Family Dashboard Management Modal** - Dedicated modal for managing family dashboard member visibility and ordering β Toggle member visibility on/off with Eye icon buttons β Reorder visible members with up/down arrows β Separates visible and hidden members into distinct sections β Real-time API updates (no "Save" button needed)
- β’ **Transfer History Section** - Display vehicle ownership transfer history on VehicleDetail page β Collapsible timeline showing ownership transfers β Displays from_user β to_user with relationship badges β Shows transfer date, transferred_by admin, and notes β Shows data included (service records, fuel logs, etc.) as badges
- β’ **Dashboard Shared Vehicle Badge & Filter** - Visual distinction for shared vehicles β Blue "Shared" badge on vehicle cards when vehicle is shared with you β Tooltip shows who shared the vehicle and permission level (view/edit) β Filter dropdown (All Vehicles / My Vehicles / Shared With Me) - only appears if you have shared vehicles
- β’ **LiveLink Integration** - Real-time vehicle telemetry monitoring with WiCAN OBD2 devices β **HTTPS POST Transport** - WiCAN PRO devices can push telemetry directly to MyGarage with token authentication β **MQTT Subscription** - Subscribe to MQTT broker for telemetry from any WiCAN device (PRO or standard) β **Real-time Dashboard** - Live gauges displaying speed, RPM, coolant temp, and other parameters β **Drive Sessions** - Automatic session detection on engine start/stop with trip statistics β **DTC Monitoring** - Track diagnostic trouble codes with severity levels and user notes β **Odometer Auto-Sync** - Automatic odometer updates from telemetry with LiveLink badge β **Historical Charts** - Time-series visualization with multi-parameter overlay and CSV export β **Device Management** - Link devices to vehicles, per-device tokens, firmware update notifications β **Threshold Alerts** - Configurable warnings for parameters like coolant temp and battery voltage β **Data Retention** - Configurable retention periods (30-365 days) with daily aggregation β **Wiki Documentation** - Comprehensive LiveLink guide, FAQ section, and troubleshooting
π Fixed
- β’ **MQTT Subscriber** - Removed unnecessary isinstance check that caused pyright error in CI
π Changed
- β’ **Centralized User Types** - Consolidated duplicate User interface definitions into single source of truth β Created
frontend/src/types/user.tswith canonical User interface β Updated UserManagementModal, AddEditUserModal, and SettingsSystemTab to import from shared type - β’ **Authentication Mode UI** - Redesigned Settings > System authentication configuration β Renamed "Local JWT" button to "Local" for clarity β Local and OIDC configuration now open in modal dialogs instead of inline forms β Tab buttons (None, Local, OIDC) now only select the mode; click "Configure" to open settings β Modal backdrops use blur effect for better visual hierarchy β Moved Archived Vehicles card below Authentication Mode card in layout
- β’ **LiveLink Dashboard Widget** - Removed battery voltage from compact vehicle card view (still visible in full LiveLink tab)
- β’ **LiveLink Tab Header** - Removed battery voltage from status bar for cleaner display
π Dependencies
- β’ **granian**: 2.6.1 β 2.7.0
π Dev Dependencies
- β’ **@vitejs/plugin-react-swc**: 4.2.2 β 4.2.3
- β’ **eslint-plugin-react-refresh**: 0.4.26 β 0.5.0
- β’ **globals**: 17.2.0 β 17.3.0
- β’ **jsdom**: 27.4.0 β 28.0.0
- β’ **aiomqtt**: Added >=2.3.0 for MQTT subscription support
- β’ **@types/react**: 19.2.10 β 19.2.11
- β’ **ruff**: 0.14.14 β 0.15.0
v2.20.4 2026-01-31
π Fixed
- β’ **Monthly Spending Trend Chart** - Fixed chart displaying duplicate x-axis labels and empty right half β Line components for rolling averages were providing separate
dataprops, causing Recharts to render duplicate axis entries β Mergedavg3andavg6rolling averages directly intotrendDataarray β Removed separatedataprop from Line components so they use the chart's unified dataset
π Changed
- β’ **oven/bun**: 1.3.7-alpine β 1.3.8-alpine
- β’ **axios**: 1.13.3 β 1.13.4
- β’ **autoprefixer**: 10.4.23 β 10.4.24
- β’ **Recharts Cell Migration** - Migrated deprecated
Cellcomponent toshapeprop pattern for Pie chart (Recharts 3.7.0 deprecation)
π Security
- β’ **CodeQL Alerts #905-#908** - Fixed clear-text logging false positives in POI registry β Config dict contains
api_keywhich tainted all derived values includingnameandpriorityβ Usedsanitize_for_log()to break taint chain through string transformation β Usedint()constructor for priority values
v2.20.3 2026-01-27
π Fixed
- β’ **Metric Unit Mileage** - Odometer/fuel/service records failing with 422 error when using metric units (#25) β kmβmiles conversion produced floats, but backend expects integers β Added
Math.round()to all mileage conversions in OdometerRecordForm, FuelRecordForm, ServiceRecordForm, and ServiceVisitForm - β’ **Photo Upload** - Vehicle photo upload failing with 422 "file field required" error (#24) β Fixed missing
Content-Type: multipart/form-dataheader in PhotoUpload, VehicleDetail JSON import, and SettingsBackupTab upload
π Changed
- β’ **oven/bun**: 1.3.6-alpine β 1.3.7-alpine
- β’ **axios**: 1.13.2 β 1.13.3
- β’ **react**: 19.2.3 β 19.2.4
- β’ **react-dom**: 19.2.3 β 19.2.4
- β’ **react-is**: 19.2.3 β 19.2.4
- β’ **react-router-dom**: 7.12.0 β 7.13.0
- β’ **recharts**: 3.6.0 β 3.7.0
- β’ **zod**: 4.3.5 β 4.3.6
- β’ **@types/react**: 19.2.8 β 19.2.10
- β’ **@typescript-eslint/eslint-plugin**: 8.53.1 β 8.54.0
- β’ **@typescript-eslint/parser**: 8.53.1 β 8.54.0
- β’ **@vitest/ui**: 4.0.17 β 4.0.18
- β’ **globals**: 17.0.0 β 17.2.0
- β’ **typescript-eslint**: 8.53.1 β 8.54.0
- β’ **vitest**: 4.0.17 β 4.0.18
- β’ **pandas-stubs**: 2.3.3 β 2.3.3.260113
- β’ **ruff**: 0.14.13 β 0.14.14
- β’ **types-Pillow**: 10.2.0 β 10.2.0.20240822
v2.20.2 2026-01-27
π Fixed
- β’ **PostgreSQL Compatibility** - Dashboard not showing new vehicles when using PostgreSQL (#23) β Changed
archived_visiblefield from Integer to Boolean type for proper PostgreSQL compatibility β Vehicle schema now correctly usesbooltype instead ofintfor archive visibility - β’ **NHTSA Body Class Field** - Increased
body_classfield length from 50 to 100 characters to accommodate longer NHTSA values - β’ **Migration System Database URL** - Migration runner now uses configured
DATABASE_URLenvironment variable instead of hardcoded SQLite path - β’ **Archive Endpoint Timezone** - Fixed PostgreSQL timezone issue in archive endpoint β Created
utc_now()utility function for timezone-naive datetime operations β Applied to vehicle archive/restore operations invehicles.py,window_sticker.py, andreminders.py - β’ **PostgreSQL Driver** - Added
psycopg2-binarydependency for synchronous PostgreSQL migrations
v2.20.1 2026-01-25
π Changed
- β’ **Test Infrastructure** - Improved pytest-asyncio configuration β Added
loop_scope="session"to async fixtures for proper event loop reuse β Updated pytest.ini with asyncio_default_fixture_loop_scope setting
π Fixed
- β’ **PostgreSQL Support** - Added missing
asyncpgdependency required for PostgreSQL database connections (#21) - β’ **Vehicle Edit Form - Non-Motorized Vehicle Support** - Fixed form validation blocking saves for trailers, fifth wheels, and travel trailers β Hidden "VIN Decoded Information" and "Engine & Transmission" sections for non-motorized vehicles β Added separate "Fuel Information" section for non-motorized vehicles with propane β Fixed form validation schemas to handle null values from database (was causing "Invalid input" errors on optional fields) β Form now only loads relevant fields based on vehicle type, preventing hidden field validation failures
- β’ **Integration Test Suite Alignment** - Fixed 59 integration tests to match actual API implementation β Updated route paths to match current API structure (
/api/export/vehicles/{vin}/...format) β Added requiredtitlefield to document upload tests β Fixed photo delete tests to use filename instead of numeric ID β Fixed VIN inclusion in document download/delete route paths β Updated expected HTTP status codes (422 for Pydantic validation, not 400) β Added rate limit tolerance (429) to CSV format validation tests - β’ **Export Route Bug** - Fixed
FuelRecord.mpgattribute error in CSV/JSON export βmpgfield doesn't exist on FuelRecord model; replaced withis_haulingandfuel_typefields β Updated CSV headers and JSON export to use actual model attributes
v2.20.0 2026-01-19
π Security
- β’ **CodeQL Security Remediation - 173 Issues Fixed** β **Log Injection Prevention (138 fixes)** - CWE-117 β Created
sanitize_for_log()utility function that escapes control characters (newlines, tabs, ANSI escapes) β Applied to all user-controlled values in logger calls across 44+ files β Prevents log forging and log injection attacks β **Clear-Text Logging of Sensitive Data (10 fixes)** - CWE-532 β Createdmask_coordinates()function to reduce GPS precision for privacy (~1.1km) β Createdmask_api_key()function to show only first 4 characters β Applied to shop discovery, POI providers, and integration services β **Unsafe Cyclic Imports (13 fixes)** - Python best practices β Moved runtime imports toTYPE_CHECKINGblocks in SQLAlchemy models β FixedVehiclemodel (16 forward reference imports) β FixedMaintenanceTemplatemodel (1 forward reference import) β **Partial SSRF Protection (1 fix)** - CWE-918 β Added URL validation inMaintenanceTemplateServicefor GitHub template URLs β Allowlisted hosts:raw.githubusercontent.com,github.com,raw.github.comβ Path component sanitization prevents directory traversal β **Stack Trace Exposure (1 fix)** - CWE-209 β Fixed provider test endpoint in settings routes β Exception details now logged server-side only, generic message returned to client β **Code Quality Fixes (5 fixes)** β Fixed empty-except in OSM provider with meaningful error handling β Fixed unused-import in maintenance template validator β Fixed mixed-returns in pytest conftest with properNoReturntyping
β¨ Added
- β’ **Maintenance System Overhaul - Complete Service Tracking Redesign** β **Vendors**: New vendor management system replacing address book for service providers β Dedicated vendor table with type (shop, dealer, self, other) β Vendor history tracking (service count, total spent, last visit) β Search/autocomplete for existing vendors when creating service visits β **Service Visits**: Replaced service records with comprehensive visit tracking β Visit-level data: date, vendor, mileage, notes β Multiple line items per visit (parts, labor, services) β Line items: description, category, service type, cost, notes β Tax & fees tracking: tax amount, shop supplies, misc fees β Subtotal (line items only) and calculated total (including all fees) β Attachment support migrated from old service records β **Maintenance Schedule Items**: New proactive maintenance tracking β Schedule items with due dates (by date or mileage) β Status tracking: upcoming, due soon, overdue, completed β Link service visits to schedule items when completing maintenance β "Log Service" quick action from schedule items β **UI Reorganization** β Removed duplicate reminders from Service tab (now only in Tracking β Reminders) β Moved Maintenance Templates into Maintenance Schedule modal β Collapsible service visit cards with cost breakdown in expanded view β Maintenance Schedule button opens modal with templates and schedule items
- β’ **Tax & Fees on Service Visits** β Three new fields: Tax Amount, Shop Supplies, Misc Fees β Live subtotal/total calculation in form β Cost breakdown display in service visit list (expanded view) β Totals now match real-world invoices with all charges included
- β’ **POI Finder - Interactive Map & Multiple Providers** β Interactive Leaflet map with POI markers and clustering β Map/List view toggle with persistent preference β Click marker to see POI details, click card to highlight on map β **New Providers**: Google Places, Yelp Fusion, Foursquare β Provider priority configuration in Settings β Integrations β Automatic fallback when primary provider fails or hits quota β Rate limiting and caching per provider
- β’ **POI Finder - Multi-Category Points of Interest Discovery** β Renamed "Shop Finder" to "POI Finder" with expanded functionality β Multi-category search: Auto/RV Shops, EV Charging Stations, Fuel Stations β Category toggle switches (red=off, green=on) - multiple categories can be active simultaneously β 2-column grid layout for results (responsive 1-column on mobile) β Icon-only save buttons (check icon when saved, save icon when not saved) β Category badges on POI cards with color coding β EV charging station metadata: connector types, charging speeds, network β Fuel station metadata: prices by grade, fuel types available β Multi-provider architecture with priority-based fallback β Provider management UI in Settings β Integrations β New API endpoints:
/api/poi/*with backward compatibility for/api/shop-discovery/*β Database: Addedpoi_categoryandpoi_metadatafields to address_book table β Supported providers: TomTom (priority 1), OpenStreetMap (always available fallback)
π Changed
- β’ **Navigation Updates** β Desktop header: "Find Shops" β "Find POI" β Mobile bottom nav: "Shops" β "POI" β Primary route changed from
/shop-finderto/poi-finderβ Old/shop-finderroute maintained for backward compatibility - β’ **Service Records β Service Visits Migration** β Old service records automatically migrated to new service visit format β Each old record becomes a visit with a single line item β Attachments migrated to new service visit attachment system β Vendors created from existing address book entries used in service records
π Fixed
- β’ **Service Visit Bugs** β Fixed 500 error on service visits endpoint (missing subtotal in response) β Fixed line items not saving on edit (schema missing line_items field) β Fixed 422 validation error on create (vin incorrectly required in body) β Fixed total mismatch between collapsed and expanded views
π Technical
- β’ **Backend - Maintenance System** β New models:
Vendor,ServiceVisit,ServiceLineItem,MaintenanceScheduleItemβ New schemas with full CRUD support for all new entities β Service layer with business logic for visits, line items, schedule items β Migration 028: Convert reminders to maintenance schedule items β Migration 029: Cleanup migrated reminders β Migration 030: Create vendors, service_visits, service_line_items tables β Migration 031: Add tax/fee columns to service_visits - β’ **Backend - POI Providers** β Google Places provider with Places API (New) integration β Yelp Fusion provider with business search β Foursquare Places provider with FSQ Places API β Provider health monitoring and automatic failover β Request caching with configurable TTL per provider
- β’ **Frontend - Maintenance System** β New components:
VendorSearch,ServiceVisitForm,ServiceVisitList,ServiceLineItemFormβMaintenanceSchedulecomponent with status indicators β Tab reorganization in vehicle detail view β Form state management for complex nested data (visits with line items) - β’ **Frontend - POI Map** β Leaflet integration with OpenStreetMap tiles β Custom marker icons per POI category β Marker clustering for dense areas β Synchronized map/list selection state
v2.19.0 2026-01-03
β¨ Added
- β’ **Shop Discovery - Standalone Shop Finder Page** β Moved shop discovery to dedicated
/shop-finderpage (removed from Service Record form) β Navigation links in desktop header and mobile bottom nav β Geolocation-based shop discovery within 5 miles of current location β TomTom Places API primary source (2,500 free requests/day, high-quality commercial data) β OpenStreetMap Overpass fallback (unlimited free, crowd-sourced data) β Automatic fallback to OSM when TomTom unavailable or quota exceeded β Usage-based shop recommendations (previously used shops displayed first) β Save discovered shops directly to address book β TomTom API key configuration in Settings β Integrations (optional) β SSRF protection for TomTom API URLs β Works without configuration using OSM (no API key required) β Distance calculation and sorting (Haversine formula, shows miles from current location) β Shop details: name, address, phone, rating, distance, website links - β’ **Service Record Categories - Detailing & Upgrades Expansion** β New "Detailing" category with 12 service types: Car Wash, Hand Wash, Wax, Ceramic Coating, Paint Correction, Interior/Exterior Detailing, Full Detailing, Engine Bay Cleaning, Headlight Restoration, Odor Removal, Upholstery Cleaning β Added to "Upgrades" category: Accessory Upgrade (renamed from Interior Upgrade), Window Tinting, Tonneau Cover
ποΈ Removed
- β’ **Technical Service Bulletins (TSB) Feature - Complete Removal** β Removed all TSB functionality from backend and frontend β Backend: Deleted
/api/tsbsendpoints, TSB model, schemas, and routes β Frontend: Removed TSB tab, TSBList, TSBForm components β Removed TSB relationship from Vehicle model β Database: TSB table remains (data preserved for manual migration if needed) β Safety Recalls tab now shows only Safety Recalls (TSB tab removed) β **Reason:** Non-functional NHTSA TSB API, feature provided no value
π Changed
- β’ **Service Record Form - Simplified Vendor Entry** β Removed "Find Nearby Shop" button from Service Record form β Users now use standalone Shop Finder page to discover and save shops β Address Book autocomplete remains for selecting saved vendors
π Fixed
- β’ **NHTSA Recall Integration** β Fixed incorrect API endpoint causing recall checks to fail β Changed from
https://vpic.nhtsa.dot.gov/api/recallsByVehicletohttps://api.nhtsa.gov/recalls/recallsByVehicleβ Updated defaultnhtsa_recalls_api_urlsetting to use correct base URL β Recall checks now successfully retrieve active recalls from NHTSA database
v2.18.1 2026-01-01
π Fixed
- β’ **Analytics Data Quality & Calculation Accuracy** β Invalid MPG filtering: Filter out 0/negative miles driven and unrealistic MPG values (<5 or >100) β Prevents worst MPG showing 0.0 due to data entry errors or odometer corrections β Two-stage filtering: before calculation (invalid trips) and after (unrealistic MPG) β Handles edge cases: zero miles between fill-ups, negative mileage, extreme outliers β Weighted average MPG calculation: Changed from simple mean to total_miles/total_gallons β More accurate representation of overall fuel efficiency β Accounts for varying trip lengths (long highway vs short city trips) β Example: 450mi/15gal + 50mi/10gal = 20 MPG weighted vs 17.5 MPG simple mean β Recent MPG label clarity: Changed from 5-record rolling average to single most recent fill-up β Backend:
df["mpg"].iloc[-1]instead ofdf["mpg"].tail(5).mean()β Frontend: Label updated from "Recent" to "Latest Fill-Up" for clarity - β’ **Analytics UI/UX Improvements** β Card spacing consistency: Standardized Summary Stats grid spacing from
gap-4togap-6β Matches spacing standard across all analytics sections β Spacing hierarchy: major sections (gap-8), card grids (gap-6), compact rows (gap-4) β Spot rental filtering: Only display for RV-type vehicles (FifthWheel, RV, TravelTrailer) β Backend: Check vehicle_type before querying spot rental data β Frontend: Conditionally render bar chart based onhasPropaneflag β Removes empty spot rental bars from car/truck analytics - β’ **Maintenance Prediction Clarity** β Enhanced prediction display to show both AI predictions AND manual reminders β Schema additions:
has_manual_reminder,manual_reminder_date,manual_reminder_mileagefields β Backend integration: Query active reminders and fuzzy-match to service types β Frontend enhancements: β Service type displayed in larger, prominent font β "REMINDER SET" purple badge when manual reminder exists β "AI predicts:" label in blue for AI-generated predictions from service history β "You set:" label in purple for manual user reminders β Both systems displayed simultaneously with distinct styling β Helps users understand difference between automated predictions and their own reminders β All changes backward compatible (optional fields with defaults)
π Changed
- β’ **BREAKING: Service Records Schema Redesign** β Separated service category from specific service type for better analytics and predictions β **Database Changes:** β
service_typefield renamed toservice_category(Maintenance, Inspection, Collision, Upgrades) βdescriptionfield renamed toservice_typewith 50+ predefined options (Oil Change, Tire Rotation, etc.) β All existing records migrated withservice_type = 'General Service'(users update manually) β Backup table created:service_records_backup_20251229β **Analytics Impact:** β Predictions now group by specific service type instead of generic category β Example: "Next Oil Change due in 90 days" vs "Next Maintenance due in 45 days" β Higher confidence scores due to consistent service-specific intervals β **UI Changes:** β Service Record Form: Added cascading dropdowns (Category filters Service Type options) β Service Record List: Column headers updated (Category | Service Type | Mileage | Cost) β Search filters now search both category and service type fields β **Migration:** Database migration 022 runs automatically on backend startup β **User Action Required:** Update existing service records via UI to change 'General Service' to specific types β **Files Updated:** 17 total (10 backend, 4 frontend, 3 tests including exports, reports, calendar integration) β **Tests Updated:** pytest fixtures and payloads updated to match new schema (vin/mileage/service_type fields)
π Technical Details
- β’ Files modified: 5 (3 backend, 2 frontend)
- β’ Lines changed: 103 insertions(+), 23 deletions(-)
- β’ Quality checks: β Ruff, β ESLint, β TypeScript type check
- β’ Commit:
05c1488- "fix: resolve 6 analytics bugs - MPG calculations, UI spacing, predictions"
v2.18.0 2025-12-27
π Security
- β’ **CodeQL Security Improvements** β Fixed 15 stack trace exposure vulnerabilities (94% reduction) β 7 notification test endpoints (ntfy, Gotify, Pushover, Slack, Discord, Telegram, Email) β 7 CSV import endpoints (service, fuel, odometer, reminder, note, warranty, tax) β 1 insurance document parsing endpoint β Replaced
str(e)with generic error messages to prevent leaking implementation details β Stack traces now only logged server-side, not exposed to API consumers β Added VIN validation before using in file paths (prevents directory traversal) β Validates VIN format (17 alphanumeric characters) before creating directories β Protects window sticker uploads and photo storage endpoints (4 locations) β Mitigates path injection attacks like../../etcβ Fixed 7 clear-text logging of sensitive data warnings in OIDC service (88% reduction) β Removed full URL logging (URLs may contain secrets/tokens in query params) β Removed authorization code and redirect URI from error logs β Added suppression comment for properly masked client_secret β **Overall improvement:** 454 warnings fixed (74% reduction from 610 to 156 warnings)
β¨ Added
- β’ **Propane Tank Size Tracking** β New tank size selection dropdown (20lb, 33lb, 100lb, 420lb) in propane entry form β Number of tanks input field β Auto-calculation of propane gallons based on tank size Γ quantity β Conversion formula: gallons = (pounds Γ· 4.24) Γ quantity β Manual override always available for precise measurements β Tank fields optional (backwards compatible with existing records) β Can edit existing records to add tank data β Database migration 021: Added
tank_size_lbandtank_quantitycolumns to fuel_records β Backend auto-calculation in create/update endpoints β Analytics service extended with tank breakdown, timeline, and refill frequency data β Support for both imperial and metric unit systems - β’ **Travel Trailer Vehicle Type** β New vehicle type:
TravelTrailerfor bumper-pull recreational trailers β Distinct fromFifthWheel(gooseneck) andTrailer(utility/cargo) β Includes propane tracking for appliances (fridge, stove, furnace, water heater) β Includes spot rental tracking for RV parks β No fuel/odometer tracking (non-motorized) β Matches NHTSA vPIC "Travel Trailer" body class classification β Database migration 020: AddedTravelTrailerto vehicle_type check constraint
π Fixed
- β’ **Fuel History UI - Propane Column Visibility** β Hide propane column in fuel history table for non-propane vehicles β Propane column now only displays when vehicle fuel_type includes "propane" β Matches existing fuel entry form behavior (form already hid propane field for non-propane vehicles) β Cleaner UI for gasoline/diesel/electric vehicles β Dynamic colSpan adjustment (9 or 10 columns) for proper table layout β Ready for RV propane tracking with BTU calculations
- β’ **Analytics - Spot Rental Inclusion** β Fixed analytics calculations to include spot rental billing costs β Spot rental costs now appear in Cost Trends with Rolling Averages β Spot rental costs now appear in Monthly Cost Trend charts (bar chart and list view) β Spot rental costs now appear in Seasonal Spending Patterns β Spot rental costs now appear in Period Comparison analysis β Updated
records_to_dataframe()to accept SpotRentalBilling records β Updatedcalculate_monthly_aggregation()to track spot_rental_cost and spot_rental_count β All analytics endpoints now query and include spot rental billing data β Addedtotal_spot_rental_costandspot_rental_countfields to MonthlyCostSummary schema β Monthly Cost Trend chart now displays spot rental as orange stacked bar β Spot rental only appears in list view when amount > 0
v2.17.4 2025-12-15
π Fixed
- β’ **Number Input Bug Across All Forms** β Fixed critical bug where numeric inputs were incorrectly formatting values (e.g., 500 β 500000, 192.68 β mangled output) β Fixed issue where deleting all input left "0100" instead of clearing properly β Updated all 13 forms to use
valueAsNumber: truewith React Hook Form for proper number handling β Removedz.coercefrom all Zod schemas and replaced with NaN transformation for optional fields β Affected forms: BillingEntry, Fuel, Service, Insurance, Propane, SpotRental, Odometer, Tax, TollTransaction, Reminder, Warranty, VehicleEdit, VehicleWizard β Fixed 40+ numeric input fields across the application
v2.17.3 2025-12-14
π Fixed
- β’ **Fifth Wheel Analytics & Reports** β Excluded fuel efficiency metrics from fifth wheel analytics (previously showing incorrectly) β Fixed cost summary PDF reports to exclude fuel data for fifth wheels β Hidden fuel efficiency alerts card for non-motorized vehicles (fifth wheels and trailers)
- β’ **Analytics UI Improvements** β Fixed propane analysis bar chart tooltip background (now displays dark theme properly) β Fixed spot rental analysis bar chart tooltip background (now displays dark theme properly) β Improved tooltip consistency across all analytics charts
- β’ **Type Safety** β Fixed TypeScript type errors in FuelRecordForm component for Decimal field handling β Fixed PropaneRecordList filter to properly handle string/number type conversions β Added proper type conversion helpers for API Decimal values returned as strings
v2.17.2 2025-12-14
π Fixed
- β’ **Spot Rental Form Improvements** β Fixed total cost auto-calculation with proper type conversion (resolved
toFixed()errors) β Simplified rate input to single field based on selected rate type (nightly/weekly/monthly) β Auto-creates first billing entry when spot rental is created with monthly rate β Billing entries now restricted to monthly rate rentals only - β’ **Propane Tank Management** β Fixed decimal validation to accept values like "30.5" in propane gallons field β Improved form validation for propane capacity inputs
- β’ **Address Book Enhancements** β Fixed address book edit functionality - now properly loads existing address data β Corrected form field binding for editing addresses
- β’ **PWA & Service Worker** β Fixed service worker MIME type (now served as
application/javascript) β Fixed manifest.json MIME type (now served asapplication/json) β Fixed icon files to be served with correctimage/pngMIME type β Added explicit root route handler to serve index.html β Improved static file serving for PWA functionality
π Changed
- β’ **Billing Entry UI** β Updated styling to match dark theme consistently across billing forms β Improved visual presentation of billing entry components
v2.17.1 2025-12-14
π Security
- β’ **[HIGH] Fixed Log Injection vulnerabilities in vehicle routes** β Prevented potential log injection attacks in vehicle route endpoints β Converted f-string logging to parameterized format to prevent log forgery
π Documentation
- β’ **Streamlined README** - Reduced from 455 to 143 lines (68% reduction) β Removed verbose configuration examples and troubleshooting details β Organized wiki links into clear sections (Getting Started, Features, Configuration, Help) β Centered badges and screenshot for improved visual presentation β All detailed information now accessible through comprehensive wiki documentation
π Fixed
- β’ **Code Quality** - Fixed ESLint and TypeScript errors in fifth wheel components β Resolved type errors in PropaneTab, BillingEntryForm, and related components β Removed unused imports and variables β Updated bun lockfile to fix CI build issues
v2.17.0 2025-12-13
β¨ Added
- β’ **Electric Vehicle Support** β New vehicle types:
ElectricandHybridβ kWh tracking for electric vehicle charging records β Smart fuel form adapts fields based on vehicle fuel type β Electric vehicles show Energy (kWh) field instead of Volume (gallons) β Hybrid vehicles show both gallons and kWh fields β Dynamic labels: "Price per kWh" for electric, "Charging Station" references β Conditional checkboxes: Full Tank and Hauling hidden for electric vehicles β Electric-specific tip: "Efficiency metrics (kWh/100mi) are calculated from charging records"
π Changed
- β’ **Smart Fuel Form** β Form now conditionally shows/hides fields based on vehicle fuel_type β Field visibility logic: β Electric: Shows kWh, hides gallons/propane/is_full_tank/is_hauling β Hybrid: Shows both gallons and kWh β Gas/Diesel: Shows gallons (existing behavior) β Propane: Shows propane_gallons β Auto-calculation updated to handle both gallons and kWh β Missed Fill-up label changes to "Missed Charging Session" for electric vehicles
π Fixed
- β’ **RV Propane Access Bug** β RV vehicles now have access to propane tab (previously only Fifth Wheels) β Updated
VehicleDetail.tsxto check for both RV and FifthWheel β UpdatedAnalytics.tsxpropane and spot rental sections for RVs β Documentation now correctly reflects RV capabilities
π Technical
- β’ **Database Changes** β Migration 019: Added
kwh NUMERIC(8, 3)column tofuel_recordstable β Updated vehicle_type constraint to include 'Electric' and 'Hybrid' - β’ **Backend Changes** β
backend/app/models/fuel.py: Added kwh field mapping βbackend/app/models/vehicle.py: Updated CheckConstraint for new vehicle types βbackend/app/schemas/vehicle.py: Added Electric/Hybrid to valid_types βbackend/app/schemas/fuel.py: Added kwh validation (0-99999.999, 3 decimal places) - β’ **Frontend Changes** β
frontend/src/types/vehicle.ts: Added Electric and Hybrid to VehicleType βfrontend/src/schemas/vehicle.ts: Added RV (was missing), Electric, and Hybrid to VEHICLE_TYPES βfrontend/src/types/fuel.ts: Added kwh field to all interfaces βfrontend/src/schemas/fuel.ts: Added optionalKwhSchema validation βfrontend/src/schemas/shared.ts: Created optionalKwhSchema validator βfrontend/src/components/FuelRecordForm.tsx: Major smart form refactor with conditional rendering βfrontend/src/pages/VehicleDetail.tsx: Fixed propane tab visibility for RVs βfrontend/src/pages/Analytics.tsx: Fixed propane/spot rental sections for RVs
v2.16.0 2025-01-28
β¨ Added
- β’ **Fifth Wheel & Trailer Enhancement System** β Propane-only tracking for fifth wheels using existing
fuel_recordstable β Propane tab visible only for fifth wheel vehicles (no fuel/odometer tabs) β Spot rental billing entries system for ongoing rental cost tracking β Multiple billing entries per rental with billing date, monthly rate, utilities (electric, water, waste) β Address book integration with RV Park category filter and autocomplete β Auto-fill address when selecting from address book β "Save to Address Book?" prompt after creating new spot rentals β Fifth wheel analytics showing propane spending trends and spot rental costs β Analytics exclude MPG/fuel economy metrics for fifth wheels and trailers β Propane analysis section with monthly cost trends and cost per gallon β Spot rental analysis section with cumulative costs and monthly averages β Billing summary cards showing total billed, billing periods, and monthly average β Expandable billing history with "View All Billings" button β Auto-calculated billing totals (monthly rate + electric + water + waste)
π Changed
- β’ **Vehicle Type Tab Visibility** β Motorized vehicles (Car, Truck, SUV, Motorcycle, RV): Fuel + Odometer tabs β Fifth Wheel: Propane tab ONLY (no fuel, no odometer) β Trailer: No fuel, no odometer, no propane tabs β RVs remain motorized and keep fuel/odometer tabs
- β’ **Spot Rental UI Redesign** β Billing summary card displays by default with last billing entry β Full billing history expandable via "View All Billings" button β Edit/delete buttons for individual billing entries β Cumulative totals and monthly averages calculated automatically
π Fixed
- β’ Fifth wheel vehicle type logic - correctly excludes both 'Trailer' and 'FifthWheel' from motorized vehicles
- β’ Propane records filtered client-side:
propane_gallons > 0 && !gallons - β’ Billing dates validated within rental check-in/check-out period
π Technical
- β’ **Database Changes** β Migration 018: Added
spot_rental_billingstable with FK tospot_rentalsβ CASCADE delete ensures billing entries removed when parent rental deleted β Existingfuel_records.propane_gallonscolumn reused (no schema changes) - β’ **Backend Changes** β New model:
SpotRentalBillingwith relationship toSpotRentalβ New endpoints:/vehicles/{vin}/spot-rentals/{rental_id}/billings(CRUD) β Analytics service:calculate_propane_costs()andcalculate_spot_rental_costs()β Fifth wheel detection in analytics route skips fuel economy calculations β Eager loading withselectinload(SpotRental.billings)prevents N+1 queries - β’ **Frontend Changes** β New components:
PropaneRecordForm,PropaneRecordList,PropaneTab,BillingEntryFormβ Updated types:SpotRentalBillinginterfaces and validation schemas β Helper functions:getBillingTotal(),getMonthlyAverage(),getLastBilling()β Address book autocomplete integration inSpotRentalFormβ Analytics conditional sections based on vehicle type
π Documentation
- β’ Added comprehensive implementation summary at
/srv/raid0/docker/documents/history/mygarage/2025-01-28-fifth-wheel-enhancements.md - β’ Total: 5 backend files created, 8 backend files modified, 7 frontend files created, 6 frontend files modified
v2.15.1 2025-12-11
π Security
- β’ **CRITICAL: Updated React to 19.2.3** - Patches CVE-2025-55182 (CVSS 10.0), a remote code execution vulnerability actively exploited in the wild β Updated
reactfrom 19.2.0 to 19.2.3 β Updatedreact-domfrom 19.2.0 to 19.2.3 β Updatedreact-isfrom 19.2.0 to 19.2.3 β Includes enhanced loop protection for React Server Functions
π Changed
- β’ **Frontend Dependencies** - Updated all low-risk dependencies for improved performance and security β Updated
vitefrom 7.2.4 to 7.2.7 (security fix for request-target validation) β Updated@testing-library/jest-domfrom 6.6.3 to 6.9.1 (new accessibility matchers) β Updated@testing-library/reactfrom 16.1.0 to 16.3.0 β Updated@testing-library/user-eventfrom 14.5.2 to 14.6.1 β Updated@typescript-eslint/eslint-pluginfrom 8.48.1 to 8.49.0 β Updated@typescript-eslint/parserfrom 8.48.1 to 8.49.0 β Updatedtypescript-eslintfrom 8.48.1 to 8.49.0 β Updatedjsdomfrom 27.2.0 to 27.3.0 β Updatedreact-hook-formfrom 7.67.0 to 7.68.0 (new FormStateSubscribe component) β Updatedreact-router-domfrom 7.9.6 to 7.10.1 (React Router v7 stabilization fixes) - β’ **Backend Dependencies** - Updated ruff linter with new features and improved performance β Updated
rufffrom 0.7.0 to 0.14.9 β New RUF100 rule for detecting unused suppressions (preview mode) β Improved performance with faster line index computation β Better rule accuracy (S506, B008, D417 improvements)
π Fixed
- β’ **Code Quality** - Fixed 26 linting violations identified by ruff 0.14.9 β Fixed 17 E712 violations: Changed SQLAlchemy boolean comparisons from
== True/Falseto.is_(True/False)β Fixed 5 F841 violations: Marked intentionally unused ownership validation variables with_β Fixed 4 F401 violations: Added# noqa: F401to imports used for availability checking - β’ **Configuration** - Updated ruff configuration to fix deprecation warning β Moved
per-file-ignoresfrom top-level to[tool.ruff.lint]section in pyproject.toml
v2.15.0 2025-12-11
β¨ Added
- β’ **Unit Conversion System** - Per-user Imperial/Metric unit preferences β Full support for distance (mi/km), volume (gal/L), fuel economy (MPG/L/100km) β Per-user preferences stored in user settings β Optional "Show Both Units" mode displays both systems simultaneously (e.g., "25 MPG (9.4 L/100km)") β Applied across all forms: Fuel, Odometer, Service records β Applied across all displays: Dashboard, Analytics, Record lists β Dynamic chart labels and tooltips adapt to user preference β Canonical storage pattern: all data stored in Imperial, converted at display time β Comprehensive conversion utilities:
UnitConverterandUnitFormatterclasses β See docs/UNIT_CONVERSION.md for technical details - β’ **Vehicle Archive System** - Safe vehicle archiving with complete data preservation β Replace dangerous "Delete" with "Archive" workflow β Archive metadata: reason, sale price, sale date, notes β Dashboard visibility toggle for archived vehicles β Visual watermark on dashboard cards for archived vehicles (diagonal red "ARCHIVED" banner) β Un-archive capability to restore vehicles to active status β Permanent delete only available after archiving β Preserves all records: service, fuel, odometer, documents, photos, notes β Archived vehicles list in Settings with management actions β Archive reasons: Sold, Traded, Totaled, Donated, End of Lease, Other β See docs/ARCHIVE_SYSTEM.md for complete guide
π Changed
- β’ **Dashboard Filtering** - Now shows active vehicles + archived vehicles with visibility enabled
- β’ **Vehicle Detail Page** - "Delete" button replaced with "Remove Vehicle" (archive workflow)
- β’ **VehicleStatisticsCard** - Added unit conversion for odometer and fuel economy displays
- β’ **Analytics Page** - All charts and tables now respect unit preferences β Fuel Economy chart Y-axis shows "MPG" or "L/100km" based on preference β All statistics, tables, and tooltips display in user's preferred units
π Fixed
- β’ **Archive System - Authentication Mode Compatibility** β Archive endpoints now work correctly in
auth_mode='none'without requiring login β CSRF middleware now skips validation whenauth_mode='none'β Archived vehicles with NULLuser_idnow visible to all users in authenticated modes β Dashboard properly refreshes after archiving a vehicle β Archive watermark positioning corrected (no longer cut off at top edge) - β’ **Unit Preferences - Non-Authenticated Support** β Unit preferences now work in
auth_mode='none'using localStorage β Settings page shows Unit System and Archived Vehicles sections regardless of auth mode β Unit preferences persist across authentication mode changes
π Technical
- β’ Added database columns:
archived_at,archive_reason,archive_sale_price,archive_sale_date,archive_notes,archived_visible - β’ New backend endpoints:
/api/vehicles/{vin}/archive,/api/vehicles/{vin}/unarchive,/api/vehicles/archived/list - β’ Archive endpoints use
optional_authfor compatibility with all authentication modes - β’ CSRF middleware checks
auth_modesetting before enforcing token validation - β’ New frontend components:
VehicleRemoveModal,ArchivedVehiclesList - β’ New React hooks:
useUnitPreferencefor accessing unit preferences (with localStorage fallback) - β’ New utility classes:
UnitConverter(conversion methods),UnitFormatter(display formatting) - β’ Dashboard endpoint filtering:
WHERE archived_at IS NULL OR (archived_at IS NOT NULL AND archived_visible = TRUE) - β’ Dashboard uses
useLocationhook to trigger reload on navigation - β’ Archived vehicles query includes NULL
user_idvehicles for authenticated users
π Documentation
- β’ Added docs/UNIT_CONVERSION.md - Complete unit conversion system guide
- β’ Added docs/ARCHIVE_SYSTEM.md - Complete vehicle archive system guide
- β’ Updated README.md - Added new features to key features list and quick links
v2.14.4 2025-12-10
π Fixed
- β’ **CI/CD Failures** - Fixed GitHub Actions workflow failures in frontend and Docker build jobs β Fixed bun.lock dependency mismatch causing
bun install --frozen-lockfileto fail β Updated bun.lock to match lucide-react 0.556.0 from package.json β Resolved "Process completed with exit code 1" errors in CI dependency installation β Fixed Docker multi-stage build failures during frontend dependency installation - β’ **Vitest Integration** - Fixed test runner compatibility issues with Bun 1.3.4 in CI environment β Changed test command from
bun test --runtobun run test:runto use Vitest instead of Bun's native test runner β Fixed 'document is not defined' errors caused by Bun's test runner not setting up jsdom environment β Added explicit vitest.config.ts as temporary workaround for Bun 1.3.4 CI compatibility β Bun 1.3.4 doesn't load test config from vite.config.ts in GitHub Actions environment
π Technical Notes
- β’ CI now passes all three jobs: Frontend Tests, Backend Tests, Docker Build Test
- β’ Lock file sync required after manual package.json version changes
- β’ Vitest configuration duplication (vite.config.ts + vitest.config.ts) is temporary until Bun 1.4+ improves integration
v2.14.3 2025-12-09
π Changed
- β’ **[BREAKING] Migrated frontend from Node.js 25 to Bun 1.3.4 runtime** β Package manager: npm β bun β Lockfile: package-lock.json β bun.lock β Docker base image: node:25-alpine β oven/bun:1.3.4-alpine β ~10-25x faster dependency installation (2-5s vs 30-60s) β ~40-60% smaller Docker images β All development commands now use
buninstead ofnpm
π Developer Impact
- β’ **Install Bun 1.3.4+ for local development**: https://bun.sh/docs/installation
- β’ Run
bun installinstead ofnpm ci - β’ Run
bun devinstead ofnpm run dev - β’ Run
bun testinstead ofnpm test - β’ See DEVELOPMENT.md for full guide
π Infrastructure
- β’ Vite 7.2.4 bundler retained (no changes to build output)
- β’ Vitest test runner retained (all tests unchanged)
- β’ Backend unchanged (Python 3.14 + FastAPI + Granian)
- β’ Zero application code changes
- β’ Production deployment compatible (same Docker interface)
- β’ CodeQL security scanning compatible
π Performance Improvements
- β’ Package install: ~10-25x faster (19s vs 30-60s)
- β’ Build time: ~1.5-2x faster (3s vs 4-5s)
- β’ Docker image: ~40-60% smaller
- β’ CI/CD runtime: ~2x faster
β¨ Added
- β’ Added compose.dev.yaml for hot reload development with Bun + Vite HMR
π Documentation
- β’ Added comprehensive DEVELOPMENT.md guide
- β’ Updated README.md with Bun installation and usage
- β’ Updated wiki: Installation, Home, Troubleshooting guides
- β’ Updated SOPs: dev-sop.md, git-sop.md
π Migration Notes
- β’ **Phase 1 complete**: Runtime swap to Bun while keeping Vite bundler
- β’ **Phase 2 evaluation**: Consider Bun.build() in 6-12 months when manual chunk splitting is supported
- β’ Rollback instructions included in Dockerfile comments
v2.14.2 2025-12-04
π Security
- β’ **[CRITICAL] Fixed Server-Side Request Forgery (SSRF) vulnerabilities (CWE-918)** β Created comprehensive URL validation utility (
backend/app/utils/url_validation.py) β Fixed SSRF in OIDC service (backend/app/services/oidc.py:100) - prevents access to internal services β Fixed SSRF in NHTSA service (backend/app/services/nhtsa.py:48) - validates API URLs β Protection includes: blocks private IPs (RFC 1918, RFC 4193), loopback, link-local, AWS metadata endpoint β DNS rebinding protection and domain allowlisting support β All HTTP requests to external services now validated - β’ **[HIGH] Fixed Log Injection vulnerabilities (CWE-117) - 200+ instances across 44 files** β Converted all f-string logging to parameterized logging format β Prevents log forgery attacks via newline injection β Created automated remediation tool (
fix_log_injection.py) β Affected files: all routes/, services/, utils/, migrations/, and core modules - β’ **[HIGH] Fixed Secret Exposure in Logs** β Created
mask_secret()function to safely log sensitive values β Fixed 4 instances of OIDC client secret exposure in logs β Secrets now show only first/last 4 chars (e.g.,oidc_****...****_abcd) - β’ **[HIGH] Fixed Path Injection vulnerabilities (CWE-22)** β Added defense-in-depth path validation in photo deletion (
backend/app/routes/photos.py:250,259) β Validates resolved paths are within PHOTO_DIR to prevent traversal attacks β Enhanced withvalidate_path_within_base()security checks - β’ **[MEDIUM] Fixed postMessage Origin Validation (CWE-20291)** β Added strict same-origin validation in service worker (
frontend/public/sw.js:147) β Prevents XSS and message spoofing from unauthorized origins β Rejects messages with console warning for security monitoring
π Changed
- β’ **Exception Handling** - Verified stack trace exposure properly handled β Production mode (default): Generic error messages only, no internal details β Debug mode: Detailed traces for development only β Error handlers in
backend/app/utils/error_handlers.pyprovide secure responses
β¨ Added
- β’ **New Security Utilities** β
backend/app/utils/url_validation.py- Comprehensive SSRF protection (447 lines) βbackend/app/exceptions.py- AddedSSRFProtectionErrorexception class βfix_log_injection.py- Automated log injection remediation script
π Fixed
- β’ **Code Quality Improvements** - Resolved 101 CodeQL NOTE-level alerts β Removed 59 unused imports from 39 Python files (automated) β Added explanatory comments to 8 empty except blocks (optional dependency checks) β Renamed 9 unused local variables to
_for intentionally unused values β Fixed useless comparison in frontend user count display β Documented 3 Pydantic validator false positives (requireclsparameter) β Documented 3 pytest.skip false positives (raises exception, never returns None)
π Documentation
- β’ **SECURITY.md** - Added comprehensive CodeQL Security Analysis section β Documented all 140 fixed vulnerabilities (2 CRITICAL, 119 HIGH, 1 MEDIUM) β Documented 17 false positives with justification β Listed 136 deferred code quality items (NOTE level) β Updated security changelog for v2.14.2
- β’ **Cyclic Imports** - Documented 47 cyclic import alerts for future architectural refactoring β Saved to
/srv/raid0/docker/documents/history/mygarage/2025-12-04-cyclic-imports-deferred.txtβ Includes recommended fixes (TYPE_CHECKING, dependency injection, lazy imports)
π Technical Notes
- β’ All security and code quality fixes are backward compatible
- β’ No API changes or breaking changes
- β’ Total files modified: 86 (47 security + 39 code quality)
- β’ CodeQL analysis: 241/272 alerts resolved (140 security + 101 code quality)
- β’ Remaining 47 alerts are cyclic imports (architectural issue, deferred to refactoring sprint)
v2.14.1 2025-12-03
β¨ Added
- β’ **Single-Source-of-Truth Version Management** β Backend now reads version from
pyproject.tomlautomatically at runtime β Addedget_version()function using Python's built-intomllibparser β Version bumps now only require updating 2 files instead of 3 β Eliminates version drift between config.py and pyproject.toml β Updated Dockerfile to copypyproject.tomlinto production image
π Changed
- β’ **Zod v4 API Migration** - Updated all validation schemas to use Zod v4 API patterns β Removed deprecated
required_errorandinvalid_type_errorparameters from schemas β Simplified error messages using singlemessageparameter β Updated z.enumerrorMapsyntax to newmessageformat β Removed unnecessaryz.preprocess()wrappers that were causing type inference issues β React Hook Form's zodResolver automatically handles empty string β undefined conversion - β’ **Form Type Safety Improvements** β Fixed defaultValues type mismatches across 15+ form components β Changed numeric field defaults from
.toString() || ''to?? undefinedpattern β Fixed boolean field defaults using??instead of||to preserve explicit false values β Improved type inference for all form schemas (now return proper types instead ofunknown) - β’ **Test Infrastructure Updates** β Changed
globaltoglobalThisfor Node.js/browser compatibility in test setup β Removed unused imports and variables across test files
π Fixed
- β’ **TypeScript Compilation Errors** - Resolved 100+ TypeScript errors caused by Zod v4 API changes β Fixed all schema validation patterns to match Zod v4 requirements β Fixed form component type mismatches for numeric and boolean fields β Fixed null safety issues in title length checks and property access β Removed unused imports and watch variables flagged by TypeScript strict mode
π Dependencies
- β’ **Frontend**: Updated jsdom from 25.0.1 to 27.2.0 (Dependabot security update)
π Technical Notes
- β’ All changes are backward compatible - no validation rules or API contracts changed
- β’ Build passes successfully with Vite
- β’ All 28 unit tests passing
- β’ 49 non-blocking TypeScript warnings remain (type inference cascades from resolver types)
v2.14.0 2025-12-01
β¨ Added
- β’ **Multi-User Management System** β Database setting
multi_user_enabledto control user creation (default: false) β Backend enforcement: blocks user creation when multi-user mode is disabled β Admin password reset endpoint (PUT /auth/users/{id}/password) for local auth users only β Multi-User Management card in Settings > System (admin-only, local auth only) β Toggle switch to enable/disable multi-user mode β User preview showing first 3 users with avatars β "Add User" button to create new accounts β "Manage All Users" button to access full user management interface β User Management modal with: β Searchable user table (by username, email, or full name) β Role badges (Admin/User) β Status badges (Active/Inactive) β Auth method badges (OIDC/Local) β Edit user details β Reset password (local users only) β Enable/disable user accounts β Delete users (cannot delete yourself) β Add/Edit User modal with: β Username field (disabled in edit mode) β Email field (required) β Full name field (optional) β Password fields with strength indicator β Password visibility toggles β Role selector (Admin/User) β Active status checkbox β OIDC user badge (when applicable) β Delete User modal with: β User information display β Data impact warnings (vehicles, service records, fuel records) β Type "DELETE" confirmation requirement β Admin badge warning for admin users β Security safeguards: β Last admin protection: cannot disable the only active admin β Last admin protection: cannot change role of the only active admin β Self-deletion prevention: users cannot delete their own account β Warning tooltips for disabled actions β Confirmation dialogs for destructive operations
π Changed
- β’ Settings > System page now uses two-column CSS Grid layout: β Left column: System Configuration + Multi-User Management β Right column: Authentication Mode + Change Password
π Fixed
- β’ Button styling consistency across multi-user management components: β Change Password button now uses correct theme (
bg-gray-700 border border-gray-600) β Create/Update button in Add/Edit User modal now uses correct theme β All buttons now match the application's standard gray button style
v2.13.0 2025-12-01
β¨ Added
- β’ **OIDC Username-Based Account Linking with Password Verification** β Prevents duplicate account creation (username1, username2, etc.) during OIDC login β When username matches but email differs, users are prompted to verify their password β New database table
oidc_pending_linksfor temporary link tokens (migration 015) β New frontend page/auth/link-accountfor password verification β Security features: β Token expiration: 5 minutes (configurable viaoidc_link_token_expire_minutes) β Max password attempts: 3 (configurable viaoidc_link_max_password_attempts) β Rate limiting: 5 requests/minute on link endpoint β One-time use tokens (deleted after successful link) β Comprehensive audit logging (success and failure) β Edge case handling: β Token expiration with user-friendly error messages β Maximum attempt lockout β OIDC-only user detection (no password) β Conflict prevention (already linked to different provider) β Inactive user checks β Backward compatible with existing OIDC flows (email-based linking still works) β Files added: βbackend/app/exceptions.py- PendingLinkRequiredException βbackend/app/models/oidc_pending_link.py- Pending link model βbackend/app/migrations/015_add_oidc_pending_links.py- Database migration βfrontend/src/pages/LinkAccount.tsx- Password verification UI β Files modified: βbackend/app/services/settings_init.py- Added 2 new settings βbackend/app/services/oidc.py- Added 3 helper functions, modified user creation logic βbackend/app/routes/oidc.py- Modified callback handler, added/link-accountendpoint βfrontend/src/App.tsx- Added route for link account page
v2.13.0 2025-11-30
β¨ Added
- β’ **Code Quality Refactoring (Phase 2)** β Complete service layer architecture for business logic separation β
VehicleService(226 lines) - Vehicle CRUD operations with integrated authorization βServiceRecordService(366 lines) - Service record management with N+1 query optimization βFuelRecordService(486 lines) - Fuel tracking with MPG calculations and caching βPhotoService(179 lines) - Photo management and thumbnail generation β Photo management extracted to dedicated router (/app/routes/photos.py, 448 lines, 7 endpoints) β Average MPG calculation now cached (5-minute TTL) for performance β Legacy photo hydration moved to one-time migration script (removed from request hot path) β Database migration 014:014_hydrate_legacy_photos.pyfor one-time photo metadata population - β’ **Authentication Mode 'None' Implementation** β Support for running application without authentication in development environments β Frontend centralized auth_mode state in AuthContext (single API call to
/settings/public) β Smart authentication dependencies checkauth_modesetting before enforcing βauth_mode='none'allows guest access (user = None) with full permissions β Settings UI allows changing Authentication Mode to "None" with security warnings β Frontend ProtectedRoute respectsauth_modefrom context (no duplicate API calls)
π Changed
- β’ **Massive Code Reduction and Organization (Phase 2)** β
vehicles.py: 1,002 β 316 lines (69% reduction) βservice.py: 404 β 185 lines (54% reduction) βfuel.py: 487 β 165 lines (66% reduction) β Total: 1,227 lines removed from route files (average 63% reduction) β Route handlers now focused purely on HTTP concerns, business logic in service layer β Removed redundant_sanitize_filenamefunction (using centralized utils version with better validation) β Consolidated duplicate VIN decode endpoints with shared_decode_vin_helper()function β All photo endpoints maintain backward compatibility with authorization checks in place - β’ **Authentication Architecture Updates** β
require_auth()now checksauth_modesetting: returns None when disabled, enforces when enabled βget_current_admin_user()checksauth_modefirst: returns None when disabled (allows all access) β All helper functions acceptOptional[User]for type safety with null checks βget_vehicle_or_403()andcheck_vehicle_ownership()handle None users (grant full access) β Vehicle/Service/Fuel service layers acceptOptional[User], show all data when user is None β Settings endpoints split:/api/settings/public(no auth) vs/api/settings(admin only) β All 100+ endpoints now work seamlessly withauth_mode='none'
π Fixed
- β’ **Code Quality Improvements (Phase 3 - 71% reduction in linting issues)** β Fixed F821 (undefined name): Added missing
Documentimport in vehicle.py:187 β Fixed E722 (bare except): Replaced with specificValueErrorin insurance.py:129 β Auto-fixed 128 actionable issues via ruff (unused imports, empty f-strings, boolean comparisons, unused variables) β Reduced total ruff issues from 181 β 53 (71% reduction) β Remaining 53 issues are intentional design patterns (documented inpyproject.toml) β E402 (46 issues): Imports after code for FastAPI initialization order and circular dependency resolution β F401 (7 issues): Unused imports in try/except blocks for optional dependency checks β Added comprehensive ruff configuration with per-file ignores - β’ **Authentication Flow Improvements** β Fixed CSRF token endpoint to work without authentication when
auth_mode='none'β Fixed ProtectedRoute and Layout to use/settings/publicinstead of admin-only endpoint β Eliminated ERROR logs ("No credentials provided") on page load before authentication β CSRF endpoint now usesoptional_authdependency, returns{"csrf_token": None}when disabled β Frontend no longer makes duplicate API calls to check auth_mode (centralized in AuthContext) β Resolved infinite loop issue (3 components independently calling/settings/publicβ 200+ requests) - β’ **Frontend Validation Error Serialization** β Fixed
TypeError: Object of type ValueError is not JSON serializablein error handlers β Validation errors now properly converted to JSON-serializable format before response - β’ **Auth Mode 'None' Backend Validation** β Removed overly restrictive validation blocking
auth_mode='none'changes (kept warning logs only) β Fixed AttributeError crashes from None user references in authorization helpers β Fixed 500 errors when accessing endpoints withauth_mode='none'enabled β Settings page now accessible without authentication when auth is disabled
π Security
- β’ **CRITICAL: Authentication & Authorization Hardening (Phase 1)** β All vehicle data endpoints now require authentication via
require_authdependency β Implemented per-vehicle authorization - users can only access their own vehicles β Addeduser_idcolumn to vehicles table with foreign key to users (database migration 013) β Admin users retain access to all vehicles for support purposes β Production safeguard:auth_mode='none'blocked in production without explicitMYGARAGE_ALLOW_AUTH_NONE=trueflag β Startup warning displayed whenauth_mode='none'is active β Authentication dependencies:require_auth()(smart enforcement) vsoptional_auth()(never enforces) β Authorization helpers:get_vehicle_or_403(),check_vehicle_ownership()with None user support β 24+ endpoints hardened: vehicles, service records, fuel records, photos, settings β Public settings endpoint (/api/settings/public) works without authentication for frontend initialization β Prevents unauthenticated data access and cross-user data leakage in production β Allows development without authentication when explicitly configured - β’ **Dependency Security Validation (Phase 3)** β Zero vulnerabilities found in 79 scanned packages (Safety v3.7.0) β All dependencies up-to-date with no known CVEs β Key packages verified: fastapi 0.123.0, sqlalchemy 2.0.29, pillow 12.0.0, argon2-cffi 25.1.0
- β’ **Code Security Validation (Phase 3)** β Bandit scan: Only 2 findings, both acceptable design choices β 0.0.0.0 binding (required for Docker container networking) β MD5 for cache keys (non-cryptographic use case with
usedforsecurity=False) β No actual security vulnerabilities detected in 41,554 lines of code β Strong security posture validated by automated scanning
β‘ Performance
- β’ **Service Layer Optimizations** β MPG calculation now cached with 5-minute TTL (automatic invalidation on data changes) β Photo hydration removed from request hot path (one-time migration instead) β N+1 query optimizations in ServiceRecordService (pre-fetches attachment counts via JOIN) β Reduced code size improves application load time and memory footprint
- β’ **Authentication Flow Optimization** β Single API call to check
auth_modeinstead of 3 duplicate calls β Resolved rate limit issues (200+ requests to/settings/publicβ 1 request) β Centralized state management prevents redundant network requests
π Technical Notes
- β’ **Service Layer Architecture**: Implements dependency injection, integrated authorization, complete business logic separation from HTTP layer
- β’ **Type Safety**: All functions use
Optional[User]to force explicit null handling throughout codebase - β’ **Smart Authentication**: Functions check
auth_modesetting dynamically - no hardcoded auth bypass logic - β’ **Graceful Degradation**: None users represent guest access with full permissions when auth is disabled
- β’ **Code Quality**: 71% reduction in linting issues, 69% reduction in main route file, production-ready code
- β’ **Backward Compatibility**: All API contracts maintained, photo endpoints work identically after extraction
- β’ Database migrations: 013 (user_id for multi-user support), 014 (legacy photo hydration)
v2.12.0 2025-11-28
β¨ Added
- β’ **Multi-Service Notification System** - Expanded from ntfy-only to 7 notification providers β **ntfy** - Self-hosted push notifications with optional token authentication β **Gotify** - Self-hosted push notification server β **Pushover** - iOS/Android push notifications β **Slack** - Team channel notifications via webhooks β **Discord** - Discord channel notifications via webhooks β **Telegram** - Bot-based notifications β **Email** - SMTP-based email notifications (with STARTTLS support) β Unified NotificationDispatcher with priority-based retry logic β Per-service test endpoints (
/api/notifications/test/{service}) β Configurable retry attempts and delays with service-specific multipliers β Event-type toggles: recalls, service due/overdue, insurance/warranty expiring, milestones - β’ **Frontend Notification Configuration UI** β Sub-tab navigation for switching between notification providers β Individual configuration forms for each service with enable toggle, credentials, and test button β Green dot indicators showing which services are enabled β Unified Event Notifications card with expandable sections β Advance warning day configuration for insurance, warranty, and service reminders β Two-column responsive layout (service config + event settings)
π Changed
- β’ Backend notification architecture refactored to abstract base class pattern
- β’ Settings system expanded with 24 new notification-related keys
- β’ Notification services use async HTTP (httpx) and async SMTP (aiosmtplib)
v2.11.0 2025-11-26
β¨ Added
- β’ **Frontend HTTP Error Handler** - New utility for consistent error message handling β
httpErrorHandler.tsmaps HTTP status codes to user-friendly messages βparseApiError()- Full error parsing with status, message, retry hints βgetErrorMessage()- Simple error message extraction βgetActionErrorMessage()- Context-aware messages ("Failed to save...") β Re-exported fromapi.tsfor convenient access throughout frontend
π Changed
- β’ **Error Handling Standardization** - Refactored generic exception handlers to use specific exception types β Reduced generic
except Exception as e:handlers from ~120 to ~71 (40% reduction) β API routes now use specific exceptions:IntegrityError,OperationalError,httpx.*,FileNotFoundError, etc. β Improved HTTP status codes: 409 for conflicts, 503 for database unavailable, 504 for timeouts β Better error messages that don't expose internal details β Remaining generic handlers are intentional fallbacks (CSV import rows, OCR, migrations)
π Fixed
- β’ **Backup Creation Logout Bug** - Fixed issue where creating backups would log users out β Exempted
/api/backup/*routes from CSRF protection (already protected by JWT authentication) β Backup endpoints are idempotent with no user input, making CSRF protection redundant β Added CSRF token storage validation to catch sessionStorage failures early β Added console warnings when CSRF tokens are missing on state-changing requests β Removed duplicate CSRF token cleanup from middleware (performance optimization)
v2.10.0 2025-11-23
π Security
- β’ **CRITICAL: CSRF Protection** - Implemented synchronizer token pattern for cross-site request forgery protection β Added
csrf_tokensdatabase table with 24-hour token expiration β CSRF tokens automatically generated on login (both local and OIDC) β Middleware validates CSRF tokens on all state-changing operations (POST/PUT/PATCH/DELETE) β Tokens returned in login response for frontend integration β Automatic cleanup of expired tokens on logout and login - β’ **CRITICAL: Settings Endpoint Security** - Fixed privilege escalation vulnerability β **BREAKING**: Split settings endpoints -
/api/settings/public(no auth) for initialization,/api/settings(admin-only) for management β All settings CRUD operations now require admin privileges (get_current_admin_user) β Public endpoint returns only whitelisted settings:auth_mode,app_name,themeβ Prevents unauthorized users from reading/modifying sensitive configuration (OIDC secrets, SMTP credentials, etc.) - β’ **HIGH: JWT Cookie Security** - Auto-detect secure cookie flag based on environment β
jwt_cookie_securenow auto-detects:Secure=truein production (debug=false),Secure=falsein development β Prevents session token exposure over unencrypted HTTP in production β Explicit override available viaJWT_COOKIE_SECUREenvironment variable β Default changed fromfalseto environment-aware - β’ **MEDIUM: OIDC State Persistence** - Database-backed state storage for multi-worker reliability β Added
oidc_statesdatabase table with 10-minute expiration β Replaces in-memory dictionary storage β Supports multi-worker deployments and container restarts during authentication flows β State validation and one-time-use enforcement via database - β’ **LOW: SQLite Pool Configuration** - Conditional pool settings for database compatibility β Pool configuration now only applied to PostgreSQL/MySQL β SQLite uses appropriate NullPool automatically β Prevents future SQLAlchemy compatibility issues
π Changed
- β’ **Database Migration 012**: Added
csrf_tokensandoidc_statestables with indexes - β’ CORS middleware now allows
X-CSRF-Tokenheader - β’ Login and logout endpoints updated to manage CSRF tokens
- β’ OIDC callback endpoint updated to generate CSRF tokens
- β’ Settings routes refactored for public/admin separation
π Technical Notes
- β’ Frontend integration required: Store CSRF token from login response, send in
X-CSRF-Tokenheader for mutations - β’ Addresses Codex security audit findings: HIGH and MEDIUM risk items resolved
- β’ Version bump: 2.8.0 β 2.10.0 (skipped 2.9.0 to align with frontend)
v2.8.0 2025-11-23
β¨ Added
- β’ **Garage Analytics Enhancements** β CSV export functionality for garage-wide data analysis β PDF export with professional garage report generation β Garage Analytics Help Modal with comprehensive feature documentation β Rolling average trend lines (3-month and 6-month) on monthly spending chart β Visual spending trend analysis with smooth overlay indicators
- β’ **Individual Vehicle Analytics Enhancements** β CSV export for vehicle-specific analytics data β PDF export with detailed vehicle reports β Export functionality mirrors garage analytics capabilities β Consistent export button styling across both analytics pages
π Changed
- β’ Standardized export button UI across Garage and Vehicle Analytics pages
- β’ Updated button styling to use garage theme colors for consistency
- β’ Removed "Export" prefix from button labels (now just "CSV" and "PDF")
π Technical Notes
- β’ Added
garage-primary,garage-primary-dark,success, anddangercolor classes to Tailwind theme - β’ Frontend analytics pages now fully support data export workflows
- β’ Export buttons use consistent
bg-garage-surfacestyling with theme-aware hover states
v2.7.0 2025-11-23
β¨ Added
- β’ **OpenID Connect (OIDC) / SSO Authentication** β Complete OIDC authentication integration with support for external identity providers (Authentik, Keycloak, etc.) β "Sign in with SSO" button on login page with dynamic provider name display β OIDC callback success page with automatic token handling and redirect β Email-based account linking - automatically links OIDC accounts to existing local accounts via verified email β Dual authentication support - users can login with either password OR OIDC after linking β Admin UI for OIDC configuration in Settings β System β OIDC tab β Provider configuration: Issuer URL, Client ID/Secret, Scopes β Auto-generated redirect URI display β Test connection functionality with detailed result feedback β Claim mapping configuration (username, email, full name) β Group-based admin role mapping β Authentik setup guide with step-by-step instructions β Database schema additions:
oidc_subject,oidc_provider,auth_methodfields on User model β 12 new OIDC settings with defaults and validation β/api/auth/oidc/config- Public OIDC configuration endpoint β/api/auth/oidc/login- OIDC flow initiation endpoint β/api/auth/oidc/callback- Provider callback handler β/api/auth/oidc/test- Admin-only connection testing endpoint
π Security
- β’ **OIDC Security Features** β CSRF protection via state parameter validation (10-minute expiration) β Replay attack protection via nonce validation in ID tokens β JWT signature verification using provider's JWKS public keys β Issuer claim validation (prevents token reuse from other providers) β Audience claim validation (ensures tokens issued for MyGarage) β Expiration validation on all tokens β NULL password protection - OIDC-only users cannot authenticate via password login β Made
hashed_passwordcolumn nullable to support OIDC-only users (migration 011)
π Changed
- β’ Authentication system now supports multiple auth methods (local password + OIDC)
- β’ User model
hashed_passwordfield is now nullable (OIDC-only users have NULL password) - β’ Login page conditionally displays SSO button based on OIDC configuration
π Dependencies
- β’ **Backend**: Added
authlib>=1.6.5for OIDC/OAuth2 authentication
π Technical Notes
- β’ Backend implementation: 532-line OIDC service with complete OAuth2 flow
- β’ Frontend implementation: OIDC success page, login page SSO integration, settings UI
- β’ Database migration 011 applied to support OIDC fields
v2.6.0 2025-11-22
β¨ Added
- β’ **Light/Dark Theme System** β User-selectable theme toggle in Settings β System tab β Comprehensive light theme for all pages and components β React Big Calendar fully themed for both light and dark modes β Theme preference persisted in both localStorage (instant) and database (cross-device sync) β ThemeContext provider for global theme state management β Sun/Moon icon toggle UI with visual active state indication β Default theme remains dark mode for existing users β Tailwind v4 CSS variable architecture for clean theme switching β Refactored 48+ components to use semantic theme-aware classes β Light mode color palette: white cards (#ffffff) on light gray background (#f3f4f6) β Dark mode color palette: slate cards (#1a1f28) on dark background (#0a0e14)
π Fixed
- β’ **Light Mode Styling Issues** β Removed 200+ hardcoded dark gray button styles (
bg-gray-700) across all components β Replaced with semantic.btn-primaryclass that adapts to both themes β Fixed modal overlays being too harsh in light mode (50% β 30% opacity) β Fixed badge colors not adapting to light mode background β Fixed text contrast issues with hardcoded gray colors β Corrected CSS architecture to properly use Tailwind v4@themedirective β Eliminated redundant CSS variable overrides β Removed all!importanthacks - proper specificity through CSS layers
π Security
- β’ **Password Hashing Migration: Bcrypt β Argon2** β Migrated from bcrypt 5.0.0 to Argon2id (argon2-cffi 25.1.0) β Argon2id is the current OWASP recommended password hashing algorithm β Hybrid verification system supports both legacy bcrypt and new Argon2 hashes β Auto-rehashing: User passwords transparently upgraded to Argon2 on next login β No password resets required - zero downtime migration β Removed 72-byte password length limitation (bcrypt restriction) β Argon2 parameters: time_cost=2, memory_cost=102400 (100MB), parallelism=8 β Migration tracking via automated database migration system (migration 010) β bcrypt temporarily retained for gradual migration support
π Changed
- β’ Tailwind CSS dark mode enabled via class-based switching
- β’ Theme preference stored in global settings table with category 'general'
- β’ CSS architecture updated to support dynamic theme switching via CSS variables
v2.5.2 2025-11-22
π Changed
- β’ **Automated Database Migration System** β Migrations now run automatically on container startup β Added
schema_migrationstracking table β Renamed migration files with numeric prefixes for ordering β Extracted inline migrations from database.py to standalone files β Prevents schema drift between development and production β No manual migration execution required after deployments
π Fixed
- β’ Database migration system now prevents schema mismatch issues
- β’ Migration tracking persists across container restarts
v2.5.1 2025-11-22
π Security
- β’ **CRITICAL: Fixed default authentication mode** β Changed default
auth_modefromnonetolocalto require authentication by default β Previously, all endpoints were publicly accessible out-of-the-box until manually configured β New instances now require authentication immediately after first admin setup - β’ **CRITICAL: Fixed rate limiting enforcement** β Wired up SlowAPI middleware to actually enforce rate limits β Previously, rate limit decorators were no-ops due to missing middleware β Auth endpoints now properly rate-limited at 5 requests/minute to prevent brute-force attacks β Upload endpoints now properly rate-limited at 20 requests/minute to prevent DoS β Default global rate limit of 200 requests/minute now enforced
- β’ **CRITICAL: Fixed open user registration** β Registration endpoint now restricted to first user only β After first admin is created, public registration is disabled β Added new admin-only
/api/auth/usersPOST endpoint for admins to create accounts β New users created by admins default to inactive and non-admin status β Prevents unauthorized account creation on public instances
π Changed
- β’ User registration flow: Only first user can self-register (becomes admin)
- β’ Subsequent users must be created by administrators through user management UI
- β’ New users require admin activation before they can log in
v2.5.0 2025-11-22
β¨ Added
- β’ **Zod + React-Hook-Form Integration** β Implemented declarative form validation using Zod v4 schemas β Created reusable schema infrastructure in
/frontend/src/schemas/βshared.ts: Common validators (mileage, currency, dates, etc.) βauth.ts: Authentication forms with password strength validation βfuel.ts: Fuel record validation βservice.ts: Service record validation with type enum βreminder.ts: Conditional validation (date OR mileage required) β AddedFormErrorcomponent for field-level error display β Migrated Register and Login forms to use react-hook-form with zodResolver β Real-time validation with field-specific error messages β Password strength indicator in registration form
π Changed
- β’ **Dependency Updates** β Updated zod: 3.24.0 β 4.1.12 β Updated @hookform/resolvers: 3.9.0 β 5.2.2 β Updated react-hook-form: 7.54.0 β 7.61.1 β Updated axios: 1.7.0 β 1.13.2 β Updated lucide-react: 0.553.0 β 0.554.0 β Updated @types/react: 19.0.6 β 19.2.6 β Updated @types/react-big-calendar: 1.8.12 β 1.16.3 β Updated @types/react-dom: 19.0.2 β 19.2.3 β Updated @typescript-eslint packages: 8.46.4 β 8.47.0 β Updated vite: 7.2.2 β 7.2.4
π Fixed
- β’ **Critical: Password Validation Mismatch** β Fixed frontend password validation to match backend requirements β Frontend now validates: uppercase, lowercase, digit, special character (!@#$...) β Previously only checked length β₯ 8, causing confusing backend rejection errors β Users now get immediate, clear feedback about password requirements
v2.4.0 2025-11-21
β¨ Added
- β’ **Unified Document Scanner with Multi-Provider Insurance Support** β Consolidated PDF/image scanning architecture for all document types β Insurance documents now use same OCR engine as window stickers (PaddleOCR + Tesseract) β Auto-detection of insurance providers from document content β Provider-specific parsers: Progressive, State Farm, GEICO, Allstate β Generic fallback parser for unknown providers β Image upload support for insurance documents (jpg, png) in addition to PDF β New
/api/insurance/parsersendpoint to list available parsers and OCR status β New/api/vehicles/{vin}/insurance/test-parseendpoint for debugging extraction β Confidence scoring (0-100%) for insurance extraction β Per-field confidence levels (high/medium/low) β Optionalproviderquery parameter to hint parser selection - β’ **Window Sticker OCR Display Enhancement** β Added display of all OCR-extracted fields that were previously stored but not shown β New Standard Equipment card (collapsible) showing categorized standard features β New Optional Equipment card (collapsible) with pricing from
window_sticker_options_detailβ New Packages card showing package groupings with prices β OCR metadata display (parser used, confidence score, VIN verification) β Drivetrain field now displayed in Powertrain card
ποΈ Removed
- β’ **pdfplumber dependency** β Removed unused pdfplumber library (PyMuPDF handles all PDF operations) β Reduces container size and maintenance burden
π Fixed
- β’ **Window Sticker Data Display Gap** β Fixed 8 OCR-extracted fields not being rendered in frontend despite being stored in database β Fields now displayed:
standard_equipment,optional_equipment,window_sticker_options_detail,window_sticker_packages,sticker_drivetrain,window_sticker_parser_used,window_sticker_confidence_score,window_sticker_extracted_vin - β’ **Stellantis OCR Parser Fixes** β Fixed environmental ratings extraction (GHG/Smog) - now correctly identifies actual ratings vs scale markers β Fixed equipment categorization - optional package items no longer appear under standard equipment β Fixed confidence score display (was showing 9500% instead of 95%)
v2.3.1 2025-11-19
π Changed
- β’ **Frontend JWT Authentication Migration** β Migrated 35 components from direct
fetch()calls to centralized axios API client β All API requests now automatically includeAuthorization: Bearer <token>header β Consistent error handling with automatic logout/redirect on 401 errors β Improved type safety and code maintainability
π Fixed
- β’ **Authentication consistency** β Eliminated "No credentials provided" errors from components bypassing auth β Fixed JWT token not being sent with dashboard, settings, form, and page requests β Corrected authentication flow in backup/restore operations β Fixed file upload/download endpoints to properly use axios with FormData and blob responses β Fixed vehicle import/export JSON functionality
π Technical Details
- β’ Updated components (35 total): β Pages: Dashboard, Register, VehicleDetail, VehicleEdit β Settings tabs: System, Files, Integrations, Notifications, Backup, AddressBook β Forms: ServiceRecord, TollTag, TollTransaction, TaxRecord, SpotRental β Uploads: PhotoUpload, WindowStickerUpload β Lists: TollTagList, TollTransactionList, TaxRecordList, SpotRentalList β Tabs: TollsTab β Utilities: AddressBookSelect, AddressBookAutocomplete, ReportsPanel, ProtectedRoute β Hooks: useAppVersion
- β’ All file downloads now use
responseType: 'blob'with axios - β’ FormData uploads work seamlessly without additional configuration
- β’ Updated fallback version in useAppVersion to 2.3.1
v2.3.0 2025-11-15
β¨ Added
- β’ **Propane tracking for fifth wheel vehicles** β Added
propane_gallonsfield to fuel records (Numeric 8,3 precision) β New propane input field in fuel record form β Propane column in fuel record list view β Automatic database migration on startup β Input validation (0-999.999 gallons, 3 decimal places) - β’ **Security improvements** (10 major fixes) β Path traversal protection for document uploads β VIN pattern validation (17-character alphanumeric) β SQL injection prevention via parameterized queries β MIME type validation for file uploads (PDF, images) β File size limits (10MB for images, 50MB for PDFs) β Password length limits (72 bytes for bcrypt compatibility) β Email format validation (max 254 characters) β User input sanitization across all endpoints β Rate limiting headers exposed in CORS configuration β Comprehensive error handling with proper HTTP status codes
π Fixed
- β’ **Critical:** Fixed fifth wheel fuel tab access β Corrected boolean operator precedence in vehicle type check β Fifth wheels can now properly access fuel tracking features
- β’ **Security:** Prevented path traversal in document downloads β Added strict filename validation β Restricted access to user-owned documents only
- β’ **Security:** Added MIME type validation for uploads β Prevents execution of malicious files β Validates against allowed types (PDF, JPG, PNG, HEIC, etc.)
- β’ **Security:** Implemented file size limits β Images: 10MB maximum β PDFs: 50MB maximum β Prevents DoS attacks via large file uploads
- β’ Input validation edge cases across multiple endpoints β Maintenance records: validated mileage, date ranges β Fuel records: validated amounts, prices, odometer readings β Documents: validated descriptions, file metadata β Settings: validated configuration values
π Changed
- β’ Updated version to 2.3.0 (MINOR bump for propane feature)
- β’ Enhanced fuel record form layout (3-column grid)
- β’ Improved About page organization and statistics
v2.2.1 2025-11-15
π Fixed
- β’ **Critical:** Removed non-functional token refresh logic β Eliminated dead code calling non-existent
/api/auth/refreshendpoint β Simplified authentication flow β Reduced unnecessary API calls - β’ **Critical:** Fixed React hooks compliance violations β Added proper
useCallbackwrappers in AuthContext β Fixed PhotoGallery dependencies β Corrected Calendar.tsx hook dependencies β Removed alleslint-disablecomments for hooks - β’ Removed 11 production console.log statements β Kept only PWA-related debug logs β Cleaner console output
β‘ Performance
- β’ **Added React.memo to 17 expensive components** β VehicleCard, PhotoGallery, ReminderCard, MaintenanceRecordItem β DocumentCard, FuelCard, FuelRecordList, MaintenanceRecordList β DocumentList, ReminderList, VehicleList, TabContent components β Analytics charts and reports components β Reduces unnecessary re-renders β Improved list/grid rendering performance
- β’ Optimized component re-rendering patterns
π Improved
- β’ **Code quality** - Better React patterns and hooks compliance
- β’ **Developer experience** - No more lint warnings
- β’ **Production logs** - Reduced noise, better signal
v2.2.0 2025-11-15
π Changed
- β’ **MAJOR:** Migrated from Uvicorn to Granian ASGI server β **+11% requests/sec** (45,000 β 50,000) β **-25% memory usage** (20MB β 15MB per worker) β More consistent latency (2.8x max/avg vs 6.8x) β Single worker mode for APScheduler compatibility
- β’ **MAJOR:** Removed deprecated libraries β Removed moment.js (~232KB), replaced with date-fns (~78KB) - **-154KB** β Removed unused chart.js and react-chartjs-2 - **-200KB** β Total bundle savings: **~350KB**
- β’ **MAJOR:** Implemented frontend code splitting β Route-based lazy loading for all pages β Manual chunk configuration (react-vendor, charts, calendar, ui, forms, utils) β **-78% initial bundle size** (~900KB β ~200KB) β **-60% time to interactive** (~2.5s β <1s)
- β’ **MAJOR:** Migrated to @vitejs/plugin-react-swc β Faster builds using SWC instead of Babel β Better development experience
- β’ Fixed Tailwind v4 PostCSS configuration β Created
postcss.config.jswith @tailwindcss/postcss plugin β Added autoprefixer support β Simplified tailwind.config.js (theme moved to CSS)
β¨ Added
- β’ Created
pyproject.tomlfor modern Python packaging β Version management in single source of truth β Dev dependencies separated (pytest, ruff) β Better tooling support - β’ **Health check logging filter** β Suppresses Docker health check logs from access logger β Reduces log noise while preserving API request visibility β Applied to Granian access logger
π Security
- β’ **bcrypt v5.0 password validation** β Added password length checks (max 72 bytes) β Prevents silent truncation vulnerability β Returns clear error for invalid passwords
π Updated
- β’ **Backend dependencies:** β FastAPI: 0.121.0 β 0.121.1 β APScheduler: 3.10.4 β 3.11.1 β Pydantic: 2.12.0 β 2.12.3 β Pillow: 11.0.0 β 12.0.0 (Python 3.14 support) β Added Granian: 2.5.7
- β’ **Frontend dependencies:** β lucide-react: 0.468.0 β 0.553.0 β react-router-dom: 7.1.1 β 7.9.6 β recharts: 3.3.0 β 3.4.1 β TypeScript: 5.6.2 β 5.9.3 β @types/react: 19.0.0 β 19.0.6 β @types/react-dom: 19.0.0 β 19.0.2 β Added date-fns: 4.1.0 β Added @tailwindcss/postcss: 4.1.17 β Added autoprefixer: 10.4.20
- β’ **v2.1.0:** Authentication UI redesign, dependency updates (Tailwind v4, Vite 7), security improvements, zero-config
- β’ **v2.0.0:** Backup system, service consolidation, enhanced features
- β’ **v1.x:** Initial development phases (7 major phases + 12 feature phases)
Key Features
Vehicle Profiles
Add vehicles by VIN and get automatic decoding via NHTSA. Stores make, model, year, engine, transmission, and more.
Service Records
Log oil changes, repairs, tire rotationsβanything. Attach receipts, set the mileage, track costs. Full history per vehicle.
Fuel Tracking
Dedicated Fuel tab with fill-ups, DEF, and propane. Automatic MPG calculations including partial fill-ups, an optional rebate field for points and cash back, and separate tracking for towing/hauling to see how loads affect efficiency.
Multi-Service Notifications
8 notification providers: ntfy, Gotify, Pushover, Slack, Discord, Telegram, Matrix, and Email. Get alerts for maintenance, recalls, and expirations.
Recall Checking
Automatic NHTSA recall lookup by VIN. See active recalls, completion status, and links to CarComplaints for known issues.
OCR Receipt Scanning
Upload a photo of a receipt and Tesseract OCR extracts the vendor, date, amount, and line items automatically.
Analytics Dashboards
Garage analytics for spending trends across all vehicles. Individual dashboards show cost breakdown per car with charts.
Calendar View
See reminders, past services, and fuel records on a calendar with status badges (overdue, due soon). Sidebar shows miles remaining and items needing attention.
Multi-Language & Currency
Per-user language preference (English, German, French, Polish, Ukrainian, Russian, Brazilian Portuguese) and currency selection (16 currencies), with dates, numbers, units and currency all following the selected language throughout the app.
Vehicle Reminders
Flexible reminder system with date-based, mileage-based, combined, and smart trigger modes. Create reminders inline from service visits with done/dismiss actions and filter views.
OIDC/SSO Auth
Works with Rauthy, Keycloak, or any OIDC provider. Multiple users with admin/regular roles. Or just use local accounts.
PDF & CSV Exports
Export service history, fuel logs, and analytics as PDF reports or CSV files. Good for insurance claims or selling a vehicle.
Address Book
Keep track of mechanics, dealerships, and parts stores. Link service records to providers for quick reference.
LiveLink (OBD2 Telemetry)
Real-time vehicle data from WiCAN OBD2 devices over MQTT or the Torque Pro Android app over HTTP. Live gauges, drive session tracking with grace periods, device commands, telemetry validation, historical charts, DTC capture, and automatic odometer syncing from your ECU.
Trips & Location
GPS-breadcrumb trips recorded from Torque with a route map and a last-known-location card. Location tracking is opt-out per vehicle.
Parts & Supplies
Light inventory for consumables β track purchases, usage, on-hand quantity, and average unit cost. Consume supplies inside a service visit and the cost folds into the visit total. Shared across the household.
Engine Hours Tracking
Hour meters for ATVs, side-by-sides, generators, and equipment, at full parity with distance β hours history, fuel-per-hour and cost-per-hour, hours-based service records and reminders, analytics, and CSV import/export. Vehicles can track distance and hours together.
Family Sharing
Share vehicles across household members with per-vehicle ownership. Unified family management in Settings with inline member actions and transfer history tracking.
Mobile Quick Entry
Streamlined mobile page for fast fuel, service, and mileage logging from your phone. Auto-selects vehicle for single-car garages.
Background Scheduler
Automated notifications for maintenance due dates, insurance/warranty expiration, NHTSA recall checks, and odometer milestones. Enable with a single environment variable.
PostgreSQL Support
Run on SQLite for simplicity or PostgreSQL for scale. Dialect-aware queries, async drivers, and fully tested migrations for both databases.
Light/Dark Theme & Accents
Toggle between light and dark themes and pick from six UI accent colours in Quick Settings. Your preference is saved to your account.
Tire Tracking
Per-position tread depth, DOT codes, and pressure readings, with wear projection and automatic low-tread reminders. Positions display as full names and follow your imperial or metric preference.
EV & PHEV Charging
Charge sessions on the fuel log with start and end state of charge, charge level (L1, L2, DCFC), home or public location, and battery state of health.
Imports & Webhooks
Import fuel history from Fuelio, Drivvo, and Tesla/ABRP CSV exports with an explicit odometer unit and decimal separator. Inbound webhooks cover fuel, odometer, and reminder completion, plus structured Telegram fuel commands.
Why Self-Host?
Your data stays with you
No cloud accounts, no subscriptions, no third parties seeing your maintenance records or vehicle info.
Works offline
Once deployed, everything works on your local network. No internet needed to log a service or check your history.
Multiple users
Household members can each have their own account and vehicles. Admin users manage settings and user access.
Full backups
Built-in backup/restore. Download your entire database and uploads as a zip file whenever you want.
Screenshots
Login
Clean login page with support for local accounts and OIDC/SSO providers like Rauthy or Keycloak.
Dashboard
Overview of all your vehicles with quick stats, recent activity, and upcoming maintenance items at a glance.
Analytics
Garage-wide analytics showing spending trends, fuel economy, and cost breakdowns across all vehicles with interactive charts.
Vehicle Overview
Individual vehicle page with VIN-decoded specs, photo gallery, and quick access to all maintenance records.
Service History
Complete service log with dates, mileage, costs, and vendors. Attach receipts and documents to any service record.
Fuel History
Track fill-ups with automatic MPG calculations. Separate tracking for normal driving vs. towing/hauling to see efficiency impact.
Odometer Tracking
Visual timeline of mileage over time with automatic syncing from service and fuel records.
LiveLink
Real-time OBD2 telemetry via WiCAN devices over MQTT. Live gauges, drive session tracking, historical charts, and automatic odometer syncing from your vehicle's ECU.
Recall Checking
Automatic NHTSA recall lookup by VIN. See active recalls, descriptions, and remedies with links to CarComplaints for known issues.
Vehicle Reminders
Flexible reminder system with date, mileage, combined, and smart trigger modes. Create reminders inline from service visits, with done/dismiss actions and filter views.