Admin > Users page crashed with "TypeError: e.split is not a function"
on native installs (SQLite default). Reported by @blazmaric in #485
with a clean diagnosis: SQLite returns lastLogin / createdAt /
updatedAt as integer milliseconds since epoch, while Postgres
returns ISO strings via the standard JSON serialiser. The page used
parseISO() on the raw value and parseISO trips on numbers.
Fix at both layers — defence in depth:
- backend/src/routes/adminUsers.js: new toIso() helper applied in
transformUser + transformInvitation. Coerces Date / number /
numeric-string / null to a single ISO 8601 string contract before
the response leaves the API. Protects every consumer (frontend
AND external API tokens / n8n) regardless of which DB driver is
underneath.
- frontend/src/services/userManagement.service.ts: same helper as
defence-in-depth for stale backends mid-deploy and any cached
pre-fix response shape. Also surfaced an existing
transformInvitation gap — invitations endpoints were returning
raw response.data.invitations without going through the
transformer.
10 unit tests pin the toIso contract: all known driver shapes
(Date, number, numeric-string, ISO-string, null/undefined/empty)
plus the full transformer paths for transformUser and
transformInvitation.
Out of scope: same epoch-ms surface may exist on other admin pages
that were never tested against SQLite (events list, customers,
webhooks, api tokens, activity log). Worth a follow-up audit pass
to apply toIso() in every snake_case→camelCase transformer the
admin routes use, but the immediate Users-page crash is the only
reported one and shipping that fix unblocks @blazmaric.