immich/server/src/dtos/user.dto.ts

172 lines
4.1 KiB
TypeScript
Raw Normal View History

import { ApiProperty } from '@nestjs/swagger';
import { Transform } from 'class-transformer';
import { IsBoolean, IsEmail, IsEnum, IsNotEmpty, IsNumber, IsPositive, IsString } from 'class-validator';
import { UserAvatarColor } from 'src/entities/user-metadata.entity';
import { UserEntity, UserStatus } from 'src/entities/user.entity';
import { getPreferences } from 'src/utils/preferences';
import { Optional, ValidateBoolean, toEmail, toSanitized } from 'src/validation';
export class UserUpdateMeDto {
@Optional()
@IsEmail({ require_tld: false })
@Transform(toEmail)
email?: string;
// TODO: migrate to the other change password endpoint
@Optional()
@IsNotEmpty()
@IsString()
password?: string;
@Optional()
@IsString()
@IsNotEmpty()
name?: string;
@ValidateBoolean({ optional: true })
memoriesEnabled?: boolean;
@Optional()
@IsEnum(UserAvatarColor)
@ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor })
avatarColor?: UserAvatarColor;
}
export class UserResponseDto {
id!: string;
name!: string;
email!: string;
profileImagePath!: string;
@IsEnum(UserAvatarColor)
@ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor })
avatarColor!: UserAvatarColor;
}
export const mapUser = (entity: UserEntity): UserResponseDto => {
return {
id: entity.id,
email: entity.email,
name: entity.name,
profileImagePath: entity.profileImagePath,
avatarColor: getPreferences(entity).avatar.color,
};
};
export class UserAdminSearchDto {
@ValidateBoolean({ optional: true })
withDeleted?: boolean;
}
export class UserAdminCreateDto {
@IsEmail({ require_tld: false })
@Transform(toEmail)
email!: string;
@IsNotEmpty()
@IsString()
password!: string;
@IsNotEmpty()
@IsString()
name!: string;
@Optional({ nullable: true })
@IsString()
@Transform(toSanitized)
storageLabel?: string | null;
@ValidateBoolean({ optional: true })
memoriesEnabled?: boolean;
@Optional({ nullable: true })
@IsNumber()
@IsPositive()
@ApiProperty({ type: 'integer', format: 'int64' })
quotaSizeInBytes?: number | null;
@ValidateBoolean({ optional: true })
shouldChangePassword?: boolean;
feat(server): email notifications (#8447) * feat(server): add `react-mail` as mail template engine and `nodemailer` * feat(server): add `smtp` related configs to `SystemConfig` * feat(web): add page for SMTP settings * feat(server): add `react-email.adapter` This adapter render the React-Email into HTML and plain/text email. The output is set as the body of the email. * feat(server): add `MailRepository` and `MailService` Allow to use the NestJS-modules-mailer module to send SMTP emails. This is the base transport for the `NotificationRepository` * feat(server): register the job dispatcher and Job for async email This allows to queue email sending jobs for the `EmailService`. * feat(server): add `NotificationRepository` and `NotificationService` This act as a middleware to properly route the notification to the right transport. As POC I've only implemented a simple SMTP transport. * feat(server): add `welcome` email template * feat(server): add the first notification on `createUser` in `UserService` This trigger an event for the `NotificationRepository` that once processes by using the global config and per-user config will carry the payload to the right notification transport. * chore: clean up * chore: clean up web * fix: type errors" * fix package lock * fix mail sending, option to ignore certs * chore: open api * chore: clean up * remove unused import * feat: email feature flag * chore: remove unused interface * small styling --------- Co-authored-by: Jason Rasmussen <jrasm91@gmail.com> Co-authored-by: Daniel Dietzler <mail@ddietzler.dev> Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2024-05-02 16:43:18 +02:00
@Optional()
@IsBoolean()
notify?: boolean;
}
export class UserAdminUpdateDto {
@Optional()
@IsEmail({ require_tld: false })
@Transform(toEmail)
email?: string;
@Optional()
@IsNotEmpty()
@IsString()
password?: string;
@Optional()
@IsString()
@IsNotEmpty()
name?: string;
@Optional({ nullable: true })
@IsString()
@Transform(toSanitized)
storageLabel?: string | null;
@ValidateBoolean({ optional: true })
shouldChangePassword?: boolean;
@ValidateBoolean({ optional: true })
memoriesEnabled?: boolean;
@Optional()
@IsEnum(UserAvatarColor)
@ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor })
avatarColor?: UserAvatarColor;
@Optional({ nullable: true })
@IsNumber()
@IsPositive()
@ApiProperty({ type: 'integer', format: 'int64' })
quotaSizeInBytes?: number | null;
}
export class UserAdminDeleteDto {
@ValidateBoolean({ optional: true })
force?: boolean;
}
export class UserAdminResponseDto extends UserResponseDto {
storageLabel!: string | null;
shouldChangePassword!: boolean;
isAdmin!: boolean;
createdAt!: Date;
deletedAt!: Date | null;
updatedAt!: Date;
oauthId!: string;
memoriesEnabled?: boolean;
@ApiProperty({ type: 'integer', format: 'int64' })
quotaSizeInBytes!: number | null;
@ApiProperty({ type: 'integer', format: 'int64' })
quotaUsageInBytes!: number | null;
@ApiProperty({ enumName: 'UserStatus', enum: UserStatus })
status!: string;
}
export function mapUserAdmin(entity: UserEntity): UserAdminResponseDto {
return {
...mapUser(entity),
storageLabel: entity.storageLabel,
shouldChangePassword: entity.shouldChangePassword,
isAdmin: entity.isAdmin,
createdAt: entity.createdAt,
deletedAt: entity.deletedAt,
updatedAt: entity.updatedAt,
oauthId: entity.oauthId,
memoriesEnabled: getPreferences(entity).memories.enabled,
quotaSizeInBytes: entity.quotaSizeInBytes,
quotaUsageInBytes: entity.quotaUsageInBytes,
status: entity.status,
};
}