millas 0.2.12-beta → 0.2.12-beta-2
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/package.json +3 -16
- package/src/admin/ActivityLog.js +153 -52
- package/src/admin/Admin.js +400 -167
- package/src/admin/AdminAuth.js +213 -98
- package/src/admin/FormGenerator.js +372 -0
- package/src/admin/HookRegistry.js +256 -0
- package/src/admin/QueryEngine.js +263 -0
- package/src/admin/ViewContext.js +309 -0
- package/src/admin/WidgetRegistry.js +406 -0
- package/src/admin/index.js +17 -0
- package/src/admin/resources/AdminResource.js +383 -97
- package/src/admin/static/admin.css +1341 -0
- package/src/admin/static/date-picker.css +157 -0
- package/src/admin/static/date-picker.js +316 -0
- package/src/admin/static/json-editor.css +649 -0
- package/src/admin/static/json-editor.js +1429 -0
- package/src/admin/static/ui.js +1044 -0
- package/src/admin/views/layouts/base.njk +65 -1013
- package/src/admin/views/pages/detail.njk +40 -16
- package/src/admin/views/pages/form.njk +47 -599
- package/src/admin/views/pages/list.njk +145 -62
- package/src/admin/views/partials/form-field.njk +53 -0
- package/src/admin/views/partials/form-footer.njk +28 -0
- package/src/admin/views/partials/form-readonly.njk +114 -0
- package/src/admin/views/partials/form-scripts.njk +476 -0
- package/src/admin/views/partials/form-widget.njk +296 -0
- package/src/admin/views/partials/json-dialog.njk +80 -0
- package/src/admin/views/partials/json-editor.njk +37 -0
- package/src/admin.zip +0 -0
- package/src/auth/Auth.js +31 -10
- package/src/auth/AuthController.js +3 -1
- package/src/auth/AuthUser.js +119 -0
- package/src/cli.js +4 -2
- package/src/commands/createsuperuser.js +254 -0
- package/src/commands/lang.js +589 -0
- package/src/commands/migrate.js +154 -81
- package/src/commands/serve.js +82 -110
- package/src/container/AppInitializer.js +215 -0
- package/src/container/Application.js +278 -253
- package/src/container/HttpServer.js +156 -0
- package/src/container/MillasApp.js +29 -279
- package/src/container/MillasConfig.js +192 -0
- package/src/core/admin.js +5 -0
- package/src/core/auth.js +9 -0
- package/src/core/db.js +9 -0
- package/src/core/foundation.js +59 -0
- package/src/core/http.js +11 -0
- package/src/core/lang.js +1 -0
- package/src/core/mail.js +6 -0
- package/src/core/queue.js +7 -0
- package/src/core/validation.js +29 -0
- package/src/facades/Admin.js +1 -1
- package/src/facades/Auth.js +22 -39
- package/src/facades/Cache.js +21 -10
- package/src/facades/Database.js +1 -1
- package/src/facades/Events.js +18 -17
- package/src/facades/Facade.js +197 -0
- package/src/facades/Http.js +42 -45
- package/src/facades/Log.js +25 -49
- package/src/facades/Mail.js +27 -32
- package/src/facades/Queue.js +22 -15
- package/src/facades/Storage.js +18 -10
- package/src/facades/Url.js +53 -0
- package/src/http/HttpClient.js +673 -0
- package/src/http/ResponseDispatcher.js +18 -111
- package/src/http/UrlGenerator.js +375 -0
- package/src/http/WelcomePage.js +273 -0
- package/src/http/adapters/ExpressAdapter.js +315 -0
- package/src/http/adapters/HttpAdapter.js +168 -0
- package/src/http/adapters/index.js +9 -0
- package/src/i18n/I18nServiceProvider.js +91 -0
- package/src/i18n/Translator.js +635 -0
- package/src/i18n/defaults.js +122 -0
- package/src/i18n/index.js +164 -0
- package/src/i18n/locales/en.js +55 -0
- package/src/i18n/locales/sw.js +48 -0
- package/src/index.js +5 -144
- package/src/logger/formatters/PrettyFormatter.js +103 -57
- package/src/logger/internal.js +2 -2
- package/src/logger/patchConsole.js +91 -81
- package/src/middleware/MiddlewareRegistry.js +62 -82
- package/src/migrations/system/0001_users.js +21 -0
- package/src/migrations/system/0002_admin_log.js +25 -0
- package/src/migrations/system/0003_sessions.js +23 -0
- package/src/orm/fields/index.js +210 -188
- package/src/orm/migration/DefaultValueParser.js +325 -0
- package/src/orm/migration/InteractiveResolver.js +191 -0
- package/src/orm/migration/Makemigrations.js +312 -0
- package/src/orm/migration/MigrationGraph.js +227 -0
- package/src/orm/migration/MigrationRunner.js +202 -108
- package/src/orm/migration/MigrationWriter.js +463 -0
- package/src/orm/migration/ModelInspector.js +412 -344
- package/src/orm/migration/ModelScanner.js +225 -0
- package/src/orm/migration/ProjectState.js +213 -0
- package/src/orm/migration/RenameDetector.js +175 -0
- package/src/orm/migration/SchemaBuilder.js +8 -81
- package/src/orm/migration/operations/base.js +57 -0
- package/src/orm/migration/operations/column.js +191 -0
- package/src/orm/migration/operations/fields.js +252 -0
- package/src/orm/migration/operations/index.js +55 -0
- package/src/orm/migration/operations/models.js +152 -0
- package/src/orm/migration/operations/registry.js +131 -0
- package/src/orm/migration/operations/special.js +51 -0
- package/src/orm/migration/utils.js +208 -0
- package/src/orm/model/Model.js +81 -13
- package/src/providers/AdminServiceProvider.js +66 -9
- package/src/providers/AuthServiceProvider.js +46 -7
- package/src/providers/CacheStorageServiceProvider.js +5 -3
- package/src/providers/DatabaseServiceProvider.js +3 -2
- package/src/providers/EventServiceProvider.js +2 -1
- package/src/providers/LogServiceProvider.js +7 -3
- package/src/providers/MailServiceProvider.js +4 -3
- package/src/providers/QueueServiceProvider.js +4 -3
- package/src/router/Router.js +119 -152
- package/src/scaffold/templates.js +83 -26
- package/src/facades/Validation.js +0 -69
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* HttpServer
|
|
5
|
+
*
|
|
6
|
+
* Owns everything that belongs to the process/server layer — the things
|
|
7
|
+
* Express puts in bin/www that have nothing to do with your app logic:
|
|
8
|
+
*
|
|
9
|
+
* - Port normalisation and validation
|
|
10
|
+
* - EADDRINUSE / EACCES error handling with clear messages
|
|
11
|
+
* - Startup log (route count, URL, Ctrl+C hint)
|
|
12
|
+
* - IPC ready signal to the hot-reload proxy (millas serve)
|
|
13
|
+
* - Graceful shutdown on SIGTERM / SIGINT
|
|
14
|
+
*
|
|
15
|
+
* The Application kernel knows nothing about any of this.
|
|
16
|
+
*
|
|
17
|
+
* ── Usage in bootstrap/server.js ────────────────────────────────────────────
|
|
18
|
+
*
|
|
19
|
+
* const app = require('./app'); // configured Millas app, no listen()
|
|
20
|
+
* const server = new HttpServer(app);
|
|
21
|
+
* server.start();
|
|
22
|
+
*
|
|
23
|
+
* ── Advanced ─────────────────────────────────────────────────────────────────
|
|
24
|
+
*
|
|
25
|
+
* new HttpServer(app, {
|
|
26
|
+
* port: 4000,
|
|
27
|
+
* host: '0.0.0.0',
|
|
28
|
+
* onStart: (port, host) => console.log(`up on ${host}:${port}`),
|
|
29
|
+
* onShutdown: () => db.close(),
|
|
30
|
+
* }).start();
|
|
31
|
+
*/
|
|
32
|
+
class HttpServer {
|
|
33
|
+
/**
|
|
34
|
+
* @param {import('./Application')} app — booted Millas Application instance
|
|
35
|
+
* @param {object} options
|
|
36
|
+
* @param {number} [options.port]
|
|
37
|
+
* @param {string} [options.host]
|
|
38
|
+
* @param {Function}[options.onStart] — (port, host) => void, called after listen
|
|
39
|
+
* @param {Function}[options.onShutdown] — async () => void, called before process.exit
|
|
40
|
+
*/
|
|
41
|
+
constructor(app, options = {}) {
|
|
42
|
+
this._app = app;
|
|
43
|
+
this._options = options;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Resolve port, boot the server, wire signals.
|
|
48
|
+
* Returns a Promise that resolves once the server is listening.
|
|
49
|
+
*/
|
|
50
|
+
async start() {
|
|
51
|
+
const port = this._resolvePort();
|
|
52
|
+
const host = this._options.host
|
|
53
|
+
|| process.env.MILLAS_HOST
|
|
54
|
+
|| process.env.HOST
|
|
55
|
+
|| 'localhost';
|
|
56
|
+
|
|
57
|
+
// Boot providers if not already done
|
|
58
|
+
if (!this._app._booted) {
|
|
59
|
+
await this._app.boot();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Mount routes + fallbacks if not already mounted
|
|
63
|
+
if (!this._app._router) {
|
|
64
|
+
this._app.mount();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Start listening — may throw on EADDRINUSE / EACCES
|
|
68
|
+
try {
|
|
69
|
+
await this._app.listen(port, host);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
this._handleListenError(err, port);
|
|
72
|
+
return; // _handleListenError always exits
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Notify the millas serve hot-reload proxy that we're ready
|
|
76
|
+
if (typeof process.send === 'function') {
|
|
77
|
+
process.send({type: 'ready', port});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// User callback
|
|
81
|
+
if (typeof this._options.onStart === 'function') {
|
|
82
|
+
this._options.onStart(port, host);
|
|
83
|
+
}
|
|
84
|
+
this._handleSignals();
|
|
85
|
+
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ── Private ────────────────────────────────────────────────────────────────
|
|
89
|
+
|
|
90
|
+
_resolvePort() {
|
|
91
|
+
const raw =
|
|
92
|
+
this._options.port ||
|
|
93
|
+
parseInt(process.env.MILLAS_INTERNAL_PORT, 10) ||
|
|
94
|
+
parseInt(process.env.APP_PORT, 10) ||
|
|
95
|
+
parseInt(process.env.PORT, 10) ||
|
|
96
|
+
3000;
|
|
97
|
+
|
|
98
|
+
return this._normalizePort(raw);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
_normalizePort(val) {
|
|
102
|
+
const n = parseInt(val, 10);
|
|
103
|
+
if (isNaN(n)) return val; // named pipe — pass through
|
|
104
|
+
if (n >= 0) return n;
|
|
105
|
+
return 3000;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
_handleListenError(err, port) {
|
|
109
|
+
const bind = typeof port === 'string' ? `pipe ${port}` : `port ${port}`;
|
|
110
|
+
|
|
111
|
+
switch (err.code) {
|
|
112
|
+
case 'EACCES':
|
|
113
|
+
console.error(
|
|
114
|
+
`✖ ${bind} requires elevated privileges.\n` +
|
|
115
|
+
` Try a port above 1024 or run with sudo.`
|
|
116
|
+
);
|
|
117
|
+
break;
|
|
118
|
+
|
|
119
|
+
case 'EADDRINUSE':
|
|
120
|
+
console.error(
|
|
121
|
+
`✖ ${bind} is already in use.\n` +
|
|
122
|
+
` Another process is listening on that port.\n` +
|
|
123
|
+
` Try: APP_PORT=3001 millas serve`
|
|
124
|
+
);
|
|
125
|
+
break;
|
|
126
|
+
|
|
127
|
+
default:
|
|
128
|
+
console.error(`✖ Listen error: ${err.message}`);
|
|
129
|
+
if (process.env.APP_DEBUG) console.error(err.stack + '\n');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
_handleSignals() {
|
|
137
|
+
const shutdown = async (signal) => {
|
|
138
|
+
process.exit(0);
|
|
139
|
+
// process.stdout.write(`\n Shutting down (${signal})…\n`);
|
|
140
|
+
//
|
|
141
|
+
// if (typeof this._options.onShutdown === 'function') {
|
|
142
|
+
// try {
|
|
143
|
+
// await this._options.onShutdown();
|
|
144
|
+
// } catch {
|
|
145
|
+
// }
|
|
146
|
+
// }
|
|
147
|
+
//
|
|
148
|
+
// await this._app.shutdown(0);
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
process.once('SIGTERM', () => shutdown("SIGTERM"));
|
|
152
|
+
process.once('SIGINT', () => shutdown('SIGINT'));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
module.exports = HttpServer;
|
|
@@ -1,299 +1,49 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const Application = require('./Application');
|
|
3
|
+
const MillasConfig = require('./MillasConfig');
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
|
-
*
|
|
8
|
-
|
|
9
|
-
*
|
|
10
|
-
* core providers, routes, and the admin panel automatically.
|
|
11
|
-
*
|
|
12
|
-
* Reduces bootstrap/app.js from ~60 lines to ~8.
|
|
13
|
-
*
|
|
14
|
-
* ── Usage ───────────────────────────────────────────────────────────────────
|
|
6
|
+
* Millas
|
|
7
|
+
|
|
8
|
+
* ── bootstrap/app.js ────────────────────────────────────────────────────────
|
|
15
9
|
*
|
|
16
|
-
* require('
|
|
17
|
-
* const { MillasApp } = require('millas');
|
|
18
|
-
* const AppServiceProvider = require('../providers/AppServiceProvider');
|
|
10
|
+
* const { Millas } = require('millas');
|
|
19
11
|
*
|
|
20
|
-
*
|
|
12
|
+
* module.exports = Millas.config()
|
|
21
13
|
* .providers([AppServiceProvider])
|
|
22
14
|
* .routes(Route => {
|
|
23
15
|
* require('../routes/web')(Route);
|
|
24
16
|
* require('../routes/api')(Route);
|
|
25
17
|
* })
|
|
26
|
-
* .withAdmin()
|
|
27
|
-
*
|
|
28
|
-
* module.exports = app.start();
|
|
29
|
-
*
|
|
30
|
-
* ── Escape hatches ──────────────────────────────────────────────────────────
|
|
31
|
-
*
|
|
32
|
-
* // Access the raw Express app
|
|
33
|
-
* app.express.use(someExpressMiddleware());
|
|
34
|
-
*
|
|
35
|
-
* // Access the DI container
|
|
36
|
-
* app.container.make('db');
|
|
37
|
-
*
|
|
38
|
-
* // Access the underlying Application kernel
|
|
39
|
-
* app.kernel
|
|
18
|
+
* .withAdmin()
|
|
19
|
+
* .create();
|
|
40
20
|
*
|
|
41
|
-
*
|
|
21
|
+
* That is everything a developer writes. The rest is handled internally.
|
|
42
22
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* mail: false,
|
|
46
|
-
* queue: false,
|
|
23
|
+
* Millas.config() → MillasConfig (chainable, collects config only)
|
|
24
|
+
* .create() → MillasInstance (sealed carrier, no callable methods)
|
|
47
25
|
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* // Add Express middleware before routes
|
|
52
|
-
* expressMiddleware: [cors(), helmet()],
|
|
53
|
-
*
|
|
54
|
-
* // Called once the app is listening
|
|
55
|
-
* onStart: (port, host) => console.log(`Running on ${host}:${port}`),
|
|
56
|
-
* })
|
|
26
|
+
* The framework's AppInitialiser receives the MillasInstance, reads its
|
|
27
|
+
* config, and boots the full application — adapter, kernel, providers,
|
|
28
|
+
* routes, admin, HTTP server — without the developer being involved.
|
|
57
29
|
*/
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
// ─── Factory ──────────────────────────────────────────────────────────────
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Create a new MillasApp instance.
|
|
64
|
-
* @param {object} [options]
|
|
65
|
-
* @param {boolean} [options.database=true] — register DatabaseServiceProvider
|
|
66
|
-
* @param {boolean} [options.cache=true] — register CacheServiceProvider
|
|
67
|
-
* @param {boolean} [options.storage=true] — register StorageServiceProvider
|
|
68
|
-
* @param {boolean} [options.mail=true] — register MailServiceProvider
|
|
69
|
-
* @param {boolean} [options.queue=true] — register QueueServiceProvider
|
|
70
|
-
* @param {boolean} [options.events=true] — register EventServiceProvider
|
|
71
|
-
* @param {boolean} [options.logging=true] — register LogServiceProvider
|
|
72
|
-
* @param {Array} [options.expressMiddleware] — Express middleware applied before routes
|
|
73
|
-
* @param {Function}[options.onStart] — callback(port, host) after listen
|
|
74
|
-
*/
|
|
75
|
-
static create(options = {}) {
|
|
76
|
-
return new MillasApp(options);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
constructor(options = {}) {
|
|
80
|
-
this._options = options;
|
|
81
|
-
this._userProviders = [];
|
|
82
|
-
this._routesCb = null;
|
|
83
|
-
this._adminOptions = null; // null = no admin; object/true = mount admin
|
|
84
|
-
this._expressApp = null;
|
|
85
|
-
this._kernel = null;
|
|
86
|
-
this._started = false;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// ─── Fluent API ───────────────────────────────────────────────────────────
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Register application service providers.
|
|
93
|
-
*
|
|
94
|
-
* .providers([AppServiceProvider, PaymentServiceProvider])
|
|
95
|
-
*/
|
|
96
|
-
providers(list = []) {
|
|
97
|
-
this._userProviders.push(...list);
|
|
98
|
-
return this;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Define application routes.
|
|
103
|
-
*
|
|
104
|
-
* .routes(Route => {
|
|
105
|
-
* require('../routes/web')(Route);
|
|
106
|
-
* require('../routes/api')(Route);
|
|
107
|
-
* })
|
|
108
|
-
*/
|
|
109
|
-
routes(callback) {
|
|
110
|
-
this._routesCb = callback;
|
|
111
|
-
return this;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Register a named middleware alias.
|
|
116
|
-
* Must be called before start().
|
|
117
|
-
*
|
|
118
|
-
* .middleware('verified', EmailVerifiedMiddleware)
|
|
119
|
-
*/
|
|
120
|
-
middleware(alias, handler) {
|
|
121
|
-
if (!this._pendingMiddleware) this._pendingMiddleware = [];
|
|
122
|
-
this._pendingMiddleware.push({ alias, handler });
|
|
123
|
-
return this;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Mount the admin panel.
|
|
128
|
-
*
|
|
129
|
-
* .withAdmin() // default /admin
|
|
130
|
-
* .withAdmin({ prefix: '/cms' }) // custom prefix
|
|
131
|
-
* .withAdmin({ prefix: '/admin', auth: { users: [...] } })
|
|
132
|
-
*/
|
|
133
|
-
withAdmin(options = {}) {
|
|
134
|
-
this._adminOptions = options;
|
|
135
|
-
return this;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// ─── Start ────────────────────────────────────────────────────────────────
|
|
139
|
-
|
|
30
|
+
const Millas = {
|
|
140
31
|
/**
|
|
141
|
-
*
|
|
142
|
-
*
|
|
32
|
+
* Start building an application config.
|
|
33
|
+
* Equivalent to Millas.config().configure(basePath).
|
|
143
34
|
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
this._expressApp = express();
|
|
150
|
-
this._expressApp.use(express.json());
|
|
151
|
-
this._expressApp.use(express.urlencoded({ extended: true }));
|
|
152
|
-
|
|
153
|
-
// Apply any extra Express middleware (e.g. helmet, cors)
|
|
154
|
-
for (const mw of (this._options.expressMiddleware || [])) {
|
|
155
|
-
this._expressApp.use(mw);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
this._kernel = new Application(this._expressApp);
|
|
159
|
-
|
|
160
|
-
// Register pending middleware aliases
|
|
161
|
-
for (const { alias, handler } of (this._pendingMiddleware || [])) {
|
|
162
|
-
this._kernel.middleware(alias, handler);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Register core providers (auto, unless disabled)
|
|
166
|
-
const coreProviders = this._buildCoreProviders();
|
|
167
|
-
this._kernel.providers([...coreProviders, ...this._userProviders]);
|
|
168
|
-
|
|
169
|
-
// Register routes
|
|
170
|
-
if (this._routesCb) {
|
|
171
|
-
this._kernel.routes(this._routesCb);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Boot asynchronously
|
|
175
|
-
const self = this;
|
|
176
|
-
const boot = (async () => {
|
|
177
|
-
await self._kernel.boot();
|
|
178
|
-
|
|
179
|
-
if (!process.env.MILLAS_ROUTE_LIST) {
|
|
180
|
-
self._kernel.mountRoutes();
|
|
181
|
-
|
|
182
|
-
// Admin panel (mounted between routes and fallbacks)
|
|
183
|
-
if (self._adminOptions !== null) {
|
|
184
|
-
const adminOpts = typeof self._adminOptions === 'object'
|
|
185
|
-
? self._adminOptions
|
|
186
|
-
: {};
|
|
187
|
-
|
|
188
|
-
try {
|
|
189
|
-
const Admin = require('../admin/Admin');
|
|
190
|
-
if (Object.keys(adminOpts).length) {
|
|
191
|
-
Admin.configure(adminOpts);
|
|
192
|
-
}
|
|
193
|
-
Admin.mount(self._expressApp);
|
|
194
|
-
} catch (err) {
|
|
195
|
-
process.stderr.write(`[millas] Admin mount failed: ${err.message}\n`);
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
self._kernel.mountFallbacks();
|
|
200
|
-
|
|
201
|
-
const onStart = self._options.onStart || null;
|
|
202
|
-
self._kernel.listen(undefined, undefined, onStart || undefined);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
self._started = true;
|
|
206
|
-
})();
|
|
207
|
-
|
|
208
|
-
// Expose the promise for testing / programmatic use
|
|
209
|
-
this._bootPromise = boot;
|
|
210
|
-
|
|
211
|
-
return this;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Await the boot promise. Useful in tests or scripts.
|
|
35
|
+
* Usage (bootstrap/app.js):
|
|
36
|
+
* module.exports = Millas.configure(__dirname)
|
|
37
|
+
* .withAdmin()
|
|
38
|
+
* .routes(Route => { ... })
|
|
39
|
+
* .create();
|
|
216
40
|
*
|
|
217
|
-
*
|
|
41
|
+
* @param {string} basePath — pass __dirname from bootstrap/app.js
|
|
42
|
+
* @returns {MillasConfig}
|
|
218
43
|
*/
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
// ─── Escape hatches ───────────────────────────────────────────────────────
|
|
225
|
-
|
|
226
|
-
/** The raw Express application instance. */
|
|
227
|
-
get express() { return this._expressApp; }
|
|
228
|
-
|
|
229
|
-
/** The underlying Application kernel (also accessible as .app for back-compat). */
|
|
230
|
-
get kernel() { return this._kernel; }
|
|
231
|
-
get app() { return this._kernel; }
|
|
232
|
-
|
|
233
|
-
/** The DI container. */
|
|
234
|
-
get container() { return this._kernel?._container; }
|
|
235
|
-
|
|
236
|
-
/** The Route instance — used by route:list command. */
|
|
237
|
-
get route() { return this._kernel?._route; }
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* The raw Express app — backward-compatible alias.
|
|
241
|
-
* Allows: const { expressApp } = require('./bootstrap/app')
|
|
242
|
-
*/
|
|
243
|
-
get expressApp() { return this._expressApp; }
|
|
244
|
-
|
|
245
|
-
// ─── Internal ─────────────────────────────────────────────────────────────
|
|
246
|
-
|
|
247
|
-
_buildCoreProviders() {
|
|
248
|
-
const opts = this._options;
|
|
249
|
-
const providers = [];
|
|
250
|
-
|
|
251
|
-
// Helper — require a provider, silently skip if not found
|
|
252
|
-
const load = (path) => {
|
|
253
|
-
try { return require(path); } catch { return null; }
|
|
254
|
-
};
|
|
255
|
-
|
|
256
|
-
// Logging — first so all other providers can use Log
|
|
257
|
-
if (opts.logging !== false) {
|
|
258
|
-
const p = load('../providers/LogServiceProvider');
|
|
259
|
-
if (p) providers.push(p);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Database
|
|
263
|
-
if (opts.database !== false) {
|
|
264
|
-
const p = load('../providers/DatabaseServiceProvider');
|
|
265
|
-
if (p) providers.push(p);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// Cache + Storage
|
|
269
|
-
if (opts.cache !== false || opts.storage !== false) {
|
|
270
|
-
const p = load('../providers/CacheStorageServiceProvider');
|
|
271
|
-
if (p) {
|
|
272
|
-
if (opts.cache !== false && p.CacheServiceProvider) providers.push(p.CacheServiceProvider);
|
|
273
|
-
if (opts.storage !== false && p.StorageServiceProvider) providers.push(p.StorageServiceProvider);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// Mail
|
|
278
|
-
if (opts.mail !== false) {
|
|
279
|
-
const p = load('../providers/MailServiceProvider');
|
|
280
|
-
if (p) providers.push(p);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// Queue
|
|
284
|
-
if (opts.queue !== false) {
|
|
285
|
-
const p = load('../providers/QueueServiceProvider');
|
|
286
|
-
if (p) providers.push(p);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// Events
|
|
290
|
-
if (opts.events !== false) {
|
|
291
|
-
const p = load('../providers/EventServiceProvider');
|
|
292
|
-
if (p) providers.push(p);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
return providers;
|
|
296
|
-
}
|
|
297
|
-
}
|
|
44
|
+
configure(basePath) {
|
|
45
|
+
return new MillasConfig().configure(basePath);
|
|
46
|
+
},
|
|
47
|
+
};
|
|
298
48
|
|
|
299
|
-
module.exports =
|
|
49
|
+
module.exports = Millas;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const AppInitializer = require("./AppInitializer");
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* MillasConfig
|
|
7
|
+
*
|
|
8
|
+
* A pure config collector — no side effects, no HTTP, no booting.
|
|
9
|
+
* Every method returns `this` for chaining.
|
|
10
|
+
* The chain ends with .create() which seals the config into a MillasInstance.
|
|
11
|
+
*
|
|
12
|
+
* ── Usage (bootstrap/app.js) ─────────────────────────────────────────────────
|
|
13
|
+
*
|
|
14
|
+
* const { Millas } = require('millas');
|
|
15
|
+
*
|
|
16
|
+
* module.exports = Millas.config()
|
|
17
|
+
* .providers([AppServiceProvider])
|
|
18
|
+
* .routes(Route => {
|
|
19
|
+
* require('../routes/web')(Route);
|
|
20
|
+
* require('../routes/api')(Route);
|
|
21
|
+
* })
|
|
22
|
+
* .withAdmin()
|
|
23
|
+
* .create();
|
|
24
|
+
*/
|
|
25
|
+
class MillasConfig {
|
|
26
|
+
constructor() {
|
|
27
|
+
this._config = {
|
|
28
|
+
// Absolute path to the project root — passed to all providers
|
|
29
|
+
// so they never need to call process.cwd() at runtime.
|
|
30
|
+
// Set via .configure(__dirname) in bootstrap/app.js.
|
|
31
|
+
basePath: null,
|
|
32
|
+
|
|
33
|
+
// Service providers
|
|
34
|
+
providers: [],
|
|
35
|
+
|
|
36
|
+
// Route registration callback: (Route) => void
|
|
37
|
+
routes: null,
|
|
38
|
+
|
|
39
|
+
// Named middleware aliases: [{ alias, handler }]
|
|
40
|
+
middleware: [],
|
|
41
|
+
|
|
42
|
+
// Core service toggles
|
|
43
|
+
logging: true,
|
|
44
|
+
database: true,
|
|
45
|
+
auth: true, // AuthServiceProvider — always on by default
|
|
46
|
+
cache: true,
|
|
47
|
+
storage: true,
|
|
48
|
+
mail: true,
|
|
49
|
+
queue: true,
|
|
50
|
+
events: true,
|
|
51
|
+
|
|
52
|
+
// Admin panel — null means disabled, {} or options object means enabled
|
|
53
|
+
admin: null,
|
|
54
|
+
|
|
55
|
+
// Raw adapter-level middleware (e.g. helmet, compression)
|
|
56
|
+
adapterMiddleware: [],
|
|
57
|
+
|
|
58
|
+
// Lifecycle callbacks
|
|
59
|
+
onStart: null,
|
|
60
|
+
onShutdown: null,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ── Chainable config methods ───────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Set the application base path. Must be the first call.
|
|
68
|
+
* Pass __dirname from bootstrap/app.js — Laravel style.
|
|
69
|
+
*
|
|
70
|
+
* Millas.configure(__dirname)
|
|
71
|
+
*
|
|
72
|
+
* All providers use this to locate config files, models, and routes
|
|
73
|
+
* without relying on process.cwd() at runtime.
|
|
74
|
+
*
|
|
75
|
+
* @param {string} basePath — absolute path to the project root
|
|
76
|
+
*/
|
|
77
|
+
configure(basePath) {
|
|
78
|
+
this._config.basePath = basePath;
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Register application service providers.
|
|
84
|
+
*
|
|
85
|
+
* .providers([AppServiceProvider, PaymentServiceProvider])
|
|
86
|
+
*/
|
|
87
|
+
providers(list = []) {
|
|
88
|
+
this._config.providers.push(...list);
|
|
89
|
+
return this;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Register application routes.
|
|
94
|
+
*
|
|
95
|
+
* .routes(Route => {
|
|
96
|
+
* require('../routes/web')(Route);
|
|
97
|
+
* require('../routes/api')(Route);
|
|
98
|
+
* })
|
|
99
|
+
*/
|
|
100
|
+
routes(callback) {
|
|
101
|
+
this._config.routes = callback;
|
|
102
|
+
return this;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Register a named middleware alias.
|
|
107
|
+
*
|
|
108
|
+
* .middleware('verified', EmailVerifiedMiddleware)
|
|
109
|
+
*/
|
|
110
|
+
middleware(alias, handler) {
|
|
111
|
+
this._config.middleware.push({alias, handler});
|
|
112
|
+
return this;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Enable the admin panel.
|
|
117
|
+
*
|
|
118
|
+
* AuthServiceProvider and AdminServiceProvider are registered
|
|
119
|
+
* automatically — no need to add them to .providers([]).
|
|
120
|
+
*
|
|
121
|
+
* .withAdmin()
|
|
122
|
+
* .withAdmin({ prefix: '/cms', title: 'My CMS' })
|
|
123
|
+
*
|
|
124
|
+
* First-time setup:
|
|
125
|
+
* millas migrate # creates users + admin_log + sessions tables
|
|
126
|
+
* millas createsuperuser # creates your first admin user
|
|
127
|
+
*/
|
|
128
|
+
withAdmin(options = {}) {
|
|
129
|
+
this._config.admin = options;
|
|
130
|
+
return this;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Disable individual core services.
|
|
135
|
+
*
|
|
136
|
+
* .disable('mail', 'queue')
|
|
137
|
+
*/
|
|
138
|
+
disable(...services) {
|
|
139
|
+
for (const svc of services) {
|
|
140
|
+
if (svc in this._config) this._config[svc] = false;
|
|
141
|
+
}
|
|
142
|
+
return this;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Apply raw adapter-level middleware (e.g. helmet, compression).
|
|
147
|
+
* These are applied before any Millas routes.
|
|
148
|
+
*
|
|
149
|
+
* .use(helmet())
|
|
150
|
+
* .use(compression())
|
|
151
|
+
*/
|
|
152
|
+
use(...fns) {
|
|
153
|
+
this._config.adapterMiddleware.push(...fns);
|
|
154
|
+
return this;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Called once the server is listening.
|
|
159
|
+
*
|
|
160
|
+
* .onStart((port, host) => console.log(`up on ${host}:${port}`))
|
|
161
|
+
*/
|
|
162
|
+
onStart(fn) {
|
|
163
|
+
this._config.onStart = fn;
|
|
164
|
+
return this;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Called before graceful shutdown.
|
|
169
|
+
*
|
|
170
|
+
* .onShutdown(async () => db.close())
|
|
171
|
+
*/
|
|
172
|
+
onShutdown(fn) {
|
|
173
|
+
this._config.onShutdown = fn;
|
|
174
|
+
return this;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ── Terminal method ────────────────────────────────────────────────────────
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
*
|
|
181
|
+
*
|
|
182
|
+
* module.exports = Millas.config()
|
|
183
|
+
* .providers([AppServiceProvider])
|
|
184
|
+
* .routes(...)
|
|
185
|
+
* .create();
|
|
186
|
+
*/
|
|
187
|
+
async create() {
|
|
188
|
+
return (new AppInitializer(Object.freeze({...this._config}))).boot();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
module.exports = MillasConfig;
|
package/src/core/auth.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const AuthUser = require("../auth/AuthUser");
|
|
2
|
+
const JwtDriver = require("../auth/JwtDriver");
|
|
3
|
+
const AuthMiddleware = require("../auth/AuthMiddleware");
|
|
4
|
+
const RoleMiddleware = require("../auth/RoleMiddleware");
|
|
5
|
+
const AuthController = require("../auth/AuthController");
|
|
6
|
+
module.exports = {
|
|
7
|
+
AuthUser, JwtDriver, AuthMiddleware, RoleMiddleware,
|
|
8
|
+
AuthController,
|
|
9
|
+
}
|