aegisnode 0.1.0 → 0.1.1

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
@@ -2,77 +2,24 @@
2
2
 
3
3
  ![AegisNode Banner](assets/aegisnode-banner.svg)
4
4
 
5
- AegisNode is a modular, view-first Node.js framework for building web apps, JSON APIs, and hybrid projects without spending the first part of the project wiring the same infrastructure again and again.
6
- It gives you a structured project layout, runtime injection, CLI scaffolding, and production-ready defaults while still keeping the Node.js and Express ecosystem familiar.
5
+ AegisNode is a modular, view-first Node.js framework for building web apps, JSON APIs, and hybrid projects.
6
+ It gives you a structured project layout, runtime injection, CLI scaffolding, and production-ready defaults so you can start building features instead of first wiring routing, config loading, auth, uploads, and other framework glue.
7
7
 
8
8
  AegisNode is designed for developers who want more structure than raw Express, but do not want a framework that hides the Node.js runtime behind too many abstractions.
9
- It keeps the request/response model familiar while organizing the codebase around clear app boundaries, runtime-injected dependencies, and reusable layers such as views, services, models, validators, and subscribers.
9
+ It keeps the request/response model familiar while organizing the codebase around clear app boundaries and reusable layers such as views, services, models, validators, subscribers, and app-local utilities.
10
10
 
11
11
  It works well for projects that mix server-rendered pages and JSON endpoints, for teams that want a consistent project shape from the start, and for codebases that need built-in support for common backend concerns like auth, uploads, i18n, mail, maintenance mode, and environment-driven configuration.
12
- The goal is to reduce setup time, remove repetitive infrastructure work, and give the project a cleaner long-term structure without making day-to-day development feel heavy.
13
-
14
- ## Documentation Guide
15
-
16
- If you are new to AegisNode, read this README in this order:
17
- - Quick Start: create a project, install dependencies, run the server, and understand startup mode rules.
18
- - Core Concepts And App Structure: understand apps, layers, injected runtime context, and project flow.
19
- - Common Tasks And Feature Guides: uploads, API apps, auth, templates, i18n, mail, and related features.
20
- - Full Settings Reference: use this as the complete config manual once you already know what feature you need.
21
- - Runtime Patterns And Advanced Topics: validators, strict layers, subscribers, and security details.
22
-
23
- Standalone HTML handbook:
24
- - Open `docs/index.html` in a browser for a sidebar-based documentation view.
25
-
26
- ## How AegisNode Helps
27
-
28
- AegisNode helps by standardizing the parts that usually consume time early in a project:
29
- - project scaffolding and app generation,
30
- - route and layer organization,
31
- - dependency injection and shared runtime context,
32
- - config loading and environment overrides,
33
- - auth, upload, mail, websocket, and i18n integration,
34
- - operational helpers such as health checks, maintenance mode, and project diagnostics.
35
-
36
- This means you spend less time writing framework glue and more time writing business features.
37
-
38
- ## Why Use AegisNode
39
-
40
- Choose AegisNode if you want a project starter that remains readable as it grows.
41
- It keeps the development model simple, but adds enough structure and tooling to make larger codebases easier to navigate, extend, and maintain.
42
-
43
- Core features:
44
-
45
- - CLI generators (`startproject`, `createapp`, `runserver`)
46
- - App scaffold repair command (`fix`)
47
- - Startup entry generator (`generateloader`)
48
- - Project health checker (`doctor`)
49
- - Dependency updater (`updatedeps`)
50
- - Maintenance mode with custom HTML responses
51
- - Generators for app artifacts (`generate view|model|validator|dto|service|subscriber|route`)
52
- - DI container
53
- - Event system with subscribers
54
- - Modular app structure
55
- - SQL/NoSQL bootstrap via QueryMesh/Mongoose
56
- - WebSocket bootstrap using Socket.IO
57
- - Built-in file uploads with size/type limits (`route.upload`)
58
- - Built-in mail transport wrapper (`mail.send`, `req.aegis.mail.send`)
59
- - Centralized config and loaders
60
- - Security headers via Helmet (configurable CSP) + CSRF protection for form submissions
61
- - Built-in rate limiting for basic DDoS resistance
62
- - Root route file `routes.js` (not `routes/` folder)
63
- - Automatic default confirmation page on `/` when no custom `/` route exists
64
- - App folder uses `views.js` (not `controllers/` folder)
65
- - `createapp` uses file modules: `views.js`, `models.js`, `validators.js`, `routes.js`, `subscribers.js`, `services.js`, `utils.js`
66
- - `createapp` also generates app tests in `apps/<app>/tests`
67
- - EJS templates configurable in `settings.js` with Django-style base layout flow
68
- - Built-in runtime helpers (`money`, `number`, `dateTime`, `timeElapsed`, `toObjectId`) + `jlive` bridge
69
-
70
- `startproject` creates `app.js`, `loader.cjs`, `.env`, `settings.js`, and `routes.js` without creating any default app.
71
- Use `startproject --typescript` to generate `app.ts`, `settings.ts`, `routes.ts`, app `*.ts` files, and `tsconfig.json` instead.
72
- It does not create `public/` or `logs/`; create your own folders and set them in `settings.js` or `settings.ts`.
73
12
 
