From 8f37784eae09f6f56389eb099cc26de9d862d051 Mon Sep 17 00:00:00 2001
From: Jason Rasmussen <jrasm91@gmail.com>
Date: Wed, 22 May 2024 14:31:12 -0400
Subject: [PATCH] refactor(server): /user profile endpoint (#9669)

* refactor(server): user profile endpoint

* chore: open api
---
 mobile/lib/widgets/common/user_avatar.dart    |   2 +-
 .../widgets/common/user_circle_avatar.dart    |   2 +-
 mobile/openapi/README.md                      | Bin 27345 -> 27345 bytes
 mobile/openapi/lib/api/user_api.dart          | Bin 15780 -> 15780 bytes
 open-api/immich-openapi-specs.json            |  86 +++++++++---------
 open-api/typescript-sdk/src/fetch-client.ts   |  20 ++--
 server/src/controllers/user.controller.ts     |   2 +-
 web/src/lib/utils.ts                          |   2 +-
 8 files changed, 57 insertions(+), 57 deletions(-)

diff --git a/mobile/lib/widgets/common/user_avatar.dart b/mobile/lib/widgets/common/user_avatar.dart
index c61a3adbeb..8dfe00b2b9 100644
--- a/mobile/lib/widgets/common/user_avatar.dart
+++ b/mobile/lib/widgets/common/user_avatar.dart
@@ -6,7 +6,7 @@ import 'package:immich_mobile/entities/user.entity.dart';
 
 Widget userAvatar(BuildContext context, User u, {double? radius}) {
   final url =
-      "${Store.get(StoreKey.serverEndpoint)}/user/profile-image/${u.id}";
+      "${Store.get(StoreKey.serverEndpoint)}/users/${u.id}/profile-image";
   final nameFirstLetter = u.name.isNotEmpty ? u.name[0] : "";
   return CircleAvatar(
     radius: radius,
diff --git a/mobile/lib/widgets/common/user_circle_avatar.dart b/mobile/lib/widgets/common/user_circle_avatar.dart
index 1f8529a80a..9dd924d98e 100644
--- a/mobile/lib/widgets/common/user_circle_avatar.dart
+++ b/mobile/lib/widgets/common/user_circle_avatar.dart
@@ -24,7 +24,7 @@ class UserCircleAvatar extends ConsumerWidget {
   Widget build(BuildContext context, WidgetRef ref) {
     bool isDarkTheme = Theme.of(context).brightness == Brightness.dark;
     final profileImageUrl =
-        '${Store.get(StoreKey.serverEndpoint)}/user/profile-image/${user.id}?d=${Random().nextInt(1024)}';
+        '${Store.get(StoreKey.serverEndpoint)}/users/${user.id}/profile-image?d=${Random().nextInt(1024)}';
 
     final textIcon = Text(
       user.name[0].toUpperCase(),
diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md
index ad38d3f49e6f5dd14038e7509ac14ba687ae950d..c3a4921601c08bd030ae47981c4692c95e22797a 100644
GIT binary patch
delta 23
fcmcb3mGR<L#tpNbSgJEqYA4q_C~iLBB&Y!ZhJ6YK

delta 37
tcmcb3mGR<L#tpNbgbIrC(=u~Xbu)7l(^K`UGgE3OD>?{oKHwy%0RT8M4)_27

diff --git a/mobile/openapi/lib/api/user_api.dart b/mobile/openapi/lib/api/user_api.dart
index 09c050471054e7ad6f5a3269b9af0b0595412faf..301169cb9a8ba5996011a4843b04942e58c5af1c 100644
GIT binary patch
delta 49
zcmZ2dy`*|Wiz-WXW=iek1b&Oj2UPb6fjIgFMfqu&IjOptxrynin;UhwnKsYV;1&e{
D05=l%

delta 55
zcmV-70LcHOd!&1?ge4Mia&Km7Y-KHJZDD6+FMDZZeUs4vNt53tos-N04U;VtUXxuA
N-jld3)U%l=3l)&S7XJVM

diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json
index cb334534bd..03dafc33cf 100644
--- a/open-api/immich-openapi-specs.json
+++ b/open-api/immich-openapi-specs.json
@@ -6327,49 +6327,6 @@
         ]
       }
     },
-    "/users/profile-image/{id}": {
-      "get": {
-        "operationId": "getProfileImage",
-        "parameters": [
-          {
-            "name": "id",
-            "required": true,
-            "in": "path",
-            "schema": {
-              "format": "uuid",
-              "type": "string"
-            }
-          }
-        ],
-        "responses": {
-          "200": {
-            "content": {
-              "application/octet-stream": {
-                "schema": {
-                  "format": "binary",
-                  "type": "string"
-                }
-              }
-            },
-            "description": ""
-          }
-        },
-        "security": [
-          {
-            "bearer": []
-          },
-          {
-            "cookie": []
-          },
-          {
-            "api_key": []
-          }
-        ],
-        "tags": [
-          "User"
-        ]
-      }
-    },
     "/users/{id}": {
       "delete": {
         "operationId": "deleteUser",
@@ -6462,6 +6419,49 @@
         ]
       }
     },
+    "/users/{id}/profile-image": {
+      "get": {
+        "operationId": "getProfileImage",
+        "parameters": [
+          {
+            "name": "id",
+            "required": true,
+            "in": "path",
+            "schema": {
+              "format": "uuid",
+              "type": "string"
+            }
+          }
+        ],
+        "responses": {
+          "200": {
+            "content": {
+              "application/octet-stream": {
+                "schema": {
+                  "format": "binary",
+                  "type": "string"
+                }
+              }
+            },
+            "description": ""
+          }
+        },
+        "security": [
+          {
+            "bearer": []
+          },
+          {
+            "cookie": []
+          },
+          {
+            "api_key": []
+          }
+        ],
+        "tags": [
+          "User"
+        ]
+      }
+    },
     "/users/{id}/restore": {
       "post": {
         "operationId": "restoreUser",
diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts
index 29a8f5c0fe..b830f0c280 100644
--- a/open-api/typescript-sdk/src/fetch-client.ts
+++ b/open-api/typescript-sdk/src/fetch-client.ts
@@ -2776,16 +2776,6 @@ export function createProfileImage({ createProfileImageDto }: {
         body: createProfileImageDto
     })));
 }
-export function getProfileImage({ id }: {
-    id: string;
-}, opts?: Oazapfts.RequestOpts) {
-    return oazapfts.ok(oazapfts.fetchBlob<{
-        status: 200;
-        data: Blob;
-    }>(`/users/profile-image/${encodeURIComponent(id)}`, {
-        ...opts
-    }));
-}
 export function deleteUser({ id, deleteUserDto }: {
     id: string;
     deleteUserDto: DeleteUserDto;
@@ -2809,6 +2799,16 @@ export function getUserById({ id }: {
         ...opts
     }));
 }
+export function getProfileImage({ id }: {
+    id: string;
+}, opts?: Oazapfts.RequestOpts) {
+    return oazapfts.ok(oazapfts.fetchBlob<{
+        status: 200;
+        data: Blob;
+    }>(`/users/${encodeURIComponent(id)}/profile-image`, {
+        ...opts
+    }));
+}
 export function restoreUser({ id }: {
     id: string;
 }, opts?: Oazapfts.RequestOpts) {
diff --git a/server/src/controllers/user.controller.ts b/server/src/controllers/user.controller.ts
index 4c058e7aae..1b995c5944 100644
--- a/server/src/controllers/user.controller.ts
+++ b/server/src/controllers/user.controller.ts
@@ -101,7 +101,7 @@ export class UserController {
     return this.service.createProfileImage(auth, fileInfo);
   }
 
-  @Get('profile-image/:id')
+  @Get(':id/profile-image')
   @FileResponse()
   @Authenticated()
   async getProfileImage(@Res() res: Response, @Next() next: NextFunction, @Param() { id }: UUIDParamDto) {
diff --git a/web/src/lib/utils.ts b/web/src/lib/utils.ts
index 5c055b875d..60add8ae7a 100644
--- a/web/src/lib/utils.ts
+++ b/web/src/lib/utils.ts
@@ -169,7 +169,7 @@ export const getAssetThumbnailUrl = (...[assetId, format]: [string, ThumbnailFor
 };
 
 export const getProfileImageUrl = (...[userId]: [string]) => {
-  const path = `/user/profile-image/${userId}`;
+  const path = `/users/${userId}/profile-image`;
   return createUrl(path);
 };