feat: connect wallet favorites to backend

This commit is contained in:
TerryM
2026-06-04 17:06:29 +08:00
parent fd19ed438e
commit 01eab88c0f
14 changed files with 479 additions and 172 deletions

View File

@@ -0,0 +1,313 @@
# Backend fixes required for Wallet Login + Favorites production readiness
Date: 2026-06-04
Environment tested: `https://arkie-library-stag.com/apnew/api`
## Summary
Frontend has been updated to the new backend contract:
- Wallet login: `POST /api/auth/wallet/login` with `{ address }`
- Wallet session check: `GET /api/auth/wallet/me` with `Authorization: Bearer <token>`
- Favorites list/status: `GET /api/favorites` and `GET /api/favorites?ids=...`
- Favorite mutation: `POST /api/posts/{id}/favorite` with `{ add: true|false }`
Staging confirms the new login endpoint works, but favorite mutation currently accepts an invalid Bearer token. This must be fixed before production trust.
---
## Priority 0 — Fix favorite mutation authentication
### Current staging behavior
The following request currently returns `200 OK` even with an invalid token:
```bash
curl -i -X POST \
"https://arkie-library-stag.com/apnew/api/posts/8f4a571c-3477-4b05-91be-d85907048de5/favorite" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer invalid-token" \
--data '{"add":true}'
```
Observed response:
```http
HTTP 200
{"ok":true}
```
### Required behavior
`POST /api/posts/{id}/favorite` must require a valid wallet JWT.
Invalid, missing, expired, malformed, or unverifiable tokens must return:
```http
HTTP 401 Unauthorized
```
Recommended response body:
```json
{
"error": "unauthorized"
}
```
### Acceptance tests
#### Missing token
```bash
curl -i -X POST \
"https://arkie-library-stag.com/apnew/api/posts/{postId}/favorite" \
-H "Content-Type: application/json" \
--data '{"add":true}'
```
Expected:
```http
HTTP 401
```
#### Invalid token
```bash
curl -i -X POST \
"https://arkie-library-stag.com/apnew/api/posts/{postId}/favorite" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer invalid-token" \
--data '{"add":true}'
```
Expected:
```http
HTTP 401
```
#### Valid token
```bash
TOKEN=$(curl -s -X POST \
"https://arkie-library-stag.com/apnew/api/auth/wallet/login" \
-H "Content-Type: application/json" \
--data '{"address":"0x0000000000000000000000000000000000000001"}' \
| jq -r .token)
curl -i -X POST \
"https://arkie-library-stag.com/apnew/api/posts/{postId}/favorite" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
--data '{"add":true}'
```
Expected:
```http
HTTP 200
```
Response should include at least:
```json
{
"ok": true,
"favorited": true
}
```
Then cancel:
```bash
curl -i -X POST \
"https://arkie-library-stag.com/apnew/api/posts/{postId}/favorite" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
--data '{"add":false}'
```
Expected:
```json
{
"ok": true,
"favorited": false
}
```
---
## Priority 1 — Confirm wallet login security model
### Current contract
```http
POST /api/auth/wallet/login
Content-Type: application/json
{
"address": "0x..."
}
```
Response:
```json
{
"token": "<jwt>",
"wallet": "0x..."
}
```
This is what the frontend now uses.
### Production risk
This flow does not prove wallet ownership. Any client can submit any wallet address and receive a token for that address.
If wallet identity is only used for low-risk favorites, this may be acceptable as an MVP. If wallet identity will be used for user identity, permissions, membership, rewards, asset ownership, admin behavior, or anything security-sensitive, backend should require signature verification.
### Recommended secure production flow
If stronger security is required, backend should use nonce + signature:
1. `POST /api/auth/wallet/nonce` with `{ address }`
2. Backend returns a one-time message / nonce.
3. Frontend asks wallet to sign the message.
4. `POST /api/auth/wallet/verify` with `{ address, message, signature }`
5. Backend verifies recovered address equals requested address.
6. Backend issues JWT.
If backend decides to keep the simplified `{ address }` login, please explicitly confirm that this is an accepted production risk.
---
## Priority 2 — Normalize favorites response contract
Frontend currently supports the staging response shape, but the response should be made explicit.
### Favorites list
```http
GET /api/favorites?lang=&limit=&page=&sort=&category=&q=
Authorization: Bearer <token>
```
Current staging response observed:
```json
{
"items": [
{
"id": "...",
"postType": "image",
"categoryId": 11,
"categorySlug": "official-assets",
"language": "zh",
"title": "..."
}
]
}
```
Recommended production response:
```json
{
"items": [],
"page": 1,
"limit": 24,
"total": 0
}
```
`page`, `limit`, and `total` are needed for correct pagination.
### Favorite status by ids
```http
GET /api/favorites?ids=id1,id2,id3
Authorization: Bearer <token>
```
Current staging response observed:
```json
{
"items": []
}
```
This works, but for frontend performance and clarity, recommended response is:
```json
{
"ids": ["id1", "id3"]
}
```
Meaning: only IDs that are already favorited by the current wallet user.
Frontend currently accepts both:
- `{ ids: string[] }`
- `{ items: Resource[] }`
But backend should document and standardize one shape.
---
## Priority 3 — Required status codes
Please standardize these responses:
| Case | Expected status |
| --- | --- |
| Missing Bearer token on protected endpoint | `401` |
| Invalid/expired Bearer token | `401` |
| Valid token but post ID does not exist | `404` |
| Invalid JSON body | `400` |
| Invalid `add` value | `400` |
| Successful favorite add/remove | `200` |
Protected endpoints:
- `GET /api/auth/wallet/me`
- `GET /api/favorites`
- `GET /api/favorites?ids=...`
- `POST /api/posts/{id}/favorite`
---
## Frontend compatibility notes
The frontend currently calls these staging paths through the same-origin prefix:
```txt
/apnew/api/auth/wallet/login
/apnew/api/auth/wallet/me
/apnew/api/favorites
/apnew/api/favorites?ids=...
/apnew/api/posts/{id}/favorite
```
In frontend source this is written as `/api/...`; staging build uses `VITE_API_PREFIX=/apnew`.
Please keep backend routes under `/api/...` behind the proxy.
---
## Final production checklist
Backend should confirm all of the following before production release:
- [ ] `POST /api/posts/{id}/favorite` rejects missing token with `401`.
- [ ] `POST /api/posts/{id}/favorite` rejects invalid token with `401`.
- [ ] `POST /api/posts/{id}/favorite` only changes favorites for the wallet from the validated JWT.
- [ ] `GET /api/favorites` requires a valid Bearer token.
- [ ] `GET /api/favorites?ids=...` requires a valid Bearer token, unless explicitly declared public/legacy.
- [ ] `GET /api/auth/wallet/me` validates token and returns the wallet address from the token.
- [ ] Backend explicitly confirms whether simplified `{ address }` login is acceptable for production, or switches to nonce/signature verification.