apps/starter が参照実装である。新イベントは starter をコピーして以下の6ファイルを埋めるだけで、admin(共有パスワード認証)・schema 駆動 CMS・セッションカードスタジオが動く。
1. workspace を追加する
apps/<event>/package.json を作る。@event/ は workspace 解決("")で参照する。
{
"name": "my-event",
"scripts": { "dev": "next dev -p 3401", "build": "next build", "typecheck": "tsc --noEmit" },
"dependencies": { "@event/fours-sdk": "*", "@event/schema": "*", "next": "^15.2.3", "react": "^19.2.0", "react-dom": "^19.2.0" }
}
2. next.config — createNextConfig + transpilePackages
// next.config.ts
import { createNextConfig } from "@event/core";
// distDir 分離 / dev memory cache / removeConsole / セキュリティヘッダ(CSP 込み) が既定で付く
const nextConfig = createNextConfig({
transpilePackages: [
"@event/schema", "@event/fours-sdk", "@event/core", "@event/cms",
"@event/session-card", "@event/shell", "@event/asset-kit",
],
security: { csp: { fourS: true, vercelBlob: true } },
});
export default nextConfig;
3. adminAuth と境界 middleware
cookie 名はイベントごとに一意にする。middleware は「境界」のみで、認可の再検査は各 server action / route が行う。
// src/lib/adminAuth.ts
import { createSharedPasswordAuth } from "@event/core";
export const adminAuth = createSharedPasswordAuth({ cookieName: "starter_admin_session" });
// src/middleware.ts
import { createAuthGateMiddleware } from "@event/shell";
export const middleware = createAuthGateMiddleware({
cookieName: "starter_admin_session",
bypassPrefixes: ["/api/auth/"],
});
export const config = { matcher: ["/admin/:path*", "/api/admin/:path*"] };
ログイン route は adminAuth.verifyPassword() + 失敗時 loginJitter()、dev 用ワンクリックログインは createDevLoginHandler({ cookieName }) を /login/dev に mount する(第37章)。
4. CMS registry — schema を足すだけで admin が生える
// src/lib/cms.ts(抜粋)
import { createContentStore } from "@event/core";
import { createCmsActions, createContentReader, createResourceRegistry } from "@event/cms";
const NEWS_SCHEMA: ResourceSchema = {
name: "news", label: "News (お知らせ)", shape: "list", primaryLabel: "title",
csvImport: { help: "列順: title<TAB>slug<TAB>body", headers: ["title", "slug", "body"] },
fields: [
{ type: "datetime", name: "publishedAt", label: "公開日時" },
{ type: "text", name: "title", label: "タイトル", required: true },
{ type: "image", name: "coverImageUrl", label: "カバー画像", aspect: "wide" },
{ type: "markdown", name: "body", label: "本文", rows: 10 },
{ type: "boolean", name: "visible", label: "公開する" },
],
};
export const registry = createResourceRegistry([NEWS_SCHEMA, SETTINGS_SCHEMA]);
export const reader = createContentReader();
export const store = createContentStore({ commitPrefix: "chore(starter-cms)" });
export const cms = createCmsActions({ registry, store, reader, requireAuth: requireAdmin });
/admin/cms/[resource] は registry から schema を引くだけの1ページで全リソースを賄う(第13章)。
5. nav registry
// src/lib/nav.ts — 中央配列のハードコード禁止。新機能はここに1行足すだけ
import { createNavRegistry } from "@event/shell";
export const nav = createNavRegistry([
{ id: "dash", group: "Overview", label: "ダッシュボード", href: "/admin", code: "DASH", order: 1 },
{ id: "cms-news", group: "コンテンツ", label: "News", href: "/admin/cms/news", code: "NEWS", order: 1 },
{ id: "card-studio", group: "ビジュアル", label: "セッションカード", href: "/admin/card", code: "CARD", order: 1 },
]);
admin layout で <AdminNav groups={nav.groups(() => true)} /> を <Suspense> に包んで描画する。
6. env を設定して起動
# .env.local — 最小構成
ADMIN_PASSWORD=... # admin 共有パスワード
FOURS_EVENT_SLUG=ivs26 # 4S イベント slug
FOURS_PUBLIC_READ=1 # 公開読み取りモード
# 本番のみ: GITHUB_TOKEN / GITHUB_REPO_OWNER / GITHUB_REPO_NAME(contentStore commit)
# 画像アップロードを使うなら: BLOB_READ_WRITE_TOKEN
yarn dev:starter # → http://localhost:3401
dev では contentStore が content/*.json へ fs 書き込みし、本番では同じコードが Octokit commit → Vercel rebuild に切り替わる(第14章)。全 env の一覧は第4章を参照。