74
- Environment files are loaded automatically before `settings.js` or `settings.ts` is imported.
75
- Supported files:
13
+ Read this README in this order:
14
+ 1. Quick Start: get a project created, installed, and running.
15
+ 2. Core Concepts And App Structure: understand where code lives and how layers fit together.
16
+ 3. Common Tasks And Feature Guides: jump to the feature you need once the basics are clear.
17
+ 4. Full Settings Reference: use this when you already know which config block you are looking for.
18
+ 5. Runtime Patterns And Advanced Topics: read this last for middleware, validators, subscribers, and strict-layer behavior.
19
+
20
+ If you prefer a sidebar-based handbook, open `docs/index.html` in a browser.
21
+
22
+ Environment files are loaded automatically before `settings.js` or `settings.ts` is imported:
76
23
  - `.env`
77
24
  - `.env.local`
78
25
  - `.env.<NODE_ENV>`
@@ -82,203 +29,132 @@ Shell or hosting-panel environment variables win over values from `.env` files.
82
29
 
83
30
  ## Quick Start
84
31
 
85
- ### CLI
32
+ Use this section to get a project running first. Skip to later sections only when you need a specific feature or configuration block.
33
+
34
+ ### Create A Project
86
35
 
87
36
  ```bash
88
37
  npm install -g aegisnode
89
38
 
39
+ mkdir blog && cd blog
90
40
  aegisnode startproject blog
91
- aegisnode startproject blog-ts --typescript
92
- npm --prefix blog install
93
- aegisnode runserver --project blog
94
-
95
- aegisnode createapp users --project blog
96
- aegisnode fix --app users --project blog
97
- aegisnode generate view profile --app users --project blog
98
- aegisnode generate route profile --app users --project blog
99
- aegisnode generateloader --project blog
100
- aegisnode doctor --project blog
101
- aegisnode doctor --app users --project blog
102
- aegisnode updatedeps --project blog
103
- ```
104
-
105
- `cd blog` is optional. You can run commands from parent folder with `--project blog`.
106
-
107
- Use `--typescript` on `startproject` when you want a TypeScript scaffold. That generates `app.ts`, `settings.ts`, `routes.ts`, `tsconfig.json`, app files like `views.ts`/`services.ts`, and generated artifacts such as `profile.view.ts`.
108
-
109
- ### JavaScript vs TypeScript Projects
110
-
111
- The project type is chosen once at `startproject` time:
112
- - `aegisnode startproject blog` creates a JavaScript project
113
- - `aegisnode startproject blog --typescript` creates a TypeScript project
114
-
115
- After that, the rest of the CLI follows the project automatically:
116
- - `createapp` generates `views.js` / `services.js` / `routes.js` in JavaScript projects, or `views.ts` / `services.ts` / `routes.ts` in TypeScript projects
117
- - `generate` creates artifacts with the same extension as the project, for example `profile.view.js` or `profile.view.ts`
118
- - `fix`, `doctor`, and `generateloader` also check and repair the matching project file type automatically
119
-
120
- `createapp`, `fix`, `generate`, `runserver`, `generateloader`, `doctor`, and `updatedeps` are project-level commands.
121
- Run them from the project root; do not `cd` into `apps/<app>`.
122
- Startup mode rules:
123
- - Development (`env === development`): start with `aegisnode runserver` only.
124
- - Non-development (`env !== development`): start with `node loader.cjs` (or your process manager/host pointing to `loader.cjs`).
125
- - `node app.js` and `node loader.cjs` are rejected in development mode.
126
- - `aegisnode runserver` is rejected outside development mode.
127
-
128
- ### Trust Proxy
129
-
130
- If your app runs behind Nginx, Apache, Passenger, or another reverse proxy that terminates HTTPS before the Node process, set top-level `trustProxy` in `settings.js`:
131
-
132
- ```js
133
- export default {
134
- trustProxy: 1,
135
- };
136
41
  ```
137
42
 
138
- This is the AegisNode equivalent of `app.set('trust proxy', 1)` in raw Express. It makes `req.secure`, `req.protocol`, client IP detection, secure cookies, and HTTPS-aware auth logic behave correctly behind the proxy.
139
-
140
- Prefer an exact value such as `1`, `'loopback'`, or a subnet string instead of `true`.
141
-
142
- ### Deploy On Phusion Passenger
143
-
144
- AegisNode supports Passenger-style startup using the generated `loader.cjs`.
145
-
146
- Passenger setup (Apache/Nginx/Plesk/cPanel/etc.):
147
- 1. Set **Application Root** to your project folder.
148
- 2. Set **Startup File** to `loader.cjs`.
149
- 3. Install dependencies in project root (for example: `npm install --omit=dev`).
150
- 4. Set environment variables (at minimum `NODE_ENV=production`; keep `PORT` managed by Passenger).
151
- 5. Restart the Node app from your hosting panel/service.
152
-
153
- Plesk note: these map to **Application Root** and **Application Startup File** fields.
154
-
155
- HTTPS note:
156
- - If TLS is terminated by Passenger/Apache/Nginx, keep `https.enabled` off and set top-level `trustProxy` to `1` (or another exact proxy-hop/subnet value) so `req.secure`, secure cookies, and OAuth2 HTTPS checks work correctly.
157
- - Only enable `https` in `settings.js` when Node itself should serve TLS directly.
158
-
159
- How it works:
160
- - `loader.cjs` imports `app.js` in JavaScript projects or `app.ts` in TypeScript projects.
161
- - `app.js` / `app.ts` starts AegisNode with project root resolved from its own file location, so it works correctly under Passenger.
43
+ Create and enter the project directory first. `startproject` scaffolds into the current empty directory; it does not create a nested folder for you.
44
+ Use plain `startproject` for a JavaScript project. Add `--typescript` once if you want the whole scaffold to use `.ts`.
45
+ For a TypeScript project, use `aegisnode startproject blog --typescript` inside the target folder instead.
162
46
 
