@nestjs-cms/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/README.md +601 -28
- package/dist/src/resource/resource.controller.js +1 -0
- package/dist/src/resource/resource.controller.js.map +1 -1
- package/dist/src/resource/resource.module.d.ts +4 -1
- package/dist/src/resource/resource.module.js +12 -1
- package/dist/src/resource/resource.module.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
Add a resource-driven CMS backend to a NestJS application. Define each resource in one file; the module discovers it and provides validated CRUD APIs, filters, lifecycle hooks, and S3-backed attachments.
|
|
4
4
|
|
|
5
5
|
For a complete API and admin workspace with login, start with
|
|
6
|
-
[Build
|
|
6
|
+
[Build a CMS from scratch](#build-a-cms-from-scratch), or
|
|
7
|
+
[use the demo as a template](#build-your-own-cms-repository). For a backend-only
|
|
7
8
|
integration into a new NestJS application, follow the quick start below.
|
|
8
9
|
|
|
9
10
|
## Quick start
|
|
@@ -175,6 +176,573 @@ configureCmsApp(app);
|
|
|
175
176
|
|
|
176
177
|
Set `CORS_ORIGINS=http://localhost:5173`, for example. Your host application still controls its listening port, bind address, and bootstrap process. If it already manages CORS and shutdown hooks, keep that configuration instead.
|
|
177
178
|
|
|
179
|
+
## Build a CMS from scratch
|
|
180
|
+
|
|
181
|
+
Use this route to create your own repository without cloning or renaming the
|
|
182
|
+
demo. You will build a NestJS API, a Next.js admin at `/admin`, PostgreSQL, and
|
|
183
|
+
MinIO, including administrator login, user and role management, and image uploads.
|
|
184
|
+
Docker runs the database and storage; Node.js runs the two applications.
|
|
185
|
+
|
|
186
|
+
`@nestjs-cms/auth` supplies users, roles, database-backed sessions, and the
|
|
187
|
+
login API. You configure the module once; no copied authentication files are
|
|
188
|
+
needed. This walkthrough uses the latest npm releases of all three CMS packages.
|
|
189
|
+
|
|
190
|
+
### 1. Create the repository and two apps
|
|
191
|
+
|
|
192
|
+
Install Node.js 22+, npm, Git, and Docker with Docker Compose. Choose your project
|
|
193
|
+
folder name in the first command; subsequent commands use workspace paths, so
|
|
194
|
+
there are no demo-specific package names to rename:
|
|
195
|
+
|
|
196
|
+
```sh
|
|
197
|
+
mkdir my-cms
|
|
198
|
+
cd my-cms
|
|
199
|
+
git init
|
|
200
|
+
mkdir apps
|
|
201
|
+
npx @nestjs/cli@11 new api --directory apps/api --package-manager npm --skip-git --skip-install
|
|
202
|
+
npx create-next-app@16 apps/admin --ts --app --src-dir --eslint --no-tailwind --use-npm --import-alias "@/*" --skip-install --disable-git
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
For any additional Next.js prompts, keep the defaults. These commands use NestJS
|
|
206
|
+
11 and Next.js 16; the CMS packages are installed from their latest npm releases
|
|
207
|
+
in the next step. See the [Nest CLI options](https://docs.nestjs.com/cli/usages)
|
|
208
|
+
and [Next.js CLI options](https://nextjs.org/docs/app/api-reference/cli/create-next-app)
|
|
209
|
+
for the scaffold commands.
|
|
210
|
+
|
|
211
|
+
Create `package.json` in the repository root:
|
|
212
|
+
|
|
213
|
+
```json
|
|
214
|
+
{
|
|
215
|
+
"name": "my-cms",
|
|
216
|
+
"private": true,
|
|
217
|
+
"workspaces": ["apps/*"],
|
|
218
|
+
"scripts": {
|
|
219
|
+
"dev:api": "npm run start:dev --workspace=apps/api",
|
|
220
|
+
"dev:admin": "npm run dev --workspace=apps/admin -- --hostname 127.0.0.1 --port 3001",
|
|
221
|
+
"build": "npm run build --workspaces",
|
|
222
|
+
"typecheck:admin": "tsc --noEmit -p apps/admin/tsconfig.json",
|
|
223
|
+
"start:api": "npm run start:prod --workspace=apps/api",
|
|
224
|
+
"start:admin": "npm run start --workspace=apps/admin -- --hostname 127.0.0.1 --port 3001"
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
You can change `name` to your own project name. Create a root `.gitignore`:
|
|
230
|
+
|
|
231
|
+
```gitignore
|
|
232
|
+
node_modules/
|
|
233
|
+
dist/
|
|
234
|
+
.next/
|
|
235
|
+
coverage/
|
|
236
|
+
*.tsbuildinfo
|
|
237
|
+
.env
|
|
238
|
+
.env.*
|
|
239
|
+
!.env.example
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Run all remaining shell commands from this repository root unless stated otherwise:
|
|
243
|
+
|
|
244
|
+
```sh
|
|
245
|
+
npm install
|
|
246
|
+
npm install @nestjs-cms/core@latest @nestjs-cms/auth@latest dotenv --workspace=apps/api
|
|
247
|
+
npm install @nestjs-cms/admin@latest @mui/material-nextjs@9 @emotion/cache --workspace=apps/admin
|
|
248
|
+
mkdir -p docker apps/api/src/resources apps/admin/src/sections
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Keep the single root `package-lock.json` in Git. The resulting layout will be:
|
|
252
|
+
|
|
253
|
+
```text
|
|
254
|
+
my-cms/
|
|
255
|
+
apps/api/src/ NestJS host, authentication, and resources
|
|
256
|
+
apps/admin/src/ Next.js shell and admin configuration
|
|
257
|
+
docker/ MinIO Dockerfile and bucket initializer
|
|
258
|
+
docker-compose.yml PostgreSQL and MinIO; no pgAdmin
|
|
259
|
+
.env Local settings, excluded from Git
|
|
260
|
+
package.json
|
|
261
|
+
package-lock.json
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### 2. Create the shared environment file
|
|
265
|
+
|
|
266
|
+
Create `.env` in the repository root. Replace the example passwords with your
|
|
267
|
+
own values; the administrator password must have 12–128 characters:
|
|
268
|
+
|
|
269
|
+
```dotenv
|
|
270
|
+
NODE_ENV=development
|
|
271
|
+
PORT=3000
|
|
272
|
+
DB_TYPE=postgres
|
|
273
|
+
DB_HOST=127.0.0.1
|
|
274
|
+
DB_PORT=5432
|
|
275
|
+
DB_USERNAME=cms
|
|
276
|
+
DB_PASSWORD=change-this-local-db-password
|
|
277
|
+
DB_DATABASE=cms
|
|
278
|
+
DB_SYNCHRONIZE=true
|
|
279
|
+
|
|
280
|
+
S3_REGION=us-east-1
|
|
281
|
+
S3_BUCKET=cms-attachments
|
|
282
|
+
S3_ENDPOINT=http://127.0.0.1:9000
|
|
283
|
+
S3_FORCE_PATH_STYLE=true
|
|
284
|
+
S3_ACCESS_KEY_ID=minioadmin
|
|
285
|
+
S3_SECRET_ACCESS_KEY=change-this-local-storage-password
|
|
286
|
+
ATTACHMENT_MAX_BYTES=20971520
|
|
287
|
+
|
|
288
|
+
ADMIN_ORIGIN=http://localhost:3001
|
|
289
|
+
ADMIN_BOOTSTRAP_EMAIL=admin@example.com
|
|
290
|
+
ADMIN_BOOTSTRAP_PASSWORD=choose-a-unique-admin-password
|
|
291
|
+
ADMIN_API_KEY=
|
|
292
|
+
CORS_ORIGINS=
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Bootstrap creates the first administrator only when the users table is empty.
|
|
296
|
+
Remove both bootstrap values after the first successful startup; existing
|
|
297
|
+
accounts and passwords are never overwritten. `ADMIN_API_KEY` may be empty for
|
|
298
|
+
local development because session authentication still protects the API. No
|
|
299
|
+
secret is passed to the frontend.
|
|
300
|
+
|
|
301
|
+
### 3. Create Docker Compose and the storage files
|
|
302
|
+
|
|
303
|
+
Create `docker-compose.yml` in the repository root:
|
|
304
|
+
|
|
305
|
+
```yaml
|
|
306
|
+
services:
|
|
307
|
+
postgres:
|
|
308
|
+
image: postgres:17-alpine
|
|
309
|
+
environment:
|
|
310
|
+
POSTGRES_USER: ${DB_USERNAME:?Set DB_USERNAME in .env}
|
|
311
|
+
POSTGRES_PASSWORD: ${DB_PASSWORD:?Set DB_PASSWORD in .env}
|
|
312
|
+
POSTGRES_DB: ${DB_DATABASE:?Set DB_DATABASE in .env}
|
|
313
|
+
ports:
|
|
314
|
+
- "127.0.0.1:${DB_PORT:-5432}:5432"
|
|
315
|
+
volumes:
|
|
316
|
+
- postgres-data:/var/lib/postgresql/data
|
|
317
|
+
healthcheck:
|
|
318
|
+
test: ["CMD-SHELL", 'pg_isready -U "$$POSTGRES_USER" -d "$$POSTGRES_DB"']
|
|
319
|
+
interval: 5s
|
|
320
|
+
timeout: 3s
|
|
321
|
+
retries: 30
|
|
322
|
+
|
|
323
|
+
minio:
|
|
324
|
+
build:
|
|
325
|
+
context: ./docker
|
|
326
|
+
dockerfile: minio.Dockerfile
|
|
327
|
+
command: server /data --console-address ':9001'
|
|
328
|
+
environment:
|
|
329
|
+
MINIO_ROOT_USER: ${S3_ACCESS_KEY_ID:?Set S3_ACCESS_KEY_ID in .env}
|
|
330
|
+
MINIO_ROOT_PASSWORD: ${S3_SECRET_ACCESS_KEY:?Set S3_SECRET_ACCESS_KEY in .env}
|
|
331
|
+
ports:
|
|
332
|
+
- "127.0.0.1:9000:9000"
|
|
333
|
+
- "127.0.0.1:9001:9001"
|
|
334
|
+
volumes:
|
|
335
|
+
- minio-data:/data
|
|
336
|
+
healthcheck:
|
|
337
|
+
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
|
|
338
|
+
interval: 5s
|
|
339
|
+
timeout: 3s
|
|
340
|
+
retries: 30
|
|
341
|
+
|
|
342
|
+
minio-init:
|
|
343
|
+
build:
|
|
344
|
+
context: ./docker
|
|
345
|
+
dockerfile: storage-init.Dockerfile
|
|
346
|
+
depends_on:
|
|
347
|
+
minio:
|
|
348
|
+
condition: service_healthy
|
|
349
|
+
environment:
|
|
350
|
+
S3_ENDPOINT: http://minio:9000
|
|
351
|
+
S3_REGION: ${S3_REGION:-us-east-1}
|
|
352
|
+
S3_BUCKET: ${S3_BUCKET:?Set S3_BUCKET in .env}
|
|
353
|
+
S3_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID:?Set S3_ACCESS_KEY_ID in .env}
|
|
354
|
+
S3_SECRET_ACCESS_KEY: ${S3_SECRET_ACCESS_KEY:?Set S3_SECRET_ACCESS_KEY in .env}
|
|
355
|
+
|
|
356
|
+
volumes:
|
|
357
|
+
postgres-data:
|
|
358
|
+
minio-data:
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
Create `docker/minio.Dockerfile`. This builds a pinned MinIO source release:
|
|
362
|
+
|
|
363
|
+
```dockerfile
|
|
364
|
+
FROM golang:1.24.8-bookworm AS build
|
|
365
|
+
ENV CGO_ENABLED=0
|
|
366
|
+
RUN go install github.com/minio/minio@RELEASE.2025-10-15T17-29-55Z
|
|
367
|
+
|
|
368
|
+
FROM debian:bookworm-slim
|
|
369
|
+
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates curl && rm -rf /var/lib/apt/lists/*
|
|
370
|
+
COPY --from=build /go/bin/minio /usr/local/bin/minio
|
|
371
|
+
EXPOSE 9000 9001
|
|
372
|
+
ENTRYPOINT ["minio"]
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Create `docker/storage-init.Dockerfile`:
|
|
376
|
+
|
|
377
|
+
```dockerfile
|
|
378
|
+
FROM node:22-alpine
|
|
379
|
+
WORKDIR /app
|
|
380
|
+
RUN npm init -y && npm install --save-exact @aws-sdk/client-s3@3.850.0
|
|
381
|
+
COPY init-s3.cjs ./init-s3.cjs
|
|
382
|
+
CMD ["node", "init-s3.cjs"]
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Create `docker/init-s3.cjs`:
|
|
386
|
+
|
|
387
|
+
```js
|
|
388
|
+
const { S3Client, CreateBucketCommand } = require("@aws-sdk/client-s3");
|
|
389
|
+
|
|
390
|
+
const client = new S3Client({
|
|
391
|
+
endpoint: process.env.S3_ENDPOINT,
|
|
392
|
+
region: process.env.S3_REGION,
|
|
393
|
+
forcePathStyle: true,
|
|
394
|
+
credentials: {
|
|
395
|
+
accessKeyId: process.env.S3_ACCESS_KEY_ID,
|
|
396
|
+
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
|
|
397
|
+
},
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
async function main() {
|
|
401
|
+
const Bucket = process.env.S3_BUCKET;
|
|
402
|
+
try {
|
|
403
|
+
await client.send(new CreateBucketCommand({ Bucket }));
|
|
404
|
+
} catch (error) {
|
|
405
|
+
if (error.name !== "BucketAlreadyOwnedByYou") throw error;
|
|
406
|
+
}
|
|
407
|
+
console.log(`Private bucket ready: ${Bucket}`);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
main()
|
|
411
|
+
.catch((error) => {
|
|
412
|
+
console.error(error);
|
|
413
|
+
process.exitCode = 1;
|
|
414
|
+
})
|
|
415
|
+
.finally(() => client.destroy());
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
Start the services:
|
|
419
|
+
|
|
420
|
+
```sh
|
|
421
|
+
docker compose up -d --build
|
|
422
|
+
docker compose ps -a
|
|
423
|
+
docker compose logs minio-init
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
The first MinIO build can take several minutes. Continue when PostgreSQL and
|
|
427
|
+
MinIO are healthy and `minio-init` has exited with code 0. The bucket is private;
|
|
428
|
+
uploads and downloads will go through your authenticated API.
|
|
429
|
+
|
|
430
|
+
There is no pgAdmin service. You can inspect the database using
|
|
431
|
+
`docker compose exec postgres sh -c 'psql -U "$POSTGRES_USER" -d "$POSTGRES_DB"'`.
|
|
432
|
+
The MinIO console is at `http://localhost:9001`; sign in with your S3 credentials.
|
|
433
|
+
|
|
434
|
+
### 4. Configure the NestJS host and a resource
|
|
435
|
+
|
|
436
|
+
Create `apps/api/src/env.ts` so the API loads the root `.env` even though npm runs
|
|
437
|
+
its workspace commands from `apps/api`:
|
|
438
|
+
|
|
439
|
+
```ts
|
|
440
|
+
import { config } from "dotenv";
|
|
441
|
+
import { resolve } from "node:path";
|
|
442
|
+
|
|
443
|
+
config({ path: resolve(__dirname, "../../../.env") });
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
Replace `apps/api/tsconfig.json` with the following. This explicitly uses CommonJS
|
|
447
|
+
and keeps compiled resources at `dist/resources`, where discovery expects them:
|
|
448
|
+
|
|
449
|
+
```json
|
|
450
|
+
{
|
|
451
|
+
"compilerOptions": {
|
|
452
|
+
"module": "commonjs",
|
|
453
|
+
"moduleResolution": "node",
|
|
454
|
+
"target": "ES2023",
|
|
455
|
+
"rootDir": "./src",
|
|
456
|
+
"outDir": "./dist",
|
|
457
|
+
"declaration": true,
|
|
458
|
+
"sourceMap": true,
|
|
459
|
+
"removeComments": true,
|
|
460
|
+
"emitDecoratorMetadata": true,
|
|
461
|
+
"experimentalDecorators": true,
|
|
462
|
+
"esModuleInterop": true,
|
|
463
|
+
"strict": true,
|
|
464
|
+
"skipLibCheck": true,
|
|
465
|
+
"incremental": true
|
|
466
|
+
},
|
|
467
|
+
"include": ["src/**/*.ts"],
|
|
468
|
+
"exclude": ["node_modules", "dist", "test", "**/*.spec.ts"]
|
|
469
|
+
}
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
Create `apps/api/src/resources/pages.ts`:
|
|
473
|
+
|
|
474
|
+
```ts
|
|
475
|
+
import { defineResource, z } from "@nestjs-cms/core";
|
|
476
|
+
|
|
477
|
+
export default defineResource({
|
|
478
|
+
name: "pages",
|
|
479
|
+
schema: z
|
|
480
|
+
.object({
|
|
481
|
+
title: z.string().trim().min(1).max(200),
|
|
482
|
+
status: z.enum(["draft", "published"]).default("draft"),
|
|
483
|
+
})
|
|
484
|
+
.strict(),
|
|
485
|
+
columns: {
|
|
486
|
+
title: { type: String, length: 200 },
|
|
487
|
+
status: { type: String, length: 20 },
|
|
488
|
+
},
|
|
489
|
+
filterable: ["title", "status"],
|
|
490
|
+
sortable: ["title", "createdAt"],
|
|
491
|
+
attachments: [
|
|
492
|
+
{ field: "cover", mimeTypes: ["image/jpeg", "image/png", "image/webp"] },
|
|
493
|
+
],
|
|
494
|
+
});
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
### 5. Enable authentication, users, and roles
|
|
498
|
+
|
|
499
|
+
Replace `apps/api/src/app.module.ts`:
|
|
500
|
+
|
|
501
|
+
```ts
|
|
502
|
+
import "./env";
|
|
503
|
+
import { Module } from "@nestjs/common";
|
|
504
|
+
import { join } from "node:path";
|
|
505
|
+
import { CmsAuthModule } from "@nestjs-cms/auth";
|
|
506
|
+
|
|
507
|
+
@Module({
|
|
508
|
+
imports: [
|
|
509
|
+
CmsAuthModule.forRoot({
|
|
510
|
+
resourcesPath: join(__dirname, "resources"),
|
|
511
|
+
adminOrigin: process.env.ADMIN_ORIGIN ?? "http://localhost:3001",
|
|
512
|
+
bootstrapAdmin: process.env.ADMIN_BOOTSTRAP_EMAIL
|
|
513
|
+
? {
|
|
514
|
+
email: process.env.ADMIN_BOOTSTRAP_EMAIL,
|
|
515
|
+
password: process.env.ADMIN_BOOTSTRAP_PASSWORD ?? "",
|
|
516
|
+
}
|
|
517
|
+
: undefined,
|
|
518
|
+
}),
|
|
519
|
+
],
|
|
520
|
+
})
|
|
521
|
+
export class AppModule {}
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
This registers the CMS and authentication together. Do not also import
|
|
525
|
+
`ResourceModule.forRoot`, create your own `users`/`roles` definitions, or copy
|
|
526
|
+
an auth guard. The default admin prefix is `/admin`. The package automatically
|
|
527
|
+
registers `pages:read`, `pages:write`, and the built-in `users:manage` permission.
|
|
528
|
+
|
|
529
|
+
Replace `apps/api/src/main.ts`:
|
|
530
|
+
|
|
531
|
+
```ts
|
|
532
|
+
import "./env";
|
|
533
|
+
import { NestFactory } from "@nestjs/core";
|
|
534
|
+
import { AppModule } from "./app.module";
|
|
535
|
+
|
|
536
|
+
async function bootstrap() {
|
|
537
|
+
const app = await NestFactory.create(AppModule);
|
|
538
|
+
app.enableShutdownHooks();
|
|
539
|
+
await app.listen(Number(process.env.PORT ?? 3000), "127.0.0.1");
|
|
540
|
+
}
|
|
541
|
+
void bootstrap();
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
Do not add a global `/api` prefix: CMS routes and the auth controller already
|
|
545
|
+
include it. The generated `app.controller.ts` and `app.service.ts` are unused
|
|
546
|
+
and can be deleted along with their generated test. Only the registered auth
|
|
547
|
+
controller and CMS routes are needed here.
|
|
548
|
+
|
|
549
|
+
### 6. Configure the admin and its API proxy
|
|
550
|
+
|
|
551
|
+
Replace `apps/admin/next.config.ts`:
|
|
552
|
+
|
|
553
|
+
```ts
|
|
554
|
+
import type { NextConfig } from "next";
|
|
555
|
+
|
|
556
|
+
const nextConfig: NextConfig = {
|
|
557
|
+
basePath: "/admin",
|
|
558
|
+
async rewrites() {
|
|
559
|
+
return [
|
|
560
|
+
{
|
|
561
|
+
source: "/api/:path*",
|
|
562
|
+
destination: "http://127.0.0.1:3000/api/:path*",
|
|
563
|
+
},
|
|
564
|
+
];
|
|
565
|
+
},
|
|
566
|
+
};
|
|
567
|
+
|
|
568
|
+
export default nextConfig;
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
Next.js adds `/admin` to the rewrite source, so the browser calls
|
|
572
|
+
`/admin/api/pages` and the API receives `/api/pages`. This also keeps the login
|
|
573
|
+
cookie on the admin origin. The frontend does not need to read the root `.env`.
|
|
574
|
+
|
|
575
|
+
Create `apps/admin/src/sections/pages.ts`:
|
|
576
|
+
|
|
577
|
+
```ts
|
|
578
|
+
import { defineSection } from "@nestjs-cms/admin";
|
|
579
|
+
|
|
580
|
+
export default defineSection({
|
|
581
|
+
key: "pages",
|
|
582
|
+
label: "Pages",
|
|
583
|
+
singular: "page",
|
|
584
|
+
description: "Manage page content and cover images.",
|
|
585
|
+
readPermission: "pages:read",
|
|
586
|
+
writePermission: "pages:write",
|
|
587
|
+
defaults: { title: "", status: "draft" },
|
|
588
|
+
listFields: [
|
|
589
|
+
{ field: "title", label: "Title" },
|
|
590
|
+
{ field: "status", label: "Status" },
|
|
591
|
+
{ field: "cover", label: "Cover", type: "attachment" },
|
|
592
|
+
],
|
|
593
|
+
searchFields: [{ field: "title", label: "Title", type: "text" }],
|
|
594
|
+
formFields: [
|
|
595
|
+
{
|
|
596
|
+
field: "title",
|
|
597
|
+
label: "Title",
|
|
598
|
+
type: "text",
|
|
599
|
+
required: true,
|
|
600
|
+
maxLength: 200,
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
field: "status",
|
|
604
|
+
label: "Status",
|
|
605
|
+
type: "select",
|
|
606
|
+
items: [
|
|
607
|
+
{ value: "draft", label: "Draft" },
|
|
608
|
+
{ value: "published", label: "Published" },
|
|
609
|
+
],
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
field: "cover",
|
|
613
|
+
label: "Cover",
|
|
614
|
+
type: "attachment",
|
|
615
|
+
accept: "image/jpeg,image/png,image/webp",
|
|
616
|
+
},
|
|
617
|
+
],
|
|
618
|
+
});
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
Create `apps/admin/src/admin.config.ts`:
|
|
622
|
+
|
|
623
|
+
```ts
|
|
624
|
+
"use client";
|
|
625
|
+
|
|
626
|
+
import { defineAdmin, createAuthSections } from "@nestjs-cms/admin";
|
|
627
|
+
import pages from "./sections/pages";
|
|
628
|
+
|
|
629
|
+
export default defineAdmin({
|
|
630
|
+
title: "My CMS",
|
|
631
|
+
apiBaseUrl: "/admin/api",
|
|
632
|
+
defaultSection: "pages",
|
|
633
|
+
sections: [
|
|
634
|
+
pages,
|
|
635
|
+
...createAuthSections([
|
|
636
|
+
{ value: "pages:read", label: "View pages" },
|
|
637
|
+
{ value: "pages:write", label: "Edit pages" },
|
|
638
|
+
]),
|
|
639
|
+
],
|
|
640
|
+
});
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
Replace `apps/admin/src/app/page.tsx`:
|
|
644
|
+
|
|
645
|
+
```tsx
|
|
646
|
+
"use client";
|
|
647
|
+
|
|
648
|
+
import { AdminPortal } from "@nestjs-cms/admin";
|
|
649
|
+
import config from "../admin.config";
|
|
650
|
+
|
|
651
|
+
export default function Page() {
|
|
652
|
+
return <AdminPortal config={config} />;
|
|
653
|
+
}
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
Replace `apps/admin/src/app/layout.tsx`:
|
|
657
|
+
|
|
658
|
+
```tsx
|
|
659
|
+
import type { ReactNode } from "react";
|
|
660
|
+
import { AppRouterCacheProvider } from "@mui/material-nextjs/v16-appRouter";
|
|
661
|
+
import "@nestjs-cms/admin/styles.css";
|
|
662
|
+
|
|
663
|
+
export default function RootLayout({ children }: { children: ReactNode }) {
|
|
664
|
+
return (
|
|
665
|
+
<html lang="en">
|
|
666
|
+
<body>
|
|
667
|
+
<AppRouterCacheProvider options={{ enableCssLayer: true }}>
|
|
668
|
+
{children}
|
|
669
|
+
</AppRouterCacheProvider>
|
|
670
|
+
</body>
|
|
671
|
+
</html>
|
|
672
|
+
);
|
|
673
|
+
}
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
The package supplies its theme and CSS. The generated Next.js starter stylesheet
|
|
677
|
+
and font imports are not needed. No Tailwind configuration is required.
|
|
678
|
+
|
|
679
|
+
### 7. Start, sign in, and create a page
|
|
680
|
+
|
|
681
|
+
With Docker services healthy, run these commands in two terminals from the root:
|
|
682
|
+
|
|
683
|
+
```sh
|
|
684
|
+
# Terminal 1
|
|
685
|
+
npm run dev:api
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
```sh
|
|
689
|
+
# Terminal 2
|
|
690
|
+
npm run dev:admin
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
Open **http://localhost:3001/admin**. Sign in with `ADMIN_BOOTSTRAP_EMAIL` and
|
|
694
|
+
`ADMIN_BOOTSTRAP_PASSWORD` from `.env`. Create a page, enter a title, choose a status, select
|
|
695
|
+
a cover image, and save. Reopen it to check that its content and image were saved.
|
|
696
|
+
Create an Editor role with both page permissions in **Roles**, then create a
|
|
697
|
+
user with that role and a password in **Users**. Sign in as that user to check
|
|
698
|
+
restricted access. Remove both bootstrap variables after the first successful
|
|
699
|
+
startup. Sessions and accounts persist across API restarts.
|
|
700
|
+
|
|
701
|
+
Use the `localhost` browser address shown above: `ADMIN_ORIGIN` must match it
|
|
702
|
+
exactly. The API is at `http://127.0.0.1:3000/api`, but direct unauthenticated
|
|
703
|
+
requests return 401. Browser requests, including images, go through
|
|
704
|
+
`http://localhost:3001/admin/api/*`, where the session cookie is available.
|
|
705
|
+
|
|
706
|
+
If login fails, check the credentials and API logs; after ten login attempts in
|
|
707
|
+
one minute, wait a minute before retrying. If uploads return 503, check
|
|
708
|
+
`docker compose logs minio-init` and the S3 values in `.env`. If the database port
|
|
709
|
+
is occupied, change `DB_PORT` and recreate the Compose services. If you change
|
|
710
|
+
`PORT`, update the Next.js rewrite destination too.
|
|
711
|
+
|
|
712
|
+
### 8. Build and extend the starter
|
|
713
|
+
|
|
714
|
+
Check and build both apps:
|
|
715
|
+
|
|
716
|
+
```sh
|
|
717
|
+
npm run typecheck:admin
|
|
718
|
+
npm run build
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
To smoke-test the compiled API locally, stop `dev:api` and run `npm run start:api`
|
|
722
|
+
with `NODE_ENV=development`; keep `dev:admin` running. Use `start:admin` for the
|
|
723
|
+
built frontend. A production deployment must use HTTPS and the settings below.
|
|
724
|
+
|
|
725
|
+
Add more backend files in `apps/api/src/resources` and corresponding frontend
|
|
726
|
+
sections in `apps/admin/src/sections`; import each section into `admin.config.ts`
|
|
727
|
+
and add it to `sections` in navigation order. Restart the API after adding a
|
|
728
|
+
resource. See [resource definitions](docs/resources.md) and
|
|
729
|
+
[admin configuration](packages/admin/README.md) for categories, grouped forms,
|
|
730
|
+
nested fields, and custom dashboards. New resources default to `<resource>:read` and `<resource>:write` permissions.
|
|
731
|
+
Add those permission choices to `createAuthSections`, and grant them to the
|
|
732
|
+
appropriate roles. Existing roles are never silently granted new permissions.
|
|
733
|
+
|
|
734
|
+
For deployment, set `NODE_ENV=production`, use HTTPS and a public HTTPS
|
|
735
|
+
`ADMIN_ORIGIN`, set `DB_SYNCHRONIZE=false`, and apply reviewed migrations for
|
|
736
|
+
all resource and authentication tables. The core currently also requires a
|
|
737
|
+
server-only `ADMIN_API_KEY` of at least 32 characters. Update the Next.js proxy
|
|
738
|
+
if the API runs on another host. See the
|
|
739
|
+
[auth package guide](packages/auth/README.md) for migrations, sessions, permission
|
|
740
|
+
mappings, and deployment details, and [operations](docs/operations.md) for storage.
|
|
741
|
+
|
|
742
|
+
Stop infrastructure with `docker compose down`. Volumes preserve the database and
|
|
743
|
+
attachments; `docker compose down -v` deletes both. Changing credentials in `.env`
|
|
744
|
+
does not change the credentials already initialized in a PostgreSQL volume.
|
|
745
|
+
|
|
178
746
|
## Build your own CMS repository
|
|
179
747
|
|
|
180
748
|
This walkthrough uses `kongyim/nestjs-cms-demo` as a starting template for your own
|
|
@@ -184,9 +752,8 @@ All package installation commands use the latest releases from npm.
|
|
|
184
752
|
|
|
185
753
|
The two packages have different responsibilities: `@nestjs-cms/core` supplies
|
|
186
754
|
resource APIs and attachments; `@nestjs-cms/admin` supplies the React portal.
|
|
187
|
-
|
|
188
|
-
server-side permissions
|
|
189
|
-
installing the packages alone does not create an authenticated CMS.
|
|
755
|
+
`@nestjs-cms/auth` supplies login, sessions, account bootstrap, built-in users and
|
|
756
|
+
roles, and server-side permissions through one module registration.
|
|
190
757
|
|
|
191
758
|
### 1. Create your workspace from the demo
|
|
192
759
|
|
|
@@ -210,8 +777,7 @@ The workspace already includes the app configuration and root npm scripts:
|
|
|
210
777
|
my-cms/
|
|
211
778
|
apps/
|
|
212
779
|
api/
|
|
213
|
-
src/
|
|
214
|
-
src/resources/ Backend schemas for pages, categories, roles, and users
|
|
780
|
+
src/resources/ Backend schemas for pages and categories
|
|
215
781
|
src/app.module.ts CMS and authentication registration
|
|
216
782
|
src/env.ts Loads the shared root .env
|
|
217
783
|
admin/
|
|
@@ -235,7 +801,7 @@ below use the template's existing `@nestjs-cms-demo/api` and
|
|
|
235
801
|
From `my-cms`, update both workspaces:
|
|
236
802
|
|
|
237
803
|
```sh
|
|
238
|
-
npm install @nestjs-cms/core@latest --workspace=@nestjs-cms-demo/api
|
|
804
|
+
npm install @nestjs-cms/core@latest @nestjs-cms/auth@latest --workspace=@nestjs-cms-demo/api
|
|
239
805
|
npm install @nestjs-cms/admin@latest --workspace=@nestjs-cms-demo/admin
|
|
240
806
|
```
|
|
241
807
|
|
|
@@ -247,8 +813,8 @@ replace all imports from `'nestjs-cms'` with `'@nestjs-cms/core'` in
|
|
|
247
813
|
npm uninstall nestjs-cms --workspace=@nestjs-cms-demo/api
|
|
248
814
|
```
|
|
249
815
|
|
|
250
|
-
|
|
251
|
-
|
|
816
|
+
For an older demo checkout with copied auth files, follow the migration steps in
|
|
817
|
+
[the auth package guide](packages/auth/README.md#migrating-the-demo). No local tarballs or library checkout are needed. Commit
|
|
252
818
|
the updated package files and root lockfile with your application.
|
|
253
819
|
|
|
254
820
|
### 3. Configure the database, storage, and first administrator
|
|
@@ -361,14 +927,14 @@ browser requests through the admin URL. No separate browser CORS setup is needed
|
|
|
361
927
|
|
|
362
928
|
Start with these files:
|
|
363
929
|
|
|
364
|
-
| Change | File or directory
|
|
365
|
-
| ------------------------------------------------ |
|
|
366
|
-
| Portal title, initial screen, navigation order | `apps/admin/src/admin.config.ts`
|
|
367
|
-
| Dashboard content | `apps/admin/src/components/dashboard.tsx`
|
|
368
|
-
| Backend fields, validation, filters, attachments | `apps/api/src/resources/`
|
|
369
|
-
| List columns, search controls, form fields | `apps/admin/src/sections/`
|
|
370
|
-
| Server-side resource permissions | `apps/api/src/
|
|
371
|
-
| Admin URL prefix and API destination | `apps/admin/next.config.ts`
|
|
930
|
+
| Change | File or directory |
|
|
931
|
+
| ------------------------------------------------ | ----------------------------------------------------- |
|
|
932
|
+
| Portal title, initial screen, navigation order | `apps/admin/src/admin.config.ts` |
|
|
933
|
+
| Dashboard content | `apps/admin/src/components/dashboard.tsx` |
|
|
934
|
+
| Backend fields, validation, filters, attachments | `apps/api/src/resources/` |
|
|
935
|
+
| List columns, search controls, form fields | `apps/admin/src/sections/` |
|
|
936
|
+
| Server-side resource permissions | `resourcePermissions` in `apps/api/src/app.module.ts` |
|
|
937
|
+
| Admin URL prefix and API destination | `apps/admin/next.config.ts` |
|
|
372
938
|
|
|
373
939
|
For example, change the `title` in `admin.config.ts` and reorder its `sections`
|
|
374
940
|
array to rearrange navigation. `defaultSection` selects the first screen without
|
|
@@ -380,7 +946,7 @@ using [the resource guide](docs/resources.md), then create a matching frontend
|
|
|
380
946
|
section whose `key` is the resource name. Import the section in `admin.config.ts`
|
|
381
947
|
and add it to `sections`. Backend resources are discovered automatically on
|
|
382
948
|
restart; frontend sections are registered explicitly. Match form fields to the
|
|
383
|
-
backend schema, and update the
|
|
949
|
+
backend schema, and update the module permission mapping and role permissions for the new
|
|
384
950
|
resource. Hiding navigation alone does not restrict API access.
|
|
385
951
|
|
|
386
952
|
The [admin package guide](packages/admin/README.md) covers grouped forms,
|
|
@@ -459,10 +1025,10 @@ npm ci
|
|
|
459
1025
|
npm pack
|
|
460
1026
|
```
|
|
461
1027
|
|
|
462
|
-
This builds `nestjs-cms-core-0.1.
|
|
1028
|
+
This builds `nestjs-cms-core-0.1.3.tgz`, including JavaScript and TypeScript declarations. In the new NestJS project, replace the npm installation step with:
|
|
463
1029
|
|
|
464
1030
|
```sh
|
|
465
|
-
npm install /absolute/path/to/nestjs-cms-core-0.1.
|
|
1031
|
+
npm install /absolute/path/to/nestjs-cms-core-0.1.3.tgz
|
|
466
1032
|
```
|
|
467
1033
|
|
|
468
1034
|
The package excludes the demo application, demo resources, demo migrations, tests, and local secrets. Packing does not publish anything to a registry.
|
|
@@ -498,10 +1064,12 @@ output, or use a locally packed package as described above.
|
|
|
498
1064
|
|
|
499
1065
|
## Publishing updates to npm
|
|
500
1066
|
|
|
501
|
-
|
|
502
|
-
`
|
|
503
|
-
`@nestjs-cms/
|
|
504
|
-
|
|
1067
|
+
The authentication package requires the new core integration API. Publish
|
|
1068
|
+
`@nestjs-cms/core` 0.1.3 first, then `@nestjs-cms/auth` 0.1.0 and
|
|
1069
|
+
`@nestjs-cms/admin` 0.1.2. Their versions are already prepared in this checkout;
|
|
1070
|
+
run `release:check` and `release` in each package directory without bumping them
|
|
1071
|
+
again unless that version has already been published. Nothing is published by
|
|
1072
|
+
`release:check`.
|
|
505
1073
|
|
|
506
1074
|
After confirming `npm view @nestjs-cms/core version` returns the new release,
|
|
507
1075
|
the owner of the old package can mark it deprecated:
|
|
@@ -513,9 +1081,9 @@ npm deprecate 'nestjs-cms@*' 'Renamed to @nestjs-cms/core. Install @nestjs-cms/c
|
|
|
513
1081
|
This keeps existing versions available while directing users to the new package.
|
|
514
1082
|
For subsequent releases, follow the steps below.
|
|
515
1083
|
|
|
516
|
-
|
|
517
1084
|
Run the following commands in the package you changed: the repository root for
|
|
518
|
-
`@nestjs-cms/core`,
|
|
1085
|
+
`@nestjs-cms/core`, `packages/auth` for `@nestjs-cms/auth`, or
|
|
1086
|
+
`packages/admin` for `@nestjs-cms/admin`. The packages have
|
|
519
1087
|
independent versions; release only the packages you changed.
|
|
520
1088
|
|
|
521
1089
|
```sh
|
|
@@ -557,13 +1125,18 @@ publication rules.
|
|
|
557
1125
|
|
|
558
1126
|
## Documentation
|
|
559
1127
|
|
|
1128
|
+
- [Authentication, users, roles, and sessions](packages/auth/README.md)
|
|
560
1129
|
- [Resource definitions, hooks, and service methods](docs/resources.md)
|
|
561
1130
|
- [API contract and frontend examples](docs/api.md)
|
|
562
1131
|
- [Environment, databases, storage, and deployment](docs/operations.md)
|
|
563
1132
|
|
|
564
1133
|
`ResourceService` is exported for use in other application services. It provides CRUD and `createAttachment`, `findAttachment`, `downloadAttachment`, and `removeAttachment`. All browser attachment traffic goes through the backend API.
|
|
565
1134
|
|
|
566
|
-
User login, roles, and resource
|
|
1135
|
+
User login, roles, sessions, and resource permissions are provided by the optional
|
|
1136
|
+
[`@nestjs-cms/auth`](packages/auth/README.md) package. Core alone uses API-key
|
|
1137
|
+
authentication; production requires a server-only key of at least 32 characters.
|
|
1138
|
+
The auth package validates the session and permissions before supplying that key
|
|
1139
|
+
internally. It never exposes the key to the browser.
|
|
567
1140
|
|
|
568
1141
|
## Develop this library
|
|
569
1142
|
|
|
@@ -595,7 +1168,7 @@ Integration tests use the dedicated `cms_test` database and real MinIO services.
|
|
|
595
1168
|
|
|
596
1169
|
## Scope
|
|
597
1170
|
|
|
598
|
-
The package implements resource discovery, CRUD, filters, transactional hooks, and backend-managed attachments. Import/export, image resizing, encrypted/hashed fields, automatic relation expansion
|
|
1171
|
+
The package implements resource discovery, CRUD, filters, transactional hooks, and backend-managed attachments. Import/export, image resizing, encrypted/hashed fields, and automatic relation expansion are not included in core. Resource permissions are available through `@nestjs-cms/auth`. Multilingual and nested fields use JSON. See the [comparison with the original project](docs/resources.md#comparison-with-the-original-project) for differences from `polyu-souvenirs`.
|
|
599
1172
|
|
|
600
1173
|
## Reusable admin frontend
|
|
601
1174
|
|
|
@@ -157,6 +157,7 @@ __decorate([
|
|
|
157
157
|
], ResourceController.prototype, "remove", null);
|
|
158
158
|
exports.ResourceController = ResourceController = __decorate([
|
|
159
159
|
(0, common_1.Controller)("api"),
|
|
160
|
+
(0, common_1.SetMetadata)("nestjs-cms:resource-controller", true),
|
|
160
161
|
(0, common_1.UseGuards)(api_key_guard_1.ApiKeyGuard),
|
|
161
162
|
(0, common_1.UseFilters)(error_filter_1.ErrorFilter),
|
|
162
163
|
__metadata("design:paramtypes", [resource_service_1.ResourceService,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource.controller.js","sourceRoot":"","sources":["../../../src/resource/resource.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,
|
|
1
|
+
{"version":3,"file":"resource.controller.js","sourceRoot":"","sources":["../../../src/resource/resource.controller.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { DynamicModule } from "@nestjs/common";
|
|
2
2
|
import { DataSource } from "typeorm";
|
|
3
|
+
import type { ResourceDefinition } from "./resource.types";
|
|
3
4
|
export interface ResourceModuleOptions {
|
|
4
5
|
/** Absolute path to the consumer's resource directory. */
|
|
5
|
-
resourcesPath
|
|
6
|
+
resourcesPath?: string;
|
|
7
|
+
/** Explicit definitions, combined with any discovered resources. */
|
|
8
|
+
resources?: ResourceDefinition[];
|
|
6
9
|
/** Use .ts only with a TypeScript runtime such as ts-node. Default: .js. */
|
|
7
10
|
extension?: ".ts" | ".js";
|
|
8
11
|
/** Optional dedicated CMS data source. This module closes it on shutdown. */
|
|
@@ -30,7 +30,18 @@ let ResourceModule = ResourceModule_1 = class ResourceModule {
|
|
|
30
30
|
}
|
|
31
31
|
static forRoot(options) {
|
|
32
32
|
// Scan once so the database and API use exactly the same definitions.
|
|
33
|
-
const resources =
|
|
33
|
+
const resources = [
|
|
34
|
+
...(options.resourcesPath
|
|
35
|
+
? (0, resource_loader_1.loadResources)(options.resourcesPath, options.extension ?? ".js")
|
|
36
|
+
: []),
|
|
37
|
+
...(options.resources ?? []),
|
|
38
|
+
];
|
|
39
|
+
const names = new Set();
|
|
40
|
+
for (const resource of resources) {
|
|
41
|
+
if (names.has(resource.name))
|
|
42
|
+
throw new Error(`Duplicate resource name: ${resource.name}`);
|
|
43
|
+
names.add(resource.name);
|
|
44
|
+
}
|
|
34
45
|
const registry = new resource_registry_1.ResourceRegistry(resources);
|
|
35
46
|
const source = options.dataSource ?? (0, create_data_source_1.createCmsDataSource)(resources);
|
|
36
47
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource.module.js","sourceRoot":"","sources":["../../../src/resource/resource.module.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAAuD;AACvD,qCAAqC;AACrC,uEAAqE;AACrE,uDAAkD;AAClD,+DAA2D;AAC3D,2DAAuD;AACvD,mEAA8D;AAC9D,yDAAqD;AACrD,sEAAkE;AAClE,sDAAkD;AAClD,oDAA+C;AAC/C,kDAA8C;
|
|
1
|
+
{"version":3,"file":"resource.module.js","sourceRoot":"","sources":["../../../src/resource/resource.module.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAAuD;AACvD,qCAAqC;AACrC,uEAAqE;AACrE,uDAAkD;AAClD,+DAA2D;AAC3D,2DAAuD;AACvD,mEAA8D;AAC9D,yDAAqD;AACrD,sEAAkE;AAClE,sDAAkD;AAClD,oDAA+C;AAC/C,kDAA8C;AAevC,IAAM,cAAc,sBAApB,MAAM,cAAc;IACI;IAA7B,YAA6B,EAAc;QAAd,OAAE,GAAF,EAAE,CAAY;IAAG,CAAC;IAE/C,MAAM,CAAC,OAAO,CAAC,OAA8B;QAC3C,sEAAsE;QACtE,MAAM,SAAS,GAAG;YAChB,GAAG,CAAC,OAAO,CAAC,aAAa;gBACvB,CAAC,CAAC,IAAA,+BAAa,EAAC,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC;gBAClE,CAAC,CAAC,EAAE,CAAC;YACP,GAAG,CAAC,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC;SAC7B,CAAC;QACF,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;QAChC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,IAAI,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAC1B,MAAM,IAAI,KAAK,CAAC,4BAA4B,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;YAC/D,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC3B,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,oCAAgB,CAAC,SAAS,CAAC,CAAC;QACjD,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,IAAI,IAAA,wCAAmB,EAAC,SAAS,CAAC,CAAC;QACpE,OAAO;YACL,MAAM,EAAE,gBAAc;YACtB,WAAW,EAAE,CAAC,wCAAkB,CAAC;YACjC,SAAS,EAAE;gBACT;oBACE,OAAO,EAAE,oBAAU;oBACnB,UAAU,EAAE,GAAG,EAAE,CACf,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,EAAE;iBACtD;gBACD,EAAE,OAAO,EAAE,oCAAgB,EAAE,QAAQ,EAAE,QAAQ,EAAE;gBACjD,2CAAmB;gBACnB,kCAAe;gBACf,sCAAiB;gBACjB,sBAAS;gBACT,2BAAW;gBACX,0BAAW;aACZ;YACD,OAAO,EAAE;gBACP,kCAAe;gBACf,oCAAgB;gBAChB,sCAAiB;gBACjB,oBAAU;aACX;SACF,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,IAAI,IAAI,CAAC,EAAE,CAAC,aAAa;YAAE,MAAM,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC;IACrD,CAAC;CACF,CAAA;AAhDY,wCAAc;yBAAd,cAAc;IAD1B,IAAA,eAAM,EAAC,EAAE,CAAC;qCAEwB,oBAAU;GADhC,cAAc,CAgD1B"}
|