@nestjs-cms/core 0.0.0-stage → 0.1.2
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/.env.example +21 -0
- package/README.md +606 -2
- package/dist/src/api-key.guard.d.ts +4 -0
- package/dist/src/api-key.guard.js +29 -0
- package/dist/src/api-key.guard.js.map +1 -0
- package/dist/src/config.d.ts +43 -0
- package/dist/src/config.js +47 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/database/create-data-source.d.ts +4 -0
- package/dist/src/database/create-data-source.js +23 -0
- package/dist/src/database/create-data-source.js.map +1 -0
- package/dist/src/error.filter.d.ts +6 -0
- package/dist/src/error.filter.js +54 -0
- package/dist/src/error.filter.js.map +1 -0
- package/dist/src/index.d.ts +12 -0
- package/dist/src/index.js +23 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/resource/query.d.ts +15 -0
- package/dist/src/resource/query.js +147 -0
- package/dist/src/resource/query.js.map +1 -0
- package/dist/src/resource/resource-record.store.d.ts +7 -0
- package/dist/src/resource/resource-record.store.js +39 -0
- package/dist/src/resource/resource-record.store.js.map +1 -0
- package/dist/src/resource/resource.controller.d.ts +46 -0
- package/dist/src/resource/resource.controller.js +165 -0
- package/dist/src/resource/resource.controller.js.map +1 -0
- package/dist/src/resource/resource.loader.d.ts +2 -0
- package/dist/src/resource/resource.loader.js +43 -0
- package/dist/src/resource/resource.loader.js.map +1 -0
- package/dist/src/resource/resource.module.d.ts +16 -0
- package/dist/src/resource/resource.module.js +70 -0
- package/dist/src/resource/resource.module.js.map +1 -0
- package/dist/src/resource/resource.registry.d.ts +21 -0
- package/dist/src/resource/resource.registry.js +74 -0
- package/dist/src/resource/resource.registry.js.map +1 -0
- package/dist/src/resource/resource.service.d.ts +60 -0
- package/dist/src/resource/resource.service.js +150 -0
- package/dist/src/resource/resource.service.js.map +1 -0
- package/dist/src/resource/resource.types.d.ts +33 -0
- package/dist/src/resource/resource.types.js +7 -0
- package/dist/src/resource/resource.types.js.map +1 -0
- package/dist/src/resource/validation.d.ts +3 -0
- package/dist/src/resource/validation.js +19 -0
- package/dist/src/resource/validation.js.map +1 -0
- package/dist/src/setup.d.ts +3 -0
- package/dist/src/setup.js +16 -0
- package/dist/src/setup.js.map +1 -0
- package/dist/src/storage/attachment.entity.d.ts +23 -0
- package/dist/src/storage/attachment.entity.js +37 -0
- package/dist/src/storage/attachment.entity.js.map +1 -0
- package/dist/src/storage/attachment.service.d.ts +46 -0
- package/dist/src/storage/attachment.service.js +190 -0
- package/dist/src/storage/attachment.service.js.map +1 -0
- package/dist/src/storage/attachment.types.d.ts +5 -0
- package/dist/src/storage/attachment.types.js +3 -0
- package/dist/src/storage/attachment.types.js.map +1 -0
- package/dist/src/storage/s3.service.d.ts +8 -0
- package/dist/src/storage/s3.service.js +65 -0
- package/dist/src/storage/s3.service.js.map +1 -0
- package/docs/api.md +166 -0
- package/docs/operations.md +166 -0
- package/docs/resources.md +151 -0
- package/package.json +82 -4
package/.env.example
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
NODE_ENV=development
|
|
2
|
+
PORT=3000
|
|
3
|
+
HOST=127.0.0.1
|
|
4
|
+
CORS_ORIGINS=
|
|
5
|
+
# Optional shared API key for development/integration; rights/auth module is out of scope.
|
|
6
|
+
ADMIN_API_KEY=
|
|
7
|
+
DB_TYPE=postgres
|
|
8
|
+
DB_HOST=127.0.0.1
|
|
9
|
+
DB_PORT=5432
|
|
10
|
+
DB_USERNAME=cms
|
|
11
|
+
DB_PASSWORD=cms_dev_password
|
|
12
|
+
DB_DATABASE=cms
|
|
13
|
+
# Dev only. Use migrations in production.
|
|
14
|
+
DB_SYNCHRONIZE=true
|
|
15
|
+
S3_REGION=us-east-1
|
|
16
|
+
S3_BUCKET=cms-attachments
|
|
17
|
+
S3_ENDPOINT=http://127.0.0.1:9000
|
|
18
|
+
S3_FORCE_PATH_STYLE=true
|
|
19
|
+
S3_ACCESS_KEY_ID=minioadmin
|
|
20
|
+
S3_SECRET_ACCESS_KEY=minioadmin123
|
|
21
|
+
ATTACHMENT_MAX_BYTES=20971520
|
package/README.md
CHANGED
|
@@ -1,3 +1,607 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @nestjs-cms/core
|
|
2
2
|
|
|
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
|
+
|
|
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
|
|
7
|
+
integration into a new NestJS application, follow the quick start below.
|
|
8
|
+
|
|
9
|
+
## Quick start
|
|
10
|
+
|
|
11
|
+
Requires Node.js 22+, npm, and a PostgreSQL database. The package supports NestJS 11 with the Express adapter.
|
|
12
|
+
|
|
13
|
+
### 1. Create a NestJS project and install the package
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
npx @nestjs/cli@11 new nestjs-cms-demo --package-manager npm
|
|
17
|
+
cd nestjs-cms-demo
|
|
18
|
+
npm install @nestjs-cms/core
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The npm package includes compiled JavaScript and TypeScript declarations. Imports use `"@nestjs-cms/core"`.
|
|
22
|
+
|
|
23
|
+
The package includes Zod, TypeORM, PostgreSQL/MySQL drivers, dotenv, and the S3 client. The NestJS project supplies the NestJS runtime dependencies.
|
|
24
|
+
|
|
25
|
+
### 2. Create a resource
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
mkdir -p src/resources
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Create `src/resources/pages.ts`:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { defineResource, z } from "@nestjs-cms/core";
|
|
35
|
+
|
|
36
|
+
export default defineResource({
|
|
37
|
+
name: "pages",
|
|
38
|
+
schema: z
|
|
39
|
+
.object({
|
|
40
|
+
title: z.string().trim().min(1).max(200),
|
|
41
|
+
status: z.enum(["draft", "published"]).default("draft"),
|
|
42
|
+
})
|
|
43
|
+
.strict(),
|
|
44
|
+
columns: {
|
|
45
|
+
title: { type: String, length: 200 },
|
|
46
|
+
status: { type: String, length: 20 },
|
|
47
|
+
},
|
|
48
|
+
filterable: ["title", "status"],
|
|
49
|
+
sortable: ["title", "createdAt"],
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Keep the folder inside `src` so the standard Nest compiler builds it into `dist/resources`. Each additional resource requires only one new file in this folder. No resource index or per-resource registration is needed.
|
|
54
|
+
|
|
55
|
+
### 3. Import the CMS module once
|
|
56
|
+
|
|
57
|
+
Add `ResourceModule.forRoot(...)` to the `imports` array in `src/app.module.ts`:
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { Module } from "@nestjs/common";
|
|
61
|
+
import { join } from "node:path";
|
|
62
|
+
import { ResourceModule } from "@nestjs-cms/core";
|
|
63
|
+
|
|
64
|
+
@Module({
|
|
65
|
+
imports: [
|
|
66
|
+
ResourceModule.forRoot({ resourcesPath: join(__dirname, "resources") }),
|
|
67
|
+
],
|
|
68
|
+
})
|
|
69
|
+
export class AppModule {}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
You can keep your existing controllers, providers, and other imports. The module's API-key guard and error filter apply only to CMS routes.
|
|
73
|
+
|
|
74
|
+
### 4. Configure `.env`
|
|
75
|
+
|
|
76
|
+
Create `.env` in the new project's root:
|
|
77
|
+
|
|
78
|
+
```dotenv
|
|
79
|
+
NODE_ENV=development
|
|
80
|
+
PORT=3000
|
|
81
|
+
DB_TYPE=postgres
|
|
82
|
+
DB_HOST=127.0.0.1
|
|
83
|
+
DB_PORT=5432
|
|
84
|
+
DB_USERNAME=cms
|
|
85
|
+
DB_PASSWORD=cms_dev_password
|
|
86
|
+
DB_DATABASE=cms
|
|
87
|
+
DB_SYNCHRONIZE=true
|
|
88
|
+
ADMIN_API_KEY=
|
|
89
|
+
CORS_ORIGINS=
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Use credentials for an existing PostgreSQL database. The package loads `.env` automatically. With `DB_SYNCHRONIZE=true`, it creates the resource tables in that database during startup. Use migrations instead in production.
|
|
93
|
+
|
|
94
|
+
If you need a local database and port 5432 is available:
|
|
95
|
+
|
|
96
|
+
```sh
|
|
97
|
+
docker run --name nestjs-cms-demo-postgres -d \
|
|
98
|
+
-e POSTGRES_USER=cms -e POSTGRES_PASSWORD=cms_dev_password -e POSTGRES_DB=cms \
|
|
99
|
+
-p 127.0.0.1:5432:5432 postgres:17-alpine
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Wait for PostgreSQL to be ready before starting the application. The example above supports plain resource CRUD. **Attachments also require working S3/MinIO storage and an attachment field in the resource definition.**
|
|
103
|
+
|
|
104
|
+
### 4a. Configure storage for attachments
|
|
105
|
+
|
|
106
|
+
For local MinIO, add these settings to your application's `.env`:
|
|
107
|
+
|
|
108
|
+
```dotenv
|
|
109
|
+
S3_REGION=us-east-1
|
|
110
|
+
S3_BUCKET=cms-attachments
|
|
111
|
+
S3_ENDPOINT=http://127.0.0.1:9000
|
|
112
|
+
S3_FORCE_PATH_STYLE=true
|
|
113
|
+
S3_ACCESS_KEY_ID=minioadmin
|
|
114
|
+
S3_SECRET_ACCESS_KEY=minioadmin123
|
|
115
|
+
ATTACHMENT_MAX_BYTES=20971520
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
These credentials match this library's development Compose setup. From the `nestjs-cms` repository checkout, start storage with:
|
|
119
|
+
|
|
120
|
+
```sh
|
|
121
|
+
docker compose up -d --build minio minio-init
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Wait for MinIO to become healthy and `minio-init` to finish successfully; it creates the private `cms-attachments` bucket. The npm package does not start storage or include the Compose setup. You can also use an existing MinIO server: supply its endpoint and credentials and create the bucket beforehand. The endpoint above assumes the application runs on your host machine.
|
|
125
|
+
|
|
126
|
+
For AWS S3 instead, use your bucket's region and name, omit `S3_ENDPOINT`, and set `S3_FORCE_PATH_STYLE=false`. Supply both credential variables, or omit both when using an IAM role. See [AWS S3 configuration](docs/operations.md#aws-s3-configuration) for permissions.
|
|
127
|
+
|
|
128
|
+
In `src/resources/pages.ts`, add this property to the existing `defineResource({...})` object:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
attachments: [
|
|
132
|
+
{ field: "cover", mimeTypes: ["image/jpeg", "image/png", "image/webp"] },
|
|
133
|
+
],
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Attachment fields are declared separately from the Zod schema and database columns. After restarting the application and creating a page, upload a file using its record ID:
|
|
137
|
+
|
|
138
|
+
```sh
|
|
139
|
+
curl -X POST http://localhost:3000/api/pages/RECORD_ID/attachments/cover \
|
|
140
|
+
-F 'file=@/absolute/path/to/cover.png'
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Replace `RECORD_ID` and the file path with your values. If `ADMIN_API_KEY` is configured, include the `X-API-Key` header. Uploads and downloads always pass through the backend attachment API; the browser never connects directly to S3/MinIO.
|
|
144
|
+
|
|
145
|
+
Without working object storage, the application can start and plain resource CRUD still works. Storage is contacted when an attachment operation needs it: uploads fail with HTTP 503 (`Object storage upload unavailable`), and downloads of existing attachments fail with HTTP 503 (`Attachment content unavailable`). There is no local-filesystem fallback. Omitting the S3 settings does not disable attachments; the client uses the default region/bucket and AWS credential chain, which only works if that environment already has access to the matching storage.
|
|
146
|
+
|
|
147
|
+
### 5. Start the application
|
|
148
|
+
|
|
149
|
+
```sh
|
|
150
|
+
npm run start:dev
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
```sh
|
|
154
|
+
curl http://localhost:3000/api/_resources
|
|
155
|
+
curl -X POST http://localhost:3000/api/pages \
|
|
156
|
+
-H 'Content-Type: application/json' \
|
|
157
|
+
-d '{"title":"About us"}'
|
|
158
|
+
curl http://localhost:3000/api/pages
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
After adding or removing resource files, rebuild/restart the application. The standard Nest CLI TypeScript build emits the resource files separately; bundlers require additional configuration to preserve those files for runtime discovery.
|
|
162
|
+
|
|
163
|
+
For production, run the host project's `npm run build` and `npm run start:prod`, disable schema synchronization, and apply reviewed migrations. The library does not run or install its demo migrations in your application.
|
|
164
|
+
|
|
165
|
+
### Optional: CORS and shutdown hooks
|
|
166
|
+
|
|
167
|
+
Same-origin frontends do not require CORS. To apply `CORS_ORIGINS` for a separate frontend and enable graceful shutdown, call the helper in your application's existing `src/main.ts`:
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
import { configureCmsApp } from "@nestjs-cms/core";
|
|
171
|
+
|
|
172
|
+
// After NestFactory.create(AppModule), before app.listen(...):
|
|
173
|
+
configureCmsApp(app);
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
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
|
+
## Build your own CMS repository
|
|
179
|
+
|
|
180
|
+
This walkthrough uses `kongyim/nestjs-cms-demo` as a starting template for your own
|
|
181
|
+
repository. It gives you a NestJS API, a Next.js admin with a dashboard, pages,
|
|
182
|
+
categories, users and roles, plus PostgreSQL and MinIO for local development.
|
|
183
|
+
All package installation commands use the latest releases from npm.
|
|
184
|
+
|
|
185
|
+
The two packages have different responsibilities: `@nestjs-cms/core` supplies
|
|
186
|
+
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.
|
|
190
|
+
|
|
191
|
+
### 1. Create your workspace from the demo
|
|
192
|
+
|
|
193
|
+
Install Node.js 22+, npm, Git, and Docker with Docker Compose. Start Docker, then
|
|
194
|
+
run these commands in the directory where you keep your projects:
|
|
195
|
+
|
|
196
|
+
```sh
|
|
197
|
+
git clone https://github.com/kongyim/nestjs-cms-demo.git my-cms
|
|
198
|
+
cd my-cms
|
|
199
|
+
git remote rename origin upstream
|
|
200
|
+
npm install
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
This keeps the demo's history and names its remote `upstream`. To push to your
|
|
204
|
+
own repository later, create an empty Git repository with your hosting provider,
|
|
205
|
+
then run `git remote add origin <your-repository-url>`.
|
|
206
|
+
|
|
207
|
+
The workspace already includes the app configuration and root npm scripts:
|
|
208
|
+
|
|
209
|
+
```text
|
|
210
|
+
my-cms/
|
|
211
|
+
apps/
|
|
212
|
+
api/
|
|
213
|
+
src/auth/ Login, sessions, passwords, and permission enforcement
|
|
214
|
+
src/resources/ Backend schemas for pages, categories, roles, and users
|
|
215
|
+
src/app.module.ts CMS and authentication registration
|
|
216
|
+
src/env.ts Loads the shared root .env
|
|
217
|
+
admin/
|
|
218
|
+
app/ Next.js App Router shell
|
|
219
|
+
src/admin.config.ts
|
|
220
|
+
src/sections/ Resource forms and dashboard registration
|
|
221
|
+
src/components/ Custom dashboard component
|
|
222
|
+
next.config.ts Admin base path and same-origin API proxy
|
|
223
|
+
docker/ MinIO build and private-bucket initialization
|
|
224
|
+
docker-compose.yml
|
|
225
|
+
package.json npm workspaces and commands for both apps
|
|
226
|
+
.env Create this in step 3; do not commit it
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
The folder name `my-cms` does not change the npm workspace names. The commands
|
|
230
|
+
below use the template's existing `@nestjs-cms-demo/api` and
|
|
231
|
+
`@nestjs-cms-demo/admin` names.
|
|
232
|
+
|
|
233
|
+
### 2. Install the latest CMS packages
|
|
234
|
+
|
|
235
|
+
From `my-cms`, update both workspaces:
|
|
236
|
+
|
|
237
|
+
```sh
|
|
238
|
+
npm install @nestjs-cms/core@latest --workspace=@nestjs-cms-demo/api
|
|
239
|
+
npm install @nestjs-cms/admin@latest --workspace=@nestjs-cms-demo/admin
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
If your copy of the template still uses the previous backend name `nestjs-cms`,
|
|
243
|
+
replace all imports from `'nestjs-cms'` with `'@nestjs-cms/core'` in
|
|
244
|
+
`apps/api/src` and `apps/api/test`, including type imports, then remove it:
|
|
245
|
+
|
|
246
|
+
```sh
|
|
247
|
+
npm uninstall nestjs-cms --workspace=@nestjs-cms-demo/api
|
|
248
|
+
```
|
|
249
|
+
|
|
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
|
|
252
|
+
the updated package files and root lockfile with your application.
|
|
253
|
+
|
|
254
|
+
### 3. Configure the database, storage, and first administrator
|
|
255
|
+
|
|
256
|
+
Create `.env` in `my-cms` (beside `docker-compose.yml`), using this complete local
|
|
257
|
+
development configuration. Replace the example passwords before starting:
|
|
258
|
+
|
|
259
|
+
```dotenv
|
|
260
|
+
NODE_ENV=development
|
|
261
|
+
PORT=3000
|
|
262
|
+
|
|
263
|
+
DB_TYPE=postgres
|
|
264
|
+
DB_HOST=127.0.0.1
|
|
265
|
+
DB_PORT=5432
|
|
266
|
+
DB_USERNAME=cms
|
|
267
|
+
DB_PASSWORD=change-this-local-db-password
|
|
268
|
+
DB_DATABASE=cms
|
|
269
|
+
DB_SYNCHRONIZE=true
|
|
270
|
+
|
|
271
|
+
S3_REGION=us-east-1
|
|
272
|
+
S3_BUCKET=cms-attachments
|
|
273
|
+
S3_ENDPOINT=http://127.0.0.1:9000
|
|
274
|
+
S3_FORCE_PATH_STYLE=true
|
|
275
|
+
S3_ACCESS_KEY_ID=minioadmin
|
|
276
|
+
S3_SECRET_ACCESS_KEY=change-this-local-storage-password
|
|
277
|
+
ATTACHMENT_MAX_BYTES=20971520
|
|
278
|
+
|
|
279
|
+
PGADMIN_DEFAULT_EMAIL=admin@example.com
|
|
280
|
+
PGADMIN_DEFAULT_PASSWORD=change-this-local-pgadmin-password
|
|
281
|
+
|
|
282
|
+
ADMIN_BASE_PATH=/admin
|
|
283
|
+
ADMIN_ORIGIN=http://localhost:3001
|
|
284
|
+
ADMIN_BOOTSTRAP_EMAIL=admin@example.com
|
|
285
|
+
ADMIN_BOOTSTRAP_PASSWORD=choose-a-unique-admin-password
|
|
286
|
+
ADMIN_API_KEY=
|
|
287
|
+
CORS_ORIGINS=
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Use a 12–128 character administrator password. `ADMIN_API_KEY` can be empty for
|
|
291
|
+
this local setup: the demo still requires a valid user session and checks
|
|
292
|
+
permissions. Never expose a shared API key in frontend configuration.
|
|
293
|
+
|
|
294
|
+
The API and Next.js configuration load this root `.env`; Docker Compose reads
|
|
295
|
+
it too. `ADMIN_ORIGIN` is the browser origin without `/admin`. Keep browser URLs
|
|
296
|
+
on `localhost` consistently so origin checks and session cookies work together.
|
|
297
|
+
|
|
298
|
+
### 4. Start PostgreSQL and MinIO
|
|
299
|
+
|
|
300
|
+
From the workspace root:
|
|
301
|
+
|
|
302
|
+
```sh
|
|
303
|
+
docker compose up -d --build
|
|
304
|
+
docker compose ps -a
|
|
305
|
+
docker compose logs minio-init
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
The first build compiles MinIO and can take several minutes. Wait until
|
|
309
|
+
PostgreSQL and MinIO are healthy and `minio-init` exits with code 0. The initializer
|
|
310
|
+
creates the private bucket configured by `S3_BUCKET`.
|
|
311
|
+
|
|
312
|
+
The API runs on your host and connects to `127.0.0.1:5432` and
|
|
313
|
+
`http://127.0.0.1:9000`. If port 5432 is occupied, choose another `DB_PORT` in
|
|
314
|
+
`.env`; Compose and the API both use it. The other default ports are 3000, 3001,
|
|
315
|
+
8080, 9000, and 9001; stop conflicting services before continuing.
|
|
316
|
+
|
|
317
|
+
### 5. Start both apps and sign in
|
|
318
|
+
|
|
319
|
+
Run each command in a separate terminal, both from `my-cms`:
|
|
320
|
+
|
|
321
|
+
```sh
|
|
322
|
+
# Terminal 1
|
|
323
|
+
npm run dev:api
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
```sh
|
|
327
|
+
# Terminal 2
|
|
328
|
+
npm run dev:admin
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Open **http://localhost:3001/admin** and sign in with your
|
|
332
|
+
`ADMIN_BOOTSTRAP_EMAIL` and `ADMIN_BOOTSTRAP_PASSWORD`. The first API startup
|
|
333
|
+
creates the tables and, only when the users table is empty, creates the
|
|
334
|
+
Administrator role and account. After a successful login, remove both bootstrap
|
|
335
|
+
variables from `.env` and restart the API. Changing those variables later does
|
|
336
|
+
not reset an existing account's password.
|
|
337
|
+
|
|
338
|
+
Try the complete workflow:
|
|
339
|
+
|
|
340
|
+
1. Create a category in **Categories**.
|
|
341
|
+
2. Create a page in **Pages**, select the category, and save it.
|
|
342
|
+
3. Edit the page, add an attachment, save, and reopen it to check persistence.
|
|
343
|
+
4. Create an Editor role with `pages:read` and `pages:write`, then create a user
|
|
344
|
+
with that role and a password. Sign in as that user to check restricted access.
|
|
345
|
+
|
|
346
|
+
| Service | Local URL | Login |
|
|
347
|
+
| ------------- | ----------------------------- | ----------------------------------------------------- |
|
|
348
|
+
| Admin portal | `http://localhost:3001/admin` | CMS user email and password |
|
|
349
|
+
| NestJS API | `http://localhost:3000/api` | Session-protected; use the admin proxy in the browser |
|
|
350
|
+
| MinIO console | `http://localhost:9001` | `S3_ACCESS_KEY_ID` / `S3_SECRET_ACCESS_KEY` |
|
|
351
|
+
| pgAdmin | `http://localhost:8080` | `PGADMIN_DEFAULT_EMAIL` / `PGADMIN_DEFAULT_PASSWORD` |
|
|
352
|
+
|
|
353
|
+
In pgAdmin, open the preconfigured **CMS PostgreSQL** connection and enter
|
|
354
|
+
`DB_PASSWORD`. Its internal database host is `postgres`, not `localhost`.
|
|
355
|
+
|
|
356
|
+
Browser requests to `/admin/api/*` are proxied by Next.js to the NestJS `/api/*`
|
|
357
|
+
routes. The demo's default session cookie uses `/admin/api`, so test authenticated
|
|
358
|
+
browser requests through the admin URL. No separate browser CORS setup is needed.
|
|
359
|
+
|
|
360
|
+
### 6. Customize your CMS
|
|
361
|
+
|
|
362
|
+
Start with these files:
|
|
363
|
+
|
|
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` |
|
|
372
|
+
|
|
373
|
+
For example, change the `title` in `admin.config.ts` and reorder its `sections`
|
|
374
|
+
array to rearrange navigation. `defaultSection` selects the first screen without
|
|
375
|
+
changing that order. Use `defineCustomSection` for a custom React page such as the
|
|
376
|
+
dashboard; use `defineSection` for a resource list and editor.
|
|
377
|
+
|
|
378
|
+
To add a resource, create its backend definition in `apps/api/src/resources`
|
|
379
|
+
using [the resource guide](docs/resources.md), then create a matching frontend
|
|
380
|
+
section whose `key` is the resource name. Import the section in `admin.config.ts`
|
|
381
|
+
and add it to `sections`. Backend resources are discovered automatically on
|
|
382
|
+
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
|
|
384
|
+
resource. Hiding navigation alone does not restrict API access.
|
|
385
|
+
|
|
386
|
+
The [admin package guide](packages/admin/README.md) covers grouped forms,
|
|
387
|
+
responsive field widths, nested JSON, attachment previews, password actions,
|
|
388
|
+
and custom pages.
|
|
389
|
+
|
|
390
|
+
To move the portal, set `ADMIN_BASE_PATH=/cms` (or `/tools/cms`) in `.env` and
|
|
391
|
+
restart both apps. Sign in again at the new path. Set `ADMIN_BASE_PATH=` for `/`.
|
|
392
|
+
Production builds must be rebuilt after a base-path change because Next.js embeds
|
|
393
|
+
the path in browser assets.
|
|
394
|
+
|
|
395
|
+
### 7. Check and build your repository
|
|
396
|
+
|
|
397
|
+
From the workspace root:
|
|
398
|
+
|
|
399
|
+
```sh
|
|
400
|
+
npm test -- --runInBand
|
|
401
|
+
npm run typecheck:admin
|
|
402
|
+
npm run build
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
The build compiles both apps. To run the built applications, stop the development
|
|
406
|
+
servers and use separate terminals:
|
|
407
|
+
|
|
408
|
+
```sh
|
|
409
|
+
# Terminal 1
|
|
410
|
+
npm run start:api
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
```sh
|
|
414
|
+
# Terminal 2
|
|
415
|
+
npm run start:admin
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
For deployment, use HTTPS, `NODE_ENV=production`, `DB_SYNCHRONIZE=false`, reviewed
|
|
419
|
+
database migrations, a server-only `ADMIN_API_KEY` of at least 32 characters, and
|
|
420
|
+
the public admin origin in `ADMIN_ORIGIN`. Migrations must include the host's
|
|
421
|
+
resource, credential, and session tables. If the API runs on another host, update
|
|
422
|
+
the rewrite destination in `apps/admin/next.config.ts` before building. See
|
|
423
|
+
[operations](docs/operations.md) for database and storage configuration.
|
|
424
|
+
|
|
425
|
+
To stop local infrastructure, run `docker compose down`; the volumes retain your
|
|
426
|
+
data. Adding `-v` deletes database and storage data. Changing database credentials
|
|
427
|
+
in `.env` does not update a PostgreSQL user already stored in a volume.
|
|
428
|
+
|
|
429
|
+
### Troubleshooting the workspace
|
|
430
|
+
|
|
431
|
+
| Symptom | Check |
|
|
432
|
+
| ------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
|
|
433
|
+
| API cannot connect to PostgreSQL | Check `docker compose ps`, `DB_PORT`, and root `.env` credentials. |
|
|
434
|
+
| Login fails on a fresh setup | Check API startup logs, bootstrap password length, and whether the users table already contains an account. |
|
|
435
|
+
| Login works but requests fail or return 403 | Use `http://localhost:3001/admin`, check `ADMIN_ORIGIN`, and verify the user's role permissions. |
|
|
436
|
+
| Attachments return 503 | Check MinIO health, `minio-init` logs, and the API's S3 endpoint, credentials, and bucket. |
|
|
437
|
+
| A new resource or section is missing | Restart the API for resource discovery; import the frontend section and add it to `sections`. |
|
|
438
|
+
|
|
439
|
+
## Migrating from `nestjs-cms`
|
|
440
|
+
|
|
441
|
+
The backend package is now named `@nestjs-cms/core`. Existing applications can
|
|
442
|
+
switch with:
|
|
443
|
+
|
|
444
|
+
```sh
|
|
445
|
+
npm uninstall nestjs-cms
|
|
446
|
+
npm install @nestjs-cms/core
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
Replace imports from `"nestjs-cms"` with `"@nestjs-cms/core"`, including type
|
|
450
|
+
imports. The API is unchanged by this rename. The admin package remains
|
|
451
|
+
`@nestjs-cms/admin`; the GitHub repository remains `kongyim/nestjs-cms`.
|
|
452
|
+
|
|
453
|
+
## Install a local package
|
|
454
|
+
|
|
455
|
+
From this library's checkout:
|
|
456
|
+
|
|
457
|
+
```sh
|
|
458
|
+
npm ci
|
|
459
|
+
npm pack
|
|
460
|
+
```
|
|
461
|
+
|
|
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:
|
|
463
|
+
|
|
464
|
+
```sh
|
|
465
|
+
npm install /absolute/path/to/nestjs-cms-core-0.1.0.tgz
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
The package excludes the demo application, demo resources, demo migrations, tests, and local secrets. Packing does not publish anything to a registry.
|
|
469
|
+
|
|
470
|
+
## Install from GitHub (optional)
|
|
471
|
+
|
|
472
|
+
To use an unpublished revision, install from GitHub instead of npm. This requires
|
|
473
|
+
Git and a GitHub SSH key with access to the repository:
|
|
474
|
+
|
|
475
|
+
```sh
|
|
476
|
+
npm install git+ssh://git@github.com/kongyim/nestjs-cms.git
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
The package's `prepare` script builds TypeScript during Git installation. Keep
|
|
480
|
+
npm lifecycle scripts enabled. Append `#<tag-or-commit>` to the Git URL to select
|
|
481
|
+
an existing revision. Imports still use `"@nestjs-cms/core"`.
|
|
482
|
+
|
|
483
|
+
### Troubleshooting: Cannot find module `@nestjs-cms/core`
|
|
484
|
+
|
|
485
|
+
Run `npm ls @nestjs-cms/core` in the consuming application and check that
|
|
486
|
+
`node_modules/@nestjs-cms/core/dist/src/index.js` and `index.d.ts` exist. To reinstall
|
|
487
|
+
the published package, stop the development server and run:
|
|
488
|
+
|
|
489
|
+
```sh
|
|
490
|
+
npm uninstall @nestjs-cms/core
|
|
491
|
+
npm install @nestjs-cms/core
|
|
492
|
+
npm run start:dev
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
For Git installations, missing compiled files can indicate an older revision or
|
|
496
|
+
a failed `prepare` script. Reinstall with `--foreground-scripts` to see build
|
|
497
|
+
output, or use a locally packed package as described above.
|
|
498
|
+
|
|
499
|
+
## Publishing updates to npm
|
|
500
|
+
|
|
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).
|
|
505
|
+
|
|
506
|
+
After confirming `npm view @nestjs-cms/core version` returns the new release,
|
|
507
|
+
the owner of the old package can mark it deprecated:
|
|
508
|
+
|
|
509
|
+
```sh
|
|
510
|
+
npm deprecate 'nestjs-cms@*' 'Renamed to @nestjs-cms/core. Install @nestjs-cms/core and update your imports.'
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
This keeps existing versions available while directing users to the new package.
|
|
514
|
+
For subsequent releases, follow the steps below.
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
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
|
|
519
|
+
independent versions; release only the packages you changed.
|
|
520
|
+
|
|
521
|
+
```sh
|
|
522
|
+
npm ci
|
|
523
|
+
npm run release:check
|
|
524
|
+
npm run version:patch
|
|
525
|
+
npm run release
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
- `release:check` runs tests, builds the package, and previews the files to publish
|
|
529
|
+
without uploading anything. Review that file list before continuing. For backend
|
|
530
|
+
changes involving the database or storage, also run `npm run test:integration`
|
|
531
|
+
with the required services running.
|
|
532
|
+
- `version:patch` updates `package.json` and `package-lock.json` automatically
|
|
533
|
+
(for example, `0.1.0` → `0.1.1`). No manual version editing is needed.
|
|
534
|
+
Use `version:minor` for a feature release (`0.1.0` → `0.2.0`) or
|
|
535
|
+
`version:major` for a major release (`0.1.0` → `1.0.0`). During `0.x`
|
|
536
|
+
development, document breaking changes explicitly and use a minor release;
|
|
537
|
+
from `1.0.0` onward, use a major release for breaking changes.
|
|
538
|
+
- `release` publishes the current version publicly. Tests run again through
|
|
539
|
+
`prepublishOnly`, and the existing packaging hook builds the package before
|
|
540
|
+
upload. If needed, run `npm login --auth-type=web` first and complete npm's
|
|
541
|
+
authentication prompts.
|
|
542
|
+
|
|
543
|
+
Every published update needs a new version, including README-only updates on npm.
|
|
544
|
+
A published name/version cannot be reused. These scripts do not create Git commits
|
|
545
|
+
or tags: review and commit the code, README, and version/lockfile changes, then
|
|
546
|
+
push them to GitHub to keep the release source recorded.
|
|
547
|
+
|
|
548
|
+
If publication fails after the version bump, fix the problem and run
|
|
549
|
+
`npm run release` again without bumping again, provided that version was not
|
|
550
|
+
published. If the outcome is unclear, check `npm view <package-name>@<version> version`
|
|
551
|
+
first. The version scripts only change local files; `release:check` does not bump
|
|
552
|
+
versions, and `release` does not bump automatically.
|
|
553
|
+
|
|
554
|
+
See [npm version](https://docs.npmjs.com/cli/commands/npm-version/) for version
|
|
555
|
+
updates and [npm publish](https://docs.npmjs.com/cli/commands/npm-publish/) for
|
|
556
|
+
publication rules.
|
|
557
|
+
|
|
558
|
+
## Documentation
|
|
559
|
+
|
|
560
|
+
- [Resource definitions, hooks, and service methods](docs/resources.md)
|
|
561
|
+
- [API contract and frontend examples](docs/api.md)
|
|
562
|
+
- [Environment, databases, storage, and deployment](docs/operations.md)
|
|
563
|
+
|
|
564
|
+
`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
|
+
|
|
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.
|
|
567
|
+
|
|
568
|
+
## Develop this library
|
|
569
|
+
|
|
570
|
+
The repository also contains a demo application with `categories` and `articles`. These instructions are for library contributors, not package consumers:
|
|
571
|
+
|
|
572
|
+
```sh
|
|
573
|
+
npm ci
|
|
574
|
+
cp .env.example .env
|
|
575
|
+
docker compose up -d --build
|
|
576
|
+
# Wait for postgres/minio to be healthy and minio-init to exit with code 0.
|
|
577
|
+
npm run seed
|
|
578
|
+
npm run dev
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
The first MinIO build compiles a pinned source release and takes longer than subsequent starts. The demo API runs at `http://localhost:3000/api`, and the MinIO console at `http://localhost:9001`.
|
|
582
|
+
|
|
583
|
+
```sh
|
|
584
|
+
npm run format
|
|
585
|
+
npm run format:check
|
|
586
|
+
npm run build
|
|
587
|
+
npm test
|
|
588
|
+
npm run test:integration
|
|
589
|
+
# Optional MySQL verification:
|
|
590
|
+
docker compose --profile mysql up -d mysql
|
|
591
|
+
DB_TYPE=mysql DB_PORT=3307 npm run test:integration
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
Integration tests use the dedicated `cms_test` database and real MinIO services. The repository's VS Code settings enable formatting on save when the recommended Prettier extension is installed. These editor settings are not installed into consuming applications.
|
|
595
|
+
|
|
596
|
+
## Scope
|
|
597
|
+
|
|
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`.
|
|
599
|
+
|
|
600
|
+
## Reusable admin frontend
|
|
601
|
+
|
|
602
|
+
The companion React package lives in [packages/admin](packages/admin/README.md).
|
|
603
|
+
It provides the portal UI, explicitly configured sections in navigation order,
|
|
604
|
+
nested JSON fields, deferred attachments, and inline password actions. It is
|
|
605
|
+
packaged independently as `@nestjs-cms/admin`; the core backend package does not
|
|
606
|
+
install frontend dependencies. See the package README for build/install steps,
|
|
607
|
+
Next.js setup, and authentication/transport adapters.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.ApiKeyGuard = void 0;
|
|
10
|
+
const common_1 = require("@nestjs/common");
|
|
11
|
+
const node_crypto_1 = require("node:crypto");
|
|
12
|
+
const config_1 = require("./config");
|
|
13
|
+
let ApiKeyGuard = class ApiKeyGuard {
|
|
14
|
+
canActivate(context) {
|
|
15
|
+
if (!config_1.config.ADMIN_API_KEY)
|
|
16
|
+
return true;
|
|
17
|
+
const header = context.switchToHttp().getRequest().headers["x-api-key"];
|
|
18
|
+
const expected = Buffer.from(config_1.config.ADMIN_API_KEY);
|
|
19
|
+
const actual = Buffer.from(typeof header === "string" ? header : "");
|
|
20
|
+
if (actual.length !== expected.length || !(0, node_crypto_1.timingSafeEqual)(actual, expected))
|
|
21
|
+
throw new common_1.UnauthorizedException();
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
exports.ApiKeyGuard = ApiKeyGuard;
|
|
26
|
+
exports.ApiKeyGuard = ApiKeyGuard = __decorate([
|
|
27
|
+
(0, common_1.Injectable)()
|
|
28
|
+
], ApiKeyGuard);
|
|
29
|
+
//# sourceMappingURL=api-key.guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-key.guard.js","sourceRoot":"","sources":["../../src/api-key.guard.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAKwB;AACxB,6CAA8C;AAC9C,qCAAkC;AAE3B,IAAM,WAAW,GAAjB,MAAM,WAAW;IACtB,WAAW,CAAC,OAAyB;QACnC,IAAI,CAAC,eAAM,CAAC,aAAa;YAAE,OAAO,IAAI,CAAC;QACvC,MAAM,MAAM,GAAY,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAC,OAAO,CACjE,WAAW,CACZ,CAAC;QACF,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,eAAM,CAAC,aAAa,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACrE,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,IAAI,CAAC,IAAA,6BAAe,EAAC,MAAM,EAAE,QAAQ,CAAC;YACzE,MAAM,IAAI,8BAAqB,EAAE,CAAC;QACpC,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AAZY,kCAAW;sBAAX,WAAW;IADvB,IAAA,mBAAU,GAAE;GACA,WAAW,CAYvB"}
|