47
+ `startproject` creates `app.js`, `loader.cjs`, `.env`, `settings.js`, and `routes.js` in the current directory without creating any default app.
48
+ Use `startproject --typescript` to generate `app.ts`, `settings.ts`, `routes.ts`, app `*.ts` files, and `tsconfig.json` instead.
49
+ It also creates `public/` and `templates/` so the default `staticDir` and `templates.dir` targets already exist.
50
+ Create any additional folders such as `logs/` yourself when needed.
163
51
 
164
- Generated routes are auto-wired into `apps/<app>/routes.js`.
165
- `createapp` auto-detects the project root when run inside the project or from a parent folder containing exactly one AegisNode project.
166
- After `createapp user`, `routes.js` is updated with central mapping style:
167
- `route.use('/user', user);`
168
- Only apps declared in `settings.apps` are allowed to load/mount. Startup fails when routes reference an undeclared app.
169
- `--mount` accepts only safe path segments (`a-z`, `A-Z`, `0-9`, `_`, `-`, `:`).
170
-
171
- By default, new app routes are API-ready:
172
- - `GET /<mount>` list
173
- - `POST /<mount>` create
174
- - `GET /<mount>/:id` read
175
- - `PUT /<mount>/:id` update
176
- - `DELETE /<mount>/:id` delete
177
-
178
- Default flow is `route -> validator -> service -> model`.
179
- Default app tests generated by `createapp`:
180
- - JavaScript projects: `apps/<app>/tests/models.test.js`, `validators.test.js`, `services.test.js`, `routes.test.js`
181
- - TypeScript projects: `apps/<app>/tests/models.test.ts`, `validators.test.ts`, `services.test.ts`, `routes.test.ts`
182
-
183
- Run all project tests:
52
+ ### Install Dependencies And Run The Server
184
53
 
185
54
  ```bash
186
- npm test
55
+ npm install
56
+ aegisnode runserver
187
57
  ```
188
58
 
189
- Run project preflight checks:
59
+ If you choose to stay outside the project folder, the project-level commands also accept `--project <path>`.
60
+
61
+ ### Create Your First App
190
62
 
191
63
  ```bash
192
- aegisnode doctor
64
+ aegisnode createapp users
65
+ aegisnode generate view profile --app users
66
+ aegisnode generate route profile --app users
193
67
  ```
194
68
 
195
- `doctor` checks:
196
- - Project structure (`settings.js`, `routes.js`, app folders)
197
- - Startup entry files (`app.js`, `loader.cjs`), with production errors when `loader.cjs` is missing
198
- - App declarations vs filesystem
199
- - Security baseline (`appSecret`, csrf/headers/ddos toggles)
200
- - Auth safety checks (JWT secret, OAuth2 `allowHttp` in production)
201
- - Template directory availability
69
+ `createapp` updates `settings.apps` and the root `routes.js` or `routes.ts` mapping for you.
70
+ It also generates default app tests under `apps/<app>/tests`.
202
71
 
203
- Run app-level scaffold checks for one app:
72
+ ### Startup Mode Rules
204
73
 
205
- ```bash
206
- aegisnode doctor --app users
207
- ```
74
+ - Development (`env === 'development'`): start with `aegisnode runserver`.
75
+ - Non-development (`env !== 'development'`): start with `node loader.cjs`.
76
+ - `node app.js` and `node loader.cjs` are blocked in development mode.
77
+ - `aegisnode runserver` is blocked outside development mode.
208
78
 
209
- App-level doctor focuses on the named app:
210
- - Missing `views.js`, `models.js`, `services.js`, `validators.js`, `routes.js`, `subscribers.js`, `utils.js`
211
- - Missing generated test files under `apps/<app>/tests`
212
- - Missing `settings.apps` declaration for that app
213
- - Missing central `routes.js` import/mount when `autoMountApps` is off
79
+ ### Project Maintenance Commands
214
80
 
215
- Repair a partially missing app scaffold:
81
+ Use these after the project already exists:
216
82
 
217
83
  ```bash
84
+ aegisnode doctor
85
+ aegisnode doctor --app users
218
86
  aegisnode fix --app users
87
+ aegisnode generateloader
88
+ aegisnode updatedeps
219
89
  ```
220
90
 
221
- `fix` recreates missing default app files and tests without overwriting existing files. If the app is missing from `settings.apps` or central `routes.js`, it restores those registrations too.
91
+ What they do:
92
+ - `doctor`: checks project structure, startup entry files, app declarations, and security/auth basics.
93
+ - `doctor --app`: checks one app for missing scaffold files, tests, and registrations.
94
+ - `fix --app`: recreates missing app scaffold files without overwriting existing files.
95
+ - `generateloader`: restores `loader.cjs` and `app.js` when startup files are missing.
96
+ - `updatedeps`: rewrites package dependency ranges to the current npm `latest` versions and reinstalls.
222
97
 
