mirror of
https://github.com/immich-app/immich.git
synced 2025-07-09 03:06:56 -04:00
Merge branch 'main' into chore/backup-wakelock
This commit is contained in:
commit
e69eab18b2
@ -344,16 +344,16 @@ describe('/albums', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /albums/count', () => {
|
describe('GET /albums/statistics', () => {
|
||||||
it('should require authentication', async () => {
|
it('should require authentication', async () => {
|
||||||
const { status, body } = await request(app).get('/albums/count');
|
const { status, body } = await request(app).get('/albums/statistics');
|
||||||
expect(status).toBe(401);
|
expect(status).toBe(401);
|
||||||
expect(body).toEqual(errorDto.unauthorized);
|
expect(body).toEqual(errorDto.unauthorized);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return total count of albums the user has access to', async () => {
|
it('should return total count of albums the user has access to', async () => {
|
||||||
const { status, body } = await request(app)
|
const { status, body } = await request(app)
|
||||||
.get('/albums/count')
|
.get('/albums/statistics')
|
||||||
.set('Authorization', `Bearer ${user1.accessToken}`);
|
.set('Authorization', `Bearer ${user1.accessToken}`);
|
||||||
|
|
||||||
expect(status).toBe(200);
|
expect(status).toBe(200);
|
||||||
|
@ -363,6 +363,8 @@ describe('/asset', () => {
|
|||||||
utils.createAsset(user1.accessToken),
|
utils.createAsset(user1.accessToken),
|
||||||
utils.createAsset(user1.accessToken),
|
utils.createAsset(user1.accessToken),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
await utils.waitForQueueFinish(admin.accessToken, 'thumbnailGeneration');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should require authentication', async () => {
|
it('should require authentication', async () => {
|
||||||
|
@ -18,7 +18,7 @@ class ApiService implements Authentication {
|
|||||||
late AlbumsApi albumsApi;
|
late AlbumsApi albumsApi;
|
||||||
late AssetsApi assetsApi;
|
late AssetsApi assetsApi;
|
||||||
late SearchApi searchApi;
|
late SearchApi searchApi;
|
||||||
late ServerInfoApi serverInfoApi;
|
late ServerApi serverInfoApi;
|
||||||
late MapApi mapApi;
|
late MapApi mapApi;
|
||||||
late PartnersApi partnersApi;
|
late PartnersApi partnersApi;
|
||||||
late PeopleApi peopleApi;
|
late PeopleApi peopleApi;
|
||||||
@ -50,7 +50,7 @@ class ApiService implements Authentication {
|
|||||||
oAuthApi = OAuthApi(_apiClient);
|
oAuthApi = OAuthApi(_apiClient);
|
||||||
albumsApi = AlbumsApi(_apiClient);
|
albumsApi = AlbumsApi(_apiClient);
|
||||||
assetsApi = AssetsApi(_apiClient);
|
assetsApi = AssetsApi(_apiClient);
|
||||||
serverInfoApi = ServerInfoApi(_apiClient);
|
serverInfoApi = ServerApi(_apiClient);
|
||||||
searchApi = SearchApi(_apiClient);
|
searchApi = SearchApi(_apiClient);
|
||||||
mapApi = MapApi(_apiClient);
|
mapApi = MapApi(_apiClient);
|
||||||
partnersApi = PartnersApi(_apiClient);
|
partnersApi = PartnersApi(_apiClient);
|
||||||
|
31
mobile/openapi/README.md
generated
31
mobile/openapi/README.md
generated
@ -86,8 +86,8 @@ Class | Method | HTTP request | Description
|
|||||||
*AlbumsApi* | [**addUsersToAlbum**](doc//AlbumsApi.md#adduserstoalbum) | **PUT** /albums/{id}/users |
|
*AlbumsApi* | [**addUsersToAlbum**](doc//AlbumsApi.md#adduserstoalbum) | **PUT** /albums/{id}/users |
|
||||||
*AlbumsApi* | [**createAlbum**](doc//AlbumsApi.md#createalbum) | **POST** /albums |
|
*AlbumsApi* | [**createAlbum**](doc//AlbumsApi.md#createalbum) | **POST** /albums |
|
||||||
*AlbumsApi* | [**deleteAlbum**](doc//AlbumsApi.md#deletealbum) | **DELETE** /albums/{id} |
|
*AlbumsApi* | [**deleteAlbum**](doc//AlbumsApi.md#deletealbum) | **DELETE** /albums/{id} |
|
||||||
*AlbumsApi* | [**getAlbumCount**](doc//AlbumsApi.md#getalbumcount) | **GET** /albums/count |
|
|
||||||
*AlbumsApi* | [**getAlbumInfo**](doc//AlbumsApi.md#getalbuminfo) | **GET** /albums/{id} |
|
*AlbumsApi* | [**getAlbumInfo**](doc//AlbumsApi.md#getalbuminfo) | **GET** /albums/{id} |
|
||||||
|
*AlbumsApi* | [**getAlbumStatistics**](doc//AlbumsApi.md#getalbumstatistics) | **GET** /albums/statistics |
|
||||||
*AlbumsApi* | [**getAllAlbums**](doc//AlbumsApi.md#getallalbums) | **GET** /albums |
|
*AlbumsApi* | [**getAllAlbums**](doc//AlbumsApi.md#getallalbums) | **GET** /albums |
|
||||||
*AlbumsApi* | [**removeAssetFromAlbum**](doc//AlbumsApi.md#removeassetfromalbum) | **DELETE** /albums/{id}/assets |
|
*AlbumsApi* | [**removeAssetFromAlbum**](doc//AlbumsApi.md#removeassetfromalbum) | **DELETE** /albums/{id}/assets |
|
||||||
*AlbumsApi* | [**removeUserFromAlbum**](doc//AlbumsApi.md#removeuserfromalbum) | **DELETE** /albums/{id}/user/{userId} |
|
*AlbumsApi* | [**removeUserFromAlbum**](doc//AlbumsApi.md#removeuserfromalbum) | **DELETE** /albums/{id}/user/{userId} |
|
||||||
@ -115,16 +115,7 @@ Class | Method | HTTP request | Description
|
|||||||
*AuthenticationApi* | [**logout**](doc//AuthenticationApi.md#logout) | **POST** /auth/logout |
|
*AuthenticationApi* | [**logout**](doc//AuthenticationApi.md#logout) | **POST** /auth/logout |
|
||||||
*AuthenticationApi* | [**signUpAdmin**](doc//AuthenticationApi.md#signupadmin) | **POST** /auth/admin-sign-up |
|
*AuthenticationApi* | [**signUpAdmin**](doc//AuthenticationApi.md#signupadmin) | **POST** /auth/admin-sign-up |
|
||||||
*AuthenticationApi* | [**validateAccessToken**](doc//AuthenticationApi.md#validateaccesstoken) | **POST** /auth/validateToken |
|
*AuthenticationApi* | [**validateAccessToken**](doc//AuthenticationApi.md#validateaccesstoken) | **POST** /auth/validateToken |
|
||||||
*DeprecatedApi* | [**getAboutInfo**](doc//DeprecatedApi.md#getaboutinfo) | **GET** /server-info/about |
|
|
||||||
*DeprecatedApi* | [**getPersonAssets**](doc//DeprecatedApi.md#getpersonassets) | **GET** /people/{id}/assets |
|
*DeprecatedApi* | [**getPersonAssets**](doc//DeprecatedApi.md#getpersonassets) | **GET** /people/{id}/assets |
|
||||||
*DeprecatedApi* | [**getServerConfig**](doc//DeprecatedApi.md#getserverconfig) | **GET** /server-info/config |
|
|
||||||
*DeprecatedApi* | [**getServerFeatures**](doc//DeprecatedApi.md#getserverfeatures) | **GET** /server-info/features |
|
|
||||||
*DeprecatedApi* | [**getServerStatistics**](doc//DeprecatedApi.md#getserverstatistics) | **GET** /server-info/statistics |
|
|
||||||
*DeprecatedApi* | [**getServerVersion**](doc//DeprecatedApi.md#getserverversion) | **GET** /server-info/version |
|
|
||||||
*DeprecatedApi* | [**getStorage**](doc//DeprecatedApi.md#getstorage) | **GET** /server-info/storage |
|
|
||||||
*DeprecatedApi* | [**getSupportedMediaTypes**](doc//DeprecatedApi.md#getsupportedmediatypes) | **GET** /server-info/media-types |
|
|
||||||
*DeprecatedApi* | [**getTheme**](doc//DeprecatedApi.md#gettheme) | **GET** /server-info/theme |
|
|
||||||
*DeprecatedApi* | [**pingServer**](doc//DeprecatedApi.md#pingserver) | **GET** /server-info/ping |
|
|
||||||
*DownloadApi* | [**downloadArchive**](doc//DownloadApi.md#downloadarchive) | **POST** /download/archive |
|
*DownloadApi* | [**downloadArchive**](doc//DownloadApi.md#downloadarchive) | **POST** /download/archive |
|
||||||
*DownloadApi* | [**getDownloadInfo**](doc//DownloadApi.md#getdownloadinfo) | **POST** /download/info |
|
*DownloadApi* | [**getDownloadInfo**](doc//DownloadApi.md#getdownloadinfo) | **POST** /download/info |
|
||||||
*DuplicatesApi* | [**getAssetDuplicates**](doc//DuplicatesApi.md#getassetduplicates) | **GET** /duplicates |
|
*DuplicatesApi* | [**getAssetDuplicates**](doc//DuplicatesApi.md#getassetduplicates) | **GET** /duplicates |
|
||||||
@ -182,17 +173,17 @@ Class | Method | HTTP request | Description
|
|||||||
*SearchApi* | [**searchPlaces**](doc//SearchApi.md#searchplaces) | **GET** /search/places |
|
*SearchApi* | [**searchPlaces**](doc//SearchApi.md#searchplaces) | **GET** /search/places |
|
||||||
*SearchApi* | [**searchSmart**](doc//SearchApi.md#searchsmart) | **POST** /search/smart |
|
*SearchApi* | [**searchSmart**](doc//SearchApi.md#searchsmart) | **POST** /search/smart |
|
||||||
*ServerApi* | [**deleteServerLicense**](doc//ServerApi.md#deleteserverlicense) | **DELETE** /server/license |
|
*ServerApi* | [**deleteServerLicense**](doc//ServerApi.md#deleteserverlicense) | **DELETE** /server/license |
|
||||||
|
*ServerApi* | [**getAboutInfo**](doc//ServerApi.md#getaboutinfo) | **GET** /server/about |
|
||||||
|
*ServerApi* | [**getServerConfig**](doc//ServerApi.md#getserverconfig) | **GET** /server/config |
|
||||||
|
*ServerApi* | [**getServerFeatures**](doc//ServerApi.md#getserverfeatures) | **GET** /server/features |
|
||||||
*ServerApi* | [**getServerLicense**](doc//ServerApi.md#getserverlicense) | **GET** /server/license |
|
*ServerApi* | [**getServerLicense**](doc//ServerApi.md#getserverlicense) | **GET** /server/license |
|
||||||
|
*ServerApi* | [**getServerStatistics**](doc//ServerApi.md#getserverstatistics) | **GET** /server/statistics |
|
||||||
|
*ServerApi* | [**getServerVersion**](doc//ServerApi.md#getserverversion) | **GET** /server/version |
|
||||||
|
*ServerApi* | [**getStorage**](doc//ServerApi.md#getstorage) | **GET** /server/storage |
|
||||||
|
*ServerApi* | [**getSupportedMediaTypes**](doc//ServerApi.md#getsupportedmediatypes) | **GET** /server/media-types |
|
||||||
|
*ServerApi* | [**getTheme**](doc//ServerApi.md#gettheme) | **GET** /server/theme |
|
||||||
|
*ServerApi* | [**pingServer**](doc//ServerApi.md#pingserver) | **GET** /server/ping |
|
||||||
*ServerApi* | [**setServerLicense**](doc//ServerApi.md#setserverlicense) | **PUT** /server/license |
|
*ServerApi* | [**setServerLicense**](doc//ServerApi.md#setserverlicense) | **PUT** /server/license |
|
||||||
*ServerInfoApi* | [**getAboutInfo**](doc//ServerInfoApi.md#getaboutinfo) | **GET** /server-info/about |
|
|
||||||
*ServerInfoApi* | [**getServerConfig**](doc//ServerInfoApi.md#getserverconfig) | **GET** /server-info/config |
|
|
||||||
*ServerInfoApi* | [**getServerFeatures**](doc//ServerInfoApi.md#getserverfeatures) | **GET** /server-info/features |
|
|
||||||
*ServerInfoApi* | [**getServerStatistics**](doc//ServerInfoApi.md#getserverstatistics) | **GET** /server-info/statistics |
|
|
||||||
*ServerInfoApi* | [**getServerVersion**](doc//ServerInfoApi.md#getserverversion) | **GET** /server-info/version |
|
|
||||||
*ServerInfoApi* | [**getStorage**](doc//ServerInfoApi.md#getstorage) | **GET** /server-info/storage |
|
|
||||||
*ServerInfoApi* | [**getSupportedMediaTypes**](doc//ServerInfoApi.md#getsupportedmediatypes) | **GET** /server-info/media-types |
|
|
||||||
*ServerInfoApi* | [**getTheme**](doc//ServerInfoApi.md#gettheme) | **GET** /server-info/theme |
|
|
||||||
*ServerInfoApi* | [**pingServer**](doc//ServerInfoApi.md#pingserver) | **GET** /server-info/ping |
|
|
||||||
*SessionsApi* | [**deleteAllSessions**](doc//SessionsApi.md#deleteallsessions) | **DELETE** /sessions |
|
*SessionsApi* | [**deleteAllSessions**](doc//SessionsApi.md#deleteallsessions) | **DELETE** /sessions |
|
||||||
*SessionsApi* | [**deleteSession**](doc//SessionsApi.md#deletesession) | **DELETE** /sessions/{id} |
|
*SessionsApi* | [**deleteSession**](doc//SessionsApi.md#deletesession) | **DELETE** /sessions/{id} |
|
||||||
*SessionsApi* | [**getSessions**](doc//SessionsApi.md#getsessions) | **GET** /sessions |
|
*SessionsApi* | [**getSessions**](doc//SessionsApi.md#getsessions) | **GET** /sessions |
|
||||||
@ -265,8 +256,8 @@ Class | Method | HTTP request | Description
|
|||||||
- [ActivityStatisticsResponseDto](doc//ActivityStatisticsResponseDto.md)
|
- [ActivityStatisticsResponseDto](doc//ActivityStatisticsResponseDto.md)
|
||||||
- [AddUsersDto](doc//AddUsersDto.md)
|
- [AddUsersDto](doc//AddUsersDto.md)
|
||||||
- [AdminOnboardingUpdateDto](doc//AdminOnboardingUpdateDto.md)
|
- [AdminOnboardingUpdateDto](doc//AdminOnboardingUpdateDto.md)
|
||||||
- [AlbumCountResponseDto](doc//AlbumCountResponseDto.md)
|
|
||||||
- [AlbumResponseDto](doc//AlbumResponseDto.md)
|
- [AlbumResponseDto](doc//AlbumResponseDto.md)
|
||||||
|
- [AlbumStatisticsResponseDto](doc//AlbumStatisticsResponseDto.md)
|
||||||
- [AlbumUserAddDto](doc//AlbumUserAddDto.md)
|
- [AlbumUserAddDto](doc//AlbumUserAddDto.md)
|
||||||
- [AlbumUserCreateDto](doc//AlbumUserCreateDto.md)
|
- [AlbumUserCreateDto](doc//AlbumUserCreateDto.md)
|
||||||
- [AlbumUserResponseDto](doc//AlbumUserResponseDto.md)
|
- [AlbumUserResponseDto](doc//AlbumUserResponseDto.md)
|
||||||
|
3
mobile/openapi/lib/api.dart
generated
3
mobile/openapi/lib/api.dart
generated
@ -51,7 +51,6 @@ part 'api/partners_api.dart';
|
|||||||
part 'api/people_api.dart';
|
part 'api/people_api.dart';
|
||||||
part 'api/search_api.dart';
|
part 'api/search_api.dart';
|
||||||
part 'api/server_api.dart';
|
part 'api/server_api.dart';
|
||||||
part 'api/server_info_api.dart';
|
|
||||||
part 'api/sessions_api.dart';
|
part 'api/sessions_api.dart';
|
||||||
part 'api/shared_links_api.dart';
|
part 'api/shared_links_api.dart';
|
||||||
part 'api/stacks_api.dart';
|
part 'api/stacks_api.dart';
|
||||||
@ -73,8 +72,8 @@ part 'model/activity_response_dto.dart';
|
|||||||
part 'model/activity_statistics_response_dto.dart';
|
part 'model/activity_statistics_response_dto.dart';
|
||||||
part 'model/add_users_dto.dart';
|
part 'model/add_users_dto.dart';
|
||||||
part 'model/admin_onboarding_update_dto.dart';
|
part 'model/admin_onboarding_update_dto.dart';
|
||||||
part 'model/album_count_response_dto.dart';
|
|
||||||
part 'model/album_response_dto.dart';
|
part 'model/album_response_dto.dart';
|
||||||
|
part 'model/album_statistics_response_dto.dart';
|
||||||
part 'model/album_user_add_dto.dart';
|
part 'model/album_user_add_dto.dart';
|
||||||
part 'model/album_user_create_dto.dart';
|
part 'model/album_user_create_dto.dart';
|
||||||
part 'model/album_user_response_dto.dart';
|
part 'model/album_user_response_dto.dart';
|
||||||
|
82
mobile/openapi/lib/api/albums_api.dart
generated
82
mobile/openapi/lib/api/albums_api.dart
generated
@ -218,47 +218,6 @@ class AlbumsApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs an HTTP 'GET /albums/count' operation and returns the [Response].
|
|
||||||
Future<Response> getAlbumCountWithHttpInfo() async {
|
|
||||||
// ignore: prefer_const_declarations
|
|
||||||
final path = r'/albums/count';
|
|
||||||
|
|
||||||
// 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<AlbumCountResponseDto?> getAlbumCount() async {
|
|
||||||
final response = await getAlbumCountWithHttpInfo();
|
|
||||||
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), 'AlbumCountResponseDto',) as AlbumCountResponseDto;
|
|
||||||
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Performs an HTTP 'GET /albums/{id}' operation and returns the [Response].
|
/// Performs an HTTP 'GET /albums/{id}' operation and returns the [Response].
|
||||||
/// Parameters:
|
/// Parameters:
|
||||||
///
|
///
|
||||||
@ -322,6 +281,47 @@ class AlbumsApi {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Performs an HTTP 'GET /albums/statistics' operation and returns the [Response].
|
||||||
|
Future<Response> getAlbumStatisticsWithHttpInfo() async {
|
||||||
|
// ignore: prefer_const_declarations
|
||||||
|
final path = r'/albums/statistics';
|
||||||
|
|
||||||
|
// 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<AlbumStatisticsResponseDto?> getAlbumStatistics() async {
|
||||||
|
final response = await getAlbumStatisticsWithHttpInfo();
|
||||||
|
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), 'AlbumStatisticsResponseDto',) as AlbumStatisticsResponseDto;
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/// Performs an HTTP 'GET /albums' operation and returns the [Response].
|
/// Performs an HTTP 'GET /albums' operation and returns the [Response].
|
||||||
/// Parameters:
|
/// Parameters:
|
||||||
///
|
///
|
||||||
|
396
mobile/openapi/lib/api/deprecated_api.dart
generated
396
mobile/openapi/lib/api/deprecated_api.dart
generated
@ -16,50 +16,6 @@ class DeprecatedApi {
|
|||||||
|
|
||||||
final ApiClient apiClient;
|
final ApiClient apiClient;
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
///
|
|
||||||
/// Note: This method returns the HTTP [Response].
|
|
||||||
Future<Response> getAboutInfoWithHttpInfo() async {
|
|
||||||
// ignore: prefer_const_declarations
|
|
||||||
final path = r'/server-info/about';
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
Future<ServerAboutResponseDto?> getAboutInfo() async {
|
|
||||||
final response = await getAboutInfoWithHttpInfo();
|
|
||||||
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), 'ServerAboutResponseDto',) as ServerAboutResponseDto;
|
|
||||||
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.113.0
|
/// This property was deprecated in v1.113.0
|
||||||
///
|
///
|
||||||
/// Note: This method returns the HTTP [Response].
|
/// Note: This method returns the HTTP [Response].
|
||||||
@ -115,356 +71,4 @@ class DeprecatedApi {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
///
|
|
||||||
/// Note: This method returns the HTTP [Response].
|
|
||||||
Future<Response> getServerConfigWithHttpInfo() async {
|
|
||||||
// ignore: prefer_const_declarations
|
|
||||||
final path = r'/server-info/config';
|
|
||||||
|
|
||||||
// ignore: prefer_final_locals
|
|
||||||
Object? postBody;
|
|
||||||
|
|
||||||
final queryParams = <QueryParam>[];
|
|
||||||
final headerParams = <String, String>{};
|
|
||||||
final formParams = <String, String>{};
|
|
||||||
|
|
||||||
const contentTypes = <String>[];
|
|
||||||
|
|
||||||
|
|
||||||
return apiClient.invokeAPI(
|
|
||||||
path,
|
|
||||||
'GET',
|
|
||||||
queryParams,
|
|
||||||
postBody,
|
|
||||||
headerParams,
|
|
||||||
formParams,
|
|
||||||
contentTypes.isEmpty ? null : contentTypes.first,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
Future<ServerConfigDto?> getServerConfig() async {
|
|
||||||
final response = await getServerConfigWithHttpInfo();
|
|
||||||
if (response.statusCode >= HttpStatus.badRequest) {
|
|
||||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
|
||||||
}
|
|
||||||
// When a remote server returns no body with a status of 204, we shall not decode it.
|
|
||||||
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
|
||||||
// FormatException when trying to decode an empty string.
|
|
||||||
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
|
||||||
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'ServerConfigDto',) as ServerConfigDto;
|
|
||||||
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
///
|
|
||||||
/// Note: This method returns the HTTP [Response].
|
|
||||||
Future<Response> getServerFeaturesWithHttpInfo() async {
|
|
||||||
// ignore: prefer_const_declarations
|
|
||||||
final path = r'/server-info/features';
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
Future<ServerFeaturesDto?> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
///
|
|
||||||
/// Note: This method returns the HTTP [Response].
|
|
||||||
Future<Response> getServerStatisticsWithHttpInfo() async {
|
|
||||||
// ignore: prefer_const_declarations
|
|
||||||
final path = r'/server-info/statistics';
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
Future<ServerStatsResponseDto?> getServerStatistics() async {
|
|
||||||
final response = await getServerStatisticsWithHttpInfo();
|
|
||||||
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), 'ServerStatsResponseDto',) as ServerStatsResponseDto;
|
|
||||||
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
///
|
|
||||||
/// Note: This method returns the HTTP [Response].
|
|
||||||
Future<Response> getServerVersionWithHttpInfo() async {
|
|
||||||
// ignore: prefer_const_declarations
|
|
||||||
final path = r'/server-info/version';
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
Future<ServerVersionResponseDto?> getServerVersion() async {
|
|
||||||
final response = await getServerVersionWithHttpInfo();
|
|
||||||
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), 'ServerVersionResponseDto',) as ServerVersionResponseDto;
|
|
||||||
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
///
|
|
||||||
/// Note: This method returns the HTTP [Response].
|
|
||||||
Future<Response> getStorageWithHttpInfo() async {
|
|
||||||
// ignore: prefer_const_declarations
|
|
||||||
final path = r'/server-info/storage';
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
Future<ServerStorageResponseDto?> getStorage() async {
|
|
||||||
final response = await getStorageWithHttpInfo();
|
|
||||||
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), 'ServerStorageResponseDto',) as ServerStorageResponseDto;
|
|
||||||
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
///
|
|
||||||
/// Note: This method returns the HTTP [Response].
|
|
||||||
Future<Response> getSupportedMediaTypesWithHttpInfo() async {
|
|
||||||
// ignore: prefer_const_declarations
|
|
||||||
final path = r'/server-info/media-types';
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
Future<ServerMediaTypesResponseDto?> getSupportedMediaTypes() async {
|
|
||||||
final response = await getSupportedMediaTypesWithHttpInfo();
|
|
||||||
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), 'ServerMediaTypesResponseDto',) as ServerMediaTypesResponseDto;
|
|
||||||
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
///
|
|
||||||
/// Note: This method returns the HTTP [Response].
|
|
||||||
Future<Response> getThemeWithHttpInfo() async {
|
|
||||||
// ignore: prefer_const_declarations
|
|
||||||
final path = r'/server-info/theme';
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
Future<ServerThemeDto?> getTheme() async {
|
|
||||||
final response = await getThemeWithHttpInfo();
|
|
||||||
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), 'ServerThemeDto',) as ServerThemeDto;
|
|
||||||
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
///
|
|
||||||
/// Note: This method returns the HTTP [Response].
|
|
||||||
Future<Response> pingServerWithHttpInfo() async {
|
|
||||||
// ignore: prefer_const_declarations
|
|
||||||
final path = r'/server-info/ping';
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
Future<ServerPingResponse?> pingServer() async {
|
|
||||||
final response = await pingServerWithHttpInfo();
|
|
||||||
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), 'ServerPingResponse',) as ServerPingResponse;
|
|
||||||
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
369
mobile/openapi/lib/api/server_api.dart
generated
369
mobile/openapi/lib/api/server_api.dart
generated
@ -49,6 +49,129 @@ class ServerApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Performs an HTTP 'GET /server/about' operation and returns the [Response].
|
||||||
|
Future<Response> getAboutInfoWithHttpInfo() async {
|
||||||
|
// ignore: prefer_const_declarations
|
||||||
|
final path = r'/server/about';
|
||||||
|
|
||||||
|
// 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<ServerAboutResponseDto?> getAboutInfo() async {
|
||||||
|
final response = await getAboutInfoWithHttpInfo();
|
||||||
|
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), 'ServerAboutResponseDto',) as ServerAboutResponseDto;
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs an HTTP 'GET /server/config' operation and returns the [Response].
|
||||||
|
Future<Response> getServerConfigWithHttpInfo() async {
|
||||||
|
// ignore: prefer_const_declarations
|
||||||
|
final path = r'/server/config';
|
||||||
|
|
||||||
|
// ignore: prefer_final_locals
|
||||||
|
Object? postBody;
|
||||||
|
|
||||||
|
final queryParams = <QueryParam>[];
|
||||||
|
final headerParams = <String, String>{};
|
||||||
|
final formParams = <String, String>{};
|
||||||
|
|
||||||
|
const contentTypes = <String>[];
|
||||||
|
|
||||||
|
|
||||||
|
return apiClient.invokeAPI(
|
||||||
|
path,
|
||||||
|
'GET',
|
||||||
|
queryParams,
|
||||||
|
postBody,
|
||||||
|
headerParams,
|
||||||
|
formParams,
|
||||||
|
contentTypes.isEmpty ? null : contentTypes.first,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<ServerConfigDto?> getServerConfig() async {
|
||||||
|
final response = await getServerConfigWithHttpInfo();
|
||||||
|
if (response.statusCode >= HttpStatus.badRequest) {
|
||||||
|
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||||
|
}
|
||||||
|
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||||
|
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||||
|
// FormatException when trying to decode an empty string.
|
||||||
|
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||||
|
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'ServerConfigDto',) as ServerConfigDto;
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs an HTTP 'GET /server/features' operation and returns the [Response].
|
||||||
|
Future<Response> getServerFeaturesWithHttpInfo() async {
|
||||||
|
// ignore: prefer_const_declarations
|
||||||
|
final path = r'/server/features';
|
||||||
|
|
||||||
|
// 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<ServerFeaturesDto?> 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/license' operation and returns the [Response].
|
/// Performs an HTTP 'GET /server/license' operation and returns the [Response].
|
||||||
Future<Response> getServerLicenseWithHttpInfo() async {
|
Future<Response> getServerLicenseWithHttpInfo() async {
|
||||||
// ignore: prefer_const_declarations
|
// ignore: prefer_const_declarations
|
||||||
@ -90,6 +213,252 @@ class ServerApi {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Performs an HTTP 'GET /server/statistics' operation and returns the [Response].
|
||||||
|
Future<Response> getServerStatisticsWithHttpInfo() async {
|
||||||
|
// ignore: prefer_const_declarations
|
||||||
|
final path = r'/server/statistics';
|
||||||
|
|
||||||
|
// 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<ServerStatsResponseDto?> getServerStatistics() async {
|
||||||
|
final response = await getServerStatisticsWithHttpInfo();
|
||||||
|
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), 'ServerStatsResponseDto',) as ServerStatsResponseDto;
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs an HTTP 'GET /server/version' operation and returns the [Response].
|
||||||
|
Future<Response> getServerVersionWithHttpInfo() async {
|
||||||
|
// ignore: prefer_const_declarations
|
||||||
|
final path = r'/server/version';
|
||||||
|
|
||||||
|
// 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<ServerVersionResponseDto?> getServerVersion() async {
|
||||||
|
final response = await getServerVersionWithHttpInfo();
|
||||||
|
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), 'ServerVersionResponseDto',) as ServerVersionResponseDto;
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs an HTTP 'GET /server/storage' operation and returns the [Response].
|
||||||
|
Future<Response> getStorageWithHttpInfo() async {
|
||||||
|
// ignore: prefer_const_declarations
|
||||||
|
final path = r'/server/storage';
|
||||||
|
|
||||||
|
// 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<ServerStorageResponseDto?> getStorage() async {
|
||||||
|
final response = await getStorageWithHttpInfo();
|
||||||
|
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), 'ServerStorageResponseDto',) as ServerStorageResponseDto;
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs an HTTP 'GET /server/media-types' operation and returns the [Response].
|
||||||
|
Future<Response> getSupportedMediaTypesWithHttpInfo() async {
|
||||||
|
// ignore: prefer_const_declarations
|
||||||
|
final path = r'/server/media-types';
|
||||||
|
|
||||||
|
// 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<ServerMediaTypesResponseDto?> getSupportedMediaTypes() async {
|
||||||
|
final response = await getSupportedMediaTypesWithHttpInfo();
|
||||||
|
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), 'ServerMediaTypesResponseDto',) as ServerMediaTypesResponseDto;
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs an HTTP 'GET /server/theme' operation and returns the [Response].
|
||||||
|
Future<Response> getThemeWithHttpInfo() async {
|
||||||
|
// ignore: prefer_const_declarations
|
||||||
|
final path = r'/server/theme';
|
||||||
|
|
||||||
|
// 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<ServerThemeDto?> getTheme() async {
|
||||||
|
final response = await getThemeWithHttpInfo();
|
||||||
|
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), 'ServerThemeDto',) as ServerThemeDto;
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs an HTTP 'GET /server/ping' operation and returns the [Response].
|
||||||
|
Future<Response> pingServerWithHttpInfo() async {
|
||||||
|
// ignore: prefer_const_declarations
|
||||||
|
final path = r'/server/ping';
|
||||||
|
|
||||||
|
// 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<ServerPingResponse?> pingServer() async {
|
||||||
|
final response = await pingServerWithHttpInfo();
|
||||||
|
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), 'ServerPingResponse',) as ServerPingResponse;
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/// Performs an HTTP 'PUT /server/license' operation and returns the [Response].
|
/// Performs an HTTP 'PUT /server/license' operation and returns the [Response].
|
||||||
/// Parameters:
|
/// Parameters:
|
||||||
///
|
///
|
||||||
|
414
mobile/openapi/lib/api/server_info_api.dart
generated
414
mobile/openapi/lib/api/server_info_api.dart
generated
@ -1,414 +0,0 @@
|
|||||||
//
|
|
||||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
|
||||||
//
|
|
||||||
// @dart=2.18
|
|
||||||
|
|
||||||
// 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 ServerInfoApi {
|
|
||||||
ServerInfoApi([ApiClient? apiClient]) : apiClient = apiClient ?? defaultApiClient;
|
|
||||||
|
|
||||||
final ApiClient apiClient;
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
///
|
|
||||||
/// Note: This method returns the HTTP [Response].
|
|
||||||
Future<Response> getAboutInfoWithHttpInfo() async {
|
|
||||||
// ignore: prefer_const_declarations
|
|
||||||
final path = r'/server-info/about';
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
Future<ServerAboutResponseDto?> getAboutInfo() async {
|
|
||||||
final response = await getAboutInfoWithHttpInfo();
|
|
||||||
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), 'ServerAboutResponseDto',) as ServerAboutResponseDto;
|
|
||||||
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
///
|
|
||||||
/// Note: This method returns the HTTP [Response].
|
|
||||||
Future<Response> getServerConfigWithHttpInfo() async {
|
|
||||||
// ignore: prefer_const_declarations
|
|
||||||
final path = r'/server-info/config';
|
|
||||||
|
|
||||||
// ignore: prefer_final_locals
|
|
||||||
Object? postBody;
|
|
||||||
|
|
||||||
final queryParams = <QueryParam>[];
|
|
||||||
final headerParams = <String, String>{};
|
|
||||||
final formParams = <String, String>{};
|
|
||||||
|
|
||||||
const contentTypes = <String>[];
|
|
||||||
|
|
||||||
|
|
||||||
return apiClient.invokeAPI(
|
|
||||||
path,
|
|
||||||
'GET',
|
|
||||||
queryParams,
|
|
||||||
postBody,
|
|
||||||
headerParams,
|
|
||||||
formParams,
|
|
||||||
contentTypes.isEmpty ? null : contentTypes.first,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
Future<ServerConfigDto?> getServerConfig() async {
|
|
||||||
final response = await getServerConfigWithHttpInfo();
|
|
||||||
if (response.statusCode >= HttpStatus.badRequest) {
|
|
||||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
|
||||||
}
|
|
||||||
// When a remote server returns no body with a status of 204, we shall not decode it.
|
|
||||||
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
|
||||||
// FormatException when trying to decode an empty string.
|
|
||||||
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
|
||||||
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'ServerConfigDto',) as ServerConfigDto;
|
|
||||||
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
///
|
|
||||||
/// Note: This method returns the HTTP [Response].
|
|
||||||
Future<Response> getServerFeaturesWithHttpInfo() async {
|
|
||||||
// ignore: prefer_const_declarations
|
|
||||||
final path = r'/server-info/features';
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
Future<ServerFeaturesDto?> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
///
|
|
||||||
/// Note: This method returns the HTTP [Response].
|
|
||||||
Future<Response> getServerStatisticsWithHttpInfo() async {
|
|
||||||
// ignore: prefer_const_declarations
|
|
||||||
final path = r'/server-info/statistics';
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
Future<ServerStatsResponseDto?> getServerStatistics() async {
|
|
||||||
final response = await getServerStatisticsWithHttpInfo();
|
|
||||||
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), 'ServerStatsResponseDto',) as ServerStatsResponseDto;
|
|
||||||
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
///
|
|
||||||
/// Note: This method returns the HTTP [Response].
|
|
||||||
Future<Response> getServerVersionWithHttpInfo() async {
|
|
||||||
// ignore: prefer_const_declarations
|
|
||||||
final path = r'/server-info/version';
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
Future<ServerVersionResponseDto?> getServerVersion() async {
|
|
||||||
final response = await getServerVersionWithHttpInfo();
|
|
||||||
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), 'ServerVersionResponseDto',) as ServerVersionResponseDto;
|
|
||||||
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
///
|
|
||||||
/// Note: This method returns the HTTP [Response].
|
|
||||||
Future<Response> getStorageWithHttpInfo() async {
|
|
||||||
// ignore: prefer_const_declarations
|
|
||||||
final path = r'/server-info/storage';
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
Future<ServerStorageResponseDto?> getStorage() async {
|
|
||||||
final response = await getStorageWithHttpInfo();
|
|
||||||
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), 'ServerStorageResponseDto',) as ServerStorageResponseDto;
|
|
||||||
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
///
|
|
||||||
/// Note: This method returns the HTTP [Response].
|
|
||||||
Future<Response> getSupportedMediaTypesWithHttpInfo() async {
|
|
||||||
// ignore: prefer_const_declarations
|
|
||||||
final path = r'/server-info/media-types';
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
Future<ServerMediaTypesResponseDto?> getSupportedMediaTypes() async {
|
|
||||||
final response = await getSupportedMediaTypesWithHttpInfo();
|
|
||||||
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), 'ServerMediaTypesResponseDto',) as ServerMediaTypesResponseDto;
|
|
||||||
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
///
|
|
||||||
/// Note: This method returns the HTTP [Response].
|
|
||||||
Future<Response> getThemeWithHttpInfo() async {
|
|
||||||
// ignore: prefer_const_declarations
|
|
||||||
final path = r'/server-info/theme';
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
Future<ServerThemeDto?> getTheme() async {
|
|
||||||
final response = await getThemeWithHttpInfo();
|
|
||||||
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), 'ServerThemeDto',) as ServerThemeDto;
|
|
||||||
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
///
|
|
||||||
/// Note: This method returns the HTTP [Response].
|
|
||||||
Future<Response> pingServerWithHttpInfo() async {
|
|
||||||
// ignore: prefer_const_declarations
|
|
||||||
final path = r'/server-info/ping';
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This property was deprecated in v1.107.0
|
|
||||||
Future<ServerPingResponse?> pingServer() async {
|
|
||||||
final response = await pingServerWithHttpInfo();
|
|
||||||
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), 'ServerPingResponse',) as ServerPingResponse;
|
|
||||||
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
4
mobile/openapi/lib/api_client.dart
generated
4
mobile/openapi/lib/api_client.dart
generated
@ -201,10 +201,10 @@ class ApiClient {
|
|||||||
return AddUsersDto.fromJson(value);
|
return AddUsersDto.fromJson(value);
|
||||||
case 'AdminOnboardingUpdateDto':
|
case 'AdminOnboardingUpdateDto':
|
||||||
return AdminOnboardingUpdateDto.fromJson(value);
|
return AdminOnboardingUpdateDto.fromJson(value);
|
||||||
case 'AlbumCountResponseDto':
|
|
||||||
return AlbumCountResponseDto.fromJson(value);
|
|
||||||
case 'AlbumResponseDto':
|
case 'AlbumResponseDto':
|
||||||
return AlbumResponseDto.fromJson(value);
|
return AlbumResponseDto.fromJson(value);
|
||||||
|
case 'AlbumStatisticsResponseDto':
|
||||||
|
return AlbumStatisticsResponseDto.fromJson(value);
|
||||||
case 'AlbumUserAddDto':
|
case 'AlbumUserAddDto':
|
||||||
return AlbumUserAddDto.fromJson(value);
|
return AlbumUserAddDto.fromJson(value);
|
||||||
case 'AlbumUserCreateDto':
|
case 'AlbumUserCreateDto':
|
||||||
|
@ -10,9 +10,9 @@
|
|||||||
|
|
||||||
part of openapi.api;
|
part of openapi.api;
|
||||||
|
|
||||||
class AlbumCountResponseDto {
|
class AlbumStatisticsResponseDto {
|
||||||
/// Returns a new [AlbumCountResponseDto] instance.
|
/// Returns a new [AlbumStatisticsResponseDto] instance.
|
||||||
AlbumCountResponseDto({
|
AlbumStatisticsResponseDto({
|
||||||
required this.notShared,
|
required this.notShared,
|
||||||
required this.owned,
|
required this.owned,
|
||||||
required this.shared,
|
required this.shared,
|
||||||
@ -25,7 +25,7 @@ class AlbumCountResponseDto {
|
|||||||
int shared;
|
int shared;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) => identical(this, other) || other is AlbumCountResponseDto &&
|
bool operator ==(Object other) => identical(this, other) || other is AlbumStatisticsResponseDto &&
|
||||||
other.notShared == notShared &&
|
other.notShared == notShared &&
|
||||||
other.owned == owned &&
|
other.owned == owned &&
|
||||||
other.shared == shared;
|
other.shared == shared;
|
||||||
@ -38,7 +38,7 @@ class AlbumCountResponseDto {
|
|||||||
(shared.hashCode);
|
(shared.hashCode);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'AlbumCountResponseDto[notShared=$notShared, owned=$owned, shared=$shared]';
|
String toString() => 'AlbumStatisticsResponseDto[notShared=$notShared, owned=$owned, shared=$shared]';
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final json = <String, dynamic>{};
|
final json = <String, dynamic>{};
|
||||||
@ -48,14 +48,14 @@ class AlbumCountResponseDto {
|
|||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a new [AlbumCountResponseDto] instance and imports its values from
|
/// Returns a new [AlbumStatisticsResponseDto] instance and imports its values from
|
||||||
/// [value] if it's a [Map], null otherwise.
|
/// [value] if it's a [Map], null otherwise.
|
||||||
// ignore: prefer_constructors_over_static_methods
|
// ignore: prefer_constructors_over_static_methods
|
||||||
static AlbumCountResponseDto? fromJson(dynamic value) {
|
static AlbumStatisticsResponseDto? fromJson(dynamic value) {
|
||||||
if (value is Map) {
|
if (value is Map) {
|
||||||
final json = value.cast<String, dynamic>();
|
final json = value.cast<String, dynamic>();
|
||||||
|
|
||||||
return AlbumCountResponseDto(
|
return AlbumStatisticsResponseDto(
|
||||||
notShared: mapValueOfType<int>(json, r'notShared')!,
|
notShared: mapValueOfType<int>(json, r'notShared')!,
|
||||||
owned: mapValueOfType<int>(json, r'owned')!,
|
owned: mapValueOfType<int>(json, r'owned')!,
|
||||||
shared: mapValueOfType<int>(json, r'shared')!,
|
shared: mapValueOfType<int>(json, r'shared')!,
|
||||||
@ -64,11 +64,11 @@ class AlbumCountResponseDto {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<AlbumCountResponseDto> listFromJson(dynamic json, {bool growable = false,}) {
|
static List<AlbumStatisticsResponseDto> listFromJson(dynamic json, {bool growable = false,}) {
|
||||||
final result = <AlbumCountResponseDto>[];
|
final result = <AlbumStatisticsResponseDto>[];
|
||||||
if (json is List && json.isNotEmpty) {
|
if (json is List && json.isNotEmpty) {
|
||||||
for (final row in json) {
|
for (final row in json) {
|
||||||
final value = AlbumCountResponseDto.fromJson(row);
|
final value = AlbumStatisticsResponseDto.fromJson(row);
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
result.add(value);
|
result.add(value);
|
||||||
}
|
}
|
||||||
@ -77,12 +77,12 @@ class AlbumCountResponseDto {
|
|||||||
return result.toList(growable: growable);
|
return result.toList(growable: growable);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Map<String, AlbumCountResponseDto> mapFromJson(dynamic json) {
|
static Map<String, AlbumStatisticsResponseDto> mapFromJson(dynamic json) {
|
||||||
final map = <String, AlbumCountResponseDto>{};
|
final map = <String, AlbumStatisticsResponseDto>{};
|
||||||
if (json is Map && json.isNotEmpty) {
|
if (json is Map && json.isNotEmpty) {
|
||||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||||
for (final entry in json.entries) {
|
for (final entry in json.entries) {
|
||||||
final value = AlbumCountResponseDto.fromJson(entry.value);
|
final value = AlbumStatisticsResponseDto.fromJson(entry.value);
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
map[entry.key] = value;
|
map[entry.key] = value;
|
||||||
}
|
}
|
||||||
@ -91,14 +91,14 @@ class AlbumCountResponseDto {
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
// maps a json object with a list of AlbumCountResponseDto-objects as value to a dart map
|
// maps a json object with a list of AlbumStatisticsResponseDto-objects as value to a dart map
|
||||||
static Map<String, List<AlbumCountResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
static Map<String, List<AlbumStatisticsResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||||
final map = <String, List<AlbumCountResponseDto>>{};
|
final map = <String, List<AlbumStatisticsResponseDto>>{};
|
||||||
if (json is Map && json.isNotEmpty) {
|
if (json is Map && json.isNotEmpty) {
|
||||||
// ignore: parameter_assignments
|
// ignore: parameter_assignments
|
||||||
json = json.cast<String, dynamic>();
|
json = json.cast<String, dynamic>();
|
||||||
for (final entry in json.entries) {
|
for (final entry in json.entries) {
|
||||||
map[entry.key] = AlbumCountResponseDto.listFromJson(entry.value, growable: growable,);
|
map[entry.key] = AlbumStatisticsResponseDto.listFromJson(entry.value, growable: growable,);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
12
mobile/openapi/lib/model/permission.dart
generated
12
mobile/openapi/lib/model/permission.dart
generated
@ -36,7 +36,6 @@ class Permission {
|
|||||||
static const assetPeriodRead = Permission._(r'asset.read');
|
static const assetPeriodRead = Permission._(r'asset.read');
|
||||||
static const assetPeriodUpdate = Permission._(r'asset.update');
|
static const assetPeriodUpdate = Permission._(r'asset.update');
|
||||||
static const assetPeriodDelete = Permission._(r'asset.delete');
|
static const assetPeriodDelete = Permission._(r'asset.delete');
|
||||||
static const assetPeriodRestore = Permission._(r'asset.restore');
|
|
||||||
static const assetPeriodShare = Permission._(r'asset.share');
|
static const assetPeriodShare = Permission._(r'asset.share');
|
||||||
static const assetPeriodView = Permission._(r'asset.view');
|
static const assetPeriodView = Permission._(r'asset.view');
|
||||||
static const assetPeriodDownload = Permission._(r'asset.download');
|
static const assetPeriodDownload = Permission._(r'asset.download');
|
||||||
@ -78,6 +77,9 @@ class Permission {
|
|||||||
static const personPeriodStatistics = Permission._(r'person.statistics');
|
static const personPeriodStatistics = Permission._(r'person.statistics');
|
||||||
static const personPeriodMerge = Permission._(r'person.merge');
|
static const personPeriodMerge = Permission._(r'person.merge');
|
||||||
static const personPeriodReassign = Permission._(r'person.reassign');
|
static const personPeriodReassign = Permission._(r'person.reassign');
|
||||||
|
static const sessionPeriodRead = Permission._(r'session.read');
|
||||||
|
static const sessionPeriodUpdate = Permission._(r'session.update');
|
||||||
|
static const sessionPeriodDelete = Permission._(r'session.delete');
|
||||||
static const sharedLinkPeriodCreate = Permission._(r'sharedLink.create');
|
static const sharedLinkPeriodCreate = Permission._(r'sharedLink.create');
|
||||||
static const sharedLinkPeriodRead = Permission._(r'sharedLink.read');
|
static const sharedLinkPeriodRead = Permission._(r'sharedLink.read');
|
||||||
static const sharedLinkPeriodUpdate = Permission._(r'sharedLink.update');
|
static const sharedLinkPeriodUpdate = Permission._(r'sharedLink.update');
|
||||||
@ -114,7 +116,6 @@ class Permission {
|
|||||||
assetPeriodRead,
|
assetPeriodRead,
|
||||||
assetPeriodUpdate,
|
assetPeriodUpdate,
|
||||||
assetPeriodDelete,
|
assetPeriodDelete,
|
||||||
assetPeriodRestore,
|
|
||||||
assetPeriodShare,
|
assetPeriodShare,
|
||||||
assetPeriodView,
|
assetPeriodView,
|
||||||
assetPeriodDownload,
|
assetPeriodDownload,
|
||||||
@ -156,6 +157,9 @@ class Permission {
|
|||||||
personPeriodStatistics,
|
personPeriodStatistics,
|
||||||
personPeriodMerge,
|
personPeriodMerge,
|
||||||
personPeriodReassign,
|
personPeriodReassign,
|
||||||
|
sessionPeriodRead,
|
||||||
|
sessionPeriodUpdate,
|
||||||
|
sessionPeriodDelete,
|
||||||
sharedLinkPeriodCreate,
|
sharedLinkPeriodCreate,
|
||||||
sharedLinkPeriodRead,
|
sharedLinkPeriodRead,
|
||||||
sharedLinkPeriodUpdate,
|
sharedLinkPeriodUpdate,
|
||||||
@ -227,7 +231,6 @@ class PermissionTypeTransformer {
|
|||||||
case r'asset.read': return Permission.assetPeriodRead;
|
case r'asset.read': return Permission.assetPeriodRead;
|
||||||
case r'asset.update': return Permission.assetPeriodUpdate;
|
case r'asset.update': return Permission.assetPeriodUpdate;
|
||||||
case r'asset.delete': return Permission.assetPeriodDelete;
|
case r'asset.delete': return Permission.assetPeriodDelete;
|
||||||
case r'asset.restore': return Permission.assetPeriodRestore;
|
|
||||||
case r'asset.share': return Permission.assetPeriodShare;
|
case r'asset.share': return Permission.assetPeriodShare;
|
||||||
case r'asset.view': return Permission.assetPeriodView;
|
case r'asset.view': return Permission.assetPeriodView;
|
||||||
case r'asset.download': return Permission.assetPeriodDownload;
|
case r'asset.download': return Permission.assetPeriodDownload;
|
||||||
@ -269,6 +272,9 @@ class PermissionTypeTransformer {
|
|||||||
case r'person.statistics': return Permission.personPeriodStatistics;
|
case r'person.statistics': return Permission.personPeriodStatistics;
|
||||||
case r'person.merge': return Permission.personPeriodMerge;
|
case r'person.merge': return Permission.personPeriodMerge;
|
||||||
case r'person.reassign': return Permission.personPeriodReassign;
|
case r'person.reassign': return Permission.personPeriodReassign;
|
||||||
|
case r'session.read': return Permission.sessionPeriodRead;
|
||||||
|
case r'session.update': return Permission.sessionPeriodUpdate;
|
||||||
|
case r'session.delete': return Permission.sessionPeriodDelete;
|
||||||
case r'sharedLink.create': return Permission.sharedLinkPeriodCreate;
|
case r'sharedLink.create': return Permission.sharedLinkPeriodCreate;
|
||||||
case r'sharedLink.read': return Permission.sharedLinkPeriodRead;
|
case r'sharedLink.read': return Permission.sharedLinkPeriodRead;
|
||||||
case r'sharedLink.update': return Permission.sharedLinkPeriodUpdate;
|
case r'sharedLink.update': return Permission.sharedLinkPeriodUpdate;
|
||||||
|
@ -660,16 +660,16 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/albums/count": {
|
"/albums/statistics": {
|
||||||
"get": {
|
"get": {
|
||||||
"operationId": "getAlbumCount",
|
"operationId": "getAlbumStatistics",
|
||||||
"parameters": [],
|
"parameters": [],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"content": {
|
"content": {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
"schema": {
|
"schema": {
|
||||||
"$ref": "#/components/schemas/AlbumCountResponseDto"
|
"$ref": "#/components/schemas/AlbumStatisticsResponseDto"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -3643,7 +3643,7 @@
|
|||||||
"operationId": "unlinkOAuthAccount",
|
"operationId": "unlinkOAuthAccount",
|
||||||
"parameters": [],
|
"parameters": [],
|
||||||
"responses": {
|
"responses": {
|
||||||
"201": {
|
"200": {
|
||||||
"content": {
|
"content": {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
"schema": {
|
"schema": {
|
||||||
@ -4771,10 +4771,8 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/server-info/about": {
|
"/server/about": {
|
||||||
"get": {
|
"get": {
|
||||||
"deprecated": true,
|
|
||||||
"description": "This property was deprecated in v1.107.0",
|
|
||||||
"operationId": "getAboutInfo",
|
"operationId": "getAboutInfo",
|
||||||
"parameters": [],
|
"parameters": [],
|
||||||
"responses": {
|
"responses": {
|
||||||
@ -4801,18 +4799,12 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"Server Info",
|
"Server"
|
||||||
"Deprecated"
|
]
|
||||||
],
|
|
||||||
"x-immich-lifecycle": {
|
|
||||||
"deprecatedAt": "v1.107.0"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/server-info/config": {
|
"/server/config": {
|
||||||
"get": {
|
"get": {
|
||||||
"deprecated": true,
|
|
||||||
"description": "This property was deprecated in v1.107.0",
|
|
||||||
"operationId": "getServerConfig",
|
"operationId": "getServerConfig",
|
||||||
"parameters": [],
|
"parameters": [],
|
||||||
"responses": {
|
"responses": {
|
||||||
@ -4828,18 +4820,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tags": [
|
"tags": [
|
||||||
"Server Info",
|
"Server"
|
||||||
"Deprecated"
|
]
|
||||||
],
|
|
||||||
"x-immich-lifecycle": {
|
|
||||||
"deprecatedAt": "v1.107.0"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/server-info/features": {
|
"/server/features": {
|
||||||
"get": {
|
"get": {
|
||||||
"deprecated": true,
|
|
||||||
"description": "This property was deprecated in v1.107.0",
|
|
||||||
"operationId": "getServerFeatures",
|
"operationId": "getServerFeatures",
|
||||||
"parameters": [],
|
"parameters": [],
|
||||||
"responses": {
|
"responses": {
|
||||||
@ -4855,196 +4841,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tags": [
|
"tags": [
|
||||||
"Server Info",
|
"Server"
|
||||||
"Deprecated"
|
]
|
||||||
],
|
|
||||||
"x-immich-lifecycle": {
|
|
||||||
"deprecatedAt": "v1.107.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/server-info/media-types": {
|
|
||||||
"get": {
|
|
||||||
"deprecated": true,
|
|
||||||
"description": "This property was deprecated in v1.107.0",
|
|
||||||
"operationId": "getSupportedMediaTypes",
|
|
||||||
"parameters": [],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/ServerMediaTypesResponseDto"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"description": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tags": [
|
|
||||||
"Server Info",
|
|
||||||
"Deprecated"
|
|
||||||
],
|
|
||||||
"x-immich-lifecycle": {
|
|
||||||
"deprecatedAt": "v1.107.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/server-info/ping": {
|
|
||||||
"get": {
|
|
||||||
"deprecated": true,
|
|
||||||
"description": "This property was deprecated in v1.107.0",
|
|
||||||
"operationId": "pingServer",
|
|
||||||
"parameters": [],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/ServerPingResponse"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"description": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tags": [
|
|
||||||
"Server Info",
|
|
||||||
"Deprecated"
|
|
||||||
],
|
|
||||||
"x-immich-lifecycle": {
|
|
||||||
"deprecatedAt": "v1.107.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/server-info/statistics": {
|
|
||||||
"get": {
|
|
||||||
"deprecated": true,
|
|
||||||
"description": "This property was deprecated in v1.107.0",
|
|
||||||
"operationId": "getServerStatistics",
|
|
||||||
"parameters": [],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/ServerStatsResponseDto"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"description": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"bearer": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cookie": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"api_key": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"Server Info",
|
|
||||||
"Deprecated"
|
|
||||||
],
|
|
||||||
"x-immich-lifecycle": {
|
|
||||||
"deprecatedAt": "v1.107.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/server-info/storage": {
|
|
||||||
"get": {
|
|
||||||
"deprecated": true,
|
|
||||||
"description": "This property was deprecated in v1.107.0",
|
|
||||||
"operationId": "getStorage",
|
|
||||||
"parameters": [],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/ServerStorageResponseDto"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"description": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"bearer": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cookie": []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"api_key": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"Server Info",
|
|
||||||
"Deprecated"
|
|
||||||
],
|
|
||||||
"x-immich-lifecycle": {
|
|
||||||
"deprecatedAt": "v1.107.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/server-info/theme": {
|
|
||||||
"get": {
|
|
||||||
"deprecated": true,
|
|
||||||
"description": "This property was deprecated in v1.107.0",
|
|
||||||
"operationId": "getTheme",
|
|
||||||
"parameters": [],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/ServerThemeDto"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"description": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tags": [
|
|
||||||
"Server Info",
|
|
||||||
"Deprecated"
|
|
||||||
],
|
|
||||||
"x-immich-lifecycle": {
|
|
||||||
"deprecatedAt": "v1.107.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/server-info/version": {
|
|
||||||
"get": {
|
|
||||||
"deprecated": true,
|
|
||||||
"description": "This property was deprecated in v1.107.0",
|
|
||||||
"operationId": "getServerVersion",
|
|
||||||
"parameters": [],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/ServerVersionResponseDto"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"description": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tags": [
|
|
||||||
"Server Info",
|
|
||||||
"Deprecated"
|
|
||||||
],
|
|
||||||
"x-immich-lifecycle": {
|
|
||||||
"deprecatedAt": "v1.107.0"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/server/license": {
|
"/server/license": {
|
||||||
@ -5145,6 +4943,154 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/server/media-types": {
|
||||||
|
"get": {
|
||||||
|
"operationId": "getSupportedMediaTypes",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ServerMediaTypesResponseDto"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
"Server"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/server/ping": {
|
||||||
|
"get": {
|
||||||
|
"operationId": "pingServer",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ServerPingResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
"Server"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/server/statistics": {
|
||||||
|
"get": {
|
||||||
|
"operationId": "getServerStatistics",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ServerStatsResponseDto"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"bearer": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cookie": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"api_key": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Server"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/server/storage": {
|
||||||
|
"get": {
|
||||||
|
"operationId": "getStorage",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ServerStorageResponseDto"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"bearer": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cookie": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"api_key": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Server"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/server/theme": {
|
||||||
|
"get": {
|
||||||
|
"operationId": "getTheme",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ServerThemeDto"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
"Server"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/server/version": {
|
||||||
|
"get": {
|
||||||
|
"operationId": "getServerVersion",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ServerVersionResponseDto"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
"Server"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"/sessions": {
|
"/sessions": {
|
||||||
"delete": {
|
"delete": {
|
||||||
"operationId": "deleteAllSessions",
|
"operationId": "deleteAllSessions",
|
||||||
@ -7505,25 +7451,6 @@
|
|||||||
],
|
],
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
"AlbumCountResponseDto": {
|
|
||||||
"properties": {
|
|
||||||
"notShared": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"owned": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"shared": {
|
|
||||||
"type": "integer"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"notShared",
|
|
||||||
"owned",
|
|
||||||
"shared"
|
|
||||||
],
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"AlbumResponseDto": {
|
"AlbumResponseDto": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"albumName": {
|
"albumName": {
|
||||||
@ -7611,6 +7538,25 @@
|
|||||||
],
|
],
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
|
"AlbumStatisticsResponseDto": {
|
||||||
|
"properties": {
|
||||||
|
"notShared": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"owned": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"shared": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"notShared",
|
||||||
|
"owned",
|
||||||
|
"shared"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
"AlbumUserAddDto": {
|
"AlbumUserAddDto": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"role": {
|
"role": {
|
||||||
@ -9972,7 +9918,6 @@
|
|||||||
"asset.read",
|
"asset.read",
|
||||||
"asset.update",
|
"asset.update",
|
||||||
"asset.delete",
|
"asset.delete",
|
||||||
"asset.restore",
|
|
||||||
"asset.share",
|
"asset.share",
|
||||||
"asset.view",
|
"asset.view",
|
||||||
"asset.download",
|
"asset.download",
|
||||||
@ -10014,6 +9959,9 @@
|
|||||||
"person.statistics",
|
"person.statistics",
|
||||||
"person.merge",
|
"person.merge",
|
||||||
"person.reassign",
|
"person.reassign",
|
||||||
|
"session.read",
|
||||||
|
"session.update",
|
||||||
|
"session.delete",
|
||||||
"sharedLink.create",
|
"sharedLink.create",
|
||||||
"sharedLink.read",
|
"sharedLink.read",
|
||||||
"sharedLink.update",
|
"sharedLink.update",
|
||||||
|
@ -268,7 +268,7 @@ export type CreateAlbumDto = {
|
|||||||
assetIds?: string[];
|
assetIds?: string[];
|
||||||
description?: string;
|
description?: string;
|
||||||
};
|
};
|
||||||
export type AlbumCountResponseDto = {
|
export type AlbumStatisticsResponseDto = {
|
||||||
notShared: number;
|
notShared: number;
|
||||||
owned: number;
|
owned: number;
|
||||||
shared: number;
|
shared: number;
|
||||||
@ -869,6 +869,15 @@ export type ServerFeaturesDto = {
|
|||||||
smartSearch: boolean;
|
smartSearch: boolean;
|
||||||
trash: boolean;
|
trash: boolean;
|
||||||
};
|
};
|
||||||
|
export type LicenseResponseDto = {
|
||||||
|
activatedAt: string;
|
||||||
|
activationKey: string;
|
||||||
|
licenseKey: string;
|
||||||
|
};
|
||||||
|
export type LicenseKeyDto = {
|
||||||
|
activationKey: string;
|
||||||
|
licenseKey: string;
|
||||||
|
};
|
||||||
export type ServerMediaTypesResponseDto = {
|
export type ServerMediaTypesResponseDto = {
|
||||||
image: string[];
|
image: string[];
|
||||||
sidecar: string[];
|
sidecar: string[];
|
||||||
@ -909,15 +918,6 @@ export type ServerVersionResponseDto = {
|
|||||||
minor: number;
|
minor: number;
|
||||||
patch: number;
|
patch: number;
|
||||||
};
|
};
|
||||||
export type LicenseResponseDto = {
|
|
||||||
activatedAt: string;
|
|
||||||
activationKey: string;
|
|
||||||
licenseKey: string;
|
|
||||||
};
|
|
||||||
export type LicenseKeyDto = {
|
|
||||||
activationKey: string;
|
|
||||||
licenseKey: string;
|
|
||||||
};
|
|
||||||
export type SessionResponseDto = {
|
export type SessionResponseDto = {
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
current: boolean;
|
current: boolean;
|
||||||
@ -1369,11 +1369,11 @@ export function createAlbum({ createAlbumDto }: {
|
|||||||
body: createAlbumDto
|
body: createAlbumDto
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
export function getAlbumCount(opts?: Oazapfts.RequestOpts) {
|
export function getAlbumStatistics(opts?: Oazapfts.RequestOpts) {
|
||||||
return oazapfts.ok(oazapfts.fetchJson<{
|
return oazapfts.ok(oazapfts.fetchJson<{
|
||||||
status: 200;
|
status: 200;
|
||||||
data: AlbumCountResponseDto;
|
data: AlbumStatisticsResponseDto;
|
||||||
}>("/albums/count", {
|
}>("/albums/statistics", {
|
||||||
...opts
|
...opts
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -2168,7 +2168,7 @@ export function redirectOAuthToMobile(opts?: Oazapfts.RequestOpts) {
|
|||||||
}
|
}
|
||||||
export function unlinkOAuthAccount(opts?: Oazapfts.RequestOpts) {
|
export function unlinkOAuthAccount(opts?: Oazapfts.RequestOpts) {
|
||||||
return oazapfts.ok(oazapfts.fetchJson<{
|
return oazapfts.ok(oazapfts.fetchJson<{
|
||||||
status: 201;
|
status: 200;
|
||||||
data: UserAdminResponseDto;
|
data: UserAdminResponseDto;
|
||||||
}>("/oauth/unlink", {
|
}>("/oauth/unlink", {
|
||||||
...opts,
|
...opts,
|
||||||
@ -2458,102 +2458,27 @@ export function getSearchSuggestions({ country, includeNull, make, model, state,
|
|||||||
...opts
|
...opts
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* This property was deprecated in v1.107.0
|
|
||||||
*/
|
|
||||||
export function getAboutInfo(opts?: Oazapfts.RequestOpts) {
|
export function getAboutInfo(opts?: Oazapfts.RequestOpts) {
|
||||||
return oazapfts.ok(oazapfts.fetchJson<{
|
return oazapfts.ok(oazapfts.fetchJson<{
|
||||||
status: 200;
|
status: 200;
|
||||||
data: ServerAboutResponseDto;
|
data: ServerAboutResponseDto;
|
||||||
}>("/server-info/about", {
|
}>("/server/about", {
|
||||||
...opts
|
...opts
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* This property was deprecated in v1.107.0
|
|
||||||
*/
|
|
||||||
export function getServerConfig(opts?: Oazapfts.RequestOpts) {
|
export function getServerConfig(opts?: Oazapfts.RequestOpts) {
|
||||||
return oazapfts.ok(oazapfts.fetchJson<{
|
return oazapfts.ok(oazapfts.fetchJson<{
|
||||||
status: 200;
|
status: 200;
|
||||||
data: ServerConfigDto;
|
data: ServerConfigDto;
|
||||||
}>("/server-info/config", {
|
}>("/server/config", {
|
||||||
...opts
|
...opts
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* This property was deprecated in v1.107.0
|
|
||||||
*/
|
|
||||||
export function getServerFeatures(opts?: Oazapfts.RequestOpts) {
|
export function getServerFeatures(opts?: Oazapfts.RequestOpts) {
|
||||||
return oazapfts.ok(oazapfts.fetchJson<{
|
return oazapfts.ok(oazapfts.fetchJson<{
|
||||||
status: 200;
|
status: 200;
|
||||||
data: ServerFeaturesDto;
|
data: ServerFeaturesDto;
|
||||||
}>("/server-info/features", {
|
}>("/server/features", {
|
||||||
...opts
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* This property was deprecated in v1.107.0
|
|
||||||
*/
|
|
||||||
export function getSupportedMediaTypes(opts?: Oazapfts.RequestOpts) {
|
|
||||||
return oazapfts.ok(oazapfts.fetchJson<{
|
|
||||||
status: 200;
|
|
||||||
data: ServerMediaTypesResponseDto;
|
|
||||||
}>("/server-info/media-types", {
|
|
||||||
...opts
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* This property was deprecated in v1.107.0
|
|
||||||
*/
|
|
||||||
export function pingServer(opts?: Oazapfts.RequestOpts) {
|
|
||||||
return oazapfts.ok(oazapfts.fetchJson<{
|
|
||||||
status: 200;
|
|
||||||
data: ServerPingResponseRead;
|
|
||||||
}>("/server-info/ping", {
|
|
||||||
...opts
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* This property was deprecated in v1.107.0
|
|
||||||
*/
|
|
||||||
export function getServerStatistics(opts?: Oazapfts.RequestOpts) {
|
|
||||||
return oazapfts.ok(oazapfts.fetchJson<{
|
|
||||||
status: 200;
|
|
||||||
data: ServerStatsResponseDto;
|
|
||||||
}>("/server-info/statistics", {
|
|
||||||
...opts
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* This property was deprecated in v1.107.0
|
|
||||||
*/
|
|
||||||
export function getStorage(opts?: Oazapfts.RequestOpts) {
|
|
||||||
return oazapfts.ok(oazapfts.fetchJson<{
|
|
||||||
status: 200;
|
|
||||||
data: ServerStorageResponseDto;
|
|
||||||
}>("/server-info/storage", {
|
|
||||||
...opts
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* This property was deprecated in v1.107.0
|
|
||||||
*/
|
|
||||||
export function getTheme(opts?: Oazapfts.RequestOpts) {
|
|
||||||
return oazapfts.ok(oazapfts.fetchJson<{
|
|
||||||
status: 200;
|
|
||||||
data: ServerThemeDto;
|
|
||||||
}>("/server-info/theme", {
|
|
||||||
...opts
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* This property was deprecated in v1.107.0
|
|
||||||
*/
|
|
||||||
export function getServerVersion(opts?: Oazapfts.RequestOpts) {
|
|
||||||
return oazapfts.ok(oazapfts.fetchJson<{
|
|
||||||
status: 200;
|
|
||||||
data: ServerVersionResponseDto;
|
|
||||||
}>("/server-info/version", {
|
|
||||||
...opts
|
...opts
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@ -2585,6 +2510,54 @@ export function setServerLicense({ licenseKeyDto }: {
|
|||||||
body: licenseKeyDto
|
body: licenseKeyDto
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
|
export function getSupportedMediaTypes(opts?: Oazapfts.RequestOpts) {
|
||||||
|
return oazapfts.ok(oazapfts.fetchJson<{
|
||||||
|
status: 200;
|
||||||
|
data: ServerMediaTypesResponseDto;
|
||||||
|
}>("/server/media-types", {
|
||||||
|
...opts
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
export function pingServer(opts?: Oazapfts.RequestOpts) {
|
||||||
|
return oazapfts.ok(oazapfts.fetchJson<{
|
||||||
|
status: 200;
|
||||||
|
data: ServerPingResponseRead;
|
||||||
|
}>("/server/ping", {
|
||||||
|
...opts
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
export function getServerStatistics(opts?: Oazapfts.RequestOpts) {
|
||||||
|
return oazapfts.ok(oazapfts.fetchJson<{
|
||||||
|
status: 200;
|
||||||
|
data: ServerStatsResponseDto;
|
||||||
|
}>("/server/statistics", {
|
||||||
|
...opts
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
export function getStorage(opts?: Oazapfts.RequestOpts) {
|
||||||
|
return oazapfts.ok(oazapfts.fetchJson<{
|
||||||
|
status: 200;
|
||||||
|
data: ServerStorageResponseDto;
|
||||||
|
}>("/server/storage", {
|
||||||
|
...opts
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
export function getTheme(opts?: Oazapfts.RequestOpts) {
|
||||||
|
return oazapfts.ok(oazapfts.fetchJson<{
|
||||||
|
status: 200;
|
||||||
|
data: ServerThemeDto;
|
||||||
|
}>("/server/theme", {
|
||||||
|
...opts
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
export function getServerVersion(opts?: Oazapfts.RequestOpts) {
|
||||||
|
return oazapfts.ok(oazapfts.fetchJson<{
|
||||||
|
status: 200;
|
||||||
|
data: ServerVersionResponseDto;
|
||||||
|
}>("/server/version", {
|
||||||
|
...opts
|
||||||
|
}));
|
||||||
|
}
|
||||||
export function deleteAllSessions(opts?: Oazapfts.RequestOpts) {
|
export function deleteAllSessions(opts?: Oazapfts.RequestOpts) {
|
||||||
return oazapfts.ok(oazapfts.fetchText("/sessions", {
|
return oazapfts.ok(oazapfts.fetchText("/sessions", {
|
||||||
...opts,
|
...opts,
|
||||||
@ -3205,7 +3178,6 @@ export enum Permission {
|
|||||||
AssetRead = "asset.read",
|
AssetRead = "asset.read",
|
||||||
AssetUpdate = "asset.update",
|
AssetUpdate = "asset.update",
|
||||||
AssetDelete = "asset.delete",
|
AssetDelete = "asset.delete",
|
||||||
AssetRestore = "asset.restore",
|
|
||||||
AssetShare = "asset.share",
|
AssetShare = "asset.share",
|
||||||
AssetView = "asset.view",
|
AssetView = "asset.view",
|
||||||
AssetDownload = "asset.download",
|
AssetDownload = "asset.download",
|
||||||
@ -3247,6 +3219,9 @@ export enum Permission {
|
|||||||
PersonStatistics = "person.statistics",
|
PersonStatistics = "person.statistics",
|
||||||
PersonMerge = "person.merge",
|
PersonMerge = "person.merge",
|
||||||
PersonReassign = "person.reassign",
|
PersonReassign = "person.reassign",
|
||||||
|
SessionRead = "session.read",
|
||||||
|
SessionUpdate = "session.update",
|
||||||
|
SessionDelete = "session.delete",
|
||||||
SharedLinkCreate = "sharedLink.create",
|
SharedLinkCreate = "sharedLink.create",
|
||||||
SharedLinkRead = "sharedLink.read",
|
SharedLinkRead = "sharedLink.read",
|
||||||
SharedLinkUpdate = "sharedLink.update",
|
SharedLinkUpdate = "sharedLink.update",
|
||||||
|
@ -79,7 +79,11 @@
|
|||||||
"schedule": "on tuesday"
|
"schedule": "on tuesday"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"ignorePaths": ["mobile/openapi/pubspec.yaml", "mobile/ios", "mobile/android"],
|
"ignorePaths": [
|
||||||
|
"mobile/openapi/pubspec.yaml",
|
||||||
|
"mobile/ios",
|
||||||
|
"mobile/android"
|
||||||
|
],
|
||||||
"ignoreDeps": ["http", "intl"],
|
"ignoreDeps": ["http", "intl"],
|
||||||
"labels": ["dependencies"]
|
"labels": ["dependencies", "changelog:skip"]
|
||||||
}
|
}
|
||||||
|
@ -25,12 +25,6 @@ export class ActivityController {
|
|||||||
return this.service.getAll(auth, dto);
|
return this.service.getAll(auth, dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('statistics')
|
|
||||||
@Authenticated({ permission: Permission.ACTIVITY_STATISTICS })
|
|
||||||
getActivityStatistics(@Auth() auth: AuthDto, @Query() dto: ActivityDto): Promise<ActivityStatisticsResponseDto> {
|
|
||||||
return this.service.getStatistics(auth, dto);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
@Authenticated({ permission: Permission.ACTIVITY_CREATE })
|
@Authenticated({ permission: Permission.ACTIVITY_CREATE })
|
||||||
async createActivity(
|
async createActivity(
|
||||||
@ -45,6 +39,12 @@ export class ActivityController {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get('statistics')
|
||||||
|
@Authenticated({ permission: Permission.ACTIVITY_STATISTICS })
|
||||||
|
getActivityStatistics(@Auth() auth: AuthDto, @Query() dto: ActivityDto): Promise<ActivityStatisticsResponseDto> {
|
||||||
|
return this.service.getStatistics(auth, dto);
|
||||||
|
}
|
||||||
|
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
@HttpCode(HttpStatus.NO_CONTENT)
|
@HttpCode(HttpStatus.NO_CONTENT)
|
||||||
@Authenticated({ permission: Permission.ACTIVITY_DELETE })
|
@Authenticated({ permission: Permission.ACTIVITY_DELETE })
|
||||||
|
@ -2,9 +2,9 @@ import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query } from '@
|
|||||||
import { ApiTags } from '@nestjs/swagger';
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
import {
|
import {
|
||||||
AddUsersDto,
|
AddUsersDto,
|
||||||
AlbumCountResponseDto,
|
|
||||||
AlbumInfoDto,
|
AlbumInfoDto,
|
||||||
AlbumResponseDto,
|
AlbumResponseDto,
|
||||||
|
AlbumStatisticsResponseDto,
|
||||||
CreateAlbumDto,
|
CreateAlbumDto,
|
||||||
GetAlbumsDto,
|
GetAlbumsDto,
|
||||||
UpdateAlbumDto,
|
UpdateAlbumDto,
|
||||||
@ -22,12 +22,6 @@ import { ParseMeUUIDPipe, UUIDParamDto } from 'src/validation';
|
|||||||
export class AlbumController {
|
export class AlbumController {
|
||||||
constructor(private service: AlbumService) {}
|
constructor(private service: AlbumService) {}
|
||||||
|
|
||||||
@Get('count')
|
|
||||||
@Authenticated({ permission: Permission.ALBUM_STATISTICS })
|
|
||||||
getAlbumCount(@Auth() auth: AuthDto): Promise<AlbumCountResponseDto> {
|
|
||||||
return this.service.getCount(auth);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@Authenticated({ permission: Permission.ALBUM_READ })
|
@Authenticated({ permission: Permission.ALBUM_READ })
|
||||||
getAllAlbums(@Auth() auth: AuthDto, @Query() query: GetAlbumsDto): Promise<AlbumResponseDto[]> {
|
getAllAlbums(@Auth() auth: AuthDto, @Query() query: GetAlbumsDto): Promise<AlbumResponseDto[]> {
|
||||||
@ -40,6 +34,12 @@ export class AlbumController {
|
|||||||
return this.service.create(auth, dto);
|
return this.service.create(auth, dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get('statistics')
|
||||||
|
@Authenticated({ permission: Permission.ALBUM_STATISTICS })
|
||||||
|
getAlbumStatistics(@Auth() auth: AuthDto): Promise<AlbumStatisticsResponseDto> {
|
||||||
|
return this.service.getStatistics(auth);
|
||||||
|
}
|
||||||
|
|
||||||
@Authenticated({ permission: Permission.ALBUM_READ, sharedLink: true })
|
@Authenticated({ permission: Permission.ALBUM_READ, sharedLink: true })
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
getAlbumInfo(
|
getAlbumInfo(
|
||||||
|
@ -93,8 +93,8 @@ export class AssetMediaController {
|
|||||||
@Put(':id/original')
|
@Put(':id/original')
|
||||||
@UseInterceptors(FileUploadInterceptor)
|
@UseInterceptors(FileUploadInterceptor)
|
||||||
@ApiConsumes('multipart/form-data')
|
@ApiConsumes('multipart/form-data')
|
||||||
@Authenticated({ sharedLink: true })
|
|
||||||
@EndpointLifecycle({ addedAt: 'v1.106.0' })
|
@EndpointLifecycle({ addedAt: 'v1.106.0' })
|
||||||
|
@Authenticated({ sharedLink: true })
|
||||||
async replaceAsset(
|
async replaceAsset(
|
||||||
@Auth() auth: AuthDto,
|
@Auth() auth: AuthDto,
|
||||||
@Param() { id }: UUIDParamDto,
|
@Param() { id }: UUIDParamDto,
|
||||||
|
@ -51,8 +51,8 @@ export class AssetController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post('jobs')
|
@Post('jobs')
|
||||||
@HttpCode(HttpStatus.NO_CONTENT)
|
|
||||||
@Authenticated()
|
@Authenticated()
|
||||||
|
@HttpCode(HttpStatus.NO_CONTENT)
|
||||||
runAssetJobs(@Auth() auth: AuthDto, @Body() dto: AssetJobsDto): Promise<void> {
|
runAssetJobs(@Auth() auth: AuthDto, @Body() dto: AssetJobsDto): Promise<void> {
|
||||||
return this.service.run(auth, dto);
|
return this.service.run(auth, dto);
|
||||||
}
|
}
|
||||||
|
@ -31,18 +31,18 @@ export class LibraryController {
|
|||||||
return this.service.create(dto);
|
return this.service.create(dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Put(':id')
|
|
||||||
@Authenticated({ permission: Permission.LIBRARY_UPDATE, admin: true })
|
|
||||||
updateLibrary(@Param() { id }: UUIDParamDto, @Body() dto: UpdateLibraryDto): Promise<LibraryResponseDto> {
|
|
||||||
return this.service.update(id, dto);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
@Authenticated({ permission: Permission.LIBRARY_READ, admin: true })
|
@Authenticated({ permission: Permission.LIBRARY_READ, admin: true })
|
||||||
getLibrary(@Param() { id }: UUIDParamDto): Promise<LibraryResponseDto> {
|
getLibrary(@Param() { id }: UUIDParamDto): Promise<LibraryResponseDto> {
|
||||||
return this.service.get(id);
|
return this.service.get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Put(':id')
|
||||||
|
@Authenticated({ permission: Permission.LIBRARY_UPDATE, admin: true })
|
||||||
|
updateLibrary(@Param() { id }: UUIDParamDto, @Body() dto: UpdateLibraryDto): Promise<LibraryResponseDto> {
|
||||||
|
return this.service.update(id, dto);
|
||||||
|
}
|
||||||
|
|
||||||
@Post(':id/validate')
|
@Post(':id/validate')
|
||||||
@HttpCode(200)
|
@HttpCode(200)
|
||||||
@Authenticated({ admin: true })
|
@Authenticated({ admin: true })
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Body, Controller, HttpCode, Post } from '@nestjs/common';
|
import { Body, Controller, HttpCode, HttpStatus, Post } from '@nestjs/common';
|
||||||
import { ApiTags } from '@nestjs/swagger';
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { SystemConfigSmtpDto } from 'src/dtos/system-config.dto';
|
import { SystemConfigSmtpDto } from 'src/dtos/system-config.dto';
|
||||||
@ -11,7 +11,7 @@ export class NotificationController {
|
|||||||
constructor(private service: NotificationService) {}
|
constructor(private service: NotificationService) {}
|
||||||
|
|
||||||
@Post('test-email')
|
@Post('test-email')
|
||||||
@HttpCode(200)
|
@HttpCode(HttpStatus.OK)
|
||||||
@Authenticated({ admin: true })
|
@Authenticated({ admin: true })
|
||||||
sendTestEmail(@Auth() auth: AuthDto, @Body() dto: SystemConfigSmtpDto) {
|
sendTestEmail(@Auth() auth: AuthDto, @Body() dto: SystemConfigSmtpDto) {
|
||||||
return this.service.sendTestEmail(auth.user.id, dto);
|
return this.service.sendTestEmail(auth.user.id, dto);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Body, Controller, Get, HttpStatus, Post, Redirect, Req, Res } from '@nestjs/common';
|
import { Body, Controller, Get, HttpCode, HttpStatus, Post, Redirect, Req, Res } from '@nestjs/common';
|
||||||
import { ApiTags } from '@nestjs/swagger';
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
import { Request, Response } from 'express';
|
import { Request, Response } from 'express';
|
||||||
import { AuthType } from 'src/constants';
|
import { AuthType } from 'src/constants';
|
||||||
@ -58,6 +58,7 @@ export class OAuthController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post('unlink')
|
@Post('unlink')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
@Authenticated()
|
@Authenticated()
|
||||||
unlinkOAuthAccount(@Auth() auth: AuthDto): Promise<UserAdminResponseDto> {
|
unlinkOAuthAccount(@Auth() auth: AuthDto): Promise<UserAdminResponseDto> {
|
||||||
return this.service.unlink(auth);
|
return this.service.unlink(auth);
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
import { Body, Controller, Delete, Get, Param, Post, Put, Query } from '@nestjs/common';
|
import { Body, Controller, Delete, Get, Param, Post, Put, Query } from '@nestjs/common';
|
||||||
import { ApiQuery, ApiTags } from '@nestjs/swagger';
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { PartnerResponseDto, PartnerSearchDto, UpdatePartnerDto } from 'src/dtos/partner.dto';
|
import { PartnerResponseDto, PartnerSearchDto, UpdatePartnerDto } from 'src/dtos/partner.dto';
|
||||||
import { Permission } from 'src/enum';
|
import { Permission } from 'src/enum';
|
||||||
import { PartnerDirection } from 'src/interfaces/partner.interface';
|
|
||||||
import { Auth, Authenticated } from 'src/middleware/auth.guard';
|
import { Auth, Authenticated } from 'src/middleware/auth.guard';
|
||||||
import { PartnerService } from 'src/services/partner.service';
|
import { PartnerService } from 'src/services/partner.service';
|
||||||
import { UUIDParamDto } from 'src/validation';
|
import { UUIDParamDto } from 'src/validation';
|
||||||
@ -14,9 +13,7 @@ export class PartnerController {
|
|||||||
constructor(private service: PartnerService) {}
|
constructor(private service: PartnerService) {}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@ApiQuery({ name: 'direction', type: 'string', enum: PartnerDirection, required: true })
|
|
||||||
@Authenticated({ permission: Permission.PARTNER_READ })
|
@Authenticated({ permission: Permission.PARTNER_READ })
|
||||||
// TODO: remove 'direction' and convert to full query dto
|
|
||||||
getPartners(@Auth() auth: AuthDto, @Query() dto: PartnerSearchDto): Promise<PartnerResponseDto[]> {
|
getPartners(@Auth() auth: AuthDto, @Query() dto: PartnerSearchDto): Promise<PartnerResponseDto[]> {
|
||||||
return this.service.search(auth, dto);
|
return this.service.search(auth, dto);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Controller, Get } from '@nestjs/common';
|
import { Controller, Get } from '@nestjs/common';
|
||||||
import { ApiTags } from '@nestjs/swagger';
|
import { ApiExcludeController, ApiTags } from '@nestjs/swagger';
|
||||||
import { EndpointLifecycle } from 'src/decorators';
|
import { EndpointLifecycle } from 'src/decorators';
|
||||||
import {
|
import {
|
||||||
ServerAboutResponseDto,
|
ServerAboutResponseDto,
|
||||||
@ -16,6 +16,7 @@ import { Authenticated } from 'src/middleware/auth.guard';
|
|||||||
import { ServerService } from 'src/services/server.service';
|
import { ServerService } from 'src/services/server.service';
|
||||||
import { VersionService } from 'src/services/version.service';
|
import { VersionService } from 'src/services/version.service';
|
||||||
|
|
||||||
|
@ApiExcludeController()
|
||||||
@ApiTags('Server Info')
|
@ApiTags('Server Info')
|
||||||
@Controller('server-info')
|
@Controller('server-info')
|
||||||
export class ServerInfoController {
|
export class ServerInfoController {
|
||||||
@ -68,9 +69,9 @@ export class ServerInfoController {
|
|||||||
return this.service.getConfig();
|
return this.service.getConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Authenticated({ admin: true })
|
|
||||||
@EndpointLifecycle({ deprecatedAt: 'v1.107.0' })
|
|
||||||
@Get('statistics')
|
@Get('statistics')
|
||||||
|
@EndpointLifecycle({ deprecatedAt: 'v1.107.0' })
|
||||||
|
@Authenticated({ admin: true })
|
||||||
getServerStatistics(): Promise<ServerStatsResponseDto> {
|
getServerStatistics(): Promise<ServerStatsResponseDto> {
|
||||||
return this.service.getStatistics();
|
return this.service.getStatistics();
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Body, Controller, Delete, Get, Put } from '@nestjs/common';
|
import { Body, Controller, Delete, Get, Put } from '@nestjs/common';
|
||||||
import { ApiExcludeEndpoint, ApiNotFoundResponse, ApiTags } from '@nestjs/swagger';
|
import { ApiNotFoundResponse, ApiTags } from '@nestjs/swagger';
|
||||||
import { LicenseKeyDto, LicenseResponseDto } from 'src/dtos/license.dto';
|
import { LicenseKeyDto, LicenseResponseDto } from 'src/dtos/license.dto';
|
||||||
import {
|
import {
|
||||||
ServerAboutResponseDto,
|
ServerAboutResponseDto,
|
||||||
@ -26,57 +26,48 @@ export class ServerController {
|
|||||||
|
|
||||||
@Get('about')
|
@Get('about')
|
||||||
@Authenticated()
|
@Authenticated()
|
||||||
@ApiExcludeEndpoint()
|
|
||||||
getAboutInfo(): Promise<ServerAboutResponseDto> {
|
getAboutInfo(): Promise<ServerAboutResponseDto> {
|
||||||
return this.service.getAboutInfo();
|
return this.service.getAboutInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('storage')
|
@Get('storage')
|
||||||
@Authenticated()
|
@Authenticated()
|
||||||
@ApiExcludeEndpoint()
|
|
||||||
getStorage(): Promise<ServerStorageResponseDto> {
|
getStorage(): Promise<ServerStorageResponseDto> {
|
||||||
return this.service.getStorage();
|
return this.service.getStorage();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('ping')
|
@Get('ping')
|
||||||
@ApiExcludeEndpoint()
|
|
||||||
pingServer(): ServerPingResponse {
|
pingServer(): ServerPingResponse {
|
||||||
return this.service.ping();
|
return this.service.ping();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('version')
|
@Get('version')
|
||||||
@ApiExcludeEndpoint()
|
|
||||||
getServerVersion(): ServerVersionResponseDto {
|
getServerVersion(): ServerVersionResponseDto {
|
||||||
return this.versionService.getVersion();
|
return this.versionService.getVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('features')
|
@Get('features')
|
||||||
@ApiExcludeEndpoint()
|
|
||||||
getServerFeatures(): Promise<ServerFeaturesDto> {
|
getServerFeatures(): Promise<ServerFeaturesDto> {
|
||||||
return this.service.getFeatures();
|
return this.service.getFeatures();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('theme')
|
@Get('theme')
|
||||||
@ApiExcludeEndpoint()
|
|
||||||
getTheme(): Promise<ServerThemeDto> {
|
getTheme(): Promise<ServerThemeDto> {
|
||||||
return this.service.getTheme();
|
return this.service.getTheme();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('config')
|
@Get('config')
|
||||||
@ApiExcludeEndpoint()
|
|
||||||
getServerConfig(): Promise<ServerConfigDto> {
|
getServerConfig(): Promise<ServerConfigDto> {
|
||||||
return this.service.getConfig();
|
return this.service.getConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Authenticated({ admin: true })
|
|
||||||
@Get('statistics')
|
@Get('statistics')
|
||||||
@ApiExcludeEndpoint()
|
@Authenticated({ admin: true })
|
||||||
getServerStatistics(): Promise<ServerStatsResponseDto> {
|
getServerStatistics(): Promise<ServerStatsResponseDto> {
|
||||||
return this.service.getStatistics();
|
return this.service.getStatistics();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('media-types')
|
@Get('media-types')
|
||||||
@ApiExcludeEndpoint()
|
|
||||||
getSupportedMediaTypes(): ServerMediaTypesResponseDto {
|
getSupportedMediaTypes(): ServerMediaTypesResponseDto {
|
||||||
return this.service.getSupportedMediaTypes();
|
return this.service.getSupportedMediaTypes();
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import { Controller, Delete, Get, HttpCode, HttpStatus, Param } from '@nestjs/co
|
|||||||
import { ApiTags } from '@nestjs/swagger';
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { SessionResponseDto } from 'src/dtos/session.dto';
|
import { SessionResponseDto } from 'src/dtos/session.dto';
|
||||||
|
import { Permission } from 'src/enum';
|
||||||
import { Auth, Authenticated } from 'src/middleware/auth.guard';
|
import { Auth, Authenticated } from 'src/middleware/auth.guard';
|
||||||
import { SessionService } from 'src/services/session.service';
|
import { SessionService } from 'src/services/session.service';
|
||||||
import { UUIDParamDto } from 'src/validation';
|
import { UUIDParamDto } from 'src/validation';
|
||||||
@ -12,21 +13,21 @@ export class SessionController {
|
|||||||
constructor(private service: SessionService) {}
|
constructor(private service: SessionService) {}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@Authenticated()
|
@Authenticated({ permission: Permission.SESSION_READ })
|
||||||
getSessions(@Auth() auth: AuthDto): Promise<SessionResponseDto[]> {
|
getSessions(@Auth() auth: AuthDto): Promise<SessionResponseDto[]> {
|
||||||
return this.service.getAll(auth);
|
return this.service.getAll(auth);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete()
|
@Delete()
|
||||||
|
@Authenticated({ permission: Permission.SESSION_DELETE })
|
||||||
@HttpCode(HttpStatus.NO_CONTENT)
|
@HttpCode(HttpStatus.NO_CONTENT)
|
||||||
@Authenticated()
|
|
||||||
deleteAllSessions(@Auth() auth: AuthDto): Promise<void> {
|
deleteAllSessions(@Auth() auth: AuthDto): Promise<void> {
|
||||||
return this.service.deleteAll(auth);
|
return this.service.deleteAll(auth);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
|
@Authenticated({ permission: Permission.SESSION_DELETE })
|
||||||
@HttpCode(HttpStatus.NO_CONTENT)
|
@HttpCode(HttpStatus.NO_CONTENT)
|
||||||
@Authenticated()
|
|
||||||
deleteSession(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<void> {
|
deleteSession(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<void> {
|
||||||
return this.service.delete(auth, id);
|
return this.service.delete(auth, id);
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,8 @@ export class StackController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Delete()
|
@Delete()
|
||||||
@HttpCode(HttpStatus.NO_CONTENT)
|
|
||||||
@Authenticated({ permission: Permission.STACK_DELETE })
|
@Authenticated({ permission: Permission.STACK_DELETE })
|
||||||
|
@HttpCode(HttpStatus.NO_CONTENT)
|
||||||
deleteStacks(@Auth() auth: AuthDto, @Body() dto: BulkIdsDto): Promise<void> {
|
deleteStacks(@Auth() auth: AuthDto, @Body() dto: BulkIdsDto): Promise<void> {
|
||||||
return this.service.deleteAll(auth, dto);
|
return this.service.deleteAll(auth, dto);
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import { ApiTags } from '@nestjs/swagger';
|
|||||||
import { AssetResponseDto } from 'src/dtos/asset-response.dto';
|
import { AssetResponseDto } from 'src/dtos/asset-response.dto';
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { TimeBucketAssetDto, TimeBucketDto, TimeBucketResponseDto } from 'src/dtos/time-bucket.dto';
|
import { TimeBucketAssetDto, TimeBucketDto, TimeBucketResponseDto } from 'src/dtos/time-bucket.dto';
|
||||||
|
import { Permission } from 'src/enum';
|
||||||
import { Auth, Authenticated } from 'src/middleware/auth.guard';
|
import { Auth, Authenticated } from 'src/middleware/auth.guard';
|
||||||
import { TimelineService } from 'src/services/timeline.service';
|
import { TimelineService } from 'src/services/timeline.service';
|
||||||
|
|
||||||
@ -11,14 +12,14 @@ import { TimelineService } from 'src/services/timeline.service';
|
|||||||
export class TimelineController {
|
export class TimelineController {
|
||||||
constructor(private service: TimelineService) {}
|
constructor(private service: TimelineService) {}
|
||||||
|
|
||||||
@Authenticated({ sharedLink: true })
|
|
||||||
@Get('buckets')
|
@Get('buckets')
|
||||||
|
@Authenticated({ permission: Permission.ASSET_READ, sharedLink: true })
|
||||||
getTimeBuckets(@Auth() auth: AuthDto, @Query() dto: TimeBucketDto): Promise<TimeBucketResponseDto[]> {
|
getTimeBuckets(@Auth() auth: AuthDto, @Query() dto: TimeBucketDto): Promise<TimeBucketResponseDto[]> {
|
||||||
return this.service.getTimeBuckets(auth, dto);
|
return this.service.getTimeBuckets(auth, dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Authenticated({ sharedLink: true })
|
|
||||||
@Get('bucket')
|
@Get('bucket')
|
||||||
|
@Authenticated({ permission: Permission.ASSET_READ, sharedLink: true })
|
||||||
getTimeBucket(@Auth() auth: AuthDto, @Query() dto: TimeBucketAssetDto): Promise<AssetResponseDto[]> {
|
getTimeBucket(@Auth() auth: AuthDto, @Query() dto: TimeBucketAssetDto): Promise<AssetResponseDto[]> {
|
||||||
return this.service.getTimeBucket(auth, dto) as Promise<AssetResponseDto[]>;
|
return this.service.getTimeBucket(auth, dto) as Promise<AssetResponseDto[]>;
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import { Body, Controller, HttpCode, HttpStatus, Post } from '@nestjs/common';
|
|||||||
import { ApiTags } from '@nestjs/swagger';
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
|
import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
|
import { Permission } from 'src/enum';
|
||||||
import { Auth, Authenticated } from 'src/middleware/auth.guard';
|
import { Auth, Authenticated } from 'src/middleware/auth.guard';
|
||||||
import { TrashService } from 'src/services/trash.service';
|
import { TrashService } from 'src/services/trash.service';
|
||||||
|
|
||||||
@ -12,21 +13,21 @@ export class TrashController {
|
|||||||
|
|
||||||
@Post('empty')
|
@Post('empty')
|
||||||
@HttpCode(HttpStatus.NO_CONTENT)
|
@HttpCode(HttpStatus.NO_CONTENT)
|
||||||
@Authenticated()
|
@Authenticated({ permission: Permission.ASSET_DELETE })
|
||||||
emptyTrash(@Auth() auth: AuthDto): Promise<void> {
|
emptyTrash(@Auth() auth: AuthDto): Promise<void> {
|
||||||
return this.service.empty(auth);
|
return this.service.empty(auth);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('restore')
|
@Post('restore')
|
||||||
@HttpCode(HttpStatus.NO_CONTENT)
|
@HttpCode(HttpStatus.NO_CONTENT)
|
||||||
@Authenticated()
|
@Authenticated({ permission: Permission.ASSET_DELETE })
|
||||||
restoreTrash(@Auth() auth: AuthDto): Promise<void> {
|
restoreTrash(@Auth() auth: AuthDto): Promise<void> {
|
||||||
return this.service.restore(auth);
|
return this.service.restore(auth);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('restore/assets')
|
@Post('restore/assets')
|
||||||
@HttpCode(HttpStatus.NO_CONTENT)
|
@HttpCode(HttpStatus.NO_CONTENT)
|
||||||
@Authenticated()
|
@Authenticated({ permission: Permission.ASSET_DELETE })
|
||||||
restoreAssets(@Auth() auth: AuthDto, @Body() dto: BulkIdsDto): Promise<void> {
|
restoreAssets(@Auth() auth: AuthDto, @Body() dto: BulkIdsDto): Promise<void> {
|
||||||
return this.service.restoreAssets(auth, dto);
|
return this.service.restoreAssets(auth, dto);
|
||||||
}
|
}
|
||||||
|
@ -1,312 +0,0 @@
|
|||||||
import { BadRequestException, UnauthorizedException } from '@nestjs/common';
|
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
|
||||||
import { SharedLinkEntity } from 'src/entities/shared-link.entity';
|
|
||||||
import { AlbumUserRole, Permission } from 'src/enum';
|
|
||||||
import { IAccessRepository } from 'src/interfaces/access.interface';
|
|
||||||
import { setDifference, setIsEqual, setUnion } from 'src/utils/set';
|
|
||||||
|
|
||||||
let instance: AccessCore | null;
|
|
||||||
|
|
||||||
export class AccessCore {
|
|
||||||
private constructor(private repository: IAccessRepository) {}
|
|
||||||
|
|
||||||
static create(repository: IAccessRepository) {
|
|
||||||
if (!instance) {
|
|
||||||
instance = new AccessCore(repository);
|
|
||||||
}
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
static reset() {
|
|
||||||
instance = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
requireUploadAccess(auth: AuthDto | null): AuthDto {
|
|
||||||
if (!auth || (auth.sharedLink && !auth.sharedLink.allowUpload)) {
|
|
||||||
throw new UnauthorizedException();
|
|
||||||
}
|
|
||||||
return auth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if user has access to all ids, for the given permission.
|
|
||||||
* Throws error if user does not have access to any of the ids.
|
|
||||||
*/
|
|
||||||
async requirePermission(auth: AuthDto, permission: Permission, ids: string[] | string) {
|
|
||||||
ids = Array.isArray(ids) ? ids : [ids];
|
|
||||||
const allowedIds = await this.checkAccess(auth, permission, ids);
|
|
||||||
if (!setIsEqual(new Set(ids), allowedIds)) {
|
|
||||||
throw new BadRequestException(`Not found or no ${permission} access`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return ids that user has access to, for the given permission.
|
|
||||||
* Check is done for each id, and only allowed ids are returned.
|
|
||||||
*
|
|
||||||
* @returns Set<string>
|
|
||||||
*/
|
|
||||||
async checkAccess(auth: AuthDto, permission: Permission, ids: Set<string> | string[]): Promise<Set<string>> {
|
|
||||||
const idSet = Array.isArray(ids) ? new Set(ids) : ids;
|
|
||||||
if (idSet.size === 0) {
|
|
||||||
return new Set();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auth.sharedLink) {
|
|
||||||
return this.checkAccessSharedLink(auth.sharedLink, permission, idSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.checkAccessOther(auth, permission, idSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async checkAccessSharedLink(
|
|
||||||
sharedLink: SharedLinkEntity,
|
|
||||||
permission: Permission,
|
|
||||||
ids: Set<string>,
|
|
||||||
): Promise<Set<string>> {
|
|
||||||
const sharedLinkId = sharedLink.id;
|
|
||||||
|
|
||||||
switch (permission) {
|
|
||||||
case Permission.ASSET_READ: {
|
|
||||||
return await this.repository.asset.checkSharedLinkAccess(sharedLinkId, ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.ASSET_VIEW: {
|
|
||||||
return await this.repository.asset.checkSharedLinkAccess(sharedLinkId, ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.ASSET_DOWNLOAD: {
|
|
||||||
return sharedLink.allowDownload
|
|
||||||
? await this.repository.asset.checkSharedLinkAccess(sharedLinkId, ids)
|
|
||||||
: new Set();
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.ASSET_UPLOAD: {
|
|
||||||
return sharedLink.allowUpload ? ids : new Set();
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.ASSET_SHARE: {
|
|
||||||
// TODO: fix this to not use sharedLink.userId for access control
|
|
||||||
return await this.repository.asset.checkOwnerAccess(sharedLink.userId, ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.ALBUM_READ: {
|
|
||||||
return await this.repository.album.checkSharedLinkAccess(sharedLinkId, ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.ALBUM_DOWNLOAD: {
|
|
||||||
return sharedLink.allowDownload
|
|
||||||
? await this.repository.album.checkSharedLinkAccess(sharedLinkId, ids)
|
|
||||||
: new Set();
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.ALBUM_ADD_ASSET: {
|
|
||||||
return sharedLink.allowUpload
|
|
||||||
? await this.repository.album.checkSharedLinkAccess(sharedLinkId, ids)
|
|
||||||
: new Set();
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
return new Set();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async checkAccessOther(auth: AuthDto, permission: Permission, ids: Set<string>): Promise<Set<string>> {
|
|
||||||
switch (permission) {
|
|
||||||
// uses album id
|
|
||||||
case Permission.ACTIVITY_CREATE: {
|
|
||||||
return await this.repository.activity.checkCreateAccess(auth.user.id, ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
// uses activity id
|
|
||||||
case Permission.ACTIVITY_DELETE: {
|
|
||||||
const isOwner = await this.repository.activity.checkOwnerAccess(auth.user.id, ids);
|
|
||||||
const isAlbumOwner = await this.repository.activity.checkAlbumOwnerAccess(
|
|
||||||
auth.user.id,
|
|
||||||
setDifference(ids, isOwner),
|
|
||||||
);
|
|
||||||
return setUnion(isOwner, isAlbumOwner);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.ASSET_READ: {
|
|
||||||
const isOwner = await this.repository.asset.checkOwnerAccess(auth.user.id, ids);
|
|
||||||
const isAlbum = await this.repository.asset.checkAlbumAccess(auth.user.id, setDifference(ids, isOwner));
|
|
||||||
const isPartner = await this.repository.asset.checkPartnerAccess(
|
|
||||||
auth.user.id,
|
|
||||||
setDifference(ids, isOwner, isAlbum),
|
|
||||||
);
|
|
||||||
return setUnion(isOwner, isAlbum, isPartner);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.ASSET_SHARE: {
|
|
||||||
const isOwner = await this.repository.asset.checkOwnerAccess(auth.user.id, ids);
|
|
||||||
const isPartner = await this.repository.asset.checkPartnerAccess(auth.user.id, setDifference(ids, isOwner));
|
|
||||||
return setUnion(isOwner, isPartner);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.ASSET_VIEW: {
|
|
||||||
const isOwner = await this.repository.asset.checkOwnerAccess(auth.user.id, ids);
|
|
||||||
const isAlbum = await this.repository.asset.checkAlbumAccess(auth.user.id, setDifference(ids, isOwner));
|
|
||||||
const isPartner = await this.repository.asset.checkPartnerAccess(
|
|
||||||
auth.user.id,
|
|
||||||
setDifference(ids, isOwner, isAlbum),
|
|
||||||
);
|
|
||||||
return setUnion(isOwner, isAlbum, isPartner);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.ASSET_DOWNLOAD: {
|
|
||||||
const isOwner = await this.repository.asset.checkOwnerAccess(auth.user.id, ids);
|
|
||||||
const isAlbum = await this.repository.asset.checkAlbumAccess(auth.user.id, setDifference(ids, isOwner));
|
|
||||||
const isPartner = await this.repository.asset.checkPartnerAccess(
|
|
||||||
auth.user.id,
|
|
||||||
setDifference(ids, isOwner, isAlbum),
|
|
||||||
);
|
|
||||||
return setUnion(isOwner, isAlbum, isPartner);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.ASSET_UPDATE: {
|
|
||||||
return await this.repository.asset.checkOwnerAccess(auth.user.id, ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.ASSET_DELETE: {
|
|
||||||
return await this.repository.asset.checkOwnerAccess(auth.user.id, ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.ASSET_RESTORE: {
|
|
||||||
return await this.repository.asset.checkOwnerAccess(auth.user.id, ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.ALBUM_READ: {
|
|
||||||
const isOwner = await this.repository.album.checkOwnerAccess(auth.user.id, ids);
|
|
||||||
const isShared = await this.repository.album.checkSharedAlbumAccess(
|
|
||||||
auth.user.id,
|
|
||||||
setDifference(ids, isOwner),
|
|
||||||
AlbumUserRole.VIEWER,
|
|
||||||
);
|
|
||||||
return setUnion(isOwner, isShared);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.ALBUM_ADD_ASSET: {
|
|
||||||
const isOwner = await this.repository.album.checkOwnerAccess(auth.user.id, ids);
|
|
||||||
const isShared = await this.repository.album.checkSharedAlbumAccess(
|
|
||||||
auth.user.id,
|
|
||||||
setDifference(ids, isOwner),
|
|
||||||
AlbumUserRole.EDITOR,
|
|
||||||
);
|
|
||||||
return setUnion(isOwner, isShared);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.ALBUM_UPDATE: {
|
|
||||||
return await this.repository.album.checkOwnerAccess(auth.user.id, ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.ALBUM_DELETE: {
|
|
||||||
return await this.repository.album.checkOwnerAccess(auth.user.id, ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.ALBUM_SHARE: {
|
|
||||||
return await this.repository.album.checkOwnerAccess(auth.user.id, ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.ALBUM_DOWNLOAD: {
|
|
||||||
const isOwner = await this.repository.album.checkOwnerAccess(auth.user.id, ids);
|
|
||||||
const isShared = await this.repository.album.checkSharedAlbumAccess(
|
|
||||||
auth.user.id,
|
|
||||||
setDifference(ids, isOwner),
|
|
||||||
AlbumUserRole.VIEWER,
|
|
||||||
);
|
|
||||||
return setUnion(isOwner, isShared);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.ALBUM_REMOVE_ASSET: {
|
|
||||||
const isOwner = await this.repository.album.checkOwnerAccess(auth.user.id, ids);
|
|
||||||
const isShared = await this.repository.album.checkSharedAlbumAccess(
|
|
||||||
auth.user.id,
|
|
||||||
setDifference(ids, isOwner),
|
|
||||||
AlbumUserRole.EDITOR,
|
|
||||||
);
|
|
||||||
return setUnion(isOwner, isShared);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.ASSET_UPLOAD: {
|
|
||||||
return ids.has(auth.user.id) ? new Set([auth.user.id]) : new Set<string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.ARCHIVE_READ: {
|
|
||||||
return ids.has(auth.user.id) ? new Set([auth.user.id]) : new Set();
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.AUTH_DEVICE_DELETE: {
|
|
||||||
return await this.repository.authDevice.checkOwnerAccess(auth.user.id, ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.TIMELINE_READ: {
|
|
||||||
const isOwner = ids.has(auth.user.id) ? new Set([auth.user.id]) : new Set<string>();
|
|
||||||
const isPartner = await this.repository.timeline.checkPartnerAccess(auth.user.id, setDifference(ids, isOwner));
|
|
||||||
return setUnion(isOwner, isPartner);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.TIMELINE_DOWNLOAD: {
|
|
||||||
return ids.has(auth.user.id) ? new Set([auth.user.id]) : new Set();
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.MEMORY_READ: {
|
|
||||||
return this.repository.memory.checkOwnerAccess(auth.user.id, ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.MEMORY_UPDATE: {
|
|
||||||
return this.repository.memory.checkOwnerAccess(auth.user.id, ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.MEMORY_DELETE: {
|
|
||||||
return this.repository.memory.checkOwnerAccess(auth.user.id, ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.MEMORY_DELETE: {
|
|
||||||
return this.repository.memory.checkOwnerAccess(auth.user.id, ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.PERSON_READ: {
|
|
||||||
return await this.repository.person.checkOwnerAccess(auth.user.id, ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.PERSON_UPDATE: {
|
|
||||||
return await this.repository.person.checkOwnerAccess(auth.user.id, ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.PERSON_MERGE: {
|
|
||||||
return await this.repository.person.checkOwnerAccess(auth.user.id, ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.PERSON_CREATE: {
|
|
||||||
return this.repository.person.checkFaceOwnerAccess(auth.user.id, ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.PERSON_REASSIGN: {
|
|
||||||
return this.repository.person.checkFaceOwnerAccess(auth.user.id, ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.PARTNER_UPDATE: {
|
|
||||||
return await this.repository.partner.checkUpdateAccess(auth.user.id, ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.STACK_READ: {
|
|
||||||
return this.repository.stack.checkOwnerAccess(auth.user.id, ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.STACK_UPDATE: {
|
|
||||||
return this.repository.stack.checkOwnerAccess(auth.user.id, ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
case Permission.STACK_DELETE: {
|
|
||||||
return this.repository.stack.checkOwnerAccess(auth.user.id, ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
return new Set();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -95,7 +95,7 @@ export class GetAlbumsDto {
|
|||||||
assetId?: string;
|
assetId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AlbumCountResponseDto {
|
export class AlbumStatisticsResponseDto {
|
||||||
@ApiProperty({ type: 'integer' })
|
@ApiProperty({ type: 'integer' })
|
||||||
owned!: number;
|
owned!: number;
|
||||||
|
|
||||||
|
@ -54,7 +54,6 @@ export enum Permission {
|
|||||||
ASSET_READ = 'asset.read',
|
ASSET_READ = 'asset.read',
|
||||||
ASSET_UPDATE = 'asset.update',
|
ASSET_UPDATE = 'asset.update',
|
||||||
ASSET_DELETE = 'asset.delete',
|
ASSET_DELETE = 'asset.delete',
|
||||||
ASSET_RESTORE = 'asset.restore',
|
|
||||||
ASSET_SHARE = 'asset.share',
|
ASSET_SHARE = 'asset.share',
|
||||||
ASSET_VIEW = 'asset.view',
|
ASSET_VIEW = 'asset.view',
|
||||||
ASSET_DOWNLOAD = 'asset.download',
|
ASSET_DOWNLOAD = 'asset.download',
|
||||||
@ -107,6 +106,10 @@ export enum Permission {
|
|||||||
PERSON_MERGE = 'person.merge',
|
PERSON_MERGE = 'person.merge',
|
||||||
PERSON_REASSIGN = 'person.reassign',
|
PERSON_REASSIGN = 'person.reassign',
|
||||||
|
|
||||||
|
SESSION_READ = 'session.read',
|
||||||
|
SESSION_UPDATE = 'session.update',
|
||||||
|
SESSION_DELETE = 'session.delete',
|
||||||
|
|
||||||
SHARED_LINK_CREATE = 'sharedLink.create',
|
SHARED_LINK_CREATE = 'sharedLink.create',
|
||||||
SHARED_LINK_READ = 'sharedLink.read',
|
SHARED_LINK_READ = 'sharedLink.read',
|
||||||
SHARED_LINK_UPDATE = 'sharedLink.update',
|
SHARED_LINK_UPDATE = 'sharedLink.update',
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { AccessCore } from 'src/cores/access.core';
|
|
||||||
import {
|
import {
|
||||||
ActivityCreateDto,
|
ActivityCreateDto,
|
||||||
ActivityDto,
|
ActivityDto,
|
||||||
@ -16,20 +15,17 @@ import { ActivityEntity } from 'src/entities/activity.entity';
|
|||||||
import { Permission } from 'src/enum';
|
import { Permission } from 'src/enum';
|
||||||
import { IAccessRepository } from 'src/interfaces/access.interface';
|
import { IAccessRepository } from 'src/interfaces/access.interface';
|
||||||
import { IActivityRepository } from 'src/interfaces/activity.interface';
|
import { IActivityRepository } from 'src/interfaces/activity.interface';
|
||||||
|
import { requireAccess } from 'src/utils/access';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ActivityService {
|
export class ActivityService {
|
||||||
private access: AccessCore;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(IAccessRepository) accessRepository: IAccessRepository,
|
@Inject(IAccessRepository) private access: IAccessRepository,
|
||||||
@Inject(IActivityRepository) private repository: IActivityRepository,
|
@Inject(IActivityRepository) private repository: IActivityRepository,
|
||||||
) {
|
) {}
|
||||||
this.access = AccessCore.create(accessRepository);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getAll(auth: AuthDto, dto: ActivitySearchDto): Promise<ActivityResponseDto[]> {
|
async getAll(auth: AuthDto, dto: ActivitySearchDto): Promise<ActivityResponseDto[]> {
|
||||||
await this.access.requirePermission(auth, Permission.ALBUM_READ, dto.albumId);
|
await requireAccess(this.access, { auth, permission: Permission.ALBUM_READ, ids: [dto.albumId] });
|
||||||
const activities = await this.repository.search({
|
const activities = await this.repository.search({
|
||||||
userId: dto.userId,
|
userId: dto.userId,
|
||||||
albumId: dto.albumId,
|
albumId: dto.albumId,
|
||||||
@ -41,12 +37,12 @@ export class ActivityService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getStatistics(auth: AuthDto, dto: ActivityDto): Promise<ActivityStatisticsResponseDto> {
|
async getStatistics(auth: AuthDto, dto: ActivityDto): Promise<ActivityStatisticsResponseDto> {
|
||||||
await this.access.requirePermission(auth, Permission.ALBUM_READ, dto.albumId);
|
await requireAccess(this.access, { auth, permission: Permission.ALBUM_READ, ids: [dto.albumId] });
|
||||||
return { comments: await this.repository.getStatistics(dto.assetId, dto.albumId) };
|
return { comments: await this.repository.getStatistics(dto.assetId, dto.albumId) };
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(auth: AuthDto, dto: ActivityCreateDto): Promise<MaybeDuplicate<ActivityResponseDto>> {
|
async create(auth: AuthDto, dto: ActivityCreateDto): Promise<MaybeDuplicate<ActivityResponseDto>> {
|
||||||
await this.access.requirePermission(auth, Permission.ACTIVITY_CREATE, dto.albumId);
|
await requireAccess(this.access, { auth, permission: Permission.ACTIVITY_CREATE, ids: [dto.albumId] });
|
||||||
|
|
||||||
const common = {
|
const common = {
|
||||||
userId: auth.user.id,
|
userId: auth.user.id,
|
||||||
@ -80,7 +76,7 @@ export class ActivityService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async delete(auth: AuthDto, id: string): Promise<void> {
|
async delete(auth: AuthDto, id: string): Promise<void> {
|
||||||
await this.access.requirePermission(auth, Permission.ACTIVITY_DELETE, id);
|
await requireAccess(this.access, { auth, permission: Permission.ACTIVITY_DELETE, ids: [id] });
|
||||||
await this.repository.delete(id);
|
await this.repository.delete(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,12 +43,12 @@ describe(AlbumService.name, () => {
|
|||||||
expect(sut).toBeDefined();
|
expect(sut).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getCount', () => {
|
describe('getStatistics', () => {
|
||||||
it('should get the album count', async () => {
|
it('should get the album count', async () => {
|
||||||
albumMock.getOwned.mockResolvedValue([]);
|
albumMock.getOwned.mockResolvedValue([]);
|
||||||
albumMock.getShared.mockResolvedValue([]);
|
albumMock.getShared.mockResolvedValue([]);
|
||||||
albumMock.getNotShared.mockResolvedValue([]);
|
albumMock.getNotShared.mockResolvedValue([]);
|
||||||
await expect(sut.getCount(authStub.admin)).resolves.toEqual({
|
await expect(sut.getStatistics(authStub.admin)).resolves.toEqual({
|
||||||
owned: 0,
|
owned: 0,
|
||||||
shared: 0,
|
shared: 0,
|
||||||
notShared: 0,
|
notShared: 0,
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
|
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
|
||||||
import { AccessCore } from 'src/cores/access.core';
|
|
||||||
import {
|
import {
|
||||||
AddUsersDto,
|
AddUsersDto,
|
||||||
AlbumCountResponseDto,
|
|
||||||
AlbumInfoDto,
|
AlbumInfoDto,
|
||||||
AlbumResponseDto,
|
AlbumResponseDto,
|
||||||
|
AlbumStatisticsResponseDto,
|
||||||
CreateAlbumDto,
|
CreateAlbumDto,
|
||||||
GetAlbumsDto,
|
GetAlbumsDto,
|
||||||
UpdateAlbumDto,
|
UpdateAlbumDto,
|
||||||
@ -24,23 +23,21 @@ import { AlbumAssetCount, AlbumInfoOptions, IAlbumRepository } from 'src/interfa
|
|||||||
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
||||||
import { IEventRepository } from 'src/interfaces/event.interface';
|
import { IEventRepository } from 'src/interfaces/event.interface';
|
||||||
import { IUserRepository } from 'src/interfaces/user.interface';
|
import { IUserRepository } from 'src/interfaces/user.interface';
|
||||||
|
import { checkAccess, requireAccess } from 'src/utils/access';
|
||||||
import { addAssets, removeAssets } from 'src/utils/asset.util';
|
import { addAssets, removeAssets } from 'src/utils/asset.util';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AlbumService {
|
export class AlbumService {
|
||||||
private access: AccessCore;
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(IAccessRepository) private accessRepository: IAccessRepository,
|
@Inject(IAccessRepository) private access: IAccessRepository,
|
||||||
@Inject(IAlbumRepository) private albumRepository: IAlbumRepository,
|
@Inject(IAlbumRepository) private albumRepository: IAlbumRepository,
|
||||||
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
|
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
|
||||||
@Inject(IEventRepository) private eventRepository: IEventRepository,
|
@Inject(IEventRepository) private eventRepository: IEventRepository,
|
||||||
@Inject(IUserRepository) private userRepository: IUserRepository,
|
@Inject(IUserRepository) private userRepository: IUserRepository,
|
||||||
@Inject(IAlbumUserRepository) private albumUserRepository: IAlbumUserRepository,
|
@Inject(IAlbumUserRepository) private albumUserRepository: IAlbumUserRepository,
|
||||||
) {
|
) {}
|
||||||
this.access = AccessCore.create(accessRepository);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getCount(auth: AuthDto): Promise<AlbumCountResponseDto> {
|
async getStatistics(auth: AuthDto): Promise<AlbumStatisticsResponseDto> {
|
||||||
const [owned, shared, notShared] = await Promise.all([
|
const [owned, shared, notShared] = await Promise.all([
|
||||||
this.albumRepository.getOwned(auth.user.id),
|
this.albumRepository.getOwned(auth.user.id),
|
||||||
this.albumRepository.getShared(auth.user.id),
|
this.albumRepository.getShared(auth.user.id),
|
||||||
@ -102,7 +99,7 @@ export class AlbumService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async get(auth: AuthDto, id: string, dto: AlbumInfoDto): Promise<AlbumResponseDto> {
|
async get(auth: AuthDto, id: string, dto: AlbumInfoDto): Promise<AlbumResponseDto> {
|
||||||
await this.access.requirePermission(auth, Permission.ALBUM_READ, id);
|
await requireAccess(this.access, { auth, permission: Permission.ALBUM_READ, ids: [id] });
|
||||||
await this.albumRepository.updateThumbnails();
|
await this.albumRepository.updateThumbnails();
|
||||||
const withAssets = dto.withoutAssets === undefined ? true : !dto.withoutAssets;
|
const withAssets = dto.withoutAssets === undefined ? true : !dto.withoutAssets;
|
||||||
const album = await this.findOrFail(id, { withAssets });
|
const album = await this.findOrFail(id, { withAssets });
|
||||||
@ -126,7 +123,11 @@ export class AlbumService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const allowedAssetIdsSet = await this.access.checkAccess(auth, Permission.ASSET_SHARE, new Set(dto.assetIds));
|
const allowedAssetIdsSet = await checkAccess(this.access, {
|
||||||
|
auth,
|
||||||
|
permission: Permission.ASSET_SHARE,
|
||||||
|
ids: dto.assetIds || [],
|
||||||
|
});
|
||||||
const assets = [...allowedAssetIdsSet].map((id) => ({ id }) as AssetEntity);
|
const assets = [...allowedAssetIdsSet].map((id) => ({ id }) as AssetEntity);
|
||||||
|
|
||||||
const album = await this.albumRepository.create({
|
const album = await this.albumRepository.create({
|
||||||
@ -146,7 +147,7 @@ export class AlbumService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async update(auth: AuthDto, id: string, dto: UpdateAlbumDto): Promise<AlbumResponseDto> {
|
async update(auth: AuthDto, id: string, dto: UpdateAlbumDto): Promise<AlbumResponseDto> {
|
||||||
await this.access.requirePermission(auth, Permission.ALBUM_UPDATE, id);
|
await requireAccess(this.access, { auth, permission: Permission.ALBUM_UPDATE, ids: [id] });
|
||||||
|
|
||||||
const album = await this.findOrFail(id, { withAssets: true });
|
const album = await this.findOrFail(id, { withAssets: true });
|
||||||
|
|
||||||
@ -169,17 +170,17 @@ export class AlbumService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async delete(auth: AuthDto, id: string): Promise<void> {
|
async delete(auth: AuthDto, id: string): Promise<void> {
|
||||||
await this.access.requirePermission(auth, Permission.ALBUM_DELETE, id);
|
await requireAccess(this.access, { auth, permission: Permission.ALBUM_DELETE, ids: [id] });
|
||||||
await this.albumRepository.delete(id);
|
await this.albumRepository.delete(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
async addAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise<BulkIdResponseDto[]> {
|
async addAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise<BulkIdResponseDto[]> {
|
||||||
const album = await this.findOrFail(id, { withAssets: false });
|
const album = await this.findOrFail(id, { withAssets: false });
|
||||||
await this.access.requirePermission(auth, Permission.ALBUM_ADD_ASSET, id);
|
await requireAccess(this.access, { auth, permission: Permission.ALBUM_ADD_ASSET, ids: [id] });
|
||||||
|
|
||||||
const results = await addAssets(
|
const results = await addAssets(
|
||||||
auth,
|
auth,
|
||||||
{ accessRepository: this.accessRepository, repository: this.albumRepository },
|
{ access: this.access, bulk: this.albumRepository },
|
||||||
{ parentId: id, assetIds: dto.ids },
|
{ parentId: id, assetIds: dto.ids },
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -198,12 +199,12 @@ export class AlbumService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async removeAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise<BulkIdResponseDto[]> {
|
async removeAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise<BulkIdResponseDto[]> {
|
||||||
await this.access.requirePermission(auth, Permission.ALBUM_REMOVE_ASSET, id);
|
await requireAccess(this.access, { auth, permission: Permission.ALBUM_REMOVE_ASSET, ids: [id] });
|
||||||
|
|
||||||
const album = await this.findOrFail(id, { withAssets: false });
|
const album = await this.findOrFail(id, { withAssets: false });
|
||||||
const results = await removeAssets(
|
const results = await removeAssets(
|
||||||
auth,
|
auth,
|
||||||
{ accessRepository: this.accessRepository, repository: this.albumRepository },
|
{ access: this.access, bulk: this.albumRepository },
|
||||||
{ parentId: id, assetIds: dto.ids, canAlwaysRemove: Permission.ALBUM_DELETE },
|
{ parentId: id, assetIds: dto.ids, canAlwaysRemove: Permission.ALBUM_DELETE },
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -219,7 +220,7 @@ export class AlbumService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async addUsers(auth: AuthDto, id: string, { albumUsers }: AddUsersDto): Promise<AlbumResponseDto> {
|
async addUsers(auth: AuthDto, id: string, { albumUsers }: AddUsersDto): Promise<AlbumResponseDto> {
|
||||||
await this.access.requirePermission(auth, Permission.ALBUM_SHARE, id);
|
await requireAccess(this.access, { auth, permission: Permission.ALBUM_SHARE, ids: [id] });
|
||||||
|
|
||||||
const album = await this.findOrFail(id, { withAssets: false });
|
const album = await this.findOrFail(id, { withAssets: false });
|
||||||
|
|
||||||
@ -263,15 +264,14 @@ export class AlbumService {
|
|||||||
|
|
||||||
// non-admin can remove themselves
|
// non-admin can remove themselves
|
||||||
if (auth.user.id !== userId) {
|
if (auth.user.id !== userId) {
|
||||||
await this.access.requirePermission(auth, Permission.ALBUM_SHARE, id);
|
await requireAccess(this.access, { auth, permission: Permission.ALBUM_SHARE, ids: [id] });
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.albumUserRepository.delete({ albumId: id, userId });
|
await this.albumUserRepository.delete({ albumId: id, userId });
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateUser(auth: AuthDto, id: string, userId: string, dto: Partial<AlbumUserEntity>): Promise<void> {
|
async updateUser(auth: AuthDto, id: string, userId: string, dto: Partial<AlbumUserEntity>): Promise<void> {
|
||||||
await this.access.requirePermission(auth, Permission.ALBUM_SHARE, id);
|
await requireAccess(this.access, { auth, permission: Permission.ALBUM_SHARE, ids: [id] });
|
||||||
|
|
||||||
await this.albumUserRepository.update({ albumId: id, userId }, { role: dto.role });
|
await this.albumUserRepository.update({ albumId: id, userId }, { role: dto.role });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ import {
|
|||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { extname } from 'node:path';
|
import { extname } from 'node:path';
|
||||||
import sanitize from 'sanitize-filename';
|
import sanitize from 'sanitize-filename';
|
||||||
import { AccessCore } from 'src/cores/access.core';
|
|
||||||
import { StorageCore, StorageFolder } from 'src/cores/storage.core';
|
import { StorageCore, StorageFolder } from 'src/cores/storage.core';
|
||||||
import {
|
import {
|
||||||
AssetBulkUploadCheckResponseDto,
|
AssetBulkUploadCheckResponseDto,
|
||||||
@ -36,6 +35,7 @@ import { IJobRepository, JobName } from 'src/interfaces/job.interface';
|
|||||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
||||||
import { IStorageRepository } from 'src/interfaces/storage.interface';
|
import { IStorageRepository } from 'src/interfaces/storage.interface';
|
||||||
import { IUserRepository } from 'src/interfaces/user.interface';
|
import { IUserRepository } from 'src/interfaces/user.interface';
|
||||||
|
import { requireAccess, requireUploadAccess } from 'src/utils/access';
|
||||||
import { getAssetFiles } from 'src/utils/asset.util';
|
import { getAssetFiles } from 'src/utils/asset.util';
|
||||||
import { CacheControl, ImmichFileResponse } from 'src/utils/file';
|
import { CacheControl, ImmichFileResponse } from 'src/utils/file';
|
||||||
import { mimeTypes } from 'src/utils/mime-types';
|
import { mimeTypes } from 'src/utils/mime-types';
|
||||||
@ -57,10 +57,8 @@ export interface UploadFile {
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AssetMediaService {
|
export class AssetMediaService {
|
||||||
private access: AccessCore;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(IAccessRepository) accessRepository: IAccessRepository,
|
@Inject(IAccessRepository) private access: IAccessRepository,
|
||||||
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
|
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
|
||||||
@Inject(IJobRepository) private jobRepository: IJobRepository,
|
@Inject(IJobRepository) private jobRepository: IJobRepository,
|
||||||
@Inject(IStorageRepository) private storageRepository: IStorageRepository,
|
@Inject(IStorageRepository) private storageRepository: IStorageRepository,
|
||||||
@ -69,7 +67,6 @@ export class AssetMediaService {
|
|||||||
@Inject(ILoggerRepository) private logger: ILoggerRepository,
|
@Inject(ILoggerRepository) private logger: ILoggerRepository,
|
||||||
) {
|
) {
|
||||||
this.logger.setContext(AssetMediaService.name);
|
this.logger.setContext(AssetMediaService.name);
|
||||||
this.access = AccessCore.create(accessRepository);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getUploadAssetIdByChecksum(auth: AuthDto, checksum?: string): Promise<AssetMediaResponseDto | undefined> {
|
async getUploadAssetIdByChecksum(auth: AuthDto, checksum?: string): Promise<AssetMediaResponseDto | undefined> {
|
||||||
@ -86,7 +83,7 @@ export class AssetMediaService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
canUploadFile({ auth, fieldName, file }: UploadRequest): true {
|
canUploadFile({ auth, fieldName, file }: UploadRequest): true {
|
||||||
this.access.requireUploadAccess(auth);
|
requireUploadAccess(auth);
|
||||||
|
|
||||||
const filename = file.originalName;
|
const filename = file.originalName;
|
||||||
|
|
||||||
@ -118,7 +115,7 @@ export class AssetMediaService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getUploadFilename({ auth, fieldName, file }: UploadRequest): string {
|
getUploadFilename({ auth, fieldName, file }: UploadRequest): string {
|
||||||
this.access.requireUploadAccess(auth);
|
requireUploadAccess(auth);
|
||||||
|
|
||||||
const originalExtension = extname(file.originalName);
|
const originalExtension = extname(file.originalName);
|
||||||
|
|
||||||
@ -132,7 +129,7 @@ export class AssetMediaService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getUploadFolder({ auth, fieldName, file }: UploadRequest): string {
|
getUploadFolder({ auth, fieldName, file }: UploadRequest): string {
|
||||||
auth = this.access.requireUploadAccess(auth);
|
auth = requireUploadAccess(auth);
|
||||||
|
|
||||||
let folder = StorageCore.getNestedFolder(StorageFolder.UPLOAD, auth.user.id, file.uuid);
|
let folder = StorageCore.getNestedFolder(StorageFolder.UPLOAD, auth.user.id, file.uuid);
|
||||||
if (fieldName === UploadFieldName.PROFILE_DATA) {
|
if (fieldName === UploadFieldName.PROFILE_DATA) {
|
||||||
@ -151,12 +148,12 @@ export class AssetMediaService {
|
|||||||
sidecarFile?: UploadFile,
|
sidecarFile?: UploadFile,
|
||||||
): Promise<AssetMediaResponseDto> {
|
): Promise<AssetMediaResponseDto> {
|
||||||
try {
|
try {
|
||||||
await this.access.requirePermission(
|
await requireAccess(this.access, {
|
||||||
auth,
|
auth,
|
||||||
Permission.ASSET_UPLOAD,
|
permission: Permission.ASSET_UPLOAD,
|
||||||
// do not need an id here, but the interface requires it
|
// do not need an id here, but the interface requires it
|
||||||
auth.user.id,
|
ids: [auth.user.id],
|
||||||
);
|
});
|
||||||
|
|
||||||
this.requireQuota(auth, file.size);
|
this.requireQuota(auth, file.size);
|
||||||
|
|
||||||
@ -195,7 +192,7 @@ export class AssetMediaService {
|
|||||||
sidecarFile?: UploadFile,
|
sidecarFile?: UploadFile,
|
||||||
): Promise<AssetMediaResponseDto> {
|
): Promise<AssetMediaResponseDto> {
|
||||||
try {
|
try {
|
||||||
await this.access.requirePermission(auth, Permission.ASSET_UPDATE, id);
|
await requireAccess(this.access, { auth, permission: Permission.ASSET_UPDATE, ids: [id] });
|
||||||
const asset = (await this.assetRepository.getById(id)) as AssetEntity;
|
const asset = (await this.assetRepository.getById(id)) as AssetEntity;
|
||||||
|
|
||||||
this.requireQuota(auth, file.size);
|
this.requireQuota(auth, file.size);
|
||||||
@ -219,7 +216,7 @@ export class AssetMediaService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async downloadOriginal(auth: AuthDto, id: string): Promise<ImmichFileResponse> {
|
async downloadOriginal(auth: AuthDto, id: string): Promise<ImmichFileResponse> {
|
||||||
await this.access.requirePermission(auth, Permission.ASSET_DOWNLOAD, id);
|
await requireAccess(this.access, { auth, permission: Permission.ASSET_DOWNLOAD, ids: [id] });
|
||||||
|
|
||||||
const asset = await this.findOrFail(id);
|
const asset = await this.findOrFail(id);
|
||||||
if (!asset) {
|
if (!asset) {
|
||||||
@ -234,7 +231,7 @@ export class AssetMediaService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async viewThumbnail(auth: AuthDto, id: string, dto: AssetMediaOptionsDto): Promise<ImmichFileResponse> {
|
async viewThumbnail(auth: AuthDto, id: string, dto: AssetMediaOptionsDto): Promise<ImmichFileResponse> {
|
||||||
await this.access.requirePermission(auth, Permission.ASSET_VIEW, id);
|
await requireAccess(this.access, { auth, permission: Permission.ASSET_VIEW, ids: [id] });
|
||||||
|
|
||||||
const asset = await this.findOrFail(id);
|
const asset = await this.findOrFail(id);
|
||||||
const size = dto.size ?? AssetMediaSize.THUMBNAIL;
|
const size = dto.size ?? AssetMediaSize.THUMBNAIL;
|
||||||
@ -257,7 +254,7 @@ export class AssetMediaService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async playbackVideo(auth: AuthDto, id: string): Promise<ImmichFileResponse> {
|
async playbackVideo(auth: AuthDto, id: string): Promise<ImmichFileResponse> {
|
||||||
await this.access.requirePermission(auth, Permission.ASSET_VIEW, id);
|
await requireAccess(this.access, { auth, permission: Permission.ASSET_VIEW, ids: [id] });
|
||||||
|
|
||||||
const asset = await this.findOrFail(id);
|
const asset = await this.findOrFail(id);
|
||||||
if (!asset) {
|
if (!asset) {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { BadRequestException, Inject } from '@nestjs/common';
|
import { BadRequestException, Inject } from '@nestjs/common';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { DateTime, Duration } from 'luxon';
|
import { DateTime, Duration } from 'luxon';
|
||||||
import { AccessCore } from 'src/cores/access.core';
|
|
||||||
import { SystemConfigCore } from 'src/cores/system-config.core';
|
import { SystemConfigCore } from 'src/cores/system-config.core';
|
||||||
import {
|
import {
|
||||||
AssetResponseDto,
|
AssetResponseDto,
|
||||||
@ -39,15 +38,15 @@ import { IPartnerRepository } from 'src/interfaces/partner.interface';
|
|||||||
import { IStackRepository } from 'src/interfaces/stack.interface';
|
import { IStackRepository } from 'src/interfaces/stack.interface';
|
||||||
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
|
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
|
||||||
import { IUserRepository } from 'src/interfaces/user.interface';
|
import { IUserRepository } from 'src/interfaces/user.interface';
|
||||||
|
import { requireAccess } from 'src/utils/access';
|
||||||
import { getAssetFiles, getMyPartnerIds } from 'src/utils/asset.util';
|
import { getAssetFiles, getMyPartnerIds } from 'src/utils/asset.util';
|
||||||
import { usePagination } from 'src/utils/pagination';
|
import { usePagination } from 'src/utils/pagination';
|
||||||
|
|
||||||
export class AssetService {
|
export class AssetService {
|
||||||
private access: AccessCore;
|
|
||||||
private configCore: SystemConfigCore;
|
private configCore: SystemConfigCore;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(IAccessRepository) accessRepository: IAccessRepository,
|
@Inject(IAccessRepository) private access: IAccessRepository,
|
||||||
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
|
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
|
||||||
@Inject(IJobRepository) private jobRepository: IJobRepository,
|
@Inject(IJobRepository) private jobRepository: IJobRepository,
|
||||||
@Inject(ISystemMetadataRepository) systemMetadataRepository: ISystemMetadataRepository,
|
@Inject(ISystemMetadataRepository) systemMetadataRepository: ISystemMetadataRepository,
|
||||||
@ -58,7 +57,6 @@ export class AssetService {
|
|||||||
@Inject(ILoggerRepository) private logger: ILoggerRepository,
|
@Inject(ILoggerRepository) private logger: ILoggerRepository,
|
||||||
) {
|
) {
|
||||||
this.logger.setContext(AssetService.name);
|
this.logger.setContext(AssetService.name);
|
||||||
this.access = AccessCore.create(accessRepository);
|
|
||||||
this.configCore = SystemConfigCore.create(systemMetadataRepository, this.logger);
|
this.configCore = SystemConfigCore.create(systemMetadataRepository, this.logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,7 +107,7 @@ export class AssetService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async get(auth: AuthDto, id: string): Promise<AssetResponseDto | SanitizedAssetResponseDto> {
|
async get(auth: AuthDto, id: string): Promise<AssetResponseDto | SanitizedAssetResponseDto> {
|
||||||
await this.access.requirePermission(auth, Permission.ASSET_READ, id);
|
await requireAccess(this.access, { auth, permission: Permission.ASSET_READ, ids: [id] });
|
||||||
|
|
||||||
const asset = await this.assetRepository.getById(
|
const asset = await this.assetRepository.getById(
|
||||||
id,
|
id,
|
||||||
@ -158,7 +156,7 @@ export class AssetService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async update(auth: AuthDto, id: string, dto: UpdateAssetDto): Promise<AssetResponseDto> {
|
async update(auth: AuthDto, id: string, dto: UpdateAssetDto): Promise<AssetResponseDto> {
|
||||||
await this.access.requirePermission(auth, Permission.ASSET_UPDATE, id);
|
await requireAccess(this.access, { auth, permission: Permission.ASSET_UPDATE, ids: [id] });
|
||||||
|
|
||||||
const { description, dateTimeOriginal, latitude, longitude, rating, ...rest } = dto;
|
const { description, dateTimeOriginal, latitude, longitude, rating, ...rest } = dto;
|
||||||
await this.updateMetadata({ id, description, dateTimeOriginal, latitude, longitude, rating });
|
await this.updateMetadata({ id, description, dateTimeOriginal, latitude, longitude, rating });
|
||||||
@ -182,7 +180,7 @@ export class AssetService {
|
|||||||
|
|
||||||
async updateAll(auth: AuthDto, dto: AssetBulkUpdateDto): Promise<void> {
|
async updateAll(auth: AuthDto, dto: AssetBulkUpdateDto): Promise<void> {
|
||||||
const { ids, dateTimeOriginal, latitude, longitude, ...options } = dto;
|
const { ids, dateTimeOriginal, latitude, longitude, ...options } = dto;
|
||||||
await this.access.requirePermission(auth, Permission.ASSET_UPDATE, ids);
|
await requireAccess(this.access, { auth, permission: Permission.ASSET_UPDATE, ids });
|
||||||
|
|
||||||
for (const id of ids) {
|
for (const id of ids) {
|
||||||
await this.updateMetadata({ id, dateTimeOriginal, latitude, longitude });
|
await this.updateMetadata({ id, dateTimeOriginal, latitude, longitude });
|
||||||
@ -278,7 +276,7 @@ export class AssetService {
|
|||||||
async deleteAll(auth: AuthDto, dto: AssetBulkDeleteDto): Promise<void> {
|
async deleteAll(auth: AuthDto, dto: AssetBulkDeleteDto): Promise<void> {
|
||||||
const { ids, force } = dto;
|
const { ids, force } = dto;
|
||||||
|
|
||||||
await this.access.requirePermission(auth, Permission.ASSET_DELETE, ids);
|
await requireAccess(this.access, { auth, permission: Permission.ASSET_DELETE, ids });
|
||||||
|
|
||||||
if (force) {
|
if (force) {
|
||||||
await this.jobRepository.queueAll(
|
await this.jobRepository.queueAll(
|
||||||
@ -294,7 +292,7 @@ export class AssetService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async run(auth: AuthDto, dto: AssetJobsDto) {
|
async run(auth: AuthDto, dto: AssetJobsDto) {
|
||||||
await this.access.requirePermission(auth, Permission.ASSET_UPDATE, dto.assetIds);
|
await requireAccess(this.access, { auth, permission: Permission.ASSET_UPDATE, ids: dto.assetIds });
|
||||||
|
|
||||||
const jobs: JobItem[] = [];
|
const jobs: JobItem[] = [];
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ import { BadRequestException, Inject, Injectable } from '@nestjs/common';
|
|||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { resolve } from 'node:path';
|
import { resolve } from 'node:path';
|
||||||
import { AUDIT_LOG_MAX_DURATION } from 'src/constants';
|
import { AUDIT_LOG_MAX_DURATION } from 'src/constants';
|
||||||
import { AccessCore } from 'src/cores/access.core';
|
|
||||||
import { StorageCore, StorageFolder } from 'src/cores/storage.core';
|
import { StorageCore, StorageFolder } from 'src/cores/storage.core';
|
||||||
import {
|
import {
|
||||||
AuditDeletesDto,
|
AuditDeletesDto,
|
||||||
@ -24,15 +23,14 @@ import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
|||||||
import { IPersonRepository } from 'src/interfaces/person.interface';
|
import { IPersonRepository } from 'src/interfaces/person.interface';
|
||||||
import { IStorageRepository } from 'src/interfaces/storage.interface';
|
import { IStorageRepository } from 'src/interfaces/storage.interface';
|
||||||
import { IUserRepository } from 'src/interfaces/user.interface';
|
import { IUserRepository } from 'src/interfaces/user.interface';
|
||||||
|
import { requireAccess } from 'src/utils/access';
|
||||||
import { getAssetFiles } from 'src/utils/asset.util';
|
import { getAssetFiles } from 'src/utils/asset.util';
|
||||||
import { usePagination } from 'src/utils/pagination';
|
import { usePagination } from 'src/utils/pagination';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuditService {
|
export class AuditService {
|
||||||
private access: AccessCore;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(IAccessRepository) accessRepository: IAccessRepository,
|
@Inject(IAccessRepository) private access: IAccessRepository,
|
||||||
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
|
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
|
||||||
@Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository,
|
@Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository,
|
||||||
@Inject(IPersonRepository) private personRepository: IPersonRepository,
|
@Inject(IPersonRepository) private personRepository: IPersonRepository,
|
||||||
@ -41,7 +39,6 @@ export class AuditService {
|
|||||||
@Inject(IUserRepository) private userRepository: IUserRepository,
|
@Inject(IUserRepository) private userRepository: IUserRepository,
|
||||||
@Inject(ILoggerRepository) private logger: ILoggerRepository,
|
@Inject(ILoggerRepository) private logger: ILoggerRepository,
|
||||||
) {
|
) {
|
||||||
this.access = AccessCore.create(accessRepository);
|
|
||||||
this.logger.setContext(AuditService.name);
|
this.logger.setContext(AuditService.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,7 +49,7 @@ export class AuditService {
|
|||||||
|
|
||||||
async getDeletes(auth: AuthDto, dto: AuditDeletesDto): Promise<AuditDeletesResponseDto> {
|
async getDeletes(auth: AuthDto, dto: AuditDeletesDto): Promise<AuditDeletesResponseDto> {
|
||||||
const userId = dto.userId || auth.user.id;
|
const userId = dto.userId || auth.user.id;
|
||||||
await this.access.requirePermission(auth, Permission.TIMELINE_READ, userId);
|
await requireAccess(this.access, { auth, permission: Permission.TIMELINE_READ, ids: [userId] });
|
||||||
|
|
||||||
const audits = await this.repository.getAfter(dto.after, {
|
const audits = await this.repository.getAfter(dto.after, {
|
||||||
userIds: [userId],
|
userIds: [userId],
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
|
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
|
||||||
import { parse } from 'node:path';
|
import { parse } from 'node:path';
|
||||||
import { AccessCore } from 'src/cores/access.core';
|
|
||||||
import { StorageCore } from 'src/cores/storage.core';
|
import { StorageCore } from 'src/cores/storage.core';
|
||||||
import { AssetIdsDto } from 'src/dtos/asset.dto';
|
import { AssetIdsDto } from 'src/dtos/asset.dto';
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
@ -11,21 +10,19 @@ import { IAccessRepository } from 'src/interfaces/access.interface';
|
|||||||
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
||||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
||||||
import { ImmichReadStream, IStorageRepository } from 'src/interfaces/storage.interface';
|
import { ImmichReadStream, IStorageRepository } from 'src/interfaces/storage.interface';
|
||||||
|
import { requireAccess } from 'src/utils/access';
|
||||||
import { HumanReadableSize } from 'src/utils/bytes';
|
import { HumanReadableSize } from 'src/utils/bytes';
|
||||||
import { usePagination } from 'src/utils/pagination';
|
import { usePagination } from 'src/utils/pagination';
|
||||||
import { getPreferences } from 'src/utils/preferences';
|
import { getPreferences } from 'src/utils/preferences';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DownloadService {
|
export class DownloadService {
|
||||||
private access: AccessCore;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(IAccessRepository) accessRepository: IAccessRepository,
|
@Inject(IAccessRepository) private access: IAccessRepository,
|
||||||
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
|
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
|
||||||
@Inject(ILoggerRepository) private logger: ILoggerRepository,
|
@Inject(ILoggerRepository) private logger: ILoggerRepository,
|
||||||
@Inject(IStorageRepository) private storageRepository: IStorageRepository,
|
@Inject(IStorageRepository) private storageRepository: IStorageRepository,
|
||||||
) {
|
) {
|
||||||
this.access = AccessCore.create(accessRepository);
|
|
||||||
this.logger.setContext(DownloadService.name);
|
this.logger.setContext(DownloadService.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,7 +73,7 @@ export class DownloadService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async downloadArchive(auth: AuthDto, dto: AssetIdsDto): Promise<ImmichReadStream> {
|
async downloadArchive(auth: AuthDto, dto: AssetIdsDto): Promise<ImmichReadStream> {
|
||||||
await this.access.requirePermission(auth, Permission.ASSET_DOWNLOAD, dto.assetIds);
|
await requireAccess(this.access, { auth, permission: Permission.ASSET_DOWNLOAD, ids: dto.assetIds });
|
||||||
|
|
||||||
const zip = this.storageRepository.createZipStream();
|
const zip = this.storageRepository.createZipStream();
|
||||||
const assets = await this.assetRepository.getByIds(dto.assetIds);
|
const assets = await this.assetRepository.getByIds(dto.assetIds);
|
||||||
@ -119,20 +116,20 @@ export class DownloadService {
|
|||||||
|
|
||||||
if (dto.assetIds) {
|
if (dto.assetIds) {
|
||||||
const assetIds = dto.assetIds;
|
const assetIds = dto.assetIds;
|
||||||
await this.access.requirePermission(auth, Permission.ASSET_DOWNLOAD, assetIds);
|
await requireAccess(this.access, { auth, permission: Permission.ASSET_DOWNLOAD, ids: assetIds });
|
||||||
const assets = await this.assetRepository.getByIds(assetIds, { exifInfo: true });
|
const assets = await this.assetRepository.getByIds(assetIds, { exifInfo: true });
|
||||||
return usePagination(PAGINATION_SIZE, () => ({ hasNextPage: false, items: assets }));
|
return usePagination(PAGINATION_SIZE, () => ({ hasNextPage: false, items: assets }));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dto.albumId) {
|
if (dto.albumId) {
|
||||||
const albumId = dto.albumId;
|
const albumId = dto.albumId;
|
||||||
await this.access.requirePermission(auth, Permission.ALBUM_DOWNLOAD, albumId);
|
await requireAccess(this.access, { auth, permission: Permission.ALBUM_DOWNLOAD, ids: [albumId] });
|
||||||
return usePagination(PAGINATION_SIZE, (pagination) => this.assetRepository.getByAlbumId(pagination, albumId));
|
return usePagination(PAGINATION_SIZE, (pagination) => this.assetRepository.getByAlbumId(pagination, albumId));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dto.userId) {
|
if (dto.userId) {
|
||||||
const userId = dto.userId;
|
const userId = dto.userId;
|
||||||
await this.access.requirePermission(auth, Permission.TIMELINE_DOWNLOAD, userId);
|
await requireAccess(this.access, { auth, permission: Permission.TIMELINE_DOWNLOAD, ids: [userId] });
|
||||||
return usePagination(PAGINATION_SIZE, (pagination) =>
|
return usePagination(PAGINATION_SIZE, (pagination) =>
|
||||||
this.assetRepository.getByUserId(pagination, userId, { isVisible: true }),
|
this.assetRepository.getByUserId(pagination, userId, { isVisible: true }),
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
|
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
|
||||||
import { AccessCore } from 'src/cores/access.core';
|
|
||||||
import { BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
|
import { BulkIdResponseDto, BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { MemoryCreateDto, MemoryResponseDto, MemoryUpdateDto, mapMemory } from 'src/dtos/memory.dto';
|
import { MemoryCreateDto, MemoryResponseDto, MemoryUpdateDto, mapMemory } from 'src/dtos/memory.dto';
|
||||||
@ -7,18 +6,15 @@ import { AssetEntity } from 'src/entities/asset.entity';
|
|||||||
import { Permission } from 'src/enum';
|
import { Permission } from 'src/enum';
|
||||||
import { IAccessRepository } from 'src/interfaces/access.interface';
|
import { IAccessRepository } from 'src/interfaces/access.interface';
|
||||||
import { IMemoryRepository } from 'src/interfaces/memory.interface';
|
import { IMemoryRepository } from 'src/interfaces/memory.interface';
|
||||||
|
import { checkAccess, requireAccess } from 'src/utils/access';
|
||||||
import { addAssets, removeAssets } from 'src/utils/asset.util';
|
import { addAssets, removeAssets } from 'src/utils/asset.util';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MemoryService {
|
export class MemoryService {
|
||||||
private access: AccessCore;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(IAccessRepository) private accessRepository: IAccessRepository,
|
@Inject(IAccessRepository) private access: IAccessRepository,
|
||||||
@Inject(IMemoryRepository) private repository: IMemoryRepository,
|
@Inject(IMemoryRepository) private repository: IMemoryRepository,
|
||||||
) {
|
) {}
|
||||||
this.access = AccessCore.create(accessRepository);
|
|
||||||
}
|
|
||||||
|
|
||||||
async search(auth: AuthDto) {
|
async search(auth: AuthDto) {
|
||||||
const memories = await this.repository.search(auth.user.id);
|
const memories = await this.repository.search(auth.user.id);
|
||||||
@ -26,7 +22,7 @@ export class MemoryService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async get(auth: AuthDto, id: string): Promise<MemoryResponseDto> {
|
async get(auth: AuthDto, id: string): Promise<MemoryResponseDto> {
|
||||||
await this.access.requirePermission(auth, Permission.MEMORY_READ, id);
|
await requireAccess(this.access, { auth, permission: Permission.MEMORY_READ, ids: [id] });
|
||||||
const memory = await this.findOrFail(id);
|
const memory = await this.findOrFail(id);
|
||||||
return mapMemory(memory);
|
return mapMemory(memory);
|
||||||
}
|
}
|
||||||
@ -35,7 +31,11 @@ export class MemoryService {
|
|||||||
// TODO validate type/data combination
|
// TODO validate type/data combination
|
||||||
|
|
||||||
const assetIds = dto.assetIds || [];
|
const assetIds = dto.assetIds || [];
|
||||||
const allowedAssetIds = await this.access.checkAccess(auth, Permission.ASSET_SHARE, assetIds);
|
const allowedAssetIds = await checkAccess(this.access, {
|
||||||
|
auth,
|
||||||
|
permission: Permission.ASSET_SHARE,
|
||||||
|
ids: assetIds,
|
||||||
|
});
|
||||||
const memory = await this.repository.create({
|
const memory = await this.repository.create({
|
||||||
ownerId: auth.user.id,
|
ownerId: auth.user.id,
|
||||||
type: dto.type,
|
type: dto.type,
|
||||||
@ -50,7 +50,7 @@ export class MemoryService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async update(auth: AuthDto, id: string, dto: MemoryUpdateDto): Promise<MemoryResponseDto> {
|
async update(auth: AuthDto, id: string, dto: MemoryUpdateDto): Promise<MemoryResponseDto> {
|
||||||
await this.access.requirePermission(auth, Permission.MEMORY_UPDATE, id);
|
await requireAccess(this.access, { auth, permission: Permission.MEMORY_UPDATE, ids: [id] });
|
||||||
|
|
||||||
const memory = await this.repository.update({
|
const memory = await this.repository.update({
|
||||||
id,
|
id,
|
||||||
@ -63,14 +63,14 @@ export class MemoryService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async remove(auth: AuthDto, id: string): Promise<void> {
|
async remove(auth: AuthDto, id: string): Promise<void> {
|
||||||
await this.access.requirePermission(auth, Permission.MEMORY_DELETE, id);
|
await requireAccess(this.access, { auth, permission: Permission.MEMORY_DELETE, ids: [id] });
|
||||||
await this.repository.delete(id);
|
await this.repository.delete(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
async addAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise<BulkIdResponseDto[]> {
|
async addAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise<BulkIdResponseDto[]> {
|
||||||
await this.access.requirePermission(auth, Permission.MEMORY_READ, id);
|
await requireAccess(this.access, { auth, permission: Permission.MEMORY_READ, ids: [id] });
|
||||||
|
|
||||||
const repos = { accessRepository: this.accessRepository, repository: this.repository };
|
const repos = { access: this.access, bulk: this.repository };
|
||||||
const results = await addAssets(auth, repos, { parentId: id, assetIds: dto.ids });
|
const results = await addAssets(auth, repos, { parentId: id, assetIds: dto.ids });
|
||||||
|
|
||||||
const hasSuccess = results.find(({ success }) => success);
|
const hasSuccess = results.find(({ success }) => success);
|
||||||
@ -82,9 +82,9 @@ export class MemoryService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async removeAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise<BulkIdResponseDto[]> {
|
async removeAssets(auth: AuthDto, id: string, dto: BulkIdsDto): Promise<BulkIdResponseDto[]> {
|
||||||
await this.access.requirePermission(auth, Permission.MEMORY_UPDATE, id);
|
await requireAccess(this.access, { auth, permission: Permission.MEMORY_UPDATE, ids: [id] });
|
||||||
|
|
||||||
const repos = { accessRepository: this.accessRepository, repository: this.repository };
|
const repos = { access: this.access, bulk: this.repository };
|
||||||
const results = await removeAssets(auth, repos, {
|
const results = await removeAssets(auth, repos, {
|
||||||
parentId: id,
|
parentId: id,
|
||||||
assetIds: dto.ids,
|
assetIds: dto.ids,
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
|
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
|
||||||
import { AccessCore } from 'src/cores/access.core';
|
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { PartnerResponseDto, PartnerSearchDto, UpdatePartnerDto } from 'src/dtos/partner.dto';
|
import { PartnerResponseDto, PartnerSearchDto, UpdatePartnerDto } from 'src/dtos/partner.dto';
|
||||||
import { mapUser } from 'src/dtos/user.dto';
|
import { mapUser } from 'src/dtos/user.dto';
|
||||||
@ -7,16 +6,14 @@ import { PartnerEntity } from 'src/entities/partner.entity';
|
|||||||
import { Permission } from 'src/enum';
|
import { Permission } from 'src/enum';
|
||||||
import { IAccessRepository } from 'src/interfaces/access.interface';
|
import { IAccessRepository } from 'src/interfaces/access.interface';
|
||||||
import { IPartnerRepository, PartnerDirection, PartnerIds } from 'src/interfaces/partner.interface';
|
import { IPartnerRepository, PartnerDirection, PartnerIds } from 'src/interfaces/partner.interface';
|
||||||
|
import { requireAccess } from 'src/utils/access';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PartnerService {
|
export class PartnerService {
|
||||||
private access: AccessCore;
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(IPartnerRepository) private repository: IPartnerRepository,
|
@Inject(IPartnerRepository) private repository: IPartnerRepository,
|
||||||
@Inject(IAccessRepository) accessRepository: IAccessRepository,
|
@Inject(IAccessRepository) private access: IAccessRepository,
|
||||||
) {
|
) {}
|
||||||
this.access = AccessCore.create(accessRepository);
|
|
||||||
}
|
|
||||||
|
|
||||||
async create(auth: AuthDto, sharedWithId: string): Promise<PartnerResponseDto> {
|
async create(auth: AuthDto, sharedWithId: string): Promise<PartnerResponseDto> {
|
||||||
const partnerId: PartnerIds = { sharedById: auth.user.id, sharedWithId };
|
const partnerId: PartnerIds = { sharedById: auth.user.id, sharedWithId };
|
||||||
@ -49,7 +46,7 @@ export class PartnerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async update(auth: AuthDto, sharedById: string, dto: UpdatePartnerDto): Promise<PartnerResponseDto> {
|
async update(auth: AuthDto, sharedById: string, dto: UpdatePartnerDto): Promise<PartnerResponseDto> {
|
||||||
await this.access.requirePermission(auth, Permission.PARTNER_UPDATE, sharedById);
|
await requireAccess(this.access, { auth, permission: Permission.PARTNER_UPDATE, ids: [sharedById] });
|
||||||
const partnerId: PartnerIds = { sharedById, sharedWithId: auth.user.id };
|
const partnerId: PartnerIds = { sharedById, sharedWithId: auth.user.id };
|
||||||
|
|
||||||
const entity = await this.repository.update({ ...partnerId, inTimeline: dto.inTimeline });
|
const entity = await this.repository.update({ ...partnerId, inTimeline: dto.inTimeline });
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { BadRequestException, Inject, Injectable, NotFoundException } from '@nestjs/common';
|
import { BadRequestException, Inject, Injectable, NotFoundException } from '@nestjs/common';
|
||||||
import { ImageFormat } from 'src/config';
|
import { ImageFormat } from 'src/config';
|
||||||
import { FACE_THUMBNAIL_SIZE } from 'src/constants';
|
import { FACE_THUMBNAIL_SIZE } from 'src/constants';
|
||||||
import { AccessCore } from 'src/cores/access.core';
|
|
||||||
import { StorageCore } from 'src/cores/storage.core';
|
import { StorageCore } from 'src/cores/storage.core';
|
||||||
import { SystemConfigCore } from 'src/cores/system-config.core';
|
import { SystemConfigCore } from 'src/cores/system-config.core';
|
||||||
import { BulkIdErrorReason, BulkIdResponseDto } from 'src/dtos/asset-ids.response.dto';
|
import { BulkIdErrorReason, BulkIdResponseDto } from 'src/dtos/asset-ids.response.dto';
|
||||||
@ -50,6 +49,7 @@ import { IPersonRepository, UpdateFacesData } from 'src/interfaces/person.interf
|
|||||||
import { ISearchRepository } from 'src/interfaces/search.interface';
|
import { ISearchRepository } from 'src/interfaces/search.interface';
|
||||||
import { IStorageRepository } from 'src/interfaces/storage.interface';
|
import { IStorageRepository } from 'src/interfaces/storage.interface';
|
||||||
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
|
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
|
||||||
|
import { checkAccess, requireAccess } from 'src/utils/access';
|
||||||
import { getAssetFiles } from 'src/utils/asset.util';
|
import { getAssetFiles } from 'src/utils/asset.util';
|
||||||
import { CacheControl, ImmichFileResponse } from 'src/utils/file';
|
import { CacheControl, ImmichFileResponse } from 'src/utils/file';
|
||||||
import { mimeTypes } from 'src/utils/mime-types';
|
import { mimeTypes } from 'src/utils/mime-types';
|
||||||
@ -59,12 +59,11 @@ import { IsNull } from 'typeorm';
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PersonService {
|
export class PersonService {
|
||||||
private access: AccessCore;
|
|
||||||
private configCore: SystemConfigCore;
|
private configCore: SystemConfigCore;
|
||||||
private storageCore: StorageCore;
|
private storageCore: StorageCore;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(IAccessRepository) accessRepository: IAccessRepository,
|
@Inject(IAccessRepository) private access: IAccessRepository,
|
||||||
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
|
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
|
||||||
@Inject(IMachineLearningRepository) private machineLearningRepository: IMachineLearningRepository,
|
@Inject(IMachineLearningRepository) private machineLearningRepository: IMachineLearningRepository,
|
||||||
@Inject(IMoveRepository) moveRepository: IMoveRepository,
|
@Inject(IMoveRepository) moveRepository: IMoveRepository,
|
||||||
@ -77,7 +76,6 @@ export class PersonService {
|
|||||||
@Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository,
|
@Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository,
|
||||||
@Inject(ILoggerRepository) private logger: ILoggerRepository,
|
@Inject(ILoggerRepository) private logger: ILoggerRepository,
|
||||||
) {
|
) {
|
||||||
this.access = AccessCore.create(accessRepository);
|
|
||||||
this.logger.setContext(PersonService.name);
|
this.logger.setContext(PersonService.name);
|
||||||
this.configCore = SystemConfigCore.create(systemMetadataRepository, this.logger);
|
this.configCore = SystemConfigCore.create(systemMetadataRepository, this.logger);
|
||||||
this.storageCore = StorageCore.create(
|
this.storageCore = StorageCore.create(
|
||||||
@ -114,7 +112,7 @@ export class PersonService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async reassignFaces(auth: AuthDto, personId: string, dto: AssetFaceUpdateDto): Promise<PersonResponseDto[]> {
|
async reassignFaces(auth: AuthDto, personId: string, dto: AssetFaceUpdateDto): Promise<PersonResponseDto[]> {
|
||||||
await this.access.requirePermission(auth, Permission.PERSON_UPDATE, personId);
|
await requireAccess(this.access, { auth, permission: Permission.PERSON_UPDATE, ids: [personId] });
|
||||||
const person = await this.findOrFail(personId);
|
const person = await this.findOrFail(personId);
|
||||||
const result: PersonResponseDto[] = [];
|
const result: PersonResponseDto[] = [];
|
||||||
const changeFeaturePhoto: string[] = [];
|
const changeFeaturePhoto: string[] = [];
|
||||||
@ -122,7 +120,7 @@ export class PersonService {
|
|||||||
const faces = await this.repository.getFacesByIds([{ personId: data.personId, assetId: data.assetId }]);
|
const faces = await this.repository.getFacesByIds([{ personId: data.personId, assetId: data.assetId }]);
|
||||||
|
|
||||||
for (const face of faces) {
|
for (const face of faces) {
|
||||||
await this.access.requirePermission(auth, Permission.PERSON_CREATE, face.id);
|
await requireAccess(this.access, { auth, permission: Permission.PERSON_CREATE, ids: [face.id] });
|
||||||
if (person.faceAssetId === null) {
|
if (person.faceAssetId === null) {
|
||||||
changeFeaturePhoto.push(person.id);
|
changeFeaturePhoto.push(person.id);
|
||||||
}
|
}
|
||||||
@ -143,9 +141,8 @@ export class PersonService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async reassignFacesById(auth: AuthDto, personId: string, dto: FaceDto): Promise<PersonResponseDto> {
|
async reassignFacesById(auth: AuthDto, personId: string, dto: FaceDto): Promise<PersonResponseDto> {
|
||||||
await this.access.requirePermission(auth, Permission.PERSON_UPDATE, personId);
|
await requireAccess(this.access, { auth, permission: Permission.PERSON_UPDATE, ids: [personId] });
|
||||||
|
await requireAccess(this.access, { auth, permission: Permission.PERSON_CREATE, ids: [dto.id] });
|
||||||
await this.access.requirePermission(auth, Permission.PERSON_CREATE, dto.id);
|
|
||||||
const face = await this.repository.getFaceById(dto.id);
|
const face = await this.repository.getFaceById(dto.id);
|
||||||
const person = await this.findOrFail(personId);
|
const person = await this.findOrFail(personId);
|
||||||
|
|
||||||
@ -161,7 +158,7 @@ export class PersonService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getFacesById(auth: AuthDto, dto: FaceDto): Promise<AssetFaceResponseDto[]> {
|
async getFacesById(auth: AuthDto, dto: FaceDto): Promise<AssetFaceResponseDto[]> {
|
||||||
await this.access.requirePermission(auth, Permission.ASSET_READ, dto.id);
|
await requireAccess(this.access, { auth, permission: Permission.ASSET_READ, ids: [dto.id] });
|
||||||
const faces = await this.repository.getFaces(dto.id);
|
const faces = await this.repository.getFaces(dto.id);
|
||||||
return faces.map((asset) => mapFaces(asset, auth));
|
return faces.map((asset) => mapFaces(asset, auth));
|
||||||
}
|
}
|
||||||
@ -188,17 +185,17 @@ export class PersonService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getById(auth: AuthDto, id: string): Promise<PersonResponseDto> {
|
async getById(auth: AuthDto, id: string): Promise<PersonResponseDto> {
|
||||||
await this.access.requirePermission(auth, Permission.PERSON_READ, id);
|
await requireAccess(this.access, { auth, permission: Permission.PERSON_READ, ids: [id] });
|
||||||
return this.findOrFail(id).then(mapPerson);
|
return this.findOrFail(id).then(mapPerson);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getStatistics(auth: AuthDto, id: string): Promise<PersonStatisticsResponseDto> {
|
async getStatistics(auth: AuthDto, id: string): Promise<PersonStatisticsResponseDto> {
|
||||||
await this.access.requirePermission(auth, Permission.PERSON_READ, id);
|
await requireAccess(this.access, { auth, permission: Permission.PERSON_READ, ids: [id] });
|
||||||
return this.repository.getStatistics(id);
|
return this.repository.getStatistics(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getThumbnail(auth: AuthDto, id: string): Promise<ImmichFileResponse> {
|
async getThumbnail(auth: AuthDto, id: string): Promise<ImmichFileResponse> {
|
||||||
await this.access.requirePermission(auth, Permission.PERSON_READ, id);
|
await requireAccess(this.access, { auth, permission: Permission.PERSON_READ, ids: [id] });
|
||||||
const person = await this.repository.getById(id);
|
const person = await this.repository.getById(id);
|
||||||
if (!person || !person.thumbnailPath) {
|
if (!person || !person.thumbnailPath) {
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
@ -212,7 +209,7 @@ export class PersonService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getAssets(auth: AuthDto, id: string): Promise<AssetResponseDto[]> {
|
async getAssets(auth: AuthDto, id: string): Promise<AssetResponseDto[]> {
|
||||||
await this.access.requirePermission(auth, Permission.PERSON_READ, id);
|
await requireAccess(this.access, { auth, permission: Permission.PERSON_READ, ids: [id] });
|
||||||
const assets = await this.repository.getAssets(id);
|
const assets = await this.repository.getAssets(id);
|
||||||
return assets.map((asset) => mapAsset(asset));
|
return assets.map((asset) => mapAsset(asset));
|
||||||
}
|
}
|
||||||
@ -227,13 +224,13 @@ export class PersonService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async update(auth: AuthDto, id: string, dto: PersonUpdateDto): Promise<PersonResponseDto> {
|
async update(auth: AuthDto, id: string, dto: PersonUpdateDto): Promise<PersonResponseDto> {
|
||||||
await this.access.requirePermission(auth, Permission.PERSON_UPDATE, id);
|
await requireAccess(this.access, { auth, permission: Permission.PERSON_UPDATE, ids: [id] });
|
||||||
|
|
||||||
const { name, birthDate, isHidden, featureFaceAssetId: assetId } = dto;
|
const { name, birthDate, isHidden, featureFaceAssetId: assetId } = dto;
|
||||||
// TODO: set by faceId directly
|
// TODO: set by faceId directly
|
||||||
let faceId: string | undefined = undefined;
|
let faceId: string | undefined = undefined;
|
||||||
if (assetId) {
|
if (assetId) {
|
||||||
await this.access.requirePermission(auth, Permission.ASSET_READ, assetId);
|
await requireAccess(this.access, { auth, permission: Permission.ASSET_READ, ids: [assetId] });
|
||||||
const [face] = await this.repository.getFacesByIds([{ personId: id, assetId }]);
|
const [face] = await this.repository.getFacesByIds([{ personId: id, assetId }]);
|
||||||
if (!face) {
|
if (!face) {
|
||||||
throw new BadRequestException('Invalid assetId for feature face');
|
throw new BadRequestException('Invalid assetId for feature face');
|
||||||
@ -587,13 +584,17 @@ export class PersonService {
|
|||||||
throw new BadRequestException('Cannot merge a person into themselves');
|
throw new BadRequestException('Cannot merge a person into themselves');
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.access.requirePermission(auth, Permission.PERSON_UPDATE, id);
|
await requireAccess(this.access, { auth, permission: Permission.PERSON_UPDATE, ids: [id] });
|
||||||
let primaryPerson = await this.findOrFail(id);
|
let primaryPerson = await this.findOrFail(id);
|
||||||
const primaryName = primaryPerson.name || primaryPerson.id;
|
const primaryName = primaryPerson.name || primaryPerson.id;
|
||||||
|
|
||||||
const results: BulkIdResponseDto[] = [];
|
const results: BulkIdResponseDto[] = [];
|
||||||
|
|
||||||
const allowedIds = await this.access.checkAccess(auth, Permission.PERSON_MERGE, mergeIds);
|
const allowedIds = await checkAccess(this.access, {
|
||||||
|
auth,
|
||||||
|
permission: Permission.PERSON_MERGE,
|
||||||
|
ids: mergeIds,
|
||||||
|
});
|
||||||
|
|
||||||
for (const mergeId of mergeIds) {
|
for (const mergeId of mergeIds) {
|
||||||
const hasAccess = allowedIds.has(mergeId);
|
const hasAccess = allowedIds.has(mergeId);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { AccessCore } from 'src/cores/access.core';
|
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { SessionResponseDto, mapSession } from 'src/dtos/session.dto';
|
import { SessionResponseDto, mapSession } from 'src/dtos/session.dto';
|
||||||
import { Permission } from 'src/enum';
|
import { Permission } from 'src/enum';
|
||||||
@ -8,18 +7,16 @@ import { IAccessRepository } from 'src/interfaces/access.interface';
|
|||||||
import { JobStatus } from 'src/interfaces/job.interface';
|
import { JobStatus } from 'src/interfaces/job.interface';
|
||||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
||||||
import { ISessionRepository } from 'src/interfaces/session.interface';
|
import { ISessionRepository } from 'src/interfaces/session.interface';
|
||||||
|
import { requireAccess } from 'src/utils/access';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SessionService {
|
export class SessionService {
|
||||||
private access: AccessCore;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(IAccessRepository) accessRepository: IAccessRepository,
|
@Inject(IAccessRepository) private access: IAccessRepository,
|
||||||
@Inject(ILoggerRepository) private logger: ILoggerRepository,
|
@Inject(ILoggerRepository) private logger: ILoggerRepository,
|
||||||
@Inject(ISessionRepository) private sessionRepository: ISessionRepository,
|
@Inject(ISessionRepository) private sessionRepository: ISessionRepository,
|
||||||
) {
|
) {
|
||||||
this.logger.setContext(SessionService.name);
|
this.logger.setContext(SessionService.name);
|
||||||
this.access = AccessCore.create(accessRepository);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleCleanup() {
|
async handleCleanup() {
|
||||||
@ -47,7 +44,7 @@ export class SessionService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async delete(auth: AuthDto, id: string): Promise<void> {
|
async delete(auth: AuthDto, id: string): Promise<void> {
|
||||||
await this.access.requirePermission(auth, Permission.AUTH_DEVICE_DELETE, id);
|
await requireAccess(this.access, { auth, permission: Permission.AUTH_DEVICE_DELETE, ids: [id] });
|
||||||
await this.sessionRepository.delete(id);
|
await this.sessionRepository.delete(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { BadRequestException, ForbiddenException, Inject, Injectable, UnauthorizedException } from '@nestjs/common';
|
import { BadRequestException, ForbiddenException, Inject, Injectable, UnauthorizedException } from '@nestjs/common';
|
||||||
import { DEFAULT_EXTERNAL_DOMAIN } from 'src/constants';
|
import { DEFAULT_EXTERNAL_DOMAIN } from 'src/constants';
|
||||||
import { AccessCore } from 'src/cores/access.core';
|
|
||||||
import { SystemConfigCore } from 'src/cores/system-config.core';
|
import { SystemConfigCore } from 'src/cores/system-config.core';
|
||||||
import { AssetIdErrorReason, AssetIdsResponseDto } from 'src/dtos/asset-ids.response.dto';
|
import { AssetIdErrorReason, AssetIdsResponseDto } from 'src/dtos/asset-ids.response.dto';
|
||||||
import { AssetIdsDto } from 'src/dtos/asset.dto';
|
import { AssetIdsDto } from 'src/dtos/asset.dto';
|
||||||
@ -21,22 +20,21 @@ import { ICryptoRepository } from 'src/interfaces/crypto.interface';
|
|||||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
||||||
import { ISharedLinkRepository } from 'src/interfaces/shared-link.interface';
|
import { ISharedLinkRepository } from 'src/interfaces/shared-link.interface';
|
||||||
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
|
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
|
||||||
|
import { checkAccess, requireAccess } from 'src/utils/access';
|
||||||
import { OpenGraphTags } from 'src/utils/misc';
|
import { OpenGraphTags } from 'src/utils/misc';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SharedLinkService {
|
export class SharedLinkService {
|
||||||
private access: AccessCore;
|
|
||||||
private configCore: SystemConfigCore;
|
private configCore: SystemConfigCore;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(IAccessRepository) accessRepository: IAccessRepository,
|
@Inject(IAccessRepository) private access: IAccessRepository,
|
||||||
@Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository,
|
@Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository,
|
||||||
@Inject(ILoggerRepository) private logger: ILoggerRepository,
|
@Inject(ILoggerRepository) private logger: ILoggerRepository,
|
||||||
@Inject(ISharedLinkRepository) private repository: ISharedLinkRepository,
|
@Inject(ISharedLinkRepository) private repository: ISharedLinkRepository,
|
||||||
@Inject(ISystemMetadataRepository) systemMetadataRepository: ISystemMetadataRepository,
|
@Inject(ISystemMetadataRepository) systemMetadataRepository: ISystemMetadataRepository,
|
||||||
) {
|
) {
|
||||||
this.logger.setContext(SharedLinkService.name);
|
this.logger.setContext(SharedLinkService.name);
|
||||||
this.access = AccessCore.create(accessRepository);
|
|
||||||
this.configCore = SystemConfigCore.create(systemMetadataRepository, this.logger);
|
this.configCore = SystemConfigCore.create(systemMetadataRepository, this.logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +67,7 @@ export class SharedLinkService {
|
|||||||
if (!dto.albumId) {
|
if (!dto.albumId) {
|
||||||
throw new BadRequestException('Invalid albumId');
|
throw new BadRequestException('Invalid albumId');
|
||||||
}
|
}
|
||||||
await this.access.requirePermission(auth, Permission.ALBUM_SHARE, dto.albumId);
|
await requireAccess(this.access, { auth, permission: Permission.ALBUM_SHARE, ids: [dto.albumId] });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +76,7 @@ export class SharedLinkService {
|
|||||||
throw new BadRequestException('Invalid assetIds');
|
throw new BadRequestException('Invalid assetIds');
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.access.requirePermission(auth, Permission.ASSET_SHARE, dto.assetIds);
|
await requireAccess(this.access, { auth, permission: Permission.ASSET_SHARE, ids: dto.assetIds });
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -139,7 +137,11 @@ export class SharedLinkService {
|
|||||||
|
|
||||||
const existingAssetIds = new Set(sharedLink.assets.map((asset) => asset.id));
|
const existingAssetIds = new Set(sharedLink.assets.map((asset) => asset.id));
|
||||||
const notPresentAssetIds = dto.assetIds.filter((assetId) => !existingAssetIds.has(assetId));
|
const notPresentAssetIds = dto.assetIds.filter((assetId) => !existingAssetIds.has(assetId));
|
||||||
const allowedAssetIds = await this.access.checkAccess(auth, Permission.ASSET_SHARE, notPresentAssetIds);
|
const allowedAssetIds = await checkAccess(this.access, {
|
||||||
|
auth,
|
||||||
|
permission: Permission.ASSET_SHARE,
|
||||||
|
ids: notPresentAssetIds,
|
||||||
|
});
|
||||||
|
|
||||||
const results: AssetIdsResponseDto[] = [];
|
const results: AssetIdsResponseDto[] = [];
|
||||||
for (const assetId of dto.assetIds) {
|
for (const assetId of dto.assetIds) {
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
|
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
|
||||||
import { AccessCore } from 'src/cores/access.core';
|
|
||||||
import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
|
import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { StackCreateDto, StackResponseDto, StackSearchDto, StackUpdateDto, mapStack } from 'src/dtos/stack.dto';
|
import { StackCreateDto, StackResponseDto, StackSearchDto, StackUpdateDto, mapStack } from 'src/dtos/stack.dto';
|
||||||
@ -7,18 +6,15 @@ import { Permission } from 'src/enum';
|
|||||||
import { IAccessRepository } from 'src/interfaces/access.interface';
|
import { IAccessRepository } from 'src/interfaces/access.interface';
|
||||||
import { ClientEvent, IEventRepository } from 'src/interfaces/event.interface';
|
import { ClientEvent, IEventRepository } from 'src/interfaces/event.interface';
|
||||||
import { IStackRepository } from 'src/interfaces/stack.interface';
|
import { IStackRepository } from 'src/interfaces/stack.interface';
|
||||||
|
import { requireAccess } from 'src/utils/access';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class StackService {
|
export class StackService {
|
||||||
private access: AccessCore;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(IAccessRepository) accessRepository: IAccessRepository,
|
@Inject(IAccessRepository) private access: IAccessRepository,
|
||||||
@Inject(IEventRepository) private eventRepository: IEventRepository,
|
@Inject(IEventRepository) private eventRepository: IEventRepository,
|
||||||
@Inject(IStackRepository) private stackRepository: IStackRepository,
|
@Inject(IStackRepository) private stackRepository: IStackRepository,
|
||||||
) {
|
) {}
|
||||||
this.access = AccessCore.create(accessRepository);
|
|
||||||
}
|
|
||||||
|
|
||||||
async search(auth: AuthDto, dto: StackSearchDto): Promise<StackResponseDto[]> {
|
async search(auth: AuthDto, dto: StackSearchDto): Promise<StackResponseDto[]> {
|
||||||
const stacks = await this.stackRepository.search({
|
const stacks = await this.stackRepository.search({
|
||||||
@ -30,7 +26,7 @@ export class StackService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async create(auth: AuthDto, dto: StackCreateDto): Promise<StackResponseDto> {
|
async create(auth: AuthDto, dto: StackCreateDto): Promise<StackResponseDto> {
|
||||||
await this.access.requirePermission(auth, Permission.ASSET_UPDATE, dto.assetIds);
|
await requireAccess(this.access, { auth, permission: Permission.ASSET_UPDATE, ids: dto.assetIds });
|
||||||
|
|
||||||
const stack = await this.stackRepository.create({ ownerId: auth.user.id, assetIds: dto.assetIds });
|
const stack = await this.stackRepository.create({ ownerId: auth.user.id, assetIds: dto.assetIds });
|
||||||
|
|
||||||
@ -40,13 +36,13 @@ export class StackService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async get(auth: AuthDto, id: string): Promise<StackResponseDto> {
|
async get(auth: AuthDto, id: string): Promise<StackResponseDto> {
|
||||||
await this.access.requirePermission(auth, Permission.STACK_READ, id);
|
await requireAccess(this.access, { auth, permission: Permission.STACK_READ, ids: [id] });
|
||||||
const stack = await this.findOrFail(id);
|
const stack = await this.findOrFail(id);
|
||||||
return mapStack(stack, { auth });
|
return mapStack(stack, { auth });
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(auth: AuthDto, id: string, dto: StackUpdateDto): Promise<StackResponseDto> {
|
async update(auth: AuthDto, id: string, dto: StackUpdateDto): Promise<StackResponseDto> {
|
||||||
await this.access.requirePermission(auth, Permission.STACK_UPDATE, id);
|
await requireAccess(this.access, { auth, permission: Permission.STACK_UPDATE, ids: [id] });
|
||||||
const stack = await this.findOrFail(id);
|
const stack = await this.findOrFail(id);
|
||||||
if (dto.primaryAssetId && !stack.assets.some(({ id }) => id === dto.primaryAssetId)) {
|
if (dto.primaryAssetId && !stack.assets.some(({ id }) => id === dto.primaryAssetId)) {
|
||||||
throw new BadRequestException('Primary asset must be in the stack');
|
throw new BadRequestException('Primary asset must be in the stack');
|
||||||
@ -60,14 +56,14 @@ export class StackService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async delete(auth: AuthDto, id: string): Promise<void> {
|
async delete(auth: AuthDto, id: string): Promise<void> {
|
||||||
await this.access.requirePermission(auth, Permission.STACK_DELETE, id);
|
await requireAccess(this.access, { auth, permission: Permission.STACK_DELETE, ids: [id] });
|
||||||
await this.stackRepository.delete(id);
|
await this.stackRepository.delete(id);
|
||||||
|
|
||||||
this.eventRepository.clientSend(ClientEvent.ASSET_STACK_UPDATE, auth.user.id, []);
|
this.eventRepository.clientSend(ClientEvent.ASSET_STACK_UPDATE, auth.user.id, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteAll(auth: AuthDto, dto: BulkIdsDto): Promise<void> {
|
async deleteAll(auth: AuthDto, dto: BulkIdsDto): Promise<void> {
|
||||||
await this.access.requirePermission(auth, Permission.STACK_DELETE, dto.ids);
|
await requireAccess(this.access, { auth, permission: Permission.STACK_DELETE, ids: dto.ids });
|
||||||
await this.stackRepository.deleteAll(dto.ids);
|
await this.stackRepository.deleteAll(dto.ids);
|
||||||
|
|
||||||
this.eventRepository.clientSend(ClientEvent.ASSET_STACK_UPDATE, auth.user.id, []);
|
this.eventRepository.clientSend(ClientEvent.ASSET_STACK_UPDATE, auth.user.id, []);
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { Inject } from '@nestjs/common';
|
import { Inject } from '@nestjs/common';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { AUDIT_LOG_MAX_DURATION } from 'src/constants';
|
import { AUDIT_LOG_MAX_DURATION } from 'src/constants';
|
||||||
import { AccessCore } from 'src/cores/access.core';
|
|
||||||
import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto';
|
import { AssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto';
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { AssetDeltaSyncDto, AssetDeltaSyncResponseDto, AssetFullSyncDto } from 'src/dtos/sync.dto';
|
import { AssetDeltaSyncDto, AssetDeltaSyncResponseDto, AssetFullSyncDto } from 'src/dtos/sync.dto';
|
||||||
@ -10,27 +9,24 @@ import { IAccessRepository } from 'src/interfaces/access.interface';
|
|||||||
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
||||||
import { IAuditRepository } from 'src/interfaces/audit.interface';
|
import { IAuditRepository } from 'src/interfaces/audit.interface';
|
||||||
import { IPartnerRepository } from 'src/interfaces/partner.interface';
|
import { IPartnerRepository } from 'src/interfaces/partner.interface';
|
||||||
|
import { requireAccess } from 'src/utils/access';
|
||||||
import { getMyPartnerIds } from 'src/utils/asset.util';
|
import { getMyPartnerIds } from 'src/utils/asset.util';
|
||||||
import { setIsEqual } from 'src/utils/set';
|
import { setIsEqual } from 'src/utils/set';
|
||||||
|
|
||||||
const FULL_SYNC = { needsFullSync: true, deleted: [], upserted: [] };
|
const FULL_SYNC = { needsFullSync: true, deleted: [], upserted: [] };
|
||||||
|
|
||||||
export class SyncService {
|
export class SyncService {
|
||||||
private access: AccessCore;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(IAccessRepository) accessRepository: IAccessRepository,
|
@Inject(IAccessRepository) private access: IAccessRepository,
|
||||||
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
|
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
|
||||||
@Inject(IPartnerRepository) private partnerRepository: IPartnerRepository,
|
@Inject(IPartnerRepository) private partnerRepository: IPartnerRepository,
|
||||||
@Inject(IAuditRepository) private auditRepository: IAuditRepository,
|
@Inject(IAuditRepository) private auditRepository: IAuditRepository,
|
||||||
) {
|
) {}
|
||||||
this.access = AccessCore.create(accessRepository);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getFullSync(auth: AuthDto, dto: AssetFullSyncDto): Promise<AssetResponseDto[]> {
|
async getFullSync(auth: AuthDto, dto: AssetFullSyncDto): Promise<AssetResponseDto[]> {
|
||||||
// mobile implementation is faster if this is a single id
|
// mobile implementation is faster if this is a single id
|
||||||
const userId = dto.userId || auth.user.id;
|
const userId = dto.userId || auth.user.id;
|
||||||
await this.access.requirePermission(auth, Permission.TIMELINE_READ, userId);
|
await requireAccess(this.access, { auth, permission: Permission.TIMELINE_READ, ids: [userId] });
|
||||||
const assets = await this.assetRepository.getAllForUserFullSync({
|
const assets = await this.assetRepository.getAllForUserFullSync({
|
||||||
ownerId: userId,
|
ownerId: userId,
|
||||||
updatedUntil: dto.updatedUntil,
|
updatedUntil: dto.updatedUntil,
|
||||||
@ -54,7 +50,7 @@ export class SyncService {
|
|||||||
return FULL_SYNC;
|
return FULL_SYNC;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.access.requirePermission(auth, Permission.TIMELINE_READ, dto.userIds);
|
await requireAccess(this.access, { auth, permission: Permission.TIMELINE_READ, ids: dto.userIds });
|
||||||
|
|
||||||
const limit = 10_000;
|
const limit = 10_000;
|
||||||
const upserted = await this.assetRepository.getChangedDeltaSync({ limit, updatedAfter: dto.updatedAfter, userIds });
|
const upserted = await this.assetRepository.getChangedDeltaSync({ limit, updatedAfter: dto.updatedAfter, userIds });
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { BadRequestException, Inject } from '@nestjs/common';
|
import { BadRequestException, Inject } from '@nestjs/common';
|
||||||
import { AccessCore } from 'src/cores/access.core';
|
|
||||||
import { AssetResponseDto, SanitizedAssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto';
|
import { AssetResponseDto, SanitizedAssetResponseDto, mapAsset } from 'src/dtos/asset-response.dto';
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { TimeBucketAssetDto, TimeBucketDto, TimeBucketResponseDto } from 'src/dtos/time-bucket.dto';
|
import { TimeBucketAssetDto, TimeBucketDto, TimeBucketResponseDto } from 'src/dtos/time-bucket.dto';
|
||||||
@ -7,18 +6,15 @@ import { Permission } from 'src/enum';
|
|||||||
import { IAccessRepository } from 'src/interfaces/access.interface';
|
import { IAccessRepository } from 'src/interfaces/access.interface';
|
||||||
import { IAssetRepository, TimeBucketOptions } from 'src/interfaces/asset.interface';
|
import { IAssetRepository, TimeBucketOptions } from 'src/interfaces/asset.interface';
|
||||||
import { IPartnerRepository } from 'src/interfaces/partner.interface';
|
import { IPartnerRepository } from 'src/interfaces/partner.interface';
|
||||||
|
import { requireAccess } from 'src/utils/access';
|
||||||
import { getMyPartnerIds } from 'src/utils/asset.util';
|
import { getMyPartnerIds } from 'src/utils/asset.util';
|
||||||
|
|
||||||
export class TimelineService {
|
export class TimelineService {
|
||||||
private accessCore: AccessCore;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(IAccessRepository) accessRepository: IAccessRepository,
|
@Inject(IAccessRepository) private access: IAccessRepository,
|
||||||
@Inject(IAssetRepository) private repository: IAssetRepository,
|
@Inject(IAssetRepository) private repository: IAssetRepository,
|
||||||
@Inject(IPartnerRepository) private partnerRepository: IPartnerRepository,
|
@Inject(IPartnerRepository) private partnerRepository: IPartnerRepository,
|
||||||
) {
|
) {}
|
||||||
this.accessCore = AccessCore.create(accessRepository);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getTimeBuckets(auth: AuthDto, dto: TimeBucketDto): Promise<TimeBucketResponseDto[]> {
|
async getTimeBuckets(auth: AuthDto, dto: TimeBucketDto): Promise<TimeBucketResponseDto[]> {
|
||||||
await this.timeBucketChecks(auth, dto);
|
await this.timeBucketChecks(auth, dto);
|
||||||
@ -60,15 +56,15 @@ export class TimelineService {
|
|||||||
|
|
||||||
private async timeBucketChecks(auth: AuthDto, dto: TimeBucketDto) {
|
private async timeBucketChecks(auth: AuthDto, dto: TimeBucketDto) {
|
||||||
if (dto.albumId) {
|
if (dto.albumId) {
|
||||||
await this.accessCore.requirePermission(auth, Permission.ALBUM_READ, [dto.albumId]);
|
await requireAccess(this.access, { auth, permission: Permission.ALBUM_READ, ids: [dto.albumId] });
|
||||||
} else {
|
} else {
|
||||||
dto.userId = dto.userId || auth.user.id;
|
dto.userId = dto.userId || auth.user.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dto.userId) {
|
if (dto.userId) {
|
||||||
await this.accessCore.requirePermission(auth, Permission.TIMELINE_READ, [dto.userId]);
|
await requireAccess(this.access, { auth, permission: Permission.TIMELINE_READ, ids: [dto.userId] });
|
||||||
if (dto.isArchived !== false) {
|
if (dto.isArchived !== false) {
|
||||||
await this.accessCore.requirePermission(auth, Permission.ARCHIVE_READ, [dto.userId]);
|
await requireAccess(this.access, { auth, permission: Permission.ARCHIVE_READ, ids: [dto.userId] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { Inject } from '@nestjs/common';
|
import { Inject } from '@nestjs/common';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import { AccessCore } from 'src/cores/access.core';
|
|
||||||
import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
|
import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { Permission } from 'src/enum';
|
import { Permission } from 'src/enum';
|
||||||
@ -8,23 +7,20 @@ import { IAccessRepository } from 'src/interfaces/access.interface';
|
|||||||
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
||||||
import { ClientEvent, IEventRepository } from 'src/interfaces/event.interface';
|
import { ClientEvent, IEventRepository } from 'src/interfaces/event.interface';
|
||||||
import { IJobRepository, JOBS_ASSET_PAGINATION_SIZE, JobName } from 'src/interfaces/job.interface';
|
import { IJobRepository, JOBS_ASSET_PAGINATION_SIZE, JobName } from 'src/interfaces/job.interface';
|
||||||
|
import { requireAccess } from 'src/utils/access';
|
||||||
import { usePagination } from 'src/utils/pagination';
|
import { usePagination } from 'src/utils/pagination';
|
||||||
|
|
||||||
export class TrashService {
|
export class TrashService {
|
||||||
private access: AccessCore;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(IAccessRepository) accessRepository: IAccessRepository,
|
@Inject(IAccessRepository) private access: IAccessRepository,
|
||||||
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
|
@Inject(IAssetRepository) private assetRepository: IAssetRepository,
|
||||||
@Inject(IJobRepository) private jobRepository: IJobRepository,
|
@Inject(IJobRepository) private jobRepository: IJobRepository,
|
||||||
@Inject(IEventRepository) private eventRepository: IEventRepository,
|
@Inject(IEventRepository) private eventRepository: IEventRepository,
|
||||||
) {
|
) {}
|
||||||
this.access = AccessCore.create(accessRepository);
|
|
||||||
}
|
|
||||||
|
|
||||||
async restoreAssets(auth: AuthDto, dto: BulkIdsDto): Promise<void> {
|
async restoreAssets(auth: AuthDto, dto: BulkIdsDto): Promise<void> {
|
||||||
const { ids } = dto;
|
const { ids } = dto;
|
||||||
await this.access.requirePermission(auth, Permission.ASSET_RESTORE, ids);
|
await requireAccess(this.access, { auth, permission: Permission.ASSET_DELETE, ids });
|
||||||
await this.restoreAndSend(auth, ids);
|
await this.restoreAndSend(auth, ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
import { Permission } from 'src/enum';
|
import { BadRequestException, UnauthorizedException } from '@nestjs/common';
|
||||||
import { setIsSuperset } from 'src/utils/set';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
|
import { SharedLinkEntity } from 'src/entities/shared-link.entity';
|
||||||
|
import { AlbumUserRole, Permission } from 'src/enum';
|
||||||
|
import { IAccessRepository } from 'src/interfaces/access.interface';
|
||||||
|
import { setDifference, setIsEqual, setIsSuperset, setUnion } from 'src/utils/set';
|
||||||
|
|
||||||
export type GrantedRequest = {
|
export type GrantedRequest = {
|
||||||
requested: Permission[];
|
requested: Permission[];
|
||||||
@ -13,3 +17,264 @@ export const isGranted = ({ requested, current }: GrantedRequest) => {
|
|||||||
|
|
||||||
return setIsSuperset(new Set(current), new Set(requested));
|
return setIsSuperset(new Set(current), new Set(requested));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type AccessRequest = {
|
||||||
|
auth: AuthDto;
|
||||||
|
permission: Permission;
|
||||||
|
ids: Set<string> | string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type SharedLinkAccessRequest = { sharedLink: SharedLinkEntity; permission: Permission; ids: Set<string> };
|
||||||
|
type OtherAccessRequest = { auth: AuthDto; permission: Permission; ids: Set<string> };
|
||||||
|
|
||||||
|
export const requireUploadAccess = (auth: AuthDto | null): AuthDto => {
|
||||||
|
if (!auth || (auth.sharedLink && !auth.sharedLink.allowUpload)) {
|
||||||
|
throw new UnauthorizedException();
|
||||||
|
}
|
||||||
|
return auth;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const requireAccess = async (access: IAccessRepository, request: AccessRequest) => {
|
||||||
|
const allowedIds = await checkAccess(access, request);
|
||||||
|
if (!setIsEqual(new Set(request.ids), allowedIds)) {
|
||||||
|
throw new BadRequestException(`Not found or no ${request.permission} access`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const checkAccess = async (access: IAccessRepository, { ids, auth, permission }: AccessRequest) => {
|
||||||
|
const idSet = Array.isArray(ids) ? new Set(ids) : ids;
|
||||||
|
if (idSet.size === 0) {
|
||||||
|
return new Set<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return auth.sharedLink
|
||||||
|
? checkSharedLinkAccess(access, { sharedLink: auth.sharedLink, permission, ids: idSet })
|
||||||
|
: checkOtherAccess(access, { auth, permission, ids: idSet });
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkSharedLinkAccess = async (access: IAccessRepository, request: SharedLinkAccessRequest) => {
|
||||||
|
const { sharedLink, permission, ids } = request;
|
||||||
|
const sharedLinkId = sharedLink.id;
|
||||||
|
|
||||||
|
switch (permission) {
|
||||||
|
case Permission.ASSET_READ: {
|
||||||
|
return await access.asset.checkSharedLinkAccess(sharedLinkId, ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.ASSET_VIEW: {
|
||||||
|
return await access.asset.checkSharedLinkAccess(sharedLinkId, ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.ASSET_DOWNLOAD: {
|
||||||
|
return sharedLink.allowDownload ? await access.asset.checkSharedLinkAccess(sharedLinkId, ids) : new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.ASSET_UPLOAD: {
|
||||||
|
return sharedLink.allowUpload ? ids : new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.ASSET_SHARE: {
|
||||||
|
// TODO: fix this to not use sharedLink.userId for access control
|
||||||
|
return await access.asset.checkOwnerAccess(sharedLink.userId, ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.ALBUM_READ: {
|
||||||
|
return await access.album.checkSharedLinkAccess(sharedLinkId, ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.ALBUM_DOWNLOAD: {
|
||||||
|
return sharedLink.allowDownload ? await access.album.checkSharedLinkAccess(sharedLinkId, ids) : new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.ALBUM_ADD_ASSET: {
|
||||||
|
return sharedLink.allowUpload ? await access.album.checkSharedLinkAccess(sharedLinkId, ids) : new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
return new Set<string>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkOtherAccess = async (access: IAccessRepository, request: OtherAccessRequest) => {
|
||||||
|
const { auth, permission, ids } = request;
|
||||||
|
|
||||||
|
switch (permission) {
|
||||||
|
// uses album id
|
||||||
|
case Permission.ACTIVITY_CREATE: {
|
||||||
|
return await access.activity.checkCreateAccess(auth.user.id, ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
// uses activity id
|
||||||
|
case Permission.ACTIVITY_DELETE: {
|
||||||
|
const isOwner = await access.activity.checkOwnerAccess(auth.user.id, ids);
|
||||||
|
const isAlbumOwner = await access.activity.checkAlbumOwnerAccess(auth.user.id, setDifference(ids, isOwner));
|
||||||
|
return setUnion(isOwner, isAlbumOwner);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.ASSET_READ: {
|
||||||
|
const isOwner = await access.asset.checkOwnerAccess(auth.user.id, ids);
|
||||||
|
const isAlbum = await access.asset.checkAlbumAccess(auth.user.id, setDifference(ids, isOwner));
|
||||||
|
const isPartner = await access.asset.checkPartnerAccess(auth.user.id, setDifference(ids, isOwner, isAlbum));
|
||||||
|
return setUnion(isOwner, isAlbum, isPartner);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.ASSET_SHARE: {
|
||||||
|
const isOwner = await access.asset.checkOwnerAccess(auth.user.id, ids);
|
||||||
|
const isPartner = await access.asset.checkPartnerAccess(auth.user.id, setDifference(ids, isOwner));
|
||||||
|
return setUnion(isOwner, isPartner);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.ASSET_VIEW: {
|
||||||
|
const isOwner = await access.asset.checkOwnerAccess(auth.user.id, ids);
|
||||||
|
const isAlbum = await access.asset.checkAlbumAccess(auth.user.id, setDifference(ids, isOwner));
|
||||||
|
const isPartner = await access.asset.checkPartnerAccess(auth.user.id, setDifference(ids, isOwner, isAlbum));
|
||||||
|
return setUnion(isOwner, isAlbum, isPartner);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.ASSET_DOWNLOAD: {
|
||||||
|
const isOwner = await access.asset.checkOwnerAccess(auth.user.id, ids);
|
||||||
|
const isAlbum = await access.asset.checkAlbumAccess(auth.user.id, setDifference(ids, isOwner));
|
||||||
|
const isPartner = await access.asset.checkPartnerAccess(auth.user.id, setDifference(ids, isOwner, isAlbum));
|
||||||
|
return setUnion(isOwner, isAlbum, isPartner);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.ASSET_UPDATE: {
|
||||||
|
return await access.asset.checkOwnerAccess(auth.user.id, ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.ASSET_DELETE: {
|
||||||
|
return await access.asset.checkOwnerAccess(auth.user.id, ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.ALBUM_READ: {
|
||||||
|
const isOwner = await access.album.checkOwnerAccess(auth.user.id, ids);
|
||||||
|
const isShared = await access.album.checkSharedAlbumAccess(
|
||||||
|
auth.user.id,
|
||||||
|
setDifference(ids, isOwner),
|
||||||
|
AlbumUserRole.VIEWER,
|
||||||
|
);
|
||||||
|
return setUnion(isOwner, isShared);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.ALBUM_ADD_ASSET: {
|
||||||
|
const isOwner = await access.album.checkOwnerAccess(auth.user.id, ids);
|
||||||
|
const isShared = await access.album.checkSharedAlbumAccess(
|
||||||
|
auth.user.id,
|
||||||
|
setDifference(ids, isOwner),
|
||||||
|
AlbumUserRole.EDITOR,
|
||||||
|
);
|
||||||
|
return setUnion(isOwner, isShared);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.ALBUM_UPDATE: {
|
||||||
|
return await access.album.checkOwnerAccess(auth.user.id, ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.ALBUM_DELETE: {
|
||||||
|
return await access.album.checkOwnerAccess(auth.user.id, ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.ALBUM_SHARE: {
|
||||||
|
return await access.album.checkOwnerAccess(auth.user.id, ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.ALBUM_DOWNLOAD: {
|
||||||
|
const isOwner = await access.album.checkOwnerAccess(auth.user.id, ids);
|
||||||
|
const isShared = await access.album.checkSharedAlbumAccess(
|
||||||
|
auth.user.id,
|
||||||
|
setDifference(ids, isOwner),
|
||||||
|
AlbumUserRole.VIEWER,
|
||||||
|
);
|
||||||
|
return setUnion(isOwner, isShared);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.ALBUM_REMOVE_ASSET: {
|
||||||
|
const isOwner = await access.album.checkOwnerAccess(auth.user.id, ids);
|
||||||
|
const isShared = await access.album.checkSharedAlbumAccess(
|
||||||
|
auth.user.id,
|
||||||
|
setDifference(ids, isOwner),
|
||||||
|
AlbumUserRole.EDITOR,
|
||||||
|
);
|
||||||
|
return setUnion(isOwner, isShared);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.ASSET_UPLOAD: {
|
||||||
|
return ids.has(auth.user.id) ? new Set([auth.user.id]) : new Set<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.ARCHIVE_READ: {
|
||||||
|
return ids.has(auth.user.id) ? new Set([auth.user.id]) : new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.AUTH_DEVICE_DELETE: {
|
||||||
|
return await access.authDevice.checkOwnerAccess(auth.user.id, ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.TIMELINE_READ: {
|
||||||
|
const isOwner = ids.has(auth.user.id) ? new Set([auth.user.id]) : new Set<string>();
|
||||||
|
const isPartner = await access.timeline.checkPartnerAccess(auth.user.id, setDifference(ids, isOwner));
|
||||||
|
return setUnion(isOwner, isPartner);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.TIMELINE_DOWNLOAD: {
|
||||||
|
return ids.has(auth.user.id) ? new Set([auth.user.id]) : new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.MEMORY_READ: {
|
||||||
|
return access.memory.checkOwnerAccess(auth.user.id, ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.MEMORY_UPDATE: {
|
||||||
|
return access.memory.checkOwnerAccess(auth.user.id, ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.MEMORY_DELETE: {
|
||||||
|
return access.memory.checkOwnerAccess(auth.user.id, ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.MEMORY_DELETE: {
|
||||||
|
return access.memory.checkOwnerAccess(auth.user.id, ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.PERSON_READ: {
|
||||||
|
return await access.person.checkOwnerAccess(auth.user.id, ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.PERSON_UPDATE: {
|
||||||
|
return await access.person.checkOwnerAccess(auth.user.id, ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.PERSON_MERGE: {
|
||||||
|
return await access.person.checkOwnerAccess(auth.user.id, ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.PERSON_CREATE: {
|
||||||
|
return access.person.checkFaceOwnerAccess(auth.user.id, ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.PERSON_REASSIGN: {
|
||||||
|
return access.person.checkFaceOwnerAccess(auth.user.id, ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.PARTNER_UPDATE: {
|
||||||
|
return await access.partner.checkUpdateAccess(auth.user.id, ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.STACK_READ: {
|
||||||
|
return access.stack.checkOwnerAccess(auth.user.id, ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.STACK_UPDATE: {
|
||||||
|
return access.stack.checkOwnerAccess(auth.user.id, ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
case Permission.STACK_DELETE: {
|
||||||
|
return access.stack.checkOwnerAccess(auth.user.id, ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
return new Set<string>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { AccessCore } from 'src/cores/access.core';
|
|
||||||
import { BulkIdErrorReason, BulkIdResponseDto } from 'src/dtos/asset-ids.response.dto';
|
import { BulkIdErrorReason, BulkIdResponseDto } from 'src/dtos/asset-ids.response.dto';
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { AssetFileEntity } from 'src/entities/asset-files.entity';
|
import { AssetFileEntity } from 'src/entities/asset-files.entity';
|
||||||
import { AssetFileType, Permission } from 'src/enum';
|
import { AssetFileType, Permission } from 'src/enum';
|
||||||
import { IAccessRepository } from 'src/interfaces/access.interface';
|
import { IAccessRepository } from 'src/interfaces/access.interface';
|
||||||
import { IPartnerRepository } from 'src/interfaces/partner.interface';
|
import { IPartnerRepository } from 'src/interfaces/partner.interface';
|
||||||
|
import { checkAccess } from 'src/utils/access';
|
||||||
|
|
||||||
export interface IBulkAsset {
|
export interface IBulkAsset {
|
||||||
getAssetIds: (id: string, assetIds: string[]) => Promise<Set<string>>;
|
getAssetIds: (id: string, assetIds: string[]) => Promise<Set<string>>;
|
||||||
@ -23,15 +23,17 @@ export const getAssetFiles = (files?: AssetFileEntity[]) => ({
|
|||||||
|
|
||||||
export const addAssets = async (
|
export const addAssets = async (
|
||||||
auth: AuthDto,
|
auth: AuthDto,
|
||||||
repositories: { accessRepository: IAccessRepository; repository: IBulkAsset },
|
repositories: { access: IAccessRepository; bulk: IBulkAsset },
|
||||||
dto: { parentId: string; assetIds: string[] },
|
dto: { parentId: string; assetIds: string[] },
|
||||||
) => {
|
) => {
|
||||||
const { accessRepository, repository } = repositories;
|
const { access, bulk } = repositories;
|
||||||
const access = AccessCore.create(accessRepository);
|
const existingAssetIds = await bulk.getAssetIds(dto.parentId, dto.assetIds);
|
||||||
|
|
||||||
const existingAssetIds = await repository.getAssetIds(dto.parentId, dto.assetIds);
|
|
||||||
const notPresentAssetIds = dto.assetIds.filter((id) => !existingAssetIds.has(id));
|
const notPresentAssetIds = dto.assetIds.filter((id) => !existingAssetIds.has(id));
|
||||||
const allowedAssetIds = await access.checkAccess(auth, Permission.ASSET_SHARE, notPresentAssetIds);
|
const allowedAssetIds = await checkAccess(access, {
|
||||||
|
auth,
|
||||||
|
permission: Permission.ASSET_SHARE,
|
||||||
|
ids: notPresentAssetIds,
|
||||||
|
});
|
||||||
|
|
||||||
const results: BulkIdResponseDto[] = [];
|
const results: BulkIdResponseDto[] = [];
|
||||||
for (const assetId of dto.assetIds) {
|
for (const assetId of dto.assetIds) {
|
||||||
@ -53,7 +55,7 @@ export const addAssets = async (
|
|||||||
|
|
||||||
const newAssetIds = results.filter(({ success }) => success).map(({ id }) => id);
|
const newAssetIds = results.filter(({ success }) => success).map(({ id }) => id);
|
||||||
if (newAssetIds.length > 0) {
|
if (newAssetIds.length > 0) {
|
||||||
await repository.addAssetIds(dto.parentId, newAssetIds);
|
await bulk.addAssetIds(dto.parentId, newAssetIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
@ -61,18 +63,17 @@ export const addAssets = async (
|
|||||||
|
|
||||||
export const removeAssets = async (
|
export const removeAssets = async (
|
||||||
auth: AuthDto,
|
auth: AuthDto,
|
||||||
repositories: { accessRepository: IAccessRepository; repository: IBulkAsset },
|
repositories: { access: IAccessRepository; bulk: IBulkAsset },
|
||||||
dto: { parentId: string; assetIds: string[]; canAlwaysRemove: Permission },
|
dto: { parentId: string; assetIds: string[]; canAlwaysRemove: Permission },
|
||||||
) => {
|
) => {
|
||||||
const { accessRepository, repository } = repositories;
|
const { access, bulk } = repositories;
|
||||||
const access = AccessCore.create(accessRepository);
|
|
||||||
|
|
||||||
// check if the user can always remove from the parent album, memory, etc.
|
// check if the user can always remove from the parent album, memory, etc.
|
||||||
const canAlwaysRemove = await access.checkAccess(auth, dto.canAlwaysRemove, [dto.parentId]);
|
const canAlwaysRemove = await checkAccess(access, { auth, permission: dto.canAlwaysRemove, ids: [dto.parentId] });
|
||||||
const existingAssetIds = await repository.getAssetIds(dto.parentId, dto.assetIds);
|
const existingAssetIds = await bulk.getAssetIds(dto.parentId, dto.assetIds);
|
||||||
const allowedAssetIds = canAlwaysRemove.has(dto.parentId)
|
const allowedAssetIds = canAlwaysRemove.has(dto.parentId)
|
||||||
? existingAssetIds
|
? existingAssetIds
|
||||||
: await access.checkAccess(auth, Permission.ASSET_SHARE, existingAssetIds);
|
: await checkAccess(access, { auth, permission: Permission.ASSET_SHARE, ids: existingAssetIds });
|
||||||
|
|
||||||
const results: BulkIdResponseDto[] = [];
|
const results: BulkIdResponseDto[] = [];
|
||||||
for (const assetId of dto.assetIds) {
|
for (const assetId of dto.assetIds) {
|
||||||
@ -94,7 +95,7 @@ export const removeAssets = async (
|
|||||||
|
|
||||||
const removedIds = results.filter(({ success }) => success).map(({ id }) => id);
|
const removedIds = results.filter(({ success }) => success).map(({ id }) => id);
|
||||||
if (removedIds.length > 0) {
|
if (removedIds.length > 0) {
|
||||||
await repository.removeAssetIds(dto.parentId, removedIds);
|
await bulk.removeAssetIds(dto.parentId, removedIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { AccessCore } from 'src/cores/access.core';
|
|
||||||
import { IAccessRepository } from 'src/interfaces/access.interface';
|
import { IAccessRepository } from 'src/interfaces/access.interface';
|
||||||
import { Mocked, vitest } from 'vitest';
|
import { Mocked, vitest } from 'vitest';
|
||||||
|
|
||||||
@ -14,11 +13,7 @@ export interface IAccessRepositoryMock {
|
|||||||
timeline: Mocked<IAccessRepository['timeline']>;
|
timeline: Mocked<IAccessRepository['timeline']>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const newAccessRepositoryMock = (reset = true): IAccessRepositoryMock => {
|
export const newAccessRepositoryMock = (): IAccessRepositoryMock => {
|
||||||
if (reset) {
|
|
||||||
AccessCore.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
activity: {
|
activity: {
|
||||||
checkOwnerAccess: vitest.fn().mockResolvedValue(new Set()),
|
checkOwnerAccess: vitest.fn().mockResolvedValue(new Set()),
|
||||||
|
@ -6,6 +6,16 @@ export default defineConfig({
|
|||||||
test: {
|
test: {
|
||||||
root: './',
|
root: './',
|
||||||
globals: true,
|
globals: true,
|
||||||
|
coverage: {
|
||||||
|
provider: 'v8',
|
||||||
|
include: ['src/cores/**', 'src/interfaces/**', 'src/services/**', 'src/utils/**'],
|
||||||
|
thresholds: {
|
||||||
|
lines: 80,
|
||||||
|
statements: 80,
|
||||||
|
branches: 85,
|
||||||
|
functions: 85,
|
||||||
|
},
|
||||||
|
},
|
||||||
server: {
|
server: {
|
||||||
deps: {
|
deps: {
|
||||||
fallbackCJS: true,
|
fallbackCJS: true,
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { type AlbumCountResponseDto, getAlbumCount } from '@immich/sdk';
|
import { type AlbumStatisticsResponseDto, getAlbumStatistics } from '@immich/sdk';
|
||||||
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
|
import LoadingSpinner from '$lib/components/shared-components/loading-spinner.svelte';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
|
|
||||||
export let albumCountType: keyof AlbumCountResponseDto;
|
export let albumType: keyof AlbumStatisticsResponseDto;
|
||||||
|
|
||||||
const handleAlbumCount = async () => {
|
const handleAlbumCount = async () => {
|
||||||
try {
|
try {
|
||||||
return await getAlbumCount();
|
return await getAlbumStatistics();
|
||||||
} catch {
|
} catch {
|
||||||
return { owned: 0, shared: 0, notShared: 0 };
|
return { owned: 0, shared: 0, notShared: 0 };
|
||||||
}
|
}
|
||||||
@ -18,6 +18,6 @@
|
|||||||
<LoadingSpinner />
|
<LoadingSpinner />
|
||||||
{:then data}
|
{:then data}
|
||||||
<div>
|
<div>
|
||||||
<p>{$t('albums_count', { values: { count: data[albumCountType] } })}</p>
|
<p>{$t('albums_count', { values: { count: data[albumType] } })}</p>
|
||||||
</div>
|
</div>
|
||||||
{/await}
|
{/await}
|
||||||
|
@ -79,7 +79,7 @@
|
|||||||
bind:isSelected={isSharingSelected}
|
bind:isSelected={isSharingSelected}
|
||||||
>
|
>
|
||||||
<svelte:fragment slot="moreInformation">
|
<svelte:fragment slot="moreInformation">
|
||||||
<MoreInformationAlbums albumCountType="shared" />
|
<MoreInformationAlbums albumType="shared" />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</SideBarLink>
|
</SideBarLink>
|
||||||
{/if}
|
{/if}
|
||||||
@ -100,7 +100,7 @@
|
|||||||
</SideBarLink>
|
</SideBarLink>
|
||||||
<SideBarLink title={$t('albums')} routeId="/(user)/albums" icon={mdiImageAlbum} flippedLogo>
|
<SideBarLink title={$t('albums')} routeId="/(user)/albums" icon={mdiImageAlbum} flippedLogo>
|
||||||
<svelte:fragment slot="moreInformation">
|
<svelte:fragment slot="moreInformation">
|
||||||
<MoreInformationAlbums albumCountType="owned" />
|
<MoreInformationAlbums albumType="owned" />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</SideBarLink>
|
</SideBarLink>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user