Next.js App Router skeleton with shadcn/ui and Notion-based CMS sync.
Preset reference:
- style:
lyra(radix-lyra) - theme:
amber - icon library:
hugeicons - font:
jetbrains-mono
npm install
npm run prisma:generate
npm run devOpen http://localhost:3000.
npm run dev
npm run lint
npm run build
npm run prisma:generate
npm run prisma:migrate:dev
npm run prisma:migrate:deploy
npm run setup:notion:cms-db
npm run setup:notion:resume-dbCreate local env first:
cp docker/dev.env.example docker/dev.envdocker compose -f docker-compose.dev.yml up -d --build
docker compose -f docker-compose.dev.yml psServices:
next: Next.js appworker: polls and processes queued Notion sync jobspostgres: PostgreSQL 16
Worker poll config (docker/dev.env):
NOTION_SYNC_ACTIVE_POLL_INTERVAL_MS: poll interval when queue is active (default1000)NOTION_SYNC_IDLE_POLL_INTERVAL_MS: poll interval when queue is idle (default15000)NOTION_SYNC_PUBLISHED_SCHEDULE_ENABLED: enable hourly published-page enqueue (defaulttrue)NOTION_SYNC_PUBLISHED_SCHEDULE_INTERVAL_MS: schedule interval for published-page enqueue (default3600000)NOTION_ENV_DATABASE_ID:NOTION.ENVdatabase id used by Studio Notion settings testNOTION_SOURCE_PAGE_ID: Notion page id where Studio scanschild_databaseblocks for model/source mappingSTUDIO_SESSION_SECRET: session signing secret for Studio auth cookie
Studio login credentials are read from Notion NOTION.ENV (KEY/VALUE):
ADMIN_USER_NAMEADMIN_USER_PWD
Notion setup scripts:
- Set
PARENT_PAGE_IDindocker/dev.env(the parent Notion page where DB will be created) npm run setup:notion:cms-dbcreates the Articles databasenpm run setup:notion:resume-dbcreates the Resume database
Resume data source schema is fixed to:
Name,Section,Group,Location,Summary,Date,Tags,VisibilitySection Order,Group Order,Item Order- Notion built-in fields used by Resume mapping:
page.icon->resume.logopage.created_timepage.last_edited_time
- No legacy fallback fields are supported.
- Data source IDs are managed in Studio Settings (
/studio/settings/notion) and stored in DB.
Health check:
curl http://localhost:${HOST_PORT:-3000}/api/healthgitdockerdocker compose- A cloned repo at your deploy path (example:
/opt/quan-studio)
cp docker/prod.env.example docker/prod.envFill docker/prod.env with real values, especially:
POSTGRES_*DATABASE_URLNOTION_*CLOUDFLARED_TOKENNEXT_PUBLIC_SITE_URLNOTION_SYNC_PUBLISHED_SCHEDULE_*
./scripts/deploy-prod.sh /opt/quan-studio <git_sha_or_tag>This command will:
git fetch+ checkout target ref- Build production image from
Dockerfile - Run
prisma migrate deploy - Start
next + worker + postgres + cloudflared - Run smoke checks:
/api/health/api/public/posts/studio/posts
./scripts/deploy-prod.sh /opt/quan-studio <previous_git_sha>Triggered on push and pull_request to main.
Quality gates:
npm run lintnpm run typechecknpm run prisma:validatenpm run build
Triggered after CI completed with success on main.
Deploys over Tailscale SSH by uploading and executing scripts/deploy-prod.sh.
Required GitHub Secrets:
TS_OAUTH_CLIENT_IDTS_OAUTH_SECRETPROD_SSH_HOSTPROD_SSH_USERPROD_DEPLOY_PATH
CLOUDFLARED_TOKEN is runtime-only and should be set in server docker/prod.env.
Push to main will be blocked locally if required GitHub Secrets are missing.
One-time setup:
npm install
gh auth loginManual check:
npm run check:github-secretsPages:
//resume/blog/blog/[slug]/studio/studio/login/studio/posts/studio/settings/notion
APIs:
GET /api/public/postsGET /api/public/posts/[slug]GET /api/public/resumeGET /api/public/projectsGET /api/public/models/[modelKey]POST /api/studio/auth/loginPOST /api/studio/auth/logoutGET /api/studio/postsGET /api/studio/notion/articlesGET /api/studio/settings/notion/modelsPOST /api/studio/settings/notion/models/refreshPATCH /api/studio/settings/notion/models/select-sourceGET /api/studio/settings/notion/schema-mappingPATCH /api/studio/settings/notion/schema-mappingGET /api/studio/settings/notion/model-definitionsPOST /api/studio/settings/notion/model-definitionsPATCH /api/studio/settings/notion/model-definitions/[modelKey]POST /api/studio/settings/notion/model-definitions/[modelKey]/fieldsPATCH /api/studio/settings/notion/model-definitions/[modelKey]/fields/[fieldKey]DELETE /api/studio/settings/notion/model-definitions/[modelKey]/fields/[fieldKey]POST /api/studio/settings/notion/models/provisionPOST /api/studio/settings/notion/models/migrateGET /api/studio/sync-jobsPOST /api/studio/sync-jobsPOST /api/studio/sync-jobs/process-nextPOST /api/studio/sync-jobs/enqueue-publishedPOST /api/studio/sync-jobs/[id]/retryPOST /api/integrations/notion/webhook/button
Current Notion integration is Studio DB-First and runtime no longer reads static model registry files.
Model source of truth:
- DB table
notion_model_definitions - DB table
notion_model_fields - DB table
notion_model_bindings
Runtime config sources:
- Env:
NOTION_SOURCE_PAGE_ID: source page for scanning candidate databases/data sources.
- DB table
integration_configs:notion.schema.field_mapping(explicit field override mapping)
Studio settings flow:
- Create/edit models and fields in
/studio/settings/notion. - Scan
NOTION_SOURCE_PAGE_IDfor candidatedata_source_id. - Bind model to one
data_source_id. - Run schema mapping check and save explicit overrides when needed.
App field rule:
appFieldmust start with model prefix:<modelKey>.<fieldName>(for exampleresume.location).
Public data contract:
- Query model data from
GET /api/public/models/[modelKey]. - Response shape is typed and consistent:
meta:modelKey,dataSourceId,generatedAt,schemaVersionrows: array of mapped app fields
GET /api/public/resumeandGET /api/public/projectsare thin wrappers over the same model query use case.
For implementation details, see:
/Users/quan/quan-studio/docs/notion-module-usage.md
src/app- App Router pages and route handlers (
(site),(studio),api)
- App Router pages and route handlers (
src/domain- Domain entities, rules, and repository interfaces (
post,notion-sync)
- Domain entities, rules, and repository interfaces (
src/application- Use cases and application-level errors
src/infrastructure- Prisma client, repository implementations, Notion API client, Studio auth
src/interface- HTTP handler wrapper, validators, DTO mapping, dependency container
src/presentation- UI components, feature modules, API clients, frontend types
prisma/schema.prisma- Database schema (
posts,notion_sync_jobs,integration_configs)
- Database schema (
Dockerfile.dev+docker-compose.dev.yml- Dev container stack (
next+worker+postgres)
- Dev container stack (
spec/README.mdspec/MVP_SPEC.mdspec/FRONTEND_SPEC.mdspec/BACKEND_SPEC.mdspec/INFRA_SPEC.mdspec/EDITOR_FORMAT_GUIDE.md