223
- Regenerate project startup entry files if needed:
98
+ Run project tests with:
224
99
 
225
100
  ```bash
226
- aegisnode generateloader
101
+ npm test
227
102
  ```
228
103
 
229
- This restores `loader.cjs` and also recreates `app.js` if it is missing.
104
+ ### JavaScript vs TypeScript Projects
230
105
 
231
- Update project dependencies to the current npm `latest` dist-tag:
106
+ The project type is chosen once at `startproject` time:
107
+ - `aegisnode startproject blog` scaffolds a JavaScript project in the current directory
108
+ - `aegisnode startproject blog --typescript` scaffolds a TypeScript project in the current directory
232
109
 
233
- ```bash
234
- aegisnode updatedeps
235
- ```
110
+ After that, the rest of the CLI follows the project automatically:
111
+ - `createapp` generates `views.js` / `services.js` / `routes.js` in JavaScript projects, or `views.ts` / `services.ts` / `routes.ts` in TypeScript projects
112
+ - `generate` creates artifacts with the same extension as the project, for example `profile.view.js` or `profile.view.ts`
113
+ - `fix`, `doctor`, and `generateloader` also check and repair the matching project file type automatically
236
114
 
237
- `updatedeps` rewrites `dependencies`, `devDependencies`, `optionalDependencies`, and
238
- `peerDependencies` in the project `package.json`, then runs the detected package manager's
239
- `install`. It skips non-registry specs such as `file:`, `workspace:`, and git/http sources.
115
+ `createapp`, `fix`, `generate`, `runserver`, `generateloader`, `doctor`, and `updatedeps` are project-level commands.
116
+ Run them from the project root; do not `cd` into `apps/<app>`.
240
117
 
241
- ### Maintenance Mode
118
+ ## Core Concepts And App Structure
242
119
 
243
- Enable maintenance mode in `settings.js` to serve a maintenance route with `503 Service Unavailable`.
244
- If that route is missing or does not respond, AegisNode renders its internal maintenance fallback view:
120
+ Use this section to understand the project shape before you start adding more features. The goal here is to make it clear where code belongs and what each layer is responsible for.
245
121
 
246
- ```js
247
- export default {
248
- maintenance: {
249
- enabled: true,
250
- route: '/maintenance',
251
- excludePaths: ['/health'],
252
- retryAfter: 120,
253
- },
254
- };
255
- ```
122
+ ### Generated Project Shape
256
123
 
257
- ```js
258
- export default {
259
- register(route) {
260
- route.get('/maintenance', (req, res) => {
261
- res.render('maintenance', {
262
- title: 'Scheduled maintenance',
263
- });
264
- });
265
- },
266
- };
267
- ```
124
+ `startproject` creates the runtime entry files and base config for you in the current directory:
125
+ - JavaScript projects: `app.js`, `loader.cjs`, `.env`, `settings.js`, `routes.js`
126
+ - TypeScript projects: `app.ts`, `loader.cjs`, `.env`, `settings.ts`, `routes.ts`, `tsconfig.json`
268
127
 
269
- Notes:
270
- - `maintenance.route` is internally rewritten, so requests like `/users` can display your maintenance page without a redirect.
271
- - If `maintenance.route` is not defined, or the route does not answer, the bundled fallback view is rendered.
272
- - `excludePaths` lets selected endpoints keep running during maintenance.
273
- - `retryAfter` sets the HTTP `Retry-After` header.
274
- - `maintenance: true` uses the built-in default maintenance page.
275
- - `maintenance: '<html>...</html>'` is still accepted as a shorthand for direct custom HTML.
128
+ `createapp` then adds feature modules under `apps/<app>/`:
129
+ - `views.js` or `views.ts`
130
+ - `models.js` or `models.ts`
131
+ - `services.js` or `services.ts`
132
+ - `validators.js` or `validators.ts`
133
+ - `routes.js` or `routes.ts`
134
+ - `subscribers.js` or `subscribers.ts`
135
+ - `utils.js` or `utils.ts`
136
+ - `tests/`
276
137
 
277
- ### Generated Settings Config
138
+ Other scaffold rules to know:
139
+ - `createapp` auto-detects the project root when you run it inside the project or from a parent folder containing exactly one AegisNode project.
140
+ - New apps are registered in `settings.apps` and mounted in the root `routes.js` or `routes.ts`.
141
+ - Only apps declared in `settings.apps` are allowed to load or mount.
142
+ - `--mount` accepts only safe path segments (`a-z`, `A-Z`, `0-9`, `_`, `-`, `:`).
143
+ - New app routes are generated in an API-ready CRUD shape by default:
144
+ - `GET /<mount>`
145
+ - `POST /<mount>`
146
+ - `GET /<mount>/:id`
147
+ - `PUT /<mount>/:id`
148
+ - `DELETE /<mount>/:id`
149
+ - Default app tests generated by `createapp` are:
150
+ - JavaScript projects: `apps/<app>/tests/models.test.js`, `validators.test.js`, `services.test.js`, `routes.test.js`
151
+ - TypeScript projects: `apps/<app>/tests/models.test.ts`, `validators.test.ts`, `services.test.ts`, `routes.test.ts`
278
152
 
