millas 0.1.0

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.
Files changed (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +137 -0
  3. package/bin/millas.js +6 -0
  4. package/package.json +56 -0
  5. package/src/admin/Admin.js +617 -0
  6. package/src/admin/index.js +13 -0
  7. package/src/admin/resources/AdminResource.js +317 -0
  8. package/src/auth/Auth.js +254 -0
  9. package/src/auth/AuthController.js +188 -0
  10. package/src/auth/AuthMiddleware.js +67 -0
  11. package/src/auth/Hasher.js +51 -0
  12. package/src/auth/JwtDriver.js +74 -0
  13. package/src/auth/RoleMiddleware.js +44 -0
  14. package/src/cache/Cache.js +231 -0
  15. package/src/cache/drivers/FileDriver.js +152 -0
  16. package/src/cache/drivers/MemoryDriver.js +158 -0
  17. package/src/cache/drivers/NullDriver.js +27 -0
  18. package/src/cache/index.js +8 -0
  19. package/src/cli.js +27 -0
  20. package/src/commands/make.js +61 -0
  21. package/src/commands/migrate.js +174 -0
  22. package/src/commands/new.js +50 -0
  23. package/src/commands/queue.js +92 -0
  24. package/src/commands/route.js +93 -0
  25. package/src/commands/serve.js +50 -0
  26. package/src/container/Application.js +177 -0
  27. package/src/container/Container.js +281 -0
  28. package/src/container/index.js +13 -0
  29. package/src/controller/Controller.js +367 -0
  30. package/src/errors/HttpError.js +29 -0
  31. package/src/events/Event.js +39 -0
  32. package/src/events/EventEmitter.js +151 -0
  33. package/src/events/Listener.js +46 -0
  34. package/src/events/index.js +15 -0
  35. package/src/index.js +93 -0
  36. package/src/mail/Mail.js +210 -0
  37. package/src/mail/MailMessage.js +196 -0
  38. package/src/mail/TemplateEngine.js +150 -0
  39. package/src/mail/drivers/LogDriver.js +36 -0
  40. package/src/mail/drivers/MailgunDriver.js +84 -0
  41. package/src/mail/drivers/SendGridDriver.js +97 -0
  42. package/src/mail/drivers/SmtpDriver.js +67 -0
  43. package/src/mail/index.js +19 -0
  44. package/src/middleware/AuthMiddleware.js +46 -0
  45. package/src/middleware/CorsMiddleware.js +59 -0
  46. package/src/middleware/LogMiddleware.js +61 -0
  47. package/src/middleware/Middleware.js +36 -0
  48. package/src/middleware/MiddlewarePipeline.js +94 -0
  49. package/src/middleware/ThrottleMiddleware.js +61 -0
  50. package/src/orm/drivers/DatabaseManager.js +135 -0
  51. package/src/orm/fields/index.js +132 -0
  52. package/src/orm/index.js +19 -0
  53. package/src/orm/migration/MigrationRunner.js +216 -0
  54. package/src/orm/migration/ModelInspector.js +338 -0
  55. package/src/orm/migration/SchemaBuilder.js +173 -0
  56. package/src/orm/model/Model.js +371 -0
  57. package/src/orm/query/QueryBuilder.js +197 -0
  58. package/src/providers/AdminServiceProvider.js +40 -0
  59. package/src/providers/AuthServiceProvider.js +53 -0
  60. package/src/providers/CacheStorageServiceProvider.js +71 -0
  61. package/src/providers/DatabaseServiceProvider.js +45 -0
  62. package/src/providers/EventServiceProvider.js +34 -0
  63. package/src/providers/MailServiceProvider.js +51 -0
  64. package/src/providers/ProviderRegistry.js +82 -0
  65. package/src/providers/QueueServiceProvider.js +52 -0
  66. package/src/providers/ServiceProvider.js +45 -0
  67. package/src/queue/Job.js +135 -0
  68. package/src/queue/Queue.js +147 -0
  69. package/src/queue/drivers/DatabaseDriver.js +194 -0
  70. package/src/queue/drivers/SyncDriver.js +72 -0
  71. package/src/queue/index.js +16 -0
  72. package/src/queue/workers/QueueWorker.js +140 -0
  73. package/src/router/MiddlewareRegistry.js +82 -0
  74. package/src/router/Route.js +255 -0
  75. package/src/router/RouteGroup.js +19 -0
  76. package/src/router/RouteRegistry.js +55 -0
  77. package/src/router/Router.js +138 -0
  78. package/src/router/index.js +15 -0
  79. package/src/scaffold/generator.js +34 -0
  80. package/src/scaffold/maker.js +272 -0
  81. package/src/scaffold/templates.js +350 -0
  82. package/src/storage/Storage.js +170 -0
  83. package/src/storage/drivers/LocalDriver.js +215 -0
  84. package/src/storage/index.js +6 -0
@@ -0,0 +1,371 @@
1
+ 'use strict';
2
+
3
+ const QueryBuilder = require('../query/QueryBuilder');
4
+ const DatabaseManager = require('../drivers/DatabaseManager');
5
+
6
+ /**
7
+ * Model
8
+ *
9
+ * Base class for all Millas ORM models.
10
+ *
11
+ * Define your schema with static fields and interact via static
12
+ * query methods — no instantiation needed for reads.
13
+ *
14
+ * Usage:
15
+ *
16
+ * const { Model, fields } = require('millas/src/orm');
17
+ *
18
+ * class User extends Model {
19
+ * static table = 'users';
20
+ * static fields = {
21
+ * id: fields.id(),
22
+ * name: fields.string(),
23
+ * email: fields.string({ unique: true }),
24
+ * active: fields.boolean({ default: true }),
25
+ * created_at: fields.timestamp(),
26
+ * updated_at: fields.timestamp(),
27
+ * };
28
+ * }
29
+ *
30
+ * // CRUD
31
+ * const user = await User.create({ name: 'Alice', email: 'a@b.com' });
32
+ * const users = await User.all();
33
+ * const found = await User.find(1);
34
+ * const alice = await User.findBy('email', 'a@b.com');
35
+ * await user.update({ name: 'Alice Smith' });
36
+ * await user.delete();
37
+ *
38
+ * // Query builder
39
+ * const active = await User.where('active', true).orderBy('name').get();
40
+ * const admins = await User.where('role', 'admin').limit(10).get();
41
+ * const page = await User.where('active', true).paginate(1, 15);
42
+ */
43
+ class Model {
44
+ // ─── Static schema config ─────────────────────────────────────────────────
45
+
46
+ /** @type {string} Table name — defaults to pluralised lowercase class name */
47
+ static get table() {
48
+ return this._table || (this._table = this._defaultTable());
49
+ }
50
+ static set table(v) { this._table = v; }
51
+
52
+ /** @type {string} Primary key column name */
53
+ static primaryKey = 'id';
54
+
55
+ /** @type {boolean} Whether to auto-manage created_at / updated_at */
56
+ static timestamps = true;
57
+
58
+ /** @type {object} Field definitions */
59
+ static fields = {};
60
+
61
+ /** @type {string|null} Named DB connection to use */
62
+ static connection = null;
63
+
64
+ // ─── Static CRUD methods ──────────────────────────────────────────────────
65
+
66
+ /**
67
+ * Return all rows.
68
+ */
69
+ static async all() {
70
+ const rows = await this._db();
71
+ return rows.map(r => this._hydrate(r));
72
+ }
73
+
74
+ /**
75
+ * Find by primary key. Returns null if not found.
76
+ */
77
+ static async find(id) {
78
+ const row = await this._db().where(this.primaryKey, id).first();
79
+ return row ? this._hydrate(row) : null;
80
+ }
81
+
82
+ /**
83
+ * Find by primary key. Throws 404 HttpError if not found.
84
+ */
85
+ static async findOrFail(id) {
86
+ const result = await this.find(id);
87
+ if (!result) {
88
+ const HttpError = require('../../errors/HttpError');
89
+ throw new HttpError(404, `${this.name} #${id} not found`);
90
+ }
91
+ return result;
92
+ }
93
+
94
+ /**
95
+ * Find the first row matching column = value.
96
+ */
97
+ static async findBy(column, value) {
98
+ const row = await this._db().where(column, value).first();
99
+ return row ? this._hydrate(row) : null;
100
+ }
101
+
102
+ /**
103
+ * Find the first row matching column = value or throw 404.
104
+ */
105
+ static async findByOrFail(column, value) {
106
+ const result = await this.findBy(column, value);
107
+ if (!result) {
108
+ const HttpError = require('../../errors/HttpError');
109
+ throw new HttpError(404, `${this.name} not found`);
110
+ }
111
+ return result;
112
+ }
113
+
114
+ /**
115
+ * Create a new row and return the model instance.
116
+ */
117
+ static async create(data) {
118
+ const now = new Date().toISOString();
119
+ const payload = {
120
+ ...this._applyDefaults(data),
121
+ ...(this.timestamps ? { created_at: now, updated_at: now } : {}),
122
+ };
123
+
124
+ const [id] = await this._db().insert(payload);
125
+ return this.find(id);
126
+ }
127
+
128
+ /**
129
+ * Find by column or create if not found.
130
+ */
131
+ static async firstOrCreate(search, extra = {}) {
132
+ const existing = await this.findBy(Object.keys(search)[0], Object.values(search)[0]);
133
+ if (existing) return existing;
134
+ return this.create({ ...search, ...extra });
135
+ }
136
+
137
+ /**
138
+ * Update or create based on search criteria.
139
+ */
140
+ static async updateOrCreate(search, data) {
141
+ const existing = await this.where(search).first();
142
+ if (existing) {
143
+ await existing.update(data);
144
+ return existing;
145
+ }
146
+ return this.create({ ...search, ...data });
147
+ }
148
+
149
+ /**
150
+ * Count rows.
151
+ */
152
+ static async count(column = '*') {
153
+ const result = await this._db().count(`${column} as count`);
154
+ const row = Array.isArray(result) ? result[0] : result;
155
+ return Number(row?.count ?? 0);
156
+ }
157
+
158
+ /**
159
+ * Check whether any row matches the optional conditions.
160
+ */
161
+ static async exists(conditions = {}) {
162
+ let q = this._db();
163
+ if (Object.keys(conditions).length) q = q.where(conditions);
164
+ const result = await q.count('* as count');
165
+ const row = Array.isArray(result) ? result[0] : result;
166
+ return Number(row?.count ?? 0) > 0;
167
+ }
168
+
169
+ /**
170
+ * Bulk insert — returns number of rows inserted.
171
+ */
172
+ static async insert(rows) {
173
+ const now = new Date().toISOString();
174
+ const payload = rows.map(r => ({
175
+ ...this._applyDefaults(r),
176
+ ...(this.timestamps ? { created_at: now, updated_at: now } : {}),
177
+ }));
178
+ return this._db().insert(payload);
179
+ }
180
+
181
+ /**
182
+ * Delete rows by primary key.
183
+ */
184
+ static async destroy(...ids) {
185
+ return this._db().whereIn(this.primaryKey, ids.flat()).delete();
186
+ }
187
+
188
+ /**
189
+ * Truncate the table.
190
+ */
191
+ static async truncate() {
192
+ return this._db().truncate();
193
+ }
194
+
195
+ // ─── Query Builder entry points ───────────────────────────────────────────
196
+
197
+ static where(column, operatorOrValue, value) {
198
+ const qb = new QueryBuilder(this._db(), this);
199
+ return qb.where(column, operatorOrValue, value);
200
+ }
201
+
202
+ static whereIn(column, values) {
203
+ return new QueryBuilder(this._db(), this).whereIn(column, values);
204
+ }
205
+
206
+ static whereNull(column) {
207
+ return new QueryBuilder(this._db(), this).whereNull(column);
208
+ }
209
+
210
+ static whereNotNull(column) {
211
+ return new QueryBuilder(this._db(), this).whereNotNull(column);
212
+ }
213
+
214
+ static orderBy(column, dir = 'asc') {
215
+ return new QueryBuilder(this._db(), this).orderBy(column, dir);
216
+ }
217
+
218
+ static latest(column = 'created_at') {
219
+ return new QueryBuilder(this._db(), this).latest(column);
220
+ }
221
+
222
+ static oldest(column = 'created_at') {
223
+ return new QueryBuilder(this._db(), this).oldest(column);
224
+ }
225
+
226
+ static limit(n) {
227
+ return new QueryBuilder(this._db(), this).limit(n);
228
+ }
229
+
230
+ static select(...cols) {
231
+ return new QueryBuilder(this._db(), this).select(...cols);
232
+ }
233
+
234
+ /**
235
+ * Raw query builder — escape hatch.
236
+ */
237
+ static query() {
238
+ return new QueryBuilder(this._db(), this);
239
+ }
240
+
241
+ /**
242
+ * Paginate static shorthand.
243
+ */
244
+ static async paginate(page = 1, perPage = 15) {
245
+ const offset = (page - 1) * perPage;
246
+ const total = await this.count();
247
+ const rows = await this._db().limit(perPage).offset(offset);
248
+ return {
249
+ data: rows.map(r => this._hydrate(r)),
250
+ total,
251
+ page: Number(page),
252
+ perPage,
253
+ lastPage: Math.ceil(total / perPage),
254
+ };
255
+ }
256
+
257
+ // ─── Instance methods ─────────────────────────────────────────────────────
258
+
259
+ constructor(attributes = {}) {
260
+ Object.assign(this, attributes);
261
+ this._original = { ...attributes };
262
+ this._isDirty = false;
263
+ }
264
+
265
+ /**
266
+ * Persist changes to this instance back to the database.
267
+ */
268
+ async update(data = {}) {
269
+ const now = new Date().toISOString();
270
+ const payload = {
271
+ ...data,
272
+ ...(this.constructor.timestamps ? { updated_at: now } : {}),
273
+ };
274
+
275
+ await this.constructor._db()
276
+ .where(this.constructor.primaryKey, this[this.constructor.primaryKey])
277
+ .update(payload);
278
+
279
+ Object.assign(this, payload);
280
+ return this;
281
+ }
282
+
283
+ /**
284
+ * Save any changes made directly to this instance's properties.
285
+ */
286
+ async save() {
287
+ const dirty = this._getDirty();
288
+ if (!Object.keys(dirty).length) return this;
289
+ return this.update(dirty);
290
+ }
291
+
292
+ /**
293
+ * Delete this row from the database.
294
+ */
295
+ async delete() {
296
+ await this.constructor._db()
297
+ .where(this.constructor.primaryKey, this[this.constructor.primaryKey])
298
+ .delete();
299
+ return this;
300
+ }
301
+
302
+ /**
303
+ * Reload this instance's attributes from the database.
304
+ */
305
+ async refresh() {
306
+ const fresh = await this.constructor.find(this[this.constructor.primaryKey]);
307
+ if (fresh) Object.assign(this, fresh);
308
+ return this;
309
+ }
310
+
311
+ /**
312
+ * Return a plain object representation.
313
+ */
314
+ toJSON() {
315
+ const obj = {};
316
+ for (const key of Object.keys(this)) {
317
+ if (!key.startsWith('_')) obj[key] = this[key];
318
+ }
319
+ return obj;
320
+ }
321
+
322
+ /**
323
+ * Check whether this instance has been persisted.
324
+ */
325
+ get isNew() {
326
+ return !this[this.constructor.primaryKey];
327
+ }
328
+
329
+ // ─── Internal ─────────────────────────────────────────────────────────────
330
+
331
+ static _db() {
332
+ const db = DatabaseManager.connection(this.connection || null);
333
+ return db(this.table);
334
+ }
335
+
336
+ static _hydrate(row) {
337
+ return new this(row);
338
+ }
339
+
340
+ static _applyDefaults(data) {
341
+ const result = { ...data };
342
+ for (const [key, field] of Object.entries(this.fields)) {
343
+ if (!(key in result) && field.default !== undefined) {
344
+ result[key] = typeof field.default === 'function'
345
+ ? field.default()
346
+ : field.default;
347
+ }
348
+ }
349
+ return result;
350
+ }
351
+
352
+ static _defaultTable() {
353
+ const name = this.name.toLowerCase();
354
+ if (name.endsWith('y') && !['ay','ey','iy','oy','uy'].some(s => name.endsWith(s)))
355
+ return name.slice(0, -1) + 'ies';
356
+ if (/(?:s|sh|ch|x|z)$/.test(name)) return name + 'es';
357
+ return name + 's';
358
+ }
359
+
360
+ _getDirty() {
361
+ const dirty = {};
362
+ for (const key of Object.keys(this)) {
363
+ if (!key.startsWith('_') && this[key] !== this._original[key]) {
364
+ dirty[key] = this[key];
365
+ }
366
+ }
367
+ return dirty;
368
+ }
369
+ }
370
+
371
+ module.exports = Model;
@@ -0,0 +1,197 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * QueryBuilder
5
+ *
6
+ * Fluent query builder for Millas ORM.
7
+ * Wraps a knex query builder instance and adds result casting.
8
+ *
9
+ * Never instantiated directly — accessed via Model methods:
10
+ * User.where('active', true).orderBy('name').limit(10).get()
11
+ */
12
+ class QueryBuilder {
13
+ constructor(knexQuery, ModelClass) {
14
+ this._query = knexQuery;
15
+ this._model = ModelClass;
16
+ }
17
+
18
+ // ─── WHERE ────────────────────────────────────────────────────────────────
19
+
20
+ where(column, operatorOrValue, value) {
21
+ if (typeof column === 'object') {
22
+ this._query = this._query.where(column);
23
+ } else if (value !== undefined) {
24
+ this._query = this._query.where(column, operatorOrValue, value);
25
+ } else {
26
+ this._query = this._query.where(column, operatorOrValue);
27
+ }
28
+ return this;
29
+ }
30
+
31
+ orWhere(column, operatorOrValue, value) {
32
+ if (value !== undefined) {
33
+ this._query = this._query.orWhere(column, operatorOrValue, value);
34
+ } else {
35
+ this._query = this._query.orWhere(column, operatorOrValue);
36
+ }
37
+ return this;
38
+ }
39
+
40
+ whereNull(column) { this._query = this._query.whereNull(column); return this; }
41
+ whereNotNull(column) { this._query = this._query.whereNotNull(column); return this; }
42
+
43
+ whereIn(column, values) {
44
+ this._query = this._query.whereIn(column, values);
45
+ return this;
46
+ }
47
+
48
+ whereNotIn(column, values) {
49
+ this._query = this._query.whereNotIn(column, values);
50
+ return this;
51
+ }
52
+
53
+ whereBetween(column, [min, max]) {
54
+ this._query = this._query.whereBetween(column, [min, max]);
55
+ return this;
56
+ }
57
+
58
+ whereLike(column, pattern) {
59
+ this._query = this._query.whereLike(column, pattern);
60
+ return this;
61
+ }
62
+
63
+ // ─── ORDER / LIMIT / OFFSET ───────────────────────────────────────────────
64
+
65
+ orderBy(column, direction = 'asc') {
66
+ this._query = this._query.orderBy(column, direction);
67
+ return this;
68
+ }
69
+
70
+ latest(column = 'created_at') {
71
+ return this.orderBy(column, 'desc');
72
+ }
73
+
74
+ oldest(column = 'created_at') {
75
+ return this.orderBy(column, 'asc');
76
+ }
77
+
78
+ limit(n) { this._query = this._query.limit(n); return this; }
79
+ offset(n) { this._query = this._query.offset(n); return this; }
80
+ skip(n) { return this.offset(n); }
81
+ take(n) { return this.limit(n); }
82
+
83
+ // ─── SELECT ───────────────────────────────────────────────────────────────
84
+
85
+ select(...columns) {
86
+ this._query = this._query.select(...columns);
87
+ return this;
88
+ }
89
+
90
+ // ─── EXECUTION ────────────────────────────────────────────────────────────
91
+
92
+ /**
93
+ * Fetch all matching rows — returns array of model instances.
94
+ */
95
+ async get() {
96
+ const rows = await this._query;
97
+ return rows.map(r => this._model._hydrate(r));
98
+ }
99
+
100
+ /**
101
+ * Fetch the first matching row — returns a model instance or null.
102
+ */
103
+ async first() {
104
+ const row = await this._query.first();
105
+ return row ? this._model._hydrate(row) : null;
106
+ }
107
+
108
+ /**
109
+ * Fetch by primary key — throws if not found.
110
+ */
111
+ async firstOrFail(message) {
112
+ const result = await this.first();
113
+ if (!result) {
114
+ const HttpError = require('../../errors/HttpError');
115
+ throw new HttpError(404, message || `${this._model.name} not found`);
116
+ }
117
+ return result;
118
+ }
119
+
120
+ /**
121
+ * Count matching rows.
122
+ */
123
+ async count(column = '*') {
124
+ const result = await this._query.count(`${column} as count`);
125
+ const row = Array.isArray(result) ? result[0] : result;
126
+ return Number(row?.count ?? 0);
127
+ }
128
+
129
+ /**
130
+ * Check whether any rows match.
131
+ */
132
+ async exists() {
133
+ return (await this.count()) > 0;
134
+ }
135
+
136
+ /**
137
+ * Pluck a single column as a flat array.
138
+ */
139
+ async pluck(column) {
140
+ const rows = await this._query;
141
+ return rows.map(r => r[column]);
142
+ }
143
+
144
+ /**
145
+ * Paginate results.
146
+ * Returns { data, total, page, perPage, lastPage }
147
+ */
148
+ async paginate(page = 1, perPage = 15) {
149
+ const offset = (page - 1) * perPage;
150
+
151
+ // Clone for count without limit/offset
152
+ const total = await this._model
153
+ .query()
154
+ .where(this._getWhereBindings())
155
+ .count();
156
+
157
+ const rows = await this._query.clone().limit(perPage).offset(offset).select('*');
158
+
159
+ return {
160
+ data: rows.map(r => this._model._hydrate(r)),
161
+ total,
162
+ page: Number(page),
163
+ perPage,
164
+ lastPage: Math.ceil(total / perPage),
165
+ };
166
+ }
167
+
168
+ /**
169
+ * Update matching rows.
170
+ */
171
+ async update(data) {
172
+ return this._query.update({ ...data, updated_at: new Date().toISOString() });
173
+ }
174
+
175
+ /**
176
+ * Delete matching rows.
177
+ */
178
+ async delete() {
179
+ return this._query.delete();
180
+ }
181
+
182
+ /**
183
+ * Raw underlying knex query (escape hatch).
184
+ */
185
+ toSQL() {
186
+ return this._query.toSQL();
187
+ }
188
+
189
+ // ─── Internal ─────────────────────────────────────────────────────────────
190
+
191
+ _getWhereBindings() {
192
+ // Lightweight — used for paginate count clone
193
+ return {};
194
+ }
195
+ }
196
+
197
+ module.exports = QueryBuilder;
@@ -0,0 +1,40 @@
1
+ 'use strict';
2
+
3
+ const ServiceProvider = require('./ServiceProvider');
4
+ const Admin = require('../admin/Admin');
5
+
6
+ /**
7
+ * AdminServiceProvider
8
+ *
9
+ * Registers the Admin singleton and exposes it in the container.
10
+ *
11
+ * Add to bootstrap/app.js:
12
+ * app.providers([..., AdminServiceProvider])
13
+ *
14
+ * Then register resources and mount:
15
+ * Admin.register(UserResource);
16
+ * Admin.mount(route, expressApp);
17
+ */
18
+ class AdminServiceProvider extends ServiceProvider {
19
+ register(container) {
20
+ container.instance('Admin', Admin);
21
+ container.instance('AdminResource', require('../admin/resources/AdminResource').AdminResource);
22
+ container.instance('AdminField', require('../admin/resources/AdminResource').AdminField);
23
+ container.instance('AdminFilter', require('../admin/resources/AdminResource').AdminFilter);
24
+ }
25
+
26
+ async boot(container) {
27
+ let adminConfig = {};
28
+ try {
29
+ adminConfig = require(process.cwd() + '/config/admin');
30
+ } catch { /* config is optional */ }
31
+
32
+ Admin.configure({
33
+ prefix: adminConfig.prefix || '/admin',
34
+ title: adminConfig.title || process.env.APP_NAME || 'Millas Admin',
35
+ ...adminConfig,
36
+ });
37
+ }
38
+ }
39
+
40
+ module.exports = AdminServiceProvider;
@@ -0,0 +1,53 @@
1
+ 'use strict';
2
+
3
+ const ServiceProvider = require('./ServiceProvider');
4
+ const Auth = require('../auth/Auth');
5
+ const AuthMiddleware = require('../auth/AuthMiddleware');
6
+ const RoleMiddleware = require('../auth/RoleMiddleware');
7
+
8
+ /**
9
+ * AuthServiceProvider
10
+ *
11
+ * Configures the Auth facade and registers auth-related
12
+ * bindings and middleware aliases.
13
+ *
14
+ * Add to bootstrap/app.js:
15
+ * app.providers([DatabaseServiceProvider, AuthServiceProvider, AppServiceProvider])
16
+ */
17
+ class AuthServiceProvider extends ServiceProvider {
18
+ register(container) {
19
+ container.instance('Auth', Auth);
20
+ container.instance('AuthMiddleware', AuthMiddleware);
21
+ }
22
+
23
+ async boot(container, app) {
24
+ // Load auth config
25
+ let authConfig;
26
+ try {
27
+ authConfig = require(process.cwd() + '/config/auth');
28
+ } catch {
29
+ authConfig = {
30
+ default: 'jwt',
31
+ guards: { jwt: { driver: 'jwt', secret: process.env.APP_KEY || 'dev-secret', expiresIn: '7d' } },
32
+ };
33
+ }
34
+
35
+ // Load the User model if available
36
+ let UserModel = null;
37
+ try {
38
+ UserModel = require(process.cwd() + '/app/models/User');
39
+ } catch { /* User model optional at boot */ }
40
+
41
+ // Configure the Auth singleton
42
+ Auth.configure(authConfig, UserModel);
43
+
44
+ // Register 'auth' middleware alias with the real JWT implementation
45
+ if (app && app.mwRegistry) {
46
+ app.mwRegistry.register('auth', AuthMiddleware);
47
+ app.mwRegistry.register('role', new RoleMiddleware([]));
48
+ app.mwRegistry.register('admin', new RoleMiddleware(['admin']));
49
+ }
50
+ }
51
+ }
52
+
53
+ module.exports = AuthServiceProvider;