mirror of
https://github.com/immich-app/immich.git
synced 2025-12-06 04:41:40 -08:00
Compare commits
9 Commits
v1.125.1
...
dev/metric
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b422bd0f7 | ||
|
|
f33a662f48 | ||
|
|
0232655da2 | ||
|
|
ac4c57247e | ||
|
|
fb01bd956f | ||
|
|
902d4d0275 | ||
|
|
db997f9173 | ||
|
|
e9197cde67 | ||
|
|
874f707c92 |
128
cli/src/api/open-api/api.ts
generated
128
cli/src/api/open-api/api.ts
generated
@@ -3062,6 +3062,12 @@ export interface ServerFeaturesDto {
|
||||
* @memberof ServerFeaturesDto
|
||||
*/
|
||||
'map': boolean;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof ServerFeaturesDto
|
||||
*/
|
||||
'metrics': boolean;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
@@ -3566,6 +3572,12 @@ export interface SystemConfigDto {
|
||||
* @memberof SystemConfigDto
|
||||
*/
|
||||
'map': SystemConfigMapDto;
|
||||
/**
|
||||
*
|
||||
* @type {SystemConfigMetricsDto}
|
||||
* @memberof SystemConfigDto
|
||||
*/
|
||||
'metrics': SystemConfigMetricsDto;
|
||||
/**
|
||||
*
|
||||
* @type {SystemConfigNewVersionCheckDto}
|
||||
@@ -3908,6 +3920,19 @@ export interface SystemConfigMapDto {
|
||||
*/
|
||||
'lightStyle': string;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface SystemConfigMetricsDto
|
||||
*/
|
||||
export interface SystemConfigMetricsDto {
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof SystemConfigMetricsDto
|
||||
*/
|
||||
'enabled': boolean;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
@@ -12692,6 +12717,109 @@ export class LibraryApi extends BaseAPI {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* MetricsApi - axios parameter creator
|
||||
* @export
|
||||
*/
|
||||
export const MetricsApiAxiosParamCreator = function (configuration?: Configuration) {
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
getMetrics: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
const localVarPath = `/metrics`;
|
||||
// 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;
|
||||
|
||||
// authentication cookie required
|
||||
|
||||
// authentication api_key required
|
||||
await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration)
|
||||
|
||||
// authentication bearer required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* MetricsApi - functional programming interface
|
||||
* @export
|
||||
*/
|
||||
export const MetricsApiFp = function(configuration?: Configuration) {
|
||||
const localVarAxiosParamCreator = MetricsApiAxiosParamCreator(configuration)
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async getMetrics(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<object>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.getMetrics(options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* MetricsApi - factory interface
|
||||
* @export
|
||||
*/
|
||||
export const MetricsApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
|
||||
const localVarFp = MetricsApiFp(configuration)
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
getMetrics(options?: AxiosRequestConfig): AxiosPromise<object> {
|
||||
return localVarFp.getMetrics(options).then((request) => request(axios, basePath));
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* MetricsApi - object-oriented interface
|
||||
* @export
|
||||
* @class MetricsApi
|
||||
* @extends {BaseAPI}
|
||||
*/
|
||||
export class MetricsApi extends BaseAPI {
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof MetricsApi
|
||||
*/
|
||||
public getMetrics(options?: AxiosRequestConfig) {
|
||||
return MetricsApiFp(this.configuration).getMetrics(options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* OAuthApi - axios parameter creator
|
||||
* @export
|
||||
|
||||
6
mobile/openapi/.openapi-generator/FILES
generated
6
mobile/openapi/.openapi-generator/FILES
generated
@@ -89,6 +89,7 @@ doc/MapMarkerResponseDto.md
|
||||
doc/MapTheme.md
|
||||
doc/MemoryLaneResponseDto.md
|
||||
doc/MergePersonDto.md
|
||||
doc/MetricsApi.md
|
||||
doc/ModelType.md
|
||||
doc/OAuthApi.md
|
||||
doc/OAuthAuthorizeResponseDto.md
|
||||
@@ -145,6 +146,7 @@ doc/SystemConfigLibraryScanDto.md
|
||||
doc/SystemConfigLoggingDto.md
|
||||
doc/SystemConfigMachineLearningDto.md
|
||||
doc/SystemConfigMapDto.md
|
||||
doc/SystemConfigMetricsDto.md
|
||||
doc/SystemConfigNewVersionCheckDto.md
|
||||
doc/SystemConfigOAuthDto.md
|
||||
doc/SystemConfigPasswordLoginDto.md
|
||||
@@ -188,6 +190,7 @@ lib/api/authentication_api.dart
|
||||
lib/api/face_api.dart
|
||||
lib/api/job_api.dart
|
||||
lib/api/library_api.dart
|
||||
lib/api/metrics_api.dart
|
||||
lib/api/o_auth_api.dart
|
||||
lib/api/partner_api.dart
|
||||
lib/api/person_api.dart
|
||||
@@ -331,6 +334,7 @@ lib/model/system_config_library_scan_dto.dart
|
||||
lib/model/system_config_logging_dto.dart
|
||||
lib/model/system_config_machine_learning_dto.dart
|
||||
lib/model/system_config_map_dto.dart
|
||||
lib/model/system_config_metrics_dto.dart
|
||||
lib/model/system_config_new_version_check_dto.dart
|
||||
lib/model/system_config_o_auth_dto.dart
|
||||
lib/model/system_config_password_login_dto.dart
|
||||
@@ -448,6 +452,7 @@ test/map_marker_response_dto_test.dart
|
||||
test/map_theme_test.dart
|
||||
test/memory_lane_response_dto_test.dart
|
||||
test/merge_person_dto_test.dart
|
||||
test/metrics_api_test.dart
|
||||
test/model_type_test.dart
|
||||
test/o_auth_api_test.dart
|
||||
test/o_auth_authorize_response_dto_test.dart
|
||||
@@ -504,6 +509,7 @@ test/system_config_library_scan_dto_test.dart
|
||||
test/system_config_logging_dto_test.dart
|
||||
test/system_config_machine_learning_dto_test.dart
|
||||
test/system_config_map_dto_test.dart
|
||||
test/system_config_metrics_dto_test.dart
|
||||
test/system_config_new_version_check_dto_test.dart
|
||||
test/system_config_o_auth_dto_test.dart
|
||||
test/system_config_password_login_dto_test.dart
|
||||
|
||||
2
mobile/openapi/README.md
generated
2
mobile/openapi/README.md
generated
@@ -145,6 +145,7 @@ Class | Method | HTTP request | Description
|
||||
*LibraryApi* | [**removeOfflineFiles**](doc//LibraryApi.md#removeofflinefiles) | **POST** /library/{id}/removeOffline |
|
||||
*LibraryApi* | [**scanLibrary**](doc//LibraryApi.md#scanlibrary) | **POST** /library/{id}/scan |
|
||||
*LibraryApi* | [**updateLibrary**](doc//LibraryApi.md#updatelibrary) | **PUT** /library/{id} |
|
||||
*MetricsApi* | [**getMetrics**](doc//MetricsApi.md#getmetrics) | **GET** /metrics |
|
||||
*OAuthApi* | [**finishOAuth**](doc//OAuthApi.md#finishoauth) | **POST** /oauth/callback |
|
||||
*OAuthApi* | [**generateOAuthConfig**](doc//OAuthApi.md#generateoauthconfig) | **POST** /oauth/config |
|
||||
*OAuthApi* | [**linkOAuthAccount**](doc//OAuthApi.md#linkoauthaccount) | **POST** /oauth/link |
|
||||
@@ -337,6 +338,7 @@ Class | Method | HTTP request | Description
|
||||
- [SystemConfigLoggingDto](doc//SystemConfigLoggingDto.md)
|
||||
- [SystemConfigMachineLearningDto](doc//SystemConfigMachineLearningDto.md)
|
||||
- [SystemConfigMapDto](doc//SystemConfigMapDto.md)
|
||||
- [SystemConfigMetricsDto](doc//SystemConfigMetricsDto.md)
|
||||
- [SystemConfigNewVersionCheckDto](doc//SystemConfigNewVersionCheckDto.md)
|
||||
- [SystemConfigOAuthDto](doc//SystemConfigOAuthDto.md)
|
||||
- [SystemConfigPasswordLoginDto](doc//SystemConfigPasswordLoginDto.md)
|
||||
|
||||
65
mobile/openapi/doc/MetricsApi.md
generated
Normal file
65
mobile/openapi/doc/MetricsApi.md
generated
Normal file
@@ -0,0 +1,65 @@
|
||||
# openapi.api.MetricsApi
|
||||
|
||||
## Load the API package
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
```
|
||||
|
||||
All URIs are relative to */api*
|
||||
|
||||
Method | HTTP request | Description
|
||||
------------- | ------------- | -------------
|
||||
[**getMetrics**](MetricsApi.md#getmetrics) | **GET** /metrics |
|
||||
|
||||
|
||||
# **getMetrics**
|
||||
> Object getMetrics()
|
||||
|
||||
|
||||
|
||||
### Example
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
// TODO Configure API key authorization: cookie
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure API key authorization: api_key
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure HTTP Bearer authorization: bearer
|
||||
// Case 1. Use String Token
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
|
||||
// Case 2. Use Function which generate token.
|
||||
// String yourTokenGeneratorFunction() { ... }
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = MetricsApi();
|
||||
|
||||
try {
|
||||
final result = api_instance.getMetrics();
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling MetricsApi->getMetrics: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
This endpoint does not need any parameter.
|
||||
|
||||
### Return type
|
||||
|
||||
[**Object**](Object.md)
|
||||
|
||||
### Authorization
|
||||
|
||||
[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer)
|
||||
|
||||
### 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)
|
||||
|
||||
1
mobile/openapi/doc/ServerFeaturesDto.md
generated
1
mobile/openapi/doc/ServerFeaturesDto.md
generated
@@ -12,6 +12,7 @@ Name | Type | Description | Notes
|
||||
**configFile** | **bool** | |
|
||||
**facialRecognition** | **bool** | |
|
||||
**map** | **bool** | |
|
||||
**metrics** | **bool** | |
|
||||
**oauth** | **bool** | |
|
||||
**oauthAutoLaunch** | **bool** | |
|
||||
**passwordLogin** | **bool** | |
|
||||
|
||||
1
mobile/openapi/doc/SystemConfigDto.md
generated
1
mobile/openapi/doc/SystemConfigDto.md
generated
@@ -14,6 +14,7 @@ Name | Type | Description | Notes
|
||||
**logging** | [**SystemConfigLoggingDto**](SystemConfigLoggingDto.md) | |
|
||||
**machineLearning** | [**SystemConfigMachineLearningDto**](SystemConfigMachineLearningDto.md) | |
|
||||
**map** | [**SystemConfigMapDto**](SystemConfigMapDto.md) | |
|
||||
**metrics** | [**SystemConfigMetricsDto**](SystemConfigMetricsDto.md) | |
|
||||
**newVersionCheck** | [**SystemConfigNewVersionCheckDto**](SystemConfigNewVersionCheckDto.md) | |
|
||||
**oauth** | [**SystemConfigOAuthDto**](SystemConfigOAuthDto.md) | |
|
||||
**passwordLogin** | [**SystemConfigPasswordLoginDto**](SystemConfigPasswordLoginDto.md) | |
|
||||
|
||||
15
mobile/openapi/doc/SystemConfigMetricsDto.md
generated
Normal file
15
mobile/openapi/doc/SystemConfigMetricsDto.md
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
# openapi.model.SystemConfigMetricsDto
|
||||
|
||||
## Load the model package
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
```
|
||||
|
||||
## Properties
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**enabled** | **bool** | |
|
||||
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
2
mobile/openapi/lib/api.dart
generated
2
mobile/openapi/lib/api.dart
generated
@@ -37,6 +37,7 @@ part 'api/authentication_api.dart';
|
||||
part 'api/face_api.dart';
|
||||
part 'api/job_api.dart';
|
||||
part 'api/library_api.dart';
|
||||
part 'api/metrics_api.dart';
|
||||
part 'api/o_auth_api.dart';
|
||||
part 'api/partner_api.dart';
|
||||
part 'api/person_api.dart';
|
||||
@@ -173,6 +174,7 @@ part 'model/system_config_library_scan_dto.dart';
|
||||
part 'model/system_config_logging_dto.dart';
|
||||
part 'model/system_config_machine_learning_dto.dart';
|
||||
part 'model/system_config_map_dto.dart';
|
||||
part 'model/system_config_metrics_dto.dart';
|
||||
part 'model/system_config_new_version_check_dto.dart';
|
||||
part 'model/system_config_o_auth_dto.dart';
|
||||
part 'model/system_config_password_login_dto.dart';
|
||||
|
||||
59
mobile/openapi/lib/api/metrics_api.dart
generated
Normal file
59
mobile/openapi/lib/api/metrics_api.dart
generated
Normal file
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// 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 MetricsApi {
|
||||
MetricsApi([ApiClient? apiClient]) : apiClient = apiClient ?? defaultApiClient;
|
||||
|
||||
final ApiClient apiClient;
|
||||
|
||||
/// Performs an HTTP 'GET /metrics' operation and returns the [Response].
|
||||
Future<Response> getMetricsWithHttpInfo() async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/metrics';
|
||||
|
||||
// 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<Object?> getMetrics() async {
|
||||
final response = await getMetricsWithHttpInfo();
|
||||
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), 'Object',) as Object;
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
2
mobile/openapi/lib/api_client.dart
generated
2
mobile/openapi/lib/api_client.dart
generated
@@ -433,6 +433,8 @@ class ApiClient {
|
||||
return SystemConfigMachineLearningDto.fromJson(value);
|
||||
case 'SystemConfigMapDto':
|
||||
return SystemConfigMapDto.fromJson(value);
|
||||
case 'SystemConfigMetricsDto':
|
||||
return SystemConfigMetricsDto.fromJson(value);
|
||||
case 'SystemConfigNewVersionCheckDto':
|
||||
return SystemConfigNewVersionCheckDto.fromJson(value);
|
||||
case 'SystemConfigOAuthDto':
|
||||
|
||||
10
mobile/openapi/lib/model/server_features_dto.dart
generated
10
mobile/openapi/lib/model/server_features_dto.dart
generated
@@ -17,6 +17,7 @@ class ServerFeaturesDto {
|
||||
required this.configFile,
|
||||
required this.facialRecognition,
|
||||
required this.map,
|
||||
required this.metrics,
|
||||
required this.oauth,
|
||||
required this.oauthAutoLaunch,
|
||||
required this.passwordLogin,
|
||||
@@ -34,6 +35,8 @@ class ServerFeaturesDto {
|
||||
|
||||
bool map;
|
||||
|
||||
bool metrics;
|
||||
|
||||
bool oauth;
|
||||
|
||||
bool oauthAutoLaunch;
|
||||
@@ -54,6 +57,7 @@ class ServerFeaturesDto {
|
||||
other.configFile == configFile &&
|
||||
other.facialRecognition == facialRecognition &&
|
||||
other.map == map &&
|
||||
other.metrics == metrics &&
|
||||
other.oauth == oauth &&
|
||||
other.oauthAutoLaunch == oauthAutoLaunch &&
|
||||
other.passwordLogin == passwordLogin &&
|
||||
@@ -69,6 +73,7 @@ class ServerFeaturesDto {
|
||||
(configFile.hashCode) +
|
||||
(facialRecognition.hashCode) +
|
||||
(map.hashCode) +
|
||||
(metrics.hashCode) +
|
||||
(oauth.hashCode) +
|
||||
(oauthAutoLaunch.hashCode) +
|
||||
(passwordLogin.hashCode) +
|
||||
@@ -78,7 +83,7 @@ class ServerFeaturesDto {
|
||||
(trash.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'ServerFeaturesDto[clipEncode=$clipEncode, configFile=$configFile, facialRecognition=$facialRecognition, map=$map, oauth=$oauth, oauthAutoLaunch=$oauthAutoLaunch, passwordLogin=$passwordLogin, reverseGeocoding=$reverseGeocoding, search=$search, sidecar=$sidecar, trash=$trash]';
|
||||
String toString() => 'ServerFeaturesDto[clipEncode=$clipEncode, configFile=$configFile, facialRecognition=$facialRecognition, map=$map, metrics=$metrics, oauth=$oauth, oauthAutoLaunch=$oauthAutoLaunch, passwordLogin=$passwordLogin, reverseGeocoding=$reverseGeocoding, search=$search, sidecar=$sidecar, trash=$trash]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
@@ -86,6 +91,7 @@ class ServerFeaturesDto {
|
||||
json[r'configFile'] = this.configFile;
|
||||
json[r'facialRecognition'] = this.facialRecognition;
|
||||
json[r'map'] = this.map;
|
||||
json[r'metrics'] = this.metrics;
|
||||
json[r'oauth'] = this.oauth;
|
||||
json[r'oauthAutoLaunch'] = this.oauthAutoLaunch;
|
||||
json[r'passwordLogin'] = this.passwordLogin;
|
||||
@@ -108,6 +114,7 @@ class ServerFeaturesDto {
|
||||
configFile: mapValueOfType<bool>(json, r'configFile')!,
|
||||
facialRecognition: mapValueOfType<bool>(json, r'facialRecognition')!,
|
||||
map: mapValueOfType<bool>(json, r'map')!,
|
||||
metrics: mapValueOfType<bool>(json, r'metrics')!,
|
||||
oauth: mapValueOfType<bool>(json, r'oauth')!,
|
||||
oauthAutoLaunch: mapValueOfType<bool>(json, r'oauthAutoLaunch')!,
|
||||
passwordLogin: mapValueOfType<bool>(json, r'passwordLogin')!,
|
||||
@@ -166,6 +173,7 @@ class ServerFeaturesDto {
|
||||
'configFile',
|
||||
'facialRecognition',
|
||||
'map',
|
||||
'metrics',
|
||||
'oauth',
|
||||
'oauthAutoLaunch',
|
||||
'passwordLogin',
|
||||
|
||||
10
mobile/openapi/lib/model/system_config_dto.dart
generated
10
mobile/openapi/lib/model/system_config_dto.dart
generated
@@ -19,6 +19,7 @@ class SystemConfigDto {
|
||||
required this.logging,
|
||||
required this.machineLearning,
|
||||
required this.map,
|
||||
required this.metrics,
|
||||
required this.newVersionCheck,
|
||||
required this.oauth,
|
||||
required this.passwordLogin,
|
||||
@@ -41,6 +42,8 @@ class SystemConfigDto {
|
||||
|
||||
SystemConfigMapDto map;
|
||||
|
||||
SystemConfigMetricsDto metrics;
|
||||
|
||||
SystemConfigNewVersionCheckDto newVersionCheck;
|
||||
|
||||
SystemConfigOAuthDto oauth;
|
||||
@@ -65,6 +68,7 @@ class SystemConfigDto {
|
||||
other.logging == logging &&
|
||||
other.machineLearning == machineLearning &&
|
||||
other.map == map &&
|
||||
other.metrics == metrics &&
|
||||
other.newVersionCheck == newVersionCheck &&
|
||||
other.oauth == oauth &&
|
||||
other.passwordLogin == passwordLogin &&
|
||||
@@ -83,6 +87,7 @@ class SystemConfigDto {
|
||||
(logging.hashCode) +
|
||||
(machineLearning.hashCode) +
|
||||
(map.hashCode) +
|
||||
(metrics.hashCode) +
|
||||
(newVersionCheck.hashCode) +
|
||||
(oauth.hashCode) +
|
||||
(passwordLogin.hashCode) +
|
||||
@@ -93,7 +98,7 @@ class SystemConfigDto {
|
||||
(trash.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'SystemConfigDto[ffmpeg=$ffmpeg, job=$job, library_=$library_, logging=$logging, machineLearning=$machineLearning, map=$map, newVersionCheck=$newVersionCheck, oauth=$oauth, passwordLogin=$passwordLogin, reverseGeocoding=$reverseGeocoding, storageTemplate=$storageTemplate, theme=$theme, thumbnail=$thumbnail, trash=$trash]';
|
||||
String toString() => 'SystemConfigDto[ffmpeg=$ffmpeg, job=$job, library_=$library_, logging=$logging, machineLearning=$machineLearning, map=$map, metrics=$metrics, newVersionCheck=$newVersionCheck, oauth=$oauth, passwordLogin=$passwordLogin, reverseGeocoding=$reverseGeocoding, storageTemplate=$storageTemplate, theme=$theme, thumbnail=$thumbnail, trash=$trash]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
@@ -103,6 +108,7 @@ class SystemConfigDto {
|
||||
json[r'logging'] = this.logging;
|
||||
json[r'machineLearning'] = this.machineLearning;
|
||||
json[r'map'] = this.map;
|
||||
json[r'metrics'] = this.metrics;
|
||||
json[r'newVersionCheck'] = this.newVersionCheck;
|
||||
json[r'oauth'] = this.oauth;
|
||||
json[r'passwordLogin'] = this.passwordLogin;
|
||||
@@ -128,6 +134,7 @@ class SystemConfigDto {
|
||||
logging: SystemConfigLoggingDto.fromJson(json[r'logging'])!,
|
||||
machineLearning: SystemConfigMachineLearningDto.fromJson(json[r'machineLearning'])!,
|
||||
map: SystemConfigMapDto.fromJson(json[r'map'])!,
|
||||
metrics: SystemConfigMetricsDto.fromJson(json[r'metrics'])!,
|
||||
newVersionCheck: SystemConfigNewVersionCheckDto.fromJson(json[r'newVersionCheck'])!,
|
||||
oauth: SystemConfigOAuthDto.fromJson(json[r'oauth'])!,
|
||||
passwordLogin: SystemConfigPasswordLoginDto.fromJson(json[r'passwordLogin'])!,
|
||||
@@ -189,6 +196,7 @@ class SystemConfigDto {
|
||||
'logging',
|
||||
'machineLearning',
|
||||
'map',
|
||||
'metrics',
|
||||
'newVersionCheck',
|
||||
'oauth',
|
||||
'passwordLogin',
|
||||
|
||||
98
mobile/openapi/lib/model/system_config_metrics_dto.dart
generated
Normal file
98
mobile/openapi/lib/model/system_config_metrics_dto.dart
generated
Normal file
@@ -0,0 +1,98 @@
|
||||
//
|
||||
// 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 SystemConfigMetricsDto {
|
||||
/// Returns a new [SystemConfigMetricsDto] instance.
|
||||
SystemConfigMetricsDto({
|
||||
required this.enabled,
|
||||
});
|
||||
|
||||
bool enabled;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is SystemConfigMetricsDto &&
|
||||
other.enabled == enabled;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(enabled.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'SystemConfigMetricsDto[enabled=$enabled]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'enabled'] = this.enabled;
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [SystemConfigMetricsDto] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static SystemConfigMetricsDto? fromJson(dynamic value) {
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return SystemConfigMetricsDto(
|
||||
enabled: mapValueOfType<bool>(json, r'enabled')!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<SystemConfigMetricsDto> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <SystemConfigMetricsDto>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = SystemConfigMetricsDto.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, SystemConfigMetricsDto> mapFromJson(dynamic json) {
|
||||
final map = <String, SystemConfigMetricsDto>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = SystemConfigMetricsDto.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of SystemConfigMetricsDto-objects as value to a dart map
|
||||
static Map<String, List<SystemConfigMetricsDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<SystemConfigMetricsDto>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = SystemConfigMetricsDto.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'enabled',
|
||||
};
|
||||
}
|
||||
|
||||
26
mobile/openapi/test/metrics_api_test.dart
generated
Normal file
26
mobile/openapi/test/metrics_api_test.dart
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// 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 MetricsApi
|
||||
void main() {
|
||||
// final instance = MetricsApi();
|
||||
|
||||
group('tests for MetricsApi', () {
|
||||
//Future<Object> getMetrics() async
|
||||
test('test getMetrics', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
@@ -36,6 +36,11 @@ void main() {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// bool metrics
|
||||
test('to test the property `metrics`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// bool oauth
|
||||
test('to test the property `oauth`', () async {
|
||||
// TODO
|
||||
|
||||
5
mobile/openapi/test/system_config_dto_test.dart
generated
5
mobile/openapi/test/system_config_dto_test.dart
generated
@@ -46,6 +46,11 @@ void main() {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// SystemConfigMetricsDto metrics
|
||||
test('to test the property `metrics`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// SystemConfigNewVersionCheckDto newVersionCheck
|
||||
test('to test the property `newVersionCheck`', () async {
|
||||
// TODO
|
||||
|
||||
27
mobile/openapi/test/system_config_metrics_dto_test.dart
generated
Normal file
27
mobile/openapi/test/system_config_metrics_dto_test.dart
generated
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// 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 SystemConfigMetricsDto
|
||||
void main() {
|
||||
// final instance = SystemConfigMetricsDto();
|
||||
|
||||
group('test SystemConfigMetricsDto', () {
|
||||
// bool enabled
|
||||
test('to test the property `enabled`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
@@ -3716,6 +3716,38 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/metrics": {
|
||||
"get": {
|
||||
"operationId": "getMetrics",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearer": []
|
||||
},
|
||||
{
|
||||
"cookie": []
|
||||
},
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Metrics"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/oauth/authorize": {
|
||||
"post": {
|
||||
"operationId": "startOAuth",
|
||||
@@ -8628,6 +8660,9 @@
|
||||
"map": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"metrics": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"oauth": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -8655,6 +8690,7 @@
|
||||
"configFile",
|
||||
"facialRecognition",
|
||||
"map",
|
||||
"metrics",
|
||||
"trash",
|
||||
"reverseGeocoding",
|
||||
"oauth",
|
||||
@@ -9027,6 +9063,9 @@
|
||||
"map": {
|
||||
"$ref": "#/components/schemas/SystemConfigMapDto"
|
||||
},
|
||||
"metrics": {
|
||||
"$ref": "#/components/schemas/SystemConfigMetricsDto"
|
||||
},
|
||||
"newVersionCheck": {
|
||||
"$ref": "#/components/schemas/SystemConfigNewVersionCheckDto"
|
||||
},
|
||||
@@ -9057,6 +9096,7 @@
|
||||
"logging",
|
||||
"machineLearning",
|
||||
"map",
|
||||
"metrics",
|
||||
"newVersionCheck",
|
||||
"oauth",
|
||||
"passwordLogin",
|
||||
@@ -9279,6 +9319,17 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"SystemConfigMetricsDto": {
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"enabled"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"SystemConfigNewVersionCheckDto": {
|
||||
"properties": {
|
||||
"enabled": {
|
||||
|
||||
@@ -5,6 +5,7 @@ import pkg from 'src/../../package.json';
|
||||
|
||||
export const AUDIT_LOG_MAX_DURATION = Duration.fromObject({ days: 100 });
|
||||
export const ONE_HOUR = Duration.fromObject({ hours: 1 });
|
||||
export const TWENTY_FOUR_HOURS = Duration.fromObject({ hours: 24 });
|
||||
|
||||
export interface IVersion {
|
||||
major: number;
|
||||
|
||||
@@ -11,6 +11,7 @@ import { JobService } from './job';
|
||||
import { LibraryService } from './library';
|
||||
import { MediaService } from './media';
|
||||
import { MetadataService } from './metadata';
|
||||
import { MetricsService } from './metrics';
|
||||
import { PartnerService } from './partner';
|
||||
import { PersonService } from './person';
|
||||
import { SearchService } from './search';
|
||||
@@ -34,6 +35,7 @@ const providers: Provider[] = [
|
||||
JobService,
|
||||
MediaService,
|
||||
MetadataService,
|
||||
MetricsService,
|
||||
LibraryService,
|
||||
PersonService,
|
||||
PartnerService,
|
||||
|
||||
@@ -14,6 +14,7 @@ export * from './job';
|
||||
export * from './library';
|
||||
export * from './media';
|
||||
export * from './metadata';
|
||||
export * from './metrics';
|
||||
export * from './partner';
|
||||
export * from './person';
|
||||
export * from './repositories';
|
||||
|
||||
@@ -81,6 +81,9 @@ export enum JobName {
|
||||
SIDECAR_DISCOVERY = 'sidecar-discovery',
|
||||
SIDECAR_SYNC = 'sidecar-sync',
|
||||
SIDECAR_WRITE = 'sidecar-write',
|
||||
|
||||
// metrics
|
||||
METRICS = 'metrics',
|
||||
}
|
||||
|
||||
export const JOBS_ASSET_PAGINATION_SIZE = 1000;
|
||||
@@ -95,6 +98,7 @@ export const JOBS_TO_QUEUE: Record<JobName, QueueName> = {
|
||||
[JobName.CLEAN_OLD_AUDIT_LOGS]: QueueName.BACKGROUND_TASK,
|
||||
[JobName.PERSON_CLEANUP]: QueueName.BACKGROUND_TASK,
|
||||
[JobName.PERSON_DELETE]: QueueName.BACKGROUND_TASK,
|
||||
[JobName.METRICS]: QueueName.BACKGROUND_TASK,
|
||||
|
||||
// conversion
|
||||
[JobName.QUEUE_VIDEO_CONVERSION]: QueueName.VIDEO_CONVERSION,
|
||||
|
||||
2
server/src/domain/metrics/index.ts
Normal file
2
server/src/domain/metrics/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './metrics.dto';
|
||||
export * from './metrics.service';
|
||||
31
server/src/domain/metrics/metrics.dto.ts
Normal file
31
server/src/domain/metrics/metrics.dto.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
class MetricsServerInfo {
|
||||
cpuCount!: number;
|
||||
cpuModel!: string;
|
||||
memory!: number;
|
||||
version!: string;
|
||||
}
|
||||
|
||||
class MetricsAssetCount {
|
||||
image!: number;
|
||||
video!: number;
|
||||
total!: number;
|
||||
}
|
||||
|
||||
export interface Metrics {
|
||||
serverInfo: {
|
||||
cpuCount: number;
|
||||
cpuModel: string;
|
||||
memory: number;
|
||||
version: string;
|
||||
};
|
||||
assetCount: {
|
||||
image: number;
|
||||
video: number;
|
||||
total: number;
|
||||
};
|
||||
}
|
||||
|
||||
export class MetricsDto implements Metrics {
|
||||
serverInfo!: MetricsServerInfo;
|
||||
assetCount!: MetricsAssetCount;
|
||||
}
|
||||
60
server/src/domain/metrics/metrics.service.ts
Normal file
60
server/src/domain/metrics/metrics.service.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { isDev, serverVersion } from '../domain.constant';
|
||||
import { JobName } from '../job';
|
||||
import { ISystemConfigRepository } from '../repositories';
|
||||
import { IJobRepository } from '../repositories/job.repository';
|
||||
import { IMetricsRepository } from '../repositories/metrics.repository';
|
||||
import { FeatureFlag, SystemConfigCore } from '../system-config';
|
||||
import { MetricsDto } from './metrics.dto';
|
||||
|
||||
@Injectable()
|
||||
export class MetricsService {
|
||||
private configCore: SystemConfigCore;
|
||||
|
||||
constructor(
|
||||
@Inject(IJobRepository) private jobRepository: IJobRepository,
|
||||
@Inject(IMetricsRepository) private repository: IMetricsRepository,
|
||||
@Inject(ISystemConfigRepository) systemConfigRepository: ISystemConfigRepository,
|
||||
) {
|
||||
this.configCore = SystemConfigCore.create(systemConfigRepository);
|
||||
}
|
||||
|
||||
async handleQueueMetrics() {
|
||||
if (!(await this.configCore.hasFeature(FeatureFlag.METRICS))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO
|
||||
// if (isDev) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
await this.jobRepository.queue({ name: JobName.METRICS });
|
||||
}
|
||||
|
||||
async handleSendMetrics() {
|
||||
const metrics = await this.getMetrics();
|
||||
|
||||
await this.repository.sendMetrics(metrics);
|
||||
return true;
|
||||
}
|
||||
|
||||
async getMetrics() {
|
||||
const metrics = new MetricsDto();
|
||||
|
||||
metrics.serverInfo = {
|
||||
cpuCount: this.repository.getCpuCount(),
|
||||
cpuModel: this.repository.getCpuModel(),
|
||||
memory: this.repository.getMemory(),
|
||||
version: serverVersion.toString(),
|
||||
};
|
||||
|
||||
metrics.assetCount = {
|
||||
image: await this.repository.getImageCount(),
|
||||
video: await this.repository.getVideoCount(),
|
||||
total: await this.repository.getAssetCount(),
|
||||
};
|
||||
|
||||
return metrics;
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ export * from './library.repository';
|
||||
export * from './machine-learning.repository';
|
||||
export * from './media.repository';
|
||||
export * from './metadata.repository';
|
||||
export * from './metrics.repository';
|
||||
export * from './move.repository';
|
||||
export * from './partner.repository';
|
||||
export * from './person.repository';
|
||||
|
||||
@@ -89,7 +89,10 @@ export type JobItem =
|
||||
| { name: JobName.LIBRARY_REMOVE_OFFLINE; data: IEntityJob }
|
||||
| { name: JobName.LIBRARY_DELETE; data: IEntityJob }
|
||||
| { name: JobName.LIBRARY_QUEUE_SCAN_ALL; data: IBaseJob }
|
||||
| { name: JobName.LIBRARY_QUEUE_CLEANUP; data: IBaseJob };
|
||||
| { name: JobName.LIBRARY_QUEUE_CLEANUP; data: IBaseJob }
|
||||
|
||||
// Metrics
|
||||
| { name: JobName.METRICS; data?: IBaseJob };
|
||||
|
||||
export type JobHandler<T = any> = (data: T) => boolean | Promise<boolean>;
|
||||
export type JobItemHandler = (item: JobItem) => Promise<void>;
|
||||
|
||||
13
server/src/domain/repositories/metrics.repository.ts
Normal file
13
server/src/domain/repositories/metrics.repository.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { MetricsDto } from '../metrics';
|
||||
|
||||
export const IMetricsRepository = 'IMetricsRepository';
|
||||
|
||||
export interface IMetricsRepository {
|
||||
getAssetCount(): Promise<number>;
|
||||
getCpuCount(): number;
|
||||
getCpuModel(): string;
|
||||
getMemory(): number;
|
||||
getImageCount(): Promise<number>;
|
||||
getVideoCount(): Promise<number>;
|
||||
sendMetrics(payload: MetricsDto): Promise<void>;
|
||||
}
|
||||
@@ -93,6 +93,7 @@ export class ServerFeaturesDto implements FeatureFlags {
|
||||
configFile!: boolean;
|
||||
facialRecognition!: boolean;
|
||||
map!: boolean;
|
||||
metrics!: boolean;
|
||||
trash!: boolean;
|
||||
reverseGeocoding!: boolean;
|
||||
oauth!: boolean;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export * from './system-config-ffmpeg.dto';
|
||||
export * from './system-config-library.dto';
|
||||
export * from './system-config-metrics.dto';
|
||||
export * from './system-config-oauth.dto';
|
||||
export * from './system-config-password-login.dto';
|
||||
export * from './system-config-storage-template.dto';
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import { IsBoolean } from 'class-validator';
|
||||
|
||||
export class SystemConfigMetricsDto {
|
||||
@IsBoolean()
|
||||
enabled!: boolean;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { SystemConfig } from '@app/infra/entities';
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsObject, ValidateNested } from 'class-validator';
|
||||
import { SystemConfigMetricsDto } from '.';
|
||||
import { SystemConfigFFmpegDto } from './system-config-ffmpeg.dto';
|
||||
import { SystemConfigJobDto } from './system-config-job.dto';
|
||||
import { SystemConfigLibraryDto } from './system-config-library.dto';
|
||||
@@ -37,6 +38,11 @@ export class SystemConfigDto implements SystemConfig {
|
||||
@IsObject()
|
||||
map!: SystemConfigMapDto;
|
||||
|
||||
@Type(() => SystemConfigMetricsDto)
|
||||
@ValidateNested()
|
||||
@IsObject()
|
||||
metrics!: SystemConfigMetricsDto;
|
||||
|
||||
@Type(() => SystemConfigNewVersionCheckDto)
|
||||
@ValidateNested()
|
||||
@IsObject()
|
||||
|
||||
@@ -82,6 +82,9 @@ export const defaults = Object.freeze<SystemConfig>({
|
||||
lightStyle: '',
|
||||
darkStyle: '',
|
||||
},
|
||||
metrics: {
|
||||
enabled: false,
|
||||
},
|
||||
reverseGeocoding: {
|
||||
enabled: true,
|
||||
},
|
||||
@@ -132,6 +135,7 @@ export enum FeatureFlag {
|
||||
CLIP_ENCODE = 'clipEncode',
|
||||
FACIAL_RECOGNITION = 'facialRecognition',
|
||||
MAP = 'map',
|
||||
METRICS = 'metrics',
|
||||
REVERSE_GEOCODING = 'reverseGeocoding',
|
||||
SIDECAR = 'sidecar',
|
||||
SEARCH = 'search',
|
||||
@@ -204,6 +208,7 @@ export class SystemConfigCore {
|
||||
[FeatureFlag.CLIP_ENCODE]: mlEnabled && config.machineLearning.clip.enabled,
|
||||
[FeatureFlag.FACIAL_RECOGNITION]: mlEnabled && config.machineLearning.facialRecognition.enabled,
|
||||
[FeatureFlag.MAP]: config.map.enabled,
|
||||
[FeatureFlag.METRICS]: config.metrics.enabled,
|
||||
[FeatureFlag.REVERSE_GEOCODING]: config.reverseGeocoding.enabled,
|
||||
[FeatureFlag.SIDECAR]: true,
|
||||
[FeatureFlag.SEARCH]: true,
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
FaceController,
|
||||
JobController,
|
||||
LibraryController,
|
||||
MetricsController,
|
||||
OAuthController,
|
||||
PartnerController,
|
||||
PersonController,
|
||||
@@ -54,6 +55,7 @@ import { ErrorInterceptor, FileUploadInterceptor } from './interceptors';
|
||||
FaceController,
|
||||
JobController,
|
||||
LibraryController,
|
||||
MetricsController,
|
||||
OAuthController,
|
||||
PartnerController,
|
||||
SearchController,
|
||||
|
||||
@@ -3,12 +3,14 @@ import {
|
||||
DatabaseService,
|
||||
JobService,
|
||||
LibraryService,
|
||||
MetricsService,
|
||||
ONE_HOUR,
|
||||
OpenGraphTags,
|
||||
ServerInfoService,
|
||||
SharedLinkService,
|
||||
StorageService,
|
||||
SystemConfigService,
|
||||
TWENTY_FOUR_HOURS,
|
||||
WEB_ROOT_PATH,
|
||||
} from '@app/domain';
|
||||
import { ImmichLogger } from '@app/infra/logger';
|
||||
@@ -46,6 +48,7 @@ export class AppService {
|
||||
private configService: SystemConfigService,
|
||||
private jobService: JobService,
|
||||
private libraryService: LibraryService,
|
||||
private metricsService: MetricsService,
|
||||
private serverService: ServerInfoService,
|
||||
private sharedLinkService: SharedLinkService,
|
||||
private storageService: StorageService,
|
||||
@@ -57,6 +60,11 @@ export class AppService {
|
||||
await this.serverService.handleVersionCheck();
|
||||
}
|
||||
|
||||
@Interval(TWENTY_FOUR_HOURS.as('milliseconds'))
|
||||
async onMetricsSend() {
|
||||
await this.metricsService.handleQueueMetrics();
|
||||
}
|
||||
|
||||
@Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT)
|
||||
async onNightlyJob() {
|
||||
await this.jobService.handleNightlyJobs();
|
||||
|
||||
@@ -8,6 +8,7 @@ export * from './auth.controller';
|
||||
export * from './face.controller';
|
||||
export * from './job.controller';
|
||||
export * from './library.controller';
|
||||
export * from './metrics.controller';
|
||||
export * from './oauth.controller';
|
||||
export * from './partner.controller';
|
||||
export * from './person.controller';
|
||||
|
||||
18
server/src/immich/controllers/metrics.controller.ts
Normal file
18
server/src/immich/controllers/metrics.controller.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Metrics, MetricsService } from '@app/domain';
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { Authenticated } from '../app.guard';
|
||||
import { UseValidation } from '../app.utils';
|
||||
|
||||
@ApiTags('Metrics')
|
||||
@Controller('metrics')
|
||||
@Authenticated()
|
||||
@UseValidation()
|
||||
export class MetricsController {
|
||||
constructor(private service: MetricsService) {}
|
||||
|
||||
@Get()
|
||||
getMetrics(): Promise<Partial<Metrics>> {
|
||||
return this.service.getMetrics();
|
||||
}
|
||||
}
|
||||
@@ -66,6 +66,8 @@ export enum SystemConfigKey {
|
||||
MAP_LIGHT_STYLE = 'map.lightStyle',
|
||||
MAP_DARK_STYLE = 'map.darkStyle',
|
||||
|
||||
METRICS_ENABLED = 'metrics.enabled',
|
||||
|
||||
REVERSE_GEOCODING_ENABLED = 'reverseGeocoding.enabled',
|
||||
|
||||
NEW_VERSION_CHECK_ENABLED = 'newVersionCheck.enabled',
|
||||
@@ -196,6 +198,9 @@ export interface SystemConfig {
|
||||
lightStyle: string;
|
||||
darkStyle: string;
|
||||
};
|
||||
metrics: {
|
||||
enabled: boolean;
|
||||
};
|
||||
reverseGeocoding: {
|
||||
enabled: boolean;
|
||||
};
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
IMachineLearningRepository,
|
||||
IMediaRepository,
|
||||
IMetadataRepository,
|
||||
IMetricsRepository,
|
||||
IMoveRepository,
|
||||
IPartnerRepository,
|
||||
IPersonRepository,
|
||||
@@ -51,6 +52,7 @@ import {
|
||||
MachineLearningRepository,
|
||||
MediaRepository,
|
||||
MetadataRepository,
|
||||
MetricsRepository,
|
||||
MoveRepository,
|
||||
PartnerRepository,
|
||||
PersonRepository,
|
||||
@@ -78,6 +80,7 @@ const providers: Provider[] = [
|
||||
{ provide: IKeyRepository, useClass: ApiKeyRepository },
|
||||
{ provide: IMachineLearningRepository, useClass: MachineLearningRepository },
|
||||
{ provide: IMetadataRepository, useClass: MetadataRepository },
|
||||
{ provide: IMetricsRepository, useClass: MetricsRepository },
|
||||
{ provide: IMoveRepository, useClass: MoveRepository },
|
||||
{ provide: IPartnerRepository, useClass: PartnerRepository },
|
||||
{ provide: IPersonRepository, useClass: PersonRepository },
|
||||
|
||||
@@ -13,6 +13,7 @@ export * from './library.repository';
|
||||
export * from './machine-learning.repository';
|
||||
export * from './media.repository';
|
||||
export * from './metadata.repository';
|
||||
export * from './metrics.repository';
|
||||
export * from './move.repository';
|
||||
export * from './partner.repository';
|
||||
export * from './person.repository';
|
||||
|
||||
40
server/src/infra/repositories/metrics.repository.ts
Normal file
40
server/src/infra/repositories/metrics.repository.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { MetricsDto } from '@app/domain/metrics';
|
||||
import { IMetricsRepository } from '@app/domain/repositories/metrics.repository';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import axios from 'axios';
|
||||
import os from 'os';
|
||||
import { Repository } from 'typeorm';
|
||||
import { AssetEntity, AssetType } from '../entities';
|
||||
|
||||
@Injectable()
|
||||
export class MetricsRepository implements IMetricsRepository {
|
||||
constructor(@InjectRepository(AssetEntity) private assetRepository: Repository<AssetEntity>) {}
|
||||
async sendMetrics(payload: MetricsDto): Promise<void> {
|
||||
await axios.post('IMMICH-DATA-DOMAIN', payload);
|
||||
}
|
||||
|
||||
getAssetCount() {
|
||||
return this.assetRepository.count();
|
||||
}
|
||||
|
||||
getCpuCount() {
|
||||
return os.cpus().length;
|
||||
}
|
||||
|
||||
getCpuModel() {
|
||||
return os.cpus()[0].model;
|
||||
}
|
||||
|
||||
getMemory() {
|
||||
return os.totalmem();
|
||||
}
|
||||
|
||||
getImageCount() {
|
||||
return this.assetRepository.count({ where: { isVisible: true, type: AssetType.IMAGE } });
|
||||
}
|
||||
|
||||
getVideoCount() {
|
||||
return this.assetRepository.count({ where: { isVisible: true, type: AssetType.VIDEO } });
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
LibraryService,
|
||||
MediaService,
|
||||
MetadataService,
|
||||
MetricsService,
|
||||
PersonService,
|
||||
SmartInfoService,
|
||||
StorageService,
|
||||
@@ -27,6 +28,7 @@ export class AppService {
|
||||
private libraryService: LibraryService,
|
||||
private mediaService: MediaService,
|
||||
private metadataService: MetadataService,
|
||||
private metricsService: MetricsService,
|
||||
private personService: PersonService,
|
||||
private smartInfoService: SmartInfoService,
|
||||
private storageTemplateService: StorageTemplateService,
|
||||
@@ -60,6 +62,7 @@ export class AppService {
|
||||
[JobName.VIDEO_CONVERSION]: (data) => this.mediaService.handleVideoConversion(data),
|
||||
[JobName.QUEUE_METADATA_EXTRACTION]: (data) => this.metadataService.handleQueueMetadataExtraction(data),
|
||||
[JobName.METADATA_EXTRACTION]: (data) => this.metadataService.handleMetadataExtraction(data),
|
||||
[JobName.METRICS]: () => this.metricsService.handleSendMetrics(),
|
||||
[JobName.LINK_LIVE_PHOTOS]: (data) => this.metadataService.handleLivePhotoLinking(data),
|
||||
[JobName.QUEUE_RECOGNIZE_FACES]: (data) => this.personService.handleQueueRecognizeFaces(data),
|
||||
[JobName.RECOGNIZE_FACES]: (data) => this.personService.handleRecognizeFaces(data),
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
AuditApi,
|
||||
ActivityApi,
|
||||
FaceApi,
|
||||
MetricsApi,
|
||||
} from './open-api';
|
||||
import { BASE_PATH } from './open-api/base';
|
||||
import { DUMMY_BASE_URL, toPathString } from './open-api/common';
|
||||
@@ -37,6 +38,7 @@ class ImmichApi {
|
||||
public faceApi: FaceApi;
|
||||
public jobApi: JobApi;
|
||||
public keyApi: APIKeyApi;
|
||||
public metricsApi: MetricsApi;
|
||||
public oauthApi: OAuthApi;
|
||||
public partnerApi: PartnerApi;
|
||||
public searchApi: SearchApi;
|
||||
@@ -65,6 +67,7 @@ class ImmichApi {
|
||||
this.faceApi = new FaceApi(this.config);
|
||||
this.jobApi = new JobApi(this.config);
|
||||
this.keyApi = new APIKeyApi(this.config);
|
||||
this.metricsApi = new MetricsApi(this.config);
|
||||
this.oauthApi = new OAuthApi(this.config);
|
||||
this.partnerApi = new PartnerApi(this.config);
|
||||
this.searchApi = new SearchApi(this.config);
|
||||
|
||||
128
web/src/api/open-api/api.ts
generated
128
web/src/api/open-api/api.ts
generated
@@ -3062,6 +3062,12 @@ export interface ServerFeaturesDto {
|
||||
* @memberof ServerFeaturesDto
|
||||
*/
|
||||
'map': boolean;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof ServerFeaturesDto
|
||||
*/
|
||||
'metrics': boolean;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
@@ -3566,6 +3572,12 @@ export interface SystemConfigDto {
|
||||
* @memberof SystemConfigDto
|
||||
*/
|
||||
'map': SystemConfigMapDto;
|
||||
/**
|
||||
*
|
||||
* @type {SystemConfigMetricsDto}
|
||||
* @memberof SystemConfigDto
|
||||
*/
|
||||
'metrics': SystemConfigMetricsDto;
|
||||
/**
|
||||
*
|
||||
* @type {SystemConfigNewVersionCheckDto}
|
||||
@@ -3908,6 +3920,19 @@ export interface SystemConfigMapDto {
|
||||
*/
|
||||
'lightStyle': string;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface SystemConfigMetricsDto
|
||||
*/
|
||||
export interface SystemConfigMetricsDto {
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof SystemConfigMetricsDto
|
||||
*/
|
||||
'enabled': boolean;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
@@ -12692,6 +12717,109 @@ export class LibraryApi extends BaseAPI {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* MetricsApi - axios parameter creator
|
||||
* @export
|
||||
*/
|
||||
export const MetricsApiAxiosParamCreator = function (configuration?: Configuration) {
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
getMetrics: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
const localVarPath = `/metrics`;
|
||||
// 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;
|
||||
|
||||
// authentication cookie required
|
||||
|
||||
// authentication api_key required
|
||||
await setApiKeyToObject(localVarHeaderParameter, "x-api-key", configuration)
|
||||
|
||||
// authentication bearer required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* MetricsApi - functional programming interface
|
||||
* @export
|
||||
*/
|
||||
export const MetricsApiFp = function(configuration?: Configuration) {
|
||||
const localVarAxiosParamCreator = MetricsApiAxiosParamCreator(configuration)
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async getMetrics(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<object>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.getMetrics(options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* MetricsApi - factory interface
|
||||
* @export
|
||||
*/
|
||||
export const MetricsApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
|
||||
const localVarFp = MetricsApiFp(configuration)
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
getMetrics(options?: AxiosRequestConfig): AxiosPromise<object> {
|
||||
return localVarFp.getMetrics(options).then((request) => request(axios, basePath));
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* MetricsApi - object-oriented interface
|
||||
* @export
|
||||
* @class MetricsApi
|
||||
* @extends {BaseAPI}
|
||||
*/
|
||||
export class MetricsApi extends BaseAPI {
|
||||
/**
|
||||
*
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof MetricsApi
|
||||
*/
|
||||
public getMetrics(options?: AxiosRequestConfig) {
|
||||
return MetricsApiFp(this.configuration).getMetrics(options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* OAuthApi - axios parameter creator
|
||||
* @export
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
<script lang="ts">
|
||||
import {
|
||||
notificationController,
|
||||
NotificationType,
|
||||
} from '$lib/components/shared-components/notification/notification';
|
||||
import { handleError } from '$lib/utils/handle-error';
|
||||
import { api, SystemConfigMetricsDto } 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 type { ResetOptions } from '$lib/utils/dipatch';
|
||||
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
|
||||
|
||||
export let config: SystemConfigMetricsDto; // this is the config that is being edited
|
||||
export let disabled = false;
|
||||
|
||||
let savedConfig: SystemConfigMetricsDto;
|
||||
let defaultConfig: SystemConfigMetricsDto;
|
||||
|
||||
const handleReset = (detail: ResetOptions) => {
|
||||
if (detail.default) {
|
||||
resetToDefault();
|
||||
} else {
|
||||
reset();
|
||||
}
|
||||
};
|
||||
|
||||
async function refreshConfig() {
|
||||
[savedConfig, defaultConfig] = await Promise.all([
|
||||
api.systemConfigApi.getConfig().then((res) => res.data.metrics),
|
||||
api.systemConfigApi.getConfigDefaults().then((res) => res.data.metrics),
|
||||
]);
|
||||
}
|
||||
|
||||
async function saveSetting() {
|
||||
try {
|
||||
const { data: current } = await api.systemConfigApi.getConfig();
|
||||
const { data: updated } = await api.systemConfigApi.updateConfig({
|
||||
systemConfigDto: {
|
||||
...current,
|
||||
metrics: {
|
||||
enabled: config.enabled,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
config = { ...updated.metrics };
|
||||
savedConfig = { ...updated.metrics };
|
||||
|
||||
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();
|
||||
|
||||
config = { ...resetConfig.metrics };
|
||||
savedConfig = { ...resetConfig.metrics };
|
||||
|
||||
notificationController.show({
|
||||
message: 'Reset settings to the recent saved settings',
|
||||
type: NotificationType.Info,
|
||||
});
|
||||
}
|
||||
|
||||
async function resetToDefault() {
|
||||
const { data: configs } = await api.systemConfigApi.getConfigDefaults();
|
||||
|
||||
config = { ...configs.metrics };
|
||||
defaultConfig = { ...configs.metrics };
|
||||
|
||||
notificationController.show({
|
||||
message: 'Reset map settings to default',
|
||||
type: NotificationType.Info,
|
||||
});
|
||||
}
|
||||
|
||||
function getSharedMetrics() {
|
||||
return api.metricsApi.getMetrics().then((response) => response.data);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="mt-2">
|
||||
{#await refreshConfig() 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 sharing of anonymous usage data"
|
||||
bind:checked={config.enabled}
|
||||
/>
|
||||
|
||||
{#if config.enabled}
|
||||
{#await getSharedMetrics()}
|
||||
<LoadingSpinner />
|
||||
{:then metrics}
|
||||
<div class="mt-2 rounded-lg bg-gray-200 p-4 text-xs dark:bg-gray-700 dark:text-immich-dark-fg">
|
||||
<pre><code>{JSON.stringify(metrics, null, 2)}</code></pre>
|
||||
</div>
|
||||
{/await}
|
||||
{/if}
|
||||
|
||||
<SettingButtonsRow
|
||||
on:reset={({ detail }) => handleReset(detail)}
|
||||
on:save={saveSetting}
|
||||
showResetToDefault={!isEqual(savedConfig, defaultConfig)}
|
||||
{disabled}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{/await}
|
||||
</div>
|
||||
@@ -7,8 +7,9 @@
|
||||
import Icon from '$lib/components/elements/icon.svelte';
|
||||
import { asByteUnitString } from '../../utils/byte-units';
|
||||
import LoadingSpinner from './loading-spinner.svelte';
|
||||
import { mdiCloud, mdiDns } from '@mdi/js';
|
||||
import { mdiCloud, mdiDns, mdiEyeOutline } from '@mdi/js';
|
||||
import { serverInfoStore } from '$lib/stores/server-info.store';
|
||||
import { featureFlags } from '$lib/stores/server-config.store';
|
||||
|
||||
const { serverVersion, connected } = websocketStore;
|
||||
|
||||
@@ -70,7 +71,12 @@
|
||||
<Icon path={mdiDns} size={'24'} />
|
||||
</div>
|
||||
<div class="hidden text-xs group-hover:sm:block md:block">
|
||||
<p class="text-sm font-medium text-immich-primary dark:text-immich-dark-primary">Server</p>
|
||||
<div class="text-sm font-medium text-immich-primary dark:text-immich-dark-primary flex justify-between">
|
||||
<p>Server</p>
|
||||
{#if $featureFlags.metrics}
|
||||
<Icon path={mdiEyeOutline} title="This instance is currently sharing metrics with Immich." />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="mt-2 flex justify-between justify-items-center">
|
||||
<p>Status</p>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
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 MetricsSettings from '$lib/components/admin-page/settings/metrics-settings/metrics-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';
|
||||
@@ -63,6 +64,13 @@
|
||||
|
||||
<section id="setting-content" class="flex place-content-center sm:mx-4">
|
||||
<section class="w-full pb-28 sm:w-5/6 md:w-[850px]">
|
||||
<SettingAccordion
|
||||
title="Anonymous Usage Data Settings"
|
||||
subtitle="Manage if you want to share anonymous usage data with Immich"
|
||||
>
|
||||
<MetricsSettings disabled={$featureFlags.configFile} config={configs.metrics} />
|
||||
</SettingAccordion>
|
||||
|
||||
<SettingAccordion
|
||||
title="Job Settings"
|
||||
subtitle="Manage job concurrency"
|
||||
|
||||
Reference in New Issue
Block a user