feature/cli-reset-password (#4585)

* feat: add cli to reset password

* chore: add information for password reset command

* fix: add information for password reset command
This commit is contained in:
Ong Chung Yau
2025-06-07 02:16:16 +08:00
committed by GitHub
parent 2cd8db0c53
commit 6dcb65cedb
3 changed files with 85 additions and 2 deletions
+3
View File
@@ -20,6 +20,9 @@
"start-worker": "run-script-os",
"start-worker:windows": "cd packages/server/bin && run worker",
"start-worker:default": "cd packages/server/bin && ./run worker",
"user": "run-script-os",
"user:windows": "cd packages/server/bin && run user",
"user:default": "cd packages/server/bin && ./run user",
"test": "turbo run test",
"clean": "pnpm --filter \"./packages/**\" clean",
"nuke": "pnpm --filter \"./packages/**\" nuke && rimraf node_modules .turbo",
+2 -2
View File
@@ -1,6 +1,6 @@
import { Command, Flags } from '@oclif/core'
import path from 'path'
import dotenv from 'dotenv'
import path from 'path'
import logger from '../utils/logger'
dotenv.config({ path: path.join(__dirname, '..', '..', '.env'), override: true })
@@ -120,7 +120,7 @@ export abstract class BaseCommand extends Command {
logger.error('unhandledRejection: ', err)
})
const { flags } = await this.parse(BaseCommand)
const { flags } = await this.parse(this.constructor as any)
if (flags.PORT) process.env.PORT = flags.PORT
if (flags.CORS_ORIGINS) process.env.CORS_ORIGINS = flags.CORS_ORIGINS
if (flags.IFRAME_ORIGINS) process.env.IFRAME_ORIGINS = flags.IFRAME_ORIGINS
+80
View File
@@ -0,0 +1,80 @@
import { Args } from '@oclif/core'
import { QueryRunner } from 'typeorm'
import * as DataSource from '../DataSource'
import { User } from '../enterprise/database/entities/user.entity'
import { getHash } from '../enterprise/utils/encryption.util'
import { isInvalidPassword } from '../enterprise/utils/validation.util'
import logger from '../utils/logger'
import { BaseCommand } from './base'
export default class user extends BaseCommand {
static args = {
email: Args.string({
description: 'Email address to search for in the user database'
}),
password: Args.string({
description: 'New password for that user'
})
}
async run(): Promise<void> {
const { args } = await this.parse(user)
let queryRunner: QueryRunner | undefined
try {
logger.info('Initializing DataSource')
const dataSource = await DataSource.getDataSource()
await dataSource.initialize()
queryRunner = dataSource.createQueryRunner()
await queryRunner.connect()
if (args.email && args.password) {
logger.info('Running resetPassword')
await this.resetPassword(queryRunner, args.email, args.password)
} else {
logger.info('Running listUserEmails')
await this.listUserEmails(queryRunner)
}
} catch (error) {
logger.error(error)
} finally {
if (queryRunner && !queryRunner.isReleased) await queryRunner.release()
await this.gracefullyExit()
}
}
async listUserEmails(queryRunner: QueryRunner) {
logger.info('Listing all user emails')
const users = await queryRunner.manager.find(User, {
select: ['email']
})
const emails = users.map((user) => user.email)
logger.info(`Email addresses: ${emails.join(', ')}`)
logger.info(`Email count: ${emails.length}`)
logger.info('To reset user password, run the following command: pnpm user --email "myEmail" --password "myPassword"')
}
async resetPassword(queryRunner: QueryRunner, email: string, password: string) {
logger.info(`Finding user by email: ${email}`)
const user = await queryRunner.manager.findOne(User, {
where: { email }
})
if (!user) throw new Error(`User not found with email: ${email}`)
if (isInvalidPassword(password)) {
const errors = []
if (!/(?=.*[a-z])/.test(password)) errors.push('at least one lowercase letter')
if (!/(?=.*[A-Z])/.test(password)) errors.push('at least one uppercase letter')
if (!/(?=.*\d)/.test(password)) errors.push('at least one number')
if (!/(?=.*[^a-zA-Z0-9])/.test(password)) errors.push('at least one special character')
if (password.length < 8) errors.push('minimum length of 8 characters')
throw new Error(`Invalid password: Must contain ${errors.join(', ')}`)
}
user.credential = getHash(password)
await queryRunner.manager.save(user)
logger.info(`Password reset for user: ${email}`)
}
}