feat(admin): document-vault limit fields on the admin surfaces

The two vault knobs become editable where operators already work: the
app-settings card gains the per-file upload cap (MB, 1-100, mirrors the
hard server ceiling) and the default per-account storage quota (GB,
commit-on-blur with clamped conversion to bytes); the user edit form
gains the per-user quota override (GB, empty = instance default). The
admin users list now carries the override so the form can show it.

Refs: document-vault plan W3-T7
This commit is contained in:
Marc-André Bombeck committed 2026-07-07 13:34:55 +02:00
1 parent 0ade02e163
commit 0d37061d43
11 files changed
+229 -7

No files matched your search

@@ -67,6 +67,8 @@ describe("GET /api/admin/users", () => {
email: "alice@example.com",
role: "ADMIN",
createdAt: created,
mfaEnforced: true,
documentQuotaBytes: BigInt(2_147_483_648),
_count: { passkeys: 2 },
},
{
@@ -75,6 +77,8 @@ describe("GET /api/admin/users", () => {
email: null,
role: "USER",
createdAt: created,
mfaEnforced: false,
documentQuotaBytes: null,
_count: { passkeys: 0 },
},
] as never);
@@ -91,10 +95,15 @@ describe("GET /api/admin/users", () => {
email: "alice@example.com",
role: "ADMIN",
createdAt: created.toISOString(),
mfaEnforced: true,
// BigInt in Prisma, plain number on the wire.
documentQuotaBytes: 2_147_483_648,
passkeyCount: 2,
});
expect(body.data[1].passkeyCount).toBe(0);
expect(body.data[1].email).toBeNull();
// No override → explicit null (the instance default applies).
expect(body.data[1].documentQuotaBytes).toBeNull();
});
it("queries Prisma with the safe select shape (no password / sensitive cols)", async () => {
@@ -112,6 +121,7 @@ describe("GET /api/admin/users", () => {
role: true,
createdAt: true,
mfaEnforced: true,
documentQuotaBytes: true,
_count: { select: { passkeys: true } },
});
expect(args.select).not.toHaveProperty("passwordHash");
+6
View File
@@ -17,6 +17,7 @@ export const GET = apiHandler(async () => {
role: true,
createdAt: true,
mfaEnforced: true,
documentQuotaBytes: true,
_count: { select: { passkeys: true } },
},
orderBy: { createdAt: "asc" },
@@ -32,6 +33,11 @@ export const GET = apiHandler(async () => {
// v1.23 — per-user "require a second factor" override. The effective
// requirement is the OR of this and the instance-wide policy.
mfaEnforced: u.mfaEnforced,
// Document vault — per-user storage-quota override. Null = the
// instance default applies. BigInt in Prisma, number on the wire
// (max override 1 TiB, comfortably inside Number's safe range).
documentQuotaBytes:
u.documentQuotaBytes === null ? null : Number(u.documentQuotaBytes),
passkeyCount: u._count.passkeys,
})),
);