Commentaires modérés sur toute entité : invité ou compte, réponses, note 1–5, bannissements, RGPD et HTML sûr
Installer
genpm add @core/commentsCe que vous obtenez
- Source dans src/lib/comments/, 9 fichiers. (23 ko)
- Règles IA dans src/lib/comments/AGENTS.md, plus les fichiers de règles de l’IDE.
- Variables d’environnement ajoutées à .env.example : COMMENTS_NOTIFY_TO.
- Résout @core/antispam, @core/auth, @core/contracts, @core/db, @core/email, @core/jobs pour vous.
README
Ce paquet n’a pas de README.
Voici exactement ce que lit votre IA quand elle travaille dans src/lib/comments. Rien d’autre n’est ajouté à son contexte.
@core/comments — rules for AI agents
Purpose
Comments on anything (entityType + entityId: posts, products, pages): guests (name + email, stored only as a hash)
or signed-in users, one level of replies, optional 1–5 rating (reused by @core/reviews), moderation queue with email
alerts, bans, GDPR erasure and safe HTML rendering. Anti-spam via @core/antispam. Tables: comments, comment_bans.
Map
index.ts— public API:postComment,listComments,moderate,ban,eraseComments,ratingSummary,commentHtml,commentsAdminResource.comments.ts— logic and thecomments.notifyjob.admin.ts— moderation queue.schema.ts— tables.adapters/hono.ts—commentRoutes({ currentUser }).adapters/next.ts—commentRoute({ currentUser })→{ GET, POST }.
Integration
- Env: optional
COMMENTS_NOTIFY_TO(moderators, comma-separated) andEMAIL_FROM. Migrations as insrc/lib/db/AGENTS.md; the @core/jobs cron must run. - Mount the endpoints at
/api/comments/:type/:idwithcurrentUserfrom @core/auth (getUser/getUserFromCookieHeader). - The form posts
body(andname,emailfor guests,parentIdfor replies) plus the @core/antispam fields (honeypotProps,_ts). Show "your comment awaits moderation" when the response status ispending. - Render the list from
GETorlistComments(type, id); render each body withcommentHtml(body)(escaped; the only HTML allowed). - Add
commentsAdminResourcetosrc/genpm/admin.ts(permissionscomments:read,comments:moderate). - Verify: a guest comment is pending until approved in the admin, then it is listed.
Conventions
- Default moderation is
guests: guests and users without an approved comment wait for approval. Usenoneonly on trusted, private sites. - Erase on request with
eraseComments({ email })or({ userId }); delete user comments when the account is deleted. - Show ratings only from
ratingSummary(approved comments).
Don't
- Don't store or show guest emails; don't render comment bodies as Markdown/HTML.
- Don't auto-approve guests to "increase engagement".
- Don't expose
pending,spamordeletedcomments in public endpoints.
# @core/comments — rules for AI agents
## Purpose
Comments on anything (`entityType` + `entityId`: posts, products, pages): guests (name + email, stored only as a hash)
or signed-in users, one level of replies, optional 1–5 rating (reused by @core/reviews), moderation queue with email
alerts, bans, GDPR erasure and safe HTML rendering. Anti-spam via @core/antispam. Tables: `comments`, `comment_bans`.
## Map
- `index.ts` — public API: `postComment`, `listComments`, `moderate`, `ban`, `eraseComments`, `ratingSummary`, `commentHtml`, `commentsAdminResource`.
- `comments.ts` — logic and the `comments.notify` job. `admin.ts` — moderation queue. `schema.ts` — tables.
- `adapters/hono.ts` — `commentRoutes({ currentUser })`. `adapters/next.ts` — `commentRoute({ currentUser })` → `{ GET, POST }`.
## Integration
1. Env: optional `COMMENTS_NOTIFY_TO` (moderators, comma-separated) and `EMAIL_FROM`. Migrations as in `src/lib/db/AGENTS.md`; the @core/jobs cron must run.
2. Mount the endpoints at `/api/comments/:type/:id` with `currentUser` from @core/auth (`getUser`/`getUserFromCookieHeader`).
3. The form posts `body` (and `name`, `email` for guests, `parentId` for replies) plus the @core/antispam fields (`honeypotProps`, `_ts`).
Show "your comment awaits moderation" when the response status is `pending`.
4. Render the list from `GET` or `listComments(type, id)`; render each body with `commentHtml(body)` (escaped; the only HTML allowed).
5. Add `commentsAdminResource` to `src/genpm/admin.ts` (permissions `comments:read`, `comments:moderate`).
6. Verify: a guest comment is pending until approved in the admin, then it is listed.
## Conventions
- Default moderation is `guests`: guests and users without an approved comment wait for approval. Use `none` only on trusted, private sites.
- Erase on request with `eraseComments({ email })` or `({ userId })`; delete user comments when the account is deleted.
- Show ratings only from `ratingSummary` (approved comments).
## Don't
- Don't store or show guest emails; don't render comment bodies as Markdown/HTML.
- Don't auto-approve guests to "increase engagement".
- Don't expose `pending`, `spam` or `deleted` comments in public endpoints.
L’arborescence exacte qui sera injectée, après .genpmignore. Épinglée à
// Recurso de panel: cola de moderación (por defecto `pending`) con aprobar, spam, borrar y bloquear al autor.
import { and, count, desc, eq, ilike, type SQL, sql } from 'drizzle-orm';
import { z } from 'zod';
import type { AdminContext, AdminResource } from '../contracts/index.ts';
import { getDb } from '../db/index.ts';
import { ban, CommentError, moderate } from './comments.ts';
import { type Comment, comments } from './schema.ts';
async function need(ctx: AdminContext, perm: string) {
if (!(await ctx.can(perm))) throw new CommentError('forbidden', `forbidden: ${perm}`);
}
const act = (name: string, label: string, run: (id: string) => Promise<Comment>, available?: (c: Comment) => boolean, confirm = false) => ({
name,
label,
permission: 'comments:moderate',
confirm,
available,
async run(id: string, _input: unknown, ctx: AdminContext) {
await need(ctx, 'comments:moderate');
return run(id);
},
});
export const commentsAdminResource: AdminResource<Comment> = {
name: 'comments',
label: { singular: 'Comment', plural: 'Comments' },
group: 'Community',
fields: [
{ name: 'authorName', label: 'Author', type: 'text', readOnly: true, list: true },
{ name: 'body', label: 'Comment', type: 'textarea', readOnly: true, list: true },
{ name: 'rating', label: 'Rating', type: 'number', readOnly: true },
{ name: 'entityType', label: 'On', type: 'text', readOnly: true, list: true },
{ name: 'entityId', label: 'Item', type: 'text', readOnly: true },
{ name: 'status', label: 'Status', type: 'select', readOnly: true, list: true, options: ['pending', 'approved', 'spam', 'deleted'].map((v) => ({ value: v, label: v })) },
{ name: 'createdAt', label: 'Date', type: 'datetime', readOnly: true, list: true },
],
input: z.object({}),
title: (c) => `${c.authorName}: ${c.body.slice(0, 60)}`,
async list(q, ctx) {
await need(ctx, 'comments:read');
const conds: SQL[] = [sql`${comments.status} = ${q.filters?.status ?? 'pending'}`];
if (q.filters?.entityType) conds.push(eq(comments.entityType, q.filters.entityType));
if (q.filters?.entityId) conds.push(eq(comments.entityId, q.filters.entityId));
if (q.search) conds.push(ilike(comments.body, `%${q.search.replace(/[%_\\]/g, (m) => `\\${m}`)}%`));
const where = and(...conds);
const [total] = await getDb().select({ n: count() }).from(comments).where(where);
const rows = await getDb().select().from(comments).where(where).orderBy(desc(comments.createdAt)).limit(q.pageSize).offset((Math.max(q.page, 1) - 1) * q.pageSize);
return { rows, total: total?.n ?? 0 };
},
async get(id, ctx) {
await need(ctx, 'comments:read');
const [row] = await getDb().select().from(comments).where(eq(comments.id, id));
return row ?? null;
},
actions: [
act('approve', 'Approve', (id) => moderate(id, 'approve'), (c) => c.status !== 'approved'),
act('spam', 'Spam', (id) => moderate(id, 'spam'), (c) => c.status !== 'spam'),
act('delete', 'Delete', (id) => moderate(id, 'delete'), (c) => c.status !== 'deleted', true),
act('restore', 'Back to queue', (id) => moderate(id, 'restore'), (c) => c.status === 'spam' || c.status === 'deleted'),
act(
'ban',
'Ban author',
async (id) => {
await ban({ commentId: id });
return moderate(id, 'spam');
},
undefined,
true,
),
],
};
Ce paquet ne déclare aucun serveur MCP.
| Version | Commit | Publié | Analyse |
|---|---|---|---|
| 1.0.1 | abfbbe8 | il y a 5 heures | analyse réussie |
- genpm
- @core/antispam ^1.0.0@core/auth ^1.1.0@core/contracts ^1.0.0@core/db ^1.0.0@core/email ^1.0.1@core/jobs ^1.0.0
- npm
- zod ^4.0.0
- proposé
- GenPM propose la commande npm et ne l’exécute que si vous acceptez.
- Utilisé par (2)
- @core/kit-blog ^1.0.0@core/reviews ^1.0.0
- analyse
- analyse réussie · 0 problème
- commit
- v1.0.1 → abfbbe8ddb3706a9640b94d9dea43a45e70da1a9 · vérifié après téléchargement
- scripts
- Aucun. GenPM n’exécute jamais le code des paquets.
- licence
- MIT
- Qualité
- 100/100
- Licence reconnuevalidé
- AGENTS.md explique son objectifvalidé
- AGENTS.md donne les étapes d’intégrationvalidé
- AGENTS.md liste conventions ou interditsvalidé
- Contient des testsvalidé
- Analyse de sécurité réussievalidé
- Publié au cours des 6 derniers moisvalidé
- Éditeur vérifiévalidé
- Résumé et mots-clésvalidé
- signalement
- Vous avez repéré un problème ?