Bound credentials, brake password changes, cap bug reports, page the user list, structure every error (B-94, SEC-54, SEC-55, API-09, API-15, API-16, API-19, API-21, API-28, API-33, REG-12, AUD-42)
- login and password-change fields: at most 256 characters
- wrong current password: per-user brake (5 free, 2s doubling to 300s), 429 even for the
right password while locked; password_changed audited in the same transaction
- POST /bug-reports: 10 per IP per hour, consumed only by a stored report
- GET /admin/users: limit/after_id paging, the panel walks the pages
- 401/403/404/405/422/503 all answer {code, message, params}; the 422 omits the input
- static files revalidated on every use (Cache-Control: no-cache)
- error.* keys for the new codes in all seven languages
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c1b192f6d1
commit
24e5fc3d8b
23 files changed
+374
-111
No files matched your search
+10
-3
@@ -34,8 +34,8 @@ async function callAdmin(method, path, body) {
|
||||
const headers = { 'Content-Type': 'application/json', 'X-Admin-Token': adminToken };
|
||||
const res = await fetch(path, { method, headers, body: body ? JSON.stringify(body) : undefined });
|
||||
const data = await res.json().catch(() => ({}));
|
||||
// detail is a bare string on the admin endpoints, but the shared dependencies
|
||||
// (auth) answer with the structured {code, message} form of app/api/errors.py.
|
||||
// Every error answers with the structured {code, message, params} detail (API-16);
|
||||
// the bare-string fallback only covers a proxy's own error page.
|
||||
if (!res.ok) throw new Error(data.detail?.message || data.detail || res.statusText);
|
||||
return data;
|
||||
}
|
||||
@@ -230,7 +230,14 @@ async function adminSave() {
|
||||
|
||||
async function loadUsers() {
|
||||
try {
|
||||
const users = await callAdmin('GET', '/admin/users');
|
||||
// B-94 (API-33): the list comes in pages of up to 500, by ascending id.
|
||||
const users = [];
|
||||
for (;;) {
|
||||
const afterId = users.length ? users[users.length - 1].id : 0;
|
||||
const page = await callAdmin('GET', `/admin/users?limit=500&after_id=${afterId}`);
|
||||
users.push(...page);
|
||||
if (page.length < 500) break;
|
||||
}
|
||||
const tbody = document.getElementById('users-tbody');
|
||||
tbody.innerHTML = users.map((u) => `
|
||||
<tr>
|
||||
|
||||
Reference in new issue
Block a user