mirror of
https://github.com/immich-app/immich.git
synced 2024-12-25 10:43:13 +02:00
refactor(server): auth dtos (#4881)
* refactor: auth dtos * chore: open api
This commit is contained in:
parent
5c602bf4d4
commit
5423f1c25b
41
cli/src/api/open-api/api.ts
generated
41
cli/src/api/open-api/api.ts
generated
@ -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));
|
||||
},
|
||||
/**
|
||||
|
3
mobile/openapi/.openapi-generator/FILES
generated
3
mobile/openapi/.openapi-generator/FILES
generated
@ -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
|
||||
|
BIN
mobile/openapi/README.md
generated
BIN
mobile/openapi/README.md
generated
Binary file not shown.
BIN
mobile/openapi/doc/AdminSignupResponseDto.md
generated
BIN
mobile/openapi/doc/AdminSignupResponseDto.md
generated
Binary file not shown.
BIN
mobile/openapi/doc/AuthenticationApi.md
generated
BIN
mobile/openapi/doc/AuthenticationApi.md
generated
Binary file not shown.
BIN
mobile/openapi/doc/LoginResponseDto.md
generated
BIN
mobile/openapi/doc/LoginResponseDto.md
generated
Binary file not shown.
BIN
mobile/openapi/lib/api.dart
generated
BIN
mobile/openapi/lib/api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/authentication_api.dart
generated
BIN
mobile/openapi/lib/api/authentication_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api_client.dart
generated
BIN
mobile/openapi/lib/api_client.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/model/admin_signup_response_dto.dart
generated
BIN
mobile/openapi/lib/model/admin_signup_response_dto.dart
generated
Binary file not shown.
BIN
mobile/openapi/test/admin_signup_response_dto_test.dart
generated
BIN
mobile/openapi/test/admin_signup_response_dto_test.dart
generated
Binary file not shown.
BIN
mobile/openapi/test/authentication_api_test.dart
generated
BIN
mobile/openapi/test/authentication_api_test.dart
generated
Binary file not shown.
@ -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"
|
||||
}
|
||||
},
|
||||
|
132
server/src/domain/auth/auth.dto.ts
Normal file
132
server/src/domain/auth/auth.dto.ts
Normal 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;
|
||||
}
|
@ -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');
|
||||
|
||||
|
@ -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> {
|
||||
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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';
|
@ -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');
|
||||
});
|
||||
});
|
@ -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;
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class OAuthCallbackDto {
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
@ApiProperty()
|
||||
url!: string;
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class OAuthConfigDto {
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
redirectUri!: string;
|
||||
}
|
@ -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');
|
||||
});
|
||||
});
|
@ -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;
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
export * from './auth.constant';
|
||||
export * from './auth.dto';
|
||||
export * from './auth.service';
|
||||
export * from './dto';
|
||||
export * from './response-dto';
|
||||
|
@ -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,
|
||||
};
|
||||
}
|
@ -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,
|
||||
});
|
@ -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';
|
@ -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,
|
||||
};
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
export class LogoutResponseDto {
|
||||
successful!: boolean;
|
||||
redirectUri!: string;
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
export class OAuthConfigResponseDto {
|
||||
enabled!: boolean;
|
||||
passwordLoginEnabled!: boolean;
|
||||
url?: string;
|
||||
buttonText?: string;
|
||||
autoLaunch?: boolean;
|
||||
}
|
||||
|
||||
export class OAuthAuthorizeResponseDto {
|
||||
url!: string;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
export class ValidateAccessTokenResponseDto {
|
||||
authStatus!: boolean;
|
||||
}
|
@ -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')
|
||||
|
@ -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);
|
||||
|
@ -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 () => {
|
||||
|
8
server/test/fixtures/auth.stub.ts
vendored
8
server/test/fixtures/auth.stub.ts
vendored
@ -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',
|
||||
|
41
web/src/api/open-api/api.ts
generated
41
web/src/api/open-api/api.ts
generated
@ -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));
|
||||
},
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user