fix(users): /api/admin/users always returns a FullUserDto[]

This commit is contained in:
Edouard Vanbelle
2026-08-21 23:59:40 +02:00
parent a8fa281a02
commit 537e7f15ef
8 changed files with 62 additions and 86 deletions
+1 -4
View File
@@ -64,10 +64,7 @@ describe('admin mutate-based endpoints', () => {
describe('admin read endpoints', () => {
it('call apiJson for the listing/settings reads', async () => {
await admin.listUsers(25, 0);
expect(jsonMock).toHaveBeenCalledWith(
'/api/admin/users?limit=25&offset=0&summary=true',
expect.anything()
);
expect(jsonMock).toHaveBeenCalledWith('/api/admin/users?limit=25&offset=0', expect.anything());
await admin.getDashboard();
await admin.getSmtpInfo();
await admin.getOidcSettings();
+5 -3
View File
@@ -277,10 +277,12 @@ export function revokeAdminSession(sessionId: string): Promise<void> {
// ── Users ───────────────────────────────────────────────────────────────
/** List the compact rows rendered by the management table; full account
* details remain available through {@link getUserAdmin}. */
/** List admin users — always returns `FullUser` rows. The former
* `?summary` toggle is retired; a single canonical shape carries
* the vignette + admin-visible extras the table needs. Single-user
* details still available via {@link getUserAdmin}. */
export function listUsers(limit: number, offset: number): Promise<AdminUsersPage> {
return apiJson<AdminUsersPage>(`/api/admin/users?limit=${limit}&offset=${offset}&summary=true`, {
return apiJson<AdminUsersPage>(`/api/admin/users?limit=${limit}&offset=${offset}`, {
credentials: 'same-origin'
});
}
@@ -137,16 +137,20 @@ class PreferencesStore {
this.pendingPatch = {};
if (Object.keys(patch).length === 0) return;
const previousUser = session.user;
// `session.user` is a derived read-through on `session.me.full.user`
// — the source of truth is `session.me: SelfUser`. Snapshot + assign
// there so the optimistic update / rollback matches the store shape
// (see `docs/plan/userdto-refactor.md` for the layering).
const previousMe = session.me;
try {
const updated = await updateProfile({ ui_preferences: patch });
session.user = updated;
session.me = updated;
} catch {
// Roll back to whatever the server last confirmed. The
// optimistic local mutation is discarded and the derived
// `hideDotfiles` / other getters snap back on the next
// reactivity tick.
session.user = previousUser;
session.me = previousMe;
ui.notify(
t('preferences.save_failed', "Couldn't save your preference. Please try again."),
'error'