feat: add system configuration platform
This commit is contained in:
58
.trellis/spec/backend/deployment.md
Normal file
58
.trellis/spec/backend/deployment.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# Deployment Targets
|
||||
|
||||
## Scenario: Wulanchabu Server Deployment
|
||||
|
||||
### 1. Scope / Trigger
|
||||
|
||||
- Trigger: the user asks to deploy this project.
|
||||
- Default target: Wulanchabu server, reached through SSH host alias `wlcb1`.
|
||||
- This preference is project-specific and should be used for future deployment requests unless the user explicitly names another target.
|
||||
|
||||
### 2. Signatures
|
||||
|
||||
- SSH entrypoint: `ssh wlcb1`
|
||||
- Repository remote: push code to `origin` before deploying.
|
||||
- Database migration command: `pnpm db:migrate`
|
||||
- Production build command: `pnpm build`
|
||||
|
||||
### 3. Contracts
|
||||
|
||||
- Required environment on the server:
|
||||
- `DATABASE_URL` must point to the production PostgreSQL database.
|
||||
- Node.js and pnpm must be available.
|
||||
- Deployment should run from the server-side checkout for this repository.
|
||||
- Code should be pushed to `origin` before server deployment so `wlcb1` deploys committed source.
|
||||
|
||||
### 4. Validation & Error Matrix
|
||||
|
||||
- Missing `DATABASE_URL` -> deployment is not healthy; setup/login APIs return database configuration failures.
|
||||
- Migration failure -> stop deployment and report the failing migration output.
|
||||
- Build failure -> stop deployment and report the failing command output.
|
||||
- SSH failure -> report that `wlcb1` could not be reached.
|
||||
|
||||
### 5. Good/Base/Bad Cases
|
||||
|
||||
- Good: commit locally, push to `origin`, SSH to `wlcb1`, update checkout, install deps, migrate DB, build, restart app.
|
||||
- Base: if the server has a project-specific deploy script, run that script after pushing.
|
||||
- Bad: deploy uncommitted local files, deploy to a different host by default, or skip migrations after schema changes.
|
||||
|
||||
### 6. Tests Required
|
||||
|
||||
- `pnpm lint`
|
||||
- `pnpm type-check`
|
||||
- `pnpm build`
|
||||
- After deploy, verify the app responds and `/api/auth/bootstrap` returns a structured JSON response.
|
||||
|
||||
### 7. Wrong vs Correct
|
||||
|
||||
#### Wrong
|
||||
|
||||
```bash
|
||||
ssh some-other-host
|
||||
```
|
||||
|
||||
#### Correct
|
||||
|
||||
```bash
|
||||
ssh wlcb1
|
||||
```
|
||||
@@ -21,6 +21,7 @@
|
||||
| [authentication.md](./authentication.md) | better-auth, sessions, OAuth, protected procedures | Auth-related features |
|
||||
| [logging.md](./logging.md) | Structured logging, Sentry tracing, telemetry | Debugging, observability |
|
||||
| [local-json-mvp.md](./local-json-mvp.md) | Local JSON persistence/session/API contracts before database adoption | Single-repo MVP features without DB/oRPC deps |
|
||||
| [deployment.md](./deployment.md) | Wulanchabu deployment target and validation contract | Deploying this project |
|
||||
| [performance.md](./performance.md) | Concurrency, caching, batch processing, streaming | Performance optimization |
|
||||
| [ai-sdk-integration.md](./ai-sdk-integration.md) | Vercel AI SDK, tool calling, prompt patterns | AI-powered features |
|
||||
| [quality.md](./quality.md) | Pre-commit checklist for backend code | Before committing |
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
- Default data file: `.data/teatea.json`.
|
||||
- Override key: `TEATEA_DATA_DIR` points to the directory containing `teatea.json`.
|
||||
- `.data/` must stay ignored; it may contain account hashes and session IDs.
|
||||
- Routes or layouts that read local JSON state or session cookies must opt out of static prerendering with `export const dynamic = "force-dynamic"`.
|
||||
- API JSON responses backed by local JSON state or session cookies must include `Cache-Control: no-store`.
|
||||
- API response shape:
|
||||
|
||||
```ts
|
||||
@@ -51,6 +53,7 @@ type ApiResult<T extends Record<string, unknown>> =
|
||||
- Good: UI submits to Route Handler, Route Handler validates input, calls `requirePermission`, mutates data through `updateData`, writes audit log, returns `ApiResult`.
|
||||
- Base: Server Component reads data directly via `readData` after session/permission checks.
|
||||
- Bad: UI or route handler reads `.data/teatea.json` directly, bypasses `requirePermission`, or stores auth state in localStorage.
|
||||
- Bad: `/app/*`, `/login`, or `/setup` is prerendered as static content and caches a redirect based on build-time empty data.
|
||||
|
||||
### 6. Tests Required
|
||||
|
||||
@@ -60,6 +63,8 @@ type ApiResult<T extends Record<string, unknown>> =
|
||||
- Manual or automated integration assertions:
|
||||
- first setup creates admin and cookie session
|
||||
- protected app route redirects without cookie
|
||||
- protected app route renders with a valid cookie after setup and does not return a cached `/setup` redirect
|
||||
- auth/bootstrap/session API responses include `Cache-Control: no-store`
|
||||
- CRUD mutation persists across reload/API list
|
||||
- denied permission returns 403 and writes an audit log
|
||||
|
||||
@@ -84,3 +89,28 @@ cookieStore.set({
|
||||
});
|
||||
```
|
||||
|
||||
#### Wrong
|
||||
|
||||
```ts
|
||||
export default async function AppLayout({ children }: AppLayoutProps) {
|
||||
const bootstrap = await getBootstrapState();
|
||||
if (bootstrap.setupRequired) {
|
||||
redirect("/setup");
|
||||
}
|
||||
return children;
|
||||
}
|
||||
```
|
||||
|
||||
#### Correct
|
||||
|
||||
```ts
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export default async function AppLayout({ children }: AppLayoutProps) {
|
||||
const bootstrap = await getBootstrapState();
|
||||
if (bootstrap.setupRequired) {
|
||||
redirect("/setup");
|
||||
}
|
||||
return children;
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user