Repository navigation
v0.2.3 — vehicle photos, locale-aware rendering, server-info & self-update
Docker image: ghcr.io/varunpan/quicklogger:v0.2.3 (and :latest)
Pull with docker compose pull && docker compose up -d if you're tracking :latest.
[0.2.3] — 2026-05-28
Added
- Vehicle images from LubeLogger. Every vehicle-row surface
(Log Fuel, History, Maintenance, vehicle picker) now shows the
actual vehicle photo stored in LubeLogger (when set), proxied
through the new/api/vehicle/imageendpoint. Unblocked by
LubeLogger v1.6.5, which added API-key auth on/images/*. The
generic car icon stays as the fallback for vehicles without a
photo or when the image isn't reachable. - Structured server logger with JSON stdout, optional rotating
logfile, per-requestrequest_id(also exposed asX-Request-ID),
and secret redaction. Env vars:LOG_LEVEL,LOG_PRETTY,
LOG_FILE_PATH,LOG_FILE_MAX_SIZE_MB,LOG_FILE_MAX_FILES.
Defaults preserve current behaviour (stdout only, info level). - Client + service-worker error forwarding via the new
POST /api/logendpoint.window.errorandunhandledrejection
from the browser, plus install / runtime errors from the service
worker, now land in the server log stream taggedsource: clientor
source: service-worker. - LubeLogger server info on Settings. The Settings page now shows a
read-only LubeLogger block: a connection indicator (connected / API key
rejected / can't reach), the running LubeLogger version, and an "update
available" hint when a newer LubeLogger release exists. Backed by the new
GET /api/server-infohealth probe, which merges LubeLogger's/api/info
and/api/versionand always returns 200. Cached per-device under
quicklogger-server-infofor instant paint. ServerInfo.lubeloggerCurrency— the LubeLogger instance currency
(ISO 4217) is now part of the/api/server-inforesponse, sourced from
LUBELOGGER_CURRENCY(defaultUSD). Cached client-side and consumed
by client-side currency rendering for upstream-cached entries.- Tolerant-read migration for legacy
quicklogger.lastFuelup.*cache
entries written under LubeLogger's instance locale — parsed using
cacheddateFormatfrom/api/info. Unknown patterns / empty cache
degrade to cache miss; upstream refetch repopulates on next call. - quicklogger self-update check. quicklogger now compares its own running
version against the latest quicklogger GitHub release and surfaces an
informational "update available" hint in three places — a Settings
quickloggerblock (amber badge +v{current} → v{latest}+ a release-notes
link), a dismissible home banner, and an amber dot in the drawer footer.
Backed by the new server modulesrc/lib/server/github-release.ts(3 s
timeout, 1 h TTL cache, hardcodedvarunpan/quickloggerrepo), folded into
the existingGET /api/server-infoprobe as a thirdPromise.allSettledarm
that can't disturb the LubeLogger fields. Deploy stays manual
(docker compose pull && up -d) — no auto-update, no Docker-socket access.
Changed
- Service worker preserves a new
quicklogger-vehicle-images-v1
cache across shell upgrades. Image bytes survive new releases via
a fixed-name cache that theactivatehandler whitelists alongside
the per-version shell cache. Stale-while-revalidate semantics —
served from cache instantly, refreshed in the background. - LubeLogger upstream errors return structured JSON instead of a
passthrough of the upstream message. Bodies now includeupstream
(which call),upstream_status, andupstream_body_previewso
client-side error UI can say which upstream call failed. /api/server-infoboot refresh moves to the root layout. Settings now
paints from cache only; the layout fires the refresh on every app boot so
cached locale / currency / dateFormat are fresh for client-side rendering
consumers.- LubeLogger client always sends
culture-invariant: true. GET responses
return typed JSON (ISO dates, numeric fields, booleans,notesmay be
null) regardless of LubeLogger's instance locale. Read path simplifies:
last-fillup.tsdrops MDY↔ISO converters,/api/vehicle/last-fuelup's
parseDatecollapses toDate.parse(s). - Date and number rendering consumes LubeLogger's instance locale
(from/api/info), not hardcodeden-US.format.tsresolves locale
via the cachedquicklogger-server-infoon every call. - Currency rendering uses
Intl.NumberFormat({ style: 'currency' })—
per-entry code for queue rows, instance currency for upstream rows.
Replaces the hardcoded$prefix on the home strip and history list.
Removed
isoToLubeloggerDate(the ISO→M/D/YYYYconverter in/api/fuelup).
POST now sends ISOYYYY-MM-DDdirectly underculture-invariant: true;
LubeLogger parses with invariant culture.
Fixed
- iOS Safari pump OCR failed with
400 multipart parse failed—
pump submissions from iOS Safari intermittently sent animage
multipart part containing zero bytes, triggering a server-side
multipart parse error. Two compounding causes:prefillDateFromPhoto
racing against the OCR pipeline'screateImageBitmapon the same
PHAsset-backed HEIC File, andOffscreenCanvas.convertToBlob
intermittently returning zero-byte Blobs on iOS Safari 16.4+. Two
defensive fixes: photos are now buffered into memory at pick time
(so EXIF prefill and OCR resize operate on independent in-memory
copies, not the original File), andresizeForOcrdetects zero-byte
OffscreenCanvas.convertToBloboutput and falls back to
HTMLCanvasElement.toBlob, which iOS Safari handles reliably. - Opaque
"OCR failed (400)"toast — the toast now surfaces the
server's specific rejection reason (e.g.,"OCR rejected photo: multipart parse failed","OCR rejected photo: empty image")
instead of the bare status code, so 400s are diagnosable on-device
without server-log access. - OCR misclassifications now log the raw LLM response alongside
the validation error, so an odometer photo sent to the fuel-pump
slot can be diagnosed from logs without re-running the request. - Crop modal fits tall portrait photos to the viewport — the
full-screen image used to render at its natural pixel height on
desktop browsers, pushing the corner handles above and below the
visible window so the rectangle couldn't be resized. The image is
now bounded by the viewport on every screen size, keeping every
handle reachable. - Crop handles stay grabbable at the image edge — when the crop
rectangle is dragged flush against an image boundary, the corner
and edge handles now clamp inside the image instead of spilling
into the modal padding where the host'soverflow-hiddenmade
them unreachable. - Submit-success toast, "Will log" preview banner, and OCR pump-suggestion
preview no longer hardcode$/USD— they now render viaformatCost
so they match the LubeLogger instance currency. en-US/USD output stays
$42.18but loses the redundant trailingUSDlabel on the preview
banner. The "Will log" FX target also tracks
serverInfo.lubeloggerCurrencyinstead of a constant'USD'.
Tests
LubeLoggerClient.fetchImagecoverage — happy path with
x-api-keyheader round-trip,LubeLoggerErroron 4xx + 5xx./api/vehicle/imageroute coverage — 400 on missing/invalid
vehicleId, 404 on vehicle not found / emptyimageLocation/
path-guard rejection, 200 with copiedcontent-type+
cache-control: no-storeon happy path, 502 onLubeLoggerError,
5-minute vehicles-cache deduplication.- Logger module coverage — JSON shape, child contexts, secret
redaction (depth + cycle), pretty mode, Error unpacking. - Hooks coverage —
request_idgeneration,X-Request-IDheader
round-trip, silenced-path carve-outs. /api/logcoverage — happy path, size + batch limits, rate
limit, level validation.- Client logger coverage — buffer overflow drop-oldest, size +
time flush triggers, client-side redaction. - Branch-point logger assertions on OCR pipeline + LubeLogger
client + currency / budget / audit modules.
Full changelog: CHANGELOG.md