@appxdigital/appx-core 0.1.117 → 0.1.118
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/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AppX
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
# AppX Core
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="./public/appx-core-logo.png" alt="AppX Core Logo" width="220" />
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
**AppX Core** is a production-oriented backend foundation that accelerates the delivery of secure, data-access-controlled APIs with an integrated admin backoffice. It scaffolds a native **NestJS** application and extends it with opinionated building blocks for authentication, authorization (ABAC-first), CRUD acceleration, and administration through **AdminJS**.
|
|
8
|
+
|
|
9
|
+
Built and maintained by **AppX** (appx-digital.com).
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Table of Contents
|
|
14
|
+
|
|
15
|
+
- [Why AppX Core](#why-appx-core)
|
|
16
|
+
- [Key Features](#key-features)
|
|
17
|
+
- [Architecture Overview](#architecture-overview)
|
|
18
|
+
- [Getting Started](#getting-started)
|
|
19
|
+
- [Prerequisites](#prerequisites)
|
|
20
|
+
- [1) Install the CLI](#1-install-the-cli)
|
|
21
|
+
- [2) Create a New Project](#2-create-a-new-project)
|
|
22
|
+
- [3) Configure Environment](#3-configure-environment)
|
|
23
|
+
- [4) Define Data Models](#4-define-data-models)
|
|
24
|
+
- [5) Generate Modules and CRUD](#5-generate-modules-and-crud)
|
|
25
|
+
- [6) Run Migrations](#6-run-migrations)
|
|
26
|
+
- [7) Run the App](#7-run-the-app)
|
|
27
|
+
- [What you get out of the box](#what-you-get-out-of-the-box)
|
|
28
|
+
- [Core Concepts](#core-concepts)
|
|
29
|
+
- [Project Structure](#project-structure)
|
|
30
|
+
- [Admin Backoffice](#admin-backoffice)
|
|
31
|
+
- [Authentication](#authentication)
|
|
32
|
+
- [Authorization Model](#authorization-model)
|
|
33
|
+
- [Permissions](#permissions)
|
|
34
|
+
- [Permissions Configuration Shape](#permissions-configuration-shape)
|
|
35
|
+
- [Rules](#rules)
|
|
36
|
+
- [Action Aliases and Fallbacks](#action-aliases-and-fallbacks)
|
|
37
|
+
- [Custom Controller Actions](#custom-controller-actions)
|
|
38
|
+
- [Temporarily Exposing Models](#temporarily-exposing-models)
|
|
39
|
+
- [Generated CRUD Endpoints](#generated-crud-endpoints)
|
|
40
|
+
- [Examples](#examples)
|
|
41
|
+
- [Example 1: Admin-Only Operational Endpoint](#example-1-admin-only-operational-endpoint)
|
|
42
|
+
- [Example 2: “My Profile” Endpoint (User Can Only See Self)](#example-2-my-profile-endpoint-user-can-only-see-self)
|
|
43
|
+
- [Example 3: Registration Email Availability Check](#example-3-registration-email-availability-check)
|
|
44
|
+
- [Limitations](#limitations)
|
|
45
|
+
- [FAQ](#faq)
|
|
46
|
+
- [Roadmap](#roadmap)
|
|
47
|
+
- [Support](#support)
|
|
48
|
+
- [Security](#security)
|
|
49
|
+
- [Contributing](#contributing)
|
|
50
|
+
- [License](#license)
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Why AppX Core
|
|
55
|
+
|
|
56
|
+
Modern applications often fail not because teams cannot build endpoints, but because they ship access control too late, build inconsistent admin tooling, and recreate the same authentication and backoffice plumbing across projects.
|
|
57
|
+
|
|
58
|
+
AppX Core puts **security and data-access control first**, while keeping development fast:
|
|
59
|
+
|
|
60
|
+
- Start from a **native NestJS** structure (no “framework lock-in”).
|
|
61
|
+
- Define data in **Prisma** once.
|
|
62
|
+
- Generate a robust baseline for CRUD and service structure.
|
|
63
|
+
- Enforce permissions where it matters: **at the data layer** (ABAC).
|
|
64
|
+
- Operate and administrate through a standard **AdminJS** backoffice.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Key Features
|
|
69
|
+
|
|
70
|
+
- **NestJS-native scaffold** (standard structure, editable modules)
|
|
71
|
+
- **Prisma ORM integration** with opinionated permissions enforcement
|
|
72
|
+
- **ABAC-first permission filtering** (attribute-based access control)
|
|
73
|
+
- **AdminJS backoffice** exposed at `/admin` with configurable access and models
|
|
74
|
+
- **Authentication out-of-the-box**
|
|
75
|
+
- Cookie/session auth
|
|
76
|
+
- JWT auth (recommended)
|
|
77
|
+
- **Fast CRUD acceleration**
|
|
78
|
+
- Generated modules, controllers, and services per model
|
|
79
|
+
- Standard REST CRUD endpoints
|
|
80
|
+
- **Developer-friendly extension points**
|
|
81
|
+
- Add custom endpoints with `@Permission(...)`
|
|
82
|
+
- Use services for business logic, controllers as transport layer
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Architecture Overview
|
|
87
|
+
|
|
88
|
+
AppX Core is designed as a **base layer** on top of NestJS:
|
|
89
|
+
|
|
90
|
+
- Your application remains a NestJS project with conventional modules.
|
|
91
|
+
- AppX Core provides:
|
|
92
|
+
- base configuration and wiring
|
|
93
|
+
- authentication modules
|
|
94
|
+
- permissions system (ABAC)
|
|
95
|
+
- generation pipeline based on Prisma schema
|
|
96
|
+
- AdminJS integration
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Getting Started
|
|
101
|
+
|
|
102
|
+
### Prerequisites
|
|
103
|
+
|
|
104
|
+
- **Node.js 20+**
|
|
105
|
+
- A running database (**MySQL** or **PostgreSQL** tested/validated)
|
|
106
|
+
|
|
107
|
+
### 1) Install the CLI
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
npm install -g @appxdigital/appx-core-cli
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 2) Create a New Project
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
appx-core create
|
|
117
|
+
cd <your-project>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 3) Configure Environment
|
|
121
|
+
|
|
122
|
+
Create a `.env` file at the project root:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
## DataBase Configurations ##
|
|
126
|
+
DB_HOST=127.0.0.1
|
|
127
|
+
DB_PORT=3306
|
|
128
|
+
DB_USER=root
|
|
129
|
+
DB_PASSWORD=
|
|
130
|
+
DB_NAME=generic
|
|
131
|
+
DB_PROVIDER=mysql
|
|
132
|
+
DB_URL="${DB_PROVIDER}://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}"
|
|
133
|
+
|
|
134
|
+
## Project configurations ##
|
|
135
|
+
APP_PORT=3000
|
|
136
|
+
USE_TRANSACTION=true
|
|
137
|
+
|
|
138
|
+
## Sessions ##
|
|
139
|
+
SESSION_SECRET="change-me"
|
|
140
|
+
SESSION_COOKIE_NAME="session_nestjs-project"
|
|
141
|
+
SESSION_TTL=86400
|
|
142
|
+
|
|
143
|
+
## JWT ##
|
|
144
|
+
JWT_EXPIRES_IN=10d
|
|
145
|
+
JWT_REFRESH_EXPIRES_IN=1y
|
|
146
|
+
JWT_SECRET="change-me"
|
|
147
|
+
JWT_REFRESH_SECRET="change-me"
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### 4) Define Data Models
|
|
151
|
+
|
|
152
|
+
Edit:
|
|
153
|
+
|
|
154
|
+
- `prisma/schema.prisma`
|
|
155
|
+
|
|
156
|
+
Add your application models. You can add fields and relations freely. Avoid removing or changing types of the default models shipped with AppX Core.
|
|
157
|
+
|
|
158
|
+
If you already have an existing database schema and want to import it:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
npx prisma db pull
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Important:** `db pull` overrides `schema.prisma`. Ensure you preserve default AppX Core models/configuration and merge accordingly.
|
|
165
|
+
|
|
166
|
+
### 5) Generate Modules and CRUD
|
|
167
|
+
|
|
168
|
+
After you edit `schema.prisma`, run:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
appx-core generate
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Run this **every time** you change the Prisma schema. This is the only required generation step (no need to run `prisma generate` separately).
|
|
175
|
+
|
|
176
|
+
### 6) Run Migrations
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
npx prisma migrate dev
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
You will be asked for a migration name.
|
|
183
|
+
|
|
184
|
+
### 7) Run the App
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
npm run start:dev
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### What you get out of the box
|
|
191
|
+
|
|
192
|
+
- Admin backoffice at **`/admin`** (after you include models in `src/config/admin.config.ts`)
|
|
193
|
+
- Auth endpoints with no prefix:
|
|
194
|
+
- `POST /auth/register`
|
|
195
|
+
- `POST /auth/login`
|
|
196
|
+
- `POST /auth/logout`
|
|
197
|
+
- `GET /me`
|
|
198
|
+
- `POST /login/jwt` (recommended)
|
|
199
|
+
- `POST /logout/jwt`
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Core Concepts
|
|
204
|
+
|
|
205
|
+
### Project Structure
|
|
206
|
+
|
|
207
|
+
AppX Core generates a standard NestJS structure. You will typically see:
|
|
208
|
+
|
|
209
|
+
- `src/main.ts` – app bootstrap and cross-cutting config (cookies, sessions, logs, CORS)
|
|
210
|
+
- `src/app.module.ts` – high-level module wiring
|
|
211
|
+
|
|
212
|
+
Most customization happens in:
|
|
213
|
+
|
|
214
|
+
- `src/config/permissions.config.ts` – granular ABAC rules
|
|
215
|
+
- `src/config/admin.config.ts` – AdminJS configuration and model registration
|
|
216
|
+
- `prisma/schema.prisma` – your data model source of truth
|
|
217
|
+
|
|
218
|
+
### Admin Backoffice
|
|
219
|
+
|
|
220
|
+
- AdminJS is exposed at **`/admin`**
|
|
221
|
+
- Models must be included/configured in `admin.config.ts` to appear in the UI.
|
|
222
|
+
|
|
223
|
+
### Authentication
|
|
224
|
+
|
|
225
|
+
Cookie/session:
|
|
226
|
+
|
|
227
|
+
- `POST /auth/register`
|
|
228
|
+
- `POST /auth/login`
|
|
229
|
+
- `POST /auth/logout`
|
|
230
|
+
- `GET /me`
|
|
231
|
+
|
|
232
|
+
JWT (recommended):
|
|
233
|
+
|
|
234
|
+
- `POST /login/jwt`
|
|
235
|
+
- `POST /logout/jwt`
|
|
236
|
+
|
|
237
|
+
### Authorization Model
|
|
238
|
+
|
|
239
|
+
AppX Core emphasizes enforcement at the data layer:
|
|
240
|
+
|
|
241
|
+
- Permissions are designed to be applied automatically during database operations (ABAC), not only at the route layer.
|
|
242
|
+
- Implement permissions using:
|
|
243
|
+
- `permissions.config.ts`
|
|
244
|
+
- `@Permission('actionName')` on endpoints
|
|
245
|
+
- ABAC conditions aligned with Prisma `where` filters
|
|
246
|
+
|
|
247
|
+
Unauthenticated requests are treated as a “guest context” (not a role).
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## Permissions
|
|
252
|
+
|
|
253
|
+
### Permissions Configuration Shape
|
|
254
|
+
|
|
255
|
+
Permissions live in `src/config/permissions.config.ts`:
|
|
256
|
+
|
|
257
|
+
```ts
|
|
258
|
+
export const PermissionsConfig = {
|
|
259
|
+
ModelName: {
|
|
260
|
+
ADMIN: {
|
|
261
|
+
findMany: 'ALL',
|
|
262
|
+
create: 'ALL',
|
|
263
|
+
updateMany: 'ALL',
|
|
264
|
+
deleteMany: 'ALL',
|
|
265
|
+
},
|
|
266
|
+
USER: {
|
|
267
|
+
findMany: { conditions: { id: PermissionPlaceholder.USER_ID } },
|
|
268
|
+
updateMany: { conditions: { id: PermissionPlaceholder.USER_ID } },
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Rules
|
|
275
|
+
|
|
276
|
+
- `'ALL'` – unrestricted access
|
|
277
|
+
- `{ conditions: { ... } }` – ABAC filtering using Prisma-like `where` syntax
|
|
278
|
+
|
|
279
|
+
Example: allow deletion only if related refresh tokens are expired:
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
deleteMany: {
|
|
283
|
+
conditions: {
|
|
284
|
+
refreshTokens: {
|
|
285
|
+
every: { expiresAt: { lt: new Date() } },
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Action Aliases and Fallbacks
|
|
292
|
+
|
|
293
|
+
To avoid duplicated config, the permissions engine supports fallback behavior. In practice:
|
|
294
|
+
|
|
295
|
+
- Define `findMany` when possible, as it generally covers read scenarios.
|
|
296
|
+
- `count` may fall back to read permissions when not explicitly defined.
|
|
297
|
+
|
|
298
|
+
### Custom Controller Actions
|
|
299
|
+
|
|
300
|
+
Add custom endpoints and protect them with `@Permission(...)`.
|
|
301
|
+
|
|
302
|
+
```ts
|
|
303
|
+
@Permission('exportAuditReport')
|
|
304
|
+
@Get('/audit/export')
|
|
305
|
+
exportAuditReport() {
|
|
306
|
+
return this.service.exportAuditReport();
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Temporarily Exposing Models
|
|
311
|
+
|
|
312
|
+
Some operations require privileged access (e.g., registration pre-checks). You can temporarily expose a model:
|
|
313
|
+
|
|
314
|
+
- Endpoint-level (decorator’s second argument), or
|
|
315
|
+
- Service-level scoped exposure
|
|
316
|
+
|
|
317
|
+
Endpoint-level example:
|
|
318
|
+
|
|
319
|
+
```ts
|
|
320
|
+
@Permission('checkEmailAvailability', ['user'])
|
|
321
|
+
@Get('/auth/email-available')
|
|
322
|
+
async isEmailAvailable(@Query('email') email: string) {
|
|
323
|
+
return this.service.isEmailAvailable(email);
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
Service-level example:
|
|
328
|
+
|
|
329
|
+
```ts
|
|
330
|
+
await this.prisma.withExposedModels(['user'], async () => {
|
|
331
|
+
// privileged operations here
|
|
332
|
+
});
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Use this sparingly and intentionally. Prefer ABAC whenever feasible.
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## Generated CRUD Endpoints
|
|
340
|
+
|
|
341
|
+
For each generated model/controller (subject to permissions):
|
|
342
|
+
|
|
343
|
+
- `GET /[model_name]`
|
|
344
|
+
- `GET /[model_name]/:id`
|
|
345
|
+
- `POST /[model_name]`
|
|
346
|
+
- `PUT /[model_name]/:id`
|
|
347
|
+
- `DELETE /[model_name]/:id`
|
|
348
|
+
|
|
349
|
+
To show a model in the admin backoffice, register it in `src/config/admin.config.ts`.
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## Examples
|
|
354
|
+
|
|
355
|
+
### Example 1: Admin-Only Operational Endpoint
|
|
356
|
+
|
|
357
|
+
Use case: expose a lightweight operational endpoint to verify that background jobs ran recently.
|
|
358
|
+
|
|
359
|
+
Controller:
|
|
360
|
+
|
|
361
|
+
```ts
|
|
362
|
+
@Permission('viewSystemHealth')
|
|
363
|
+
@Get('/ops/health')
|
|
364
|
+
getHealth() {
|
|
365
|
+
return this.service.getHealthSummary();
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
Permissions (`permissions.config.ts`):
|
|
370
|
+
|
|
371
|
+
```ts
|
|
372
|
+
export const PermissionsConfig = {
|
|
373
|
+
System: {
|
|
374
|
+
ADMIN: {
|
|
375
|
+
viewSystemHealth: 'ALL',
|
|
376
|
+
},
|
|
377
|
+
USER: {
|
|
378
|
+
// Not defined => denied
|
|
379
|
+
},
|
|
380
|
+
},
|
|
381
|
+
};
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
Service (example logic):
|
|
385
|
+
|
|
386
|
+
```ts
|
|
387
|
+
async getHealthSummary() {
|
|
388
|
+
return {
|
|
389
|
+
status: 'ok',
|
|
390
|
+
timestamp: new Date().toISOString(),
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### Example 2: “My Profile” Endpoint (User Can Only See Self)
|
|
396
|
+
|
|
397
|
+
Use case: ensure users can only access their own record, even if they try other IDs.
|
|
398
|
+
|
|
399
|
+
Controller:
|
|
400
|
+
|
|
401
|
+
```ts
|
|
402
|
+
@Permission('readMyProfile')
|
|
403
|
+
@Get('/me/profile')
|
|
404
|
+
getMyProfile() {
|
|
405
|
+
return this.service.getMyProfile();
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
Permissions:
|
|
410
|
+
|
|
411
|
+
```ts
|
|
412
|
+
export const PermissionsConfig = {
|
|
413
|
+
User: {
|
|
414
|
+
ADMIN: { readMyProfile: 'ALL' },
|
|
415
|
+
USER: { readMyProfile: { conditions: { id: PermissionPlaceholder.USER_ID } } },
|
|
416
|
+
},
|
|
417
|
+
};
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
Service (Prisma query remains straightforward; ABAC filtering is applied by the core):
|
|
421
|
+
|
|
422
|
+
```ts
|
|
423
|
+
async getMyProfile() {
|
|
424
|
+
return this.prisma.model.user.findFirst({
|
|
425
|
+
where: { id: PermissionPlaceholder.USER_ID },
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
### Example 3: Registration Email Availability Check
|
|
431
|
+
|
|
432
|
+
Use case: a public endpoint checks if an email is already used. This must access user data safely, even without a logged-in user.
|
|
433
|
+
|
|
434
|
+
Controller:
|
|
435
|
+
|
|
436
|
+
```ts
|
|
437
|
+
@Permission('checkEmailAvailability', ['user'])
|
|
438
|
+
@Get('/auth/email-available')
|
|
439
|
+
async isEmailAvailable(@Query('email') email: string) {
|
|
440
|
+
return this.service.isEmailAvailable(email);
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
Service:
|
|
445
|
+
|
|
446
|
+
```ts
|
|
447
|
+
async isEmailAvailable(email: string) {
|
|
448
|
+
let exists = false;
|
|
449
|
+
|
|
450
|
+
await this.prisma.withExposedModels(['user'], async () => {
|
|
451
|
+
const user = await this.prisma.model.user.findFirst({ where: { email } });
|
|
452
|
+
exists = !!user;
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
return { available: !exists };
|
|
456
|
+
}
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
This pattern should be used sparingly and intentionally.
|
|
460
|
+
|
|
461
|
+
---
|
|
462
|
+
|
|
463
|
+
## Limitations
|
|
464
|
+
|
|
465
|
+
### Prisma method restrictions (permission safety)
|
|
466
|
+
|
|
467
|
+
Avoid these Prisma methods because they can bypass or complicate granular permission filtering:
|
|
468
|
+
|
|
469
|
+
- `findUnique` → use `findFirst`
|
|
470
|
+
- `delete` → use `deleteMany` with a single-item filter
|
|
471
|
+
- `update` → use `updateMany` with a single-item filter
|
|
472
|
+
|
|
473
|
+
Operational guidance:
|
|
474
|
+
|
|
475
|
+
- Prefer `findMany`, `findFirst`, `updateMany`, and `deleteMany`.
|
|
476
|
+
- When you intend a single-record operation, use a restrictive `where` clause.
|
|
477
|
+
|
|
478
|
+
### GraphQL status
|
|
479
|
+
|
|
480
|
+
GraphQL is part of the direction, but is **not fully ready** in the current release. REST CRUD + AdminJS are the primary integration surfaces for now.
|
|
481
|
+
|
|
482
|
+
---
|
|
483
|
+
|
|
484
|
+
## FAQ
|
|
485
|
+
|
|
486
|
+
**Are CRUD endpoints only for AdminJS?**
|
|
487
|
+
No. They can be used for any API consumer. Permissions are enforced consistently.
|
|
488
|
+
|
|
489
|
+
**Do I need to treat generated modules as read-only?**
|
|
490
|
+
No. Generated NestJS modules are **editable** and intended to be extended.
|
|
491
|
+
|
|
492
|
+
---
|
|
493
|
+
|
|
494
|
+
## Roadmap
|
|
495
|
+
|
|
496
|
+
Planned and under consideration:
|
|
497
|
+
|
|
498
|
+
- Production-ready GraphQL
|
|
499
|
+
- Object storage patterns (S3-friendly out-of-the-box support)
|
|
500
|
+
- Forgot-password flow
|
|
501
|
+
- Email templates + sending primitives
|
|
502
|
+
- Push notifications primitives
|
|
503
|
+
- WebSockets support
|
|
504
|
+
- Stronger testing baseline (unit tests scaffolding and patterns)
|
|
505
|
+
- “Agent-ready” development guidance (`agents.txt`, conventions for coding agents)
|
|
506
|
+
- Optional AI-assisted project scaffolding
|
|
507
|
+
|
|
508
|
+
---
|
|
509
|
+
|
|
510
|
+
## Support
|
|
511
|
+
|
|
512
|
+
Email: **core@appx-digital.com**
|
|
513
|
+
|
|
514
|
+
---
|
|
515
|
+
|
|
516
|
+
## Security
|
|
517
|
+
|
|
518
|
+
Report vulnerabilities privately to **core@appx-digital.com**. Do not open public issues for sensitive reports.
|
|
519
|
+
|
|
520
|
+
---
|
|
521
|
+
|
|
522
|
+
## Contributing
|
|
523
|
+
|
|
524
|
+
1. Fork the repository.
|
|
525
|
+
2. Create a feature branch.
|
|
526
|
+
3. Keep changes focused; add tests where applicable.
|
|
527
|
+
4. Open a PR with clear motivation and impact.
|
|
528
|
+
|
|
529
|
+
---
|
|
530
|
+
|
|
531
|
+
## License
|
|
532
|
+
|
|
533
|
+
MIT License. See [LICENSE](LICENSE).
|
|
@@ -227,6 +227,7 @@ async function createAdminJsModule(adminConfig, permissionConfig) {
|
|
|
227
227
|
});
|
|
228
228
|
const requestContextLayer = httpAdapter.router.stack.splice(httpAdapter.router.stack.length - 1, 1)[0];
|
|
229
229
|
httpAdapter.router.stack.splice(adminIdx, 0, requestContextLayer);
|
|
230
|
+
console.log(httpAdapter.router);
|
|
230
231
|
}
|
|
231
232
|
},
|
|
232
233
|
};
|
|
@@ -5,7 +5,7 @@ declare const LocalStrategy_base: new (...args: [] | [options: import("passport-
|
|
|
5
5
|
validate(...args: any[]): unknown;
|
|
6
6
|
};
|
|
7
7
|
export declare class LocalStrategy extends LocalStrategy_base {
|
|
8
|
-
|
|
8
|
+
protected readonly authService: AuthService;
|
|
9
9
|
private readonly usernameField;
|
|
10
10
|
private readonly passwordField;
|
|
11
11
|
constructor(authService: AuthService);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local.strategy.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/local.strategy.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,QAAQ,EAAC,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAC,WAAW,EAAC,MAAM,gBAAgB,CAAC;AAC3C,OAAO,kBAAkB,CAAC;;;;AAmB1B,qBACa,aAAc,SAAQ,kBAA0B;IAI7C,
|
|
1
|
+
{"version":3,"file":"local.strategy.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/local.strategy.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,QAAQ,EAAC,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAC,WAAW,EAAC,MAAM,gBAAgB,CAAC;AAC3C,OAAO,kBAAkB,CAAC;;;;AAmB1B,qBACa,aAAc,SAAQ,kBAA0B;IAI7C,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,WAAW;IAHvD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;gBAER,WAAW,EAAE,WAAW;IAYjD,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;CAOnE"}
|
package/package.json
CHANGED