From 9628ea2d245f289f324fea1048daa9a603ad2b4c Mon Sep 17 00:00:00 2001
From: Ben <45583362+ben-basten@users.noreply.github.com>
Date: Sun, 26 May 2024 21:43:30 +0000
Subject: [PATCH 01/14] fix(web): keyboard event propagation in modals (#9713)
* fix: key events propagating from modal, visible close button focus
* feat: set initial focus on the text field for album creation
* chore: step back duplicated changes
---
.../full-screen-modal.svelte | 21 +++++++++++--------
1 file changed, 12 insertions(+), 9 deletions(-)
diff --git a/web/src/lib/components/shared-components/full-screen-modal.svelte b/web/src/lib/components/shared-components/full-screen-modal.svelte
index dfd42be568..afc465a32d 100644
--- a/web/src/lib/components/shared-components/full-screen-modal.svelte
+++ b/web/src/lib/components/shared-components/full-screen-modal.svelte
@@ -43,13 +43,16 @@
}
-
-
+ {
+ event.stopPropagation();
+ }}
+>
+
{/if}
-
-
+
+
From 50f9b2d44eaf4c74d26c3c885e6e6043219a124a Mon Sep 17 00:00:00 2001
From: Alexandre Bouijoux <0q8hnjtu2@mozmail.com>
Date: Sun, 26 May 2024 23:45:05 +0200
Subject: [PATCH 02/14] docs: update README fr (#9764)
Update README_fr_FR.md
---
readme_i18n/README_fr_FR.md | 48 +++++++++++++++++++++++++++----------
1 file changed, 36 insertions(+), 12 deletions(-)
diff --git a/readme_i18n/README_fr_FR.md b/readme_i18n/README_fr_FR.md
index 08be3cc02f..47f0dab740 100644
--- a/readme_i18n/README_fr_FR.md
+++ b/readme_i18n/README_fr_FR.md
@@ -11,7 +11,7 @@
-Immich - Solution de sauvegarde performante et auto-hébergée des photos et des vidéos
+Immich - Solution de sauvegarde performante et auto-hébergée de photos et de vidéos
@@ -36,16 +36,16 @@
## Clause de non-responsabilité
- ⚠️ Le projet est en **très fort** développement.
-- ⚠️ Attendez-vous à rencontrer des bugs et des changements importants.
-- ⚠️ **N'utilisez pas cette application comme seule façon de sauvegarder vos photos et vos vidéos.**
+- ⚠️ Attendez-vous à rencontrer des bogues et des changements importants.
+- ⚠️ **N'utilisez pas cette application comme seul support de sauvegarde de vos photos et vos vidéos.**
- ⚠️ Ayez toujours un plan de sauvegarde en [3-2-1](https://www.seagate.com/fr/fr/blog/what-is-a-3-2-1-backup-strategy/) pour vos précieuses photos et vidéos !
## Sommaire
- [Documentation officielle](https://immich.app/docs)
- [Feuille de route](https://github.com/orgs/immich-app/projects/1)
-- [Démo](#demo)
-- [Fonctionnalités](#features)
+- [Démo](#démo)
+- [Fonctionnalités](#fonctionnalités)
- [Introduction](https://immich.app/docs/overview/introduction)
- [Installation](https://immich.app/docs/install/requirements)
- [Contribution](https://immich.app/docs/overview/support-the-project)
@@ -56,26 +56,31 @@ Vous pouvez trouver la documentation principale ainsi que les guides d'installat
## Démo
-Vous pouvez accéder à la démo Web sur https://demo.immich.app
+Vous pouvez accéder à la démo en ligne sur https://demo.immich.app
-Pour l'application mobile, vous pouvez utiliser `https://demo.immich.app/api` dans le champ 'URL du point d'accès au serveur'
+Pour l'application mobile, vous pouvez utiliser `https://demo.immich.app/api` dans le champ `URL du point d'accès au serveur`
-```bash title="Demo Credential"
+```bash title="Identifiants pour la démo"
Les identifiants
email: demo@immich.app
mot de passe: demo
```
```
-Caractéristiques: Plan gratuit Oracle VM - Amsterdam - 2.4Ghz quatre-cœurs ARM64 CPU, 24GB RAM
+Caractéristiques : Plan gratuit Oracle VM - Amsterdam - 2.4Ghz quatre-cœurs ARM64 CPU, 24GB RAM
```
-# Fonctionnalités
+## Activités
+
+
+
+## Fonctionnalités
| Fonctionnalités | Mobile | Web |
| ---------------------------------------------------------------- | ------ | --- |
| Téléverser et voir les vidéos et photos | Oui | Oui |
| Sauvegarde automatique quand l'application est ouverte | Oui | N/A |
+| Prévention contre la duplication des photos et des vidéos | Oui | Oui |
| Sélection des albums à sauvegarder | Oui | N/A |
| Télécharger les photos et les vidéos sur l'appareil | Oui | Oui |
| Support multi-utilisateur | Oui | Oui |
@@ -89,13 +94,32 @@ Caractéristiques: Plan gratuit Oracle VM - Amsterdam - 2.4Ghz quatre-cœurs ARM
| Défilement virtuel | Oui | Oui |
| Support de l'OAuth | Oui | Oui |
| Clés d'API | N/A | Oui |
-| Sauvegarde et lecture des LivePhotos | iOS | Oui |
+| Sauvegarde et lecture des LivePhoto/MotionPhoto | Oui | Oui |
+| Support de l'affichage des images à 360° | Non | Oui |
| Structure de stockage définissable | Oui | Oui |
| Partage public | Non | Oui |
| Archives et favoris | Oui | Oui |
-| Carte globale | Non | Oui |
+| Carte globale | Oui | Oui |
| Partage entre utilisateurs | Oui | Oui |
| Reconnaissance et regroupement facial | Oui | Oui |
| Souvenirs (il y a x années) | Oui | Oui |
| Support hors-ligne | Oui | Non |
| Gallerie en lecture seule | Oui | Oui |
+| Empilage de photos | Oui | Oui |
+
+
+## Contributeurs
+
+
+
+
+
+## Historique des favoris
+
+
+
+
+
+
+
+
From e7c8501930a988dfb6c23ce1c48b0beb076a58c2 Mon Sep 17 00:00:00 2001
From: Mert <101130780+mertalev@users.noreply.github.com>
Date: Sun, 26 May 2024 18:04:23 -0400
Subject: [PATCH 03/14] fix(server): search duplicates of the same asset type
(#9747)
* search by type
* make sql
---------
Co-authored-by: Alex
---
server/src/interfaces/search.interface.ts | 3 ++-
server/src/queries/search.repository.sql | 3 ++-
server/src/repositories/search.repository.ts | 13 +++++++++----
server/src/services/duplicate.service.spec.ts | 2 ++
server/src/services/duplicate.service.ts | 1 +
5 files changed, 16 insertions(+), 6 deletions(-)
diff --git a/server/src/interfaces/search.interface.ts b/server/src/interfaces/search.interface.ts
index 57523aa940..ce9e2a1940 100644
--- a/server/src/interfaces/search.interface.ts
+++ b/server/src/interfaces/search.interface.ts
@@ -155,8 +155,9 @@ export interface FaceEmbeddingSearch extends SearchEmbeddingOptions {
export interface AssetDuplicateSearch {
assetId: string;
embedding: Embedding;
- userIds: string[];
maxDistance?: number;
+ type: AssetType;
+ userIds: string[];
}
export interface FaceSearchResult {
diff --git a/server/src/queries/search.repository.sql b/server/src/queries/search.repository.sql
index 1a4245592b..9efeae6248 100644
--- a/server/src/queries/search.repository.sql
+++ b/server/src/queries/search.repository.sql
@@ -204,6 +204,7 @@ WITH
"asset"."ownerId" IN ($2)
AND "asset"."id" != $3
AND "asset"."isVisible" = $4
+ AND "asset"."type" = $5
)
AND ("asset"."deletedAt" IS NULL)
ORDER BY
@@ -216,7 +217,7 @@ SELECT
FROM
"cte" "res"
WHERE
- res.distance <= $5
+ res.distance <= $6
-- SearchRepository.searchFaces
START TRANSACTION
diff --git a/server/src/repositories/search.repository.ts b/server/src/repositories/search.repository.ts
index 072d452777..f0c5dcb364 100644
--- a/server/src/repositories/search.repository.ts
+++ b/server/src/repositories/search.repository.ts
@@ -160,6 +160,7 @@ export class SearchRepository implements ISearchRepository {
assetId,
embedding,
maxDistance,
+ type,
userIds,
}: AssetDuplicateSearch): Promise {
const cte = this.assetRepository.createQueryBuilder('asset');
@@ -171,18 +172,22 @@ export class SearchRepository implements ISearchRepository {
.where('asset.ownerId IN (:...userIds )')
.andWhere('asset.id != :assetId')
.andWhere('asset.isVisible = :isVisible')
+ .andWhere('asset.type = :type')
.orderBy('search.embedding <=> :embedding')
.limit(64)
- .setParameters({ assetId, embedding: asVector(embedding), isVisible: true, userIds });
+ .setParameters({ assetId, embedding: asVector(embedding), isVisible: true, type, userIds });
const builder = this.assetRepository.manager
.createQueryBuilder()
.addCommonTableExpression(cte, 'cte')
.from('cte', 'res')
- .select('res.*')
- .where('res.distance <= :maxDistance', { maxDistance });
+ .select('res.*');
- return builder.getRawMany() as any as Promise;
+ if (maxDistance) {
+ builder.where('res.distance <= :maxDistance', { maxDistance });
+ }
+
+ return builder.getRawMany() as Promise;
}
@GenerateSql({
diff --git a/server/src/services/duplicate.service.spec.ts b/server/src/services/duplicate.service.spec.ts
index 4560d9024c..79374ea7ae 100644
--- a/server/src/services/duplicate.service.spec.ts
+++ b/server/src/services/duplicate.service.spec.ts
@@ -215,6 +215,7 @@ describe(SearchService.name, () => {
assetId: assetStub.hasEmbedding.id,
embedding: assetStub.hasEmbedding.smartSearch!.embedding,
maxDistance: 0.03,
+ type: assetStub.hasEmbedding.type,
userIds: [assetStub.hasEmbedding.ownerId],
});
expect(assetMock.updateDuplicates).toHaveBeenCalledWith({
@@ -240,6 +241,7 @@ describe(SearchService.name, () => {
assetId: assetStub.hasEmbedding.id,
embedding: assetStub.hasEmbedding.smartSearch!.embedding,
maxDistance: 0.03,
+ type: assetStub.hasEmbedding.type,
userIds: [assetStub.hasEmbedding.ownerId],
});
expect(assetMock.updateDuplicates).toHaveBeenCalledWith({
diff --git a/server/src/services/duplicate.service.ts b/server/src/services/duplicate.service.ts
index 95a12bd18e..6313ffa21f 100644
--- a/server/src/services/duplicate.service.ts
+++ b/server/src/services/duplicate.service.ts
@@ -94,6 +94,7 @@ export class DuplicateService {
assetId: asset.id,
embedding: asset.smartSearch.embedding,
maxDistance: machineLearning.duplicateDetection.maxDistance,
+ type: asset.type,
userIds: [asset.ownerId],
});
From 75830a4878561046f07314dff6043cfa82b6cdfb Mon Sep 17 00:00:00 2001
From: Jason Rasmussen
Date: Sun, 26 May 2024 18:15:52 -0400
Subject: [PATCH 04/14] refactor(server): user endpoints (#9730)
* refactor(server): user endpoints
* fix repos
* fix unit tests
---------
Co-authored-by: Daniel Dietzler
Co-authored-by: Alex
---
cli/src/commands/auth.ts | 6 +-
cli/src/commands/server-info.ts | 4 +-
cli/src/utils.ts | 4 +-
e2e/src/api/specs/album.e2e-spec.ts | 4 +-
e2e/src/api/specs/asset.e2e-spec.ts | 4 +-
e2e/src/api/specs/shared-link.e2e-spec.ts | 4 +-
e2e/src/api/specs/user-admin.e2e-spec.ts | 317 ++++++++
e2e/src/api/specs/user.e2e-spec.ts | 315 +++-----
e2e/src/utils.ts | 8 +-
mobile/lib/entities/user.entity.dart | 14 +-
.../providers/authentication.provider.dart | 10 +-
mobile/lib/providers/user.provider.dart | 2 +-
.../lib/routing/tab_navigation_observer.dart | 2 +-
mobile/lib/services/user.service.dart | 8 +-
mobile/openapi/README.md | 26 +-
mobile/openapi/lib/api.dart | 9 +-
.../openapi/lib/api/authentication_api.dart | 8 +-
mobile/openapi/lib/api/o_auth_api.dart | 8 +-
mobile/openapi/lib/api/user_api.dart | 332 +++++---
mobile/openapi/lib/api_client.dart | 18 +-
.../lib/model/activity_response_dto.dart | 4 +-
.../lib/model/partner_response_dto.dart | 119 +--
...er_dto.dart => user_admin_create_dto.dart} | 36 +-
...er_dto.dart => user_admin_delete_dto.dart} | 36 +-
.../lib/model/user_admin_response_dto.dart | 243 ++++++
...er_dto.dart => user_admin_update_dto.dart} | 67 +-
mobile/openapi/lib/model/user_dto.dart | 130 ---
.../openapi/lib/model/user_response_dto.dart | 119 +--
.../openapi/lib/model/user_update_me_dto.dart | 175 +++++
open-api/immich-openapi-specs.json | 738 ++++++++++--------
open-api/typescript-sdk/README.md | 9 +
open-api/typescript-sdk/src/fetch-client.ts | 221 +++---
.../commands/reset-admin-password.command.ts | 4 +-
server/src/controllers/auth.controller.ts | 8 +-
server/src/controllers/index.ts | 2 +
server/src/controllers/oauth.controller.ts | 6 +-
.../src/controllers/user-admin.controller.ts | 63 ++
server/src/controllers/user.controller.ts | 60 +-
server/src/cores/user.core.ts | 43 +-
server/src/dtos/activity.dto.ts | 6 +-
server/src/dtos/user.dto.spec.ts | 29 +-
server/src/dtos/user.dto.ts | 112 +--
server/src/queries/api.key.repository.sql | 6 +-
server/src/queries/session.repository.sql | 6 +-
server/src/repositories/api-key.repository.ts | 4 +-
server/src/repositories/session.repository.ts | 9 +-
server/src/services/auth.service.spec.ts | 1 +
server/src/services/auth.service.ts | 27 +-
server/src/services/cli.service.ts | 17 +-
server/src/services/index.ts | 2 +
server/src/services/partner.service.spec.ts | 47 +-
server/src/services/partner.service.ts | 8 +-
.../src/services/user-admin.service.spec.ts | 197 +++++
server/src/services/user-admin.service.ts | 154 ++++
server/src/services/user.service.spec.ts | 264 +------
server/src/services/user.service.ts | 111 +--
server/src/validation.ts | 2 +-
.../admin-page/delete-confirm-dialogue.svelte | 6 +-
.../admin-page/restore-dialogue.svelte | 4 +-
.../album-page/share-info-modal.svelte | 4 +-
.../album-page/user-selection-modal.svelte | 8 +-
.../forms/change-password-form.svelte | 11 +-
.../components/forms/create-user-form.svelte | 6 +-
.../components/forms/edit-user-form.svelte | 20 +-
.../forms/library-user-picker-form.svelte | 4 +-
.../navigation-bar/account-info-panel.svelte | 7 +-
.../memories-settings.svelte | 13 +-
.../user-settings-page/oauth-settings.svelte | 4 +-
.../partner-selection-modal.svelte | 9 +-
.../user-profile-settings.svelte | 17 +-
web/src/lib/stores/user.store.ts | 6 +-
web/src/lib/utils.ts | 3 +-
web/src/lib/utils/auth.ts | 4 +-
.../[[photos=photos]]/[[assetId=id]]/+page.ts | 4 +-
.../admin/library-management/+page.svelte | 4 +-
.../routes/admin/library-management/+page.ts | 4 +-
.../routes/admin/user-management/+page.svelte | 22 +-
web/src/routes/admin/user-management/+page.ts | 4 +-
.../routes/auth/change-password/+page.svelte | 2 +-
web/src/test-data/factories/user-factory.ts | 13 +-
80 files changed, 2453 insertions(+), 1914 deletions(-)
create mode 100644 e2e/src/api/specs/user-admin.e2e-spec.ts
rename mobile/openapi/lib/model/{create_user_dto.dart => user_admin_create_dto.dart} (80%)
rename mobile/openapi/lib/model/{delete_user_dto.dart => user_admin_delete_dto.dart} (67%)
create mode 100644 mobile/openapi/lib/model/user_admin_response_dto.dart
rename mobile/openapi/lib/model/{update_user_dto.dart => user_admin_update_dto.dart} (73%)
delete mode 100644 mobile/openapi/lib/model/user_dto.dart
create mode 100644 mobile/openapi/lib/model/user_update_me_dto.dart
create mode 100644 server/src/controllers/user-admin.controller.ts
create mode 100644 server/src/services/user-admin.service.spec.ts
create mode 100644 server/src/services/user-admin.service.ts
diff --git a/cli/src/commands/auth.ts b/cli/src/commands/auth.ts
index 6675201a7b..f0011c6a24 100644
--- a/cli/src/commands/auth.ts
+++ b/cli/src/commands/auth.ts
@@ -1,4 +1,4 @@
-import { getMyUserInfo } from '@immich/sdk';
+import { getMyUser } from '@immich/sdk';
import { existsSync } from 'node:fs';
import { mkdir, unlink } from 'node:fs/promises';
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);
- const [error, userInfo] = await withError(getMyUserInfo());
+ const [error, user] = await withError(getMyUser());
if (error) {
logError(error, 'Failed to load user info');
process.exit(1);
}
- console.log(`Logged in as ${userInfo.email}`);
+ console.log(`Logged in as ${user.email}`);
if (!existsSync(configDir)) {
// Create config folder if it doesn't exist
diff --git a/cli/src/commands/server-info.ts b/cli/src/commands/server-info.ts
index 074513bd61..bea49231c9 100644
--- a/cli/src/commands/server-info.ts
+++ b/cli/src/commands/server-info.ts
@@ -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';
export const serverInfo = async (options: BaseOptions) => {
@@ -8,7 +8,7 @@ export const serverInfo = async (options: BaseOptions) => {
getServerVersion(),
getSupportedMediaTypes(),
getAssetStatistics({}),
- getMyUserInfo(),
+ getMyUser(),
]);
console.log(`Server Info (via ${userInfo.email})`);
diff --git a/cli/src/utils.ts b/cli/src/utils.ts
index 3b239bacc4..4919a2b3ca 100644
--- a/cli/src/utils.ts
+++ b/cli/src/utils.ts
@@ -1,4 +1,4 @@
-import { getMyUserInfo, init, isHttpError } from '@immich/sdk';
+import { getMyUser, init, isHttpError } from '@immich/sdk';
import { glob } from 'fast-glob';
import { createHash } from 'node:crypto';
import { createReadStream } from 'node:fs';
@@ -48,7 +48,7 @@ export const connect = async (url: string, key: string) => {
init({ baseUrl: url, apiKey: key });
- const [error] = await withError(getMyUserInfo());
+ const [error] = await withError(getMyUser());
if (isHttpError(error)) {
logError(error, 'Failed to connect to server');
process.exit(1);
diff --git a/e2e/src/api/specs/album.e2e-spec.ts b/e2e/src/api/specs/album.e2e-spec.ts
index 4a231dbf9b..319cc4033d 100644
--- a/e2e/src/api/specs/album.e2e-spec.ts
+++ b/e2e/src/api/specs/album.e2e-spec.ts
@@ -4,7 +4,7 @@ import {
AlbumUserRole,
AssetFileUploadResponseDto,
AssetOrder,
- deleteUser,
+ deleteUserAdmin,
getAlbumInfo,
LoginResponseDto,
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', () => {
diff --git a/e2e/src/api/specs/asset.e2e-spec.ts b/e2e/src/api/specs/asset.e2e-spec.ts
index 98dca464bc..caf032e130 100644
--- a/e2e/src/api/specs/asset.e2e-spec.ts
+++ b/e2e/src/api/specs/asset.e2e-spec.ts
@@ -5,7 +5,7 @@ import {
LoginResponseDto,
SharedLinkType,
getAssetInfo,
- getMyUserInfo,
+ getMyUser,
updateAssets,
} from '@immich/sdk';
import { exiftool } from 'exiftool-vendored';
@@ -1162,7 +1162,7 @@ describe('/asset', () => {
expect(body).toEqual({ id: expect.any(String), duplicate: false });
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 }));
});
diff --git a/e2e/src/api/specs/shared-link.e2e-spec.ts b/e2e/src/api/specs/shared-link.e2e-spec.ts
index aa4ec7e349..0d76fb6efe 100644
--- a/e2e/src/api/specs/shared-link.e2e-spec.ts
+++ b/e2e/src/api/specs/shared-link.e2e-spec.ts
@@ -5,7 +5,7 @@ import {
SharedLinkResponseDto,
SharedLinkType,
createAlbum,
- deleteUser,
+ deleteUserAdmin,
} from '@immich/sdk';
import { createUserDto, uuidDto } from 'src/fixtures';
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}', () => {
diff --git a/e2e/src/api/specs/user-admin.e2e-spec.ts b/e2e/src/api/specs/user-admin.e2e-spec.ts
new file mode 100644
index 0000000000..ac2b3e693a
--- /dev/null
+++ b/e2e/src/api/specs/user-admin.e2e-spec.ts
@@ -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);
+ });
+ });
+});
diff --git a/e2e/src/api/specs/user.e2e-spec.ts b/e2e/src/api/specs/user.e2e-spec.ts
index 08b2d34ef6..0cc08479d3 100644
--- a/e2e/src/api/specs/user.e2e-spec.ts
+++ b/e2e/src/api/specs/user.e2e-spec.ts
@@ -1,37 +1,28 @@
-import { LoginResponseDto, deleteUser, getUserById } from '@immich/sdk';
-import { Socket } from 'socket.io-client';
-import { createUserDto, userDto } from 'src/fixtures';
+import { LoginResponseDto, SharedLinkType, deleteUserAdmin, getMyUser, login } from '@immich/sdk';
+import { createUserDto } 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';
+import { beforeAll, describe, expect, it } from 'vitest';
describe('/users', () => {
- let websocket: Socket;
-
let admin: LoginResponseDto;
let deletedUser: LoginResponseDto;
- let userToDelete: LoginResponseDto;
- let userToHardDelete: LoginResponseDto;
let nonAdmin: LoginResponseDto;
beforeAll(async () => {
await utils.resetDatabase();
admin = await utils.adminSetup({ onboarding: false });
- [websocket, deletedUser, nonAdmin, userToDelete, userToHardDelete] = await Promise.all([
- utils.connectWebsocket(admin.accessToken),
+ [deletedUser, nonAdmin] = await Promise.all([
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 deleteUser({ id: deletedUser.userId, deleteUserDto: {} }, { headers: asBearerAuth(admin.accessToken) });
- });
-
- afterAll(() => {
- utils.disconnectWebsocket(websocket);
+ await deleteUserAdmin(
+ { id: deletedUser.userId, userAdminDeleteDto: {} },
+ { headers: asBearerAuth(admin.accessToken) },
+ );
});
describe('GET /users', () => {
@@ -44,71 +35,14 @@ describe('/users', () => {
it('should get users', async () => {
const { status, body } = await request(app).get('/users').set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toEqual(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' }),
- ]),
- );
- });
-
- 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).toHaveLength(2);
expect(body).toEqual(
expect.arrayContaining([
expect.objectContaining({ email: 'admin@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', () => {
@@ -118,154 +52,54 @@ describe('/users', () => {
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}`);
expect(status).toBe(200);
expect(body).toMatchObject({
id: admin.userId,
email: 'admin@immich.cloud',
+ memoriesEnabled: true,
+ quotaUsageInBytes: 0,
});
});
});
- describe('POST /users', () => {
+ describe('PUT /users/me', () => {
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(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 () => {
+ const dto = { [key]: null };
const { status, body } = await request(app)
- .post(`/users`)
+ .put(`/users/me`)
.set('Authorization', `Bearer ${admin.accessToken}`)
- .send({ ...createUserDto.user1, [key]: null });
+ .send(dto);
expect(status).toBe(400);
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 () => {
- 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)
- .put(`/users`)
- .send({
- id: admin.userId,
- name: 'Name',
- })
+ .put(`/users/me`)
+ .send({ name: 'Name' })
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
@@ -274,17 +108,13 @@ describe('/users', () => {
updatedAt: expect.any(String),
name: 'Name',
});
- expect(before.updatedAt).not.toEqual(body.updatedAt);
});
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)
- .put(`/users`)
- .send({
- id: admin.userId,
- memoriesEnabled: false,
- })
+ .put(`/users/me`)
+ .send({ memoriesEnabled: false })
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
@@ -293,7 +123,80 @@ describe('/users', () => {
updatedAt: expect.anything(),
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(),
+ });
});
});
});
diff --git a/e2e/src/utils.ts b/e2e/src/utils.ts
index 1454135c12..f9bc7a4445 100644
--- a/e2e/src/utils.ts
+++ b/e2e/src/utils.ts
@@ -5,10 +5,10 @@ import {
CreateAlbumDto,
CreateAssetDto,
CreateLibraryDto,
- CreateUserDto,
MetadataSearchDto,
PersonCreateDto,
SharedLinkCreateDto,
+ UserAdminCreateDto,
ValidateLibraryDto,
createAlbum,
createApiKey,
@@ -16,7 +16,7 @@ import {
createPartner,
createPerson,
createSharedLink,
- createUser,
+ createUserAdmin,
deleteAssets,
getAllJobsStatus,
getAssetInfo,
@@ -273,8 +273,8 @@ export const utils = {
return response;
},
- userSetup: async (accessToken: string, dto: CreateUserDto) => {
- await createUser({ createUserDto: dto }, { headers: asBearerAuth(accessToken) });
+ userSetup: async (accessToken: string, dto: UserAdminCreateDto) => {
+ await createUserAdmin({ userAdminCreateDto: dto }, { headers: asBearerAuth(accessToken) });
return login({
loginCredentialDto: { email: dto.email, password: dto.password },
});
diff --git a/mobile/lib/entities/user.entity.dart b/mobile/lib/entities/user.entity.dart
index d02be2f30a..b6adcf5d87 100644
--- a/mobile/lib/entities/user.entity.dart
+++ b/mobile/lib/entities/user.entity.dart
@@ -27,7 +27,7 @@ class User {
Id get isarId => fastHash(id);
- User.fromUserDto(UserResponseDto dto)
+ User.fromUserDto(UserAdminResponseDto dto)
: id = dto.id,
updatedAt = dto.updatedAt,
email = dto.email,
@@ -44,21 +44,21 @@ class User {
User.fromPartnerDto(PartnerResponseDto dto)
: id = dto.id,
- updatedAt = dto.updatedAt,
+ updatedAt = DateTime.now(),
email = dto.email,
name = dto.name,
isPartnerSharedBy = false,
isPartnerSharedWith = false,
profileImagePath = dto.profileImagePath,
- isAdmin = dto.isAdmin,
- memoryEnabled = dto.memoriesEnabled ?? false,
+ isAdmin = false,
+ memoryEnabled = false,
avatarColor = dto.avatarColor.toAvatarColor(),
inTimeline = dto.inTimeline ?? false,
- quotaUsageInBytes = dto.quotaUsageInBytes ?? 0,
- quotaSizeInBytes = dto.quotaSizeInBytes ?? 0;
+ quotaUsageInBytes = 0,
+ quotaSizeInBytes = 0;
/// Base user dto used where the complete user object is not required
- User.fromSimpleUserDto(UserDto dto)
+ User.fromSimpleUserDto(UserResponseDto dto)
: id = dto.id,
email = dto.email,
name = dto.name,
diff --git a/mobile/lib/providers/authentication.provider.dart b/mobile/lib/providers/authentication.provider.dart
index a595d43c86..073ee09db1 100644
--- a/mobile/lib/providers/authentication.provider.dart
+++ b/mobile/lib/providers/authentication.provider.dart
@@ -138,11 +138,9 @@ class AuthenticationNotifier extends StateNotifier {
Future changePassword(String newPassword) async {
try {
- await _apiService.userApi.updateUser(
- UpdateUserDto(
- id: state.userId,
+ await _apiService.userApi.updateMyUser(
+ UserUpdateMeDto(
password: newPassword,
- shouldChangePassword: false,
),
);
@@ -178,9 +176,9 @@ class AuthenticationNotifier extends StateNotifier {
user = offlineUser;
retResult = false;
} else {
- UserResponseDto? userResponseDto;
+ UserAdminResponseDto? userResponseDto;
try {
- userResponseDto = await _apiService.userApi.getMyUserInfo();
+ userResponseDto = await _apiService.userApi.getMyUser();
} on ApiException catch (error, stackTrace) {
_log.severe(
"Error getting user information from the server [API EXCEPTION]",
diff --git a/mobile/lib/providers/user.provider.dart b/mobile/lib/providers/user.provider.dart
index eb2824ec3f..bf052ebbba 100644
--- a/mobile/lib/providers/user.provider.dart
+++ b/mobile/lib/providers/user.provider.dart
@@ -20,7 +20,7 @@ class CurrentUserProvider extends StateNotifier {
refresh() async {
try {
- final user = await _apiService.userApi.getMyUserInfo();
+ final user = await _apiService.userApi.getMyUser();
if (user != null) {
Store.put(
StoreKey.currentUser,
diff --git a/mobile/lib/routing/tab_navigation_observer.dart b/mobile/lib/routing/tab_navigation_observer.dart
index f88adbda91..8825e2ef02 100644
--- a/mobile/lib/routing/tab_navigation_observer.dart
+++ b/mobile/lib/routing/tab_navigation_observer.dart
@@ -57,7 +57,7 @@ class TabNavigationObserver extends AutoRouterObserver {
// Update user info
try {
final userResponseDto =
- await ref.read(apiServiceProvider).userApi.getMyUserInfo();
+ await ref.read(apiServiceProvider).userApi.getMyUser();
if (userResponseDto == null) {
return;
diff --git a/mobile/lib/services/user.service.dart b/mobile/lib/services/user.service.dart
index 81100f1624..4e88bab12c 100644
--- a/mobile/lib/services/user.service.dart
+++ b/mobile/lib/services/user.service.dart
@@ -37,10 +37,10 @@ class UserService {
this._partnerService,
);
- Future?> _getAllUsers({required bool isAll}) async {
+ Future?> _getAllUsers() async {
try {
- final dto = await _apiService.userApi.getAllUsers(isAll);
- return dto?.map(User.fromUserDto).toList();
+ final dto = await _apiService.userApi.searchUsers();
+ return dto?.map(User.fromSimpleUserDto).toList();
} catch (e) {
_log.warning("Failed get all users", e);
return null;
@@ -71,7 +71,7 @@ class UserService {
}
Future?> getUsersFromServer() async {
- final List? users = await _getAllUsers(isAll: true);
+ final List? users = await _getAllUsers();
final List? sharedBy =
await _partnerService.getPartners(PartnerDirection.sharedBy);
final List? sharedWith =
diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md
index dbbbdc2fee..273585c368 100644
--- a/mobile/openapi/README.md
+++ b/mobile/openapi/README.md
@@ -212,15 +212,18 @@ Class | Method | HTTP request | Description
*TrashApi* | [**restoreAssets**](doc//TrashApi.md#restoreassets) | **POST** /trash/restore/assets |
*TrashApi* | [**restoreTrash**](doc//TrashApi.md#restoretrash) | **POST** /trash/restore |
*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* | [**deleteUser**](doc//UserApi.md#deleteuser) | **DELETE** /users/{id} |
-*UserApi* | [**getAllUsers**](doc//UserApi.md#getallusers) | **GET** /users |
-*UserApi* | [**getMyUserInfo**](doc//UserApi.md#getmyuserinfo) | **GET** /users/me |
+*UserApi* | [**deleteUserAdmin**](doc//UserApi.md#deleteuseradmin) | **DELETE** /admin/users/{id} |
+*UserApi* | [**getMyUser**](doc//UserApi.md#getmyuser) | **GET** /users/me |
*UserApi* | [**getProfileImage**](doc//UserApi.md#getprofileimage) | **GET** /users/{id}/profile-image |
-*UserApi* | [**getUserById**](doc//UserApi.md#getuserbyid) | **GET** /users/{id} |
-*UserApi* | [**restoreUser**](doc//UserApi.md#restoreuser) | **POST** /users/{id}/restore |
-*UserApi* | [**updateUser**](doc//UserApi.md#updateuser) | **PUT** /users |
+*UserApi* | [**getUser**](doc//UserApi.md#getuser) | **GET** /users/{id} |
+*UserApi* | [**getUserAdmin**](doc//UserApi.md#getuseradmin) | **GET** /admin/users/{id} |
+*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
@@ -280,8 +283,6 @@ Class | Method | HTTP request | Description
- [CreateLibraryDto](doc//CreateLibraryDto.md)
- [CreateProfileImageResponseDto](doc//CreateProfileImageResponseDto.md)
- [CreateTagDto](doc//CreateTagDto.md)
- - [CreateUserDto](doc//CreateUserDto.md)
- - [DeleteUserDto](doc//DeleteUserDto.md)
- [DownloadArchiveInfo](doc//DownloadArchiveInfo.md)
- [DownloadInfoDto](doc//DownloadInfoDto.md)
- [DownloadResponseDto](doc//DownloadResponseDto.md)
@@ -402,12 +403,15 @@ Class | Method | HTTP request | Description
- [UpdatePartnerDto](doc//UpdatePartnerDto.md)
- [UpdateStackParentDto](doc//UpdateStackParentDto.md)
- [UpdateTagDto](doc//UpdateTagDto.md)
- - [UpdateUserDto](doc//UpdateUserDto.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)
- - [UserDto](doc//UserDto.md)
- [UserResponseDto](doc//UserResponseDto.md)
- [UserStatus](doc//UserStatus.md)
+ - [UserUpdateMeDto](doc//UserUpdateMeDto.md)
- [ValidateAccessTokenResponseDto](doc//ValidateAccessTokenResponseDto.md)
- [ValidateLibraryDto](doc//ValidateLibraryDto.md)
- [ValidateLibraryImportPathResponseDto](doc//ValidateLibraryImportPathResponseDto.md)
diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart
index 7e71c9db3e..d7223a1ecf 100644
--- a/mobile/openapi/lib/api.dart
+++ b/mobile/openapi/lib/api.dart
@@ -112,8 +112,6 @@ part 'model/create_album_dto.dart';
part 'model/create_library_dto.dart';
part 'model/create_profile_image_response_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_info_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_stack_parent_dto.dart';
part 'model/update_tag_dto.dart';
-part 'model/update_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_dto.dart';
part 'model/user_response_dto.dart';
part 'model/user_status.dart';
+part 'model/user_update_me_dto.dart';
part 'model/validate_access_token_response_dto.dart';
part 'model/validate_library_dto.dart';
part 'model/validate_library_import_path_response_dto.dart';
diff --git a/mobile/openapi/lib/api/authentication_api.dart b/mobile/openapi/lib/api/authentication_api.dart
index c2aa50e7e7..cb81867425 100644
--- a/mobile/openapi/lib/api/authentication_api.dart
+++ b/mobile/openapi/lib/api/authentication_api.dart
@@ -48,7 +48,7 @@ class AuthenticationApi {
/// Parameters:
///
/// * [ChangePasswordDto] changePasswordDto (required):
- Future changePassword(ChangePasswordDto changePasswordDto,) async {
+ Future changePassword(ChangePasswordDto changePasswordDto,) async {
final response = await changePasswordWithHttpInfo(changePasswordDto,);
if (response.statusCode >= HttpStatus.badRequest) {
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"
// FormatException when trying to decode an empty string.
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;
@@ -183,7 +183,7 @@ class AuthenticationApi {
/// Parameters:
///
/// * [SignUpDto] signUpDto (required):
- Future signUpAdmin(SignUpDto signUpDto,) async {
+ Future signUpAdmin(SignUpDto signUpDto,) async {
final response = await signUpAdminWithHttpInfo(signUpDto,);
if (response.statusCode >= HttpStatus.badRequest) {
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"
// FormatException when trying to decode an empty string.
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;
diff --git a/mobile/openapi/lib/api/o_auth_api.dart b/mobile/openapi/lib/api/o_auth_api.dart
index 9c238f01dc..aafcb28461 100644
--- a/mobile/openapi/lib/api/o_auth_api.dart
+++ b/mobile/openapi/lib/api/o_auth_api.dart
@@ -95,7 +95,7 @@ class OAuthApi {
/// Parameters:
///
/// * [OAuthCallbackDto] oAuthCallbackDto (required):
- Future linkOAuthAccount(OAuthCallbackDto oAuthCallbackDto,) async {
+ Future linkOAuthAccount(OAuthCallbackDto oAuthCallbackDto,) async {
final response = await linkOAuthAccountWithHttpInfo(oAuthCallbackDto,);
if (response.statusCode >= HttpStatus.badRequest) {
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"
// FormatException when trying to decode an empty string.
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;
@@ -216,7 +216,7 @@ class OAuthApi {
);
}
- Future unlinkOAuthAccount() async {
+ Future unlinkOAuthAccount() async {
final response = await unlinkOAuthAccountWithHttpInfo();
if (response.statusCode >= HttpStatus.badRequest) {
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"
// FormatException when trying to decode an empty string.
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;
diff --git a/mobile/openapi/lib/api/user_api.dart b/mobile/openapi/lib/api/user_api.dart
index 301169cb9a..3c1a3ff4e7 100644
--- a/mobile/openapi/lib/api/user_api.dart
+++ b/mobile/openapi/lib/api/user_api.dart
@@ -73,16 +73,16 @@ class UserApi {
return null;
}
- /// Performs an HTTP 'POST /users' operation and returns the [Response].
+ /// Performs an HTTP 'POST /admin/users' operation and returns the [Response].
/// Parameters:
///
- /// * [CreateUserDto] createUserDto (required):
- Future createUserWithHttpInfo(CreateUserDto createUserDto,) async {
+ /// * [UserAdminCreateDto] userAdminCreateDto (required):
+ Future createUserAdminWithHttpInfo(UserAdminCreateDto userAdminCreateDto,) async {
// ignore: prefer_const_declarations
- final path = r'/users';
+ final path = r'/admin/users';
// ignore: prefer_final_locals
- Object? postBody = createUserDto;
+ Object? postBody = userAdminCreateDto;
final queryParams = [];
final headerParams = {};
@@ -104,9 +104,9 @@ class UserApi {
/// Parameters:
///
- /// * [CreateUserDto] createUserDto (required):
- Future createUser(CreateUserDto createUserDto,) async {
- final response = await createUserWithHttpInfo(createUserDto,);
+ /// * [UserAdminCreateDto] userAdminCreateDto (required):
+ Future createUserAdmin(UserAdminCreateDto userAdminCreateDto,) async {
+ final response = await createUserAdminWithHttpInfo(userAdminCreateDto,);
if (response.statusCode >= HttpStatus.badRequest) {
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"
// FormatException when trying to decode an empty string.
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;
@@ -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:
///
/// * [String] id (required):
///
- /// * [DeleteUserDto] deleteUserDto (required):
- Future deleteUserWithHttpInfo(String id, DeleteUserDto deleteUserDto,) async {
+ /// * [UserAdminDeleteDto] userAdminDeleteDto (required):
+ Future deleteUserAdminWithHttpInfo(String id, UserAdminDeleteDto userAdminDeleteDto,) async {
// ignore: prefer_const_declarations
- final path = r'/users/{id}'
+ final path = r'/admin/users/{id}'
.replaceAll('{id}', id);
// ignore: prefer_final_locals
- Object? postBody = deleteUserDto;
+ Object? postBody = userAdminDeleteDto;
final queryParams = [];
final headerParams = {};
@@ -189,9 +189,9 @@ class UserApi {
///
/// * [String] id (required):
///
- /// * [DeleteUserDto] deleteUserDto (required):
- Future deleteUser(String id, DeleteUserDto deleteUserDto,) async {
- final response = await deleteUserWithHttpInfo(id, deleteUserDto,);
+ /// * [UserAdminDeleteDto] userAdminDeleteDto (required):
+ Future deleteUserAdmin(String id, UserAdminDeleteDto userAdminDeleteDto,) async {
+ final response = await deleteUserAdminWithHttpInfo(id, userAdminDeleteDto,);
if (response.statusCode >= HttpStatus.badRequest) {
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"
// FormatException when trying to decode an empty string.
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 getAllUsersWithHttpInfo(bool isAll,) async {
- // ignore: prefer_const_declarations
- final path = r'/users';
-
- // ignore: prefer_final_locals
- Object? postBody;
-
- final queryParams = [];
- final headerParams = {};
- final formParams = {};
-
- queryParams.addAll(_queryParams('', 'isAll', isAll));
-
- const contentTypes = [];
-
-
- return apiClient.invokeAPI(
- path,
- 'GET',
- queryParams,
- postBody,
- headerParams,
- formParams,
- contentTypes.isEmpty ? null : contentTypes.first,
- );
- }
-
- /// Parameters:
- ///
- /// * [bool] isAll (required):
- Future?> 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') as List)
- .cast()
- .toList(growable: false);
-
- }
- return null;
- }
-
/// Performs an HTTP 'GET /users/me' operation and returns the [Response].
- Future getMyUserInfoWithHttpInfo() async {
+ Future getMyUserWithHttpInfo() async {
// ignore: prefer_const_declarations
final path = r'/users/me';
@@ -283,8 +231,8 @@ class UserApi {
);
}
- Future getMyUserInfo() async {
- final response = await getMyUserInfoWithHttpInfo();
+ Future getMyUser() async {
+ final response = await getMyUserWithHttpInfo();
if (response.statusCode >= HttpStatus.badRequest) {
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"
// FormatException when trying to decode an empty string.
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;
@@ -350,7 +298,7 @@ class UserApi {
/// Parameters:
///
/// * [String] id (required):
- Future getUserByIdWithHttpInfo(String id,) async {
+ Future getUserWithHttpInfo(String id,) async {
// ignore: prefer_const_declarations
final path = r'/users/{id}'
.replaceAll('{id}', id);
@@ -379,8 +327,8 @@ class UserApi {
/// Parameters:
///
/// * [String] id (required):
- Future getUserById(String id,) async {
- final response = await getUserByIdWithHttpInfo(id,);
+ Future getUser(String id,) async {
+ final response = await getUserWithHttpInfo(id,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -394,13 +342,61 @@ class UserApi {
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:
///
/// * [String] id (required):
- Future restoreUserWithHttpInfo(String id,) async {
+ Future getUserAdminWithHttpInfo(String id,) async {
// 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 = [];
+ final headerParams = {};
+ final formParams = {};
+
+ const contentTypes = [];
+
+
+ return apiClient.invokeAPI(
+ path,
+ 'GET',
+ queryParams,
+ postBody,
+ headerParams,
+ formParams,
+ contentTypes.isEmpty ? null : contentTypes.first,
+ );
+ }
+
+ /// Parameters:
+ ///
+ /// * [String] id (required):
+ Future 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 restoreUserAdminWithHttpInfo(String id,) async {
+ // ignore: prefer_const_declarations
+ final path = r'/admin/users/{id}/restore'
.replaceAll('{id}', id);
// ignore: prefer_final_locals
@@ -427,8 +423,8 @@ class UserApi {
/// Parameters:
///
/// * [String] id (required):
- Future restoreUser(String id,) async {
- final response = await restoreUserWithHttpInfo(id,);
+ Future restoreUserAdmin(String id,) async {
+ final response = await restoreUserAdminWithHttpInfo(id,);
if (response.statusCode >= HttpStatus.badRequest) {
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"
// FormatException when trying to decode an empty string.
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 /users' operation and returns the [Response].
- /// Parameters:
- ///
- /// * [UpdateUserDto] updateUserDto (required):
- Future updateUserWithHttpInfo(UpdateUserDto updateUserDto,) async {
+ /// Performs an HTTP 'GET /users' operation and returns the [Response].
+ Future searchUsersWithHttpInfo() async {
// ignore: prefer_const_declarations
final path = r'/users';
// ignore: prefer_final_locals
- Object? postBody = updateUserDto;
+ Object? postBody;
+
+ final queryParams = [];
+ final headerParams = {};
+ final formParams = {};
+
+ const contentTypes = [];
+
+
+ return apiClient.invokeAPI(
+ path,
+ 'GET',
+ queryParams,
+ postBody,
+ headerParams,
+ formParams,
+ contentTypes.isEmpty ? null : contentTypes.first,
+ );
+ }
+
+ Future?> 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') as List)
+ .cast()
+ .toList(growable: false);
+
+ }
+ return null;
+ }
+
+ /// Performs an HTTP 'GET /admin/users' operation and returns the [Response].
+ /// Parameters:
+ ///
+ /// * [bool] withDeleted:
+ Future searchUsersAdminWithHttpInfo({ bool? withDeleted, }) async {
+ // ignore: prefer_const_declarations
+ final path = r'/admin/users';
+
+ // ignore: prefer_final_locals
+ Object? postBody;
+
+ final queryParams = [];
+ final headerParams = {};
+ final formParams = {};
+
+ if (withDeleted != null) {
+ queryParams.addAll(_queryParams('', 'withDeleted', withDeleted));
+ }
+
+ const contentTypes = [];
+
+
+ return apiClient.invokeAPI(
+ path,
+ 'GET',
+ queryParams,
+ postBody,
+ headerParams,
+ formParams,
+ contentTypes.isEmpty ? null : contentTypes.first,
+ );
+ }
+
+ /// Parameters:
+ ///
+ /// * [bool] withDeleted:
+ Future?> 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') as List)
+ .cast()
+ .toList(growable: false);
+
+ }
+ return null;
+ }
+
+ /// Performs an HTTP 'PUT /users/me' operation and returns the [Response].
+ /// Parameters:
+ ///
+ /// * [UserUpdateMeDto] userUpdateMeDto (required):
+ Future updateMyUserWithHttpInfo(UserUpdateMeDto userUpdateMeDto,) async {
+ // ignore: prefer_const_declarations
+ final path = r'/users/me';
+
+ // ignore: prefer_final_locals
+ Object? postBody = userUpdateMeDto;
final queryParams = [];
final headerParams = {};
@@ -473,9 +567,9 @@ class UserApi {
/// Parameters:
///
- /// * [UpdateUserDto] updateUserDto (required):
- Future updateUser(UpdateUserDto updateUserDto,) async {
- final response = await updateUserWithHttpInfo(updateUserDto,);
+ /// * [UserUpdateMeDto] userUpdateMeDto (required):
+ Future updateMyUser(UserUpdateMeDto userUpdateMeDto,) async {
+ final response = await updateMyUserWithHttpInfo(userUpdateMeDto,);
if (response.statusCode >= HttpStatus.badRequest) {
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"
// FormatException when trying to decode an empty string.
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 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 = [];
+ final headerParams = {};
+ final formParams = {};
+
+ const contentTypes = ['application/json'];
+
+
+ return apiClient.invokeAPI(
+ path,
+ 'PUT',
+ queryParams,
+ postBody,
+ headerParams,
+ formParams,
+ contentTypes.isEmpty ? null : contentTypes.first,
+ );
+ }
+
+ /// Parameters:
+ ///
+ /// * [String] id (required):
+ ///
+ /// * [UserAdminUpdateDto] userAdminUpdateDto (required):
+ Future 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;
diff --git a/mobile/openapi/lib/api_client.dart b/mobile/openapi/lib/api_client.dart
index 1f959757da..bd3433872a 100644
--- a/mobile/openapi/lib/api_client.dart
+++ b/mobile/openapi/lib/api_client.dart
@@ -292,10 +292,6 @@ class ApiClient {
return CreateProfileImageResponseDto.fromJson(value);
case 'CreateTagDto':
return CreateTagDto.fromJson(value);
- case 'CreateUserDto':
- return CreateUserDto.fromJson(value);
- case 'DeleteUserDto':
- return DeleteUserDto.fromJson(value);
case 'DownloadArchiveInfo':
return DownloadArchiveInfo.fromJson(value);
case 'DownloadInfoDto':
@@ -536,18 +532,24 @@ class ApiClient {
return UpdateStackParentDto.fromJson(value);
case 'UpdateTagDto':
return UpdateTagDto.fromJson(value);
- case 'UpdateUserDto':
- return UpdateUserDto.fromJson(value);
case 'UsageByUserDto':
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':
return UserAvatarColorTypeTransformer().decode(value);
- case 'UserDto':
- return UserDto.fromJson(value);
case 'UserResponseDto':
return UserResponseDto.fromJson(value);
case 'UserStatus':
return UserStatusTypeTransformer().decode(value);
+ case 'UserUpdateMeDto':
+ return UserUpdateMeDto.fromJson(value);
case 'ValidateAccessTokenResponseDto':
return ValidateAccessTokenResponseDto.fromJson(value);
case 'ValidateLibraryDto':
diff --git a/mobile/openapi/lib/model/activity_response_dto.dart b/mobile/openapi/lib/model/activity_response_dto.dart
index d276d19e6c..cd7a4f482f 100644
--- a/mobile/openapi/lib/model/activity_response_dto.dart
+++ b/mobile/openapi/lib/model/activity_response_dto.dart
@@ -31,7 +31,7 @@ class ActivityResponseDto {
ActivityResponseDtoTypeEnum type;
- UserDto user;
+ UserResponseDto user;
@override
bool operator ==(Object other) => identical(this, other) || other is ActivityResponseDto &&
@@ -87,7 +87,7 @@ class ActivityResponseDto {
createdAt: mapDateTime(json, r'createdAt', r'')!,
id: mapValueOfType(json, r'id')!,
type: ActivityResponseDtoTypeEnum.fromJson(json[r'type'])!,
- user: UserDto.fromJson(json[r'user'])!,
+ user: UserResponseDto.fromJson(json[r'user'])!,
);
}
return null;
diff --git a/mobile/openapi/lib/model/partner_response_dto.dart b/mobile/openapi/lib/model/partner_response_dto.dart
index 1efd91c346..7c3cf03bd9 100644
--- a/mobile/openapi/lib/model/partner_response_dto.dart
+++ b/mobile/openapi/lib/model/partner_response_dto.dart
@@ -14,30 +14,15 @@ class PartnerResponseDto {
/// Returns a new [PartnerResponseDto] instance.
PartnerResponseDto({
required this.avatarColor,
- required this.createdAt,
- required this.deletedAt,
required this.email,
required this.id,
this.inTimeline,
- 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;
@@ -50,121 +35,44 @@ class PartnerResponseDto {
///
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 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 PartnerResponseDto &&
other.avatarColor == avatarColor &&
- other.createdAt == createdAt &&
- other.deletedAt == deletedAt &&
other.email == email &&
other.id == id &&
other.inTimeline == inTimeline &&
- 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;
+ other.profileImagePath == profileImagePath;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(avatarColor.hashCode) +
- (createdAt.hashCode) +
- (deletedAt == null ? 0 : deletedAt!.hashCode) +
(email.hashCode) +
(id.hashCode) +
(inTimeline == null ? 0 : inTimeline!.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);
+ (profileImagePath.hashCode);
@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 toJson() {
final json = {};
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;
if (this.inTimeline != null) {
json[r'inTimeline'] = this.inTimeline;
} else {
// 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'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;
}
@@ -177,22 +85,11 @@ class PartnerResponseDto {
return PartnerResponseDto(
avatarColor: UserAvatarColor.fromJson(json[r'avatarColor'])!,
- createdAt: mapDateTime(json, r'createdAt', r'')!,
- deletedAt: mapDateTime(json, r'deletedAt', r''),
email: mapValueOfType(json, r'email')!,
id: mapValueOfType(json, r'id')!,
inTimeline: mapValueOfType(json, r'inTimeline'),
- isAdmin: mapValueOfType(json, r'isAdmin')!,
- memoriesEnabled: mapValueOfType(json, r'memoriesEnabled'),
name: mapValueOfType(json, r'name')!,
- oauthId: mapValueOfType(json, r'oauthId')!,
profileImagePath: mapValueOfType(json, r'profileImagePath')!,
- quotaSizeInBytes: mapValueOfType(json, r'quotaSizeInBytes'),
- quotaUsageInBytes: mapValueOfType(json, r'quotaUsageInBytes'),
- shouldChangePassword: mapValueOfType(json, r'shouldChangePassword')!,
- status: UserStatus.fromJson(json[r'status'])!,
- storageLabel: mapValueOfType(json, r'storageLabel'),
- updatedAt: mapDateTime(json, r'updatedAt', r'')!,
);
}
return null;
@@ -241,20 +138,10 @@ class PartnerResponseDto {
/// The list of required keys that must be present in a JSON.
static const requiredKeys = {
'avatarColor',
- 'createdAt',
- 'deletedAt',
'email',
'id',
- 'isAdmin',
'name',
- 'oauthId',
'profileImagePath',
- 'quotaSizeInBytes',
- 'quotaUsageInBytes',
- 'shouldChangePassword',
- 'status',
- 'storageLabel',
- 'updatedAt',
};
}
diff --git a/mobile/openapi/lib/model/create_user_dto.dart b/mobile/openapi/lib/model/user_admin_create_dto.dart
similarity index 80%
rename from mobile/openapi/lib/model/create_user_dto.dart
rename to mobile/openapi/lib/model/user_admin_create_dto.dart
index 4b0bdd55da..daf8854e01 100644
--- a/mobile/openapi/lib/model/create_user_dto.dart
+++ b/mobile/openapi/lib/model/user_admin_create_dto.dart
@@ -10,9 +10,9 @@
part of openapi.api;
-class CreateUserDto {
- /// Returns a new [CreateUserDto] instance.
- CreateUserDto({
+class UserAdminCreateDto {
+ /// Returns a new [UserAdminCreateDto] instance.
+ UserAdminCreateDto({
required this.email,
this.memoriesEnabled,
required this.name,
@@ -59,7 +59,7 @@ class CreateUserDto {
String? storageLabel;
@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.memoriesEnabled == memoriesEnabled &&
other.name == name &&
@@ -82,7 +82,7 @@ class CreateUserDto {
(storageLabel == null ? 0 : storageLabel!.hashCode);
@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 toJson() {
final json = {};
@@ -117,14 +117,14 @@ class CreateUserDto {
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.
// ignore: prefer_constructors_over_static_methods
- static CreateUserDto? fromJson(dynamic value) {
+ static UserAdminCreateDto? fromJson(dynamic value) {
if (value is Map) {
final json = value.cast();
- return CreateUserDto(
+ return UserAdminCreateDto(
email: mapValueOfType(json, r'email')!,
memoriesEnabled: mapValueOfType(json, r'memoriesEnabled'),
name: mapValueOfType(json, r'name')!,
@@ -138,11 +138,11 @@ class CreateUserDto {
return null;
}
- static List listFromJson(dynamic json, {bool growable = false,}) {
- final result = [];
+ static List listFromJson(dynamic json, {bool growable = false,}) {
+ final result = [];
if (json is List && json.isNotEmpty) {
for (final row in json) {
- final value = CreateUserDto.fromJson(row);
+ final value = UserAdminCreateDto.fromJson(row);
if (value != null) {
result.add(value);
}
@@ -151,12 +151,12 @@ class CreateUserDto {
return result.toList(growable: growable);
}
- static Map mapFromJson(dynamic json) {
- final map = {};
+ static Map mapFromJson(dynamic json) {
+ final map = {};
if (json is Map && json.isNotEmpty) {
json = json.cast(); // ignore: parameter_assignments
for (final entry in json.entries) {
- final value = CreateUserDto.fromJson(entry.value);
+ final value = UserAdminCreateDto.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
@@ -165,14 +165,14 @@ class CreateUserDto {
return map;
}
- // maps a json object with a list of CreateUserDto-objects as value to a dart map
- static Map> mapListFromJson(dynamic json, {bool growable = false,}) {
- final map = >{};
+ // maps a json object with a list of UserAdminCreateDto-objects as value to a dart map
+ static Map> mapListFromJson(dynamic json, {bool growable = false,}) {
+ final map = >{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast();
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;
diff --git a/mobile/openapi/lib/model/delete_user_dto.dart b/mobile/openapi/lib/model/user_admin_delete_dto.dart
similarity index 67%
rename from mobile/openapi/lib/model/delete_user_dto.dart
rename to mobile/openapi/lib/model/user_admin_delete_dto.dart
index a758991fa9..7778b15775 100644
--- a/mobile/openapi/lib/model/delete_user_dto.dart
+++ b/mobile/openapi/lib/model/user_admin_delete_dto.dart
@@ -10,9 +10,9 @@
part of openapi.api;
-class DeleteUserDto {
- /// Returns a new [DeleteUserDto] instance.
- DeleteUserDto({
+class UserAdminDeleteDto {
+ /// Returns a new [UserAdminDeleteDto] instance.
+ UserAdminDeleteDto({
this.force,
});
@@ -25,7 +25,7 @@ class DeleteUserDto {
bool? force;
@override
- bool operator ==(Object other) => identical(this, other) || other is DeleteUserDto &&
+ bool operator ==(Object other) => identical(this, other) || other is UserAdminDeleteDto &&
other.force == force;
@override
@@ -34,7 +34,7 @@ class DeleteUserDto {
(force == null ? 0 : force!.hashCode);
@override
- String toString() => 'DeleteUserDto[force=$force]';
+ String toString() => 'UserAdminDeleteDto[force=$force]';
Map toJson() {
final json = {};
@@ -46,25 +46,25 @@ class DeleteUserDto {
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.
// ignore: prefer_constructors_over_static_methods
- static DeleteUserDto? fromJson(dynamic value) {
+ static UserAdminDeleteDto? fromJson(dynamic value) {
if (value is Map) {
final json = value.cast();
- return DeleteUserDto(
+ return UserAdminDeleteDto(
force: mapValueOfType(json, r'force'),
);
}
return null;
}
- static List listFromJson(dynamic json, {bool growable = false,}) {
- final result = [];
+ static List listFromJson(dynamic json, {bool growable = false,}) {
+ final result = [];
if (json is List && json.isNotEmpty) {
for (final row in json) {
- final value = DeleteUserDto.fromJson(row);
+ final value = UserAdminDeleteDto.fromJson(row);
if (value != null) {
result.add(value);
}
@@ -73,12 +73,12 @@ class DeleteUserDto {
return result.toList(growable: growable);
}
- static Map mapFromJson(dynamic json) {
- final map = {};
+ static Map mapFromJson(dynamic json) {
+ final map = {};
if (json is Map && json.isNotEmpty) {
json = json.cast(); // ignore: parameter_assignments
for (final entry in json.entries) {
- final value = DeleteUserDto.fromJson(entry.value);
+ final value = UserAdminDeleteDto.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
@@ -87,14 +87,14 @@ class DeleteUserDto {
return map;
}
- // maps a json object with a list of DeleteUserDto-objects as value to a dart map
- static Map> mapListFromJson(dynamic json, {bool growable = false,}) {
- final map = >{};
+ // maps a json object with a list of UserAdminDeleteDto-objects as value to a dart map
+ static Map> mapListFromJson(dynamic json, {bool growable = false,}) {
+ final map = >{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast();
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;
diff --git a/mobile/openapi/lib/model/user_admin_response_dto.dart b/mobile/openapi/lib/model/user_admin_response_dto.dart
new file mode 100644
index 0000000000..3fc8c2e274
--- /dev/null
+++ b/mobile/openapi/lib/model/user_admin_response_dto.dart
@@ -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 toJson() {
+ final json = {};
+ 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();
+
+ return UserAdminResponseDto(
+ avatarColor: UserAvatarColor.fromJson(json[r'avatarColor'])!,
+ createdAt: mapDateTime(json, r'createdAt', r'')!,
+ deletedAt: mapDateTime(json, r'deletedAt', r''),
+ email: mapValueOfType(json, r'email')!,
+ id: mapValueOfType(json, r'id')!,
+ isAdmin: mapValueOfType(json, r'isAdmin')!,
+ memoriesEnabled: mapValueOfType(json, r'memoriesEnabled'),
+ name: mapValueOfType(json, r'name')!,
+ oauthId: mapValueOfType(json, r'oauthId')!,
+ profileImagePath: mapValueOfType(json, r'profileImagePath')!,
+ quotaSizeInBytes: mapValueOfType(json, r'quotaSizeInBytes'),
+ quotaUsageInBytes: mapValueOfType(json, r'quotaUsageInBytes'),
+ shouldChangePassword: mapValueOfType(json, r'shouldChangePassword')!,
+ status: UserStatus.fromJson(json[r'status'])!,
+ storageLabel: mapValueOfType(json, r'storageLabel'),
+ updatedAt: mapDateTime(json, r'updatedAt', r'')!,
+ );
+ }
+ return null;
+ }
+
+ static List listFromJson(dynamic json, {bool growable = false,}) {
+ final result = [];
+ 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 mapFromJson(dynamic json) {
+ final map = {};
+ if (json is Map && json.isNotEmpty) {
+ json = json.cast(); // 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> mapListFromJson(dynamic json, {bool growable = false,}) {
+ final map = >{};
+ if (json is Map && json.isNotEmpty) {
+ // ignore: parameter_assignments
+ json = json.cast();
+ 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 = {
+ 'avatarColor',
+ 'createdAt',
+ 'deletedAt',
+ 'email',
+ 'id',
+ 'isAdmin',
+ 'name',
+ 'oauthId',
+ 'profileImagePath',
+ 'quotaSizeInBytes',
+ 'quotaUsageInBytes',
+ 'shouldChangePassword',
+ 'status',
+ 'storageLabel',
+ 'updatedAt',
+ };
+}
+
diff --git a/mobile/openapi/lib/model/update_user_dto.dart b/mobile/openapi/lib/model/user_admin_update_dto.dart
similarity index 73%
rename from mobile/openapi/lib/model/update_user_dto.dart
rename to mobile/openapi/lib/model/user_admin_update_dto.dart
index caa0600793..ecd145248f 100644
--- a/mobile/openapi/lib/model/update_user_dto.dart
+++ b/mobile/openapi/lib/model/user_admin_update_dto.dart
@@ -10,13 +10,11 @@
part of openapi.api;
-class UpdateUserDto {
- /// Returns a new [UpdateUserDto] instance.
- UpdateUserDto({
+class UserAdminUpdateDto {
+ /// Returns a new [UserAdminUpdateDto] instance.
+ UserAdminUpdateDto({
this.avatarColor,
this.email,
- required this.id,
- this.isAdmin,
this.memoriesEnabled,
this.name,
this.password,
@@ -41,16 +39,6 @@ class UpdateUserDto {
///
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
/// does not include a default value (using the "default:" property), however, the generated
@@ -86,20 +74,12 @@ class UpdateUserDto {
///
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;
@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.email == email &&
- other.id == id &&
- other.isAdmin == isAdmin &&
other.memoriesEnabled == memoriesEnabled &&
other.name == name &&
other.password == password &&
@@ -112,8 +92,6 @@ class UpdateUserDto {
// ignore: unnecessary_parenthesis
(avatarColor == null ? 0 : avatarColor!.hashCode) +
(email == null ? 0 : email!.hashCode) +
- (id.hashCode) +
- (isAdmin == null ? 0 : isAdmin!.hashCode) +
(memoriesEnabled == null ? 0 : memoriesEnabled!.hashCode) +
(name == null ? 0 : name!.hashCode) +
(password == null ? 0 : password!.hashCode) +
@@ -122,7 +100,7 @@ class UpdateUserDto {
(storageLabel == null ? 0 : storageLabel!.hashCode);
@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 toJson() {
final json = {};
@@ -135,12 +113,6 @@ class UpdateUserDto {
json[r'email'] = this.email;
} else {
// 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) {
json[r'memoriesEnabled'] = this.memoriesEnabled;
@@ -175,18 +147,16 @@ class UpdateUserDto {
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.
// ignore: prefer_constructors_over_static_methods
- static UpdateUserDto? fromJson(dynamic value) {
+ static UserAdminUpdateDto? fromJson(dynamic value) {
if (value is Map) {
final json = value.cast();
- return UpdateUserDto(
+ return UserAdminUpdateDto(
avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']),
email: mapValueOfType(json, r'email'),
- id: mapValueOfType(json, r'id')!,
- isAdmin: mapValueOfType(json, r'isAdmin'),
memoriesEnabled: mapValueOfType(json, r'memoriesEnabled'),
name: mapValueOfType(json, r'name'),
password: mapValueOfType(json, r'password'),
@@ -198,11 +168,11 @@ class UpdateUserDto {
return null;
}
- static List listFromJson(dynamic json, {bool growable = false,}) {
- final result = [];
+ static List listFromJson(dynamic json, {bool growable = false,}) {
+ final result = [];
if (json is List && json.isNotEmpty) {
for (final row in json) {
- final value = UpdateUserDto.fromJson(row);
+ final value = UserAdminUpdateDto.fromJson(row);
if (value != null) {
result.add(value);
}
@@ -211,12 +181,12 @@ class UpdateUserDto {
return result.toList(growable: growable);
}
- static Map mapFromJson(dynamic json) {
- final map = {};
+ static Map mapFromJson(dynamic json) {
+ final map = {};
if (json is Map && json.isNotEmpty) {
json = json.cast(); // ignore: parameter_assignments
for (final entry in json.entries) {
- final value = UpdateUserDto.fromJson(entry.value);
+ final value = UserAdminUpdateDto.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
@@ -225,14 +195,14 @@ class UpdateUserDto {
return map;
}
- // maps a json object with a list of UpdateUserDto-objects as value to a dart map
- static Map> mapListFromJson(dynamic json, {bool growable = false,}) {
- final map = >{};
+ // maps a json object with a list of UserAdminUpdateDto-objects as value to a dart map
+ static Map> mapListFromJson(dynamic json, {bool growable = false,}) {
+ final map = >{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast();
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;
@@ -240,7 +210,6 @@ class UpdateUserDto {
/// The list of required keys that must be present in a JSON.
static const requiredKeys = {
- 'id',
};
}
diff --git a/mobile/openapi/lib/model/user_dto.dart b/mobile/openapi/lib/model/user_dto.dart
deleted file mode 100644
index 1c4c4eb0b4..0000000000
--- a/mobile/openapi/lib/model/user_dto.dart
+++ /dev/null
@@ -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 toJson() {
- final json = {};
- 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();
-
- return UserDto(
- avatarColor: UserAvatarColor.fromJson(json[r'avatarColor'])!,
- email: mapValueOfType(json, r'email')!,
- id: mapValueOfType(json, r'id')!,
- name: mapValueOfType(json, r'name')!,
- profileImagePath: mapValueOfType(json, r'profileImagePath')!,
- );
- }
- return null;
- }
-
- static List listFromJson(dynamic json, {bool growable = false,}) {
- final result = [];
- 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 mapFromJson(dynamic json) {
- final map = {};
- if (json is Map && json.isNotEmpty) {
- json = json.cast(); // 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> mapListFromJson(dynamic json, {bool growable = false,}) {
- final map = >{};
- if (json is Map && json.isNotEmpty) {
- // ignore: parameter_assignments
- json = json.cast();
- 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 = {
- 'avatarColor',
- 'email',
- 'id',
- 'name',
- 'profileImagePath',
- };
-}
-
diff --git a/mobile/openapi/lib/model/user_response_dto.dart b/mobile/openapi/lib/model/user_response_dto.dart
index 063b3d33b6..41c1899848 100644
--- a/mobile/openapi/lib/model/user_response_dto.dart
+++ b/mobile/openapi/lib/model/user_response_dto.dart
@@ -14,141 +14,49 @@ class UserResponseDto {
/// Returns a new [UserResponseDto] instance.
UserResponseDto({
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 UserResponseDto &&
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;
+ other.profileImagePath == profileImagePath;
@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);
+ (profileImagePath.hashCode);
@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 toJson() {
final json = {};
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;
}
@@ -161,21 +69,10 @@ class UserResponseDto {
return UserResponseDto(
avatarColor: UserAvatarColor.fromJson(json[r'avatarColor'])!,
- createdAt: mapDateTime(json, r'createdAt', r'')!,
- deletedAt: mapDateTime(json, r'deletedAt', r''),
email: mapValueOfType(json, r'email')!,
id: mapValueOfType(json, r'id')!,
- isAdmin: mapValueOfType(json, r'isAdmin')!,
- memoriesEnabled: mapValueOfType(json, r'memoriesEnabled'),
name: mapValueOfType(json, r'name')!,
- oauthId: mapValueOfType(json, r'oauthId')!,
profileImagePath: mapValueOfType(json, r'profileImagePath')!,
- quotaSizeInBytes: mapValueOfType(json, r'quotaSizeInBytes'),
- quotaUsageInBytes: mapValueOfType(json, r'quotaUsageInBytes'),
- shouldChangePassword: mapValueOfType(json, r'shouldChangePassword')!,
- status: UserStatus.fromJson(json[r'status'])!,
- storageLabel: mapValueOfType(json, r'storageLabel'),
- updatedAt: mapDateTime(json, r'updatedAt', r'')!,
);
}
return null;
@@ -224,20 +121,10 @@ class UserResponseDto {
/// The list of required keys that must be present in a JSON.
static const requiredKeys = {
'avatarColor',
- 'createdAt',
- 'deletedAt',
'email',
'id',
- 'isAdmin',
'name',
- 'oauthId',
'profileImagePath',
- 'quotaSizeInBytes',
- 'quotaUsageInBytes',
- 'shouldChangePassword',
- 'status',
- 'storageLabel',
- 'updatedAt',
};
}
diff --git a/mobile/openapi/lib/model/user_update_me_dto.dart b/mobile/openapi/lib/model/user_update_me_dto.dart
new file mode 100644
index 0000000000..1b54d4a383
--- /dev/null
+++ b/mobile/openapi/lib/model/user_update_me_dto.dart
@@ -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 toJson() {
+ final json = {};
+ 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();
+
+ return UserUpdateMeDto(
+ avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']),
+ email: mapValueOfType(json, r'email'),
+ memoriesEnabled: mapValueOfType(json, r'memoriesEnabled'),
+ name: mapValueOfType(json, r'name'),
+ password: mapValueOfType(json, r'password'),
+ );
+ }
+ return null;
+ }
+
+ static List listFromJson(dynamic json, {bool growable = false,}) {
+ final result = [];
+ 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 mapFromJson(dynamic json) {
+ final map = {};
+ if (json is Map && json.isNotEmpty) {
+ json = json.cast(); // 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> mapListFromJson(dynamic json, {bool growable = false,}) {
+ final map = >{};
+ if (json is Map && json.isNotEmpty) {
+ // ignore: parameter_assignments
+ json = json.cast();
+ 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 = {
+ };
+}
+
diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json
index 82e328bc47..558823e62b 100644
--- a/open-api/immich-openapi-specs.json
+++ b/open-api/immich-openapi-specs.json
@@ -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": {
"get": {
"operationId": "getAllAlbums",
@@ -1879,7 +2147,7 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/UserResponseDto"
+ "$ref": "#/components/schemas/UserAdminResponseDto"
}
}
},
@@ -1910,7 +2178,7 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/UserResponseDto"
+ "$ref": "#/components/schemas/UserAdminResponseDto"
}
}
},
@@ -3160,7 +3428,7 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/UserResponseDto"
+ "$ref": "#/components/schemas/UserAdminResponseDto"
}
}
},
@@ -3206,7 +3474,7 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/UserResponseDto"
+ "$ref": "#/components/schemas/UserAdminResponseDto"
}
}
},
@@ -6030,17 +6298,8 @@
},
"/users": {
"get": {
- "operationId": "getAllUsers",
- "parameters": [
- {
- "name": "isAll",
- "required": true,
- "in": "query",
- "schema": {
- "type": "boolean"
- }
- }
- ],
+ "operationId": "searchUsers",
+ "parameters": [],
"responses": {
"200": {
"content": {
@@ -6070,26 +6329,18 @@
"tags": [
"User"
]
- },
- "post": {
- "operationId": "createUser",
+ }
+ },
+ "/users/me": {
+ "get": {
+ "operationId": "getMyUser",
"parameters": [],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/CreateUserDto"
- }
- }
- },
- "required": true
- },
"responses": {
- "201": {
+ "200": {
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/UserResponseDto"
+ "$ref": "#/components/schemas/UserAdminResponseDto"
}
}
},
@@ -6112,13 +6363,13 @@
]
},
"put": {
- "operationId": "updateUser",
+ "operationId": "updateMyUser",
"parameters": [],
"requestBody": {
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/UpdateUserDto"
+ "$ref": "#/components/schemas/UserUpdateMeDto"
}
}
},
@@ -6129,39 +6380,7 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/UserResponseDto"
- }
- }
- },
- "description": ""
- }
- },
- "security": [
- {
- "bearer": []
- },
- {
- "cookie": []
- },
- {
- "api_key": []
- }
- ],
- "tags": [
- "User"
- ]
- }
- },
- "/users/me": {
- "get": {
- "operationId": "getMyUserInfo",
- "parameters": [],
- "responses": {
- "200": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/UserResponseDto"
+ "$ref": "#/components/schemas/UserAdminResponseDto"
}
}
},
@@ -6251,58 +6470,8 @@
}
},
"/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": {
- "operationId": "getUserById",
+ "operationId": "getUser",
"parameters": [
{
"name": "id",
@@ -6384,48 +6553,6 @@
"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": {
@@ -6567,7 +6694,7 @@
"type": "string"
},
"user": {
- "$ref": "#/components/schemas/UserDto"
+ "$ref": "#/components/schemas/UserResponseDto"
}
},
"required": [
@@ -7775,52 +7902,6 @@
],
"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": {
"properties": {
"assetIds": {
@@ -8803,15 +8884,6 @@
"avatarColor": {
"$ref": "#/components/schemas/UserAvatarColor"
},
- "createdAt": {
- "format": "date-time",
- "type": "string"
- },
- "deletedAt": {
- "format": "date-time",
- "nullable": true,
- "type": "string"
- },
"email": {
"type": "string"
},
@@ -8821,62 +8893,19 @@
"inTimeline": {
"type": "boolean"
},
- "isAdmin": {
- "type": "boolean"
- },
- "memoriesEnabled": {
- "type": "boolean"
- },
"name": {
"type": "string"
},
- "oauthId": {
- "type": "string"
- },
"profileImagePath": {
"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": [
"avatarColor",
- "createdAt",
- "deletedAt",
"email",
"id",
- "isAdmin",
"name",
- "oauthId",
- "profileImagePath",
- "quotaSizeInBytes",
- "quotaUsageInBytes",
- "shouldChangePassword",
- "status",
- "storageLabel",
- "updatedAt"
+ "profileImagePath"
],
"type": "object"
},
@@ -10810,48 +10839,6 @@
},
"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": {
"properties": {
"photos": {
@@ -10886,49 +10873,53 @@
],
"type": "object"
},
- "UserAvatarColor": {
- "enum": [
- "primary",
- "pink",
- "red",
- "yellow",
- "blue",
- "green",
- "purple",
- "orange",
- "gray",
- "amber"
- ],
- "type": "string"
- },
- "UserDto": {
+ "UserAdminCreateDto": {
"properties": {
- "avatarColor": {
- "$ref": "#/components/schemas/UserAvatarColor"
- },
"email": {
"type": "string"
},
- "id": {
- "type": "string"
+ "memoriesEnabled": {
+ "type": "boolean"
},
"name": {
"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"
}
},
"required": [
- "avatarColor",
"email",
- "id",
"name",
- "profileImagePath"
+ "password"
],
"type": "object"
},
- "UserResponseDto": {
+ "UserAdminDeleteDto": {
+ "properties": {
+ "force": {
+ "type": "boolean"
+ }
+ },
+ "type": "object"
+ },
+ "UserAdminResponseDto": {
"properties": {
"avatarColor": {
"$ref": "#/components/schemas/UserAvatarColor"
@@ -11007,6 +10998,81 @@
],
"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": {
"enum": [
"active",
@@ -11015,6 +11081,26 @@
],
"type": "string"
},
+ "UserUpdateMeDto": {
+ "properties": {
+ "avatarColor": {
+ "$ref": "#/components/schemas/UserAvatarColor"
+ },
+ "email": {
+ "type": "string"
+ },
+ "memoriesEnabled": {
+ "type": "boolean"
+ },
+ "name": {
+ "type": "string"
+ },
+ "password": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
"ValidateAccessTokenResponseDto": {
"properties": {
"authStatus": {
diff --git a/open-api/typescript-sdk/README.md b/open-api/typescript-sdk/README.md
index 91b702d43e..53a83a4237 100644
--- a/open-api/typescript-sdk/README.md
+++ b/open-api/typescript-sdk/README.md
@@ -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).
```typescript
+<<<<<<< HEAD
+import { getAllAlbums, getAllAssets, getMyUser, init } from "@immich/sdk";
+=======
import { getAllAlbums, getMyUserInfo, init } from "@immich/sdk";
+>>>>>>> e7c8501930a988dfb6c23ce1c48b0beb076a58c2
const API_KEY = ""; // process.env.IMMICH_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();
+>>>>>>> e7c8501930a988dfb6c23ce1c48b0beb076a58c2
const albums = await getAllAlbums({});
console.log({ user, albums });
diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts
index adbae62bbd..2c07072f68 100644
--- a/open-api/typescript-sdk/src/fetch-client.ts
+++ b/open-api/typescript-sdk/src/fetch-client.ts
@@ -14,7 +14,7 @@ const oazapfts = Oazapfts.runtime(defaults);
export const servers = {
server1: "/api"
};
-export type UserDto = {
+export type UserResponseDto = {
avatarColor: UserAvatarColor;
email: string;
id: string;
@@ -27,7 +27,7 @@ export type ActivityResponseDto = {
createdAt: string;
id: string;
"type": Type;
- user: UserDto;
+ user: UserResponseDto;
};
export type ActivityCreateDto = {
albumId: string;
@@ -38,7 +38,7 @@ export type ActivityCreateDto = {
export type ActivityStatisticsResponseDto = {
comments: number;
};
-export type UserResponseDto = {
+export type UserAdminResponseDto = {
avatarColor: UserAvatarColor;
createdAt: string;
deletedAt: string | null;
@@ -56,6 +56,29 @@ export type UserResponseDto = {
storageLabel: string | null;
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 = {
role: AlbumUserRole;
user: UserResponseDto;
@@ -517,22 +540,11 @@ export type OAuthCallbackDto = {
};
export type PartnerResponseDto = {
avatarColor: UserAvatarColor;
- createdAt: string;
- deletedAt: string | null;
email: string;
id: string;
inTimeline?: boolean;
- isAdmin: boolean;
- memoriesEnabled?: boolean;
name: string;
- oauthId: string;
profileImagePath: string;
- quotaSizeInBytes: number | null;
- quotaUsageInBytes: number | null;
- shouldChangePassword: boolean;
- status: UserStatus;
- storageLabel: string | null;
- updatedAt: string;
};
export type UpdatePartnerDto = {
inTimeline: boolean;
@@ -1060,27 +1072,12 @@ export type TimeBucketResponseDto = {
count: number;
timeBucket: string;
};
-export type CreateUserDto = {
- email: string;
- memoriesEnabled?: boolean;
- name: string;
- notify?: boolean;
- password: string;
- quotaSizeInBytes?: number | null;
- shouldChangePassword?: boolean;
- storageLabel?: string | null;
-};
-export type UpdateUserDto = {
+export type UserUpdateMeDto = {
avatarColor?: UserAvatarColor;
email?: string;
- id: string;
- isAdmin?: boolean;
memoriesEnabled?: boolean;
name?: string;
password?: string;
- quotaSizeInBytes?: number | null;
- shouldChangePassword?: boolean;
- storageLabel?: string;
};
export type CreateProfileImageDto = {
file: Blob;
@@ -1089,9 +1086,6 @@ export type CreateProfileImageResponseDto = {
profileImagePath: string;
userId: string;
};
-export type DeleteUserDto = {
- force?: boolean;
-};
export function getActivities({ albumId, assetId, level, $type, userId }: {
albumId: string;
assetId?: string;
@@ -1146,6 +1140,77 @@ export function deleteActivity({ id }: {
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 }: {
assetId?: string;
shared?: boolean;
@@ -1589,7 +1654,7 @@ export function signUpAdmin({ signUpDto }: {
}, opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 201;
- data: UserResponseDto;
+ data: UserAdminResponseDto;
}>("/auth/admin-sign-up", oazapfts.json({
...opts,
method: "POST",
@@ -1601,7 +1666,7 @@ export function changePassword({ changePasswordDto }: {
}, opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 200;
- data: UserResponseDto;
+ data: UserAdminResponseDto;
}>("/auth/change-password", oazapfts.json({
...opts,
method: "POST",
@@ -1934,7 +1999,7 @@ export function linkOAuthAccount({ oAuthCallbackDto }: {
}, opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 201;
- data: UserResponseDto;
+ data: UserAdminResponseDto;
}>("/oauth/link", oazapfts.json({
...opts,
method: "POST",
@@ -1949,7 +2014,7 @@ export function redirectOAuthToMobile(opts?: Oazapfts.RequestOpts) {
export function unlinkOAuthAccount(opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 201;
- data: UserResponseDto;
+ data: UserAdminResponseDto;
}>("/oauth/unlink", {
...opts,
method: "POST"
@@ -2687,50 +2752,34 @@ export function restoreAssets({ bulkIdsDto }: {
body: bulkIdsDto
})));
}
-export function getAllUsers({ isAll }: {
- isAll: boolean;
-}, opts?: Oazapfts.RequestOpts) {
+export function searchUsers(opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 200;
data: UserResponseDto[];
- }>(`/users${QS.query(QS.explode({
- isAll
- }))}`, {
+ }>("/users", {
...opts
}));
}
-export function createUser({ createUserDto }: {
- 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) {
+export function getMyUser(opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 200;
- data: UserResponseDto;
- }>("/users", oazapfts.json({
- ...opts,
- method: "PUT",
- body: updateUserDto
- })));
-}
-export function getMyUserInfo(opts?: Oazapfts.RequestOpts) {
- return oazapfts.ok(oazapfts.fetchJson<{
- status: 200;
- data: UserResponseDto;
+ data: UserAdminResponseDto;
}>("/users/me", {
...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) {
return oazapfts.ok(oazapfts.fetchText("/users/profile-image", {
...opts,
@@ -2749,20 +2798,7 @@ export function createProfileImage({ createProfileImageDto }: {
body: createProfileImageDto
})));
}
-export function deleteUser({ id, deleteUserDto }: {
- 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 }: {
+export function getUser({ id }: {
id: string;
}, opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
@@ -2782,17 +2818,6 @@ export function getProfileImage({ id }: {
...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 {
Album = "album",
Asset = "asset"
@@ -2817,15 +2842,15 @@ export enum UserAvatarColor {
Gray = "gray",
Amber = "amber"
}
-export enum AlbumUserRole {
- Editor = "editor",
- Viewer = "viewer"
-}
export enum UserStatus {
Active = "active",
Removing = "removing",
Deleted = "deleted"
}
+export enum AlbumUserRole {
+ Editor = "editor",
+ Viewer = "viewer"
+}
export enum TagTypeEnum {
Object = "OBJECT",
Face = "FACE",
diff --git a/server/src/commands/reset-admin-password.command.ts b/server/src/commands/reset-admin-password.command.ts
index 32f77109b0..e5dee49837 100644
--- a/server/src/commands/reset-admin-password.command.ts
+++ b/server/src/commands/reset-admin-password.command.ts
@@ -1,9 +1,9 @@
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';
const prompt = (inquirer: InquirerService) => {
- return function ask(admin: UserResponseDto) {
+ return function ask(admin: UserAdminResponseDto) {
const { id, oauthId, email, name } = admin;
console.log(`Found Admin:
- ID=${id}
diff --git a/server/src/controllers/auth.controller.ts b/server/src/controllers/auth.controller.ts
index 40fdf90916..7dcef9df5f 100644
--- a/server/src/controllers/auth.controller.ts
+++ b/server/src/controllers/auth.controller.ts
@@ -12,7 +12,7 @@ import {
SignUpDto,
ValidateAccessTokenResponseDto,
} 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 { AuthService, LoginDetails } from 'src/services/auth.service';
import { respondWithCookie, respondWithoutCookie } from 'src/utils/response';
@@ -40,7 +40,7 @@ export class AuthController {
}
@Post('admin-sign-up')
- signUpAdmin(@Body() dto: SignUpDto): Promise {
+ signUpAdmin(@Body() dto: SignUpDto): Promise {
return this.service.adminSignUp(dto);
}
@@ -54,8 +54,8 @@ export class AuthController {
@Post('change-password')
@HttpCode(HttpStatus.OK)
@Authenticated()
- changePassword(@Auth() auth: AuthDto, @Body() dto: ChangePasswordDto): Promise {
- return this.service.changePassword(auth, dto).then(mapUser);
+ changePassword(@Auth() auth: AuthDto, @Body() dto: ChangePasswordDto): Promise {
+ return this.service.changePassword(auth, dto);
}
@Post('logout')
diff --git a/server/src/controllers/index.ts b/server/src/controllers/index.ts
index 187ba4b4db..ca454b6a1d 100644
--- a/server/src/controllers/index.ts
+++ b/server/src/controllers/index.ts
@@ -27,6 +27,7 @@ import { SystemMetadataController } from 'src/controllers/system-metadata.contro
import { TagController } from 'src/controllers/tag.controller';
import { TimelineController } from 'src/controllers/timeline.controller';
import { TrashController } from 'src/controllers/trash.controller';
+import { UserAdminController } from 'src/controllers/user-admin.controller';
import { UserController } from 'src/controllers/user.controller';
export const controllers = [
@@ -59,5 +60,6 @@ export const controllers = [
TagController,
TimelineController,
TrashController,
+ UserAdminController,
UserController,
];
diff --git a/server/src/controllers/oauth.controller.ts b/server/src/controllers/oauth.controller.ts
index 3b498c7ddd..764e67d676 100644
--- a/server/src/controllers/oauth.controller.ts
+++ b/server/src/controllers/oauth.controller.ts
@@ -10,7 +10,7 @@ import {
OAuthCallbackDto,
OAuthConfigDto,
} 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 { AuthService, LoginDetails } from 'src/services/auth.service';
import { respondWithCookie } from 'src/utils/response';
@@ -53,13 +53,13 @@ export class OAuthController {
@Post('link')
@Authenticated()
- linkOAuthAccount(@Auth() auth: AuthDto, @Body() dto: OAuthCallbackDto): Promise {
+ linkOAuthAccount(@Auth() auth: AuthDto, @Body() dto: OAuthCallbackDto): Promise {
return this.service.link(auth, dto);
}
@Post('unlink')
@Authenticated()
- unlinkOAuthAccount(@Auth() auth: AuthDto): Promise {
+ unlinkOAuthAccount(@Auth() auth: AuthDto): Promise {
return this.service.unlink(auth);
}
}
diff --git a/server/src/controllers/user-admin.controller.ts b/server/src/controllers/user-admin.controller.ts
new file mode 100644
index 0000000000..4d0b781e81
--- /dev/null
+++ b/server/src/controllers/user-admin.controller.ts
@@ -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 {
+ return this.service.search(auth, dto);
+ }
+
+ @Post()
+ @Authenticated({ admin: true })
+ createUserAdmin(@Body() createUserDto: UserAdminCreateDto): Promise {
+ return this.service.create(createUserDto);
+ }
+
+ @Get(':id')
+ @Authenticated({ admin: true })
+ getUserAdmin(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise {
+ return this.service.get(auth, id);
+ }
+
+ @Put(':id')
+ @Authenticated({ admin: true })
+ updateUserAdmin(
+ @Auth() auth: AuthDto,
+ @Param() { id }: UUIDParamDto,
+ @Body() dto: UserAdminUpdateDto,
+ ): Promise {
+ return this.service.update(auth, id, dto);
+ }
+
+ @Delete(':id')
+ @Authenticated({ admin: true })
+ deleteUserAdmin(
+ @Auth() auth: AuthDto,
+ @Param() { id }: UUIDParamDto,
+ @Body() dto: UserAdminDeleteDto,
+ ): Promise {
+ return this.service.delete(auth, id, dto);
+ }
+
+ @Post(':id/restore')
+ @Authenticated({ admin: true })
+ restoreUserAdmin(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise