diff --git a/cli/src/api/open-api/api.ts b/cli/src/api/open-api/api.ts index 03d260a6b1..850aeae7f3 100644 --- a/cli/src/api/open-api/api.ts +++ b/cli/src/api/open-api/api.ts @@ -2087,6 +2087,43 @@ export interface SearchResponseDto { */ 'assets': SearchAssetResponseDto; } +/** + * + * @export + * @interface ServerFeaturesDto + */ +export interface ServerFeaturesDto { + /** + * + * @type {boolean} + * @memberof ServerFeaturesDto + */ + 'machineLearning': boolean; + /** + * + * @type {boolean} + * @memberof ServerFeaturesDto + */ + 'oauth': boolean; + /** + * + * @type {boolean} + * @memberof ServerFeaturesDto + */ + 'oauthAutoLaunch': boolean; + /** + * + * @type {boolean} + * @memberof ServerFeaturesDto + */ + 'passwordLogin': boolean; + /** + * + * @type {boolean} + * @memberof ServerFeaturesDto + */ + 'search': boolean; +} /** * * @export @@ -2208,25 +2245,25 @@ export interface ServerStatsResponseDto { /** * * @export - * @interface ServerVersionReponseDto + * @interface ServerVersionResponseDto */ -export interface ServerVersionReponseDto { +export interface ServerVersionResponseDto { /** * * @type {number} - * @memberof ServerVersionReponseDto + * @memberof ServerVersionResponseDto */ 'major': number; /** * * @type {number} - * @memberof ServerVersionReponseDto + * @memberof ServerVersionResponseDto */ 'minor': number; /** * * @type {number} - * @memberof ServerVersionReponseDto + * @memberof ServerVersionResponseDto */ 'patch': number; } @@ -10156,6 +10193,35 @@ export class SearchApi extends BaseAPI { */ export const ServerInfoApiAxiosParamCreator = function (configuration?: Configuration) { return { + /** + * + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getServerFeatures: async (options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/server-info/features`; + // 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. @@ -10329,6 +10395,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 getServerFeatures(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.getServerFeatures(options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, /** * * @param {*} [options] Override http request option. @@ -10343,7 +10418,7 @@ export const ServerInfoApiFp = function(configuration?: Configuration) { * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async getServerVersion(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + async getServerVersion(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getServerVersion(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, @@ -10384,6 +10459,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} + */ + getServerFeatures(options?: AxiosRequestConfig): AxiosPromise { + return localVarFp.getServerFeatures(options).then((request) => request(axios, basePath)); + }, /** * * @param {*} [options] Override http request option. @@ -10397,7 +10480,7 @@ export const ServerInfoApiFactory = function (configuration?: Configuration, bas * @param {*} [options] Override http request option. * @throws {RequiredError} */ - getServerVersion(options?: AxiosRequestConfig): AxiosPromise { + getServerVersion(options?: AxiosRequestConfig): AxiosPromise { return localVarFp.getServerVersion(options).then((request) => request(axios, basePath)); }, /** @@ -10434,6 +10517,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 getServerFeatures(options?: AxiosRequestConfig) { + return ServerInfoApiFp(this.configuration).getServerFeatures(options).then((request) => request(this.axios, this.basePath)); + } + /** * * @param {*} [options] Override http request option. diff --git a/cli/src/cli/base-command.ts b/cli/src/cli/base-command.ts index 06247e29c8..c2fb8fee92 100644 --- a/cli/src/cli/base-command.ts +++ b/cli/src/cli/base-command.ts @@ -4,14 +4,14 @@ import { SessionService } from '../services/session.service'; import { LoginError } from '../cores/errors/login-error'; import { exit } from 'node:process'; import os from 'os'; -import { ServerVersionReponseDto, UserResponseDto } from 'src/api/open-api'; +import { ServerVersionResponseDto, UserResponseDto } from 'src/api/open-api'; export abstract class BaseCommand { protected sessionService!: SessionService; protected immichApi!: ImmichApi; protected deviceId!: string; protected user!: UserResponseDto; - protected serverVersion!: ServerVersionReponseDto; + protected serverVersion!: ServerVersionResponseDto; protected configDir; protected authPath; diff --git a/mobile/lib/shared/models/server_info_state.model.dart b/mobile/lib/shared/models/server_info_state.model.dart index 7b1ea9c918..6623ac1666 100644 --- a/mobile/lib/shared/models/server_info_state.model.dart +++ b/mobile/lib/shared/models/server_info_state.model.dart @@ -1,7 +1,7 @@ import 'package:openapi/api.dart'; class ServerInfoState { - final ServerVersionReponseDto serverVersion; + final ServerVersionResponseDto serverVersion; final bool isVersionMismatch; final String versionMismatchErrorMessage; @@ -12,7 +12,7 @@ class ServerInfoState { }); ServerInfoState copyWith({ - ServerVersionReponseDto? serverVersion, + ServerVersionResponseDto? serverVersion, bool? isVersionMismatch, String? versionMismatchErrorMessage, }) { diff --git a/mobile/lib/shared/providers/server_info.provider.dart b/mobile/lib/shared/providers/server_info.provider.dart index 2613b85224..ff0945ead0 100644 --- a/mobile/lib/shared/providers/server_info.provider.dart +++ b/mobile/lib/shared/providers/server_info.provider.dart @@ -10,7 +10,7 @@ class ServerInfoNotifier extends StateNotifier { ServerInfoNotifier(this._serverInfoService) : super( ServerInfoState( - serverVersion: ServerVersionReponseDto( + serverVersion: ServerVersionResponseDto( major: 0, patch_: 0, minor: 0, @@ -23,7 +23,7 @@ class ServerInfoNotifier extends StateNotifier { final ServerInfoService _serverInfoService; getServerVersion() async { - ServerVersionReponseDto? serverVersion = + ServerVersionResponseDto? serverVersion = await _serverInfoService.getServerVersion(); if (serverVersion == null) { diff --git a/mobile/lib/shared/services/server_info.service.dart b/mobile/lib/shared/services/server_info.service.dart index b112fc46ca..bf923dfab0 100644 --- a/mobile/lib/shared/services/server_info.service.dart +++ b/mobile/lib/shared/services/server_info.service.dart @@ -24,7 +24,7 @@ class ServerInfoService { } } - Future getServerVersion() async { + Future getServerVersion() async { try { return await _apiService.serverInfoApi.getServerVersion(); } catch (e) { diff --git a/mobile/openapi/.openapi-generator/FILES b/mobile/openapi/.openapi-generator/FILES index d8e4b1fad8..c51b8a3e02 100644 --- a/mobile/openapi/.openapi-generator/FILES +++ b/mobile/openapi/.openapi-generator/FILES @@ -85,12 +85,13 @@ doc/SearchExploreResponseDto.md doc/SearchFacetCountResponseDto.md doc/SearchFacetResponseDto.md doc/SearchResponseDto.md +doc/ServerFeaturesDto.md doc/ServerInfoApi.md doc/ServerInfoResponseDto.md doc/ServerMediaTypesResponseDto.md doc/ServerPingResponse.md doc/ServerStatsResponseDto.md -doc/ServerVersionReponseDto.md +doc/ServerVersionResponseDto.md doc/SharedLinkApi.md doc/SharedLinkCreateDto.md doc/SharedLinkEditDto.md @@ -223,11 +224,12 @@ 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_features_dto.dart lib/model/server_info_response_dto.dart lib/model/server_media_types_response_dto.dart lib/model/server_ping_response.dart lib/model/server_stats_response_dto.dart -lib/model/server_version_reponse_dto.dart +lib/model/server_version_response_dto.dart lib/model/shared_link_create_dto.dart lib/model/shared_link_edit_dto.dart lib/model/shared_link_response_dto.dart @@ -342,12 +344,13 @@ 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_features_dto_test.dart test/server_info_api_test.dart test/server_info_response_dto_test.dart test/server_media_types_response_dto_test.dart test/server_ping_response_test.dart test/server_stats_response_dto_test.dart -test/server_version_reponse_dto_test.dart +test/server_version_response_dto_test.dart test/shared_link_api_test.dart test/shared_link_create_dto_test.dart test/shared_link_edit_dto_test.dart diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 042d69591f..6c6108a9fe 100644 --- a/mobile/openapi/README.md +++ b/mobile/openapi/README.md @@ -140,6 +140,7 @@ Class | Method | HTTP request | Description *SearchApi* | [**getExploreData**](doc//SearchApi.md#getexploredata) | **GET** /search/explore | *SearchApi* | [**getSearchConfig**](doc//SearchApi.md#getsearchconfig) | **GET** /search/config | *SearchApi* | [**search**](doc//SearchApi.md#search) | **GET** /search | +*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 | *ServerInfoApi* | [**getStats**](doc//ServerInfoApi.md#getstats) | **GET** /server-info/stats | @@ -252,11 +253,12 @@ Class | Method | HTTP request | Description - [SearchFacetCountResponseDto](doc//SearchFacetCountResponseDto.md) - [SearchFacetResponseDto](doc//SearchFacetResponseDto.md) - [SearchResponseDto](doc//SearchResponseDto.md) + - [ServerFeaturesDto](doc//ServerFeaturesDto.md) - [ServerInfoResponseDto](doc//ServerInfoResponseDto.md) - [ServerMediaTypesResponseDto](doc//ServerMediaTypesResponseDto.md) - [ServerPingResponse](doc//ServerPingResponse.md) - [ServerStatsResponseDto](doc//ServerStatsResponseDto.md) - - [ServerVersionReponseDto](doc//ServerVersionReponseDto.md) + - [ServerVersionResponseDto](doc//ServerVersionResponseDto.md) - [SharedLinkCreateDto](doc//SharedLinkCreateDto.md) - [SharedLinkEditDto](doc//SharedLinkEditDto.md) - [SharedLinkResponseDto](doc//SharedLinkResponseDto.md) diff --git a/mobile/openapi/doc/ServerFeaturesDto.md b/mobile/openapi/doc/ServerFeaturesDto.md new file mode 100644 index 0000000000..303b89f188 --- /dev/null +++ b/mobile/openapi/doc/ServerFeaturesDto.md @@ -0,0 +1,19 @@ +# openapi.model.ServerFeaturesDto + +## Load the model package +```dart +import 'package:openapi/api.dart'; +``` + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**machineLearning** | **bool** | | +**oauth** | **bool** | | +**oauthAutoLaunch** | **bool** | | +**passwordLogin** | **bool** | | +**search** | **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) + + diff --git a/mobile/openapi/doc/ServerInfoApi.md b/mobile/openapi/doc/ServerInfoApi.md index 0666c7d414..62758afbe8 100644 --- a/mobile/openapi/doc/ServerInfoApi.md +++ b/mobile/openapi/doc/ServerInfoApi.md @@ -9,6 +9,7 @@ All URIs are relative to */api* Method | HTTP request | Description ------------- | ------------- | ------------- +[**getServerFeatures**](ServerInfoApi.md#getserverfeatures) | **GET** /server-info/features | [**getServerInfo**](ServerInfoApi.md#getserverinfo) | **GET** /server-info | [**getServerVersion**](ServerInfoApi.md#getserverversion) | **GET** /server-info/version | [**getStats**](ServerInfoApi.md#getstats) | **GET** /server-info/stats | @@ -16,6 +17,43 @@ Method | HTTP request | Description [**pingServer**](ServerInfoApi.md#pingserver) | **GET** /server-info/ping | +# **getServerFeatures** +> ServerFeaturesDto getServerFeatures() + + + +### Example +```dart +import 'package:openapi/api.dart'; + +final api_instance = ServerInfoApi(); + +try { + final result = api_instance.getServerFeatures(); + print(result); +} catch (e) { + print('Exception when calling ServerInfoApi->getServerFeatures: $e\n'); +} +``` + +### Parameters +This endpoint does not need any parameter. + +### Return type + +[**ServerFeaturesDto**](ServerFeaturesDto.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) + # **getServerInfo** > ServerInfoResponseDto getServerInfo() @@ -68,7 +106,7 @@ This endpoint does not need any parameter. [[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) # **getServerVersion** -> ServerVersionReponseDto getServerVersion() +> ServerVersionResponseDto getServerVersion() @@ -91,7 +129,7 @@ This endpoint does not need any parameter. ### Return type -[**ServerVersionReponseDto**](ServerVersionReponseDto.md) +[**ServerVersionResponseDto**](ServerVersionResponseDto.md) ### Authorization diff --git a/mobile/openapi/doc/ServerVersionReponseDto.md b/mobile/openapi/doc/ServerVersionResponseDto.md similarity index 91% rename from mobile/openapi/doc/ServerVersionReponseDto.md rename to mobile/openapi/doc/ServerVersionResponseDto.md index 68dfa972cc..b48291f12f 100644 --- a/mobile/openapi/doc/ServerVersionReponseDto.md +++ b/mobile/openapi/doc/ServerVersionResponseDto.md @@ -1,4 +1,4 @@ -# openapi.model.ServerVersionReponseDto +# openapi.model.ServerVersionResponseDto ## Load the model package ```dart diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart index e83c7b868b..064a265175 100644 --- a/mobile/openapi/lib/api.dart +++ b/mobile/openapi/lib/api.dart @@ -116,11 +116,12 @@ 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_features_dto.dart'; part 'model/server_info_response_dto.dart'; part 'model/server_media_types_response_dto.dart'; part 'model/server_ping_response.dart'; part 'model/server_stats_response_dto.dart'; -part 'model/server_version_reponse_dto.dart'; +part 'model/server_version_response_dto.dart'; part 'model/shared_link_create_dto.dart'; part 'model/shared_link_edit_dto.dart'; part 'model/shared_link_response_dto.dart'; diff --git a/mobile/openapi/lib/api/server_info_api.dart b/mobile/openapi/lib/api/server_info_api.dart index fb34e09ce8..3b7899cc26 100644 --- a/mobile/openapi/lib/api/server_info_api.dart +++ b/mobile/openapi/lib/api/server_info_api.dart @@ -16,6 +16,47 @@ class ServerInfoApi { final ApiClient apiClient; + /// Performs an HTTP 'GET /server-info/features' operation and returns the [Response]. + Future getServerFeaturesWithHttpInfo() async { + // ignore: prefer_const_declarations + final path = r'/server-info/features'; + + // ignore: prefer_final_locals + Object? postBody; + + final queryParams = []; + final headerParams = {}; + final formParams = {}; + + const contentTypes = []; + + + return apiClient.invokeAPI( + path, + 'GET', + queryParams, + postBody, + headerParams, + formParams, + contentTypes.isEmpty ? null : contentTypes.first, + ); + } + + Future getServerFeatures() async { + final response = await getServerFeaturesWithHttpInfo(); + 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), 'ServerFeaturesDto',) as ServerFeaturesDto; + + } + return null; + } + /// Performs an HTTP 'GET /server-info' operation and returns the [Response]. Future getServerInfoWithHttpInfo() async { // ignore: prefer_const_declarations @@ -83,7 +124,7 @@ class ServerInfoApi { ); } - Future getServerVersion() async { + Future getServerVersion() async { final response = await getServerVersionWithHttpInfo(); if (response.statusCode >= HttpStatus.badRequest) { throw ApiException(response.statusCode, await _decodeBodyBytes(response)); @@ -92,7 +133,7 @@ class ServerInfoApi { // 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), 'ServerVersionReponseDto',) as ServerVersionReponseDto; + return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'ServerVersionResponseDto',) as ServerVersionResponseDto; } return null; diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart index a46580899d..830aab9369 100644 --- a/mobile/openapi/lib/api_client.dart +++ b/mobile/openapi/lib/api_client.dart @@ -327,6 +327,8 @@ class ApiClient { return SearchFacetResponseDto.fromJson(value); case 'SearchResponseDto': return SearchResponseDto.fromJson(value); + case 'ServerFeaturesDto': + return ServerFeaturesDto.fromJson(value); case 'ServerInfoResponseDto': return ServerInfoResponseDto.fromJson(value); case 'ServerMediaTypesResponseDto': @@ -335,8 +337,8 @@ class ApiClient { return ServerPingResponse.fromJson(value); case 'ServerStatsResponseDto': return ServerStatsResponseDto.fromJson(value); - case 'ServerVersionReponseDto': - return ServerVersionReponseDto.fromJson(value); + case 'ServerVersionResponseDto': + return ServerVersionResponseDto.fromJson(value); case 'SharedLinkCreateDto': return SharedLinkCreateDto.fromJson(value); case 'SharedLinkEditDto': diff --git a/mobile/openapi/lib/model/server_features_dto.dart b/mobile/openapi/lib/model/server_features_dto.dart new file mode 100644 index 0000000000..0c83fa7921 --- /dev/null +++ b/mobile/openapi/lib/model/server_features_dto.dart @@ -0,0 +1,130 @@ +// +// 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 ServerFeaturesDto { + /// Returns a new [ServerFeaturesDto] instance. + ServerFeaturesDto({ + required this.machineLearning, + required this.oauth, + required this.oauthAutoLaunch, + required this.passwordLogin, + required this.search, + }); + + bool machineLearning; + + bool oauth; + + bool oauthAutoLaunch; + + bool passwordLogin; + + bool search; + + @override + bool operator ==(Object other) => identical(this, other) || other is ServerFeaturesDto && + other.machineLearning == machineLearning && + other.oauth == oauth && + other.oauthAutoLaunch == oauthAutoLaunch && + other.passwordLogin == passwordLogin && + other.search == search; + + @override + int get hashCode => + // ignore: unnecessary_parenthesis + (machineLearning.hashCode) + + (oauth.hashCode) + + (oauthAutoLaunch.hashCode) + + (passwordLogin.hashCode) + + (search.hashCode); + + @override + String toString() => 'ServerFeaturesDto[machineLearning=$machineLearning, oauth=$oauth, oauthAutoLaunch=$oauthAutoLaunch, passwordLogin=$passwordLogin, search=$search]'; + + Map toJson() { + final json = {}; + json[r'machineLearning'] = this.machineLearning; + json[r'oauth'] = this.oauth; + json[r'oauthAutoLaunch'] = this.oauthAutoLaunch; + json[r'passwordLogin'] = this.passwordLogin; + json[r'search'] = this.search; + return json; + } + + /// Returns a new [ServerFeaturesDto] instance and imports its values from + /// [value] if it's a [Map], null otherwise. + // ignore: prefer_constructors_over_static_methods + static ServerFeaturesDto? fromJson(dynamic value) { + if (value is Map) { + final json = value.cast(); + + return ServerFeaturesDto( + machineLearning: mapValueOfType(json, r'machineLearning')!, + oauth: mapValueOfType(json, r'oauth')!, + oauthAutoLaunch: mapValueOfType(json, r'oauthAutoLaunch')!, + passwordLogin: mapValueOfType(json, r'passwordLogin')!, + search: mapValueOfType(json, r'search')!, + ); + } + return null; + } + + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; + if (json is List && json.isNotEmpty) { + for (final row in json) { + final value = ServerFeaturesDto.fromJson(row); + if (value != null) { + result.add(value); + } + } + } + return result.toList(growable: growable); + } + + static Map mapFromJson(dynamic json) { + final map = {}; + if (json is Map && json.isNotEmpty) { + json = json.cast(); // ignore: parameter_assignments + for (final entry in json.entries) { + final value = ServerFeaturesDto.fromJson(entry.value); + if (value != null) { + map[entry.key] = value; + } + } + } + return map; + } + + // maps a json object with a list of ServerFeaturesDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; + if (json is Map && json.isNotEmpty) { + // ignore: parameter_assignments + json = json.cast(); + for (final entry in json.entries) { + map[entry.key] = ServerFeaturesDto.listFromJson(entry.value, growable: growable,); + } + } + return map; + } + + /// The list of required keys that must be present in a JSON. + static const requiredKeys = { + 'machineLearning', + 'oauth', + 'oauthAutoLaunch', + 'passwordLogin', + 'search', + }; +} + diff --git a/mobile/openapi/lib/model/server_version_reponse_dto.dart b/mobile/openapi/lib/model/server_version_response_dto.dart similarity index 63% rename from mobile/openapi/lib/model/server_version_reponse_dto.dart rename to mobile/openapi/lib/model/server_version_response_dto.dart index a1ec0e68e5..353d65049b 100644 --- a/mobile/openapi/lib/model/server_version_reponse_dto.dart +++ b/mobile/openapi/lib/model/server_version_response_dto.dart @@ -10,9 +10,9 @@ part of openapi.api; -class ServerVersionReponseDto { - /// Returns a new [ServerVersionReponseDto] instance. - ServerVersionReponseDto({ +class ServerVersionResponseDto { + /// Returns a new [ServerVersionResponseDto] instance. + ServerVersionResponseDto({ required this.major, required this.minor, required this.patch_, @@ -25,7 +25,7 @@ class ServerVersionReponseDto { int patch_; @override - bool operator ==(Object other) => identical(this, other) || other is ServerVersionReponseDto && + bool operator ==(Object other) => identical(this, other) || other is ServerVersionResponseDto && other.major == major && other.minor == minor && other.patch_ == patch_; @@ -38,7 +38,7 @@ class ServerVersionReponseDto { (patch_.hashCode); @override - String toString() => 'ServerVersionReponseDto[major=$major, minor=$minor, patch_=$patch_]'; + String toString() => 'ServerVersionResponseDto[major=$major, minor=$minor, patch_=$patch_]'; Map toJson() { final json = {}; @@ -48,14 +48,14 @@ class ServerVersionReponseDto { return json; } - /// Returns a new [ServerVersionReponseDto] instance and imports its values from + /// Returns a new [ServerVersionResponseDto] instance and imports its values from /// [value] if it's a [Map], null otherwise. // ignore: prefer_constructors_over_static_methods - static ServerVersionReponseDto? fromJson(dynamic value) { + static ServerVersionResponseDto? fromJson(dynamic value) { if (value is Map) { final json = value.cast(); - return ServerVersionReponseDto( + return ServerVersionResponseDto( major: mapValueOfType(json, r'major')!, minor: mapValueOfType(json, r'minor')!, patch_: mapValueOfType(json, r'patch')!, @@ -64,11 +64,11 @@ class ServerVersionReponseDto { return null; } - static List listFromJson(dynamic json, {bool growable = false,}) { - final result = []; + static List listFromJson(dynamic json, {bool growable = false,}) { + final result = []; if (json is List && json.isNotEmpty) { for (final row in json) { - final value = ServerVersionReponseDto.fromJson(row); + final value = ServerVersionResponseDto.fromJson(row); if (value != null) { result.add(value); } @@ -77,12 +77,12 @@ class ServerVersionReponseDto { return result.toList(growable: growable); } - static Map mapFromJson(dynamic json) { - final map = {}; + static Map mapFromJson(dynamic json) { + final map = {}; if (json is Map && json.isNotEmpty) { json = json.cast(); // ignore: parameter_assignments for (final entry in json.entries) { - final value = ServerVersionReponseDto.fromJson(entry.value); + final value = ServerVersionResponseDto.fromJson(entry.value); if (value != null) { map[entry.key] = value; } @@ -91,14 +91,14 @@ class ServerVersionReponseDto { return map; } - // maps a json object with a list of ServerVersionReponseDto-objects as value to a dart map - static Map> mapListFromJson(dynamic json, {bool growable = false,}) { - final map = >{}; + // maps a json object with a list of ServerVersionResponseDto-objects as value to a dart map + static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + final map = >{}; if (json is Map && json.isNotEmpty) { // ignore: parameter_assignments json = json.cast(); for (final entry in json.entries) { - map[entry.key] = ServerVersionReponseDto.listFromJson(entry.value, growable: growable,); + map[entry.key] = ServerVersionResponseDto.listFromJson(entry.value, growable: growable,); } } return map; diff --git a/mobile/openapi/test/server_features_dto_test.dart b/mobile/openapi/test/server_features_dto_test.dart new file mode 100644 index 0000000000..d2ae364c4b --- /dev/null +++ b/mobile/openapi/test/server_features_dto_test.dart @@ -0,0 +1,47 @@ +// +// 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 ServerFeaturesDto +void main() { + // final instance = ServerFeaturesDto(); + + group('test ServerFeaturesDto', () { + // bool machineLearning + test('to test the property `machineLearning`', () async { + // TODO + }); + + // bool oauth + test('to test the property `oauth`', () async { + // TODO + }); + + // bool oauthAutoLaunch + test('to test the property `oauthAutoLaunch`', () async { + // TODO + }); + + // bool passwordLogin + test('to test the property `passwordLogin`', () async { + // TODO + }); + + // bool search + test('to test the property `search`', () async { + // TODO + }); + + + }); + +} diff --git a/mobile/openapi/test/server_info_api_test.dart b/mobile/openapi/test/server_info_api_test.dart index 4fee682a25..8ca3e30ef1 100644 --- a/mobile/openapi/test/server_info_api_test.dart +++ b/mobile/openapi/test/server_info_api_test.dart @@ -17,12 +17,17 @@ void main() { // final instance = ServerInfoApi(); group('tests for ServerInfoApi', () { + //Future getServerFeatures() async + test('test getServerFeatures', () async { + // TODO + }); + //Future getServerInfo() async test('test getServerInfo', () async { // TODO }); - //Future getServerVersion() async + //Future getServerVersion() async test('test getServerVersion', () async { // TODO }); diff --git a/mobile/openapi/test/server_version_reponse_dto_test.dart b/mobile/openapi/test/server_version_response_dto_test.dart similarity index 82% rename from mobile/openapi/test/server_version_reponse_dto_test.dart rename to mobile/openapi/test/server_version_response_dto_test.dart index 3095e7a462..add42ccd66 100644 --- a/mobile/openapi/test/server_version_reponse_dto_test.dart +++ b/mobile/openapi/test/server_version_response_dto_test.dart @@ -11,11 +11,11 @@ import 'package:openapi/api.dart'; import 'package:test/test.dart'; -// tests for ServerVersionReponseDto +// tests for ServerVersionResponseDto void main() { - // final instance = ServerVersionReponseDto(); + // final instance = ServerVersionResponseDto(); - group('test ServerVersionReponseDto', () { + group('test ServerVersionResponseDto', () { // int major test('to test the property `major`', () async { // TODO diff --git a/server/immich-openapi-specs.json b/server/immich-openapi-specs.json index 4078e687d6..91f72c1708 100644 --- a/server/immich-openapi-specs.json +++ b/server/immich-openapi-specs.json @@ -3248,6 +3248,27 @@ ] } }, + "/server-info/features": { + "get": { + "operationId": "getServerFeatures", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServerFeaturesDto" + } + } + }, + "description": "" + } + }, + "tags": [ + "Server Info" + ] + } + }, "/server-info/media-types": { "get": { "operationId": "getSupportedMediaTypes", @@ -3331,7 +3352,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ServerVersionReponseDto" + "$ref": "#/components/schemas/ServerVersionResponseDto" } } }, @@ -6331,6 +6352,33 @@ ], "type": "object" }, + "ServerFeaturesDto": { + "properties": { + "machineLearning": { + "type": "boolean" + }, + "oauth": { + "type": "boolean" + }, + "oauthAutoLaunch": { + "type": "boolean" + }, + "passwordLogin": { + "type": "boolean" + }, + "search": { + "type": "boolean" + } + }, + "required": [ + "machineLearning", + "search", + "oauth", + "oauthAutoLaunch", + "passwordLogin" + ], + "type": "object" + }, "ServerInfoResponseDto": { "properties": { "diskAvailable": { @@ -6450,7 +6498,7 @@ ], "type": "object" }, - "ServerVersionReponseDto": { + "ServerVersionResponseDto": { "properties": { "major": { "type": "integer" diff --git a/server/src/domain/domain.constant.ts b/server/src/domain/domain.constant.ts index 4c881d7eee..7b60b796aa 100644 --- a/server/src/domain/domain.constant.ts +++ b/server/src/domain/domain.constant.ts @@ -21,6 +21,8 @@ export const SERVER_VERSION = `${serverVersion.major}.${serverVersion.minor}.${s export const APP_MEDIA_LOCATION = process.env.IMMICH_MEDIA_LOCATION || './upload'; +export const SEARCH_ENABLED = process.env.TYPESENSE_ENABLED !== 'false'; + export const MACHINE_LEARNING_URL = process.env.IMMICH_MACHINE_LEARNING_URL || 'http://immich-machine-learning:3003'; export const MACHINE_LEARNING_ENABLED = MACHINE_LEARNING_URL !== 'false'; diff --git a/server/src/domain/server-info/index.ts b/server/src/domain/server-info/index.ts index 72113665a8..74a46a52b8 100644 --- a/server/src/domain/server-info/index.ts +++ b/server/src/domain/server-info/index.ts @@ -1,2 +1,2 @@ -export * from './response-dto'; +export * from './server-info.dto'; export * from './server-info.service'; diff --git a/server/src/domain/server-info/response-dto/index.ts b/server/src/domain/server-info/response-dto/index.ts deleted file mode 100644 index 47cbd2ff8f..0000000000 --- a/server/src/domain/server-info/response-dto/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './server-info-response.dto'; -export * from './server-ping-response.dto'; -export * from './server-stats-response.dto'; -export * from './server-version-response.dto'; -export * from './usage-by-user-response.dto'; diff --git a/server/src/domain/server-info/response-dto/server-info-response.dto.ts b/server/src/domain/server-info/response-dto/server-info-response.dto.ts deleted file mode 100644 index e844da6899..0000000000 --- a/server/src/domain/server-info/response-dto/server-info-response.dto.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; - -export class ServerInfoResponseDto { - diskSize!: string; - diskUse!: string; - diskAvailable!: string; - - @ApiProperty({ type: 'integer', format: 'int64' }) - diskSizeRaw!: number; - - @ApiProperty({ type: 'integer', format: 'int64' }) - diskUseRaw!: number; - - @ApiProperty({ type: 'integer', format: 'int64' }) - diskAvailableRaw!: number; - - @ApiProperty({ type: 'number', format: 'float' }) - diskUsagePercentage!: number; -} diff --git a/server/src/domain/server-info/response-dto/server-ping-response.dto.ts b/server/src/domain/server-info/response-dto/server-ping-response.dto.ts deleted file mode 100644 index 8b41b4af1b..0000000000 --- a/server/src/domain/server-info/response-dto/server-ping-response.dto.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ApiResponseProperty } from '@nestjs/swagger'; - -export class ServerPingResponse { - constructor(res: string) { - this.res = res; - } - - @ApiResponseProperty({ type: String, example: 'pong' }) - res!: string; -} diff --git a/server/src/domain/server-info/response-dto/server-stats-response.dto.ts b/server/src/domain/server-info/response-dto/server-stats-response.dto.ts deleted file mode 100644 index 1459ba452a..0000000000 --- a/server/src/domain/server-info/response-dto/server-stats-response.dto.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { UsageByUserDto } from './usage-by-user-response.dto'; - -export class ServerStatsResponseDto { - @ApiProperty({ type: 'integer' }) - photos = 0; - - @ApiProperty({ type: 'integer' }) - videos = 0; - - @ApiProperty({ type: 'integer', format: 'int64' }) - usage = 0; - - @ApiProperty({ - isArray: true, - type: UsageByUserDto, - title: 'Array of usage for each user', - example: [ - { - photos: 1, - videos: 1, - diskUsageRaw: 1, - }, - ], - }) - usageByUser: UsageByUserDto[] = []; -} - -export class ServerMediaTypesResponseDto { - video!: string[]; - image!: string[]; - sidecar!: string[]; -} diff --git a/server/src/domain/server-info/response-dto/server-version-response.dto.ts b/server/src/domain/server-info/response-dto/server-version-response.dto.ts deleted file mode 100644 index 373fa734f8..0000000000 --- a/server/src/domain/server-info/response-dto/server-version-response.dto.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { IServerVersion } from '@app/domain'; -import { ApiProperty } from '@nestjs/swagger'; - -export class ServerVersionReponseDto implements IServerVersion { - @ApiProperty({ type: 'integer' }) - major!: number; - @ApiProperty({ type: 'integer' }) - minor!: number; - @ApiProperty({ type: 'integer' }) - patch!: number; -} diff --git a/server/src/domain/server-info/response-dto/usage-by-user-response.dto.ts b/server/src/domain/server-info/response-dto/usage-by-user-response.dto.ts deleted file mode 100644 index ac3a829077..0000000000 --- a/server/src/domain/server-info/response-dto/usage-by-user-response.dto.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; - -export class UsageByUserDto { - @ApiProperty({ type: 'string' }) - userId!: string; - @ApiProperty({ type: 'string' }) - userFirstName!: string; - @ApiProperty({ type: 'string' }) - userLastName!: string; - @ApiProperty({ type: 'integer' }) - photos!: number; - @ApiProperty({ type: 'integer' }) - videos!: number; - @ApiProperty({ type: 'integer', format: 'int64' }) - usage!: number; -} diff --git a/server/src/domain/server-info/server-info.dto.ts b/server/src/domain/server-info/server-info.dto.ts new file mode 100644 index 0000000000..ea0699aa6f --- /dev/null +++ b/server/src/domain/server-info/server-info.dto.ts @@ -0,0 +1,89 @@ +import { IServerVersion } from '@app/domain'; +import { ApiProperty, ApiResponseProperty } from '@nestjs/swagger'; + +export class ServerPingResponse { + @ApiResponseProperty({ type: String, example: 'pong' }) + res!: string; +} + +export class ServerInfoResponseDto { + diskSize!: string; + diskUse!: string; + diskAvailable!: string; + + @ApiProperty({ type: 'integer', format: 'int64' }) + diskSizeRaw!: number; + + @ApiProperty({ type: 'integer', format: 'int64' }) + diskUseRaw!: number; + + @ApiProperty({ type: 'integer', format: 'int64' }) + diskAvailableRaw!: number; + + @ApiProperty({ type: 'number', format: 'float' }) + diskUsagePercentage!: number; +} + +export class ServerVersionResponseDto implements IServerVersion { + @ApiProperty({ type: 'integer' }) + major!: number; + @ApiProperty({ type: 'integer' }) + minor!: number; + @ApiProperty({ type: 'integer' }) + patch!: number; +} + +export class UsageByUserDto { + @ApiProperty({ type: 'string' }) + userId!: string; + @ApiProperty({ type: 'string' }) + userFirstName!: string; + @ApiProperty({ type: 'string' }) + userLastName!: string; + @ApiProperty({ type: 'integer' }) + photos!: number; + @ApiProperty({ type: 'integer' }) + videos!: number; + @ApiProperty({ type: 'integer', format: 'int64' }) + usage!: number; +} + +export class ServerStatsResponseDto { + @ApiProperty({ type: 'integer' }) + photos = 0; + + @ApiProperty({ type: 'integer' }) + videos = 0; + + @ApiProperty({ type: 'integer', format: 'int64' }) + usage = 0; + + @ApiProperty({ + isArray: true, + type: UsageByUserDto, + title: 'Array of usage for each user', + example: [ + { + photos: 1, + videos: 1, + diskUsageRaw: 1, + }, + ], + }) + usageByUser: UsageByUserDto[] = []; +} + +export class ServerMediaTypesResponseDto { + video!: string[]; + image!: string[]; + sidecar!: string[]; +} + +export class ServerFeaturesDto { + machineLearning!: boolean; + search!: boolean; + + oauth!: boolean; + oauthAutoLaunch!: boolean; + passwordLogin!: boolean; +} diff --git a/server/src/domain/server-info/server-info.service.spec.ts b/server/src/domain/server-info/server-info.service.spec.ts index ebb0d800fb..764e1c8891 100644 --- a/server/src/domain/server-info/server-info.service.spec.ts +++ b/server/src/domain/server-info/server-info.service.spec.ts @@ -1,19 +1,22 @@ -import { newStorageRepositoryMock, newUserRepositoryMock } from '@test'; +import { newStorageRepositoryMock, newSystemConfigRepositoryMock, newUserRepositoryMock } from '@test'; import { serverVersion } from '../domain.constant'; +import { ISystemConfigRepository } from '../index'; import { IStorageRepository } from '../storage'; import { IUserRepository } from '../user'; import { ServerInfoService } from './server-info.service'; describe(ServerInfoService.name, () => { let sut: ServerInfoService; + let configMock: jest.Mocked; let storageMock: jest.Mocked; let userMock: jest.Mocked; beforeEach(() => { + configMock = newSystemConfigRepositoryMock(); storageMock = newStorageRepositoryMock(); userMock = newUserRepositoryMock(); - sut = new ServerInfoService(userMock, storageMock); + sut = new ServerInfoService(configMock, userMock, storageMock); }); it('should work', () => { @@ -140,6 +143,19 @@ 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({ + machineLearning: true, + oauth: false, + oauthAutoLaunch: false, + passwordLogin: true, + search: true, + }); + expect(configMock.load).toHaveBeenCalled(); + }); + }); }); describe('getStats', () => { diff --git a/server/src/domain/server-info/server-info.service.ts b/server/src/domain/server-info/server-info.service.ts index 4a7e7f22bb..e628d12bad 100644 --- a/server/src/domain/server-info/server-info.service.ts +++ b/server/src/domain/server-info/server-info.service.ts @@ -1,24 +1,31 @@ import { Inject, Injectable } from '@nestjs/common'; -import { mimeTypes, serverVersion } from '../domain.constant'; +import { MACHINE_LEARNING_ENABLED, mimeTypes, SEARCH_ENABLED, serverVersion } from '../domain.constant'; import { asHumanReadable } from '../domain.util'; import { IStorageRepository, StorageCore, StorageFolder } from '../storage'; +import { ISystemConfigRepository } from '../system-config'; +import { SystemConfigCore } from '../system-config/system-config.core'; import { IUserRepository, UserStatsQueryResponse } from '../user'; import { + ServerFeaturesDto, ServerInfoResponseDto, ServerMediaTypesResponseDto, ServerPingResponse, ServerStatsResponseDto, UsageByUserDto, -} from './response-dto'; +} from './server-info.dto'; @Injectable() export class ServerInfoService { private storageCore = new StorageCore(); + private configCore: SystemConfigCore; constructor( + @Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository, @Inject(IUserRepository) private userRepository: IUserRepository, @Inject(IStorageRepository) private storageRepository: IStorageRepository, - ) {} + ) { + this.configCore = new SystemConfigCore(configRepository); + } async getInfo(): Promise { const libraryBase = this.storageCore.getBaseFolder(StorageFolder.LIBRARY); @@ -38,13 +45,27 @@ export class ServerInfoService { } ping(): ServerPingResponse { - return new ServerPingResponse('pong'); + return { res: 'pong' }; } getVersion() { return serverVersion; } + async getFeatures(): Promise { + const config = await this.configCore.getConfig(); + + return { + machineLearning: MACHINE_LEARNING_ENABLED, + search: SEARCH_ENABLED, + + // TODO: use these instead of `POST oauth/config` + oauth: config.oauth.enabled, + oauthAutoLaunch: config.oauth.autoLaunch, + passwordLogin: config.passwordLogin.enabled, + }; + } + async getStats(): Promise { const userStats: UserStatsQueryResponse[] = await this.userRepository.getUserStats(); const serverStats = new ServerStatsResponseDto(); diff --git a/server/src/immich/controllers/server-info.controller.ts b/server/src/immich/controllers/server-info.controller.ts index 59b6351787..3b69c35321 100644 --- a/server/src/immich/controllers/server-info.controller.ts +++ b/server/src/immich/controllers/server-info.controller.ts @@ -1,10 +1,11 @@ import { + ServerFeaturesDto, ServerInfoResponseDto, ServerInfoService, ServerMediaTypesResponseDto, ServerPingResponse, ServerStatsResponseDto, - ServerVersionReponseDto, + ServerVersionResponseDto, } from '@app/domain'; import { Controller, Get } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; @@ -24,25 +25,31 @@ export class ServerInfoController { } @PublicRoute() - @Get('/ping') + @Get('ping') pingServer(): ServerPingResponse { return this.service.ping(); } @PublicRoute() - @Get('/version') - getServerVersion(): ServerVersionReponseDto { + @Get('version') + getServerVersion(): ServerVersionResponseDto { return this.service.getVersion(); } + @PublicRoute() + @Get('features') + getServerFeatures(): Promise { + return this.service.getFeatures(); + } + @AdminRoute() - @Get('/stats') + @Get('stats') getStats(): Promise { return this.service.getStats(); } @PublicRoute() - @Get('/media-types') + @Get('media-types') getSupportedMediaTypes(): ServerMediaTypesResponseDto { return this.service.getSupportedMediaTypes(); } diff --git a/web/src/api/open-api/api.ts b/web/src/api/open-api/api.ts index 03d260a6b1..850aeae7f3 100644 --- a/web/src/api/open-api/api.ts +++ b/web/src/api/open-api/api.ts @@ -2087,6 +2087,43 @@ export interface SearchResponseDto { */ 'assets': SearchAssetResponseDto; } +/** + * + * @export + * @interface ServerFeaturesDto + */ +export interface ServerFeaturesDto { + /** + * + * @type {boolean} + * @memberof ServerFeaturesDto + */ + 'machineLearning': boolean; + /** + * + * @type {boolean} + * @memberof ServerFeaturesDto + */ + 'oauth': boolean; + /** + * + * @type {boolean} + * @memberof ServerFeaturesDto + */ + 'oauthAutoLaunch': boolean; + /** + * + * @type {boolean} + * @memberof ServerFeaturesDto + */ + 'passwordLogin': boolean; + /** + * + * @type {boolean} + * @memberof ServerFeaturesDto + */ + 'search': boolean; +} /** * * @export @@ -2208,25 +2245,25 @@ export interface ServerStatsResponseDto { /** * * @export - * @interface ServerVersionReponseDto + * @interface ServerVersionResponseDto */ -export interface ServerVersionReponseDto { +export interface ServerVersionResponseDto { /** * * @type {number} - * @memberof ServerVersionReponseDto + * @memberof ServerVersionResponseDto */ 'major': number; /** * * @type {number} - * @memberof ServerVersionReponseDto + * @memberof ServerVersionResponseDto */ 'minor': number; /** * * @type {number} - * @memberof ServerVersionReponseDto + * @memberof ServerVersionResponseDto */ 'patch': number; } @@ -10156,6 +10193,35 @@ export class SearchApi extends BaseAPI { */ export const ServerInfoApiAxiosParamCreator = function (configuration?: Configuration) { return { + /** + * + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getServerFeatures: async (options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/server-info/features`; + // 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. @@ -10329,6 +10395,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 getServerFeatures(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.getServerFeatures(options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, /** * * @param {*} [options] Override http request option. @@ -10343,7 +10418,7 @@ export const ServerInfoApiFp = function(configuration?: Configuration) { * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async getServerVersion(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + async getServerVersion(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { const localVarAxiosArgs = await localVarAxiosParamCreator.getServerVersion(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, @@ -10384,6 +10459,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} + */ + getServerFeatures(options?: AxiosRequestConfig): AxiosPromise { + return localVarFp.getServerFeatures(options).then((request) => request(axios, basePath)); + }, /** * * @param {*} [options] Override http request option. @@ -10397,7 +10480,7 @@ export const ServerInfoApiFactory = function (configuration?: Configuration, bas * @param {*} [options] Override http request option. * @throws {RequiredError} */ - getServerVersion(options?: AxiosRequestConfig): AxiosPromise { + getServerVersion(options?: AxiosRequestConfig): AxiosPromise { return localVarFp.getServerVersion(options).then((request) => request(axios, basePath)); }, /** @@ -10434,6 +10517,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 getServerFeatures(options?: AxiosRequestConfig) { + return ServerInfoApiFp(this.configuration).getServerFeatures(options).then((request) => request(this.axios, this.basePath)); + } + /** * * @param {*} [options] Override http request option. diff --git a/web/src/lib/components/admin-page/jobs/job-tile-button.svelte b/web/src/lib/components/admin-page/jobs/job-tile-button.svelte index b794c387c8..709ed60924 100644 --- a/web/src/lib/components/admin-page/jobs/job-tile-button.svelte +++ b/web/src/lib/components/admin-page/jobs/job-tile-button.svelte @@ -4,17 +4,23 @@ - {#each jobDetailsArray as [jobName, { title, subtitle, allText, missingText, allowForceCommand, icon, component, handleCommand: handleCommandOverride }]} + {#each jobList as [jobName, { title, subtitle, disabled, allText, missingText, allowForceCommand, icon, component, handleCommand: handleCommandOverride }]} {@const { jobCounts, queueStatus } = jobs[jobName]}
- - -
- -
-
-
+ {#if $featureFlags.search} + + +
+ +
+
+
+ {/if} diff --git a/web/src/lib/components/shared-components/side-bar/side-bar.svelte b/web/src/lib/components/shared-components/side-bar/side-bar.svelte index 0abf174815..770bbfa238 100644 --- a/web/src/lib/components/shared-components/side-bar/side-bar.svelte +++ b/web/src/lib/components/shared-components/side-bar/side-bar.svelte @@ -17,6 +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'; const getStats = async (dto: AssetApiGetAssetStatsRequest) => { const { data: stats } = await api.assetApi.getAssetStats(dto); @@ -56,9 +57,11 @@ - - - + {#if $featureFlags.search} + + + + {/if} diff --git a/web/src/lib/components/shared-components/version-announcement-box.svelte b/web/src/lib/components/shared-components/version-announcement-box.svelte index a59ab0e40b..af98568283 100644 --- a/web/src/lib/components/shared-components/version-announcement-box.svelte +++ b/web/src/lib/components/shared-components/version-announcement-box.svelte @@ -2,16 +2,16 @@ import { getGithubVersion } from '$lib/utils/get-github-version'; import { onMount } from 'svelte'; import FullScreenModal from './full-screen-modal.svelte'; - import type { ServerVersionReponseDto } from '@api'; + import type { ServerVersionResponseDto } from '@api'; import Button from '../elements/buttons/button.svelte'; - export let serverVersion: ServerVersionReponseDto; + export let serverVersion: ServerVersionResponseDto; let showModal = false; let githubVersion: string; $: serverVersionName = semverToName(serverVersion); - function semverToName({ major, minor, patch }: ServerVersionReponseDto) { + function semverToName({ major, minor, patch }: ServerVersionResponseDto) { return `v${major}.${minor}.${patch}`; } diff --git a/web/src/lib/stores/feature-flags.store.ts b/web/src/lib/stores/feature-flags.store.ts new file mode 100644 index 0000000000..119ecd557e --- /dev/null +++ b/web/src/lib/stores/feature-flags.store.ts @@ -0,0 +1,17 @@ +import { api, ServerFeaturesDto } from '@api'; +import { writable } from 'svelte/store'; + +export type FeatureFlags = ServerFeaturesDto; + +export const featureFlags = writable({ + machineLearning: true, + search: true, + oauth: true, + oauthAutoLaunch: true, + passwordLogin: true, +}); + +export const loadFeatureFlags = async () => { + const { data } = await api.serverInfoApi.getServerFeatures(); + featureFlags.update(() => data); +}; diff --git a/web/src/routes/+layout.svelte b/web/src/routes/+layout.svelte index d224461bb2..c57c04f511 100644 --- a/web/src/routes/+layout.svelte +++ b/web/src/routes/+layout.svelte @@ -1,6 +1,5 @@