Kit de site autogerenciável para Next.js: /admin para editar páginas com blocos, mídia, menus, ajustes e usuários
Instalar
genpm add @core/kit-cmsO que você recebe
- Código em src/app/(cms)/, 38 arquivos. (39 kB)
- Regras de IA em src/app/(cms)/AGENTS.md, mais arquivos de regras da IDE.
- Resolve @core/admin, @core/blocks, @core/content, @core/forms, @core/media, @core/rich-text, @core/search, @core/seo, @core/site para você.
README
Este pacote não tem README.
Isto é exatamente o que sua IA lê quando trabalha em src/app/(cms). Nada mais é adicionado ao contexto dela.
@core/kit-cms — rules for AI agents
Purpose
Turns a Next.js (App Router) site into a self-managed one: /admin where non-technical people edit pages built with
blocks, images, menus, site settings, forms, redirects and users, with drafts, signed previews, scheduled publishing
and roles. Glue only: all logic lives in @core/admin, content, media, rich-text, blocks, site, seo, forms, search,
auth and rbac (the last two arrive through @core/admin). Public routes: pages created in the admin (/[...slug]), /search, /sitemap.xml, /robots.txt,
/llms.txt. Base for kit-blog, kit-landing and kit-store.
Map
admin/[[...path]]/page.tsx— panel (server checks session +admin:access).admin/login/page.tsx— OAuth sign-in.api/admin/[...path]— admin API.api/media/uploads…— uploads.api/preview,api/exit-preview— draft mode.api/forms/[key]— form posts.api/cron— @core/jobs.api/search— JSON search.[...slug]/page.tsx— pages of thepagescollection (redirects before the 404).search/page.tsx— results._lib/page.tsx—CmsPage,pageMetadata._lib/home.tsx— home page from thehomeentry._lib/session.ts._components/— admin client app and themedia/richTextfield editors._templates/genpm/*.ts— the project's registration files._templates/middleware.ts— redirects middleware._templates/layout.tsx— root layout for a new site._templates/env.example— every variable the kit and its packages read._lib/maintenance.ts— dailycms.maintenancejob (prunes rate limits and old jobs), loaded byapi/cron._scripts/create-owner.ts— gives the first signed-in user theownerrole (and creates the default roles)._scripts/setup-schedules.ts— schedules the daily jobs of the installed kits (cms.maintenance,search.reindex,store.maintenance,experiments.prune) and queues the first reindex.
Integration
- Check Next ≥ 15.5 with App Router,
src/appand the@/*→src/*alias. Withoutsrc/: install with--dest app/(cms)and adapt@/imports. GenPM packages import each other with.tsextensions:tsconfig.jsonneeds"allowImportingTsExtensions": true(Next projects already have"noEmit": true). Works with webpack and Turbopack. Innext.configsethtmlLimitedBots: /.*/: metadata comes from the database, and without it Next 15 streams<title>, description and canonical into<body>for every client except a few bots (Lighthouse, link previews and some crawlers miss them). - Env: copy
_templates/env.exampleto the project's.env.example(merge if it exists; Next's default.gitignorehas.env*, so add a line!.env.example) and the real values to.env.local. Required:DATABASE_URL,AUTH_SECRET, one OAuth provider (GITHUB_CLIENT_ID/SECRETorGOOGLE_…),STORAGE_*,EMAIL_FROM+RESEND_API_KEY,SITE_URL,CONTENT_PREVIEW_SECRET(≥ 32 chars),ANTISPAM_SECRET,CRON_SECRET. WithSTORAGE_DRIVER=localalso mount the storage route (src/lib/storage/AGENTS.md, Integration 1). - Mount the @core/auth routes (
app/auth/login/[provider],app/auth/callback/[provider],app/auth/logout) as its AGENTS.md says. - Copy
_templates/genpm/*.tstosrc/genpm/(skip files that exist and merge by hand). These files belong to the project: later kits add lines to them. Copy_templates/middleware.tstosrc/middleware.ts(or merge into the existing one). - Migrations: GenPM does not install dev tools — run
src/lib/db/AGENTS.mdsteps 3–5 (drizzle-kit+tsx, thedb:generate/db:migratescripts), generate one migration for all packages and apply it where a database exists. - Schedule
GET /api/cronevery minute withAuthorization: Bearer $CRON_SECRET(e.g.vercel.jsoncrons). Once the database is migrated, the person runsnpx tsx "src/app/(cms)/_scripts/setup-schedules.ts"(idempotent; it schedules the daily jobs of every installed kit, so run it again after adding another kit). - The person signs in once at
/admin/login, then runsnpx tsx "src/app/(cms)/_scripts/create-owner.ts" <their email>. Never run it for them with an email they did not give you. - New site: create
src/app/page.tsxwithexport { default, generateMetadata } from './(cms)/_lib/home';and create thehomepage in the admin (blocks: hero, features, cta…). Existing site: follow "Retrofit" below. - Root layout: new site → copy
_templates/layout.tsxtosrc/app/layout.tsx. Existing layout → import@/lib/ui/ui.cssand@/lib/blocks/blocks.css, render menus withgetMenu('header')/getMenu('footer')and the site name withgetSiteSettings()(@core/site), and addexport const dynamic = 'force-dynamic'(it reads the database; without itnext buildtries to prerender and fails withoutDATABASE_URL). - Verify:
npx tsc --noEmitandnpx next buildpass without a database. Then, with one: sign in at/admin, edit a page, open "Preview", publish, and see the change on the public URL. - Delete every
src/lib/*/adapters/hono.ts(rm src/lib/*/adapters/hono.ts): they importhono, which a Next project does not install, andtscchecks every file. Keepadapters/next.ts.
Retrofit (make an existing site editable)
Follow the @core/content procedure page by page, one commit per page: inventory visible literals → a singleton per
page (or a collection when repeated) defined in src/genpm/content.ts → seed with the current values (seedEntry) →
replace literals with typed reads → compare the rendered HTML before/after (must be identical). Keep markup and design;
only the data source changes: skip className="ui-root" on <body> and don't add buildMetadata tags the site did not
have (keep its titles and descriptions, now read from the CMS). Local images go to @core/media with their alt text;
SVG (which @core/media rejects) stays in public/ with its path in a text field. Keep the root layout's existing
header/footer markup and feed it from getSiteSettings() / getMenu(). Existing routes keep priority over
[...slug]. Non-Next sites (static HTML, Astro, Vite): out of scope for 1.0 — say so.
Conventions
- Page sections are blocks registered in
src/genpm/blocks.ts; add new blocks there, never per-page components. - Admin resources are registered in
src/genpm/admin.ts; never write a custom admin page for a resource. - The
pagesentry with slughomeis/; other slugs map to/<slug>. - Default roles:
editorpublishes pages but cannot see users or settings beyond content;authoredits only own entries.
Don't
- Don't invent legal texts (privacy, terms, imprint): create the pages empty with a "pending review" note for the owner.
- Don't expose
/adminor/apiin the sitemap, and don't removerobots: noindexfrom admin pages. - Don't bypass
CRON_SECRETor callrunDue()from a public route. - Don't grant roles or create owners from code paths reachable by visitors.
# @core/kit-cms — rules for AI agents
## Purpose
Turns a Next.js (App Router) site into a self-managed one: `/admin` where non-technical people edit pages built with
blocks, images, menus, site settings, forms, redirects and users, with drafts, signed previews, scheduled publishing
and roles. Glue only: all logic lives in @core/admin, content, media, rich-text, blocks, site, seo, forms, search,
auth and rbac (the last two arrive through @core/admin). Public routes: pages created in the admin (`/[...slug]`), `/search`, `/sitemap.xml`, `/robots.txt`,
`/llms.txt`. Base for kit-blog, kit-landing and kit-store.
## Map
- `admin/[[...path]]/page.tsx` — panel (server checks session + `admin:access`). `admin/login/page.tsx` — OAuth sign-in.
- `api/admin/[...path]` — admin API. `api/media/uploads…` — uploads. `api/preview`, `api/exit-preview` — draft mode.
`api/forms/[key]` — form posts. `api/cron` — @core/jobs. `api/search` — JSON search.
- `[...slug]/page.tsx` — pages of the `pages` collection (redirects before the 404). `search/page.tsx` — results.
- `_lib/page.tsx` — `CmsPage`, `pageMetadata`. `_lib/home.tsx` — home page from the `home` entry. `_lib/session.ts`.
- `_components/` — admin client app and the `media` / `richText` field editors.
- `_templates/genpm/*.ts` — the project's registration files. `_templates/middleware.ts` — redirects middleware.
`_templates/layout.tsx` — root layout for a new site. `_templates/env.example` — every variable the kit and its packages read.
- `_lib/maintenance.ts` — daily `cms.maintenance` job (prunes rate limits and old jobs), loaded by `api/cron`.
- `_scripts/create-owner.ts` — gives the first signed-in user the `owner` role (and creates the default roles).
- `_scripts/setup-schedules.ts` — schedules the daily jobs of the installed kits (`cms.maintenance`, `search.reindex`,
`store.maintenance`, `experiments.prune`) and queues the first reindex.
## Integration
1. Check Next ≥ 15.5 with App Router, `src/app` and the `@/*` → `src/*` alias. Without `src/`: install with `--dest app/(cms)` and adapt `@/` imports.
GenPM packages import each other with `.ts` extensions: `tsconfig.json` needs `"allowImportingTsExtensions": true`
(Next projects already have `"noEmit": true`). Works with webpack and Turbopack. In `next.config` set
`htmlLimitedBots: /.*/`: metadata comes from the database, and without it Next 15 streams `<title>`, description and
canonical into `<body>` for every client except a few bots (Lighthouse, link previews and some crawlers miss them).
2. Env: copy `_templates/env.example` to the project's `.env.example` (merge if it exists; Next's default `.gitignore`
has `.env*`, so add a line `!.env.example`) and the real values to `.env.local`. Required: `DATABASE_URL`, `AUTH_SECRET`, one OAuth provider
(`GITHUB_CLIENT_ID/SECRET` or `GOOGLE_…`), `STORAGE_*`, `EMAIL_FROM` + `RESEND_API_KEY`, `SITE_URL`,
`CONTENT_PREVIEW_SECRET` (≥ 32 chars), `ANTISPAM_SECRET`, `CRON_SECRET`. With `STORAGE_DRIVER=local` also mount the
storage route (`src/lib/storage/AGENTS.md`, Integration 1).
3. Mount the @core/auth routes (`app/auth/login/[provider]`, `app/auth/callback/[provider]`, `app/auth/logout`) as its AGENTS.md says.
4. Copy `_templates/genpm/*.ts` to `src/genpm/` (skip files that exist and merge by hand). These files belong to the project:
later kits add lines to them. Copy `_templates/middleware.ts` to `src/middleware.ts` (or merge into the existing one).
5. Migrations: GenPM does not install dev tools — run `src/lib/db/AGENTS.md` steps 3–5 (`drizzle-kit` + `tsx`, the
`db:generate` / `db:migrate` scripts), generate one migration for all packages and apply it where a database exists.
6. Schedule `GET /api/cron` every minute with `Authorization: Bearer $CRON_SECRET` (e.g. `vercel.json` crons). Once the
database is migrated, the person runs `npx tsx "src/app/(cms)/_scripts/setup-schedules.ts"` (idempotent; it schedules
the daily jobs of every installed kit, so run it again after adding another kit).
7. The person signs in once at `/admin/login`, then runs `npx tsx "src/app/(cms)/_scripts/create-owner.ts" <their email>`.
Never run it for them with an email they did not give you.
8. New site: create `src/app/page.tsx` with `export { default, generateMetadata } from './(cms)/_lib/home';` and create
the `home` page in the admin (blocks: hero, features, cta…). Existing site: follow "Retrofit" below.
9. Root layout: new site → copy `_templates/layout.tsx` to `src/app/layout.tsx`. Existing layout → import `@/lib/ui/ui.css`
and `@/lib/blocks/blocks.css`, render menus with `getMenu('header')` / `getMenu('footer')` and the site name with
`getSiteSettings()` (@core/site), and add `export const dynamic = 'force-dynamic'` (it reads the database; without it
`next build` tries to prerender and fails without `DATABASE_URL`).
10. Verify: `npx tsc --noEmit` and `npx next build` pass without a database. Then, with one: sign in at `/admin`, edit a
page, open "Preview", publish, and see the change on the public URL.
11. Delete every `src/lib/*/adapters/hono.ts` (`rm src/lib/*/adapters/hono.ts`): they import `hono`, which a Next
project does not install, and `tsc` checks every file. Keep `adapters/next.ts`.
## Retrofit (make an existing site editable)
Follow the @core/content procedure page by page, one commit per page: inventory visible literals → a singleton per
page (or a collection when repeated) defined in `src/genpm/content.ts` → seed with the current values (`seedEntry`) →
replace literals with typed reads → compare the rendered HTML before/after (must be identical). Keep markup and design;
only the data source changes: skip `className="ui-root"` on `<body>` and don't add `buildMetadata` tags the site did not
have (keep its titles and descriptions, now read from the CMS). Local images go to @core/media with their alt text;
SVG (which @core/media rejects) stays in `public/` with its path in a text field. Keep the root layout's existing
header/footer markup and feed it from `getSiteSettings()` / `getMenu()`. Existing routes keep priority over
`[...slug]`. Non-Next sites (static HTML, Astro, Vite): out of scope for 1.0 — say so.
## Conventions
- Page sections are blocks registered in `src/genpm/blocks.ts`; add new blocks there, never per-page components.
- Admin resources are registered in `src/genpm/admin.ts`; never write a custom admin page for a resource.
- The `pages` entry with slug `home` is `/`; other slugs map to `/<slug>`.
- Default roles: `editor` publishes pages but cannot see users or settings beyond content; `author` edits only own entries.
## Don't
- Don't invent legal texts (privacy, terms, imprint): create the pages empty with a "pending review" note for the owner.
- Don't expose `/admin` or `/api` in the sitemap, and don't remove `robots: noindex` from admin pages.
- Don't bypass `CRON_SECRET` or call `runDue()` from a public route.
- Don't grant roles or create owners from code paths reachable by visitors.
A árvore exata que será injetada, após o .genpmignore. Fixada em
// Render de una página de la colección `pages` con sus bloques. Lo usan el catch-all y la portada del proyecto
// (`app/page.tsx` → `export { default, generateMetadata } from './(cms)/_lib/home'`).
import type { Metadata } from 'next';
import { draftMode } from 'next/headers';
import type { ReactNode } from 'react';
import { admin as _registered, blocks, pagePath } from '@/genpm';
import { type BlockInstance, RenderBlocks } from '@/lib/blocks';
import { loadBlockMedia } from '@/lib/blocks/media';
import { getEntry } from '@/lib/content';
import { Form } from '@/lib/forms';
import { buildMetadata, type PageSeo } from '@/lib/seo';
void _registered; // importar @/genpm registra colecciones, formularios y fuentes
type PageData = { title: string; blocks: BlockInstance[]; seo?: { title?: string; description?: string; image?: string; noindex?: boolean } };
export type SearchParams = Record<string, string | string[] | undefined>;
export async function loadPage(slug: string) {
const { isEnabled } = await draftMode();
return getEntry<PageData>('pages', slug, { draft: isEnabled });
}
export async function pageMetadata(slug: string): Promise<Metadata> {
const page = await loadPage(slug);
if (!page) return {};
const seo: PageSeo = { title: page.data.seo?.title ?? page.data.title, path: pagePath(page), locale: page.locale };
if (page.data.seo?.description) seo.description = page.data.seo.description;
if (page.data.seo?.image) seo.image = page.data.seo.image;
if (page.data.seo?.noindex || page.status !== 'published') seo.noindex = true;
return (await buildMetadata(seo)) as Metadata;
}
/** Página completa, o null si no existe (la ruta decide entre 404 y redirección). */
export async function CmsPage(p: { slug: string; searchParams?: SearchParams; embeds?: 'link' | 'iframe' }): Promise<ReactNode> {
const page = await loadPage(p.slug);
if (!page) return null;
const media = await loadBlockMedia(page.data.blocks, blocks);
return (
<RenderBlocks
blocks={page.data.blocks}
registry={blocks}
media={media}
embeds={p.embeds ?? 'link'}
renderForm={(key) => <Form formKey={key} searchParams={p.searchParams ?? {}} />}
/>
);
}
Este pacote não declara servidores MCP.
| Versão | Commit | Publicado | Análise |
|---|---|---|---|
| 1.0.1 | bb5d8e0 | há 4 horas | análise aprovada |
- genpm
- @core/admin ^1.0.0@core/blocks ^1.0.0@core/content ^1.1.0@core/forms ^1.0.0@core/media ^1.0.0@core/rich-text ^1.0.0@core/search ^1.0.0@core/seo ^1.0.0@core/site ^1.0.0
- proposto
- O GenPM propõe o comando npm e só o executa se você disser sim.
- análise
- análise aprovada · 0 achados
- commit
- v1.0.1 → bb5d8e0aed9a7a877e1234a71887c1c33af7acea · verificado após o download
- scripts
- Nenhum. O GenPM nunca executa código de pacotes.
- licença
- MIT
- Qualidade
- 100/100
- Licença reconhecidacumprido
- AGENTS.md explica o propósitocumprido
- AGENTS.md tem passos de integraçãocumprido
- AGENTS.md lista convenções ou proibiçõescumprido
- Inclui testescumprido
- Escaneamento de segurança aprovadocumprido
- Publicado nos últimos 6 mesescumprido
- Publicador verificadocumprido
- Resumo e palavras-chavecumprido
- denúncia
- Viu algo errado?