mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-03 19:29:32 -05:00 
			
		
		
		
	refactor(server): user endpoints (#9730)
* refactor(server): user endpoints * fix repos * fix unit tests --------- Co-authored-by: Daniel Dietzler <mail@ddietzler.dev> Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
		
							parent
							
								
									e7c8501930
								
							
						
					
					
						commit
						75830a4878
					
				@ -1,4 +1,4 @@
 | 
				
			|||||||
import { getMyUserInfo } from '@immich/sdk';
 | 
					import { getMyUser } from '@immich/sdk';
 | 
				
			||||||
import { existsSync } from 'node:fs';
 | 
					import { existsSync } from 'node:fs';
 | 
				
			||||||
import { mkdir, unlink } from 'node:fs/promises';
 | 
					import { mkdir, unlink } from 'node:fs/promises';
 | 
				
			||||||
import { BaseOptions, connect, getAuthFilePath, logError, withError, writeAuthFile } from 'src/utils';
 | 
					import { BaseOptions, connect, getAuthFilePath, logError, withError, writeAuthFile } from 'src/utils';
 | 
				
			||||||
@ -10,13 +10,13 @@ export const login = async (url: string, key: string, options: BaseOptions) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  await connect(url, key);
 | 
					  await connect(url, key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const [error, userInfo] = await withError(getMyUserInfo());
 | 
					  const [error, user] = await withError(getMyUser());
 | 
				
			||||||
  if (error) {
 | 
					  if (error) {
 | 
				
			||||||
    logError(error, 'Failed to load user info');
 | 
					    logError(error, 'Failed to load user info');
 | 
				
			||||||
    process.exit(1);
 | 
					    process.exit(1);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  console.log(`Logged in as ${userInfo.email}`);
 | 
					  console.log(`Logged in as ${user.email}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!existsSync(configDir)) {
 | 
					  if (!existsSync(configDir)) {
 | 
				
			||||||
    // Create config folder if it doesn't exist
 | 
					    // Create config folder if it doesn't exist
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { getAssetStatistics, getMyUserInfo, getServerVersion, getSupportedMediaTypes } from '@immich/sdk';
 | 
					import { getAssetStatistics, getMyUser, getServerVersion, getSupportedMediaTypes } from '@immich/sdk';
 | 
				
			||||||
import { BaseOptions, authenticate } from 'src/utils';
 | 
					import { BaseOptions, authenticate } from 'src/utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const serverInfo = async (options: BaseOptions) => {
 | 
					export const serverInfo = async (options: BaseOptions) => {
 | 
				
			||||||
@ -8,7 +8,7 @@ export const serverInfo = async (options: BaseOptions) => {
 | 
				
			|||||||
    getServerVersion(),
 | 
					    getServerVersion(),
 | 
				
			||||||
    getSupportedMediaTypes(),
 | 
					    getSupportedMediaTypes(),
 | 
				
			||||||
    getAssetStatistics({}),
 | 
					    getAssetStatistics({}),
 | 
				
			||||||
    getMyUserInfo(),
 | 
					    getMyUser(),
 | 
				
			||||||
  ]);
 | 
					  ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  console.log(`Server Info (via ${userInfo.email})`);
 | 
					  console.log(`Server Info (via ${userInfo.email})`);
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
import { getMyUserInfo, init, isHttpError } from '@immich/sdk';
 | 
					import { getMyUser, init, isHttpError } from '@immich/sdk';
 | 
				
			||||||
import { glob } from 'fast-glob';
 | 
					import { glob } from 'fast-glob';
 | 
				
			||||||
import { createHash } from 'node:crypto';
 | 
					import { createHash } from 'node:crypto';
 | 
				
			||||||
import { createReadStream } from 'node:fs';
 | 
					import { createReadStream } from 'node:fs';
 | 
				
			||||||
@ -48,7 +48,7 @@ export const connect = async (url: string, key: string) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  init({ baseUrl: url, apiKey: key });
 | 
					  init({ baseUrl: url, apiKey: key });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const [error] = await withError(getMyUserInfo());
 | 
					  const [error] = await withError(getMyUser());
 | 
				
			||||||
  if (isHttpError(error)) {
 | 
					  if (isHttpError(error)) {
 | 
				
			||||||
    logError(error, 'Failed to connect to server');
 | 
					    logError(error, 'Failed to connect to server');
 | 
				
			||||||
    process.exit(1);
 | 
					    process.exit(1);
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,7 @@ import {
 | 
				
			|||||||
  AlbumUserRole,
 | 
					  AlbumUserRole,
 | 
				
			||||||
  AssetFileUploadResponseDto,
 | 
					  AssetFileUploadResponseDto,
 | 
				
			||||||
  AssetOrder,
 | 
					  AssetOrder,
 | 
				
			||||||
  deleteUser,
 | 
					  deleteUserAdmin,
 | 
				
			||||||
  getAlbumInfo,
 | 
					  getAlbumInfo,
 | 
				
			||||||
  LoginResponseDto,
 | 
					  LoginResponseDto,
 | 
				
			||||||
  SharedLinkType,
 | 
					  SharedLinkType,
 | 
				
			||||||
@ -107,7 +107,7 @@ describe('/albums', () => {
 | 
				
			|||||||
      }),
 | 
					      }),
 | 
				
			||||||
    ]);
 | 
					    ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    await deleteUser({ id: user3.userId, deleteUserDto: {} }, { headers: asBearerAuth(admin.accessToken) });
 | 
					    await deleteUserAdmin({ id: user3.userId, userAdminDeleteDto: {} }, { headers: asBearerAuth(admin.accessToken) });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  describe('GET /albums', () => {
 | 
					  describe('GET /albums', () => {
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@ import {
 | 
				
			|||||||
  LoginResponseDto,
 | 
					  LoginResponseDto,
 | 
				
			||||||
  SharedLinkType,
 | 
					  SharedLinkType,
 | 
				
			||||||
  getAssetInfo,
 | 
					  getAssetInfo,
 | 
				
			||||||
  getMyUserInfo,
 | 
					  getMyUser,
 | 
				
			||||||
  updateAssets,
 | 
					  updateAssets,
 | 
				
			||||||
} from '@immich/sdk';
 | 
					} from '@immich/sdk';
 | 
				
			||||||
import { exiftool } from 'exiftool-vendored';
 | 
					import { exiftool } from 'exiftool-vendored';
 | 
				
			||||||
@ -1162,7 +1162,7 @@ describe('/asset', () => {
 | 
				
			|||||||
      expect(body).toEqual({ id: expect.any(String), duplicate: false });
 | 
					      expect(body).toEqual({ id: expect.any(String), duplicate: false });
 | 
				
			||||||
      expect(status).toBe(201);
 | 
					      expect(status).toBe(201);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const user = await getMyUserInfo({ headers: asBearerAuth(quotaUser.accessToken) });
 | 
					      const user = await getMyUser({ headers: asBearerAuth(quotaUser.accessToken) });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      expect(user).toEqual(expect.objectContaining({ quotaUsageInBytes: 70 }));
 | 
					      expect(user).toEqual(expect.objectContaining({ quotaUsageInBytes: 70 }));
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@ import {
 | 
				
			|||||||
  SharedLinkResponseDto,
 | 
					  SharedLinkResponseDto,
 | 
				
			||||||
  SharedLinkType,
 | 
					  SharedLinkType,
 | 
				
			||||||
  createAlbum,
 | 
					  createAlbum,
 | 
				
			||||||
  deleteUser,
 | 
					  deleteUserAdmin,
 | 
				
			||||||
} from '@immich/sdk';
 | 
					} from '@immich/sdk';
 | 
				
			||||||
import { createUserDto, uuidDto } from 'src/fixtures';
 | 
					import { createUserDto, uuidDto } from 'src/fixtures';
 | 
				
			||||||
import { errorDto } from 'src/responses';
 | 
					import { errorDto } from 'src/responses';
 | 
				
			||||||
@ -86,7 +86,7 @@ describe('/shared-links', () => {
 | 
				
			|||||||
        }),
 | 
					        }),
 | 
				
			||||||
      ]);
 | 
					      ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    await deleteUser({ id: user2.userId, deleteUserDto: {} }, { headers: asBearerAuth(admin.accessToken) });
 | 
					    await deleteUserAdmin({ id: user2.userId, userAdminDeleteDto: {} }, { headers: asBearerAuth(admin.accessToken) });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  describe('GET /share/${key}', () => {
 | 
					  describe('GET /share/${key}', () => {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										317
									
								
								e2e/src/api/specs/user-admin.e2e-spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										317
									
								
								e2e/src/api/specs/user-admin.e2e-spec.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,317 @@
 | 
				
			|||||||
 | 
					import { LoginResponseDto, deleteUserAdmin, getMyUser, getUserAdmin, login } from '@immich/sdk';
 | 
				
			||||||
 | 
					import { Socket } from 'socket.io-client';
 | 
				
			||||||
 | 
					import { createUserDto, uuidDto } from 'src/fixtures';
 | 
				
			||||||
 | 
					import { errorDto } from 'src/responses';
 | 
				
			||||||
 | 
					import { app, asBearerAuth, utils } from 'src/utils';
 | 
				
			||||||
 | 
					import request from 'supertest';
 | 
				
			||||||
 | 
					import { afterAll, beforeAll, describe, expect, it } from 'vitest';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe('/admin/users', () => {
 | 
				
			||||||
 | 
					  let websocket: Socket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let admin: LoginResponseDto;
 | 
				
			||||||
 | 
					  let nonAdmin: LoginResponseDto;
 | 
				
			||||||
 | 
					  let deletedUser: LoginResponseDto;
 | 
				
			||||||
 | 
					  let userToDelete: LoginResponseDto;
 | 
				
			||||||
 | 
					  let userToHardDelete: LoginResponseDto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  beforeAll(async () => {
 | 
				
			||||||
 | 
					    await utils.resetDatabase();
 | 
				
			||||||
 | 
					    admin = await utils.adminSetup({ onboarding: false });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [websocket, nonAdmin, deletedUser, userToDelete, userToHardDelete] = await Promise.all([
 | 
				
			||||||
 | 
					      utils.connectWebsocket(admin.accessToken),
 | 
				
			||||||
 | 
					      utils.userSetup(admin.accessToken, createUserDto.user1),
 | 
				
			||||||
 | 
					      utils.userSetup(admin.accessToken, createUserDto.user2),
 | 
				
			||||||
 | 
					      utils.userSetup(admin.accessToken, createUserDto.user3),
 | 
				
			||||||
 | 
					      utils.userSetup(admin.accessToken, createUserDto.user4),
 | 
				
			||||||
 | 
					    ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await deleteUserAdmin(
 | 
				
			||||||
 | 
					      { id: deletedUser.userId, userAdminDeleteDto: {} },
 | 
				
			||||||
 | 
					      { headers: asBearerAuth(admin.accessToken) },
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  afterAll(() => {
 | 
				
			||||||
 | 
					    utils.disconnectWebsocket(websocket);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('GET /admin/users', () => {
 | 
				
			||||||
 | 
					    it('should require authentication', async () => {
 | 
				
			||||||
 | 
					      const { status, body } = await request(app).get(`/admin/users`);
 | 
				
			||||||
 | 
					      expect(status).toBe(401);
 | 
				
			||||||
 | 
					      expect(body).toEqual(errorDto.unauthorized);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should require authorization', async () => {
 | 
				
			||||||
 | 
					      const { status, body } = await request(app)
 | 
				
			||||||
 | 
					        .get(`/admin/users`)
 | 
				
			||||||
 | 
					        .set('Authorization', `Bearer ${nonAdmin.accessToken}`);
 | 
				
			||||||
 | 
					      expect(status).toBe(403);
 | 
				
			||||||
 | 
					      expect(body).toEqual(errorDto.forbidden);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should hide deleted users by default', async () => {
 | 
				
			||||||
 | 
					      const { status, body } = await request(app)
 | 
				
			||||||
 | 
					        .get(`/admin/users`)
 | 
				
			||||||
 | 
					        .set('Authorization', `Bearer ${admin.accessToken}`);
 | 
				
			||||||
 | 
					      expect(status).toBe(200);
 | 
				
			||||||
 | 
					      expect(body).toHaveLength(4);
 | 
				
			||||||
 | 
					      expect(body).toEqual(
 | 
				
			||||||
 | 
					        expect.arrayContaining([
 | 
				
			||||||
 | 
					          expect.objectContaining({ email: admin.userEmail }),
 | 
				
			||||||
 | 
					          expect.objectContaining({ email: nonAdmin.userEmail }),
 | 
				
			||||||
 | 
					          expect.objectContaining({ email: userToDelete.userEmail }),
 | 
				
			||||||
 | 
					          expect.objectContaining({ email: userToHardDelete.userEmail }),
 | 
				
			||||||
 | 
					        ]),
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should include deleted users', async () => {
 | 
				
			||||||
 | 
					      const { status, body } = await request(app)
 | 
				
			||||||
 | 
					        .get(`/admin/users?withDeleted=true`)
 | 
				
			||||||
 | 
					        .set('Authorization', `Bearer ${admin.accessToken}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(status).toBe(200);
 | 
				
			||||||
 | 
					      expect(body).toHaveLength(5);
 | 
				
			||||||
 | 
					      expect(body).toEqual(
 | 
				
			||||||
 | 
					        expect.arrayContaining([
 | 
				
			||||||
 | 
					          expect.objectContaining({ email: admin.userEmail }),
 | 
				
			||||||
 | 
					          expect.objectContaining({ email: nonAdmin.userEmail }),
 | 
				
			||||||
 | 
					          expect.objectContaining({ email: userToDelete.userEmail }),
 | 
				
			||||||
 | 
					          expect.objectContaining({ email: userToHardDelete.userEmail }),
 | 
				
			||||||
 | 
					          expect.objectContaining({ email: deletedUser.userEmail }),
 | 
				
			||||||
 | 
					        ]),
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('POST /admin/users', () => {
 | 
				
			||||||
 | 
					    it('should require authentication', async () => {
 | 
				
			||||||
 | 
					      const { status, body } = await request(app).post(`/admin/users`).send(createUserDto.user1);
 | 
				
			||||||
 | 
					      expect(status).toBe(401);
 | 
				
			||||||
 | 
					      expect(body).toEqual(errorDto.unauthorized);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should require authorization', async () => {
 | 
				
			||||||
 | 
					      const { status, body } = await request(app)
 | 
				
			||||||
 | 
					        .post(`/admin/users`)
 | 
				
			||||||
 | 
					        .set('Authorization', `Bearer ${nonAdmin.accessToken}`)
 | 
				
			||||||
 | 
					        .send(createUserDto.user1);
 | 
				
			||||||
 | 
					      expect(status).toBe(403);
 | 
				
			||||||
 | 
					      expect(body).toEqual(errorDto.forbidden);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (const key of [
 | 
				
			||||||
 | 
					      'password',
 | 
				
			||||||
 | 
					      'email',
 | 
				
			||||||
 | 
					      'name',
 | 
				
			||||||
 | 
					      'quotaSizeInBytes',
 | 
				
			||||||
 | 
					      'shouldChangePassword',
 | 
				
			||||||
 | 
					      'memoriesEnabled',
 | 
				
			||||||
 | 
					      'notify',
 | 
				
			||||||
 | 
					    ]) {
 | 
				
			||||||
 | 
					      it(`should not allow null ${key}`, async () => {
 | 
				
			||||||
 | 
					        const { status, body } = await request(app)
 | 
				
			||||||
 | 
					          .post(`/admin/users`)
 | 
				
			||||||
 | 
					          .set('Authorization', `Bearer ${admin.accessToken}`)
 | 
				
			||||||
 | 
					          .send({ ...createUserDto.user1, [key]: null });
 | 
				
			||||||
 | 
					        expect(status).toBe(400);
 | 
				
			||||||
 | 
					        expect(body).toEqual(errorDto.badRequest());
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should ignore `isAdmin`', async () => {
 | 
				
			||||||
 | 
					      const { status, body } = await request(app)
 | 
				
			||||||
 | 
					        .post(`/admin/users`)
 | 
				
			||||||
 | 
					        .send({
 | 
				
			||||||
 | 
					          isAdmin: true,
 | 
				
			||||||
 | 
					          email: 'user5@immich.cloud',
 | 
				
			||||||
 | 
					          password: 'password123',
 | 
				
			||||||
 | 
					          name: 'Immich',
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .set('Authorization', `Bearer ${admin.accessToken}`);
 | 
				
			||||||
 | 
					      expect(body).toMatchObject({
 | 
				
			||||||
 | 
					        email: 'user5@immich.cloud',
 | 
				
			||||||
 | 
					        isAdmin: false,
 | 
				
			||||||
 | 
					        shouldChangePassword: true,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      expect(status).toBe(201);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should create a user without memories enabled', async () => {
 | 
				
			||||||
 | 
					      const { status, body } = await request(app)
 | 
				
			||||||
 | 
					        .post(`/admin/users`)
 | 
				
			||||||
 | 
					        .send({
 | 
				
			||||||
 | 
					          email: 'no-memories@immich.cloud',
 | 
				
			||||||
 | 
					          password: 'Password123',
 | 
				
			||||||
 | 
					          name: 'No Memories',
 | 
				
			||||||
 | 
					          memoriesEnabled: false,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .set('Authorization', `Bearer ${admin.accessToken}`);
 | 
				
			||||||
 | 
					      expect(body).toMatchObject({
 | 
				
			||||||
 | 
					        email: 'no-memories@immich.cloud',
 | 
				
			||||||
 | 
					        memoriesEnabled: false,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      expect(status).toBe(201);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('PUT /admin/users/:id', () => {
 | 
				
			||||||
 | 
					    it('should require authentication', async () => {
 | 
				
			||||||
 | 
					      const { status, body } = await request(app).put(`/admin/users/${uuidDto.notFound}`);
 | 
				
			||||||
 | 
					      expect(status).toBe(401);
 | 
				
			||||||
 | 
					      expect(body).toEqual(errorDto.unauthorized);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should require authorization', async () => {
 | 
				
			||||||
 | 
					      const { status, body } = await request(app)
 | 
				
			||||||
 | 
					        .put(`/admin/users/${uuidDto.notFound}`)
 | 
				
			||||||
 | 
					        .set('Authorization', `Bearer ${nonAdmin.accessToken}`);
 | 
				
			||||||
 | 
					      expect(status).toBe(403);
 | 
				
			||||||
 | 
					      expect(body).toEqual(errorDto.forbidden);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (const key of ['password', 'email', 'name', 'shouldChangePassword', 'memoriesEnabled']) {
 | 
				
			||||||
 | 
					      it(`should not allow null ${key}`, async () => {
 | 
				
			||||||
 | 
					        const { status, body } = await request(app)
 | 
				
			||||||
 | 
					          .put(`/admin/users/${uuidDto.notFound}`)
 | 
				
			||||||
 | 
					          .set('Authorization', `Bearer ${admin.accessToken}`)
 | 
				
			||||||
 | 
					          .send({ [key]: null });
 | 
				
			||||||
 | 
					        expect(status).toBe(400);
 | 
				
			||||||
 | 
					        expect(body).toEqual(errorDto.badRequest());
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should not allow a non-admin to become an admin', async () => {
 | 
				
			||||||
 | 
					      const { status, body } = await request(app)
 | 
				
			||||||
 | 
					        .put(`/admin/users/${nonAdmin.userId}`)
 | 
				
			||||||
 | 
					        .send({ isAdmin: true })
 | 
				
			||||||
 | 
					        .set('Authorization', `Bearer ${admin.accessToken}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(status).toBe(200);
 | 
				
			||||||
 | 
					      expect(body).toMatchObject({ isAdmin: false });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('ignores updates to profileImagePath', async () => {
 | 
				
			||||||
 | 
					      const { status, body } = await request(app)
 | 
				
			||||||
 | 
					        .put(`/admin/users/${admin.userId}`)
 | 
				
			||||||
 | 
					        .send({ profileImagePath: 'invalid.jpg' })
 | 
				
			||||||
 | 
					        .set('Authorization', `Bearer ${admin.accessToken}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(status).toBe(200);
 | 
				
			||||||
 | 
					      expect(body).toMatchObject({ id: admin.userId, profileImagePath: '' });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should update first and last name', async () => {
 | 
				
			||||||
 | 
					      const before = await getUserAdmin({ id: admin.userId }, { headers: asBearerAuth(admin.accessToken) });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const { status, body } = await request(app)
 | 
				
			||||||
 | 
					        .put(`/admin/users/${admin.userId}`)
 | 
				
			||||||
 | 
					        .send({ name: 'Name' })
 | 
				
			||||||
 | 
					        .set('Authorization', `Bearer ${admin.accessToken}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(status).toBe(200);
 | 
				
			||||||
 | 
					      expect(body).toEqual({
 | 
				
			||||||
 | 
					        ...before,
 | 
				
			||||||
 | 
					        updatedAt: expect.any(String),
 | 
				
			||||||
 | 
					        name: 'Name',
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      expect(before.updatedAt).not.toEqual(body.updatedAt);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should update memories enabled', async () => {
 | 
				
			||||||
 | 
					      const before = await getUserAdmin({ id: admin.userId }, { headers: asBearerAuth(admin.accessToken) });
 | 
				
			||||||
 | 
					      const { status, body } = await request(app)
 | 
				
			||||||
 | 
					        .put(`/admin/users/${admin.userId}`)
 | 
				
			||||||
 | 
					        .send({ memoriesEnabled: false })
 | 
				
			||||||
 | 
					        .set('Authorization', `Bearer ${admin.accessToken}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(status).toBe(200);
 | 
				
			||||||
 | 
					      expect(body).toMatchObject({
 | 
				
			||||||
 | 
					        ...before,
 | 
				
			||||||
 | 
					        updatedAt: expect.anything(),
 | 
				
			||||||
 | 
					        memoriesEnabled: false,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      expect(before.updatedAt).not.toEqual(body.updatedAt);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should update password', async () => {
 | 
				
			||||||
 | 
					      const { status, body } = await request(app)
 | 
				
			||||||
 | 
					        .put(`/admin/users/${nonAdmin.userId}`)
 | 
				
			||||||
 | 
					        .send({ password: 'super-secret' })
 | 
				
			||||||
 | 
					        .set('Authorization', `Bearer ${admin.accessToken}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(status).toBe(200);
 | 
				
			||||||
 | 
					      expect(body).toMatchObject({ email: nonAdmin.userEmail });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const token = await login({ loginCredentialDto: { email: nonAdmin.userEmail, password: 'super-secret' } });
 | 
				
			||||||
 | 
					      expect(token.accessToken).toBeDefined();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const user = await getMyUser({ headers: asBearerAuth(token.accessToken) });
 | 
				
			||||||
 | 
					      expect(user).toMatchObject({ email: nonAdmin.userEmail });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('DELETE /admin/users/:id', () => {
 | 
				
			||||||
 | 
					    it('should require authentication', async () => {
 | 
				
			||||||
 | 
					      const { status, body } = await request(app).delete(`/admin/users/${userToDelete.userId}`);
 | 
				
			||||||
 | 
					      expect(status).toBe(401);
 | 
				
			||||||
 | 
					      expect(body).toEqual(errorDto.unauthorized);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should require authorization', async () => {
 | 
				
			||||||
 | 
					      const { status, body } = await request(app)
 | 
				
			||||||
 | 
					        .delete(`/admin/users/${userToDelete.userId}`)
 | 
				
			||||||
 | 
					        .set('Authorization', `Bearer ${nonAdmin.accessToken}`);
 | 
				
			||||||
 | 
					      expect(status).toBe(403);
 | 
				
			||||||
 | 
					      expect(body).toEqual(errorDto.forbidden);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should delete user', async () => {
 | 
				
			||||||
 | 
					      const { status, body } = await request(app)
 | 
				
			||||||
 | 
					        .delete(`/admin/users/${userToDelete.userId}`)
 | 
				
			||||||
 | 
					        .set('Authorization', `Bearer ${admin.accessToken}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(status).toBe(200);
 | 
				
			||||||
 | 
					      expect(body).toMatchObject({
 | 
				
			||||||
 | 
					        id: userToDelete.userId,
 | 
				
			||||||
 | 
					        updatedAt: expect.any(String),
 | 
				
			||||||
 | 
					        deletedAt: expect.any(String),
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should hard delete a user', async () => {
 | 
				
			||||||
 | 
					      const { status, body } = await request(app)
 | 
				
			||||||
 | 
					        .delete(`/admin/users/${userToHardDelete.userId}`)
 | 
				
			||||||
 | 
					        .send({ force: true })
 | 
				
			||||||
 | 
					        .set('Authorization', `Bearer ${admin.accessToken}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(status).toBe(200);
 | 
				
			||||||
 | 
					      expect(body).toMatchObject({
 | 
				
			||||||
 | 
					        id: userToHardDelete.userId,
 | 
				
			||||||
 | 
					        updatedAt: expect.any(String),
 | 
				
			||||||
 | 
					        deletedAt: expect.any(String),
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await utils.waitForWebsocketEvent({ event: 'userDelete', id: userToHardDelete.userId, timeout: 5000 });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('POST /admin/users/:id/restore', () => {
 | 
				
			||||||
 | 
					    it('should require authentication', async () => {
 | 
				
			||||||
 | 
					      const { status, body } = await request(app).post(`/admin/users/${userToDelete.userId}/restore`);
 | 
				
			||||||
 | 
					      expect(status).toBe(401);
 | 
				
			||||||
 | 
					      expect(body).toEqual(errorDto.unauthorized);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should require authorization', async () => {
 | 
				
			||||||
 | 
					      const { status, body } = await request(app)
 | 
				
			||||||
 | 
					        .post(`/admin/users/${userToDelete.userId}/restore`)
 | 
				
			||||||
 | 
					        .set('Authorization', `Bearer ${nonAdmin.accessToken}`);
 | 
				
			||||||
 | 
					      expect(status).toBe(403);
 | 
				
			||||||
 | 
					      expect(body).toEqual(errorDto.forbidden);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
@ -1,37 +1,28 @@
 | 
				
			|||||||
import { LoginResponseDto, deleteUser, getUserById } from '@immich/sdk';
 | 
					import { LoginResponseDto, SharedLinkType, deleteUserAdmin, getMyUser, login } from '@immich/sdk';
 | 
				
			||||||
import { Socket } from 'socket.io-client';
 | 
					import { createUserDto } from 'src/fixtures';
 | 
				
			||||||
import { createUserDto, userDto } from 'src/fixtures';
 | 
					 | 
				
			||||||
import { errorDto } from 'src/responses';
 | 
					import { errorDto } from 'src/responses';
 | 
				
			||||||
import { app, asBearerAuth, utils } from 'src/utils';
 | 
					import { app, asBearerAuth, utils } from 'src/utils';
 | 
				
			||||||
import request from 'supertest';
 | 
					import request from 'supertest';
 | 
				
			||||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
 | 
					import { beforeAll, describe, expect, it } from 'vitest';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('/users', () => {
 | 
					describe('/users', () => {
 | 
				
			||||||
  let websocket: Socket;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let admin: LoginResponseDto;
 | 
					  let admin: LoginResponseDto;
 | 
				
			||||||
  let deletedUser: LoginResponseDto;
 | 
					  let deletedUser: LoginResponseDto;
 | 
				
			||||||
  let userToDelete: LoginResponseDto;
 | 
					 | 
				
			||||||
  let userToHardDelete: LoginResponseDto;
 | 
					 | 
				
			||||||
  let nonAdmin: LoginResponseDto;
 | 
					  let nonAdmin: LoginResponseDto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeAll(async () => {
 | 
					  beforeAll(async () => {
 | 
				
			||||||
    await utils.resetDatabase();
 | 
					    await utils.resetDatabase();
 | 
				
			||||||
    admin = await utils.adminSetup({ onboarding: false });
 | 
					    admin = await utils.adminSetup({ onboarding: false });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [websocket, deletedUser, nonAdmin, userToDelete, userToHardDelete] = await Promise.all([
 | 
					    [deletedUser, nonAdmin] = await Promise.all([
 | 
				
			||||||
      utils.connectWebsocket(admin.accessToken),
 | 
					 | 
				
			||||||
      utils.userSetup(admin.accessToken, createUserDto.user1),
 | 
					      utils.userSetup(admin.accessToken, createUserDto.user1),
 | 
				
			||||||
      utils.userSetup(admin.accessToken, createUserDto.user2),
 | 
					      utils.userSetup(admin.accessToken, createUserDto.user2),
 | 
				
			||||||
      utils.userSetup(admin.accessToken, createUserDto.user3),
 | 
					 | 
				
			||||||
      utils.userSetup(admin.accessToken, createUserDto.user4),
 | 
					 | 
				
			||||||
    ]);
 | 
					    ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    await deleteUser({ id: deletedUser.userId, deleteUserDto: {} }, { headers: asBearerAuth(admin.accessToken) });
 | 
					    await deleteUserAdmin(
 | 
				
			||||||
  });
 | 
					      { id: deletedUser.userId, userAdminDeleteDto: {} },
 | 
				
			||||||
 | 
					      { headers: asBearerAuth(admin.accessToken) },
 | 
				
			||||||
  afterAll(() => {
 | 
					    );
 | 
				
			||||||
    utils.disconnectWebsocket(websocket);
 | 
					 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  describe('GET /users', () => {
 | 
					  describe('GET /users', () => {
 | 
				
			||||||
@ -44,71 +35,14 @@ describe('/users', () => {
 | 
				
			|||||||
    it('should get users', async () => {
 | 
					    it('should get users', async () => {
 | 
				
			||||||
      const { status, body } = await request(app).get('/users').set('Authorization', `Bearer ${admin.accessToken}`);
 | 
					      const { status, body } = await request(app).get('/users').set('Authorization', `Bearer ${admin.accessToken}`);
 | 
				
			||||||
      expect(status).toEqual(200);
 | 
					      expect(status).toEqual(200);
 | 
				
			||||||
      expect(body).toHaveLength(5);
 | 
					      expect(body).toHaveLength(2);
 | 
				
			||||||
      expect(body).toEqual(
 | 
					 | 
				
			||||||
        expect.arrayContaining([
 | 
					 | 
				
			||||||
          expect.objectContaining({ email: 'admin@immich.cloud' }),
 | 
					 | 
				
			||||||
          expect.objectContaining({ email: 'user1@immich.cloud' }),
 | 
					 | 
				
			||||||
          expect.objectContaining({ email: 'user2@immich.cloud' }),
 | 
					 | 
				
			||||||
          expect.objectContaining({ email: 'user3@immich.cloud' }),
 | 
					 | 
				
			||||||
          expect.objectContaining({ email: 'user4@immich.cloud' }),
 | 
					 | 
				
			||||||
        ]),
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('should hide deleted users', async () => {
 | 
					 | 
				
			||||||
      const { status, body } = await request(app)
 | 
					 | 
				
			||||||
        .get(`/users`)
 | 
					 | 
				
			||||||
        .query({ isAll: true })
 | 
					 | 
				
			||||||
        .set('Authorization', `Bearer ${admin.accessToken}`);
 | 
					 | 
				
			||||||
      expect(status).toBe(200);
 | 
					 | 
				
			||||||
      expect(body).toHaveLength(4);
 | 
					 | 
				
			||||||
      expect(body).toEqual(
 | 
					      expect(body).toEqual(
 | 
				
			||||||
        expect.arrayContaining([
 | 
					        expect.arrayContaining([
 | 
				
			||||||
          expect.objectContaining({ email: 'admin@immich.cloud' }),
 | 
					          expect.objectContaining({ email: 'admin@immich.cloud' }),
 | 
				
			||||||
          expect.objectContaining({ email: 'user2@immich.cloud' }),
 | 
					          expect.objectContaining({ email: 'user2@immich.cloud' }),
 | 
				
			||||||
          expect.objectContaining({ email: 'user3@immich.cloud' }),
 | 
					 | 
				
			||||||
          expect.objectContaining({ email: 'user4@immich.cloud' }),
 | 
					 | 
				
			||||||
        ]),
 | 
					        ]),
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('should include deleted users', async () => {
 | 
					 | 
				
			||||||
      const { status, body } = await request(app)
 | 
					 | 
				
			||||||
        .get(`/users`)
 | 
					 | 
				
			||||||
        .query({ isAll: false })
 | 
					 | 
				
			||||||
        .set('Authorization', `Bearer ${admin.accessToken}`);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      expect(status).toBe(200);
 | 
					 | 
				
			||||||
      expect(body).toHaveLength(5);
 | 
					 | 
				
			||||||
      expect(body).toEqual(
 | 
					 | 
				
			||||||
        expect.arrayContaining([
 | 
					 | 
				
			||||||
          expect.objectContaining({ email: 'admin@immich.cloud' }),
 | 
					 | 
				
			||||||
          expect.objectContaining({ email: 'user1@immich.cloud' }),
 | 
					 | 
				
			||||||
          expect.objectContaining({ email: 'user2@immich.cloud' }),
 | 
					 | 
				
			||||||
          expect.objectContaining({ email: 'user3@immich.cloud' }),
 | 
					 | 
				
			||||||
          expect.objectContaining({ email: 'user4@immich.cloud' }),
 | 
					 | 
				
			||||||
        ]),
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  describe('GET /users/:id', () => {
 | 
					 | 
				
			||||||
    it('should require authentication', async () => {
 | 
					 | 
				
			||||||
      const { status } = await request(app).get(`/users/${admin.userId}`);
 | 
					 | 
				
			||||||
      expect(status).toEqual(401);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('should get the user info', async () => {
 | 
					 | 
				
			||||||
      const { status, body } = await request(app)
 | 
					 | 
				
			||||||
        .get(`/users/${admin.userId}`)
 | 
					 | 
				
			||||||
        .set('Authorization', `Bearer ${admin.accessToken}`);
 | 
					 | 
				
			||||||
      expect(status).toBe(200);
 | 
					 | 
				
			||||||
      expect(body).toMatchObject({
 | 
					 | 
				
			||||||
        id: admin.userId,
 | 
					 | 
				
			||||||
        email: 'admin@immich.cloud',
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  describe('GET /users/me', () => {
 | 
					  describe('GET /users/me', () => {
 | 
				
			||||||
@ -118,154 +52,54 @@ describe('/users', () => {
 | 
				
			|||||||
      expect(body).toEqual(errorDto.unauthorized);
 | 
					      expect(body).toEqual(errorDto.unauthorized);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('should get my info', async () => {
 | 
					    it('should not work for shared links', async () => {
 | 
				
			||||||
 | 
					      const album = await utils.createAlbum(admin.accessToken, { albumName: 'Album' });
 | 
				
			||||||
 | 
					      const sharedLink = await utils.createSharedLink(admin.accessToken, {
 | 
				
			||||||
 | 
					        type: SharedLinkType.Album,
 | 
				
			||||||
 | 
					        albumId: album.id,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      const { status, body } = await request(app).get(`/users/me?key=${sharedLink.key}`);
 | 
				
			||||||
 | 
					      expect(status).toBe(403);
 | 
				
			||||||
 | 
					      expect(body).toEqual(errorDto.forbidden);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should get my user', async () => {
 | 
				
			||||||
      const { status, body } = await request(app).get(`/users/me`).set('Authorization', `Bearer ${admin.accessToken}`);
 | 
					      const { status, body } = await request(app).get(`/users/me`).set('Authorization', `Bearer ${admin.accessToken}`);
 | 
				
			||||||
      expect(status).toBe(200);
 | 
					      expect(status).toBe(200);
 | 
				
			||||||
      expect(body).toMatchObject({
 | 
					      expect(body).toMatchObject({
 | 
				
			||||||
        id: admin.userId,
 | 
					        id: admin.userId,
 | 
				
			||||||
        email: 'admin@immich.cloud',
 | 
					        email: 'admin@immich.cloud',
 | 
				
			||||||
 | 
					        memoriesEnabled: true,
 | 
				
			||||||
 | 
					        quotaUsageInBytes: 0,
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  describe('POST /users', () => {
 | 
					  describe('PUT /users/me', () => {
 | 
				
			||||||
    it('should require authentication', async () => {
 | 
					    it('should require authentication', async () => {
 | 
				
			||||||
      const { status, body } = await request(app).post(`/users`).send(createUserDto.user1);
 | 
					      const { status, body } = await request(app).put(`/users/me`);
 | 
				
			||||||
      expect(status).toBe(401);
 | 
					      expect(status).toBe(401);
 | 
				
			||||||
      expect(body).toEqual(errorDto.unauthorized);
 | 
					      expect(body).toEqual(errorDto.unauthorized);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (const key of Object.keys(createUserDto.user1)) {
 | 
					    for (const key of ['email', 'name', 'memoriesEnabled', 'avatarColor']) {
 | 
				
			||||||
      it(`should not allow null ${key}`, async () => {
 | 
					      it(`should not allow null ${key}`, async () => {
 | 
				
			||||||
 | 
					        const dto = { [key]: null };
 | 
				
			||||||
        const { status, body } = await request(app)
 | 
					        const { status, body } = await request(app)
 | 
				
			||||||
          .post(`/users`)
 | 
					          .put(`/users/me`)
 | 
				
			||||||
          .set('Authorization', `Bearer ${admin.accessToken}`)
 | 
					          .set('Authorization', `Bearer ${admin.accessToken}`)
 | 
				
			||||||
          .send({ ...createUserDto.user1, [key]: null });
 | 
					          .send(dto);
 | 
				
			||||||
        expect(status).toBe(400);
 | 
					        expect(status).toBe(400);
 | 
				
			||||||
        expect(body).toEqual(errorDto.badRequest());
 | 
					        expect(body).toEqual(errorDto.badRequest());
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('should ignore `isAdmin`', async () => {
 | 
					 | 
				
			||||||
      const { status, body } = await request(app)
 | 
					 | 
				
			||||||
        .post(`/users`)
 | 
					 | 
				
			||||||
        .send({
 | 
					 | 
				
			||||||
          isAdmin: true,
 | 
					 | 
				
			||||||
          email: 'user5@immich.cloud',
 | 
					 | 
				
			||||||
          password: 'password123',
 | 
					 | 
				
			||||||
          name: 'Immich',
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        .set('Authorization', `Bearer ${admin.accessToken}`);
 | 
					 | 
				
			||||||
      expect(body).toMatchObject({
 | 
					 | 
				
			||||||
        email: 'user5@immich.cloud',
 | 
					 | 
				
			||||||
        isAdmin: false,
 | 
					 | 
				
			||||||
        shouldChangePassword: true,
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
      expect(status).toBe(201);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('should create a user without memories enabled', async () => {
 | 
					 | 
				
			||||||
      const { status, body } = await request(app)
 | 
					 | 
				
			||||||
        .post(`/users`)
 | 
					 | 
				
			||||||
        .send({
 | 
					 | 
				
			||||||
          email: 'no-memories@immich.cloud',
 | 
					 | 
				
			||||||
          password: 'Password123',
 | 
					 | 
				
			||||||
          name: 'No Memories',
 | 
					 | 
				
			||||||
          memoriesEnabled: false,
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        .set('Authorization', `Bearer ${admin.accessToken}`);
 | 
					 | 
				
			||||||
      expect(body).toMatchObject({
 | 
					 | 
				
			||||||
        email: 'no-memories@immich.cloud',
 | 
					 | 
				
			||||||
        memoriesEnabled: false,
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
      expect(status).toBe(201);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  describe('DELETE /users/:id', () => {
 | 
					 | 
				
			||||||
    it('should require authentication', async () => {
 | 
					 | 
				
			||||||
      const { status, body } = await request(app).delete(`/users/${userToDelete.userId}`);
 | 
					 | 
				
			||||||
      expect(status).toBe(401);
 | 
					 | 
				
			||||||
      expect(body).toEqual(errorDto.unauthorized);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('should delete user', async () => {
 | 
					 | 
				
			||||||
      const { status, body } = await request(app)
 | 
					 | 
				
			||||||
        .delete(`/users/${userToDelete.userId}`)
 | 
					 | 
				
			||||||
        .set('Authorization', `Bearer ${admin.accessToken}`);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      expect(status).toBe(200);
 | 
					 | 
				
			||||||
      expect(body).toMatchObject({
 | 
					 | 
				
			||||||
        id: userToDelete.userId,
 | 
					 | 
				
			||||||
        updatedAt: expect.any(String),
 | 
					 | 
				
			||||||
        deletedAt: expect.any(String),
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('should hard delete user', async () => {
 | 
					 | 
				
			||||||
      const { status, body } = await request(app)
 | 
					 | 
				
			||||||
        .delete(`/users/${userToHardDelete.userId}`)
 | 
					 | 
				
			||||||
        .send({ force: true })
 | 
					 | 
				
			||||||
        .set('Authorization', `Bearer ${admin.accessToken}`);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      expect(status).toBe(200);
 | 
					 | 
				
			||||||
      expect(body).toMatchObject({
 | 
					 | 
				
			||||||
        id: userToHardDelete.userId,
 | 
					 | 
				
			||||||
        updatedAt: expect.any(String),
 | 
					 | 
				
			||||||
        deletedAt: expect.any(String),
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      await utils.waitForWebsocketEvent({ event: 'userDelete', id: userToHardDelete.userId, timeout: 5000 });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  describe('PUT /users', () => {
 | 
					 | 
				
			||||||
    it('should require authentication', async () => {
 | 
					 | 
				
			||||||
      const { status, body } = await request(app).put(`/users`);
 | 
					 | 
				
			||||||
      expect(status).toBe(401);
 | 
					 | 
				
			||||||
      expect(body).toEqual(errorDto.unauthorized);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (const key of Object.keys(userDto.admin)) {
 | 
					 | 
				
			||||||
      it(`should not allow null ${key}`, async () => {
 | 
					 | 
				
			||||||
        const { status, body } = await request(app)
 | 
					 | 
				
			||||||
          .put(`/users`)
 | 
					 | 
				
			||||||
          .set('Authorization', `Bearer ${admin.accessToken}`)
 | 
					 | 
				
			||||||
          .send({ ...userDto.admin, [key]: null });
 | 
					 | 
				
			||||||
        expect(status).toBe(400);
 | 
					 | 
				
			||||||
        expect(body).toEqual(errorDto.badRequest());
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('should not allow a non-admin to become an admin', async () => {
 | 
					 | 
				
			||||||
      const { status, body } = await request(app)
 | 
					 | 
				
			||||||
        .put(`/users`)
 | 
					 | 
				
			||||||
        .send({ isAdmin: true, id: nonAdmin.userId })
 | 
					 | 
				
			||||||
        .set('Authorization', `Bearer ${admin.accessToken}`);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      expect(status).toBe(400);
 | 
					 | 
				
			||||||
      expect(body).toEqual(errorDto.alreadyHasAdmin);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('ignores updates to profileImagePath', async () => {
 | 
					 | 
				
			||||||
      const { status, body } = await request(app)
 | 
					 | 
				
			||||||
        .put(`/users`)
 | 
					 | 
				
			||||||
        .send({ id: admin.userId, profileImagePath: 'invalid.jpg' })
 | 
					 | 
				
			||||||
        .set('Authorization', `Bearer ${admin.accessToken}`);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      expect(status).toBe(200);
 | 
					 | 
				
			||||||
      expect(body).toMatchObject({ id: admin.userId, profileImagePath: '' });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('should update first and last name', async () => {
 | 
					    it('should update first and last name', async () => {
 | 
				
			||||||
      const before = await getUserById({ id: admin.userId }, { headers: asBearerAuth(admin.accessToken) });
 | 
					      const before = await getMyUser({ headers: asBearerAuth(admin.accessToken) });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const { status, body } = await request(app)
 | 
					      const { status, body } = await request(app)
 | 
				
			||||||
        .put(`/users`)
 | 
					        .put(`/users/me`)
 | 
				
			||||||
        .send({
 | 
					        .send({ name: 'Name' })
 | 
				
			||||||
          id: admin.userId,
 | 
					 | 
				
			||||||
          name: 'Name',
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        .set('Authorization', `Bearer ${admin.accessToken}`);
 | 
					        .set('Authorization', `Bearer ${admin.accessToken}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      expect(status).toBe(200);
 | 
					      expect(status).toBe(200);
 | 
				
			||||||
@ -274,17 +108,13 @@ describe('/users', () => {
 | 
				
			|||||||
        updatedAt: expect.any(String),
 | 
					        updatedAt: expect.any(String),
 | 
				
			||||||
        name: 'Name',
 | 
					        name: 'Name',
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      expect(before.updatedAt).not.toEqual(body.updatedAt);
 | 
					 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('should update memories enabled', async () => {
 | 
					    it('should update memories enabled', async () => {
 | 
				
			||||||
      const before = await getUserById({ id: admin.userId }, { headers: asBearerAuth(admin.accessToken) });
 | 
					      const before = await getMyUser({ headers: asBearerAuth(admin.accessToken) });
 | 
				
			||||||
      const { status, body } = await request(app)
 | 
					      const { status, body } = await request(app)
 | 
				
			||||||
        .put(`/users`)
 | 
					        .put(`/users/me`)
 | 
				
			||||||
        .send({
 | 
					        .send({ memoriesEnabled: false })
 | 
				
			||||||
          id: admin.userId,
 | 
					 | 
				
			||||||
          memoriesEnabled: false,
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        .set('Authorization', `Bearer ${admin.accessToken}`);
 | 
					        .set('Authorization', `Bearer ${admin.accessToken}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      expect(status).toBe(200);
 | 
					      expect(status).toBe(200);
 | 
				
			||||||
@ -293,7 +123,80 @@ describe('/users', () => {
 | 
				
			|||||||
        updatedAt: expect.anything(),
 | 
					        updatedAt: expect.anything(),
 | 
				
			||||||
        memoriesEnabled: false,
 | 
					        memoriesEnabled: false,
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      expect(before.updatedAt).not.toEqual(body.updatedAt);
 | 
					
 | 
				
			||||||
 | 
					      const after = await getMyUser({ headers: asBearerAuth(admin.accessToken) });
 | 
				
			||||||
 | 
					      expect(after.memoriesEnabled).toBe(false);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** @deprecated */
 | 
				
			||||||
 | 
					    it('should allow a user to change their password (deprecated)', async () => {
 | 
				
			||||||
 | 
					      const user = await getMyUser({ headers: asBearerAuth(nonAdmin.accessToken) });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(user.shouldChangePassword).toBe(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const { status, body } = await request(app)
 | 
				
			||||||
 | 
					        .put(`/users/me`)
 | 
				
			||||||
 | 
					        .send({ password: 'super-secret' })
 | 
				
			||||||
 | 
					        .set('Authorization', `Bearer ${nonAdmin.accessToken}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(status).toBe(200);
 | 
				
			||||||
 | 
					      expect(body).toMatchObject({
 | 
				
			||||||
 | 
					        email: nonAdmin.userEmail,
 | 
				
			||||||
 | 
					        shouldChangePassword: false,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const token = await login({ loginCredentialDto: { email: nonAdmin.userEmail, password: 'super-secret' } });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(token.accessToken).toBeDefined();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should not allow user to change to a taken email', async () => {
 | 
				
			||||||
 | 
					      const { status, body } = await request(app)
 | 
				
			||||||
 | 
					        .put(`/users/me`)
 | 
				
			||||||
 | 
					        .send({ email: 'admin@immich.cloud' })
 | 
				
			||||||
 | 
					        .set('Authorization', `Bearer ${nonAdmin.accessToken}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(status).toBe(400);
 | 
				
			||||||
 | 
					      expect(body).toMatchObject(errorDto.badRequest('Email already in use by another account'));
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should update my email', async () => {
 | 
				
			||||||
 | 
					      const before = await getMyUser({ headers: asBearerAuth(nonAdmin.accessToken) });
 | 
				
			||||||
 | 
					      const { status, body } = await request(app)
 | 
				
			||||||
 | 
					        .put(`/users/me`)
 | 
				
			||||||
 | 
					        .send({ email: 'non-admin@immich.cloud' })
 | 
				
			||||||
 | 
					        .set('Authorization', `Bearer ${nonAdmin.accessToken}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(status).toBe(200);
 | 
				
			||||||
 | 
					      expect(body).toMatchObject({
 | 
				
			||||||
 | 
					        ...before,
 | 
				
			||||||
 | 
					        email: 'non-admin@immich.cloud',
 | 
				
			||||||
 | 
					        updatedAt: expect.anything(),
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('GET /users/:id', () => {
 | 
				
			||||||
 | 
					    it('should require authentication', async () => {
 | 
				
			||||||
 | 
					      const { status } = await request(app).get(`/users/${admin.userId}`);
 | 
				
			||||||
 | 
					      expect(status).toEqual(401);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should get the user', async () => {
 | 
				
			||||||
 | 
					      const { status, body } = await request(app)
 | 
				
			||||||
 | 
					        .get(`/users/${admin.userId}`)
 | 
				
			||||||
 | 
					        .set('Authorization', `Bearer ${admin.accessToken}`);
 | 
				
			||||||
 | 
					      expect(status).toBe(200);
 | 
				
			||||||
 | 
					      expect(body).toMatchObject({
 | 
				
			||||||
 | 
					        id: admin.userId,
 | 
				
			||||||
 | 
					        email: 'admin@immich.cloud',
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(body).not.toMatchObject({
 | 
				
			||||||
 | 
					        shouldChangePassword: expect.anything(),
 | 
				
			||||||
 | 
					        memoriesEnabled: expect.anything(),
 | 
				
			||||||
 | 
					        storageLabel: expect.anything(),
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
				
			|||||||
@ -5,10 +5,10 @@ import {
 | 
				
			|||||||
  CreateAlbumDto,
 | 
					  CreateAlbumDto,
 | 
				
			||||||
  CreateAssetDto,
 | 
					  CreateAssetDto,
 | 
				
			||||||
  CreateLibraryDto,
 | 
					  CreateLibraryDto,
 | 
				
			||||||
  CreateUserDto,
 | 
					 | 
				
			||||||
  MetadataSearchDto,
 | 
					  MetadataSearchDto,
 | 
				
			||||||
  PersonCreateDto,
 | 
					  PersonCreateDto,
 | 
				
			||||||
  SharedLinkCreateDto,
 | 
					  SharedLinkCreateDto,
 | 
				
			||||||
 | 
					  UserAdminCreateDto,
 | 
				
			||||||
  ValidateLibraryDto,
 | 
					  ValidateLibraryDto,
 | 
				
			||||||
  createAlbum,
 | 
					  createAlbum,
 | 
				
			||||||
  createApiKey,
 | 
					  createApiKey,
 | 
				
			||||||
@ -16,7 +16,7 @@ import {
 | 
				
			|||||||
  createPartner,
 | 
					  createPartner,
 | 
				
			||||||
  createPerson,
 | 
					  createPerson,
 | 
				
			||||||
  createSharedLink,
 | 
					  createSharedLink,
 | 
				
			||||||
  createUser,
 | 
					  createUserAdmin,
 | 
				
			||||||
  deleteAssets,
 | 
					  deleteAssets,
 | 
				
			||||||
  getAllJobsStatus,
 | 
					  getAllJobsStatus,
 | 
				
			||||||
  getAssetInfo,
 | 
					  getAssetInfo,
 | 
				
			||||||
@ -273,8 +273,8 @@ export const utils = {
 | 
				
			|||||||
    return response;
 | 
					    return response;
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  userSetup: async (accessToken: string, dto: CreateUserDto) => {
 | 
					  userSetup: async (accessToken: string, dto: UserAdminCreateDto) => {
 | 
				
			||||||
    await createUser({ createUserDto: dto }, { headers: asBearerAuth(accessToken) });
 | 
					    await createUserAdmin({ userAdminCreateDto: dto }, { headers: asBearerAuth(accessToken) });
 | 
				
			||||||
    return login({
 | 
					    return login({
 | 
				
			||||||
      loginCredentialDto: { email: dto.email, password: dto.password },
 | 
					      loginCredentialDto: { email: dto.email, password: dto.password },
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
				
			|||||||
@ -27,7 +27,7 @@ class User {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  Id get isarId => fastHash(id);
 | 
					  Id get isarId => fastHash(id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  User.fromUserDto(UserResponseDto dto)
 | 
					  User.fromUserDto(UserAdminResponseDto dto)
 | 
				
			||||||
      : id = dto.id,
 | 
					      : id = dto.id,
 | 
				
			||||||
        updatedAt = dto.updatedAt,
 | 
					        updatedAt = dto.updatedAt,
 | 
				
			||||||
        email = dto.email,
 | 
					        email = dto.email,
 | 
				
			||||||
@ -44,21 +44,21 @@ class User {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  User.fromPartnerDto(PartnerResponseDto dto)
 | 
					  User.fromPartnerDto(PartnerResponseDto dto)
 | 
				
			||||||
      : id = dto.id,
 | 
					      : id = dto.id,
 | 
				
			||||||
        updatedAt = dto.updatedAt,
 | 
					        updatedAt = DateTime.now(),
 | 
				
			||||||
        email = dto.email,
 | 
					        email = dto.email,
 | 
				
			||||||
        name = dto.name,
 | 
					        name = dto.name,
 | 
				
			||||||
        isPartnerSharedBy = false,
 | 
					        isPartnerSharedBy = false,
 | 
				
			||||||
        isPartnerSharedWith = false,
 | 
					        isPartnerSharedWith = false,
 | 
				
			||||||
        profileImagePath = dto.profileImagePath,
 | 
					        profileImagePath = dto.profileImagePath,
 | 
				
			||||||
        isAdmin = dto.isAdmin,
 | 
					        isAdmin = false,
 | 
				
			||||||
        memoryEnabled = dto.memoriesEnabled ?? false,
 | 
					        memoryEnabled = false,
 | 
				
			||||||
        avatarColor = dto.avatarColor.toAvatarColor(),
 | 
					        avatarColor = dto.avatarColor.toAvatarColor(),
 | 
				
			||||||
        inTimeline = dto.inTimeline ?? false,
 | 
					        inTimeline = dto.inTimeline ?? false,
 | 
				
			||||||
        quotaUsageInBytes = dto.quotaUsageInBytes ?? 0,
 | 
					        quotaUsageInBytes = 0,
 | 
				
			||||||
        quotaSizeInBytes = dto.quotaSizeInBytes ?? 0;
 | 
					        quotaSizeInBytes = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Base user dto used where the complete user object is not required
 | 
					  /// Base user dto used where the complete user object is not required
 | 
				
			||||||
  User.fromSimpleUserDto(UserDto dto)
 | 
					  User.fromSimpleUserDto(UserResponseDto dto)
 | 
				
			||||||
      : id = dto.id,
 | 
					      : id = dto.id,
 | 
				
			||||||
        email = dto.email,
 | 
					        email = dto.email,
 | 
				
			||||||
        name = dto.name,
 | 
					        name = dto.name,
 | 
				
			||||||
 | 
				
			|||||||
@ -138,11 +138,9 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  Future<bool> changePassword(String newPassword) async {
 | 
					  Future<bool> changePassword(String newPassword) async {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      await _apiService.userApi.updateUser(
 | 
					      await _apiService.userApi.updateMyUser(
 | 
				
			||||||
        UpdateUserDto(
 | 
					        UserUpdateMeDto(
 | 
				
			||||||
          id: state.userId,
 | 
					 | 
				
			||||||
          password: newPassword,
 | 
					          password: newPassword,
 | 
				
			||||||
          shouldChangePassword: false,
 | 
					 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -178,9 +176,9 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
 | 
				
			|||||||
      user = offlineUser;
 | 
					      user = offlineUser;
 | 
				
			||||||
      retResult = false;
 | 
					      retResult = false;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      UserResponseDto? userResponseDto;
 | 
					      UserAdminResponseDto? userResponseDto;
 | 
				
			||||||
      try {
 | 
					      try {
 | 
				
			||||||
        userResponseDto = await _apiService.userApi.getMyUserInfo();
 | 
					        userResponseDto = await _apiService.userApi.getMyUser();
 | 
				
			||||||
      } on ApiException catch (error, stackTrace) {
 | 
					      } on ApiException catch (error, stackTrace) {
 | 
				
			||||||
        _log.severe(
 | 
					        _log.severe(
 | 
				
			||||||
          "Error getting user information from the server [API EXCEPTION]",
 | 
					          "Error getting user information from the server [API EXCEPTION]",
 | 
				
			||||||
 | 
				
			|||||||
@ -20,7 +20,7 @@ class CurrentUserProvider extends StateNotifier<User?> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  refresh() async {
 | 
					  refresh() async {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      final user = await _apiService.userApi.getMyUserInfo();
 | 
					      final user = await _apiService.userApi.getMyUser();
 | 
				
			||||||
      if (user != null) {
 | 
					      if (user != null) {
 | 
				
			||||||
        Store.put(
 | 
					        Store.put(
 | 
				
			||||||
          StoreKey.currentUser,
 | 
					          StoreKey.currentUser,
 | 
				
			||||||
 | 
				
			|||||||
@ -57,7 +57,7 @@ class TabNavigationObserver extends AutoRouterObserver {
 | 
				
			|||||||
      // Update user info
 | 
					      // Update user info
 | 
				
			||||||
      try {
 | 
					      try {
 | 
				
			||||||
        final userResponseDto =
 | 
					        final userResponseDto =
 | 
				
			||||||
            await ref.read(apiServiceProvider).userApi.getMyUserInfo();
 | 
					            await ref.read(apiServiceProvider).userApi.getMyUser();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (userResponseDto == null) {
 | 
					        if (userResponseDto == null) {
 | 
				
			||||||
          return;
 | 
					          return;
 | 
				
			||||||
 | 
				
			|||||||
@ -37,10 +37,10 @@ class UserService {
 | 
				
			|||||||
    this._partnerService,
 | 
					    this._partnerService,
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Future<List<User>?> _getAllUsers({required bool isAll}) async {
 | 
					  Future<List<User>?> _getAllUsers() async {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      final dto = await _apiService.userApi.getAllUsers(isAll);
 | 
					      final dto = await _apiService.userApi.searchUsers();
 | 
				
			||||||
      return dto?.map(User.fromUserDto).toList();
 | 
					      return dto?.map(User.fromSimpleUserDto).toList();
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      _log.warning("Failed get all users", e);
 | 
					      _log.warning("Failed get all users", e);
 | 
				
			||||||
      return null;
 | 
					      return null;
 | 
				
			||||||
@ -71,7 +71,7 @@ class UserService {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Future<List<User>?> getUsersFromServer() async {
 | 
					  Future<List<User>?> getUsersFromServer() async {
 | 
				
			||||||
    final List<User>? users = await _getAllUsers(isAll: true);
 | 
					    final List<User>? users = await _getAllUsers();
 | 
				
			||||||
    final List<User>? sharedBy =
 | 
					    final List<User>? sharedBy =
 | 
				
			||||||
        await _partnerService.getPartners(PartnerDirection.sharedBy);
 | 
					        await _partnerService.getPartners(PartnerDirection.sharedBy);
 | 
				
			||||||
    final List<User>? sharedWith =
 | 
					    final List<User>? sharedWith =
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										26
									
								
								mobile/openapi/README.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										26
									
								
								mobile/openapi/README.md
									
									
									
										generated
									
									
									
								
							@ -212,15 +212,18 @@ Class | Method | HTTP request | Description
 | 
				
			|||||||
*TrashApi* | [**restoreAssets**](doc//TrashApi.md#restoreassets) | **POST** /trash/restore/assets | 
 | 
					*TrashApi* | [**restoreAssets**](doc//TrashApi.md#restoreassets) | **POST** /trash/restore/assets | 
 | 
				
			||||||
*TrashApi* | [**restoreTrash**](doc//TrashApi.md#restoretrash) | **POST** /trash/restore | 
 | 
					*TrashApi* | [**restoreTrash**](doc//TrashApi.md#restoretrash) | **POST** /trash/restore | 
 | 
				
			||||||
*UserApi* | [**createProfileImage**](doc//UserApi.md#createprofileimage) | **POST** /users/profile-image | 
 | 
					*UserApi* | [**createProfileImage**](doc//UserApi.md#createprofileimage) | **POST** /users/profile-image | 
 | 
				
			||||||
*UserApi* | [**createUser**](doc//UserApi.md#createuser) | **POST** /users | 
 | 
					*UserApi* | [**createUserAdmin**](doc//UserApi.md#createuseradmin) | **POST** /admin/users | 
 | 
				
			||||||
*UserApi* | [**deleteProfileImage**](doc//UserApi.md#deleteprofileimage) | **DELETE** /users/profile-image | 
 | 
					*UserApi* | [**deleteProfileImage**](doc//UserApi.md#deleteprofileimage) | **DELETE** /users/profile-image | 
 | 
				
			||||||
*UserApi* | [**deleteUser**](doc//UserApi.md#deleteuser) | **DELETE** /users/{id} | 
 | 
					*UserApi* | [**deleteUserAdmin**](doc//UserApi.md#deleteuseradmin) | **DELETE** /admin/users/{id} | 
 | 
				
			||||||
*UserApi* | [**getAllUsers**](doc//UserApi.md#getallusers) | **GET** /users | 
 | 
					*UserApi* | [**getMyUser**](doc//UserApi.md#getmyuser) | **GET** /users/me | 
 | 
				
			||||||
*UserApi* | [**getMyUserInfo**](doc//UserApi.md#getmyuserinfo) | **GET** /users/me | 
 | 
					 | 
				
			||||||
*UserApi* | [**getProfileImage**](doc//UserApi.md#getprofileimage) | **GET** /users/{id}/profile-image | 
 | 
					*UserApi* | [**getProfileImage**](doc//UserApi.md#getprofileimage) | **GET** /users/{id}/profile-image | 
 | 
				
			||||||
*UserApi* | [**getUserById**](doc//UserApi.md#getuserbyid) | **GET** /users/{id} | 
 | 
					*UserApi* | [**getUser**](doc//UserApi.md#getuser) | **GET** /users/{id} | 
 | 
				
			||||||
*UserApi* | [**restoreUser**](doc//UserApi.md#restoreuser) | **POST** /users/{id}/restore | 
 | 
					*UserApi* | [**getUserAdmin**](doc//UserApi.md#getuseradmin) | **GET** /admin/users/{id} | 
 | 
				
			||||||
*UserApi* | [**updateUser**](doc//UserApi.md#updateuser) | **PUT** /users | 
 | 
					*UserApi* | [**restoreUserAdmin**](doc//UserApi.md#restoreuseradmin) | **POST** /admin/users/{id}/restore | 
 | 
				
			||||||
 | 
					*UserApi* | [**searchUsers**](doc//UserApi.md#searchusers) | **GET** /users | 
 | 
				
			||||||
 | 
					*UserApi* | [**searchUsersAdmin**](doc//UserApi.md#searchusersadmin) | **GET** /admin/users | 
 | 
				
			||||||
 | 
					*UserApi* | [**updateMyUser**](doc//UserApi.md#updatemyuser) | **PUT** /users/me | 
 | 
				
			||||||
 | 
					*UserApi* | [**updateUserAdmin**](doc//UserApi.md#updateuseradmin) | **PUT** /admin/users/{id} | 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Documentation For Models
 | 
					## Documentation For Models
 | 
				
			||||||
@ -280,8 +283,6 @@ Class | Method | HTTP request | Description
 | 
				
			|||||||
 - [CreateLibraryDto](doc//CreateLibraryDto.md)
 | 
					 - [CreateLibraryDto](doc//CreateLibraryDto.md)
 | 
				
			||||||
 - [CreateProfileImageResponseDto](doc//CreateProfileImageResponseDto.md)
 | 
					 - [CreateProfileImageResponseDto](doc//CreateProfileImageResponseDto.md)
 | 
				
			||||||
 - [CreateTagDto](doc//CreateTagDto.md)
 | 
					 - [CreateTagDto](doc//CreateTagDto.md)
 | 
				
			||||||
 - [CreateUserDto](doc//CreateUserDto.md)
 | 
					 | 
				
			||||||
 - [DeleteUserDto](doc//DeleteUserDto.md)
 | 
					 | 
				
			||||||
 - [DownloadArchiveInfo](doc//DownloadArchiveInfo.md)
 | 
					 - [DownloadArchiveInfo](doc//DownloadArchiveInfo.md)
 | 
				
			||||||
 - [DownloadInfoDto](doc//DownloadInfoDto.md)
 | 
					 - [DownloadInfoDto](doc//DownloadInfoDto.md)
 | 
				
			||||||
 - [DownloadResponseDto](doc//DownloadResponseDto.md)
 | 
					 - [DownloadResponseDto](doc//DownloadResponseDto.md)
 | 
				
			||||||
@ -402,12 +403,15 @@ Class | Method | HTTP request | Description
 | 
				
			|||||||
 - [UpdatePartnerDto](doc//UpdatePartnerDto.md)
 | 
					 - [UpdatePartnerDto](doc//UpdatePartnerDto.md)
 | 
				
			||||||
 - [UpdateStackParentDto](doc//UpdateStackParentDto.md)
 | 
					 - [UpdateStackParentDto](doc//UpdateStackParentDto.md)
 | 
				
			||||||
 - [UpdateTagDto](doc//UpdateTagDto.md)
 | 
					 - [UpdateTagDto](doc//UpdateTagDto.md)
 | 
				
			||||||
 - [UpdateUserDto](doc//UpdateUserDto.md)
 | 
					 | 
				
			||||||
 - [UsageByUserDto](doc//UsageByUserDto.md)
 | 
					 - [UsageByUserDto](doc//UsageByUserDto.md)
 | 
				
			||||||
 | 
					 - [UserAdminCreateDto](doc//UserAdminCreateDto.md)
 | 
				
			||||||
 | 
					 - [UserAdminDeleteDto](doc//UserAdminDeleteDto.md)
 | 
				
			||||||
 | 
					 - [UserAdminResponseDto](doc//UserAdminResponseDto.md)
 | 
				
			||||||
 | 
					 - [UserAdminUpdateDto](doc//UserAdminUpdateDto.md)
 | 
				
			||||||
 - [UserAvatarColor](doc//UserAvatarColor.md)
 | 
					 - [UserAvatarColor](doc//UserAvatarColor.md)
 | 
				
			||||||
 - [UserDto](doc//UserDto.md)
 | 
					 | 
				
			||||||
 - [UserResponseDto](doc//UserResponseDto.md)
 | 
					 - [UserResponseDto](doc//UserResponseDto.md)
 | 
				
			||||||
 - [UserStatus](doc//UserStatus.md)
 | 
					 - [UserStatus](doc//UserStatus.md)
 | 
				
			||||||
 | 
					 - [UserUpdateMeDto](doc//UserUpdateMeDto.md)
 | 
				
			||||||
 - [ValidateAccessTokenResponseDto](doc//ValidateAccessTokenResponseDto.md)
 | 
					 - [ValidateAccessTokenResponseDto](doc//ValidateAccessTokenResponseDto.md)
 | 
				
			||||||
 - [ValidateLibraryDto](doc//ValidateLibraryDto.md)
 | 
					 - [ValidateLibraryDto](doc//ValidateLibraryDto.md)
 | 
				
			||||||
 - [ValidateLibraryImportPathResponseDto](doc//ValidateLibraryImportPathResponseDto.md)
 | 
					 - [ValidateLibraryImportPathResponseDto](doc//ValidateLibraryImportPathResponseDto.md)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										9
									
								
								mobile/openapi/lib/api.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										9
									
								
								mobile/openapi/lib/api.dart
									
									
									
										generated
									
									
									
								
							@ -112,8 +112,6 @@ part 'model/create_album_dto.dart';
 | 
				
			|||||||
part 'model/create_library_dto.dart';
 | 
					part 'model/create_library_dto.dart';
 | 
				
			||||||
part 'model/create_profile_image_response_dto.dart';
 | 
					part 'model/create_profile_image_response_dto.dart';
 | 
				
			||||||
part 'model/create_tag_dto.dart';
 | 
					part 'model/create_tag_dto.dart';
 | 
				
			||||||
part 'model/create_user_dto.dart';
 | 
					 | 
				
			||||||
part 'model/delete_user_dto.dart';
 | 
					 | 
				
			||||||
part 'model/download_archive_info.dart';
 | 
					part 'model/download_archive_info.dart';
 | 
				
			||||||
part 'model/download_info_dto.dart';
 | 
					part 'model/download_info_dto.dart';
 | 
				
			||||||
part 'model/download_response_dto.dart';
 | 
					part 'model/download_response_dto.dart';
 | 
				
			||||||
@ -234,12 +232,15 @@ part 'model/update_library_dto.dart';
 | 
				
			|||||||
part 'model/update_partner_dto.dart';
 | 
					part 'model/update_partner_dto.dart';
 | 
				
			||||||
part 'model/update_stack_parent_dto.dart';
 | 
					part 'model/update_stack_parent_dto.dart';
 | 
				
			||||||
part 'model/update_tag_dto.dart';
 | 
					part 'model/update_tag_dto.dart';
 | 
				
			||||||
part 'model/update_user_dto.dart';
 | 
					 | 
				
			||||||
part 'model/usage_by_user_dto.dart';
 | 
					part 'model/usage_by_user_dto.dart';
 | 
				
			||||||
 | 
					part 'model/user_admin_create_dto.dart';
 | 
				
			||||||
 | 
					part 'model/user_admin_delete_dto.dart';
 | 
				
			||||||
 | 
					part 'model/user_admin_response_dto.dart';
 | 
				
			||||||
 | 
					part 'model/user_admin_update_dto.dart';
 | 
				
			||||||
part 'model/user_avatar_color.dart';
 | 
					part 'model/user_avatar_color.dart';
 | 
				
			||||||
part 'model/user_dto.dart';
 | 
					 | 
				
			||||||
part 'model/user_response_dto.dart';
 | 
					part 'model/user_response_dto.dart';
 | 
				
			||||||
part 'model/user_status.dart';
 | 
					part 'model/user_status.dart';
 | 
				
			||||||
 | 
					part 'model/user_update_me_dto.dart';
 | 
				
			||||||
part 'model/validate_access_token_response_dto.dart';
 | 
					part 'model/validate_access_token_response_dto.dart';
 | 
				
			||||||
part 'model/validate_library_dto.dart';
 | 
					part 'model/validate_library_dto.dart';
 | 
				
			||||||
part 'model/validate_library_import_path_response_dto.dart';
 | 
					part 'model/validate_library_import_path_response_dto.dart';
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										8
									
								
								mobile/openapi/lib/api/authentication_api.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								mobile/openapi/lib/api/authentication_api.dart
									
									
									
										generated
									
									
									
								
							@ -48,7 +48,7 @@ class AuthenticationApi {
 | 
				
			|||||||
  /// Parameters:
 | 
					  /// Parameters:
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// * [ChangePasswordDto] changePasswordDto (required):
 | 
					  /// * [ChangePasswordDto] changePasswordDto (required):
 | 
				
			||||||
  Future<UserResponseDto?> changePassword(ChangePasswordDto changePasswordDto,) async {
 | 
					  Future<UserAdminResponseDto?> changePassword(ChangePasswordDto changePasswordDto,) async {
 | 
				
			||||||
    final response = await changePasswordWithHttpInfo(changePasswordDto,);
 | 
					    final response = await changePasswordWithHttpInfo(changePasswordDto,);
 | 
				
			||||||
    if (response.statusCode >= HttpStatus.badRequest) {
 | 
					    if (response.statusCode >= HttpStatus.badRequest) {
 | 
				
			||||||
      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
 | 
					      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
 | 
				
			||||||
@ -57,7 +57,7 @@ class AuthenticationApi {
 | 
				
			|||||||
    // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
 | 
					    // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
 | 
				
			||||||
    // FormatException when trying to decode an empty string.
 | 
					    // FormatException when trying to decode an empty string.
 | 
				
			||||||
    if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
 | 
					    if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
 | 
				
			||||||
      return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserResponseDto',) as UserResponseDto;
 | 
					      return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserAdminResponseDto',) as UserAdminResponseDto;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
@ -183,7 +183,7 @@ class AuthenticationApi {
 | 
				
			|||||||
  /// Parameters:
 | 
					  /// Parameters:
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// * [SignUpDto] signUpDto (required):
 | 
					  /// * [SignUpDto] signUpDto (required):
 | 
				
			||||||
  Future<UserResponseDto?> signUpAdmin(SignUpDto signUpDto,) async {
 | 
					  Future<UserAdminResponseDto?> signUpAdmin(SignUpDto signUpDto,) async {
 | 
				
			||||||
    final response = await signUpAdminWithHttpInfo(signUpDto,);
 | 
					    final response = await signUpAdminWithHttpInfo(signUpDto,);
 | 
				
			||||||
    if (response.statusCode >= HttpStatus.badRequest) {
 | 
					    if (response.statusCode >= HttpStatus.badRequest) {
 | 
				
			||||||
      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
 | 
					      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
 | 
				
			||||||
@ -192,7 +192,7 @@ class AuthenticationApi {
 | 
				
			|||||||
    // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
 | 
					    // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
 | 
				
			||||||
    // FormatException when trying to decode an empty string.
 | 
					    // FormatException when trying to decode an empty string.
 | 
				
			||||||
    if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
 | 
					    if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
 | 
				
			||||||
      return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserResponseDto',) as UserResponseDto;
 | 
					      return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserAdminResponseDto',) as UserAdminResponseDto;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										8
									
								
								mobile/openapi/lib/api/o_auth_api.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								mobile/openapi/lib/api/o_auth_api.dart
									
									
									
										generated
									
									
									
								
							@ -95,7 +95,7 @@ class OAuthApi {
 | 
				
			|||||||
  /// Parameters:
 | 
					  /// Parameters:
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// * [OAuthCallbackDto] oAuthCallbackDto (required):
 | 
					  /// * [OAuthCallbackDto] oAuthCallbackDto (required):
 | 
				
			||||||
  Future<UserResponseDto?> linkOAuthAccount(OAuthCallbackDto oAuthCallbackDto,) async {
 | 
					  Future<UserAdminResponseDto?> linkOAuthAccount(OAuthCallbackDto oAuthCallbackDto,) async {
 | 
				
			||||||
    final response = await linkOAuthAccountWithHttpInfo(oAuthCallbackDto,);
 | 
					    final response = await linkOAuthAccountWithHttpInfo(oAuthCallbackDto,);
 | 
				
			||||||
    if (response.statusCode >= HttpStatus.badRequest) {
 | 
					    if (response.statusCode >= HttpStatus.badRequest) {
 | 
				
			||||||
      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
 | 
					      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
 | 
				
			||||||
@ -104,7 +104,7 @@ class OAuthApi {
 | 
				
			|||||||
    // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
 | 
					    // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
 | 
				
			||||||
    // FormatException when trying to decode an empty string.
 | 
					    // FormatException when trying to decode an empty string.
 | 
				
			||||||
    if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
 | 
					    if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
 | 
				
			||||||
      return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserResponseDto',) as UserResponseDto;
 | 
					      return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserAdminResponseDto',) as UserAdminResponseDto;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
@ -216,7 +216,7 @@ class OAuthApi {
 | 
				
			|||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Future<UserResponseDto?> unlinkOAuthAccount() async {
 | 
					  Future<UserAdminResponseDto?> unlinkOAuthAccount() async {
 | 
				
			||||||
    final response = await unlinkOAuthAccountWithHttpInfo();
 | 
					    final response = await unlinkOAuthAccountWithHttpInfo();
 | 
				
			||||||
    if (response.statusCode >= HttpStatus.badRequest) {
 | 
					    if (response.statusCode >= HttpStatus.badRequest) {
 | 
				
			||||||
      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
 | 
					      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
 | 
				
			||||||
@ -225,7 +225,7 @@ class OAuthApi {
 | 
				
			|||||||
    // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
 | 
					    // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
 | 
				
			||||||
    // FormatException when trying to decode an empty string.
 | 
					    // FormatException when trying to decode an empty string.
 | 
				
			||||||
    if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
 | 
					    if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
 | 
				
			||||||
      return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserResponseDto',) as UserResponseDto;
 | 
					      return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserAdminResponseDto',) as UserAdminResponseDto;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										332
									
								
								mobile/openapi/lib/api/user_api.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										332
									
								
								mobile/openapi/lib/api/user_api.dart
									
									
									
										generated
									
									
									
								
							@ -73,16 +73,16 @@ class UserApi {
 | 
				
			|||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Performs an HTTP 'POST /users' operation and returns the [Response].
 | 
					  /// Performs an HTTP 'POST /admin/users' operation and returns the [Response].
 | 
				
			||||||
  /// Parameters:
 | 
					  /// Parameters:
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// * [CreateUserDto] createUserDto (required):
 | 
					  /// * [UserAdminCreateDto] userAdminCreateDto (required):
 | 
				
			||||||
  Future<Response> createUserWithHttpInfo(CreateUserDto createUserDto,) async {
 | 
					  Future<Response> createUserAdminWithHttpInfo(UserAdminCreateDto userAdminCreateDto,) async {
 | 
				
			||||||
    // ignore: prefer_const_declarations
 | 
					    // ignore: prefer_const_declarations
 | 
				
			||||||
    final path = r'/users';
 | 
					    final path = r'/admin/users';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // ignore: prefer_final_locals
 | 
					    // ignore: prefer_final_locals
 | 
				
			||||||
    Object? postBody = createUserDto;
 | 
					    Object? postBody = userAdminCreateDto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final queryParams = <QueryParam>[];
 | 
					    final queryParams = <QueryParam>[];
 | 
				
			||||||
    final headerParams = <String, String>{};
 | 
					    final headerParams = <String, String>{};
 | 
				
			||||||
@ -104,9 +104,9 @@ class UserApi {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  /// Parameters:
 | 
					  /// Parameters:
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// * [CreateUserDto] createUserDto (required):
 | 
					  /// * [UserAdminCreateDto] userAdminCreateDto (required):
 | 
				
			||||||
  Future<UserResponseDto?> createUser(CreateUserDto createUserDto,) async {
 | 
					  Future<UserAdminResponseDto?> createUserAdmin(UserAdminCreateDto userAdminCreateDto,) async {
 | 
				
			||||||
    final response = await createUserWithHttpInfo(createUserDto,);
 | 
					    final response = await createUserAdminWithHttpInfo(userAdminCreateDto,);
 | 
				
			||||||
    if (response.statusCode >= HttpStatus.badRequest) {
 | 
					    if (response.statusCode >= HttpStatus.badRequest) {
 | 
				
			||||||
      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
 | 
					      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -114,7 +114,7 @@ class UserApi {
 | 
				
			|||||||
    // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
 | 
					    // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
 | 
				
			||||||
    // FormatException when trying to decode an empty string.
 | 
					    // FormatException when trying to decode an empty string.
 | 
				
			||||||
    if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
 | 
					    if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
 | 
				
			||||||
      return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserResponseDto',) as UserResponseDto;
 | 
					      return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserAdminResponseDto',) as UserAdminResponseDto;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
@ -153,19 +153,19 @@ class UserApi {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Performs an HTTP 'DELETE /users/{id}' operation and returns the [Response].
 | 
					  /// Performs an HTTP 'DELETE /admin/users/{id}' operation and returns the [Response].
 | 
				
			||||||
  /// Parameters:
 | 
					  /// Parameters:
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// * [String] id (required):
 | 
					  /// * [String] id (required):
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// * [DeleteUserDto] deleteUserDto (required):
 | 
					  /// * [UserAdminDeleteDto] userAdminDeleteDto (required):
 | 
				
			||||||
  Future<Response> deleteUserWithHttpInfo(String id, DeleteUserDto deleteUserDto,) async {
 | 
					  Future<Response> deleteUserAdminWithHttpInfo(String id, UserAdminDeleteDto userAdminDeleteDto,) async {
 | 
				
			||||||
    // ignore: prefer_const_declarations
 | 
					    // ignore: prefer_const_declarations
 | 
				
			||||||
    final path = r'/users/{id}'
 | 
					    final path = r'/admin/users/{id}'
 | 
				
			||||||
      .replaceAll('{id}', id);
 | 
					      .replaceAll('{id}', id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // ignore: prefer_final_locals
 | 
					    // ignore: prefer_final_locals
 | 
				
			||||||
    Object? postBody = deleteUserDto;
 | 
					    Object? postBody = userAdminDeleteDto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final queryParams = <QueryParam>[];
 | 
					    final queryParams = <QueryParam>[];
 | 
				
			||||||
    final headerParams = <String, String>{};
 | 
					    final headerParams = <String, String>{};
 | 
				
			||||||
@ -189,9 +189,9 @@ class UserApi {
 | 
				
			|||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// * [String] id (required):
 | 
					  /// * [String] id (required):
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// * [DeleteUserDto] deleteUserDto (required):
 | 
					  /// * [UserAdminDeleteDto] userAdminDeleteDto (required):
 | 
				
			||||||
  Future<UserResponseDto?> deleteUser(String id, DeleteUserDto deleteUserDto,) async {
 | 
					  Future<UserAdminResponseDto?> deleteUserAdmin(String id, UserAdminDeleteDto userAdminDeleteDto,) async {
 | 
				
			||||||
    final response = await deleteUserWithHttpInfo(id, deleteUserDto,);
 | 
					    final response = await deleteUserAdminWithHttpInfo(id, userAdminDeleteDto,);
 | 
				
			||||||
    if (response.statusCode >= HttpStatus.badRequest) {
 | 
					    if (response.statusCode >= HttpStatus.badRequest) {
 | 
				
			||||||
      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
 | 
					      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -199,66 +199,14 @@ class UserApi {
 | 
				
			|||||||
    // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
 | 
					    // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
 | 
				
			||||||
    // FormatException when trying to decode an empty string.
 | 
					    // FormatException when trying to decode an empty string.
 | 
				
			||||||
    if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
 | 
					    if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
 | 
				
			||||||
      return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserResponseDto',) as UserResponseDto;
 | 
					      return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserAdminResponseDto',) as UserAdminResponseDto;
 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return null;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /// Performs an HTTP 'GET /users' operation and returns the [Response].
 | 
					 | 
				
			||||||
  /// Parameters:
 | 
					 | 
				
			||||||
  ///
 | 
					 | 
				
			||||||
  /// * [bool] isAll (required):
 | 
					 | 
				
			||||||
  Future<Response> getAllUsersWithHttpInfo(bool isAll,) async {
 | 
					 | 
				
			||||||
    // ignore: prefer_const_declarations
 | 
					 | 
				
			||||||
    final path = r'/users';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // ignore: prefer_final_locals
 | 
					 | 
				
			||||||
    Object? postBody;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    final queryParams = <QueryParam>[];
 | 
					 | 
				
			||||||
    final headerParams = <String, String>{};
 | 
					 | 
				
			||||||
    final formParams = <String, String>{};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      queryParams.addAll(_queryParams('', 'isAll', isAll));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const contentTypes = <String>[];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return apiClient.invokeAPI(
 | 
					 | 
				
			||||||
      path,
 | 
					 | 
				
			||||||
      'GET',
 | 
					 | 
				
			||||||
      queryParams,
 | 
					 | 
				
			||||||
      postBody,
 | 
					 | 
				
			||||||
      headerParams,
 | 
					 | 
				
			||||||
      formParams,
 | 
					 | 
				
			||||||
      contentTypes.isEmpty ? null : contentTypes.first,
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /// Parameters:
 | 
					 | 
				
			||||||
  ///
 | 
					 | 
				
			||||||
  /// * [bool] isAll (required):
 | 
					 | 
				
			||||||
  Future<List<UserResponseDto>?> getAllUsers(bool isAll,) async {
 | 
					 | 
				
			||||||
    final response = await getAllUsersWithHttpInfo(isAll,);
 | 
					 | 
				
			||||||
    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) {
 | 
					 | 
				
			||||||
      final responseBody = await _decodeBodyBytes(response);
 | 
					 | 
				
			||||||
      return (await apiClient.deserializeAsync(responseBody, 'List<UserResponseDto>') as List)
 | 
					 | 
				
			||||||
        .cast<UserResponseDto>()
 | 
					 | 
				
			||||||
        .toList(growable: false);
 | 
					 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Performs an HTTP 'GET /users/me' operation and returns the [Response].
 | 
					  /// Performs an HTTP 'GET /users/me' operation and returns the [Response].
 | 
				
			||||||
  Future<Response> getMyUserInfoWithHttpInfo() async {
 | 
					  Future<Response> getMyUserWithHttpInfo() async {
 | 
				
			||||||
    // ignore: prefer_const_declarations
 | 
					    // ignore: prefer_const_declarations
 | 
				
			||||||
    final path = r'/users/me';
 | 
					    final path = r'/users/me';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -283,8 +231,8 @@ class UserApi {
 | 
				
			|||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Future<UserResponseDto?> getMyUserInfo() async {
 | 
					  Future<UserAdminResponseDto?> getMyUser() async {
 | 
				
			||||||
    final response = await getMyUserInfoWithHttpInfo();
 | 
					    final response = await getMyUserWithHttpInfo();
 | 
				
			||||||
    if (response.statusCode >= HttpStatus.badRequest) {
 | 
					    if (response.statusCode >= HttpStatus.badRequest) {
 | 
				
			||||||
      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
 | 
					      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -292,7 +240,7 @@ class UserApi {
 | 
				
			|||||||
    // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
 | 
					    // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
 | 
				
			||||||
    // FormatException when trying to decode an empty string.
 | 
					    // FormatException when trying to decode an empty string.
 | 
				
			||||||
    if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
 | 
					    if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
 | 
				
			||||||
      return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserResponseDto',) as UserResponseDto;
 | 
					      return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserAdminResponseDto',) as UserAdminResponseDto;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
@ -350,7 +298,7 @@ class UserApi {
 | 
				
			|||||||
  /// Parameters:
 | 
					  /// Parameters:
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// * [String] id (required):
 | 
					  /// * [String] id (required):
 | 
				
			||||||
  Future<Response> getUserByIdWithHttpInfo(String id,) async {
 | 
					  Future<Response> getUserWithHttpInfo(String id,) async {
 | 
				
			||||||
    // ignore: prefer_const_declarations
 | 
					    // ignore: prefer_const_declarations
 | 
				
			||||||
    final path = r'/users/{id}'
 | 
					    final path = r'/users/{id}'
 | 
				
			||||||
      .replaceAll('{id}', id);
 | 
					      .replaceAll('{id}', id);
 | 
				
			||||||
@ -379,8 +327,8 @@ class UserApi {
 | 
				
			|||||||
  /// Parameters:
 | 
					  /// Parameters:
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// * [String] id (required):
 | 
					  /// * [String] id (required):
 | 
				
			||||||
  Future<UserResponseDto?> getUserById(String id,) async {
 | 
					  Future<UserResponseDto?> getUser(String id,) async {
 | 
				
			||||||
    final response = await getUserByIdWithHttpInfo(id,);
 | 
					    final response = await getUserWithHttpInfo(id,);
 | 
				
			||||||
    if (response.statusCode >= HttpStatus.badRequest) {
 | 
					    if (response.statusCode >= HttpStatus.badRequest) {
 | 
				
			||||||
      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
 | 
					      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -394,13 +342,61 @@ class UserApi {
 | 
				
			|||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Performs an HTTP 'POST /users/{id}/restore' operation and returns the [Response].
 | 
					  /// Performs an HTTP 'GET /admin/users/{id}' operation and returns the [Response].
 | 
				
			||||||
  /// Parameters:
 | 
					  /// Parameters:
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// * [String] id (required):
 | 
					  /// * [String] id (required):
 | 
				
			||||||
  Future<Response> restoreUserWithHttpInfo(String id,) async {
 | 
					  Future<Response> getUserAdminWithHttpInfo(String id,) async {
 | 
				
			||||||
    // ignore: prefer_const_declarations
 | 
					    // ignore: prefer_const_declarations
 | 
				
			||||||
    final path = r'/users/{id}/restore'
 | 
					    final path = r'/admin/users/{id}'
 | 
				
			||||||
 | 
					      .replaceAll('{id}', id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 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,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Parameters:
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// * [String] id (required):
 | 
				
			||||||
 | 
					  Future<UserAdminResponseDto?> getUserAdmin(String id,) async {
 | 
				
			||||||
 | 
					    final response = await getUserAdminWithHttpInfo(id,);
 | 
				
			||||||
 | 
					    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), 'UserAdminResponseDto',) as UserAdminResponseDto;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Performs an HTTP 'POST /admin/users/{id}/restore' operation and returns the [Response].
 | 
				
			||||||
 | 
					  /// Parameters:
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// * [String] id (required):
 | 
				
			||||||
 | 
					  Future<Response> restoreUserAdminWithHttpInfo(String id,) async {
 | 
				
			||||||
 | 
					    // ignore: prefer_const_declarations
 | 
				
			||||||
 | 
					    final path = r'/admin/users/{id}/restore'
 | 
				
			||||||
      .replaceAll('{id}', id);
 | 
					      .replaceAll('{id}', id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // ignore: prefer_final_locals
 | 
					    // ignore: prefer_final_locals
 | 
				
			||||||
@ -427,8 +423,8 @@ class UserApi {
 | 
				
			|||||||
  /// Parameters:
 | 
					  /// Parameters:
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// * [String] id (required):
 | 
					  /// * [String] id (required):
 | 
				
			||||||
  Future<UserResponseDto?> restoreUser(String id,) async {
 | 
					  Future<UserAdminResponseDto?> restoreUserAdmin(String id,) async {
 | 
				
			||||||
    final response = await restoreUserWithHttpInfo(id,);
 | 
					    final response = await restoreUserAdminWithHttpInfo(id,);
 | 
				
			||||||
    if (response.statusCode >= HttpStatus.badRequest) {
 | 
					    if (response.statusCode >= HttpStatus.badRequest) {
 | 
				
			||||||
      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
 | 
					      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -436,22 +432,120 @@ class UserApi {
 | 
				
			|||||||
    // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
 | 
					    // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
 | 
				
			||||||
    // FormatException when trying to decode an empty string.
 | 
					    // FormatException when trying to decode an empty string.
 | 
				
			||||||
    if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
 | 
					    if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
 | 
				
			||||||
      return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserResponseDto',) as UserResponseDto;
 | 
					      return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserAdminResponseDto',) as UserAdminResponseDto;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Performs an HTTP 'PUT /users' operation and returns the [Response].
 | 
					  /// Performs an HTTP 'GET /users' operation and returns the [Response].
 | 
				
			||||||
  /// Parameters:
 | 
					  Future<Response> searchUsersWithHttpInfo() async {
 | 
				
			||||||
  ///
 | 
					 | 
				
			||||||
  /// * [UpdateUserDto] updateUserDto (required):
 | 
					 | 
				
			||||||
  Future<Response> updateUserWithHttpInfo(UpdateUserDto updateUserDto,) async {
 | 
					 | 
				
			||||||
    // ignore: prefer_const_declarations
 | 
					    // ignore: prefer_const_declarations
 | 
				
			||||||
    final path = r'/users';
 | 
					    final path = r'/users';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // ignore: prefer_final_locals
 | 
					    // ignore: prefer_final_locals
 | 
				
			||||||
    Object? postBody = updateUserDto;
 | 
					    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<List<UserResponseDto>?> searchUsers() async {
 | 
				
			||||||
 | 
					    final response = await searchUsersWithHttpInfo();
 | 
				
			||||||
 | 
					    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) {
 | 
				
			||||||
 | 
					      final responseBody = await _decodeBodyBytes(response);
 | 
				
			||||||
 | 
					      return (await apiClient.deserializeAsync(responseBody, 'List<UserResponseDto>') as List)
 | 
				
			||||||
 | 
					        .cast<UserResponseDto>()
 | 
				
			||||||
 | 
					        .toList(growable: false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Performs an HTTP 'GET /admin/users' operation and returns the [Response].
 | 
				
			||||||
 | 
					  /// Parameters:
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// * [bool] withDeleted:
 | 
				
			||||||
 | 
					  Future<Response> searchUsersAdminWithHttpInfo({ bool? withDeleted, }) async {
 | 
				
			||||||
 | 
					    // ignore: prefer_const_declarations
 | 
				
			||||||
 | 
					    final path = r'/admin/users';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ignore: prefer_final_locals
 | 
				
			||||||
 | 
					    Object? postBody;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    final queryParams = <QueryParam>[];
 | 
				
			||||||
 | 
					    final headerParams = <String, String>{};
 | 
				
			||||||
 | 
					    final formParams = <String, String>{};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (withDeleted != null) {
 | 
				
			||||||
 | 
					      queryParams.addAll(_queryParams('', 'withDeleted', withDeleted));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const contentTypes = <String>[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return apiClient.invokeAPI(
 | 
				
			||||||
 | 
					      path,
 | 
				
			||||||
 | 
					      'GET',
 | 
				
			||||||
 | 
					      queryParams,
 | 
				
			||||||
 | 
					      postBody,
 | 
				
			||||||
 | 
					      headerParams,
 | 
				
			||||||
 | 
					      formParams,
 | 
				
			||||||
 | 
					      contentTypes.isEmpty ? null : contentTypes.first,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Parameters:
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// * [bool] withDeleted:
 | 
				
			||||||
 | 
					  Future<List<UserAdminResponseDto>?> searchUsersAdmin({ bool? withDeleted, }) async {
 | 
				
			||||||
 | 
					    final response = await searchUsersAdminWithHttpInfo( withDeleted: withDeleted, );
 | 
				
			||||||
 | 
					    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) {
 | 
				
			||||||
 | 
					      final responseBody = await _decodeBodyBytes(response);
 | 
				
			||||||
 | 
					      return (await apiClient.deserializeAsync(responseBody, 'List<UserAdminResponseDto>') as List)
 | 
				
			||||||
 | 
					        .cast<UserAdminResponseDto>()
 | 
				
			||||||
 | 
					        .toList(growable: false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Performs an HTTP 'PUT /users/me' operation and returns the [Response].
 | 
				
			||||||
 | 
					  /// Parameters:
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// * [UserUpdateMeDto] userUpdateMeDto (required):
 | 
				
			||||||
 | 
					  Future<Response> updateMyUserWithHttpInfo(UserUpdateMeDto userUpdateMeDto,) async {
 | 
				
			||||||
 | 
					    // ignore: prefer_const_declarations
 | 
				
			||||||
 | 
					    final path = r'/users/me';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ignore: prefer_final_locals
 | 
				
			||||||
 | 
					    Object? postBody = userUpdateMeDto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final queryParams = <QueryParam>[];
 | 
					    final queryParams = <QueryParam>[];
 | 
				
			||||||
    final headerParams = <String, String>{};
 | 
					    final headerParams = <String, String>{};
 | 
				
			||||||
@ -473,9 +567,9 @@ class UserApi {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  /// Parameters:
 | 
					  /// Parameters:
 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// * [UpdateUserDto] updateUserDto (required):
 | 
					  /// * [UserUpdateMeDto] userUpdateMeDto (required):
 | 
				
			||||||
  Future<UserResponseDto?> updateUser(UpdateUserDto updateUserDto,) async {
 | 
					  Future<UserAdminResponseDto?> updateMyUser(UserUpdateMeDto userUpdateMeDto,) async {
 | 
				
			||||||
    final response = await updateUserWithHttpInfo(updateUserDto,);
 | 
					    final response = await updateMyUserWithHttpInfo(userUpdateMeDto,);
 | 
				
			||||||
    if (response.statusCode >= HttpStatus.badRequest) {
 | 
					    if (response.statusCode >= HttpStatus.badRequest) {
 | 
				
			||||||
      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
 | 
					      throw ApiException(response.statusCode, await _decodeBodyBytes(response));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -483,7 +577,59 @@ class UserApi {
 | 
				
			|||||||
    // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
 | 
					    // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
 | 
				
			||||||
    // FormatException when trying to decode an empty string.
 | 
					    // FormatException when trying to decode an empty string.
 | 
				
			||||||
    if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
 | 
					    if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
 | 
				
			||||||
      return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserResponseDto',) as UserResponseDto;
 | 
					      return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserAdminResponseDto',) as UserAdminResponseDto;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Performs an HTTP 'PUT /admin/users/{id}' operation and returns the [Response].
 | 
				
			||||||
 | 
					  /// Parameters:
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// * [String] id (required):
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// * [UserAdminUpdateDto] userAdminUpdateDto (required):
 | 
				
			||||||
 | 
					  Future<Response> updateUserAdminWithHttpInfo(String id, UserAdminUpdateDto userAdminUpdateDto,) async {
 | 
				
			||||||
 | 
					    // ignore: prefer_const_declarations
 | 
				
			||||||
 | 
					    final path = r'/admin/users/{id}'
 | 
				
			||||||
 | 
					      .replaceAll('{id}', id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ignore: prefer_final_locals
 | 
				
			||||||
 | 
					    Object? postBody = userAdminUpdateDto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    final queryParams = <QueryParam>[];
 | 
				
			||||||
 | 
					    final headerParams = <String, String>{};
 | 
				
			||||||
 | 
					    final formParams = <String, String>{};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const contentTypes = <String>['application/json'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return apiClient.invokeAPI(
 | 
				
			||||||
 | 
					      path,
 | 
				
			||||||
 | 
					      'PUT',
 | 
				
			||||||
 | 
					      queryParams,
 | 
				
			||||||
 | 
					      postBody,
 | 
				
			||||||
 | 
					      headerParams,
 | 
				
			||||||
 | 
					      formParams,
 | 
				
			||||||
 | 
					      contentTypes.isEmpty ? null : contentTypes.first,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Parameters:
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// * [String] id (required):
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// * [UserAdminUpdateDto] userAdminUpdateDto (required):
 | 
				
			||||||
 | 
					  Future<UserAdminResponseDto?> updateUserAdmin(String id, UserAdminUpdateDto userAdminUpdateDto,) async {
 | 
				
			||||||
 | 
					    final response = await updateUserAdminWithHttpInfo(id, userAdminUpdateDto,);
 | 
				
			||||||
 | 
					    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), 'UserAdminResponseDto',) as UserAdminResponseDto;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										18
									
								
								mobile/openapi/lib/api_client.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										18
									
								
								mobile/openapi/lib/api_client.dart
									
									
									
										generated
									
									
									
								
							@ -292,10 +292,6 @@ class ApiClient {
 | 
				
			|||||||
          return CreateProfileImageResponseDto.fromJson(value);
 | 
					          return CreateProfileImageResponseDto.fromJson(value);
 | 
				
			||||||
        case 'CreateTagDto':
 | 
					        case 'CreateTagDto':
 | 
				
			||||||
          return CreateTagDto.fromJson(value);
 | 
					          return CreateTagDto.fromJson(value);
 | 
				
			||||||
        case 'CreateUserDto':
 | 
					 | 
				
			||||||
          return CreateUserDto.fromJson(value);
 | 
					 | 
				
			||||||
        case 'DeleteUserDto':
 | 
					 | 
				
			||||||
          return DeleteUserDto.fromJson(value);
 | 
					 | 
				
			||||||
        case 'DownloadArchiveInfo':
 | 
					        case 'DownloadArchiveInfo':
 | 
				
			||||||
          return DownloadArchiveInfo.fromJson(value);
 | 
					          return DownloadArchiveInfo.fromJson(value);
 | 
				
			||||||
        case 'DownloadInfoDto':
 | 
					        case 'DownloadInfoDto':
 | 
				
			||||||
@ -536,18 +532,24 @@ class ApiClient {
 | 
				
			|||||||
          return UpdateStackParentDto.fromJson(value);
 | 
					          return UpdateStackParentDto.fromJson(value);
 | 
				
			||||||
        case 'UpdateTagDto':
 | 
					        case 'UpdateTagDto':
 | 
				
			||||||
          return UpdateTagDto.fromJson(value);
 | 
					          return UpdateTagDto.fromJson(value);
 | 
				
			||||||
        case 'UpdateUserDto':
 | 
					 | 
				
			||||||
          return UpdateUserDto.fromJson(value);
 | 
					 | 
				
			||||||
        case 'UsageByUserDto':
 | 
					        case 'UsageByUserDto':
 | 
				
			||||||
          return UsageByUserDto.fromJson(value);
 | 
					          return UsageByUserDto.fromJson(value);
 | 
				
			||||||
 | 
					        case 'UserAdminCreateDto':
 | 
				
			||||||
 | 
					          return UserAdminCreateDto.fromJson(value);
 | 
				
			||||||
 | 
					        case 'UserAdminDeleteDto':
 | 
				
			||||||
 | 
					          return UserAdminDeleteDto.fromJson(value);
 | 
				
			||||||
 | 
					        case 'UserAdminResponseDto':
 | 
				
			||||||
 | 
					          return UserAdminResponseDto.fromJson(value);
 | 
				
			||||||
 | 
					        case 'UserAdminUpdateDto':
 | 
				
			||||||
 | 
					          return UserAdminUpdateDto.fromJson(value);
 | 
				
			||||||
        case 'UserAvatarColor':
 | 
					        case 'UserAvatarColor':
 | 
				
			||||||
          return UserAvatarColorTypeTransformer().decode(value);
 | 
					          return UserAvatarColorTypeTransformer().decode(value);
 | 
				
			||||||
        case 'UserDto':
 | 
					 | 
				
			||||||
          return UserDto.fromJson(value);
 | 
					 | 
				
			||||||
        case 'UserResponseDto':
 | 
					        case 'UserResponseDto':
 | 
				
			||||||
          return UserResponseDto.fromJson(value);
 | 
					          return UserResponseDto.fromJson(value);
 | 
				
			||||||
        case 'UserStatus':
 | 
					        case 'UserStatus':
 | 
				
			||||||
          return UserStatusTypeTransformer().decode(value);
 | 
					          return UserStatusTypeTransformer().decode(value);
 | 
				
			||||||
 | 
					        case 'UserUpdateMeDto':
 | 
				
			||||||
 | 
					          return UserUpdateMeDto.fromJson(value);
 | 
				
			||||||
        case 'ValidateAccessTokenResponseDto':
 | 
					        case 'ValidateAccessTokenResponseDto':
 | 
				
			||||||
          return ValidateAccessTokenResponseDto.fromJson(value);
 | 
					          return ValidateAccessTokenResponseDto.fromJson(value);
 | 
				
			||||||
        case 'ValidateLibraryDto':
 | 
					        case 'ValidateLibraryDto':
 | 
				
			||||||
 | 
				
			|||||||
@ -31,7 +31,7 @@ class ActivityResponseDto {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  ActivityResponseDtoTypeEnum type;
 | 
					  ActivityResponseDtoTypeEnum type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  UserDto user;
 | 
					  UserResponseDto user;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  bool operator ==(Object other) => identical(this, other) || other is ActivityResponseDto &&
 | 
					  bool operator ==(Object other) => identical(this, other) || other is ActivityResponseDto &&
 | 
				
			||||||
@ -87,7 +87,7 @@ class ActivityResponseDto {
 | 
				
			|||||||
        createdAt: mapDateTime(json, r'createdAt', r'')!,
 | 
					        createdAt: mapDateTime(json, r'createdAt', r'')!,
 | 
				
			||||||
        id: mapValueOfType<String>(json, r'id')!,
 | 
					        id: mapValueOfType<String>(json, r'id')!,
 | 
				
			||||||
        type: ActivityResponseDtoTypeEnum.fromJson(json[r'type'])!,
 | 
					        type: ActivityResponseDtoTypeEnum.fromJson(json[r'type'])!,
 | 
				
			||||||
        user: UserDto.fromJson(json[r'user'])!,
 | 
					        user: UserResponseDto.fromJson(json[r'user'])!,
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										119
									
								
								mobile/openapi/lib/model/partner_response_dto.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										119
									
								
								mobile/openapi/lib/model/partner_response_dto.dart
									
									
									
										generated
									
									
									
								
							@ -14,30 +14,15 @@ class PartnerResponseDto {
 | 
				
			|||||||
  /// Returns a new [PartnerResponseDto] instance.
 | 
					  /// Returns a new [PartnerResponseDto] instance.
 | 
				
			||||||
  PartnerResponseDto({
 | 
					  PartnerResponseDto({
 | 
				
			||||||
    required this.avatarColor,
 | 
					    required this.avatarColor,
 | 
				
			||||||
    required this.createdAt,
 | 
					 | 
				
			||||||
    required this.deletedAt,
 | 
					 | 
				
			||||||
    required this.email,
 | 
					    required this.email,
 | 
				
			||||||
    required this.id,
 | 
					    required this.id,
 | 
				
			||||||
    this.inTimeline,
 | 
					    this.inTimeline,
 | 
				
			||||||
    required this.isAdmin,
 | 
					 | 
				
			||||||
    this.memoriesEnabled,
 | 
					 | 
				
			||||||
    required this.name,
 | 
					    required this.name,
 | 
				
			||||||
    required this.oauthId,
 | 
					 | 
				
			||||||
    required this.profileImagePath,
 | 
					    required this.profileImagePath,
 | 
				
			||||||
    required this.quotaSizeInBytes,
 | 
					 | 
				
			||||||
    required this.quotaUsageInBytes,
 | 
					 | 
				
			||||||
    required this.shouldChangePassword,
 | 
					 | 
				
			||||||
    required this.status,
 | 
					 | 
				
			||||||
    required this.storageLabel,
 | 
					 | 
				
			||||||
    required this.updatedAt,
 | 
					 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  UserAvatarColor avatarColor;
 | 
					  UserAvatarColor avatarColor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  DateTime createdAt;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  DateTime? deletedAt;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  String email;
 | 
					  String email;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  String id;
 | 
					  String id;
 | 
				
			||||||
@ -50,121 +35,44 @@ class PartnerResponseDto {
 | 
				
			|||||||
  ///
 | 
					  ///
 | 
				
			||||||
  bool? inTimeline;
 | 
					  bool? inTimeline;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool isAdmin;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ///
 | 
					 | 
				
			||||||
  /// Please note: This property should have been non-nullable! Since the specification file
 | 
					 | 
				
			||||||
  /// does not include a default value (using the "default:" property), however, the generated
 | 
					 | 
				
			||||||
  /// source code must fall back to having a nullable type.
 | 
					 | 
				
			||||||
  /// Consider adding a "default:" property in the specification file to hide this note.
 | 
					 | 
				
			||||||
  ///
 | 
					 | 
				
			||||||
  bool? memoriesEnabled;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  String name;
 | 
					  String name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  String oauthId;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  String profileImagePath;
 | 
					  String profileImagePath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  int? quotaSizeInBytes;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  int? quotaUsageInBytes;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  bool shouldChangePassword;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  UserStatus status;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  String? storageLabel;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  DateTime updatedAt;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  bool operator ==(Object other) => identical(this, other) || other is PartnerResponseDto &&
 | 
					  bool operator ==(Object other) => identical(this, other) || other is PartnerResponseDto &&
 | 
				
			||||||
    other.avatarColor == avatarColor &&
 | 
					    other.avatarColor == avatarColor &&
 | 
				
			||||||
    other.createdAt == createdAt &&
 | 
					 | 
				
			||||||
    other.deletedAt == deletedAt &&
 | 
					 | 
				
			||||||
    other.email == email &&
 | 
					    other.email == email &&
 | 
				
			||||||
    other.id == id &&
 | 
					    other.id == id &&
 | 
				
			||||||
    other.inTimeline == inTimeline &&
 | 
					    other.inTimeline == inTimeline &&
 | 
				
			||||||
    other.isAdmin == isAdmin &&
 | 
					 | 
				
			||||||
    other.memoriesEnabled == memoriesEnabled &&
 | 
					 | 
				
			||||||
    other.name == name &&
 | 
					    other.name == name &&
 | 
				
			||||||
    other.oauthId == oauthId &&
 | 
					    other.profileImagePath == profileImagePath;
 | 
				
			||||||
    other.profileImagePath == profileImagePath &&
 | 
					 | 
				
			||||||
    other.quotaSizeInBytes == quotaSizeInBytes &&
 | 
					 | 
				
			||||||
    other.quotaUsageInBytes == quotaUsageInBytes &&
 | 
					 | 
				
			||||||
    other.shouldChangePassword == shouldChangePassword &&
 | 
					 | 
				
			||||||
    other.status == status &&
 | 
					 | 
				
			||||||
    other.storageLabel == storageLabel &&
 | 
					 | 
				
			||||||
    other.updatedAt == updatedAt;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  int get hashCode =>
 | 
					  int get hashCode =>
 | 
				
			||||||
    // ignore: unnecessary_parenthesis
 | 
					    // ignore: unnecessary_parenthesis
 | 
				
			||||||
    (avatarColor.hashCode) +
 | 
					    (avatarColor.hashCode) +
 | 
				
			||||||
    (createdAt.hashCode) +
 | 
					 | 
				
			||||||
    (deletedAt == null ? 0 : deletedAt!.hashCode) +
 | 
					 | 
				
			||||||
    (email.hashCode) +
 | 
					    (email.hashCode) +
 | 
				
			||||||
    (id.hashCode) +
 | 
					    (id.hashCode) +
 | 
				
			||||||
    (inTimeline == null ? 0 : inTimeline!.hashCode) +
 | 
					    (inTimeline == null ? 0 : inTimeline!.hashCode) +
 | 
				
			||||||
    (isAdmin.hashCode) +
 | 
					 | 
				
			||||||
    (memoriesEnabled == null ? 0 : memoriesEnabled!.hashCode) +
 | 
					 | 
				
			||||||
    (name.hashCode) +
 | 
					    (name.hashCode) +
 | 
				
			||||||
    (oauthId.hashCode) +
 | 
					    (profileImagePath.hashCode);
 | 
				
			||||||
    (profileImagePath.hashCode) +
 | 
					 | 
				
			||||||
    (quotaSizeInBytes == null ? 0 : quotaSizeInBytes!.hashCode) +
 | 
					 | 
				
			||||||
    (quotaUsageInBytes == null ? 0 : quotaUsageInBytes!.hashCode) +
 | 
					 | 
				
			||||||
    (shouldChangePassword.hashCode) +
 | 
					 | 
				
			||||||
    (status.hashCode) +
 | 
					 | 
				
			||||||
    (storageLabel == null ? 0 : storageLabel!.hashCode) +
 | 
					 | 
				
			||||||
    (updatedAt.hashCode);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  String toString() => 'PartnerResponseDto[avatarColor=$avatarColor, createdAt=$createdAt, deletedAt=$deletedAt, email=$email, id=$id, inTimeline=$inTimeline, isAdmin=$isAdmin, memoriesEnabled=$memoriesEnabled, name=$name, oauthId=$oauthId, profileImagePath=$profileImagePath, quotaSizeInBytes=$quotaSizeInBytes, quotaUsageInBytes=$quotaUsageInBytes, shouldChangePassword=$shouldChangePassword, status=$status, storageLabel=$storageLabel, updatedAt=$updatedAt]';
 | 
					  String toString() => 'PartnerResponseDto[avatarColor=$avatarColor, email=$email, id=$id, inTimeline=$inTimeline, name=$name, profileImagePath=$profileImagePath]';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Map<String, dynamic> toJson() {
 | 
					  Map<String, dynamic> toJson() {
 | 
				
			||||||
    final json = <String, dynamic>{};
 | 
					    final json = <String, dynamic>{};
 | 
				
			||||||
      json[r'avatarColor'] = this.avatarColor;
 | 
					      json[r'avatarColor'] = this.avatarColor;
 | 
				
			||||||
      json[r'createdAt'] = this.createdAt.toUtc().toIso8601String();
 | 
					 | 
				
			||||||
    if (this.deletedAt != null) {
 | 
					 | 
				
			||||||
      json[r'deletedAt'] = this.deletedAt!.toUtc().toIso8601String();
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
    //  json[r'deletedAt'] = null;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
      json[r'email'] = this.email;
 | 
					      json[r'email'] = this.email;
 | 
				
			||||||
      json[r'id'] = this.id;
 | 
					      json[r'id'] = this.id;
 | 
				
			||||||
    if (this.inTimeline != null) {
 | 
					    if (this.inTimeline != null) {
 | 
				
			||||||
      json[r'inTimeline'] = this.inTimeline;
 | 
					      json[r'inTimeline'] = this.inTimeline;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
    //  json[r'inTimeline'] = null;
 | 
					    //  json[r'inTimeline'] = null;
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
      json[r'isAdmin'] = this.isAdmin;
 | 
					 | 
				
			||||||
    if (this.memoriesEnabled != null) {
 | 
					 | 
				
			||||||
      json[r'memoriesEnabled'] = this.memoriesEnabled;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
    //  json[r'memoriesEnabled'] = null;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
      json[r'name'] = this.name;
 | 
					      json[r'name'] = this.name;
 | 
				
			||||||
      json[r'oauthId'] = this.oauthId;
 | 
					 | 
				
			||||||
      json[r'profileImagePath'] = this.profileImagePath;
 | 
					      json[r'profileImagePath'] = this.profileImagePath;
 | 
				
			||||||
    if (this.quotaSizeInBytes != null) {
 | 
					 | 
				
			||||||
      json[r'quotaSizeInBytes'] = this.quotaSizeInBytes;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
    //  json[r'quotaSizeInBytes'] = null;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (this.quotaUsageInBytes != null) {
 | 
					 | 
				
			||||||
      json[r'quotaUsageInBytes'] = this.quotaUsageInBytes;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
    //  json[r'quotaUsageInBytes'] = null;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
      json[r'shouldChangePassword'] = this.shouldChangePassword;
 | 
					 | 
				
			||||||
      json[r'status'] = this.status;
 | 
					 | 
				
			||||||
    if (this.storageLabel != null) {
 | 
					 | 
				
			||||||
      json[r'storageLabel'] = this.storageLabel;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
    //  json[r'storageLabel'] = null;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
      json[r'updatedAt'] = this.updatedAt.toUtc().toIso8601String();
 | 
					 | 
				
			||||||
    return json;
 | 
					    return json;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -177,22 +85,11 @@ class PartnerResponseDto {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      return PartnerResponseDto(
 | 
					      return PartnerResponseDto(
 | 
				
			||||||
        avatarColor: UserAvatarColor.fromJson(json[r'avatarColor'])!,
 | 
					        avatarColor: UserAvatarColor.fromJson(json[r'avatarColor'])!,
 | 
				
			||||||
        createdAt: mapDateTime(json, r'createdAt', r'')!,
 | 
					 | 
				
			||||||
        deletedAt: mapDateTime(json, r'deletedAt', r''),
 | 
					 | 
				
			||||||
        email: mapValueOfType<String>(json, r'email')!,
 | 
					        email: mapValueOfType<String>(json, r'email')!,
 | 
				
			||||||
        id: mapValueOfType<String>(json, r'id')!,
 | 
					        id: mapValueOfType<String>(json, r'id')!,
 | 
				
			||||||
        inTimeline: mapValueOfType<bool>(json, r'inTimeline'),
 | 
					        inTimeline: mapValueOfType<bool>(json, r'inTimeline'),
 | 
				
			||||||
        isAdmin: mapValueOfType<bool>(json, r'isAdmin')!,
 | 
					 | 
				
			||||||
        memoriesEnabled: mapValueOfType<bool>(json, r'memoriesEnabled'),
 | 
					 | 
				
			||||||
        name: mapValueOfType<String>(json, r'name')!,
 | 
					        name: mapValueOfType<String>(json, r'name')!,
 | 
				
			||||||
        oauthId: mapValueOfType<String>(json, r'oauthId')!,
 | 
					 | 
				
			||||||
        profileImagePath: mapValueOfType<String>(json, r'profileImagePath')!,
 | 
					        profileImagePath: mapValueOfType<String>(json, r'profileImagePath')!,
 | 
				
			||||||
        quotaSizeInBytes: mapValueOfType<int>(json, r'quotaSizeInBytes'),
 | 
					 | 
				
			||||||
        quotaUsageInBytes: mapValueOfType<int>(json, r'quotaUsageInBytes'),
 | 
					 | 
				
			||||||
        shouldChangePassword: mapValueOfType<bool>(json, r'shouldChangePassword')!,
 | 
					 | 
				
			||||||
        status: UserStatus.fromJson(json[r'status'])!,
 | 
					 | 
				
			||||||
        storageLabel: mapValueOfType<String>(json, r'storageLabel'),
 | 
					 | 
				
			||||||
        updatedAt: mapDateTime(json, r'updatedAt', r'')!,
 | 
					 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
@ -241,20 +138,10 @@ class PartnerResponseDto {
 | 
				
			|||||||
  /// 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>{
 | 
				
			||||||
    'avatarColor',
 | 
					    'avatarColor',
 | 
				
			||||||
    'createdAt',
 | 
					 | 
				
			||||||
    'deletedAt',
 | 
					 | 
				
			||||||
    'email',
 | 
					    'email',
 | 
				
			||||||
    'id',
 | 
					    'id',
 | 
				
			||||||
    'isAdmin',
 | 
					 | 
				
			||||||
    'name',
 | 
					    'name',
 | 
				
			||||||
    'oauthId',
 | 
					 | 
				
			||||||
    'profileImagePath',
 | 
					    'profileImagePath',
 | 
				
			||||||
    'quotaSizeInBytes',
 | 
					 | 
				
			||||||
    'quotaUsageInBytes',
 | 
					 | 
				
			||||||
    'shouldChangePassword',
 | 
					 | 
				
			||||||
    'status',
 | 
					 | 
				
			||||||
    'storageLabel',
 | 
					 | 
				
			||||||
    'updatedAt',
 | 
					 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -10,9 +10,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
part of openapi.api;
 | 
					part of openapi.api;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CreateUserDto {
 | 
					class UserAdminCreateDto {
 | 
				
			||||||
  /// Returns a new [CreateUserDto] instance.
 | 
					  /// Returns a new [UserAdminCreateDto] instance.
 | 
				
			||||||
  CreateUserDto({
 | 
					  UserAdminCreateDto({
 | 
				
			||||||
    required this.email,
 | 
					    required this.email,
 | 
				
			||||||
    this.memoriesEnabled,
 | 
					    this.memoriesEnabled,
 | 
				
			||||||
    required this.name,
 | 
					    required this.name,
 | 
				
			||||||
@ -59,7 +59,7 @@ class CreateUserDto {
 | 
				
			|||||||
  String? storageLabel;
 | 
					  String? storageLabel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  bool operator ==(Object other) => identical(this, other) || other is CreateUserDto &&
 | 
					  bool operator ==(Object other) => identical(this, other) || other is UserAdminCreateDto &&
 | 
				
			||||||
    other.email == email &&
 | 
					    other.email == email &&
 | 
				
			||||||
    other.memoriesEnabled == memoriesEnabled &&
 | 
					    other.memoriesEnabled == memoriesEnabled &&
 | 
				
			||||||
    other.name == name &&
 | 
					    other.name == name &&
 | 
				
			||||||
@ -82,7 +82,7 @@ class CreateUserDto {
 | 
				
			|||||||
    (storageLabel == null ? 0 : storageLabel!.hashCode);
 | 
					    (storageLabel == null ? 0 : storageLabel!.hashCode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  String toString() => 'CreateUserDto[email=$email, memoriesEnabled=$memoriesEnabled, name=$name, notify=$notify, password=$password, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]';
 | 
					  String toString() => 'UserAdminCreateDto[email=$email, memoriesEnabled=$memoriesEnabled, name=$name, notify=$notify, password=$password, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Map<String, dynamic> toJson() {
 | 
					  Map<String, dynamic> toJson() {
 | 
				
			||||||
    final json = <String, dynamic>{};
 | 
					    final json = <String, dynamic>{};
 | 
				
			||||||
@ -117,14 +117,14 @@ class CreateUserDto {
 | 
				
			|||||||
    return json;
 | 
					    return json;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Returns a new [CreateUserDto] instance and imports its values from
 | 
					  /// Returns a new [UserAdminCreateDto] 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 CreateUserDto? fromJson(dynamic value) {
 | 
					  static UserAdminCreateDto? fromJson(dynamic value) {
 | 
				
			||||||
    if (value is Map) {
 | 
					    if (value is Map) {
 | 
				
			||||||
      final json = value.cast<String, dynamic>();
 | 
					      final json = value.cast<String, dynamic>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return CreateUserDto(
 | 
					      return UserAdminCreateDto(
 | 
				
			||||||
        email: mapValueOfType<String>(json, r'email')!,
 | 
					        email: mapValueOfType<String>(json, r'email')!,
 | 
				
			||||||
        memoriesEnabled: mapValueOfType<bool>(json, r'memoriesEnabled'),
 | 
					        memoriesEnabled: mapValueOfType<bool>(json, r'memoriesEnabled'),
 | 
				
			||||||
        name: mapValueOfType<String>(json, r'name')!,
 | 
					        name: mapValueOfType<String>(json, r'name')!,
 | 
				
			||||||
@ -138,11 +138,11 @@ class CreateUserDto {
 | 
				
			|||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static List<CreateUserDto> listFromJson(dynamic json, {bool growable = false,}) {
 | 
					  static List<UserAdminCreateDto> listFromJson(dynamic json, {bool growable = false,}) {
 | 
				
			||||||
    final result = <CreateUserDto>[];
 | 
					    final result = <UserAdminCreateDto>[];
 | 
				
			||||||
    if (json is List && json.isNotEmpty) {
 | 
					    if (json is List && json.isNotEmpty) {
 | 
				
			||||||
      for (final row in json) {
 | 
					      for (final row in json) {
 | 
				
			||||||
        final value = CreateUserDto.fromJson(row);
 | 
					        final value = UserAdminCreateDto.fromJson(row);
 | 
				
			||||||
        if (value != null) {
 | 
					        if (value != null) {
 | 
				
			||||||
          result.add(value);
 | 
					          result.add(value);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -151,12 +151,12 @@ class CreateUserDto {
 | 
				
			|||||||
    return result.toList(growable: growable);
 | 
					    return result.toList(growable: growable);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static Map<String, CreateUserDto> mapFromJson(dynamic json) {
 | 
					  static Map<String, UserAdminCreateDto> mapFromJson(dynamic json) {
 | 
				
			||||||
    final map = <String, CreateUserDto>{};
 | 
					    final map = <String, UserAdminCreateDto>{};
 | 
				
			||||||
    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 = CreateUserDto.fromJson(entry.value);
 | 
					        final value = UserAdminCreateDto.fromJson(entry.value);
 | 
				
			||||||
        if (value != null) {
 | 
					        if (value != null) {
 | 
				
			||||||
          map[entry.key] = value;
 | 
					          map[entry.key] = value;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -165,14 +165,14 @@ class CreateUserDto {
 | 
				
			|||||||
    return map;
 | 
					    return map;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // maps a json object with a list of CreateUserDto-objects as value to a dart map
 | 
					  // maps a json object with a list of UserAdminCreateDto-objects as value to a dart map
 | 
				
			||||||
  static Map<String, List<CreateUserDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
 | 
					  static Map<String, List<UserAdminCreateDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
 | 
				
			||||||
    final map = <String, List<CreateUserDto>>{};
 | 
					    final map = <String, List<UserAdminCreateDto>>{};
 | 
				
			||||||
    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] = CreateUserDto.listFromJson(entry.value, growable: growable,);
 | 
					        map[entry.key] = UserAdminCreateDto.listFromJson(entry.value, growable: growable,);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return map;
 | 
					    return map;
 | 
				
			||||||
@ -10,9 +10,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
part of openapi.api;
 | 
					part of openapi.api;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DeleteUserDto {
 | 
					class UserAdminDeleteDto {
 | 
				
			||||||
  /// Returns a new [DeleteUserDto] instance.
 | 
					  /// Returns a new [UserAdminDeleteDto] instance.
 | 
				
			||||||
  DeleteUserDto({
 | 
					  UserAdminDeleteDto({
 | 
				
			||||||
    this.force,
 | 
					    this.force,
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -25,7 +25,7 @@ class DeleteUserDto {
 | 
				
			|||||||
  bool? force;
 | 
					  bool? force;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  bool operator ==(Object other) => identical(this, other) || other is DeleteUserDto &&
 | 
					  bool operator ==(Object other) => identical(this, other) || other is UserAdminDeleteDto &&
 | 
				
			||||||
    other.force == force;
 | 
					    other.force == force;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
@ -34,7 +34,7 @@ class DeleteUserDto {
 | 
				
			|||||||
    (force == null ? 0 : force!.hashCode);
 | 
					    (force == null ? 0 : force!.hashCode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  String toString() => 'DeleteUserDto[force=$force]';
 | 
					  String toString() => 'UserAdminDeleteDto[force=$force]';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Map<String, dynamic> toJson() {
 | 
					  Map<String, dynamic> toJson() {
 | 
				
			||||||
    final json = <String, dynamic>{};
 | 
					    final json = <String, dynamic>{};
 | 
				
			||||||
@ -46,25 +46,25 @@ class DeleteUserDto {
 | 
				
			|||||||
    return json;
 | 
					    return json;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Returns a new [DeleteUserDto] instance and imports its values from
 | 
					  /// Returns a new [UserAdminDeleteDto] 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 DeleteUserDto? fromJson(dynamic value) {
 | 
					  static UserAdminDeleteDto? fromJson(dynamic value) {
 | 
				
			||||||
    if (value is Map) {
 | 
					    if (value is Map) {
 | 
				
			||||||
      final json = value.cast<String, dynamic>();
 | 
					      final json = value.cast<String, dynamic>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return DeleteUserDto(
 | 
					      return UserAdminDeleteDto(
 | 
				
			||||||
        force: mapValueOfType<bool>(json, r'force'),
 | 
					        force: mapValueOfType<bool>(json, r'force'),
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static List<DeleteUserDto> listFromJson(dynamic json, {bool growable = false,}) {
 | 
					  static List<UserAdminDeleteDto> listFromJson(dynamic json, {bool growable = false,}) {
 | 
				
			||||||
    final result = <DeleteUserDto>[];
 | 
					    final result = <UserAdminDeleteDto>[];
 | 
				
			||||||
    if (json is List && json.isNotEmpty) {
 | 
					    if (json is List && json.isNotEmpty) {
 | 
				
			||||||
      for (final row in json) {
 | 
					      for (final row in json) {
 | 
				
			||||||
        final value = DeleteUserDto.fromJson(row);
 | 
					        final value = UserAdminDeleteDto.fromJson(row);
 | 
				
			||||||
        if (value != null) {
 | 
					        if (value != null) {
 | 
				
			||||||
          result.add(value);
 | 
					          result.add(value);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -73,12 +73,12 @@ class DeleteUserDto {
 | 
				
			|||||||
    return result.toList(growable: growable);
 | 
					    return result.toList(growable: growable);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static Map<String, DeleteUserDto> mapFromJson(dynamic json) {
 | 
					  static Map<String, UserAdminDeleteDto> mapFromJson(dynamic json) {
 | 
				
			||||||
    final map = <String, DeleteUserDto>{};
 | 
					    final map = <String, UserAdminDeleteDto>{};
 | 
				
			||||||
    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 = DeleteUserDto.fromJson(entry.value);
 | 
					        final value = UserAdminDeleteDto.fromJson(entry.value);
 | 
				
			||||||
        if (value != null) {
 | 
					        if (value != null) {
 | 
				
			||||||
          map[entry.key] = value;
 | 
					          map[entry.key] = value;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -87,14 +87,14 @@ class DeleteUserDto {
 | 
				
			|||||||
    return map;
 | 
					    return map;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // maps a json object with a list of DeleteUserDto-objects as value to a dart map
 | 
					  // maps a json object with a list of UserAdminDeleteDto-objects as value to a dart map
 | 
				
			||||||
  static Map<String, List<DeleteUserDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
 | 
					  static Map<String, List<UserAdminDeleteDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
 | 
				
			||||||
    final map = <String, List<DeleteUserDto>>{};
 | 
					    final map = <String, List<UserAdminDeleteDto>>{};
 | 
				
			||||||
    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] = DeleteUserDto.listFromJson(entry.value, growable: growable,);
 | 
					        map[entry.key] = UserAdminDeleteDto.listFromJson(entry.value, growable: growable,);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return map;
 | 
					    return map;
 | 
				
			||||||
							
								
								
									
										243
									
								
								mobile/openapi/lib/model/user_admin_response_dto.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										243
									
								
								mobile/openapi/lib/model/user_admin_response_dto.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							@ -0,0 +1,243 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					// 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 UserAdminResponseDto {
 | 
				
			||||||
 | 
					  /// Returns a new [UserAdminResponseDto] instance.
 | 
				
			||||||
 | 
					  UserAdminResponseDto({
 | 
				
			||||||
 | 
					    required this.avatarColor,
 | 
				
			||||||
 | 
					    required this.createdAt,
 | 
				
			||||||
 | 
					    required this.deletedAt,
 | 
				
			||||||
 | 
					    required this.email,
 | 
				
			||||||
 | 
					    required this.id,
 | 
				
			||||||
 | 
					    required this.isAdmin,
 | 
				
			||||||
 | 
					    this.memoriesEnabled,
 | 
				
			||||||
 | 
					    required this.name,
 | 
				
			||||||
 | 
					    required this.oauthId,
 | 
				
			||||||
 | 
					    required this.profileImagePath,
 | 
				
			||||||
 | 
					    required this.quotaSizeInBytes,
 | 
				
			||||||
 | 
					    required this.quotaUsageInBytes,
 | 
				
			||||||
 | 
					    required this.shouldChangePassword,
 | 
				
			||||||
 | 
					    required this.status,
 | 
				
			||||||
 | 
					    required this.storageLabel,
 | 
				
			||||||
 | 
					    required this.updatedAt,
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  UserAvatarColor avatarColor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  DateTime createdAt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  DateTime? deletedAt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  String email;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  String id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool isAdmin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// Please note: This property should have been non-nullable! Since the specification file
 | 
				
			||||||
 | 
					  /// does not include a default value (using the "default:" property), however, the generated
 | 
				
			||||||
 | 
					  /// source code must fall back to having a nullable type.
 | 
				
			||||||
 | 
					  /// Consider adding a "default:" property in the specification file to hide this note.
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  bool? memoriesEnabled;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  String name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  String oauthId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  String profileImagePath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  int? quotaSizeInBytes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  int? quotaUsageInBytes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool shouldChangePassword;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  UserStatus status;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  String? storageLabel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  DateTime updatedAt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  bool operator ==(Object other) => identical(this, other) || other is UserAdminResponseDto &&
 | 
				
			||||||
 | 
					    other.avatarColor == avatarColor &&
 | 
				
			||||||
 | 
					    other.createdAt == createdAt &&
 | 
				
			||||||
 | 
					    other.deletedAt == deletedAt &&
 | 
				
			||||||
 | 
					    other.email == email &&
 | 
				
			||||||
 | 
					    other.id == id &&
 | 
				
			||||||
 | 
					    other.isAdmin == isAdmin &&
 | 
				
			||||||
 | 
					    other.memoriesEnabled == memoriesEnabled &&
 | 
				
			||||||
 | 
					    other.name == name &&
 | 
				
			||||||
 | 
					    other.oauthId == oauthId &&
 | 
				
			||||||
 | 
					    other.profileImagePath == profileImagePath &&
 | 
				
			||||||
 | 
					    other.quotaSizeInBytes == quotaSizeInBytes &&
 | 
				
			||||||
 | 
					    other.quotaUsageInBytes == quotaUsageInBytes &&
 | 
				
			||||||
 | 
					    other.shouldChangePassword == shouldChangePassword &&
 | 
				
			||||||
 | 
					    other.status == status &&
 | 
				
			||||||
 | 
					    other.storageLabel == storageLabel &&
 | 
				
			||||||
 | 
					    other.updatedAt == updatedAt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  int get hashCode =>
 | 
				
			||||||
 | 
					    // ignore: unnecessary_parenthesis
 | 
				
			||||||
 | 
					    (avatarColor.hashCode) +
 | 
				
			||||||
 | 
					    (createdAt.hashCode) +
 | 
				
			||||||
 | 
					    (deletedAt == null ? 0 : deletedAt!.hashCode) +
 | 
				
			||||||
 | 
					    (email.hashCode) +
 | 
				
			||||||
 | 
					    (id.hashCode) +
 | 
				
			||||||
 | 
					    (isAdmin.hashCode) +
 | 
				
			||||||
 | 
					    (memoriesEnabled == null ? 0 : memoriesEnabled!.hashCode) +
 | 
				
			||||||
 | 
					    (name.hashCode) +
 | 
				
			||||||
 | 
					    (oauthId.hashCode) +
 | 
				
			||||||
 | 
					    (profileImagePath.hashCode) +
 | 
				
			||||||
 | 
					    (quotaSizeInBytes == null ? 0 : quotaSizeInBytes!.hashCode) +
 | 
				
			||||||
 | 
					    (quotaUsageInBytes == null ? 0 : quotaUsageInBytes!.hashCode) +
 | 
				
			||||||
 | 
					    (shouldChangePassword.hashCode) +
 | 
				
			||||||
 | 
					    (status.hashCode) +
 | 
				
			||||||
 | 
					    (storageLabel == null ? 0 : storageLabel!.hashCode) +
 | 
				
			||||||
 | 
					    (updatedAt.hashCode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  String toString() => 'UserAdminResponseDto[avatarColor=$avatarColor, createdAt=$createdAt, deletedAt=$deletedAt, email=$email, id=$id, isAdmin=$isAdmin, memoriesEnabled=$memoriesEnabled, name=$name, oauthId=$oauthId, profileImagePath=$profileImagePath, quotaSizeInBytes=$quotaSizeInBytes, quotaUsageInBytes=$quotaUsageInBytes, shouldChangePassword=$shouldChangePassword, status=$status, storageLabel=$storageLabel, updatedAt=$updatedAt]';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Map<String, dynamic> toJson() {
 | 
				
			||||||
 | 
					    final json = <String, dynamic>{};
 | 
				
			||||||
 | 
					      json[r'avatarColor'] = this.avatarColor;
 | 
				
			||||||
 | 
					      json[r'createdAt'] = this.createdAt.toUtc().toIso8601String();
 | 
				
			||||||
 | 
					    if (this.deletedAt != null) {
 | 
				
			||||||
 | 
					      json[r'deletedAt'] = this.deletedAt!.toUtc().toIso8601String();
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					    //  json[r'deletedAt'] = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					      json[r'email'] = this.email;
 | 
				
			||||||
 | 
					      json[r'id'] = this.id;
 | 
				
			||||||
 | 
					      json[r'isAdmin'] = this.isAdmin;
 | 
				
			||||||
 | 
					    if (this.memoriesEnabled != null) {
 | 
				
			||||||
 | 
					      json[r'memoriesEnabled'] = this.memoriesEnabled;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					    //  json[r'memoriesEnabled'] = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					      json[r'name'] = this.name;
 | 
				
			||||||
 | 
					      json[r'oauthId'] = this.oauthId;
 | 
				
			||||||
 | 
					      json[r'profileImagePath'] = this.profileImagePath;
 | 
				
			||||||
 | 
					    if (this.quotaSizeInBytes != null) {
 | 
				
			||||||
 | 
					      json[r'quotaSizeInBytes'] = this.quotaSizeInBytes;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					    //  json[r'quotaSizeInBytes'] = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (this.quotaUsageInBytes != null) {
 | 
				
			||||||
 | 
					      json[r'quotaUsageInBytes'] = this.quotaUsageInBytes;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					    //  json[r'quotaUsageInBytes'] = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					      json[r'shouldChangePassword'] = this.shouldChangePassword;
 | 
				
			||||||
 | 
					      json[r'status'] = this.status;
 | 
				
			||||||
 | 
					    if (this.storageLabel != null) {
 | 
				
			||||||
 | 
					      json[r'storageLabel'] = this.storageLabel;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					    //  json[r'storageLabel'] = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					      json[r'updatedAt'] = this.updatedAt.toUtc().toIso8601String();
 | 
				
			||||||
 | 
					    return json;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Returns a new [UserAdminResponseDto] instance and imports its values from
 | 
				
			||||||
 | 
					  /// [value] if it's a [Map], null otherwise.
 | 
				
			||||||
 | 
					  // ignore: prefer_constructors_over_static_methods
 | 
				
			||||||
 | 
					  static UserAdminResponseDto? fromJson(dynamic value) {
 | 
				
			||||||
 | 
					    if (value is Map) {
 | 
				
			||||||
 | 
					      final json = value.cast<String, dynamic>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return UserAdminResponseDto(
 | 
				
			||||||
 | 
					        avatarColor: UserAvatarColor.fromJson(json[r'avatarColor'])!,
 | 
				
			||||||
 | 
					        createdAt: mapDateTime(json, r'createdAt', r'')!,
 | 
				
			||||||
 | 
					        deletedAt: mapDateTime(json, r'deletedAt', r''),
 | 
				
			||||||
 | 
					        email: mapValueOfType<String>(json, r'email')!,
 | 
				
			||||||
 | 
					        id: mapValueOfType<String>(json, r'id')!,
 | 
				
			||||||
 | 
					        isAdmin: mapValueOfType<bool>(json, r'isAdmin')!,
 | 
				
			||||||
 | 
					        memoriesEnabled: mapValueOfType<bool>(json, r'memoriesEnabled'),
 | 
				
			||||||
 | 
					        name: mapValueOfType<String>(json, r'name')!,
 | 
				
			||||||
 | 
					        oauthId: mapValueOfType<String>(json, r'oauthId')!,
 | 
				
			||||||
 | 
					        profileImagePath: mapValueOfType<String>(json, r'profileImagePath')!,
 | 
				
			||||||
 | 
					        quotaSizeInBytes: mapValueOfType<int>(json, r'quotaSizeInBytes'),
 | 
				
			||||||
 | 
					        quotaUsageInBytes: mapValueOfType<int>(json, r'quotaUsageInBytes'),
 | 
				
			||||||
 | 
					        shouldChangePassword: mapValueOfType<bool>(json, r'shouldChangePassword')!,
 | 
				
			||||||
 | 
					        status: UserStatus.fromJson(json[r'status'])!,
 | 
				
			||||||
 | 
					        storageLabel: mapValueOfType<String>(json, r'storageLabel'),
 | 
				
			||||||
 | 
					        updatedAt: mapDateTime(json, r'updatedAt', r'')!,
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static List<UserAdminResponseDto> listFromJson(dynamic json, {bool growable = false,}) {
 | 
				
			||||||
 | 
					    final result = <UserAdminResponseDto>[];
 | 
				
			||||||
 | 
					    if (json is List && json.isNotEmpty) {
 | 
				
			||||||
 | 
					      for (final row in json) {
 | 
				
			||||||
 | 
					        final value = UserAdminResponseDto.fromJson(row);
 | 
				
			||||||
 | 
					        if (value != null) {
 | 
				
			||||||
 | 
					          result.add(value);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return result.toList(growable: growable);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static Map<String, UserAdminResponseDto> mapFromJson(dynamic json) {
 | 
				
			||||||
 | 
					    final map = <String, UserAdminResponseDto>{};
 | 
				
			||||||
 | 
					    if (json is Map && json.isNotEmpty) {
 | 
				
			||||||
 | 
					      json = json.cast<String, dynamic>(); // ignore: parameter_assignments
 | 
				
			||||||
 | 
					      for (final entry in json.entries) {
 | 
				
			||||||
 | 
					        final value = UserAdminResponseDto.fromJson(entry.value);
 | 
				
			||||||
 | 
					        if (value != null) {
 | 
				
			||||||
 | 
					          map[entry.key] = value;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return map;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // maps a json object with a list of UserAdminResponseDto-objects as value to a dart map
 | 
				
			||||||
 | 
					  static Map<String, List<UserAdminResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
 | 
				
			||||||
 | 
					    final map = <String, List<UserAdminResponseDto>>{};
 | 
				
			||||||
 | 
					    if (json is Map && json.isNotEmpty) {
 | 
				
			||||||
 | 
					      // ignore: parameter_assignments
 | 
				
			||||||
 | 
					      json = json.cast<String, dynamic>();
 | 
				
			||||||
 | 
					      for (final entry in json.entries) {
 | 
				
			||||||
 | 
					        map[entry.key] = UserAdminResponseDto.listFromJson(entry.value, growable: growable,);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return map;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// The list of required keys that must be present in a JSON.
 | 
				
			||||||
 | 
					  static const requiredKeys = <String>{
 | 
				
			||||||
 | 
					    'avatarColor',
 | 
				
			||||||
 | 
					    'createdAt',
 | 
				
			||||||
 | 
					    'deletedAt',
 | 
				
			||||||
 | 
					    'email',
 | 
				
			||||||
 | 
					    'id',
 | 
				
			||||||
 | 
					    'isAdmin',
 | 
				
			||||||
 | 
					    'name',
 | 
				
			||||||
 | 
					    'oauthId',
 | 
				
			||||||
 | 
					    'profileImagePath',
 | 
				
			||||||
 | 
					    'quotaSizeInBytes',
 | 
				
			||||||
 | 
					    'quotaUsageInBytes',
 | 
				
			||||||
 | 
					    'shouldChangePassword',
 | 
				
			||||||
 | 
					    'status',
 | 
				
			||||||
 | 
					    'storageLabel',
 | 
				
			||||||
 | 
					    'updatedAt',
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -10,13 +10,11 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
part of openapi.api;
 | 
					part of openapi.api;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class UpdateUserDto {
 | 
					class UserAdminUpdateDto {
 | 
				
			||||||
  /// Returns a new [UpdateUserDto] instance.
 | 
					  /// Returns a new [UserAdminUpdateDto] instance.
 | 
				
			||||||
  UpdateUserDto({
 | 
					  UserAdminUpdateDto({
 | 
				
			||||||
    this.avatarColor,
 | 
					    this.avatarColor,
 | 
				
			||||||
    this.email,
 | 
					    this.email,
 | 
				
			||||||
    required this.id,
 | 
					 | 
				
			||||||
    this.isAdmin,
 | 
					 | 
				
			||||||
    this.memoriesEnabled,
 | 
					    this.memoriesEnabled,
 | 
				
			||||||
    this.name,
 | 
					    this.name,
 | 
				
			||||||
    this.password,
 | 
					    this.password,
 | 
				
			||||||
@ -41,16 +39,6 @@ class UpdateUserDto {
 | 
				
			|||||||
  ///
 | 
					  ///
 | 
				
			||||||
  String? email;
 | 
					  String? email;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  String id;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ///
 | 
					 | 
				
			||||||
  /// Please note: This property should have been non-nullable! Since the specification file
 | 
					 | 
				
			||||||
  /// does not include a default value (using the "default:" property), however, the generated
 | 
					 | 
				
			||||||
  /// source code must fall back to having a nullable type.
 | 
					 | 
				
			||||||
  /// Consider adding a "default:" property in the specification file to hide this note.
 | 
					 | 
				
			||||||
  ///
 | 
					 | 
				
			||||||
  bool? isAdmin;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ///
 | 
					  ///
 | 
				
			||||||
  /// Please note: This property should have been non-nullable! Since the specification file
 | 
					  /// Please note: This property should have been non-nullable! Since the specification file
 | 
				
			||||||
  /// does not include a default value (using the "default:" property), however, the generated
 | 
					  /// does not include a default value (using the "default:" property), however, the generated
 | 
				
			||||||
@ -86,20 +74,12 @@ class UpdateUserDto {
 | 
				
			|||||||
  ///
 | 
					  ///
 | 
				
			||||||
  bool? shouldChangePassword;
 | 
					  bool? shouldChangePassword;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ///
 | 
					 | 
				
			||||||
  /// Please note: This property should have been non-nullable! Since the specification file
 | 
					 | 
				
			||||||
  /// does not include a default value (using the "default:" property), however, the generated
 | 
					 | 
				
			||||||
  /// source code must fall back to having a nullable type.
 | 
					 | 
				
			||||||
  /// Consider adding a "default:" property in the specification file to hide this note.
 | 
					 | 
				
			||||||
  ///
 | 
					 | 
				
			||||||
  String? storageLabel;
 | 
					  String? storageLabel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  bool operator ==(Object other) => identical(this, other) || other is UpdateUserDto &&
 | 
					  bool operator ==(Object other) => identical(this, other) || other is UserAdminUpdateDto &&
 | 
				
			||||||
    other.avatarColor == avatarColor &&
 | 
					    other.avatarColor == avatarColor &&
 | 
				
			||||||
    other.email == email &&
 | 
					    other.email == email &&
 | 
				
			||||||
    other.id == id &&
 | 
					 | 
				
			||||||
    other.isAdmin == isAdmin &&
 | 
					 | 
				
			||||||
    other.memoriesEnabled == memoriesEnabled &&
 | 
					    other.memoriesEnabled == memoriesEnabled &&
 | 
				
			||||||
    other.name == name &&
 | 
					    other.name == name &&
 | 
				
			||||||
    other.password == password &&
 | 
					    other.password == password &&
 | 
				
			||||||
@ -112,8 +92,6 @@ class UpdateUserDto {
 | 
				
			|||||||
    // ignore: unnecessary_parenthesis
 | 
					    // ignore: unnecessary_parenthesis
 | 
				
			||||||
    (avatarColor == null ? 0 : avatarColor!.hashCode) +
 | 
					    (avatarColor == null ? 0 : avatarColor!.hashCode) +
 | 
				
			||||||
    (email == null ? 0 : email!.hashCode) +
 | 
					    (email == null ? 0 : email!.hashCode) +
 | 
				
			||||||
    (id.hashCode) +
 | 
					 | 
				
			||||||
    (isAdmin == null ? 0 : isAdmin!.hashCode) +
 | 
					 | 
				
			||||||
    (memoriesEnabled == null ? 0 : memoriesEnabled!.hashCode) +
 | 
					    (memoriesEnabled == null ? 0 : memoriesEnabled!.hashCode) +
 | 
				
			||||||
    (name == null ? 0 : name!.hashCode) +
 | 
					    (name == null ? 0 : name!.hashCode) +
 | 
				
			||||||
    (password == null ? 0 : password!.hashCode) +
 | 
					    (password == null ? 0 : password!.hashCode) +
 | 
				
			||||||
@ -122,7 +100,7 @@ class UpdateUserDto {
 | 
				
			|||||||
    (storageLabel == null ? 0 : storageLabel!.hashCode);
 | 
					    (storageLabel == null ? 0 : storageLabel!.hashCode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  String toString() => 'UpdateUserDto[avatarColor=$avatarColor, email=$email, id=$id, isAdmin=$isAdmin, memoriesEnabled=$memoriesEnabled, name=$name, password=$password, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]';
 | 
					  String toString() => 'UserAdminUpdateDto[avatarColor=$avatarColor, email=$email, memoriesEnabled=$memoriesEnabled, name=$name, password=$password, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Map<String, dynamic> toJson() {
 | 
					  Map<String, dynamic> toJson() {
 | 
				
			||||||
    final json = <String, dynamic>{};
 | 
					    final json = <String, dynamic>{};
 | 
				
			||||||
@ -135,12 +113,6 @@ class UpdateUserDto {
 | 
				
			|||||||
      json[r'email'] = this.email;
 | 
					      json[r'email'] = this.email;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
    //  json[r'email'] = null;
 | 
					    //  json[r'email'] = null;
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
      json[r'id'] = this.id;
 | 
					 | 
				
			||||||
    if (this.isAdmin != null) {
 | 
					 | 
				
			||||||
      json[r'isAdmin'] = this.isAdmin;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
    //  json[r'isAdmin'] = null;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (this.memoriesEnabled != null) {
 | 
					    if (this.memoriesEnabled != null) {
 | 
				
			||||||
      json[r'memoriesEnabled'] = this.memoriesEnabled;
 | 
					      json[r'memoriesEnabled'] = this.memoriesEnabled;
 | 
				
			||||||
@ -175,18 +147,16 @@ class UpdateUserDto {
 | 
				
			|||||||
    return json;
 | 
					    return json;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /// Returns a new [UpdateUserDto] instance and imports its values from
 | 
					  /// Returns a new [UserAdminUpdateDto] 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 UpdateUserDto? fromJson(dynamic value) {
 | 
					  static UserAdminUpdateDto? fromJson(dynamic value) {
 | 
				
			||||||
    if (value is Map) {
 | 
					    if (value is Map) {
 | 
				
			||||||
      final json = value.cast<String, dynamic>();
 | 
					      final json = value.cast<String, dynamic>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return UpdateUserDto(
 | 
					      return UserAdminUpdateDto(
 | 
				
			||||||
        avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']),
 | 
					        avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']),
 | 
				
			||||||
        email: mapValueOfType<String>(json, r'email'),
 | 
					        email: mapValueOfType<String>(json, r'email'),
 | 
				
			||||||
        id: mapValueOfType<String>(json, r'id')!,
 | 
					 | 
				
			||||||
        isAdmin: mapValueOfType<bool>(json, r'isAdmin'),
 | 
					 | 
				
			||||||
        memoriesEnabled: mapValueOfType<bool>(json, r'memoriesEnabled'),
 | 
					        memoriesEnabled: mapValueOfType<bool>(json, r'memoriesEnabled'),
 | 
				
			||||||
        name: mapValueOfType<String>(json, r'name'),
 | 
					        name: mapValueOfType<String>(json, r'name'),
 | 
				
			||||||
        password: mapValueOfType<String>(json, r'password'),
 | 
					        password: mapValueOfType<String>(json, r'password'),
 | 
				
			||||||
@ -198,11 +168,11 @@ class UpdateUserDto {
 | 
				
			|||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static List<UpdateUserDto> listFromJson(dynamic json, {bool growable = false,}) {
 | 
					  static List<UserAdminUpdateDto> listFromJson(dynamic json, {bool growable = false,}) {
 | 
				
			||||||
    final result = <UpdateUserDto>[];
 | 
					    final result = <UserAdminUpdateDto>[];
 | 
				
			||||||
    if (json is List && json.isNotEmpty) {
 | 
					    if (json is List && json.isNotEmpty) {
 | 
				
			||||||
      for (final row in json) {
 | 
					      for (final row in json) {
 | 
				
			||||||
        final value = UpdateUserDto.fromJson(row);
 | 
					        final value = UserAdminUpdateDto.fromJson(row);
 | 
				
			||||||
        if (value != null) {
 | 
					        if (value != null) {
 | 
				
			||||||
          result.add(value);
 | 
					          result.add(value);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -211,12 +181,12 @@ class UpdateUserDto {
 | 
				
			|||||||
    return result.toList(growable: growable);
 | 
					    return result.toList(growable: growable);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static Map<String, UpdateUserDto> mapFromJson(dynamic json) {
 | 
					  static Map<String, UserAdminUpdateDto> mapFromJson(dynamic json) {
 | 
				
			||||||
    final map = <String, UpdateUserDto>{};
 | 
					    final map = <String, UserAdminUpdateDto>{};
 | 
				
			||||||
    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 = UpdateUserDto.fromJson(entry.value);
 | 
					        final value = UserAdminUpdateDto.fromJson(entry.value);
 | 
				
			||||||
        if (value != null) {
 | 
					        if (value != null) {
 | 
				
			||||||
          map[entry.key] = value;
 | 
					          map[entry.key] = value;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -225,14 +195,14 @@ class UpdateUserDto {
 | 
				
			|||||||
    return map;
 | 
					    return map;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // maps a json object with a list of UpdateUserDto-objects as value to a dart map
 | 
					  // maps a json object with a list of UserAdminUpdateDto-objects as value to a dart map
 | 
				
			||||||
  static Map<String, List<UpdateUserDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
 | 
					  static Map<String, List<UserAdminUpdateDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
 | 
				
			||||||
    final map = <String, List<UpdateUserDto>>{};
 | 
					    final map = <String, List<UserAdminUpdateDto>>{};
 | 
				
			||||||
    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] = UpdateUserDto.listFromJson(entry.value, growable: growable,);
 | 
					        map[entry.key] = UserAdminUpdateDto.listFromJson(entry.value, growable: growable,);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return map;
 | 
					    return map;
 | 
				
			||||||
@ -240,7 +210,6 @@ class UpdateUserDto {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  /// 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>{
 | 
				
			||||||
    'id',
 | 
					 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										130
									
								
								mobile/openapi/lib/model/user_dto.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										130
									
								
								mobile/openapi/lib/model/user_dto.dart
									
									
									
										generated
									
									
									
								
							@ -1,130 +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 UserDto {
 | 
					 | 
				
			||||||
  /// Returns a new [UserDto] instance.
 | 
					 | 
				
			||||||
  UserDto({
 | 
					 | 
				
			||||||
    required this.avatarColor,
 | 
					 | 
				
			||||||
    required this.email,
 | 
					 | 
				
			||||||
    required this.id,
 | 
					 | 
				
			||||||
    required this.name,
 | 
					 | 
				
			||||||
    required this.profileImagePath,
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  UserAvatarColor avatarColor;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  String email;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  String id;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  String name;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  String profileImagePath;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @override
 | 
					 | 
				
			||||||
  bool operator ==(Object other) => identical(this, other) || other is UserDto &&
 | 
					 | 
				
			||||||
    other.avatarColor == avatarColor &&
 | 
					 | 
				
			||||||
    other.email == email &&
 | 
					 | 
				
			||||||
    other.id == id &&
 | 
					 | 
				
			||||||
    other.name == name &&
 | 
					 | 
				
			||||||
    other.profileImagePath == profileImagePath;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @override
 | 
					 | 
				
			||||||
  int get hashCode =>
 | 
					 | 
				
			||||||
    // ignore: unnecessary_parenthesis
 | 
					 | 
				
			||||||
    (avatarColor.hashCode) +
 | 
					 | 
				
			||||||
    (email.hashCode) +
 | 
					 | 
				
			||||||
    (id.hashCode) +
 | 
					 | 
				
			||||||
    (name.hashCode) +
 | 
					 | 
				
			||||||
    (profileImagePath.hashCode);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @override
 | 
					 | 
				
			||||||
  String toString() => 'UserDto[avatarColor=$avatarColor, email=$email, id=$id, name=$name, profileImagePath=$profileImagePath]';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  Map<String, dynamic> toJson() {
 | 
					 | 
				
			||||||
    final json = <String, dynamic>{};
 | 
					 | 
				
			||||||
      json[r'avatarColor'] = this.avatarColor;
 | 
					 | 
				
			||||||
      json[r'email'] = this.email;
 | 
					 | 
				
			||||||
      json[r'id'] = this.id;
 | 
					 | 
				
			||||||
      json[r'name'] = this.name;
 | 
					 | 
				
			||||||
      json[r'profileImagePath'] = this.profileImagePath;
 | 
					 | 
				
			||||||
    return json;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /// Returns a new [UserDto] instance and imports its values from
 | 
					 | 
				
			||||||
  /// [value] if it's a [Map], null otherwise.
 | 
					 | 
				
			||||||
  // ignore: prefer_constructors_over_static_methods
 | 
					 | 
				
			||||||
  static UserDto? fromJson(dynamic value) {
 | 
					 | 
				
			||||||
    if (value is Map) {
 | 
					 | 
				
			||||||
      final json = value.cast<String, dynamic>();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return UserDto(
 | 
					 | 
				
			||||||
        avatarColor: UserAvatarColor.fromJson(json[r'avatarColor'])!,
 | 
					 | 
				
			||||||
        email: mapValueOfType<String>(json, r'email')!,
 | 
					 | 
				
			||||||
        id: mapValueOfType<String>(json, r'id')!,
 | 
					 | 
				
			||||||
        name: mapValueOfType<String>(json, r'name')!,
 | 
					 | 
				
			||||||
        profileImagePath: mapValueOfType<String>(json, r'profileImagePath')!,
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return null;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  static List<UserDto> listFromJson(dynamic json, {bool growable = false,}) {
 | 
					 | 
				
			||||||
    final result = <UserDto>[];
 | 
					 | 
				
			||||||
    if (json is List && json.isNotEmpty) {
 | 
					 | 
				
			||||||
      for (final row in json) {
 | 
					 | 
				
			||||||
        final value = UserDto.fromJson(row);
 | 
					 | 
				
			||||||
        if (value != null) {
 | 
					 | 
				
			||||||
          result.add(value);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return result.toList(growable: growable);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  static Map<String, UserDto> mapFromJson(dynamic json) {
 | 
					 | 
				
			||||||
    final map = <String, UserDto>{};
 | 
					 | 
				
			||||||
    if (json is Map && json.isNotEmpty) {
 | 
					 | 
				
			||||||
      json = json.cast<String, dynamic>(); // ignore: parameter_assignments
 | 
					 | 
				
			||||||
      for (final entry in json.entries) {
 | 
					 | 
				
			||||||
        final value = UserDto.fromJson(entry.value);
 | 
					 | 
				
			||||||
        if (value != null) {
 | 
					 | 
				
			||||||
          map[entry.key] = value;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return map;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // maps a json object with a list of UserDto-objects as value to a dart map
 | 
					 | 
				
			||||||
  static Map<String, List<UserDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
 | 
					 | 
				
			||||||
    final map = <String, List<UserDto>>{};
 | 
					 | 
				
			||||||
    if (json is Map && json.isNotEmpty) {
 | 
					 | 
				
			||||||
      // ignore: parameter_assignments
 | 
					 | 
				
			||||||
      json = json.cast<String, dynamic>();
 | 
					 | 
				
			||||||
      for (final entry in json.entries) {
 | 
					 | 
				
			||||||
        map[entry.key] = UserDto.listFromJson(entry.value, growable: growable,);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return map;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /// The list of required keys that must be present in a JSON.
 | 
					 | 
				
			||||||
  static const requiredKeys = <String>{
 | 
					 | 
				
			||||||
    'avatarColor',
 | 
					 | 
				
			||||||
    'email',
 | 
					 | 
				
			||||||
    'id',
 | 
					 | 
				
			||||||
    'name',
 | 
					 | 
				
			||||||
    'profileImagePath',
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							
								
								
									
										119
									
								
								mobile/openapi/lib/model/user_response_dto.dart
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										119
									
								
								mobile/openapi/lib/model/user_response_dto.dart
									
									
									
										generated
									
									
									
								
							@ -14,141 +14,49 @@ class UserResponseDto {
 | 
				
			|||||||
  /// Returns a new [UserResponseDto] instance.
 | 
					  /// Returns a new [UserResponseDto] instance.
 | 
				
			||||||
  UserResponseDto({
 | 
					  UserResponseDto({
 | 
				
			||||||
    required this.avatarColor,
 | 
					    required this.avatarColor,
 | 
				
			||||||
    required this.createdAt,
 | 
					 | 
				
			||||||
    required this.deletedAt,
 | 
					 | 
				
			||||||
    required this.email,
 | 
					    required this.email,
 | 
				
			||||||
    required this.id,
 | 
					    required this.id,
 | 
				
			||||||
    required this.isAdmin,
 | 
					 | 
				
			||||||
    this.memoriesEnabled,
 | 
					 | 
				
			||||||
    required this.name,
 | 
					    required this.name,
 | 
				
			||||||
    required this.oauthId,
 | 
					 | 
				
			||||||
    required this.profileImagePath,
 | 
					    required this.profileImagePath,
 | 
				
			||||||
    required this.quotaSizeInBytes,
 | 
					 | 
				
			||||||
    required this.quotaUsageInBytes,
 | 
					 | 
				
			||||||
    required this.shouldChangePassword,
 | 
					 | 
				
			||||||
    required this.status,
 | 
					 | 
				
			||||||
    required this.storageLabel,
 | 
					 | 
				
			||||||
    required this.updatedAt,
 | 
					 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  UserAvatarColor avatarColor;
 | 
					  UserAvatarColor avatarColor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  DateTime createdAt;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  DateTime? deletedAt;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  String email;
 | 
					  String email;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  String id;
 | 
					  String id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool isAdmin;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ///
 | 
					 | 
				
			||||||
  /// Please note: This property should have been non-nullable! Since the specification file
 | 
					 | 
				
			||||||
  /// does not include a default value (using the "default:" property), however, the generated
 | 
					 | 
				
			||||||
  /// source code must fall back to having a nullable type.
 | 
					 | 
				
			||||||
  /// Consider adding a "default:" property in the specification file to hide this note.
 | 
					 | 
				
			||||||
  ///
 | 
					 | 
				
			||||||
  bool? memoriesEnabled;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  String name;
 | 
					  String name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  String oauthId;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  String profileImagePath;
 | 
					  String profileImagePath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  int? quotaSizeInBytes;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  int? quotaUsageInBytes;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  bool shouldChangePassword;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  UserStatus status;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  String? storageLabel;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  DateTime updatedAt;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  bool operator ==(Object other) => identical(this, other) || other is UserResponseDto &&
 | 
					  bool operator ==(Object other) => identical(this, other) || other is UserResponseDto &&
 | 
				
			||||||
    other.avatarColor == avatarColor &&
 | 
					    other.avatarColor == avatarColor &&
 | 
				
			||||||
    other.createdAt == createdAt &&
 | 
					 | 
				
			||||||
    other.deletedAt == deletedAt &&
 | 
					 | 
				
			||||||
    other.email == email &&
 | 
					    other.email == email &&
 | 
				
			||||||
    other.id == id &&
 | 
					    other.id == id &&
 | 
				
			||||||
    other.isAdmin == isAdmin &&
 | 
					 | 
				
			||||||
    other.memoriesEnabled == memoriesEnabled &&
 | 
					 | 
				
			||||||
    other.name == name &&
 | 
					    other.name == name &&
 | 
				
			||||||
    other.oauthId == oauthId &&
 | 
					    other.profileImagePath == profileImagePath;
 | 
				
			||||||
    other.profileImagePath == profileImagePath &&
 | 
					 | 
				
			||||||
    other.quotaSizeInBytes == quotaSizeInBytes &&
 | 
					 | 
				
			||||||
    other.quotaUsageInBytes == quotaUsageInBytes &&
 | 
					 | 
				
			||||||
    other.shouldChangePassword == shouldChangePassword &&
 | 
					 | 
				
			||||||
    other.status == status &&
 | 
					 | 
				
			||||||
    other.storageLabel == storageLabel &&
 | 
					 | 
				
			||||||
    other.updatedAt == updatedAt;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  int get hashCode =>
 | 
					  int get hashCode =>
 | 
				
			||||||
    // ignore: unnecessary_parenthesis
 | 
					    // ignore: unnecessary_parenthesis
 | 
				
			||||||
    (avatarColor.hashCode) +
 | 
					    (avatarColor.hashCode) +
 | 
				
			||||||
    (createdAt.hashCode) +
 | 
					 | 
				
			||||||
    (deletedAt == null ? 0 : deletedAt!.hashCode) +
 | 
					 | 
				
			||||||
    (email.hashCode) +
 | 
					    (email.hashCode) +
 | 
				
			||||||
    (id.hashCode) +
 | 
					    (id.hashCode) +
 | 
				
			||||||
    (isAdmin.hashCode) +
 | 
					 | 
				
			||||||
    (memoriesEnabled == null ? 0 : memoriesEnabled!.hashCode) +
 | 
					 | 
				
			||||||
    (name.hashCode) +
 | 
					    (name.hashCode) +
 | 
				
			||||||
    (oauthId.hashCode) +
 | 
					    (profileImagePath.hashCode);
 | 
				
			||||||
    (profileImagePath.hashCode) +
 | 
					 | 
				
			||||||
    (quotaSizeInBytes == null ? 0 : quotaSizeInBytes!.hashCode) +
 | 
					 | 
				
			||||||
    (quotaUsageInBytes == null ? 0 : quotaUsageInBytes!.hashCode) +
 | 
					 | 
				
			||||||
    (shouldChangePassword.hashCode) +
 | 
					 | 
				
			||||||
    (status.hashCode) +
 | 
					 | 
				
			||||||
    (storageLabel == null ? 0 : storageLabel!.hashCode) +
 | 
					 | 
				
			||||||
    (updatedAt.hashCode);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  String toString() => 'UserResponseDto[avatarColor=$avatarColor, createdAt=$createdAt, deletedAt=$deletedAt, email=$email, id=$id, isAdmin=$isAdmin, memoriesEnabled=$memoriesEnabled, name=$name, oauthId=$oauthId, profileImagePath=$profileImagePath, quotaSizeInBytes=$quotaSizeInBytes, quotaUsageInBytes=$quotaUsageInBytes, shouldChangePassword=$shouldChangePassword, status=$status, storageLabel=$storageLabel, updatedAt=$updatedAt]';
 | 
					  String toString() => 'UserResponseDto[avatarColor=$avatarColor, email=$email, id=$id, name=$name, profileImagePath=$profileImagePath]';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Map<String, dynamic> toJson() {
 | 
					  Map<String, dynamic> toJson() {
 | 
				
			||||||
    final json = <String, dynamic>{};
 | 
					    final json = <String, dynamic>{};
 | 
				
			||||||
      json[r'avatarColor'] = this.avatarColor;
 | 
					      json[r'avatarColor'] = this.avatarColor;
 | 
				
			||||||
      json[r'createdAt'] = this.createdAt.toUtc().toIso8601String();
 | 
					 | 
				
			||||||
    if (this.deletedAt != null) {
 | 
					 | 
				
			||||||
      json[r'deletedAt'] = this.deletedAt!.toUtc().toIso8601String();
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
    //  json[r'deletedAt'] = null;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
      json[r'email'] = this.email;
 | 
					      json[r'email'] = this.email;
 | 
				
			||||||
      json[r'id'] = this.id;
 | 
					      json[r'id'] = this.id;
 | 
				
			||||||
      json[r'isAdmin'] = this.isAdmin;
 | 
					 | 
				
			||||||
    if (this.memoriesEnabled != null) {
 | 
					 | 
				
			||||||
      json[r'memoriesEnabled'] = this.memoriesEnabled;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
    //  json[r'memoriesEnabled'] = null;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
      json[r'name'] = this.name;
 | 
					      json[r'name'] = this.name;
 | 
				
			||||||
      json[r'oauthId'] = this.oauthId;
 | 
					 | 
				
			||||||
      json[r'profileImagePath'] = this.profileImagePath;
 | 
					      json[r'profileImagePath'] = this.profileImagePath;
 | 
				
			||||||
    if (this.quotaSizeInBytes != null) {
 | 
					 | 
				
			||||||
      json[r'quotaSizeInBytes'] = this.quotaSizeInBytes;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
    //  json[r'quotaSizeInBytes'] = null;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (this.quotaUsageInBytes != null) {
 | 
					 | 
				
			||||||
      json[r'quotaUsageInBytes'] = this.quotaUsageInBytes;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
    //  json[r'quotaUsageInBytes'] = null;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
      json[r'shouldChangePassword'] = this.shouldChangePassword;
 | 
					 | 
				
			||||||
      json[r'status'] = this.status;
 | 
					 | 
				
			||||||
    if (this.storageLabel != null) {
 | 
					 | 
				
			||||||
      json[r'storageLabel'] = this.storageLabel;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
    //  json[r'storageLabel'] = null;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
      json[r'updatedAt'] = this.updatedAt.toUtc().toIso8601String();
 | 
					 | 
				
			||||||
    return json;
 | 
					    return json;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -161,21 +69,10 @@ class UserResponseDto {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      return UserResponseDto(
 | 
					      return UserResponseDto(
 | 
				
			||||||
        avatarColor: UserAvatarColor.fromJson(json[r'avatarColor'])!,
 | 
					        avatarColor: UserAvatarColor.fromJson(json[r'avatarColor'])!,
 | 
				
			||||||
        createdAt: mapDateTime(json, r'createdAt', r'')!,
 | 
					 | 
				
			||||||
        deletedAt: mapDateTime(json, r'deletedAt', r''),
 | 
					 | 
				
			||||||
        email: mapValueOfType<String>(json, r'email')!,
 | 
					        email: mapValueOfType<String>(json, r'email')!,
 | 
				
			||||||
        id: mapValueOfType<String>(json, r'id')!,
 | 
					        id: mapValueOfType<String>(json, r'id')!,
 | 
				
			||||||
        isAdmin: mapValueOfType<bool>(json, r'isAdmin')!,
 | 
					 | 
				
			||||||
        memoriesEnabled: mapValueOfType<bool>(json, r'memoriesEnabled'),
 | 
					 | 
				
			||||||
        name: mapValueOfType<String>(json, r'name')!,
 | 
					        name: mapValueOfType<String>(json, r'name')!,
 | 
				
			||||||
        oauthId: mapValueOfType<String>(json, r'oauthId')!,
 | 
					 | 
				
			||||||
        profileImagePath: mapValueOfType<String>(json, r'profileImagePath')!,
 | 
					        profileImagePath: mapValueOfType<String>(json, r'profileImagePath')!,
 | 
				
			||||||
        quotaSizeInBytes: mapValueOfType<int>(json, r'quotaSizeInBytes'),
 | 
					 | 
				
			||||||
        quotaUsageInBytes: mapValueOfType<int>(json, r'quotaUsageInBytes'),
 | 
					 | 
				
			||||||
        shouldChangePassword: mapValueOfType<bool>(json, r'shouldChangePassword')!,
 | 
					 | 
				
			||||||
        status: UserStatus.fromJson(json[r'status'])!,
 | 
					 | 
				
			||||||
        storageLabel: mapValueOfType<String>(json, r'storageLabel'),
 | 
					 | 
				
			||||||
        updatedAt: mapDateTime(json, r'updatedAt', r'')!,
 | 
					 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
@ -224,20 +121,10 @@ class UserResponseDto {
 | 
				
			|||||||
  /// 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>{
 | 
				
			||||||
    'avatarColor',
 | 
					    'avatarColor',
 | 
				
			||||||
    'createdAt',
 | 
					 | 
				
			||||||
    'deletedAt',
 | 
					 | 
				
			||||||
    'email',
 | 
					    'email',
 | 
				
			||||||
    'id',
 | 
					    'id',
 | 
				
			||||||
    'isAdmin',
 | 
					 | 
				
			||||||
    'name',
 | 
					    'name',
 | 
				
			||||||
    'oauthId',
 | 
					 | 
				
			||||||
    'profileImagePath',
 | 
					    'profileImagePath',
 | 
				
			||||||
    'quotaSizeInBytes',
 | 
					 | 
				
			||||||
    'quotaUsageInBytes',
 | 
					 | 
				
			||||||
    'shouldChangePassword',
 | 
					 | 
				
			||||||
    'status',
 | 
					 | 
				
			||||||
    'storageLabel',
 | 
					 | 
				
			||||||
    'updatedAt',
 | 
					 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										175
									
								
								mobile/openapi/lib/model/user_update_me_dto.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								mobile/openapi/lib/model/user_update_me_dto.dart
									
									
									
										generated
									
									
									
										Normal file
									
								
							@ -0,0 +1,175 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					// 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 UserUpdateMeDto {
 | 
				
			||||||
 | 
					  /// Returns a new [UserUpdateMeDto] instance.
 | 
				
			||||||
 | 
					  UserUpdateMeDto({
 | 
				
			||||||
 | 
					    this.avatarColor,
 | 
				
			||||||
 | 
					    this.email,
 | 
				
			||||||
 | 
					    this.memoriesEnabled,
 | 
				
			||||||
 | 
					    this.name,
 | 
				
			||||||
 | 
					    this.password,
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// Please note: This property should have been non-nullable! Since the specification file
 | 
				
			||||||
 | 
					  /// does not include a default value (using the "default:" property), however, the generated
 | 
				
			||||||
 | 
					  /// source code must fall back to having a nullable type.
 | 
				
			||||||
 | 
					  /// Consider adding a "default:" property in the specification file to hide this note.
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  UserAvatarColor? avatarColor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// Please note: This property should have been non-nullable! Since the specification file
 | 
				
			||||||
 | 
					  /// does not include a default value (using the "default:" property), however, the generated
 | 
				
			||||||
 | 
					  /// source code must fall back to having a nullable type.
 | 
				
			||||||
 | 
					  /// Consider adding a "default:" property in the specification file to hide this note.
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  String? email;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// Please note: This property should have been non-nullable! Since the specification file
 | 
				
			||||||
 | 
					  /// does not include a default value (using the "default:" property), however, the generated
 | 
				
			||||||
 | 
					  /// source code must fall back to having a nullable type.
 | 
				
			||||||
 | 
					  /// Consider adding a "default:" property in the specification file to hide this note.
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  bool? memoriesEnabled;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// Please note: This property should have been non-nullable! Since the specification file
 | 
				
			||||||
 | 
					  /// does not include a default value (using the "default:" property), however, the generated
 | 
				
			||||||
 | 
					  /// source code must fall back to having a nullable type.
 | 
				
			||||||
 | 
					  /// Consider adding a "default:" property in the specification file to hide this note.
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  String? name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// Please note: This property should have been non-nullable! Since the specification file
 | 
				
			||||||
 | 
					  /// does not include a default value (using the "default:" property), however, the generated
 | 
				
			||||||
 | 
					  /// source code must fall back to having a nullable type.
 | 
				
			||||||
 | 
					  /// Consider adding a "default:" property in the specification file to hide this note.
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  String? password;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  bool operator ==(Object other) => identical(this, other) || other is UserUpdateMeDto &&
 | 
				
			||||||
 | 
					    other.avatarColor == avatarColor &&
 | 
				
			||||||
 | 
					    other.email == email &&
 | 
				
			||||||
 | 
					    other.memoriesEnabled == memoriesEnabled &&
 | 
				
			||||||
 | 
					    other.name == name &&
 | 
				
			||||||
 | 
					    other.password == password;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  int get hashCode =>
 | 
				
			||||||
 | 
					    // ignore: unnecessary_parenthesis
 | 
				
			||||||
 | 
					    (avatarColor == null ? 0 : avatarColor!.hashCode) +
 | 
				
			||||||
 | 
					    (email == null ? 0 : email!.hashCode) +
 | 
				
			||||||
 | 
					    (memoriesEnabled == null ? 0 : memoriesEnabled!.hashCode) +
 | 
				
			||||||
 | 
					    (name == null ? 0 : name!.hashCode) +
 | 
				
			||||||
 | 
					    (password == null ? 0 : password!.hashCode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  String toString() => 'UserUpdateMeDto[avatarColor=$avatarColor, email=$email, memoriesEnabled=$memoriesEnabled, name=$name, password=$password]';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Map<String, dynamic> toJson() {
 | 
				
			||||||
 | 
					    final json = <String, dynamic>{};
 | 
				
			||||||
 | 
					    if (this.avatarColor != null) {
 | 
				
			||||||
 | 
					      json[r'avatarColor'] = this.avatarColor;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					    //  json[r'avatarColor'] = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (this.email != null) {
 | 
				
			||||||
 | 
					      json[r'email'] = this.email;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					    //  json[r'email'] = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (this.memoriesEnabled != null) {
 | 
				
			||||||
 | 
					      json[r'memoriesEnabled'] = this.memoriesEnabled;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					    //  json[r'memoriesEnabled'] = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (this.name != null) {
 | 
				
			||||||
 | 
					      json[r'name'] = this.name;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					    //  json[r'name'] = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (this.password != null) {
 | 
				
			||||||
 | 
					      json[r'password'] = this.password;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					    //  json[r'password'] = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return json;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Returns a new [UserUpdateMeDto] instance and imports its values from
 | 
				
			||||||
 | 
					  /// [value] if it's a [Map], null otherwise.
 | 
				
			||||||
 | 
					  // ignore: prefer_constructors_over_static_methods
 | 
				
			||||||
 | 
					  static UserUpdateMeDto? fromJson(dynamic value) {
 | 
				
			||||||
 | 
					    if (value is Map) {
 | 
				
			||||||
 | 
					      final json = value.cast<String, dynamic>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return UserUpdateMeDto(
 | 
				
			||||||
 | 
					        avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']),
 | 
				
			||||||
 | 
					        email: mapValueOfType<String>(json, r'email'),
 | 
				
			||||||
 | 
					        memoriesEnabled: mapValueOfType<bool>(json, r'memoriesEnabled'),
 | 
				
			||||||
 | 
					        name: mapValueOfType<String>(json, r'name'),
 | 
				
			||||||
 | 
					        password: mapValueOfType<String>(json, r'password'),
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static List<UserUpdateMeDto> listFromJson(dynamic json, {bool growable = false,}) {
 | 
				
			||||||
 | 
					    final result = <UserUpdateMeDto>[];
 | 
				
			||||||
 | 
					    if (json is List && json.isNotEmpty) {
 | 
				
			||||||
 | 
					      for (final row in json) {
 | 
				
			||||||
 | 
					        final value = UserUpdateMeDto.fromJson(row);
 | 
				
			||||||
 | 
					        if (value != null) {
 | 
				
			||||||
 | 
					          result.add(value);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return result.toList(growable: growable);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static Map<String, UserUpdateMeDto> mapFromJson(dynamic json) {
 | 
				
			||||||
 | 
					    final map = <String, UserUpdateMeDto>{};
 | 
				
			||||||
 | 
					    if (json is Map && json.isNotEmpty) {
 | 
				
			||||||
 | 
					      json = json.cast<String, dynamic>(); // ignore: parameter_assignments
 | 
				
			||||||
 | 
					      for (final entry in json.entries) {
 | 
				
			||||||
 | 
					        final value = UserUpdateMeDto.fromJson(entry.value);
 | 
				
			||||||
 | 
					        if (value != null) {
 | 
				
			||||||
 | 
					          map[entry.key] = value;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return map;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // maps a json object with a list of UserUpdateMeDto-objects as value to a dart map
 | 
				
			||||||
 | 
					  static Map<String, List<UserUpdateMeDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
 | 
				
			||||||
 | 
					    final map = <String, List<UserUpdateMeDto>>{};
 | 
				
			||||||
 | 
					    if (json is Map && json.isNotEmpty) {
 | 
				
			||||||
 | 
					      // ignore: parameter_assignments
 | 
				
			||||||
 | 
					      json = json.cast<String, dynamic>();
 | 
				
			||||||
 | 
					      for (final entry in json.entries) {
 | 
				
			||||||
 | 
					        map[entry.key] = UserUpdateMeDto.listFromJson(entry.value, growable: growable,);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return map;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// The list of required keys that must be present in a JSON.
 | 
				
			||||||
 | 
					  static const requiredKeys = <String>{
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -206,6 +206,274 @@
 | 
				
			|||||||
        ]
 | 
					        ]
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "/admin/users": {
 | 
				
			||||||
 | 
					      "get": {
 | 
				
			||||||
 | 
					        "operationId": "searchUsersAdmin",
 | 
				
			||||||
 | 
					        "parameters": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "name": "withDeleted",
 | 
				
			||||||
 | 
					            "required": false,
 | 
				
			||||||
 | 
					            "in": "query",
 | 
				
			||||||
 | 
					            "schema": {
 | 
				
			||||||
 | 
					              "type": "boolean"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "200": {
 | 
				
			||||||
 | 
					            "content": {
 | 
				
			||||||
 | 
					              "application/json": {
 | 
				
			||||||
 | 
					                "schema": {
 | 
				
			||||||
 | 
					                  "items": {
 | 
				
			||||||
 | 
					                    "$ref": "#/components/schemas/UserAdminResponseDto"
 | 
				
			||||||
 | 
					                  },
 | 
				
			||||||
 | 
					                  "type": "array"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "description": ""
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "security": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "bearer": []
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "cookie": []
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "api_key": []
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "User"
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "post": {
 | 
				
			||||||
 | 
					        "operationId": "createUserAdmin",
 | 
				
			||||||
 | 
					        "parameters": [],
 | 
				
			||||||
 | 
					        "requestBody": {
 | 
				
			||||||
 | 
					          "content": {
 | 
				
			||||||
 | 
					            "application/json": {
 | 
				
			||||||
 | 
					              "schema": {
 | 
				
			||||||
 | 
					                "$ref": "#/components/schemas/UserAdminCreateDto"
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "required": true
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "201": {
 | 
				
			||||||
 | 
					            "content": {
 | 
				
			||||||
 | 
					              "application/json": {
 | 
				
			||||||
 | 
					                "schema": {
 | 
				
			||||||
 | 
					                  "$ref": "#/components/schemas/UserAdminResponseDto"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "description": ""
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "security": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "bearer": []
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "cookie": []
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "api_key": []
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "User"
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "/admin/users/{id}": {
 | 
				
			||||||
 | 
					      "delete": {
 | 
				
			||||||
 | 
					        "operationId": "deleteUserAdmin",
 | 
				
			||||||
 | 
					        "parameters": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "name": "id",
 | 
				
			||||||
 | 
					            "required": true,
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "schema": {
 | 
				
			||||||
 | 
					              "format": "uuid",
 | 
				
			||||||
 | 
					              "type": "string"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "requestBody": {
 | 
				
			||||||
 | 
					          "content": {
 | 
				
			||||||
 | 
					            "application/json": {
 | 
				
			||||||
 | 
					              "schema": {
 | 
				
			||||||
 | 
					                "$ref": "#/components/schemas/UserAdminDeleteDto"
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "required": true
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "200": {
 | 
				
			||||||
 | 
					            "content": {
 | 
				
			||||||
 | 
					              "application/json": {
 | 
				
			||||||
 | 
					                "schema": {
 | 
				
			||||||
 | 
					                  "$ref": "#/components/schemas/UserAdminResponseDto"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "description": ""
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "security": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "bearer": []
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "cookie": []
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "api_key": []
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "User"
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "get": {
 | 
				
			||||||
 | 
					        "operationId": "getUserAdmin",
 | 
				
			||||||
 | 
					        "parameters": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "name": "id",
 | 
				
			||||||
 | 
					            "required": true,
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "schema": {
 | 
				
			||||||
 | 
					              "format": "uuid",
 | 
				
			||||||
 | 
					              "type": "string"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "200": {
 | 
				
			||||||
 | 
					            "content": {
 | 
				
			||||||
 | 
					              "application/json": {
 | 
				
			||||||
 | 
					                "schema": {
 | 
				
			||||||
 | 
					                  "$ref": "#/components/schemas/UserAdminResponseDto"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "description": ""
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "security": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "bearer": []
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "cookie": []
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "api_key": []
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "User"
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "put": {
 | 
				
			||||||
 | 
					        "operationId": "updateUserAdmin",
 | 
				
			||||||
 | 
					        "parameters": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "name": "id",
 | 
				
			||||||
 | 
					            "required": true,
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "schema": {
 | 
				
			||||||
 | 
					              "format": "uuid",
 | 
				
			||||||
 | 
					              "type": "string"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "requestBody": {
 | 
				
			||||||
 | 
					          "content": {
 | 
				
			||||||
 | 
					            "application/json": {
 | 
				
			||||||
 | 
					              "schema": {
 | 
				
			||||||
 | 
					                "$ref": "#/components/schemas/UserAdminUpdateDto"
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "required": true
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "200": {
 | 
				
			||||||
 | 
					            "content": {
 | 
				
			||||||
 | 
					              "application/json": {
 | 
				
			||||||
 | 
					                "schema": {
 | 
				
			||||||
 | 
					                  "$ref": "#/components/schemas/UserAdminResponseDto"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "description": ""
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "security": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "bearer": []
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "cookie": []
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "api_key": []
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "User"
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "/admin/users/{id}/restore": {
 | 
				
			||||||
 | 
					      "post": {
 | 
				
			||||||
 | 
					        "operationId": "restoreUserAdmin",
 | 
				
			||||||
 | 
					        "parameters": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "name": "id",
 | 
				
			||||||
 | 
					            "required": true,
 | 
				
			||||||
 | 
					            "in": "path",
 | 
				
			||||||
 | 
					            "schema": {
 | 
				
			||||||
 | 
					              "format": "uuid",
 | 
				
			||||||
 | 
					              "type": "string"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "responses": {
 | 
				
			||||||
 | 
					          "201": {
 | 
				
			||||||
 | 
					            "content": {
 | 
				
			||||||
 | 
					              "application/json": {
 | 
				
			||||||
 | 
					                "schema": {
 | 
				
			||||||
 | 
					                  "$ref": "#/components/schemas/UserAdminResponseDto"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "description": ""
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "security": [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "bearer": []
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "cookie": []
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            "api_key": []
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "tags": [
 | 
				
			||||||
 | 
					          "User"
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "/albums": {
 | 
					    "/albums": {
 | 
				
			||||||
      "get": {
 | 
					      "get": {
 | 
				
			||||||
        "operationId": "getAllAlbums",
 | 
					        "operationId": "getAllAlbums",
 | 
				
			||||||
@ -1879,7 +2147,7 @@
 | 
				
			|||||||
            "content": {
 | 
					            "content": {
 | 
				
			||||||
              "application/json": {
 | 
					              "application/json": {
 | 
				
			||||||
                "schema": {
 | 
					                "schema": {
 | 
				
			||||||
                  "$ref": "#/components/schemas/UserResponseDto"
 | 
					                  "$ref": "#/components/schemas/UserAdminResponseDto"
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
@ -1910,7 +2178,7 @@
 | 
				
			|||||||
            "content": {
 | 
					            "content": {
 | 
				
			||||||
              "application/json": {
 | 
					              "application/json": {
 | 
				
			||||||
                "schema": {
 | 
					                "schema": {
 | 
				
			||||||
                  "$ref": "#/components/schemas/UserResponseDto"
 | 
					                  "$ref": "#/components/schemas/UserAdminResponseDto"
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
@ -3160,7 +3428,7 @@
 | 
				
			|||||||
            "content": {
 | 
					            "content": {
 | 
				
			||||||
              "application/json": {
 | 
					              "application/json": {
 | 
				
			||||||
                "schema": {
 | 
					                "schema": {
 | 
				
			||||||
                  "$ref": "#/components/schemas/UserResponseDto"
 | 
					                  "$ref": "#/components/schemas/UserAdminResponseDto"
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
@ -3206,7 +3474,7 @@
 | 
				
			|||||||
            "content": {
 | 
					            "content": {
 | 
				
			||||||
              "application/json": {
 | 
					              "application/json": {
 | 
				
			||||||
                "schema": {
 | 
					                "schema": {
 | 
				
			||||||
                  "$ref": "#/components/schemas/UserResponseDto"
 | 
					                  "$ref": "#/components/schemas/UserAdminResponseDto"
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
@ -6030,17 +6298,8 @@
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    "/users": {
 | 
					    "/users": {
 | 
				
			||||||
      "get": {
 | 
					      "get": {
 | 
				
			||||||
        "operationId": "getAllUsers",
 | 
					        "operationId": "searchUsers",
 | 
				
			||||||
        "parameters": [
 | 
					        "parameters": [],
 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            "name": "isAll",
 | 
					 | 
				
			||||||
            "required": true,
 | 
					 | 
				
			||||||
            "in": "query",
 | 
					 | 
				
			||||||
            "schema": {
 | 
					 | 
				
			||||||
              "type": "boolean"
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        ],
 | 
					 | 
				
			||||||
        "responses": {
 | 
					        "responses": {
 | 
				
			||||||
          "200": {
 | 
					          "200": {
 | 
				
			||||||
            "content": {
 | 
					            "content": {
 | 
				
			||||||
@ -6070,26 +6329,18 @@
 | 
				
			|||||||
        "tags": [
 | 
					        "tags": [
 | 
				
			||||||
          "User"
 | 
					          "User"
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
      "post": {
 | 
					    "/users/me": {
 | 
				
			||||||
        "operationId": "createUser",
 | 
					      "get": {
 | 
				
			||||||
 | 
					        "operationId": "getMyUser",
 | 
				
			||||||
        "parameters": [],
 | 
					        "parameters": [],
 | 
				
			||||||
        "requestBody": {
 | 
					 | 
				
			||||||
          "content": {
 | 
					 | 
				
			||||||
            "application/json": {
 | 
					 | 
				
			||||||
              "schema": {
 | 
					 | 
				
			||||||
                "$ref": "#/components/schemas/CreateUserDto"
 | 
					 | 
				
			||||||
              }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "required": true
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        "responses": {
 | 
					        "responses": {
 | 
				
			||||||
          "201": {
 | 
					          "200": {
 | 
				
			||||||
            "content": {
 | 
					            "content": {
 | 
				
			||||||
              "application/json": {
 | 
					              "application/json": {
 | 
				
			||||||
                "schema": {
 | 
					                "schema": {
 | 
				
			||||||
                  "$ref": "#/components/schemas/UserResponseDto"
 | 
					                  "$ref": "#/components/schemas/UserAdminResponseDto"
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
@ -6112,13 +6363,13 @@
 | 
				
			|||||||
        ]
 | 
					        ]
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      "put": {
 | 
					      "put": {
 | 
				
			||||||
        "operationId": "updateUser",
 | 
					        "operationId": "updateMyUser",
 | 
				
			||||||
        "parameters": [],
 | 
					        "parameters": [],
 | 
				
			||||||
        "requestBody": {
 | 
					        "requestBody": {
 | 
				
			||||||
          "content": {
 | 
					          "content": {
 | 
				
			||||||
            "application/json": {
 | 
					            "application/json": {
 | 
				
			||||||
              "schema": {
 | 
					              "schema": {
 | 
				
			||||||
                "$ref": "#/components/schemas/UpdateUserDto"
 | 
					                "$ref": "#/components/schemas/UserUpdateMeDto"
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
@ -6129,39 +6380,7 @@
 | 
				
			|||||||
            "content": {
 | 
					            "content": {
 | 
				
			||||||
              "application/json": {
 | 
					              "application/json": {
 | 
				
			||||||
                "schema": {
 | 
					                "schema": {
 | 
				
			||||||
                  "$ref": "#/components/schemas/UserResponseDto"
 | 
					                  "$ref": "#/components/schemas/UserAdminResponseDto"
 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
              }
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "description": ""
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        "security": [
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            "bearer": []
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            "cookie": []
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            "api_key": []
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        ],
 | 
					 | 
				
			||||||
        "tags": [
 | 
					 | 
				
			||||||
          "User"
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "/users/me": {
 | 
					 | 
				
			||||||
      "get": {
 | 
					 | 
				
			||||||
        "operationId": "getMyUserInfo",
 | 
					 | 
				
			||||||
        "parameters": [],
 | 
					 | 
				
			||||||
        "responses": {
 | 
					 | 
				
			||||||
          "200": {
 | 
					 | 
				
			||||||
            "content": {
 | 
					 | 
				
			||||||
              "application/json": {
 | 
					 | 
				
			||||||
                "schema": {
 | 
					 | 
				
			||||||
                  "$ref": "#/components/schemas/UserResponseDto"
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
@ -6251,58 +6470,8 @@
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "/users/{id}": {
 | 
					    "/users/{id}": {
 | 
				
			||||||
      "delete": {
 | 
					 | 
				
			||||||
        "operationId": "deleteUser",
 | 
					 | 
				
			||||||
        "parameters": [
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            "name": "id",
 | 
					 | 
				
			||||||
            "required": true,
 | 
					 | 
				
			||||||
            "in": "path",
 | 
					 | 
				
			||||||
            "schema": {
 | 
					 | 
				
			||||||
              "format": "uuid",
 | 
					 | 
				
			||||||
              "type": "string"
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        ],
 | 
					 | 
				
			||||||
        "requestBody": {
 | 
					 | 
				
			||||||
          "content": {
 | 
					 | 
				
			||||||
            "application/json": {
 | 
					 | 
				
			||||||
              "schema": {
 | 
					 | 
				
			||||||
                "$ref": "#/components/schemas/DeleteUserDto"
 | 
					 | 
				
			||||||
              }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "required": true
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        "responses": {
 | 
					 | 
				
			||||||
          "200": {
 | 
					 | 
				
			||||||
            "content": {
 | 
					 | 
				
			||||||
              "application/json": {
 | 
					 | 
				
			||||||
                "schema": {
 | 
					 | 
				
			||||||
                  "$ref": "#/components/schemas/UserResponseDto"
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
              }
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "description": ""
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        "security": [
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            "bearer": []
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            "cookie": []
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            "api_key": []
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        ],
 | 
					 | 
				
			||||||
        "tags": [
 | 
					 | 
				
			||||||
          "User"
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "get": {
 | 
					      "get": {
 | 
				
			||||||
        "operationId": "getUserById",
 | 
					        "operationId": "getUser",
 | 
				
			||||||
        "parameters": [
 | 
					        "parameters": [
 | 
				
			||||||
          {
 | 
					          {
 | 
				
			||||||
            "name": "id",
 | 
					            "name": "id",
 | 
				
			||||||
@ -6384,48 +6553,6 @@
 | 
				
			|||||||
          "User"
 | 
					          "User"
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "/users/{id}/restore": {
 | 
					 | 
				
			||||||
      "post": {
 | 
					 | 
				
			||||||
        "operationId": "restoreUser",
 | 
					 | 
				
			||||||
        "parameters": [
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            "name": "id",
 | 
					 | 
				
			||||||
            "required": true,
 | 
					 | 
				
			||||||
            "in": "path",
 | 
					 | 
				
			||||||
            "schema": {
 | 
					 | 
				
			||||||
              "format": "uuid",
 | 
					 | 
				
			||||||
              "type": "string"
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        ],
 | 
					 | 
				
			||||||
        "responses": {
 | 
					 | 
				
			||||||
          "201": {
 | 
					 | 
				
			||||||
            "content": {
 | 
					 | 
				
			||||||
              "application/json": {
 | 
					 | 
				
			||||||
                "schema": {
 | 
					 | 
				
			||||||
                  "$ref": "#/components/schemas/UserResponseDto"
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
              }
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "description": ""
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        "security": [
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            "bearer": []
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            "cookie": []
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          {
 | 
					 | 
				
			||||||
            "api_key": []
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        ],
 | 
					 | 
				
			||||||
        "tags": [
 | 
					 | 
				
			||||||
          "User"
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "info": {
 | 
					  "info": {
 | 
				
			||||||
@ -6567,7 +6694,7 @@
 | 
				
			|||||||
            "type": "string"
 | 
					            "type": "string"
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          "user": {
 | 
					          "user": {
 | 
				
			||||||
            "$ref": "#/components/schemas/UserDto"
 | 
					            "$ref": "#/components/schemas/UserResponseDto"
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "required": [
 | 
					        "required": [
 | 
				
			||||||
@ -7775,52 +7902,6 @@
 | 
				
			|||||||
        ],
 | 
					        ],
 | 
				
			||||||
        "type": "object"
 | 
					        "type": "object"
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      "CreateUserDto": {
 | 
					 | 
				
			||||||
        "properties": {
 | 
					 | 
				
			||||||
          "email": {
 | 
					 | 
				
			||||||
            "type": "string"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "memoriesEnabled": {
 | 
					 | 
				
			||||||
            "type": "boolean"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "name": {
 | 
					 | 
				
			||||||
            "type": "string"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "notify": {
 | 
					 | 
				
			||||||
            "type": "boolean"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "password": {
 | 
					 | 
				
			||||||
            "type": "string"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "quotaSizeInBytes": {
 | 
					 | 
				
			||||||
            "format": "int64",
 | 
					 | 
				
			||||||
            "minimum": 1,
 | 
					 | 
				
			||||||
            "nullable": true,
 | 
					 | 
				
			||||||
            "type": "integer"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "shouldChangePassword": {
 | 
					 | 
				
			||||||
            "type": "boolean"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "storageLabel": {
 | 
					 | 
				
			||||||
            "nullable": true,
 | 
					 | 
				
			||||||
            "type": "string"
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        "required": [
 | 
					 | 
				
			||||||
          "email",
 | 
					 | 
				
			||||||
          "name",
 | 
					 | 
				
			||||||
          "password"
 | 
					 | 
				
			||||||
        ],
 | 
					 | 
				
			||||||
        "type": "object"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "DeleteUserDto": {
 | 
					 | 
				
			||||||
        "properties": {
 | 
					 | 
				
			||||||
          "force": {
 | 
					 | 
				
			||||||
            "type": "boolean"
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        "type": "object"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "DownloadArchiveInfo": {
 | 
					      "DownloadArchiveInfo": {
 | 
				
			||||||
        "properties": {
 | 
					        "properties": {
 | 
				
			||||||
          "assetIds": {
 | 
					          "assetIds": {
 | 
				
			||||||
@ -8803,15 +8884,6 @@
 | 
				
			|||||||
          "avatarColor": {
 | 
					          "avatarColor": {
 | 
				
			||||||
            "$ref": "#/components/schemas/UserAvatarColor"
 | 
					            "$ref": "#/components/schemas/UserAvatarColor"
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          "createdAt": {
 | 
					 | 
				
			||||||
            "format": "date-time",
 | 
					 | 
				
			||||||
            "type": "string"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "deletedAt": {
 | 
					 | 
				
			||||||
            "format": "date-time",
 | 
					 | 
				
			||||||
            "nullable": true,
 | 
					 | 
				
			||||||
            "type": "string"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "email": {
 | 
					          "email": {
 | 
				
			||||||
            "type": "string"
 | 
					            "type": "string"
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
@ -8821,62 +8893,19 @@
 | 
				
			|||||||
          "inTimeline": {
 | 
					          "inTimeline": {
 | 
				
			||||||
            "type": "boolean"
 | 
					            "type": "boolean"
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          "isAdmin": {
 | 
					 | 
				
			||||||
            "type": "boolean"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "memoriesEnabled": {
 | 
					 | 
				
			||||||
            "type": "boolean"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "name": {
 | 
					          "name": {
 | 
				
			||||||
            "type": "string"
 | 
					            "type": "string"
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          "oauthId": {
 | 
					 | 
				
			||||||
            "type": "string"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "profileImagePath": {
 | 
					          "profileImagePath": {
 | 
				
			||||||
            "type": "string"
 | 
					            "type": "string"
 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "quotaSizeInBytes": {
 | 
					 | 
				
			||||||
            "format": "int64",
 | 
					 | 
				
			||||||
            "nullable": true,
 | 
					 | 
				
			||||||
            "type": "integer"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "quotaUsageInBytes": {
 | 
					 | 
				
			||||||
            "format": "int64",
 | 
					 | 
				
			||||||
            "nullable": true,
 | 
					 | 
				
			||||||
            "type": "integer"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "shouldChangePassword": {
 | 
					 | 
				
			||||||
            "type": "boolean"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "status": {
 | 
					 | 
				
			||||||
            "$ref": "#/components/schemas/UserStatus"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "storageLabel": {
 | 
					 | 
				
			||||||
            "nullable": true,
 | 
					 | 
				
			||||||
            "type": "string"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "updatedAt": {
 | 
					 | 
				
			||||||
            "format": "date-time",
 | 
					 | 
				
			||||||
            "type": "string"
 | 
					 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "required": [
 | 
					        "required": [
 | 
				
			||||||
          "avatarColor",
 | 
					          "avatarColor",
 | 
				
			||||||
          "createdAt",
 | 
					 | 
				
			||||||
          "deletedAt",
 | 
					 | 
				
			||||||
          "email",
 | 
					          "email",
 | 
				
			||||||
          "id",
 | 
					          "id",
 | 
				
			||||||
          "isAdmin",
 | 
					 | 
				
			||||||
          "name",
 | 
					          "name",
 | 
				
			||||||
          "oauthId",
 | 
					          "profileImagePath"
 | 
				
			||||||
          "profileImagePath",
 | 
					 | 
				
			||||||
          "quotaSizeInBytes",
 | 
					 | 
				
			||||||
          "quotaUsageInBytes",
 | 
					 | 
				
			||||||
          "shouldChangePassword",
 | 
					 | 
				
			||||||
          "status",
 | 
					 | 
				
			||||||
          "storageLabel",
 | 
					 | 
				
			||||||
          "updatedAt"
 | 
					 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        "type": "object"
 | 
					        "type": "object"
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
@ -10810,48 +10839,6 @@
 | 
				
			|||||||
        },
 | 
					        },
 | 
				
			||||||
        "type": "object"
 | 
					        "type": "object"
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      "UpdateUserDto": {
 | 
					 | 
				
			||||||
        "properties": {
 | 
					 | 
				
			||||||
          "avatarColor": {
 | 
					 | 
				
			||||||
            "$ref": "#/components/schemas/UserAvatarColor"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "email": {
 | 
					 | 
				
			||||||
            "type": "string"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "id": {
 | 
					 | 
				
			||||||
            "format": "uuid",
 | 
					 | 
				
			||||||
            "type": "string"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "isAdmin": {
 | 
					 | 
				
			||||||
            "type": "boolean"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "memoriesEnabled": {
 | 
					 | 
				
			||||||
            "type": "boolean"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "name": {
 | 
					 | 
				
			||||||
            "type": "string"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "password": {
 | 
					 | 
				
			||||||
            "type": "string"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "quotaSizeInBytes": {
 | 
					 | 
				
			||||||
            "format": "int64",
 | 
					 | 
				
			||||||
            "minimum": 1,
 | 
					 | 
				
			||||||
            "nullable": true,
 | 
					 | 
				
			||||||
            "type": "integer"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "shouldChangePassword": {
 | 
					 | 
				
			||||||
            "type": "boolean"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "storageLabel": {
 | 
					 | 
				
			||||||
            "type": "string"
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        "required": [
 | 
					 | 
				
			||||||
          "id"
 | 
					 | 
				
			||||||
        ],
 | 
					 | 
				
			||||||
        "type": "object"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "UsageByUserDto": {
 | 
					      "UsageByUserDto": {
 | 
				
			||||||
        "properties": {
 | 
					        "properties": {
 | 
				
			||||||
          "photos": {
 | 
					          "photos": {
 | 
				
			||||||
@ -10886,49 +10873,53 @@
 | 
				
			|||||||
        ],
 | 
					        ],
 | 
				
			||||||
        "type": "object"
 | 
					        "type": "object"
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      "UserAvatarColor": {
 | 
					      "UserAdminCreateDto": {
 | 
				
			||||||
        "enum": [
 | 
					 | 
				
			||||||
          "primary",
 | 
					 | 
				
			||||||
          "pink",
 | 
					 | 
				
			||||||
          "red",
 | 
					 | 
				
			||||||
          "yellow",
 | 
					 | 
				
			||||||
          "blue",
 | 
					 | 
				
			||||||
          "green",
 | 
					 | 
				
			||||||
          "purple",
 | 
					 | 
				
			||||||
          "orange",
 | 
					 | 
				
			||||||
          "gray",
 | 
					 | 
				
			||||||
          "amber"
 | 
					 | 
				
			||||||
        ],
 | 
					 | 
				
			||||||
        "type": "string"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "UserDto": {
 | 
					 | 
				
			||||||
        "properties": {
 | 
					        "properties": {
 | 
				
			||||||
          "avatarColor": {
 | 
					 | 
				
			||||||
            "$ref": "#/components/schemas/UserAvatarColor"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "email": {
 | 
					          "email": {
 | 
				
			||||||
            "type": "string"
 | 
					            "type": "string"
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          "id": {
 | 
					          "memoriesEnabled": {
 | 
				
			||||||
            "type": "string"
 | 
					            "type": "boolean"
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          "name": {
 | 
					          "name": {
 | 
				
			||||||
            "type": "string"
 | 
					            "type": "string"
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          "profileImagePath": {
 | 
					          "notify": {
 | 
				
			||||||
 | 
					            "type": "boolean"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "password": {
 | 
				
			||||||
 | 
					            "type": "string"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "quotaSizeInBytes": {
 | 
				
			||||||
 | 
					            "format": "int64",
 | 
				
			||||||
 | 
					            "minimum": 1,
 | 
				
			||||||
 | 
					            "nullable": true,
 | 
				
			||||||
 | 
					            "type": "integer"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "shouldChangePassword": {
 | 
				
			||||||
 | 
					            "type": "boolean"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "storageLabel": {
 | 
				
			||||||
 | 
					            "nullable": true,
 | 
				
			||||||
            "type": "string"
 | 
					            "type": "string"
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "required": [
 | 
					        "required": [
 | 
				
			||||||
          "avatarColor",
 | 
					 | 
				
			||||||
          "email",
 | 
					          "email",
 | 
				
			||||||
          "id",
 | 
					 | 
				
			||||||
          "name",
 | 
					          "name",
 | 
				
			||||||
          "profileImagePath"
 | 
					          "password"
 | 
				
			||||||
        ],
 | 
					        ],
 | 
				
			||||||
        "type": "object"
 | 
					        "type": "object"
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      "UserResponseDto": {
 | 
					      "UserAdminDeleteDto": {
 | 
				
			||||||
 | 
					        "properties": {
 | 
				
			||||||
 | 
					          "force": {
 | 
				
			||||||
 | 
					            "type": "boolean"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "type": "object"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "UserAdminResponseDto": {
 | 
				
			||||||
        "properties": {
 | 
					        "properties": {
 | 
				
			||||||
          "avatarColor": {
 | 
					          "avatarColor": {
 | 
				
			||||||
            "$ref": "#/components/schemas/UserAvatarColor"
 | 
					            "$ref": "#/components/schemas/UserAvatarColor"
 | 
				
			||||||
@ -11007,6 +10998,81 @@
 | 
				
			|||||||
        ],
 | 
					        ],
 | 
				
			||||||
        "type": "object"
 | 
					        "type": "object"
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
 | 
					      "UserAdminUpdateDto": {
 | 
				
			||||||
 | 
					        "properties": {
 | 
				
			||||||
 | 
					          "avatarColor": {
 | 
				
			||||||
 | 
					            "$ref": "#/components/schemas/UserAvatarColor"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "email": {
 | 
				
			||||||
 | 
					            "type": "string"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "memoriesEnabled": {
 | 
				
			||||||
 | 
					            "type": "boolean"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "name": {
 | 
				
			||||||
 | 
					            "type": "string"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "password": {
 | 
				
			||||||
 | 
					            "type": "string"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "quotaSizeInBytes": {
 | 
				
			||||||
 | 
					            "format": "int64",
 | 
				
			||||||
 | 
					            "minimum": 1,
 | 
				
			||||||
 | 
					            "nullable": true,
 | 
				
			||||||
 | 
					            "type": "integer"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "shouldChangePassword": {
 | 
				
			||||||
 | 
					            "type": "boolean"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "storageLabel": {
 | 
				
			||||||
 | 
					            "nullable": true,
 | 
				
			||||||
 | 
					            "type": "string"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "type": "object"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "UserAvatarColor": {
 | 
				
			||||||
 | 
					        "enum": [
 | 
				
			||||||
 | 
					          "primary",
 | 
				
			||||||
 | 
					          "pink",
 | 
				
			||||||
 | 
					          "red",
 | 
				
			||||||
 | 
					          "yellow",
 | 
				
			||||||
 | 
					          "blue",
 | 
				
			||||||
 | 
					          "green",
 | 
				
			||||||
 | 
					          "purple",
 | 
				
			||||||
 | 
					          "orange",
 | 
				
			||||||
 | 
					          "gray",
 | 
				
			||||||
 | 
					          "amber"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "type": "string"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "UserResponseDto": {
 | 
				
			||||||
 | 
					        "properties": {
 | 
				
			||||||
 | 
					          "avatarColor": {
 | 
				
			||||||
 | 
					            "$ref": "#/components/schemas/UserAvatarColor"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "email": {
 | 
				
			||||||
 | 
					            "type": "string"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "id": {
 | 
				
			||||||
 | 
					            "type": "string"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "name": {
 | 
				
			||||||
 | 
					            "type": "string"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "profileImagePath": {
 | 
				
			||||||
 | 
					            "type": "string"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "required": [
 | 
				
			||||||
 | 
					          "avatarColor",
 | 
				
			||||||
 | 
					          "email",
 | 
				
			||||||
 | 
					          "id",
 | 
				
			||||||
 | 
					          "name",
 | 
				
			||||||
 | 
					          "profileImagePath"
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        "type": "object"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
      "UserStatus": {
 | 
					      "UserStatus": {
 | 
				
			||||||
        "enum": [
 | 
					        "enum": [
 | 
				
			||||||
          "active",
 | 
					          "active",
 | 
				
			||||||
@ -11015,6 +11081,26 @@
 | 
				
			|||||||
        ],
 | 
					        ],
 | 
				
			||||||
        "type": "string"
 | 
					        "type": "string"
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
 | 
					      "UserUpdateMeDto": {
 | 
				
			||||||
 | 
					        "properties": {
 | 
				
			||||||
 | 
					          "avatarColor": {
 | 
				
			||||||
 | 
					            "$ref": "#/components/schemas/UserAvatarColor"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "email": {
 | 
				
			||||||
 | 
					            "type": "string"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "memoriesEnabled": {
 | 
				
			||||||
 | 
					            "type": "boolean"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "name": {
 | 
				
			||||||
 | 
					            "type": "string"
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					          "password": {
 | 
				
			||||||
 | 
					            "type": "string"
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "type": "object"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
      "ValidateAccessTokenResponseDto": {
 | 
					      "ValidateAccessTokenResponseDto": {
 | 
				
			||||||
        "properties": {
 | 
					        "properties": {
 | 
				
			||||||
          "authStatus": {
 | 
					          "authStatus": {
 | 
				
			||||||
 | 
				
			|||||||
@ -13,13 +13,22 @@ npm i --save @immich/sdk
 | 
				
			|||||||
For a more detailed example, check out the [`@immich/cli`](https://github.com/immich-app/immich/tree/main/cli).
 | 
					For a more detailed example, check out the [`@immich/cli`](https://github.com/immich-app/immich/tree/main/cli).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```typescript
 | 
					```typescript
 | 
				
			||||||
 | 
					<<<<<<< HEAD
 | 
				
			||||||
 | 
					import { getAllAlbums, getAllAssets, getMyUser, init } from "@immich/sdk";
 | 
				
			||||||
 | 
					=======
 | 
				
			||||||
import { getAllAlbums, getMyUserInfo, init } from "@immich/sdk";
 | 
					import { getAllAlbums, getMyUserInfo, init } from "@immich/sdk";
 | 
				
			||||||
 | 
					>>>>>>> e7c8501930a988dfb6c23ce1c48b0beb076a58c2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const API_KEY = "<API_KEY>"; // process.env.IMMICH_API_KEY
 | 
					const API_KEY = "<API_KEY>"; // process.env.IMMICH_API_KEY
 | 
				
			||||||
 | 
					
 | 
				
			||||||
init({ baseUrl: "https://demo.immich.app/api", apiKey: API_KEY });
 | 
					init({ baseUrl: "https://demo.immich.app/api", apiKey: API_KEY });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<<<<<<< HEAD
 | 
				
			||||||
 | 
					const user = await getMyUser();
 | 
				
			||||||
 | 
					const assets = await getAllAssets({ take: 1000 });
 | 
				
			||||||
 | 
					=======
 | 
				
			||||||
const user = await getMyUserInfo();
 | 
					const user = await getMyUserInfo();
 | 
				
			||||||
 | 
					>>>>>>> e7c8501930a988dfb6c23ce1c48b0beb076a58c2
 | 
				
			||||||
const albums = await getAllAlbums({});
 | 
					const albums = await getAllAlbums({});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
console.log({ user, albums });
 | 
					console.log({ user, albums });
 | 
				
			||||||
 | 
				
			|||||||
@ -14,7 +14,7 @@ const oazapfts = Oazapfts.runtime(defaults);
 | 
				
			|||||||
export const servers = {
 | 
					export const servers = {
 | 
				
			||||||
    server1: "/api"
 | 
					    server1: "/api"
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
export type UserDto = {
 | 
					export type UserResponseDto = {
 | 
				
			||||||
    avatarColor: UserAvatarColor;
 | 
					    avatarColor: UserAvatarColor;
 | 
				
			||||||
    email: string;
 | 
					    email: string;
 | 
				
			||||||
    id: string;
 | 
					    id: string;
 | 
				
			||||||
@ -27,7 +27,7 @@ export type ActivityResponseDto = {
 | 
				
			|||||||
    createdAt: string;
 | 
					    createdAt: string;
 | 
				
			||||||
    id: string;
 | 
					    id: string;
 | 
				
			||||||
    "type": Type;
 | 
					    "type": Type;
 | 
				
			||||||
    user: UserDto;
 | 
					    user: UserResponseDto;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
export type ActivityCreateDto = {
 | 
					export type ActivityCreateDto = {
 | 
				
			||||||
    albumId: string;
 | 
					    albumId: string;
 | 
				
			||||||
@ -38,7 +38,7 @@ export type ActivityCreateDto = {
 | 
				
			|||||||
export type ActivityStatisticsResponseDto = {
 | 
					export type ActivityStatisticsResponseDto = {
 | 
				
			||||||
    comments: number;
 | 
					    comments: number;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
export type UserResponseDto = {
 | 
					export type UserAdminResponseDto = {
 | 
				
			||||||
    avatarColor: UserAvatarColor;
 | 
					    avatarColor: UserAvatarColor;
 | 
				
			||||||
    createdAt: string;
 | 
					    createdAt: string;
 | 
				
			||||||
    deletedAt: string | null;
 | 
					    deletedAt: string | null;
 | 
				
			||||||
@ -56,6 +56,29 @@ export type UserResponseDto = {
 | 
				
			|||||||
    storageLabel: string | null;
 | 
					    storageLabel: string | null;
 | 
				
			||||||
    updatedAt: string;
 | 
					    updatedAt: string;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					export type UserAdminCreateDto = {
 | 
				
			||||||
 | 
					    email: string;
 | 
				
			||||||
 | 
					    memoriesEnabled?: boolean;
 | 
				
			||||||
 | 
					    name: string;
 | 
				
			||||||
 | 
					    notify?: boolean;
 | 
				
			||||||
 | 
					    password: string;
 | 
				
			||||||
 | 
					    quotaSizeInBytes?: number | null;
 | 
				
			||||||
 | 
					    shouldChangePassword?: boolean;
 | 
				
			||||||
 | 
					    storageLabel?: string | null;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					export type UserAdminDeleteDto = {
 | 
				
			||||||
 | 
					    force?: boolean;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					export type UserAdminUpdateDto = {
 | 
				
			||||||
 | 
					    avatarColor?: UserAvatarColor;
 | 
				
			||||||
 | 
					    email?: string;
 | 
				
			||||||
 | 
					    memoriesEnabled?: boolean;
 | 
				
			||||||
 | 
					    name?: string;
 | 
				
			||||||
 | 
					    password?: string;
 | 
				
			||||||
 | 
					    quotaSizeInBytes?: number | null;
 | 
				
			||||||
 | 
					    shouldChangePassword?: boolean;
 | 
				
			||||||
 | 
					    storageLabel?: string | null;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
export type AlbumUserResponseDto = {
 | 
					export type AlbumUserResponseDto = {
 | 
				
			||||||
    role: AlbumUserRole;
 | 
					    role: AlbumUserRole;
 | 
				
			||||||
    user: UserResponseDto;
 | 
					    user: UserResponseDto;
 | 
				
			||||||
@ -517,22 +540,11 @@ export type OAuthCallbackDto = {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
export type PartnerResponseDto = {
 | 
					export type PartnerResponseDto = {
 | 
				
			||||||
    avatarColor: UserAvatarColor;
 | 
					    avatarColor: UserAvatarColor;
 | 
				
			||||||
    createdAt: string;
 | 
					 | 
				
			||||||
    deletedAt: string | null;
 | 
					 | 
				
			||||||
    email: string;
 | 
					    email: string;
 | 
				
			||||||
    id: string;
 | 
					    id: string;
 | 
				
			||||||
    inTimeline?: boolean;
 | 
					    inTimeline?: boolean;
 | 
				
			||||||
    isAdmin: boolean;
 | 
					 | 
				
			||||||
    memoriesEnabled?: boolean;
 | 
					 | 
				
			||||||
    name: string;
 | 
					    name: string;
 | 
				
			||||||
    oauthId: string;
 | 
					 | 
				
			||||||
    profileImagePath: string;
 | 
					    profileImagePath: string;
 | 
				
			||||||
    quotaSizeInBytes: number | null;
 | 
					 | 
				
			||||||
    quotaUsageInBytes: number | null;
 | 
					 | 
				
			||||||
    shouldChangePassword: boolean;
 | 
					 | 
				
			||||||
    status: UserStatus;
 | 
					 | 
				
			||||||
    storageLabel: string | null;
 | 
					 | 
				
			||||||
    updatedAt: string;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
export type UpdatePartnerDto = {
 | 
					export type UpdatePartnerDto = {
 | 
				
			||||||
    inTimeline: boolean;
 | 
					    inTimeline: boolean;
 | 
				
			||||||
@ -1060,27 +1072,12 @@ export type TimeBucketResponseDto = {
 | 
				
			|||||||
    count: number;
 | 
					    count: number;
 | 
				
			||||||
    timeBucket: string;
 | 
					    timeBucket: string;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
export type CreateUserDto = {
 | 
					export type UserUpdateMeDto = {
 | 
				
			||||||
    email: string;
 | 
					 | 
				
			||||||
    memoriesEnabled?: boolean;
 | 
					 | 
				
			||||||
    name: string;
 | 
					 | 
				
			||||||
    notify?: boolean;
 | 
					 | 
				
			||||||
    password: string;
 | 
					 | 
				
			||||||
    quotaSizeInBytes?: number | null;
 | 
					 | 
				
			||||||
    shouldChangePassword?: boolean;
 | 
					 | 
				
			||||||
    storageLabel?: string | null;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
export type UpdateUserDto = {
 | 
					 | 
				
			||||||
    avatarColor?: UserAvatarColor;
 | 
					    avatarColor?: UserAvatarColor;
 | 
				
			||||||
    email?: string;
 | 
					    email?: string;
 | 
				
			||||||
    id: string;
 | 
					 | 
				
			||||||
    isAdmin?: boolean;
 | 
					 | 
				
			||||||
    memoriesEnabled?: boolean;
 | 
					    memoriesEnabled?: boolean;
 | 
				
			||||||
    name?: string;
 | 
					    name?: string;
 | 
				
			||||||
    password?: string;
 | 
					    password?: string;
 | 
				
			||||||
    quotaSizeInBytes?: number | null;
 | 
					 | 
				
			||||||
    shouldChangePassword?: boolean;
 | 
					 | 
				
			||||||
    storageLabel?: string;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
export type CreateProfileImageDto = {
 | 
					export type CreateProfileImageDto = {
 | 
				
			||||||
    file: Blob;
 | 
					    file: Blob;
 | 
				
			||||||
@ -1089,9 +1086,6 @@ export type CreateProfileImageResponseDto = {
 | 
				
			|||||||
    profileImagePath: string;
 | 
					    profileImagePath: string;
 | 
				
			||||||
    userId: string;
 | 
					    userId: string;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
export type DeleteUserDto = {
 | 
					 | 
				
			||||||
    force?: boolean;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
export function getActivities({ albumId, assetId, level, $type, userId }: {
 | 
					export function getActivities({ albumId, assetId, level, $type, userId }: {
 | 
				
			||||||
    albumId: string;
 | 
					    albumId: string;
 | 
				
			||||||
    assetId?: string;
 | 
					    assetId?: string;
 | 
				
			||||||
@ -1146,6 +1140,77 @@ export function deleteActivity({ id }: {
 | 
				
			|||||||
        method: "DELETE"
 | 
					        method: "DELETE"
 | 
				
			||||||
    }));
 | 
					    }));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					export function searchUsersAdmin({ withDeleted }: {
 | 
				
			||||||
 | 
					    withDeleted?: boolean;
 | 
				
			||||||
 | 
					}, opts?: Oazapfts.RequestOpts) {
 | 
				
			||||||
 | 
					    return oazapfts.ok(oazapfts.fetchJson<{
 | 
				
			||||||
 | 
					        status: 200;
 | 
				
			||||||
 | 
					        data: UserAdminResponseDto[];
 | 
				
			||||||
 | 
					    }>(`/admin/users${QS.query(QS.explode({
 | 
				
			||||||
 | 
					        withDeleted
 | 
				
			||||||
 | 
					    }))}`, {
 | 
				
			||||||
 | 
					        ...opts
 | 
				
			||||||
 | 
					    }));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export function createUserAdmin({ userAdminCreateDto }: {
 | 
				
			||||||
 | 
					    userAdminCreateDto: UserAdminCreateDto;
 | 
				
			||||||
 | 
					}, opts?: Oazapfts.RequestOpts) {
 | 
				
			||||||
 | 
					    return oazapfts.ok(oazapfts.fetchJson<{
 | 
				
			||||||
 | 
					        status: 201;
 | 
				
			||||||
 | 
					        data: UserAdminResponseDto;
 | 
				
			||||||
 | 
					    }>("/admin/users", oazapfts.json({
 | 
				
			||||||
 | 
					        ...opts,
 | 
				
			||||||
 | 
					        method: "POST",
 | 
				
			||||||
 | 
					        body: userAdminCreateDto
 | 
				
			||||||
 | 
					    })));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export function deleteUserAdmin({ id, userAdminDeleteDto }: {
 | 
				
			||||||
 | 
					    id: string;
 | 
				
			||||||
 | 
					    userAdminDeleteDto: UserAdminDeleteDto;
 | 
				
			||||||
 | 
					}, opts?: Oazapfts.RequestOpts) {
 | 
				
			||||||
 | 
					    return oazapfts.ok(oazapfts.fetchJson<{
 | 
				
			||||||
 | 
					        status: 200;
 | 
				
			||||||
 | 
					        data: UserAdminResponseDto;
 | 
				
			||||||
 | 
					    }>(`/admin/users/${encodeURIComponent(id)}`, oazapfts.json({
 | 
				
			||||||
 | 
					        ...opts,
 | 
				
			||||||
 | 
					        method: "DELETE",
 | 
				
			||||||
 | 
					        body: userAdminDeleteDto
 | 
				
			||||||
 | 
					    })));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export function getUserAdmin({ id }: {
 | 
				
			||||||
 | 
					    id: string;
 | 
				
			||||||
 | 
					}, opts?: Oazapfts.RequestOpts) {
 | 
				
			||||||
 | 
					    return oazapfts.ok(oazapfts.fetchJson<{
 | 
				
			||||||
 | 
					        status: 200;
 | 
				
			||||||
 | 
					        data: UserAdminResponseDto;
 | 
				
			||||||
 | 
					    }>(`/admin/users/${encodeURIComponent(id)}`, {
 | 
				
			||||||
 | 
					        ...opts
 | 
				
			||||||
 | 
					    }));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export function updateUserAdmin({ id, userAdminUpdateDto }: {
 | 
				
			||||||
 | 
					    id: string;
 | 
				
			||||||
 | 
					    userAdminUpdateDto: UserAdminUpdateDto;
 | 
				
			||||||
 | 
					}, opts?: Oazapfts.RequestOpts) {
 | 
				
			||||||
 | 
					    return oazapfts.ok(oazapfts.fetchJson<{
 | 
				
			||||||
 | 
					        status: 200;
 | 
				
			||||||
 | 
					        data: UserAdminResponseDto;
 | 
				
			||||||
 | 
					    }>(`/admin/users/${encodeURIComponent(id)}`, oazapfts.json({
 | 
				
			||||||
 | 
					        ...opts,
 | 
				
			||||||
 | 
					        method: "PUT",
 | 
				
			||||||
 | 
					        body: userAdminUpdateDto
 | 
				
			||||||
 | 
					    })));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export function restoreUserAdmin({ id }: {
 | 
				
			||||||
 | 
					    id: string;
 | 
				
			||||||
 | 
					}, opts?: Oazapfts.RequestOpts) {
 | 
				
			||||||
 | 
					    return oazapfts.ok(oazapfts.fetchJson<{
 | 
				
			||||||
 | 
					        status: 201;
 | 
				
			||||||
 | 
					        data: UserAdminResponseDto;
 | 
				
			||||||
 | 
					    }>(`/admin/users/${encodeURIComponent(id)}/restore`, {
 | 
				
			||||||
 | 
					        ...opts,
 | 
				
			||||||
 | 
					        method: "POST"
 | 
				
			||||||
 | 
					    }));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
export function getAllAlbums({ assetId, shared }: {
 | 
					export function getAllAlbums({ assetId, shared }: {
 | 
				
			||||||
    assetId?: string;
 | 
					    assetId?: string;
 | 
				
			||||||
    shared?: boolean;
 | 
					    shared?: boolean;
 | 
				
			||||||
@ -1589,7 +1654,7 @@ export function signUpAdmin({ signUpDto }: {
 | 
				
			|||||||
}, opts?: Oazapfts.RequestOpts) {
 | 
					}, opts?: Oazapfts.RequestOpts) {
 | 
				
			||||||
    return oazapfts.ok(oazapfts.fetchJson<{
 | 
					    return oazapfts.ok(oazapfts.fetchJson<{
 | 
				
			||||||
        status: 201;
 | 
					        status: 201;
 | 
				
			||||||
        data: UserResponseDto;
 | 
					        data: UserAdminResponseDto;
 | 
				
			||||||
    }>("/auth/admin-sign-up", oazapfts.json({
 | 
					    }>("/auth/admin-sign-up", oazapfts.json({
 | 
				
			||||||
        ...opts,
 | 
					        ...opts,
 | 
				
			||||||
        method: "POST",
 | 
					        method: "POST",
 | 
				
			||||||
@ -1601,7 +1666,7 @@ export function changePassword({ changePasswordDto }: {
 | 
				
			|||||||
}, opts?: Oazapfts.RequestOpts) {
 | 
					}, opts?: Oazapfts.RequestOpts) {
 | 
				
			||||||
    return oazapfts.ok(oazapfts.fetchJson<{
 | 
					    return oazapfts.ok(oazapfts.fetchJson<{
 | 
				
			||||||
        status: 200;
 | 
					        status: 200;
 | 
				
			||||||
        data: UserResponseDto;
 | 
					        data: UserAdminResponseDto;
 | 
				
			||||||
    }>("/auth/change-password", oazapfts.json({
 | 
					    }>("/auth/change-password", oazapfts.json({
 | 
				
			||||||
        ...opts,
 | 
					        ...opts,
 | 
				
			||||||
        method: "POST",
 | 
					        method: "POST",
 | 
				
			||||||
@ -1934,7 +1999,7 @@ export function linkOAuthAccount({ oAuthCallbackDto }: {
 | 
				
			|||||||
}, opts?: Oazapfts.RequestOpts) {
 | 
					}, opts?: Oazapfts.RequestOpts) {
 | 
				
			||||||
    return oazapfts.ok(oazapfts.fetchJson<{
 | 
					    return oazapfts.ok(oazapfts.fetchJson<{
 | 
				
			||||||
        status: 201;
 | 
					        status: 201;
 | 
				
			||||||
        data: UserResponseDto;
 | 
					        data: UserAdminResponseDto;
 | 
				
			||||||
    }>("/oauth/link", oazapfts.json({
 | 
					    }>("/oauth/link", oazapfts.json({
 | 
				
			||||||
        ...opts,
 | 
					        ...opts,
 | 
				
			||||||
        method: "POST",
 | 
					        method: "POST",
 | 
				
			||||||
@ -1949,7 +2014,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: 201;
 | 
				
			||||||
        data: UserResponseDto;
 | 
					        data: UserAdminResponseDto;
 | 
				
			||||||
    }>("/oauth/unlink", {
 | 
					    }>("/oauth/unlink", {
 | 
				
			||||||
        ...opts,
 | 
					        ...opts,
 | 
				
			||||||
        method: "POST"
 | 
					        method: "POST"
 | 
				
			||||||
@ -2687,50 +2752,34 @@ export function restoreAssets({ bulkIdsDto }: {
 | 
				
			|||||||
        body: bulkIdsDto
 | 
					        body: bulkIdsDto
 | 
				
			||||||
    })));
 | 
					    })));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
export function getAllUsers({ isAll }: {
 | 
					export function searchUsers(opts?: Oazapfts.RequestOpts) {
 | 
				
			||||||
    isAll: boolean;
 | 
					 | 
				
			||||||
}, opts?: Oazapfts.RequestOpts) {
 | 
					 | 
				
			||||||
    return oazapfts.ok(oazapfts.fetchJson<{
 | 
					    return oazapfts.ok(oazapfts.fetchJson<{
 | 
				
			||||||
        status: 200;
 | 
					        status: 200;
 | 
				
			||||||
        data: UserResponseDto[];
 | 
					        data: UserResponseDto[];
 | 
				
			||||||
    }>(`/users${QS.query(QS.explode({
 | 
					    }>("/users", {
 | 
				
			||||||
        isAll
 | 
					 | 
				
			||||||
    }))}`, {
 | 
					 | 
				
			||||||
        ...opts
 | 
					        ...opts
 | 
				
			||||||
    }));
 | 
					    }));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
export function createUser({ createUserDto }: {
 | 
					export function getMyUser(opts?: Oazapfts.RequestOpts) {
 | 
				
			||||||
    createUserDto: CreateUserDto;
 | 
					 | 
				
			||||||
}, opts?: Oazapfts.RequestOpts) {
 | 
					 | 
				
			||||||
    return oazapfts.ok(oazapfts.fetchJson<{
 | 
					 | 
				
			||||||
        status: 201;
 | 
					 | 
				
			||||||
        data: UserResponseDto;
 | 
					 | 
				
			||||||
    }>("/users", oazapfts.json({
 | 
					 | 
				
			||||||
        ...opts,
 | 
					 | 
				
			||||||
        method: "POST",
 | 
					 | 
				
			||||||
        body: createUserDto
 | 
					 | 
				
			||||||
    })));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
export function updateUser({ updateUserDto }: {
 | 
					 | 
				
			||||||
    updateUserDto: UpdateUserDto;
 | 
					 | 
				
			||||||
}, opts?: Oazapfts.RequestOpts) {
 | 
					 | 
				
			||||||
    return oazapfts.ok(oazapfts.fetchJson<{
 | 
					    return oazapfts.ok(oazapfts.fetchJson<{
 | 
				
			||||||
        status: 200;
 | 
					        status: 200;
 | 
				
			||||||
        data: UserResponseDto;
 | 
					        data: UserAdminResponseDto;
 | 
				
			||||||
    }>("/users", oazapfts.json({
 | 
					 | 
				
			||||||
        ...opts,
 | 
					 | 
				
			||||||
        method: "PUT",
 | 
					 | 
				
			||||||
        body: updateUserDto
 | 
					 | 
				
			||||||
    })));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
export function getMyUserInfo(opts?: Oazapfts.RequestOpts) {
 | 
					 | 
				
			||||||
    return oazapfts.ok(oazapfts.fetchJson<{
 | 
					 | 
				
			||||||
        status: 200;
 | 
					 | 
				
			||||||
        data: UserResponseDto;
 | 
					 | 
				
			||||||
    }>("/users/me", {
 | 
					    }>("/users/me", {
 | 
				
			||||||
        ...opts
 | 
					        ...opts
 | 
				
			||||||
    }));
 | 
					    }));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					export function updateMyUser({ userUpdateMeDto }: {
 | 
				
			||||||
 | 
					    userUpdateMeDto: UserUpdateMeDto;
 | 
				
			||||||
 | 
					}, opts?: Oazapfts.RequestOpts) {
 | 
				
			||||||
 | 
					    return oazapfts.ok(oazapfts.fetchJson<{
 | 
				
			||||||
 | 
					        status: 200;
 | 
				
			||||||
 | 
					        data: UserAdminResponseDto;
 | 
				
			||||||
 | 
					    }>("/users/me", oazapfts.json({
 | 
				
			||||||
 | 
					        ...opts,
 | 
				
			||||||
 | 
					        method: "PUT",
 | 
				
			||||||
 | 
					        body: userUpdateMeDto
 | 
				
			||||||
 | 
					    })));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
export function deleteProfileImage(opts?: Oazapfts.RequestOpts) {
 | 
					export function deleteProfileImage(opts?: Oazapfts.RequestOpts) {
 | 
				
			||||||
    return oazapfts.ok(oazapfts.fetchText("/users/profile-image", {
 | 
					    return oazapfts.ok(oazapfts.fetchText("/users/profile-image", {
 | 
				
			||||||
        ...opts,
 | 
					        ...opts,
 | 
				
			||||||
@ -2749,20 +2798,7 @@ export function createProfileImage({ createProfileImageDto }: {
 | 
				
			|||||||
        body: createProfileImageDto
 | 
					        body: createProfileImageDto
 | 
				
			||||||
    })));
 | 
					    })));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
export function deleteUser({ id, deleteUserDto }: {
 | 
					export function getUser({ id }: {
 | 
				
			||||||
    id: string;
 | 
					 | 
				
			||||||
    deleteUserDto: DeleteUserDto;
 | 
					 | 
				
			||||||
}, opts?: Oazapfts.RequestOpts) {
 | 
					 | 
				
			||||||
    return oazapfts.ok(oazapfts.fetchJson<{
 | 
					 | 
				
			||||||
        status: 200;
 | 
					 | 
				
			||||||
        data: UserResponseDto;
 | 
					 | 
				
			||||||
    }>(`/users/${encodeURIComponent(id)}`, oazapfts.json({
 | 
					 | 
				
			||||||
        ...opts,
 | 
					 | 
				
			||||||
        method: "DELETE",
 | 
					 | 
				
			||||||
        body: deleteUserDto
 | 
					 | 
				
			||||||
    })));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
export function getUserById({ id }: {
 | 
					 | 
				
			||||||
    id: string;
 | 
					    id: string;
 | 
				
			||||||
}, opts?: Oazapfts.RequestOpts) {
 | 
					}, opts?: Oazapfts.RequestOpts) {
 | 
				
			||||||
    return oazapfts.ok(oazapfts.fetchJson<{
 | 
					    return oazapfts.ok(oazapfts.fetchJson<{
 | 
				
			||||||
@ -2782,17 +2818,6 @@ export function getProfileImage({ id }: {
 | 
				
			|||||||
        ...opts
 | 
					        ...opts
 | 
				
			||||||
    }));
 | 
					    }));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
export function restoreUser({ id }: {
 | 
					 | 
				
			||||||
    id: string;
 | 
					 | 
				
			||||||
}, opts?: Oazapfts.RequestOpts) {
 | 
					 | 
				
			||||||
    return oazapfts.ok(oazapfts.fetchJson<{
 | 
					 | 
				
			||||||
        status: 201;
 | 
					 | 
				
			||||||
        data: UserResponseDto;
 | 
					 | 
				
			||||||
    }>(`/users/${encodeURIComponent(id)}/restore`, {
 | 
					 | 
				
			||||||
        ...opts,
 | 
					 | 
				
			||||||
        method: "POST"
 | 
					 | 
				
			||||||
    }));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
export enum ReactionLevel {
 | 
					export enum ReactionLevel {
 | 
				
			||||||
    Album = "album",
 | 
					    Album = "album",
 | 
				
			||||||
    Asset = "asset"
 | 
					    Asset = "asset"
 | 
				
			||||||
@ -2817,15 +2842,15 @@ export enum UserAvatarColor {
 | 
				
			|||||||
    Gray = "gray",
 | 
					    Gray = "gray",
 | 
				
			||||||
    Amber = "amber"
 | 
					    Amber = "amber"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
export enum AlbumUserRole {
 | 
					 | 
				
			||||||
    Editor = "editor",
 | 
					 | 
				
			||||||
    Viewer = "viewer"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
export enum UserStatus {
 | 
					export enum UserStatus {
 | 
				
			||||||
    Active = "active",
 | 
					    Active = "active",
 | 
				
			||||||
    Removing = "removing",
 | 
					    Removing = "removing",
 | 
				
			||||||
    Deleted = "deleted"
 | 
					    Deleted = "deleted"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					export enum AlbumUserRole {
 | 
				
			||||||
 | 
					    Editor = "editor",
 | 
				
			||||||
 | 
					    Viewer = "viewer"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
export enum TagTypeEnum {
 | 
					export enum TagTypeEnum {
 | 
				
			||||||
    Object = "OBJECT",
 | 
					    Object = "OBJECT",
 | 
				
			||||||
    Face = "FACE",
 | 
					    Face = "FACE",
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,9 @@
 | 
				
			|||||||
import { Command, CommandRunner, InquirerService, Question, QuestionSet } from 'nest-commander';
 | 
					import { Command, CommandRunner, InquirerService, Question, QuestionSet } from 'nest-commander';
 | 
				
			||||||
import { UserResponseDto } from 'src/dtos/user.dto';
 | 
					import { UserAdminResponseDto } from 'src/dtos/user.dto';
 | 
				
			||||||
import { CliService } from 'src/services/cli.service';
 | 
					import { CliService } from 'src/services/cli.service';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const prompt = (inquirer: InquirerService) => {
 | 
					const prompt = (inquirer: InquirerService) => {
 | 
				
			||||||
  return function ask(admin: UserResponseDto) {
 | 
					  return function ask(admin: UserAdminResponseDto) {
 | 
				
			||||||
    const { id, oauthId, email, name } = admin;
 | 
					    const { id, oauthId, email, name } = admin;
 | 
				
			||||||
    console.log(`Found Admin:
 | 
					    console.log(`Found Admin:
 | 
				
			||||||
- ID=${id}
 | 
					- ID=${id}
 | 
				
			||||||
 | 
				
			|||||||
@ -12,7 +12,7 @@ import {
 | 
				
			|||||||
  SignUpDto,
 | 
					  SignUpDto,
 | 
				
			||||||
  ValidateAccessTokenResponseDto,
 | 
					  ValidateAccessTokenResponseDto,
 | 
				
			||||||
} from 'src/dtos/auth.dto';
 | 
					} from 'src/dtos/auth.dto';
 | 
				
			||||||
import { UserResponseDto, mapUser } from 'src/dtos/user.dto';
 | 
					import { UserAdminResponseDto } from 'src/dtos/user.dto';
 | 
				
			||||||
import { Auth, Authenticated, GetLoginDetails } from 'src/middleware/auth.guard';
 | 
					import { Auth, Authenticated, GetLoginDetails } from 'src/middleware/auth.guard';
 | 
				
			||||||
import { AuthService, LoginDetails } from 'src/services/auth.service';
 | 
					import { AuthService, LoginDetails } from 'src/services/auth.service';
 | 
				
			||||||
import { respondWithCookie, respondWithoutCookie } from 'src/utils/response';
 | 
					import { respondWithCookie, respondWithoutCookie } from 'src/utils/response';
 | 
				
			||||||
@ -40,7 +40,7 @@ export class AuthController {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Post('admin-sign-up')
 | 
					  @Post('admin-sign-up')
 | 
				
			||||||
  signUpAdmin(@Body() dto: SignUpDto): Promise<UserResponseDto> {
 | 
					  signUpAdmin(@Body() dto: SignUpDto): Promise<UserAdminResponseDto> {
 | 
				
			||||||
    return this.service.adminSignUp(dto);
 | 
					    return this.service.adminSignUp(dto);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -54,8 +54,8 @@ export class AuthController {
 | 
				
			|||||||
  @Post('change-password')
 | 
					  @Post('change-password')
 | 
				
			||||||
  @HttpCode(HttpStatus.OK)
 | 
					  @HttpCode(HttpStatus.OK)
 | 
				
			||||||
  @Authenticated()
 | 
					  @Authenticated()
 | 
				
			||||||
  changePassword(@Auth() auth: AuthDto, @Body() dto: ChangePasswordDto): Promise<UserResponseDto> {
 | 
					  changePassword(@Auth() auth: AuthDto, @Body() dto: ChangePasswordDto): Promise<UserAdminResponseDto> {
 | 
				
			||||||
    return this.service.changePassword(auth, dto).then(mapUser);
 | 
					    return this.service.changePassword(auth, dto);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Post('logout')
 | 
					  @Post('logout')
 | 
				
			||||||
 | 
				
			|||||||
@ -27,6 +27,7 @@ import { SystemMetadataController } from 'src/controllers/system-metadata.contro
 | 
				
			|||||||
import { TagController } from 'src/controllers/tag.controller';
 | 
					import { TagController } from 'src/controllers/tag.controller';
 | 
				
			||||||
import { TimelineController } from 'src/controllers/timeline.controller';
 | 
					import { TimelineController } from 'src/controllers/timeline.controller';
 | 
				
			||||||
import { TrashController } from 'src/controllers/trash.controller';
 | 
					import { TrashController } from 'src/controllers/trash.controller';
 | 
				
			||||||
 | 
					import { UserAdminController } from 'src/controllers/user-admin.controller';
 | 
				
			||||||
import { UserController } from 'src/controllers/user.controller';
 | 
					import { UserController } from 'src/controllers/user.controller';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const controllers = [
 | 
					export const controllers = [
 | 
				
			||||||
@ -59,5 +60,6 @@ export const controllers = [
 | 
				
			|||||||
  TagController,
 | 
					  TagController,
 | 
				
			||||||
  TimelineController,
 | 
					  TimelineController,
 | 
				
			||||||
  TrashController,
 | 
					  TrashController,
 | 
				
			||||||
 | 
					  UserAdminController,
 | 
				
			||||||
  UserController,
 | 
					  UserController,
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
				
			|||||||
@ -10,7 +10,7 @@ import {
 | 
				
			|||||||
  OAuthCallbackDto,
 | 
					  OAuthCallbackDto,
 | 
				
			||||||
  OAuthConfigDto,
 | 
					  OAuthConfigDto,
 | 
				
			||||||
} from 'src/dtos/auth.dto';
 | 
					} from 'src/dtos/auth.dto';
 | 
				
			||||||
import { UserResponseDto } from 'src/dtos/user.dto';
 | 
					import { UserAdminResponseDto } from 'src/dtos/user.dto';
 | 
				
			||||||
import { Auth, Authenticated, GetLoginDetails } from 'src/middleware/auth.guard';
 | 
					import { Auth, Authenticated, GetLoginDetails } from 'src/middleware/auth.guard';
 | 
				
			||||||
import { AuthService, LoginDetails } from 'src/services/auth.service';
 | 
					import { AuthService, LoginDetails } from 'src/services/auth.service';
 | 
				
			||||||
import { respondWithCookie } from 'src/utils/response';
 | 
					import { respondWithCookie } from 'src/utils/response';
 | 
				
			||||||
@ -53,13 +53,13 @@ export class OAuthController {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  @Post('link')
 | 
					  @Post('link')
 | 
				
			||||||
  @Authenticated()
 | 
					  @Authenticated()
 | 
				
			||||||
  linkOAuthAccount(@Auth() auth: AuthDto, @Body() dto: OAuthCallbackDto): Promise<UserResponseDto> {
 | 
					  linkOAuthAccount(@Auth() auth: AuthDto, @Body() dto: OAuthCallbackDto): Promise<UserAdminResponseDto> {
 | 
				
			||||||
    return this.service.link(auth, dto);
 | 
					    return this.service.link(auth, dto);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Post('unlink')
 | 
					  @Post('unlink')
 | 
				
			||||||
  @Authenticated()
 | 
					  @Authenticated()
 | 
				
			||||||
  unlinkOAuthAccount(@Auth() auth: AuthDto): Promise<UserResponseDto> {
 | 
					  unlinkOAuthAccount(@Auth() auth: AuthDto): Promise<UserAdminResponseDto> {
 | 
				
			||||||
    return this.service.unlink(auth);
 | 
					    return this.service.unlink(auth);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										63
									
								
								server/src/controllers/user-admin.controller.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								server/src/controllers/user-admin.controller.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					import { Body, Controller, Delete, Get, Param, Post, Put, Query } from '@nestjs/common';
 | 
				
			||||||
 | 
					import { ApiTags } from '@nestjs/swagger';
 | 
				
			||||||
 | 
					import { AuthDto } from 'src/dtos/auth.dto';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  UserAdminCreateDto,
 | 
				
			||||||
 | 
					  UserAdminDeleteDto,
 | 
				
			||||||
 | 
					  UserAdminResponseDto,
 | 
				
			||||||
 | 
					  UserAdminSearchDto,
 | 
				
			||||||
 | 
					  UserAdminUpdateDto,
 | 
				
			||||||
 | 
					} from 'src/dtos/user.dto';
 | 
				
			||||||
 | 
					import { Auth, Authenticated } from 'src/middleware/auth.guard';
 | 
				
			||||||
 | 
					import { UserAdminService } from 'src/services/user-admin.service';
 | 
				
			||||||
 | 
					import { UUIDParamDto } from 'src/validation';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@ApiTags('User')
 | 
				
			||||||
 | 
					@Controller('admin/users')
 | 
				
			||||||
 | 
					export class UserAdminController {
 | 
				
			||||||
 | 
					  constructor(private service: UserAdminService) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Get()
 | 
				
			||||||
 | 
					  @Authenticated({ admin: true })
 | 
				
			||||||
 | 
					  searchUsersAdmin(@Auth() auth: AuthDto, @Query() dto: UserAdminSearchDto): Promise<UserAdminResponseDto[]> {
 | 
				
			||||||
 | 
					    return this.service.search(auth, dto);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Post()
 | 
				
			||||||
 | 
					  @Authenticated({ admin: true })
 | 
				
			||||||
 | 
					  createUserAdmin(@Body() createUserDto: UserAdminCreateDto): Promise<UserAdminResponseDto> {
 | 
				
			||||||
 | 
					    return this.service.create(createUserDto);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Get(':id')
 | 
				
			||||||
 | 
					  @Authenticated({ admin: true })
 | 
				
			||||||
 | 
					  getUserAdmin(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<UserAdminResponseDto> {
 | 
				
			||||||
 | 
					    return this.service.get(auth, id);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Put(':id')
 | 
				
			||||||
 | 
					  @Authenticated({ admin: true })
 | 
				
			||||||
 | 
					  updateUserAdmin(
 | 
				
			||||||
 | 
					    @Auth() auth: AuthDto,
 | 
				
			||||||
 | 
					    @Param() { id }: UUIDParamDto,
 | 
				
			||||||
 | 
					    @Body() dto: UserAdminUpdateDto,
 | 
				
			||||||
 | 
					  ): Promise<UserAdminResponseDto> {
 | 
				
			||||||
 | 
					    return this.service.update(auth, id, dto);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Delete(':id')
 | 
				
			||||||
 | 
					  @Authenticated({ admin: true })
 | 
				
			||||||
 | 
					  deleteUserAdmin(
 | 
				
			||||||
 | 
					    @Auth() auth: AuthDto,
 | 
				
			||||||
 | 
					    @Param() { id }: UUIDParamDto,
 | 
				
			||||||
 | 
					    @Body() dto: UserAdminDeleteDto,
 | 
				
			||||||
 | 
					  ): Promise<UserAdminResponseDto> {
 | 
				
			||||||
 | 
					    return this.service.delete(auth, id, dto);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Post(':id/restore')
 | 
				
			||||||
 | 
					  @Authenticated({ admin: true })
 | 
				
			||||||
 | 
					  restoreUserAdmin(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<UserAdminResponseDto> {
 | 
				
			||||||
 | 
					    return this.service.restore(auth, id);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -10,7 +10,6 @@ import {
 | 
				
			|||||||
  Param,
 | 
					  Param,
 | 
				
			||||||
  Post,
 | 
					  Post,
 | 
				
			||||||
  Put,
 | 
					  Put,
 | 
				
			||||||
  Query,
 | 
					 | 
				
			||||||
  Res,
 | 
					  Res,
 | 
				
			||||||
  UploadedFile,
 | 
					  UploadedFile,
 | 
				
			||||||
  UseInterceptors,
 | 
					  UseInterceptors,
 | 
				
			||||||
@ -19,7 +18,7 @@ import { ApiBody, ApiConsumes, ApiTags } from '@nestjs/swagger';
 | 
				
			|||||||
import { NextFunction, Response } from 'express';
 | 
					import { NextFunction, Response } from 'express';
 | 
				
			||||||
import { AuthDto } from 'src/dtos/auth.dto';
 | 
					import { AuthDto } from 'src/dtos/auth.dto';
 | 
				
			||||||
import { CreateProfileImageDto, CreateProfileImageResponseDto } from 'src/dtos/user-profile.dto';
 | 
					import { CreateProfileImageDto, CreateProfileImageResponseDto } from 'src/dtos/user-profile.dto';
 | 
				
			||||||
import { CreateUserDto, DeleteUserDto, UpdateUserDto, UserResponseDto } from 'src/dtos/user.dto';
 | 
					import { UserAdminResponseDto, UserResponseDto, UserUpdateMeDto } from 'src/dtos/user.dto';
 | 
				
			||||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
 | 
					import { ILoggerRepository } from 'src/interfaces/logger.interface';
 | 
				
			||||||
import { Auth, Authenticated, FileResponse } from 'src/middleware/auth.guard';
 | 
					import { Auth, Authenticated, FileResponse } from 'src/middleware/auth.guard';
 | 
				
			||||||
import { FileUploadInterceptor, Route } from 'src/middleware/file-upload.interceptor';
 | 
					import { FileUploadInterceptor, Route } from 'src/middleware/file-upload.interceptor';
 | 
				
			||||||
@ -37,58 +36,28 @@ export class UserController {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  @Get()
 | 
					  @Get()
 | 
				
			||||||
  @Authenticated()
 | 
					  @Authenticated()
 | 
				
			||||||
  getAllUsers(@Auth() auth: AuthDto, @Query('isAll') isAll: boolean): Promise<UserResponseDto[]> {
 | 
					  searchUsers(): Promise<UserResponseDto[]> {
 | 
				
			||||||
    return this.service.getAll(auth, isAll);
 | 
					    return this.service.search();
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Post()
 | 
					 | 
				
			||||||
  @Authenticated({ admin: true })
 | 
					 | 
				
			||||||
  createUser(@Body() createUserDto: CreateUserDto): Promise<UserResponseDto> {
 | 
					 | 
				
			||||||
    return this.service.create(createUserDto);
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Get('me')
 | 
					  @Get('me')
 | 
				
			||||||
  @Authenticated()
 | 
					  @Authenticated()
 | 
				
			||||||
  getMyUserInfo(@Auth() auth: AuthDto): Promise<UserResponseDto> {
 | 
					  getMyUser(@Auth() auth: AuthDto): UserAdminResponseDto {
 | 
				
			||||||
    return this.service.getMe(auth);
 | 
					    return this.service.getMe(auth);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Put('me')
 | 
				
			||||||
 | 
					  @Authenticated()
 | 
				
			||||||
 | 
					  updateMyUser(@Auth() auth: AuthDto, @Body() dto: UserUpdateMeDto): Promise<UserAdminResponseDto> {
 | 
				
			||||||
 | 
					    return this.service.updateMe(auth, dto);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Get(':id')
 | 
					  @Get(':id')
 | 
				
			||||||
  @Authenticated()
 | 
					  @Authenticated()
 | 
				
			||||||
  getUserById(@Param() { id }: UUIDParamDto): Promise<UserResponseDto> {
 | 
					  getUser(@Param() { id }: UUIDParamDto): Promise<UserResponseDto> {
 | 
				
			||||||
    return this.service.get(id);
 | 
					    return this.service.get(id);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Delete('profile-image')
 | 
					 | 
				
			||||||
  @HttpCode(HttpStatus.NO_CONTENT)
 | 
					 | 
				
			||||||
  @Authenticated()
 | 
					 | 
				
			||||||
  deleteProfileImage(@Auth() auth: AuthDto): Promise<void> {
 | 
					 | 
				
			||||||
    return this.service.deleteProfileImage(auth);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Delete(':id')
 | 
					 | 
				
			||||||
  @Authenticated({ admin: true })
 | 
					 | 
				
			||||||
  deleteUser(
 | 
					 | 
				
			||||||
    @Auth() auth: AuthDto,
 | 
					 | 
				
			||||||
    @Param() { id }: UUIDParamDto,
 | 
					 | 
				
			||||||
    @Body() dto: DeleteUserDto,
 | 
					 | 
				
			||||||
  ): Promise<UserResponseDto> {
 | 
					 | 
				
			||||||
    return this.service.delete(auth, id, dto);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Post(':id/restore')
 | 
					 | 
				
			||||||
  @Authenticated({ admin: true })
 | 
					 | 
				
			||||||
  restoreUser(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<UserResponseDto> {
 | 
					 | 
				
			||||||
    return this.service.restore(auth, id);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // TODO: replace with @Put(':id')
 | 
					 | 
				
			||||||
  @Put()
 | 
					 | 
				
			||||||
  @Authenticated()
 | 
					 | 
				
			||||||
  updateUser(@Auth() auth: AuthDto, @Body() updateUserDto: UpdateUserDto): Promise<UserResponseDto> {
 | 
					 | 
				
			||||||
    return this.service.update(auth, updateUserDto);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @UseInterceptors(FileUploadInterceptor)
 | 
					  @UseInterceptors(FileUploadInterceptor)
 | 
				
			||||||
  @ApiConsumes('multipart/form-data')
 | 
					  @ApiConsumes('multipart/form-data')
 | 
				
			||||||
  @ApiBody({ description: 'A new avatar for the user', type: CreateProfileImageDto })
 | 
					  @ApiBody({ description: 'A new avatar for the user', type: CreateProfileImageDto })
 | 
				
			||||||
@ -101,6 +70,13 @@ export class UserController {
 | 
				
			|||||||
    return this.service.createProfileImage(auth, fileInfo);
 | 
					    return this.service.createProfileImage(auth, fileInfo);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Delete('profile-image')
 | 
				
			||||||
 | 
					  @HttpCode(HttpStatus.NO_CONTENT)
 | 
				
			||||||
 | 
					  @Authenticated()
 | 
				
			||||||
 | 
					  deleteProfileImage(@Auth() auth: AuthDto): Promise<void> {
 | 
				
			||||||
 | 
					    return this.service.deleteProfileImage(auth);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Get(':id/profile-image')
 | 
					  @Get(':id/profile-image')
 | 
				
			||||||
  @FileResponse()
 | 
					  @FileResponse()
 | 
				
			||||||
  @Authenticated()
 | 
					  @Authenticated()
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,6 @@
 | 
				
			|||||||
import { BadRequestException, ForbiddenException } from '@nestjs/common';
 | 
					import { BadRequestException } from '@nestjs/common';
 | 
				
			||||||
import sanitize from 'sanitize-filename';
 | 
					import sanitize from 'sanitize-filename';
 | 
				
			||||||
import { SALT_ROUNDS } from 'src/constants';
 | 
					import { SALT_ROUNDS } from 'src/constants';
 | 
				
			||||||
import { UserResponseDto } from 'src/dtos/user.dto';
 | 
					 | 
				
			||||||
import { UserEntity } from 'src/entities/user.entity';
 | 
					import { UserEntity } from 'src/entities/user.entity';
 | 
				
			||||||
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
 | 
					import { ICryptoRepository } from 'src/interfaces/crypto.interface';
 | 
				
			||||||
import { IUserRepository } from 'src/interfaces/user.interface';
 | 
					import { IUserRepository } from 'src/interfaces/user.interface';
 | 
				
			||||||
@ -26,46 +25,6 @@ export class UserCore {
 | 
				
			|||||||
    instance = null;
 | 
					    instance = null;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // TODO: move auth related checks to the service layer
 | 
					 | 
				
			||||||
  async updateUser(user: UserEntity | UserResponseDto, id: string, dto: Partial<UserEntity>): Promise<UserEntity> {
 | 
					 | 
				
			||||||
    if (!user.isAdmin && user.id !== id) {
 | 
					 | 
				
			||||||
      throw new ForbiddenException('You are not allowed to update this user');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!user.isAdmin) {
 | 
					 | 
				
			||||||
      // Users can never update the isAdmin property.
 | 
					 | 
				
			||||||
      delete dto.isAdmin;
 | 
					 | 
				
			||||||
      delete dto.storageLabel;
 | 
					 | 
				
			||||||
    } else if (dto.isAdmin && user.id !== id) {
 | 
					 | 
				
			||||||
      // Admin cannot create another admin.
 | 
					 | 
				
			||||||
      throw new BadRequestException('The server already has an admin');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (dto.email) {
 | 
					 | 
				
			||||||
      const duplicate = await this.userRepository.getByEmail(dto.email);
 | 
					 | 
				
			||||||
      if (duplicate && duplicate.id !== id) {
 | 
					 | 
				
			||||||
        throw new BadRequestException('Email already in use by another account');
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (dto.storageLabel) {
 | 
					 | 
				
			||||||
      const duplicate = await this.userRepository.getByStorageLabel(dto.storageLabel);
 | 
					 | 
				
			||||||
      if (duplicate && duplicate.id !== id) {
 | 
					 | 
				
			||||||
        throw new BadRequestException('Storage label already in use by another account');
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (dto.password) {
 | 
					 | 
				
			||||||
      dto.password = await this.cryptoRepository.hashBcrypt(dto.password, SALT_ROUNDS);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (dto.storageLabel === '') {
 | 
					 | 
				
			||||||
      dto.storageLabel = null;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return this.userRepository.update(id, { ...dto, updatedAt: new Date() });
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  async createUser(dto: Partial<UserEntity> & { email: string }): Promise<UserEntity> {
 | 
					  async createUser(dto: Partial<UserEntity> & { email: string }): Promise<UserEntity> {
 | 
				
			||||||
    const user = await this.userRepository.getByEmail(dto.email);
 | 
					    const user = await this.userRepository.getByEmail(dto.email);
 | 
				
			||||||
    if (user) {
 | 
					    if (user) {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
import { ApiProperty } from '@nestjs/swagger';
 | 
					import { ApiProperty } from '@nestjs/swagger';
 | 
				
			||||||
import { IsEnum, IsNotEmpty, IsString, ValidateIf } from 'class-validator';
 | 
					import { IsEnum, IsNotEmpty, IsString, ValidateIf } from 'class-validator';
 | 
				
			||||||
import { UserDto, mapSimpleUser } from 'src/dtos/user.dto';
 | 
					import { UserResponseDto, mapUser } from 'src/dtos/user.dto';
 | 
				
			||||||
import { ActivityEntity } from 'src/entities/activity.entity';
 | 
					import { ActivityEntity } from 'src/entities/activity.entity';
 | 
				
			||||||
import { Optional, ValidateUUID } from 'src/validation';
 | 
					import { Optional, ValidateUUID } from 'src/validation';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -20,7 +20,7 @@ export class ActivityResponseDto {
 | 
				
			|||||||
  id!: string;
 | 
					  id!: string;
 | 
				
			||||||
  createdAt!: Date;
 | 
					  createdAt!: Date;
 | 
				
			||||||
  type!: ReactionType;
 | 
					  type!: ReactionType;
 | 
				
			||||||
  user!: UserDto;
 | 
					  user!: UserResponseDto;
 | 
				
			||||||
  assetId!: string | null;
 | 
					  assetId!: string | null;
 | 
				
			||||||
  comment?: string | null;
 | 
					  comment?: string | null;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -73,6 +73,6 @@ export function mapActivity(activity: ActivityEntity): ActivityResponseDto {
 | 
				
			|||||||
    createdAt: activity.createdAt,
 | 
					    createdAt: activity.createdAt,
 | 
				
			||||||
    comment: activity.comment,
 | 
					    comment: activity.comment,
 | 
				
			||||||
    type: activity.isLiked ? ReactionType.LIKE : ReactionType.COMMENT,
 | 
					    type: activity.isLiked ? ReactionType.LIKE : ReactionType.COMMENT,
 | 
				
			||||||
    user: mapSimpleUser(activity.user),
 | 
					    user: mapUser(activity.user),
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,12 +1,12 @@
 | 
				
			|||||||
import { plainToInstance } from 'class-transformer';
 | 
					import { plainToInstance } from 'class-transformer';
 | 
				
			||||||
import { validate } from 'class-validator';
 | 
					import { validate } from 'class-validator';
 | 
				
			||||||
import { CreateUserDto, CreateUserOAuthDto, UpdateUserDto } from 'src/dtos/user.dto';
 | 
					import { UserAdminCreateDto, UserUpdateMeDto } from 'src/dtos/user.dto';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('update user DTO', () => {
 | 
					describe('update user DTO', () => {
 | 
				
			||||||
  it('should allow emails without a tld', async () => {
 | 
					  it('should allow emails without a tld', async () => {
 | 
				
			||||||
    const someEmail = 'test@test';
 | 
					    const someEmail = 'test@test';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const dto = plainToInstance(UpdateUserDto, {
 | 
					    const dto = plainToInstance(UserUpdateMeDto, {
 | 
				
			||||||
      email: someEmail,
 | 
					      email: someEmail,
 | 
				
			||||||
      id: '3fe388e4-2078-44d7-b36c-39d9dee3a657',
 | 
					      id: '3fe388e4-2078-44d7-b36c-39d9dee3a657',
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@ -18,22 +18,22 @@ describe('update user DTO', () => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
describe('create user DTO', () => {
 | 
					describe('create user DTO', () => {
 | 
				
			||||||
  it('validates the email', async () => {
 | 
					  it('validates the email', async () => {
 | 
				
			||||||
    const params: Partial<CreateUserDto> = {
 | 
					    const params: Partial<UserAdminCreateDto> = {
 | 
				
			||||||
      email: undefined,
 | 
					      email: undefined,
 | 
				
			||||||
      password: 'password',
 | 
					      password: 'password',
 | 
				
			||||||
      name: 'name',
 | 
					      name: 'name',
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    let dto: CreateUserDto = plainToInstance(CreateUserDto, params);
 | 
					    let dto: UserAdminCreateDto = plainToInstance(UserAdminCreateDto, params);
 | 
				
			||||||
    let errors = await validate(dto);
 | 
					    let errors = await validate(dto);
 | 
				
			||||||
    expect(errors).toHaveLength(1);
 | 
					    expect(errors).toHaveLength(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    params.email = 'invalid email';
 | 
					    params.email = 'invalid email';
 | 
				
			||||||
    dto = plainToInstance(CreateUserDto, params);
 | 
					    dto = plainToInstance(UserAdminCreateDto, params);
 | 
				
			||||||
    errors = await validate(dto);
 | 
					    errors = await validate(dto);
 | 
				
			||||||
    expect(errors).toHaveLength(1);
 | 
					    expect(errors).toHaveLength(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    params.email = 'valid@email.com';
 | 
					    params.email = 'valid@email.com';
 | 
				
			||||||
    dto = plainToInstance(CreateUserDto, params);
 | 
					    dto = plainToInstance(UserAdminCreateDto, params);
 | 
				
			||||||
    errors = await validate(dto);
 | 
					    errors = await validate(dto);
 | 
				
			||||||
    expect(errors).toHaveLength(0);
 | 
					    expect(errors).toHaveLength(0);
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
@ -41,7 +41,7 @@ describe('create user DTO', () => {
 | 
				
			|||||||
  it('should allow emails without a tld', async () => {
 | 
					  it('should allow emails without a tld', async () => {
 | 
				
			||||||
    const someEmail = 'test@test';
 | 
					    const someEmail = 'test@test';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const dto = plainToInstance(CreateUserDto, {
 | 
					    const dto = plainToInstance(UserAdminCreateDto, {
 | 
				
			||||||
      email: someEmail,
 | 
					      email: someEmail,
 | 
				
			||||||
      password: 'some password',
 | 
					      password: 'some password',
 | 
				
			||||||
      name: 'some name',
 | 
					      name: 'some name',
 | 
				
			||||||
@ -51,18 +51,3 @@ describe('create user DTO', () => {
 | 
				
			|||||||
    expect(dto.email).toEqual(someEmail);
 | 
					    expect(dto.email).toEqual(someEmail);
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					 | 
				
			||||||
describe('create user oauth DTO', () => {
 | 
					 | 
				
			||||||
  it('should allow emails without a tld', async () => {
 | 
					 | 
				
			||||||
    const someEmail = 'test@test';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const dto = plainToInstance(CreateUserOAuthDto, {
 | 
					 | 
				
			||||||
      email: someEmail,
 | 
					 | 
				
			||||||
      oauthId: 'some oauth id',
 | 
					 | 
				
			||||||
      name: 'some name',
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
    const errors = await validate(dto);
 | 
					 | 
				
			||||||
    expect(errors).toHaveLength(0);
 | 
					 | 
				
			||||||
    expect(dto.email).toEqual(someEmail);
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -1,12 +1,63 @@
 | 
				
			|||||||
import { ApiProperty } from '@nestjs/swagger';
 | 
					import { ApiProperty } from '@nestjs/swagger';
 | 
				
			||||||
import { Transform } from 'class-transformer';
 | 
					import { Transform } from 'class-transformer';
 | 
				
			||||||
import { IsBoolean, IsEmail, IsEnum, IsNotEmpty, IsNumber, IsPositive, IsString, IsUUID } from 'class-validator';
 | 
					import { IsBoolean, IsEmail, IsEnum, IsNotEmpty, IsNumber, IsPositive, IsString } from 'class-validator';
 | 
				
			||||||
import { UserAvatarColor } from 'src/entities/user-metadata.entity';
 | 
					import { UserAvatarColor } from 'src/entities/user-metadata.entity';
 | 
				
			||||||
import { UserEntity, UserStatus } from 'src/entities/user.entity';
 | 
					import { UserEntity, UserStatus } from 'src/entities/user.entity';
 | 
				
			||||||
import { getPreferences } from 'src/utils/preferences';
 | 
					import { getPreferences } from 'src/utils/preferences';
 | 
				
			||||||
import { Optional, ValidateBoolean, toEmail, toSanitized } from 'src/validation';
 | 
					import { Optional, ValidateBoolean, toEmail, toSanitized } from 'src/validation';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class CreateUserDto {
 | 
					export class UserUpdateMeDto {
 | 
				
			||||||
 | 
					  @Optional()
 | 
				
			||||||
 | 
					  @IsEmail({ require_tld: false })
 | 
				
			||||||
 | 
					  @Transform(toEmail)
 | 
				
			||||||
 | 
					  email?: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // TODO: migrate to the other change password endpoint
 | 
				
			||||||
 | 
					  @Optional()
 | 
				
			||||||
 | 
					  @IsNotEmpty()
 | 
				
			||||||
 | 
					  @IsString()
 | 
				
			||||||
 | 
					  password?: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Optional()
 | 
				
			||||||
 | 
					  @IsString()
 | 
				
			||||||
 | 
					  @IsNotEmpty()
 | 
				
			||||||
 | 
					  name?: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @ValidateBoolean({ optional: true })
 | 
				
			||||||
 | 
					  memoriesEnabled?: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Optional()
 | 
				
			||||||
 | 
					  @IsEnum(UserAvatarColor)
 | 
				
			||||||
 | 
					  @ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor })
 | 
				
			||||||
 | 
					  avatarColor?: UserAvatarColor;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class UserResponseDto {
 | 
				
			||||||
 | 
					  id!: string;
 | 
				
			||||||
 | 
					  name!: string;
 | 
				
			||||||
 | 
					  email!: string;
 | 
				
			||||||
 | 
					  profileImagePath!: string;
 | 
				
			||||||
 | 
					  @IsEnum(UserAvatarColor)
 | 
				
			||||||
 | 
					  @ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor })
 | 
				
			||||||
 | 
					  avatarColor!: UserAvatarColor;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const mapUser = (entity: UserEntity): UserResponseDto => {
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    id: entity.id,
 | 
				
			||||||
 | 
					    email: entity.email,
 | 
				
			||||||
 | 
					    name: entity.name,
 | 
				
			||||||
 | 
					    profileImagePath: entity.profileImagePath,
 | 
				
			||||||
 | 
					    avatarColor: getPreferences(entity).avatar.color,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class UserAdminSearchDto {
 | 
				
			||||||
 | 
					  @ValidateBoolean({ optional: true })
 | 
				
			||||||
 | 
					  withDeleted?: boolean;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class UserAdminCreateDto {
 | 
				
			||||||
  @IsEmail({ require_tld: false })
 | 
					  @IsEmail({ require_tld: false })
 | 
				
			||||||
  @Transform(toEmail)
 | 
					  @Transform(toEmail)
 | 
				
			||||||
  email!: string;
 | 
					  email!: string;
 | 
				
			||||||
@ -41,23 +92,7 @@ export class CreateUserDto {
 | 
				
			|||||||
  notify?: boolean;
 | 
					  notify?: boolean;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class CreateUserOAuthDto {
 | 
					export class UserAdminUpdateDto {
 | 
				
			||||||
  @IsEmail({ require_tld: false })
 | 
					 | 
				
			||||||
  @Transform(({ value }) => value?.toLowerCase())
 | 
					 | 
				
			||||||
  email!: string;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @IsNotEmpty()
 | 
					 | 
				
			||||||
  oauthId!: string;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  name?: string;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class DeleteUserDto {
 | 
					 | 
				
			||||||
  @ValidateBoolean({ optional: true })
 | 
					 | 
				
			||||||
  force?: boolean;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export class UpdateUserDto {
 | 
					 | 
				
			||||||
  @Optional()
 | 
					  @Optional()
 | 
				
			||||||
  @IsEmail({ require_tld: false })
 | 
					  @IsEmail({ require_tld: false })
 | 
				
			||||||
  @Transform(toEmail)
 | 
					  @Transform(toEmail)
 | 
				
			||||||
@ -73,18 +108,10 @@ export class UpdateUserDto {
 | 
				
			|||||||
  @IsNotEmpty()
 | 
					  @IsNotEmpty()
 | 
				
			||||||
  name?: string;
 | 
					  name?: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Optional()
 | 
					  @Optional({ nullable: true })
 | 
				
			||||||
  @IsString()
 | 
					  @IsString()
 | 
				
			||||||
  @Transform(toSanitized)
 | 
					  @Transform(toSanitized)
 | 
				
			||||||
  storageLabel?: string;
 | 
					  storageLabel?: string | null;
 | 
				
			||||||
 | 
					 | 
				
			||||||
  @IsNotEmpty()
 | 
					 | 
				
			||||||
  @IsUUID('4')
 | 
					 | 
				
			||||||
  @ApiProperty({ format: 'uuid' })
 | 
					 | 
				
			||||||
  id!: string;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @ValidateBoolean({ optional: true })
 | 
					 | 
				
			||||||
  isAdmin?: boolean;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @ValidateBoolean({ optional: true })
 | 
					  @ValidateBoolean({ optional: true })
 | 
				
			||||||
  shouldChangePassword?: boolean;
 | 
					  shouldChangePassword?: boolean;
 | 
				
			||||||
@ -104,17 +131,12 @@ export class UpdateUserDto {
 | 
				
			|||||||
  quotaSizeInBytes?: number | null;
 | 
					  quotaSizeInBytes?: number | null;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class UserDto {
 | 
					export class UserAdminDeleteDto {
 | 
				
			||||||
  id!: string;
 | 
					  @ValidateBoolean({ optional: true })
 | 
				
			||||||
  name!: string;
 | 
					  force?: boolean;
 | 
				
			||||||
  email!: string;
 | 
					 | 
				
			||||||
  profileImagePath!: string;
 | 
					 | 
				
			||||||
  @IsEnum(UserAvatarColor)
 | 
					 | 
				
			||||||
  @ApiProperty({ enumName: 'UserAvatarColor', enum: UserAvatarColor })
 | 
					 | 
				
			||||||
  avatarColor!: UserAvatarColor;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class UserResponseDto extends UserDto {
 | 
					export class UserAdminResponseDto extends UserResponseDto {
 | 
				
			||||||
  storageLabel!: string | null;
 | 
					  storageLabel!: string | null;
 | 
				
			||||||
  shouldChangePassword!: boolean;
 | 
					  shouldChangePassword!: boolean;
 | 
				
			||||||
  isAdmin!: boolean;
 | 
					  isAdmin!: boolean;
 | 
				
			||||||
@ -131,19 +153,9 @@ export class UserResponseDto extends UserDto {
 | 
				
			|||||||
  status!: string;
 | 
					  status!: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const mapSimpleUser = (entity: UserEntity): UserDto => {
 | 
					export function mapUserAdmin(entity: UserEntity): UserAdminResponseDto {
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    id: entity.id,
 | 
					    ...mapUser(entity),
 | 
				
			||||||
    email: entity.email,
 | 
					 | 
				
			||||||
    name: entity.name,
 | 
					 | 
				
			||||||
    profileImagePath: entity.profileImagePath,
 | 
					 | 
				
			||||||
    avatarColor: getPreferences(entity).avatar.color,
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function mapUser(entity: UserEntity): UserResponseDto {
 | 
					 | 
				
			||||||
  return {
 | 
					 | 
				
			||||||
    ...mapSimpleUser(entity),
 | 
					 | 
				
			||||||
    storageLabel: entity.storageLabel,
 | 
					    storageLabel: entity.storageLabel,
 | 
				
			||||||
    shouldChangePassword: entity.shouldChangePassword,
 | 
					    shouldChangePassword: entity.shouldChangePassword,
 | 
				
			||||||
    isAdmin: entity.isAdmin,
 | 
					    isAdmin: entity.isAdmin,
 | 
				
			||||||
 | 
				
			|||||||
@ -22,13 +22,17 @@ FROM
 | 
				
			|||||||
      "APIKeyEntity__APIKeyEntity_user"."status" AS "APIKeyEntity__APIKeyEntity_user_status",
 | 
					      "APIKeyEntity__APIKeyEntity_user"."status" AS "APIKeyEntity__APIKeyEntity_user_status",
 | 
				
			||||||
      "APIKeyEntity__APIKeyEntity_user"."updatedAt" AS "APIKeyEntity__APIKeyEntity_user_updatedAt",
 | 
					      "APIKeyEntity__APIKeyEntity_user"."updatedAt" AS "APIKeyEntity__APIKeyEntity_user_updatedAt",
 | 
				
			||||||
      "APIKeyEntity__APIKeyEntity_user"."quotaSizeInBytes" AS "APIKeyEntity__APIKeyEntity_user_quotaSizeInBytes",
 | 
					      "APIKeyEntity__APIKeyEntity_user"."quotaSizeInBytes" AS "APIKeyEntity__APIKeyEntity_user_quotaSizeInBytes",
 | 
				
			||||||
      "APIKeyEntity__APIKeyEntity_user"."quotaUsageInBytes" AS "APIKeyEntity__APIKeyEntity_user_quotaUsageInBytes"
 | 
					      "APIKeyEntity__APIKeyEntity_user"."quotaUsageInBytes" AS "APIKeyEntity__APIKeyEntity_user_quotaUsageInBytes",
 | 
				
			||||||
 | 
					      "7f5f7a38bf327bfbbf826778460704c9a50fe6f4"."userId" AS "7f5f7a38bf327bfbbf826778460704c9a50fe6f4_userId",
 | 
				
			||||||
 | 
					      "7f5f7a38bf327bfbbf826778460704c9a50fe6f4"."key" AS "7f5f7a38bf327bfbbf826778460704c9a50fe6f4_key",
 | 
				
			||||||
 | 
					      "7f5f7a38bf327bfbbf826778460704c9a50fe6f4"."value" AS "7f5f7a38bf327bfbbf826778460704c9a50fe6f4_value"
 | 
				
			||||||
    FROM
 | 
					    FROM
 | 
				
			||||||
      "api_keys" "APIKeyEntity"
 | 
					      "api_keys" "APIKeyEntity"
 | 
				
			||||||
      LEFT JOIN "users" "APIKeyEntity__APIKeyEntity_user" ON "APIKeyEntity__APIKeyEntity_user"."id" = "APIKeyEntity"."userId"
 | 
					      LEFT JOIN "users" "APIKeyEntity__APIKeyEntity_user" ON "APIKeyEntity__APIKeyEntity_user"."id" = "APIKeyEntity"."userId"
 | 
				
			||||||
      AND (
 | 
					      AND (
 | 
				
			||||||
        "APIKeyEntity__APIKeyEntity_user"."deletedAt" IS NULL
 | 
					        "APIKeyEntity__APIKeyEntity_user"."deletedAt" IS NULL
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
 | 
					      LEFT JOIN "user_metadata" "7f5f7a38bf327bfbbf826778460704c9a50fe6f4" ON "7f5f7a38bf327bfbbf826778460704c9a50fe6f4"."userId" = "APIKeyEntity__APIKeyEntity_user"."id"
 | 
				
			||||||
    WHERE
 | 
					    WHERE
 | 
				
			||||||
      (("APIKeyEntity"."key" = $1))
 | 
					      (("APIKeyEntity"."key" = $1))
 | 
				
			||||||
  ) "distinctAlias"
 | 
					  ) "distinctAlias"
 | 
				
			||||||
 | 
				
			|||||||
@ -38,13 +38,17 @@ FROM
 | 
				
			|||||||
      "SessionEntity__SessionEntity_user"."status" AS "SessionEntity__SessionEntity_user_status",
 | 
					      "SessionEntity__SessionEntity_user"."status" AS "SessionEntity__SessionEntity_user_status",
 | 
				
			||||||
      "SessionEntity__SessionEntity_user"."updatedAt" AS "SessionEntity__SessionEntity_user_updatedAt",
 | 
					      "SessionEntity__SessionEntity_user"."updatedAt" AS "SessionEntity__SessionEntity_user_updatedAt",
 | 
				
			||||||
      "SessionEntity__SessionEntity_user"."quotaSizeInBytes" AS "SessionEntity__SessionEntity_user_quotaSizeInBytes",
 | 
					      "SessionEntity__SessionEntity_user"."quotaSizeInBytes" AS "SessionEntity__SessionEntity_user_quotaSizeInBytes",
 | 
				
			||||||
      "SessionEntity__SessionEntity_user"."quotaUsageInBytes" AS "SessionEntity__SessionEntity_user_quotaUsageInBytes"
 | 
					      "SessionEntity__SessionEntity_user"."quotaUsageInBytes" AS "SessionEntity__SessionEntity_user_quotaUsageInBytes",
 | 
				
			||||||
 | 
					      "469e6aa7ff79eff78f8441f91ba15bb07d3634dd"."userId" AS "469e6aa7ff79eff78f8441f91ba15bb07d3634dd_userId",
 | 
				
			||||||
 | 
					      "469e6aa7ff79eff78f8441f91ba15bb07d3634dd"."key" AS "469e6aa7ff79eff78f8441f91ba15bb07d3634dd_key",
 | 
				
			||||||
 | 
					      "469e6aa7ff79eff78f8441f91ba15bb07d3634dd"."value" AS "469e6aa7ff79eff78f8441f91ba15bb07d3634dd_value"
 | 
				
			||||||
    FROM
 | 
					    FROM
 | 
				
			||||||
      "sessions" "SessionEntity"
 | 
					      "sessions" "SessionEntity"
 | 
				
			||||||
      LEFT JOIN "users" "SessionEntity__SessionEntity_user" ON "SessionEntity__SessionEntity_user"."id" = "SessionEntity"."userId"
 | 
					      LEFT JOIN "users" "SessionEntity__SessionEntity_user" ON "SessionEntity__SessionEntity_user"."id" = "SessionEntity"."userId"
 | 
				
			||||||
      AND (
 | 
					      AND (
 | 
				
			||||||
        "SessionEntity__SessionEntity_user"."deletedAt" IS NULL
 | 
					        "SessionEntity__SessionEntity_user"."deletedAt" IS NULL
 | 
				
			||||||
      )
 | 
					      )
 | 
				
			||||||
 | 
					      LEFT JOIN "user_metadata" "469e6aa7ff79eff78f8441f91ba15bb07d3634dd" ON "469e6aa7ff79eff78f8441f91ba15bb07d3634dd"."userId" = "SessionEntity__SessionEntity_user"."id"
 | 
				
			||||||
    WHERE
 | 
					    WHERE
 | 
				
			||||||
      (("SessionEntity"."token" = $1))
 | 
					      (("SessionEntity"."token" = $1))
 | 
				
			||||||
  ) "distinctAlias"
 | 
					  ) "distinctAlias"
 | 
				
			||||||
 | 
				
			|||||||
@ -34,7 +34,9 @@ export class ApiKeyRepository implements IKeyRepository {
 | 
				
			|||||||
      },
 | 
					      },
 | 
				
			||||||
      where: { key: hashedToken },
 | 
					      where: { key: hashedToken },
 | 
				
			||||||
      relations: {
 | 
					      relations: {
 | 
				
			||||||
        user: true,
 | 
					        user: {
 | 
				
			||||||
 | 
					          metadata: true,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -18,7 +18,14 @@ export class SessionRepository implements ISessionRepository {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  @GenerateSql({ params: [DummyValue.STRING] })
 | 
					  @GenerateSql({ params: [DummyValue.STRING] })
 | 
				
			||||||
  getByToken(token: string): Promise<SessionEntity | null> {
 | 
					  getByToken(token: string): Promise<SessionEntity | null> {
 | 
				
			||||||
    return this.repository.findOne({ where: { token }, relations: { user: true } });
 | 
					    return this.repository.findOne({
 | 
				
			||||||
 | 
					      where: { token },
 | 
				
			||||||
 | 
					      relations: {
 | 
				
			||||||
 | 
					        user: {
 | 
				
			||||||
 | 
					          metadata: true,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  getByUserId(userId: string): Promise<SessionEntity[]> {
 | 
					  getByUserId(userId: string): Promise<SessionEntity[]> {
 | 
				
			||||||
 | 
				
			|||||||
@ -138,6 +138,7 @@ describe('AuthService', () => {
 | 
				
			|||||||
        email: 'test@immich.com',
 | 
					        email: 'test@immich.com',
 | 
				
			||||||
        password: 'hash-password',
 | 
					        password: 'hash-password',
 | 
				
			||||||
      } as UserEntity);
 | 
					      } as UserEntity);
 | 
				
			||||||
 | 
					      userMock.update.mockResolvedValue(userStub.user1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      await sut.changePassword(auth, dto);
 | 
					      await sut.changePassword(auth, dto);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -11,7 +11,7 @@ import { DateTime } from 'luxon';
 | 
				
			|||||||
import { IncomingHttpHeaders } from 'node:http';
 | 
					import { IncomingHttpHeaders } from 'node:http';
 | 
				
			||||||
import { ClientMetadata, Issuer, UserinfoResponse, custom, generators } from 'openid-client';
 | 
					import { ClientMetadata, Issuer, UserinfoResponse, custom, generators } from 'openid-client';
 | 
				
			||||||
import { SystemConfig } from 'src/config';
 | 
					import { SystemConfig } from 'src/config';
 | 
				
			||||||
import { AuthType, LOGIN_URL, MOBILE_REDIRECT } from 'src/constants';
 | 
					import { AuthType, LOGIN_URL, MOBILE_REDIRECT, SALT_ROUNDS } from 'src/constants';
 | 
				
			||||||
import { SystemConfigCore } from 'src/cores/system-config.core';
 | 
					import { SystemConfigCore } from 'src/cores/system-config.core';
 | 
				
			||||||
import { UserCore } from 'src/cores/user.core';
 | 
					import { UserCore } from 'src/cores/user.core';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
@ -27,7 +27,7 @@ import {
 | 
				
			|||||||
  SignUpDto,
 | 
					  SignUpDto,
 | 
				
			||||||
  mapLoginResponse,
 | 
					  mapLoginResponse,
 | 
				
			||||||
} from 'src/dtos/auth.dto';
 | 
					} from 'src/dtos/auth.dto';
 | 
				
			||||||
import { UserResponseDto, mapUser } from 'src/dtos/user.dto';
 | 
					import { UserAdminResponseDto, mapUserAdmin } from 'src/dtos/user.dto';
 | 
				
			||||||
import { UserEntity } from 'src/entities/user.entity';
 | 
					import { UserEntity } from 'src/entities/user.entity';
 | 
				
			||||||
import { IKeyRepository } from 'src/interfaces/api-key.interface';
 | 
					import { IKeyRepository } from 'src/interfaces/api-key.interface';
 | 
				
			||||||
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
 | 
					import { ICryptoRepository } from 'src/interfaces/crypto.interface';
 | 
				
			||||||
@ -109,7 +109,7 @@ export class AuthService {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async changePassword(auth: AuthDto, dto: ChangePasswordDto) {
 | 
					  async changePassword(auth: AuthDto, dto: ChangePasswordDto): Promise<UserAdminResponseDto> {
 | 
				
			||||||
    const { password, newPassword } = dto;
 | 
					    const { password, newPassword } = dto;
 | 
				
			||||||
    const user = await this.userRepository.getByEmail(auth.user.email, true);
 | 
					    const user = await this.userRepository.getByEmail(auth.user.email, true);
 | 
				
			||||||
    if (!user) {
 | 
					    if (!user) {
 | 
				
			||||||
@ -121,10 +121,14 @@ export class AuthService {
 | 
				
			|||||||
      throw new BadRequestException('Wrong password');
 | 
					      throw new BadRequestException('Wrong password');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return this.userCore.updateUser(auth.user, auth.user.id, { password: newPassword });
 | 
					    const hashedPassword = await this.cryptoRepository.hashBcrypt(newPassword, SALT_ROUNDS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const updatedUser = await this.userRepository.update(user.id, { password: hashedPassword });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return mapUserAdmin(updatedUser);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async adminSignUp(dto: SignUpDto): Promise<UserResponseDto> {
 | 
					  async adminSignUp(dto: SignUpDto): Promise<UserAdminResponseDto> {
 | 
				
			||||||
    const adminUser = await this.userRepository.getAdmin();
 | 
					    const adminUser = await this.userRepository.getAdmin();
 | 
				
			||||||
    if (adminUser) {
 | 
					    if (adminUser) {
 | 
				
			||||||
      throw new BadRequestException('The server already has an admin');
 | 
					      throw new BadRequestException('The server already has an admin');
 | 
				
			||||||
@ -138,7 +142,7 @@ export class AuthService {
 | 
				
			|||||||
      storageLabel: 'admin',
 | 
					      storageLabel: 'admin',
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return mapUser(admin);
 | 
					    return mapUserAdmin(admin);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async validate(headers: IncomingHttpHeaders, params: Record<string, string>): Promise<AuthDto> {
 | 
					  async validate(headers: IncomingHttpHeaders, params: Record<string, string>): Promise<AuthDto> {
 | 
				
			||||||
@ -237,7 +241,7 @@ export class AuthService {
 | 
				
			|||||||
    return this.createLoginResponse(user, loginDetails);
 | 
					    return this.createLoginResponse(user, loginDetails);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async link(auth: AuthDto, dto: OAuthCallbackDto): Promise<UserResponseDto> {
 | 
					  async link(auth: AuthDto, dto: OAuthCallbackDto): Promise<UserAdminResponseDto> {
 | 
				
			||||||
    const config = await this.configCore.getConfig();
 | 
					    const config = await this.configCore.getConfig();
 | 
				
			||||||
    const { sub: oauthId } = await this.getOAuthProfile(config, dto.url);
 | 
					    const { sub: oauthId } = await this.getOAuthProfile(config, dto.url);
 | 
				
			||||||
    const duplicate = await this.userRepository.getByOAuthId(oauthId);
 | 
					    const duplicate = await this.userRepository.getByOAuthId(oauthId);
 | 
				
			||||||
@ -245,11 +249,14 @@ export class AuthService {
 | 
				
			|||||||
      this.logger.warn(`OAuth link account failed: sub is already linked to another user (${duplicate.email}).`);
 | 
					      this.logger.warn(`OAuth link account failed: sub is already linked to another user (${duplicate.email}).`);
 | 
				
			||||||
      throw new BadRequestException('This OAuth account has already been linked to another user.');
 | 
					      throw new BadRequestException('This OAuth account has already been linked to another user.');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return mapUser(await this.userRepository.update(auth.user.id, { oauthId }));
 | 
					
 | 
				
			||||||
 | 
					    const user = await this.userRepository.update(auth.user.id, { oauthId });
 | 
				
			||||||
 | 
					    return mapUserAdmin(user);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async unlink(auth: AuthDto): Promise<UserResponseDto> {
 | 
					  async unlink(auth: AuthDto): Promise<UserAdminResponseDto> {
 | 
				
			||||||
    return mapUser(await this.userRepository.update(auth.user.id, { oauthId: '' }));
 | 
					    const user = await this.userRepository.update(auth.user.id, { oauthId: '' });
 | 
				
			||||||
 | 
					    return mapUserAdmin(user);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private async getLogoutEndpoint(authType: AuthType): Promise<string> {
 | 
					  private async getLogoutEndpoint(authType: AuthType): Promise<string> {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
import { Inject, Injectable } from '@nestjs/common';
 | 
					import { Inject, Injectable } from '@nestjs/common';
 | 
				
			||||||
 | 
					import { SALT_ROUNDS } from 'src/constants';
 | 
				
			||||||
import { SystemConfigCore } from 'src/cores/system-config.core';
 | 
					import { SystemConfigCore } from 'src/cores/system-config.core';
 | 
				
			||||||
import { UserCore } from 'src/cores/user.core';
 | 
					import { UserAdminResponseDto, mapUserAdmin } from 'src/dtos/user.dto';
 | 
				
			||||||
import { UserResponseDto, mapUser } from 'src/dtos/user.dto';
 | 
					 | 
				
			||||||
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
 | 
					import { ICryptoRepository } from 'src/interfaces/crypto.interface';
 | 
				
			||||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
 | 
					import { ILoggerRepository } from 'src/interfaces/logger.interface';
 | 
				
			||||||
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
 | 
					import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
 | 
				
			||||||
@ -10,7 +10,6 @@ import { IUserRepository } from 'src/interfaces/user.interface';
 | 
				
			|||||||
@Injectable()
 | 
					@Injectable()
 | 
				
			||||||
export class CliService {
 | 
					export class CliService {
 | 
				
			||||||
  private configCore: SystemConfigCore;
 | 
					  private configCore: SystemConfigCore;
 | 
				
			||||||
  private userCore: UserCore;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
    @Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository,
 | 
					    @Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository,
 | 
				
			||||||
@ -18,26 +17,26 @@ export class CliService {
 | 
				
			|||||||
    @Inject(IUserRepository) private userRepository: IUserRepository,
 | 
					    @Inject(IUserRepository) private userRepository: IUserRepository,
 | 
				
			||||||
    @Inject(ILoggerRepository) private logger: ILoggerRepository,
 | 
					    @Inject(ILoggerRepository) private logger: ILoggerRepository,
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    this.userCore = UserCore.create(cryptoRepository, userRepository);
 | 
					 | 
				
			||||||
    this.logger.setContext(CliService.name);
 | 
					    this.logger.setContext(CliService.name);
 | 
				
			||||||
    this.configCore = SystemConfigCore.create(systemMetadataRepository, this.logger);
 | 
					    this.configCore = SystemConfigCore.create(systemMetadataRepository, this.logger);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async listUsers(): Promise<UserResponseDto[]> {
 | 
					  async listUsers(): Promise<UserAdminResponseDto[]> {
 | 
				
			||||||
    const users = await this.userRepository.getList({ withDeleted: true });
 | 
					    const users = await this.userRepository.getList({ withDeleted: true });
 | 
				
			||||||
    return users.map((user) => mapUser(user));
 | 
					    return users.map((user) => mapUserAdmin(user));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async resetAdminPassword(ask: (admin: UserResponseDto) => Promise<string | undefined>) {
 | 
					  async resetAdminPassword(ask: (admin: UserAdminResponseDto) => Promise<string | undefined>) {
 | 
				
			||||||
    const admin = await this.userRepository.getAdmin();
 | 
					    const admin = await this.userRepository.getAdmin();
 | 
				
			||||||
    if (!admin) {
 | 
					    if (!admin) {
 | 
				
			||||||
      throw new Error('Admin account does not exist');
 | 
					      throw new Error('Admin account does not exist');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const providedPassword = await ask(mapUser(admin));
 | 
					    const providedPassword = await ask(mapUserAdmin(admin));
 | 
				
			||||||
    const password = providedPassword || this.cryptoRepository.newPassword(24);
 | 
					    const password = providedPassword || this.cryptoRepository.newPassword(24);
 | 
				
			||||||
 | 
					    const hashedPassword = await this.cryptoRepository.hashBcrypt(password, SALT_ROUNDS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    await this.userCore.updateUser(admin, admin.id, { password });
 | 
					    await this.userRepository.update(admin.id, { password: hashedPassword });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return { admin, password, provided: !!providedPassword };
 | 
					    return { admin, password, provided: !!providedPassword };
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -33,6 +33,7 @@ import { SystemMetadataService } from 'src/services/system-metadata.service';
 | 
				
			|||||||
import { TagService } from 'src/services/tag.service';
 | 
					import { TagService } from 'src/services/tag.service';
 | 
				
			||||||
import { TimelineService } from 'src/services/timeline.service';
 | 
					import { TimelineService } from 'src/services/timeline.service';
 | 
				
			||||||
import { TrashService } from 'src/services/trash.service';
 | 
					import { TrashService } from 'src/services/trash.service';
 | 
				
			||||||
 | 
					import { UserAdminService } from 'src/services/user-admin.service';
 | 
				
			||||||
import { UserService } from 'src/services/user.service';
 | 
					import { UserService } from 'src/services/user.service';
 | 
				
			||||||
import { VersionService } from 'src/services/version.service';
 | 
					import { VersionService } from 'src/services/version.service';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -73,5 +74,6 @@ export const services = [
 | 
				
			|||||||
  TimelineService,
 | 
					  TimelineService,
 | 
				
			||||||
  TrashService,
 | 
					  TrashService,
 | 
				
			||||||
  UserService,
 | 
					  UserService,
 | 
				
			||||||
 | 
					  UserAdminService,
 | 
				
			||||||
  VersionService,
 | 
					  VersionService,
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,4 @@
 | 
				
			|||||||
import { BadRequestException } from '@nestjs/common';
 | 
					import { BadRequestException } from '@nestjs/common';
 | 
				
			||||||
import { PartnerResponseDto } from 'src/dtos/partner.dto';
 | 
					 | 
				
			||||||
import { UserAvatarColor } from 'src/entities/user-metadata.entity';
 | 
					 | 
				
			||||||
import { IAccessRepository } from 'src/interfaces/access.interface';
 | 
					import { IAccessRepository } from 'src/interfaces/access.interface';
 | 
				
			||||||
import { IPartnerRepository, PartnerDirection } from 'src/interfaces/partner.interface';
 | 
					import { IPartnerRepository, PartnerDirection } from 'src/interfaces/partner.interface';
 | 
				
			||||||
import { PartnerService } from 'src/services/partner.service';
 | 
					import { PartnerService } from 'src/services/partner.service';
 | 
				
			||||||
@ -9,45 +7,6 @@ import { partnerStub } from 'test/fixtures/partner.stub';
 | 
				
			|||||||
import { newPartnerRepositoryMock } from 'test/repositories/partner.repository.mock';
 | 
					import { newPartnerRepositoryMock } from 'test/repositories/partner.repository.mock';
 | 
				
			||||||
import { Mocked } from 'vitest';
 | 
					import { Mocked } from 'vitest';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const responseDto = {
 | 
					 | 
				
			||||||
  admin: <PartnerResponseDto>{
 | 
					 | 
				
			||||||
    email: 'admin@test.com',
 | 
					 | 
				
			||||||
    name: 'admin_name',
 | 
					 | 
				
			||||||
    id: 'admin_id',
 | 
					 | 
				
			||||||
    isAdmin: true,
 | 
					 | 
				
			||||||
    oauthId: '',
 | 
					 | 
				
			||||||
    profileImagePath: '',
 | 
					 | 
				
			||||||
    shouldChangePassword: false,
 | 
					 | 
				
			||||||
    storageLabel: 'admin',
 | 
					 | 
				
			||||||
    createdAt: new Date('2021-01-01'),
 | 
					 | 
				
			||||||
    deletedAt: null,
 | 
					 | 
				
			||||||
    updatedAt: new Date('2021-01-01'),
 | 
					 | 
				
			||||||
    memoriesEnabled: true,
 | 
					 | 
				
			||||||
    avatarColor: UserAvatarColor.GRAY,
 | 
					 | 
				
			||||||
    quotaSizeInBytes: null,
 | 
					 | 
				
			||||||
    inTimeline: true,
 | 
					 | 
				
			||||||
    quotaUsageInBytes: 0,
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  user1: <PartnerResponseDto>{
 | 
					 | 
				
			||||||
    email: 'immich@test.com',
 | 
					 | 
				
			||||||
    name: 'immich_name',
 | 
					 | 
				
			||||||
    id: 'user-id',
 | 
					 | 
				
			||||||
    isAdmin: false,
 | 
					 | 
				
			||||||
    oauthId: '',
 | 
					 | 
				
			||||||
    profileImagePath: '',
 | 
					 | 
				
			||||||
    shouldChangePassword: false,
 | 
					 | 
				
			||||||
    storageLabel: null,
 | 
					 | 
				
			||||||
    createdAt: new Date('2021-01-01'),
 | 
					 | 
				
			||||||
    deletedAt: null,
 | 
					 | 
				
			||||||
    updatedAt: new Date('2021-01-01'),
 | 
					 | 
				
			||||||
    memoriesEnabled: true,
 | 
					 | 
				
			||||||
    avatarColor: UserAvatarColor.PRIMARY,
 | 
					 | 
				
			||||||
    inTimeline: true,
 | 
					 | 
				
			||||||
    quotaSizeInBytes: null,
 | 
					 | 
				
			||||||
    quotaUsageInBytes: 0,
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
describe(PartnerService.name, () => {
 | 
					describe(PartnerService.name, () => {
 | 
				
			||||||
  let sut: PartnerService;
 | 
					  let sut: PartnerService;
 | 
				
			||||||
  let partnerMock: Mocked<IPartnerRepository>;
 | 
					  let partnerMock: Mocked<IPartnerRepository>;
 | 
				
			||||||
@ -65,13 +24,13 @@ describe(PartnerService.name, () => {
 | 
				
			|||||||
  describe('getAll', () => {
 | 
					  describe('getAll', () => {
 | 
				
			||||||
    it("should return a list of partners with whom I've shared my library", async () => {
 | 
					    it("should return a list of partners with whom I've shared my library", async () => {
 | 
				
			||||||
      partnerMock.getAll.mockResolvedValue([partnerStub.adminToUser1, partnerStub.user1ToAdmin1]);
 | 
					      partnerMock.getAll.mockResolvedValue([partnerStub.adminToUser1, partnerStub.user1ToAdmin1]);
 | 
				
			||||||
      await expect(sut.getAll(authStub.user1, PartnerDirection.SharedBy)).resolves.toEqual([responseDto.admin]);
 | 
					      await expect(sut.getAll(authStub.user1, PartnerDirection.SharedBy)).resolves.toBeDefined();
 | 
				
			||||||
      expect(partnerMock.getAll).toHaveBeenCalledWith(authStub.user1.user.id);
 | 
					      expect(partnerMock.getAll).toHaveBeenCalledWith(authStub.user1.user.id);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('should return a list of partners who have shared their libraries with me', async () => {
 | 
					    it('should return a list of partners who have shared their libraries with me', async () => {
 | 
				
			||||||
      partnerMock.getAll.mockResolvedValue([partnerStub.adminToUser1, partnerStub.user1ToAdmin1]);
 | 
					      partnerMock.getAll.mockResolvedValue([partnerStub.adminToUser1, partnerStub.user1ToAdmin1]);
 | 
				
			||||||
      await expect(sut.getAll(authStub.user1, PartnerDirection.SharedWith)).resolves.toEqual([responseDto.admin]);
 | 
					      await expect(sut.getAll(authStub.user1, PartnerDirection.SharedWith)).resolves.toBeDefined();
 | 
				
			||||||
      expect(partnerMock.getAll).toHaveBeenCalledWith(authStub.user1.user.id);
 | 
					      expect(partnerMock.getAll).toHaveBeenCalledWith(authStub.user1.user.id);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
@ -81,7 +40,7 @@ describe(PartnerService.name, () => {
 | 
				
			|||||||
      partnerMock.get.mockResolvedValue(null);
 | 
					      partnerMock.get.mockResolvedValue(null);
 | 
				
			||||||
      partnerMock.create.mockResolvedValue(partnerStub.adminToUser1);
 | 
					      partnerMock.create.mockResolvedValue(partnerStub.adminToUser1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      await expect(sut.create(authStub.admin, authStub.user1.user.id)).resolves.toEqual(responseDto.user1);
 | 
					      await expect(sut.create(authStub.admin, authStub.user1.user.id)).resolves.toBeDefined();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      expect(partnerMock.create).toHaveBeenCalledWith({
 | 
					      expect(partnerMock.create).toHaveBeenCalledWith({
 | 
				
			||||||
        sharedById: authStub.admin.user.id,
 | 
					        sharedById: authStub.admin.user.id,
 | 
				
			||||||
 | 
				
			|||||||
@ -25,7 +25,7 @@ export class PartnerService {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const partner = await this.repository.create(partnerId);
 | 
					    const partner = await this.repository.create(partnerId);
 | 
				
			||||||
    return this.mapToPartnerEntity(partner, PartnerDirection.SharedBy);
 | 
					    return this.mapPartner(partner, PartnerDirection.SharedBy);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async remove(auth: AuthDto, sharedWithId: string): Promise<void> {
 | 
					  async remove(auth: AuthDto, sharedWithId: string): Promise<void> {
 | 
				
			||||||
@ -44,7 +44,7 @@ export class PartnerService {
 | 
				
			|||||||
    return partners
 | 
					    return partners
 | 
				
			||||||
      .filter((partner) => partner.sharedBy && partner.sharedWith) // Filter out soft deleted users
 | 
					      .filter((partner) => partner.sharedBy && partner.sharedWith) // Filter out soft deleted users
 | 
				
			||||||
      .filter((partner) => partner[key] === auth.user.id)
 | 
					      .filter((partner) => partner[key] === auth.user.id)
 | 
				
			||||||
      .map((partner) => this.mapToPartnerEntity(partner, direction));
 | 
					      .map((partner) => this.mapPartner(partner, direction));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async update(auth: AuthDto, sharedById: string, dto: UpdatePartnerDto): Promise<PartnerResponseDto> {
 | 
					  async update(auth: AuthDto, sharedById: string, dto: UpdatePartnerDto): Promise<PartnerResponseDto> {
 | 
				
			||||||
@ -52,10 +52,10 @@ export class PartnerService {
 | 
				
			|||||||
    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 });
 | 
				
			||||||
    return this.mapToPartnerEntity(entity, PartnerDirection.SharedWith);
 | 
					    return this.mapPartner(entity, PartnerDirection.SharedWith);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private mapToPartnerEntity(partner: PartnerEntity, direction: PartnerDirection): PartnerResponseDto {
 | 
					  private mapPartner(partner: PartnerEntity, direction: PartnerDirection): PartnerResponseDto {
 | 
				
			||||||
    // this is opposite to return the non-me user of the "partner"
 | 
					    // this is opposite to return the non-me user of the "partner"
 | 
				
			||||||
    const user = mapUser(
 | 
					    const user = mapUser(
 | 
				
			||||||
      direction === PartnerDirection.SharedBy ? partner.sharedWith : partner.sharedBy,
 | 
					      direction === PartnerDirection.SharedBy ? partner.sharedWith : partner.sharedBy,
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										197
									
								
								server/src/services/user-admin.service.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								server/src/services/user-admin.service.spec.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,197 @@
 | 
				
			|||||||
 | 
					import { BadRequestException, ForbiddenException } from '@nestjs/common';
 | 
				
			||||||
 | 
					import { mapUserAdmin } from 'src/dtos/user.dto';
 | 
				
			||||||
 | 
					import { UserStatus } from 'src/entities/user.entity';
 | 
				
			||||||
 | 
					import { IAlbumRepository } from 'src/interfaces/album.interface';
 | 
				
			||||||
 | 
					import { ICryptoRepository } from 'src/interfaces/crypto.interface';
 | 
				
			||||||
 | 
					import { IJobRepository, JobName } from 'src/interfaces/job.interface';
 | 
				
			||||||
 | 
					import { ILoggerRepository } from 'src/interfaces/logger.interface';
 | 
				
			||||||
 | 
					import { IUserRepository } from 'src/interfaces/user.interface';
 | 
				
			||||||
 | 
					import { UserAdminService } from 'src/services/user-admin.service';
 | 
				
			||||||
 | 
					import { authStub } from 'test/fixtures/auth.stub';
 | 
				
			||||||
 | 
					import { userStub } from 'test/fixtures/user.stub';
 | 
				
			||||||
 | 
					import { newAlbumRepositoryMock } from 'test/repositories/album.repository.mock';
 | 
				
			||||||
 | 
					import { newCryptoRepositoryMock } from 'test/repositories/crypto.repository.mock';
 | 
				
			||||||
 | 
					import { newJobRepositoryMock } from 'test/repositories/job.repository.mock';
 | 
				
			||||||
 | 
					import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock';
 | 
				
			||||||
 | 
					import { newUserRepositoryMock } from 'test/repositories/user.repository.mock';
 | 
				
			||||||
 | 
					import { Mocked, describe } from 'vitest';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					describe(UserAdminService.name, () => {
 | 
				
			||||||
 | 
					  let sut: UserAdminService;
 | 
				
			||||||
 | 
					  let userMock: Mocked<IUserRepository>;
 | 
				
			||||||
 | 
					  let cryptoRepositoryMock: Mocked<ICryptoRepository>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let albumMock: Mocked<IAlbumRepository>;
 | 
				
			||||||
 | 
					  let jobMock: Mocked<IJobRepository>;
 | 
				
			||||||
 | 
					  let loggerMock: Mocked<ILoggerRepository>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  beforeEach(() => {
 | 
				
			||||||
 | 
					    albumMock = newAlbumRepositoryMock();
 | 
				
			||||||
 | 
					    cryptoRepositoryMock = newCryptoRepositoryMock();
 | 
				
			||||||
 | 
					    jobMock = newJobRepositoryMock();
 | 
				
			||||||
 | 
					    userMock = newUserRepositoryMock();
 | 
				
			||||||
 | 
					    loggerMock = newLoggerRepositoryMock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sut = new UserAdminService(albumMock, cryptoRepositoryMock, jobMock, userMock, loggerMock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    userMock.get.mockImplementation((userId) =>
 | 
				
			||||||
 | 
					      Promise.resolve([userStub.admin, userStub.user1].find((user) => user.id === userId) ?? null),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('create', () => {
 | 
				
			||||||
 | 
					    it('should not create a user if there is no local admin account', async () => {
 | 
				
			||||||
 | 
					      userMock.getAdmin.mockResolvedValueOnce(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await expect(
 | 
				
			||||||
 | 
					        sut.create({
 | 
				
			||||||
 | 
					          email: 'john_smith@email.com',
 | 
				
			||||||
 | 
					          name: 'John Smith',
 | 
				
			||||||
 | 
					          password: 'password',
 | 
				
			||||||
 | 
					        }),
 | 
				
			||||||
 | 
					      ).rejects.toBeInstanceOf(BadRequestException);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should create user', async () => {
 | 
				
			||||||
 | 
					      userMock.getAdmin.mockResolvedValue(userStub.admin);
 | 
				
			||||||
 | 
					      userMock.create.mockResolvedValue(userStub.user1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await expect(
 | 
				
			||||||
 | 
					        sut.create({
 | 
				
			||||||
 | 
					          email: userStub.user1.email,
 | 
				
			||||||
 | 
					          name: userStub.user1.name,
 | 
				
			||||||
 | 
					          password: 'password',
 | 
				
			||||||
 | 
					          storageLabel: 'label',
 | 
				
			||||||
 | 
					        }),
 | 
				
			||||||
 | 
					      ).resolves.toEqual(mapUserAdmin(userStub.user1));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(userMock.getAdmin).toBeCalled();
 | 
				
			||||||
 | 
					      expect(userMock.create).toBeCalledWith({
 | 
				
			||||||
 | 
					        email: userStub.user1.email,
 | 
				
			||||||
 | 
					        name: userStub.user1.name,
 | 
				
			||||||
 | 
					        storageLabel: 'label',
 | 
				
			||||||
 | 
					        password: expect.anything(),
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('update', () => {
 | 
				
			||||||
 | 
					    it('should update the user', async () => {
 | 
				
			||||||
 | 
					      const update = {
 | 
				
			||||||
 | 
					        shouldChangePassword: true,
 | 
				
			||||||
 | 
					        email: 'immich@test.com',
 | 
				
			||||||
 | 
					        storageLabel: 'storage_label',
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					      userMock.getByEmail.mockResolvedValue(null);
 | 
				
			||||||
 | 
					      userMock.getByStorageLabel.mockResolvedValue(null);
 | 
				
			||||||
 | 
					      userMock.update.mockResolvedValue(userStub.user1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await sut.update(authStub.user1, userStub.user1.id, update);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(userMock.getByEmail).toHaveBeenCalledWith(update.email);
 | 
				
			||||||
 | 
					      expect(userMock.getByStorageLabel).toHaveBeenCalledWith(update.storageLabel);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should not set an empty string for storage label', async () => {
 | 
				
			||||||
 | 
					      userMock.update.mockResolvedValue(userStub.user1);
 | 
				
			||||||
 | 
					      await sut.update(authStub.admin, userStub.user1.id, { storageLabel: '' });
 | 
				
			||||||
 | 
					      expect(userMock.update).toHaveBeenCalledWith(userStub.user1.id, {
 | 
				
			||||||
 | 
					        storageLabel: null,
 | 
				
			||||||
 | 
					        updatedAt: expect.any(Date),
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should not change an email to one already in use', async () => {
 | 
				
			||||||
 | 
					      const dto = { id: userStub.user1.id, email: 'updated@test.com' };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      userMock.get.mockResolvedValue(userStub.user1);
 | 
				
			||||||
 | 
					      userMock.getByEmail.mockResolvedValue(userStub.admin);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await expect(sut.update(authStub.admin, userStub.user1.id, dto)).rejects.toBeInstanceOf(BadRequestException);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(userMock.update).not.toHaveBeenCalled();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should not let the admin change the storage label to one already in use', async () => {
 | 
				
			||||||
 | 
					      const dto = { id: userStub.user1.id, storageLabel: 'admin' };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      userMock.get.mockResolvedValue(userStub.user1);
 | 
				
			||||||
 | 
					      userMock.getByStorageLabel.mockResolvedValue(userStub.admin);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await expect(sut.update(authStub.admin, userStub.user1.id, dto)).rejects.toBeInstanceOf(BadRequestException);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(userMock.update).not.toHaveBeenCalled();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('update user information should throw error if user not found', async () => {
 | 
				
			||||||
 | 
					      userMock.get.mockResolvedValueOnce(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await expect(
 | 
				
			||||||
 | 
					        sut.update(authStub.admin, userStub.user1.id, { shouldChangePassword: true }),
 | 
				
			||||||
 | 
					      ).rejects.toBeInstanceOf(BadRequestException);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('delete', () => {
 | 
				
			||||||
 | 
					    it('should throw error if user could not be found', async () => {
 | 
				
			||||||
 | 
					      userMock.get.mockResolvedValue(null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await expect(sut.delete(authStub.admin, userStub.admin.id, {})).rejects.toThrowError(BadRequestException);
 | 
				
			||||||
 | 
					      expect(userMock.delete).not.toHaveBeenCalled();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('cannot delete admin user', async () => {
 | 
				
			||||||
 | 
					      await expect(sut.delete(authStub.admin, userStub.admin.id, {})).rejects.toBeInstanceOf(ForbiddenException);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should require the auth user be an admin', async () => {
 | 
				
			||||||
 | 
					      await expect(sut.delete(authStub.user1, authStub.admin.user.id, {})).rejects.toBeInstanceOf(ForbiddenException);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(userMock.delete).not.toHaveBeenCalled();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should delete user', async () => {
 | 
				
			||||||
 | 
					      userMock.get.mockResolvedValue(userStub.user1);
 | 
				
			||||||
 | 
					      userMock.update.mockResolvedValue(userStub.user1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await expect(sut.delete(authStub.admin, userStub.user1.id, {})).resolves.toEqual(mapUserAdmin(userStub.user1));
 | 
				
			||||||
 | 
					      expect(userMock.update).toHaveBeenCalledWith(userStub.user1.id, {
 | 
				
			||||||
 | 
					        status: UserStatus.DELETED,
 | 
				
			||||||
 | 
					        deletedAt: expect.any(Date),
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should force delete user', async () => {
 | 
				
			||||||
 | 
					      userMock.get.mockResolvedValue(userStub.user1);
 | 
				
			||||||
 | 
					      userMock.update.mockResolvedValue(userStub.user1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await expect(sut.delete(authStub.admin, userStub.user1.id, { force: true })).resolves.toEqual(
 | 
				
			||||||
 | 
					        mapUserAdmin(userStub.user1),
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      expect(userMock.update).toHaveBeenCalledWith(userStub.user1.id, {
 | 
				
			||||||
 | 
					        status: UserStatus.REMOVING,
 | 
				
			||||||
 | 
					        deletedAt: expect.any(Date),
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      expect(jobMock.queue).toHaveBeenCalledWith({
 | 
				
			||||||
 | 
					        name: JobName.USER_DELETION,
 | 
				
			||||||
 | 
					        data: { id: userStub.user1.id, force: true },
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe('restore', () => {
 | 
				
			||||||
 | 
					    it('should throw error if user could not be found', async () => {
 | 
				
			||||||
 | 
					      userMock.get.mockResolvedValue(null);
 | 
				
			||||||
 | 
					      await expect(sut.restore(authStub.admin, userStub.admin.id)).rejects.toThrowError(BadRequestException);
 | 
				
			||||||
 | 
					      expect(userMock.update).not.toHaveBeenCalled();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it('should restore an user', async () => {
 | 
				
			||||||
 | 
					      userMock.get.mockResolvedValue(userStub.user1);
 | 
				
			||||||
 | 
					      userMock.update.mockResolvedValue(userStub.user1);
 | 
				
			||||||
 | 
					      await expect(sut.restore(authStub.admin, userStub.user1.id)).resolves.toEqual(mapUserAdmin(userStub.user1));
 | 
				
			||||||
 | 
					      expect(userMock.update).toHaveBeenCalledWith(userStub.user1.id, { status: UserStatus.ACTIVE, deletedAt: null });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										154
									
								
								server/src/services/user-admin.service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								server/src/services/user-admin.service.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,154 @@
 | 
				
			|||||||
 | 
					import { BadRequestException, ForbiddenException, Inject, Injectable } from '@nestjs/common';
 | 
				
			||||||
 | 
					import { SALT_ROUNDS } from 'src/constants';
 | 
				
			||||||
 | 
					import { UserCore } from 'src/cores/user.core';
 | 
				
			||||||
 | 
					import { AuthDto } from 'src/dtos/auth.dto';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  UserAdminCreateDto,
 | 
				
			||||||
 | 
					  UserAdminDeleteDto,
 | 
				
			||||||
 | 
					  UserAdminResponseDto,
 | 
				
			||||||
 | 
					  UserAdminSearchDto,
 | 
				
			||||||
 | 
					  UserAdminUpdateDto,
 | 
				
			||||||
 | 
					  mapUserAdmin,
 | 
				
			||||||
 | 
					} from 'src/dtos/user.dto';
 | 
				
			||||||
 | 
					import { UserMetadataKey } from 'src/entities/user-metadata.entity';
 | 
				
			||||||
 | 
					import { UserStatus } from 'src/entities/user.entity';
 | 
				
			||||||
 | 
					import { IAlbumRepository } from 'src/interfaces/album.interface';
 | 
				
			||||||
 | 
					import { ICryptoRepository } from 'src/interfaces/crypto.interface';
 | 
				
			||||||
 | 
					import { IJobRepository, JobName } from 'src/interfaces/job.interface';
 | 
				
			||||||
 | 
					import { ILoggerRepository } from 'src/interfaces/logger.interface';
 | 
				
			||||||
 | 
					import { IUserRepository, UserFindOptions } from 'src/interfaces/user.interface';
 | 
				
			||||||
 | 
					import { getPreferences, getPreferencesPartial } from 'src/utils/preferences';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Injectable()
 | 
				
			||||||
 | 
					export class UserAdminService {
 | 
				
			||||||
 | 
					  private userCore: UserCore;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
 | 
					    @Inject(IAlbumRepository) private albumRepository: IAlbumRepository,
 | 
				
			||||||
 | 
					    @Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository,
 | 
				
			||||||
 | 
					    @Inject(IJobRepository) private jobRepository: IJobRepository,
 | 
				
			||||||
 | 
					    @Inject(IUserRepository) private userRepository: IUserRepository,
 | 
				
			||||||
 | 
					    @Inject(ILoggerRepository) private logger: ILoggerRepository,
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
 | 
					    this.userCore = UserCore.create(cryptoRepository, userRepository);
 | 
				
			||||||
 | 
					    this.logger.setContext(UserAdminService.name);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async search(auth: AuthDto, dto: UserAdminSearchDto): Promise<UserAdminResponseDto[]> {
 | 
				
			||||||
 | 
					    const users = await this.userRepository.getList({ withDeleted: dto.withDeleted });
 | 
				
			||||||
 | 
					    return users.map((user) => mapUserAdmin(user));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async create(dto: UserAdminCreateDto): Promise<UserAdminResponseDto> {
 | 
				
			||||||
 | 
					    const { memoriesEnabled, notify, ...rest } = dto;
 | 
				
			||||||
 | 
					    let user = await this.userCore.createUser(rest);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // TODO remove and replace with entire dto.preferences config
 | 
				
			||||||
 | 
					    if (memoriesEnabled === false) {
 | 
				
			||||||
 | 
					      await this.userRepository.upsertMetadata(user.id, {
 | 
				
			||||||
 | 
					        key: UserMetadataKey.PREFERENCES,
 | 
				
			||||||
 | 
					        value: { memories: { enabled: false } },
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      user = await this.findOrFail(user.id, {});
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const tempPassword = user.shouldChangePassword ? rest.password : undefined;
 | 
				
			||||||
 | 
					    if (notify) {
 | 
				
			||||||
 | 
					      await this.jobRepository.queue({ name: JobName.NOTIFY_SIGNUP, data: { id: user.id, tempPassword } });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return mapUserAdmin(user);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async get(auth: AuthDto, id: string): Promise<UserAdminResponseDto> {
 | 
				
			||||||
 | 
					    const user = await this.findOrFail(id, { withDeleted: true });
 | 
				
			||||||
 | 
					    return mapUserAdmin(user);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async update(auth: AuthDto, id: string, dto: UserAdminUpdateDto): Promise<UserAdminResponseDto> {
 | 
				
			||||||
 | 
					    const user = await this.findOrFail(id, {});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (dto.quotaSizeInBytes && user.quotaSizeInBytes !== dto.quotaSizeInBytes) {
 | 
				
			||||||
 | 
					      await this.userRepository.syncUsage(id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // TODO replace with entire preferences object
 | 
				
			||||||
 | 
					    if (dto.memoriesEnabled !== undefined || dto.avatarColor) {
 | 
				
			||||||
 | 
					      const newPreferences = getPreferences(user);
 | 
				
			||||||
 | 
					      if (dto.memoriesEnabled !== undefined) {
 | 
				
			||||||
 | 
					        newPreferences.memories.enabled = dto.memoriesEnabled;
 | 
				
			||||||
 | 
					        delete dto.memoriesEnabled;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (dto.avatarColor) {
 | 
				
			||||||
 | 
					        newPreferences.avatar.color = dto.avatarColor;
 | 
				
			||||||
 | 
					        delete dto.avatarColor;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      await this.userRepository.upsertMetadata(id, {
 | 
				
			||||||
 | 
					        key: UserMetadataKey.PREFERENCES,
 | 
				
			||||||
 | 
					        value: getPreferencesPartial(user, newPreferences),
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (dto.email) {
 | 
				
			||||||
 | 
					      const duplicate = await this.userRepository.getByEmail(dto.email);
 | 
				
			||||||
 | 
					      if (duplicate && duplicate.id !== id) {
 | 
				
			||||||
 | 
					        throw new BadRequestException('Email already in use by another account');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (dto.storageLabel) {
 | 
				
			||||||
 | 
					      const duplicate = await this.userRepository.getByStorageLabel(dto.storageLabel);
 | 
				
			||||||
 | 
					      if (duplicate && duplicate.id !== id) {
 | 
				
			||||||
 | 
					        throw new BadRequestException('Storage label already in use by another account');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (dto.password) {
 | 
				
			||||||
 | 
					      dto.password = await this.cryptoRepository.hashBcrypt(dto.password, SALT_ROUNDS);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (dto.storageLabel === '') {
 | 
				
			||||||
 | 
					      dto.storageLabel = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const updatedUser = await this.userRepository.update(id, { ...dto, updatedAt: new Date() });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return mapUserAdmin(updatedUser);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async delete(auth: AuthDto, id: string, dto: UserAdminDeleteDto): Promise<UserAdminResponseDto> {
 | 
				
			||||||
 | 
					    const { force } = dto;
 | 
				
			||||||
 | 
					    const { isAdmin } = await this.findOrFail(id, {});
 | 
				
			||||||
 | 
					    if (isAdmin) {
 | 
				
			||||||
 | 
					      throw new ForbiddenException('Cannot delete admin user');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await this.albumRepository.softDeleteAll(id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const status = force ? UserStatus.REMOVING : UserStatus.DELETED;
 | 
				
			||||||
 | 
					    const user = await this.userRepository.update(id, { status, deletedAt: new Date() });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (force) {
 | 
				
			||||||
 | 
					      await this.jobRepository.queue({ name: JobName.USER_DELETION, data: { id: user.id, force } });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return mapUserAdmin(user);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async restore(auth: AuthDto, id: string): Promise<UserAdminResponseDto> {
 | 
				
			||||||
 | 
					    await this.findOrFail(id, { withDeleted: true });
 | 
				
			||||||
 | 
					    await this.albumRepository.restoreAll(id);
 | 
				
			||||||
 | 
					    const user = await this.userRepository.update(id, { deletedAt: null, status: UserStatus.ACTIVE });
 | 
				
			||||||
 | 
					    return mapUserAdmin(user);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private async findOrFail(id: string, options: UserFindOptions) {
 | 
				
			||||||
 | 
					    const user = await this.userRepository.get(id, options);
 | 
				
			||||||
 | 
					    if (!user) {
 | 
				
			||||||
 | 
					      throw new BadRequestException('User not found');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return user;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,11 +1,5 @@
 | 
				
			|||||||
import {
 | 
					import { BadRequestException, InternalServerErrorException, NotFoundException } from '@nestjs/common';
 | 
				
			||||||
  BadRequestException,
 | 
					import { UserEntity } from 'src/entities/user.entity';
 | 
				
			||||||
  ForbiddenException,
 | 
					 | 
				
			||||||
  InternalServerErrorException,
 | 
					 | 
				
			||||||
  NotFoundException,
 | 
					 | 
				
			||||||
} from '@nestjs/common';
 | 
					 | 
				
			||||||
import { UpdateUserDto, mapUser } from 'src/dtos/user.dto';
 | 
					 | 
				
			||||||
import { UserEntity, UserStatus } from 'src/entities/user.entity';
 | 
					 | 
				
			||||||
import { IAlbumRepository } from 'src/interfaces/album.interface';
 | 
					import { IAlbumRepository } from 'src/interfaces/album.interface';
 | 
				
			||||||
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
 | 
					import { ICryptoRepository } from 'src/interfaces/crypto.interface';
 | 
				
			||||||
import { IJobRepository, JobName } from 'src/interfaces/job.interface';
 | 
					import { IJobRepository, JobName } from 'src/interfaces/job.interface';
 | 
				
			||||||
@ -63,13 +57,13 @@ describe(UserService.name, () => {
 | 
				
			|||||||
  describe('getAll', () => {
 | 
					  describe('getAll', () => {
 | 
				
			||||||
    it('should get all users', async () => {
 | 
					    it('should get all users', async () => {
 | 
				
			||||||
      userMock.getList.mockResolvedValue([userStub.admin]);
 | 
					      userMock.getList.mockResolvedValue([userStub.admin]);
 | 
				
			||||||
      await expect(sut.getAll(authStub.admin, false)).resolves.toEqual([
 | 
					      await expect(sut.search()).resolves.toEqual([
 | 
				
			||||||
        expect.objectContaining({
 | 
					        expect.objectContaining({
 | 
				
			||||||
          id: authStub.admin.user.id,
 | 
					          id: authStub.admin.user.id,
 | 
				
			||||||
          email: authStub.admin.user.email,
 | 
					          email: authStub.admin.user.email,
 | 
				
			||||||
        }),
 | 
					        }),
 | 
				
			||||||
      ]);
 | 
					      ]);
 | 
				
			||||||
      expect(userMock.getList).toHaveBeenCalledWith({ withDeleted: true });
 | 
					      expect(userMock.getList).toHaveBeenCalledWith({ withDeleted: false });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -82,255 +76,17 @@ describe(UserService.name, () => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    it('should throw an error if a user is not found', async () => {
 | 
					    it('should throw an error if a user is not found', async () => {
 | 
				
			||||||
      userMock.get.mockResolvedValue(null);
 | 
					      userMock.get.mockResolvedValue(null);
 | 
				
			||||||
      await expect(sut.get(authStub.admin.user.id)).rejects.toBeInstanceOf(NotFoundException);
 | 
					      await expect(sut.get(authStub.admin.user.id)).rejects.toBeInstanceOf(BadRequestException);
 | 
				
			||||||
      expect(userMock.get).toHaveBeenCalledWith(authStub.admin.user.id, { withDeleted: false });
 | 
					      expect(userMock.get).toHaveBeenCalledWith(authStub.admin.user.id, { withDeleted: false });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  describe('getMe', () => {
 | 
					  describe('getMe', () => {
 | 
				
			||||||
    it("should get the auth user's info", async () => {
 | 
					    it("should get the auth user's info", () => {
 | 
				
			||||||
      userMock.get.mockResolvedValue(userStub.admin);
 | 
					      const user = authStub.admin.user;
 | 
				
			||||||
      await sut.getMe(authStub.admin);
 | 
					      expect(sut.getMe(authStub.admin)).toMatchObject({
 | 
				
			||||||
      expect(userMock.get).toHaveBeenCalledWith(authStub.admin.user.id, {});
 | 
					        id: user.id,
 | 
				
			||||||
    });
 | 
					        email: user.email,
 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('should throw an error if a user is not found', async () => {
 | 
					 | 
				
			||||||
      userMock.get.mockResolvedValue(null);
 | 
					 | 
				
			||||||
      await expect(sut.getMe(authStub.admin)).rejects.toBeInstanceOf(BadRequestException);
 | 
					 | 
				
			||||||
      expect(userMock.get).toHaveBeenCalledWith(authStub.admin.user.id, {});
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  describe('update', () => {
 | 
					 | 
				
			||||||
    it('should update user', async () => {
 | 
					 | 
				
			||||||
      const update: UpdateUserDto = {
 | 
					 | 
				
			||||||
        id: userStub.user1.id,
 | 
					 | 
				
			||||||
        shouldChangePassword: true,
 | 
					 | 
				
			||||||
        email: 'immich@test.com',
 | 
					 | 
				
			||||||
        storageLabel: 'storage_label',
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
      userMock.getByEmail.mockResolvedValue(null);
 | 
					 | 
				
			||||||
      userMock.getByStorageLabel.mockResolvedValue(null);
 | 
					 | 
				
			||||||
      userMock.update.mockResolvedValue(userStub.user1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      await sut.update({ user: { ...authStub.user1.user, isAdmin: true } }, update);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      expect(userMock.getByEmail).toHaveBeenCalledWith(update.email);
 | 
					 | 
				
			||||||
      expect(userMock.getByStorageLabel).toHaveBeenCalledWith(update.storageLabel);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('should not set an empty string for storage label', async () => {
 | 
					 | 
				
			||||||
      userMock.update.mockResolvedValue(userStub.user1);
 | 
					 | 
				
			||||||
      await sut.update(authStub.admin, { id: userStub.user1.id, storageLabel: '' });
 | 
					 | 
				
			||||||
      expect(userMock.update).toHaveBeenCalledWith(userStub.user1.id, {
 | 
					 | 
				
			||||||
        id: userStub.user1.id,
 | 
					 | 
				
			||||||
        storageLabel: null,
 | 
					 | 
				
			||||||
        updatedAt: expect.any(Date),
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('should omit a storage label set by non-admin users', async () => {
 | 
					 | 
				
			||||||
      userMock.update.mockResolvedValue(userStub.user1);
 | 
					 | 
				
			||||||
      await sut.update({ user: userStub.user1 }, { id: userStub.user1.id, storageLabel: 'admin' });
 | 
					 | 
				
			||||||
      expect(userMock.update).toHaveBeenCalledWith(userStub.user1.id, {
 | 
					 | 
				
			||||||
        id: userStub.user1.id,
 | 
					 | 
				
			||||||
        updatedAt: expect.any(Date),
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('user can only update its information', async () => {
 | 
					 | 
				
			||||||
      userMock.get.mockResolvedValueOnce({
 | 
					 | 
				
			||||||
        ...userStub.user1,
 | 
					 | 
				
			||||||
        id: 'not_immich_auth_user_id',
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      const result = sut.update(
 | 
					 | 
				
			||||||
        { user: userStub.user1 },
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          id: 'not_immich_auth_user_id',
 | 
					 | 
				
			||||||
          password: 'I take over your account now',
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
      await expect(result).rejects.toBeInstanceOf(ForbiddenException);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('should let a user change their email', async () => {
 | 
					 | 
				
			||||||
      const dto = { id: userStub.user1.id, email: 'updated@test.com' };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      userMock.get.mockResolvedValue(userStub.user1);
 | 
					 | 
				
			||||||
      userMock.update.mockResolvedValue(userStub.user1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      await sut.update({ user: userStub.user1 }, dto);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      expect(userMock.update).toHaveBeenCalledWith(userStub.user1.id, {
 | 
					 | 
				
			||||||
        id: 'user-id',
 | 
					 | 
				
			||||||
        email: 'updated@test.com',
 | 
					 | 
				
			||||||
        updatedAt: expect.any(Date),
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('should not let a user change their email to one already in use', async () => {
 | 
					 | 
				
			||||||
      const dto = { id: userStub.user1.id, email: 'updated@test.com' };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      userMock.get.mockResolvedValue(userStub.user1);
 | 
					 | 
				
			||||||
      userMock.getByEmail.mockResolvedValue(userStub.admin);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      await expect(sut.update({ user: userStub.user1 }, dto)).rejects.toBeInstanceOf(BadRequestException);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      expect(userMock.update).not.toHaveBeenCalled();
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('should not let the admin change the storage label to one already in use', async () => {
 | 
					 | 
				
			||||||
      const dto = { id: userStub.user1.id, storageLabel: 'admin' };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      userMock.get.mockResolvedValue(userStub.user1);
 | 
					 | 
				
			||||||
      userMock.getByStorageLabel.mockResolvedValue(userStub.admin);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      await expect(sut.update(authStub.admin, dto)).rejects.toBeInstanceOf(BadRequestException);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      expect(userMock.update).not.toHaveBeenCalled();
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('admin can update any user information', async () => {
 | 
					 | 
				
			||||||
      const update: UpdateUserDto = {
 | 
					 | 
				
			||||||
        id: userStub.user1.id,
 | 
					 | 
				
			||||||
        shouldChangePassword: true,
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      userMock.update.mockResolvedValueOnce(userStub.user1);
 | 
					 | 
				
			||||||
      await sut.update(authStub.admin, update);
 | 
					 | 
				
			||||||
      expect(userMock.update).toHaveBeenCalledWith(userStub.user1.id, {
 | 
					 | 
				
			||||||
        id: 'user-id',
 | 
					 | 
				
			||||||
        shouldChangePassword: true,
 | 
					 | 
				
			||||||
        updatedAt: expect.any(Date),
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('update user information should throw error if user not found', async () => {
 | 
					 | 
				
			||||||
      userMock.get.mockResolvedValueOnce(null);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      const result = sut.update(authStub.admin, {
 | 
					 | 
				
			||||||
        id: userStub.user1.id,
 | 
					 | 
				
			||||||
        shouldChangePassword: true,
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      await expect(result).rejects.toBeInstanceOf(BadRequestException);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('should let the admin update himself', async () => {
 | 
					 | 
				
			||||||
      const dto = { id: userStub.admin.id, shouldChangePassword: true, isAdmin: true };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      userMock.update.mockResolvedValueOnce(userStub.admin);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      await sut.update(authStub.admin, dto);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      expect(userMock.update).toHaveBeenCalledWith(userStub.admin.id, { ...dto, updatedAt: expect.any(Date) });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('should not let the another user become an admin', async () => {
 | 
					 | 
				
			||||||
      const dto = { id: userStub.user1.id, shouldChangePassword: true, isAdmin: true };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      userMock.get.mockResolvedValueOnce(userStub.user1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      await expect(sut.update(authStub.admin, dto)).rejects.toBeInstanceOf(BadRequestException);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  describe('restore', () => {
 | 
					 | 
				
			||||||
    it('should throw error if user could not be found', async () => {
 | 
					 | 
				
			||||||
      userMock.get.mockResolvedValue(null);
 | 
					 | 
				
			||||||
      await expect(sut.restore(authStub.admin, userStub.admin.id)).rejects.toThrowError(BadRequestException);
 | 
					 | 
				
			||||||
      expect(userMock.update).not.toHaveBeenCalled();
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('should restore an user', async () => {
 | 
					 | 
				
			||||||
      userMock.get.mockResolvedValue(userStub.user1);
 | 
					 | 
				
			||||||
      userMock.update.mockResolvedValue(userStub.user1);
 | 
					 | 
				
			||||||
      await expect(sut.restore(authStub.admin, userStub.user1.id)).resolves.toEqual(mapUser(userStub.user1));
 | 
					 | 
				
			||||||
      expect(userMock.update).toHaveBeenCalledWith(userStub.user1.id, { status: UserStatus.ACTIVE, deletedAt: null });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  describe('delete', () => {
 | 
					 | 
				
			||||||
    it('should throw error if user could not be found', async () => {
 | 
					 | 
				
			||||||
      userMock.get.mockResolvedValue(null);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      await expect(sut.delete(authStub.admin, userStub.admin.id, {})).rejects.toThrowError(BadRequestException);
 | 
					 | 
				
			||||||
      expect(userMock.delete).not.toHaveBeenCalled();
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('cannot delete admin user', async () => {
 | 
					 | 
				
			||||||
      await expect(sut.delete(authStub.admin, userStub.admin.id, {})).rejects.toBeInstanceOf(ForbiddenException);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('should require the auth user be an admin', async () => {
 | 
					 | 
				
			||||||
      await expect(sut.delete(authStub.user1, authStub.admin.user.id, {})).rejects.toBeInstanceOf(ForbiddenException);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      expect(userMock.delete).not.toHaveBeenCalled();
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('should delete user', async () => {
 | 
					 | 
				
			||||||
      userMock.get.mockResolvedValue(userStub.user1);
 | 
					 | 
				
			||||||
      userMock.update.mockResolvedValue(userStub.user1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      await expect(sut.delete(authStub.admin, userStub.user1.id, {})).resolves.toEqual(mapUser(userStub.user1));
 | 
					 | 
				
			||||||
      expect(userMock.update).toHaveBeenCalledWith(userStub.user1.id, {
 | 
					 | 
				
			||||||
        status: UserStatus.DELETED,
 | 
					 | 
				
			||||||
        deletedAt: expect.any(Date),
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('should force delete user', async () => {
 | 
					 | 
				
			||||||
      userMock.get.mockResolvedValue(userStub.user1);
 | 
					 | 
				
			||||||
      userMock.update.mockResolvedValue(userStub.user1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      await expect(sut.delete(authStub.admin, userStub.user1.id, { force: true })).resolves.toEqual(
 | 
					 | 
				
			||||||
        mapUser(userStub.user1),
 | 
					 | 
				
			||||||
      );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      expect(userMock.update).toHaveBeenCalledWith(userStub.user1.id, {
 | 
					 | 
				
			||||||
        status: UserStatus.REMOVING,
 | 
					 | 
				
			||||||
        deletedAt: expect.any(Date),
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
      expect(jobMock.queue).toHaveBeenCalledWith({
 | 
					 | 
				
			||||||
        name: JobName.USER_DELETION,
 | 
					 | 
				
			||||||
        data: { id: userStub.user1.id, force: true },
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  describe('create', () => {
 | 
					 | 
				
			||||||
    it('should not create a user if there is no local admin account', async () => {
 | 
					 | 
				
			||||||
      userMock.getAdmin.mockResolvedValueOnce(null);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      await expect(
 | 
					 | 
				
			||||||
        sut.create({
 | 
					 | 
				
			||||||
          email: 'john_smith@email.com',
 | 
					 | 
				
			||||||
          name: 'John Smith',
 | 
					 | 
				
			||||||
          password: 'password',
 | 
					 | 
				
			||||||
        }),
 | 
					 | 
				
			||||||
      ).rejects.toBeInstanceOf(BadRequestException);
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    it('should create user', async () => {
 | 
					 | 
				
			||||||
      userMock.getAdmin.mockResolvedValue(userStub.admin);
 | 
					 | 
				
			||||||
      userMock.create.mockResolvedValue(userStub.user1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      await expect(
 | 
					 | 
				
			||||||
        sut.create({
 | 
					 | 
				
			||||||
          email: userStub.user1.email,
 | 
					 | 
				
			||||||
          name: userStub.user1.name,
 | 
					 | 
				
			||||||
          password: 'password',
 | 
					 | 
				
			||||||
          storageLabel: 'label',
 | 
					 | 
				
			||||||
        }),
 | 
					 | 
				
			||||||
      ).resolves.toEqual(mapUser(userStub.user1));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      expect(userMock.getAdmin).toBeCalled();
 | 
					 | 
				
			||||||
      expect(userMock.create).toBeCalledWith({
 | 
					 | 
				
			||||||
        email: userStub.user1.email,
 | 
					 | 
				
			||||||
        name: userStub.user1.name,
 | 
					 | 
				
			||||||
        storageLabel: 'label',
 | 
					 | 
				
			||||||
        password: expect.anything(),
 | 
					 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
				
			|||||||
@ -1,13 +1,13 @@
 | 
				
			|||||||
import { BadRequestException, ForbiddenException, Inject, Injectable, NotFoundException } from '@nestjs/common';
 | 
					import { BadRequestException, Inject, Injectable, NotFoundException } from '@nestjs/common';
 | 
				
			||||||
import { DateTime } from 'luxon';
 | 
					import { DateTime } from 'luxon';
 | 
				
			||||||
 | 
					import { SALT_ROUNDS } from 'src/constants';
 | 
				
			||||||
import { StorageCore, StorageFolder } from 'src/cores/storage.core';
 | 
					import { StorageCore, StorageFolder } from 'src/cores/storage.core';
 | 
				
			||||||
import { SystemConfigCore } from 'src/cores/system-config.core';
 | 
					import { SystemConfigCore } from 'src/cores/system-config.core';
 | 
				
			||||||
import { UserCore } from 'src/cores/user.core';
 | 
					 | 
				
			||||||
import { AuthDto } from 'src/dtos/auth.dto';
 | 
					import { AuthDto } from 'src/dtos/auth.dto';
 | 
				
			||||||
import { CreateProfileImageResponseDto, mapCreateProfileImageResponse } from 'src/dtos/user-profile.dto';
 | 
					import { CreateProfileImageResponseDto, mapCreateProfileImageResponse } from 'src/dtos/user-profile.dto';
 | 
				
			||||||
import { CreateUserDto, DeleteUserDto, UpdateUserDto, UserResponseDto, mapUser } from 'src/dtos/user.dto';
 | 
					import { UserAdminResponseDto, UserResponseDto, UserUpdateMeDto, mapUser, mapUserAdmin } from 'src/dtos/user.dto';
 | 
				
			||||||
import { UserMetadataKey } from 'src/entities/user-metadata.entity';
 | 
					import { UserMetadataKey } from 'src/entities/user-metadata.entity';
 | 
				
			||||||
import { UserEntity, UserStatus } from 'src/entities/user.entity';
 | 
					import { UserEntity } from 'src/entities/user.entity';
 | 
				
			||||||
import { IAlbumRepository } from 'src/interfaces/album.interface';
 | 
					import { IAlbumRepository } from 'src/interfaces/album.interface';
 | 
				
			||||||
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
 | 
					import { ICryptoRepository } from 'src/interfaces/crypto.interface';
 | 
				
			||||||
import { IEntityJob, IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface';
 | 
					import { IEntityJob, IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface';
 | 
				
			||||||
@ -21,73 +21,30 @@ import { getPreferences, getPreferencesPartial } from 'src/utils/preferences';
 | 
				
			|||||||
@Injectable()
 | 
					@Injectable()
 | 
				
			||||||
export class UserService {
 | 
					export class UserService {
 | 
				
			||||||
  private configCore: SystemConfigCore;
 | 
					  private configCore: SystemConfigCore;
 | 
				
			||||||
  private userCore: UserCore;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
    @Inject(IAlbumRepository) private albumRepository: IAlbumRepository,
 | 
					    @Inject(IAlbumRepository) private albumRepository: IAlbumRepository,
 | 
				
			||||||
    @Inject(ICryptoRepository) cryptoRepository: ICryptoRepository,
 | 
					    @Inject(ICryptoRepository) private cryptoRepository: ICryptoRepository,
 | 
				
			||||||
    @Inject(IJobRepository) private jobRepository: IJobRepository,
 | 
					    @Inject(IJobRepository) private jobRepository: IJobRepository,
 | 
				
			||||||
    @Inject(IStorageRepository) private storageRepository: IStorageRepository,
 | 
					    @Inject(IStorageRepository) private storageRepository: IStorageRepository,
 | 
				
			||||||
    @Inject(ISystemMetadataRepository) systemMetadataRepository: ISystemMetadataRepository,
 | 
					    @Inject(ISystemMetadataRepository) systemMetadataRepository: ISystemMetadataRepository,
 | 
				
			||||||
    @Inject(IUserRepository) private userRepository: IUserRepository,
 | 
					    @Inject(IUserRepository) private userRepository: IUserRepository,
 | 
				
			||||||
    @Inject(ILoggerRepository) private logger: ILoggerRepository,
 | 
					    @Inject(ILoggerRepository) private logger: ILoggerRepository,
 | 
				
			||||||
  ) {
 | 
					  ) {
 | 
				
			||||||
    this.userCore = UserCore.create(cryptoRepository, userRepository);
 | 
					 | 
				
			||||||
    this.logger.setContext(UserService.name);
 | 
					    this.logger.setContext(UserService.name);
 | 
				
			||||||
    this.configCore = SystemConfigCore.create(systemMetadataRepository, this.logger);
 | 
					    this.configCore = SystemConfigCore.create(systemMetadataRepository, this.logger);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async listUsers(): Promise<UserResponseDto[]> {
 | 
					  async search(): Promise<UserResponseDto[]> {
 | 
				
			||||||
    const users = await this.userRepository.getList({ withDeleted: true });
 | 
					    const users = await this.userRepository.getList({ withDeleted: false });
 | 
				
			||||||
    return users.map((user) => mapUser(user));
 | 
					    return users.map((user) => mapUser(user));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async getAll(auth: AuthDto, isAll: boolean): Promise<UserResponseDto[]> {
 | 
					  getMe(auth: AuthDto): UserAdminResponseDto {
 | 
				
			||||||
    const users = await this.userRepository.getList({ withDeleted: !isAll });
 | 
					    return mapUserAdmin(auth.user);
 | 
				
			||||||
    return users.map((user) => mapUser(user));
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  async get(userId: string): Promise<UserResponseDto> {
 | 
					 | 
				
			||||||
    const user = await this.userRepository.get(userId, { withDeleted: false });
 | 
					 | 
				
			||||||
    if (!user) {
 | 
					 | 
				
			||||||
      throw new NotFoundException('User not found');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return mapUser(user);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  getMe(auth: AuthDto): Promise<UserResponseDto> {
 | 
					 | 
				
			||||||
    return this.findOrFail(auth.user.id, {}).then(mapUser);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  async create(dto: CreateUserDto): Promise<UserResponseDto> {
 | 
					 | 
				
			||||||
    const { memoriesEnabled, notify, ...rest } = dto;
 | 
					 | 
				
			||||||
    let user = await this.userCore.createUser(rest);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // TODO remove and replace with entire dto.preferences config
 | 
					 | 
				
			||||||
    if (memoriesEnabled === false) {
 | 
					 | 
				
			||||||
      await this.userRepository.upsertMetadata(user.id, {
 | 
					 | 
				
			||||||
        key: UserMetadataKey.PREFERENCES,
 | 
					 | 
				
			||||||
        value: { memories: { enabled: false } },
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      user = await this.findOrFail(user.id, {});
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const tempPassword = user.shouldChangePassword ? rest.password : undefined;
 | 
					 | 
				
			||||||
    if (notify) {
 | 
					 | 
				
			||||||
      await this.jobRepository.queue({ name: JobName.NOTIFY_SIGNUP, data: { id: user.id, tempPassword } });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return mapUser(user);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  async update(auth: AuthDto, dto: UpdateUserDto): Promise<UserResponseDto> {
 | 
					 | 
				
			||||||
    const user = await this.findOrFail(dto.id, {});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (dto.quotaSizeInBytes && user.quotaSizeInBytes !== dto.quotaSizeInBytes) {
 | 
					 | 
				
			||||||
      await this.userRepository.syncUsage(dto.id);
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async updateMe({ user }: AuthDto, dto: UserUpdateMeDto): Promise<UserAdminResponseDto> {
 | 
				
			||||||
    // TODO replace with entire preferences object
 | 
					    // TODO replace with entire preferences object
 | 
				
			||||||
    if (dto.memoriesEnabled !== undefined || dto.avatarColor) {
 | 
					    if (dto.memoriesEnabled !== undefined || dto.avatarColor) {
 | 
				
			||||||
      const newPreferences = getPreferences(user);
 | 
					      const newPreferences = getPreferences(user);
 | 
				
			||||||
@ -101,42 +58,40 @@ export class UserService {
 | 
				
			|||||||
        delete dto.avatarColor;
 | 
					        delete dto.avatarColor;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      await this.userRepository.upsertMetadata(dto.id, {
 | 
					      await this.userRepository.upsertMetadata(user.id, {
 | 
				
			||||||
        key: UserMetadataKey.PREFERENCES,
 | 
					        key: UserMetadataKey.PREFERENCES,
 | 
				
			||||||
        value: getPreferencesPartial(user, newPreferences),
 | 
					        value: getPreferencesPartial(user, newPreferences),
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const updatedUser = await this.userCore.updateUser(auth.user, dto.id, dto);
 | 
					    if (dto.email) {
 | 
				
			||||||
 | 
					      const duplicate = await this.userRepository.getByEmail(dto.email);
 | 
				
			||||||
    return mapUser(updatedUser);
 | 
					      if (duplicate && duplicate.id !== user.id) {
 | 
				
			||||||
 | 
					        throw new BadRequestException('Email already in use by another account');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async delete(auth: AuthDto, id: string, dto: DeleteUserDto): Promise<UserResponseDto> {
 | 
					    const update: Partial<UserEntity> = {
 | 
				
			||||||
    const { force } = dto;
 | 
					      email: dto.email,
 | 
				
			||||||
    const { isAdmin } = await this.findOrFail(id, {});
 | 
					      name: dto.name,
 | 
				
			||||||
    if (isAdmin) {
 | 
					    };
 | 
				
			||||||
      throw new ForbiddenException('Cannot delete admin user');
 | 
					
 | 
				
			||||||
 | 
					    if (dto.password) {
 | 
				
			||||||
 | 
					      const hashedPassword = await this.cryptoRepository.hashBcrypt(dto.password, SALT_ROUNDS);
 | 
				
			||||||
 | 
					      update.password = hashedPassword;
 | 
				
			||||||
 | 
					      update.shouldChangePassword = false;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    await this.albumRepository.softDeleteAll(id);
 | 
					    const updatedUser = await this.userRepository.update(user.id, update);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const status = force ? UserStatus.REMOVING : UserStatus.DELETED;
 | 
					    return mapUserAdmin(updatedUser);
 | 
				
			||||||
    const user = await this.userRepository.update(id, { status, deletedAt: new Date() });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (force) {
 | 
					 | 
				
			||||||
      await this.jobRepository.queue({ name: JobName.USER_DELETION, data: { id: user.id, force } });
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async get(id: string): Promise<UserResponseDto> {
 | 
				
			||||||
 | 
					    const user = await this.findOrFail(id, { withDeleted: false });
 | 
				
			||||||
    return mapUser(user);
 | 
					    return mapUser(user);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async restore(auth: AuthDto, id: string): Promise<UserResponseDto> {
 | 
					 | 
				
			||||||
    await this.findOrFail(id, { withDeleted: true });
 | 
					 | 
				
			||||||
    await this.albumRepository.restoreAll(id);
 | 
					 | 
				
			||||||
    return this.userRepository.update(id, { deletedAt: null, status: UserStatus.ACTIVE }).then(mapUser);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  async createProfileImage(auth: AuthDto, fileInfo: Express.Multer.File): Promise<CreateProfileImageResponseDto> {
 | 
					  async createProfileImage(auth: AuthDto, fileInfo: Express.Multer.File): Promise<CreateProfileImageResponseDto> {
 | 
				
			||||||
    const { profileImagePath: oldpath } = await this.findOrFail(auth.user.id, { withDeleted: false });
 | 
					    const { profileImagePath: oldpath } = await this.findOrFail(auth.user.id, { withDeleted: false });
 | 
				
			||||||
    const updatedUser = await this.userRepository.update(auth.user.id, { profileImagePath: fileInfo.path });
 | 
					    const updatedUser = await this.userRepository.update(auth.user.id, { profileImagePath: fileInfo.path });
 | 
				
			||||||
 | 
				
			|||||||
@ -154,7 +154,7 @@ export function validateCronExpression(expression: string) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type IValue = { value: string };
 | 
					type IValue = { value: string };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const toEmail = ({ value }: IValue) => value?.toLowerCase();
 | 
					export const toEmail = ({ value }: IValue) => (value ? value.toLowerCase() : value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const toSanitized = ({ value }: IValue) => sanitize((value || '').replaceAll('.', ''));
 | 
					export const toSanitized = ({ value }: IValue) => sanitize((value || '').replaceAll('.', ''));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
  import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte';
 | 
					  import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte';
 | 
				
			||||||
  import { handleError } from '$lib/utils/handle-error';
 | 
					  import { handleError } from '$lib/utils/handle-error';
 | 
				
			||||||
  import { deleteUser, type UserResponseDto } from '@immich/sdk';
 | 
					  import { deleteUserAdmin, type UserResponseDto } from '@immich/sdk';
 | 
				
			||||||
  import { serverConfig } from '$lib/stores/server-config.store';
 | 
					  import { serverConfig } from '$lib/stores/server-config.store';
 | 
				
			||||||
  import { createEventDispatcher } from 'svelte';
 | 
					  import { createEventDispatcher } from 'svelte';
 | 
				
			||||||
  import Checkbox from '$lib/components/elements/checkbox.svelte';
 | 
					  import Checkbox from '$lib/components/elements/checkbox.svelte';
 | 
				
			||||||
@ -20,9 +20,9 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  const handleDeleteUser = async () => {
 | 
					  const handleDeleteUser = async () => {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const { deletedAt } = await deleteUser({
 | 
					      const { deletedAt } = await deleteUserAdmin({
 | 
				
			||||||
        id: user.id,
 | 
					        id: user.id,
 | 
				
			||||||
        deleteUserDto: { force: forceDelete },
 | 
					        userAdminDeleteDto: { force: forceDelete },
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (deletedAt == undefined) {
 | 
					      if (deletedAt == undefined) {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
  import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte';
 | 
					  import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte';
 | 
				
			||||||
  import { handleError } from '$lib/utils/handle-error';
 | 
					  import { handleError } from '$lib/utils/handle-error';
 | 
				
			||||||
  import { restoreUser, type UserResponseDto } from '@immich/sdk';
 | 
					  import { restoreUserAdmin, type UserResponseDto } from '@immich/sdk';
 | 
				
			||||||
  import { createEventDispatcher } from 'svelte';
 | 
					  import { createEventDispatcher } from 'svelte';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  export let user: UserResponseDto;
 | 
					  export let user: UserResponseDto;
 | 
				
			||||||
@ -14,7 +14,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  const handleRestoreUser = async () => {
 | 
					  const handleRestoreUser = async () => {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const { deletedAt } = await restoreUser({ id: user.id });
 | 
					      const { deletedAt } = await restoreUserAdmin({ id: user.id });
 | 
				
			||||||
      if (deletedAt == undefined) {
 | 
					      if (deletedAt == undefined) {
 | 
				
			||||||
        dispatch('success');
 | 
					        dispatch('success');
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
  import {
 | 
					  import {
 | 
				
			||||||
    getMyUserInfo,
 | 
					    getMyUser,
 | 
				
			||||||
    removeUserFromAlbum,
 | 
					    removeUserFromAlbum,
 | 
				
			||||||
    type AlbumResponseDto,
 | 
					    type AlbumResponseDto,
 | 
				
			||||||
    type UserResponseDto,
 | 
					    type UserResponseDto,
 | 
				
			||||||
@ -36,7 +36,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  onMount(async () => {
 | 
					  onMount(async () => {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      currentUser = await getMyUserInfo();
 | 
					      currentUser = await getMyUser();
 | 
				
			||||||
    } catch (error) {
 | 
					    } catch (error) {
 | 
				
			||||||
      handleError(error, 'Unable to refresh user');
 | 
					      handleError(error, 'Unable to refresh user');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -7,7 +7,7 @@
 | 
				
			|||||||
  import {
 | 
					  import {
 | 
				
			||||||
    AlbumUserRole,
 | 
					    AlbumUserRole,
 | 
				
			||||||
    getAllSharedLinks,
 | 
					    getAllSharedLinks,
 | 
				
			||||||
    getAllUsers,
 | 
					    searchUsers,
 | 
				
			||||||
    type AlbumResponseDto,
 | 
					    type AlbumResponseDto,
 | 
				
			||||||
    type AlbumUserAddDto,
 | 
					    type AlbumUserAddDto,
 | 
				
			||||||
    type SharedLinkResponseDto,
 | 
					    type SharedLinkResponseDto,
 | 
				
			||||||
@ -36,10 +36,10 @@
 | 
				
			|||||||
  let sharedLinks: SharedLinkResponseDto[] = [];
 | 
					  let sharedLinks: SharedLinkResponseDto[] = [];
 | 
				
			||||||
  onMount(async () => {
 | 
					  onMount(async () => {
 | 
				
			||||||
    await getSharedLinks();
 | 
					    await getSharedLinks();
 | 
				
			||||||
    const data = await getAllUsers({ isAll: false });
 | 
					    const data = await searchUsers();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // remove invalid users
 | 
					    // remove album owner
 | 
				
			||||||
    users = data.filter((user) => !(user.deletedAt || user.id === album.ownerId));
 | 
					    users = data.filter((user) => user.id !== album.ownerId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Remove the existed shared users from the album
 | 
					    // Remove the existed shared users from the album
 | 
				
			||||||
    for (const sharedUser of album.albumUsers) {
 | 
					    for (const sharedUser of album.albumUsers) {
 | 
				
			||||||
 | 
				
			|||||||
@ -2,9 +2,8 @@
 | 
				
			|||||||
  import { createEventDispatcher } from 'svelte';
 | 
					  import { createEventDispatcher } from 'svelte';
 | 
				
			||||||
  import Button from '../elements/buttons/button.svelte';
 | 
					  import Button from '../elements/buttons/button.svelte';
 | 
				
			||||||
  import PasswordField from '../shared-components/password-field.svelte';
 | 
					  import PasswordField from '../shared-components/password-field.svelte';
 | 
				
			||||||
  import { updateUser, type UserResponseDto } from '@immich/sdk';
 | 
					  import { updateMyUser } from '@immich/sdk';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  export let user: UserResponseDto;
 | 
					 | 
				
			||||||
  let errorMessage: string;
 | 
					  let errorMessage: string;
 | 
				
			||||||
  let success: string;
 | 
					  let success: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -31,13 +30,7 @@
 | 
				
			|||||||
    if (valid) {
 | 
					    if (valid) {
 | 
				
			||||||
      errorMessage = '';
 | 
					      errorMessage = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      await updateUser({
 | 
					      await updateMyUser({ userUpdateMeDto: { password: String(password) } });
 | 
				
			||||||
        updateUserDto: {
 | 
					 | 
				
			||||||
          id: user.id,
 | 
					 | 
				
			||||||
          password: String(password),
 | 
					 | 
				
			||||||
          shouldChangePassword: false,
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      dispatch('success');
 | 
					      dispatch('success');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,7 @@
 | 
				
			|||||||
  import { serverInfo } from '$lib/stores/server-info.store';
 | 
					  import { serverInfo } from '$lib/stores/server-info.store';
 | 
				
			||||||
  import { convertToBytes } from '$lib/utils/byte-converter';
 | 
					  import { convertToBytes } from '$lib/utils/byte-converter';
 | 
				
			||||||
  import { handleError } from '$lib/utils/handle-error';
 | 
					  import { handleError } from '$lib/utils/handle-error';
 | 
				
			||||||
  import { createUser } from '@immich/sdk';
 | 
					  import { createUserAdmin } from '@immich/sdk';
 | 
				
			||||||
  import { createEventDispatcher } from 'svelte';
 | 
					  import { createEventDispatcher } from 'svelte';
 | 
				
			||||||
  import Button from '../elements/buttons/button.svelte';
 | 
					  import Button from '../elements/buttons/button.svelte';
 | 
				
			||||||
  import PasswordField from '../shared-components/password-field.svelte';
 | 
					  import PasswordField from '../shared-components/password-field.svelte';
 | 
				
			||||||
@ -49,8 +49,8 @@
 | 
				
			|||||||
      error = '';
 | 
					      error = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      try {
 | 
					      try {
 | 
				
			||||||
        await createUser({
 | 
					        await createUserAdmin({
 | 
				
			||||||
          createUserDto: {
 | 
					          userAdminCreateDto: {
 | 
				
			||||||
            email,
 | 
					            email,
 | 
				
			||||||
            password,
 | 
					            password,
 | 
				
			||||||
            shouldChangePassword,
 | 
					            shouldChangePassword,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,16 +1,16 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
  import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte';
 | 
					  import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte';
 | 
				
			||||||
 | 
					  import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
 | 
				
			||||||
  import { AppRoute } from '$lib/constants';
 | 
					  import { AppRoute } from '$lib/constants';
 | 
				
			||||||
  import { serverInfo } from '$lib/stores/server-info.store';
 | 
					  import { serverInfo } from '$lib/stores/server-info.store';
 | 
				
			||||||
  import { convertFromBytes, convertToBytes } from '$lib/utils/byte-converter';
 | 
					  import { convertFromBytes, convertToBytes } from '$lib/utils/byte-converter';
 | 
				
			||||||
  import { handleError } from '$lib/utils/handle-error';
 | 
					  import { handleError } from '$lib/utils/handle-error';
 | 
				
			||||||
  import { updateUser, type UserResponseDto } from '@immich/sdk';
 | 
					  import { updateUserAdmin, type UserAdminResponseDto } from '@immich/sdk';
 | 
				
			||||||
 | 
					  import { mdiAccountEditOutline } from '@mdi/js';
 | 
				
			||||||
  import { createEventDispatcher } from 'svelte';
 | 
					  import { createEventDispatcher } from 'svelte';
 | 
				
			||||||
  import Button from '../elements/buttons/button.svelte';
 | 
					  import Button from '../elements/buttons/button.svelte';
 | 
				
			||||||
  import FullScreenModal from '$lib/components/shared-components/full-screen-modal.svelte';
 | 
					 | 
				
			||||||
  import { mdiAccountEditOutline } from '@mdi/js';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  export let user: UserResponseDto;
 | 
					  export let user: UserAdminResponseDto;
 | 
				
			||||||
  export let canResetPassword = true;
 | 
					  export let canResetPassword = true;
 | 
				
			||||||
  export let newPassword: string;
 | 
					  export let newPassword: string;
 | 
				
			||||||
  export let onClose: () => void;
 | 
					  export let onClose: () => void;
 | 
				
			||||||
@ -36,9 +36,9 @@
 | 
				
			|||||||
  const editUser = async () => {
 | 
					  const editUser = async () => {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const { id, email, name, storageLabel } = user;
 | 
					      const { id, email, name, storageLabel } = user;
 | 
				
			||||||
      await updateUser({
 | 
					      await updateUserAdmin({
 | 
				
			||||||
        updateUserDto: {
 | 
					 | 
				
			||||||
        id,
 | 
					        id,
 | 
				
			||||||
 | 
					        userAdminUpdateDto: {
 | 
				
			||||||
          email,
 | 
					          email,
 | 
				
			||||||
          name,
 | 
					          name,
 | 
				
			||||||
          storageLabel: storageLabel || '',
 | 
					          storageLabel: storageLabel || '',
 | 
				
			||||||
@ -56,9 +56,9 @@
 | 
				
			|||||||
    try {
 | 
					    try {
 | 
				
			||||||
      newPassword = generatePassword();
 | 
					      newPassword = generatePassword();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      await updateUser({
 | 
					      await updateUserAdmin({
 | 
				
			||||||
        updateUserDto: {
 | 
					 | 
				
			||||||
        id: user.id,
 | 
					        id: user.id,
 | 
				
			||||||
 | 
					        userAdminUpdateDto: {
 | 
				
			||||||
          password: newPassword,
 | 
					          password: newPassword,
 | 
				
			||||||
          shouldChangePassword: true,
 | 
					          shouldChangePassword: true,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,7 @@
 | 
				
			|||||||
  import FullScreenModal from '../shared-components/full-screen-modal.svelte';
 | 
					  import FullScreenModal from '../shared-components/full-screen-modal.svelte';
 | 
				
			||||||
  import { mdiFolderSync } from '@mdi/js';
 | 
					  import { mdiFolderSync } from '@mdi/js';
 | 
				
			||||||
  import { onMount } from 'svelte';
 | 
					  import { onMount } from 'svelte';
 | 
				
			||||||
  import { getAllUsers } from '@immich/sdk';
 | 
					  import { searchUsersAdmin } from '@immich/sdk';
 | 
				
			||||||
  import { user } from '$lib/stores/user.store';
 | 
					  import { user } from '$lib/stores/user.store';
 | 
				
			||||||
  import SettingSelect from '$lib/components/shared-components/settings/setting-select.svelte';
 | 
					  import SettingSelect from '$lib/components/shared-components/settings/setting-select.svelte';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -13,7 +13,7 @@
 | 
				
			|||||||
  let userOptions: { value: string; text: string }[] = [];
 | 
					  let userOptions: { value: string; text: string }[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  onMount(async () => {
 | 
					  onMount(async () => {
 | 
				
			||||||
    const users = await getAllUsers({ isAll: true });
 | 
					    const users = await searchUsersAdmin({});
 | 
				
			||||||
    userOptions = users.map((user) => ({ value: user.id, text: user.name }));
 | 
					    userOptions = users.map((user) => ({ value: user.id, text: user.name }));
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -4,7 +4,7 @@
 | 
				
			|||||||
  import { AppRoute } from '$lib/constants';
 | 
					  import { AppRoute } from '$lib/constants';
 | 
				
			||||||
  import { user } from '$lib/stores/user.store';
 | 
					  import { user } from '$lib/stores/user.store';
 | 
				
			||||||
  import { handleError } from '$lib/utils/handle-error';
 | 
					  import { handleError } from '$lib/utils/handle-error';
 | 
				
			||||||
  import { deleteProfileImage, updateUser, type UserAvatarColor } from '@immich/sdk';
 | 
					  import { deleteProfileImage, updateMyUser, type UserAvatarColor } from '@immich/sdk';
 | 
				
			||||||
  import { mdiCog, mdiLogout, mdiPencil } from '@mdi/js';
 | 
					  import { mdiCog, mdiLogout, mdiPencil } from '@mdi/js';
 | 
				
			||||||
  import { createEventDispatcher } from 'svelte';
 | 
					  import { createEventDispatcher } from 'svelte';
 | 
				
			||||||
  import { fade } from 'svelte/transition';
 | 
					  import { fade } from 'svelte/transition';
 | 
				
			||||||
@ -27,9 +27,8 @@
 | 
				
			|||||||
        await deleteProfileImage();
 | 
					        await deleteProfileImage();
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      $user = await updateUser({
 | 
					      $user = await updateMyUser({
 | 
				
			||||||
        updateUserDto: {
 | 
					        userUpdateMeDto: {
 | 
				
			||||||
          id: $user.id,
 | 
					 | 
				
			||||||
          email: $user.email,
 | 
					          email: $user.email,
 | 
				
			||||||
          name: $user.name,
 | 
					          name: $user.name,
 | 
				
			||||||
          avatarColor: color,
 | 
					          avatarColor: color,
 | 
				
			||||||
 | 
				
			|||||||
@ -3,23 +3,18 @@
 | 
				
			|||||||
    notificationController,
 | 
					    notificationController,
 | 
				
			||||||
    NotificationType,
 | 
					    NotificationType,
 | 
				
			||||||
  } from '$lib/components/shared-components/notification/notification';
 | 
					  } from '$lib/components/shared-components/notification/notification';
 | 
				
			||||||
  import { updateUser, type UserResponseDto } from '@immich/sdk';
 | 
					  import { updateMyUser, type UserAdminResponseDto } from '@immich/sdk';
 | 
				
			||||||
  import { fade } from 'svelte/transition';
 | 
					  import { fade } from 'svelte/transition';
 | 
				
			||||||
  import { handleError } from '../../utils/handle-error';
 | 
					  import { handleError } from '../../utils/handle-error';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  import Button from '../elements/buttons/button.svelte';
 | 
					 | 
				
			||||||
  import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
 | 
					  import SettingSwitch from '$lib/components/shared-components/settings/setting-switch.svelte';
 | 
				
			||||||
 | 
					  import Button from '../elements/buttons/button.svelte';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  export let user: UserResponseDto;
 | 
					  export let user: UserAdminResponseDto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleSave = async () => {
 | 
					  const handleSave = async () => {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const data = await updateUser({
 | 
					      const data = await updateMyUser({ userUpdateMeDto: { memoriesEnabled: user.memoriesEnabled } });
 | 
				
			||||||
        updateUserDto: {
 | 
					 | 
				
			||||||
          id: user.id,
 | 
					 | 
				
			||||||
          memoriesEnabled: user.memoriesEnabled,
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      Object.assign(user, data);
 | 
					      Object.assign(user, data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,7 @@
 | 
				
			|||||||
  import { goto } from '$app/navigation';
 | 
					  import { goto } from '$app/navigation';
 | 
				
			||||||
  import { featureFlags } from '$lib/stores/server-config.store';
 | 
					  import { featureFlags } from '$lib/stores/server-config.store';
 | 
				
			||||||
  import { oauth } from '$lib/utils';
 | 
					  import { oauth } from '$lib/utils';
 | 
				
			||||||
  import { type UserResponseDto } from '@immich/sdk';
 | 
					  import { type UserAdminResponseDto } from '@immich/sdk';
 | 
				
			||||||
  import { onMount } from 'svelte';
 | 
					  import { onMount } from 'svelte';
 | 
				
			||||||
  import { fade } from 'svelte/transition';
 | 
					  import { fade } from 'svelte/transition';
 | 
				
			||||||
  import { handleError } from '../../utils/handle-error';
 | 
					  import { handleError } from '../../utils/handle-error';
 | 
				
			||||||
@ -10,7 +10,7 @@
 | 
				
			|||||||
  import LoadingSpinner from '../shared-components/loading-spinner.svelte';
 | 
					  import LoadingSpinner from '../shared-components/loading-spinner.svelte';
 | 
				
			||||||
  import { notificationController, NotificationType } from '../shared-components/notification/notification';
 | 
					  import { notificationController, NotificationType } from '../shared-components/notification/notification';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  export let user: UserResponseDto;
 | 
					  export let user: UserAdminResponseDto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let loading = true;
 | 
					  let loading = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
  import { getAllUsers, getPartners, type UserResponseDto } from '@immich/sdk';
 | 
					  import { searchUsers, getPartners, type UserResponseDto } from '@immich/sdk';
 | 
				
			||||||
  import { createEventDispatcher, onMount } from 'svelte';
 | 
					  import { createEventDispatcher, onMount } from 'svelte';
 | 
				
			||||||
  import Button from '../elements/buttons/button.svelte';
 | 
					  import Button from '../elements/buttons/button.svelte';
 | 
				
			||||||
  import UserAvatar from '../shared-components/user-avatar.svelte';
 | 
					  import UserAvatar from '../shared-components/user-avatar.svelte';
 | 
				
			||||||
@ -14,11 +14,10 @@
 | 
				
			|||||||
  const dispatch = createEventDispatcher<{ 'add-users': UserResponseDto[] }>();
 | 
					  const dispatch = createEventDispatcher<{ 'add-users': UserResponseDto[] }>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  onMount(async () => {
 | 
					  onMount(async () => {
 | 
				
			||||||
    // TODO: update endpoint to have a query param for deleted users
 | 
					    let users = await searchUsers();
 | 
				
			||||||
    let users = await getAllUsers({ isAll: false });
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // remove invalid users
 | 
					    // remove current user
 | 
				
			||||||
    users = users.filter((_user) => !(_user.deletedAt || _user.id === user.id));
 | 
					    users = users.filter((_user) => _user.id !== user.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // exclude partners from the list of users available for selection
 | 
					    // exclude partners from the list of users available for selection
 | 
				
			||||||
    const partners = await getPartners({ direction: 'shared-by' });
 | 
					    const partners = await getPartners({ direction: 'shared-by' });
 | 
				
			||||||
 | 
				
			|||||||
@ -3,23 +3,22 @@
 | 
				
			|||||||
    notificationController,
 | 
					    notificationController,
 | 
				
			||||||
    NotificationType,
 | 
					    NotificationType,
 | 
				
			||||||
  } from '$lib/components/shared-components/notification/notification';
 | 
					  } from '$lib/components/shared-components/notification/notification';
 | 
				
			||||||
  import { fade } from 'svelte/transition';
 | 
					 | 
				
			||||||
  import { handleError } from '../../utils/handle-error';
 | 
					 | 
				
			||||||
  import Button from '../elements/buttons/button.svelte';
 | 
					 | 
				
			||||||
  import { user } from '$lib/stores/user.store';
 | 
					 | 
				
			||||||
  import { cloneDeep } from 'lodash-es';
 | 
					 | 
				
			||||||
  import { updateUser } from '@immich/sdk';
 | 
					 | 
				
			||||||
  import SettingInputField, {
 | 
					  import SettingInputField, {
 | 
				
			||||||
    SettingInputFieldType,
 | 
					    SettingInputFieldType,
 | 
				
			||||||
  } from '$lib/components/shared-components/settings/setting-input-field.svelte';
 | 
					  } from '$lib/components/shared-components/settings/setting-input-field.svelte';
 | 
				
			||||||
 | 
					  import { user } from '$lib/stores/user.store';
 | 
				
			||||||
 | 
					  import { updateMyUser } from '@immich/sdk';
 | 
				
			||||||
 | 
					  import { cloneDeep } from 'lodash-es';
 | 
				
			||||||
 | 
					  import { fade } from 'svelte/transition';
 | 
				
			||||||
 | 
					  import { handleError } from '../../utils/handle-error';
 | 
				
			||||||
 | 
					  import Button from '../elements/buttons/button.svelte';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let editedUser = cloneDeep($user);
 | 
					  let editedUser = cloneDeep($user);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const handleSaveProfile = async () => {
 | 
					  const handleSaveProfile = async () => {
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      const data = await updateUser({
 | 
					      const data = await updateMyUser({
 | 
				
			||||||
        updateUserDto: {
 | 
					        userUpdateMeDto: {
 | 
				
			||||||
          id: editedUser.id,
 | 
					 | 
				
			||||||
          email: editedUser.email,
 | 
					          email: editedUser.email,
 | 
				
			||||||
          name: editedUser.name,
 | 
					          name: editedUser.name,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
				
			|||||||
@ -1,12 +1,12 @@
 | 
				
			|||||||
import type { UserResponseDto } from '@immich/sdk';
 | 
					import type { UserAdminResponseDto } from '@immich/sdk';
 | 
				
			||||||
import { writable } from 'svelte/store';
 | 
					import { writable } from 'svelte/store';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const user = writable<UserResponseDto>();
 | 
					export const user = writable<UserAdminResponseDto>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Reset the store to its initial undefined value. Make sure to
 | 
					 * Reset the store to its initial undefined value. Make sure to
 | 
				
			||||||
 * only do this _after_ redirecting to an unauthenticated page.
 | 
					 * only do this _after_ redirecting to an unauthenticated page.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export const resetSavedUser = () => {
 | 
					export const resetSavedUser = () => {
 | 
				
			||||||
  user.set(undefined as unknown as UserResponseDto);
 | 
					  user.set(undefined as unknown as UserAdminResponseDto);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -11,7 +11,6 @@ import {
 | 
				
			|||||||
  startOAuth,
 | 
					  startOAuth,
 | 
				
			||||||
  unlinkOAuthAccount,
 | 
					  unlinkOAuthAccount,
 | 
				
			||||||
  type SharedLinkResponseDto,
 | 
					  type SharedLinkResponseDto,
 | 
				
			||||||
  type UserResponseDto,
 | 
					 | 
				
			||||||
} from '@immich/sdk';
 | 
					} from '@immich/sdk';
 | 
				
			||||||
import { mdiCogRefreshOutline, mdiDatabaseRefreshOutline, mdiImageRefreshOutline } from '@mdi/js';
 | 
					import { mdiCogRefreshOutline, mdiDatabaseRefreshOutline, mdiImageRefreshOutline } from '@mdi/js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -264,7 +263,7 @@ export const oauth = {
 | 
				
			|||||||
  login: (location: Location) => {
 | 
					  login: (location: Location) => {
 | 
				
			||||||
    return finishOAuth({ oAuthCallbackDto: { url: location.href } });
 | 
					    return finishOAuth({ oAuthCallbackDto: { url: location.href } });
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  link: (location: Location): Promise<UserResponseDto> => {
 | 
					  link: (location: Location) => {
 | 
				
			||||||
    return linkOAuthAccount({ oAuthCallbackDto: { url: location.href } });
 | 
					    return linkOAuthAccount({ oAuthCallbackDto: { url: location.href } });
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  unlink: () => {
 | 
					  unlink: () => {
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
import { browser } from '$app/environment';
 | 
					import { browser } from '$app/environment';
 | 
				
			||||||
import { serverInfo } from '$lib/stores/server-info.store';
 | 
					import { serverInfo } from '$lib/stores/server-info.store';
 | 
				
			||||||
import { user } from '$lib/stores/user.store';
 | 
					import { user } from '$lib/stores/user.store';
 | 
				
			||||||
import { getMyUserInfo, getStorage } from '@immich/sdk';
 | 
					import { getMyUser, getStorage } from '@immich/sdk';
 | 
				
			||||||
import { redirect } from '@sveltejs/kit';
 | 
					import { redirect } from '@sveltejs/kit';
 | 
				
			||||||
import { get } from 'svelte/store';
 | 
					import { get } from 'svelte/store';
 | 
				
			||||||
import { AppRoute } from '../constants';
 | 
					import { AppRoute } from '../constants';
 | 
				
			||||||
@ -15,7 +15,7 @@ export const loadUser = async () => {
 | 
				
			|||||||
  try {
 | 
					  try {
 | 
				
			||||||
    let loaded = get(user);
 | 
					    let loaded = get(user);
 | 
				
			||||||
    if (!loaded && hasAuthCookie()) {
 | 
					    if (!loaded && hasAuthCookie()) {
 | 
				
			||||||
      loaded = await getMyUserInfo();
 | 
					      loaded = await getMyUser();
 | 
				
			||||||
      user.set(loaded);
 | 
					      user.set(loaded);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return loaded;
 | 
					    return loaded;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,12 +1,12 @@
 | 
				
			|||||||
import { authenticate } from '$lib/utils/auth';
 | 
					import { authenticate } from '$lib/utils/auth';
 | 
				
			||||||
import { getAssetInfoFromParam } from '$lib/utils/navigation';
 | 
					import { getAssetInfoFromParam } from '$lib/utils/navigation';
 | 
				
			||||||
import { getUserById } from '@immich/sdk';
 | 
					import { getUser } from '@immich/sdk';
 | 
				
			||||||
import type { PageLoad } from './$types';
 | 
					import type { PageLoad } from './$types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const load = (async ({ params }) => {
 | 
					export const load = (async ({ params }) => {
 | 
				
			||||||
  await authenticate();
 | 
					  await authenticate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const partner = await getUserById({ id: params.userId });
 | 
					  const partner = await getUser({ id: params.userId });
 | 
				
			||||||
  const asset = await getAssetInfoFromParam(params);
 | 
					  const asset = await getAssetInfoFromParam(params);
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    asset,
 | 
					    asset,
 | 
				
			||||||
 | 
				
			|||||||
@ -23,7 +23,7 @@
 | 
				
			|||||||
    deleteLibrary,
 | 
					    deleteLibrary,
 | 
				
			||||||
    getAllLibraries,
 | 
					    getAllLibraries,
 | 
				
			||||||
    getLibraryStatistics,
 | 
					    getLibraryStatistics,
 | 
				
			||||||
    getUserById,
 | 
					    getUserAdmin,
 | 
				
			||||||
    removeOfflineFiles,
 | 
					    removeOfflineFiles,
 | 
				
			||||||
    scanLibrary,
 | 
					    scanLibrary,
 | 
				
			||||||
    updateLibrary,
 | 
					    updateLibrary,
 | 
				
			||||||
@ -99,7 +99,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  const refreshStats = async (listIndex: number) => {
 | 
					  const refreshStats = async (listIndex: number) => {
 | 
				
			||||||
    stats[listIndex] = await getLibraryStatistics({ id: libraries[listIndex].id });
 | 
					    stats[listIndex] = await getLibraryStatistics({ id: libraries[listIndex].id });
 | 
				
			||||||
    owner[listIndex] = await getUserById({ id: libraries[listIndex].ownerId });
 | 
					    owner[listIndex] = await getUserAdmin({ id: libraries[listIndex].ownerId });
 | 
				
			||||||
    photos[listIndex] = stats[listIndex].photos;
 | 
					    photos[listIndex] = stats[listIndex].photos;
 | 
				
			||||||
    videos[listIndex] = stats[listIndex].videos;
 | 
					    videos[listIndex] = stats[listIndex].videos;
 | 
				
			||||||
    totalCount[listIndex] = stats[listIndex].total;
 | 
					    totalCount[listIndex] = stats[listIndex].total;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,11 @@
 | 
				
			|||||||
import { authenticate, requestServerInfo } from '$lib/utils/auth';
 | 
					import { authenticate, requestServerInfo } from '$lib/utils/auth';
 | 
				
			||||||
import { getAllUsers } from '@immich/sdk';
 | 
					import { searchUsersAdmin } from '@immich/sdk';
 | 
				
			||||||
import type { PageLoad } from './$types';
 | 
					import type { PageLoad } from './$types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const load = (async () => {
 | 
					export const load = (async () => {
 | 
				
			||||||
  await authenticate({ admin: true });
 | 
					  await authenticate({ admin: true });
 | 
				
			||||||
  await requestServerInfo();
 | 
					  await requestServerInfo();
 | 
				
			||||||
  const allUsers = await getAllUsers({ isAll: false });
 | 
					  const allUsers = await searchUsersAdmin({ withDeleted: false });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    allUsers,
 | 
					    allUsers,
 | 
				
			||||||
 | 
				
			|||||||
@ -1,14 +1,15 @@
 | 
				
			|||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
  import { page } from '$app/stores';
 | 
					  import { page } from '$app/stores';
 | 
				
			||||||
  import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte';
 | 
					 | 
				
			||||||
  import DeleteConfirmDialog from '$lib/components/admin-page/delete-confirm-dialogue.svelte';
 | 
					  import DeleteConfirmDialog from '$lib/components/admin-page/delete-confirm-dialogue.svelte';
 | 
				
			||||||
  import LinkButton from '$lib/components/elements/buttons/link-button.svelte';
 | 
					 | 
				
			||||||
  import RestoreDialogue from '$lib/components/admin-page/restore-dialogue.svelte';
 | 
					  import RestoreDialogue from '$lib/components/admin-page/restore-dialogue.svelte';
 | 
				
			||||||
  import Button from '$lib/components/elements/buttons/button.svelte';
 | 
					  import Button from '$lib/components/elements/buttons/button.svelte';
 | 
				
			||||||
 | 
					  import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
 | 
				
			||||||
 | 
					  import LinkButton from '$lib/components/elements/buttons/link-button.svelte';
 | 
				
			||||||
  import Icon from '$lib/components/elements/icon.svelte';
 | 
					  import Icon from '$lib/components/elements/icon.svelte';
 | 
				
			||||||
  import CreateUserForm from '$lib/components/forms/create-user-form.svelte';
 | 
					  import CreateUserForm from '$lib/components/forms/create-user-form.svelte';
 | 
				
			||||||
  import EditUserForm from '$lib/components/forms/edit-user-form.svelte';
 | 
					  import EditUserForm from '$lib/components/forms/edit-user-form.svelte';
 | 
				
			||||||
  import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
 | 
					  import UserPageLayout from '$lib/components/layouts/user-page-layout.svelte';
 | 
				
			||||||
 | 
					  import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte';
 | 
				
			||||||
  import {
 | 
					  import {
 | 
				
			||||||
    NotificationType,
 | 
					    NotificationType,
 | 
				
			||||||
    notificationController,
 | 
					    notificationController,
 | 
				
			||||||
@ -17,28 +18,27 @@
 | 
				
			|||||||
  import { serverConfig } from '$lib/stores/server-config.store';
 | 
					  import { serverConfig } from '$lib/stores/server-config.store';
 | 
				
			||||||
  import { user } from '$lib/stores/user.store';
 | 
					  import { user } from '$lib/stores/user.store';
 | 
				
			||||||
  import { websocketEvents } from '$lib/stores/websocket';
 | 
					  import { websocketEvents } from '$lib/stores/websocket';
 | 
				
			||||||
  import { asByteUnitString } from '$lib/utils/byte-units';
 | 
					 | 
				
			||||||
  import { copyToClipboard } from '$lib/utils';
 | 
					  import { copyToClipboard } from '$lib/utils';
 | 
				
			||||||
  import { UserStatus, getAllUsers, type UserResponseDto } from '@immich/sdk';
 | 
					  import { asByteUnitString } from '$lib/utils/byte-units';
 | 
				
			||||||
 | 
					  import { UserStatus, searchUsersAdmin, type UserAdminResponseDto } from '@immich/sdk';
 | 
				
			||||||
  import { mdiClose, mdiContentCopy, mdiDeleteRestore, mdiPencilOutline, mdiTrashCanOutline } from '@mdi/js';
 | 
					  import { mdiClose, mdiContentCopy, mdiDeleteRestore, mdiPencilOutline, mdiTrashCanOutline } from '@mdi/js';
 | 
				
			||||||
  import { DateTime } from 'luxon';
 | 
					  import { DateTime } from 'luxon';
 | 
				
			||||||
  import { onMount } from 'svelte';
 | 
					  import { onMount } from 'svelte';
 | 
				
			||||||
  import type { PageData } from './$types';
 | 
					  import type { PageData } from './$types';
 | 
				
			||||||
  import CircleIconButton from '$lib/components/elements/buttons/circle-icon-button.svelte';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  export let data: PageData;
 | 
					  export let data: PageData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let allUsers: UserResponseDto[] = [];
 | 
					  let allUsers: UserAdminResponseDto[] = [];
 | 
				
			||||||
  let shouldShowEditUserForm = false;
 | 
					  let shouldShowEditUserForm = false;
 | 
				
			||||||
  let shouldShowCreateUserForm = false;
 | 
					  let shouldShowCreateUserForm = false;
 | 
				
			||||||
  let shouldShowPasswordResetSuccess = false;
 | 
					  let shouldShowPasswordResetSuccess = false;
 | 
				
			||||||
  let shouldShowDeleteConfirmDialog = false;
 | 
					  let shouldShowDeleteConfirmDialog = false;
 | 
				
			||||||
  let shouldShowRestoreDialog = false;
 | 
					  let shouldShowRestoreDialog = false;
 | 
				
			||||||
  let selectedUser: UserResponseDto;
 | 
					  let selectedUser: UserAdminResponseDto;
 | 
				
			||||||
  let newPassword: string;
 | 
					  let newPassword: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const refresh = async () => {
 | 
					  const refresh = async () => {
 | 
				
			||||||
    allUsers = await getAllUsers({ isAll: false });
 | 
					    allUsers = await searchUsersAdmin({ withDeleted: true });
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const onDeleteSuccess = (userId: string) => {
 | 
					  const onDeleteSuccess = (userId: string) => {
 | 
				
			||||||
@ -75,7 +75,7 @@
 | 
				
			|||||||
    shouldShowCreateUserForm = false;
 | 
					    shouldShowCreateUserForm = false;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const editUserHandler = (user: UserResponseDto) => {
 | 
					  const editUserHandler = (user: UserAdminResponseDto) => {
 | 
				
			||||||
    selectedUser = user;
 | 
					    selectedUser = user;
 | 
				
			||||||
    shouldShowEditUserForm = true;
 | 
					    shouldShowEditUserForm = true;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
@ -91,7 +91,7 @@
 | 
				
			|||||||
    shouldShowPasswordResetSuccess = true;
 | 
					    shouldShowPasswordResetSuccess = true;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const deleteUserHandler = (user: UserResponseDto) => {
 | 
					  const deleteUserHandler = (user: UserAdminResponseDto) => {
 | 
				
			||||||
    selectedUser = user;
 | 
					    selectedUser = user;
 | 
				
			||||||
    shouldShowDeleteConfirmDialog = true;
 | 
					    shouldShowDeleteConfirmDialog = true;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
@ -101,7 +101,7 @@
 | 
				
			|||||||
    shouldShowDeleteConfirmDialog = false;
 | 
					    shouldShowDeleteConfirmDialog = false;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const restoreUserHandler = (user: UserResponseDto) => {
 | 
					  const restoreUserHandler = (user: UserAdminResponseDto) => {
 | 
				
			||||||
    selectedUser = user;
 | 
					    selectedUser = user;
 | 
				
			||||||
    shouldShowRestoreDialog = true;
 | 
					    shouldShowRestoreDialog = true;
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,11 @@
 | 
				
			|||||||
import { authenticate, requestServerInfo } from '$lib/utils/auth';
 | 
					import { authenticate, requestServerInfo } from '$lib/utils/auth';
 | 
				
			||||||
import { getAllUsers } from '@immich/sdk';
 | 
					import { searchUsersAdmin } from '@immich/sdk';
 | 
				
			||||||
import type { PageLoad } from './$types';
 | 
					import type { PageLoad } from './$types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const load = (async () => {
 | 
					export const load = (async () => {
 | 
				
			||||||
  await authenticate({ admin: true });
 | 
					  await authenticate({ admin: true });
 | 
				
			||||||
  await requestServerInfo();
 | 
					  await requestServerInfo();
 | 
				
			||||||
  const allUsers = await getAllUsers({ isAll: false });
 | 
					  const allUsers = await searchUsersAdmin({ withDeleted: true });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    allUsers,
 | 
					    allUsers,
 | 
				
			||||||
 | 
				
			|||||||
@ -25,5 +25,5 @@
 | 
				
			|||||||
    enter the new password below.
 | 
					    enter the new password below.
 | 
				
			||||||
  </p>
 | 
					  </p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <ChangePasswordForm user={$user} on:success={onSuccess} />
 | 
					  <ChangePasswordForm on:success={onSuccess} />
 | 
				
			||||||
</FullscreenContainer>
 | 
					</FullscreenContainer>
 | 
				
			||||||
 | 
				
			|||||||
@ -1,22 +1,11 @@
 | 
				
			|||||||
import { faker } from '@faker-js/faker';
 | 
					import { faker } from '@faker-js/faker';
 | 
				
			||||||
import { UserAvatarColor, UserStatus, type UserResponseDto } from '@immich/sdk';
 | 
					import { UserAvatarColor, type UserResponseDto } from '@immich/sdk';
 | 
				
			||||||
import { Sync } from 'factory.ts';
 | 
					import { Sync } from 'factory.ts';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const userFactory = Sync.makeFactory<UserResponseDto>({
 | 
					export const userFactory = Sync.makeFactory<UserResponseDto>({
 | 
				
			||||||
  id: Sync.each(() => faker.string.uuid()),
 | 
					  id: Sync.each(() => faker.string.uuid()),
 | 
				
			||||||
  email: Sync.each(() => faker.internet.email()),
 | 
					  email: Sync.each(() => faker.internet.email()),
 | 
				
			||||||
  name: Sync.each(() => faker.person.fullName()),
 | 
					  name: Sync.each(() => faker.person.fullName()),
 | 
				
			||||||
  storageLabel: Sync.each(() => faker.string.alphanumeric()),
 | 
					 | 
				
			||||||
  profileImagePath: '',
 | 
					  profileImagePath: '',
 | 
				
			||||||
  shouldChangePassword: Sync.each(() => faker.datatype.boolean()),
 | 
					 | 
				
			||||||
  isAdmin: true,
 | 
					 | 
				
			||||||
  createdAt: Sync.each(() => faker.date.past().toISOString()),
 | 
					 | 
				
			||||||
  deletedAt: null,
 | 
					 | 
				
			||||||
  updatedAt: Sync.each(() => faker.date.past().toISOString()),
 | 
					 | 
				
			||||||
  memoriesEnabled: true,
 | 
					 | 
				
			||||||
  oauthId: '',
 | 
					 | 
				
			||||||
  avatarColor: UserAvatarColor.Primary,
 | 
					  avatarColor: UserAvatarColor.Primary,
 | 
				
			||||||
  quotaUsageInBytes: 0,
 | 
					 | 
				
			||||||
  quotaSizeInBytes: null,
 | 
					 | 
				
			||||||
  status: UserStatus.Active,
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user