@gzl10/nexus-backend 0.12.4 → 0.12.7

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
@@ -4,11 +4,11 @@
4
4
  [![npm downloads](https://img.shields.io/npm/dm/@gzl10/nexus-backend.svg)](https://www.npmjs.com/package/@gzl10/nexus-backend)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6
6
 
7
- > **Warning**: This project is currently in testing/experimental phase. Use at your own risk.
7
+ > **Warning**: This project is in experimental phase. Use at your own risk.
8
8
 
9
- Backend as a Service (BaaS) with Express 5, Knex and CASL. Build modular, type-safe backends with authentication, authorization, and real-time features.
9
+ Backend as a Service (BaaS) with Express 5, Knex, and CASL. Build modular, type-safe backends with authentication, authorization, and real-time features.
10
10
 
11
- **Repository**: [https://gitlab.gzl10.com/oss/nexus](https://gitlab.gzl10.com/oss/nexus)
11
+ **Repository**: [gitlab.gzl10.com/oss/nexus](https://gitlab.gzl10.com/oss/nexus)
12
12
 
13
13
  ## Installation
14
14
 
@@ -21,79 +21,155 @@ pnpm add @gzl10/nexus-backend
21
21
  ```typescript
22
22
  import { start } from '@gzl10/nexus-backend'
23
23
 
24
- start()
24
+ await start({
25
+ port: 3000,
26
+ database: { url: 'file:./data/nexus.db' }
27
+ })
25
28
  ```
26
29
 
27
30
  ## Features
28
31
 
32
+ ### Core Modules
33
+
34
+ | Module | Description |
35
+ | ------ | ----------- |
36
+ | **auth** | JWT authentication with access/refresh tokens, OTP challenge, password reset |
37
+ | **users** | User management with multi-role support |
38
+ | **storage** | File storage (local/S3), thumbnails, deduplication |
39
+ | **mail** | Email sending via SMTP (Nodemailer) |
40
+ | **notifications** | In-app notification system |
41
+ | **schedules** | Scheduled tasks (cron jobs) |
42
+ | **compliance** | GDPR consent tracking, legal documents |
43
+ | **tags** | Tagging system for any entity |
44
+ | **links** | Entity relationships and linking |
45
+
46
+ ### Platform Features
47
+
29
48
  | Feature | Description |
30
49
  | ------- | ----------- |
31
- | Express 5 | Modern async error handling |
32
- | Knex ORM | SQLite, PostgreSQL, MySQL support |
33
- | CASL | Attribute-based access control |
34
- | JWT Auth | Access + refresh tokens with rotation |
35
- | Socket.IO | Real-time events and notifications |
36
- | Modular | Plugin architecture for extensions |
37
- | Storage | Local and S3-compatible storage |
38
- | Rate Limiting | Built-in rate limiting middleware |
50
+ | **Express 5** | Modern async error handling |
51
+ | **Multi-DB** | SQLite, PostgreSQL, MySQL via Knex |
52
+ | **CASL** | Attribute-based access control with multi-role |
53
+ | **Socket.IO** | Real-time events and notifications |
54
+ | **Plugin Architecture** | Extend with custom modules and plugins |
55
+ | **OpenAPI** | Auto-generated API documentation |
56
+ | **Rate Limiting** | Built-in rate limiting middleware |
39
57
 
40
58
  ## Configuration
41
59
 
42
- Create a `.env` file:
60
+ ```typescript
61
+ import { start } from '@gzl10/nexus-backend'
62
+
63
+ await start({
64
+ port: 3000,
65
+ host: '0.0.0.0',
66
+
67
+ // Frontend SPA
68
+ ui: {
69
+ enabled: true,
70
+ base: '/',
71
+ path: '../ui/dist'
72
+ },
73
+
74
+ // Custom routes
75
+ beforeRoutes: (app) => {
76
+ app.get('/api/health_custom', (_req, res) => res.json({ ok: true }))
77
+ },
78
+ afterRoutes: (app, serveSPA) => {
79
+ serveSPA('/admin', '../admin/dist')
80
+ },
81
+
82
+ // Event subscriptions
83
+ events: {
84
+ 'auth.login': ({ userId }) => console.log('Login:', userId),
85
+ 'db.users.created': ({ data }) => console.log('New user:', data.email)
86
+ },
87
+
88
+ // Custom CASL rules
89
+ casl: (user, { can }) => {
90
+ if (user.email.endsWith('@admin.com')) can('manage', 'all')
91
+ },
92
+
93
+ // Lifecycle hooks
94
+ onReady: (app, port, host) => console.log(`Ready at ${host}:${port}`),
95
+ beforeClose: () => console.log('Shutting down...')
96
+ })
97
+ ```
98
+
99
+ ### Config Options
100
+
101
+ | Option | Type | Default | Description |
102
+ | ------ | ---- | ------- | ----------- |
103
+ | `port` | `number` | `3000` | Server port |
104
+ | `host` | `string` | `0.0.0.0` | Server host |
105
+ | `ui.enabled` | `boolean` | `true` | Serve frontend SPA |
106
+ | `ui.base` | `string` | `/` | Base path for UI |
107
+ | `ui.path` | `string` | `../ui/dist` | Path to frontend dist |
108
+ | `database.url` | `string` | - | Database connection URL |
109
+ | `plugins` | `PluginManifest[]` | - | Plugins to register |
110
+ | `modules` | `ModuleManifest[]` | - | Standalone modules |
111
+ | `beforeRoutes` | `function` | - | Hook before module routes |
112
+ | `afterRoutes` | `function` | - | Hook after module routes |
113
+ | `events` | `object` | - | Event subscriptions |
114
+ | `casl` | `function` | - | Custom CASL rules |
115
+ | `onReady` | `function` | - | Server ready callback |
116
+ | `beforeClose` | `function` | - | Pre-shutdown callback |
117
+
118
+ ### Environment Variables
43
119
 
44
120
  ```env
45
- PORT=3000
46
- CORS_ORIGIN=http://localhost:5173
47
- DATABASE_URL=file:./data/nexus.db
48
- JWT_SECRET=your-secret-key
121
+ AUTH_SECRET=your-secret-key-min-32-chars # Required (min 32 chars)
122
+ DATABASE_URL=file:./data/nexus.db # SQLite, PostgreSQL, or MySQL
49
123
  ```
50
124
 
51
- ## CLI
125
+ See `.env.example` for full list of options.
52
126
 
53
- ```bash
54
- # Start server
55
- npx nexus start
127
+ ## Extending with Plugins
56
128
 
57
- # Run migrations
58
- npx nexus migrate
129
+ ```typescript
130
+ import { start } from '@gzl10/nexus-backend'
131
+ import { cmsPlugin } from '@gzl10/nexus-plugin-cms'
132
+ import { myCustomModule } from './my-module'
59
133
 
60
- # Seed database
61
- npx nexus seed
134
+ await start({
135
+ plugins: [cmsPlugin],
136
+ modules: [myCustomModule]
137
+ })
62
138
  ```
63
139
 
64
140
  ## Exported Functions
65
141
 
66
142
  | Function | Description |
67
143
  | -------- | ----------- |
68
- | `start()` | Start the server |
144
+ | `start(config?)` | Start the server |
69
145
  | `stop()` | Stop the server |
70
- | `createApp()` | Create Express app instance |
71
- | `registerModule()` | Register a custom module |
72
- | `registerPlugin()` | Register a plugin |
73
- | `defineAbilityFor()` | Create CASL abilities |
74
- | `getIO()` | Get Socket.IO instance |
146
+ | `restart(config?)` | Restart with new config |
147
+ | `db` | Knex database instance |
148
+ | `nexusEvents` | Event emitter for system events |
149
+ | `defineAbilityFor(user, permissions)` | Create CASL abilities |
150
+ | `getCoreModules()` | Get core modules list |
151
+ | `getUserModules()` | Get user modules list |
152
+
153
+ ## CLI
154
+
155
+ ```bash
156
+ npx nexus start # Start server
157
+ npx nexus migrate # Run migrations
158
+ npx nexus seed # Seed database
159
+ ```
75
160
 
76
161
  ## Development
77
162
 
78
163
  ```bash
79
- pnpm dev # Start dev server with watch
164
+ pnpm dev # Start dev server
80
165
  pnpm build # Build for production
81
- pnpm start # Start production server
82
166
  pnpm typecheck # Type check
83
- pnpm lint # Linting
84
167
  pnpm test # Run tests
85
- pnpm test:coverage # Tests with coverage
86
168
  ```
87
169
 
88
170
  ## Stack
89
171
 
90
- - **Express 5** - Web framework
91
- - **Knex** - SQL query builder
92
- - **CASL** - Authorization
93
- - **JWT** - Authentication
94
- - **Socket.IO** - Real-time
95
- - **Zod** - Validation
96
- - **Pino** - Logging
172
+ Express 5 | Knex | CASL | JWT | Socket.IO | Zod | Pino
97
173
 
98
174
  ## License
99
175
 
package/dist/cli.js CHANGED
@@ -30,7 +30,7 @@ program.command("ui").description("Open UI in browser").option("-p, --port <port
30
30
  const baseUrl = (process.env["BACKEND_URL"] || `http://localhost:${port}`).replace(/\/$/, "");
31
31
  const url = `${baseUrl}/ui`;
32
32
  try {
33
- const res = await fetch(`${baseUrl}/health`);
33
+ const res = await fetch(`${baseUrl}/api/health`);
34
34
  if (!res.ok) throw new Error();
35
35
  } catch {
36
36
  consola.error(`Nexus no est\xE1 corriendo en ${baseUrl}`);
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/env-loader.ts","../src/cli.ts"],"sourcesContent":["/**\n * Carga .env antes de cualquier otro import del proyecto\n * Este archivo SOLO importa de node/npm, nunca de archivos locales\n */\nimport { existsSync } from 'fs'\nimport { join, dirname } from 'path'\nimport { config } from 'dotenv'\n\nlet dir = process.cwd()\nconsole.log('[env-loader] Starting search from cwd:', dir)\n\nfor (let i = 0; i < 3; i++) {\n const envPath = join(dir, '.env')\n const exists = existsSync(envPath)\n console.log(`[env-loader] Checking: ${envPath}`, exists ? '✓ FOUND' : '✗ not found')\n if (exists) {\n config({ path: envPath })\n console.log('[env-loader] Loaded:', envPath)\n break\n }\n const parent = dirname(dir)\n if (parent === dir) break\n dir = parent\n}\n","#!/usr/bin/env node\nimport './env-loader.js'\nimport { Command } from 'commander'\nimport { consola } from 'consola'\n\nconst program = new Command()\n\nprogram\n .name('nexus')\n .description('Nexus Backend CLI')\n .version('0.1.0')\n\nprogram\n .command('ui')\n .description('Open UI in browser')\n .option('-p, --port <port>', 'Nexus backend port')\n .action(async (options) => {\n const port = parseInt(options.port || process.env['PORT'] || '3000', 10)\n const baseUrl = (process.env['BACKEND_URL'] || `http://localhost:${port}`).replace(/\\/$/, '')\n const url = `${baseUrl}/ui`\n\n // Verificar que Nexus está corriendo\n try {\n const res = await fetch(`${baseUrl}/health`)\n if (!res.ok) throw new Error()\n } catch {\n consola.error(`Nexus no está corriendo en ${baseUrl}`)\n consola.info('Inicia el servidor primero con: pnpm dev')\n process.exit(1)\n }\n\n // Abrir navegador\n const { exec } = await import('child_process')\n const cmd = process.platform === 'darwin' ? 'open'\n : process.platform === 'win32' ? 'start'\n : 'xdg-open'\n\n exec(`${cmd} ${url}`)\n consola.success(`Abriendo: ${url}`)\n })\n\nprogram.parse()\n"],"mappings":";;;AAIA,SAAS,kBAAkB;AAC3B,SAAS,MAAM,eAAe;AAC9B,SAAS,cAAc;AAEvB,IAAI,MAAM,QAAQ,IAAI;AACtB,QAAQ,IAAI,0CAA0C,GAAG;AAEzD,SAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,QAAM,UAAU,KAAK,KAAK,MAAM;AAChC,QAAM,SAAS,WAAW,OAAO;AACjC,UAAQ,IAAI,0BAA0B,OAAO,IAAI,SAAS,iBAAY,kBAAa;AACnF,MAAI,QAAQ;AACV,WAAO,EAAE,MAAM,QAAQ,CAAC;AACxB,YAAQ,IAAI,wBAAwB,OAAO;AAC3C;AAAA,EACF;AACA,QAAM,SAAS,QAAQ,GAAG;AAC1B,MAAI,WAAW,IAAK;AACpB,QAAM;AACR;;;ACrBA,SAAS,eAAe;AACxB,SAAS,eAAe;AAExB,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,OAAO,EACZ,YAAY,mBAAmB,EAC/B,QAAQ,OAAO;AAElB,QACG,QAAQ,IAAI,EACZ,YAAY,oBAAoB,EAChC,OAAO,qBAAqB,oBAAoB,EAChD,OAAO,OAAO,YAAY;AACzB,QAAM,OAAO,SAAS,QAAQ,QAAQ,QAAQ,IAAI,MAAM,KAAK,QAAQ,EAAE;AACvE,QAAM,WAAW,QAAQ,IAAI,aAAa,KAAK,oBAAoB,IAAI,IAAI,QAAQ,OAAO,EAAE;AAC5F,QAAM,MAAM,GAAG,OAAO;AAGtB,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,GAAG,OAAO,SAAS;AAC3C,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM;AAAA,EAC/B,QAAQ;AACN,YAAQ,MAAM,iCAA8B,OAAO,EAAE;AACrD,YAAQ,KAAK,0CAA0C;AACvD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,EAAE,KAAK,IAAI,MAAM,OAAO,eAAe;AAC7C,QAAM,MAAM,QAAQ,aAAa,WAAW,SAChC,QAAQ,aAAa,UAAU,UAC/B;AAEZ,OAAK,GAAG,GAAG,IAAI,GAAG,EAAE;AACpB,UAAQ,QAAQ,aAAa,GAAG,EAAE;AACpC,CAAC;AAEH,QAAQ,MAAM;","names":[]}
1
+ {"version":3,"sources":["../src/env-loader.ts","../src/cli.ts"],"sourcesContent":["/**\n * Loads .env before any other project import.\n * This file ONLY imports from node/npm, never from local files.\n */\nimport { existsSync } from 'fs'\nimport { join, dirname } from 'path'\nimport { config } from 'dotenv'\n\nlet dir = process.cwd()\nconsole.log('[env-loader] Starting search from cwd:', dir)\n\nfor (let i = 0; i < 3; i++) {\n const envPath = join(dir, '.env')\n const exists = existsSync(envPath)\n console.log(`[env-loader] Checking: ${envPath}`, exists ? '✓ FOUND' : '✗ not found')\n if (exists) {\n config({ path: envPath })\n console.log('[env-loader] Loaded:', envPath)\n break\n }\n const parent = dirname(dir)\n if (parent === dir) break\n dir = parent\n}\n","#!/usr/bin/env node\nimport './env-loader.js'\nimport { Command } from 'commander'\nimport { consola } from 'consola'\n\nconst program = new Command()\n\nprogram\n .name('nexus')\n .description('Nexus Backend CLI')\n .version('0.1.0')\n\nprogram\n .command('ui')\n .description('Open UI in browser')\n .option('-p, --port <port>', 'Nexus backend port')\n .action(async (options) => {\n const port = parseInt(options.port || process.env['PORT'] || '3000', 10)\n const baseUrl = (process.env['BACKEND_URL'] || `http://localhost:${port}`).replace(/\\/$/, '')\n const url = `${baseUrl}/ui`\n\n // Verificar que Nexus está corriendo\n try {\n const res = await fetch(`${baseUrl}/api/health`)\n if (!res.ok) throw new Error()\n } catch {\n consola.error(`Nexus no está corriendo en ${baseUrl}`)\n consola.info('Inicia el servidor primero con: pnpm dev')\n process.exit(1)\n }\n\n // Abrir navegador\n const { exec } = await import('child_process')\n const cmd = process.platform === 'darwin' ? 'open'\n : process.platform === 'win32' ? 'start'\n : 'xdg-open'\n\n exec(`${cmd} ${url}`)\n consola.success(`Abriendo: ${url}`)\n })\n\nprogram.parse()\n"],"mappings":";;;AAIA,SAAS,kBAAkB;AAC3B,SAAS,MAAM,eAAe;AAC9B,SAAS,cAAc;AAEvB,IAAI,MAAM,QAAQ,IAAI;AACtB,QAAQ,IAAI,0CAA0C,GAAG;AAEzD,SAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,QAAM,UAAU,KAAK,KAAK,MAAM;AAChC,QAAM,SAAS,WAAW,OAAO;AACjC,UAAQ,IAAI,0BAA0B,OAAO,IAAI,SAAS,iBAAY,kBAAa;AACnF,MAAI,QAAQ;AACV,WAAO,EAAE,MAAM,QAAQ,CAAC;AACxB,YAAQ,IAAI,wBAAwB,OAAO;AAC3C;AAAA,EACF;AACA,QAAM,SAAS,QAAQ,GAAG;AAC1B,MAAI,WAAW,IAAK;AACpB,QAAM;AACR;;;ACrBA,SAAS,eAAe;AACxB,SAAS,eAAe;AAExB,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,OAAO,EACZ,YAAY,mBAAmB,EAC/B,QAAQ,OAAO;AAElB,QACG,QAAQ,IAAI,EACZ,YAAY,oBAAoB,EAChC,OAAO,qBAAqB,oBAAoB,EAChD,OAAO,OAAO,YAAY;AACzB,QAAM,OAAO,SAAS,QAAQ,QAAQ,QAAQ,IAAI,MAAM,KAAK,QAAQ,EAAE;AACvE,QAAM,WAAW,QAAQ,IAAI,aAAa,KAAK,oBAAoB,IAAI,IAAI,QAAQ,OAAO,EAAE;AAC5F,QAAM,MAAM,GAAG,OAAO;AAGtB,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,GAAG,OAAO,aAAa;AAC/C,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM;AAAA,EAC/B,QAAQ;AACN,YAAQ,MAAM,iCAA8B,OAAO,EAAE;AACrD,YAAQ,KAAK,0CAA0C;AACvD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,EAAE,KAAK,IAAI,MAAM,OAAO,eAAe;AAC7C,QAAM,MAAM,QAAQ,aAAa,WAAW,SAChC,QAAQ,aAAa,UAAU,UAC/B;AAEZ,OAAK,GAAG,GAAG,IAAI,GAAG,EAAE;AACpB,UAAQ,QAAQ,aAAa,GAAG,EAAE;AACpC,CAAC;AAEH,QAAQ,MAAM;","names":[]}