@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
- private authService;
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,OAAO,CAAC,WAAW;IAH/B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;gBAEnB,WAAW,EAAE,WAAW;IAYtC,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;CAOnE"}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appxdigital/appx-core",
3
- "version": "0.1.117",
3
+ "version": "0.1.118",
4
4
  "description": "Appx Core is a library that provides a set of tools to help you build your application faster.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",