mirror of
				https://github.com/KevinMidboe/immich.git
				synced 2025-10-29 17:40:28 +00:00 
			
		
		
		
	feat(server): reset admin password using cli command in the server container (#928)
This commit is contained in:
		@@ -26,6 +26,7 @@ RUN mkdir -p /usr/src/app/dist \
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
COPY --from=builder /usr/src/app/node_modules ./node_modules
 | 
					COPY --from=builder /usr/src/app/node_modules ./node_modules
 | 
				
			||||||
COPY --from=builder /usr/src/app/dist ./dist
 | 
					COPY --from=builder /usr/src/app/dist ./dist
 | 
				
			||||||
 | 
					COPY --from=builder /usr/src/app/bin ./bin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RUN npm prune --production
 | 
					RUN npm prune --production
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										11
									
								
								server/apps/cli/src/app.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								server/apps/cli/src/app.module.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					import { DatabaseModule } from '@app/database';
 | 
				
			||||||
 | 
					import { UserEntity } from '@app/database/entities/user.entity';
 | 
				
			||||||
 | 
					import { Module } from '@nestjs/common';
 | 
				
			||||||
 | 
					import { TypeOrmModule } from '@nestjs/typeorm';
 | 
				
			||||||
 | 
					import { PromptPasswordQuestions, ResetAdminPasswordCommand } from './commands/reset-admin-password.command';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Module({
 | 
				
			||||||
 | 
					  imports: [DatabaseModule, TypeOrmModule.forFeature([UserEntity])],
 | 
				
			||||||
 | 
					  providers: [ResetAdminPasswordCommand, PromptPasswordQuestions],
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class AppModule {}
 | 
				
			||||||
							
								
								
									
										52
									
								
								server/apps/cli/src/commands/reset-admin-password.command.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								server/apps/cli/src/commands/reset-admin-password.command.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
				
			|||||||
 | 
					import { UserEntity } from '@app/database/entities/user.entity';
 | 
				
			||||||
 | 
					import { InjectRepository } from '@nestjs/typeorm';
 | 
				
			||||||
 | 
					import bcrypt from 'bcrypt';
 | 
				
			||||||
 | 
					import { Command, CommandRunner, InquirerService, Question, QuestionSet } from 'nest-commander';
 | 
				
			||||||
 | 
					import { randomBytes } from 'node:crypto';
 | 
				
			||||||
 | 
					import { Repository } from 'typeorm';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Command({
 | 
				
			||||||
 | 
					  name: 'reset-admin-password',
 | 
				
			||||||
 | 
					  description: 'Reset the admin password',
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					export class ResetAdminPasswordCommand extends CommandRunner {
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
 | 
					    private readonly inquirer: InquirerService,
 | 
				
			||||||
 | 
					    @InjectRepository(UserEntity) private userRepository: Repository<UserEntity>,
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
 | 
					    super();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async run(): Promise<void> {
 | 
				
			||||||
 | 
					    let { password } = await this.inquirer.ask<{ password: string }>('prompt-password', undefined);
 | 
				
			||||||
 | 
					    password = password || randomBytes(24).toString('base64').replace(/\W/g, '');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const salt = await bcrypt.genSalt();
 | 
				
			||||||
 | 
					    const hashedPassword = await bcrypt.hash(password, salt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const user = await this.userRepository.findOne({ where: { isAdmin: true } });
 | 
				
			||||||
 | 
					    if (!user) {
 | 
				
			||||||
 | 
					      console.log('Unable to reset password: no admin user.');
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    user.salt = salt;
 | 
				
			||||||
 | 
					    user.password = hashedPassword;
 | 
				
			||||||
 | 
					    user.shouldChangePassword = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await this.userRepository.save(user);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    console.log(`New password:\n${password}`);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@QuestionSet({ name: 'prompt-password' })
 | 
				
			||||||
 | 
					export class PromptPasswordQuestions {
 | 
				
			||||||
 | 
					  @Question({
 | 
				
			||||||
 | 
					    message: 'Please choose a new password (optional)',
 | 
				
			||||||
 | 
					    name: 'password',
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  parsePassword(val: string) {
 | 
				
			||||||
 | 
					    return val;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										7
									
								
								server/apps/cli/src/immich.ts
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										7
									
								
								server/apps/cli/src/immich.ts
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					import { CommandFactory } from 'nest-commander';
 | 
				
			||||||
 | 
					import { AppModule } from './app.module';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function bootstrap() {
 | 
				
			||||||
 | 
					  await CommandFactory.run(AppModule, ['warn', 'error']);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					bootstrap();
 | 
				
			||||||
							
								
								
									
										9
									
								
								server/apps/cli/tsconfig.app.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								server/apps/cli/tsconfig.app.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					  "extends": "../../tsconfig.json",
 | 
				
			||||||
 | 
					  "compilerOptions": {
 | 
				
			||||||
 | 
					    "declaration": false,
 | 
				
			||||||
 | 
					    "outDir": "../../dist/apps/cli"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  "include": ["src/**/*", "../../libs/**/*"],
 | 
				
			||||||
 | 
					  "exclude": ["node_modules", "dist", "test", "**/*spec.ts"]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										1
									
								
								server/bin/cli.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										1
									
								
								server/bin/cli.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					node ./dist/apps/cli/apps/cli/src/immich "$@"
 | 
				
			||||||
@@ -6,13 +6,15 @@
 | 
				
			|||||||
  "compilerOptions": {
 | 
					  "compilerOptions": {
 | 
				
			||||||
    "webpack": false,
 | 
					    "webpack": false,
 | 
				
			||||||
    "tsConfigPath": "apps/immich/tsconfig.app.json",
 | 
					    "tsConfigPath": "apps/immich/tsconfig.app.json",
 | 
				
			||||||
    "plugins": [  {
 | 
					    "plugins": [
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
        "name": "@nestjs/swagger",
 | 
					        "name": "@nestjs/swagger",
 | 
				
			||||||
        "options": {
 | 
					        "options": {
 | 
				
			||||||
          "classValidatorShim": false,
 | 
					          "classValidatorShim": false,
 | 
				
			||||||
          "introspectComments": true
 | 
					          "introspectComments": true
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }]
 | 
					      }
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "projects": {
 | 
					  "projects": {
 | 
				
			||||||
    "immich": {
 | 
					    "immich": {
 | 
				
			||||||
@@ -33,6 +35,15 @@
 | 
				
			|||||||
        "tsConfigPath": "apps/microservices/tsconfig.app.json"
 | 
					        "tsConfigPath": "apps/microservices/tsconfig.app.json"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "cli": {
 | 
				
			||||||
 | 
					      "type": "application",
 | 
				
			||||||
 | 
					      "root": "apps/cli",
 | 
				
			||||||
 | 
					      "entryFile": "immich",
 | 
				
			||||||
 | 
					      "sourceRoot": "apps/cli/src",
 | 
				
			||||||
 | 
					      "compilerOptions": {
 | 
				
			||||||
 | 
					        "tsConfigPath": "apps/cli/tsconfig.app.json"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "common": {
 | 
					    "common": {
 | 
				
			||||||
      "type": "library",
 | 
					      "type": "library",
 | 
				
			||||||
      "root": "libs/common",
 | 
					      "root": "libs/common",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										465
									
								
								server/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										465
									
								
								server/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -5,9 +5,12 @@
 | 
				
			|||||||
  "author": "",
 | 
					  "author": "",
 | 
				
			||||||
  "private": true,
 | 
					  "private": true,
 | 
				
			||||||
  "license": "UNLICENSED",
 | 
					  "license": "UNLICENSED",
 | 
				
			||||||
 | 
					  "bin": {
 | 
				
			||||||
 | 
					    "immich": "./bin/cli.sh"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
  "scripts": {
 | 
					  "scripts": {
 | 
				
			||||||
    "prebuild": "rimraf dist",
 | 
					    "prebuild": "rimraf dist",
 | 
				
			||||||
    "build": "nest build immich && nest build microservices",
 | 
					    "build": "nest build immich && nest build microservices && nest build cli",
 | 
				
			||||||
    "format": "prettier --write \"apps/**/*.ts\" \"libs/**/*.ts\"",
 | 
					    "format": "prettier --write \"apps/**/*.ts\" \"libs/**/*.ts\"",
 | 
				
			||||||
    "start": "nest start",
 | 
					    "start": "nest start",
 | 
				
			||||||
    "start:dev": "nest start --watch",
 | 
					    "start:dev": "nest start --watch",
 | 
				
			||||||
@@ -59,6 +62,7 @@
 | 
				
			|||||||
    "local-reverse-geocoder": "^0.12.5",
 | 
					    "local-reverse-geocoder": "^0.12.5",
 | 
				
			||||||
    "lodash": "^4.17.21",
 | 
					    "lodash": "^4.17.21",
 | 
				
			||||||
    "luxon": "^3.0.3",
 | 
					    "luxon": "^3.0.3",
 | 
				
			||||||
 | 
					    "nest-commander": "^3.3.0",
 | 
				
			||||||
    "passport": "^0.6.0",
 | 
					    "passport": "^0.6.0",
 | 
				
			||||||
    "passport-jwt": "^4.0.0",
 | 
					    "passport-jwt": "^4.0.0",
 | 
				
			||||||
    "pg": "^8.7.1",
 | 
					    "pg": "^8.7.1",
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user