mirror of
https://github.com/immich-app/immich.git
synced 2025-01-28 06:32:44 +01:00
refactor(server): user create logic (#13728)
This commit is contained in:
parent
fb995816a1
commit
43d18ccc36
4 changed files with 47 additions and 62 deletions
server/src
|
@ -23,7 +23,6 @@ import { OAuthProfile } from 'src/interfaces/oauth.interface';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { isGranted } from 'src/utils/access';
|
import { isGranted } from 'src/utils/access';
|
||||||
import { HumanReadableSize } from 'src/utils/bytes';
|
import { HumanReadableSize } from 'src/utils/bytes';
|
||||||
import { createUser } from 'src/utils/user';
|
|
||||||
|
|
||||||
export interface LoginDetails {
|
export interface LoginDetails {
|
||||||
isSecure: boolean;
|
isSecure: boolean;
|
||||||
|
@ -115,16 +114,13 @@ export class AuthService extends BaseService {
|
||||||
throw new BadRequestException('The server already has an admin');
|
throw new BadRequestException('The server already has an admin');
|
||||||
}
|
}
|
||||||
|
|
||||||
const admin = await createUser(
|
const admin = await this.createUser({
|
||||||
{ userRepo: this.userRepository, cryptoRepo: this.cryptoRepository },
|
isAdmin: true,
|
||||||
{
|
email: dto.email,
|
||||||
isAdmin: true,
|
name: dto.name,
|
||||||
email: dto.email,
|
password: dto.password,
|
||||||
name: dto.name,
|
storageLabel: 'admin',
|
||||||
password: dto.password,
|
});
|
||||||
storageLabel: 'admin',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return mapUserAdmin(admin);
|
return mapUserAdmin(admin);
|
||||||
}
|
}
|
||||||
|
@ -234,16 +230,13 @@ export class AuthService extends BaseService {
|
||||||
});
|
});
|
||||||
|
|
||||||
const userName = profile.name ?? `${profile.given_name || ''} ${profile.family_name || ''}`;
|
const userName = profile.name ?? `${profile.given_name || ''} ${profile.family_name || ''}`;
|
||||||
user = await createUser(
|
user = await this.createUser({
|
||||||
{ userRepo: this.userRepository, cryptoRepo: this.cryptoRepository },
|
name: userName,
|
||||||
{
|
email: profile.email,
|
||||||
name: userName,
|
oauthId: profile.sub,
|
||||||
email: profile.email,
|
quotaSizeInBytes: storageQuota * HumanReadableSize.GiB || null,
|
||||||
oauthId: profile.sub,
|
storageLabel: storageLabel || null,
|
||||||
quotaSizeInBytes: storageQuota * HumanReadableSize.GiB || null,
|
});
|
||||||
storageLabel: storageLabel || null,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.createLoginResponse(user, loginDetails);
|
return this.createLoginResponse(user, loginDetails);
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import { Inject } from '@nestjs/common';
|
import { BadRequestException, Inject } from '@nestjs/common';
|
||||||
|
import sanitize from 'sanitize-filename';
|
||||||
import { SystemConfig } from 'src/config';
|
import { SystemConfig } from 'src/config';
|
||||||
|
import { SALT_ROUNDS } from 'src/constants';
|
||||||
import { StorageCore } from 'src/cores/storage.core';
|
import { StorageCore } from 'src/cores/storage.core';
|
||||||
|
import { UserEntity } from 'src/entities/user.entity';
|
||||||
import { IAccessRepository } from 'src/interfaces/access.interface';
|
import { IAccessRepository } from 'src/interfaces/access.interface';
|
||||||
import { IActivityRepository } from 'src/interfaces/activity.interface';
|
import { IActivityRepository } from 'src/interfaces/activity.interface';
|
||||||
import { IAlbumUserRepository } from 'src/interfaces/album-user.interface';
|
import { IAlbumUserRepository } from 'src/interfaces/album-user.interface';
|
||||||
|
@ -119,4 +122,28 @@ export class BaseService {
|
||||||
checkAccess(request: AccessRequest) {
|
checkAccess(request: AccessRequest) {
|
||||||
return checkAccess(this.accessRepository, request);
|
return checkAccess(this.accessRepository, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async createUser(dto: Partial<UserEntity> & { email: string }): Promise<UserEntity> {
|
||||||
|
const user = await this.userRepository.getByEmail(dto.email);
|
||||||
|
if (user) {
|
||||||
|
throw new BadRequestException('User exists');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dto.isAdmin) {
|
||||||
|
const localAdmin = await this.userRepository.getAdmin();
|
||||||
|
if (!localAdmin) {
|
||||||
|
throw new BadRequestException('The first registered account must the administrator.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload: Partial<UserEntity> = { ...dto };
|
||||||
|
if (payload.password) {
|
||||||
|
payload.password = await this.cryptoRepository.hashBcrypt(payload.password, SALT_ROUNDS);
|
||||||
|
}
|
||||||
|
if (payload.storageLabel) {
|
||||||
|
payload.storageLabel = sanitize(payload.storageLabel.replaceAll('.', ''));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.userRepository.create(payload);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ import { JobName } from 'src/interfaces/job.interface';
|
||||||
import { UserFindOptions } from 'src/interfaces/user.interface';
|
import { UserFindOptions } from 'src/interfaces/user.interface';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { getPreferences, getPreferencesPartial, mergePreferences } from 'src/utils/preferences';
|
import { getPreferences, getPreferencesPartial, mergePreferences } from 'src/utils/preferences';
|
||||||
import { createUser } from 'src/utils/user';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UserAdminService extends BaseService {
|
export class UserAdminService extends BaseService {
|
||||||
|
@ -25,17 +24,18 @@ export class UserAdminService extends BaseService {
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(dto: UserAdminCreateDto): Promise<UserAdminResponseDto> {
|
async create(dto: UserAdminCreateDto): Promise<UserAdminResponseDto> {
|
||||||
const { notify, ...rest } = dto;
|
const { notify, ...userDto } = dto;
|
||||||
const config = await this.getConfig({ withCache: false });
|
const config = await this.getConfig({ withCache: false });
|
||||||
if (!config.oauth.enabled && !rest.password) {
|
if (!config.oauth.enabled && !userDto.password) {
|
||||||
throw new BadRequestException('password is required');
|
throw new BadRequestException('password is required');
|
||||||
}
|
}
|
||||||
const user = await createUser({ userRepo: this.userRepository, cryptoRepo: this.cryptoRepository }, rest);
|
|
||||||
|
const user = await this.createUser(userDto);
|
||||||
|
|
||||||
await this.eventRepository.emit('user.signup', {
|
await this.eventRepository.emit('user.signup', {
|
||||||
notify: !!notify,
|
notify: !!notify,
|
||||||
id: user.id,
|
id: user.id,
|
||||||
tempPassword: user.shouldChangePassword ? rest.password : undefined,
|
tempPassword: user.shouldChangePassword ? userDto.password : undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
return mapUserAdmin(user);
|
return mapUserAdmin(user);
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
import { BadRequestException } from '@nestjs/common';
|
|
||||||
import sanitize from 'sanitize-filename';
|
|
||||||
import { SALT_ROUNDS } from 'src/constants';
|
|
||||||
import { UserEntity } from 'src/entities/user.entity';
|
|
||||||
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
|
|
||||||
import { IUserRepository } from 'src/interfaces/user.interface';
|
|
||||||
|
|
||||||
type RepoDeps = { userRepo: IUserRepository; cryptoRepo: ICryptoRepository };
|
|
||||||
|
|
||||||
export const createUser = async (
|
|
||||||
{ userRepo, cryptoRepo }: RepoDeps,
|
|
||||||
dto: Partial<UserEntity> & { email: string },
|
|
||||||
): Promise<UserEntity> => {
|
|
||||||
const user = await userRepo.getByEmail(dto.email);
|
|
||||||
if (user) {
|
|
||||||
throw new BadRequestException('User exists');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dto.isAdmin) {
|
|
||||||
const localAdmin = await userRepo.getAdmin();
|
|
||||||
if (!localAdmin) {
|
|
||||||
throw new BadRequestException('The first registered account must the administrator.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const payload: Partial<UserEntity> = { ...dto };
|
|
||||||
if (payload.password) {
|
|
||||||
payload.password = await cryptoRepo.hashBcrypt(payload.password, SALT_ROUNDS);
|
|
||||||
}
|
|
||||||
if (payload.storageLabel) {
|
|
||||||
payload.storageLabel = sanitize(payload.storageLabel.replaceAll('.', ''));
|
|
||||||
}
|
|
||||||
|
|
||||||
return userRepo.create(payload);
|
|
||||||
};
|
|
Loading…
Reference in a new issue