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 +187 -230
- package/package.json +11 -11
- package/scripts/smoke-test.js +27 -6
- package/src/cli/commands/startproject.js +5 -4
- package/src/cli/index.js +4 -4
- package/src/cli/utils/scaffolds.js +8 -1
- package/src/runtime/config.js +1 -1
package/README.md
CHANGED
|
@@ -2,77 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
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
|
|
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
|
|
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
|
-
|
|
75
|
-
|
|
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
|
-
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
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
|
|
55
|
+
npm install
|
|
56
|
+
aegisnode runserver
|
|
187
57
|
```
|
|
188
58
|
|
|
189
|
-
|
|
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
|
|
64
|
+
aegisnode createapp users
|
|
65
|
+
aegisnode generate view profile --app users
|
|
66
|
+
aegisnode generate route profile --app users
|
|
193
67
|
```
|
|
194
68
|
|
|
195
|
-
`
|
|
196
|
-
|
|
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
|
-
|
|
72
|
+
### Startup Mode Rules
|
|
204
73
|
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
98
|
+
Run project tests with:
|
|
224
99
|
|
|
225
100
|
```bash
|
|
226
|
-
|
|
101
|
+
npm test
|
|
227
102
|
```
|
|
228
103
|
|
|
229
|
-
|
|
104
|
+
### JavaScript vs TypeScript Projects
|
|
230
105
|
|
|
231
|
-
|
|
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
|
-
|
|
234
|
-
|
|
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
|
-
`
|
|
238
|
-
|
|
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
|
-
|
|
118
|
+
## Core Concepts And App Structure
|
|
242
119
|
|
|
243
|
-
|
|
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
|
-
|
|
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
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|
-
|
|
270
|
-
- `
|
|
271
|
-
-
|
|
272
|
-
- `
|
|
273
|
-
- `
|
|
274
|
-
- `
|
|
275
|
-
- `
|
|
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
|
-
|
|
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
|
-
|
|
153
|
+
### Generated Settings Shape
|
|
280
154
|
|
|
281
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
-
|
|
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:
|
|
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` / `
|
|
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:
|
|
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.
|
|
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": "^
|
|
48
|
-
"express": "^
|
|
49
|
-
"express-rate-limit": "^8.
|
|
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.
|
|
53
|
-
"mongoose": "^
|
|
54
|
-
"multer": "^2.1.
|
|
55
|
-
"nodemailer": "^8.0.
|
|
56
|
-
"querymesh": "^0.0
|
|
57
|
-
"socket.io": "^4.8.
|
|
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.
|
|
59
|
+
"tsx": "^4.22.0"
|
|
60
60
|
}
|
|
61
61
|
}
|
package/scripts/smoke-test.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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(`
|
|
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
|
|
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
|
|
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
|
|
34
|
-
aegisnode doctor --app users
|
|
35
|
-
aegisnode updatedeps
|
|
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,
|
|
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: [
|
package/src/runtime/config.js
CHANGED