AdminResource から生成する管理画面:一覧、フォーム、アクション、ダッシュボード、RBAC、CSRF 対策、監査ログ
インストール
genpm add @core/admin含まれるもの
- src/lib/admin/ にソースコード(12 ファイル)。 (56.9 KB)
- src/lib/admin/AGENTS.md に AI ルール、加えて IDE 用のルールファイル。
- @core/auth, @core/contracts, @core/db, @core/rbac, @core/ui を自動で解決します。
README
このパッケージには README がありません。
これは AI が src/lib/admin で作業するときに読む内容そのものです。それ以外はコンテキストに追加されません。
@core/admin — rules for AI agents
Purpose
Admin panel generated from AdminResource descriptions (from @core/contracts): navigation, dashboard widgets,
searchable/sortable lists, create/edit forms, record actions with confirmation, and delete. No per-resource screens:
any module that exports an AdminResource appears in the panel. Server side: session from @core/auth, permissions
from @core/rbac (admin:access + <resource>:<action>), CSRF protection on writes and an audit log of every change.
Table: admin_audit.
Map
index.ts— server API:defineAdmin,handleAdminApi,adminContextFor,listAudit,auditAdminResource,countWidget,listWidget,fieldsFromZod.client.ts—'use client':AdminApp,createAdminApi,FieldControl,defaultLabels, renderer types.admin.css— layout on @core/ui tokens.constants.ts—ADMIN_HEADER(shared by server and client).adapters/hono.ts—adminRoutes(admin).adapters/next.ts—adminRouteHandlers(admin).
Integration
- Install @core/auth, @core/rbac and @core/ui first; run migrations as in
src/lib/db/AGENTS.md(admin_audit). - Create
src/genpm/admin.ts:
Every installed module'simport { defineAdmin, auditAdminResource } from '@/lib/admin'; import { mediaAdminResource } from '@/lib/media'; export const admin = defineAdmin({ title: 'My site', resources: [mediaAdminResource, auditAdminResource], widgets: [] });AGENTS.mdsays which resources to add here. - API route. Next:
app/api/admin/[...path]/route.ts→export const { GET, POST, PUT, DELETE } = adminRouteHandlers(admin); export const dynamic = 'force-dynamic';Hono:app.route('/api/admin', adminRoutes(admin)). - Page. Next:
app/admin/[[...path]]/page.tsxrenders a client component that imports../lib/ui/ui.css,../lib/admin/admin.cssand returns<AdminApp basePath="/admin" path={(await params).path?.join('/') ?? ''} />. Protect the page itself too (redirect to sign-in when there is no session) and addrobots: { index: false }. - Rich text, media pickers or blocks: pass
renderers={{ richText: MyEditorField, media: MyMediaPicker }}. Without them those fields fall back to a JSON textarea / relation select. - Translate the UI with
labels={{ save: t('admin.save'), … }}(keys:defaultLabels). - Verify: a user without
admin:accessgets 403 on/api/admin/schema; an editor sees only their resources; an edit creates a row inadmin_auditwith before/after.
Conventions
- Resources enforce permissions themselves (
ctx.can(...)); the panel hides what the user cannot do but the server never trusts that. Use<resource>:<action>:ownfor author-only rules. - Field names may be nested (
data.title); read-only fields are shown but never sent. - Money fields hold integer minor units (or
{ amount, currency }); the form edits decimals. - Actions that change state declare
available(row)so the panel only offers them when valid.
Don't
- Don't call the admin API from other origins or without the
x-admin-request: 1header (requests are rejected). - Don't write per-resource admin pages; extend the
AdminResourceor add a field renderer instead. - Don't put secrets or full payment data in resource rows: the audit log stores before/after snapshots.
- Don't expose
/adminin the sitemap or let it be indexed.
# @core/admin — rules for AI agents
## Purpose
Admin panel generated from `AdminResource` descriptions (from @core/contracts): navigation, dashboard widgets,
searchable/sortable lists, create/edit forms, record actions with confirmation, and delete. No per-resource screens:
any module that exports an `AdminResource` appears in the panel. Server side: session from @core/auth, permissions
from @core/rbac (`admin:access` + `<resource>:<action>`), CSRF protection on writes and an audit log of every change.
Table: `admin_audit`.
## Map
- `index.ts` — server API: `defineAdmin`, `handleAdminApi`, `adminContextFor`, `listAudit`, `auditAdminResource`, `countWidget`, `listWidget`, `fieldsFromZod`.
- `client.ts` — `'use client'`: `AdminApp`, `createAdminApi`, `FieldControl`, `defaultLabels`, renderer types.
- `admin.css` — layout on @core/ui tokens. `constants.ts` — `ADMIN_HEADER` (shared by server and client).
- `adapters/hono.ts` — `adminRoutes(admin)`. `adapters/next.ts` — `adminRouteHandlers(admin)`.
## Integration
1. Install @core/auth, @core/rbac and @core/ui first; run migrations as in `src/lib/db/AGENTS.md` (`admin_audit`).
2. Create `src/genpm/admin.ts`:
```ts
import { defineAdmin, auditAdminResource } from '@/lib/admin';
import { mediaAdminResource } from '@/lib/media';
export const admin = defineAdmin({ title: 'My site', resources: [mediaAdminResource, auditAdminResource], widgets: [] });
```
Every installed module's `AGENTS.md` says which resources to add here.
3. API route. Next: `app/api/admin/[...path]/route.ts` →
`export const { GET, POST, PUT, DELETE } = adminRouteHandlers(admin); export const dynamic = 'force-dynamic';`
Hono: `app.route('/api/admin', adminRoutes(admin))`.
4. Page. Next: `app/admin/[[...path]]/page.tsx` renders a client component that imports `../lib/ui/ui.css`,
`../lib/admin/admin.css` and returns `<AdminApp basePath="/admin" path={(await params).path?.join('/') ?? ''} />`.
Protect the page itself too (redirect to sign-in when there is no session) and add `robots: { index: false }`.
5. Rich text, media pickers or blocks: pass `renderers={{ richText: MyEditorField, media: MyMediaPicker }}`.
Without them those fields fall back to a JSON textarea / relation select.
6. Translate the UI with `labels={{ save: t('admin.save'), … }}` (keys: `defaultLabels`).
7. Verify: a user without `admin:access` gets 403 on `/api/admin/schema`; an editor sees only their resources; an edit
creates a row in `admin_audit` with before/after.
## Conventions
- Resources enforce permissions themselves (`ctx.can(...)`); the panel hides what the user cannot do but the server
never trusts that. Use `<resource>:<action>:own` for author-only rules.
- Field names may be nested (`data.title`); read-only fields are shown but never sent.
- Money fields hold integer minor units (or `{ amount, currency }`); the form edits decimals.
- Actions that change state declare `available(row)` so the panel only offers them when valid.
## Don't
- Don't call the admin API from other origins or without the `x-admin-request: 1` header (requests are rejected).
- Don't write per-resource admin pages; extend the `AdminResource` or add a field renderer instead.
- Don't put secrets or full payment data in resource rows: the audit log stores before/after snapshots.
- Don't expose `/admin` in the sitemap or let it be indexed.
.genpmignore 適用後に組み込まれる正確なツリーです。固定先:
/* Maquetación del panel sobre los tokens de @core/ui (importa antes ../ui/ui.css). */
.admin { display: grid; grid-template-columns: 15rem 1fr; min-height: 100vh; font-family: var(--ui-font); color: var(--ui-text); background: var(--ui-bg); line-height: 1.5; }
.admin :focus-visible { outline: 2px solid var(--ui-focus); outline-offset: 2px; }
.admin-nav { background: var(--ui-surface); border-inline-end: 1px solid var(--ui-border); padding: 1rem; }
.admin-nav ul { list-style: none; margin: 0 0 1rem; padding: 0; }
.admin-nav a { display: block; padding: 0.375rem 0.5rem; border-radius: var(--ui-radius); color: inherit; text-decoration: none; }
.admin-nav a[aria-current="page"] { background: var(--ui-bg); font-weight: 600; }
.admin-nav__title { font-weight: 700; margin: 0 0 1rem; }
.admin-nav__group { color: var(--ui-muted); font-size: 0.75rem; text-transform: uppercase; margin: 0 0 0.25rem; }
.admin-main { padding: 1.5rem 2rem; max-width: 72rem; outline: none; }
.admin-header { display: flex; justify-content: space-between; align-items: center; gap: 1rem; }
.admin-search { display: flex; gap: 0.5rem; align-items: start; max-width: 28rem; }
.admin-widgets { display: grid; grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr)); gap: 1rem; }
.admin-widget__value { font-size: 2rem; font-weight: 700; margin: 0; }
.admin-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-block: 1rem; }
.admin-form { max-width: 48rem; }
.admin-form__footer { display: flex; gap: 0.5rem; margin-block-start: 1rem; }
@media (max-width: 48rem) { .admin { grid-template-columns: 1fr; } .admin-main { padding: 1rem; } }
.admin-list { border: 1px solid var(--ui-border); border-radius: var(--ui-radius); padding: 1rem; }
.admin-list__item { border: 1px dashed var(--ui-border); border-radius: var(--ui-radius); padding: 0.75rem; margin-block-end: 0.75rem; }
.admin-list__tools { display: flex; gap: 0.25rem; }
このパッケージは MCP サーバーを宣言していません。
| バージョン | コミット | 公開日 | スキャン |
|---|---|---|---|
| 1.0.0 | 3b5bae0 | 4 時間前 | スキャン合格 |
- 提案
- GenPM は npm コマンドを提案し、あなたが承認した場合にのみ実行します。
- スキャン
- スキャン合格 · 指摘 0 件
- コミット
- v1.0.0 → 3b5bae0f3259afef9e31e783556de2dbcff25234 · 取得後に検証済み
- スクリプト
- なし。GenPM はパッケージのコードを実行しません。
- ライセンス
- MIT
- 品質
- 100/100
- 認識されたライセンス達成
- AGENTS.md に目的の説明がある達成
- AGENTS.md に統合手順がある達成
- AGENTS.md に規約や禁止事項がある達成
- テストを含む達成
- セキュリティスキャンに合格達成
- 過去 6 か月以内に公開達成
- 認証済みの公開者達成
- 概要とキーワード達成
- 報告
- 問題を見つけましたか?