mirror of
https://github.com/immich-app/immich.git
synced 2025-03-01 15:11:21 +01:00
feat(server,web): server config (#4006)
* feat: server config * chore: open api * fix: redirect /map to /photos when disabled
This commit is contained in:
parent
3edade6761
commit
f1db257628
48 changed files with 1103 additions and 162 deletions
112
cli/src/api/open-api/api.ts
generated
112
cli/src/api/open-api/api.ts
generated
|
@ -2343,6 +2343,31 @@ export interface SearchResponseDto {
|
|||
*/
|
||||
'assets': SearchAssetResponseDto;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface ServerConfigDto
|
||||
*/
|
||||
export interface ServerConfigDto {
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof ServerConfigDto
|
||||
*/
|
||||
'loginPageMessage': string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof ServerConfigDto
|
||||
*/
|
||||
'mapTileUrl': string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof ServerConfigDto
|
||||
*/
|
||||
'oauthButtonText': string;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
|
@ -2367,6 +2392,12 @@ export interface ServerFeaturesDto {
|
|||
* @memberof ServerFeaturesDto
|
||||
*/
|
||||
'facialRecognition': boolean;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof ServerFeaturesDto
|
||||
*/
|
||||
'map': boolean;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
|
@ -2810,6 +2841,12 @@ export interface SystemConfigDto {
|
|||
* @memberof SystemConfigDto
|
||||
*/
|
||||
'machineLearning': SystemConfigMachineLearningDto;
|
||||
/**
|
||||
*
|
||||
* @type {SystemConfigMapDto}
|
||||
* @memberof SystemConfigDto
|
||||
*/
|
||||
'map': SystemConfigMapDto;
|
||||
/**
|
||||
*
|
||||
* @type {SystemConfigOAuthDto}
|
||||
|
@ -3050,6 +3087,25 @@ export interface SystemConfigMachineLearningDto {
|
|||
*/
|
||||
'url': string;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface SystemConfigMapDto
|
||||
*/
|
||||
export interface SystemConfigMapDto {
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof SystemConfigMapDto
|
||||
*/
|
||||
'enabled': boolean;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof SystemConfigMapDto
|
||||
*/
|
||||
'tileUrl': string;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
|
@ -10825,6 +10881,35 @@ export class SearchApi extends BaseAPI {
|
|||
*/
|
||||
export const ServerInfoApiAxiosParamCreator = function (configuration?: Configuration) {
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
getServerConfig: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
const localVarPath = `/server-info/config`;
|
||||
// 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: 'GET', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
|
@ -11027,6 +11112,15 @@ export const ServerInfoApiAxiosParamCreator = function (configuration?: Configur
|
|||
export const ServerInfoApiFp = function(configuration?: Configuration) {
|
||||
const localVarAxiosParamCreator = ServerInfoApiAxiosParamCreator(configuration)
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async getServerConfig(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<ServerConfigDto>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.getServerConfig(options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
|
@ -11091,6 +11185,14 @@ export const ServerInfoApiFp = function(configuration?: Configuration) {
|
|||
export const ServerInfoApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
|
||||
const localVarFp = ServerInfoApiFp(configuration)
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
getServerConfig(options?: AxiosRequestConfig): AxiosPromise<ServerConfigDto> {
|
||||
return localVarFp.getServerConfig(options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
|
@ -11149,6 +11251,16 @@ export const ServerInfoApiFactory = function (configuration?: Configuration, bas
|
|||
* @extends {BaseAPI}
|
||||
*/
|
||||
export class ServerInfoApi extends BaseAPI {
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof ServerInfoApi
|
||||
*/
|
||||
public getServerConfig(options?: AxiosRequestConfig) {
|
||||
return ServerInfoApiFp(this.configuration).getServerConfig(options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
|
|
6
mobile/openapi/.openapi-generator/FILES
generated
6
mobile/openapi/.openapi-generator/FILES
generated
|
@ -97,6 +97,7 @@ doc/SearchExploreResponseDto.md
|
|||
doc/SearchFacetCountResponseDto.md
|
||||
doc/SearchFacetResponseDto.md
|
||||
doc/SearchResponseDto.md
|
||||
doc/ServerConfigDto.md
|
||||
doc/ServerFeaturesDto.md
|
||||
doc/ServerInfoApi.md
|
||||
doc/ServerInfoResponseDto.md
|
||||
|
@ -116,6 +117,7 @@ doc/SystemConfigDto.md
|
|||
doc/SystemConfigFFmpegDto.md
|
||||
doc/SystemConfigJobDto.md
|
||||
doc/SystemConfigMachineLearningDto.md
|
||||
doc/SystemConfigMapDto.md
|
||||
doc/SystemConfigOAuthDto.md
|
||||
doc/SystemConfigPasswordLoginDto.md
|
||||
doc/SystemConfigStorageTemplateDto.md
|
||||
|
@ -249,6 +251,7 @@ lib/model/search_explore_response_dto.dart
|
|||
lib/model/search_facet_count_response_dto.dart
|
||||
lib/model/search_facet_response_dto.dart
|
||||
lib/model/search_response_dto.dart
|
||||
lib/model/server_config_dto.dart
|
||||
lib/model/server_features_dto.dart
|
||||
lib/model/server_info_response_dto.dart
|
||||
lib/model/server_media_types_response_dto.dart
|
||||
|
@ -265,6 +268,7 @@ lib/model/system_config_dto.dart
|
|||
lib/model/system_config_f_fmpeg_dto.dart
|
||||
lib/model/system_config_job_dto.dart
|
||||
lib/model/system_config_machine_learning_dto.dart
|
||||
lib/model/system_config_map_dto.dart
|
||||
lib/model/system_config_o_auth_dto.dart
|
||||
lib/model/system_config_password_login_dto.dart
|
||||
lib/model/system_config_storage_template_dto.dart
|
||||
|
@ -382,6 +386,7 @@ test/search_explore_response_dto_test.dart
|
|||
test/search_facet_count_response_dto_test.dart
|
||||
test/search_facet_response_dto_test.dart
|
||||
test/search_response_dto_test.dart
|
||||
test/server_config_dto_test.dart
|
||||
test/server_features_dto_test.dart
|
||||
test/server_info_api_test.dart
|
||||
test/server_info_response_dto_test.dart
|
||||
|
@ -401,6 +406,7 @@ test/system_config_dto_test.dart
|
|||
test/system_config_f_fmpeg_dto_test.dart
|
||||
test/system_config_job_dto_test.dart
|
||||
test/system_config_machine_learning_dto_test.dart
|
||||
test/system_config_map_dto_test.dart
|
||||
test/system_config_o_auth_dto_test.dart
|
||||
test/system_config_password_login_dto_test.dart
|
||||
test/system_config_storage_template_dto_test.dart
|
||||
|
|
3
mobile/openapi/README.md
generated
3
mobile/openapi/README.md
generated
|
@ -142,6 +142,7 @@ Class | Method | HTTP request | Description
|
|||
*PersonApi* | [**updatePerson**](doc//PersonApi.md#updateperson) | **PUT** /person/{id} |
|
||||
*SearchApi* | [**getExploreData**](doc//SearchApi.md#getexploredata) | **GET** /search/explore |
|
||||
*SearchApi* | [**search**](doc//SearchApi.md#search) | **GET** /search |
|
||||
*ServerInfoApi* | [**getServerConfig**](doc//ServerInfoApi.md#getserverconfig) | **GET** /server-info/config |
|
||||
*ServerInfoApi* | [**getServerFeatures**](doc//ServerInfoApi.md#getserverfeatures) | **GET** /server-info/features |
|
||||
*ServerInfoApi* | [**getServerInfo**](doc//ServerInfoApi.md#getserverinfo) | **GET** /server-info |
|
||||
*ServerInfoApi* | [**getServerVersion**](doc//ServerInfoApi.md#getserverversion) | **GET** /server-info/version |
|
||||
|
@ -266,6 +267,7 @@ Class | Method | HTTP request | Description
|
|||
- [SearchFacetCountResponseDto](doc//SearchFacetCountResponseDto.md)
|
||||
- [SearchFacetResponseDto](doc//SearchFacetResponseDto.md)
|
||||
- [SearchResponseDto](doc//SearchResponseDto.md)
|
||||
- [ServerConfigDto](doc//ServerConfigDto.md)
|
||||
- [ServerFeaturesDto](doc//ServerFeaturesDto.md)
|
||||
- [ServerInfoResponseDto](doc//ServerInfoResponseDto.md)
|
||||
- [ServerMediaTypesResponseDto](doc//ServerMediaTypesResponseDto.md)
|
||||
|
@ -282,6 +284,7 @@ Class | Method | HTTP request | Description
|
|||
- [SystemConfigFFmpegDto](doc//SystemConfigFFmpegDto.md)
|
||||
- [SystemConfigJobDto](doc//SystemConfigJobDto.md)
|
||||
- [SystemConfigMachineLearningDto](doc//SystemConfigMachineLearningDto.md)
|
||||
- [SystemConfigMapDto](doc//SystemConfigMapDto.md)
|
||||
- [SystemConfigOAuthDto](doc//SystemConfigOAuthDto.md)
|
||||
- [SystemConfigPasswordLoginDto](doc//SystemConfigPasswordLoginDto.md)
|
||||
- [SystemConfigStorageTemplateDto](doc//SystemConfigStorageTemplateDto.md)
|
||||
|
|
17
mobile/openapi/doc/ServerConfigDto.md
generated
Normal file
17
mobile/openapi/doc/ServerConfigDto.md
generated
Normal file
|
@ -0,0 +1,17 @@
|
|||
# openapi.model.ServerConfigDto
|
||||
|
||||
## Load the model package
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
```
|
||||
|
||||
## Properties
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**loginPageMessage** | **String** | |
|
||||
**mapTileUrl** | **String** | |
|
||||
**oauthButtonText** | **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/doc/ServerFeaturesDto.md
generated
1
mobile/openapi/doc/ServerFeaturesDto.md
generated
|
@ -11,6 +11,7 @@ Name | Type | Description | Notes
|
|||
**clipEncode** | **bool** | |
|
||||
**configFile** | **bool** | |
|
||||
**facialRecognition** | **bool** | |
|
||||
**map** | **bool** | |
|
||||
**oauth** | **bool** | |
|
||||
**oauthAutoLaunch** | **bool** | |
|
||||
**passwordLogin** | **bool** | |
|
||||
|
|
38
mobile/openapi/doc/ServerInfoApi.md
generated
38
mobile/openapi/doc/ServerInfoApi.md
generated
|
@ -9,6 +9,7 @@ All URIs are relative to */api*
|
|||
|
||||
Method | HTTP request | Description
|
||||
------------- | ------------- | -------------
|
||||
[**getServerConfig**](ServerInfoApi.md#getserverconfig) | **GET** /server-info/config |
|
||||
[**getServerFeatures**](ServerInfoApi.md#getserverfeatures) | **GET** /server-info/features |
|
||||
[**getServerInfo**](ServerInfoApi.md#getserverinfo) | **GET** /server-info |
|
||||
[**getServerVersion**](ServerInfoApi.md#getserverversion) | **GET** /server-info/version |
|
||||
|
@ -17,6 +18,43 @@ Method | HTTP request | Description
|
|||
[**pingServer**](ServerInfoApi.md#pingserver) | **GET** /server-info/ping |
|
||||
|
||||
|
||||
# **getServerConfig**
|
||||
> ServerConfigDto getServerConfig()
|
||||
|
||||
|
||||
|
||||
### Example
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
|
||||
final api_instance = ServerInfoApi();
|
||||
|
||||
try {
|
||||
final result = api_instance.getServerConfig();
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling ServerInfoApi->getServerConfig: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
This endpoint does not need any parameter.
|
||||
|
||||
### Return type
|
||||
|
||||
[**ServerConfigDto**](ServerConfigDto.md)
|
||||
|
||||
### Authorization
|
||||
|
||||
No authorization required
|
||||
|
||||
### HTTP request headers
|
||||
|
||||
- **Content-Type**: Not defined
|
||||
- **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)
|
||||
|
||||
# **getServerFeatures**
|
||||
> ServerFeaturesDto getServerFeatures()
|
||||
|
||||
|
|
1
mobile/openapi/doc/SystemConfigDto.md
generated
1
mobile/openapi/doc/SystemConfigDto.md
generated
|
@ -11,6 +11,7 @@ Name | Type | Description | Notes
|
|||
**ffmpeg** | [**SystemConfigFFmpegDto**](SystemConfigFFmpegDto.md) | |
|
||||
**job** | [**SystemConfigJobDto**](SystemConfigJobDto.md) | |
|
||||
**machineLearning** | [**SystemConfigMachineLearningDto**](SystemConfigMachineLearningDto.md) | |
|
||||
**map** | [**SystemConfigMapDto**](SystemConfigMapDto.md) | |
|
||||
**oauth** | [**SystemConfigOAuthDto**](SystemConfigOAuthDto.md) | |
|
||||
**passwordLogin** | [**SystemConfigPasswordLoginDto**](SystemConfigPasswordLoginDto.md) | |
|
||||
**storageTemplate** | [**SystemConfigStorageTemplateDto**](SystemConfigStorageTemplateDto.md) | |
|
||||
|
|
16
mobile/openapi/doc/SystemConfigMapDto.md
generated
Normal file
16
mobile/openapi/doc/SystemConfigMapDto.md
generated
Normal file
|
@ -0,0 +1,16 @@
|
|||
# openapi.model.SystemConfigMapDto
|
||||
|
||||
## Load the model package
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
```
|
||||
|
||||
## Properties
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**enabled** | **bool** | |
|
||||
**tileUrl** | **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)
|
||||
|
||||
|
2
mobile/openapi/lib/api.dart
generated
2
mobile/openapi/lib/api.dart
generated
|
@ -128,6 +128,7 @@ part 'model/search_explore_response_dto.dart';
|
|||
part 'model/search_facet_count_response_dto.dart';
|
||||
part 'model/search_facet_response_dto.dart';
|
||||
part 'model/search_response_dto.dart';
|
||||
part 'model/server_config_dto.dart';
|
||||
part 'model/server_features_dto.dart';
|
||||
part 'model/server_info_response_dto.dart';
|
||||
part 'model/server_media_types_response_dto.dart';
|
||||
|
@ -144,6 +145,7 @@ part 'model/system_config_dto.dart';
|
|||
part 'model/system_config_f_fmpeg_dto.dart';
|
||||
part 'model/system_config_job_dto.dart';
|
||||
part 'model/system_config_machine_learning_dto.dart';
|
||||
part 'model/system_config_map_dto.dart';
|
||||
part 'model/system_config_o_auth_dto.dart';
|
||||
part 'model/system_config_password_login_dto.dart';
|
||||
part 'model/system_config_storage_template_dto.dart';
|
||||
|
|
41
mobile/openapi/lib/api/server_info_api.dart
generated
41
mobile/openapi/lib/api/server_info_api.dart
generated
|
@ -16,6 +16,47 @@ class ServerInfoApi {
|
|||
|
||||
final ApiClient apiClient;
|
||||
|
||||
/// Performs an HTTP 'GET /server-info/config' operation and returns the [Response].
|
||||
Future<Response> getServerConfigWithHttpInfo() async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/server-info/config';
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>[];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'GET',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
Future<ServerConfigDto?> getServerConfig() async {
|
||||
final response = await getServerConfigWithHttpInfo();
|
||||
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), 'ServerConfigDto',) as ServerConfigDto;
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'GET /server-info/features' operation and returns the [Response].
|
||||
Future<Response> getServerFeaturesWithHttpInfo() async {
|
||||
// ignore: prefer_const_declarations
|
||||
|
|
4
mobile/openapi/lib/api_client.dart
generated
4
mobile/openapi/lib/api_client.dart
generated
|
@ -349,6 +349,8 @@ class ApiClient {
|
|||
return SearchFacetResponseDto.fromJson(value);
|
||||
case 'SearchResponseDto':
|
||||
return SearchResponseDto.fromJson(value);
|
||||
case 'ServerConfigDto':
|
||||
return ServerConfigDto.fromJson(value);
|
||||
case 'ServerFeaturesDto':
|
||||
return ServerFeaturesDto.fromJson(value);
|
||||
case 'ServerInfoResponseDto':
|
||||
|
@ -381,6 +383,8 @@ class ApiClient {
|
|||
return SystemConfigJobDto.fromJson(value);
|
||||
case 'SystemConfigMachineLearningDto':
|
||||
return SystemConfigMachineLearningDto.fromJson(value);
|
||||
case 'SystemConfigMapDto':
|
||||
return SystemConfigMapDto.fromJson(value);
|
||||
case 'SystemConfigOAuthDto':
|
||||
return SystemConfigOAuthDto.fromJson(value);
|
||||
case 'SystemConfigPasswordLoginDto':
|
||||
|
|
114
mobile/openapi/lib/model/server_config_dto.dart
generated
Normal file
114
mobile/openapi/lib/model/server_config_dto.dart
generated
Normal file
|
@ -0,0 +1,114 @@
|
|||
//
|
||||
// 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 ServerConfigDto {
|
||||
/// Returns a new [ServerConfigDto] instance.
|
||||
ServerConfigDto({
|
||||
required this.loginPageMessage,
|
||||
required this.mapTileUrl,
|
||||
required this.oauthButtonText,
|
||||
});
|
||||
|
||||
String loginPageMessage;
|
||||
|
||||
String mapTileUrl;
|
||||
|
||||
String oauthButtonText;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is ServerConfigDto &&
|
||||
other.loginPageMessage == loginPageMessage &&
|
||||
other.mapTileUrl == mapTileUrl &&
|
||||
other.oauthButtonText == oauthButtonText;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(loginPageMessage.hashCode) +
|
||||
(mapTileUrl.hashCode) +
|
||||
(oauthButtonText.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'ServerConfigDto[loginPageMessage=$loginPageMessage, mapTileUrl=$mapTileUrl, oauthButtonText=$oauthButtonText]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'loginPageMessage'] = this.loginPageMessage;
|
||||
json[r'mapTileUrl'] = this.mapTileUrl;
|
||||
json[r'oauthButtonText'] = this.oauthButtonText;
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [ServerConfigDto] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static ServerConfigDto? fromJson(dynamic value) {
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return ServerConfigDto(
|
||||
loginPageMessage: mapValueOfType<String>(json, r'loginPageMessage')!,
|
||||
mapTileUrl: mapValueOfType<String>(json, r'mapTileUrl')!,
|
||||
oauthButtonText: mapValueOfType<String>(json, r'oauthButtonText')!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<ServerConfigDto> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <ServerConfigDto>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = ServerConfigDto.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, ServerConfigDto> mapFromJson(dynamic json) {
|
||||
final map = <String, ServerConfigDto>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = ServerConfigDto.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of ServerConfigDto-objects as value to a dart map
|
||||
static Map<String, List<ServerConfigDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<ServerConfigDto>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = ServerConfigDto.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'loginPageMessage',
|
||||
'mapTileUrl',
|
||||
'oauthButtonText',
|
||||
};
|
||||
}
|
||||
|
10
mobile/openapi/lib/model/server_features_dto.dart
generated
10
mobile/openapi/lib/model/server_features_dto.dart
generated
|
@ -16,6 +16,7 @@ class ServerFeaturesDto {
|
|||
required this.clipEncode,
|
||||
required this.configFile,
|
||||
required this.facialRecognition,
|
||||
required this.map,
|
||||
required this.oauth,
|
||||
required this.oauthAutoLaunch,
|
||||
required this.passwordLogin,
|
||||
|
@ -30,6 +31,8 @@ class ServerFeaturesDto {
|
|||
|
||||
bool facialRecognition;
|
||||
|
||||
bool map;
|
||||
|
||||
bool oauth;
|
||||
|
||||
bool oauthAutoLaunch;
|
||||
|
@ -47,6 +50,7 @@ class ServerFeaturesDto {
|
|||
other.clipEncode == clipEncode &&
|
||||
other.configFile == configFile &&
|
||||
other.facialRecognition == facialRecognition &&
|
||||
other.map == map &&
|
||||
other.oauth == oauth &&
|
||||
other.oauthAutoLaunch == oauthAutoLaunch &&
|
||||
other.passwordLogin == passwordLogin &&
|
||||
|
@ -60,6 +64,7 @@ class ServerFeaturesDto {
|
|||
(clipEncode.hashCode) +
|
||||
(configFile.hashCode) +
|
||||
(facialRecognition.hashCode) +
|
||||
(map.hashCode) +
|
||||
(oauth.hashCode) +
|
||||
(oauthAutoLaunch.hashCode) +
|
||||
(passwordLogin.hashCode) +
|
||||
|
@ -68,13 +73,14 @@ class ServerFeaturesDto {
|
|||
(tagImage.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'ServerFeaturesDto[clipEncode=$clipEncode, configFile=$configFile, facialRecognition=$facialRecognition, 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, search=$search, sidecar=$sidecar, tagImage=$tagImage]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'clipEncode'] = this.clipEncode;
|
||||
json[r'configFile'] = this.configFile;
|
||||
json[r'facialRecognition'] = this.facialRecognition;
|
||||
json[r'map'] = this.map;
|
||||
json[r'oauth'] = this.oauth;
|
||||
json[r'oauthAutoLaunch'] = this.oauthAutoLaunch;
|
||||
json[r'passwordLogin'] = this.passwordLogin;
|
||||
|
@ -95,6 +101,7 @@ class ServerFeaturesDto {
|
|||
clipEncode: mapValueOfType<bool>(json, r'clipEncode')!,
|
||||
configFile: mapValueOfType<bool>(json, r'configFile')!,
|
||||
facialRecognition: mapValueOfType<bool>(json, r'facialRecognition')!,
|
||||
map: mapValueOfType<bool>(json, r'map')!,
|
||||
oauth: mapValueOfType<bool>(json, r'oauth')!,
|
||||
oauthAutoLaunch: mapValueOfType<bool>(json, r'oauthAutoLaunch')!,
|
||||
passwordLogin: mapValueOfType<bool>(json, r'passwordLogin')!,
|
||||
|
@ -151,6 +158,7 @@ class ServerFeaturesDto {
|
|||
'clipEncode',
|
||||
'configFile',
|
||||
'facialRecognition',
|
||||
'map',
|
||||
'oauth',
|
||||
'oauthAutoLaunch',
|
||||
'passwordLogin',
|
||||
|
|
10
mobile/openapi/lib/model/system_config_dto.dart
generated
10
mobile/openapi/lib/model/system_config_dto.dart
generated
|
@ -16,6 +16,7 @@ class SystemConfigDto {
|
|||
required this.ffmpeg,
|
||||
required this.job,
|
||||
required this.machineLearning,
|
||||
required this.map,
|
||||
required this.oauth,
|
||||
required this.passwordLogin,
|
||||
required this.storageTemplate,
|
||||
|
@ -28,6 +29,8 @@ class SystemConfigDto {
|
|||
|
||||
SystemConfigMachineLearningDto machineLearning;
|
||||
|
||||
SystemConfigMapDto map;
|
||||
|
||||
SystemConfigOAuthDto oauth;
|
||||
|
||||
SystemConfigPasswordLoginDto passwordLogin;
|
||||
|
@ -41,6 +44,7 @@ class SystemConfigDto {
|
|||
other.ffmpeg == ffmpeg &&
|
||||
other.job == job &&
|
||||
other.machineLearning == machineLearning &&
|
||||
other.map == map &&
|
||||
other.oauth == oauth &&
|
||||
other.passwordLogin == passwordLogin &&
|
||||
other.storageTemplate == storageTemplate &&
|
||||
|
@ -52,19 +56,21 @@ class SystemConfigDto {
|
|||
(ffmpeg.hashCode) +
|
||||
(job.hashCode) +
|
||||
(machineLearning.hashCode) +
|
||||
(map.hashCode) +
|
||||
(oauth.hashCode) +
|
||||
(passwordLogin.hashCode) +
|
||||
(storageTemplate.hashCode) +
|
||||
(thumbnail.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'SystemConfigDto[ffmpeg=$ffmpeg, job=$job, machineLearning=$machineLearning, oauth=$oauth, passwordLogin=$passwordLogin, storageTemplate=$storageTemplate, thumbnail=$thumbnail]';
|
||||
String toString() => 'SystemConfigDto[ffmpeg=$ffmpeg, job=$job, machineLearning=$machineLearning, map=$map, oauth=$oauth, passwordLogin=$passwordLogin, storageTemplate=$storageTemplate, thumbnail=$thumbnail]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'ffmpeg'] = this.ffmpeg;
|
||||
json[r'job'] = this.job;
|
||||
json[r'machineLearning'] = this.machineLearning;
|
||||
json[r'map'] = this.map;
|
||||
json[r'oauth'] = this.oauth;
|
||||
json[r'passwordLogin'] = this.passwordLogin;
|
||||
json[r'storageTemplate'] = this.storageTemplate;
|
||||
|
@ -83,6 +89,7 @@ class SystemConfigDto {
|
|||
ffmpeg: SystemConfigFFmpegDto.fromJson(json[r'ffmpeg'])!,
|
||||
job: SystemConfigJobDto.fromJson(json[r'job'])!,
|
||||
machineLearning: SystemConfigMachineLearningDto.fromJson(json[r'machineLearning'])!,
|
||||
map: SystemConfigMapDto.fromJson(json[r'map'])!,
|
||||
oauth: SystemConfigOAuthDto.fromJson(json[r'oauth'])!,
|
||||
passwordLogin: SystemConfigPasswordLoginDto.fromJson(json[r'passwordLogin'])!,
|
||||
storageTemplate: SystemConfigStorageTemplateDto.fromJson(json[r'storageTemplate'])!,
|
||||
|
@ -137,6 +144,7 @@ class SystemConfigDto {
|
|||
'ffmpeg',
|
||||
'job',
|
||||
'machineLearning',
|
||||
'map',
|
||||
'oauth',
|
||||
'passwordLogin',
|
||||
'storageTemplate',
|
||||
|
|
106
mobile/openapi/lib/model/system_config_map_dto.dart
generated
Normal file
106
mobile/openapi/lib/model/system_config_map_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 SystemConfigMapDto {
|
||||
/// Returns a new [SystemConfigMapDto] instance.
|
||||
SystemConfigMapDto({
|
||||
required this.enabled,
|
||||
required this.tileUrl,
|
||||
});
|
||||
|
||||
bool enabled;
|
||||
|
||||
String tileUrl;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is SystemConfigMapDto &&
|
||||
other.enabled == enabled &&
|
||||
other.tileUrl == tileUrl;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(enabled.hashCode) +
|
||||
(tileUrl.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'SystemConfigMapDto[enabled=$enabled, tileUrl=$tileUrl]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'enabled'] = this.enabled;
|
||||
json[r'tileUrl'] = this.tileUrl;
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [SystemConfigMapDto] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static SystemConfigMapDto? fromJson(dynamic value) {
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return SystemConfigMapDto(
|
||||
enabled: mapValueOfType<bool>(json, r'enabled')!,
|
||||
tileUrl: mapValueOfType<String>(json, r'tileUrl')!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<SystemConfigMapDto> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <SystemConfigMapDto>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = SystemConfigMapDto.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, SystemConfigMapDto> mapFromJson(dynamic json) {
|
||||
final map = <String, SystemConfigMapDto>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = SystemConfigMapDto.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of SystemConfigMapDto-objects as value to a dart map
|
||||
static Map<String, List<SystemConfigMapDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<SystemConfigMapDto>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = SystemConfigMapDto.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'enabled',
|
||||
'tileUrl',
|
||||
};
|
||||
}
|
||||
|
37
mobile/openapi/test/server_config_dto_test.dart
generated
Normal file
37
mobile/openapi/test/server_config_dto_test.dart
generated
Normal file
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// 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 ServerConfigDto
|
||||
void main() {
|
||||
// final instance = ServerConfigDto();
|
||||
|
||||
group('test ServerConfigDto', () {
|
||||
// String loginPageMessage
|
||||
test('to test the property `loginPageMessage`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// String mapTileUrl
|
||||
test('to test the property `mapTileUrl`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// String oauthButtonText
|
||||
test('to test the property `oauthButtonText`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
}
|
|
@ -31,6 +31,11 @@ void main() {
|
|||
// TODO
|
||||
});
|
||||
|
||||
// bool map
|
||||
test('to test the property `map`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// bool oauth
|
||||
test('to test the property `oauth`', () async {
|
||||
// TODO
|
||||
|
|
5
mobile/openapi/test/server_info_api_test.dart
generated
5
mobile/openapi/test/server_info_api_test.dart
generated
|
@ -17,6 +17,11 @@ void main() {
|
|||
// final instance = ServerInfoApi();
|
||||
|
||||
group('tests for ServerInfoApi', () {
|
||||
//Future<ServerConfigDto> getServerConfig() async
|
||||
test('test getServerConfig', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
//Future<ServerFeaturesDto> getServerFeatures() async
|
||||
test('test getServerFeatures', () async {
|
||||
// TODO
|
||||
|
|
5
mobile/openapi/test/system_config_dto_test.dart
generated
5
mobile/openapi/test/system_config_dto_test.dart
generated
|
@ -31,6 +31,11 @@ void main() {
|
|||
// TODO
|
||||
});
|
||||
|
||||
// SystemConfigMapDto map
|
||||
test('to test the property `map`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// SystemConfigOAuthDto oauth
|
||||
test('to test the property `oauth`', () async {
|
||||
// TODO
|
||||
|
|
32
mobile/openapi/test/system_config_map_dto_test.dart
generated
Normal file
32
mobile/openapi/test/system_config_map_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 SystemConfigMapDto
|
||||
void main() {
|
||||
// final instance = SystemConfigMapDto();
|
||||
|
||||
group('test SystemConfigMapDto', () {
|
||||
// bool enabled
|
||||
test('to test the property `enabled`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// String tileUrl
|
||||
test('to test the property `tileUrl`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
}
|
|
@ -3342,6 +3342,27 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"/server-info/config": {
|
||||
"get": {
|
||||
"operationId": "getServerConfig",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ServerConfigDto"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Server Info"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/server-info/features": {
|
||||
"get": {
|
||||
"operationId": "getServerFeatures",
|
||||
|
@ -6618,6 +6639,25 @@
|
|||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ServerConfigDto": {
|
||||
"properties": {
|
||||
"loginPageMessage": {
|
||||
"type": "string"
|
||||
},
|
||||
"mapTileUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"oauthButtonText": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"oauthButtonText",
|
||||
"loginPageMessage",
|
||||
"mapTileUrl"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ServerFeaturesDto": {
|
||||
"properties": {
|
||||
"clipEncode": {
|
||||
|
@ -6629,6 +6669,9 @@
|
|||
"facialRecognition": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"map": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"oauth": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
@ -6649,15 +6692,16 @@
|
|||
}
|
||||
},
|
||||
"required": [
|
||||
"configFile",
|
||||
"clipEncode",
|
||||
"configFile",
|
||||
"facialRecognition",
|
||||
"sidecar",
|
||||
"search",
|
||||
"tagImage",
|
||||
"map",
|
||||
"oauth",
|
||||
"oauthAutoLaunch",
|
||||
"passwordLogin"
|
||||
"passwordLogin",
|
||||
"sidecar",
|
||||
"search",
|
||||
"tagImage"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
|
@ -6989,6 +7033,9 @@
|
|||
"machineLearning": {
|
||||
"$ref": "#/components/schemas/SystemConfigMachineLearningDto"
|
||||
},
|
||||
"map": {
|
||||
"$ref": "#/components/schemas/SystemConfigMapDto"
|
||||
},
|
||||
"oauth": {
|
||||
"$ref": "#/components/schemas/SystemConfigOAuthDto"
|
||||
},
|
||||
|
@ -7005,6 +7052,7 @@
|
|||
"required": [
|
||||
"ffmpeg",
|
||||
"machineLearning",
|
||||
"map",
|
||||
"oauth",
|
||||
"passwordLogin",
|
||||
"storageTemplate",
|
||||
|
@ -7162,6 +7210,21 @@
|
|||
],
|
||||
"type": "object"
|
||||
},
|
||||
"SystemConfigMapDto": {
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"tileUrl": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"enabled",
|
||||
"tileUrl"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"SystemConfigOAuthDto": {
|
||||
"properties": {
|
||||
"autoLaunch": {
|
||||
|
|
|
@ -79,16 +79,21 @@ export class ServerMediaTypesResponseDto {
|
|||
sidecar!: string[];
|
||||
}
|
||||
|
||||
export class ServerFeaturesDto implements FeatureFlags {
|
||||
configFile!: boolean;
|
||||
clipEncode!: boolean;
|
||||
facialRecognition!: boolean;
|
||||
sidecar!: boolean;
|
||||
search!: boolean;
|
||||
tagImage!: boolean;
|
||||
export class ServerConfigDto {
|
||||
oauthButtonText!: string;
|
||||
loginPageMessage!: string;
|
||||
mapTileUrl!: string;
|
||||
}
|
||||
|
||||
// TODO: use these instead of `POST oauth/config`
|
||||
export class ServerFeaturesDto implements FeatureFlags {
|
||||
clipEncode!: boolean;
|
||||
configFile!: boolean;
|
||||
facialRecognition!: boolean;
|
||||
map!: boolean;
|
||||
oauth!: boolean;
|
||||
oauthAutoLaunch!: boolean;
|
||||
passwordLogin!: boolean;
|
||||
sidecar!: boolean;
|
||||
search!: boolean;
|
||||
tagImage!: boolean;
|
||||
}
|
||||
|
|
|
@ -143,22 +143,34 @@ describe(ServerInfoService.name, () => {
|
|||
it('should respond the server version', () => {
|
||||
expect(sut.getVersion()).toEqual(serverVersion);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getFeatures', () => {
|
||||
it('should respond the server features', async () => {
|
||||
await expect(sut.getFeatures()).resolves.toEqual({
|
||||
clipEncode: true,
|
||||
facialRecognition: true,
|
||||
oauth: false,
|
||||
oauthAutoLaunch: false,
|
||||
passwordLogin: true,
|
||||
search: true,
|
||||
sidecar: true,
|
||||
tagImage: true,
|
||||
configFile: false,
|
||||
});
|
||||
expect(configMock.load).toHaveBeenCalled();
|
||||
describe('getFeatures', () => {
|
||||
it('should respond the server features', async () => {
|
||||
await expect(sut.getFeatures()).resolves.toEqual({
|
||||
clipEncode: true,
|
||||
facialRecognition: true,
|
||||
map: true,
|
||||
oauth: false,
|
||||
oauthAutoLaunch: false,
|
||||
passwordLogin: true,
|
||||
search: true,
|
||||
sidecar: true,
|
||||
tagImage: true,
|
||||
configFile: false,
|
||||
});
|
||||
expect(configMock.load).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getConfig', () => {
|
||||
it('should respond the server configuration', async () => {
|
||||
await expect(sut.getConfig()).resolves.toEqual({
|
||||
loginPageMessage: '',
|
||||
oauthButtonText: 'Login with OAuth',
|
||||
mapTileUrl: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
});
|
||||
expect(configMock.load).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import { IStorageRepository, StorageCore, StorageFolder } from '../storage';
|
|||
import { ISystemConfigRepository, SystemConfigCore } from '../system-config';
|
||||
import { IUserRepository, UserStatsQueryResponse } from '../user';
|
||||
import {
|
||||
ServerConfigDto,
|
||||
ServerFeaturesDto,
|
||||
ServerInfoResponseDto,
|
||||
ServerMediaTypesResponseDto,
|
||||
|
@ -55,6 +56,19 @@ export class ServerInfoService {
|
|||
return this.configCore.getFeatures();
|
||||
}
|
||||
|
||||
async getConfig(): Promise<ServerConfigDto> {
|
||||
const config = await this.configCore.getConfig();
|
||||
|
||||
// TODO move to system config
|
||||
const loginPageMessage = process.env.PUBLIC_LOGIN_PAGE_MESSAGE || '';
|
||||
|
||||
return {
|
||||
loginPageMessage,
|
||||
mapTileUrl: config.map.tileUrl,
|
||||
oauthButtonText: config.oauth.buttonText,
|
||||
};
|
||||
}
|
||||
|
||||
async getStats(): Promise<ServerStatsResponseDto> {
|
||||
const userStats: UserStatsQueryResponse[] = await this.userRepository.getUserStats();
|
||||
const serverStats = new ServerStatsResponseDto();
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
import { IsBoolean, IsString } from 'class-validator';
|
||||
|
||||
export class SystemConfigMapDto {
|
||||
@IsBoolean()
|
||||
enabled!: boolean;
|
||||
|
||||
@IsString()
|
||||
tileUrl!: string;
|
||||
}
|
|
@ -5,6 +5,7 @@ import { IsObject, ValidateNested } from 'class-validator';
|
|||
import { SystemConfigFFmpegDto } from './system-config-ffmpeg.dto';
|
||||
import { SystemConfigJobDto } from './system-config-job.dto';
|
||||
import { SystemConfigMachineLearningDto } from './system-config-machine-learning.dto';
|
||||
import { SystemConfigMapDto } from './system-config-map.dto';
|
||||
import { SystemConfigOAuthDto } from './system-config-oauth.dto';
|
||||
import { SystemConfigPasswordLoginDto } from './system-config-password-login.dto';
|
||||
import { SystemConfigStorageTemplateDto } from './system-config-storage-template.dto';
|
||||
|
@ -20,6 +21,11 @@ export class SystemConfigDto implements SystemConfig {
|
|||
@IsObject()
|
||||
machineLearning!: SystemConfigMachineLearningDto;
|
||||
|
||||
@Type(() => SystemConfigMapDto)
|
||||
@ValidateNested()
|
||||
@IsObject()
|
||||
map!: SystemConfigMapDto;
|
||||
|
||||
@Type(() => SystemConfigOAuthDto)
|
||||
@ValidateNested()
|
||||
@IsObject()
|
||||
|
|
|
@ -55,7 +55,6 @@ export const defaults = Object.freeze<SystemConfig>({
|
|||
[QueueName.THUMBNAIL_GENERATION]: { concurrency: 5 },
|
||||
[QueueName.VIDEO_CONVERSION]: { concurrency: 1 },
|
||||
},
|
||||
|
||||
machineLearning: {
|
||||
enabled: process.env.IMMICH_MACHINE_LEARNING_ENABLED !== 'false',
|
||||
url: process.env.IMMICH_MACHINE_LEARNING_URL || 'http://immich-machine-learning:3003',
|
||||
|
@ -75,6 +74,10 @@ export const defaults = Object.freeze<SystemConfig>({
|
|||
maxDistance: 0.6,
|
||||
},
|
||||
},
|
||||
map: {
|
||||
enabled: true,
|
||||
tileUrl: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
},
|
||||
oauth: {
|
||||
enabled: false,
|
||||
issuerUrl: '',
|
||||
|
@ -108,6 +111,7 @@ export enum FeatureFlag {
|
|||
CLIP_ENCODE = 'clipEncode',
|
||||
FACIAL_RECOGNITION = 'facialRecognition',
|
||||
TAG_IMAGE = 'tagImage',
|
||||
MAP = 'map',
|
||||
SIDECAR = 'sidecar',
|
||||
SEARCH = 'search',
|
||||
OAUTH = 'oauth',
|
||||
|
@ -169,6 +173,7 @@ export class SystemConfigCore {
|
|||
[FeatureFlag.CLIP_ENCODE]: mlEnabled && config.machineLearning.clip.enabled,
|
||||
[FeatureFlag.FACIAL_RECOGNITION]: mlEnabled && config.machineLearning.facialRecognition.enabled,
|
||||
[FeatureFlag.TAG_IMAGE]: mlEnabled && config.machineLearning.classification.enabled,
|
||||
[FeatureFlag.MAP]: config.map.enabled,
|
||||
[FeatureFlag.SIDECAR]: true,
|
||||
[FeatureFlag.SEARCH]: process.env.TYPESENSE_ENABLED !== 'false',
|
||||
|
||||
|
|
|
@ -73,6 +73,10 @@ const updatedConfig = Object.freeze<SystemConfig>({
|
|||
maxDistance: 0.6,
|
||||
},
|
||||
},
|
||||
map: {
|
||||
enabled: true,
|
||||
tileUrl: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
},
|
||||
oauth: {
|
||||
autoLaunch: true,
|
||||
autoRegister: true,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import {
|
||||
ServerConfigDto,
|
||||
ServerFeaturesDto,
|
||||
ServerInfoResponseDto,
|
||||
ServerInfoService,
|
||||
|
@ -42,6 +43,12 @@ export class ServerInfoController {
|
|||
return this.service.getFeatures();
|
||||
}
|
||||
|
||||
@PublicRoute()
|
||||
@Get('config')
|
||||
getServerConfig(): Promise<ServerConfigDto> {
|
||||
return this.service.getConfig();
|
||||
}
|
||||
|
||||
@AdminRoute()
|
||||
@Get('stats')
|
||||
getStats(): Promise<ServerStatsResponseDto> {
|
||||
|
|
|
@ -58,6 +58,9 @@ export enum SystemConfigKey {
|
|||
MACHINE_LEARNING_FACIAL_RECOGNITION_MIN_SCORE = 'machineLearning.facialRecognition.minScore',
|
||||
MACHINE_LEARNING_FACIAL_RECOGNITION_MAX_DISTANCE = 'machineLearning.facialRecognition.maxDistance',
|
||||
|
||||
MAP_ENABLED = 'map.enabled',
|
||||
MAP_TILE_URL = 'map.tileUrl',
|
||||
|
||||
OAUTH_ENABLED = 'oauth.enabled',
|
||||
OAUTH_ISSUER_URL = 'oauth.issuerUrl',
|
||||
OAUTH_CLIENT_ID = 'oauth.clientId',
|
||||
|
@ -164,6 +167,10 @@ export interface SystemConfig {
|
|||
maxDistance: number;
|
||||
};
|
||||
};
|
||||
map: {
|
||||
enabled: boolean;
|
||||
tileUrl: string;
|
||||
};
|
||||
oauth: {
|
||||
enabled: boolean;
|
||||
issuerUrl: string;
|
||||
|
|
|
@ -83,6 +83,7 @@ describe(`${ServerInfoController.name} (e2e)`, () => {
|
|||
clipEncode: true,
|
||||
configFile: false,
|
||||
facialRecognition: true,
|
||||
map: true,
|
||||
oauth: false,
|
||||
oauthAutoLaunch: false,
|
||||
passwordLogin: true,
|
||||
|
@ -93,6 +94,18 @@ describe(`${ServerInfoController.name} (e2e)`, () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('GET /server-info/config', () => {
|
||||
it('should respond with the server configuration', async () => {
|
||||
const { status, body } = await request(server).get('/server-info/config');
|
||||
expect(status).toBe(200);
|
||||
expect(body).toEqual({
|
||||
loginPageMessage: '',
|
||||
oauthButtonText: 'Login with OAuth',
|
||||
mapTileUrl: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /server-info/stats', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(server).get('/server-info/stats');
|
||||
|
|
112
web/src/api/open-api/api.ts
generated
112
web/src/api/open-api/api.ts
generated
|
@ -2343,6 +2343,31 @@ export interface SearchResponseDto {
|
|||
*/
|
||||
'assets': SearchAssetResponseDto;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface ServerConfigDto
|
||||
*/
|
||||
export interface ServerConfigDto {
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof ServerConfigDto
|
||||
*/
|
||||
'loginPageMessage': string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof ServerConfigDto
|
||||
*/
|
||||
'mapTileUrl': string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof ServerConfigDto
|
||||
*/
|
||||
'oauthButtonText': string;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
|
@ -2367,6 +2392,12 @@ export interface ServerFeaturesDto {
|
|||
* @memberof ServerFeaturesDto
|
||||
*/
|
||||
'facialRecognition': boolean;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof ServerFeaturesDto
|
||||
*/
|
||||
'map': boolean;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
|
@ -2810,6 +2841,12 @@ export interface SystemConfigDto {
|
|||
* @memberof SystemConfigDto
|
||||
*/
|
||||
'machineLearning': SystemConfigMachineLearningDto;
|
||||
/**
|
||||
*
|
||||
* @type {SystemConfigMapDto}
|
||||
* @memberof SystemConfigDto
|
||||
*/
|
||||
'map': SystemConfigMapDto;
|
||||
/**
|
||||
*
|
||||
* @type {SystemConfigOAuthDto}
|
||||
|
@ -3050,6 +3087,25 @@ export interface SystemConfigMachineLearningDto {
|
|||
*/
|
||||
'url': string;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface SystemConfigMapDto
|
||||
*/
|
||||
export interface SystemConfigMapDto {
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof SystemConfigMapDto
|
||||
*/
|
||||
'enabled': boolean;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof SystemConfigMapDto
|
||||
*/
|
||||
'tileUrl': string;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
|
@ -10825,6 +10881,35 @@ export class SearchApi extends BaseAPI {
|
|||
*/
|
||||
export const ServerInfoApiAxiosParamCreator = function (configuration?: Configuration) {
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
getServerConfig: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
const localVarPath = `/server-info/config`;
|
||||
// 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: 'GET', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
|
@ -11027,6 +11112,15 @@ export const ServerInfoApiAxiosParamCreator = function (configuration?: Configur
|
|||
export const ServerInfoApiFp = function(configuration?: Configuration) {
|
||||
const localVarAxiosParamCreator = ServerInfoApiAxiosParamCreator(configuration)
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async getServerConfig(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<ServerConfigDto>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.getServerConfig(options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
|
@ -11091,6 +11185,14 @@ export const ServerInfoApiFp = function(configuration?: Configuration) {
|
|||
export const ServerInfoApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
|
||||
const localVarFp = ServerInfoApiFp(configuration)
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
getServerConfig(options?: AxiosRequestConfig): AxiosPromise<ServerConfigDto> {
|
||||
return localVarFp.getServerConfig(options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
|
@ -11149,6 +11251,16 @@ export const ServerInfoApiFactory = function (configuration?: Configuration, bas
|
|||
* @extends {BaseAPI}
|
||||
*/
|
||||
export class ServerInfoApi extends BaseAPI {
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof ServerInfoApi
|
||||
*/
|
||||
public getServerConfig(options?: AxiosRequestConfig) {
|
||||
return ServerInfoApiFp(this.configuration).getServerConfig(options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
NotificationType,
|
||||
} from '$lib/components/shared-components/notification/notification';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { featureFlags } from '$lib/stores/feature-flags.store';
|
||||
import { featureFlags } from '$lib/stores/server-config.store';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { AllJobStatusResponseDto, api, JobCommand, JobCommandDto, JobName } from '@api';
|
||||
import type { ComponentType } from 'svelte';
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
<script lang="ts">
|
||||
import {
|
||||
notificationController,
|
||||
NotificationType,
|
||||
} from '$lib/components/shared-components/notification/notification';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { api, SystemConfigMapDto } from '@api';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { fade } from 'svelte/transition';
|
||||
import SettingButtonsRow from '../setting-buttons-row.svelte';
|
||||
import SettingSwitch from '../setting-switch.svelte';
|
||||
import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte';
|
||||
|
||||
export let mapConfig: SystemConfigMapDto; // this is the config that is being edited
|
||||
export let disabled = false;
|
||||
|
||||
let savedConfig: SystemConfigMapDto;
|
||||
let defaultConfig: SystemConfigMapDto;
|
||||
|
||||
async function getConfigs() {
|
||||
[savedConfig, defaultConfig] = await Promise.all([
|
||||
api.systemConfigApi.getConfig().then((res) => res.data.map),
|
||||
api.systemConfigApi.getDefaults().then((res) => res.data.map),
|
||||
]);
|
||||
}
|
||||
|
||||
async function saveSetting() {
|
||||
try {
|
||||
const { data: current } = await api.systemConfigApi.getConfig();
|
||||
const { data: updated } = await api.systemConfigApi.updateConfig({
|
||||
systemConfigDto: { ...current, map: mapConfig },
|
||||
});
|
||||
|
||||
mapConfig = { ...updated.map };
|
||||
savedConfig = { ...updated.map };
|
||||
|
||||
notificationController.show({ message: 'Settings saved', type: NotificationType.Info });
|
||||
} catch (error) {
|
||||
handleError(error, 'Unable to save settings');
|
||||
}
|
||||
}
|
||||
|
||||
async function reset() {
|
||||
const { data: resetConfig } = await api.systemConfigApi.getConfig();
|
||||
|
||||
mapConfig = { ...resetConfig.map };
|
||||
savedConfig = { ...resetConfig.map };
|
||||
|
||||
notificationController.show({
|
||||
message: 'Reset settings to the recent saved settings',
|
||||
type: NotificationType.Info,
|
||||
});
|
||||
}
|
||||
|
||||
async function resetToDefault() {
|
||||
const { data: configs } = await api.systemConfigApi.getDefaults();
|
||||
|
||||
mapConfig = { ...configs.map };
|
||||
defaultConfig = { ...configs.map };
|
||||
|
||||
notificationController.show({
|
||||
message: 'Reset map settings to default',
|
||||
type: NotificationType.Info,
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
{#await getConfigs() then}
|
||||
<div in:fade={{ duration: 500 }}>
|
||||
<form autocomplete="off" on:submit|preventDefault>
|
||||
<div class="ml-4 mt-4 flex flex-col gap-4">
|
||||
<SettingSwitch title="ENABLED" {disabled} subtitle="Enable map features" bind:checked={mapConfig.enabled} />
|
||||
|
||||
<hr />
|
||||
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.TEXT}
|
||||
label="Tile URL"
|
||||
desc="URL to a leaflet compatible tile server"
|
||||
bind:value={mapConfig.tileUrl}
|
||||
required={true}
|
||||
disabled={disabled || !mapConfig.enabled}
|
||||
isEdited={mapConfig.tileUrl !== savedConfig.tileUrl}
|
||||
/>
|
||||
|
||||
<SettingButtonsRow
|
||||
on:reset={reset}
|
||||
on:save={saveSetting}
|
||||
on:reset-to-default={resetToDefault}
|
||||
showResetToDefault={!isEqual(savedConfig, defaultConfig)}
|
||||
{disabled}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{/await}
|
||||
</div>
|
|
@ -1,18 +1,19 @@
|
|||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { locale } from '$lib/stores/preferences.store';
|
||||
import { featureFlags, serverConfig } from '$lib/stores/server-config.store';
|
||||
import { getAssetFilename } from '$lib/utils/asset-utils';
|
||||
import { AlbumResponseDto, AssetResponseDto, ThumbnailFormat, api } from '@api';
|
||||
import type { LatLngTuple } from 'leaflet';
|
||||
import { DateTime } from 'luxon';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import Calendar from 'svelte-material-icons/Calendar.svelte';
|
||||
import CameraIris from 'svelte-material-icons/CameraIris.svelte';
|
||||
import Close from 'svelte-material-icons/Close.svelte';
|
||||
import ImageOutline from 'svelte-material-icons/ImageOutline.svelte';
|
||||
import MapMarkerOutline from 'svelte-material-icons/MapMarkerOutline.svelte';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { AssetResponseDto, AlbumResponseDto, api, ThumbnailFormat } from '@api';
|
||||
import { asByteUnitString } from '../../utils/byte-units';
|
||||
import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
|
||||
import { getAssetFilename } from '$lib/utils/asset-utils';
|
||||
import UserAvatar from '../shared-components/user-avatar.svelte';
|
||||
|
||||
export let asset: AssetResponseDto;
|
||||
|
@ -268,12 +269,12 @@
|
|||
</div>
|
||||
</section>
|
||||
|
||||
{#if latlng}
|
||||
{#if latlng && $featureFlags.loaded && $featureFlags.map}
|
||||
<div class="h-[360px]">
|
||||
{#await import('../shared-components/leaflet') then { Map, TileLayer, Marker }}
|
||||
<Map center={latlng} zoom={14}>
|
||||
<TileLayer
|
||||
urlTemplate={'https://tile.openstreetmap.org/{z}/{x}/{y}.png'}
|
||||
urlTemplate={$serverConfig.mapTileUrl}
|
||||
options={{
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
||||
}}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { goto } from '$app/navigation';
|
||||
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { featureFlags } from '$lib/stores/feature-flags.store';
|
||||
import { featureFlags, serverConfig } from '$lib/stores/server-config.store';
|
||||
import { getServerErrorMessage, handleError } from '$lib/utils/handle-error';
|
||||
import { api, oauth } from '@api';
|
||||
import { createEventDispatcher, onMount } from 'svelte';
|
||||
|
@ -158,7 +158,7 @@
|
|||
<LoadingSpinner />
|
||||
</span>
|
||||
{:else}
|
||||
{$featureFlags.passwordLogin ? 'Login with OAuth' : 'Login'}
|
||||
{$serverConfig.oauthButtonText}
|
||||
{/if}
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { TileLayer, type TileLayerOptions } from 'leaflet';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { getMapContext } from './map.svelte';
|
||||
|
||||
export let urlTemplate: string;
|
||||
|
@ -15,6 +15,6 @@
|
|||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (tileLayer) tileLayer.remove();
|
||||
tileLayer?.remove();
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
import IconButton from '$lib/components/elements/buttons/icon-button.svelte';
|
||||
import Cog from 'svelte-material-icons/Cog.svelte';
|
||||
import UserAvatar from '../user-avatar.svelte';
|
||||
import { featureFlags } from '$lib/stores/feature-flags.store';
|
||||
import { featureFlags } from '$lib/stores/server-config.store';
|
||||
export let user: UserResponseDto;
|
||||
export let showUploadButton = true;
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
import SideBarButton from './side-bar-button.svelte';
|
||||
import { locale } from '$lib/stores/preferences.store';
|
||||
import SideBarSection from './side-bar-section.svelte';
|
||||
import { featureFlags } from '$lib/stores/feature-flags.store';
|
||||
import { featureFlags } from '$lib/stores/server-config.store';
|
||||
|
||||
const getStats = async (dto: AssetApiGetAssetStatsRequest) => {
|
||||
const { data: stats } = await api.assetApi.getAssetStats(dto);
|
||||
|
@ -62,9 +62,11 @@
|
|||
<SideBarButton title="Explore" logo={Magnify} isSelected={$page.route.id === '/(user)/explore'} />
|
||||
</a>
|
||||
{/if}
|
||||
<a data-sveltekit-preload-data="hover" href={AppRoute.MAP} draggable="false">
|
||||
<SideBarButton title="Map" logo={Map} isSelected={$page.route.id === '/(user)/map'} />
|
||||
</a>
|
||||
{#if $featureFlags.map}
|
||||
<a data-sveltekit-preload-data="hover" href={AppRoute.MAP} draggable="false">
|
||||
<SideBarButton title="Map" logo={Map} isSelected={$page.route.id === '/(user)/map'} />
|
||||
</a>
|
||||
{/if}
|
||||
<a data-sveltekit-preload-data="hover" href={AppRoute.SHARING} draggable="false">
|
||||
<SideBarButton
|
||||
title="Sharing"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { featureFlags } from '$lib/stores/feature-flags.store';
|
||||
import { featureFlags } from '$lib/stores/server-config.store';
|
||||
import { oauth, UserResponseDto } from '@api';
|
||||
import { onMount } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { browser } from '$app/environment';
|
||||
import { page } from '$app/stores';
|
||||
import { featureFlags } from '$lib/stores/feature-flags.store';
|
||||
import { featureFlags } from '$lib/stores/server-config.store';
|
||||
import { APIKeyResponseDto, AuthDeviceResponseDto, oauth, UserResponseDto } from '@api';
|
||||
import SettingAccordion from '../admin-page/settings/setting-accordion.svelte';
|
||||
import ChangePasswordSettings from './change-password-settings.svelte';
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
import { env } from '$env/dynamic/public';
|
||||
export const loginPageMessage: string | undefined = env.PUBLIC_LOGIN_PAGE_MESSAGE;
|
||||
|
||||
export enum AssetAction {
|
||||
ARCHIVE = 'archive',
|
||||
UNARCHIVE = 'unarchive',
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
import { api, ServerFeaturesDto } from '@api';
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export type FeatureFlags = ServerFeaturesDto & { loaded: boolean };
|
||||
|
||||
export const featureFlags = writable<FeatureFlags>({
|
||||
loaded: false,
|
||||
clipEncode: true,
|
||||
facialRecognition: true,
|
||||
sidecar: true,
|
||||
tagImage: true,
|
||||
search: true,
|
||||
oauth: false,
|
||||
oauthAutoLaunch: false,
|
||||
passwordLogin: true,
|
||||
configFile: false,
|
||||
});
|
||||
|
||||
export const loadFeatureFlags = async () => {
|
||||
const { data } = await api.serverInfoApi.getServerFeatures();
|
||||
featureFlags.update(() => ({ ...data, loaded: true }));
|
||||
};
|
37
web/src/lib/stores/server-config.store.ts
Normal file
37
web/src/lib/stores/server-config.store.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
import { api, ServerConfigDto, ServerFeaturesDto } from '@api';
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export type FeatureFlags = ServerFeaturesDto & { loaded: boolean };
|
||||
|
||||
export const featureFlags = writable<FeatureFlags>({
|
||||
loaded: false,
|
||||
clipEncode: true,
|
||||
facialRecognition: true,
|
||||
sidecar: true,
|
||||
tagImage: true,
|
||||
map: true,
|
||||
search: true,
|
||||
oauth: false,
|
||||
oauthAutoLaunch: false,
|
||||
passwordLogin: true,
|
||||
configFile: false,
|
||||
});
|
||||
|
||||
export type ServerConfig = ServerConfigDto & { loaded: boolean };
|
||||
|
||||
export const serverConfig = writable<ServerConfig>({
|
||||
loaded: false,
|
||||
oauthButtonText: '',
|
||||
mapTileUrl: '',
|
||||
loginPageMessage: '',
|
||||
});
|
||||
|
||||
export const loadConfig = async () => {
|
||||
const [{ data: flags }, { data: config }] = await Promise.all([
|
||||
api.serverInfoApi.getServerFeatures(),
|
||||
api.serverInfoApi.getServerConfig(),
|
||||
]);
|
||||
|
||||
featureFlags.update(() => ({ ...flags, loaded: true }));
|
||||
serverConfig.update(() => ({ ...config, loaded: true }));
|
||||
};
|
|
@ -1,16 +1,19 @@
|
|||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import AssetViewer from '$lib/components/asset-viewer/asset-viewer.svelte';
|
||||
import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
|
||||
import MapSettingsModal from '$lib/components/map-page/map-settings-modal.svelte';
|
||||
import Portal from '$lib/components/shared-components/portal/portal.svelte';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||
import { mapSettings } from '$lib/stores/preferences.store';
|
||||
import { featureFlags, serverConfig } from '$lib/stores/server-config.store';
|
||||
import { MapMarkerResponseDto, api } from '@api';
|
||||
import { isEqual, omit } from 'lodash-es';
|
||||
import { DateTime, Duration } from 'luxon';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import Cog from 'svelte-material-icons/Cog.svelte';
|
||||
import type { PageData } from './$types';
|
||||
import { DateTime, Duration } from 'luxon';
|
||||
import { assetViewingStore } from '$lib/stores/asset-viewing.store';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
|
@ -29,12 +32,12 @@
|
|||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (abortController) {
|
||||
abortController.abort();
|
||||
}
|
||||
abortController?.abort();
|
||||
assetViewingStore.showAssetViewer(false);
|
||||
});
|
||||
|
||||
$: $featureFlags.map || goto(AppRoute.PHOTOS);
|
||||
|
||||
async function loadMapMarkers() {
|
||||
if (abortController) {
|
||||
abortController.abort();
|
||||
|
@ -98,70 +101,72 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<UserPageLayout user={data.user} title={data.meta.title}>
|
||||
<div class="isolate h-full w-full">
|
||||
{#if leaflet}
|
||||
{@const { Map, TileLayer, AssetMarkerCluster, Control } = leaflet}
|
||||
<Map
|
||||
center={[30, 0]}
|
||||
zoom={3}
|
||||
allowDarkMode={$mapSettings.allowDarkMode}
|
||||
options={{
|
||||
maxBounds: [
|
||||
[-90, -180],
|
||||
[90, 180],
|
||||
],
|
||||
minZoom: 2,
|
||||
}}
|
||||
>
|
||||
<TileLayer
|
||||
urlTemplate={'https://tile.openstreetmap.org/{z}/{x}/{y}.png'}
|
||||
{#if $featureFlags.loaded && $featureFlags.map}
|
||||
<UserPageLayout user={data.user} title={data.meta.title}>
|
||||
<div class="isolate h-full w-full">
|
||||
{#if leaflet}
|
||||
{@const { Map, TileLayer, AssetMarkerCluster, Control } = leaflet}
|
||||
<Map
|
||||
center={[30, 0]}
|
||||
zoom={3}
|
||||
allowDarkMode={$mapSettings.allowDarkMode}
|
||||
options={{
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
||||
maxBounds: [
|
||||
[-90, -180],
|
||||
[90, 180],
|
||||
],
|
||||
minZoom: 2,
|
||||
}}
|
||||
/>
|
||||
<AssetMarkerCluster
|
||||
markers={mapMarkers}
|
||||
on:view={({ detail }) => onViewAssets(detail.assetIds, detail.activeAssetIndex)}
|
||||
/>
|
||||
<Control>
|
||||
<button
|
||||
class="flex h-8 w-8 items-center justify-center rounded-sm border-2 border-black/20 bg-white font-bold text-black/70 hover:bg-gray-50 focus:bg-gray-50"
|
||||
title="Open map settings"
|
||||
on:click={() => (showSettingsModal = true)}
|
||||
>
|
||||
<Cog size="100%" class="p-1" />
|
||||
</button>
|
||||
</Control>
|
||||
</Map>
|
||||
{/if}
|
||||
</div>
|
||||
</UserPageLayout>
|
||||
>
|
||||
<TileLayer
|
||||
urlTemplate={$serverConfig.mapTileUrl}
|
||||
options={{
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
||||
}}
|
||||
/>
|
||||
<AssetMarkerCluster
|
||||
markers={mapMarkers}
|
||||
on:view={({ detail }) => onViewAssets(detail.assetIds, detail.activeAssetIndex)}
|
||||
/>
|
||||
<Control>
|
||||
<button
|
||||
class="flex h-8 w-8 items-center justify-center rounded-sm border-2 border-black/20 bg-white font-bold text-black/70 hover:bg-gray-50 focus:bg-gray-50"
|
||||
title="Open map settings"
|
||||
on:click={() => (showSettingsModal = true)}
|
||||
>
|
||||
<Cog size="100%" class="p-1" />
|
||||
</button>
|
||||
</Control>
|
||||
</Map>
|
||||
{/if}
|
||||
</div>
|
||||
</UserPageLayout>
|
||||
|
||||
<Portal target="body">
|
||||
{#if $showAssetViewer}
|
||||
<AssetViewer
|
||||
asset={$viewingAsset}
|
||||
showNavigation={viewingAssets.length > 1}
|
||||
on:next={navigateNext}
|
||||
on:previous={navigatePrevious}
|
||||
on:close={() => assetViewingStore.showAssetViewer(false)}
|
||||
<Portal target="body">
|
||||
{#if $showAssetViewer}
|
||||
<AssetViewer
|
||||
asset={$viewingAsset}
|
||||
showNavigation={viewingAssets.length > 1}
|
||||
on:next={navigateNext}
|
||||
on:previous={navigatePrevious}
|
||||
on:close={() => assetViewingStore.showAssetViewer(false)}
|
||||
/>
|
||||
{/if}
|
||||
</Portal>
|
||||
|
||||
{#if showSettingsModal}
|
||||
<MapSettingsModal
|
||||
settings={{ ...$mapSettings }}
|
||||
on:close={() => (showSettingsModal = false)}
|
||||
on:save={async ({ detail }) => {
|
||||
const shouldUpdate = !isEqual(omit(detail, 'allowDarkMode'), omit($mapSettings, 'allowDarkMode'));
|
||||
showSettingsModal = false;
|
||||
$mapSettings = detail;
|
||||
|
||||
if (shouldUpdate) {
|
||||
mapMarkers = await loadMapMarkers();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</Portal>
|
||||
|
||||
{#if showSettingsModal}
|
||||
<MapSettingsModal
|
||||
settings={{ ...$mapSettings }}
|
||||
on:close={() => (showSettingsModal = false)}
|
||||
on:save={async ({ detail }) => {
|
||||
const shouldUpdate = !isEqual(omit(detail, 'allowDarkMode'), omit($mapSettings, 'allowDarkMode'));
|
||||
showSettingsModal = false;
|
||||
$mapSettings = detail;
|
||||
|
||||
if (shouldUpdate) {
|
||||
mapMarkers = await loadMapMarkers();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
import AppleHeader from '$lib/components/shared-components/apple-header.svelte';
|
||||
import FaviconHeader from '$lib/components/shared-components/favicon-header.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { loadFeatureFlags } from '$lib/stores/feature-flags.store';
|
||||
import { loadConfig } from '$lib/stores/server-config.store';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { dragAndDropFilesStore } from '$lib/stores/drag-and-drop-files.store';
|
||||
import { api } from '@api';
|
||||
|
@ -37,9 +37,9 @@
|
|||
|
||||
onMount(async () => {
|
||||
try {
|
||||
await loadFeatureFlags();
|
||||
await loadConfig();
|
||||
} catch (error) {
|
||||
handleError(error, 'Unable to load feature flags');
|
||||
handleError(error, 'Unable to connect to server');
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import FFmpegSettings from '$lib/components/admin-page/settings/ffmpeg/ffmpeg-settings.svelte';
|
||||
import JobSettings from '$lib/components/admin-page/settings/job-settings/job-settings.svelte';
|
||||
import MachineLearningSettings from '$lib/components/admin-page/settings/machine-learning-settings/machine-learning-settings.svelte';
|
||||
import MapSettings from '$lib/components/admin-page/settings/map-settings/map-settings.svelte';
|
||||
import OAuthSettings from '$lib/components/admin-page/settings/oauth/oauth-settings.svelte';
|
||||
import PasswordLoginSettings from '$lib/components/admin-page/settings/password-login/password-login-settings.svelte';
|
||||
import SettingAccordion from '$lib/components/admin-page/settings/setting-accordion.svelte';
|
||||
|
@ -11,7 +12,7 @@
|
|||
import Button from '$lib/components/elements/buttons/button.svelte';
|
||||
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
|
||||
import { downloadManager } from '$lib/stores/download';
|
||||
import { featureFlags } from '$lib/stores/feature-flags.store';
|
||||
import { featureFlags } from '$lib/stores/server-config.store';
|
||||
import { downloadBlob } from '$lib/utils/asset-utils';
|
||||
import { SystemConfigDto, api, copyToClipboard } from '@api';
|
||||
import Alert from 'svelte-material-icons/Alert.svelte';
|
||||
|
@ -57,20 +58,6 @@
|
|||
<span class="pl-2">Export as JSON</span>
|
||||
</Button>
|
||||
</div>
|
||||
<SettingAccordion title="Thumbnail Settings" subtitle="Manage the resolution of thumbnail sizes">
|
||||
<ThumbnailSettings disabled={$featureFlags.configFile} thumbnailConfig={configs.thumbnail} />
|
||||
</SettingAccordion>
|
||||
|
||||
<SettingAccordion
|
||||
title="Video Transcoding Settings"
|
||||
subtitle="Manage the resolution and encoding information of the video files"
|
||||
>
|
||||
<FFmpegSettings disabled={$featureFlags.configFile} ffmpegConfig={configs.ffmpeg} />
|
||||
</SettingAccordion>
|
||||
|
||||
<SettingAccordion title="Machine Learning Settings" subtitle="Manage model settings">
|
||||
<MachineLearningSettings disabled={$featureFlags.configFile} machineLearningConfig={configs.machineLearning} />
|
||||
</SettingAccordion>
|
||||
|
||||
<SettingAccordion
|
||||
title="Job Settings"
|
||||
|
@ -80,14 +67,22 @@
|
|||
<JobSettings disabled={$featureFlags.configFile} jobConfig={configs.job} />
|
||||
</SettingAccordion>
|
||||
|
||||
<SettingAccordion title="Password Authentication" subtitle="Manage login with password settings">
|
||||
<PasswordLoginSettings disabled={$featureFlags.configFile} passwordLoginConfig={configs.passwordLogin} />
|
||||
<SettingAccordion title="Machine Learning Settings" subtitle="Manage model settings">
|
||||
<MachineLearningSettings disabled={$featureFlags.configFile} machineLearningConfig={configs.machineLearning} />
|
||||
</SettingAccordion>
|
||||
|
||||
<SettingAccordion title="Map Settings" subtitle="Manage map settings">
|
||||
<MapSettings disabled={$featureFlags.configFile} mapConfig={configs.map} />
|
||||
</SettingAccordion>
|
||||
|
||||
<SettingAccordion title="OAuth Authentication" subtitle="Manage the login with OAuth settings">
|
||||
<OAuthSettings disabled={$featureFlags.configFile} oauthConfig={configs.oauth} />
|
||||
</SettingAccordion>
|
||||
|
||||
<SettingAccordion title="Password Authentication" subtitle="Manage login with password settings">
|
||||
<PasswordLoginSettings disabled={$featureFlags.configFile} passwordLoginConfig={configs.passwordLogin} />
|
||||
</SettingAccordion>
|
||||
|
||||
<SettingAccordion
|
||||
title="Storage Template"
|
||||
subtitle="Manage the folder structure and file name of the upload asset"
|
||||
|
@ -99,5 +94,16 @@
|
|||
user={data.user}
|
||||
/>
|
||||
</SettingAccordion>
|
||||
|
||||
<SettingAccordion title="Thumbnail Settings" subtitle="Manage the resolution of thumbnail sizes">
|
||||
<ThumbnailSettings disabled={$featureFlags.configFile} thumbnailConfig={configs.thumbnail} />
|
||||
</SettingAccordion>
|
||||
|
||||
<SettingAccordion
|
||||
title="Video Transcoding Settings"
|
||||
subtitle="Manage the resolution and encoding information of the video files"
|
||||
>
|
||||
<FFmpegSettings disabled={$featureFlags.configFile} ffmpegConfig={configs.ffmpeg} />
|
||||
</SettingAccordion>
|
||||
{/await}
|
||||
</section>
|
||||
|
|
|
@ -3,18 +3,17 @@
|
|||
import LoginForm from '$lib/components/forms/login-form.svelte';
|
||||
import FullscreenContainer from '$lib/components/shared-components/fullscreen-container.svelte';
|
||||
import { AppRoute } from '$lib/constants';
|
||||
import { loginPageMessage } from '$lib/constants';
|
||||
import { featureFlags } from '$lib/stores/feature-flags.store';
|
||||
import { featureFlags, serverConfig } from '$lib/stores/server-config.store';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
export let data: PageData;
|
||||
</script>
|
||||
|
||||
{#if $featureFlags.loaded}
|
||||
<FullscreenContainer title={data.meta.title} showMessage={!!loginPageMessage}>
|
||||
<FullscreenContainer title={data.meta.title} showMessage={!!$serverConfig.loginPageMessage}>
|
||||
<p slot="message">
|
||||
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
||||
{@html loginPageMessage}
|
||||
{@html $serverConfig.loginPageMessage}
|
||||
</p>
|
||||
|
||||
<LoginForm
|
||||
|
|
Loading…
Add table
Reference in a new issue