101 lines
3.9 KiB
Markdown
101 lines
3.9 KiB
Markdown
# 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.
|