diff --git a/mobile/openapi/.openapi-generator/FILES b/mobile/openapi/.openapi-generator/FILES index a596cc0d83..7b27be8b20 100644 --- a/mobile/openapi/.openapi-generator/FILES +++ b/mobile/openapi/.openapi-generator/FILES @@ -19,6 +19,7 @@ doc/AssetFileUploadResponseDto.md doc/AssetResponseDto.md doc/AssetTypeEnum.md doc/AuthenticationApi.md +doc/ChangePasswordDto.md doc/CheckDuplicateAssetDto.md doc/CheckDuplicateAssetResponseDto.md doc/CheckExistingAssetsDto.md @@ -114,6 +115,7 @@ lib/model/asset_count_by_user_id_response_dto.dart lib/model/asset_file_upload_response_dto.dart lib/model/asset_response_dto.dart lib/model/asset_type_enum.dart +lib/model/change_password_dto.dart lib/model/check_duplicate_asset_dto.dart lib/model/check_duplicate_asset_response_dto.dart lib/model/check_existing_assets_dto.dart @@ -186,6 +188,7 @@ test/asset_file_upload_response_dto_test.dart test/asset_response_dto_test.dart test/asset_type_enum_test.dart test/authentication_api_test.dart +test/change_password_dto_test.dart test/check_duplicate_asset_dto_test.dart test/check_duplicate_asset_response_dto_test.dart test/check_existing_assets_dto_test.dart diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index e29aec936e..49b0244200 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -3,7 +3,7 @@ Immich API This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: -- API version: 1.38.2 +- API version: 1.39.0 - Build package: org.openapitools.codegen.languages.DartClientCodegen ## Requirements @@ -96,6 +96,7 @@ Class | Method | HTTP request | Description *AssetApi* | [**updateAsset**](doc//AssetApi.md#updateasset) | **PUT** /asset/{assetId} | *AssetApi* | [**uploadFile**](doc//AssetApi.md#uploadfile) | **POST** /asset/upload | *AuthenticationApi* | [**adminSignUp**](doc//AuthenticationApi.md#adminsignup) | **POST** /auth/admin-sign-up | +*AuthenticationApi* | [**changePassword**](doc//AuthenticationApi.md#changepassword) | **POST** /auth/change-password | *AuthenticationApi* | [**login**](doc//AuthenticationApi.md#login) | **POST** /auth/login | *AuthenticationApi* | [**logout**](doc//AuthenticationApi.md#logout) | **POST** /auth/logout | *AuthenticationApi* | [**validateAccessToken**](doc//AuthenticationApi.md#validateaccesstoken) | **POST** /auth/validateToken | @@ -147,6 +148,7 @@ Class | Method | HTTP request | Description - [AssetFileUploadResponseDto](doc//AssetFileUploadResponseDto.md) - [AssetResponseDto](doc//AssetResponseDto.md) - [AssetTypeEnum](doc//AssetTypeEnum.md) + - [ChangePasswordDto](doc//ChangePasswordDto.md) - [CheckDuplicateAssetDto](doc//CheckDuplicateAssetDto.md) - [CheckDuplicateAssetResponseDto](doc//CheckDuplicateAssetResponseDto.md) - [CheckExistingAssetsDto](doc//CheckExistingAssetsDto.md) diff --git a/mobile/openapi/doc/AuthenticationApi.md b/mobile/openapi/doc/AuthenticationApi.md index 01f106f73f..ffcece086b 100644 --- a/mobile/openapi/doc/AuthenticationApi.md +++ b/mobile/openapi/doc/AuthenticationApi.md @@ -10,6 +10,7 @@ All URIs are relative to */api* Method | HTTP request | Description ------------- | ------------- | ------------- [**adminSignUp**](AuthenticationApi.md#adminsignup) | **POST** /auth/admin-sign-up | +[**changePassword**](AuthenticationApi.md#changepassword) | **POST** /auth/change-password | [**login**](AuthenticationApi.md#login) | **POST** /auth/login | [**logout**](AuthenticationApi.md#logout) | **POST** /auth/logout | [**validateAccessToken**](AuthenticationApi.md#validateaccesstoken) | **POST** /auth/validateToken | @@ -56,6 +57,53 @@ No authorization required [[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) +# **changePassword** +> UserResponseDto changePassword(changePasswordDto) + + + +### Example +```dart +import 'package:openapi/api.dart'; +// TODO Configure HTTP Bearer authorization: bearer +// Case 1. Use String Token +//defaultApiClient.getAuthentication('bearer').setAccessToken('YOUR_ACCESS_TOKEN'); +// Case 2. Use Function which generate token. +// String yourTokenGeneratorFunction() { ... } +//defaultApiClient.getAuthentication('bearer').setAccessToken(yourTokenGeneratorFunction); + +final api_instance = AuthenticationApi(); +final changePasswordDto = ChangePasswordDto(); // ChangePasswordDto | + +try { + final result = api_instance.changePassword(changePasswordDto); + print(result); +} catch (e) { + print('Exception when calling AuthenticationApi->changePassword: $e\n'); +} +``` + +### Parameters + +Name | Type | Description | Notes +------------- | ------------- | ------------- | ------------- + **changePasswordDto** | [**ChangePasswordDto**](ChangePasswordDto.md)| | + +### Return type + +[**UserResponseDto**](UserResponseDto.md) + +### Authorization + +[bearer](../README.md#bearer) + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + +[[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) + # **login** > LoginResponseDto login(loginCredentialDto) diff --git a/mobile/openapi/doc/ChangePasswordDto.md b/mobile/openapi/doc/ChangePasswordDto.md new file mode 100644 index 0000000000..a257395ba3 --- /dev/null +++ b/mobile/openapi/doc/ChangePasswordDto.md @@ -0,0 +1,16 @@ +# openapi.model.ChangePasswordDto + +## Load the model package +```dart +import 'package:openapi/api.dart'; +``` + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**password** | **String** | | +**newPassword** | **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) + + diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart index e227bb7529..83ca7a388b 100644 --- a/mobile/openapi/lib/api.dart +++ b/mobile/openapi/lib/api.dart @@ -51,6 +51,7 @@ part 'model/asset_count_by_user_id_response_dto.dart'; part 'model/asset_file_upload_response_dto.dart'; part 'model/asset_response_dto.dart'; part 'model/asset_type_enum.dart'; +part 'model/change_password_dto.dart'; part 'model/check_duplicate_asset_dto.dart'; part 'model/check_duplicate_asset_response_dto.dart'; part 'model/check_existing_assets_dto.dart'; diff --git a/mobile/openapi/lib/api/authentication_api.dart b/mobile/openapi/lib/api/authentication_api.dart index 8f90376a46..39888550dc 100644 --- a/mobile/openapi/lib/api/authentication_api.dart +++ b/mobile/openapi/lib/api/authentication_api.dart @@ -63,6 +63,53 @@ class AuthenticationApi { return null; } + /// Performs an HTTP 'POST /auth/change-password' operation and returns the [Response]. + /// Parameters: + /// + /// * [ChangePasswordDto] changePasswordDto (required): + Future changePasswordWithHttpInfo(ChangePasswordDto changePasswordDto,) async { + // ignore: prefer_const_declarations + final path = r'/auth/change-password'; + + // ignore: prefer_final_locals + Object? postBody = changePasswordDto; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = ['application/json']; + + + return apiClient.invokeAPI( + path, + 'POST', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + /// Parameters: + /// + /// * [ChangePasswordDto] changePasswordDto (required): + Future changePassword(ChangePasswordDto changePasswordDto,) async { + final response = await changePasswordWithHttpInfo(changePasswordDto,); + if (response.statusCode >= HttpStatus.badRequest) { + throw ApiException(response.statusCode, await _decodeBodyBytes(response)); + } + // When a remote server returns no body with a status of 204, we shall not decode it. + // 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), 'UserResponseDto',) as UserResponseDto; + + } + return null; + } + /// Performs an HTTP 'POST /auth/login' operation and returns the [Response]. /// Parameters: /// diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart index 0fd1705933..13f5a3e02d 100644 --- a/mobile/openapi/lib/api_client.dart +++ b/mobile/openapi/lib/api_client.dart @@ -218,6 +218,8 @@ class ApiClient { return AssetResponseDto.fromJson(value); case 'AssetTypeEnum': return AssetTypeEnumTypeTransformer().decode(value); + case 'ChangePasswordDto': + return ChangePasswordDto.fromJson(value); case 'CheckDuplicateAssetDto': return CheckDuplicateAssetDto.fromJson(value); case 'CheckDuplicateAssetResponseDto': diff --git a/mobile/openapi/lib/model/change_password_dto.dart b/mobile/openapi/lib/model/change_password_dto.dart new file mode 100644 index 0000000000..dc8bb31e26 --- /dev/null +++ b/mobile/openapi/lib/model/change_password_dto.dart @@ -0,0 +1,119 @@ +// +// 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 ChangePasswordDto { + /// Returns a new [ChangePasswordDto] instance. + ChangePasswordDto({ + required this.password, + required this.newPassword, + }); + + String password; + + String newPassword; + + @override + bool operator ==(Object other) => identical(this, other) || other is ChangePasswordDto && + other.password == password && + other.newPassword == newPassword; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (password.hashCode) + + (newPassword.hashCode); + + @override + String toString() => 'ChangePasswordDto[password=$password, newPassword=$newPassword]'; + + Map toJson() { + final _json = {}; + _json[r'password'] = password; + _json[r'newPassword'] = newPassword; + return _json; + } + + /// Returns a new [ChangePasswordDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static ChangePasswordDto? fromJson(dynamic value) { + if (value is Map) { + final json = value.cast(); + + // Ensure that the map contains the required keys. + // Note 1: the values aren't checked for validity beyond being non-null. + // Note 2: this code is stripped in release mode! + assert(() { + requiredKeys.forEach((key) { + assert(json.containsKey(key), 'Required key "ChangePasswordDto[$key]" is missing from JSON.'); + assert(json[key] != null, 'Required key "ChangePasswordDto[$key]" has a null value in JSON.'); + }); + return true; + }()); + + return ChangePasswordDto( + password: mapValueOfType(json, r'password')!, + newPassword: mapValueOfType(json, r'newPassword')!, + ); + } + return null; + } + + static List? listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = ChangePasswordDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = ChangePasswordDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of ChangePasswordDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = ChangePasswordDto.listFromJson(entry.value, growable: growable,); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'password', + 'newPassword', + }; +} + diff --git a/mobile/openapi/test/authentication_api_test.dart b/mobile/openapi/test/authentication_api_test.dart index 97fe5b87a6..f855d32390 100644 --- a/mobile/openapi/test/authentication_api_test.dart +++ b/mobile/openapi/test/authentication_api_test.dart @@ -22,6 +22,11 @@ void main() { // TODO }); + //Future changePassword(ChangePasswordDto changePasswordDto) async + test('test changePassword', () async { + // TODO + }); + //Future login(LoginCredentialDto loginCredentialDto) async test('test login', () async { // TODO diff --git a/mobile/openapi/test/change_password_dto_test.dart b/mobile/openapi/test/change_password_dto_test.dart new file mode 100644 index 0000000000..5095250fc6 --- /dev/null +++ b/mobile/openapi/test/change_password_dto_test.dart @@ -0,0 +1,32 @@ +// +// 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 ChangePasswordDto +void main() { + // final instance = ChangePasswordDto(); + + group('test ChangePasswordDto', () { + // String password + test('to test the property `password`', () async { + // TODO + }); + + // String newPassword + test('to test the property `newPassword`', () async { + // TODO + }); + + + }); + +} diff --git a/server/apps/immich/src/api-v1/auth/auth.controller.ts b/server/apps/immich/src/api-v1/auth/auth.controller.ts index a2dbce89ed..53b6b02b97 100644 --- a/server/apps/immich/src/api-v1/auth/auth.controller.ts +++ b/server/apps/immich/src/api-v1/auth/auth.controller.ts @@ -5,7 +5,9 @@ import { AuthType, IMMICH_AUTH_TYPE_COOKIE } from '../../constants/jwt.constant' import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator'; import { Authenticated } from '../../decorators/authenticated.decorator'; import { ImmichJwtService } from '../../modules/immich-jwt/immich-jwt.service'; +import { UserResponseDto } from '../user/response-dto/user-response.dto'; import { AuthService } from './auth.service'; +import { ChangePasswordDto } from './dto/change-password.dto'; import { LoginCredentialDto } from './dto/login-credential.dto'; import { SignUpDto } from './dto/sign-up.dto'; import { AdminSignupResponseDto } from './response-dto/admin-signup-response.dto'; @@ -45,6 +47,13 @@ export class AuthController { return new ValidateAccessTokenResponseDto(true); } + @Authenticated() + @ApiBearerAuth() + @Post('change-password') + async changePassword(@GetAuthUser() authUser: AuthUserDto, @Body() dto: ChangePasswordDto): Promise { + return this.authService.changePassword(authUser, dto); + } + @Post('/logout') async logout(@Req() req: Request, @Res({ passthrough: true }) response: Response): Promise { const authType: AuthType = req.cookies[IMMICH_AUTH_TYPE_COOKIE]; diff --git a/server/apps/immich/src/api-v1/auth/auth.service.ts b/server/apps/immich/src/api-v1/auth/auth.service.ts index cfcaf58937..090b8c86aa 100644 --- a/server/apps/immich/src/api-v1/auth/auth.service.ts +++ b/server/apps/immich/src/api-v1/auth/auth.service.ts @@ -1,9 +1,18 @@ -import { BadRequestException, Inject, Injectable, InternalServerErrorException, Logger } from '@nestjs/common'; +import { + BadRequestException, + Inject, + Injectable, + InternalServerErrorException, + Logger, + UnauthorizedException, +} from '@nestjs/common'; import * as bcrypt from 'bcrypt'; import { UserEntity } from '../../../../../libs/database/src/entities/user.entity'; import { AuthType } from '../../constants/jwt.constant'; +import { AuthUserDto } from '../../decorators/auth-user.decorator'; import { ImmichJwtService } from '../../modules/immich-jwt/immich-jwt.service'; import { IUserRepository, USER_REPOSITORY } from '../user/user-repository'; +import { ChangePasswordDto } from './dto/change-password.dto'; import { LoginCredentialDto } from './dto/login-credential.dto'; import { SignUpDto } from './dto/sign-up.dto'; import { AdminSignupResponseDto, mapAdminSignupResponse } from './response-dto/admin-signup-response.dto'; @@ -48,6 +57,23 @@ export class AuthService { return { successful: true, redirectUri: '/auth/login' }; } + public async changePassword(authUser: AuthUserDto, dto: ChangePasswordDto) { + const { password, newPassword } = dto; + const user = await this.userRepository.getByEmail(authUser.email, true); + if (!user) { + throw new UnauthorizedException(); + } + + const valid = await this.validatePassword(password, user); + if (!valid) { + throw new BadRequestException('Wrong password'); + } + + user.password = newPassword; + + return this.userRepository.update(user.id, user); + } + public async adminSignUp(dto: SignUpDto): Promise { const adminUser = await this.userRepository.getAdmin(); diff --git a/server/apps/immich/src/api-v1/auth/dto/change-password.dto.ts b/server/apps/immich/src/api-v1/auth/dto/change-password.dto.ts new file mode 100644 index 0000000000..9c5ce479ea --- /dev/null +++ b/server/apps/immich/src/api-v1/auth/dto/change-password.dto.ts @@ -0,0 +1,15 @@ +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; +} diff --git a/server/apps/immich/src/api-v1/user/user-repository.ts b/server/apps/immich/src/api-v1/user/user-repository.ts index e4dd2e6465..2c11607907 100644 --- a/server/apps/immich/src/api-v1/user/user-repository.ts +++ b/server/apps/immich/src/api-v1/user/user-repository.ts @@ -86,7 +86,7 @@ export class UserRepository implements IUserRepository { if (user.isAdmin) { const adminUser = await this.userRepository.findOne({ where: { isAdmin: true } }); - if (adminUser) { + if (adminUser && adminUser.id !== id) { throw new BadRequestException('Admin user exists'); } diff --git a/server/immich-openapi-specs.json b/server/immich-openapi-specs.json index fb8bce007e..d01a7d1a79 100644 --- a/server/immich-openapi-specs.json +++ b/server/immich-openapi-specs.json @@ -1707,6 +1707,42 @@ ] } }, + "/auth/change-password": { + "post": { + "operationId": "changePassword", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChangePasswordDto" + } + } + } + }, + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserResponseDto" + } + } + } + } + }, + "tags": [ + "Authentication" + ], + "security": [ + { + "bearer": [] + } + ] + } + }, "/auth/logout": { "post": { "operationId": "logout", @@ -3258,6 +3294,23 @@ "authStatus" ] }, + "ChangePasswordDto": { + "type": "object", + "properties": { + "password": { + "type": "string", + "example": "password" + }, + "newPassword": { + "type": "string", + "example": "password" + } + }, + "required": [ + "password", + "newPassword" + ] + }, "LogoutResponseDto": { "type": "object", "properties": { diff --git a/web/src/api/open-api/api.ts b/web/src/api/open-api/api.ts index 156fd87f6c..38b5be8cd0 100644 --- a/web/src/api/open-api/api.ts +++ b/web/src/api/open-api/api.ts @@ -4,7 +4,7 @@ * Immich * Immich API * - * The version of the OpenAPI document: 1.38.2 + * The version of the OpenAPI document: 1.39.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -481,6 +481,25 @@ export const AssetTypeEnum = { export type AssetTypeEnum = typeof AssetTypeEnum[keyof typeof AssetTypeEnum]; +/** + * + * @export + * @interface ChangePasswordDto + */ +export interface ChangePasswordDto { + /** + * + * @type {string} + * @memberof ChangePasswordDto + */ + 'password': string; + /** + * + * @type {string} + * @memberof ChangePasswordDto + */ + 'newPassword': string; +} /** * * @export @@ -4171,6 +4190,45 @@ export const AuthenticationApiAxiosParamCreator = function (configuration?: Conf options: localVarRequestOptions, }; }, + /** + * + * @param {ChangePasswordDto} changePasswordDto + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + changePassword: async (changePasswordDto: ChangePasswordDto, options: AxiosRequestConfig = {}): Promise => { + // verify required parameter 'changePasswordDto' is not null or undefined + assertParamExists('changePassword', 'changePasswordDto', changePasswordDto) + const localVarPath = `/auth/change-password`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication bearer required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(changePasswordDto, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, /** * * @param {LoginCredentialDto} loginCredentialDto @@ -4288,6 +4346,16 @@ export const AuthenticationApiFp = function(configuration?: Configuration) { const localVarAxiosArgs = await localVarAxiosParamCreator.adminSignUp(signUpDto, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, + /** + * + * @param {ChangePasswordDto} changePasswordDto + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async changePassword(changePasswordDto: ChangePasswordDto, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.changePassword(changePasswordDto, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, /** * * @param {LoginCredentialDto} loginCredentialDto @@ -4335,6 +4403,15 @@ export const AuthenticationApiFactory = function (configuration?: Configuration, adminSignUp(signUpDto: SignUpDto, options?: any): AxiosPromise { return localVarFp.adminSignUp(signUpDto, options).then((request) => request(axios, basePath)); }, + /** + * + * @param {ChangePasswordDto} changePasswordDto + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + changePassword(changePasswordDto: ChangePasswordDto, options?: any): AxiosPromise { + return localVarFp.changePassword(changePasswordDto, options).then((request) => request(axios, basePath)); + }, /** * * @param {LoginCredentialDto} loginCredentialDto @@ -4381,6 +4458,17 @@ export class AuthenticationApi extends BaseAPI { return AuthenticationApiFp(this.configuration).adminSignUp(signUpDto, options).then((request) => request(this.axios, this.basePath)); } + /** + * + * @param {ChangePasswordDto} changePasswordDto + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof AuthenticationApi + */ + public changePassword(changePasswordDto: ChangePasswordDto, options?: AxiosRequestConfig) { + return AuthenticationApiFp(this.configuration).changePassword(changePasswordDto, options).then((request) => request(this.axios, this.basePath)); + } + /** * * @param {LoginCredentialDto} loginCredentialDto diff --git a/web/src/api/open-api/base.ts b/web/src/api/open-api/base.ts index f00f196d8b..5cb76e4478 100644 --- a/web/src/api/open-api/base.ts +++ b/web/src/api/open-api/base.ts @@ -4,7 +4,7 @@ * Immich * Immich API * - * The version of the OpenAPI document: 1.38.2 + * The version of the OpenAPI document: 1.39.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/web/src/api/open-api/common.ts b/web/src/api/open-api/common.ts index a946fabb54..79a5fd9335 100644 --- a/web/src/api/open-api/common.ts +++ b/web/src/api/open-api/common.ts @@ -4,7 +4,7 @@ * Immich * Immich API * - * The version of the OpenAPI document: 1.38.2 + * The version of the OpenAPI document: 1.39.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/web/src/api/open-api/configuration.ts b/web/src/api/open-api/configuration.ts index 48794159a3..3d13f6f92c 100644 --- a/web/src/api/open-api/configuration.ts +++ b/web/src/api/open-api/configuration.ts @@ -4,7 +4,7 @@ * Immich * Immich API * - * The version of the OpenAPI document: 1.38.2 + * The version of the OpenAPI document: 1.39.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/web/src/api/open-api/index.ts b/web/src/api/open-api/index.ts index f0b9d9c785..c9ec6d14c1 100644 --- a/web/src/api/open-api/index.ts +++ b/web/src/api/open-api/index.ts @@ -4,7 +4,7 @@ * Immich * Immich API * - * The version of the OpenAPI document: 1.38.2 + * The version of the OpenAPI document: 1.39.0 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/web/src/lib/components/user-settings-page/user-settings-list.svelte b/web/src/lib/components/user-settings-page/user-settings-list.svelte index a7a9677150..0f2589dfdd 100644 --- a/web/src/lib/components/user-settings-page/user-settings-list.svelte +++ b/web/src/lib/components/user-settings-page/user-settings-list.svelte @@ -1,27 +1,154 @@ - +
- +
+
+
+ - + + + + + + +
+ +
+
+
+
+
+
+ + +
+
+
+
+ + + + + + +
+ +
+
+
+