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