millas 0.2.12-beta-1 → 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.
Files changed (82) hide show
  1. package/package.json +1 -1
  2. package/src/admin/ActivityLog.js +153 -52
  3. package/src/admin/Admin.js +400 -167
  4. package/src/admin/AdminAuth.js +213 -98
  5. package/src/admin/FormGenerator.js +372 -0
  6. package/src/admin/HookRegistry.js +256 -0
  7. package/src/admin/QueryEngine.js +263 -0
  8. package/src/admin/ViewContext.js +309 -0
  9. package/src/admin/WidgetRegistry.js +406 -0
  10. package/src/admin/index.js +17 -0
  11. package/src/admin/resources/AdminResource.js +383 -97
  12. package/src/admin/static/admin.css +1341 -0
  13. package/src/admin/static/date-picker.css +157 -0
  14. package/src/admin/static/date-picker.js +316 -0
  15. package/src/admin/static/json-editor.css +649 -0
  16. package/src/admin/static/json-editor.js +1429 -0
  17. package/src/admin/static/ui.js +1044 -0
  18. package/src/admin/views/layouts/base.njk +65 -1013
  19. package/src/admin/views/pages/detail.njk +40 -16
  20. package/src/admin/views/pages/form.njk +47 -599
  21. package/src/admin/views/pages/list.njk +145 -62
  22. package/src/admin/views/partials/form-field.njk +53 -0
  23. package/src/admin/views/partials/form-footer.njk +28 -0
  24. package/src/admin/views/partials/form-readonly.njk +114 -0
  25. package/src/admin/views/partials/form-scripts.njk +476 -0
  26. package/src/admin/views/partials/form-widget.njk +296 -0
  27. package/src/admin/views/partials/json-dialog.njk +80 -0
  28. package/src/admin/views/partials/json-editor.njk +37 -0
  29. package/src/admin.zip +0 -0
  30. package/src/auth/Auth.js +18 -2
  31. package/src/auth/AuthUser.js +65 -44
  32. package/src/cli.js +3 -1
  33. package/src/commands/createsuperuser.js +254 -0
  34. package/src/commands/lang.js +589 -0
  35. package/src/commands/migrate.js +154 -81
  36. package/src/commands/serve.js +1 -0
  37. package/src/container/AppInitializer.js +65 -8
  38. package/src/container/MillasApp.js +10 -3
  39. package/src/container/MillasConfig.js +35 -6
  40. package/src/core/admin.js +5 -0
  41. package/src/core/db.js +2 -1
  42. package/src/core/foundation.js +1 -9
  43. package/src/core/lang.js +1 -0
  44. package/src/i18n/I18nServiceProvider.js +91 -0
  45. package/src/i18n/Translator.js +635 -0
  46. package/src/i18n/defaults.js +122 -0
  47. package/src/i18n/index.js +164 -0
  48. package/src/i18n/locales/en.js +55 -0
  49. package/src/i18n/locales/sw.js +48 -0
  50. package/src/logger/formatters/PrettyFormatter.js +101 -65
  51. package/src/migrations/system/0001_users.js +21 -0
  52. package/src/migrations/system/0002_admin_log.js +25 -0
  53. package/src/migrations/system/0003_sessions.js +23 -0
  54. package/src/orm/fields/index.js +210 -188
  55. package/src/orm/migration/DefaultValueParser.js +325 -0
  56. package/src/orm/migration/InteractiveResolver.js +191 -0
  57. package/src/orm/migration/Makemigrations.js +312 -0
  58. package/src/orm/migration/MigrationGraph.js +227 -0
  59. package/src/orm/migration/MigrationRunner.js +202 -108
  60. package/src/orm/migration/MigrationWriter.js +463 -0
  61. package/src/orm/migration/ModelInspector.js +143 -74
  62. package/src/orm/migration/ModelScanner.js +225 -0
  63. package/src/orm/migration/ProjectState.js +213 -0
  64. package/src/orm/migration/RenameDetector.js +175 -0
  65. package/src/orm/migration/SchemaBuilder.js +8 -81
  66. package/src/orm/migration/operations/base.js +57 -0
  67. package/src/orm/migration/operations/column.js +191 -0
  68. package/src/orm/migration/operations/fields.js +252 -0
  69. package/src/orm/migration/operations/index.js +55 -0
  70. package/src/orm/migration/operations/models.js +152 -0
  71. package/src/orm/migration/operations/registry.js +131 -0
  72. package/src/orm/migration/operations/special.js +51 -0
  73. package/src/orm/migration/utils.js +208 -0
  74. package/src/orm/model/Model.js +81 -13
  75. package/src/providers/AdminServiceProvider.js +66 -9
  76. package/src/providers/AuthServiceProvider.js +40 -5
  77. package/src/providers/CacheStorageServiceProvider.js +2 -2
  78. package/src/providers/DatabaseServiceProvider.js +3 -2
  79. package/src/providers/LogServiceProvider.js +4 -1
  80. package/src/providers/MailServiceProvider.js +1 -1
  81. package/src/providers/QueueServiceProvider.js +1 -1
  82. package/src/scaffold/templates.js +77 -21
