mirror of
https://github.com/immich-app/immich
synced 2025-11-14 17:36:12 +00:00
Fix user e2e tests (#194)
* WIP fix user e2e tests The e2e tests still don't seem to work due to migrations not running. Changes: - update user.e2e tests to use new `userService.createUser` method - fix server `typeorm` command to use ORM config - update make test-e2e to re-create database volume every time - add User DTO - update auth.service and user.service to use User DTO - update CreateUserDto making optional properties that are optional * Fix migrations - add missing `.ts` extension to migrations path - update user e2e test for the new returned User resource
This commit is contained in:
parent
5b036067ed
commit
b359dc3cb6
10 changed files with 114 additions and 76 deletions
|
|
@ -7,6 +7,7 @@ import { ImmichJwtService } from '../../modules/immich-jwt/immich-jwt.service';
|
|||
import { JwtPayloadDto } from './dto/jwt-payload.dto';
|
||||
import { SignUpDto } from './dto/sign-up.dto';
|
||||
import * as bcrypt from 'bcrypt';
|
||||
import { mapUser, User } from '../user/response-dto/user';
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
|
|
@ -14,12 +15,24 @@ export class AuthService {
|
|||
@InjectRepository(UserEntity)
|
||||
private userRepository: Repository<UserEntity>,
|
||||
private immichJwtService: ImmichJwtService,
|
||||
) { }
|
||||
) {}
|
||||
|
||||
private async validateUser(loginCredential: LoginCredentialDto): Promise<UserEntity> {
|
||||
const user = await this.userRepository.findOne(
|
||||
{ email: loginCredential.email },
|
||||
{ select: ['id', 'email', 'password', 'salt', 'firstName', 'lastName', 'isAdmin', 'profileImagePath', 'isFirstLoggedIn'] },
|
||||
{
|
||||
select: [
|
||||
'id',
|
||||
'email',
|
||||
'password',
|
||||
'salt',
|
||||
'firstName',
|
||||
'lastName',
|
||||
'isAdmin',
|
||||
'profileImagePath',
|
||||
'isFirstLoggedIn',
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
const isAuthenticated = await this.validatePassword(user.password, loginCredential.password, user.salt);
|
||||
|
|
@ -48,38 +61,29 @@ export class AuthService {
|
|||
lastName: validatedUser.lastName,
|
||||
isAdmin: validatedUser.isAdmin,
|
||||
profileImagePath: validatedUser.profileImagePath,
|
||||
isFirstLogin: validatedUser.isFirstLoggedIn
|
||||
isFirstLogin: validatedUser.isFirstLoggedIn,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public async adminSignUp(signUpCrendential: SignUpDto) {
|
||||
public async adminSignUp(signUpCredential: SignUpDto): Promise<User> {
|
||||
const adminUser = await this.userRepository.findOne({ where: { isAdmin: true } });
|
||||
|
||||
if (adminUser) {
|
||||
throw new BadRequestException('The server already has an admin')
|
||||
throw new BadRequestException('The server already has an admin');
|
||||
}
|
||||
|
||||
|
||||
const newAdminUser = new UserEntity();
|
||||
newAdminUser.email = signUpCrendential.email;
|
||||
newAdminUser.email = signUpCredential.email;
|
||||
newAdminUser.salt = await bcrypt.genSalt();
|
||||
newAdminUser.password = await this.hashPassword(signUpCrendential.password, newAdminUser.salt);
|
||||
newAdminUser.firstName = signUpCrendential.firstName;
|
||||
newAdminUser.lastName = signUpCrendential.lastName;
|
||||
newAdminUser.password = await this.hashPassword(signUpCredential.password, newAdminUser.salt);
|
||||
newAdminUser.firstName = signUpCredential.firstName;
|
||||
newAdminUser.lastName = signUpCredential.lastName;
|
||||
newAdminUser.isAdmin = true;
|
||||
|
||||
try {
|
||||
const savedNewAdminUserUser = await this.userRepository.save(newAdminUser);
|
||||
|
||||
return {
|
||||
id: savedNewAdminUserUser.id,
|
||||
email: savedNewAdminUserUser.email,
|
||||
firstName: savedNewAdminUserUser.firstName,
|
||||
lastName: savedNewAdminUserUser.lastName,
|
||||
createdAt: savedNewAdminUserUser.createdAt,
|
||||
};
|
||||
|
||||
return mapUser(savedNewAdminUserUser);
|
||||
} catch (e) {
|
||||
Logger.error('e', 'signUp');
|
||||
throw new InternalServerErrorException('Failed to register new admin user');
|
||||
|
|
|
|||
|
|
@ -14,14 +14,14 @@ export class CreateUserDto {
|
|||
lastName: string;
|
||||
|
||||
@IsOptional()
|
||||
profileImagePath: string;
|
||||
profileImagePath?: string;
|
||||
|
||||
@IsOptional()
|
||||
isAdmin: boolean;
|
||||
isAdmin?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
isFirstLoggedIn: boolean;
|
||||
isFirstLoggedIn?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
id: string;
|
||||
id?: string;
|
||||
}
|
||||
|
|
|
|||
19
server/src/api-v1/user/response-dto/user.ts
Normal file
19
server/src/api-v1/user/response-dto/user.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import { UserEntity } from '../entities/user.entity';
|
||||
|
||||
export interface User {
|
||||
id: string;
|
||||
email: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export function mapUser(entity: UserEntity): User {
|
||||
return {
|
||||
id: entity.id,
|
||||
email: entity.email,
|
||||
firstName: entity.firstName,
|
||||
lastName: entity.lastName,
|
||||
createdAt: entity.createdAt,
|
||||
};
|
||||
}
|
||||
|
|
@ -6,19 +6,18 @@ import { CreateUserDto } from './dto/create-user.dto';
|
|||
import { UpdateUserDto } from './dto/update-user.dto';
|
||||
import { UserEntity } from './entities/user.entity';
|
||||
import * as bcrypt from 'bcrypt';
|
||||
import sharp from 'sharp';
|
||||
import { createReadStream, unlink, unlinkSync } from 'fs';
|
||||
import { createReadStream } from 'fs';
|
||||
import { Response as Res } from 'express';
|
||||
import { mapUser, User } from './response-dto/user';
|
||||
|
||||
@Injectable()
|
||||
export class UserService {
|
||||
constructor(
|
||||
@InjectRepository(UserEntity)
|
||||
private userRepository: Repository<UserEntity>,
|
||||
) { }
|
||||
) {}
|
||||
|
||||
async getAllUsers(authUser: AuthUserDto, isAll: boolean) {
|
||||
|
||||
if (isAll) {
|
||||
return await this.userRepository.find();
|
||||
}
|
||||
|
|
@ -26,8 +25,8 @@ export class UserService {
|
|||
return await this.userRepository.find({
|
||||
where: { id: Not(authUser.id) },
|
||||
order: {
|
||||
createdAt: 'DESC'
|
||||
}
|
||||
createdAt: 'DESC',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -40,14 +39,12 @@ export class UserService {
|
|||
users = await this.userRepository.find();
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
userCount: users.length
|
||||
}
|
||||
|
||||
userCount: users.length,
|
||||
};
|
||||
}
|
||||
|
||||
async createUser(createUserDto: CreateUserDto) {
|
||||
async createUser(createUserDto: CreateUserDto): Promise<User> {
|
||||
const user = await this.userRepository.findOne({ where: { email: createUserDto.email } });
|
||||
|
||||
if (user) {
|
||||
|
|
@ -62,18 +59,10 @@ export class UserService {
|
|||
newUser.lastName = createUserDto.lastName;
|
||||
newUser.isAdmin = false;
|
||||
|
||||
|
||||
try {
|
||||
const savedUser = await this.userRepository.save(newUser);
|
||||
|
||||
return {
|
||||
id: savedUser.id,
|
||||
email: savedUser.email,
|
||||
firstName: savedUser.firstName,
|
||||
lastName: savedUser.lastName,
|
||||
createdAt: savedUser.createdAt,
|
||||
};
|
||||
|
||||
return mapUser(savedUser);
|
||||
} catch (e) {
|
||||
Logger.error(e, 'Create new user');
|
||||
throw new InternalServerErrorException('Failed to register new user');
|
||||
|
|
@ -84,7 +73,6 @@ export class UserService {
|
|||
return bcrypt.hash(password, salt);
|
||||
}
|
||||
|
||||
|
||||
async updateUser(updateUserDto: UpdateUserDto) {
|
||||
const user = await this.userRepository.findOne(updateUserDto.id);
|
||||
|
||||
|
|
@ -100,10 +88,10 @@ export class UserService {
|
|||
}
|
||||
|
||||
if (updateUserDto.isAdmin) {
|
||||
const adminUser = await this.userRepository.findOne({ where: { isAdmin: true } })
|
||||
const adminUser = await this.userRepository.findOne({ where: { isAdmin: true } });
|
||||
|
||||
if (adminUser) {
|
||||
throw new BadRequestException("Admin user exists")
|
||||
throw new BadRequestException('Admin user exists');
|
||||
}
|
||||
|
||||
user.isAdmin = true;
|
||||
|
|
@ -120,7 +108,6 @@ export class UserService {
|
|||
isAdmin: updatedUser.isAdmin,
|
||||
profileImagePath: updatedUser.profileImagePath,
|
||||
};
|
||||
|
||||
} catch (e) {
|
||||
Logger.error(e, 'Create new user');
|
||||
throw new InternalServerErrorException('Failed to register new user');
|
||||
|
|
@ -130,13 +117,12 @@ export class UserService {
|
|||
async createProfileImage(authUser: AuthUserDto, fileInfo: Express.Multer.File) {
|
||||
try {
|
||||
await this.userRepository.update(authUser.id, {
|
||||
profileImagePath: fileInfo.path
|
||||
})
|
||||
|
||||
profileImagePath: fileInfo.path,
|
||||
});
|
||||
|
||||
return {
|
||||
userId: authUser.id,
|
||||
profileImagePath: fileInfo.path
|
||||
profileImagePath: fileInfo.path,
|
||||
};
|
||||
} catch (e) {
|
||||
Logger.error(e, 'Create User Profile Image');
|
||||
|
|
@ -146,7 +132,7 @@ export class UserService {
|
|||
|
||||
async getUserProfileImage(userId: string, res: Res) {
|
||||
try {
|
||||
const user = await this.userRepository.findOne({ id: userId })
|
||||
const user = await this.userRepository.findOne({ id: userId });
|
||||
|
||||
if (!user.profileImagePath) {
|
||||
// throw new BadRequestException('User does not have a profile image');
|
||||
|
|
@ -157,11 +143,10 @@ export class UserService {
|
|||
res.set({
|
||||
'Content-Type': 'image/jpeg',
|
||||
});
|
||||
const fileStream = createReadStream(user.profileImagePath)
|
||||
const fileStream = createReadStream(user.profileImagePath);
|
||||
return new StreamableFile(fileStream);
|
||||
} catch (e) {
|
||||
console.log("error getting user profile")
|
||||
console.log('error getting user profile');
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,9 +9,11 @@ export const databaseConfig: TypeOrmModuleOptions = {
|
|||
database: process.env.DB_DATABASE_NAME,
|
||||
entities: [__dirname + '/../**/*.entity.{js,ts}'],
|
||||
synchronize: false,
|
||||
migrations: [__dirname + '/../migration/*.js'],
|
||||
migrations: [__dirname + '/../migration/*.{js,ts}'],
|
||||
cli: {
|
||||
migrationsDir: __dirname + '/../migration',
|
||||
},
|
||||
migrationsRun: true,
|
||||
};
|
||||
|
||||
export default databaseConfig;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue