瀏覽代碼

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

* fix(server): Server freezes when getting statistic
* remove dead code
Alex 2 年之前
父節點
當前提交
41ffa0c015

+ 0 - 1
mobile/openapi/doc/UsageByUserDto.md

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

+ 41 - 53
mobile/openapi/lib/model/album_response_dto.dart

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

+ 58 - 69
mobile/openapi/lib/model/asset_response_dto.dart

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

+ 1 - 9
mobile/openapi/lib/model/usage_by_user_dto.dart

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

+ 41 - 53
mobile/openapi/lib/model/user_response_dto.dart

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

+ 0 - 2
server/apps/immich/src/api-v1/server-info/response-dto/server-stats-response.dto.ts

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

+ 2 - 3
server/apps/immich/src/api-v1/server-info/response-dto/usage-by-user-response.dto.ts

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

+ 33 - 48
server/apps/immich/src/api-v1/server-info/server-info.service.ts

@@ -7,8 +7,6 @@ import { UsageByUserDto } from './response-dto/usage-by-user-response.dto';
 import { AssetEntity } from '@app/database/entities/asset.entity';
 import { Repository } from 'typeorm';
 import { InjectRepository } from '@nestjs/typeorm';
-import path from 'path';
-import { readdirSync, statSync } from 'fs';
 import { asHumanReadable } from '../../utils/human-readable.util';
 
 @Injectable()
@@ -35,59 +33,46 @@ export class ServerInfoService {
   }
 
   async getStats(): Promise<ServerStatsResponseDto> {
-    const res = await this.assetRepository
-      .createQueryBuilder('asset')
-      .select(`COUNT(asset.id)`, 'count')
-      .addSelect(`asset.type`, 'type')
-      .addSelect(`asset.userId`, 'userId')
-      .groupBy('asset.type, asset.userId')
-      .addGroupBy('asset.type')
+    const serverStats = new ServerStatsResponseDto();
+
+    type UserStatsQueryResponse = {
+      assetType: string;
+      assetCount: string;
+      totalSizeInBytes: string;
+      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();
 
-    const serverStats = new ServerStatsResponseDto();
     const tmpMap = new Map<string, UsageByUserDto>();
     const getUsageByUser = (id: string) => tmpMap.get(id) || new UsageByUserDto(id);
-    res.map((item) => {
-      const usage: UsageByUserDto = getUsageByUser(item.userId);
-      if (item.type === 'IMAGE') {
-        usage.photos = parseInt(item.count);
-        serverStats.photos += usage.photos;
-      } else if (item.type === 'VIDEO') {
-        usage.videos = parseInt(item.count);
-        serverStats.videos += usage.videos;
-      }
-      tmpMap.set(item.userId, usage);
+
+    userStatsQueryResponse.forEach((r) => {
+      const usageByUser = getUsageByUser(r.userId);
+      usageByUser.photos += r.assetType === 'IMAGE' ? parseInt(r.assetCount) : 0;
+      usageByUser.videos += r.assetType === 'VIDEO' ? parseInt(r.assetCount) : 0;
+      usageByUser.usageRaw += parseInt(r.totalSizeInBytes);
+      usageByUser.usage = asHumanReadable(usageByUser.usageRaw);
+
+      serverStats.photos += r.assetType === 'IMAGE' ? parseInt(r.assetCount) : 0;
+      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());
-    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 };
+    return serverStats;
   }
 }

File diff suppressed because it is too large
+ 0 - 0
server/immich-openapi-specs.json


+ 0 - 6
web/src/api/open-api/api.ts

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

+ 8 - 12
web/src/lib/components/admin-page/server-stats/server-stats-panel.svelte

@@ -2,7 +2,6 @@
 	import { ServerStatsResponseDto, UserResponseDto } from '@api';
 	import CameraIris from 'svelte-material-icons/CameraIris.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 StatsCard from './stats-card.svelte';
 	export let stats: ServerStatsResponseDto;
@@ -27,7 +26,6 @@
 		<div class="flex mt-5 justify-between">
 			<StatsCard logo={CameraIris} title={'PHOTOS'} value={stats.photos.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} />
 		</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"
 			>
 				<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/5 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/5 font-medium text-sm">Objects</th>
-					<th class="text-center w-1/5 font-medium text-sm">Size</th>
+					<th class="text-center w-1/4 font-medium text-sm">User</th>
+					<th class="text-center w-1/4 font-medium text-sm">Photos</th>
+					<th class="text-center w-1/4 font-medium text-sm">Videos</th>
+					<th class="text-center w-1/4 font-medium text-sm">Size</th>
 				</tr>
 			</thead>
 			<tbody
@@ -57,11 +54,10 @@
 								: '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/5 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/5 text-ellipsis">{user.objects}</td>
-						<td class="text-sm px-2 w-1/5 text-ellipsis">{user.usage}</td>
+						<td class="text-sm px-2 w-1/4 text-ellipsis">{getFullName(user.userId)}</td>
+						<td class="text-sm px-2 w-1/4 text-ellipsis">{user.photos}</td>
+						<td class="text-sm px-2 w-1/4 text-ellipsis">{user.videos}</td>
+						<td class="text-sm px-2 w-1/4 text-ellipsis">{user.usage}</td>
 					</tr>
 				{/each}
 			</tbody>

+ 2 - 2
web/src/lib/components/admin-page/server-stats/stats-card.svelte

@@ -7,7 +7,7 @@
 
 	$: zeros = () => {
 		let result = '';
-		const maxLength = 9;
+		const maxLength = 13;
 		const valueLength = parseInt(value).toString().length;
 		const zeroLength = maxLength - valueLength;
 		for (let i = 0; i < zeroLength; i++) {
@@ -18,7 +18,7 @@
 </script>
 
 <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">
 		<svelte:component this={logo} size="40" />

Some files were not shown because too many files changed in this diff