fix(server): Server freezes when getting statistic (#994)

* fix(server): Server freezes when getting statistic
* remove dead code
This commit is contained in:
Alex 2022-11-20 13:09:31 -06:00 committed by GitHub
parent b3e51cc849
commit 41ffa0c015
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 187 additions and 259 deletions

View File

@ -9,7 +9,6 @@ import 'package:openapi/api.dart';
Name | Type | Description | Notes Name | Type | Description | Notes
------------ | ------------- | ------------- | ------------- ------------ | ------------- | ------------- | -------------
**userId** | **String** | | **userId** | **String** | |
**objects** | **int** | |
**videos** | **int** | | **videos** | **int** | |
**photos** | **int** | | **photos** | **int** | |
**usageRaw** | **int** | | **usageRaw** | **int** | |

View File

@ -43,51 +43,48 @@ class AlbumResponseDto {
List<AssetResponseDto> assets; List<AssetResponseDto> assets;
@override @override
bool operator ==(Object other) => bool operator ==(Object other) => identical(this, other) || other is AlbumResponseDto &&
identical(this, other) || other.assetCount == assetCount &&
other is AlbumResponseDto && other.id == id &&
other.assetCount == assetCount && other.ownerId == ownerId &&
other.id == id && other.albumName == albumName &&
other.ownerId == ownerId && other.createdAt == createdAt &&
other.albumName == albumName && other.albumThumbnailAssetId == albumThumbnailAssetId &&
other.createdAt == createdAt && other.shared == shared &&
other.albumThumbnailAssetId == albumThumbnailAssetId && other.sharedUsers == sharedUsers &&
other.shared == shared && other.assets == assets;
other.sharedUsers == sharedUsers &&
other.assets == assets;
@override @override
int get hashCode => int get hashCode =>
// ignore: unnecessary_parenthesis // ignore: unnecessary_parenthesis
(assetCount.hashCode) + (assetCount.hashCode) +
(id.hashCode) + (id.hashCode) +
(ownerId.hashCode) + (ownerId.hashCode) +
(albumName.hashCode) + (albumName.hashCode) +
(createdAt.hashCode) + (createdAt.hashCode) +
(albumThumbnailAssetId == null ? 0 : albumThumbnailAssetId!.hashCode) + (albumThumbnailAssetId == null ? 0 : albumThumbnailAssetId!.hashCode) +
(shared.hashCode) + (shared.hashCode) +
(sharedUsers.hashCode) + (sharedUsers.hashCode) +
(assets.hashCode); (assets.hashCode);
@override @override
String toString() => String toString() => 'AlbumResponseDto[assetCount=$assetCount, id=$id, ownerId=$ownerId, albumName=$albumName, createdAt=$createdAt, albumThumbnailAssetId=$albumThumbnailAssetId, shared=$shared, sharedUsers=$sharedUsers, assets=$assets]';
'AlbumResponseDto[assetCount=$assetCount, id=$id, ownerId=$ownerId, albumName=$albumName, createdAt=$createdAt, albumThumbnailAssetId=$albumThumbnailAssetId, shared=$shared, sharedUsers=$sharedUsers, assets=$assets]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final _json = <String, dynamic>{}; final _json = <String, dynamic>{};
_json[r'assetCount'] = assetCount; _json[r'assetCount'] = assetCount;
_json[r'id'] = id; _json[r'id'] = id;
_json[r'ownerId'] = ownerId; _json[r'ownerId'] = ownerId;
_json[r'albumName'] = albumName; _json[r'albumName'] = albumName;
_json[r'createdAt'] = createdAt; _json[r'createdAt'] = createdAt;
if (albumThumbnailAssetId != null) { if (albumThumbnailAssetId != null) {
_json[r'albumThumbnailAssetId'] = albumThumbnailAssetId; _json[r'albumThumbnailAssetId'] = albumThumbnailAssetId;
} else { } else {
_json[r'albumThumbnailAssetId'] = null; _json[r'albumThumbnailAssetId'] = null;
} }
_json[r'shared'] = shared; _json[r'shared'] = shared;
_json[r'sharedUsers'] = sharedUsers; _json[r'sharedUsers'] = sharedUsers;
_json[r'assets'] = assets; _json[r'assets'] = assets;
return _json; return _json;
} }
@ -101,13 +98,13 @@ class AlbumResponseDto {
// Ensure that the map contains the required keys. // Ensure that the map contains the required keys.
// Note 1: the values aren't checked for validity beyond being non-null. // Note 1: the values aren't checked for validity beyond being non-null.
// Note 2: this code is stripped in release mode! // Note 2: this code is stripped in release mode!
// assert(() { assert(() {
// requiredKeys.forEach((key) { requiredKeys.forEach((key) {
// assert(json.containsKey(key), 'Required key "AlbumResponseDto[$key]" is missing from JSON.'); assert(json.containsKey(key), 'Required key "AlbumResponseDto[$key]" is missing from JSON.');
// assert(json[key] != null, 'Required key "AlbumResponseDto[$key]" has a null value in JSON.'); assert(json[key] != null, 'Required key "AlbumResponseDto[$key]" has a null value in JSON.');
// }); });
// return true; return true;
// }()); }());
return AlbumResponseDto( return AlbumResponseDto(
assetCount: mapValueOfType<int>(json, r'assetCount')!, assetCount: mapValueOfType<int>(json, r'assetCount')!,
@ -115,8 +112,7 @@ class AlbumResponseDto {
ownerId: mapValueOfType<String>(json, r'ownerId')!, ownerId: mapValueOfType<String>(json, r'ownerId')!,
albumName: mapValueOfType<String>(json, r'albumName')!, albumName: mapValueOfType<String>(json, r'albumName')!,
createdAt: mapValueOfType<String>(json, r'createdAt')!, createdAt: mapValueOfType<String>(json, r'createdAt')!,
albumThumbnailAssetId: albumThumbnailAssetId: mapValueOfType<String>(json, r'albumThumbnailAssetId'),
mapValueOfType<String>(json, r'albumThumbnailAssetId'),
shared: mapValueOfType<bool>(json, r'shared')!, shared: mapValueOfType<bool>(json, r'shared')!,
sharedUsers: UserResponseDto.listFromJson(json[r'sharedUsers'])!, sharedUsers: UserResponseDto.listFromJson(json[r'sharedUsers'])!,
assets: AssetResponseDto.listFromJson(json[r'assets'])!, assets: AssetResponseDto.listFromJson(json[r'assets'])!,
@ -125,10 +121,7 @@ class AlbumResponseDto {
return null; return null;
} }
static List<AlbumResponseDto>? listFromJson( static List<AlbumResponseDto>? listFromJson(dynamic json, {bool growable = false,}) {
dynamic json, {
bool growable = false,
}) {
final result = <AlbumResponseDto>[]; final result = <AlbumResponseDto>[];
if (json is List && json.isNotEmpty) { if (json is List && json.isNotEmpty) {
for (final row in json) { for (final row in json) {
@ -156,18 +149,12 @@ class AlbumResponseDto {
} }
// maps a json object with a list of AlbumResponseDto-objects as value to a dart map // maps a json object with a list of AlbumResponseDto-objects as value to a dart map
static Map<String, List<AlbumResponseDto>> mapListFromJson( static Map<String, List<AlbumResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
dynamic json, {
bool growable = false,
}) {
final map = <String, List<AlbumResponseDto>>{}; final map = <String, List<AlbumResponseDto>>{};
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 = AlbumResponseDto.listFromJson( final value = AlbumResponseDto.listFromJson(entry.value, growable: growable,);
entry.value,
growable: growable,
);
if (value != null) { if (value != null) {
map[entry.key] = value; map[entry.key] = value;
} }
@ -189,3 +176,4 @@ class AlbumResponseDto {
'assets', 'assets',
}; };
} }

View File

@ -79,74 +79,71 @@ class AssetResponseDto {
String? livePhotoVideoId; String? livePhotoVideoId;
@override @override
bool operator ==(Object other) => bool operator ==(Object other) => identical(this, other) || other is AssetResponseDto &&
identical(this, other) || other.type == type &&
other is AssetResponseDto && other.id == id &&
other.type == type && other.deviceAssetId == deviceAssetId &&
other.id == id && other.ownerId == ownerId &&
other.deviceAssetId == deviceAssetId && other.deviceId == deviceId &&
other.ownerId == ownerId && other.originalPath == originalPath &&
other.deviceId == deviceId && other.resizePath == resizePath &&
other.originalPath == originalPath && other.createdAt == createdAt &&
other.resizePath == resizePath && other.modifiedAt == modifiedAt &&
other.createdAt == createdAt && other.isFavorite == isFavorite &&
other.modifiedAt == modifiedAt && other.mimeType == mimeType &&
other.isFavorite == isFavorite && other.duration == duration &&
other.mimeType == mimeType && other.webpPath == webpPath &&
other.duration == duration && other.encodedVideoPath == encodedVideoPath &&
other.webpPath == webpPath && other.exifInfo == exifInfo &&
other.encodedVideoPath == encodedVideoPath && other.smartInfo == smartInfo &&
other.exifInfo == exifInfo && other.livePhotoVideoId == livePhotoVideoId;
other.smartInfo == smartInfo &&
other.livePhotoVideoId == livePhotoVideoId;
@override @override
int get hashCode => int get hashCode =>
// ignore: unnecessary_parenthesis // ignore: unnecessary_parenthesis
(type.hashCode) + (type.hashCode) +
(id.hashCode) + (id.hashCode) +
(deviceAssetId.hashCode) + (deviceAssetId.hashCode) +
(ownerId.hashCode) + (ownerId.hashCode) +
(deviceId.hashCode) + (deviceId.hashCode) +
(originalPath.hashCode) + (originalPath.hashCode) +
(resizePath == null ? 0 : resizePath!.hashCode) + (resizePath == null ? 0 : resizePath!.hashCode) +
(createdAt.hashCode) + (createdAt.hashCode) +
(modifiedAt.hashCode) + (modifiedAt.hashCode) +
(isFavorite.hashCode) + (isFavorite.hashCode) +
(mimeType == null ? 0 : mimeType!.hashCode) + (mimeType == null ? 0 : mimeType!.hashCode) +
(duration.hashCode) + (duration.hashCode) +
(webpPath == null ? 0 : webpPath!.hashCode) + (webpPath == null ? 0 : webpPath!.hashCode) +
(encodedVideoPath == null ? 0 : encodedVideoPath!.hashCode) + (encodedVideoPath == null ? 0 : encodedVideoPath!.hashCode) +
(exifInfo == null ? 0 : exifInfo!.hashCode) + (exifInfo == null ? 0 : exifInfo!.hashCode) +
(smartInfo == null ? 0 : smartInfo!.hashCode) + (smartInfo == null ? 0 : smartInfo!.hashCode) +
(livePhotoVideoId == null ? 0 : livePhotoVideoId!.hashCode); (livePhotoVideoId == null ? 0 : livePhotoVideoId!.hashCode);
@override @override
String toString() => String toString() => 'AssetResponseDto[type=$type, id=$id, deviceAssetId=$deviceAssetId, ownerId=$ownerId, deviceId=$deviceId, originalPath=$originalPath, resizePath=$resizePath, createdAt=$createdAt, modifiedAt=$modifiedAt, isFavorite=$isFavorite, mimeType=$mimeType, duration=$duration, webpPath=$webpPath, encodedVideoPath=$encodedVideoPath, exifInfo=$exifInfo, smartInfo=$smartInfo, livePhotoVideoId=$livePhotoVideoId]';
'AssetResponseDto[type=$type, id=$id, deviceAssetId=$deviceAssetId, ownerId=$ownerId, deviceId=$deviceId, originalPath=$originalPath, resizePath=$resizePath, createdAt=$createdAt, modifiedAt=$modifiedAt, isFavorite=$isFavorite, mimeType=$mimeType, duration=$duration, webpPath=$webpPath, encodedVideoPath=$encodedVideoPath, exifInfo=$exifInfo, smartInfo=$smartInfo, livePhotoVideoId=$livePhotoVideoId]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final _json = <String, dynamic>{}; final _json = <String, dynamic>{};
_json[r'type'] = type; _json[r'type'] = type;
_json[r'id'] = id; _json[r'id'] = id;
_json[r'deviceAssetId'] = deviceAssetId; _json[r'deviceAssetId'] = deviceAssetId;
_json[r'ownerId'] = ownerId; _json[r'ownerId'] = ownerId;
_json[r'deviceId'] = deviceId; _json[r'deviceId'] = deviceId;
_json[r'originalPath'] = originalPath; _json[r'originalPath'] = originalPath;
if (resizePath != null) { if (resizePath != null) {
_json[r'resizePath'] = resizePath; _json[r'resizePath'] = resizePath;
} else { } else {
_json[r'resizePath'] = null; _json[r'resizePath'] = null;
} }
_json[r'createdAt'] = createdAt; _json[r'createdAt'] = createdAt;
_json[r'modifiedAt'] = modifiedAt; _json[r'modifiedAt'] = modifiedAt;
_json[r'isFavorite'] = isFavorite; _json[r'isFavorite'] = isFavorite;
if (mimeType != null) { if (mimeType != null) {
_json[r'mimeType'] = mimeType; _json[r'mimeType'] = mimeType;
} else { } else {
_json[r'mimeType'] = null; _json[r'mimeType'] = null;
} }
_json[r'duration'] = duration; _json[r'duration'] = duration;
if (webpPath != null) { if (webpPath != null) {
_json[r'webpPath'] = webpPath; _json[r'webpPath'] = webpPath;
} else { } else {
@ -185,13 +182,13 @@ class AssetResponseDto {
// Ensure that the map contains the required keys. // Ensure that the map contains the required keys.
// Note 1: the values aren't checked for validity beyond being non-null. // Note 1: the values aren't checked for validity beyond being non-null.
// Note 2: this code is stripped in release mode! // Note 2: this code is stripped in release mode!
// assert(() { assert(() {
// requiredKeys.forEach((key) { requiredKeys.forEach((key) {
// assert(json.containsKey(key), 'Required key "AssetResponseDto[$key]" is missing from JSON.'); assert(json.containsKey(key), 'Required key "AssetResponseDto[$key]" is missing from JSON.');
// assert(json[key] != null, 'Required key "AssetResponseDto[$key]" has a null value in JSON.'); assert(json[key] != null, 'Required key "AssetResponseDto[$key]" has a null value in JSON.');
// }); });
// return true; return true;
// }()); }());
return AssetResponseDto( return AssetResponseDto(
type: AssetTypeEnum.fromJson(json[r'type'])!, type: AssetTypeEnum.fromJson(json[r'type'])!,
@ -216,10 +213,7 @@ class AssetResponseDto {
return null; return null;
} }
static List<AssetResponseDto>? listFromJson( static List<AssetResponseDto>? listFromJson(dynamic json, {bool growable = false,}) {
dynamic json, {
bool growable = false,
}) {
final result = <AssetResponseDto>[]; final result = <AssetResponseDto>[];
if (json is List && json.isNotEmpty) { if (json is List && json.isNotEmpty) {
for (final row in json) { for (final row in json) {
@ -247,18 +241,12 @@ class AssetResponseDto {
} }
// maps a json object with a list of AssetResponseDto-objects as value to a dart map // maps a json object with a list of AssetResponseDto-objects as value to a dart map
static Map<String, List<AssetResponseDto>> mapListFromJson( static Map<String, List<AssetResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
dynamic json, {
bool growable = false,
}) {
final map = <String, List<AssetResponseDto>>{}; final map = <String, List<AssetResponseDto>>{};
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 = AssetResponseDto.listFromJson( final value = AssetResponseDto.listFromJson(entry.value, growable: growable,);
entry.value,
growable: growable,
);
if (value != null) { if (value != null) {
map[entry.key] = value; map[entry.key] = value;
} }
@ -286,3 +274,4 @@ class AssetResponseDto {
'livePhotoVideoId', 'livePhotoVideoId',
}; };
} }

View File

@ -14,7 +14,6 @@ class UsageByUserDto {
/// Returns a new [UsageByUserDto] instance. /// Returns a new [UsageByUserDto] instance.
UsageByUserDto({ UsageByUserDto({
required this.userId, required this.userId,
required this.objects,
required this.videos, required this.videos,
required this.photos, required this.photos,
required this.usageRaw, required this.usageRaw,
@ -23,8 +22,6 @@ class UsageByUserDto {
String userId; String userId;
int objects;
int videos; int videos;
int photos; int photos;
@ -36,7 +33,6 @@ class UsageByUserDto {
@override @override
bool operator ==(Object other) => identical(this, other) || other is UsageByUserDto && bool operator ==(Object other) => identical(this, other) || other is UsageByUserDto &&
other.userId == userId && other.userId == userId &&
other.objects == objects &&
other.videos == videos && other.videos == videos &&
other.photos == photos && other.photos == photos &&
other.usageRaw == usageRaw && other.usageRaw == usageRaw &&
@ -46,19 +42,17 @@ class UsageByUserDto {
int get hashCode => int get hashCode =>
// ignore: unnecessary_parenthesis // ignore: unnecessary_parenthesis
(userId.hashCode) + (userId.hashCode) +
(objects.hashCode) +
(videos.hashCode) + (videos.hashCode) +
(photos.hashCode) + (photos.hashCode) +
(usageRaw.hashCode) + (usageRaw.hashCode) +
(usage.hashCode); (usage.hashCode);
@override @override
String toString() => 'UsageByUserDto[userId=$userId, objects=$objects, videos=$videos, photos=$photos, usageRaw=$usageRaw, usage=$usage]'; String toString() => 'UsageByUserDto[userId=$userId, videos=$videos, photos=$photos, usageRaw=$usageRaw, usage=$usage]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final _json = <String, dynamic>{}; final _json = <String, dynamic>{};
_json[r'userId'] = userId; _json[r'userId'] = userId;
_json[r'objects'] = objects;
_json[r'videos'] = videos; _json[r'videos'] = videos;
_json[r'photos'] = photos; _json[r'photos'] = photos;
_json[r'usageRaw'] = usageRaw; _json[r'usageRaw'] = usageRaw;
@ -86,7 +80,6 @@ class UsageByUserDto {
return UsageByUserDto( return UsageByUserDto(
userId: mapValueOfType<String>(json, r'userId')!, userId: mapValueOfType<String>(json, r'userId')!,
objects: mapValueOfType<int>(json, r'objects')!,
videos: mapValueOfType<int>(json, r'videos')!, videos: mapValueOfType<int>(json, r'videos')!,
photos: mapValueOfType<int>(json, r'photos')!, photos: mapValueOfType<int>(json, r'photos')!,
usageRaw: mapValueOfType<int>(json, r'usageRaw')!, usageRaw: mapValueOfType<int>(json, r'usageRaw')!,
@ -141,7 +134,6 @@ class UsageByUserDto {
/// The list of required keys that must be present in a JSON. /// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{ static const requiredKeys = <String>{
'userId', 'userId',
'objects',
'videos', 'videos',
'photos', 'photos',
'usageRaw', 'usageRaw',

View File

@ -43,46 +43,43 @@ class UserResponseDto {
DateTime? deletedAt; DateTime? deletedAt;
@override @override
bool operator ==(Object other) => bool operator ==(Object other) => identical(this, other) || other is UserResponseDto &&
identical(this, other) || other.id == id &&
other is UserResponseDto && other.email == email &&
other.id == id && other.firstName == firstName &&
other.email == email && other.lastName == lastName &&
other.firstName == firstName && other.createdAt == createdAt &&
other.lastName == lastName && other.profileImagePath == profileImagePath &&
other.createdAt == createdAt && other.shouldChangePassword == shouldChangePassword &&
other.profileImagePath == profileImagePath && other.isAdmin == isAdmin &&
other.shouldChangePassword == shouldChangePassword && other.deletedAt == deletedAt;
other.isAdmin == isAdmin &&
other.deletedAt == deletedAt;
@override @override
int get hashCode => int get hashCode =>
// ignore: unnecessary_parenthesis // ignore: unnecessary_parenthesis
(id.hashCode) + (id.hashCode) +
(email.hashCode) + (email.hashCode) +
(firstName.hashCode) + (firstName.hashCode) +
(lastName.hashCode) + (lastName.hashCode) +
(createdAt.hashCode) + (createdAt.hashCode) +
(profileImagePath.hashCode) + (profileImagePath.hashCode) +
(shouldChangePassword.hashCode) + (shouldChangePassword.hashCode) +
(isAdmin.hashCode) + (isAdmin.hashCode) +
(deletedAt == null ? 0 : deletedAt!.hashCode); (deletedAt == null ? 0 : deletedAt!.hashCode);
@override @override
String toString() => String toString() => 'UserResponseDto[id=$id, email=$email, firstName=$firstName, lastName=$lastName, createdAt=$createdAt, profileImagePath=$profileImagePath, shouldChangePassword=$shouldChangePassword, isAdmin=$isAdmin, deletedAt=$deletedAt]';
'UserResponseDto[id=$id, email=$email, firstName=$firstName, lastName=$lastName, createdAt=$createdAt, profileImagePath=$profileImagePath, shouldChangePassword=$shouldChangePassword, isAdmin=$isAdmin, deletedAt=$deletedAt]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final _json = <String, dynamic>{}; final _json = <String, dynamic>{};
_json[r'id'] = id; _json[r'id'] = id;
_json[r'email'] = email; _json[r'email'] = email;
_json[r'firstName'] = firstName; _json[r'firstName'] = firstName;
_json[r'lastName'] = lastName; _json[r'lastName'] = lastName;
_json[r'createdAt'] = createdAt; _json[r'createdAt'] = createdAt;
_json[r'profileImagePath'] = profileImagePath; _json[r'profileImagePath'] = profileImagePath;
_json[r'shouldChangePassword'] = shouldChangePassword; _json[r'shouldChangePassword'] = shouldChangePassword;
_json[r'isAdmin'] = isAdmin; _json[r'isAdmin'] = isAdmin;
if (deletedAt != null) { if (deletedAt != null) {
_json[r'deletedAt'] = deletedAt!.toUtc().toIso8601String(); _json[r'deletedAt'] = deletedAt!.toUtc().toIso8601String();
} else { } else {
@ -101,13 +98,13 @@ class UserResponseDto {
// Ensure that the map contains the required keys. // Ensure that the map contains the required keys.
// Note 1: the values aren't checked for validity beyond being non-null. // Note 1: the values aren't checked for validity beyond being non-null.
// Note 2: this code is stripped in release mode! // Note 2: this code is stripped in release mode!
// assert(() { assert(() {
// requiredKeys.forEach((key) { requiredKeys.forEach((key) {
// assert(json.containsKey(key), 'Required key "UserResponseDto[$key]" is missing from JSON.'); assert(json.containsKey(key), 'Required key "UserResponseDto[$key]" is missing from JSON.');
// assert(json[key] != null, 'Required key "UserResponseDto[$key]" has a null value in JSON.'); assert(json[key] != null, 'Required key "UserResponseDto[$key]" has a null value in JSON.');
// }); });
// return true; return true;
// }()); }());
return UserResponseDto( return UserResponseDto(
id: mapValueOfType<String>(json, r'id')!, id: mapValueOfType<String>(json, r'id')!,
@ -116,8 +113,7 @@ class UserResponseDto {
lastName: mapValueOfType<String>(json, r'lastName')!, lastName: mapValueOfType<String>(json, r'lastName')!,
createdAt: mapValueOfType<String>(json, r'createdAt')!, createdAt: mapValueOfType<String>(json, r'createdAt')!,
profileImagePath: mapValueOfType<String>(json, r'profileImagePath')!, profileImagePath: mapValueOfType<String>(json, r'profileImagePath')!,
shouldChangePassword: shouldChangePassword: mapValueOfType<bool>(json, r'shouldChangePassword')!,
mapValueOfType<bool>(json, r'shouldChangePassword')!,
isAdmin: mapValueOfType<bool>(json, r'isAdmin')!, isAdmin: mapValueOfType<bool>(json, r'isAdmin')!,
deletedAt: mapDateTime(json, r'deletedAt', ''), deletedAt: mapDateTime(json, r'deletedAt', ''),
); );
@ -125,10 +121,7 @@ class UserResponseDto {
return null; return null;
} }
static List<UserResponseDto>? listFromJson( static List<UserResponseDto>? listFromJson(dynamic json, {bool growable = false,}) {
dynamic json, {
bool growable = false,
}) {
final result = <UserResponseDto>[]; final result = <UserResponseDto>[];
if (json is List && json.isNotEmpty) { if (json is List && json.isNotEmpty) {
for (final row in json) { for (final row in json) {
@ -156,18 +149,12 @@ class UserResponseDto {
} }
// maps a json object with a list of UserResponseDto-objects as value to a dart map // maps a json object with a list of UserResponseDto-objects as value to a dart map
static Map<String, List<UserResponseDto>> mapListFromJson( static Map<String, List<UserResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
dynamic json, {
bool growable = false,
}) {
final map = <String, List<UserResponseDto>>{}; final map = <String, List<UserResponseDto>>{};
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 = UserResponseDto.listFromJson( final value = UserResponseDto.listFromJson(entry.value, growable: growable,);
entry.value,
growable: growable,
);
if (value != null) { if (value != null) {
map[entry.key] = value; map[entry.key] = value;
} }
@ -189,3 +176,4 @@ class UserResponseDto {
'deletedAt', 'deletedAt',
}; };
} }

View File

@ -5,7 +5,6 @@ export class ServerStatsResponseDto {
constructor() { constructor() {
this.photos = 0; this.photos = 0;
this.videos = 0; this.videos = 0;
this.objects = 0;
this.usageByUser = []; this.usageByUser = [];
this.usageRaw = 0; this.usageRaw = 0;
this.usage = ''; this.usage = '';
@ -34,7 +33,6 @@ export class ServerStatsResponseDto {
{ {
photos: 1, photos: 1,
videos: 1, videos: 1,
objects: 1,
diskUsageRaw: 1, diskUsageRaw: 1,
}, },
], ],

View File

@ -3,16 +3,15 @@ import { ApiProperty } from '@nestjs/swagger';
export class UsageByUserDto { export class UsageByUserDto {
constructor(userId: string) { constructor(userId: string) {
this.userId = userId; this.userId = userId;
this.objects = 0;
this.videos = 0; this.videos = 0;
this.photos = 0; this.photos = 0;
this.usageRaw = 0;
this.usage = '0B';
} }
@ApiProperty({ type: 'string' }) @ApiProperty({ type: 'string' })
userId: string; userId: string;
@ApiProperty({ type: 'integer' }) @ApiProperty({ type: 'integer' })
objects: number;
@ApiProperty({ type: 'integer' })
videos: number; videos: number;
@ApiProperty({ type: 'integer' }) @ApiProperty({ type: 'integer' })
photos: number; photos: number;

View File

@ -7,8 +7,6 @@ import { UsageByUserDto } from './response-dto/usage-by-user-response.dto';
import { AssetEntity } from '@app/database/entities/asset.entity'; import { AssetEntity } from '@app/database/entities/asset.entity';
import { Repository } from 'typeorm'; import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import path from 'path';
import { readdirSync, statSync } from 'fs';
import { asHumanReadable } from '../../utils/human-readable.util'; import { asHumanReadable } from '../../utils/human-readable.util';
@Injectable() @Injectable()
@ -35,59 +33,46 @@ export class ServerInfoService {
} }
async getStats(): Promise<ServerStatsResponseDto> { async getStats(): Promise<ServerStatsResponseDto> {
const res = await this.assetRepository const serverStats = new ServerStatsResponseDto();
.createQueryBuilder('asset')
.select(`COUNT(asset.id)`, 'count') type UserStatsQueryResponse = {
.addSelect(`asset.type`, 'type') assetType: string;
.addSelect(`asset.userId`, 'userId') assetCount: string;
.groupBy('asset.type, asset.userId') totalSizeInBytes: string;
.addGroupBy('asset.type') userId: string;
};
const userStatsQueryResponse: UserStatsQueryResponse[] = await this.assetRepository
.createQueryBuilder('a')
.select('COUNT(a.id)', 'assetCount')
.addSelect('SUM(ei.fileSizeInByte)', 'totalSizeInBytes')
.addSelect('a."userId"')
.addSelect('a.type', 'assetType')
.where('a.isVisible = true')
.leftJoin('a.exifInfo', 'ei')
.groupBy('a."userId"')
.addGroupBy('a.type')
.getRawMany(); .getRawMany();
const serverStats = new ServerStatsResponseDto();
const tmpMap = new Map<string, UsageByUserDto>(); const tmpMap = new Map<string, UsageByUserDto>();
const getUsageByUser = (id: string) => tmpMap.get(id) || new UsageByUserDto(id); const getUsageByUser = (id: string) => tmpMap.get(id) || new UsageByUserDto(id);
res.map((item) => {
const usage: UsageByUserDto = getUsageByUser(item.userId); userStatsQueryResponse.forEach((r) => {
if (item.type === 'IMAGE') { const usageByUser = getUsageByUser(r.userId);
usage.photos = parseInt(item.count); usageByUser.photos += r.assetType === 'IMAGE' ? parseInt(r.assetCount) : 0;
serverStats.photos += usage.photos; usageByUser.videos += r.assetType === 'VIDEO' ? parseInt(r.assetCount) : 0;
} else if (item.type === 'VIDEO') { usageByUser.usageRaw += parseInt(r.totalSizeInBytes);
usage.videos = parseInt(item.count); usageByUser.usage = asHumanReadable(usageByUser.usageRaw);
serverStats.videos += usage.videos;
} serverStats.photos += r.assetType === 'IMAGE' ? parseInt(r.assetCount) : 0;
tmpMap.set(item.userId, usage); serverStats.videos += r.assetType === 'VIDEO' ? parseInt(r.assetCount) : 0;
serverStats.usageRaw += parseInt(r.totalSizeInBytes);
serverStats.usage = asHumanReadable(serverStats.usageRaw);
tmpMap.set(r.userId, usageByUser);
}); });
for (const userId of tmpMap.keys()) {
const usage = getUsageByUser(userId);
const userDiskUsage = await ServerInfoService.getDirectoryStats(path.join(APP_UPLOAD_LOCATION, userId));
usage.usageRaw = userDiskUsage.size;
usage.objects = userDiskUsage.fileCount;
usage.usage = asHumanReadable(usage.usageRaw);
serverStats.usageRaw += usage.usageRaw;
serverStats.objects += usage.objects;
}
serverStats.usage = asHumanReadable(serverStats.usageRaw);
serverStats.usageByUser = Array.from(tmpMap.values()); serverStats.usageByUser = Array.from(tmpMap.values());
return serverStats; return serverStats;
} }
private static async getDirectoryStats(dirPath: string) {
let size = 0;
let fileCount = 0;
for (const filename of readdirSync(dirPath)) {
const absFilename = path.join(dirPath, filename);
const fileStat = statSync(absFilename);
if (fileStat.isFile()) {
size += fileStat.size;
fileCount += 1;
} else if (fileStat.isDirectory()) {
const subDirStat = await ServerInfoService.getDirectoryStats(absFilename);
size += subDirStat.size;
fileCount += subDirStat.fileCount;
}
}
return { size, fileCount };
}
} }

File diff suppressed because one or more lines are too long

View File

@ -1632,12 +1632,6 @@ export interface UsageByUserDto {
* @memberof UsageByUserDto * @memberof UsageByUserDto
*/ */
'userId': string; 'userId': string;
/**
*
* @type {number}
* @memberof UsageByUserDto
*/
'objects': number;
/** /**
* *
* @type {number} * @type {number}

View File

@ -2,7 +2,6 @@
import { ServerStatsResponseDto, UserResponseDto } from '@api'; import { ServerStatsResponseDto, UserResponseDto } from '@api';
import CameraIris from 'svelte-material-icons/CameraIris.svelte'; import CameraIris from 'svelte-material-icons/CameraIris.svelte';
import PlayCircle from 'svelte-material-icons/PlayCircle.svelte'; import PlayCircle from 'svelte-material-icons/PlayCircle.svelte';
import FileImageOutline from 'svelte-material-icons/FileImageOutline.svelte';
import Memory from 'svelte-material-icons/Memory.svelte'; import Memory from 'svelte-material-icons/Memory.svelte';
import StatsCard from './stats-card.svelte'; import StatsCard from './stats-card.svelte';
export let stats: ServerStatsResponseDto; export let stats: ServerStatsResponseDto;
@ -27,7 +26,6 @@
<div class="flex mt-5 justify-between"> <div class="flex mt-5 justify-between">
<StatsCard logo={CameraIris} title={'PHOTOS'} value={stats.photos.toString()} /> <StatsCard logo={CameraIris} title={'PHOTOS'} value={stats.photos.toString()} />
<StatsCard logo={PlayCircle} title={'VIDEOS'} value={stats.videos.toString()} /> <StatsCard logo={PlayCircle} title={'VIDEOS'} value={stats.videos.toString()} />
<StatsCard logo={FileImageOutline} title={'OBJECTS'} value={stats.objects.toString()} />
<StatsCard logo={Memory} title={'STORAGE'} value={spaceUsage} unit={spaceUnit} /> <StatsCard logo={Memory} title={'STORAGE'} value={spaceUsage} unit={spaceUnit} />
</div> </div>
</div> </div>
@ -39,11 +37,10 @@
class="border rounded-md mb-4 bg-gray-50 dark:bg-immich-dark-gray dark:border-immich-dark-gray flex text-immich-primary dark:text-immich-dark-primary w-full h-12" class="border rounded-md mb-4 bg-gray-50 dark:bg-immich-dark-gray dark:border-immich-dark-gray flex text-immich-primary dark:text-immich-dark-primary w-full h-12"
> >
<tr class="flex w-full place-items-center"> <tr class="flex w-full place-items-center">
<th class="text-center w-1/5 font-medium text-sm">User</th> <th class="text-center w-1/4 font-medium text-sm">User</th>
<th class="text-center w-1/5 font-medium text-sm">Photos</th> <th class="text-center w-1/4 font-medium text-sm">Photos</th>
<th class="text-center w-1/5 font-medium text-sm">Videos</th> <th class="text-center w-1/4 font-medium text-sm">Videos</th>
<th class="text-center w-1/5 font-medium text-sm">Objects</th> <th class="text-center w-1/4 font-medium text-sm">Size</th>
<th class="text-center w-1/5 font-medium text-sm">Size</th>
</tr> </tr>
</thead> </thead>
<tbody <tbody
@ -57,11 +54,10 @@
: 'bg-immich-bg dark:bg-immich-dark-gray/50' : 'bg-immich-bg dark:bg-immich-dark-gray/50'
}`} }`}
> >
<td class="text-sm px-2 w-1/5 text-ellipsis">{getFullName(user.userId)}</td> <td class="text-sm px-2 w-1/4 text-ellipsis">{getFullName(user.userId)}</td>
<td class="text-sm px-2 w-1/5 text-ellipsis">{user.photos}</td> <td class="text-sm px-2 w-1/4 text-ellipsis">{user.photos}</td>
<td class="text-sm px-2 w-1/5 text-ellipsis">{user.videos}</td> <td class="text-sm px-2 w-1/4 text-ellipsis">{user.videos}</td>
<td class="text-sm px-2 w-1/5 text-ellipsis">{user.objects}</td> <td class="text-sm px-2 w-1/4 text-ellipsis">{user.usage}</td>
<td class="text-sm px-2 w-1/5 text-ellipsis">{user.usage}</td>
</tr> </tr>
{/each} {/each}
</tbody> </tbody>

View File

@ -7,7 +7,7 @@
$: zeros = () => { $: zeros = () => {
let result = ''; let result = '';
const maxLength = 9; const maxLength = 13;
const valueLength = parseInt(value).toString().length; const valueLength = parseInt(value).toString().length;
const zeroLength = maxLength - valueLength; const zeroLength = maxLength - valueLength;
for (let i = 0; i < zeroLength; i++) { for (let i = 0; i < zeroLength; i++) {
@ -18,7 +18,7 @@
</script> </script>
<div <div
class="w-[180px] h-[140px] bg-immich-gray dark:bg-immich-dark-gray rounded-3xl p-5 flex flex-col justify-between" class="w-[250px] h-[140px] bg-immich-gray dark:bg-immich-dark-gray rounded-3xl p-5 flex flex-col justify-between"
> >
<div class="flex place-items-center gap-4 text-immich-primary dark:text-immich-dark-primary"> <div class="flex place-items-center gap-4 text-immich-primary dark:text-immich-dark-primary">
<svelte:component this={logo} size="40" /> <svelte:component this={logo} size="40" />