Public: anyone can read this; the crew can change it.
GET /api/v1/admin/limits answers `{ items, next_cursor: null }` #5164
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/5146-get-api-v1-admin-limits-answers-items-ne → main 2 commits fork 3 threads · 3 unresolved
3 files
changed +10
−3
src/contract/openapi.ts +1 −1
| @@ -189,7 +189,7 @@ export const ROUTES: readonly Route[] = [ | ||
| 189 | 189 | method: "get", |
| 190 | 190 | path: "/api/v1/admin/limits", |
| 191 | 191 | summary: |
| 192 | "Read the forge's limits (a person admin's token, any scope); answers { limits: [{ key, value, isSet, defaultValue, ... }], changes }", | |
| 192 | "Read the forge's limits (a person admin's token, any scope); answers { items: [{ key, value, isSet, defaultValue, ... }], changes, next_cursor }", | |
| 193 | 193 | auth: true, |
| 194 | 194 | refusals: { not_found: "the token is not a person who administers the forge" }, |
| 195 | 195 | }, |
src/http/api.ts +1 −1
| @@ -1701,7 +1701,7 @@ export function apiRoutes(router: Router<Handler>): void { | ||
| 1701 | 1701 | const denied = accountAdmin(ctx, "ro"); |
| 1702 | 1702 | if (denied) return denied; |
| 1703 | 1703 | const listed = timePhase(ctx.request, "limits", () => listLimits(ctx.app, ctx.auth.user)); |
| 1704 | return timePhase(ctx.request, "render", () => (listed.ok ? json(listed.value) : notFound())); | |
| 1704 | return timePhase(ctx.request, "render", () => (listed.ok ? json({ items: listed.value.limits, changes: listed.value.changes, next_cursor: null }) : notFound())); | |
outdated
High (audit regression): |
||
| 1705 | 1705 | }); |
| 1706 | 1706 | |
| 1707 | 1707 | router.add("PUT", "/api/v1/admin/limits/:key", async (ctx) => { |
test/limits.test.ts +8 −1
| @@ -178,7 +178,9 @@ describe("limits", () => { | ||
| 178 | 178 | }); |
| 179 | 179 | const listed = await api(adminToken, "GET", "/admin/limits"); |
| 180 | 180 | expect(listed.status).toBe(200); |
| 181 | expect(((await listed.json()) as { limits: unknown[] }).limits).toHaveLength(LIMIT_KEYS.length); | |
| 181 | const body = (await listed.json()) as { items: unknown[]; next_cursor: unknown }; | |
| 182 | expect(body.items).toHaveLength(LIMIT_KEYS.length); | |
| 183 | expect(body.next_cursor).toBeNull(); | |
Assert |
||
| 182 | 184 | expect((await api(bobToken, "GET", "/admin/limits")).status).toBe(403); |
| 183 | 185 | expect((await api(bobToken, "PUT", "/admin/limits/share.daily_max", { value: "9" })).status).toBe(403); |
| 184 | 186 | expect((await api(adminToken, "PUT", "/admin/limits/share.daily_max", { value: "0" })).status).toBe(422); |
| @@ -187,6 +189,11 @@ describe("limits", () => { | ||
| 187 | 189 | const put = await api(adminToken, "PUT", "/admin/limits/share.daily_max", { value: "9" }); |
| 188 | 190 | expect(put.status).toBe(200); |
| 189 | 191 | expect(limitValue(app.db, "share.daily_max")).toBe(9); |
| 192 | const afterPut = (await (await api(adminToken, "GET", "/admin/limits")).json()) as { | |
| 193 | changes: { key: string }[]; | |
| 194 | }; | |
| 195 | expect(Array.isArray(afterPut.changes)).toBe(true); | |
| 196 | expect(afterPut.changes[0]?.key).toBe("share.daily_max"); | |
| 190 | 197 | expect((await api(adminToken, "PUT", "/admin/limits/share.daily_max", { value: 11 })).status).toBe(200); |
| 191 | 198 | expect(limitValue(app.db, "share.daily_max")).toBe(11); |
| 192 | 199 | expect((await api(adminToken, "PUT", "/admin/limits/share.daily_max", { value: null })).status).toBe(200); |
new above
offline
Once
changesis restored, add it here too:{ items: [...], changes, next_cursor }.