mirror of
https://github.com/samsonjs/immich.git
synced 2026-04-27 15:07:45 +00:00
feat(server,web): update email address (#1186)
* feat: change email * test: change email
This commit is contained in:
parent
fdf51a8855
commit
380f719fd8
10 changed files with 48 additions and 8 deletions
BIN
mobile/openapi/doc/UpdateUserDto.md
generated
BIN
mobile/openapi/doc/UpdateUserDto.md
generated
Binary file not shown.
BIN
mobile/openapi/lib/model/update_user_dto.dart
generated
BIN
mobile/openapi/lib/model/update_user_dto.dart
generated
Binary file not shown.
BIN
mobile/openapi/test/update_user_dto_test.dart
generated
BIN
mobile/openapi/test/update_user_dto_test.dart
generated
Binary file not shown.
|
|
@ -1,9 +1,13 @@
|
||||||
import { IsNotEmpty, IsOptional } from 'class-validator';
|
import { IsEmail, IsNotEmpty, IsOptional } from 'class-validator';
|
||||||
|
|
||||||
export class UpdateUserDto {
|
export class UpdateUserDto {
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
id!: string;
|
id!: string;
|
||||||
|
|
||||||
|
@IsEmail()
|
||||||
|
@IsOptional()
|
||||||
|
email?: string;
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
password?: string;
|
password?: string;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,13 @@ export class UserCore {
|
||||||
throw new BadRequestException('Admin user exists');
|
throw new BadRequestException('Admin user exists');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dto.email) {
|
||||||
|
const duplicate = await this.userRepository.getByEmail(dto.email);
|
||||||
|
if (duplicate && duplicate.id !== id) {
|
||||||
|
throw new BadRequestException('Email already in user by another account');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (dto.password) {
|
if (dto.password) {
|
||||||
dto.password = await hash(dto.password, SALT_ROUNDS);
|
dto.password = await hash(dto.password, SALT_ROUNDS);
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,28 @@ describe('UserService', () => {
|
||||||
await expect(result).rejects.toBeInstanceOf(ForbiddenException);
|
await expect(result).rejects.toBeInstanceOf(ForbiddenException);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should let a user change their email', async () => {
|
||||||
|
const dto = { id: immichUser.id, email: 'updated@test.com' };
|
||||||
|
|
||||||
|
userRepositoryMock.get.mockResolvedValue(immichUser);
|
||||||
|
userRepositoryMock.update.mockResolvedValue(immichUser);
|
||||||
|
|
||||||
|
await sut.updateUser(immichUser, dto);
|
||||||
|
|
||||||
|
expect(userRepositoryMock.update).toHaveBeenCalledWith(immichUser.id, { email: 'updated@test.com' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not let a user change their email to one already in use', async () => {
|
||||||
|
const dto = { id: immichUser.id, email: 'updated@test.com' };
|
||||||
|
|
||||||
|
userRepositoryMock.get.mockResolvedValue(immichUser);
|
||||||
|
userRepositoryMock.getByEmail.mockResolvedValue(adminUser);
|
||||||
|
|
||||||
|
await expect(sut.updateUser(immichUser, dto)).rejects.toBeInstanceOf(BadRequestException);
|
||||||
|
|
||||||
|
expect(userRepositoryMock.update).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it('admin can update any user information', async () => {
|
it('admin can update any user information', async () => {
|
||||||
const update: UpdateUserDto = {
|
const update: UpdateUserDto = {
|
||||||
id: immichUser.id,
|
id: immichUser.id,
|
||||||
|
|
|
||||||
|
|
@ -2400,6 +2400,9 @@
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"email": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
6
web/src/api/open-api/api.ts
generated
6
web/src/api/open-api/api.ts
generated
|
|
@ -1779,6 +1779,12 @@ export interface UpdateUserDto {
|
||||||
* @memberof UpdateUserDto
|
* @memberof UpdateUserDto
|
||||||
*/
|
*/
|
||||||
'id': string;
|
'id': string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof UpdateUserDto
|
||||||
|
*/
|
||||||
|
'email'?: string;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<script lang="ts" context="module">
|
<script lang="ts" context="module">
|
||||||
export enum SettingInputFieldType {
|
export enum SettingInputFieldType {
|
||||||
|
EMAIL = 'email',
|
||||||
TEXT = 'text',
|
TEXT = 'text',
|
||||||
NUMBER = 'number',
|
NUMBER = 'number',
|
||||||
PASSWORD = 'password'
|
PASSWORD = 'password'
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
} from '$lib/components/shared-components/notification/notification';
|
} from '$lib/components/shared-components/notification/notification';
|
||||||
import { api, UserResponseDto } from '@api';
|
import { api, UserResponseDto } from '@api';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
|
import { handleError } from '../../utils/handle-error';
|
||||||
import SettingInputField, {
|
import SettingInputField, {
|
||||||
SettingInputFieldType
|
SettingInputFieldType
|
||||||
} from '../admin-page/settings/setting-input-field.svelte';
|
} from '../admin-page/settings/setting-input-field.svelte';
|
||||||
|
|
@ -15,6 +16,7 @@
|
||||||
try {
|
try {
|
||||||
const { data } = await api.userApi.updateUser({
|
const { data } = await api.userApi.updateUser({
|
||||||
id: user.id,
|
id: user.id,
|
||||||
|
email: user.email,
|
||||||
firstName: user.firstName,
|
firstName: user.firstName,
|
||||||
lastName: user.lastName
|
lastName: user.lastName
|
||||||
});
|
});
|
||||||
|
|
@ -26,11 +28,7 @@
|
||||||
type: NotificationType.Info
|
type: NotificationType.Info
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error [user-profile] [updateProfile]', error);
|
handleError(error, 'Unable to save profile');
|
||||||
notificationController.show({
|
|
||||||
message: 'Unable to save profile',
|
|
||||||
type: NotificationType.Error
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -47,10 +45,9 @@
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SettingInputField
|
<SettingInputField
|
||||||
inputType={SettingInputFieldType.TEXT}
|
inputType={SettingInputFieldType.EMAIL}
|
||||||
label="Email"
|
label="Email"
|
||||||
bind:value={user.email}
|
bind:value={user.email}
|
||||||
disabled={true}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SettingInputField
|
<SettingInputField
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue