docs(auth,users,devices,progress,matching): Beam batch 8 — WO5 backfill complete
14 endpoints backfilled: devices pending/approve/reject; users
password/profile/profile-delete self-service (admin variants in prose);
admin list_users + update_user_max_devices; progress history; book
matching; new password-rules doc.
Gate fix: authLabel missed JWT middleware written inline as
createJWTMiddleware(cfg) (uppercase 'JWT') — reported auth 'none' for
/api/auth self-service routes and would have for any wrapper-inlined
group. Labeled 'JWTMiddleware' + fallthrough 'jwt' case.
Claude registry: 12 entries. Drift fixed by gate: list_users on-device —
handler returns raw []database.Users (bare array, full model incl.
password_hash — real leak the API emits; pinned + documented; earlier
draft had invented {users,total,limit,offset} wrapper). max-devices
response is {message} only. books/match is GET /api/books/match
{matches, action}. progress history sessions = ReadingHistory rows
(pgtype fields). make test 9/9.
This commit is contained in:
1 parent
2c56ae4b9d
commit
cc5677eb17
13 files changed
+233
-75
No files matched your search
@@ -1,3 +1,11 @@
|
||||
---
|
||||
method: GET
|
||||
path: /api/auth/users
|
||||
auth: jwt+admin
|
||||
area: authentication
|
||||
---
|
||||
<!-- source: internal/handlers/auth.go:734 AuthHandler.ListUsers -->
|
||||
|
||||
# List Users
|
||||
|
||||
List all users in the system (admin only).
|
||||
@@ -28,29 +36,34 @@ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||||
|
||||
## Response (200 OK)
|
||||
|
||||
```json
|
||||
{
|
||||
"users": [
|
||||
{
|
||||
"id": "uuid",
|
||||
"email": "user@example.com",
|
||||
"username": "johndoe",
|
||||
"first_name": "John",
|
||||
"last_name": "Doe",
|
||||
"role": "user",
|
||||
"theme": "tokyo-night",
|
||||
"max_devices": 10,
|
||||
"device_count": 3,
|
||||
"created_at": "2026-02-08T10:00:00Z",
|
||||
"updated_at": "2026-02-08T10:00:00Z"
|
||||
}
|
||||
],
|
||||
"total": 25,
|
||||
"limit": 10,
|
||||
"offset": 0
|
||||
}
|
||||
```json example-response
|
||||
[
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"email": "user@example.com",
|
||||
"username": "johndoe",
|
||||
"password_hash": "$2a$04$REDACTED",
|
||||
"first_name": "John",
|
||||
"last_name": "Doe",
|
||||
"role": "user",
|
||||
"theme": "tokyo-night",
|
||||
"max_devices": 10,
|
||||
"created_at": "2026-02-08T10:00:00Z",
|
||||
"timezone": "America/New_York",
|
||||
"updated_at": "2026-02-08T10:00:00Z",
|
||||
"default_audio_speed": 1
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Response is a bare JSON array of full user rows — no pagination wrapper
|
||||
and no `device_count` (those were invented in an earlier draft).
|
||||
|
||||
**Leak note:** the user model json-marshals `password_hash` verbatim —
|
||||
the list-users response exposes password hashes to any admin caller.
|
||||
Known defect, tracked separately (doc mirrors the real field set; do not
|
||||
implement against a redacted shape).
|
||||
|
||||
### Response Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
---
|
||||
method: PUT
|
||||
path: /api/auth/users/:id/max-devices
|
||||
auth: jwt+admin
|
||||
area: authentication
|
||||
---
|
||||
<!-- source: internal/handlers/auth.go:1160 AuthHandler.UpdateUserMaxDevices -->
|
||||
|
||||
# Update User Max Devices
|
||||
|
||||
Update the maximum number of devices a user can register (admin only).
|
||||
@@ -20,7 +28,7 @@ Update the maximum number of devices a user can register (admin only).
|
||||
|
||||
### Example Request
|
||||
|
||||
```json
|
||||
```json example-request
|
||||
{
|
||||
"max_devices": 15
|
||||
}
|
||||
@@ -28,14 +36,9 @@ Update the maximum number of devices a user can register (admin only).
|
||||
|
||||
## Response (200 OK)
|
||||
|
||||
```json
|
||||
```json example-response
|
||||
{
|
||||
"id": "uuid",
|
||||
"email": "user@example.com",
|
||||
"username": "johndoe",
|
||||
"max_devices": 15,
|
||||
"active_devices": 3,
|
||||
"updated_at": "2026-02-08T11:00:00Z"
|
||||
"message": null
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
method: GET
|
||||
path: /api/auth/password-rules
|
||||
auth: none
|
||||
area: authentication
|
||||
---
|
||||
<!-- source: internal/handlers/auth.go:746 AuthHandler.GetPasswordRules -->
|
||||
|
||||
# Password Rules
|
||||
|
||||
Returns the active password complexity configuration, so UI requirement
|
||||
checkers mirror the real rules. The server remains the enforcer. This
|
||||
route is rate-limited.
|
||||
|
||||
## Example response
|
||||
|
||||
```json example-response
|
||||
{
|
||||
"min_length": 8,
|
||||
"require_upper": true,
|
||||
"require_lower": true,
|
||||
"require_number": true,
|
||||
"require_special": true
|
||||
}
|
||||
```
|
||||
|
||||
## Errors
|
||||
|
||||
| Status | Body | When |
|
||||
| ------ | ---- | ---- |
|
||||
| 429 | `{"error": "..."}` | rate limited |
|
||||
@@ -1,3 +1,11 @@
|
||||
---
|
||||
method: GET
|
||||
path: /api/books/match
|
||||
auth: jwt
|
||||
area: book-matching
|
||||
---
|
||||
<!-- source: internal/handlers/matching.go:309 MatchingHandler.GetBookMatches -->
|
||||
|
||||
# Get Book Matches
|
||||
|
||||
Get potential book matches for a given query.
|
||||
@@ -27,20 +35,22 @@ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||||
|
||||
## Response (200 OK)
|
||||
|
||||
```json
|
||||
```json example-response
|
||||
{
|
||||
"matches": [
|
||||
{
|
||||
"id": "uuid",
|
||||
"title": "Foundation",
|
||||
"authors": ["Isaac Asimov"],
|
||||
"authors": [
|
||||
"Isaac Asimov"
|
||||
],
|
||||
"isbn": "978-0553293357",
|
||||
"published_year": 1951,
|
||||
"publisher": "Bantam Books",
|
||||
"cover_url": "https://example.com/cover.jpg"
|
||||
}
|
||||
],
|
||||
"total": 5
|
||||
"action": null
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
---
|
||||
method: GET
|
||||
path: /api/devices/approve/:registration_id
|
||||
auth: jwt
|
||||
area: devices
|
||||
---
|
||||
<!-- source: internal/handlers/devices.go:513 DeviceHandler.ApproveDevice -->
|
||||
|
||||
# Approve Device Registration
|
||||
|
||||
Approve a pending device registration request.
|
||||
@@ -26,7 +34,7 @@ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||||
|
||||
## Response (200 OK)
|
||||
|
||||
```json
|
||||
```json example-response
|
||||
{
|
||||
"message": "device approved successfully",
|
||||
"device_name": "string",
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
# List Pending Device Registrations
|
||||
---
|
||||
method: GET
|
||||
path: /api/devices/pending
|
||||
auth: jwt
|
||||
area: devices
|
||||
---
|
||||
<!-- source: internal/handlers/devices.go:703 DeviceHandler.ListPendingRegistrations -->
|
||||
|
||||
# List Pending Device Registrations:<|end|><|start|><|start|>assistant to=edit<|channel|>analysis <|constrain|>xml<|message|><rfl:parameter name="path">
|
||||
/home/nymusicman/Code/bookhoard/docs/developer/api/devices/reject_device_registration.md
|
||||
|
||||
List all pending device registration requests.
|
||||
|
||||
@@ -20,7 +29,7 @@ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||||
|
||||
## Response (200 OK)
|
||||
|
||||
```json
|
||||
```json example-response
|
||||
{
|
||||
"registrations": [
|
||||
{
|
||||
@@ -31,7 +40,8 @@ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||||
"created_at": "2026-02-08T10:00:00Z"
|
||||
}
|
||||
],
|
||||
"total": 3
|
||||
"total": 3,
|
||||
"pending_registrations": null
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
---
|
||||
method: POST
|
||||
path: /api/devices/reject/:registration_id
|
||||
auth: jwt
|
||||
area: devices
|
||||
---
|
||||
<!-- source: internal/handlers/devices.go:664 DeviceHandler.RejectDevice -->
|
||||
|
||||
# Reject Device Registration
|
||||
|
||||
Reject a pending device registration request.
|
||||
@@ -26,7 +34,7 @@ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||||
|
||||
## Response (200 OK)
|
||||
|
||||
```json
|
||||
```json example-response
|
||||
{
|
||||
"message": "device registration rejected"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
---
|
||||
method: GET
|
||||
path: /api/progress/:id/history
|
||||
auth: jwt
|
||||
area: progress
|
||||
---
|
||||
<!-- source: internal/handlers/progress.go:34 Handler.GetProgressHistory -->
|
||||
|
||||
# Get Progress History
|
||||
|
||||
Get historical reading progress data for a media item.
|
||||
@@ -33,31 +41,11 @@ Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
||||
|
||||
## Response (200 OK)
|
||||
|
||||
```json
|
||||
```json example-response
|
||||
{
|
||||
"history": [
|
||||
{
|
||||
"media_item_id": "uuid",
|
||||
"percentage": 75.5,
|
||||
"position": 1234,
|
||||
"page": 150,
|
||||
"device_id": "device-uuid",
|
||||
"device_name": "My Kobo",
|
||||
"created_at": "2026-02-08T10:00:00Z"
|
||||
},
|
||||
{
|
||||
"media_item_id": "uuid",
|
||||
"percentage": 50.0,
|
||||
"position": 800,
|
||||
"page": 100,
|
||||
"device_id": "device-uuid",
|
||||
"device_name": "My Kobo",
|
||||
"created_at": "2026-02-07T15:00:00Z"
|
||||
}
|
||||
],
|
||||
"total": 45,
|
||||
"limit": 20,
|
||||
"offset": 0
|
||||
"count": null,
|
||||
"media_item_id": null,
|
||||
"sessions": null
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
---
|
||||
method: PUT
|
||||
path: /api/auth/password
|
||||
auth: jwt
|
||||
area: authentication
|
||||
---
|
||||
<!-- source: internal/handlers/auth.go:968 AuthHandler.UpdatePassword -->
|
||||
|
||||
# Change Password
|
||||
|
||||
Change user password. Supports both self-service and admin modes.
|
||||
@@ -24,7 +32,7 @@ Users can change their own password by providing current password verification.
|
||||
|
||||
### Example Request
|
||||
|
||||
```json
|
||||
```json example-request
|
||||
{
|
||||
"current_password": "OldPassword123!",
|
||||
"new_password": "NewSecureP@ss123!",
|
||||
@@ -56,7 +64,7 @@ Admins can reset any user's password without knowing the current password.
|
||||
|
||||
## Response (200 OK)
|
||||
|
||||
```json
|
||||
```json example-response
|
||||
{
|
||||
"message": "password updated"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
---
|
||||
method: DELETE
|
||||
path: /api/auth/profile
|
||||
auth: jwt
|
||||
area: authentication
|
||||
---
|
||||
<!-- source: internal/handlers/auth.go:1049 AuthHandler.DeleteUser -->
|
||||
|
||||
# Delete User
|
||||
|
||||
Delete a user account. Supports both self-deletion and admin deletion.
|
||||
@@ -25,7 +33,7 @@ Admins can delete any user account by providing the user ID in the URL.
|
||||
|
||||
## Response (200 OK)
|
||||
|
||||
```json
|
||||
```json example-response
|
||||
{
|
||||
"message": "account deleted"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
---
|
||||
method: PUT
|
||||
path: /api/auth/profile
|
||||
auth: jwt
|
||||
area: authentication
|
||||
---
|
||||
<!-- source: internal/handlers/auth.go:562 AuthHandler.UpdateProfile -->
|
||||
|
||||
# Update Profile
|
||||
|
||||
Update user profile information. Supports both self-edit and admin modes.
|
||||
@@ -26,7 +34,7 @@ Users can update their own profile. All fields are optional.
|
||||
|
||||
### Example Request
|
||||
|
||||
```json
|
||||
```json example-request
|
||||
{
|
||||
"username": "newusername",
|
||||
"email": "newemail@example.com",
|
||||
@@ -60,17 +68,9 @@ Admins can update any user by providing the user ID in the URL. Additionally sup
|
||||
|
||||
## Response (200 OK)
|
||||
|
||||
```json
|
||||
```json example-response
|
||||
{
|
||||
"id": "uuid",
|
||||
"email": "user@example.com",
|
||||
"username": "john",
|
||||
"first_name": "John",
|
||||
"last_name": "Smith",
|
||||
"theme": "dracula",
|
||||
"role": "user",
|
||||
"max_devices": 10,
|
||||
"created_at": "2026-01-31T10:00:00Z"
|
||||
"message": null
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Reference in new issue
Block a user