mirror of
https://github.com/immich-app/immich.git
synced 2025-06-01 04:36:19 -04:00
feat(web): user profile (#1148)
* fix: allow updateUser for admin account * feat: update user first/last name * feat(web): change password
This commit is contained in:
parent
723a7c563f
commit
14db7a09e3
3
mobile/openapi/.openapi-generator/FILES
generated
3
mobile/openapi/.openapi-generator/FILES
generated
@ -19,6 +19,7 @@ doc/AssetFileUploadResponseDto.md
|
|||||||
doc/AssetResponseDto.md
|
doc/AssetResponseDto.md
|
||||||
doc/AssetTypeEnum.md
|
doc/AssetTypeEnum.md
|
||||||
doc/AuthenticationApi.md
|
doc/AuthenticationApi.md
|
||||||
|
doc/ChangePasswordDto.md
|
||||||
doc/CheckDuplicateAssetDto.md
|
doc/CheckDuplicateAssetDto.md
|
||||||
doc/CheckDuplicateAssetResponseDto.md
|
doc/CheckDuplicateAssetResponseDto.md
|
||||||
doc/CheckExistingAssetsDto.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_file_upload_response_dto.dart
|
||||||
lib/model/asset_response_dto.dart
|
lib/model/asset_response_dto.dart
|
||||||
lib/model/asset_type_enum.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_dto.dart
|
||||||
lib/model/check_duplicate_asset_response_dto.dart
|
lib/model/check_duplicate_asset_response_dto.dart
|
||||||
lib/model/check_existing_assets_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_response_dto_test.dart
|
||||||
test/asset_type_enum_test.dart
|
test/asset_type_enum_test.dart
|
||||||
test/authentication_api_test.dart
|
test/authentication_api_test.dart
|
||||||
|
test/change_password_dto_test.dart
|
||||||
test/check_duplicate_asset_dto_test.dart
|
test/check_duplicate_asset_dto_test.dart
|
||||||
test/check_duplicate_asset_response_dto_test.dart
|
test/check_duplicate_asset_response_dto_test.dart
|
||||||
test/check_existing_assets_dto_test.dart
|
test/check_existing_assets_dto_test.dart
|
||||||
|
4
mobile/openapi/README.md
generated
4
mobile/openapi/README.md
generated
@ -3,7 +3,7 @@ Immich API
|
|||||||
|
|
||||||
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
|
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
|
- Build package: org.openapitools.codegen.languages.DartClientCodegen
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
@ -96,6 +96,7 @@ Class | Method | HTTP request | Description
|
|||||||
*AssetApi* | [**updateAsset**](doc//AssetApi.md#updateasset) | **PUT** /asset/{assetId} |
|
*AssetApi* | [**updateAsset**](doc//AssetApi.md#updateasset) | **PUT** /asset/{assetId} |
|
||||||
*AssetApi* | [**uploadFile**](doc//AssetApi.md#uploadfile) | **POST** /asset/upload |
|
*AssetApi* | [**uploadFile**](doc//AssetApi.md#uploadfile) | **POST** /asset/upload |
|
||||||
*AuthenticationApi* | [**adminSignUp**](doc//AuthenticationApi.md#adminsignup) | **POST** /auth/admin-sign-up |
|
*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* | [**login**](doc//AuthenticationApi.md#login) | **POST** /auth/login |
|
||||||
*AuthenticationApi* | [**logout**](doc//AuthenticationApi.md#logout) | **POST** /auth/logout |
|
*AuthenticationApi* | [**logout**](doc//AuthenticationApi.md#logout) | **POST** /auth/logout |
|
||||||
*AuthenticationApi* | [**validateAccessToken**](doc//AuthenticationApi.md#validateaccesstoken) | **POST** /auth/validateToken |
|
*AuthenticationApi* | [**validateAccessToken**](doc//AuthenticationApi.md#validateaccesstoken) | **POST** /auth/validateToken |
|
||||||
@ -147,6 +148,7 @@ Class | Method | HTTP request | Description
|
|||||||
- [AssetFileUploadResponseDto](doc//AssetFileUploadResponseDto.md)
|
- [AssetFileUploadResponseDto](doc//AssetFileUploadResponseDto.md)
|
||||||
- [AssetResponseDto](doc//AssetResponseDto.md)
|
- [AssetResponseDto](doc//AssetResponseDto.md)
|
||||||
- [AssetTypeEnum](doc//AssetTypeEnum.md)
|
- [AssetTypeEnum](doc//AssetTypeEnum.md)
|
||||||
|
- [ChangePasswordDto](doc//ChangePasswordDto.md)
|
||||||
- [CheckDuplicateAssetDto](doc//CheckDuplicateAssetDto.md)
|
- [CheckDuplicateAssetDto](doc//CheckDuplicateAssetDto.md)
|
||||||
- [CheckDuplicateAssetResponseDto](doc//CheckDuplicateAssetResponseDto.md)
|
- [CheckDuplicateAssetResponseDto](doc//CheckDuplicateAssetResponseDto.md)
|
||||||
- [CheckExistingAssetsDto](doc//CheckExistingAssetsDto.md)
|
- [CheckExistingAssetsDto](doc//CheckExistingAssetsDto.md)
|
||||||
|
48
mobile/openapi/doc/AuthenticationApi.md
generated
48
mobile/openapi/doc/AuthenticationApi.md
generated
@ -10,6 +10,7 @@ All URIs are relative to */api*
|
|||||||
Method | HTTP request | Description
|
Method | HTTP request | Description
|
||||||
------------- | ------------- | -------------
|
------------- | ------------- | -------------
|
||||||
[**adminSignUp**](AuthenticationApi.md#adminsignup) | **POST** /auth/admin-sign-up |
|
[**adminSignUp**](AuthenticationApi.md#adminsignup) | **POST** /auth/admin-sign-up |
|
||||||
|
[**changePassword**](AuthenticationApi.md#changepassword) | **POST** /auth/change-password |
|
||||||
[**login**](AuthenticationApi.md#login) | **POST** /auth/login |
|
[**login**](AuthenticationApi.md#login) | **POST** /auth/login |
|
||||||
[**logout**](AuthenticationApi.md#logout) | **POST** /auth/logout |
|
[**logout**](AuthenticationApi.md#logout) | **POST** /auth/logout |
|
||||||
[**validateAccessToken**](AuthenticationApi.md#validateaccesstoken) | **POST** /auth/validateToken |
|
[**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)
|
[[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<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
|
||||||
|
// Case 2. Use Function which generate token.
|
||||||
|
// String yourTokenGeneratorFunction() { ... }
|
||||||
|
//defaultApiClient.getAuthentication<HttpBearerAuth>('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**
|
# **login**
|
||||||
> LoginResponseDto login(loginCredentialDto)
|
> LoginResponseDto login(loginCredentialDto)
|
||||||
|
|
||||||
|
16
mobile/openapi/doc/ChangePasswordDto.md
generated
Normal file
16
mobile/openapi/doc/ChangePasswordDto.md
generated
Normal file
@ -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)
|
||||||
|
|
||||||
|
|
1
mobile/openapi/lib/api.dart
generated
1
mobile/openapi/lib/api.dart
generated
@ -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_file_upload_response_dto.dart';
|
||||||
part 'model/asset_response_dto.dart';
|
part 'model/asset_response_dto.dart';
|
||||||
part 'model/asset_type_enum.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_dto.dart';
|
||||||
part 'model/check_duplicate_asset_response_dto.dart';
|
part 'model/check_duplicate_asset_response_dto.dart';
|
||||||
part 'model/check_existing_assets_dto.dart';
|
part 'model/check_existing_assets_dto.dart';
|
||||||
|
47
mobile/openapi/lib/api/authentication_api.dart
generated
47
mobile/openapi/lib/api/authentication_api.dart
generated
@ -63,6 +63,53 @@ class AuthenticationApi {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Performs an HTTP 'POST /auth/change-password' operation and returns the [Response].
|
||||||
|
/// Parameters:
|
||||||
|
///
|
||||||
|
/// * [ChangePasswordDto] changePasswordDto (required):
|
||||||
|
Future<Response> changePasswordWithHttpInfo(ChangePasswordDto changePasswordDto,) async {
|
||||||
|
// ignore: prefer_const_declarations
|
||||||
|
final path = r'/auth/change-password';
|
||||||
|
|
||||||
|
// ignore: prefer_final_locals
|
||||||
|
Object? postBody = changePasswordDto;
|
||||||
|
|
||||||
|
final queryParams = <QueryParam>[];
|
||||||
|
final headerParams = <String, String>{};
|
||||||
|
final formParams = <String, String>{};
|
||||||
|
|
||||||
|
const contentTypes = <String>['application/json'];
|
||||||
|
|
||||||
|
|
||||||
|
return apiClient.invokeAPI(
|
||||||
|
path,
|
||||||
|
'POST',
|
||||||
|
queryParams,
|
||||||
|
postBody,
|
||||||
|
headerParams,
|
||||||
|
formParams,
|
||||||
|
contentTypes.isEmpty ? null : contentTypes.first,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parameters:
|
||||||
|
///
|
||||||
|
/// * [ChangePasswordDto] changePasswordDto (required):
|
||||||
|
Future<UserResponseDto?> 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].
|
/// Performs an HTTP 'POST /auth/login' operation and returns the [Response].
|
||||||
/// Parameters:
|
/// Parameters:
|
||||||
///
|
///
|
||||||
|
2
mobile/openapi/lib/api_client.dart
generated
2
mobile/openapi/lib/api_client.dart
generated
@ -218,6 +218,8 @@ class ApiClient {
|
|||||||
return AssetResponseDto.fromJson(value);
|
return AssetResponseDto.fromJson(value);
|
||||||
case 'AssetTypeEnum':
|
case 'AssetTypeEnum':
|
||||||
return AssetTypeEnumTypeTransformer().decode(value);
|
return AssetTypeEnumTypeTransformer().decode(value);
|
||||||
|
case 'ChangePasswordDto':
|
||||||
|
return ChangePasswordDto.fromJson(value);
|
||||||
case 'CheckDuplicateAssetDto':
|
case 'CheckDuplicateAssetDto':
|
||||||
return CheckDuplicateAssetDto.fromJson(value);
|
return CheckDuplicateAssetDto.fromJson(value);
|
||||||
case 'CheckDuplicateAssetResponseDto':
|
case 'CheckDuplicateAssetResponseDto':
|
||||||
|
119
mobile/openapi/lib/model/change_password_dto.dart
generated
Normal file
119
mobile/openapi/lib/model/change_password_dto.dart
generated
Normal file
@ -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<String, dynamic> toJson() {
|
||||||
|
final _json = <String, dynamic>{};
|
||||||
|
_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<String, dynamic>();
|
||||||
|
|
||||||
|
// 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<String>(json, r'password')!,
|
||||||
|
newPassword: mapValueOfType<String>(json, r'newPassword')!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<ChangePasswordDto>? listFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final result = <ChangePasswordDto>[];
|
||||||
|
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<String, ChangePasswordDto> mapFromJson(dynamic json) {
|
||||||
|
final map = <String, ChangePasswordDto>{};
|
||||||
|
if (json is Map && json.isNotEmpty) {
|
||||||
|
json = json.cast<String, dynamic>(); // 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<String, List<ChangePasswordDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final map = <String, List<ChangePasswordDto>>{};
|
||||||
|
if (json is Map && json.isNotEmpty) {
|
||||||
|
json = json.cast<String, dynamic>(); // 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 = <String>{
|
||||||
|
'password',
|
||||||
|
'newPassword',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
5
mobile/openapi/test/authentication_api_test.dart
generated
5
mobile/openapi/test/authentication_api_test.dart
generated
@ -22,6 +22,11 @@ void main() {
|
|||||||
// TODO
|
// TODO
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//Future<UserResponseDto> changePassword(ChangePasswordDto changePasswordDto) async
|
||||||
|
test('test changePassword', () async {
|
||||||
|
// TODO
|
||||||
|
});
|
||||||
|
|
||||||
//Future<LoginResponseDto> login(LoginCredentialDto loginCredentialDto) async
|
//Future<LoginResponseDto> login(LoginCredentialDto loginCredentialDto) async
|
||||||
test('test login', () async {
|
test('test login', () async {
|
||||||
// TODO
|
// TODO
|
||||||
|
32
mobile/openapi/test/change_password_dto_test.dart
generated
Normal file
32
mobile/openapi/test/change_password_dto_test.dart
generated
Normal file
@ -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
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
@ -5,7 +5,9 @@ import { AuthType, IMMICH_AUTH_TYPE_COOKIE } from '../../constants/jwt.constant'
|
|||||||
import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator';
|
import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator';
|
||||||
import { Authenticated } from '../../decorators/authenticated.decorator';
|
import { Authenticated } from '../../decorators/authenticated.decorator';
|
||||||
import { ImmichJwtService } from '../../modules/immich-jwt/immich-jwt.service';
|
import { ImmichJwtService } from '../../modules/immich-jwt/immich-jwt.service';
|
||||||
|
import { UserResponseDto } from '../user/response-dto/user-response.dto';
|
||||||
import { AuthService } from './auth.service';
|
import { AuthService } from './auth.service';
|
||||||
|
import { ChangePasswordDto } from './dto/change-password.dto';
|
||||||
import { LoginCredentialDto } from './dto/login-credential.dto';
|
import { LoginCredentialDto } from './dto/login-credential.dto';
|
||||||
import { SignUpDto } from './dto/sign-up.dto';
|
import { SignUpDto } from './dto/sign-up.dto';
|
||||||
import { AdminSignupResponseDto } from './response-dto/admin-signup-response.dto';
|
import { AdminSignupResponseDto } from './response-dto/admin-signup-response.dto';
|
||||||
@ -45,6 +47,13 @@ export class AuthController {
|
|||||||
return new ValidateAccessTokenResponseDto(true);
|
return new ValidateAccessTokenResponseDto(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Authenticated()
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@Post('change-password')
|
||||||
|
async changePassword(@GetAuthUser() authUser: AuthUserDto, @Body() dto: ChangePasswordDto): Promise<UserResponseDto> {
|
||||||
|
return this.authService.changePassword(authUser, dto);
|
||||||
|
}
|
||||||
|
|
||||||
@Post('/logout')
|
@Post('/logout')
|
||||||
async logout(@Req() req: Request, @Res({ passthrough: true }) response: Response): Promise<LogoutResponseDto> {
|
async logout(@Req() req: Request, @Res({ passthrough: true }) response: Response): Promise<LogoutResponseDto> {
|
||||||
const authType: AuthType = req.cookies[IMMICH_AUTH_TYPE_COOKIE];
|
const authType: AuthType = req.cookies[IMMICH_AUTH_TYPE_COOKIE];
|
||||||
|
@ -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 * as bcrypt from 'bcrypt';
|
||||||
import { UserEntity } from '../../../../../libs/database/src/entities/user.entity';
|
import { UserEntity } from '../../../../../libs/database/src/entities/user.entity';
|
||||||
import { AuthType } from '../../constants/jwt.constant';
|
import { AuthType } from '../../constants/jwt.constant';
|
||||||
|
import { AuthUserDto } from '../../decorators/auth-user.decorator';
|
||||||
import { ImmichJwtService } from '../../modules/immich-jwt/immich-jwt.service';
|
import { ImmichJwtService } from '../../modules/immich-jwt/immich-jwt.service';
|
||||||
import { IUserRepository, USER_REPOSITORY } from '../user/user-repository';
|
import { IUserRepository, USER_REPOSITORY } from '../user/user-repository';
|
||||||
|
import { ChangePasswordDto } from './dto/change-password.dto';
|
||||||
import { LoginCredentialDto } from './dto/login-credential.dto';
|
import { LoginCredentialDto } from './dto/login-credential.dto';
|
||||||
import { SignUpDto } from './dto/sign-up.dto';
|
import { SignUpDto } from './dto/sign-up.dto';
|
||||||
import { AdminSignupResponseDto, mapAdminSignupResponse } from './response-dto/admin-signup-response.dto';
|
import { AdminSignupResponseDto, mapAdminSignupResponse } from './response-dto/admin-signup-response.dto';
|
||||||
@ -48,6 +57,23 @@ export class AuthService {
|
|||||||
return { successful: true, redirectUri: '/auth/login' };
|
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<AdminSignupResponseDto> {
|
public async adminSignUp(dto: SignUpDto): Promise<AdminSignupResponseDto> {
|
||||||
const adminUser = await this.userRepository.getAdmin();
|
const adminUser = await this.userRepository.getAdmin();
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
}
|
@ -86,7 +86,7 @@ export class UserRepository implements IUserRepository {
|
|||||||
if (user.isAdmin) {
|
if (user.isAdmin) {
|
||||||
const adminUser = await this.userRepository.findOne({ where: { isAdmin: true } });
|
const adminUser = await this.userRepository.findOne({ where: { isAdmin: true } });
|
||||||
|
|
||||||
if (adminUser) {
|
if (adminUser && adminUser.id !== id) {
|
||||||
throw new BadRequestException('Admin user exists');
|
throw new BadRequestException('Admin user exists');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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": {
|
"/auth/logout": {
|
||||||
"post": {
|
"post": {
|
||||||
"operationId": "logout",
|
"operationId": "logout",
|
||||||
@ -3258,6 +3294,23 @@
|
|||||||
"authStatus"
|
"authStatus"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"ChangePasswordDto": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"password": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "password"
|
||||||
|
},
|
||||||
|
"newPassword": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "password"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"password",
|
||||||
|
"newPassword"
|
||||||
|
]
|
||||||
|
},
|
||||||
"LogoutResponseDto": {
|
"LogoutResponseDto": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
90
web/src/api/open-api/api.ts
generated
90
web/src/api/open-api/api.ts
generated
@ -4,7 +4,7 @@
|
|||||||
* Immich
|
* Immich
|
||||||
* Immich API
|
* 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).
|
* 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 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
|
* @export
|
||||||
@ -4171,6 +4190,45 @@ export const AuthenticationApiAxiosParamCreator = function (configuration?: Conf
|
|||||||
options: localVarRequestOptions,
|
options: localVarRequestOptions,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {ChangePasswordDto} changePasswordDto
|
||||||
|
* @param {*} [options] Override http request option.
|
||||||
|
* @throws {RequiredError}
|
||||||
|
*/
|
||||||
|
changePassword: async (changePasswordDto: ChangePasswordDto, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||||
|
// 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
|
* @param {LoginCredentialDto} loginCredentialDto
|
||||||
@ -4288,6 +4346,16 @@ export const AuthenticationApiFp = function(configuration?: Configuration) {
|
|||||||
const localVarAxiosArgs = await localVarAxiosParamCreator.adminSignUp(signUpDto, options);
|
const localVarAxiosArgs = await localVarAxiosParamCreator.adminSignUp(signUpDto, options);
|
||||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
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<UserResponseDto>> {
|
||||||
|
const localVarAxiosArgs = await localVarAxiosParamCreator.changePassword(changePasswordDto, options);
|
||||||
|
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {LoginCredentialDto} loginCredentialDto
|
* @param {LoginCredentialDto} loginCredentialDto
|
||||||
@ -4335,6 +4403,15 @@ export const AuthenticationApiFactory = function (configuration?: Configuration,
|
|||||||
adminSignUp(signUpDto: SignUpDto, options?: any): AxiosPromise<AdminSignupResponseDto> {
|
adminSignUp(signUpDto: SignUpDto, options?: any): AxiosPromise<AdminSignupResponseDto> {
|
||||||
return localVarFp.adminSignUp(signUpDto, options).then((request) => request(axios, basePath));
|
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<UserResponseDto> {
|
||||||
|
return localVarFp.changePassword(changePasswordDto, options).then((request) => request(axios, basePath));
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {LoginCredentialDto} loginCredentialDto
|
* @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));
|
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
|
* @param {LoginCredentialDto} loginCredentialDto
|
||||||
|
2
web/src/api/open-api/base.ts
generated
2
web/src/api/open-api/base.ts
generated
@ -4,7 +4,7 @@
|
|||||||
* Immich
|
* Immich
|
||||||
* Immich API
|
* 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).
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
2
web/src/api/open-api/common.ts
generated
2
web/src/api/open-api/common.ts
generated
@ -4,7 +4,7 @@
|
|||||||
* Immich
|
* Immich
|
||||||
* Immich API
|
* 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).
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
2
web/src/api/open-api/configuration.ts
generated
2
web/src/api/open-api/configuration.ts
generated
@ -4,7 +4,7 @@
|
|||||||
* Immich
|
* Immich
|
||||||
* Immich API
|
* 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).
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
2
web/src/api/open-api/index.ts
generated
2
web/src/api/open-api/index.ts
generated
@ -4,7 +4,7 @@
|
|||||||
* Immich
|
* Immich
|
||||||
* Immich API
|
* 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).
|
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||||
|
@ -1,27 +1,154 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { UserResponseDto } from '@api';
|
import {
|
||||||
|
notificationController,
|
||||||
|
NotificationType
|
||||||
|
} from '$lib/components/shared-components/notification/notification';
|
||||||
|
import { api, UserResponseDto } from '@api';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
import { fade } from 'svelte/transition';
|
||||||
import SettingAccordion from '../admin-page/settings/setting-accordion.svelte';
|
import SettingAccordion from '../admin-page/settings/setting-accordion.svelte';
|
||||||
import SettingInputField, {
|
import SettingInputField, {
|
||||||
SettingInputFieldType
|
SettingInputFieldType
|
||||||
} from '../admin-page/settings/setting-input-field.svelte';
|
} from '../admin-page/settings/setting-input-field.svelte';
|
||||||
|
|
||||||
export let user: UserResponseDto;
|
export let user: UserResponseDto;
|
||||||
|
|
||||||
|
const handleSaveProfile = async () => {
|
||||||
|
try {
|
||||||
|
const { data } = await api.userApi.updateUser({
|
||||||
|
id: user.id,
|
||||||
|
firstName: user.firstName,
|
||||||
|
lastName: user.lastName
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.assign(user, data);
|
||||||
|
|
||||||
|
notificationController.show({
|
||||||
|
message: 'Saved profile',
|
||||||
|
type: NotificationType.Info
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error [user-profile] [updateProfile]', error);
|
||||||
|
notificationController.show({
|
||||||
|
message: 'Unable to save profile',
|
||||||
|
type: NotificationType.Error
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let password = '';
|
||||||
|
let newPassword = '';
|
||||||
|
let confirmPassword = '';
|
||||||
|
|
||||||
|
const handleChangePassword = async () => {
|
||||||
|
try {
|
||||||
|
await api.authenticationApi.changePassword({
|
||||||
|
password,
|
||||||
|
newPassword
|
||||||
|
});
|
||||||
|
|
||||||
|
notificationController.show({
|
||||||
|
message: 'Updated password',
|
||||||
|
type: NotificationType.Info
|
||||||
|
});
|
||||||
|
|
||||||
|
password = '';
|
||||||
|
newPassword = '';
|
||||||
|
confirmPassword = '';
|
||||||
|
} catch (error: AxiosError | any) {
|
||||||
|
console.error('Error [user-profile] [changePassword]', error);
|
||||||
|
notificationController.show({
|
||||||
|
message: error?.response?.data?.message || 'Unable to change password',
|
||||||
|
type: NotificationType.Error
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SettingAccordion title="User profile" subtitle="Manage the user information">
|
<SettingAccordion title="User Profile" subtitle="View and manage your profile">
|
||||||
<section class="my-4">
|
<section class="my-4">
|
||||||
<SettingInputField
|
<div in:fade={{ duration: 500 }}>
|
||||||
inputType={SettingInputFieldType.TEXT}
|
<form autocomplete="off" on:submit|preventDefault>
|
||||||
label="First name"
|
<div class="flex flex-col gap-4 ml-4 mt-4">
|
||||||
bind:value={user.firstName}
|
<SettingInputField
|
||||||
required={true}
|
inputType={SettingInputFieldType.TEXT}
|
||||||
/>
|
label="User ID"
|
||||||
|
bind:value={user.id}
|
||||||
|
disabled={true}
|
||||||
|
/>
|
||||||
|
|
||||||
<SettingInputField
|
<SettingInputField
|
||||||
inputType={SettingInputFieldType.TEXT}
|
inputType={SettingInputFieldType.TEXT}
|
||||||
label="Last name"
|
label="Email"
|
||||||
bind:value={user.lastName}
|
bind:value={user.email}
|
||||||
required={true}
|
disabled={true}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<SettingInputField
|
||||||
|
inputType={SettingInputFieldType.TEXT}
|
||||||
|
label="First name"
|
||||||
|
bind:value={user.firstName}
|
||||||
|
required={true}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SettingInputField
|
||||||
|
inputType={SettingInputFieldType.TEXT}
|
||||||
|
label="Last name"
|
||||||
|
bind:value={user.lastName}
|
||||||
|
required={true}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="flex justify-end">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
on:click={() => handleSaveProfile()}
|
||||||
|
class="text-sm bg-immich-primary dark:bg-immich-dark-primary hover:bg-immich-primary/75 dark:hover:bg-immich-dark-primary/80 px-4 py-2 text-white dark:text-immich-dark-gray rounded-full shadow-md font-medium disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
|
>Save
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</SettingAccordion>
|
||||||
|
|
||||||
|
<SettingAccordion title="Password" subtitle="Change your password">
|
||||||
|
<section class="my-4">
|
||||||
|
<div in:fade={{ duration: 500 }}>
|
||||||
|
<form autocomplete="off" on:submit|preventDefault>
|
||||||
|
<div class="flex flex-col gap-4 ml-4 mt-4">
|
||||||
|
<SettingInputField
|
||||||
|
inputType={SettingInputFieldType.PASSWORD}
|
||||||
|
label="Password"
|
||||||
|
bind:value={password}
|
||||||
|
required={true}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SettingInputField
|
||||||
|
inputType={SettingInputFieldType.PASSWORD}
|
||||||
|
label="New password"
|
||||||
|
bind:value={newPassword}
|
||||||
|
required={true}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SettingInputField
|
||||||
|
inputType={SettingInputFieldType.PASSWORD}
|
||||||
|
label="Confirm password"
|
||||||
|
bind:value={confirmPassword}
|
||||||
|
required={true}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="flex justify-end">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={!(password && newPassword && newPassword === confirmPassword)}
|
||||||
|
on:click={() => handleChangePassword()}
|
||||||
|
class="text-sm bg-immich-primary dark:bg-immich-dark-primary hover:bg-immich-primary/75 dark:hover:bg-immich-dark-primary/80 px-4 py-2 text-white dark:text-immich-dark-gray rounded-full shadow-md font-medium disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
|
>Save
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</SettingAccordion>
|
</SettingAccordion>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user