Skip to content

Commit dd660fc

Browse files
committed
add admin reset command
1 parent bfa2fb8 commit dd660fc

File tree

8 files changed

+222
-7
lines changed

8 files changed

+222
-7
lines changed

server/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ $ yarn run start:dev
2121
$ yarn run start:prod
2222
```
2323

24+
## Administration Commands
25+
26+
```bash
27+
# Reset admin account (creates or updates with new password)
28+
$ npm run cli:reset-admin
29+
```
30+
2431
## Run tests
2532

2633
```bash

server/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
"dev": "nest start --debug --watch",
1616
"start:prod": "node dist/main",
1717
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
18+
"cli": "ts-node -r tsconfig-paths/register src/cli.ts",
19+
"cli:reset-admin": "npm run cli reset-admin",
1820
"test": "jest",
1921
"test:ci": "jest --ci --reporters=default --reporters=jest-junit",
2022
"test:watch": "jest --watch",
@@ -44,6 +46,7 @@
4446
"@nestjs/serve-static": "^5.0.1",
4547
"@nestjs/swagger": "^11.0.3",
4648
"@nestjs/websockets": "^11.0.7",
49+
"nest-commander": "^3.13.0",
4750
"@octokit/core": "^6.1.3",
4851
"@prisma/client": "^6.9.0",
4952
"@types/bcrypt": "^5.0.2",
@@ -87,6 +90,7 @@
8790
"@types/passport-oauth2": "^1.4.17",
8891
"@types/sshpk": "^1.17.4",
8992
"@types/supertest": "^6.0.2",
93+
"tsconfig-paths": "^4.2.0",
9094
"eslint": "^9.18.0",
9195
"eslint-config-prettier": "^10.0.1",
9296
"eslint-plugin-prettier": "^5.2.2",

server/src/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { DatabaseModule } from './database/database.module';
2424
import { GroupModule } from './groups/groups.module';
2525
import { RolesModule } from './roles/roles.module';
2626
import { TokenModule } from './token/token.module';
27+
import { CliModule } from './cli/cli.module';
2728

2829
@Module({
2930
imports: [
@@ -49,6 +50,7 @@ import { TokenModule } from './token/token.module';
4950
GroupModule,
5051
RolesModule,
5152
TokenModule,
53+
CliModule,
5254
],
5355
controllers: [AppController, TemplatesController],
5456
providers: [AppService, TemplatesService],

server/src/cli.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/usr/bin/env node
2+
import { CommandFactory } from 'nest-commander';
3+
import { CliModule } from './cli/cli.module';
4+
5+
async function bootstrap() {
6+
await CommandFactory.run(CliModule, ['warn', 'error', 'log']);
7+
}
8+
9+
bootstrap();

server/src/cli/cli.module.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { Module } from '@nestjs/common';
2+
import { ResetAdminCommand } from './commands/reset-admin.command';
3+
import { DatabaseService } from '../database/database.service';
4+
5+
@Module({
6+
providers: [
7+
ResetAdminCommand,
8+
DatabaseService,
9+
],
10+
})
11+
export class CliModule {}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Command, CommandRunner } from 'nest-commander';
2+
import { Injectable, Logger } from '@nestjs/common';
3+
import { DatabaseService } from '../../database/database.service';
4+
5+
@Command({ name: 'reset-admin', description: 'Reset the admin account with a new random password' })
6+
@Injectable()
7+
export class ResetAdminCommand extends CommandRunner {
8+
private readonly logger = new Logger(ResetAdminCommand.name);
9+
10+
constructor(private readonly databaseService: DatabaseService) {
11+
super();
12+
}
13+
14+
async run(): Promise<void> {
15+
this.logger.log('Resetting admin account...');
16+
17+
try {
18+
await this.databaseService.resetAdminUser();
19+
this.logger.log('Admin account has been reset successfully');
20+
} catch (error) {
21+
this.logger.error('Failed to reset admin account', error);
22+
process.exit(1);
23+
}
24+
25+
process.exit(0);
26+
}
27+
}

server/src/database/database.service.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,78 @@ export class DatabaseService {
167167
}
168168
}
169169

170+
/**
171+
* Resets the admin user account with a new random password.
172+
* If the admin user doesn't exist, it creates one.
173+
* Prints the new username and password to the console.
174+
* @returns {Promise<void>}
175+
*/
176+
async resetAdminUser(): Promise<void> {
177+
const prisma = new PrismaClient();
178+
const adminUser = process.env.KUBERO_ADMIN_USERNAME || 'admin';
179+
const adminEmail = process.env.KUBERO_ADMIN_EMAIL || 'admin@kubero.dev';
180+
const role = process.env.KUBERO_SYSTEM_USER_ROLE || 'admin';
181+
const userGroups = ['everyone', 'admin'];
182+
183+
try {
184+
// Generate a random password
185+
const plainPassword = crypto
186+
.randomBytes(25)
187+
.toString('base64')
188+
.slice(0, 19);
189+
// Create bcrypt hash
190+
const passwordHash = await bcrypt.hash(plainPassword, 10);
191+
192+
// Check if admin user exists
193+
const existingUser = await prisma.user.findUnique({
194+
where: { id: '2' },
195+
});
196+
197+
if (existingUser) {
198+
// Update existing admin user
199+
await prisma.user.update({
200+
where: { id: '2' },
201+
data: {
202+
password: passwordHash,
203+
updatedAt: new Date(),
204+
},
205+
});
206+
console.log('\n\n\n', 'Admin account has been reset');
207+
} else {
208+
// Create new admin user
209+
await prisma.user.create({
210+
data: {
211+
id: '2',
212+
username: adminUser,
213+
email: adminEmail,
214+
password: passwordHash,
215+
isActive: true,
216+
role: { connect: { name: role } },
217+
userGroups:
218+
userGroups && Array.isArray(userGroups)
219+
? {
220+
connect: userGroups.map((g: any) => ({ name: g })),
221+
}
222+
: undefined,
223+
createdAt: new Date(),
224+
updatedAt: new Date(),
225+
},
226+
});
227+
console.log('\n\n\n', 'New admin account created');
228+
}
229+
230+
console.log(' username: ', adminUser);
231+
console.log(' password: ', plainPassword);
232+
console.log(' email: ', adminEmail, '\n\n\n');
233+
234+
this.logger.log('Admin user reset successfully.');
235+
return;
236+
} catch (error) {
237+
this.logger.error('Failed to reset admin user.', error);
238+
throw error;
239+
}
240+
}
241+
170242
private async migrateLegeacyUsers() {
171243
const prisma = new PrismaClient();
172244

0 commit comments

Comments
 (0)