279
- `startproject` generates a minimal `settings.js` and runtime defaults fill the rest.
153
+ ### Generated Settings Shape
280
154
 
281
- Access environment values anywhere with `process.env`:
155
+ `startproject` generates a minimal `settings.js` or `settings.ts`, and runtime defaults fill the rest.
156
+
157
+ Access environment values directly with `process.env` in the settings file:
282
158
 
283
159
  ```js
284
160
  export default {
@@ -289,9 +165,9 @@ export default {
289
165
  };
290
166
  ```
291
167
 
292
- Injected app layers also receive `env`, so views/services/models/validators/controllers/subscribers/loaders can use `env.MY_NAME` without importing `process.env`.
168
+ Injected app layers also receive `env`, so views, services, models, validators, controllers, subscribers, and loaders can use `env.MY_NAME` without importing `process.env`.
293
169
 
294
- `settings.js` (generated shape, or `settings.ts` in TypeScript mode):
170
+ Generated shape:
295
171
 
296
172
  ```js
297
173
  export default {
@@ -300,6 +176,13 @@ export default {
300
176
  host: process.env.HOST || '0.0.0.0',
301
177
  port: process.env.PORT ? Number(process.env.PORT) : 3000,
302
178
  trustProxy: false,
179
+ staticDir: 'public',
180
+ templates: {
181
+ enabled: true,
182
+ engine: 'ejs',
183
+ dir: 'templates',
184
+ base: 'base',
185
+ },
303
186
  security: {
304
187
  appSecret: process.env.APP_SECRET || '<generated-at-scaffold-time>',
305
188
  },
@@ -326,12 +209,11 @@ export default {
326
209
 
327
210
  Notes:
328
211
  - Keep `AEGIS_APPS_START/END` markers; `createapp` updates this list automatically.
329
- - `startproject` writes a local `.env` with a generated `APP_SECRET` and also embeds the same generated secret in `settings.js` as a fallback.
330
- - Add optional blocks manually only when needed: `https`, `templates`, `i18n`, `helpers`, `staticDir`, `websocket`, `uploads`, `mail`, `auth`, `api`, `swagger`, `loaders`, `environments`, `architecture`, `security.headers/ddos/csrf`.
212
+ - `startproject` writes a local `.env` with a generated `APP_SECRET` and also embeds the same generated secret in `settings.js` or `settings.ts` as a fallback.
213
+ - The scaffold already includes `staticDir: 'public'` and a default `templates` block, and it creates those directories for you.
214
+ - Add optional blocks only when you need them: `https`, `i18n`, `helpers`, `websocket`, `uploads`, `mail`, `auth`, `api`, `swagger`, `loaders`, `environments`, `architecture`, `security.headers/ddos/csrf`.
331
215
  - Any section you omit uses framework defaults from `src/runtime/config.js`.
332
216
 
333
- ## Core Concepts And App Structure
334
-
335
217
  ### App File Usage Examples
336
218
 
337
219
  Each generated app usually contains:
@@ -555,6 +437,77 @@ export default {
555
437
 
556
438
  ## Common Tasks And Feature Guides
557
439
 
440
+ Use this section once the project is already running and you need a specific feature, integration, or deployment-related behavior.
441
+
442
+ ### Reverse Proxies And Passenger
443
+
444
+ If HTTPS is terminated by Nginx, Apache, Passenger, or another reverse proxy before the Node process, set top-level `trustProxy` in `settings.js` or `settings.ts`:
445
+
446
+ ```js
447
+ export default {
448
+ trustProxy: 1,
449
+ };
450
+ ```
451
+
452
+ This is the AegisNode equivalent of `app.set('trust proxy', 1)` in raw Express. It makes `req.secure`, `req.protocol`, client IP detection, secure cookies, and HTTPS-aware auth logic behave correctly behind the proxy.
453
+
454
+ Prefer an exact value such as `1`, `'loopback'`, or a subnet string instead of `true`.
455
+
456
+ AegisNode also supports Passenger-style startup using the generated `loader.cjs`.
457
+
458
+ Passenger setup (Apache, Nginx, Plesk, cPanel, and similar hosts):
459
+ 1. Set **Application Root** to your project folder.
460
+ 2. Set **Startup File** to `loader.cjs`.
461
+ 3. Install dependencies in the project root, for example `npm install --omit=dev`.
462
+ 4. Set environment variables. At minimum, make sure the resolved app env is production and let Passenger manage `PORT`.
463
+ 5. Restart the Node app from the hosting panel or service manager.
464
+
465
+ Plesk note: these map to **Application Root** and **Application Startup File** fields.
466
+
467
+ HTTPS note:
468
+ - If TLS is terminated by Passenger, Apache, or Nginx, keep `https.enabled` off and use `trustProxy`.
469
+ - Only enable `https` in `settings.js` or `settings.ts` when Node itself should serve TLS directly.
470
+
471
+ How it works:
472
+ - `loader.cjs` imports `app.js` in JavaScript projects or `app.ts` in TypeScript projects.
473
+ - `app.js` or `app.ts` starts AegisNode with project root resolved from its own file location, so the same entry works under process managers and hosting panels.
474
+
475
+ ### Maintenance Mode
476
+
477
+ Enable maintenance mode in `settings.js` or `settings.ts` to serve a maintenance route with `503 Service Unavailable`.
478
+ If that route is missing or does not respond, AegisNode renders its internal maintenance fallback view.
479
+
480
+ ```js
481
+ export default {
482
+ maintenance: {
483
+ enabled: true,
484
+ route: '/maintenance',
485
+ excludePaths: ['/health'],
486
+ retryAfter: 120,
487
+ },
488
+ };
489
+ ```
490
+
491
+ ```js
492
+ export default {
493
+ register(route) {
494
+ route.get('/maintenance', (req, res) => {
495
+ res.render('maintenance', {
496
+ title: 'Scheduled maintenance',
497
+ });
498
+ });
499
+ },
500
+ };
501
+ ```
502
+
503
+ Notes:
504
+ - `maintenance.route` is internally rewritten, so requests like `/users` can display your maintenance page without a redirect.
505
+ - If `maintenance.route` is not defined, or the route does not answer, the bundled fallback view is rendered.
506
+ - `excludePaths` lets selected endpoints keep running during maintenance.
507
+ - `retryAfter` sets the HTTP `Retry-After` header.
508
+ - `maintenance: true` uses the built-in default maintenance page.
509
+ - `maintenance: '<html>...</html>'` is still accepted as a shorthand for direct custom HTML.
510
+
558
511
  ### File Uploads
559
512
 
560
513
  AegisNode provides built-in upload middleware on route API as `route.upload`.
@@ -895,7 +848,7 @@ export default {
895
848
  },
896
849
  security: {
897
850
  ddos: {
898
- maxRequests: 120,
851
+ maxRequests: 300,
899
852
  },
900
853
  },
901
854
  environments: {
@@ -1676,6 +1629,8 @@ Then in EJS:
1676
1629
  <!-- SETTINGS_REFERENCE_START -->
1677
1630
  ## Full Settings Reference
1678
1631
 
1632
+ Use this section as a config manual. It is intentionally reference-heavy and is easier to use after you already know which feature or runtime block you need to configure.
1633
+
1679
1634
  All fields below are supported in `settings.js`. If you omit a field, AegisNode uses the runtime default.
1680
1635
 
1681
1636
  Merge order used at startup:
@@ -1903,7 +1858,7 @@ security: {
1903
1858
  | --- | --- | --- |
1904
1859
  | `enabled` | `boolean` / `true` | Enable rate limiter. |
1905
1860
  | `windowMs` | `number` / `60000` | Rate limit window in milliseconds. |
1906
- | `maxRequests` | `number` / `120` | Max requests per window per key. |
1861
+ | `maxRequests` | `number` / `300` | Max requests per window per key. |
1907
1862
  | `message` | `string` / `'Too many requests, please try again later.'` | JSON error message text. |
1908
1863
  | `statusCode` | `number` / `429` | Response status code when limited. |
1909
1864
  | `standardHeaders` | `boolean` / `true` | Emit modern rate-limit headers. |
@@ -2191,6 +2146,8 @@ Behavior:
2191
2146
 
2192
2147
  ## Runtime Patterns And Advanced Topics
2193
2148
 
2149
+ Use this section after the basics are clear. It covers the runtime contracts that matter once you are shaping larger apps or enforcing stricter boundaries.
2150
+
2194
2151
  ### Middleware
2195
2152
 
2196
2153
  Route API supports Express-style middleware chains:
@@ -2556,7 +2513,7 @@ security: {
2556
2513
  ddos: {
2557
2514
  enabled: true,
2558
2515
  windowMs: 60000,
2559
- maxRequests: 120,
2516
+ maxRequests: 300,
2560
2517
  message: 'Too many requests, please try again later.',
2561
2518
  statusCode: 429,
2562
2519
  standardHeaders: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aegisnode",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "A view-first Node.js framework for modular web apps and JSON APIs with CLI scaffolding, runtime injection, auth, uploads, i18n, mail, and WebSocket support.",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -44,18 +44,18 @@
44
44
  },
45
45
  "license": "MIT",
46
46
  "dependencies": {
47
- "ejs": "^3.1.10",
48
- "express": "^4.21.2",
49
- "express-rate-limit": "^8.3.1",
47
+ "ejs": "^5.0.2",
48
+ "express": "^5.2.1",
49
+ "express-rate-limit": "^8.5.2",
50
50
  "helmet": "^8.1.0",
51
51
  "jlive": "^0.1.2",
52
- "jsonwebtoken": "^9.0.2",
53
- "mongoose": "^8.12.1",
54
- "multer": "^2.1.0",
55
- "nodemailer": "^8.0.2",
56
- "querymesh": "^0.0.7",
57
- "socket.io": "^4.8.1",
52
+ "jsonwebtoken": "^9.0.3",
53
+ "mongoose": "^9.6.2",
54
+ "multer": "^2.1.1",
55
+ "nodemailer": "^8.0.7",
56
+ "querymesh": "^0.1.0",
57
+ "socket.io": "^4.8.3",
58
58
  "swagger-ui-express": "^5.0.1",
59
- "tsx": "^4.21.0"
59
+ "tsx": "^4.22.0"
60
60
  }
61
61
  }
@@ -138,13 +138,27 @@ async function main() {
138
138
  const proxySandboxRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'aegisnode-proxy-'));
139
139
  const typescriptSandboxRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'aegisnode-ts-'));
140
140
 
141
- await startProject({ projectName, cwd: sandboxRoot });
141
+ await fs.mkdir(projectRoot, { recursive: true });
142
+ await startProject({ projectName, cwd: projectRoot });
142
143
  const generatedProjectEnv = await fs.readFile(path.join(projectRoot, '.env'), 'utf8');
143
144
  assert.match(generatedProjectEnv, /^APP_SECRET=.{16,}$/m);
144
145
  const generatedAppSecret = generatedProjectEnv.match(/^APP_SECRET=(.+)$/m)?.[1]?.trim();
145
146
  assert.ok(generatedAppSecret);
146
147
  const generatedSettings = await fs.readFile(path.join(projectRoot, 'settings.js'), 'utf8');
148
+ assert.ok(generatedSettings.includes("staticDir: 'public'"));
149
+ assert.ok(generatedSettings.includes("templates: {\n enabled: true,\n engine: 'ejs',\n dir: 'templates',\n base: 'base',\n }"));
147
150
  assert.ok(generatedSettings.includes(`appSecret: process.env.APP_SECRET || ${JSON.stringify(generatedAppSecret)}`));
151
+ const generatedConfig = await loadProjectConfig(projectRoot);
152
+ assert.equal(generatedConfig.security.ddos.maxRequests, 300);
153
+ await fs.access(path.join(projectRoot, 'public'));
154
+ await fs.access(path.join(projectRoot, 'templates'));
155
+ const nonEmptyProjectRoot = path.join(sandboxRoot, 'non-empty-project');
156
+ await fs.mkdir(nonEmptyProjectRoot, { recursive: true });
157
+ await fs.writeFile(path.join(nonEmptyProjectRoot, 'keep.txt'), 'occupied', 'utf8');
158
+ await assert.rejects(
159
+ () => startProject({ projectName: 'busy', cwd: nonEmptyProjectRoot }),
160
+ /Current directory is not empty/,
161
+ );
148
162
  await assert.rejects(
149
163
  () => runProject({
150
164
  rootDir: projectRoot,
@@ -158,11 +172,14 @@ async function main() {
158
172
 
159
173
  const tsProjectName = 'forumts';
160
174
  const tsProjectRoot = path.join(typescriptSandboxRoot, tsProjectName);
161
- await startProject({ projectName: tsProjectName, cwd: typescriptSandboxRoot, typescript: true });
175
+ await fs.mkdir(tsProjectRoot, { recursive: true });
176
+ await startProject({ projectName: tsProjectName, cwd: tsProjectRoot, typescript: true });
162
177
  await fs.access(path.join(tsProjectRoot, 'app.ts'));
163
178
  await fs.access(path.join(tsProjectRoot, 'settings.ts'));
164
179
  await fs.access(path.join(tsProjectRoot, 'routes.ts'));
165
180
  await fs.access(path.join(tsProjectRoot, 'tsconfig.json'));
181
+ await fs.access(path.join(tsProjectRoot, 'public'));
182
+ await fs.access(path.join(tsProjectRoot, 'templates'));
166
183
  const tsPackageJson = JSON.parse(await fs.readFile(path.join(tsProjectRoot, 'package.json'), 'utf8'));
167
184
  assert.equal(tsPackageJson.scripts.test, 'node --import tsx/esm --test');
168
185
  assert.equal(tsPackageJson.scripts.typecheck, 'tsc --noEmit');
@@ -218,7 +235,8 @@ async function main() {
218
235
 
219
236
  const envProjectName = 'envdemo';
220
237
  const envProjectRoot = path.join(envSandboxRoot, envProjectName);
221
- await startProject({ projectName: envProjectName, cwd: envSandboxRoot });
238
+ await fs.mkdir(envProjectRoot, { recursive: true });
239
+ await startProject({ projectName: envProjectName, cwd: envProjectRoot });
222
240
  await fs.writeFile(
223
241
  path.join(envProjectRoot, 'settings.js'),
224
242
  `export default {
@@ -271,7 +289,8 @@ async function main() {
271
289
 
272
290
  const dotenvProjectName = 'dotenvdemo';
273
291
  const dotenvProjectRoot = path.join(dotenvSandboxRoot, dotenvProjectName);
274
- await startProject({ projectName: dotenvProjectName, cwd: dotenvSandboxRoot });
292
+ await fs.mkdir(dotenvProjectRoot, { recursive: true });
293
+ await startProject({ projectName: dotenvProjectName, cwd: dotenvProjectRoot });
275
294
  await fs.writeFile(
276
295
  path.join(dotenvProjectRoot, '.env'),
277
296
  `AEGIS_TEST_HOST=127.0.0.1
@@ -306,7 +325,8 @@ AEGIS_TEST_APP_SECRET=test-dotenv-secret
306
325
 
307
326
  const httpsProjectName = 'httpsdemo';
308
327
  const httpsProjectRoot = path.join(httpsSandboxRoot, httpsProjectName);
309
- await startProject({ projectName: httpsProjectName, cwd: httpsSandboxRoot });
328
+ await fs.mkdir(httpsProjectRoot, { recursive: true });
329
+ await startProject({ projectName: httpsProjectName, cwd: httpsProjectRoot });
310
330
  await fs.mkdir(path.join(httpsProjectRoot, 'certs'), { recursive: true });
311
331
  await fs.writeFile(
312
332
  path.join(httpsProjectRoot, 'certs', 'localhost-key.pem'),
@@ -389,7 +409,8 @@ dkcqnJD4SGWVeG+KhA==
389
409
 
390
410
  const proxyProjectName = 'proxydemo';
391
411
  const proxyProjectRoot = path.join(proxySandboxRoot, proxyProjectName);
392
- await startProject({ projectName: proxyProjectName, cwd: proxySandboxRoot });
412
+ await fs.mkdir(proxyProjectRoot, { recursive: true });
413
+ await startProject({ projectName: proxyProjectName, cwd: proxyProjectRoot });
393
414
  await fs.writeFile(
394
415
  path.join(proxyProjectRoot, 'routes.js'),
395
416
  `export default {\n register(route) {\n route.get('/secure-check', (req, res) => {\n res.json({ secure: req.secure, protocol: req.protocol });\n });\n },\n};\n`,
@@ -35,7 +35,7 @@ async function assertCanCreateProject(projectDir) {
35
35
 
36
36
  const empty = await isDirectoryEmpty(projectDir);
37
37
  if (!empty) {
38
- throw new Error(`Directory already exists and is not empty: ${projectDir}`);
38
+ throw new Error(`Current directory is not empty: ${projectDir}. Create and enter an empty project directory before running "aegisnode startproject".`);
39
39
  }
40
40
  }
41
41
 
@@ -47,6 +47,8 @@ async function createBaseProjectFiles(projectRoot, projectName, { typescript = f
47
47
  await ensureDir(projectRoot);
48
48
  await Promise.all([
49
49
  ensureDir(path.join(projectRoot, 'apps')),
50
+ ensureDir(path.join(projectRoot, 'public')),
51
+ ensureDir(path.join(projectRoot, 'templates')),
50
52
  ]);
51
53
 
52
54
  await writeFile(path.join(projectRoot, withSourceExtension('app', sourceExtension)), renderProjectAppJs());
@@ -66,13 +68,12 @@ async function createBaseProjectFiles(projectRoot, projectName, { typescript = f
66
68
  export async function startProject({ projectName, cwd, typescript = false }) {
67
69
  ensureValidName(projectName, 'project');
68
70
 
69
- const projectRoot = path.resolve(cwd, projectName);
71
+ const projectRoot = path.resolve(cwd);
70
72
  await assertCanCreateProject(projectRoot);
71
73
  await createBaseProjectFiles(projectRoot, projectName, { typescript });
72
74
 
73
- console.log(`AegisNode project created at ${projectRoot}`);
75
+ console.log(`AegisNode project created in ${projectRoot}`);
74
76
  console.log('Next steps:');
75
- console.log(` cd ${projectName}`);
76
77
  console.log(' npm install');
77
78
  console.log(' aegisnode runserver');
78
79
  }
package/src/cli/index.js CHANGED
@@ -21,18 +21,18 @@ Usage:
21
21
  aegisnode updatedeps [--project <path>]
22
22
 
23
23
  Examples:
24
+ mkdir blog && cd blog
24
25
  aegisnode startproject blog
25
26
  aegisnode startproject blog --typescript
26
- cd blog
27
27
  npm install
28
28
  aegisnode runserver
29
29
  aegisnode createapp users
30
30
  aegisnode fix --app users
31
31
  aegisnode generate view user --app users
32
32
  aegisnode generate validator user --app users
33
- aegisnode generateloader --project blog
34
- aegisnode doctor --app users --project blog
35
- aegisnode updatedeps --project blog
33
+ aegisnode generateloader
34
+ aegisnode doctor --app users
35
+ aegisnode updatedeps
36
36
  `);
37
37
  }
38
38
 
@@ -109,6 +109,13 @@ export function renderProjectSettings(projectName, apps, appSecret = '') {
109
109
  host: process.env.HOST || '0.0.0.0',
110
110
  port: process.env.PORT ? Number(process.env.PORT) : 3000,
111
111
  trustProxy: false,
112
+ staticDir: 'public',
113
+ templates: {
114
+ enabled: true,
115
+ engine: 'ejs',
116
+ dir: 'templates',
117
+ base: 'base',
118
+ },
112
119
  security: {
113
120
  // Loaded from .env by default. Scaffold also embeds the generated secret as a fallback.
114
121
  // Replace or rotate APP_SECRET in production.
@@ -130,7 +137,7 @@ export function renderProjectSettings(projectName, apps, appSecret = '') {
130
137
  },
131
138
 
132
139
  // Optional sections you can add manually when needed:
133
- // maintenance, https, templates, i18n, helpers, staticDir, websocket, uploads, auth, api, mail, swagger,
140
+ // maintenance, https, i18n, helpers, websocket, uploads, auth, api, mail, swagger,
134
141
  // architecture, loaders, environments, security.headers/ddos/csrf
135
142
 
136
143
  apps: [
@@ -287,7 +287,7 @@ export function defaultConfig(rootDir) {
287
287
  ddos: {
288
288
  enabled: true,
289
289
  windowMs: 60000,
290
- maxRequests: 120,
290
+ maxRequests: 300,
291
291
  message: 'Too many requests, please try again later.',
292
292
  statusCode: 429,
293
293
  standardHeaders: true,