Skip to content

API Guide

This is a single Next.js 16 app — there is no separate API server and no OpenAPI spec. The "API surface" is three things: Server-Component data fetching, Server Actions, and Route Handlers.

SurfaceWhereUse forCross-origin?
Server Component fetchapp/**/page.tsx (async)reading data to render a pageNo
Server Action ("use server")actions/*.tsmutations from forms/componentsNo
Route Handlerapp/api/**/route.tswebhooks, health, OAuthYes

Route Handlers

Route Handlers live in next-app/app/api/ and follow the App Router route.ts convention. Use them only when something outside the app must reach an HTTP URL.

Health Check

GET /api/health

Returns { status: "ok", timestamp: "..." } from app/api/health/route.ts. Use this to verify the deployed app is reachable and the interactive API playground is pointed at the right base URL.

Current Route Handler Surface

GET|POST /api/auth/[...nextauth]      → Auth.js v5 (session, 工牌登入/登出)
GET      /api/health                  → health probe
GET      /api/openapi                 → OpenAPI 描述

瑞成 PMS 沒有金流 webhook(Stripe / ECPay billing 功能已從產品移除),也沒有 OAuth callback — 登入採工牌編號 + 密碼的 Credentials provider。

Auth Session (Auth.js v5)

Auth.js v5 uses the JWT strategy — sessions are stateless. The session object is:

ts
type Session = {
  user: {
    id: string
    badge: string         // 工牌編號,例如 "R00001"
    name: string | null
    role: 'admin' | 'operator' | 'staff'  // 管理員 / 行政 / 一般
  }
  expires: string
}

Read it server-side with auth() from @/lib/auth. The Credentials provider 以工牌編號 + 密碼 驗證,requires JWT sessions(it cannot create DrizzleAdapter database sessions)。

Server Actions

Server Actions ("use server") run on the server and are invoked directly from React components or forms. They compile to same-origin POST endpoints — they cannot be called cross-origin.

案件 (actions/cases.ts)

ts
createCase(input): Promise<Result>          // 4 步精靈建檔
editCase(id, input): Promise<Result>
voidCase(id, reason): Promise<Result>       // 軟刪除(作廢),必填原因,可復原
restoreCase(id): Promise<Result>
updateProgress(...): Promise<Result>         // 自填式進度(必填備註;覆寫他人僅 admin)
completeNode(caseId, nodeId): Promise<Result>
addPrereq / editPrereq / markPrereq / deletePrereq(...)   // 前置資料(NAS 連結)
addSubcase / editSubcase / subStatusChange / deleteSubcase(...)  // B2 母子追加案

每個 mutation 依角色呼叫對應守衛(見下方 RBAC),驗證輸入,並經 rcLog 寫入稽核日誌。 進度 % 僅技師(admin)可覆寫他人 — 行政(operator)不可填進度(核心責任界線)。

業主 CRM (actions/clients.ts)、待辦 (actions/todos.ts)、設定(accounts.ts / categories.ts / calendars.ts

業主/窗口 CRUD、自訂分類、業主工作日曆、帳號(停用為軟刪除,不硬刪)。一般人員(staff)對 CRM 唯讀。

稽核與通知 (actions/admin.ts, actions/notifications*.ts)

稽核日誌僅 admin 可讀(可篩選 + 匯出 CSV,永久不可刪不可改);通知為系統內鈴鐺,不寄 Email。

RBAC Guard Pattern

Guards live in @/lib/permissions。每個守衛解析 session 並從資料庫 re-read live role (非 JWT 快照),因此降權立即生效。權限矩陣的唯一真實來源是 lib/rc-permissions.tsRC_PERM_MATRIX,10 旗標 × 三角色)。

GuardAllows
requireAuth()任何已登入使用者
requireOperatorOrAdmin()adminoperator(文書類操作)
requireAdmin()admin(樣板/分類管理、稽核日誌、進度覆寫)
requireFlag(flag)具備指定權限旗標的角色(依 RC_PERM_MATRIX
ts
import { requireAdmin } from '@/lib/permissions'

export async function adminAction() {
  'use server'
  const session = await requireAdmin() // re-reads role from DB; redirects non-admins
  // ...admin logic
}

Server Actions are public POST endpoints — never trust an argument's TypeScript type at runtime. Validate against a shared Zod schema (lib/validations/*) or a runtime allowlist。

Validation Envelope

Item/account actions return a small state object — { error?: string } | null (null = success). Admin actions return { success: true } | { error: string }. Validation schemas are shared from lib/validations/* so the same Zod rules run on the client form and the server action.

Interactive Playground

Test live Route Handlers against your deployed Next.js app:

GET/api/health
Base URL (your deployed Next.js app)
Authorization Header (optional)
Try in Live App →

Released under the MIT License.