@minecraft-docker/mcctl 1.13.0 → 1.14.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 (33) hide show
  1. package/CHANGELOG.md +22 -6
  2. package/README.md +8 -0
  3. package/dist/commands/console/init.d.ts.map +1 -1
  4. package/dist/commands/console/init.js +51 -38
  5. package/dist/commands/console/init.js.map +1 -1
  6. package/dist/commands/console/service.d.ts.map +1 -1
  7. package/dist/commands/console/service.js +82 -19
  8. package/dist/commands/console/service.js.map +1 -1
  9. package/dist/commands/console/user.d.ts.map +1 -1
  10. package/dist/commands/console/user.js +344 -304
  11. package/dist/commands/console/user.js.map +1 -1
  12. package/dist/commands/create.d.ts +3 -0
  13. package/dist/commands/create.d.ts.map +1 -1
  14. package/dist/commands/create.js +16 -1
  15. package/dist/commands/create.js.map +1 -1
  16. package/dist/index.js +8 -1
  17. package/dist/index.js.map +1 -1
  18. package/dist/infrastructure/adapters/ClackPromptAdapter.d.ts +4 -1
  19. package/dist/infrastructure/adapters/ClackPromptAdapter.d.ts.map +1 -1
  20. package/dist/infrastructure/adapters/ClackPromptAdapter.js +92 -9
  21. package/dist/infrastructure/adapters/ClackPromptAdapter.js.map +1 -1
  22. package/dist/infrastructure/di/container.d.ts +3 -1
  23. package/dist/infrastructure/di/container.d.ts.map +1 -1
  24. package/dist/infrastructure/di/container.js +9 -1
  25. package/dist/infrastructure/di/container.js.map +1 -1
  26. package/dist/lib/admin-config.js +1 -1
  27. package/dist/lib/console-db.d.ts +64 -0
  28. package/dist/lib/console-db.d.ts.map +1 -0
  29. package/dist/lib/console-db.js +240 -0
  30. package/dist/lib/console-db.js.map +1 -0
  31. package/package.json +5 -3
  32. package/scripts/create-server.sh +63 -3
  33. package/templates/.env.example +1 -1
@@ -1,337 +1,377 @@
1
1
  import { Command } from 'commander';
2
2
  import * as p from '@clack/prompts';
3
- import { User, Username, Role, RoleEnum, YamlUserRepository, Paths, colors, } from '@minecraft-docker/shared';
4
- import { resolve } from 'node:path';
3
+ import { join } from 'node:path';
4
+ import { Paths, colors, } from '@minecraft-docker/shared';
5
+ import { ConsoleDatabase } from '../../lib/console-db.js';
5
6
  /**
6
- * Get the user repository instance
7
+ * Get the ConsoleDatabase instance
7
8
  */
