github John710/finapp v1.3.2

2 hours ago

[1.3.2] - 2026-10-25

Bug Fixes

  • Share modal silently saved an empty user list by @John710
    The user picked a person in the dropdown and hit Save, expecting the
    selection to be stored — but the two-step UX required clicking the tiny
    '+' button first, so the PUT went out with users: [] and returned
    success while nothing was shared. Selecting a user in the dropdown now
    adds them immediately (Save stays disabled while the picker still holds
    an uncommitted user). Verified against production for all four entity
    types.
  • Exempt refresh/setup-required via allowList (max:0 blocks, not exempts) by @John710
    @fastify/rate-limit treats a max of 0 as 'zero requests allowed' —
    setup-required got 429'd on every call. Route exemption belongs in
    allowList.
  • Session recovery path — refresh grace, rate-limit exemptions, client storm guard by @John710
    Multi-tab usage made sessions self-destruct: two tabs refreshing within
    the rotation window tripped the replay guard, which revoked every
    session; the logout burst then exhausted the 10/min auth rate limit,
    so the login page itself threw raw 429 toasts.
    • refresh rotation now grants the consumed token a 30-second grace
      window instead of deleting it: concurrent tab refreshes are benign,
      a token reused beyond the grace is still treated as stolen
    • /auth/refresh and /auth/setup-required are exempt from the rate
      limit (they are the recovery path); /auth/login stays at 10/min
    • api.js: after a failed refresh a latch stops the queued-request
      refresh storm; cleared on successful login
    • 429 responses surface as a localized 'too many requests' message
  • Fresh installs made the first user a non-admin by @John710
    Migration 27 defaulted users.role to 'owner' and migration 28 converted
    existing rows to 'admin' — but users registered AFTER 28 got the column
    default again, and the auth plugin maps 'owner' to 'user', locking the
    instance owner out of admin endpoints on any fresh install. Migration 30
    sets the default to 'user'; /auth/register now inserts role='admin'
    explicitly (registration is only open for the first user).
  • Trim unused SQL parameters left by the globalization pass by @John710
    Postgres rejects bound-but-unreferenced parameters with 42P18 — the
    globalization commit removed user scoping from queries but left the
    trailing userId in the args arrays (transactions tag map and tag checks,
    budgets category/tag checks, recurring category check, debts
    autocategory, categories parent checks/list/delete/merge, tags
    list/create/delete, import categories). Found by tracing the CI 500s
    via the container-log dump; parameter usage audited against every
    touched query.
  • Drop per-user indexes IF EXISTS (naming differs across pg-migrate versions) by @John710- Dedupe transaction_tags via ctid (no id column) by @John710
    transaction_tags has a composite PK (transaction_id, tag_id) — the
    duplicate cleanup referenced a non-existent id column and migration 29
    rolled back on start (caught by CI).
  • Family sharing feedback + global categories and tags by @John710
    • accounts: ShareModal was never rendered (silent template replace
      failure) — the share button did nothing
    • budgets: refetch after save (the raw create response lacks the joined
      category name, cards showed nameless chips)
    • universal button hover/active feedback (main.css) + primary Shared
      badge: an entity that is already shared shows 'Shared' instead of
      'Share' (is_shared now returned for debts and recurring too)
    • rate limits raised for real family usage: GET 240/min, mutations
      90/min (auth stays 10/min)
    • migration 29: categories and tags are now GLOBAL — same-name
      duplicates across users are merged (oldest wins), user scoping dropped,
      unique constraints become (name, type) / (name); all backend queries
      unscoped (transactions, budgets, debts, recurring, import, demodata,
      debt category autogeneration)
  • Drop unused SQL parameters after visibility rework by @John710
    Postgres errors with 42P18 when a bound parameter is never referenced
    (net-worth passed userId as $1 that the query no longer used). Visible
    account arrays are now $1 everywhere and filter placeholders start at $2;
    the same latent collision in the report helpers is fixed too.
  • Transactions list param order broke visibility scoping by @John710
    The list query passed userId as $1 while the WHERE clause expected the
    visible-account array — every list came back empty (caught by the
    blackbox suite's paired-leg check).

CI/CD

  • Changelog range starts at the previous PUBLISHED release by @John710
    Failed builds leave tags without releases; counting them as range
    boundaries dropped their commits from the next release's changelog
    (happened three times across 1.3.2 betas). The range now starts at the
    most recent tag with a published release, via gh release list
  • Dump app container logs when blackbox fails by @John710

Documentation

  • Family sharing guide, per-role feature list, changelog range fix by @John710
    • README (EN+RU): Family Sharing section — admin user management, share
      dialog, view/edit permissions, global categories/tags, created_by
    • Features list now reflects sharing, admin users, 2FA and API tokens
    • workflow: stable releases compute the range from the previous STABLE
      tag (the published-release logic stays for betas) — otherwise a stable
      after a beta series would only contain the version bump

Features

  • Family sharing on entity level, admin user management by @John710

Testing

  • Dump raw net-worth response on failure by @John710- Tighter pacing thresholds + GET-only 429 retry by @John710
    The server limiter uses fixed windows — a rolling 25/min burst can
    straddle two windows and still exceed 30. Thresholds 12/60 keep worst-case
    straddles under both limits; stray GET 429s (outside /auth) retry once.
  • Pace GET and non-GET blackbox requests against separate limits by @John710- Pace blackbox requests instead of retrying 429s by @John710
    A rolling-window pacifier keeps non-auth requests under the 100/min GET
    limit (the report burst tripped it and net-worth read a 429 error body
    as an empty series). Auth requests stay bursty so the rate-limit test
    still observes its expected 429.
  • Retry 429 in blackbox for GET requests too by @John710
    The report burst can exceed the 100/min GET limit mid-suite; the net-worth
    assert read a 429 error body as an empty series (got=undefined) and failed
    non-deterministically in CI. All methods now wait out the window and retry.

Full Changelog: v1.3.1...v1.3.2

Don't miss a new finapp release

NewReleases is sending notifications on new releases.