任意实体的审核评论:访客或用户、回复、1–5 评分、封禁、GDPR 删除和安全 HTML
代码9 个文件上下文约 552 个 token扫描通过
安装
$
genpm add @core/comments你将获得
- 源代码位于 src/lib/comments/,共 9 个文件。 (23 kB)
- AI 规则位于 src/lib/comments/AGENTS.md,另附 IDE 规则文件。
- 添加到 .env.example 的环境变量:COMMENTS_NOTIFY_TO。
- 自动为你解析 @core/antispam, @core/auth, @core/contracts, @core/db, @core/email, @core/jobs。
README
此包没有 README。
约 552 个 token→ src/lib/comments/AGENTS.md→ .cursor/rules/genpm-core-comments.mdc
这正是你的 AI 在 src/lib/comments 中工作时读取的内容。不会向其上下文添加其他任何内容。
@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.
应用 .genpmignore 后将被注入的确切目录树。固定于
// Handlers comunes: GET lista aprobada, POST nuevo comentario (form-data o JSON).
import { CommentError, listComments, type Moderation, postComment } from '../index.ts';
export type CurrentUser = (req: Request) => Promise<{ id: string; name: string | null } | null>;
const STATUS = { spam: 400, rate_limited: 429, invalid: 422, banned: 403, not_found: 404, forbidden: 403 } as const;
/** Envío HTML clásico (sin JavaScript) desde una página del mismo sitio: URL a la que volver, o null. */
function backUrl(req: Request, isJson: boolean): URL | null {
if (isJson || !(req.headers.get('accept') ?? '').includes('text/html')) return null;
const ref = req.headers.get('referer');
if (!ref) return null;
try {
const u = new URL(ref);
return u.host === new URL(req.url).host ? u : null;
} catch {
return null;
}
}
export async function handleList(req: Request, entityType: string, entityId: string): Promise<Response> {
const page = Number(new URL(req.url).searchParams.get('page') ?? 1) || 1;
return Response.json(await listComments(entityType, entityId, { page }), { headers: { 'cache-control': 'public, max-age=30' } });
}
export async function handlePost(req: Request, entityType: string, entityId: string, opts: { currentUser: CurrentUser; moderation?: Moderation }): Promise<Response> {
const isJson = (req.headers.get('content-type') ?? '').includes('application/json');
const fields: Record<string, unknown> = {};
if (isJson) Object.assign(fields, await req.json().catch(() => ({})));
else (await req.formData()).forEach((v, k) => typeof v === 'string' && (fields[k] = v));
const user = await opts.currentUser(req);
try {
const c = await postComment(
{
entityType,
entityId,
body: String(fields.body ?? ''),
...(fields.parentId ? { parentId: String(fields.parentId) } : {}),
...(fields.rating ? { rating: Number(fields.rating) } : {}),
user,
...(!user && { guest: { name: String(fields.name ?? ''), email: String(fields.email ?? '') } }),
},
{ headers: req.headers, fields, moderation: opts.moderation },
);
const back = backUrl(req, isJson);
if (back) return seeOther(back, c.status);
return Response.json({ id: c.id, status: c.status }, { status: 201 });
} catch (e) {
if (e instanceof CommentError) {
const back = backUrl(req, isJson);
return back ? seeOther(back, e.code) : Response.json({ error: e.code }, { status: STATUS[e.code] });
}
throw e;
}
}
/** Vuelve a la página con `?comment=<estado|error>#comments` (la plantilla muestra el aviso). */
function seeOther(back: URL, result: string): Response {
back.searchParams.set('comment', result);
back.hash = 'comments';
return new Response(null, { status: 303, headers: { location: back.toString() } });
}
此包未声明 MCP 服务器。
| 版本 | 提交 | 发布时间 | 扫描 |
|---|---|---|---|
| 1.0.1 | abfbbe8 | 3小时前 | 扫描通过 |
- 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
- 建议
- GenPM 会给出 npm 命令建议,只有你同意时才会运行。
- 扫描
- 扫描通过 · 0 个问题
- 提交
- v1.0.1 → abfbbe8ddb3706a9640b94d9dea43a45e70da1a9 · 获取后已校验
- 脚本
- 无。GenPM 从不运行包中的代码。
- 许可证
- MIT
- 质量
- 100/100
- 可识别的许可证已满足
- AGENTS.md 说明了用途已满足
- AGENTS.md 包含集成步骤已满足
- AGENTS.md 列出约定或禁止事项已满足
- 包含测试已满足
- 通过安全扫描已满足
- 最近 6 个月内发布已满足
- 已验证的发布者已满足
- 摘要和关键词已满足
- 举报
- 发现问题了吗?