@@ -22,10 +22,11 @@ class AuthServiceProvider extends ServiceProvider {
22
22
  }
23
23
 
24
24
  async boot(container, app) {
25
+ const basePath = container.make('basePath') || process.cwd();
25
26
  // Load auth config
26
27
  let authConfig;
27
28
  try {
28
- authConfig = require(process.cwd() + '/config/auth');
29
+ authConfig = require(basePath + '/config/auth');
29
30
  } catch {
30
31
  authConfig = {
31
32
  default: 'jwt',
@@ -33,12 +34,46 @@ class AuthServiceProvider extends ServiceProvider {
33
34
  };
34
35
  }
35
36
 
36
- // Load the app's User model if it exists.
37
- // Falls back to the built-in AuthUser so Auth always has a model to work with.
37
+ // ── Resolve the User model ──────────────────────────────────────────────
38
+ //
39
+ // Priority order (mirrors Django's AUTH_USER_MODEL pattern):
40
+ //
41
+ // 1. config/app.js → auth_user: 'User'
42
+ // The model name is looked up in app/models/index.js exports.
43
+ // This is the recommended approach — explicit and refactor-safe.
44
+ //
45
+ // 2. app/models/User.js (default export or named User export)
46
+ // Conventional fallback — works if auth_user is not set and
47
+ // the file exists at the default path.
48
+ //
49
+ // 3. Built-in AuthUser
50
+ // Abstract base class — no table. Used only as a last resort
51
+ // so Auth always has a model to work with during early dev.
52
+ //
38
53
  let UserModel;
39
54
  try {
40
- UserModel = require(process.cwd() + '/app/models/User');
41
- } catch {
55
+ // Step 1: read auth_user from config/app.js
56
+ let authUserName = null;
57
+ try {
58
+ const appConfig = require(basePath + '/config/app');
59
+ authUserName = appConfig.auth_user || null;
60
+ } catch { /* config/app.js missing or has no auth_user key */ }
61
+
62
+ if (authUserName) {
63
+ // Resolve by name from app/models/index.js
64
+ const modelsIndex = require(basePath + '/app/models/index');
65
+ const resolved = modelsIndex[authUserName];
66
+ if (!resolved) {
67
+ throw new Error(
68
+ `[AuthServiceProvider] auth_user: '${authUserName}' not found in app/models/index.js.\n` +
69
+ ` Available exports: ${Object.keys(modelsIndex).join(', ')}`
70
+ );
71
+ }
72
+ UserModel = resolved;
73
+ }
74
+ } catch (err) {
75
+ if (err.message.includes('[AuthServiceProvider]')) throw err; // re-throw config errors
76
+ // Step 3: fall back to built-in AuthUser (abstract — no table)
42
77
  UserModel = require('../auth/AuthUser');
43
78
  }
44
79
 
@@ -18,7 +18,7 @@ class CacheServiceProvider extends ServiceProvider {
18
18
  async boot() {
19
19
  let cacheConfig;
20
20
  try {
21
- cacheConfig = require(process.cwd() + '/config/cache');
21
+ cacheConfig = require((container.make('basePath') || process.cwd()) + '/config/cache');
22
22
  } catch {
23
23
  cacheConfig = {
24
24
  default: process.env.CACHE_DRIVER || 'memory',
@@ -48,7 +48,7 @@ class StorageServiceProvider extends ServiceProvider {
48
48
  async boot() {
49
49
  let storageConfig;
50
50
  try {
51
- storageConfig = require(process.cwd() + '/config/storage');
51
+ storageConfig = require((container.make('basePath') || process.cwd()) + '/config/storage');
52
52
  } catch {
53
53
  storageConfig = {
54
54
  default: process.env.STORAGE_DRIVER || 'local',
@@ -21,10 +21,11 @@ class DatabaseServiceProvider extends ServiceProvider {
21
21
  }
22
22
 
23
23
  async boot(container) {
24
+ const basePath = container.make('basePath') || process.cwd();
24
25
  // Load the database config
25
26
  let dbConfig;
26
27
  try {
27
- dbConfig = require(process.cwd() + '/config/database');
28
+ dbConfig = require(basePath + '/config/database');
28
29
  } catch {
29
30
  // Fallback for tests / programmatic use
30
31
  dbConfig = {
@@ -42,4 +43,4 @@ class DatabaseServiceProvider extends ServiceProvider {
42
43
  }
43
44
  }
44
45
 
45
- module.exports = DatabaseServiceProvider;
46
+ module.exports = DatabaseServiceProvider;
@@ -59,6 +59,9 @@ class LogServiceProvider extends ServiceProvider {
59
59
  beforeBoot(container) {
60
60
  let config = {};
61
61
  try {
62
+ // Note: beforeBoot runs before any container bindings exist.
63
+ // basePath is not yet available — process.cwd() is the correct fallback here.
64
+ // The path will be correct as long as `millas serve` is run from project root.
62
65
  config = require(process.cwd() + '/config/logging');
63
66
  } catch {
64
67
  // No config file — defaults already applied in logger/index.js
@@ -206,4 +209,4 @@ class LogServiceProvider extends ServiceProvider {
206
209
  }
207
210
  }
208
211
 
209
- module.exports = LogServiceProvider;
212
+ module.exports = LogServiceProvider;
@@ -27,7 +27,7 @@ class MailServiceProvider extends ServiceProvider {
27
27
  async boot(container) {
28
28
  let mailConfig;
29
29
  try {
30
- mailConfig = require(process.cwd() + '/config/mail');
30
+ mailConfig = require((container.make('basePath') || process.cwd()) + '/config/mail');
31
31
  } catch {
32
32
  mailConfig = {
33
33
  default: process.env.MAIL_DRIVER || 'log',
@@ -29,7 +29,7 @@ class QueueServiceProvider extends ServiceProvider {
29
29
  async boot(container) {
30
30
  let queueConfig;
31
31
  try {
32
- queueConfig = require(process.cwd() + '/config/queue');
32
+ queueConfig = require((container.make('basePath') || process.cwd()) + '/config/queue');
33
33
  } catch {
34
34
  queueConfig = {
35
35
  default: process.env.QUEUE_DRIVER || 'sync',
@@ -68,21 +68,6 @@ storage/logs/*.log
68
68
  storage/uploads/*
69
69
  !storage/uploads/.gitkeep
70
70
  database/database.sqlite
71
- `,
72
-
73
- // ─── millas.config.js ─────────────────────────────────────────
74
- 'millas.config.js': `'use strict';
75
-
76
- module.exports = {
77
- /*
78
- |--------------------------------------------------------------------------
79
- | Millas Framework Configuration
80
- |--------------------------------------------------------------------------
81
- */
82
- providers: [
83
- './providers/AppServiceProvider',
84
- ],
85
- };
86
71
  `,
87
72
 
88
73
  // ─── bootstrap/app.js ─────────────────────────────────────────
@@ -90,16 +75,30 @@ module.exports = {
90
75
 
91
76
  require('dotenv').config();
92
77
 
78
+ const path = require('path');
93
79
  const { Millas } = require('millas');
94
80
  const AppServiceProvider = require('../providers/AppServiceProvider');
95
81
 
96
- module.exports = Millas.config()
97
- .providers([AppServiceProvider])
82
+ /**
83
+ * Application configuration and bootstrap.
84
+ *
85
+ * Millas.configure(__dirname/..) sets the project root so the framework
86
+ * locates config files, models, and routes without guessing.
87
+ *
88
+ * Core providers (Database, Auth, Admin, Cache, Mail, Queue, Events)
89
+ * are wired automatically — only add your own app providers below.
90
+ *
91
+ * First-time setup:
92
+ * millas migrate # creates system tables (users, admin_log, sessions)
93
+ * millas createsuperuser # creates your first admin panel user
94
+ */
95
+ module.exports = Millas.configure(path.join(__dirname, '..'))
96
+ .withAdmin()
98
97
  .routes(Route => {
99
98
  require('../routes/web')(Route);
100
99
  require('../routes/api')(Route);
101
100
  })
102
- .withAdmin()
101
+ .providers([AppServiceProvider])
103
102
  .create();
104
103
  `,
105
104
 
@@ -153,7 +152,12 @@ module.exports = {
153
152
  key: process.env.APP_KEY || '',
154
153
  debug: process.env.APP_ENV !== 'production',
155
154
  timezone: 'UTC',
156
- locale: 'en',
155
+ locale: 'en',
156
+ fallback: 'en',
157
+
158
+ // Set use_i18n: true to enable the translation system.
159
+ // Then run: millas lang:publish <locale>
160
+ use_i18n: false,
157
161
  };
158
162
  `,
159
163
 
@@ -215,6 +219,50 @@ module.exports = {
215
219
  expiresIn: '1h',
216
220
  },
217
221
  };
222
+ `,
223
+
224
+ // ─── config/admin.js ──────────────────────────────────────────
225
+ 'config/admin.js': `'use strict';
226
+
227
+ /**
228
+ * Admin Panel Configuration
229
+ *
230
+ * Credentials are read from environment variables by default.
231
+ * Set ADMIN_EMAIL and ADMIN_PASSWORD in your .env file, or define
232
+ * static users / a model here.
233
+ */
234
+ module.exports = {
235
+ // URL prefix for the admin panel
236
+ prefix: '/admin',
237
+
238
+ // Title shown in the browser tab and sidebar
239
+ title: process.env.APP_NAME ? \`\${process.env.APP_NAME} Admin\` : 'Millas Admin',
240
+
241
+ auth: {
242
+ // ── Option 1: static user list (simple setups / local dev) ────
243
+ users: [
244
+ {
245
+ email: process.env.ADMIN_EMAIL || 'admin@example.com',
246
+ // Use a bcrypt hash in production; plain text is fine for local dev.
247
+ password: process.env.ADMIN_PASSWORD || 'change-me',
248
+ name: process.env.ADMIN_NAME || 'Admin',
249
+ },
250
+ ],
251
+
252
+ // ── Option 2: model-based lookup ──────────────────────────────
253
+ // Uncomment and set to any Millas Model that has email + password fields.
254
+ // model: require('../app/models/User'),
255
+
256
+ // ── Session cookie settings ───────────────────────────────────
257
+ cookieName: 'millas_admin',
258
+ cookieMaxAge: 60 * 60 * 8, // 8 hours
259
+ rememberAge: 60 * 60 * 24 * 30, // 30 days ("remember me")
260
+
261
+ // ── Brute-force protection ────────────────────────────────────
262
+ maxAttempts: 5,
263
+ lockoutMinutes: 15,
264
+ },
265
+ };
218
266
  `,
219
267
 
220
268
  // ─── config/mail.js ───────────────────────────────────────────
@@ -264,10 +312,18 @@ class AppServiceProvider extends ServiceProvider {
264
312
  }
265
313
 
266
314
  async boot(container, app) {
267
- // Register Admin resources:
315
+ // Register Admin resources — Admin panel is auto-mounted via .withAdmin()
268
316
  // const { Admin } = require('millas');
317
+ // const { AdminResource, AdminField } = require('millas');
269
318
  // const Post = require('../app/models/Post');
270
- // Admin.register(Post);
319
+ //
320
+ // class PostResource extends AdminResource {
321
+ // static model = Post;
322
+ // static label = 'Posts';
323
+ // static searchable = ['title', 'body'];
324
+ // }
325
+ //
326
+ // Admin.register(PostResource);
271
327
  }
272
328
  }
273
329