@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 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 your own CMS repository](#build-your-own-cms-repository). For a backend-only
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
- The demo's application code supplies login, sessions, account bootstrap, and
188
- server-side permissions. Keep that code when using the demo as your template;
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/auth/ Login, sessions, passwords, and permission enforcement
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
- Only the package name changes; keep the demo's resource definitions and
251
- authentication wiring. No local tarballs or library checkout are needed. Commit
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/auth/auth.guard.ts` |
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 host guard and role permissions for the new
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.0.tgz`, including JavaScript and TypeScript declarations. In the new NestJS project, replace the npm installation step with:
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.0.tgz
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
- For the first release under the new name, run `npm run release:check` and
502
- `npm run release` from the repository root. Version `0.1.0` can be used because
503
- `@nestjs-cms/core` is a separate npm package from `nestjs-cms` (provided that
504
- version has not already been published under the new name).
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`, or `packages/admin` for `@nestjs-cms/admin`. The packages have
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 rights are not implemented. Setting `ADMIN_API_KEY` requires `X-API-Key` on CMS requests; production currently requires a key of at least 32 characters. This is an integration mechanism, not a full authentication system.
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, and resource permissions are not included. 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`.
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":"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"}
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: string;
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 = (0, resource_loader_1.loadResources)(options.resourcesPath, options.extension ?? ".js");
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;AAYvC,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,IAAA,+BAAa,EAC7B,OAAO,CAAC,aAAa,EACrB,OAAO,CAAC,SAAS,IAAI,KAAK,CAC3B,CAAC;QACF,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;AAxCY,wCAAc;yBAAd,cAAc;IAD1B,IAAA,eAAM,EAAC,EAAE,CAAC;qCAEwB,oBAAU;GADhC,cAAc,CAwC1B"}
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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nestjs-cms/core",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Definition-driven NestJS CMS with S3 attachments",
5
5
  "engines": {
6
6
  "node": ">=22"