Skip to content

v0.2.3 — vehicle photos, locale-aware rendering, server-info & self-update

Choose a tag to compare

@varunpan varunpan released this 28 May 22:25
· 237 commits to main since this release

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/image endpoint. 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-request request_id (also exposed as X-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/log endpoint. window.error and unhandledrejection
    from the browser, plus install / runtime errors from the service
    worker, now land in the server log stream tagged source: client or
    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-info health probe, which merges LubeLogger's /api/info
    and /api/version and always returns 200. Cached per-device under
    quicklogger-server-info for instant paint.
  • ServerInfo.lubeloggerCurrency — the LubeLogger instance currency
    (ISO 4217) is now part of the /api/server-info response, sourced from
    LUBELOGGER_CURRENCY (default USD). 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
    cached dateFormat from /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
    quicklogger block (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 module src/lib/server/github-release.ts (3 s
    timeout, 1 h TTL cache, hardcoded varunpan/quicklogger repo), folded into
    the existing GET /api/server-info probe as a third Promise.allSettled arm
    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 the activate handler 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 include upstream
    (which call), upstream_status, and upstream_body_preview so
    client-side error UI can say which upstream call failed.
  • /api/server-info boot 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, notes may be
    null) regardless of LubeLogger's instance locale. Read path simplifies:
    last-fillup.ts drops MDY↔ISO converters, /api/vehicle/last-fuelup's
    parseDate collapses to Date.parse(s).
  • Date and number rendering consumes LubeLogger's instance locale
    (from /api/info), not hardcoded en-US. format.ts resolves locale
    via the cached quicklogger-server-info on 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/YYYY converter in /api/fuelup).
    POST now sends ISO YYYY-MM-DD directly under culture-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 an image
    multipart part containing zero bytes, triggering a server-side
    multipart parse error. Two compounding causes: prefillDateFromPhoto
    racing against the OCR pipeline's createImageBitmap on the same
    PHAsset-backed HEIC File, and OffscreenCanvas.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), and resizeForOcr detects zero-byte
    OffscreenCanvas.convertToBlob output 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's overflow-hidden made
    them unreachable.
  • Submit-success toast, "Will log" preview banner, and OCR pump-suggestion
    preview no longer hardcode $/USD
    — they now render via formatCost
    so they match the LubeLogger instance currency. en-US/USD output stays
    $42.18 but loses the redundant trailing USD label on the preview
    banner. The "Will log" FX target also tracks
    serverInfo.lubeloggerCurrency instead of a constant 'USD'.

Tests

  • LubeLoggerClient.fetchImage coverage — happy path with
    x-api-key header round-trip, LubeLoggerError on 4xx + 5xx.
  • /api/vehicle/image route coverage — 400 on missing/invalid
    vehicleId, 404 on vehicle not found / empty imageLocation /
    path-guard rejection, 200 with copied content-type +
    cache-control: no-store on happy path, 502 on LubeLoggerError,
    5-minute vehicles-cache deduplication.
  • Logger module coverage — JSON shape, child contexts, secret
    redaction (depth + cycle), pretty mode, Error unpacking.
  • Hooks coverage — request_id generation, X-Request-ID header
    round-trip, silenced-path carve-outs.
  • /api/log coverage — 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