Audit fixes: VIP self-grant, draft leak, onboarding flag, hero personalization, gated downloads
Deploy GitHub Pages / deploy (push) Canceled after 0s
Deploy GitHub Pages / deploy (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
# Agent Publish API — /api/agent/posts
|
||||
|
||||
Publish and manage blog posts programmatically. Auth: send the agent key in the
|
||||
`x-agent-key` header. The key lives on the VPS in `/root/anwarpi/.env.production`
|
||||
(`AGENT_API_KEY`).
|
||||
|
||||
- Base URL: `https://anwarpy.ai`
|
||||
- Content-Type: `application/json`
|
||||
- Posts are created **draft** by default. Pass `"publish": true` to publish
|
||||
(sets `status: published` + `publishedAt` now). Unpublishing stays admin-only
|
||||
(`/admin`).
|
||||
- `access` controls gating: `"free"` (public) or `"vip"` (members only).
|
||||
- `content` (Lexical) may be omitted on create — a minimal valid doc is
|
||||
inserted; update it later with PUT. Other writable fields: `title`, `slug`,
|
||||
`dek`, `teaser`, `category` (`agents` | `models` | `automation` |
|
||||
`foundations`), `readingMinutes`, `cover`.
|
||||
- `attachments` attaches per-post work files: an array of
|
||||
`{ "filename": "agent-state.md", "dataBase64": "…", "access": "vip" }`.
|
||||
`access` is per file (`"free"` downloads for anyone, `"vip"` members only)
|
||||
and defaults to `"vip"`. Max 5 files, ≤5MB each, extensions pdf/zip/md/csv/
|
||||
txt/json. On PUT, sending `attachments` **replaces** the post's file set —
|
||||
omit the field to keep existing files.
|
||||
|
||||
## Create + publish a post with an image
|
||||
|
||||
```bash
|
||||
KEY=033d1eed44ca6abf483de10735c47155d38f7b5bc9d46449
|
||||
|
||||
# Inline base64 image (jpeg/png/webp, ≤5MB)
|
||||
python3 - <<'EOF' > /tmp/post.json
|
||||
import base64, json, pathlib
|
||||
print(json.dumps({
|
||||
"title": "دليلي الجديد",
|
||||
"slug": "my-new-guide",
|
||||
"dek": "وصف قصير يظهر في القوائم.",
|
||||
"teaser": "نص المعاينة للزوار غير المشتركين.",
|
||||
"category": "وكلاء",
|
||||
"access": "vip", # or "free"
|
||||
"readingMinutes": 9,
|
||||
"publish": True, # omit → draft
|
||||
"coverImage": {
|
||||
"filename": "my-cover.jpg",
|
||||
"mimeType": "image/jpeg",
|
||||
"dataBase64": base64.b64encode(pathlib.Path("my-cover.jpg").read_bytes()).decode(),
|
||||
},
|
||||
}))
|
||||
EOF
|
||||
curl -s -X POST https://anwarpy.ai/api/agent/posts \
|
||||
-H "x-agent-key: $KEY" -H "Content-Type: application/json" \
|
||||
--data @/tmp/post.json
|
||||
```
|
||||
|
||||
Response `201`:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 113, "title": "دليلي الجديد", "slug": "my-new-guide",
|
||||
"status": "published", "access": "vip", "category": "agents",
|
||||
"cover": { "id": 113, "url": "/api/media/file/my-cover.jpg" },
|
||||
"attachments": [
|
||||
{ "id": 42, "filename": "agent-state.md", "access": "vip", "url": "/api/files/42" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Update a post
|
||||
|
||||
```bash
|
||||
curl -s -X PUT https://anwarpy.ai/api/agent/posts/my-new-guide \
|
||||
-H "x-agent-key: $KEY" -H "Content-Type: application/json" \
|
||||
-d '{"access": "free", "publish": true,
|
||||
"coverImage": {"url": "https://example.com/cover.png"}}'
|
||||
```
|
||||
|
||||
- `coverImage` accepts `{ "dataBase64": "…" }` or `{ "url": "https://…" }`
|
||||
(server fetches and stores it; `filename`/`mimeType` optional).
|
||||
- `cover` may also be an existing media ID (integer).
|
||||
|
||||
## Where the image appears
|
||||
|
||||
The cover is rendered on:
|
||||
|
||||
1. **Post page** (`/posts/<slug>`) — full-width cover under the header.
|
||||
2. **Articles listing** (`/articles`) — thumbnail on each card.
|
||||
3. **Homepage** (`/`) — "أحدث الأدلة" section (latest 6 published posts).
|
||||
|
||||
If a post has no cover, the catalog card falls back to the category's default
|
||||
decorative cover; the post page and listing simply omit the image.
|
||||
|
||||
## Rules
|
||||
|
||||
- The agent **cannot** set `status` directly — only via `"publish": true`.
|
||||
- The agent **cannot** unpublish or delete — do that in `/admin`.
|
||||
- Images are validated: jpeg/png/webp, ≤5MB; stored as Payload `media`
|
||||
documents and served from `/api/media/file/<filename>`.
|
||||
- Attachments are stored as Payload `files` documents under `files-private/`
|
||||
(volume-mount it like `media/` on the VPS). Downloads go through
|
||||
`GET /api/files/<id>`: `free` files stream to anyone, `vip` files return
|
||||
401 anonymous / 403 for non-members. The `vip` badge on the post page links
|
||||
only for entitled viewers.
|
||||
Reference in New Issue
Block a user