refactor(server): auth dtos (#4881)

* refactor: auth dtos

* chore: open api
This commit is contained in:
Jason Rasmussen 2023-11-09 10:14:15 -05:00 committed by GitHub
parent 5c602bf4d4
commit 5423f1c25b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 187 additions and 657 deletions

View file

@ -209,43 +209,6 @@ export interface AddUsersDto {
*/
'sharedUserIds': Array<string>;
}
/**
*
* @export
* @interface AdminSignupResponseDto
*/
export interface AdminSignupResponseDto {
/**
*
* @type {string}
* @memberof AdminSignupResponseDto
*/
'createdAt': string;
/**
*
* @type {string}
* @memberof AdminSignupResponseDto
*/
'email': string;
/**
*
* @type {string}
* @memberof AdminSignupResponseDto
*/
'firstName': string;
/**
*
* @type {string}
* @memberof AdminSignupResponseDto
*/
'id': string;
/**
*
* @type {string}
* @memberof AdminSignupResponseDto
*/
'lastName': string;
}
/**
*
* @export
@ -10509,7 +10472,7 @@ export const AuthenticationApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async signUpAdmin(signUpDto: SignUpDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<AdminSignupResponseDto>> {
async signUpAdmin(signUpDto: SignUpDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<UserResponseDto>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.signUpAdmin(signUpDto, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
@ -10589,7 +10552,7 @@ export const AuthenticationApiFactory = function (configuration?: Configuration,
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
signUpAdmin(requestParameters: AuthenticationApiSignUpAdminRequest, options?: AxiosRequestConfig): AxiosPromise<AdminSignupResponseDto> {
signUpAdmin(requestParameters: AuthenticationApiSignUpAdminRequest, options?: AxiosRequestConfig): AxiosPromise<UserResponseDto> {
return localVarFp.signUpAdmin(requestParameters.signUpDto, options).then((request) => request(axios, basePath));
},
/**

View file

@ -13,7 +13,6 @@ doc/ActivityCreateDto.md
doc/ActivityResponseDto.md
doc/ActivityStatisticsResponseDto.md
doc/AddUsersDto.md
doc/AdminSignupResponseDto.md
doc/AlbumApi.md
doc/AlbumCountResponseDto.md
doc/AlbumResponseDto.md
@ -198,7 +197,6 @@ lib/model/activity_create_dto.dart
lib/model/activity_response_dto.dart
lib/model/activity_statistics_response_dto.dart
lib/model/add_users_dto.dart
lib/model/admin_signup_response_dto.dart
lib/model/album_count_response_dto.dart
lib/model/album_response_dto.dart
lib/model/all_job_status_response_dto.dart
@ -347,7 +345,6 @@ test/activity_create_dto_test.dart
test/activity_response_dto_test.dart
test/activity_statistics_response_dto_test.dart
test/add_users_dto_test.dart
test/admin_signup_response_dto_test.dart
test/album_api_test.dart
test/album_count_response_dto_test.dart
test/album_response_dto_test.dart

View file

@ -212,7 +212,6 @@ Class | Method | HTTP request | Description
- [ActivityResponseDto](doc//ActivityResponseDto.md)
- [ActivityStatisticsResponseDto](doc//ActivityStatisticsResponseDto.md)
- [AddUsersDto](doc//AddUsersDto.md)
- [AdminSignupResponseDto](doc//AdminSignupResponseDto.md)
- [AlbumCountResponseDto](doc//AlbumCountResponseDto.md)
- [AlbumResponseDto](doc//AlbumResponseDto.md)
- [AllJobStatusResponseDto](doc//AllJobStatusResponseDto.md)

View file

@ -1,19 +0,0 @@
# openapi.model.AdminSignupResponseDto
## Load the model package
```dart
import 'package:openapi/api.dart';
```
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**createdAt** | [**DateTime**](DateTime.md) | |
**email** | **String** | |
**firstName** | **String** | |
**id** | **String** | |
**lastName** | **String** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View file

@ -322,7 +322,7 @@ void (empty response body)
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **signUpAdmin**
> AdminSignupResponseDto signUpAdmin(signUpDto)
> UserResponseDto signUpAdmin(signUpDto)
@ -349,7 +349,7 @@ Name | Type | Description | Notes
### Return type
[**AdminSignupResponseDto**](AdminSignupResponseDto.md)
[**UserResponseDto**](UserResponseDto.md)
### Authorization

View file

@ -8,14 +8,14 @@ import 'package:openapi/api.dart';
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**accessToken** | **String** | | [readonly]
**firstName** | **String** | | [readonly]
**isAdmin** | **bool** | | [readonly]
**lastName** | **String** | | [readonly]
**profileImagePath** | **String** | | [readonly]
**shouldChangePassword** | **bool** | | [readonly]
**userEmail** | **String** | | [readonly]
**userId** | **String** | | [readonly]
**accessToken** | **String** | |
**firstName** | **String** | |
**isAdmin** | **bool** | |
**lastName** | **String** | |
**profileImagePath** | **String** | |
**shouldChangePassword** | **bool** | |
**userEmail** | **String** | |
**userId** | **String** | |
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View file

@ -54,7 +54,6 @@ part 'model/activity_create_dto.dart';
part 'model/activity_response_dto.dart';
part 'model/activity_statistics_response_dto.dart';
part 'model/add_users_dto.dart';
part 'model/admin_signup_response_dto.dart';
part 'model/album_count_response_dto.dart';
part 'model/album_response_dto.dart';
part 'model/all_job_status_response_dto.dart';

View file

@ -300,7 +300,7 @@ class AuthenticationApi {
/// Parameters:
///
/// * [SignUpDto] signUpDto (required):
Future<AdminSignupResponseDto?> signUpAdmin(SignUpDto signUpDto,) async {
Future<UserResponseDto?> signUpAdmin(SignUpDto signUpDto,) async {
final response = await signUpAdminWithHttpInfo(signUpDto,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
@ -309,7 +309,7 @@ class AuthenticationApi {
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'AdminSignupResponseDto',) as AdminSignupResponseDto;
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserResponseDto',) as UserResponseDto;
}
return null;

View file

@ -197,8 +197,6 @@ class ApiClient {
return ActivityStatisticsResponseDto.fromJson(value);
case 'AddUsersDto':
return AddUsersDto.fromJson(value);
case 'AdminSignupResponseDto':
return AdminSignupResponseDto.fromJson(value);
case 'AlbumCountResponseDto':
return AlbumCountResponseDto.fromJson(value);
case 'AlbumResponseDto':

View file

@ -1,130 +0,0 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.12
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
part of openapi.api;
class AdminSignupResponseDto {
/// Returns a new [AdminSignupResponseDto] instance.
AdminSignupResponseDto({
required this.createdAt,
required this.email,
required this.firstName,
required this.id,
required this.lastName,
});
DateTime createdAt;
String email;
String firstName;
String id;
String lastName;
@override
bool operator ==(Object other) => identical(this, other) || other is AdminSignupResponseDto &&
other.createdAt == createdAt &&
other.email == email &&
other.firstName == firstName &&
other.id == id &&
other.lastName == lastName;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(createdAt.hashCode) +
(email.hashCode) +
(firstName.hashCode) +
(id.hashCode) +
(lastName.hashCode);
@override
String toString() => 'AdminSignupResponseDto[createdAt=$createdAt, email=$email, firstName=$firstName, id=$id, lastName=$lastName]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'createdAt'] = this.createdAt.toUtc().toIso8601String();
json[r'email'] = this.email;
json[r'firstName'] = this.firstName;
json[r'id'] = this.id;
json[r'lastName'] = this.lastName;
return json;
}
/// Returns a new [AdminSignupResponseDto] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static AdminSignupResponseDto? fromJson(dynamic value) {
if (value is Map) {
final json = value.cast<String, dynamic>();
return AdminSignupResponseDto(
createdAt: mapDateTime(json, r'createdAt', '')!,
email: mapValueOfType<String>(json, r'email')!,
firstName: mapValueOfType<String>(json, r'firstName')!,
id: mapValueOfType<String>(json, r'id')!,
lastName: mapValueOfType<String>(json, r'lastName')!,
);
}
return null;
}
static List<AdminSignupResponseDto> listFromJson(dynamic json, {bool growable = false,}) {
final result = <AdminSignupResponseDto>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = AdminSignupResponseDto.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
static Map<String, AdminSignupResponseDto> mapFromJson(dynamic json) {
final map = <String, AdminSignupResponseDto>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = AdminSignupResponseDto.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
}
}
return map;
}
// maps a json object with a list of AdminSignupResponseDto-objects as value to a dart map
static Map<String, List<AdminSignupResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<AdminSignupResponseDto>>{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast<String, dynamic>();
for (final entry in json.entries) {
map[entry.key] = AdminSignupResponseDto.listFromJson(entry.value, growable: growable,);
}
}
return map;
}
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
'createdAt',
'email',
'firstName',
'id',
'lastName',
};
}

View file

@ -1,47 +0,0 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.12
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
import 'package:openapi/api.dart';
import 'package:test/test.dart';
// tests for AdminSignupResponseDto
void main() {
// final instance = AdminSignupResponseDto();
group('test AdminSignupResponseDto', () {
// DateTime createdAt
test('to test the property `createdAt`', () async {
// TODO
});
// String email
test('to test the property `email`', () async {
// TODO
});
// String firstName
test('to test the property `firstName`', () async {
// TODO
});
// String id
test('to test the property `id`', () async {
// TODO
});
// String lastName
test('to test the property `lastName`', () async {
// TODO
});
});
}

View file

@ -47,7 +47,7 @@ void main() {
// TODO
});
//Future<AdminSignupResponseDto> signUpAdmin(SignUpDto signUpDto) async
//Future<UserResponseDto> signUpAdmin(SignUpDto signUpDto) async
test('test signUpAdmin', () async {
// TODO
});

View file

@ -2630,14 +2630,11 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/AdminSignupResponseDto"
"$ref": "#/components/schemas/UserResponseDto"
}
}
},
"description": ""
},
"400": {
"description": "The server already has an admin"
}
},
"tags": [
@ -5812,34 +5809,6 @@
],
"type": "object"
},
"AdminSignupResponseDto": {
"properties": {
"createdAt": {
"format": "date-time",
"type": "string"
},
"email": {
"type": "string"
},
"firstName": {
"type": "string"
},
"id": {
"type": "string"
},
"lastName": {
"type": "string"
}
},
"required": [
"id",
"email",
"firstName",
"lastName",
"createdAt"
],
"type": "object"
},
"AlbumCountResponseDto": {
"properties": {
"notShared": {
@ -7377,35 +7346,27 @@
"LoginResponseDto": {
"properties": {
"accessToken": {
"readOnly": true,
"type": "string"
},
"firstName": {
"readOnly": true,
"type": "string"
},
"isAdmin": {
"readOnly": true,
"type": "boolean"
},
"lastName": {
"readOnly": true,
"type": "string"
},
"profileImagePath": {
"readOnly": true,
"type": "string"
},
"shouldChangePassword": {
"readOnly": true,
"type": "boolean"
},
"userEmail": {
"readOnly": true,
"type": "string"
},
"userId": {
"readOnly": true,
"type": "string"
}
},

View file

@ -0,0 +1,132 @@
import { UserEntity, UserTokenEntity } from '@app/infra/entities';
import { ApiProperty } from '@nestjs/swagger';
import { Transform } from 'class-transformer';
import { IsEmail, IsNotEmpty, IsString, MinLength } from 'class-validator';
export class AuthUserDto {
id!: string;
email!: string;
isAdmin!: boolean;
isPublicUser?: boolean;
sharedLinkId?: string;
isAllowUpload?: boolean;
isAllowDownload?: boolean;
isShowMetadata?: boolean;
accessTokenId?: string;
externalPath?: string | null;
}
export class LoginCredentialDto {
@IsEmail({ require_tld: false })
@Transform(({ value }) => value?.toLowerCase())
@IsNotEmpty()
@ApiProperty({ example: 'testuser@email.com' })
email!: string;
@IsString()
@IsNotEmpty()
@ApiProperty({ example: 'password' })
password!: string;
}
export class LoginResponseDto {
accessToken!: string;
userId!: string;
userEmail!: string;
firstName!: string;
lastName!: string;
profileImagePath!: string;
isAdmin!: boolean;
shouldChangePassword!: boolean;
}
export function mapLoginResponse(entity: UserEntity, accessToken: string): LoginResponseDto {
return {
accessToken: accessToken,
userId: entity.id,
userEmail: entity.email,
firstName: entity.firstName,
lastName: entity.lastName,
isAdmin: entity.isAdmin,
profileImagePath: entity.profileImagePath,
shouldChangePassword: entity.shouldChangePassword,
};
}
export class LogoutResponseDto {
successful!: boolean;
redirectUri!: string;
}
export class SignUpDto extends LoginCredentialDto {
@IsString()
@IsNotEmpty()
@ApiProperty({ example: 'Admin' })
firstName!: string;
@IsString()
@IsNotEmpty()
@ApiProperty({ example: 'Doe' })
lastName!: string;
}
export class ChangePasswordDto {
@IsString()
@IsNotEmpty()
@ApiProperty({ example: 'password' })
password!: string;
@IsString()
@IsNotEmpty()
@MinLength(8)
@ApiProperty({ example: 'password' })
newPassword!: string;
}
export class ValidateAccessTokenResponseDto {
authStatus!: boolean;
}
export class AuthDeviceResponseDto {
id!: string;
createdAt!: string;
updatedAt!: string;
current!: boolean;
deviceType!: string;
deviceOS!: string;
}
export const mapUserToken = (entity: UserTokenEntity, currentId?: string): AuthDeviceResponseDto => ({
id: entity.id,
createdAt: entity.createdAt.toISOString(),
updatedAt: entity.updatedAt.toISOString(),
current: currentId === entity.id,
deviceOS: entity.deviceOS,
deviceType: entity.deviceType,
});
export class OAuthCallbackDto {
@IsNotEmpty()
@IsString()
@ApiProperty()
url!: string;
}
export class OAuthConfigDto {
@IsNotEmpty()
@IsString()
redirectUri!: string;
}
/** @deprecated use oauth authorize */
export class OAuthConfigResponseDto {
enabled!: boolean;
passwordLoginEnabled!: boolean;
url?: string;
buttonText?: string;
autoLaunch?: boolean;
}
export class OAuthAuthorizeResponseDto {
url!: string;
}

View file

@ -31,8 +31,8 @@ import {
IUserTokenRepository,
} from '../repositories';
import { AuthType } from './auth.constant';
import { AuthUserDto, SignUpDto } from './auth.dto';
import { AuthService } from './auth.service';
import { AuthUserDto, SignUpDto } from './dto';
// const token = Buffer.from('my-api-key', 'utf8').toString('base64');

View file

@ -32,18 +32,21 @@ import {
LOGIN_URL,
MOBILE_REDIRECT,
} from './auth.constant';
import { AuthUserDto, ChangePasswordDto, LoginCredentialDto, OAuthCallbackDto, OAuthConfigDto, SignUpDto } from './dto';
import {
AdminSignupResponseDto,
AuthDeviceResponseDto,
AuthUserDto,
ChangePasswordDto,
LoginCredentialDto,
LoginResponseDto,
LogoutResponseDto,
OAuthAuthorizeResponseDto,
OAuthCallbackDto,
OAuthConfigDto,
OAuthConfigResponseDto,
mapAdminSignupResponse,
SignUpDto,
mapLoginResponse,
mapUserToken,
} from './response-dto';
} from './auth.dto';
export interface LoginDetails {
isSecure: boolean;
@ -133,7 +136,7 @@ export class AuthService {
return this.userCore.updateUser(authUser, authUser.id, { password: newPassword });
}
async adminSignUp(dto: SignUpDto): Promise<AdminSignupResponseDto> {
async adminSignUp(dto: SignUpDto): Promise<UserResponseDto> {
const adminUser = await this.userRepository.getAdmin();
if (adminUser) {
@ -149,7 +152,7 @@ export class AuthService {
storageLabel: 'admin',
});
return mapAdminSignupResponse(admin);
return mapUser(admin);
}
async validate(headers: IncomingHttpHeaders, params: Record<string, string>): Promise<AuthUserDto> {

View file

@ -1,12 +0,0 @@
export class AuthUserDto {
id!: string;
email!: string;
isAdmin!: boolean;
isPublicUser?: boolean;
sharedLinkId?: string;
isAllowUpload?: boolean;
isAllowDownload?: boolean;
isShowMetadata?: boolean;
accessTokenId?: string;
externalPath?: string | null;
}

View file

@ -1,15 +0,0 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsString, MinLength } from 'class-validator';
export class ChangePasswordDto {
@IsString()
@IsNotEmpty()
@ApiProperty({ example: 'password' })
password!: string;
@IsString()
@IsNotEmpty()
@MinLength(8)
@ApiProperty({ example: 'password' })
newPassword!: string;
}

View file

@ -1,6 +0,0 @@
export * from './auth-user.dto';
export * from './change-password.dto';
export * from './login-credential.dto';
export * from './oauth-auth-code.dto';
export * from './oauth-config.dto';
export * from './sign-up.dto';

View file

@ -1,42 +0,0 @@
import { plainToInstance } from 'class-transformer';
import { validateSync } from 'class-validator';
import { LoginCredentialDto } from './login-credential.dto';
describe('LoginCredentialDto', () => {
it('should allow emails without a tld', () => {
const someEmail = 'test@test';
const dto = plainToInstance(LoginCredentialDto, { email: someEmail, password: 'password' });
const errors = validateSync(dto);
expect(errors).toHaveLength(0);
expect(dto.email).toEqual(someEmail);
});
it('should fail without an email', () => {
const dto = plainToInstance(LoginCredentialDto, { password: 'password' });
const errors = validateSync(dto);
expect(errors).toHaveLength(1);
expect(errors[0].property).toEqual('email');
});
it('should fail with an invalid email', () => {
const dto = plainToInstance(LoginCredentialDto, { email: 'invalid.com', password: 'password' });
const errors = validateSync(dto);
expect(errors).toHaveLength(1);
expect(errors[0].property).toEqual('email');
});
it('should make the email all lowercase', () => {
const dto = plainToInstance(LoginCredentialDto, { email: 'TeSt@ImMiCh.com', password: 'password' });
const errors = validateSync(dto);
expect(errors).toHaveLength(0);
expect(dto.email).toEqual('test@immich.com');
});
it('should fail without a password', () => {
const dto = plainToInstance(LoginCredentialDto, { email: 'test@immich.com', password: '' });
const errors = validateSync(dto);
expect(errors).toHaveLength(1);
expect(errors[0].property).toEqual('password');
});
});

View file

@ -1,16 +0,0 @@
import { ApiProperty } from '@nestjs/swagger';
import { Transform } from 'class-transformer';
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
export class LoginCredentialDto {
@IsEmail({ require_tld: false })
@Transform(({ value }) => value?.toLowerCase())
@IsNotEmpty()
@ApiProperty({ example: 'testuser@email.com' })
email!: string;
@IsString()
@IsNotEmpty()
@ApiProperty({ example: 'password' })
password!: string;
}

View file

@ -1,9 +0,0 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsString } from 'class-validator';
export class OAuthCallbackDto {
@IsNotEmpty()
@IsString()
@ApiProperty()
url!: string;
}

View file

@ -1,7 +0,0 @@
import { IsNotEmpty, IsString } from 'class-validator';
export class OAuthConfigDto {
@IsNotEmpty()
@IsString()
redirectUri!: string;
}

View file

@ -1,58 +0,0 @@
import { plainToInstance } from 'class-transformer';
import { validateSync } from 'class-validator';
import { SignUpDto } from './sign-up.dto';
describe('SignUpDto', () => {
it('should require all fields', () => {
const dto = plainToInstance(SignUpDto, {
email: '',
password: '',
firstName: '',
lastName: '',
});
const errors = validateSync(dto);
expect(errors).toHaveLength(4);
expect(errors[0].property).toEqual('email');
expect(errors[1].property).toEqual('password');
expect(errors[2].property).toEqual('firstName');
expect(errors[3].property).toEqual('lastName');
});
it('should require a valid email', () => {
const dto = plainToInstance(SignUpDto, {
email: 'immich.com',
password: 'password',
firstName: 'first name',
lastName: 'last name',
});
const errors = validateSync(dto);
expect(errors).toHaveLength(1);
expect(errors[0].property).toEqual('email');
});
it('should allow emails without a tld', () => {
const someEmail = 'test@test';
const dto = plainToInstance(SignUpDto, {
email: someEmail,
password: 'password',
firstName: 'first name',
lastName: 'last name',
});
const errors = validateSync(dto);
expect(errors).toHaveLength(0);
expect(dto.email).toEqual(someEmail);
});
it('should make the email all lowercase', () => {
const dto = plainToInstance(SignUpDto, {
email: 'TeSt@ImMiCh.com',
password: 'password',
firstName: 'first name',
lastName: 'last name',
});
const errors = validateSync(dto);
expect(errors).toHaveLength(0);
expect(dto.email).toEqual('test@immich.com');
});
});

View file

@ -1,26 +0,0 @@
import { ApiProperty } from '@nestjs/swagger';
import { Transform } from 'class-transformer';
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
export class SignUpDto {
@IsEmail({ require_tld: false })
@Transform(({ value }) => value?.toLowerCase())
@IsNotEmpty()
@ApiProperty({ example: 'testuser@email.com' })
email!: string;
@IsString()
@IsNotEmpty()
@ApiProperty({ example: 'password' })
password!: string;
@IsString()
@IsNotEmpty()
@ApiProperty({ example: 'Admin' })
firstName!: string;
@IsString()
@IsNotEmpty()
@ApiProperty({ example: 'Doe' })
lastName!: string;
}

View file

@ -1,4 +1,3 @@
export * from './auth.constant';
export * from './auth.dto';
export * from './auth.service';
export * from './dto';
export * from './response-dto';

View file

@ -1,19 +0,0 @@
import { UserEntity } from '@app/infra/entities';
export class AdminSignupResponseDto {
id!: string;
email!: string;
firstName!: string;
lastName!: string;
createdAt!: Date;
}
export function mapAdminSignupResponse(entity: UserEntity): AdminSignupResponseDto {
return {
id: entity.id,
email: entity.email,
firstName: entity.firstName,
lastName: entity.lastName,
createdAt: entity.createdAt,
};
}

View file

@ -1,19 +0,0 @@
import { UserTokenEntity } from '@app/infra/entities';
export class AuthDeviceResponseDto {
id!: string;
createdAt!: string;
updatedAt!: string;
current!: boolean;
deviceType!: string;
deviceOS!: string;
}
export const mapUserToken = (entity: UserTokenEntity, currentId?: string): AuthDeviceResponseDto => ({
id: entity.id,
createdAt: entity.createdAt.toISOString(),
updatedAt: entity.updatedAt.toISOString(),
current: currentId === entity.id,
deviceOS: entity.deviceOS,
deviceType: entity.deviceType,
});

View file

@ -1,6 +0,0 @@
export * from './admin-signup-response.dto';
export * from './auth-device-response.dto';
export * from './login-response.dto';
export * from './logout-response.dto';
export * from './oauth-config-response.dto';
export * from './validate-asset-token-response.dto';

View file

@ -1,41 +0,0 @@
import { UserEntity } from '@app/infra/entities';
import { ApiResponseProperty } from '@nestjs/swagger';
export class LoginResponseDto {
@ApiResponseProperty()
accessToken!: string;
@ApiResponseProperty()
userId!: string;
@ApiResponseProperty()
userEmail!: string;
@ApiResponseProperty()
firstName!: string;
@ApiResponseProperty()
lastName!: string;
@ApiResponseProperty()
profileImagePath!: string;
@ApiResponseProperty()
isAdmin!: boolean;
@ApiResponseProperty()
shouldChangePassword!: boolean;
}
export function mapLoginResponse(entity: UserEntity, accessToken: string): LoginResponseDto {
return {
accessToken: accessToken,
userId: entity.id,
userEmail: entity.email,
firstName: entity.firstName,
lastName: entity.lastName,
isAdmin: entity.isAdmin,
profileImagePath: entity.profileImagePath,
shouldChangePassword: entity.shouldChangePassword,
};
}

View file

@ -1,4 +0,0 @@
export class LogoutResponseDto {
successful!: boolean;
redirectUri!: string;
}

View file

@ -1,11 +0,0 @@
export class OAuthConfigResponseDto {
enabled!: boolean;
passwordLoginEnabled!: boolean;
url?: string;
buttonText?: string;
autoLaunch?: boolean;
}
export class OAuthAuthorizeResponseDto {
url!: string;
}

View file

@ -1,3 +0,0 @@
export class ValidateAccessTokenResponseDto {
authStatus!: boolean;
}

View file

@ -1,5 +1,4 @@
import {
AdminSignupResponseDto,
AuthDeviceResponseDto,
AuthService,
AuthUserDto,
@ -15,7 +14,7 @@ import {
ValidateAccessTokenResponseDto,
} from '@app/domain';
import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Req, Res } from '@nestjs/common';
import { ApiBadRequestResponse, ApiTags } from '@nestjs/swagger';
import { ApiTags } from '@nestjs/swagger';
import { Request, Response } from 'express';
import { AuthUser, Authenticated, GetLoginDetails, PublicRoute } from '../app.guard';
import { UseValidation } from '../app.utils';
@ -42,9 +41,8 @@ export class AuthController {
@PublicRoute()
@Post('admin-sign-up')
@ApiBadRequestResponse({ description: 'The server already has an admin' })
signUpAdmin(@Body() signUpCredential: SignUpDto): Promise<AdminSignupResponseDto> {
return this.service.adminSignUp(signUpCredential);
signUpAdmin(@Body() dto: SignUpDto): Promise<UserResponseDto> {
return this.service.adminSignUp(dto);
}
@Get('devices')

View file

@ -1,5 +1,5 @@
import { AdminSignupResponseDto, AuthDeviceResponseDto, LoginCredentialDto, LoginResponseDto } from '@app/domain';
import { adminSignupStub, loginResponseStub, loginStub, signupResponseStub } from '@test';
import { AuthDeviceResponseDto, LoginCredentialDto, LoginResponseDto, UserResponseDto } from '@app/domain';
import { adminSignupStub, loginResponseStub, loginStub } from '@test';
import request from 'supertest';
export const authApi = {
@ -7,9 +7,8 @@ export const authApi = {
const { status, body } = await request(server).post('/auth/admin-sign-up').send(adminSignupStub);
expect(status).toBe(201);
expect(body).toEqual(signupResponseStub);
return body as AdminSignupResponseDto;
return body as UserResponseDto;
},
adminLogin: async (server: any) => {
const { status, body } = await request(server).post('/auth/login').send(loginStub.admin);

View file

@ -8,7 +8,6 @@ import {
errorStub,
loginResponseStub,
loginStub,
signupResponseStub,
uuidStub,
} from '@test/fixtures';
import { testApp } from '@test/test-utils';
@ -19,6 +18,24 @@ const lastName = 'Admin';
const password = 'Password123';
const email = 'admin@immich.app';
const adminSignupResponse = {
id: expect.any(String),
firstName: 'Immich',
lastName: 'Admin',
email: 'admin@immich.app',
storageLabel: 'admin',
externalPath: null,
profileImagePath: '',
// why? lol
shouldChangePassword: true,
isAdmin: true,
createdAt: expect.any(String),
updatedAt: expect.any(String),
deletedAt: null,
oauthId: '',
memoriesEnabled: true,
};
describe(`${AuthController.name} (e2e)`, () => {
let server: any;
let accessToken: string;
@ -84,7 +101,7 @@ describe(`${AuthController.name} (e2e)`, () => {
.post('/auth/admin-sign-up')
.send({ ...adminSignupStub, email: 'admin@local' });
expect(status).toEqual(201);
expect(body).toEqual({ ...signupResponseStub, email: 'admin@local' });
expect(body).toEqual({ ...adminSignupResponse, email: 'admin@local' });
});
it('should transform email to lower case', async () => {
@ -92,7 +109,7 @@ describe(`${AuthController.name} (e2e)`, () => {
.post('/auth/admin-sign-up')
.send({ ...adminSignupStub, email: 'aDmIn@IMMICH.app' });
expect(status).toEqual(201);
expect(body).toEqual(signupResponseStub);
expect(body).toEqual(adminSignupResponse);
});
it('should not allow a second admin to sign up', async () => {

View file

@ -12,14 +12,6 @@ export const userSignupStub = {
memoriesEnabled: true,
};
export const signupResponseStub = {
id: expect.any(String),
email: 'admin@immich.app',
firstName: 'Immich',
lastName: 'Admin',
createdAt: expect.any(String),
};
export const loginStub = {
admin: {
email: 'admin@immich.app',

View file

@ -209,43 +209,6 @@ export interface AddUsersDto {
*/
'sharedUserIds': Array<string>;
}
/**
*
* @export
* @interface AdminSignupResponseDto
*/
export interface AdminSignupResponseDto {
/**
*
* @type {string}
* @memberof AdminSignupResponseDto
*/
'createdAt': string;
/**
*
* @type {string}
* @memberof AdminSignupResponseDto
*/
'email': string;
/**
*
* @type {string}
* @memberof AdminSignupResponseDto
*/
'firstName': string;
/**
*
* @type {string}
* @memberof AdminSignupResponseDto
*/
'id': string;
/**
*
* @type {string}
* @memberof AdminSignupResponseDto
*/
'lastName': string;
}
/**
*
* @export
@ -10509,7 +10472,7 @@ export const AuthenticationApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async signUpAdmin(signUpDto: SignUpDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<AdminSignupResponseDto>> {
async signUpAdmin(signUpDto: SignUpDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<UserResponseDto>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.signUpAdmin(signUpDto, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
@ -10589,7 +10552,7 @@ export const AuthenticationApiFactory = function (configuration?: Configuration,
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
signUpAdmin(requestParameters: AuthenticationApiSignUpAdminRequest, options?: AxiosRequestConfig): AxiosPromise<AdminSignupResponseDto> {
signUpAdmin(requestParameters: AuthenticationApiSignUpAdminRequest, options?: AxiosRequestConfig): AxiosPromise<UserResponseDto> {
return localVarFp.signUpAdmin(requestParameters.signUpDto, options).then((request) => request(axios, basePath));
},
/**