8
- function getUserRepository() {
9
+ function getConsoleDb() {
9
10
  const paths = new Paths();
10
- const usersFile = resolve(paths.platform, 'users.yaml');
11
- return new YamlUserRepository(usersFile);
11
+ const dbPath = join(paths.root, 'data', 'mcctl.db');
12
+ const db = new ConsoleDatabase(dbPath);
13
+ db.ensureSchema();
14
+ return db;
12
15
  }
13
16
  /**
14
17
  * List all users
15
18
  */
16
19
  async function listUsers(options) {
17
- const repo = getUserRepository();
18
- const users = await repo.findAll();
19
- if (options.json) {
20
- console.log(JSON.stringify(users.map((u) => u.toJSON()), null, 2));
21
- return;
22
- }
23
- if (users.length === 0) {
24
- console.log(colors.yellow('No users found.'));
25
- console.log(`Run ${colors.cyan('mcctl console init')} to create an admin user.`);
26
- return;
20
+ const db = getConsoleDb();
21
+ try {
22
+ const users = db.findAllUsers();
23
+ if (options.json) {
24
+ console.log(JSON.stringify(users.map((u) => ({
25
+ id: u.id,
26
+ name: u.name,
27
+ email: u.email,
28
+ role: u.role,
29
+ emailVerified: u.emailVerified,
30
+ banned: u.banned,
31
+ createdAt: u.createdAt.toISOString(),
32
+ updatedAt: u.updatedAt.toISOString(),
33
+ })), null, 2));
34
+ return;
35
+ }
36
+ if (users.length === 0) {
37
+ console.log(colors.yellow('No users found.'));
38
+ console.log(`Run ${colors.cyan('mcctl console init')} to create an admin user.`);
39
+ return;
40
+ }
41
+ console.log();
42
+ console.log(colors.bold('Users:'));
43
+ console.log();
44
+ // Table header
45
+ const header = `${colors.dim('Email'.padEnd(30))}${colors.dim('Name'.padEnd(20))}${colors.dim('Role'.padEnd(12))}${colors.dim('Created')}`;
46
+ console.log(header);
47
+ console.log(colors.dim('-'.repeat(75)));
48
+ // Table rows
49
+ for (const user of users) {
50
+ const roleColor = user.role === 'admin' ? colors.red : colors.blue;
51
+ const row = `${user.email.padEnd(30)}${user.name.padEnd(20)}${roleColor(user.role.padEnd(12))}${user.createdAt.toISOString().split('T')[0]}`;
52
+ console.log(row);
53
+ }
54
+ console.log();
55
+ console.log(`Total: ${colors.cyan(users.length.toString())} user(s)`);
27
56
  }
28
- console.log();
29
- console.log(colors.bold('Users:'));
30
- console.log();
31
- // Table header
32
- const header = `${colors.dim('Username'.padEnd(20))}${colors.dim('Role'.padEnd(12))}${colors.dim('Created')}`;
33
- console.log(header);
34
- console.log(colors.dim('-'.repeat(50)));
35
- // Table rows
36
- for (const user of users) {
37
- const roleColor = user.role.isAdmin ? colors.red : colors.blue;
38
- const row = `${user.username.value.padEnd(20)}${roleColor(user.role.value.padEnd(12))}${user.createdAt.toISOString().split('T')[0]}`;
39
- console.log(row);
57
+ finally {
58
+ db.close();
40
59
  }
41
- console.log();
42
- console.log(`Total: ${colors.cyan(users.length.toString())} user(s)`);
43
60
  }
44
61
  /**
45
62
  * Add a new user
46
63
  */
47
- async function addUser(usernameArg, options) {
48
- const repo = getUserRepository();
49
- let username;
50
- let role;
51
- let password;
52
- if (usernameArg && options.role && options.password) {
53
- // CLI mode
54
- username = usernameArg;
55
- role = options.role.toLowerCase();
56
- password = options.password;
57
- }
58
- else {
59
- // Interactive mode
60
- p.intro(colors.cyan('Add New User'));
61
- const result = await p.group({
62
- username: () => p.text({
63
- message: 'Username:',
64
- placeholder: 'operator1',
65
- validate: (value) => {
66
- if (!value || value.trim().length === 0) {
67
- return 'Username is required';
68
- }
69
- if (value.length < 3) {
70
- return 'Username must be at least 3 characters';
71
- }
72
- if (!/^[a-zA-Z0-9_-]+$/.test(value)) {
73
- return 'Username can only contain letters, numbers, underscores, and hyphens';
74
- }
75
- return undefined;
76
- },
77
- }),
78
- role: () => p.select({
79
- message: 'Role:',
80
- options: [
81
- { value: RoleEnum.ADMIN, label: 'Admin - Full access' },
82
- { value: RoleEnum.VIEWER, label: 'Viewer - Read-only access' },
83
- ],
84
- }),
85
- password: () => p.password({
86
- message: 'Password:',
87
- validate: (value) => {
88
- if (!value || value.length < 8) {
89
- return 'Password must be at least 8 characters';
90
- }
91
- return undefined;
64
+ async function addUser(emailArg, options) {
65
+ const db = getConsoleDb();
66
+ try {
67
+ let email;
68
+ let name;
69
+ let role;
70
+ let password;
71
+ if (emailArg && options.role && options.password) {
72
+ // CLI mode
73
+ email = emailArg;
74
+ name = emailArg.split('@')[0] ?? emailArg;
75
+ role = options.role.toLowerCase();
76
+ password = options.password;
77
+ }
78
+ else {
79
+ // Interactive mode
80
+ p.intro(colors.cyan('Add New User'));
81
+ const result = await p.group({
82
+ email: () => p.text({
83
+ message: 'Email:',
84
+ placeholder: 'user@example.com',
85
+ validate: (value) => {
86
+ if (!value || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
87
+ return 'Valid email address is required';
88
+ }
89
+ return undefined;
90
+ },
91
+ }),
92
+ name: () => p.text({
93
+ message: 'Name:',
94
+ placeholder: 'User Name',
95
+ validate: (value) => {
96
+ if (!value || value.trim().length === 0) {
97
+ return 'Name is required';
98
+ }
99
+ return undefined;
100
+ },
101
+ }),
102
+ role: () => p.select({
103
+ message: 'Role:',
104
+ options: [
105
+ { value: 'admin', label: 'Admin - Full access' },
106
+ { value: 'user', label: 'User - Standard access' },
107
+ ],
108
+ }),
109
+ password: () => p.password({
110
+ message: 'Password:',
111
+ validate: (value) => {
112
+ if (!value || value.length < 8) {
113
+ return 'Password must be at least 8 characters';
114
+ }
115
+ return undefined;
116
+ },
117
+ }),
118
+ confirmPassword: () => p.password({
119
+ message: 'Confirm password:',
120
+ }),
121
+ }, {
122
+ onCancel: () => {
123
+ p.cancel('Operation cancelled.');
124
+ process.exit(0);
92
125
  },
93
- }),
94
- confirmPassword: () => p.password({
95
- message: 'Confirm password:',
96
- }),
97
- }, {
98
- onCancel: () => {
99
- p.cancel('Operation cancelled.');
100
- process.exit(0);
101
- },
102
- });
103
- if (result.password !== result.confirmPassword) {
104
- p.cancel('Passwords do not match.');
105
- process.exit(1);
126
+ });
127
+ if (result.password !== result.confirmPassword) {
128
+ p.cancel('Passwords do not match.');
129
+ process.exit(1);
130
+ }
131
+ email = result.email;
132
+ name = result.name;
133
+ role = result.role;
134
+ password = result.password;
106
135
  }
107
- username = result.username;
108
- role = result.role;
109
- password = result.password;
110
- }
111
- // Check if user already exists
112
- try {
113
- const existingUser = await repo.findByUsername(Username.create(username));
136
+ // Check if user already exists
137
+ const existingUser = db.findUserByEmail(email);
114
138
  if (existingUser) {
115
- console.error(colors.red(`User '${username}' already exists.`));
139
+ console.error(colors.red(`User '${email}' already exists.`));
116
140
  process.exit(1);
117
141
  }
142
+ // Create user
143
+ db.createUser({ email, name, password, role });
144
+ if (emailArg) {
145
+ console.log(JSON.stringify({ success: true, email }));
146
+ }
147
+ else {
148
+ p.outro(colors.green(`User '${email}' created successfully!`));
149
+ }
118
150
  }
119
- catch {
120
- // Username validation failed
121
- console.error(colors.red(`Invalid username: ${username}`));
122
- process.exit(1);
123
- }
124
- // Create user
125
- const passwordHash = await repo.hashPassword(password);
126
- const user = User.create({
127
- username: Username.create(username),
128
- passwordHash,
129
- role: Role.create(role),
130
- });
131
- await repo.save(user);
132
- if (usernameArg) {
133
- console.log(JSON.stringify({ success: true, username: user.username.value }));
134
- }
135
- else {
136
- p.outro(colors.green(`User '${username}' created successfully!`));
151
+ finally {
152
+ db.close();
137
153
  }
138
154
  }
139
155
  /**
140
156
  * Remove a user
141
157
  */
142
- async function removeUser(usernameArg, options) {
143
- const repo = getUserRepository();
144
- let username;
145
- if (usernameArg) {
146
- username = usernameArg;
147
- }
148
- else {
149
- // Interactive mode
150
- const users = await repo.findAll();
151
- if (users.length === 0) {
152
- console.error(colors.red('No users found.'));
153
- process.exit(1);
158
+ async function removeUser(emailArg, options) {
159
+ const db = getConsoleDb();
160
+ try {
161
+ let targetUser;
162
+ if (emailArg) {
163
+ const user = db.findUserByEmail(emailArg);
164
+ if (!user) {
165
+ console.error(colors.red(`User '${emailArg}' not found.`));
166
+ process.exit(1);
167
+ }
168
+ targetUser = user;
154
169
  }
155
- const result = await p.select({
156
- message: 'Select user to remove:',
157
- options: users.map((u) => ({
158
- value: u.username.value,
159
- label: `${u.username.value} (${u.role.value})`,
160
- })),
161
- });
162
- if (p.isCancel(result)) {
163
- p.cancel('Operation cancelled.');
164
- process.exit(0);
170
+ else {
171
+ // Interactive mode
172
+ const users = db.findAllUsers();
173
+ if (users.length === 0) {
174
+ console.error(colors.red('No users found.'));
175
+ process.exit(1);
176
+ }
177
+ const result = await p.select({
178
+ message: 'Select user to remove:',
179
+ options: users.map((u) => ({
180
+ value: u.email,
181
+ label: `${u.email} (${u.role})`,
182
+ })),
183
+ });
184
+ if (p.isCancel(result)) {
185
+ p.cancel('Operation cancelled.');
186
+ process.exit(0);
187
+ }
188
+ const user = db.findUserByEmail(result);
189
+ if (!user) {
190
+ console.error(colors.red('User not found.'));
191
+ process.exit(1);
192
+ }
193
+ targetUser = user;
165
194
  }
166
- username = result;
167
- }
168
- // Find user
169
- const user = await repo.findByUsername(Username.create(username));
170
- if (!user) {
171
- console.error(colors.red(`User '${username}' not found.`));
172
- process.exit(1);
173
- }
174
- // Check if this is the last admin
175
- if (user.isAdmin) {
176
- const allUsers = await repo.findAll();
177
- const adminCount = allUsers.filter((u) => u.isAdmin).length;
178
- if (adminCount <= 1) {
179
- console.error(colors.red('Cannot delete the last admin user.'));
180
- process.exit(1);
195
+ // Check if this is the last admin
196
+ if (targetUser.role === 'admin') {
197
+ const allUsers = db.findAllUsers();
198
+ const adminCount = allUsers.filter((u) => u.role === 'admin').length;
199
+ if (adminCount <= 1) {
200
+ console.error(colors.red('Cannot delete the last admin user.'));
201
+ process.exit(1);
202
+ }
181
203
  }
182
- }
183
- // Confirm deletion
184
- if (!options.force) {
185
- const confirmed = await p.confirm({
186
- message: `Are you sure you want to delete user '${username}'?`,
187
- });
188
- if (p.isCancel(confirmed) || !confirmed) {
189
- p.cancel('Operation cancelled.');
190
- process.exit(0);
204
+ // Confirm deletion
205
+ if (!options.force) {
206
+ const confirmed = await p.confirm({
207
+ message: `Are you sure you want to delete user '${targetUser.email}'?`,
208
+ });
209
+ if (p.isCancel(confirmed) || !confirmed) {
210
+ p.cancel('Operation cancelled.');
211
+ process.exit(0);
212
+ }
191
213
  }
214
+ db.deleteUser(targetUser.id);
215
+ console.log(colors.green(`User '${targetUser.email}' deleted successfully.`));
216
+ }
217
+ finally {
218
+ db.close();
192
219
  }
193
- await repo.delete(user.id);
194
- console.log(colors.green(`User '${username}' deleted successfully.`));
195
220
  }
196
221
  /**
197
- * Update a user
222
+ * Update a user's role
198
223
  */
199
- async function updateUser(usernameArg, options) {
200
- const repo = getUserRepository();
201
- let username;
202
- if (usernameArg) {
203
- username = usernameArg;
204
- }
205
- else {
206
- // Interactive mode
207
- const users = await repo.findAll();
208
- if (users.length === 0) {
209
- console.error(colors.red('No users found.'));
210
- process.exit(1);
224
+ async function updateUser(emailArg, options) {
225
+ const db = getConsoleDb();
226
+ try {
227
+ let targetUser;
228
+ if (emailArg) {
229
+ const user = db.findUserByEmail(emailArg);
230
+ if (!user) {
231
+ console.error(colors.red(`User '${emailArg}' not found.`));
232
+ process.exit(1);
233
+ }
234
+ targetUser = user;
211
235
  }
212
- const result = await p.select({
213
- message: 'Select user to update:',
214
- options: users.map((u) => ({
215
- value: u.username.value,
216
- label: `${u.username.value} (${u.role.value})`,
217
- })),
218
- });
219
- if (p.isCancel(result)) {
220
- p.cancel('Operation cancelled.');
221
- process.exit(0);
236
+ else {
237
+ // Interactive mode
238
+ const users = db.findAllUsers();
239
+ if (users.length === 0) {
240
+ console.error(colors.red('No users found.'));
241
+ process.exit(1);
242
+ }
243
+ const result = await p.select({
244
+ message: 'Select user to update:',
245
+ options: users.map((u) => ({
246
+ value: u.email,
247
+ label: `${u.email} (${u.role})`,
248
+ })),
249
+ });
250
+ if (p.isCancel(result)) {
251
+ p.cancel('Operation cancelled.');
252
+ process.exit(0);
253
+ }
254
+ const user = db.findUserByEmail(result);
255
+ if (!user) {
256
+ console.error(colors.red('User not found.'));
257
+ process.exit(1);
258
+ }
259
+ targetUser = user;
222
260
  }
223
- username = result;
224
- }
225
- // Find user
226
- const user = await repo.findByUsername(Username.create(username));
227
- if (!user) {
228
- console.error(colors.red(`User '${username}' not found.`));
229
- process.exit(1);
230
- }
231
- // Update role
232
- let newRole;
233
- if (options.role) {
234
- newRole = options.role.toLowerCase();
235
- }
236
- else {
237
- const result = await p.select({
238
- message: 'New role:',
239
- initialValue: user.role.value,
240
- options: [
241
- { value: RoleEnum.ADMIN, label: 'Admin - Full access' },
242
- { value: RoleEnum.VIEWER, label: 'Viewer - Read-only access' },
243
- ],
244
- });
245
- if (p.isCancel(result)) {
246
- p.cancel('Operation cancelled.');
247
- process.exit(0);
261
+ // Update role
262
+ let newRole;
263
+ if (options.role) {
264
+ newRole = options.role.toLowerCase();
248
265
  }
249
- newRole = result;
250
- }
251
- // Check if this would remove the last admin
252
- if (user.isAdmin && newRole !== RoleEnum.ADMIN) {
253
- const allUsers = await repo.findAll();
254
- const adminCount = allUsers.filter((u) => u.isAdmin).length;
255
- if (adminCount <= 1) {
256
- console.error(colors.red('Cannot change role of the last admin user.'));
257
- process.exit(1);
266
+ else {
267
+ const result = await p.select({
268
+ message: 'New role:',
269
+ initialValue: targetUser.role,
270
+ options: [
271
+ { value: 'admin', label: 'Admin - Full access' },
272
+ { value: 'user', label: 'User - Standard access' },
273
+ ],
274
+ });
275
+ if (p.isCancel(result)) {
276
+ p.cancel('Operation cancelled.');
277
+ process.exit(0);
278
+ }
279
+ newRole = result;
258
280
  }
281
+ // Check if this would remove the last admin
282
+ if (targetUser.role === 'admin' && newRole !== 'admin') {
283
+ const allUsers = db.findAllUsers();
284
+ const adminCount = allUsers.filter((u) => u.role === 'admin').length;
285
+ if (adminCount <= 1) {
286
+ console.error(colors.red('Cannot change role of the last admin user.'));
287
+ process.exit(1);
288
+ }
289
+ }
290
+ db.updateUserRole(targetUser.id, newRole);
291
+ console.log(colors.green(`User '${targetUser.email}' updated to role '${newRole}'.`));
292
+ }
293
+ finally {
294
+ db.close();
259
295
  }
260
- user.updateRole(Role.create(newRole));
261
- await repo.save(user);
262
- console.log(colors.green(`User '${username}' updated to role '${newRole}'.`));
263
296
  }
264
297
  /**
265
298
  * Reset a user's password
266
299
  */
267
- async function resetPassword(usernameArg, options) {
268
- const repo = getUserRepository();
269
- let username;
270
- if (usernameArg) {
271
- username = usernameArg;
272
- }
273
- else {
274
- // Interactive mode
275
- const users = await repo.findAll();
276
- if (users.length === 0) {
277
- console.error(colors.red('No users found.'));
278
- process.exit(1);
279
- }
280
- const result = await p.select({
281
- message: 'Select user to reset password:',
282
- options: users.map((u) => ({
283
- value: u.username.value,
284
- label: `${u.username.value} (${u.role.value})`,
285
- })),
286
- });
287
- if (p.isCancel(result)) {
288
- p.cancel('Operation cancelled.');
289
- process.exit(0);
300
+ async function resetPassword(emailArg, options) {
301
+ const db = getConsoleDb();
302
+ try {
303
+ let targetUser;
304
+ if (emailArg) {
305
+ const user = db.findUserByEmail(emailArg);
306
+ if (!user) {
307
+ console.error(colors.red(`User '${emailArg}' not found.`));
308
+ process.exit(1);
309
+ }
310
+ targetUser = user;
290
311
  }
291
- username = result;
292
- }
293
- // Find user
294
- const user = await repo.findByUsername(Username.create(username));
295
- if (!user) {
296
- console.error(colors.red(`User '${username}' not found.`));
297
- process.exit(1);
298
- }
299
- // Get new password
300
- let newPassword;
301
- if (options.password) {
302
- newPassword = options.password;
303
- }
304
- else {
305
- const result = await p.group({
306
- password: () => p.password({
307
- message: 'New password:',
308
- validate: (value) => {
309
- if (!value || value.length < 8) {
310
- return 'Password must be at least 8 characters';
311
- }
312
- return undefined;
313
- },
314
- }),
315
- confirmPassword: () => p.password({
316
- message: 'Confirm password:',
317
- }),
318
- }, {
319
- onCancel: () => {
312
+ else {
313
+ // Interactive mode
314
+ const users = db.findAllUsers();
315
+ if (users.length === 0) {
316
+ console.error(colors.red('No users found.'));
317
+ process.exit(1);
318
+ }
319
+ const result = await p.select({
320
+ message: 'Select user to reset password:',
321
+ options: users.map((u) => ({
322
+ value: u.email,
323
+ label: `${u.email} (${u.role})`,
324
+ })),
325
+ });
326
+ if (p.isCancel(result)) {
320
327
  p.cancel('Operation cancelled.');
321
328
  process.exit(0);
322
- },
323
- });
324
- if (result.password !== result.confirmPassword) {
325
- p.cancel('Passwords do not match.');
326
- process.exit(1);
329
+ }
330
+ const user = db.findUserByEmail(result);
331
+ if (!user) {
332
+ console.error(colors.red('User not found.'));
333
+ process.exit(1);
334
+ }
335
+ targetUser = user;
327
336
  }
328
- newPassword = result.password;
337
+ // Get new password
338
+ let newPassword;
339
+ if (options.password) {
340
+ newPassword = options.password;
341
+ }
342
+ else {
343
+ const result = await p.group({
344
+ password: () => p.password({
345
+ message: 'New password:',
346
+ validate: (value) => {
347
+ if (!value || value.length < 8) {
348
+ return 'Password must be at least 8 characters';
349
+ }
350
+ return undefined;
351
+ },
352
+ }),
353
+ confirmPassword: () => p.password({
354
+ message: 'Confirm password:',
355
+ }),
356
+ }, {
357
+ onCancel: () => {
358
+ p.cancel('Operation cancelled.');
359
+ process.exit(0);
360
+ },
361
+ });
362
+ if (result.password !== result.confirmPassword) {
363
+ p.cancel('Passwords do not match.');
364
+ process.exit(1);
365
+ }
366
+ newPassword = result.password;
367
+ }
368
+ // Update password
369
+ db.updatePassword(targetUser.id, newPassword);
370
+ console.log(colors.green(`Password for '${targetUser.email}' has been reset.`));
371
+ }
372
+ finally {
373
+ db.close();
329
374
  }
330
- // Update password
331
- const passwordHash = await repo.hashPassword(newPassword);
332
- user.updatePasswordHash(passwordHash);
333
- await repo.save(user);
334
- console.log(colors.green(`Password for '${username}' has been reset.`));
335
375
  }
336
376
  /**
337
377
  * Create console user command
@@ -344,14 +384,14 @@ Examples:
344
384
  $ mcctl console user list List all users
345
385
  $ mcctl console user list --json List users in JSON format
346
386
  $ mcctl console user add Add user (interactive)
347
- $ mcctl console user add operator1 --role viewer --password "secret"
387
+ $ mcctl console user add user@example.com --role admin --password "secret"
348
388
  $ mcctl console user remove Remove user (interactive)
349
- $ mcctl console user remove operator1 Remove user directly
350
- $ mcctl console user remove operator1 --force Remove without confirmation
389
+ $ mcctl console user remove user@example.com Remove user directly
390
+ $ mcctl console user remove user@example.com --force Remove without confirmation
351
391
  $ mcctl console user update Update user (interactive)
352
- $ mcctl console user update operator1 --role admin
392
+ $ mcctl console user update user@example.com --role admin
353
393
  $ mcctl console user reset-password Reset password (interactive)
354
- $ mcctl console user reset-password operator1
394
+ $ mcctl console user reset-password user@example.com
355
395
  `);
356
396
  // list subcommand
357
397
  cmd
@@ -369,13 +409,13 @@ Examples:
369
409
  });
370
410
  // add subcommand
371
411
  cmd
372
- .command('add [username]')
412
+ .command('add [email]')
373
413
  .description('Add a new user')
374
- .option('--role <role>', 'User role (admin, viewer)')
414
+ .option('--role <role>', 'User role (admin, user)')
375
415
  .option('--password <password>', 'User password')
376
- .action(async (username, options) => {
416
+ .action(async (email, options) => {
377
417
  try {
378
- await addUser(username, options);
418
+ await addUser(email, options);
379
419
  }
380
420
  catch (error) {
381
421
  console.error(colors.red(`Error: ${error.message}`));
@@ -384,12 +424,12 @@ Examples:
384
424
  });
385
425
  // remove subcommand
386
426
  cmd
387
- .command('remove [username]')
427
+ .command('remove [email]')
388
428
  .description('Remove a user')
389
429
  .option('--force', 'Skip confirmation')
390
- .action(async (username, options) => {
430
+ .action(async (email, options) => {
391
431
  try {
392
- await removeUser(username, options);
432
+ await removeUser(email, options);
393
433
  }
394
434
  catch (error) {
395
435
  console.error(colors.red(`Error: ${error.message}`));
@@ -398,12 +438,12 @@ Examples:
398
438
  });
399
439
  // update subcommand
400
440
  cmd
401
- .command('update [username]')
441
+ .command('update [email]')
402
442
  .description('Update a user')
403
- .option('--role <role>', 'New role (admin, viewer)')
404
- .action(async (username, options) => {
443
+ .option('--role <role>', 'New role (admin, user)')
444
+ .action(async (email, options) => {
405
445
  try {
406
- await updateUser(username, options);
446
+ await updateUser(email, options);
407
447
  }
408
448
  catch (error) {
409
449
  console.error(colors.red(`Error: ${error.message}`));
@@ -412,12 +452,12 @@ Examples:
412
452
  });
413
453
  // reset-password subcommand
414
454
  cmd
415
- .command('reset-password [username]')
455
+ .command('reset-password [email]')
416
456
  .description('Reset a user password')
417
457
  .option('--password <password>', 'New password (for non-interactive use)')
418
- .action(async (username, options) => {
458
+ .action(async (email, options) => {
419
459
  try {
420
- await resetPassword(username, options);
460
+ await resetPassword(email, options);
421
461
  }
422
462
  catch (error) {
423
463
  console.error(colors.red(`Error: ${error.message}`));