@plumbus/core 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/ai/ai-service.d.ts.map +1 -1
- package/dist/ai/ai-service.js +1 -0
- package/dist/ai/ai-service.js.map +1 -1
- package/dist/cli/__tests__/cli.test.js +90 -1
- package/dist/cli/__tests__/cli.test.js.map +1 -1
- package/dist/cli/__tests__/init.test.js +9 -0
- package/dist/cli/__tests__/init.test.js.map +1 -1
- package/dist/cli/__tests__/start.test.d.ts +2 -0
- package/dist/cli/__tests__/start.test.d.ts.map +1 -0
- package/dist/cli/__tests__/start.test.js +91 -0
- package/dist/cli/__tests__/start.test.js.map +1 -0
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +5 -3
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/commands/index.d.ts +2 -0
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +3 -0
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +20 -21
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/start.d.ts +25 -0
- package/dist/cli/commands/start.d.ts.map +1 -0
- package/dist/cli/commands/start.js +125 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/commands/translation.d.ts +3 -0
- package/dist/cli/commands/translation.d.ts.map +1 -0
- package/dist/cli/commands/translation.js +277 -0
- package/dist/cli/commands/translation.js.map +1 -0
- package/dist/cli/commands/ui.d.ts +8 -2
- package/dist/cli/commands/ui.d.ts.map +1 -1
- package/dist/cli/commands/ui.js +13 -5
- package/dist/cli/commands/ui.js.map +1 -1
- package/dist/cli/discover.d.ts +2 -0
- package/dist/cli/discover.d.ts.map +1 -1
- package/dist/cli/discover.js +11 -1
- package/dist/cli/discover.js.map +1 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/templates/resources.d.ts +1 -0
- package/dist/cli/templates/resources.d.ts.map +1 -1
- package/dist/cli/templates/resources.js +16 -0
- package/dist/cli/templates/resources.js.map +1 -1
- package/dist/config/loader.d.ts +2 -0
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js.map +1 -1
- package/dist/define/__tests__/defineTranslation.test.d.ts +2 -0
- package/dist/define/__tests__/defineTranslation.test.d.ts.map +1 -0
- package/dist/define/__tests__/defineTranslation.test.js +90 -0
- package/dist/define/__tests__/defineTranslation.test.js.map +1 -0
- package/dist/define/defineTranslation.d.ts +21 -0
- package/dist/define/defineTranslation.d.ts.map +1 -0
- package/dist/define/defineTranslation.js +67 -0
- package/dist/define/defineTranslation.js.map +1 -0
- package/dist/define/index.d.ts +1 -0
- package/dist/define/index.d.ts.map +1 -1
- package/dist/define/index.js +1 -0
- package/dist/define/index.js.map +1 -1
- package/dist/execution/context-factory.d.ts +2 -0
- package/dist/execution/context-factory.d.ts.map +1 -1
- package/dist/execution/context-factory.js +5 -0
- package/dist/execution/context-factory.js.map +1 -1
- package/dist/fields/index.d.ts +1 -0
- package/dist/fields/index.d.ts.map +1 -1
- package/dist/fields/index.js +1 -0
- package/dist/fields/index.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/testing/context.js +2 -2
- package/dist/testing/context.js.map +1 -1
- package/dist/translations/__tests__/translation-resolver.test.d.ts +2 -0
- package/dist/translations/__tests__/translation-resolver.test.d.ts.map +1 -0
- package/dist/translations/__tests__/translation-resolver.test.js +140 -0
- package/dist/translations/__tests__/translation-resolver.test.js.map +1 -0
- package/dist/translations/index.d.ts +40 -0
- package/dist/translations/index.d.ts.map +1 -0
- package/dist/translations/index.js +144 -0
- package/dist/translations/index.js.map +1 -0
- package/dist/types/context.d.ts +13 -7
- package/dist/types/context.d.ts.map +1 -1
- package/dist/types/fields.d.ts +1 -0
- package/dist/types/fields.d.ts.map +1 -1
- package/dist/types/flow.d.ts +2 -0
- package/dist/types/flow.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/translation.d.ts +28 -0
- package/dist/types/translation.d.ts.map +1 -0
- package/dist/types/translation.js +6 -0
- package/dist/types/translation.js.map +1 -0
- package/instructions/deployment.md +332 -0
- package/instructions/translations.md +78 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"translation.js","sourceRoot":"","sources":["../../src/types/translation.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,0DAA0D;AAC1D,uDAAuD;AACvD,2DAA2D"}
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
# Deployment
|
|
2
|
+
|
|
3
|
+
Plumbus applications deploy as up to three services — a **backend** (Fastify API server), a **frontend** (Next.js), and optionally an **admin dashboard** (Next.js) — backed by PostgreSQL and Redis. This document covers production configuration, containerization, and operational readiness.
|
|
4
|
+
|
|
5
|
+
## Service Topology
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
+-----------+ +-----------+ +-----------+
|
|
9
|
+
| frontend | | admin | | backend |
|
|
10
|
+
| Next.js | | Next.js | | Fastify |
|
|
11
|
+
| :3001 |---->| :3002 |---->| :3000 |
|
|
12
|
+
+-----------+ +-----------+ +-----+-----+
|
|
13
|
+
|
|
|
14
|
+
+-----+-----+
|
|
15
|
+
| |
|
|
16
|
+
+---v---+ +----v--+
|
|
17
|
+
| PG | | Redis |
|
|
18
|
+
| :5432 | | :6379 |
|
|
19
|
+
+-------+ +-------+
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
- **Frontend** and **admin** proxy API calls to the backend via server-side route handlers (`app/api/[...path]/route.ts`). Browsers never connect directly to the backend.
|
|
23
|
+
- **Backend** connects to PostgreSQL (data) and Redis (job queue, event dispatch).
|
|
24
|
+
|
|
25
|
+
## Backend Deployment
|
|
26
|
+
|
|
27
|
+
### Production Command
|
|
28
|
+
|
|
29
|
+
Use `plumbus start` for production. It forces production environment, binds to `0.0.0.0`, and requires `AUTH_SECRET`:
|
|
30
|
+
|
|
31
|
+
| Behavior | `plumbus dev` | `plumbus start` |
|
|
32
|
+
|----------|---------------|------------------|
|
|
33
|
+
| Environment | `development` | `production` |
|
|
34
|
+
| Default host | `localhost` | `0.0.0.0` |
|
|
35
|
+
| Log level | `debug` | `info` |
|
|
36
|
+
| DB SSL | off | on |
|
|
37
|
+
| `AUTH_SECRET` | optional (uses default) | **required** (throws on startup) |
|
|
38
|
+
| Cookies | `secure: false` | `secure: true` |
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx plumbus start --port 3000
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Runtime File Requirements
|
|
45
|
+
|
|
46
|
+
The backend uses dynamic resource discovery at startup — it scans the source tree for capabilities, entities, flows, events, and prompts. The following directories **must be present** in the production image:
|
|
47
|
+
|
|
48
|
+
| Directory | Purpose |
|
|
49
|
+
|-----------|---------|
|
|
50
|
+
| `app/` | Capabilities, entities, flows, events, prompts (scanned at startup) |
|
|
51
|
+
| `config/` | `app.config.ts`, `ai.config.ts` |
|
|
52
|
+
| `.plumbus/` | Generated manifests and type definitions |
|
|
53
|
+
| `drizzle/` | Database migration SQL files |
|
|
54
|
+
| `node_modules/` | Runtime dependencies including `@plumbus/core` |
|
|
55
|
+
| `dist/` | Compiled TypeScript output |
|
|
56
|
+
|
|
57
|
+
### Entrypoint Pattern
|
|
58
|
+
|
|
59
|
+
**CRITICAL**: Do **not** run migrations automatically in the container entrypoint. In multi-replica deployments (Kubernetes, ECS), concurrent migration runs can corrupt data or cause extended downtime. Run migrations as a separate, deliberate step before deploying new containers.
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
# Run migrations BEFORE deploying new containers (CI/CD step or one-off job):
|
|
63
|
+
npx plumbus migrate apply
|
|
64
|
+
|
|
65
|
+
# Then deploy. Container entrypoint only starts the server:
|
|
66
|
+
#!/bin/sh
|
|
67
|
+
set -e
|
|
68
|
+
exec npx plumbus start --port 3000
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Health Endpoints
|
|
72
|
+
|
|
73
|
+
| Endpoint | Purpose | Healthy response |
|
|
74
|
+
|----------|---------|------------------|
|
|
75
|
+
| `GET /health` | Liveness — server is running | `{ "status": "ok" }` |
|
|
76
|
+
| `GET /ready` | Readiness — database connected | `{ "status": "ready" }` |
|
|
77
|
+
|
|
78
|
+
Use `/ready` for container health checks and load balancer probes.
|
|
79
|
+
|
|
80
|
+
## Frontend Deployment
|
|
81
|
+
|
|
82
|
+
### Standalone Output
|
|
83
|
+
|
|
84
|
+
**CRITICAL**: Add `output: "standalone"` to `next.config.mjs` for containerized or self-hosted deployment. Without this, the build requires the full `node_modules` at runtime.
|
|
85
|
+
|
|
86
|
+
```js
|
|
87
|
+
const nextConfig = {
|
|
88
|
+
output: "standalone",
|
|
89
|
+
// ... existing config
|
|
90
|
+
};
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Build and Run
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
# Build (inlines NEXT_PUBLIC_* at compile time)
|
|
97
|
+
npx next build
|
|
98
|
+
|
|
99
|
+
# Run standalone server
|
|
100
|
+
node .next/standalone/<app-dir>/server.js
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Set `PORT` and `HOSTNAME` environment variables to control the listening address:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
PORT=3001 HOSTNAME=0.0.0.0 node .next/standalone/frontend/server.js
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Static Assets
|
|
110
|
+
|
|
111
|
+
After a standalone build, static files live in `.next/static/`. Copy them to the correct path in your deployment:
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
.next/standalone/ # Server code
|
|
115
|
+
<app-dir>/.next/static/ # Static assets (must be alongside server)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Environment Variables
|
|
119
|
+
|
|
120
|
+
- `NEXT_PUBLIC_*` variables are inlined at **build time**. Set them before `next build`, not at runtime.
|
|
121
|
+
- `API_BASE_URL` (server-only) controls the backend URL for server-side proxy routes. Set this at runtime to the internal Docker network address (e.g., `http://backend:3000`).
|
|
122
|
+
- `AUTH_SECRET` is required for cookie signing.
|
|
123
|
+
|
|
124
|
+
## Environment Variables Reference
|
|
125
|
+
|
|
126
|
+
### Backend
|
|
127
|
+
|
|
128
|
+
| Variable | Required | Default | Description |
|
|
129
|
+
|----------|----------|---------|-------------|
|
|
130
|
+
| `NODE_ENV` | Yes | `development` | Set to `production` for production |
|
|
131
|
+
| `DB_HOST` | Yes | `localhost` | PostgreSQL host |
|
|
132
|
+
| `DB_PORT` | No | `5432` | PostgreSQL port |
|
|
133
|
+
| `DB_NAME` | No | App name | Database name |
|
|
134
|
+
| `DB_USER` | Yes | `postgres` | Database user |
|
|
135
|
+
| `DB_PASSWORD` | Yes | — | Database password |
|
|
136
|
+
| `QUEUE_HOST` | Yes | `localhost` | Redis host |
|
|
137
|
+
| `QUEUE_PORT` | No | `6379` | Redis port |
|
|
138
|
+
| `AUTH_SECRET` | **Prod** | — | JWT signing secret (required in production) |
|
|
139
|
+
| `AI_DEFAULT_PROVIDER` | No | `openai` | AI provider name |
|
|
140
|
+
| `AI_OPENAI_API_KEY` | If using OpenAI | — | OpenAI API key |
|
|
141
|
+
| `AI_OPENAI_MODEL` | No | `gpt-4o-mini` | Default OpenAI model |
|
|
142
|
+
| `AI_DEFAULT_MODEL` | No | `gpt-4o` | Fallback model |
|
|
143
|
+
|
|
144
|
+
### Frontend / Admin
|
|
145
|
+
|
|
146
|
+
| Variable | Build/Runtime | Description |
|
|
147
|
+
|----------|---------------|-------------|
|
|
148
|
+
| `NEXT_PUBLIC_API_BASE_URL` | Build | Public API URL (empty string if using server-side proxy) |
|
|
149
|
+
| `API_BASE_URL` | Runtime | Backend URL for server-side proxy (e.g., `http://backend:3000`) |
|
|
150
|
+
| `AUTH_SECRET` | Runtime | Cookie signing secret |
|
|
151
|
+
|
|
152
|
+
## Docker
|
|
153
|
+
|
|
154
|
+
### Backend Dockerfile (multi-stage)
|
|
155
|
+
|
|
156
|
+
```dockerfile
|
|
157
|
+
# ---- Base ----
|
|
158
|
+
FROM node:22-alpine AS base
|
|
159
|
+
RUN corepack enable && corepack prepare pnpm@latest --activate
|
|
160
|
+
WORKDIR /app
|
|
161
|
+
|
|
162
|
+
# ---- Dependencies ----
|
|
163
|
+
FROM base AS deps
|
|
164
|
+
# Do NOT copy pnpm-workspace.yaml — it may contain local dev overrides
|
|
165
|
+
# (link:../Plumbus/...) that don't exist in Docker. Packages resolve from npm.
|
|
166
|
+
COPY package.json pnpm-lock.yaml ./
|
|
167
|
+
RUN pnpm install
|
|
168
|
+
|
|
169
|
+
# ---- Build ----
|
|
170
|
+
FROM base AS build
|
|
171
|
+
COPY --from=deps /app/node_modules ./node_modules
|
|
172
|
+
COPY package.json tsconfig.json ./
|
|
173
|
+
COPY app/ ./app/
|
|
174
|
+
COPY config/ ./config/
|
|
175
|
+
COPY .plumbus/ ./.plumbus/
|
|
176
|
+
COPY drizzle/ ./drizzle/
|
|
177
|
+
RUN pnpm build
|
|
178
|
+
|
|
179
|
+
# ---- Runner ----
|
|
180
|
+
FROM node:22-alpine AS runner
|
|
181
|
+
RUN corepack enable && corepack prepare pnpm@latest --activate
|
|
182
|
+
WORKDIR /app
|
|
183
|
+
ENV NODE_ENV=production
|
|
184
|
+
RUN addgroup --system --gid 1001 plumbus && \
|
|
185
|
+
adduser --system --uid 1001 plumbus
|
|
186
|
+
COPY --from=deps /app/node_modules ./node_modules
|
|
187
|
+
COPY --from=build /app/dist ./dist
|
|
188
|
+
COPY package.json ./
|
|
189
|
+
COPY app/ ./app/
|
|
190
|
+
COPY config/ ./config/
|
|
191
|
+
COPY .plumbus/ ./.plumbus/
|
|
192
|
+
COPY drizzle/ ./drizzle/
|
|
193
|
+
# Entrypoint starts the production server
|
|
194
|
+
USER plumbus
|
|
195
|
+
EXPOSE 3000
|
|
196
|
+
HEALTHCHECK CMD curl -f http://localhost:3000/ready || exit 1
|
|
197
|
+
ENTRYPOINT ["./entrypoint.sh"]
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Frontend Dockerfile (Next.js standalone)
|
|
201
|
+
|
|
202
|
+
```dockerfile
|
|
203
|
+
FROM node:22-alpine AS base
|
|
204
|
+
RUN corepack enable && corepack prepare pnpm@latest --activate
|
|
205
|
+
WORKDIR /app
|
|
206
|
+
|
|
207
|
+
FROM base AS deps
|
|
208
|
+
COPY package.json pnpm-lock.yaml ./
|
|
209
|
+
COPY frontend/package.json ./frontend/
|
|
210
|
+
# Generate a clean workspace config — don't copy the host's pnpm-workspace.yaml
|
|
211
|
+
RUN printf 'packages:\n - "frontend"\n' > pnpm-workspace.yaml
|
|
212
|
+
RUN pnpm install
|
|
213
|
+
|
|
214
|
+
FROM base AS build
|
|
215
|
+
COPY --from=deps /app/node_modules ./node_modules
|
|
216
|
+
COPY --from=deps /app/frontend/node_modules ./frontend/node_modules
|
|
217
|
+
COPY frontend/ ./frontend/
|
|
218
|
+
WORKDIR /app/frontend
|
|
219
|
+
RUN npx next build
|
|
220
|
+
|
|
221
|
+
FROM node:22-alpine AS runner
|
|
222
|
+
WORKDIR /app
|
|
223
|
+
ENV NODE_ENV=production
|
|
224
|
+
RUN addgroup --system --gid 1001 nextjs && \
|
|
225
|
+
adduser --system --uid 1001 nextjs
|
|
226
|
+
COPY --from=build /app/frontend/.next/standalone ./
|
|
227
|
+
COPY --from=build /app/frontend/.next/static ./frontend/.next/static
|
|
228
|
+
USER nextjs
|
|
229
|
+
EXPOSE 3001
|
|
230
|
+
CMD ["node", "frontend/server.js"]
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Docker Compose Topology
|
|
234
|
+
|
|
235
|
+
```yaml
|
|
236
|
+
services:
|
|
237
|
+
postgres:
|
|
238
|
+
image: postgres:16-alpine
|
|
239
|
+
volumes: [postgres_data:/var/lib/postgresql/data]
|
|
240
|
+
redis:
|
|
241
|
+
image: redis:7-alpine
|
|
242
|
+
backend:
|
|
243
|
+
build: { dockerfile: Dockerfile.backend }
|
|
244
|
+
depends_on: [postgres, redis]
|
|
245
|
+
volumes: [uploads_data:/app/uploads]
|
|
246
|
+
frontend:
|
|
247
|
+
build: { dockerfile: Dockerfile.frontend }
|
|
248
|
+
depends_on: [backend]
|
|
249
|
+
admin:
|
|
250
|
+
build: { dockerfile: Dockerfile.admin }
|
|
251
|
+
depends_on: [backend]
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Key Docker Considerations
|
|
255
|
+
|
|
256
|
+
- **Do NOT copy `pnpm-workspace.yaml`** into Docker images. It may contain local dev overrides (`link:../Plumbus/...`) that don't exist in the build context. The backend Dockerfile doesn't need it at all. Frontend Dockerfiles generate a minimal workspace config inline (`printf 'packages: ["frontend"]' > pnpm-workspace.yaml`) to declare the sub-package.
|
|
257
|
+
- **Dependency resolution**: `@plumbus/core` and `@plumbus/ui` are declared as normal dependencies in `package.json`. Without the workspace overrides, `pnpm install` resolves them from the npm registry.
|
|
258
|
+
- **Volumes**: Mount persistent volumes for PostgreSQL data and the `uploads/` directory (user-generated media and exports).
|
|
259
|
+
- **Internal networking**: Frontends connect to backend via Docker service name (e.g., `http://backend:3000`). Set `API_BASE_URL` accordingly.
|
|
260
|
+
- **`NEXT_PUBLIC_API_BASE_URL`**: Set to empty string (`""`) if the frontend uses server-side proxy routes. The proxy route handler reads `API_BASE_URL` at runtime.
|
|
261
|
+
- **File layout**: Place all Docker files (Dockerfiles, compose, entrypoint, env template) in a `docker/` subdirectory. The `.dockerignore` stays at the project root (the build context). Set `context: ..` (parent) in compose `build:` sections so COPY paths resolve from the project root.
|
|
262
|
+
|
|
263
|
+
## Kubernetes / Helm
|
|
264
|
+
|
|
265
|
+
The Docker images are self-contained and orchestrator-agnostic. When deploying to Kubernetes:
|
|
266
|
+
|
|
267
|
+
### Required Resources
|
|
268
|
+
|
|
269
|
+
| Resource | Purpose |
|
|
270
|
+
|----------|---------|
|
|
271
|
+
| `Deployment` (backend) + `Service` | Plumbus API server — stateless, scales horizontally |
|
|
272
|
+
| `Deployment` (frontend) + `Service` | Next.js user-facing app — stateless |
|
|
273
|
+
| `Deployment` (admin) + `Service` | Next.js admin dashboard — stateless |
|
|
274
|
+
| `Ingress` | Routes traffic: `/api` -> backend, `/` -> frontend, `/admin` -> admin |
|
|
275
|
+
| `Job` (migrations) | One-off pre-deploy step — runs `npx plumbus migrate apply` |
|
|
276
|
+
| `Secret` | `DB_PASSWORD`, `AUTH_SECRET`, AI provider keys |
|
|
277
|
+
| `ConfigMap` | Non-sensitive config: `DB_HOST`, `AI_DEFAULT_PROVIDER`, etc. |
|
|
278
|
+
|
|
279
|
+
### Migrations in Kubernetes — Critical
|
|
280
|
+
|
|
281
|
+
**NEVER** run migrations in the container entrypoint or as an init container that runs on every pod replica. In a rolling deployment with multiple pods starting concurrently, parallel migration runs against the same database can corrupt data or cause extended downtime.
|
|
282
|
+
|
|
283
|
+
Run migrations as a **separate Kubernetes Job** before rolling out new pods:
|
|
284
|
+
|
|
285
|
+
```yaml
|
|
286
|
+
apiVersion: batch/v1
|
|
287
|
+
kind: Job
|
|
288
|
+
metadata:
|
|
289
|
+
name: app-migrate
|
|
290
|
+
spec:
|
|
291
|
+
template:
|
|
292
|
+
spec:
|
|
293
|
+
containers:
|
|
294
|
+
- name: migrate
|
|
295
|
+
image: your-registry/app-backend:v1.0.0
|
|
296
|
+
command: ["npx", "plumbus", "migrate", "apply"]
|
|
297
|
+
envFrom:
|
|
298
|
+
- secretRef:
|
|
299
|
+
name: app-backend-secrets
|
|
300
|
+
restartPolicy: Never
|
|
301
|
+
backoffLimit: 1
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Health Probes
|
|
305
|
+
|
|
306
|
+
```yaml
|
|
307
|
+
livenessProbe:
|
|
308
|
+
httpGet: { path: /health, port: 3000 }
|
|
309
|
+
periodSeconds: 30
|
|
310
|
+
readinessProbe:
|
|
311
|
+
httpGet: { path: /ready, port: 3000 }
|
|
312
|
+
periodSeconds: 10
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Scaling Notes
|
|
316
|
+
|
|
317
|
+
- **Backend**: Stateless — safe to scale. All replicas must share the same `AUTH_SECRET`.
|
|
318
|
+
- **Frontends**: Stateless — scale freely behind a load balancer.
|
|
319
|
+
- **Uploads**: Replace the local `uploads/` directory with object storage (S3, GCS) or a `ReadWriteMany` PersistentVolumeClaim.
|
|
320
|
+
- **PostgreSQL / Redis**: Use managed services (RDS, ElastiCache, etc.) or official Helm charts — not the Docker Compose images.
|
|
321
|
+
|
|
322
|
+
## Production Checklist
|
|
323
|
+
|
|
324
|
+
- [ ] `plumbus start` used as production server command
|
|
325
|
+
- [ ] `AUTH_SECRET` set to a strong random value (32+ characters)
|
|
326
|
+
- [ ] Database SSL enabled (automatic with `plumbus start`)
|
|
327
|
+
- [ ] `plumbus migrate apply` run as a separate step before deployment (NOT in entrypoint)
|
|
328
|
+
- [ ] Health checks configured for `/ready` endpoint
|
|
329
|
+
- [ ] `output: "standalone"` in all `next.config.mjs` files
|
|
330
|
+
- [ ] Persistent volumes for PostgreSQL data and uploads
|
|
331
|
+
- [ ] AI provider API keys configured
|
|
332
|
+
- [ ] Redis available for job queue and event dispatch
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Translations
|
|
2
|
+
|
|
3
|
+
A translation is a type-safe i18n message catalog defined with `defineTranslation()`.
|
|
4
|
+
|
|
5
|
+
## Defining a Translation
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { defineTranslation } from "@plumbus/core";
|
|
9
|
+
|
|
10
|
+
export const commonTranslation = defineTranslation({
|
|
11
|
+
name: "common",
|
|
12
|
+
defaultLocale: "en",
|
|
13
|
+
locales: ["en", "he"],
|
|
14
|
+
messages: {
|
|
15
|
+
en: {
|
|
16
|
+
"nav.overview": "Overview",
|
|
17
|
+
"greeting": "Hello {name}",
|
|
18
|
+
"items": "{count, plural, one {# item} other {# items}}",
|
|
19
|
+
},
|
|
20
|
+
he: {
|
|
21
|
+
"nav.overview": "סקירה",
|
|
22
|
+
"greeting": "שלום {name}",
|
|
23
|
+
"items": "{count, plural, one {פריט #} other {# פריטים}}",
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Rules
|
|
30
|
+
|
|
31
|
+
- Place translation files in `app/translations/<name>.translation.ts`
|
|
32
|
+
- Each file exports one `defineTranslation()` result
|
|
33
|
+
- All locales **must** have the same key set — mismatched keys throw at import time
|
|
34
|
+
- Messages use ICU MessageFormat: `{name}` for interpolation, `{count, plural, ...}` for plurals, `{gender, select, ...}` for select
|
|
35
|
+
- The `name` field is the namespace: keys resolve as `<namespace>.<key>` (e.g., `common.nav.overview`)
|
|
36
|
+
|
|
37
|
+
## Server-Side Usage
|
|
38
|
+
|
|
39
|
+
In capability handlers, use `ctx.translations.t()`:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
throw ctx.errors.notFound(ctx.translations.t("errors.projectNotFound"));
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The locale is resolved from the request context.
|
|
46
|
+
|
|
47
|
+
## CLI Commands
|
|
48
|
+
|
|
49
|
+
| Command | Description |
|
|
50
|
+
|---------|-------------|
|
|
51
|
+
| `plumbus translation new <name>` | Scaffold a new translation file |
|
|
52
|
+
| `plumbus translation export` | Export to JSON or XLIFF 2.0 for professional translation |
|
|
53
|
+
| `plumbus translation import` | Import translated JSON/XLIFF files back into source |
|
|
54
|
+
| `plumbus translation status` | Report per-locale completion percentage |
|
|
55
|
+
|
|
56
|
+
## Frontend
|
|
57
|
+
|
|
58
|
+
When translations exist, `plumbus ui generate` produces `generated/i18n/` with next-intl integration:
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
import { useTranslations } from "next-intl";
|
|
62
|
+
|
|
63
|
+
function Nav() {
|
|
64
|
+
const t = useTranslations("common");
|
|
65
|
+
return <a>{t("nav.overview")}</a>;
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Adding a Locale
|
|
70
|
+
|
|
71
|
+
1. Add the locale to `locales` array in each `defineTranslation()`
|
|
72
|
+
2. Add the message catalog with all keys
|
|
73
|
+
3. Run `plumbus translation status` to verify 100%
|
|
74
|
+
4. Run `plumbus ui generate` to regenerate frontend i18n
|
|
75
|
+
|
|
76
|
+
## Translation Sync
|
|
77
|
+
|
|
78
|
+
`defineTranslation()` validates key consistency at import time. If locale "he" is missing keys that "en" has, the app throws immediately. This ensures tests and CI catch translation drift.
|