mirror of
				https://github.com/KevinMidboe/immich.git
				synced 2025-10-29 17:40:28 +00:00 
			
		
		
		
	feat(server, web)!: Move reverse geocoding settings to the UI (#4222)
* feat: reverse geocoding settings * chore: open api * re-init geocoder if precision has been updated * update docs * chore: update verbiage * fix: re-init logic * fix: reset to default --------- Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
This commit is contained in:
		
							
								
								
									
										49
									
								
								cli/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										49
									
								
								cli/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							| @@ -1055,6 +1055,22 @@ export interface CheckExistingAssetsResponseDto { | |||||||
|      */ |      */ | ||||||
|     'existingIds': Array<string>; |     'existingIds': Array<string>; | ||||||
| } | } | ||||||
|  | /** | ||||||
|  |  *  | ||||||
|  |  * @export | ||||||
|  |  * @enum {string} | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | export const CitiesFile = { | ||||||
|  |     Cities15000: 'cities15000', | ||||||
|  |     Cities5000: 'cities5000', | ||||||
|  |     Cities1000: 'cities1000', | ||||||
|  |     Cities500: 'cities500' | ||||||
|  | } as const; | ||||||
|  | 
 | ||||||
|  | export type CitiesFile = typeof CitiesFile[keyof typeof CitiesFile]; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  *  |  *  | ||||||
|  * @export |  * @export | ||||||
| @@ -2650,6 +2666,12 @@ export interface ServerFeaturesDto { | |||||||
|      * @memberof ServerFeaturesDto |      * @memberof ServerFeaturesDto | ||||||
|      */ |      */ | ||||||
|     'passwordLogin': boolean; |     'passwordLogin': boolean; | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {boolean} | ||||||
|  |      * @memberof ServerFeaturesDto | ||||||
|  |      */ | ||||||
|  |     'reverseGeocoding': boolean; | ||||||
|     /** |     /** | ||||||
|      *  |      *  | ||||||
|      * @type {boolean} |      * @type {boolean} | ||||||
| @@ -3093,6 +3115,12 @@ export interface SystemConfigDto { | |||||||
|      * @memberof SystemConfigDto |      * @memberof SystemConfigDto | ||||||
|      */ |      */ | ||||||
|     'passwordLogin': SystemConfigPasswordLoginDto; |     'passwordLogin': SystemConfigPasswordLoginDto; | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {SystemConfigReverseGeocodingDto} | ||||||
|  |      * @memberof SystemConfigDto | ||||||
|  |      */ | ||||||
|  |     'reverseGeocoding': SystemConfigReverseGeocodingDto; | ||||||
|     /** |     /** | ||||||
|      *  |      *  | ||||||
|      * @type {SystemConfigStorageTemplateDto} |      * @type {SystemConfigStorageTemplateDto} | ||||||
| @@ -3438,6 +3466,27 @@ export interface SystemConfigPasswordLoginDto { | |||||||
|      */ |      */ | ||||||
|     'enabled': boolean; |     'enabled': boolean; | ||||||
| } | } | ||||||
|  | /** | ||||||
|  |  *  | ||||||
|  |  * @export | ||||||
|  |  * @interface SystemConfigReverseGeocodingDto | ||||||
|  |  */ | ||||||
|  | export interface SystemConfigReverseGeocodingDto { | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {CitiesFile} | ||||||
|  |      * @memberof SystemConfigReverseGeocodingDto | ||||||
|  |      */ | ||||||
|  |     'citiesFileOverride': CitiesFile; | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {boolean} | ||||||
|  |      * @memberof SystemConfigReverseGeocodingDto | ||||||
|  |      */ | ||||||
|  |     'enabled': boolean; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  *  |  *  | ||||||
|  * @export |  * @export | ||||||
|   | |||||||
| @@ -49,11 +49,9 @@ These environment variables are used by the `docker-compose.yml` file and do **N | |||||||
|  |  | ||||||
| ## Geocoding | ## Geocoding | ||||||
|  |  | ||||||
| | Variable                           | Description                         |           Default            | Services      | | | Variable                           | Description                      |           Default            | Services      | | ||||||
| | :--------------------------------- | :---------------------------------- | :--------------------------: | :------------ | | | :--------------------------------- | :------------------------------- | :--------------------------: | :------------ | | ||||||
| | `DISABLE_REVERSE_GEOCODING`        | Disable Reverse Geocoding Precision |           `false`            | microservices | | | `REVERSE_GEOCODING_DUMP_DIRECTORY` | Reverse Geocoding Dump Directory | `./.reverse-geocoding-dump/` | microservices | | ||||||
| | `REVERSE_GEOCODING_PRECISION`      | Reverse Geocoding Precision         |             `3`              | microservices | |  | ||||||
| | `REVERSE_GEOCODING_DUMP_DIRECTORY` | Reverse Geocoding Dump Directory    | `./.reverse-geocoding-dump/` | microservices | |  | ||||||
|  |  | ||||||
| ## Ports | ## Ports | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								mobile/openapi/.openapi-generator/FILES
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								mobile/openapi/.openapi-generator/FILES
									
									
									
										generated
									
									
									
								
							| @@ -43,6 +43,7 @@ doc/CheckDuplicateAssetDto.md | |||||||
| doc/CheckDuplicateAssetResponseDto.md | doc/CheckDuplicateAssetResponseDto.md | ||||||
| doc/CheckExistingAssetsDto.md | doc/CheckExistingAssetsDto.md | ||||||
| doc/CheckExistingAssetsResponseDto.md | doc/CheckExistingAssetsResponseDto.md | ||||||
|  | doc/CitiesFile.md | ||||||
| doc/ClassificationConfig.md | doc/ClassificationConfig.md | ||||||
| doc/Colorspace.md | doc/Colorspace.md | ||||||
| doc/CreateAlbumDto.md | doc/CreateAlbumDto.md | ||||||
| @@ -126,6 +127,7 @@ doc/SystemConfigMachineLearningDto.md | |||||||
| doc/SystemConfigMapDto.md | doc/SystemConfigMapDto.md | ||||||
| doc/SystemConfigOAuthDto.md | doc/SystemConfigOAuthDto.md | ||||||
| doc/SystemConfigPasswordLoginDto.md | doc/SystemConfigPasswordLoginDto.md | ||||||
|  | doc/SystemConfigReverseGeocodingDto.md | ||||||
| doc/SystemConfigStorageTemplateDto.md | doc/SystemConfigStorageTemplateDto.md | ||||||
| doc/SystemConfigTemplateStorageOptionDto.md | doc/SystemConfigTemplateStorageOptionDto.md | ||||||
| doc/SystemConfigThumbnailDto.md | doc/SystemConfigThumbnailDto.md | ||||||
| @@ -207,6 +209,7 @@ 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 | ||||||
| lib/model/check_existing_assets_response_dto.dart | lib/model/check_existing_assets_response_dto.dart | ||||||
|  | lib/model/cities_file.dart | ||||||
| lib/model/classification_config.dart | lib/model/classification_config.dart | ||||||
| lib/model/clip_config.dart | lib/model/clip_config.dart | ||||||
| lib/model/clip_mode.dart | lib/model/clip_mode.dart | ||||||
| @@ -284,6 +287,7 @@ lib/model/system_config_machine_learning_dto.dart | |||||||
| lib/model/system_config_map_dto.dart | lib/model/system_config_map_dto.dart | ||||||
| lib/model/system_config_o_auth_dto.dart | lib/model/system_config_o_auth_dto.dart | ||||||
| lib/model/system_config_password_login_dto.dart | lib/model/system_config_password_login_dto.dart | ||||||
|  | lib/model/system_config_reverse_geocoding_dto.dart | ||||||
| lib/model/system_config_storage_template_dto.dart | lib/model/system_config_storage_template_dto.dart | ||||||
| lib/model/system_config_template_storage_option_dto.dart | lib/model/system_config_template_storage_option_dto.dart | ||||||
| lib/model/system_config_thumbnail_dto.dart | lib/model/system_config_thumbnail_dto.dart | ||||||
| @@ -343,6 +347,7 @@ 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 | ||||||
| test/check_existing_assets_response_dto_test.dart | test/check_existing_assets_response_dto_test.dart | ||||||
|  | test/cities_file_test.dart | ||||||
| test/classification_config_test.dart | test/classification_config_test.dart | ||||||
| test/clip_config_test.dart | test/clip_config_test.dart | ||||||
| test/clip_mode_test.dart | test/clip_mode_test.dart | ||||||
| @@ -429,6 +434,7 @@ test/system_config_machine_learning_dto_test.dart | |||||||
| test/system_config_map_dto_test.dart | test/system_config_map_dto_test.dart | ||||||
| test/system_config_o_auth_dto_test.dart | test/system_config_o_auth_dto_test.dart | ||||||
| test/system_config_password_login_dto_test.dart | test/system_config_password_login_dto_test.dart | ||||||
|  | test/system_config_reverse_geocoding_dto_test.dart | ||||||
| test/system_config_storage_template_dto_test.dart | test/system_config_storage_template_dto_test.dart | ||||||
| test/system_config_template_storage_option_dto_test.dart | test/system_config_template_storage_option_dto_test.dart | ||||||
| test/system_config_thumbnail_dto_test.dart | test/system_config_thumbnail_dto_test.dart | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								mobile/openapi/README.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								mobile/openapi/README.md
									
									
									
										generated
									
									
									
								
							| @@ -227,6 +227,7 @@ Class | Method | HTTP request | Description | |||||||
|  - [CheckDuplicateAssetResponseDto](doc//CheckDuplicateAssetResponseDto.md) |  - [CheckDuplicateAssetResponseDto](doc//CheckDuplicateAssetResponseDto.md) | ||||||
|  - [CheckExistingAssetsDto](doc//CheckExistingAssetsDto.md) |  - [CheckExistingAssetsDto](doc//CheckExistingAssetsDto.md) | ||||||
|  - [CheckExistingAssetsResponseDto](doc//CheckExistingAssetsResponseDto.md) |  - [CheckExistingAssetsResponseDto](doc//CheckExistingAssetsResponseDto.md) | ||||||
|  |  - [CitiesFile](doc//CitiesFile.md) | ||||||
|  - [ClassificationConfig](doc//ClassificationConfig.md) |  - [ClassificationConfig](doc//ClassificationConfig.md) | ||||||
|  - [Colorspace](doc//Colorspace.md) |  - [Colorspace](doc//Colorspace.md) | ||||||
|  - [CreateAlbumDto](doc//CreateAlbumDto.md) |  - [CreateAlbumDto](doc//CreateAlbumDto.md) | ||||||
| @@ -301,6 +302,7 @@ Class | Method | HTTP request | Description | |||||||
|  - [SystemConfigMapDto](doc//SystemConfigMapDto.md) |  - [SystemConfigMapDto](doc//SystemConfigMapDto.md) | ||||||
|  - [SystemConfigOAuthDto](doc//SystemConfigOAuthDto.md) |  - [SystemConfigOAuthDto](doc//SystemConfigOAuthDto.md) | ||||||
|  - [SystemConfigPasswordLoginDto](doc//SystemConfigPasswordLoginDto.md) |  - [SystemConfigPasswordLoginDto](doc//SystemConfigPasswordLoginDto.md) | ||||||
|  |  - [SystemConfigReverseGeocodingDto](doc//SystemConfigReverseGeocodingDto.md) | ||||||
|  - [SystemConfigStorageTemplateDto](doc//SystemConfigStorageTemplateDto.md) |  - [SystemConfigStorageTemplateDto](doc//SystemConfigStorageTemplateDto.md) | ||||||
|  - [SystemConfigTemplateStorageOptionDto](doc//SystemConfigTemplateStorageOptionDto.md) |  - [SystemConfigTemplateStorageOptionDto](doc//SystemConfigTemplateStorageOptionDto.md) | ||||||
|  - [SystemConfigThumbnailDto](doc//SystemConfigThumbnailDto.md) |  - [SystemConfigThumbnailDto](doc//SystemConfigThumbnailDto.md) | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								mobile/openapi/doc/CitiesFile.md
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								mobile/openapi/doc/CitiesFile.md
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | # openapi.model.CitiesFile | ||||||
|  | 
 | ||||||
|  | ## Load the model package | ||||||
|  | ```dart | ||||||
|  | import 'package:openapi/api.dart'; | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## Properties | ||||||
|  | Name | Type | Description | Notes | ||||||
|  | ------------ | ------------- | ------------- | ------------- | ||||||
|  | 
 | ||||||
|  | [[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/doc/ServerFeaturesDto.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								mobile/openapi/doc/ServerFeaturesDto.md
									
									
									
										generated
									
									
									
								
							| @@ -15,6 +15,7 @@ Name | Type | Description | Notes | |||||||
| **oauth** | **bool** |  |  | **oauth** | **bool** |  |  | ||||||
| **oauthAutoLaunch** | **bool** |  |  | **oauthAutoLaunch** | **bool** |  |  | ||||||
| **passwordLogin** | **bool** |  |  | **passwordLogin** | **bool** |  |  | ||||||
|  | **reverseGeocoding** | **bool** |  |  | ||||||
| **search** | **bool** |  |  | **search** | **bool** |  |  | ||||||
| **sidecar** | **bool** |  |  | **sidecar** | **bool** |  |  | ||||||
| **tagImage** | **bool** |  |  | **tagImage** | **bool** |  |  | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								mobile/openapi/doc/SystemConfigDto.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								mobile/openapi/doc/SystemConfigDto.md
									
									
									
										generated
									
									
									
								
							| @@ -14,6 +14,7 @@ Name | Type | Description | Notes | |||||||
| **map** | [**SystemConfigMapDto**](SystemConfigMapDto.md) |  |  | **map** | [**SystemConfigMapDto**](SystemConfigMapDto.md) |  |  | ||||||
| **oauth** | [**SystemConfigOAuthDto**](SystemConfigOAuthDto.md) |  |  | **oauth** | [**SystemConfigOAuthDto**](SystemConfigOAuthDto.md) |  |  | ||||||
| **passwordLogin** | [**SystemConfigPasswordLoginDto**](SystemConfigPasswordLoginDto.md) |  |  | **passwordLogin** | [**SystemConfigPasswordLoginDto**](SystemConfigPasswordLoginDto.md) |  |  | ||||||
|  | **reverseGeocoding** | [**SystemConfigReverseGeocodingDto**](SystemConfigReverseGeocodingDto.md) |  |  | ||||||
| **storageTemplate** | [**SystemConfigStorageTemplateDto**](SystemConfigStorageTemplateDto.md) |  |  | **storageTemplate** | [**SystemConfigStorageTemplateDto**](SystemConfigStorageTemplateDto.md) |  |  | ||||||
| **thumbnail** | [**SystemConfigThumbnailDto**](SystemConfigThumbnailDto.md) |  |  | **thumbnail** | [**SystemConfigThumbnailDto**](SystemConfigThumbnailDto.md) |  |  | ||||||
| 
 | 
 | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								mobile/openapi/doc/SystemConfigReverseGeocodingDto.md
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								mobile/openapi/doc/SystemConfigReverseGeocodingDto.md
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | # openapi.model.SystemConfigReverseGeocodingDto | ||||||
|  | 
 | ||||||
|  | ## Load the model package | ||||||
|  | ```dart | ||||||
|  | import 'package:openapi/api.dart'; | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## Properties | ||||||
|  | Name | Type | Description | Notes | ||||||
|  | ------------ | ------------- | ------------- | ------------- | ||||||
|  | **citiesFileOverride** | [**CitiesFile**](CitiesFile.md) |  |  | ||||||
|  | **enabled** | **bool** |  |  | ||||||
|  | 
 | ||||||
|  | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
							
								
								
									
										2
									
								
								mobile/openapi/lib/api.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								mobile/openapi/lib/api.dart
									
									
									
										generated
									
									
									
								
							| @@ -80,6 +80,7 @@ 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'; | ||||||
| part 'model/check_existing_assets_response_dto.dart'; | part 'model/check_existing_assets_response_dto.dart'; | ||||||
|  | part 'model/cities_file.dart'; | ||||||
| part 'model/classification_config.dart'; | part 'model/classification_config.dart'; | ||||||
| part 'model/colorspace.dart'; | part 'model/colorspace.dart'; | ||||||
| part 'model/create_album_dto.dart'; | part 'model/create_album_dto.dart'; | ||||||
| @@ -154,6 +155,7 @@ part 'model/system_config_machine_learning_dto.dart'; | |||||||
| part 'model/system_config_map_dto.dart'; | part 'model/system_config_map_dto.dart'; | ||||||
| part 'model/system_config_o_auth_dto.dart'; | part 'model/system_config_o_auth_dto.dart'; | ||||||
| part 'model/system_config_password_login_dto.dart'; | part 'model/system_config_password_login_dto.dart'; | ||||||
|  | part 'model/system_config_reverse_geocoding_dto.dart'; | ||||||
| part 'model/system_config_storage_template_dto.dart'; | part 'model/system_config_storage_template_dto.dart'; | ||||||
| part 'model/system_config_template_storage_option_dto.dart'; | part 'model/system_config_template_storage_option_dto.dart'; | ||||||
| part 'model/system_config_thumbnail_dto.dart'; | part 'model/system_config_thumbnail_dto.dart'; | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								mobile/openapi/lib/api_client.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								mobile/openapi/lib/api_client.dart
									
									
									
										generated
									
									
									
								
							| @@ -251,6 +251,8 @@ class ApiClient { | |||||||
|           return CheckExistingAssetsDto.fromJson(value); |           return CheckExistingAssetsDto.fromJson(value); | ||||||
|         case 'CheckExistingAssetsResponseDto': |         case 'CheckExistingAssetsResponseDto': | ||||||
|           return CheckExistingAssetsResponseDto.fromJson(value); |           return CheckExistingAssetsResponseDto.fromJson(value); | ||||||
|  |         case 'CitiesFile': | ||||||
|  |           return CitiesFileTypeTransformer().decode(value); | ||||||
|         case 'ClassificationConfig': |         case 'ClassificationConfig': | ||||||
|           return ClassificationConfig.fromJson(value); |           return ClassificationConfig.fromJson(value); | ||||||
|         case 'Colorspace': |         case 'Colorspace': | ||||||
| @@ -399,6 +401,8 @@ class ApiClient { | |||||||
|           return SystemConfigOAuthDto.fromJson(value); |           return SystemConfigOAuthDto.fromJson(value); | ||||||
|         case 'SystemConfigPasswordLoginDto': |         case 'SystemConfigPasswordLoginDto': | ||||||
|           return SystemConfigPasswordLoginDto.fromJson(value); |           return SystemConfigPasswordLoginDto.fromJson(value); | ||||||
|  |         case 'SystemConfigReverseGeocodingDto': | ||||||
|  |           return SystemConfigReverseGeocodingDto.fromJson(value); | ||||||
|         case 'SystemConfigStorageTemplateDto': |         case 'SystemConfigStorageTemplateDto': | ||||||
|           return SystemConfigStorageTemplateDto.fromJson(value); |           return SystemConfigStorageTemplateDto.fromJson(value); | ||||||
|         case 'SystemConfigTemplateStorageOptionDto': |         case 'SystemConfigTemplateStorageOptionDto': | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								mobile/openapi/lib/api_helper.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										3
									
								
								mobile/openapi/lib/api_helper.dart
									
									
									
										generated
									
									
									
								
							| @@ -70,6 +70,9 @@ String parameterToString(dynamic value) { | |||||||
|   if (value is CQMode) { |   if (value is CQMode) { | ||||||
|     return CQModeTypeTransformer().encode(value).toString(); |     return CQModeTypeTransformer().encode(value).toString(); | ||||||
|   } |   } | ||||||
|  |   if (value is CitiesFile) { | ||||||
|  |     return CitiesFileTypeTransformer().encode(value).toString(); | ||||||
|  |   } | ||||||
|   if (value is Colorspace) { |   if (value is Colorspace) { | ||||||
|     return ColorspaceTypeTransformer().encode(value).toString(); |     return ColorspaceTypeTransformer().encode(value).toString(); | ||||||
|   } |   } | ||||||
|   | |||||||
							
								
								
									
										91
									
								
								mobile/openapi/lib/model/cities_file.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								mobile/openapi/lib/model/cities_file.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,91 @@ | |||||||
|  | // | ||||||
|  | // 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 CitiesFile { | ||||||
|  |   /// Instantiate a new enum with the provided [value]. | ||||||
|  |   const CitiesFile._(this.value); | ||||||
|  | 
 | ||||||
|  |   /// The underlying value of this enum member. | ||||||
|  |   final String value; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   String toString() => value; | ||||||
|  | 
 | ||||||
|  |   String toJson() => value; | ||||||
|  | 
 | ||||||
|  |   static const cities15000 = CitiesFile._(r'cities15000'); | ||||||
|  |   static const cities5000 = CitiesFile._(r'cities5000'); | ||||||
|  |   static const cities1000 = CitiesFile._(r'cities1000'); | ||||||
|  |   static const cities500 = CitiesFile._(r'cities500'); | ||||||
|  | 
 | ||||||
|  |   /// List of all possible values in this [enum][CitiesFile]. | ||||||
|  |   static const values = <CitiesFile>[ | ||||||
|  |     cities15000, | ||||||
|  |     cities5000, | ||||||
|  |     cities1000, | ||||||
|  |     cities500, | ||||||
|  |   ]; | ||||||
|  | 
 | ||||||
|  |   static CitiesFile? fromJson(dynamic value) => CitiesFileTypeTransformer().decode(value); | ||||||
|  | 
 | ||||||
|  |   static List<CitiesFile>? listFromJson(dynamic json, {bool growable = false,}) { | ||||||
|  |     final result = <CitiesFile>[]; | ||||||
|  |     if (json is List && json.isNotEmpty) { | ||||||
|  |       for (final row in json) { | ||||||
|  |         final value = CitiesFile.fromJson(row); | ||||||
|  |         if (value != null) { | ||||||
|  |           result.add(value); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return result.toList(growable: growable); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Transformation class that can [encode] an instance of [CitiesFile] to String, | ||||||
|  | /// and [decode] dynamic data back to [CitiesFile]. | ||||||
|  | class CitiesFileTypeTransformer { | ||||||
|  |   factory CitiesFileTypeTransformer() => _instance ??= const CitiesFileTypeTransformer._(); | ||||||
|  | 
 | ||||||
|  |   const CitiesFileTypeTransformer._(); | ||||||
|  | 
 | ||||||
|  |   String encode(CitiesFile data) => data.value; | ||||||
|  | 
 | ||||||
|  |   /// Decodes a [dynamic value][data] to a CitiesFile. | ||||||
|  |   /// | ||||||
|  |   /// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully, | ||||||
|  |   /// then null is returned. However, if [allowNull] is false and the [dynamic value][data] | ||||||
|  |   /// cannot be decoded successfully, then an [UnimplementedError] is thrown. | ||||||
|  |   /// | ||||||
|  |   /// The [allowNull] is very handy when an API changes and a new enum value is added or removed, | ||||||
|  |   /// and users are still using an old app with the old code. | ||||||
|  |   CitiesFile? decode(dynamic data, {bool allowNull = true}) { | ||||||
|  |     if (data != null) { | ||||||
|  |       switch (data) { | ||||||
|  |         case r'cities15000': return CitiesFile.cities15000; | ||||||
|  |         case r'cities5000': return CitiesFile.cities5000; | ||||||
|  |         case r'cities1000': return CitiesFile.cities1000; | ||||||
|  |         case r'cities500': return CitiesFile.cities500; | ||||||
|  |         default: | ||||||
|  |           if (!allowNull) { | ||||||
|  |             throw ArgumentError('Unknown enum value to decode: $data'); | ||||||
|  |           } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return null; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Singleton [CitiesFileTypeTransformer] instance. | ||||||
|  |   static CitiesFileTypeTransformer? _instance; | ||||||
|  | } | ||||||
|  | 
 | ||||||
							
								
								
									
										10
									
								
								mobile/openapi/lib/model/server_features_dto.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										10
									
								
								mobile/openapi/lib/model/server_features_dto.dart
									
									
									
										generated
									
									
									
								
							| @@ -20,6 +20,7 @@ class ServerFeaturesDto { | |||||||
|     required this.oauth, |     required this.oauth, | ||||||
|     required this.oauthAutoLaunch, |     required this.oauthAutoLaunch, | ||||||
|     required this.passwordLogin, |     required this.passwordLogin, | ||||||
|  |     required this.reverseGeocoding, | ||||||
|     required this.search, |     required this.search, | ||||||
|     required this.sidecar, |     required this.sidecar, | ||||||
|     required this.tagImage, |     required this.tagImage, | ||||||
| @@ -39,6 +40,8 @@ class ServerFeaturesDto { | |||||||
| 
 | 
 | ||||||
|   bool passwordLogin; |   bool passwordLogin; | ||||||
| 
 | 
 | ||||||
|  |   bool reverseGeocoding; | ||||||
|  | 
 | ||||||
|   bool search; |   bool search; | ||||||
| 
 | 
 | ||||||
|   bool sidecar; |   bool sidecar; | ||||||
| @@ -54,6 +57,7 @@ class ServerFeaturesDto { | |||||||
|      other.oauth == oauth && |      other.oauth == oauth && | ||||||
|      other.oauthAutoLaunch == oauthAutoLaunch && |      other.oauthAutoLaunch == oauthAutoLaunch && | ||||||
|      other.passwordLogin == passwordLogin && |      other.passwordLogin == passwordLogin && | ||||||
|  |      other.reverseGeocoding == reverseGeocoding && | ||||||
|      other.search == search && |      other.search == search && | ||||||
|      other.sidecar == sidecar && |      other.sidecar == sidecar && | ||||||
|      other.tagImage == tagImage; |      other.tagImage == tagImage; | ||||||
| @@ -68,12 +72,13 @@ class ServerFeaturesDto { | |||||||
|     (oauth.hashCode) + |     (oauth.hashCode) + | ||||||
|     (oauthAutoLaunch.hashCode) + |     (oauthAutoLaunch.hashCode) + | ||||||
|     (passwordLogin.hashCode) + |     (passwordLogin.hashCode) + | ||||||
|  |     (reverseGeocoding.hashCode) + | ||||||
|     (search.hashCode) + |     (search.hashCode) + | ||||||
|     (sidecar.hashCode) + |     (sidecar.hashCode) + | ||||||
|     (tagImage.hashCode); |     (tagImage.hashCode); | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   String toString() => 'ServerFeaturesDto[clipEncode=$clipEncode, configFile=$configFile, facialRecognition=$facialRecognition, map=$map, oauth=$oauth, oauthAutoLaunch=$oauthAutoLaunch, passwordLogin=$passwordLogin, search=$search, sidecar=$sidecar, tagImage=$tagImage]'; |   String toString() => 'ServerFeaturesDto[clipEncode=$clipEncode, configFile=$configFile, facialRecognition=$facialRecognition, map=$map, oauth=$oauth, oauthAutoLaunch=$oauthAutoLaunch, passwordLogin=$passwordLogin, reverseGeocoding=$reverseGeocoding, search=$search, sidecar=$sidecar, tagImage=$tagImage]'; | ||||||
| 
 | 
 | ||||||
|   Map<String, dynamic> toJson() { |   Map<String, dynamic> toJson() { | ||||||
|     final json = <String, dynamic>{}; |     final json = <String, dynamic>{}; | ||||||
| @@ -84,6 +89,7 @@ class ServerFeaturesDto { | |||||||
|       json[r'oauth'] = this.oauth; |       json[r'oauth'] = this.oauth; | ||||||
|       json[r'oauthAutoLaunch'] = this.oauthAutoLaunch; |       json[r'oauthAutoLaunch'] = this.oauthAutoLaunch; | ||||||
|       json[r'passwordLogin'] = this.passwordLogin; |       json[r'passwordLogin'] = this.passwordLogin; | ||||||
|  |       json[r'reverseGeocoding'] = this.reverseGeocoding; | ||||||
|       json[r'search'] = this.search; |       json[r'search'] = this.search; | ||||||
|       json[r'sidecar'] = this.sidecar; |       json[r'sidecar'] = this.sidecar; | ||||||
|       json[r'tagImage'] = this.tagImage; |       json[r'tagImage'] = this.tagImage; | ||||||
| @@ -105,6 +111,7 @@ class ServerFeaturesDto { | |||||||
|         oauth: mapValueOfType<bool>(json, r'oauth')!, |         oauth: mapValueOfType<bool>(json, r'oauth')!, | ||||||
|         oauthAutoLaunch: mapValueOfType<bool>(json, r'oauthAutoLaunch')!, |         oauthAutoLaunch: mapValueOfType<bool>(json, r'oauthAutoLaunch')!, | ||||||
|         passwordLogin: mapValueOfType<bool>(json, r'passwordLogin')!, |         passwordLogin: mapValueOfType<bool>(json, r'passwordLogin')!, | ||||||
|  |         reverseGeocoding: mapValueOfType<bool>(json, r'reverseGeocoding')!, | ||||||
|         search: mapValueOfType<bool>(json, r'search')!, |         search: mapValueOfType<bool>(json, r'search')!, | ||||||
|         sidecar: mapValueOfType<bool>(json, r'sidecar')!, |         sidecar: mapValueOfType<bool>(json, r'sidecar')!, | ||||||
|         tagImage: mapValueOfType<bool>(json, r'tagImage')!, |         tagImage: mapValueOfType<bool>(json, r'tagImage')!, | ||||||
| @@ -162,6 +169,7 @@ class ServerFeaturesDto { | |||||||
|     'oauth', |     'oauth', | ||||||
|     'oauthAutoLaunch', |     'oauthAutoLaunch', | ||||||
|     'passwordLogin', |     'passwordLogin', | ||||||
|  |     'reverseGeocoding', | ||||||
|     'search', |     'search', | ||||||
|     'sidecar', |     'sidecar', | ||||||
|     'tagImage', |     'tagImage', | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								mobile/openapi/lib/model/system_config_dto.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										10
									
								
								mobile/openapi/lib/model/system_config_dto.dart
									
									
									
										generated
									
									
									
								
							| @@ -19,6 +19,7 @@ class SystemConfigDto { | |||||||
|     required this.map, |     required this.map, | ||||||
|     required this.oauth, |     required this.oauth, | ||||||
|     required this.passwordLogin, |     required this.passwordLogin, | ||||||
|  |     required this.reverseGeocoding, | ||||||
|     required this.storageTemplate, |     required this.storageTemplate, | ||||||
|     required this.thumbnail, |     required this.thumbnail, | ||||||
|   }); |   }); | ||||||
| @@ -35,6 +36,8 @@ class SystemConfigDto { | |||||||
| 
 | 
 | ||||||
|   SystemConfigPasswordLoginDto passwordLogin; |   SystemConfigPasswordLoginDto passwordLogin; | ||||||
| 
 | 
 | ||||||
|  |   SystemConfigReverseGeocodingDto reverseGeocoding; | ||||||
|  | 
 | ||||||
|   SystemConfigStorageTemplateDto storageTemplate; |   SystemConfigStorageTemplateDto storageTemplate; | ||||||
| 
 | 
 | ||||||
|   SystemConfigThumbnailDto thumbnail; |   SystemConfigThumbnailDto thumbnail; | ||||||
| @@ -47,6 +50,7 @@ class SystemConfigDto { | |||||||
|      other.map == map && |      other.map == map && | ||||||
|      other.oauth == oauth && |      other.oauth == oauth && | ||||||
|      other.passwordLogin == passwordLogin && |      other.passwordLogin == passwordLogin && | ||||||
|  |      other.reverseGeocoding == reverseGeocoding && | ||||||
|      other.storageTemplate == storageTemplate && |      other.storageTemplate == storageTemplate && | ||||||
|      other.thumbnail == thumbnail; |      other.thumbnail == thumbnail; | ||||||
| 
 | 
 | ||||||
| @@ -59,11 +63,12 @@ class SystemConfigDto { | |||||||
|     (map.hashCode) + |     (map.hashCode) + | ||||||
|     (oauth.hashCode) + |     (oauth.hashCode) + | ||||||
|     (passwordLogin.hashCode) + |     (passwordLogin.hashCode) + | ||||||
|  |     (reverseGeocoding.hashCode) + | ||||||
|     (storageTemplate.hashCode) + |     (storageTemplate.hashCode) + | ||||||
|     (thumbnail.hashCode); |     (thumbnail.hashCode); | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   String toString() => 'SystemConfigDto[ffmpeg=$ffmpeg, job=$job, machineLearning=$machineLearning, map=$map, oauth=$oauth, passwordLogin=$passwordLogin, storageTemplate=$storageTemplate, thumbnail=$thumbnail]'; |   String toString() => 'SystemConfigDto[ffmpeg=$ffmpeg, job=$job, machineLearning=$machineLearning, map=$map, oauth=$oauth, passwordLogin=$passwordLogin, reverseGeocoding=$reverseGeocoding, storageTemplate=$storageTemplate, thumbnail=$thumbnail]'; | ||||||
| 
 | 
 | ||||||
|   Map<String, dynamic> toJson() { |   Map<String, dynamic> toJson() { | ||||||
|     final json = <String, dynamic>{}; |     final json = <String, dynamic>{}; | ||||||
| @@ -73,6 +78,7 @@ class SystemConfigDto { | |||||||
|       json[r'map'] = this.map; |       json[r'map'] = this.map; | ||||||
|       json[r'oauth'] = this.oauth; |       json[r'oauth'] = this.oauth; | ||||||
|       json[r'passwordLogin'] = this.passwordLogin; |       json[r'passwordLogin'] = this.passwordLogin; | ||||||
|  |       json[r'reverseGeocoding'] = this.reverseGeocoding; | ||||||
|       json[r'storageTemplate'] = this.storageTemplate; |       json[r'storageTemplate'] = this.storageTemplate; | ||||||
|       json[r'thumbnail'] = this.thumbnail; |       json[r'thumbnail'] = this.thumbnail; | ||||||
|     return json; |     return json; | ||||||
| @@ -92,6 +98,7 @@ class SystemConfigDto { | |||||||
|         map: SystemConfigMapDto.fromJson(json[r'map'])!, |         map: SystemConfigMapDto.fromJson(json[r'map'])!, | ||||||
|         oauth: SystemConfigOAuthDto.fromJson(json[r'oauth'])!, |         oauth: SystemConfigOAuthDto.fromJson(json[r'oauth'])!, | ||||||
|         passwordLogin: SystemConfigPasswordLoginDto.fromJson(json[r'passwordLogin'])!, |         passwordLogin: SystemConfigPasswordLoginDto.fromJson(json[r'passwordLogin'])!, | ||||||
|  |         reverseGeocoding: SystemConfigReverseGeocodingDto.fromJson(json[r'reverseGeocoding'])!, | ||||||
|         storageTemplate: SystemConfigStorageTemplateDto.fromJson(json[r'storageTemplate'])!, |         storageTemplate: SystemConfigStorageTemplateDto.fromJson(json[r'storageTemplate'])!, | ||||||
|         thumbnail: SystemConfigThumbnailDto.fromJson(json[r'thumbnail'])!, |         thumbnail: SystemConfigThumbnailDto.fromJson(json[r'thumbnail'])!, | ||||||
|       ); |       ); | ||||||
| @@ -147,6 +154,7 @@ class SystemConfigDto { | |||||||
|     'map', |     'map', | ||||||
|     'oauth', |     'oauth', | ||||||
|     'passwordLogin', |     'passwordLogin', | ||||||
|  |     'reverseGeocoding', | ||||||
|     'storageTemplate', |     'storageTemplate', | ||||||
|     'thumbnail', |     'thumbnail', | ||||||
|   }; |   }; | ||||||
|   | |||||||
							
								
								
									
										106
									
								
								mobile/openapi/lib/model/system_config_reverse_geocoding_dto.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								mobile/openapi/lib/model/system_config_reverse_geocoding_dto.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | |||||||
|  | // | ||||||
|  | // 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 SystemConfigReverseGeocodingDto { | ||||||
|  |   /// Returns a new [SystemConfigReverseGeocodingDto] instance. | ||||||
|  |   SystemConfigReverseGeocodingDto({ | ||||||
|  |     required this.citiesFileOverride, | ||||||
|  |     required this.enabled, | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  |   CitiesFile citiesFileOverride; | ||||||
|  | 
 | ||||||
|  |   bool enabled; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   bool operator ==(Object other) => identical(this, other) || other is SystemConfigReverseGeocodingDto && | ||||||
|  |      other.citiesFileOverride == citiesFileOverride && | ||||||
|  |      other.enabled == enabled; | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   int get hashCode => | ||||||
|  |     // ignore: unnecessary_parenthesis | ||||||
|  |     (citiesFileOverride.hashCode) + | ||||||
|  |     (enabled.hashCode); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   String toString() => 'SystemConfigReverseGeocodingDto[citiesFileOverride=$citiesFileOverride, enabled=$enabled]'; | ||||||
|  | 
 | ||||||
|  |   Map<String, dynamic> toJson() { | ||||||
|  |     final json = <String, dynamic>{}; | ||||||
|  |       json[r'citiesFileOverride'] = this.citiesFileOverride; | ||||||
|  |       json[r'enabled'] = this.enabled; | ||||||
|  |     return json; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// Returns a new [SystemConfigReverseGeocodingDto] instance and imports its values from | ||||||
|  |   /// [value] if it's a [Map], null otherwise. | ||||||
|  |   // ignore: prefer_constructors_over_static_methods | ||||||
|  |   static SystemConfigReverseGeocodingDto? fromJson(dynamic value) { | ||||||
|  |     if (value is Map) { | ||||||
|  |       final json = value.cast<String, dynamic>(); | ||||||
|  | 
 | ||||||
|  |       return SystemConfigReverseGeocodingDto( | ||||||
|  |         citiesFileOverride: CitiesFile.fromJson(json[r'citiesFileOverride'])!, | ||||||
|  |         enabled: mapValueOfType<bool>(json, r'enabled')!, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |     return null; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static List<SystemConfigReverseGeocodingDto> listFromJson(dynamic json, {bool growable = false,}) { | ||||||
|  |     final result = <SystemConfigReverseGeocodingDto>[]; | ||||||
|  |     if (json is List && json.isNotEmpty) { | ||||||
|  |       for (final row in json) { | ||||||
|  |         final value = SystemConfigReverseGeocodingDto.fromJson(row); | ||||||
|  |         if (value != null) { | ||||||
|  |           result.add(value); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return result.toList(growable: growable); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   static Map<String, SystemConfigReverseGeocodingDto> mapFromJson(dynamic json) { | ||||||
|  |     final map = <String, SystemConfigReverseGeocodingDto>{}; | ||||||
|  |     if (json is Map && json.isNotEmpty) { | ||||||
|  |       json = json.cast<String, dynamic>(); // ignore: parameter_assignments | ||||||
|  |       for (final entry in json.entries) { | ||||||
|  |         final value = SystemConfigReverseGeocodingDto.fromJson(entry.value); | ||||||
|  |         if (value != null) { | ||||||
|  |           map[entry.key] = value; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return map; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // maps a json object with a list of SystemConfigReverseGeocodingDto-objects as value to a dart map | ||||||
|  |   static Map<String, List<SystemConfigReverseGeocodingDto>> mapListFromJson(dynamic json, {bool growable = false,}) { | ||||||
|  |     final map = <String, List<SystemConfigReverseGeocodingDto>>{}; | ||||||
|  |     if (json is Map && json.isNotEmpty) { | ||||||
|  |       // ignore: parameter_assignments | ||||||
|  |       json = json.cast<String, dynamic>(); | ||||||
|  |       for (final entry in json.entries) { | ||||||
|  |         map[entry.key] = SystemConfigReverseGeocodingDto.listFromJson(entry.value, growable: growable,); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return map; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /// The list of required keys that must be present in a JSON. | ||||||
|  |   static const requiredKeys = <String>{ | ||||||
|  |     'citiesFileOverride', | ||||||
|  |     'enabled', | ||||||
|  |   }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
							
								
								
									
										21
									
								
								mobile/openapi/test/cities_file_test.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								mobile/openapi/test/cities_file_test.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | // | ||||||
|  | // 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 CitiesFile | ||||||
|  | void main() { | ||||||
|  | 
 | ||||||
|  |   group('test CitiesFile', () { | ||||||
|  | 
 | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @@ -51,6 +51,11 @@ void main() { | |||||||
|       // TODO |       // TODO | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     // bool reverseGeocoding | ||||||
|  |     test('to test the property `reverseGeocoding`', () async { | ||||||
|  |       // TODO | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|     // bool search |     // bool search | ||||||
|     test('to test the property `search`', () async { |     test('to test the property `search`', () async { | ||||||
|       // TODO |       // TODO | ||||||
|   | |||||||
							
								
								
									
										5
									
								
								mobile/openapi/test/system_config_dto_test.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										5
									
								
								mobile/openapi/test/system_config_dto_test.dart
									
									
									
										generated
									
									
									
								
							| @@ -46,6 +46,11 @@ void main() { | |||||||
|       // TODO |       // TODO | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     // SystemConfigReverseGeocodingDto reverseGeocoding | ||||||
|  |     test('to test the property `reverseGeocoding`', () async { | ||||||
|  |       // TODO | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|     // SystemConfigStorageTemplateDto storageTemplate |     // SystemConfigStorageTemplateDto storageTemplate | ||||||
|     test('to test the property `storageTemplate`', () async { |     test('to test the property `storageTemplate`', () async { | ||||||
|       // TODO |       // TODO | ||||||
|   | |||||||
							
								
								
									
										32
									
								
								mobile/openapi/test/system_config_reverse_geocoding_dto_test.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								mobile/openapi/test/system_config_reverse_geocoding_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 SystemConfigReverseGeocodingDto | ||||||
|  | void main() { | ||||||
|  |   // final instance = SystemConfigReverseGeocodingDto(); | ||||||
|  | 
 | ||||||
|  |   group('test SystemConfigReverseGeocodingDto', () { | ||||||
|  |     // CitiesFile citiesFileOverride | ||||||
|  |     test('to test the property `citiesFileOverride`', () async { | ||||||
|  |       // TODO | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     // bool enabled | ||||||
|  |     test('to test the property `enabled`', () async { | ||||||
|  |       // TODO | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @@ -5914,6 +5914,15 @@ | |||||||
|         ], |         ], | ||||||
|         "type": "object" |         "type": "object" | ||||||
|       }, |       }, | ||||||
|  |       "CitiesFile": { | ||||||
|  |         "enum": [ | ||||||
|  |           "cities15000", | ||||||
|  |           "cities5000", | ||||||
|  |           "cities1000", | ||||||
|  |           "cities500" | ||||||
|  |         ], | ||||||
|  |         "type": "string" | ||||||
|  |       }, | ||||||
|       "ClassificationConfig": { |       "ClassificationConfig": { | ||||||
|         "properties": { |         "properties": { | ||||||
|           "enabled": { |           "enabled": { | ||||||
| @@ -7229,6 +7238,9 @@ | |||||||
|           "passwordLogin": { |           "passwordLogin": { | ||||||
|             "type": "boolean" |             "type": "boolean" | ||||||
|           }, |           }, | ||||||
|  |           "reverseGeocoding": { | ||||||
|  |             "type": "boolean" | ||||||
|  |           }, | ||||||
|           "search": { |           "search": { | ||||||
|             "type": "boolean" |             "type": "boolean" | ||||||
|           }, |           }, | ||||||
| @@ -7244,6 +7256,7 @@ | |||||||
|           "configFile", |           "configFile", | ||||||
|           "facialRecognition", |           "facialRecognition", | ||||||
|           "map", |           "map", | ||||||
|  |           "reverseGeocoding", | ||||||
|           "oauth", |           "oauth", | ||||||
|           "oauthAutoLaunch", |           "oauthAutoLaunch", | ||||||
|           "passwordLogin", |           "passwordLogin", | ||||||
| @@ -7590,6 +7603,9 @@ | |||||||
|           "passwordLogin": { |           "passwordLogin": { | ||||||
|             "$ref": "#/components/schemas/SystemConfigPasswordLoginDto" |             "$ref": "#/components/schemas/SystemConfigPasswordLoginDto" | ||||||
|           }, |           }, | ||||||
|  |           "reverseGeocoding": { | ||||||
|  |             "$ref": "#/components/schemas/SystemConfigReverseGeocodingDto" | ||||||
|  |           }, | ||||||
|           "storageTemplate": { |           "storageTemplate": { | ||||||
|             "$ref": "#/components/schemas/SystemConfigStorageTemplateDto" |             "$ref": "#/components/schemas/SystemConfigStorageTemplateDto" | ||||||
|           }, |           }, | ||||||
| @@ -7603,6 +7619,7 @@ | |||||||
|           "map", |           "map", | ||||||
|           "oauth", |           "oauth", | ||||||
|           "passwordLogin", |           "passwordLogin", | ||||||
|  |           "reverseGeocoding", | ||||||
|           "storageTemplate", |           "storageTemplate", | ||||||
|           "job", |           "job", | ||||||
|           "thumbnail" |           "thumbnail" | ||||||
| @@ -7843,6 +7860,21 @@ | |||||||
|         ], |         ], | ||||||
|         "type": "object" |         "type": "object" | ||||||
|       }, |       }, | ||||||
|  |       "SystemConfigReverseGeocodingDto": { | ||||||
|  |         "properties": { | ||||||
|  |           "citiesFileOverride": { | ||||||
|  |             "$ref": "#/components/schemas/CitiesFile" | ||||||
|  |           }, | ||||||
|  |           "enabled": { | ||||||
|  |             "type": "boolean" | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         "required": [ | ||||||
|  |           "citiesFileOverride", | ||||||
|  |           "enabled" | ||||||
|  |         ], | ||||||
|  |         "type": "object" | ||||||
|  |       }, | ||||||
|       "SystemConfigStorageTemplateDto": { |       "SystemConfigStorageTemplateDto": { | ||||||
|         "properties": { |         "properties": { | ||||||
|           "template": { |           "template": { | ||||||
|   | |||||||
| @@ -1,3 +1,5 @@ | |||||||
|  | import { InitOptions } from 'local-reverse-geocoder'; | ||||||
|  |  | ||||||
| export const IGeocodingRepository = 'IGeocodingRepository'; | export const IGeocodingRepository = 'IGeocodingRepository'; | ||||||
|  |  | ||||||
| export interface GeoPoint { | export interface GeoPoint { | ||||||
| @@ -12,7 +14,7 @@ export interface ReverseGeocodeResult { | |||||||
| } | } | ||||||
|  |  | ||||||
| export interface IGeocodingRepository { | export interface IGeocodingRepository { | ||||||
|   init(): Promise<void>; |   init(options: Partial<InitOptions>): Promise<void>; | ||||||
|   reverseGeocode(point: GeoPoint): Promise<ReverseGeocodeResult>; |   reverseGeocode(point: GeoPoint): Promise<ReverseGeocodeResult>; | ||||||
|   deleteCache(): Promise<void>; |   deleteCache(): Promise<void>; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -90,6 +90,7 @@ export class ServerFeaturesDto implements FeatureFlags { | |||||||
|   configFile!: boolean; |   configFile!: boolean; | ||||||
|   facialRecognition!: boolean; |   facialRecognition!: boolean; | ||||||
|   map!: boolean; |   map!: boolean; | ||||||
|  |   reverseGeocoding!: boolean; | ||||||
|   oauth!: boolean; |   oauth!: boolean; | ||||||
|   oauthAutoLaunch!: boolean; |   oauthAutoLaunch!: boolean; | ||||||
|   passwordLogin!: boolean; |   passwordLogin!: boolean; | ||||||
|   | |||||||
| @@ -151,6 +151,7 @@ describe(ServerInfoService.name, () => { | |||||||
|         clipEncode: true, |         clipEncode: true, | ||||||
|         facialRecognition: true, |         facialRecognition: true, | ||||||
|         map: true, |         map: true, | ||||||
|  |         reverseGeocoding: true, | ||||||
|         oauth: false, |         oauth: false, | ||||||
|         oauthAutoLaunch: false, |         oauthAutoLaunch: false, | ||||||
|         passwordLogin: true, |         passwordLogin: true, | ||||||
|   | |||||||
| @@ -0,0 +1,12 @@ | |||||||
|  | import { CitiesFile } from '@app/infra/entities'; | ||||||
|  | import { ApiProperty } from '@nestjs/swagger'; | ||||||
|  | import { IsBoolean, IsEnum } from 'class-validator'; | ||||||
|  |  | ||||||
|  | export class SystemConfigReverseGeocodingDto { | ||||||
|  |   @IsBoolean() | ||||||
|  |   enabled!: boolean; | ||||||
|  |  | ||||||
|  |   @IsEnum(CitiesFile) | ||||||
|  |   @ApiProperty({ enum: CitiesFile, enumName: 'CitiesFile' }) | ||||||
|  |   citiesFileOverride!: CitiesFile; | ||||||
|  | } | ||||||
| @@ -8,6 +8,7 @@ import { SystemConfigMachineLearningDto } from './system-config-machine-learning | |||||||
| import { SystemConfigMapDto } from './system-config-map.dto'; | import { SystemConfigMapDto } from './system-config-map.dto'; | ||||||
| import { SystemConfigOAuthDto } from './system-config-oauth.dto'; | import { SystemConfigOAuthDto } from './system-config-oauth.dto'; | ||||||
| import { SystemConfigPasswordLoginDto } from './system-config-password-login.dto'; | import { SystemConfigPasswordLoginDto } from './system-config-password-login.dto'; | ||||||
|  | import { SystemConfigReverseGeocodingDto } from './system-config-reverse-geocoding.dto'; | ||||||
| import { SystemConfigStorageTemplateDto } from './system-config-storage-template.dto'; | import { SystemConfigStorageTemplateDto } from './system-config-storage-template.dto'; | ||||||
|  |  | ||||||
| export class SystemConfigDto implements SystemConfig { | export class SystemConfigDto implements SystemConfig { | ||||||
| @@ -36,6 +37,11 @@ export class SystemConfigDto implements SystemConfig { | |||||||
|   @IsObject() |   @IsObject() | ||||||
|   passwordLogin!: SystemConfigPasswordLoginDto; |   passwordLogin!: SystemConfigPasswordLoginDto; | ||||||
|  |  | ||||||
|  |   @Type(() => SystemConfigReverseGeocodingDto) | ||||||
|  |   @ValidateNested() | ||||||
|  |   @IsObject() | ||||||
|  |   reverseGeocoding!: SystemConfigReverseGeocodingDto; | ||||||
|  |  | ||||||
|   @Type(() => SystemConfigStorageTemplateDto) |   @Type(() => SystemConfigStorageTemplateDto) | ||||||
|   @ValidateNested() |   @ValidateNested() | ||||||
|   @IsObject() |   @IsObject() | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| import { | import { | ||||||
|   AudioCodec, |   AudioCodec, | ||||||
|   CQMode, |   CQMode, | ||||||
|  |   CitiesFile, | ||||||
|   Colorspace, |   Colorspace, | ||||||
|   SystemConfig, |   SystemConfig, | ||||||
|   SystemConfigEntity, |   SystemConfigEntity, | ||||||
| @@ -81,6 +82,10 @@ export const defaults = Object.freeze<SystemConfig>({ | |||||||
|     enabled: true, |     enabled: true, | ||||||
|     tileUrl: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', |     tileUrl: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', | ||||||
|   }, |   }, | ||||||
|  |   reverseGeocoding: { | ||||||
|  |     enabled: true, | ||||||
|  |     citiesFileOverride: CitiesFile.CITIES_500, | ||||||
|  |   }, | ||||||
|   oauth: { |   oauth: { | ||||||
|     enabled: false, |     enabled: false, | ||||||
|     issuerUrl: '', |     issuerUrl: '', | ||||||
| @@ -115,6 +120,7 @@ export enum FeatureFlag { | |||||||
|   FACIAL_RECOGNITION = 'facialRecognition', |   FACIAL_RECOGNITION = 'facialRecognition', | ||||||
|   TAG_IMAGE = 'tagImage', |   TAG_IMAGE = 'tagImage', | ||||||
|   MAP = 'map', |   MAP = 'map', | ||||||
|  |   REVERSE_GEOCODING = 'reverseGeocoding', | ||||||
|   SIDECAR = 'sidecar', |   SIDECAR = 'sidecar', | ||||||
|   SEARCH = 'search', |   SEARCH = 'search', | ||||||
|   OAUTH = 'oauth', |   OAUTH = 'oauth', | ||||||
| @@ -177,6 +183,7 @@ export class SystemConfigCore { | |||||||
|       [FeatureFlag.FACIAL_RECOGNITION]: mlEnabled && config.machineLearning.facialRecognition.enabled, |       [FeatureFlag.FACIAL_RECOGNITION]: mlEnabled && config.machineLearning.facialRecognition.enabled, | ||||||
|       [FeatureFlag.TAG_IMAGE]: mlEnabled && config.machineLearning.classification.enabled, |       [FeatureFlag.TAG_IMAGE]: mlEnabled && config.machineLearning.classification.enabled, | ||||||
|       [FeatureFlag.MAP]: config.map.enabled, |       [FeatureFlag.MAP]: config.map.enabled, | ||||||
|  |       [FeatureFlag.REVERSE_GEOCODING]: config.reverseGeocoding.enabled, | ||||||
|       [FeatureFlag.SIDECAR]: true, |       [FeatureFlag.SIDECAR]: true, | ||||||
|       [FeatureFlag.SEARCH]: process.env.TYPESENSE_ENABLED !== 'false', |       [FeatureFlag.SEARCH]: process.env.TYPESENSE_ENABLED !== 'false', | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| import { | import { | ||||||
|   AudioCodec, |   AudioCodec, | ||||||
|   CQMode, |   CQMode, | ||||||
|  |   CitiesFile, | ||||||
|   Colorspace, |   Colorspace, | ||||||
|   SystemConfig, |   SystemConfig, | ||||||
|   SystemConfigEntity, |   SystemConfigEntity, | ||||||
| @@ -80,6 +81,10 @@ const updatedConfig = Object.freeze<SystemConfig>({ | |||||||
|     enabled: true, |     enabled: true, | ||||||
|     tileUrl: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', |     tileUrl: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', | ||||||
|   }, |   }, | ||||||
|  |   reverseGeocoding: { | ||||||
|  |     enabled: true, | ||||||
|  |     citiesFileOverride: CitiesFile.CITIES_500, | ||||||
|  |   }, | ||||||
|   oauth: { |   oauth: { | ||||||
|     autoLaunch: true, |     autoLaunch: true, | ||||||
|     autoRegister: true, |     autoRegister: true, | ||||||
|   | |||||||
| @@ -64,6 +64,9 @@ export enum SystemConfigKey { | |||||||
|   MAP_ENABLED = 'map.enabled', |   MAP_ENABLED = 'map.enabled', | ||||||
|   MAP_TILE_URL = 'map.tileUrl', |   MAP_TILE_URL = 'map.tileUrl', | ||||||
|  |  | ||||||
|  |   REVERSE_GEOCODING_ENABLED = 'reverseGeocoding.enabled', | ||||||
|  |   REVERSE_GEOCODING_CITIES_FILE_OVERRIDE = 'reverseGeocoding.citiesFileOverride', | ||||||
|  |  | ||||||
|   OAUTH_ENABLED = 'oauth.enabled', |   OAUTH_ENABLED = 'oauth.enabled', | ||||||
|   OAUTH_ISSUER_URL = 'oauth.issuerUrl', |   OAUTH_ISSUER_URL = 'oauth.issuerUrl', | ||||||
|   OAUTH_CLIENT_ID = 'oauth.clientId', |   OAUTH_CLIENT_ID = 'oauth.clientId', | ||||||
| @@ -130,6 +133,13 @@ export enum Colorspace { | |||||||
|   P3 = 'p3', |   P3 = 'p3', | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export enum CitiesFile { | ||||||
|  |   CITIES_15000 = 'cities15000', | ||||||
|  |   CITIES_5000 = 'cities5000', | ||||||
|  |   CITIES_1000 = 'cities1000', | ||||||
|  |   CITIES_500 = 'cities500', | ||||||
|  | } | ||||||
|  |  | ||||||
| export interface SystemConfig { | export interface SystemConfig { | ||||||
|   ffmpeg: { |   ffmpeg: { | ||||||
|     crf: number; |     crf: number; | ||||||
| @@ -175,6 +185,10 @@ export interface SystemConfig { | |||||||
|     enabled: boolean; |     enabled: boolean; | ||||||
|     tileUrl: string; |     tileUrl: string; | ||||||
|   }; |   }; | ||||||
|  |   reverseGeocoding: { | ||||||
|  |     enabled: boolean; | ||||||
|  |     citiesFileOverride: CitiesFile; | ||||||
|  |   }; | ||||||
|   oauth: { |   oauth: { | ||||||
|     enabled: boolean; |     enabled: boolean; | ||||||
|     issuerUrl: string; |     issuerUrl: string; | ||||||
|   | |||||||
| @@ -2,7 +2,6 @@ import { QueueName } from '@app/domain'; | |||||||
| import { RegisterQueueOptions } from '@nestjs/bullmq'; | import { RegisterQueueOptions } from '@nestjs/bullmq'; | ||||||
| import { QueueOptions } from 'bullmq'; | import { QueueOptions } from 'bullmq'; | ||||||
| import { RedisOptions } from 'ioredis'; | import { RedisOptions } from 'ioredis'; | ||||||
| import { InitOptions } from 'local-reverse-geocoder'; |  | ||||||
| import { ConfigurationOptions } from 'typesense/lib/Typesense/Configuration'; | import { ConfigurationOptions } from 'typesense/lib/Typesense/Configuration'; | ||||||
|  |  | ||||||
| function parseRedisConfig(): RedisOptions { | function parseRedisConfig(): RedisOptions { | ||||||
| @@ -72,20 +71,5 @@ function parseTypeSenseConfig(): ConfigurationOptions { | |||||||
|  |  | ||||||
| export const typesenseConfig: ConfigurationOptions = parseTypeSenseConfig(); | export const typesenseConfig: ConfigurationOptions = parseTypeSenseConfig(); | ||||||
|  |  | ||||||
| function parseLocalGeocodingConfig(): InitOptions { | export const REVERSE_GEOCODING_DUMP_DIRECTORY = | ||||||
|   const precision = Number(process.env.REVERSE_GEOCODING_PRECISION); |   process.env.REVERSE_GEOCODING_DUMP_DIRECTORY || process.cwd() + '/.reverse-geocoding-dump/'; | ||||||
|  |  | ||||||
|   return { |  | ||||||
|     citiesFileOverride: precision ? ['cities15000', 'cities5000', 'cities1000', 'cities500'][precision] : undefined, |  | ||||||
|     load: { |  | ||||||
|       admin1: true, |  | ||||||
|       admin2: true, |  | ||||||
|       admin3And4: false, |  | ||||||
|       alternateNames: false, |  | ||||||
|     }, |  | ||||||
|     countries: [], |  | ||||||
|     dumpDirectory: process.env.REVERSE_GEOCODING_DUMP_DIRECTORY || process.cwd() + '/.reverse-geocoding-dump/', |  | ||||||
|   }; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export const localGeocodingConfig: InitOptions = parseLocalGeocodingConfig(); |  | ||||||
|   | |||||||
| @@ -1,9 +1,9 @@ | |||||||
| import { GeoPoint, IGeocodingRepository, ReverseGeocodeResult } from '@app/domain'; | import { GeoPoint, IGeocodingRepository, ReverseGeocodeResult } from '@app/domain'; | ||||||
| import { localGeocodingConfig } from '@app/infra'; | import { REVERSE_GEOCODING_DUMP_DIRECTORY } from '@app/infra'; | ||||||
| import { Injectable, Logger } from '@nestjs/common'; | import { Injectable, Logger } from '@nestjs/common'; | ||||||
| import { readdir, rm } from 'fs/promises'; | import { readdir, rm } from 'fs/promises'; | ||||||
| import { getName } from 'i18n-iso-countries'; | import { getName } from 'i18n-iso-countries'; | ||||||
| import geocoder, { AddressObject } from 'local-reverse-geocoder'; | import geocoder, { AddressObject, InitOptions } from 'local-reverse-geocoder'; | ||||||
| import path from 'path'; | import path from 'path'; | ||||||
| import { promisify } from 'util'; | import { promisify } from 'util'; | ||||||
|  |  | ||||||
| @@ -18,19 +18,33 @@ export type GeoData = AddressObject & { | |||||||
|   admin2Code?: AdminCode | string; |   admin2Code?: AdminCode | string; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| const init = (): Promise<void> => new Promise<void>((resolve) => geocoder.init(localGeocodingConfig, resolve)); |  | ||||||
| const lookup = promisify<GeoPoint[], number, AddressObject[][]>(geocoder.lookUp).bind(geocoder); | const lookup = promisify<GeoPoint[], number, AddressObject[][]>(geocoder.lookUp).bind(geocoder); | ||||||
|  |  | ||||||
| @Injectable() | @Injectable() | ||||||
| export class GeocodingRepository implements IGeocodingRepository { | export class GeocodingRepository implements IGeocodingRepository { | ||||||
|   private logger = new Logger(GeocodingRepository.name); |   private logger = new Logger(GeocodingRepository.name); | ||||||
|  |  | ||||||
|   async init(): Promise<void> { |   async init(options: Partial<InitOptions>): Promise<void> { | ||||||
|     await init(); |     return new Promise<void>((resolve) => { | ||||||
|  |       geocoder.init( | ||||||
|  |         { | ||||||
|  |           load: { | ||||||
|  |             admin1: true, | ||||||
|  |             admin2: true, | ||||||
|  |             admin3And4: false, | ||||||
|  |             alternateNames: false, | ||||||
|  |           }, | ||||||
|  |           countries: [], | ||||||
|  |           dumpDirectory: REVERSE_GEOCODING_DUMP_DIRECTORY, | ||||||
|  |           ...options, | ||||||
|  |         }, | ||||||
|  |         resolve, | ||||||
|  |       ); | ||||||
|  |     }); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   async deleteCache() { |   async deleteCache() { | ||||||
|     const dumpDirectory = localGeocodingConfig.dumpDirectory; |     const dumpDirectory = REVERSE_GEOCODING_DUMP_DIRECTORY; | ||||||
|     if (dumpDirectory) { |     if (dumpDirectory) { | ||||||
|       // delete contents |       // delete contents | ||||||
|       const items = await readdir(dumpDirectory, { withFileTypes: true }); |       const items = await readdir(dumpDirectory, { withFileTypes: true }); | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| import { | import { | ||||||
|  |   FeatureFlag, | ||||||
|   IAlbumRepository, |   IAlbumRepository, | ||||||
|   IAssetRepository, |   IAssetRepository, | ||||||
|   IBaseJob, |   IBaseJob, | ||||||
| @@ -7,17 +8,18 @@ import { | |||||||
|   IGeocodingRepository, |   IGeocodingRepository, | ||||||
|   IJobRepository, |   IJobRepository, | ||||||
|   IStorageRepository, |   IStorageRepository, | ||||||
|  |   ISystemConfigRepository, | ||||||
|   JobName, |   JobName, | ||||||
|   JOBS_ASSET_PAGINATION_SIZE, |   JOBS_ASSET_PAGINATION_SIZE, | ||||||
|   QueueName, |   QueueName, | ||||||
|   StorageCore, |   StorageCore, | ||||||
|   StorageFolder, |   StorageFolder, | ||||||
|  |   SystemConfigCore, | ||||||
|   usePagination, |   usePagination, | ||||||
|   WithoutProperty, |   WithoutProperty, | ||||||
| } from '@app/domain'; | } from '@app/domain'; | ||||||
| import { AssetEntity, AssetType, ExifEntity } from '@app/infra/entities'; | import { AssetEntity, AssetType, ExifEntity } from '@app/infra/entities'; | ||||||
| import { Inject, Logger } from '@nestjs/common'; | import { Inject, Logger } from '@nestjs/common'; | ||||||
| import { ConfigService } from '@nestjs/config'; |  | ||||||
| import { DefaultReadTaskOptions, ExifDateTime, exiftool, ReadTaskOptions, Tags } from 'exiftool-vendored'; | import { DefaultReadTaskOptions, ExifDateTime, exiftool, ReadTaskOptions, Tags } from 'exiftool-vendored'; | ||||||
| import { firstDateTime } from 'exiftool-vendored/dist/FirstDateTime'; | import { firstDateTime } from 'exiftool-vendored/dist/FirstDateTime'; | ||||||
| import * as geotz from 'geo-tz'; | import * as geotz from 'geo-tz'; | ||||||
| @@ -51,8 +53,9 @@ const validate = <T>(value: T): T | null => (typeof value === 'string' ? null : | |||||||
|  |  | ||||||
| export class MetadataExtractionProcessor { | export class MetadataExtractionProcessor { | ||||||
|   private logger = new Logger(MetadataExtractionProcessor.name); |   private logger = new Logger(MetadataExtractionProcessor.name); | ||||||
|   private reverseGeocodingEnabled: boolean; |  | ||||||
|   private storageCore: StorageCore; |   private storageCore: StorageCore; | ||||||
|  |   private configCore: SystemConfigCore; | ||||||
|  |   private oldCities?: string; | ||||||
|  |  | ||||||
|   constructor( |   constructor( | ||||||
|     @Inject(IAssetRepository) private assetRepository: IAssetRepository, |     @Inject(IAssetRepository) private assetRepository: IAssetRepository, | ||||||
| @@ -61,31 +64,35 @@ export class MetadataExtractionProcessor { | |||||||
|     @Inject(IGeocodingRepository) private geocodingRepository: IGeocodingRepository, |     @Inject(IGeocodingRepository) private geocodingRepository: IGeocodingRepository, | ||||||
|     @Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository, |     @Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository, | ||||||
|     @Inject(IStorageRepository) private storageRepository: IStorageRepository, |     @Inject(IStorageRepository) private storageRepository: IStorageRepository, | ||||||
|  |     @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository, | ||||||
|     configService: ConfigService, |  | ||||||
|   ) { |   ) { | ||||||
|     this.reverseGeocodingEnabled = !configService.get('DISABLE_REVERSE_GEOCODING'); |  | ||||||
|     this.storageCore = new StorageCore(storageRepository); |     this.storageCore = new StorageCore(storageRepository); | ||||||
|  |     this.configCore = new SystemConfigCore(configRepository); | ||||||
|  |     this.configCore.config$.subscribe(() => this.init()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   async init(deleteCache = false) { |   async init(deleteCache = false) { | ||||||
|     this.logger.log(`Reverse geocoding is ${this.reverseGeocodingEnabled ? 'enabled' : 'disabled'}`); |     const { reverseGeocoding } = await this.configCore.getConfig(); | ||||||
|     if (!this.reverseGeocodingEnabled) { |     const { citiesFileOverride } = reverseGeocoding; | ||||||
|  |  | ||||||
|  |     if (!reverseGeocoding.enabled) { | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     try { |     try { | ||||||
|       if (deleteCache) { |       if (deleteCache) { | ||||||
|         await this.geocodingRepository.deleteCache(); |         await this.geocodingRepository.deleteCache(); | ||||||
|  |       } else if (this.oldCities && this.oldCities === citiesFileOverride) { | ||||||
|  |         return; | ||||||
|       } |       } | ||||||
|       this.logger.log('Initializing Reverse Geocoding'); |  | ||||||
|  |  | ||||||
|       await this.jobRepository.pause(QueueName.METADATA_EXTRACTION); |       await this.jobRepository.pause(QueueName.METADATA_EXTRACTION); | ||||||
|       await this.geocodingRepository.init(); |       await this.geocodingRepository.init({ citiesFileOverride }); | ||||||
|       await this.jobRepository.resume(QueueName.METADATA_EXTRACTION); |       await this.jobRepository.resume(QueueName.METADATA_EXTRACTION); | ||||||
|  |  | ||||||
|       this.logger.log('Reverse Geocoding Initialized'); |       this.logger.log(`Initialized local reverse geocoder with ${citiesFileOverride}`); | ||||||
|     } catch (error: any) { |       this.oldCities = citiesFileOverride; | ||||||
|  |     } catch (error: Error | any) { | ||||||
|       this.logger.error(`Unable to initialize reverse geocoding: ${error}`, error?.stack); |       this.logger.error(`Unable to initialize reverse geocoding: ${error}`, error?.stack); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @@ -161,7 +168,7 @@ export class MetadataExtractionProcessor { | |||||||
|  |  | ||||||
|   private async applyReverseGeocoding(asset: AssetEntity, exifData: ExifEntity) { |   private async applyReverseGeocoding(asset: AssetEntity, exifData: ExifEntity) { | ||||||
|     const { latitude, longitude } = exifData; |     const { latitude, longitude } = exifData; | ||||||
|     if (!this.reverseGeocodingEnabled || !longitude || !latitude) { |     if (!(await this.configCore.hasFeature(FeatureFlag.REVERSE_GEOCODING)) || !longitude || !latitude) { | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -85,6 +85,7 @@ describe(`${ServerInfoController.name} (e2e)`, () => { | |||||||
|         configFile: false, |         configFile: false, | ||||||
|         facialRecognition: true, |         facialRecognition: true, | ||||||
|         map: true, |         map: true, | ||||||
|  |         reverseGeocoding: true, | ||||||
|         oauth: false, |         oauth: false, | ||||||
|         oauthAutoLaunch: false, |         oauthAutoLaunch: false, | ||||||
|         passwordLogin: true, |         passwordLogin: true, | ||||||
|   | |||||||
							
								
								
									
										49
									
								
								web/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										49
									
								
								web/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							| @@ -1055,6 +1055,22 @@ export interface CheckExistingAssetsResponseDto { | |||||||
|      */ |      */ | ||||||
|     'existingIds': Array<string>; |     'existingIds': Array<string>; | ||||||
| } | } | ||||||
|  | /** | ||||||
|  |  *  | ||||||
|  |  * @export | ||||||
|  |  * @enum {string} | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | export const CitiesFile = { | ||||||
|  |     Cities15000: 'cities15000', | ||||||
|  |     Cities5000: 'cities5000', | ||||||
|  |     Cities1000: 'cities1000', | ||||||
|  |     Cities500: 'cities500' | ||||||
|  | } as const; | ||||||
|  | 
 | ||||||
|  | export type CitiesFile = typeof CitiesFile[keyof typeof CitiesFile]; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  *  |  *  | ||||||
|  * @export |  * @export | ||||||
| @@ -2650,6 +2666,12 @@ export interface ServerFeaturesDto { | |||||||
|      * @memberof ServerFeaturesDto |      * @memberof ServerFeaturesDto | ||||||
|      */ |      */ | ||||||
|     'passwordLogin': boolean; |     'passwordLogin': boolean; | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {boolean} | ||||||
|  |      * @memberof ServerFeaturesDto | ||||||
|  |      */ | ||||||
|  |     'reverseGeocoding': boolean; | ||||||
|     /** |     /** | ||||||
|      *  |      *  | ||||||
|      * @type {boolean} |      * @type {boolean} | ||||||
| @@ -3093,6 +3115,12 @@ export interface SystemConfigDto { | |||||||
|      * @memberof SystemConfigDto |      * @memberof SystemConfigDto | ||||||
|      */ |      */ | ||||||
|     'passwordLogin': SystemConfigPasswordLoginDto; |     'passwordLogin': SystemConfigPasswordLoginDto; | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {SystemConfigReverseGeocodingDto} | ||||||
|  |      * @memberof SystemConfigDto | ||||||
|  |      */ | ||||||
|  |     'reverseGeocoding': SystemConfigReverseGeocodingDto; | ||||||
|     /** |     /** | ||||||
|      *  |      *  | ||||||
|      * @type {SystemConfigStorageTemplateDto} |      * @type {SystemConfigStorageTemplateDto} | ||||||
| @@ -3438,6 +3466,27 @@ export interface SystemConfigPasswordLoginDto { | |||||||
|      */ |      */ | ||||||
|     'enabled': boolean; |     'enabled': boolean; | ||||||
| } | } | ||||||
|  | /** | ||||||
|  |  *  | ||||||
|  |  * @export | ||||||
|  |  * @interface SystemConfigReverseGeocodingDto | ||||||
|  |  */ | ||||||
|  | export interface SystemConfigReverseGeocodingDto { | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {CitiesFile} | ||||||
|  |      * @memberof SystemConfigReverseGeocodingDto | ||||||
|  |      */ | ||||||
|  |     'citiesFileOverride': CitiesFile; | ||||||
|  |     /** | ||||||
|  |      *  | ||||||
|  |      * @type {boolean} | ||||||
|  |      * @memberof SystemConfigReverseGeocodingDto | ||||||
|  |      */ | ||||||
|  |     'enabled': boolean; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  *  |  *  | ||||||
|  * @export |  * @export | ||||||
|   | |||||||
| @@ -4,23 +4,25 @@ | |||||||
|     NotificationType, |     NotificationType, | ||||||
|   } from '$lib/components/shared-components/notification/notification'; |   } from '$lib/components/shared-components/notification/notification'; | ||||||
|   import { handleError } from '$lib/utils/handle-error'; |   import { handleError } from '$lib/utils/handle-error'; | ||||||
|   import { api, SystemConfigMapDto } from '@api'; |   import { api, CitiesFile, SystemConfigDto } from '@api'; | ||||||
|   import { isEqual } from 'lodash-es'; |   import { cloneDeep, isEqual } from 'lodash-es'; | ||||||
|   import { fade } from 'svelte/transition'; |   import { fade } from 'svelte/transition'; | ||||||
|  |   import SettingAccordion from '../setting-accordion.svelte'; | ||||||
|   import SettingButtonsRow from '../setting-buttons-row.svelte'; |   import SettingButtonsRow from '../setting-buttons-row.svelte'; | ||||||
|   import SettingSwitch from '../setting-switch.svelte'; |  | ||||||
|   import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte'; |   import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte'; | ||||||
|  |   import SettingSwitch from '../setting-switch.svelte'; | ||||||
|  |   import SettingSelect from '../setting-select.svelte'; | ||||||
|  |  | ||||||
|   export let mapConfig: SystemConfigMapDto; // this is the config that is being edited |   export let config: SystemConfigDto; // this is the config that is being edited | ||||||
|   export let disabled = false; |   export let disabled = false; | ||||||
|  |  | ||||||
|   let savedConfig: SystemConfigMapDto; |   let savedConfig: SystemConfigDto; | ||||||
|   let defaultConfig: SystemConfigMapDto; |   let defaultConfig: SystemConfigDto; | ||||||
|  |  | ||||||
|   async function getConfigs() { |   async function refreshConfig() { | ||||||
|     [savedConfig, defaultConfig] = await Promise.all([ |     [savedConfig, defaultConfig] = await Promise.all([ | ||||||
|       api.systemConfigApi.getConfig().then((res) => res.data.map), |       api.systemConfigApi.getConfig().then((res) => res.data), | ||||||
|       api.systemConfigApi.getDefaults().then((res) => res.data.map), |       api.systemConfigApi.getDefaults().then((res) => res.data), | ||||||
|     ]); |     ]); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -28,11 +30,21 @@ | |||||||
|     try { |     try { | ||||||
|       const { data: current } = await api.systemConfigApi.getConfig(); |       const { data: current } = await api.systemConfigApi.getConfig(); | ||||||
|       const { data: updated } = await api.systemConfigApi.updateConfig({ |       const { data: updated } = await api.systemConfigApi.updateConfig({ | ||||||
|         systemConfigDto: { ...current, map: mapConfig }, |         systemConfigDto: { | ||||||
|  |           ...current, | ||||||
|  |           map: { | ||||||
|  |             enabled: config.map.enabled, | ||||||
|  |             tileUrl: config.map.tileUrl, | ||||||
|  |           }, | ||||||
|  |           reverseGeocoding: { | ||||||
|  |             enabled: config.reverseGeocoding.enabled, | ||||||
|  |             citiesFileOverride: config.reverseGeocoding.citiesFileOverride, | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|       }); |       }); | ||||||
|  |  | ||||||
|       mapConfig = { ...updated.map }; |       config = cloneDeep(updated); | ||||||
|       savedConfig = { ...updated.map }; |       savedConfig = cloneDeep(updated); | ||||||
|  |  | ||||||
|       notificationController.show({ message: 'Settings saved', type: NotificationType.Info }); |       notificationController.show({ message: 'Settings saved', type: NotificationType.Info }); | ||||||
|     } catch (error) { |     } catch (error) { | ||||||
| @@ -43,8 +55,8 @@ | |||||||
|   async function reset() { |   async function reset() { | ||||||
|     const { data: resetConfig } = await api.systemConfigApi.getConfig(); |     const { data: resetConfig } = await api.systemConfigApi.getConfig(); | ||||||
|  |  | ||||||
|     mapConfig = { ...resetConfig.map }; |     config = cloneDeep(resetConfig); | ||||||
|     savedConfig = { ...resetConfig.map }; |     savedConfig = cloneDeep(resetConfig); | ||||||
|  |  | ||||||
|     notificationController.show({ |     notificationController.show({ | ||||||
|       message: 'Reset settings to the recent saved settings', |       message: 'Reset settings to the recent saved settings', | ||||||
| @@ -55,8 +67,8 @@ | |||||||
|   async function resetToDefault() { |   async function resetToDefault() { | ||||||
|     const { data: configs } = await api.systemConfigApi.getDefaults(); |     const { data: configs } = await api.systemConfigApi.getDefaults(); | ||||||
|  |  | ||||||
|     mapConfig = { ...configs.map }; |     config = cloneDeep(configs); | ||||||
|     defaultConfig = { ...configs.map }; |     defaultConfig = cloneDeep(configs); | ||||||
|  |  | ||||||
|     notificationController.show({ |     notificationController.show({ | ||||||
|       message: 'Reset map settings to default', |       message: 'Reset map settings to default', | ||||||
| @@ -65,30 +77,81 @@ | |||||||
|   } |   } | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <div> | <div class="mt-2"> | ||||||
|   {#await getConfigs() then} |   {#await refreshConfig() then} | ||||||
|     <div in:fade={{ duration: 500 }}> |     <div in:fade={{ duration: 500 }}> | ||||||
|       <form autocomplete="off" on:submit|preventDefault> |       <form autocomplete="off" on:submit|preventDefault> | ||||||
|         <div class="ml-4 mt-4 flex flex-col gap-4"> |         <div class="flex flex-col gap-4"> | ||||||
|           <SettingSwitch title="ENABLED" {disabled} subtitle="Enable map features" bind:checked={mapConfig.enabled} /> |           <SettingAccordion title="Map Settings" subtitle="Manage map settings"> | ||||||
|  |             <div class="ml-4 mt-4 flex flex-col gap-4"> | ||||||
|  |               <SettingSwitch | ||||||
|  |                 title="ENABLED" | ||||||
|  |                 {disabled} | ||||||
|  |                 subtitle="Enable map features" | ||||||
|  |                 bind:checked={config.map.enabled} | ||||||
|  |               /> | ||||||
|  |  | ||||||
|           <hr /> |               <hr /> | ||||||
|  |  | ||||||
|           <SettingInputField |               <SettingInputField | ||||||
|             inputType={SettingInputFieldType.TEXT} |                 inputType={SettingInputFieldType.TEXT} | ||||||
|             label="Tile URL" |                 label="Tile URL" | ||||||
|             desc="URL to a leaflet compatible tile server" |                 desc="URL to a leaflet compatible tile server" | ||||||
|             bind:value={mapConfig.tileUrl} |                 bind:value={config.map.tileUrl} | ||||||
|             required={true} |                 required={true} | ||||||
|             disabled={disabled || !mapConfig.enabled} |                 disabled={disabled || !config.map.enabled} | ||||||
|             isEdited={mapConfig.tileUrl !== savedConfig.tileUrl} |                 isEdited={config.map.tileUrl !== savedConfig.map.tileUrl} | ||||||
|           /> |               /> | ||||||
|  |             </div></SettingAccordion | ||||||
|  |           > | ||||||
|  |  | ||||||
|  |           <SettingAccordion title="Reverse Geocoding Settings"> | ||||||
|  |             <svelte:fragment slot="subtitle"> | ||||||
|  |               <p class="text-sm dark:text-immich-dark-fg"> | ||||||
|  |                 Manage <a | ||||||
|  |                   href="https://immich.app/docs/features/reverse-geocoding" | ||||||
|  |                   class="underline" | ||||||
|  |                   target="_blank" | ||||||
|  |                   rel="noreferrer">Reverse Geocoding</a | ||||||
|  |                 > settings | ||||||
|  |               </p> | ||||||
|  |             </svelte:fragment> | ||||||
|  |             <div class="ml-4 mt-4 flex flex-col gap-4"> | ||||||
|  |               <SettingSwitch | ||||||
|  |                 title="ENABLED" | ||||||
|  |                 {disabled} | ||||||
|  |                 subtitle="Enable reverse geocoding" | ||||||
|  |                 bind:checked={config.reverseGeocoding.enabled} | ||||||
|  |               /> | ||||||
|  |  | ||||||
|  |               <hr /> | ||||||
|  |  | ||||||
|  |               <SettingSelect | ||||||
|  |                 label="Precision" | ||||||
|  |                 desc="Set reverse geocoding precision" | ||||||
|  |                 name="reverse-geocoding-precision" | ||||||
|  |                 bind:value={config.reverseGeocoding.citiesFileOverride} | ||||||
|  |                 options={[ | ||||||
|  |                   { value: CitiesFile.Cities500, text: 'Cities with more than 500 people' }, | ||||||
|  |                   { value: CitiesFile.Cities1000, text: 'Cities with more than 1000 people' }, | ||||||
|  |                   { value: CitiesFile.Cities5000, text: 'Cities with more than 5000 people' }, | ||||||
|  |                   { value: CitiesFile.Cities15000, text: 'Cities with more than 15000 people' }, | ||||||
|  |                 ]} | ||||||
|  |                 disabled={disabled || !config.reverseGeocoding.enabled} | ||||||
|  |                 isEdited={config.reverseGeocoding.citiesFileOverride !== | ||||||
|  |                   savedConfig.reverseGeocoding.citiesFileOverride} | ||||||
|  |               /> | ||||||
|  |             </div></SettingAccordion | ||||||
|  |           > | ||||||
|  |  | ||||||
|           <SettingButtonsRow |           <SettingButtonsRow | ||||||
|             on:reset={reset} |             on:reset={reset} | ||||||
|             on:save={saveSetting} |             on:save={saveSetting} | ||||||
|             on:reset-to-default={resetToDefault} |             on:reset-to-default={resetToDefault} | ||||||
|             showResetToDefault={!isEqual(savedConfig, defaultConfig)} |             showResetToDefault={!isEqual( | ||||||
|  |               { ...savedConfig.map, ...savedConfig.reverseGeocoding }, | ||||||
|  |               { ...defaultConfig.map, ...defaultConfig.reverseGeocoding }, | ||||||
|  |             )} | ||||||
|             {disabled} |             {disabled} | ||||||
|           /> |           /> | ||||||
|         </div> |         </div> | ||||||
|   | |||||||
| @@ -14,7 +14,9 @@ | |||||||
|         {title} |         {title} | ||||||
|       </h2> |       </h2> | ||||||
|  |  | ||||||
|       <p class="text-sm dark:text-immich-dark-fg">{subtitle}</p> |       <slot name="subtitle"> | ||||||
|  |         <p class="text-sm dark:text-immich-dark-fg">{subtitle}</p> | ||||||
|  |       </slot> | ||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|     <button |     <button | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ export const featureFlags = writable<FeatureFlags>({ | |||||||
|   sidecar: true, |   sidecar: true, | ||||||
|   tagImage: true, |   tagImage: true, | ||||||
|   map: true, |   map: true, | ||||||
|  |   reverseGeocoding: true, | ||||||
|   search: true, |   search: true, | ||||||
|   oauth: false, |   oauth: false, | ||||||
|   oauthAutoLaunch: false, |   oauthAutoLaunch: false, | ||||||
|   | |||||||
| @@ -67,12 +67,12 @@ | |||||||
|       <JobSettings disabled={$featureFlags.configFile} jobConfig={configs.job} /> |       <JobSettings disabled={$featureFlags.configFile} jobConfig={configs.job} /> | ||||||
|     </SettingAccordion> |     </SettingAccordion> | ||||||
|  |  | ||||||
|     <SettingAccordion title="Machine Learning Settings" subtitle="Manage model settings"> |     <SettingAccordion title="Machine Learning Settings" subtitle="Manage machine learning features and settings"> | ||||||
|       <MachineLearningSettings disabled={$featureFlags.configFile} machineLearningConfig={configs.machineLearning} /> |       <MachineLearningSettings disabled={$featureFlags.configFile} machineLearningConfig={configs.machineLearning} /> | ||||||
|     </SettingAccordion> |     </SettingAccordion> | ||||||
|  |  | ||||||
|     <SettingAccordion title="Map Settings" subtitle="Manage map settings"> |     <SettingAccordion title="Map & GPS Settings" subtitle="Manage map related features and setting"> | ||||||
|       <MapSettings disabled={$featureFlags.configFile} mapConfig={configs.map} /> |       <MapSettings disabled={$featureFlags.configFile} config={configs} /> | ||||||
|     </SettingAccordion> |     </SettingAccordion> | ||||||
|  |  | ||||||
|     <SettingAccordion title="OAuth Authentication" subtitle="Manage the login with OAuth settings"> |     <SettingAccordion title="OAuth Authentication" subtitle="Manage the login with OAuth settings"> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user