docs(posts): add API and Next.js integration documentation
Add three documentation files for the posts module: - `api.md`: public API reference (list, slug, categories, images) - `admin-api.md`: admin API reference with all CRUD endpoints - `integration.md`: Next.js integration examples with code snippets
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
# Usage programmatique — Module Posts
|
||||
|
||||
Idéal pour les cron jobs, scripts d'import ou fetchers automatisés.
|
||||
|
||||
## Fonctions disponibles
|
||||
|
||||
```js
|
||||
import {
|
||||
createPost, // Créer un post
|
||||
updatePost, // Modifier un post
|
||||
getPostBySlug, // Chercher par slug
|
||||
getPostByField, // Chercher par n'importe quel champ JSONB
|
||||
upsertPost, // Créer ou mettre à jour (idempotent)
|
||||
getPosts, // Liste avec pagination
|
||||
deletePost, // Supprimer
|
||||
} from '@zen/core/modules/posts/crud';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `upsertPost(postType, rawData, uniqueField)`
|
||||
|
||||
Crée le post s'il n'existe pas, le met à jour sinon.
|
||||
|
||||
- `postType` : le type de post (`'cve'`, `'actualite'`...)
|
||||
- `rawData` : les données du post (mêmes champs que pour `createPost`)
|
||||
- `uniqueField` : le champ de déduplication (`'slug'` par défaut)
|
||||
|
||||
Retourne `{ post, created: boolean }`.
|
||||
|
||||
### Champs `relation` dans `rawData`
|
||||
|
||||
Les champs `relation` reçoivent un **tableau d'IDs** de posts existants.
|
||||
|
||||
```js
|
||||
// Correct
|
||||
{ tags: [7, 8, 12], source: [3] }
|
||||
|
||||
// Incorrect
|
||||
{ tags: ['openssh', 'vuln'], source: { id: 3 } }
|
||||
```
|
||||
|
||||
Si les posts liés n'existent pas encore, les créer d'abord avec `upsertPost` puis utiliser leurs IDs.
|
||||
|
||||
---
|
||||
|
||||
## Exemple : fetcher de CVE
|
||||
|
||||
```js
|
||||
// src/cron/fetch-cves.js
|
||||
import { upsertPost } from '@zen/core/modules/posts/crud';
|
||||
|
||||
export async function fetchAndImportCVEs() {
|
||||
const response = await fetch('https://api.example.com/cves/recent');
|
||||
const { cves } = await response.json();
|
||||
|
||||
const results = { created: 0, updated: 0, errors: 0 };
|
||||
|
||||
for (const cve of cves) {
|
||||
try {
|
||||
// Résoudre les relations : s'assurer que les tags existent
|
||||
const tagIds = [];
|
||||
for (const tagName of (cve.tags || [])) {
|
||||
const { post: tag } = await upsertPost('tag', { title: tagName }, 'slug');
|
||||
tagIds.push(tag.id);
|
||||
}
|
||||
|
||||
// Upsert du CVE, dédupliqué sur cve_id
|
||||
const { created } = await upsertPost('cve', {
|
||||
title: cve.title,
|
||||
cve_id: cve.id,
|
||||
severity: cve.severity,
|
||||
score: String(cve.cvssScore),
|
||||
product: cve.affectedProduct,
|
||||
date: cve.publishedAt,
|
||||
description: cve.description,
|
||||
tags: tagIds,
|
||||
}, 'cve_id');
|
||||
|
||||
created ? results.created++ : results.updated++;
|
||||
} catch (err) {
|
||||
console.error(`[CVE import] Error for ${cve.id}:`, err.message);
|
||||
results.errors++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`[CVE import] Done — created: ${results.created}, updated: ${results.updated}, errors: ${results.errors}`);
|
||||
return results;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Exemple : fetcher d'actualités avec source
|
||||
|
||||
```js
|
||||
import { upsertPost } from '@zen/core/modules/posts/crud';
|
||||
|
||||
export async function fetchAndImportActualites(sourceName, articles) {
|
||||
// S'assurer que la source existe
|
||||
const { post: source } = await upsertPost('source', { title: sourceName }, 'slug');
|
||||
|
||||
for (const article of articles) {
|
||||
await upsertPost('actualite', {
|
||||
title: article.title,
|
||||
date: article.publishedAt,
|
||||
resume: article.summary,
|
||||
content: article.content,
|
||||
source: [source.id],
|
||||
tags: [],
|
||||
}, 'slug');
|
||||
}
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user