@realtek/core-theme 0.0.60 → 0.0.61

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.
@@ -0,0 +1,788 @@
1
+ #!/usr/bin/env node
2
+
3
+ import process from 'node:process';
4
+ import { existsSync, readFileSync } from 'node:fs';
5
+ import { resolve as resolvePath } from 'node:path';
6
+ import { createInterface } from 'node:readline/promises';
7
+ import { stdin as input, stdout as output } from 'node:process';
8
+ import { MongoClient, ObjectId } from 'mongodb';
9
+
10
+ const DEFAULT_MONGO_URI = 'mongodb://admin:e7cH06Dy2LK2@192.168.1.88:27017/znnxt?authSource=admin&authMechanism=SCRAM-SHA-256';
11
+ const DEFAULT_ADMIN_EMAIL = 'admin@example.com';
12
+ const DEFAULT_ADMIN_PASSWORD_HASH = '$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy';
13
+ const HISTORY_COLLECTION = 'MONGO_MIGRATION_HISTORY';
14
+
15
+ const DEFAULT_TENANT_ID = '93c782a4aa626175e5d11afa';
16
+ const DEFAULT_BUSINESS_ID = '67ac80d40fd559c44345bd30';
17
+ const DEFAULT_BUSINESS_UNIT_ID = '67ac811f0fd559c44345bd31';
18
+ const DEFAULT_ADMIN_ROLE_ID = 5;
19
+ const DEFAULT_ADMIN_USER_ID = 1;
20
+
21
+ const PERMISSIONS = [
22
+ { legacyPermissionId: 31, permissionKey: 'view', permissionName: 'View' },
23
+ { legacyPermissionId: 32, permissionKey: 'add', permissionName: 'Add' },
24
+ { legacyPermissionId: 33, permissionKey: 'edit', permissionName: 'Edit' },
25
+ { legacyPermissionId: 34, permissionKey: 'delete', permissionName: 'Delete' },
26
+ { legacyPermissionId: 35, permissionKey: 'share', permissionName: 'Share' },
27
+ { legacyPermissionId: 36, permissionKey: 'approve', permissionName: 'Approve' },
28
+ { legacyPermissionId: 37, permissionKey: 'move_onboard', permissionName: 'Move Onboard' },
29
+ ];
30
+
31
+ const SIDE_MENU_STRUCTURE = {
32
+ menu: [
33
+ { key: 'dashboard', icon: 'Dashboard', label: 'Dashboard', status: 'active' },
34
+ { key: 'jobs', icon: 'Jobs', label: 'Jobs', status: 'active' },
35
+ { key: 'candidates', icon: 'Candidates', label: 'Candidates', status: 'active' },
36
+ { key: 'submissions', icon: 'Submissions', label: 'Submissions', status: 'active' },
37
+ { key: 'onboarding', icon: 'onboarding', label: 'Onboarding', status: 'active' },
38
+ { key: 'assignments', icon: 'onboarding', label: 'Assignments', status: 'active' },
39
+ { key: 'timesheet', icon: 'timesheet', label: 'Timesheet', status: 'active' },
40
+ {
41
+ key: 'network',
42
+ icon: 'Network',
43
+ label: 'Network',
44
+ status: 'active',
45
+ children: [
46
+ { key: 'network/hotlist', icon: 'Hotlist', label: 'Hotlist', status: 'active' },
47
+ { key: 'network/vendors', icon: 'Vendors', label: 'Vendors', status: 'active' },
48
+ ],
49
+ },
50
+ { key: 'campaign', icon: 'campaign', label: 'Campaign', status: 'active' },
51
+ { key: 'scheduler', icon: 'Scheduler', label: 'Scheduler', status: 'inactive' },
52
+ { key: 'reports', icon: 'Reports', label: 'Reports', status: 'inactive' },
53
+ { key: 'test-cases', icon: 'test-cases', label: 'Test Cases', status: 'active' },
54
+ { key: 'admin', icon: 'Admin', label: 'Admin', status: 'active' },
55
+ ],
56
+ footermenu: [
57
+ {
58
+ key: 'settings',
59
+ icon: 'Settings',
60
+ label: 'Settings',
61
+ status: 'inactive',
62
+ children: [
63
+ { key: 'Font Size', icon: 'fontsize', label: 'fontsize', status: 'inactive' },
64
+ ],
65
+ },
66
+ { key: 'help', icon: 'Help', label: 'Help', status: 'inactive' },
67
+ ],
68
+ };
69
+
70
+ function flattenMenuStructure(menuStructure) {
71
+ const rows = [];
72
+ let legacyMenuId = 1;
73
+ const walk = (items, parentMenuId = null, placement = 'main') => {
74
+ for (const item of items) {
75
+ const currentMenuId = legacyMenuId;
76
+ rows.push({
77
+ legacyMenuId: currentMenuId,
78
+ parentMenuId,
79
+ menuKey: item.key,
80
+ menuName: item.label,
81
+ icon: item.icon,
82
+ status: item.status,
83
+ placement,
84
+ });
85
+ legacyMenuId += 1;
86
+ if (Array.isArray(item.children) && item.children.length > 0) {
87
+ walk(item.children, currentMenuId, placement);
88
+ }
89
+ }
90
+ };
91
+
92
+ walk(menuStructure.menu, null, 'main');
93
+ walk(menuStructure.footermenu, null, 'footer');
94
+ return rows;
95
+ }
96
+
97
+ const MENUS = flattenMenuStructure(SIDE_MENU_STRUCTURE);
98
+
99
+ function parseArgs(argv) {
100
+ const options = {
101
+ postinstall: false,
102
+ skipPrompt: false,
103
+ dbName: process.env.MONGO_DB_NAME || process.env.MONGO_DATABASE || process.env.DB_NAME || '',
104
+ mongoUri: process.env.MONGO_URI || '',
105
+ };
106
+
107
+ for (let index = 0; index < argv.length; index += 1) {
108
+ const arg = argv[index];
109
+ if (arg === '--postinstall') {
110
+ options.postinstall = true;
111
+ } else if (arg === '--yes' || arg === '-y') {
112
+ options.skipPrompt = true;
113
+ } else if (arg === '--db' || arg === '--db-name') {
114
+ options.dbName = argv[index + 1] || '';
115
+ index += 1;
116
+ } else if (arg.startsWith('--db=')) {
117
+ options.dbName = arg.slice('--db='.length);
118
+ } else if (arg === '--uri' || arg === '--mongo-uri') {
119
+ options.mongoUri = argv[index + 1] || '';
120
+ index += 1;
121
+ } else if (arg.startsWith('--uri=')) {
122
+ options.mongoUri = arg.slice('--uri='.length);
123
+ }
124
+ }
125
+
126
+ options.dbName = String(options.dbName || '').trim();
127
+ options.mongoUri = String(options.mongoUri || '').trim();
128
+ return options;
129
+ }
130
+
131
+ function log(message) {
132
+ console.log(`[core-theme:init-mongodb] ${message}`);
133
+ }
134
+
135
+ function parseEnvValue(value) {
136
+ const trimmed = String(value ?? '').trim();
137
+ if (
138
+ (trimmed.startsWith('"') && trimmed.endsWith('"')) ||
139
+ (trimmed.startsWith("'") && trimmed.endsWith("'"))
140
+ ) {
141
+ return trimmed.slice(1, -1);
142
+ }
143
+ return trimmed;
144
+ }
145
+
146
+ function loadEnvFile(path) {
147
+ if (!existsSync(path)) return false;
148
+
149
+ const content = readFileSync(path, 'utf8');
150
+ content.split(/\r?\n/).forEach((line) => {
151
+ const trimmed = line.trim();
152
+ if (!trimmed || trimmed.startsWith('#')) return;
153
+
154
+ const equalIndex = trimmed.indexOf('=');
155
+ if (equalIndex <= 0) return;
156
+
157
+ const key = trimmed.slice(0, equalIndex).trim();
158
+ const value = parseEnvValue(trimmed.slice(equalIndex + 1));
159
+ if (!key || process.env[key] !== undefined) return;
160
+
161
+ process.env[key] = value;
162
+ });
163
+
164
+ return true;
165
+ }
166
+
167
+ function loadProjectEnv() {
168
+ const candidates = [
169
+ process.env.INIT_CWD,
170
+ process.cwd(),
171
+ ]
172
+ .filter(Boolean)
173
+ .map((directory) => resolvePath(directory, '.env'));
174
+
175
+ const loaded = [];
176
+ for (const path of Array.from(new Set(candidates))) {
177
+ if (loadEnvFile(path)) {
178
+ loaded.push(path);
179
+ }
180
+ }
181
+
182
+ return loaded;
183
+ }
184
+
185
+ async function promptForDbName(defaultDbName) {
186
+ const rl = createInterface({ input, output });
187
+ try {
188
+ const suffix = defaultDbName ? ` (${defaultDbName})` : '';
189
+ const answer = await rl.question(`Enter MongoDB database name${suffix}: `);
190
+ return answer.trim() || defaultDbName;
191
+ } finally {
192
+ rl.close();
193
+ }
194
+ }
195
+
196
+ async function promptForMongoUri(defaultMongoUri) {
197
+ const rl = createInterface({ input, output });
198
+ try {
199
+ const suffix = defaultMongoUri ? ` (${defaultMongoUri})` : '';
200
+ const answer = await rl.question(`Enter MongoDB URI${suffix}: `);
201
+ return answer.trim() || defaultMongoUri;
202
+ } finally {
203
+ rl.close();
204
+ }
205
+ }
206
+
207
+ function validator(schema) {
208
+ return { $jsonSchema: schema };
209
+ }
210
+
211
+ async function createOrUpdateCollection(db, name, schema) {
212
+ const exists = await db.listCollections({ name }).hasNext();
213
+ const collectionValidator = validator(schema);
214
+
215
+ if (exists) {
216
+ await db.command({ collMod: name, validator: collectionValidator });
217
+ log(`COLLECTION UPDATED : ${name}`);
218
+ return;
219
+ }
220
+
221
+ await db.createCollection(name, { validator: collectionValidator });
222
+ log(`COLLECTION CREATED : ${name}`);
223
+ }
224
+
225
+ async function createIndex(collection, keys, options = {}) {
226
+ await collection.createIndex(keys, options);
227
+ const fields = Object.keys(keys).join(', ');
228
+ log(`INDEX READY : ${collection.collectionName} (${fields})`);
229
+ }
230
+
231
+ async function upsertOne(collection, filter, update, label) {
232
+ const result = await collection.updateOne(filter, update, { upsert: true });
233
+ if (result.upsertedCount > 0) {
234
+ log(`SEEDED : ${label}`);
235
+ } else if (result.modifiedCount > 0) {
236
+ log(`UPDATED : ${label}`);
237
+ } else {
238
+ log(`UNCHANGED : ${label}`);
239
+ }
240
+ }
241
+
242
+ async function syncCounter(db, collectionName, minimumValue) {
243
+ await db.collection('counters').updateOne(
244
+ { _id: collectionName },
245
+ { $max: { seq: minimumValue } },
246
+ { upsert: true },
247
+ );
248
+ log(`COUNTER READY : ${collectionName} >= ${minimumValue}`);
249
+ }
250
+
251
+ async function createCollections(db) {
252
+ await createOrUpdateCollection(db, 'users', {
253
+ bsonType: 'object',
254
+ required: [
255
+ 'legacyUserId',
256
+ 'firstName',
257
+ 'email',
258
+ 'password',
259
+ 'roleId',
260
+ 'tenantId',
261
+ 'businessId',
262
+ 'businessUnitId',
263
+ 'isLocked',
264
+ 'status',
265
+ 'permissionVersion',
266
+ 'activeStatus',
267
+ 'isDeleted',
268
+ 'createdAt',
269
+ 'updatedAt',
270
+ ],
271
+ properties: {
272
+ legacyUserId: { bsonType: ['int', 'long'] },
273
+ userName: { bsonType: ['string', 'null'] },
274
+ firstName: { bsonType: 'string' },
275
+ lastName: { bsonType: ['string', 'null'] },
276
+ email: { bsonType: 'string' },
277
+ password: { bsonType: 'string' },
278
+ roleId: { bsonType: ['int', 'long'] },
279
+ tenantId: { bsonType: 'objectId' },
280
+ businessId: { bsonType: 'objectId' },
281
+ businessUnitId: { bsonType: 'objectId' },
282
+ isLocked: { bsonType: 'bool' },
283
+ invalidAttempts: { bsonType: ['int', 'long', 'null'] },
284
+ activeStatus: { bsonType: 'bool' },
285
+ isDeleted: { bsonType: 'bool' },
286
+ status: { bsonType: ['int', 'long'] },
287
+ permissionVersion: { bsonType: ['int', 'long'] },
288
+ verified: { bsonType: ['string', 'null'] },
289
+ createdBy: { bsonType: ['int', 'long', 'null'] },
290
+ updatedBy: { bsonType: ['int', 'long', 'null'] },
291
+ createdAt: { bsonType: 'date' },
292
+ updatedAt: { bsonType: 'date' },
293
+ },
294
+ });
295
+
296
+ await createOrUpdateCollection(db, 'menus', {
297
+ bsonType: 'object',
298
+ required: ['legacyMenuId', 'menuKey', 'menuName', 'status', 'isDeleted', 'createdAt', 'updatedAt'],
299
+ properties: {
300
+ legacyMenuId: { bsonType: ['int', 'long'] },
301
+ parentMenuId: { bsonType: ['int', 'long', 'null'] },
302
+ menuKey: { bsonType: 'string' },
303
+ menuName: { bsonType: 'string' },
304
+ icon: { bsonType: ['string', 'null'] },
305
+ placement: { enum: ['main', 'footer'] },
306
+ status: { enum: ['active', 'inactive'] },
307
+ isDeleted: { bsonType: 'bool' },
308
+ createdBy: { bsonType: ['int', 'long', 'null'] },
309
+ updatedBy: { bsonType: ['int', 'long', 'null'] },
310
+ createdAt: { bsonType: 'date' },
311
+ updatedAt: { bsonType: 'date' },
312
+ },
313
+ });
314
+
315
+ await createOrUpdateCollection(db, 'permissions', {
316
+ bsonType: 'object',
317
+ required: ['legacyPermissionId', 'permissionKey', 'permissionName', 'status', 'isDeleted', 'createdAt', 'updatedAt'],
318
+ properties: {
319
+ legacyPermissionId: { bsonType: ['int', 'long'] },
320
+ permissionKey: { bsonType: 'string' },
321
+ permissionName: { bsonType: 'string' },
322
+ status: { enum: ['ACTIVE', 'INACTIVE'] },
323
+ isDeleted: { bsonType: 'bool' },
324
+ createdBy: { bsonType: ['int', 'long', 'null'] },
325
+ updatedBy: { bsonType: ['int', 'long', 'null'] },
326
+ createdAt: { bsonType: 'date' },
327
+ updatedAt: { bsonType: 'date' },
328
+ },
329
+ });
330
+
331
+ await createOrUpdateCollection(db, 'roles', {
332
+ bsonType: 'object',
333
+ required: ['legacyRoleId', 'roleId', 'roleName', 'tenantId', 'businessId', 'businessUnitId', 'isDeleted', 'createdAt', 'updatedAt'],
334
+ properties: {
335
+ legacyId: { bsonType: ['int', 'long'] },
336
+ legacyRoleId: { bsonType: ['int', 'long'] },
337
+ roleId: { bsonType: ['int', 'long'] },
338
+ roleName: { bsonType: 'string' },
339
+ tenantId: { bsonType: 'objectId' },
340
+ businessId: { bsonType: 'objectId' },
341
+ businessUnitId: { bsonType: 'objectId' },
342
+ status: { bsonType: ['int', 'long'] },
343
+ isDeleted: { bsonType: 'bool' },
344
+ createdBy: { bsonType: ['int', 'long', 'null'] },
345
+ updatedBy: { bsonType: ['int', 'long', 'null'] },
346
+ createdAt: { bsonType: 'date' },
347
+ updatedAt: { bsonType: 'date' },
348
+ },
349
+ });
350
+
351
+ const mappingSchema = {
352
+ bsonType: 'object',
353
+ required: ['legacyMapId', 'roleId', 'menuId', 'permissions', 'tenantId', 'businessId', 'businessUnitId', 'isDeleted', 'createdAt', 'updatedAt'],
354
+ properties: {
355
+ legacyMapId: { bsonType: ['int', 'long'] },
356
+ userId: { bsonType: ['int', 'long', 'null'] },
357
+ roleId: { bsonType: ['int', 'long'] },
358
+ menuId: { bsonType: ['int', 'long'] },
359
+ permissionId: { bsonType: ['int', 'long', 'null'] },
360
+ accessValue: { bsonType: ['string', 'null'] },
361
+ permissions: { bsonType: 'object' },
362
+ tenantId: { bsonType: 'objectId' },
363
+ businessId: { bsonType: 'objectId' },
364
+ businessUnitId: { bsonType: 'objectId' },
365
+ status: { bsonType: ['int', 'long'] },
366
+ isDeleted: { bsonType: 'bool' },
367
+ createdBy: { bsonType: ['int', 'long', 'null'] },
368
+ updatedBy: { bsonType: ['int', 'long', 'null'] },
369
+ createdAt: { bsonType: 'date' },
370
+ updatedAt: { bsonType: 'date' },
371
+ },
372
+ };
373
+
374
+ await createOrUpdateCollection(db, 'role_menu_permissions', mappingSchema);
375
+ await createOrUpdateCollection(db, 'user_menu_permissions', mappingSchema);
376
+
377
+ await createOrUpdateCollection(db, 'menu_structures', {
378
+ bsonType: 'object',
379
+ required: ['roleId', 'menuStructure', 'tenantId', 'businessId', 'businessUnitId', 'isDeleted', 'createdAt', 'updatedAt'],
380
+ properties: {
381
+ roleId: { bsonType: ['int', 'long'] },
382
+ menuStructure: { bsonType: 'object' },
383
+ tenantId: { bsonType: 'objectId' },
384
+ businessId: { bsonType: 'objectId' },
385
+ businessUnitId: { bsonType: 'objectId' },
386
+ status: { bsonType: ['int', 'long'] },
387
+ isDeleted: { bsonType: 'bool' },
388
+ createdBy: { bsonType: ['int', 'long', 'null'] },
389
+ updatedBy: { bsonType: ['int', 'long', 'null'] },
390
+ createdAt: { bsonType: 'date' },
391
+ updatedAt: { bsonType: 'date' },
392
+ },
393
+ });
394
+ }
395
+
396
+ async function createIndexes(db) {
397
+ await createIndex(db.collection('users'), { userName: 1 }, { unique: true, sparse: true });
398
+ await createIndex(db.collection('users'), { email: 1 }, { unique: true });
399
+ await createIndex(db.collection('users'), { legacyUserId: 1 }, { unique: true, sparse: true });
400
+ await createIndex(db.collection('users'), { roleId: 1 });
401
+ await createIndex(db.collection('roles'), { legacyRoleId: 1 }, { unique: true, sparse: true });
402
+ await createIndex(db.collection('roles'), { roleId: 1 }, { unique: true, sparse: true });
403
+ await createIndex(db.collection('roles'), { roleName: 1 });
404
+ await createIndex(db.collection('menus'), { legacyMenuId: 1 }, { unique: true, sparse: true });
405
+ await createIndex(db.collection('menus'), { menuKey: 1 }, { unique: true, sparse: true });
406
+ await createIndex(db.collection('menus'), { parentMenuId: 1 });
407
+ await createIndex(db.collection('permissions'), { legacyPermissionId: 1 }, { unique: true, sparse: true });
408
+ await createIndex(db.collection('permissions'), { permissionKey: 1 }, { unique: true, sparse: true });
409
+ await createIndex(db.collection('role_menu_permissions'), { roleId: 1, menuId: 1 }, { unique: true });
410
+ await createIndex(db.collection('role_menu_permissions'), { legacyMapId: 1 }, { unique: true });
411
+ await createIndex(db.collection('user_menu_permissions'), { userId: 1, roleId: 1, menuId: 1 }, { unique: true });
412
+ await createIndex(db.collection('user_menu_permissions'), { legacyMapId: 1 }, { unique: true });
413
+ await createIndex(db.collection('menu_structures'), { roleId: 1 }, { unique: true });
414
+ }
415
+
416
+ async function seedPermissions(db) {
417
+ const timestamp = new Date();
418
+ for (const permission of PERMISSIONS) {
419
+ await upsertOne(
420
+ db.collection('permissions'),
421
+ { legacyPermissionId: permission.legacyPermissionId },
422
+ {
423
+ $set: {
424
+ permissionKey: permission.permissionKey,
425
+ permissionName: permission.permissionName,
426
+ status: 'ACTIVE',
427
+ isDeleted: false,
428
+ updatedAt: timestamp,
429
+ },
430
+ $setOnInsert: {
431
+ legacyPermissionId: permission.legacyPermissionId,
432
+ migrationSource: 'core-theme-admin.seed',
433
+ createdBy: 1,
434
+ createdAt: timestamp,
435
+ },
436
+ },
437
+ `permissions.${permission.permissionKey}`,
438
+ );
439
+ }
440
+ await syncCounter(db, 'permissions', Math.max(...PERMISSIONS.map((permission) => permission.legacyPermissionId)));
441
+ }
442
+
443
+ async function seedMenus(db) {
444
+ const timestamp = new Date();
445
+ for (const menu of MENUS) {
446
+ await upsertOne(
447
+ db.collection('menus'),
448
+ { legacyMenuId: menu.legacyMenuId },
449
+ {
450
+ $set: {
451
+ parentMenuId: menu.parentMenuId,
452
+ menuKey: menu.menuKey,
453
+ menuName: menu.menuName,
454
+ icon: menu.icon,
455
+ placement: menu.placement,
456
+ status: menu.status,
457
+ isDeleted: false,
458
+ updatedAt: timestamp,
459
+ },
460
+ $setOnInsert: {
461
+ legacyMenuId: menu.legacyMenuId,
462
+ migrationSource: 'core-theme-admin.seed',
463
+ createdBy: 1,
464
+ createdAt: timestamp,
465
+ },
466
+ },
467
+ `menus.${menu.menuKey}`,
468
+ );
469
+ }
470
+ await syncCounter(db, 'menus', MENUS.length);
471
+ }
472
+
473
+ async function seedRoles(db) {
474
+ const timestamp = new Date();
475
+ const tenantId = new ObjectId(process.env.DEFAULT_TENANT_ID || DEFAULT_TENANT_ID);
476
+ const businessId = new ObjectId(process.env.DEFAULT_BUSINESS_ID || DEFAULT_BUSINESS_ID);
477
+ const businessUnitId = new ObjectId(process.env.DEFAULT_BUSINESS_UNIT_ID || DEFAULT_BUSINESS_UNIT_ID);
478
+
479
+ await upsertOne(
480
+ db.collection('roles'),
481
+ { legacyRoleId: DEFAULT_ADMIN_ROLE_ID },
482
+ {
483
+ $set: {
484
+ legacyId: DEFAULT_ADMIN_ROLE_ID,
485
+ roleId: DEFAULT_ADMIN_ROLE_ID,
486
+ roleName: 'ADMIN',
487
+ roleDescription: 'Core theme package admin',
488
+ roleType: 0,
489
+ customRoleId: 0,
490
+ reportingRoleId: 0,
491
+ orgId: 0,
492
+ tenantId,
493
+ businessId,
494
+ businessUnitId,
495
+ status: 1,
496
+ isDeleted: false,
497
+ updatedBy: 1,
498
+ updatedAt: timestamp,
499
+ },
500
+ $setOnInsert: {
501
+ migrationSource: 'core-theme-admin.seed',
502
+ createdBy: 1,
503
+ createdAt: timestamp,
504
+ },
505
+ },
506
+ 'roles.ADMIN',
507
+ );
508
+
509
+ await syncCounter(db, 'roles', DEFAULT_ADMIN_ROLE_ID);
510
+ return db.collection('roles').findOne({ legacyRoleId: DEFAULT_ADMIN_ROLE_ID });
511
+ }
512
+
513
+ async function seedAdminUser(db) {
514
+ const timestamp = new Date();
515
+ const adminEmail = process.env.ADMIN_EMAIL || DEFAULT_ADMIN_EMAIL;
516
+ const adminPasswordHash = process.env.ADMIN_PASSWORD_HASH || DEFAULT_ADMIN_PASSWORD_HASH;
517
+ const tenantId = new ObjectId(process.env.DEFAULT_TENANT_ID || DEFAULT_TENANT_ID);
518
+ const businessId = new ObjectId(process.env.DEFAULT_BUSINESS_ID || DEFAULT_BUSINESS_ID);
519
+ const businessUnitId = new ObjectId(process.env.DEFAULT_BUSINESS_UNIT_ID || DEFAULT_BUSINESS_UNIT_ID);
520
+
521
+ await upsertOne(
522
+ db.collection('users'),
523
+ {
524
+ $or: [
525
+ { legacyUserId: DEFAULT_ADMIN_USER_ID },
526
+ { email: adminEmail },
527
+ { userName: 'admin' },
528
+ { username: 'admin' },
529
+ ],
530
+ },
531
+ {
532
+ $set: {
533
+ userName: 'admin',
534
+ nameTitle: '',
535
+ firstName: 'admin',
536
+ lastName: '',
537
+ email: adminEmail,
538
+ password: adminPasswordHash,
539
+ roleId: DEFAULT_ADMIN_ROLE_ID,
540
+ tenantId,
541
+ businessId,
542
+ businessUnitId,
543
+ reportingId: 0,
544
+ teamId: 0,
545
+ orgId: 0,
546
+ gender: 0,
547
+ location: '',
548
+ mobileNumber: '',
549
+ officeNumber: null,
550
+ phoneExtension: '',
551
+ phoneNumber: '',
552
+ photo: { uploadName: '', uniqueName: '', location: '' },
553
+ socialProfiles: null,
554
+ defaultValues: null,
555
+ userType: null,
556
+ verified: 'OTP_VERIFIED',
557
+ activeStatus: true,
558
+ isLocked: false,
559
+ invalidAttempts: 0,
560
+ isDeleted: false,
561
+ status: 1,
562
+ permissionVersion: 1,
563
+ lastLoginAt: timestamp,
564
+ pwdLastChangedAt: timestamp,
565
+ updatedAt: timestamp,
566
+ updatedBy: 1,
567
+ },
568
+ $setOnInsert: {
569
+ legacyUserId: DEFAULT_ADMIN_USER_ID,
570
+ migrationSource: 'core-theme-admin.seed',
571
+ createdBy: 1,
572
+ createdAt: timestamp,
573
+ },
574
+ },
575
+ `users.admin (${adminEmail})`,
576
+ );
577
+
578
+ await syncCounter(db, 'users', 1);
579
+ return db.collection('users').findOne({
580
+ $or: [
581
+ { legacyUserId: DEFAULT_ADMIN_USER_ID },
582
+ { email: adminEmail },
583
+ { userName: 'admin' },
584
+ { username: 'admin' },
585
+ ],
586
+ });
587
+ }
588
+
589
+ function allEnabledPermissions() {
590
+ return Object.fromEntries(PERMISSIONS.map((permission) => [permission.permissionKey, true]));
591
+ }
592
+
593
+ async function seedRoleMenuPermissions(db) {
594
+ const timestamp = new Date();
595
+ const tenantId = new ObjectId(process.env.DEFAULT_TENANT_ID || DEFAULT_TENANT_ID);
596
+ const businessId = new ObjectId(process.env.DEFAULT_BUSINESS_ID || DEFAULT_BUSINESS_ID);
597
+ const businessUnitId = new ObjectId(process.env.DEFAULT_BUSINESS_UNIT_ID || DEFAULT_BUSINESS_UNIT_ID);
598
+ const menus = await db.collection('menus').find({ isDeleted: false }).sort({ legacyMenuId: 1 }).toArray();
599
+
600
+ for (const menu of menus) {
601
+ await upsertOne(
602
+ db.collection('role_menu_permissions'),
603
+ { roleId: DEFAULT_ADMIN_ROLE_ID, menuId: menu.legacyMenuId },
604
+ {
605
+ $set: {
606
+ roleId: DEFAULT_ADMIN_ROLE_ID,
607
+ menuId: menu.legacyMenuId,
608
+ permissionId: null,
609
+ accessValue: null,
610
+ permissions: allEnabledPermissions(),
611
+ tenantId,
612
+ businessId,
613
+ businessUnitId,
614
+ status: 1,
615
+ isDeleted: false,
616
+ updatedBy: 1,
617
+ updatedAt: timestamp,
618
+ },
619
+ $setOnInsert: {
620
+ legacyMapId: menu.legacyMenuId,
621
+ migrationSource: 'core-theme-admin.seed',
622
+ createdBy: 1,
623
+ createdAt: timestamp,
624
+ },
625
+ },
626
+ `role_menu_permissions.ADMIN.${menu.menuKey}`,
627
+ );
628
+ }
629
+
630
+ await syncCounter(db, 'role_menu_permissions', MENUS.length);
631
+ }
632
+
633
+ async function seedUserMenuPermissions(db, adminUser) {
634
+ const timestamp = new Date();
635
+ const tenantId = new ObjectId(process.env.DEFAULT_TENANT_ID || DEFAULT_TENANT_ID);
636
+ const businessId = new ObjectId(process.env.DEFAULT_BUSINESS_ID || DEFAULT_BUSINESS_ID);
637
+ const businessUnitId = new ObjectId(process.env.DEFAULT_BUSINESS_UNIT_ID || DEFAULT_BUSINESS_UNIT_ID);
638
+ const menus = await db.collection('menus').find({ isDeleted: false }).sort({ legacyMenuId: 1 }).toArray();
639
+
640
+ for (const menu of menus) {
641
+ await upsertOne(
642
+ db.collection('user_menu_permissions'),
643
+ { userId: adminUser.legacyUserId, roleId: DEFAULT_ADMIN_ROLE_ID, menuId: menu.legacyMenuId },
644
+ {
645
+ $set: {
646
+ userId: adminUser.legacyUserId,
647
+ roleId: DEFAULT_ADMIN_ROLE_ID,
648
+ menuId: menu.legacyMenuId,
649
+ permissionId: null,
650
+ accessValue: null,
651
+ permissions: allEnabledPermissions(),
652
+ tenantId,
653
+ businessId,
654
+ businessUnitId,
655
+ status: 1,
656
+ isDeleted: false,
657
+ updatedBy: 1,
658
+ updatedAt: timestamp,
659
+ },
660
+ $setOnInsert: {
661
+ legacyMapId: menu.legacyMenuId,
662
+ migrationSource: 'core-theme-admin.seed',
663
+ createdBy: 1,
664
+ createdAt: timestamp,
665
+ },
666
+ },
667
+ `user_menu_permissions.admin.${menu.menuKey}`,
668
+ );
669
+ }
670
+
671
+ await syncCounter(db, 'user_menu_permissions', MENUS.length);
672
+ }
673
+
674
+ async function seedMenuStructure(db) {
675
+ const timestamp = new Date();
676
+ const tenantId = new ObjectId(process.env.DEFAULT_TENANT_ID || DEFAULT_TENANT_ID);
677
+ const businessId = new ObjectId(process.env.DEFAULT_BUSINESS_ID || DEFAULT_BUSINESS_ID);
678
+ const businessUnitId = new ObjectId(process.env.DEFAULT_BUSINESS_UNIT_ID || DEFAULT_BUSINESS_UNIT_ID);
679
+
680
+ await upsertOne(
681
+ db.collection('menu_structures'),
682
+ { roleId: DEFAULT_ADMIN_ROLE_ID },
683
+ {
684
+ $set: {
685
+ roleId: DEFAULT_ADMIN_ROLE_ID,
686
+ menuStructure: SIDE_MENU_STRUCTURE,
687
+ tenantId,
688
+ businessId,
689
+ businessUnitId,
690
+ status: 1,
691
+ isDeleted: false,
692
+ updatedBy: 1,
693
+ updatedAt: timestamp,
694
+ },
695
+ $setOnInsert: {
696
+ migrationSource: 'core-theme-admin.seed',
697
+ createdBy: 1,
698
+ createdAt: timestamp,
699
+ },
700
+ },
701
+ 'menu_structures.ADMIN',
702
+ );
703
+ }
704
+
705
+ async function markHistory(db) {
706
+ await db.collection(HISTORY_COLLECTION).updateOne(
707
+ { migrationName: 'core-theme-admin-mongodb-seed' },
708
+ {
709
+ $set: {
710
+ executedAt: new Date(),
711
+ status: 'SUCCESS',
712
+ },
713
+ $setOnInsert: {
714
+ migrationName: 'core-theme-admin-mongodb-seed',
715
+ },
716
+ },
717
+ { upsert: true },
718
+ );
719
+ log(`HISTORY READY : ${HISTORY_COLLECTION}`);
720
+ }
721
+
722
+ async function initializeDatabase({ mongoUri, dbName }) {
723
+ const client = new MongoClient(mongoUri);
724
+ await client.connect();
725
+
726
+ try {
727
+ await client.db('admin').command({ ping: 1 });
728
+ log(`CONNECTED : ${mongoUri}`);
729
+ const db = client.db(dbName);
730
+ log(`DATABASE : ${dbName}`);
731
+
732
+ await createCollections(db);
733
+ await createIndexes(db);
734
+ await seedPermissions(db);
735
+ await seedMenus(db);
736
+ await seedRoles(db);
737
+ const adminUser = await seedAdminUser(db);
738
+ await seedRoleMenuPermissions(db);
739
+ await seedUserMenuPermissions(db, adminUser);
740
+ await seedMenuStructure(db);
741
+ await markHistory(db);
742
+
743
+ log(`SUPER ADMIN READY : ${adminUser.email}`);
744
+ log('DEFAULT LOGIN : admin@example.com / password');
745
+ } finally {
746
+ await client.close();
747
+ }
748
+ }
749
+
750
+ async function main() {
751
+ const loadedEnvFiles = loadProjectEnv();
752
+ const options = parseArgs(process.argv.slice(2));
753
+
754
+ if (process.env.CORE_THEME_SKIP_DB_INIT === '1') {
755
+ log('SKIPPED : CORE_THEME_SKIP_DB_INIT=1');
756
+ return;
757
+ }
758
+
759
+ if (options.postinstall && !process.stdin.isTTY && !options.dbName) {
760
+ log('SKIPPED : npm install is not interactive');
761
+ log('RUN MANUALLY : npx core-theme-init-mongodb --db <databaseName>');
762
+ log('OR INSTALL WITH : MONGO_DB_NAME=<databaseName> npm install @realtek/core-theme');
763
+ return;
764
+ }
765
+
766
+ const mongoUri = options.mongoUri || (
767
+ options.skipPrompt
768
+ ? DEFAULT_MONGO_URI
769
+ : await promptForMongoUri(DEFAULT_MONGO_URI)
770
+ );
771
+ const dbName = options.dbName || (options.skipPrompt ? '' : await promptForDbName(options.dbName));
772
+ if (!mongoUri) {
773
+ throw new Error('MongoDB URI is required. Use --uri <mongoUri> or MONGO_URI.');
774
+ }
775
+ if (!dbName) {
776
+ throw new Error('MongoDB database name is required. Use --db <databaseName> or MONGO_DB_NAME.');
777
+ }
778
+
779
+ log('STARTING MONGODB ADMIN SEED');
780
+ loadedEnvFiles.forEach((path) => log(`ENV LOADED : ${path}`));
781
+ await initializeDatabase({ mongoUri, dbName });
782
+ log('MONGODB ADMIN SEED COMPLETED');
783
+ }
784
+
785
+ main().catch((error) => {
786
+ console.error(`[core-theme:init-mongodb] FAILED : ${error.message}`);
787
+ process.exitCode = 1;
788
+ });