From 47c5a3dbf6d3eef7fa9284c5cd1b2d4b5ae31276 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Thu, 20 Aug 2026 11:23:12 -0400 Subject: [PATCH 1/6] refactor: api key response dto (#30887) --- e2e/src/specs/server/api/api-key.e2e-spec.ts | 51 +++++++++++--------- e2e/src/specs/server/cli/login.e2e-spec.ts | 8 +-- e2e/src/utils.ts | 6 +-- open-api/immich-openapi-specs.json | 51 +++++++++++++++++++- packages/sdk/src/fetch-client.ts | 10 ++++ server/src/dtos/api-key.dto.ts | 4 +- server/src/services/api-key.service.ts | 7 +-- web/src/lib/services/api-key.service.ts | 4 +- 8 files changed, 104 insertions(+), 37 deletions(-) diff --git a/e2e/src/specs/server/api/api-key.e2e-spec.ts b/e2e/src/specs/server/api/api-key.e2e-spec.ts index 8ffcf1f52562ea..ec481cee01a697 100644 --- a/e2e/src/specs/server/api/api-key.e2e-spec.ts +++ b/e2e/src/specs/server/api/api-key.e2e-spec.ts @@ -34,14 +34,14 @@ describe('/api-keys', () => { permissions: [Permission.ApiKeyRead], }); expect(body).toEqual({ + id: expect.any(String), + name: 'API Key', + permissions: [Permission.ApiKeyRead], + createdAt: expect.any(String), + updatedAt: expect.any(String), secret: expect.any(String), - apiKey: { - id: expect.any(String), - name: 'API Key', - permissions: [Permission.ApiKeyRead], - createdAt: expect.any(String), - updatedAt: expect.any(String), - }, + // TODO: remove in v4 + apiKey: expect.any(Object), }); expect(status).toBe(201); }); @@ -72,14 +72,14 @@ describe('/api-keys', () => { .send({ name: 'API Key', permissions: [Permission.All] }) .set('Authorization', `Bearer ${admin.accessToken}`); expect(body).toEqual({ - apiKey: { - id: expect.any(String), - name: 'API Key', - permissions: [Permission.All], - createdAt: expect.any(String), - updatedAt: expect.any(String), - }, + id: expect.any(String), + name: 'API Key', + permissions: [Permission.All], + createdAt: expect.any(String), + updatedAt: expect.any(String), secret: expect.any(String), + // TODO: remove in v4 + apiKey: expect.any(Object), }); expect(status).toEqual(201); }); @@ -93,23 +93,30 @@ describe('/api-keys', () => { }); it('should return a list of api keys', async () => { - const [{ apiKey: apiKey1 }, { apiKey: apiKey2 }, { apiKey: apiKey3 }] = await Promise.all([ + const [apiKey1, apiKey2, apiKey3] = await Promise.all([ create(admin.accessToken, [Permission.All]), create(admin.accessToken, [Permission.All]), create(admin.accessToken, [Permission.All]), ]); + const { status, body } = await request(app).get('/api-keys').set('Authorization', `Bearer ${admin.accessToken}`); expect(body).toHaveLength(3); - expect(body).toEqual(expect.arrayContaining([apiKey1, apiKey2, apiKey3])); + expect(body).toEqual( + expect.arrayContaining([ + expect.objectContaining({ id: apiKey1.id }), + expect.objectContaining({ id: apiKey2.id }), + expect.objectContaining({ id: apiKey3.id }), + ]), + ); expect(status).toEqual(200); }); }); describe('GET /api-keys/:id', () => { it('should get api key details', async () => { - const { apiKey } = await create(user.accessToken, [Permission.All]); + const { id } = await create(user.accessToken, [Permission.All]); const { status, body } = await request(app) - .get(`/api-keys/${apiKey.id}`) + .get(`/api-keys/${id}`) .set('Authorization', `Bearer ${user.accessToken}`); expect(status).toBe(200); expect(body).toEqual({ @@ -124,9 +131,9 @@ describe('/api-keys', () => { describe('PUT /api-keys/:id', () => { it('should update api key details', async () => { - const { apiKey } = await create(user.accessToken, [Permission.All]); + const { id } = await create(user.accessToken, [Permission.All]); const { status, body } = await request(app) - .put(`/api-keys/${apiKey.id}`) + .put(`/api-keys/${id}`) .send({ name: 'new name', permissions: [Permission.ActivityCreate, Permission.ActivityRead, Permission.ActivityUpdate], @@ -145,9 +152,9 @@ describe('/api-keys', () => { describe('DELETE /api-keys/:id', () => { it('should delete an api key', async () => { - const { apiKey } = await create(user.accessToken, [Permission.All]); + const { id } = await create(user.accessToken, [Permission.All]); const { status } = await request(app) - .delete(`/api-keys/${apiKey.id}`) + .delete(`/api-keys/${id}`) .set('Authorization', `Bearer ${user.accessToken}`); expect(status).toBe(204); }); diff --git a/e2e/src/specs/server/cli/login.e2e-spec.ts b/e2e/src/specs/server/cli/login.e2e-spec.ts index caf5550b6e3cf7..98a28be448c858 100644 --- a/e2e/src/specs/server/cli/login.e2e-spec.ts +++ b/e2e/src/specs/server/cli/login.e2e-spec.ts @@ -30,8 +30,8 @@ describe(`immich login`, () => { it('should login and save auth.yml with 600', async () => { const admin = await utils.adminSetup(); - const key = await utils.createApiKey(admin.accessToken, [Permission.All]); - const { stdout, stderr, exitCode } = await immichCli(['login', app, key.secret]); + const apiKey = await utils.createApiKey(admin.accessToken, [Permission.All]); + const { stdout, stderr, exitCode } = await immichCli(['login', app, apiKey.secret]); expect(stdout.split('\n')).toEqual([ 'Logging in to http://127.0.0.1:2285/api', 'Logged in as admin@immich.cloud', @@ -47,8 +47,8 @@ describe(`immich login`, () => { it('should login without /api in the url', async () => { const admin = await utils.adminSetup(); - const key = await utils.createApiKey(admin.accessToken, [Permission.All]); - const { stdout, stderr, exitCode } = await immichCli(['login', app.replaceAll('/api', ''), key.secret]); + const apiKey = await utils.createApiKey(admin.accessToken, [Permission.All]); + const { stdout, stderr, exitCode } = await immichCli(['login', app.replaceAll('/api', ''), apiKey.secret]); expect(stdout.split('\n')).toEqual([ 'Logging in to http://127.0.0.1:2285', 'Discovered API at http://127.0.0.1:2285/api', diff --git a/e2e/src/utils.ts b/e2e/src/utils.ts index 3124dd0609f77c..6490806bff5deb 100644 --- a/e2e/src/utils.ts +++ b/e2e/src/utils.ts @@ -675,9 +675,9 @@ export const utils = { }, cliLogin: async (accessToken: string) => { - const key = await utils.createApiKey(accessToken, [Permission.All]); - await immichCli(['login', app, key.secret]); - return key.secret; + const { secret } = await utils.createApiKey(accessToken, [Permission.All]); + await immichCli(['login', app, secret]); + return secret; }, scan: async (accessToken: string, id: string) => { diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 60623a4a8d8301..8b6727168ca077 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -16979,16 +16979,63 @@ "ApiKeyCreateResponseDto": { "properties": { "apiKey": { - "$ref": "#/components/schemas/ApiKeyResponseDto" + "$ref": "#/components/schemas/ApiKeyResponseDto", + "x-immich-history": [ + { + "version": "v1", + "state": "Added" + }, + { + "version": "v3.2.0", + "state": "Deprecated" + } + ], + "x-immich-state": "Deprecated" + }, + "createdAt": { + "description": "Creation date", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "id": { + "description": "API key ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "name": { + "description": "API key name", + "type": "string" + }, + "permissions": { + "description": "List of permissions", + "items": { + "$ref": "#/components/schemas/Permission" + }, + "type": "array" }, "secret": { "description": "API key secret (only shown once)", "type": "string" + }, + "updatedAt": { + "description": "Last update date", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" } }, "required": [ "apiKey", - "secret" + "createdAt", + "id", + "name", + "permissions", + "secret", + "updatedAt" ], "type": "object" }, diff --git a/packages/sdk/src/fetch-client.ts b/packages/sdk/src/fetch-client.ts index 9a1f93cb302366..7e0fb963f79fc9 100644 --- a/packages/sdk/src/fetch-client.ts +++ b/packages/sdk/src/fetch-client.ts @@ -631,8 +631,18 @@ export type ApiKeyCreateDto = { }; export type ApiKeyCreateResponseDto = { apiKey: ApiKeyResponseDto; + /** Creation date */ + createdAt: string; + /** API key ID */ + id: string; + /** API key name */ + name: string; + /** List of permissions */ + permissions: Permission[]; /** API key secret (only shown once) */ secret: string; + /** Last update date */ + updatedAt: string; }; export type ApiKeyUpdateDto = { /** API key name */ diff --git a/server/src/dtos/api-key.dto.ts b/server/src/dtos/api-key.dto.ts index 470808e430cfe2..8c933e06bbe6ed 100644 --- a/server/src/dtos/api-key.dto.ts +++ b/server/src/dtos/api-key.dto.ts @@ -1,4 +1,5 @@ import { createZodDto } from 'nestjs-zod'; +import { HistoryBuilder } from 'src/decorators'; import { Permission } from 'src/enum'; import { isoDatetimeToDate } from 'src/validation'; import z from 'zod'; @@ -31,8 +32,9 @@ const ApiKeyResponseSchema = z const ApiKeyCreateResponseSchema = z .object({ + ...ApiKeyResponseSchema.shape, secret: z.string().describe('API key secret (only shown once)'), - apiKey: ApiKeyResponseSchema, + apiKey: ApiKeyResponseSchema.meta({ ...new HistoryBuilder().added('v1').deprecated('v3.2.0').getExtensions() }), }) .meta({ id: 'ApiKeyCreateResponseDto' }); diff --git a/server/src/services/api-key.service.ts b/server/src/services/api-key.service.ts index 2b9e1a814c069c..d65ebc4d9621a2 100644 --- a/server/src/services/api-key.service.ts +++ b/server/src/services/api-key.service.ts @@ -23,8 +23,9 @@ export class ApiKeyService extends BaseService { userId: auth.user.id, permissions: dto.permissions, }); + const apiKey = this.map(entity); - return { secret: token, apiKey: this.map(entity) }; + return { ...apiKey, secret: token, apiKey }; } async update(auth: AuthDto, id: string, dto: ApiKeyUpdateDto): Promise { @@ -58,10 +59,10 @@ export class ApiKeyService extends BaseService { const token = this.cryptoRepository.randomBytesAsText(32); const hashed = this.cryptoRepository.hashSha256(token); - const newKey = await this.apiKeyRepository.update(auth.user.id, id, { key: hashed }); + const apiKey = this.map(newKey); - return { secret: token, apiKey: this.map(newKey) }; + return { ...apiKey, secret: token, apiKey }; } async delete(auth: AuthDto, id: string): Promise { diff --git a/web/src/lib/services/api-key.service.ts b/web/src/lib/services/api-key.service.ts index e5346627ff130e..5dc990af9699f4 100644 --- a/web/src/lib/services/api-key.service.ts +++ b/web/src/lib/services/api-key.service.ts @@ -64,7 +64,7 @@ export const handleCreateApiKey = async (dto: ApiKeyCreateDto) => { } const response = await createApiKey({ apiKeyCreateDto: dto }); - eventManager.emit('ApiKeyCreate', response.apiKey); + eventManager.emit('ApiKeyCreate', response); return response; } catch (error) { @@ -105,7 +105,7 @@ export const handleRotateApiKey = async (apiKey: ApiKeyResponseDto) => { try { const response = await rotateApiKey({ id: apiKey.id }); - eventManager.emit('ApiKeyUpdate', response.apiKey); + eventManager.emit('ApiKeyUpdate', response); await modalManager.show(ApiKeySecretModal, { secret: response.secret }); } catch (error) { handleError(error, $t('errors.something_went_wrong')); From e529557160d8d54bc6abad9a0f4d66ae35a1cd8e Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Thu, 20 Aug 2026 11:46:17 -0400 Subject: [PATCH 2/6] feat: new config endpoints (#30881) * refactor: config endpoints * chore: pr feedback --- e2e/src/specs/server/api/jobs.e2e-spec.ts | 2 +- e2e/src/specs/server/api/oauth.e2e-spec.ts | 6 +- e2e/src/utils.ts | 2 +- open-api/immich-openapi-specs.json | 16577 ++++++++-------- packages/sdk/src/fetch-client.ts | 1445 +- server/src/config.ts | 449 - server/src/constants.ts | 3 + .../controllers/config-admin.controller.ts | 46 + .../controllers/config-public.controller.ts | 35 + .../src/controllers/config-user.controller.ts | 35 + .../src/controllers/config.controller.spec.ts | 116 + server/src/controllers/index.ts | 6 + .../notification-admin.controller.ts | 2 +- server/src/controllers/server.controller.ts | 12 +- .../system-config.controller.spec.ts | 2 +- .../controllers/system-config.controller.ts | 34 +- server/src/dtos/config.dto.spec.ts | 68 + server/src/dtos/config.dto.ts | 773 + server/src/dtos/model-config.dto.ts | 57 - server/src/dtos/system-config.dto.ts | 444 - server/src/enum.ts | 21 + server/src/repositories/event.repository.ts | 2 +- .../machine-learning.repository.ts | 5 +- .../repositories/server-info.repository.ts | 2 +- server/src/services/base.service.ts | 2 +- .../services/database-backup.service.spec.ts | 2 +- server/src/services/hls.service.ts | 4 +- server/src/services/library.service.spec.ts | 2 +- server/src/services/media.service.spec.ts | 2 +- server/src/services/media.service.ts | 11 +- server/src/services/metadata.service.spec.ts | 2 +- .../notification-admin.service.spec.ts | 2 +- .../services/notification-admin.service.ts | 2 +- .../src/services/notification.service.spec.ts | 5 +- server/src/services/notification.service.ts | 2 +- server/src/services/queue.service.spec.ts | 2 +- server/src/services/queue.service.ts | 2 +- .../src/services/smart-info.service.spec.ts | 2 +- server/src/services/smart-info.service.ts | 2 +- .../services/storage-template.service.spec.ts | 2 +- .../src/services/storage-template.service.ts | 4 +- .../services/system-config.service.spec.ts | 44 +- server/src/services/system-config.service.ts | 41 +- server/src/services/version.service.spec.ts | 5 +- server/src/services/version.service.ts | 12 +- server/src/types.ts | 10 +- server/src/utils/config.ts | 5 +- server/src/utils/media.ts | 14 +- server/src/utils/misc.ts | 2 +- server/src/utils/profile-image.ts | 2 +- server/test/fixtures/system-config.stub.ts | 2 +- server/test/medium.factory.ts | 2 +- server/test/utils.ts | 6 +- .../settings/SystemConfigButtonRow.svelte | 6 +- web/src/lib/managers/event-manager.svelte.ts | 4 +- .../managers/system-config-manager.svelte.ts | 8 +- web/src/lib/services/system-config.service.ts | 12 +- .../admin/system-settings/JobSettings.svelte | 4 +- .../NotificationSettings.svelte | 2 +- .../system-settings/TemplateSettings.svelte | 6 +- 60 files changed, 10800 insertions(+), 9581 deletions(-) delete mode 100644 server/src/config.ts create mode 100644 server/src/controllers/config-admin.controller.ts create mode 100644 server/src/controllers/config-public.controller.ts create mode 100644 server/src/controllers/config-user.controller.ts create mode 100644 server/src/controllers/config.controller.spec.ts create mode 100644 server/src/dtos/config.dto.spec.ts create mode 100644 server/src/dtos/config.dto.ts delete mode 100644 server/src/dtos/model-config.dto.ts delete mode 100644 server/src/dtos/system-config.dto.ts diff --git a/e2e/src/specs/server/api/jobs.e2e-spec.ts b/e2e/src/specs/server/api/jobs.e2e-spec.ts index f9d8b75c46e203..b4228eeb91b061 100644 --- a/e2e/src/specs/server/api/jobs.e2e-spec.ts +++ b/e2e/src/specs/server/api/jobs.e2e-spec.ts @@ -45,7 +45,7 @@ describe('/jobs', () => { config.machineLearning.enabled = false; config.metadata.faces.import = false; config.machineLearning.clip.enabled = false; - await updateConfig({ systemConfigDto: config }, { headers: asBearerAuth(admin.accessToken) }); + await updateConfig({ adminConfigDto: config }, { headers: asBearerAuth(admin.accessToken) }); }); it('should queue metadata extraction for missing assets', async () => { diff --git a/e2e/src/specs/server/api/oauth.e2e-spec.ts b/e2e/src/specs/server/api/oauth.e2e-spec.ts index 3b85e9e4c8f228..10d3343a789a5a 100644 --- a/e2e/src/specs/server/api/oauth.e2e-spec.ts +++ b/e2e/src/specs/server/api/oauth.e2e-spec.ts @@ -1,7 +1,7 @@ import { OAuthClient, OAuthUser, generateLogoutToken } from '@immich/e2e-auth-server'; import { + AdminConfigOAuthDto, LoginResponseDto, - SystemConfigOAuthDto, getConfigDefaults, getMyUser, getSessions, @@ -70,7 +70,7 @@ const loginWithOAuth = async (sub: OAuthUser | string, redirectUri?: string) => return { url: redirectUrl, state, codeVerifier }; }; -const setupOAuth = async (token: string, dto: Partial) => { +const setupOAuth = async (token: string, dto: Partial) => { const options = { headers: asBearerAuth(token) }; const defaults = await getConfigDefaults(options); const merged = { @@ -80,7 +80,7 @@ const setupOAuth = async (token: string, dto: Partial) => allowInsecureRequests: true, ...dto, }; - await updateConfig({ systemConfigDto: { ...defaults, oauth: merged } }, options); + await updateConfig({ adminConfigDto: { ...defaults, oauth: merged } }, options); }; describe(`/oauth`, () => { diff --git a/e2e/src/utils.ts b/e2e/src/utils.ts index 6490806bff5deb..e4216ae57b54ec 100644 --- a/e2e/src/utils.ts +++ b/e2e/src/utils.ts @@ -647,7 +647,7 @@ export const utils = { resetAdminConfig: async (accessToken: string) => { const defaultConfig = await getConfigDefaults({ headers: asBearerAuth(accessToken) }); - await updateConfig({ systemConfigDto: defaultConfig }, { headers: asBearerAuth(accessToken) }); + await updateConfig({ adminConfigDto: defaultConfig }, { headers: asBearerAuth(accessToken) }); }, isQueueEmpty: async (accessToken: string, queue: keyof QueuesResponseLegacyDto) => { diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 8b6727168ca077..4c6358472820d4 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -333,6 +333,155 @@ "x-immich-state": "Stable" } }, + "/admin/config": { + "get": { + "description": "Retrieve admin configuration.", + "operationId": "getAdminConfig", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AdminConfigDto" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "summary": "Get the admin configuration", + "tags": [ + "Config (admin)" + ], + "x-immich-admin-only": true, + "x-immich-history": [ + { + "version": "v3.2.0", + "state": "Added" + }, + { + "version": "v3.2.0", + "state": "Alpha" + } + ], + "x-immich-permission": "adminConfig.read", + "x-immich-state": "Alpha" + }, + "put": { + "description": "Update the system configuration with a new system configuration.", + "operationId": "updateAdminConfig", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AdminConfigDto" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AdminConfigDto" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "summary": "Update the system configuration", + "tags": [ + "Config (admin)" + ], + "x-immich-admin-only": true, + "x-immich-history": [ + { + "version": "v3.2.0", + "state": "Added" + }, + { + "version": "v3.2.0", + "state": "Alpha" + } + ], + "x-immich-permission": "adminConfig.update", + "x-immich-state": "Alpha" + } + }, + "/admin/config/defaults": { + "get": { + "description": "Retrieve the default value of every system configuration property.", + "operationId": "getAdminConfigDefaults", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AdminConfigDto" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "summary": "Get the system configuration defaults", + "tags": [ + "Config (admin)" + ], + "x-immich-admin-only": true, + "x-immich-history": [ + { + "version": "v3.2.0", + "state": "Added" + }, + { + "version": "v3.2.0", + "state": "Alpha" + } + ], + "x-immich-permission": "adminConfig.read", + "x-immich-state": "Alpha" + } + }, "/admin/database-backups": { "delete": { "description": "Delete a backup by its filename", @@ -1169,7 +1318,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SystemConfigSmtpDto" + "$ref": "#/components/schemas/AdminConfigSmtpDto" } } }, @@ -5712,6 +5861,98 @@ "x-immich-state": "Stable" } }, + "/config": { + "get": { + "description": "Retrieve the system configuration properties that are visible to logged in users.", + "operationId": "getUserConfig", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserConfigDto" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "summary": "Get the configuration with user visibility", + "tags": [ + "Config (user)" + ], + "x-immich-history": [ + { + "version": "v3.2.0", + "state": "Added" + }, + { + "version": "v3.2.0", + "state": "Alpha" + } + ], + "x-immich-permission": "userConfig.read", + "x-immich-state": "Alpha" + } + }, + "/config/defaults": { + "get": { + "description": "Retrieve the default value of the configuration properties that are visible to logged in users.", + "operationId": "getUserConfigDefaults", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserConfigDto" + } + } + }, + "description": "" + } + }, + "security": [ + { + "bearer": [] + }, + { + "cookie": [] + }, + { + "api_key": [] + } + ], + "summary": "Get the default configuration with user visibility", + "tags": [ + "Config (user)" + ], + "x-immich-history": [ + { + "version": "v3.2.0", + "state": "Added" + }, + { + "version": "v3.2.0", + "state": "Alpha" + } + ], + "x-immich-permission": "userConfig.read", + "x-immich-state": "Alpha" + } + }, "/download/archive": { "post": { "description": "Download a ZIP archive containing the specified assets. The assets must have been previously requested via the \"getDownloadInfo\" endpoint.", @@ -9807,6 +10048,74 @@ "x-immich-permission": "plugin.read" } }, + "/public/config": { + "get": { + "description": "Retrieve the system configuration properties that are visible to everyone.", + "operationId": "getPublicConfig", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicConfigDto" + } + } + }, + "description": "" + } + }, + "summary": "Get the public configuration", + "tags": [ + "Config (public)" + ], + "x-immich-history": [ + { + "version": "v3.2.0", + "state": "Added" + }, + { + "version": "v3.2.0", + "state": "Alpha" + } + ], + "x-immich-state": "Alpha" + } + }, + "/public/config/defaults": { + "get": { + "description": "Retrieve the default value of the configuration properties that are visible to everyone.", + "operationId": "getPublicConfigDefaults", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicConfigDto" + } + } + }, + "description": "" + } + }, + "summary": "Get the public configuration defaults", + "tags": [ + "Config (public)" + ], + "x-immich-history": [ + { + "version": "v3.2.0", + "state": "Added" + }, + { + "version": "v3.2.0", + "state": "Alpha" + } + ], + "x-immich-state": "Alpha" + } + }, "/queues": { "get": { "description": "Retrieves a list of queues.", @@ -11243,6 +11552,7 @@ }, "/server/config": { "get": { + "deprecated": true, "description": "Retrieve the current server configuration.", "operationId": "getServerConfig", "parameters": [], @@ -11260,7 +11570,8 @@ }, "summary": "Get config", "tags": [ - "Server" + "Server", + "Deprecated" ], "x-immich-history": [ { @@ -11274,13 +11585,19 @@ { "version": "v2", "state": "Stable" + }, + { + "version": "v3.2.0", + "state": "Deprecated", + "replacementId": "getPublicConfig" } ], - "x-immich-state": "Stable" + "x-immich-state": "Deprecated" } }, "/server/features": { "get": { + "deprecated": true, "description": "Retrieve available features supported by this server.", "operationId": "getServerFeatures", "parameters": [], @@ -11298,7 +11615,8 @@ }, "summary": "Get features", "tags": [ - "Server" + "Server", + "Deprecated" ], "x-immich-history": [ { @@ -11312,9 +11630,14 @@ { "version": "v2", "state": "Stable" + }, + { + "version": "v3.2.0", + "state": "Deprecated", + "replacementId": "getPublicConfig" } ], - "x-immich-state": "Stable" + "x-immich-state": "Deprecated" } }, "/server/license": { @@ -13357,6 +13680,7 @@ }, "/system-config": { "get": { + "deprecated": true, "description": "Retrieve the current system configuration.", "operationId": "getConfig", "parameters": [], @@ -13365,7 +13689,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SystemConfigDto" + "$ref": "#/components/schemas/AdminConfigDto" } } }, @@ -13385,7 +13709,8 @@ ], "summary": "Get system configuration", "tags": [ - "System config" + "System config", + "Deprecated" ], "x-immich-admin-only": true, "x-immich-history": [ @@ -13400,12 +13725,18 @@ { "version": "v2", "state": "Stable" + }, + { + "version": "v3.2.0", + "state": "Deprecated", + "replacementId": "getAdminConfig" } ], "x-immich-permission": "systemConfig.read", - "x-immich-state": "Stable" + "x-immich-state": "Deprecated" }, "put": { + "deprecated": true, "description": "Update the system configuration with a new system configuration.", "operationId": "updateConfig", "parameters": [], @@ -13413,7 +13744,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SystemConfigDto" + "$ref": "#/components/schemas/AdminConfigDto" } } }, @@ -13424,7 +13755,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SystemConfigDto" + "$ref": "#/components/schemas/AdminConfigDto" } } }, @@ -13444,7 +13775,8 @@ ], "summary": "Update system configuration", "tags": [ - "System config" + "System config", + "Deprecated" ], "x-immich-admin-only": true, "x-immich-history": [ @@ -13459,14 +13791,20 @@ { "version": "v2", "state": "Stable" + }, + { + "version": "v3.2.0", + "state": "Deprecated", + "replacementId": "updateAdminConfig" } ], "x-immich-permission": "systemConfig.update", - "x-immich-state": "Stable" + "x-immich-state": "Deprecated" } }, "/system-config/defaults": { "get": { + "deprecated": true, "description": "Retrieve the default values for the system configuration.", "operationId": "getConfigDefaults", "parameters": [], @@ -13475,7 +13813,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SystemConfigDto" + "$ref": "#/components/schemas/AdminConfigDto" } } }, @@ -13495,7 +13833,8 @@ ], "summary": "Get system configuration defaults", "tags": [ - "System config" + "System config", + "Deprecated" ], "x-immich-admin-only": true, "x-immich-history": [ @@ -13510,10 +13849,15 @@ { "version": "v2", "state": "Stable" + }, + { + "version": "v3.2.0", + "state": "Deprecated", + "replacementId": "getAdminConfigDefaults" } ], "x-immich-permission": "systemConfig.read", - "x-immich-state": "Stable" + "x-immich-state": "Deprecated" } }, "/system-config/storage-template-options": { @@ -16420,6 +16764,18 @@ "name": "Authentication (admin)", "description": "Administrative endpoints related to authentication." }, + { + "name": "Config (user)", + "description": "The system configuration properties that are visible to logged in users." + }, + { + "name": "Config (admin)", + "description": "Endpoints to view and modify the full system configuration." + }, + { + "name": "Config (public)", + "description": "The system configuration properties that are visible to everyone." + }, { "name": "Database Backups (admin)", "description": "Manage backups of the Immich database." @@ -16683,2193 +17039,2148 @@ ], "type": "object" }, - "AdminOnboardingUpdateDto": { + "AdminConfigBackupsDto": { "properties": { - "isOnboarded": { - "description": "Is admin onboarded", - "type": "boolean" + "database": { + "$ref": "#/components/schemas/AdminConfigDatabaseBackupDto" } }, "required": [ - "isOnboarded" + "database" ], "type": "object" }, - "AlbumResponseDto": { + "AdminConfigClipDto": { "properties": { - "albumName": { - "description": "Album name", - "type": "string" + "enabled": { + "description": "Whether the task is enabled", + "type": "boolean" }, - "albumThumbnailAssetId": { - "description": "Thumbnail asset ID", - "format": "uuid", - "nullable": true, - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "modelName": { + "description": "Name of the model to use", + "type": "string" + } + }, + "required": [ + "enabled", + "modelName" + ], + "type": "object" + }, + "AdminConfigDatabaseBackupDto": { + "properties": { + "cronExpression": { + "description": "Cron expression", "type": "string" }, - "albumUsers": { - "description": "First entry is always the album owner. Second entry is the auth user, if it differs from the owner. The rest are ordered alphabetically.", - "items": { - "$ref": "#/components/schemas/AlbumUserResponseDto" - }, - "minItems": 1, - "type": "array" + "enabled": { + "description": "Enabled", + "type": "boolean" }, - "assetCount": { - "description": "Number of assets", + "keepLastAmount": { + "description": "Keep last amount", "maximum": 9007199254740991, - "minimum": 0, + "minimum": 1, "type": "integer" + } + }, + "required": [ + "cronExpression", + "enabled", + "keepLastAmount" + ], + "type": "object" + }, + "AdminConfigDto": { + "description": "Configuration properties that are visible to the admin", + "properties": { + "backup": { + "$ref": "#/components/schemas/AdminConfigBackupsDto" }, - "contributorCounts": { - "items": { - "$ref": "#/components/schemas/ContributorCountResponseDto" - }, - "type": "array" + "ffmpeg": { + "$ref": "#/components/schemas/AdminConfigFFmpegDto" }, - "createdAt": { - "description": "Creation date", - "format": "date-time", - "type": "string" + "image": { + "$ref": "#/components/schemas/AdminConfigImageDto" }, - "description": { - "description": "Album description", - "type": "string", - "x-immich-history": [ - { - "version": "v1", - "state": "Added" - }, - { - "version": "v3", - "state": "Updated", - "description": "An empty string is returned instead of null for backwards compatibility; null will be returned in v4." - } - ] + "integrityChecks": { + "$ref": "#/components/schemas/AdminConfigIntegrityChecksDto" }, - "endDate": { - "description": "End date (latest asset)", - "format": "date-time", - "type": "string" + "job": { + "$ref": "#/components/schemas/AdminConfigJobDto" }, - "hasSharedLink": { - "description": "Has shared link", - "type": "boolean" + "library": { + "$ref": "#/components/schemas/AdminConfigLibraryDto" }, - "id": { - "description": "Album ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" + "logging": { + "$ref": "#/components/schemas/AdminConfigLoggingDto" }, - "isActivityEnabled": { - "description": "Activity feed enabled", - "type": "boolean" + "machineLearning": { + "$ref": "#/components/schemas/AdminConfigMachineLearningDto" }, - "lastModifiedAssetTimestamp": { - "description": "Last modified asset timestamp", - "format": "date-time", - "type": "string" + "map": { + "$ref": "#/components/schemas/AdminConfigMapDto" }, - "order": { - "$ref": "#/components/schemas/AssetOrder" + "metadata": { + "$ref": "#/components/schemas/AdminConfigMetadataDto" }, - "shared": { - "description": "Is shared album", - "type": "boolean" + "newVersionCheck": { + "$ref": "#/components/schemas/AdminConfigNewVersionCheckDto" }, - "startDate": { - "description": "Start date (earliest asset)", - "format": "date-time", - "type": "string" + "nightlyTasks": { + "$ref": "#/components/schemas/AdminConfigNightlyTasksDto" }, - "updatedAt": { - "description": "Last update date", - "format": "date-time", - "type": "string" + "notifications": { + "$ref": "#/components/schemas/AdminConfigNotificationsDto" + }, + "oauth": { + "$ref": "#/components/schemas/AdminConfigOAuthDto" + }, + "passwordLogin": { + "$ref": "#/components/schemas/AdminConfigPasswordLoginDto" + }, + "reverseGeocoding": { + "$ref": "#/components/schemas/AdminConfigReverseGeocodingDto" + }, + "server": { + "$ref": "#/components/schemas/AdminConfigServerDto" + }, + "storageTemplate": { + "$ref": "#/components/schemas/AdminConfigStorageTemplateDto" + }, + "templates": { + "$ref": "#/components/schemas/AdminConfigTemplatesDto" + }, + "theme": { + "$ref": "#/components/schemas/AdminConfigThemeDto" + }, + "trash": { + "$ref": "#/components/schemas/AdminConfigTrashDto" + }, + "user": { + "$ref": "#/components/schemas/AdminConfigUserDto" } }, "required": [ - "albumName", - "albumThumbnailAssetId", - "albumUsers", - "assetCount", - "createdAt", - "description", - "hasSharedLink", - "id", - "isActivityEnabled", - "shared", - "updatedAt" + "backup", + "ffmpeg", + "image", + "integrityChecks", + "job", + "library", + "logging", + "machineLearning", + "map", + "metadata", + "newVersionCheck", + "nightlyTasks", + "notifications", + "oauth", + "passwordLogin", + "reverseGeocoding", + "server", + "storageTemplate", + "templates", + "theme", + "trash", + "user" ], "type": "object" }, - "AlbumStatisticsResponseDto": { + "AdminConfigDuplicateDetectionDto": { "properties": { - "notShared": { - "description": "Number of non-shared albums", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "owned": { - "description": "Number of owned albums", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" + "enabled": { + "description": "Whether the task is enabled", + "type": "boolean" }, - "shared": { - "description": "Number of shared albums", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" + "maxDistance": { + "description": "Maximum distance threshold for duplicate detection", + "format": "double", + "maximum": 0.1, + "minimum": 0.001, + "type": "number" } }, "required": [ - "notShared", - "owned", - "shared" + "enabled", + "maxDistance" ], "type": "object" }, - "AlbumUserAddDto": { + "AdminConfigFFmpegDto": { "properties": { - "role": { - "$ref": "#/components/schemas/AlbumUserRole", - "default": "editor", - "description": "Album user role" + "accel": { + "$ref": "#/components/schemas/TranscodeHWAccel" }, - "userId": { - "description": "User ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "accelDecode": { + "description": "Accelerated decode", + "type": "boolean" + }, + "acceptedAudioCodecs": { + "description": "Accepted audio codecs", + "items": { + "$ref": "#/components/schemas/AudioCodec" + }, + "type": "array" + }, + "acceptedContainers": { + "description": "Accepted containers", + "items": { + "$ref": "#/components/schemas/VideoContainer" + }, + "type": "array" + }, + "acceptedVideoCodecs": { + "description": "Accepted video codecs", + "items": { + "$ref": "#/components/schemas/VideoCodec" + }, + "type": "array" + }, + "bframes": { + "description": "B-frames", + "maximum": 16, + "minimum": -1, + "type": "integer" + }, + "cqMode": { + "$ref": "#/components/schemas/CQMode" + }, + "crf": { + "description": "CRF", + "maximum": 51, + "minimum": 0, + "type": "integer" + }, + "gopSize": { + "description": "GOP size", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "maxBitrate": { + "description": "Max bitrate", "type": "string" - } - }, - "required": [ - "userId" - ], - "type": "object" - }, - "AlbumUserCreateDto": { - "properties": { - "role": { - "$ref": "#/components/schemas/AlbumUserRole" }, - "userId": { - "description": "User ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "preferredHwDevice": { + "description": "Preferred hardware device", "type": "string" - } - }, - "required": [ - "role", - "userId" - ], - "type": "object" - }, - "AlbumUserResponseDto": { - "properties": { - "role": { - "$ref": "#/components/schemas/AlbumUserRole" }, - "user": { - "$ref": "#/components/schemas/UserResponseDto" + "preset": { + "description": "Preset", + "type": "string" + }, + "realtime": { + "$ref": "#/components/schemas/AdminConfigFFmpegRealtimeDto" + }, + "refs": { + "description": "References", + "maximum": 6, + "minimum": 0, + "type": "integer" + }, + "targetAudioCodec": { + "$ref": "#/components/schemas/AudioCodec" + }, + "targetResolution": { + "description": "Target resolution", + "type": "string" + }, + "targetVideoCodec": { + "$ref": "#/components/schemas/VideoCodec" + }, + "temporalAQ": { + "description": "Temporal AQ", + "type": "boolean" + }, + "threads": { + "description": "Threads", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "tonemap": { + "$ref": "#/components/schemas/ToneMapping" + }, + "transcode": { + "$ref": "#/components/schemas/TranscodePolicy" + }, + "twoPass": { + "description": "Two pass", + "type": "boolean" } }, "required": [ - "role", - "user" + "accel", + "accelDecode", + "acceptedAudioCodecs", + "acceptedContainers", + "acceptedVideoCodecs", + "bframes", + "cqMode", + "crf", + "gopSize", + "maxBitrate", + "preferredHwDevice", + "preset", + "realtime", + "refs", + "targetAudioCodec", + "targetResolution", + "targetVideoCodec", + "temporalAQ", + "threads", + "tonemap", + "transcode", + "twoPass" ], "type": "object" }, - "AlbumUserRole": { - "description": "Album user role", - "enum": [ - "editor", - "owner", - "viewer" - ], - "type": "string" - }, - "AlbumsAddAssetsDto": { + "AdminConfigFFmpegRealtimeDto": { "properties": { - "albumIds": { - "description": "Album IDs", + "enabled": { + "description": "Enable real-time HLS transcoding (alpha)", + "type": "boolean" + }, + "resolutions": { + "description": "Resolutions to use for real-time HLS transcoding", "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" + "$ref": "#/components/schemas/HlsVideoResolution" }, "type": "array" }, - "assetIds": { - "description": "Asset IDs", + "videoCodecs": { + "description": "Video codecs to use for real-time HLS transcoding", "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" + "$ref": "#/components/schemas/VideoCodec" }, "type": "array" } }, "required": [ - "albumIds", - "assetIds" + "enabled", + "resolutions", + "videoCodecs" ], "type": "object" }, - "AlbumsAddAssetsResponseDto": { + "AdminConfigFacesDto": { "properties": { - "error": { - "$ref": "#/components/schemas/BulkIdErrorReason" - }, - "success": { - "description": "Operation success", + "import": { + "description": "Import", "type": "boolean" } }, "required": [ - "success" + "import" ], "type": "object" }, - "AlbumsResponse": { + "AdminConfigFacialRecognitionDto": { "properties": { - "defaultAssetOrder": { - "$ref": "#/components/schemas/AssetOrder" + "enabled": { + "description": "Whether the task is enabled", + "type": "boolean" + }, + "maxDistance": { + "description": "Maximum distance threshold for face recognition", + "format": "double", + "maximum": 2, + "minimum": 0.1, + "type": "number" + }, + "minFaces": { + "description": "Minimum number of faces required for recognition", + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + }, + "minScore": { + "description": "Minimum confidence score for face detection", + "format": "double", + "maximum": 1, + "minimum": 0.1, + "type": "number" + }, + "modelName": { + "description": "Name of the model to use", + "type": "string" } }, "required": [ - "defaultAssetOrder" + "enabled", + "maxDistance", + "minFaces", + "minScore", + "modelName" ], "type": "object" }, - "AlbumsUpdate": { - "description": "Album preferences", + "AdminConfigGeneratedFullsizeImageDto": { "properties": { - "defaultAssetOrder": { - "$ref": "#/components/schemas/AssetOrder" + "enabled": { + "description": "Enabled", + "type": "boolean" + }, + "format": { + "$ref": "#/components/schemas/ImageFormat" + }, + "progressive": { + "description": "Progressive", + "type": "boolean" + }, + "quality": { + "description": "Quality", + "maximum": 100, + "minimum": 1, + "type": "integer" } }, + "required": [ + "enabled", + "format", + "quality" + ], "type": "object" }, - "ApiKeyCreateDto": { + "AdminConfigGeneratedImageDto": { "properties": { - "name": { - "description": "API key name", - "type": "string" + "format": { + "$ref": "#/components/schemas/ImageFormat" }, - "permissions": { - "description": "List of permissions", - "items": { - "$ref": "#/components/schemas/Permission" - }, - "minItems": 1, - "type": "array" + "progressive": { + "description": "Progressive", + "type": "boolean" + }, + "quality": { + "description": "Quality", + "maximum": 100, + "minimum": 1, + "type": "integer" + }, + "size": { + "description": "Size", + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" } }, "required": [ - "permissions" + "format", + "quality", + "size" ], "type": "object" }, - "ApiKeyCreateResponseDto": { + "AdminConfigImageDto": { "properties": { - "apiKey": { - "$ref": "#/components/schemas/ApiKeyResponseDto", - "x-immich-history": [ - { - "version": "v1", - "state": "Added" - }, - { - "version": "v3.2.0", - "state": "Deprecated" - } - ], - "x-immich-state": "Deprecated" - }, - "createdAt": { - "description": "Creation date", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" - }, - "id": { - "description": "API key ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" + "colorspace": { + "$ref": "#/components/schemas/Colorspace" }, - "name": { - "description": "API key name", - "type": "string" + "extractEmbedded": { + "description": "Extract embedded", + "type": "boolean" }, - "permissions": { - "description": "List of permissions", - "items": { - "$ref": "#/components/schemas/Permission" - }, - "type": "array" + "fullsize": { + "$ref": "#/components/schemas/AdminConfigGeneratedFullsizeImageDto" }, - "secret": { - "description": "API key secret (only shown once)", - "type": "string" + "preview": { + "$ref": "#/components/schemas/AdminConfigGeneratedImageDto" }, - "updatedAt": { - "description": "Last update date", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" + "thumbnail": { + "$ref": "#/components/schemas/AdminConfigGeneratedImageDto" } }, "required": [ - "apiKey", - "createdAt", - "id", - "name", - "permissions", - "secret", - "updatedAt" + "colorspace", + "extractEmbedded", + "fullsize", + "preview", + "thumbnail" ], "type": "object" }, - "ApiKeyResponseDto": { + "AdminConfigIntegrityChecksDto": { + "description": "Integrity checks config", "properties": { - "createdAt": { - "description": "Creation date", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" - }, - "id": { - "description": "API key ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "name": { - "description": "API key name", - "type": "string" + "checksumFiles": { + "$ref": "#/components/schemas/AdminConfigIntegrityChecksumJobDto" }, - "permissions": { - "description": "List of permissions", - "items": { - "$ref": "#/components/schemas/Permission" - }, - "type": "array" + "missingFiles": { + "$ref": "#/components/schemas/AdminConfigIntegrityJobDto" }, - "updatedAt": { - "description": "Last update date", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" + "untrackedFiles": { + "$ref": "#/components/schemas/AdminConfigIntegrityJobDto" } }, "required": [ - "createdAt", - "id", - "name", - "permissions", - "updatedAt" + "checksumFiles", + "missingFiles", + "untrackedFiles" ], "type": "object" }, - "ApiKeyUpdateDto": { + "AdminConfigIntegrityChecksumJobDto": { + "description": "Integrity checksum job config", "properties": { - "name": { - "description": "API key name", + "cronExpression": { + "description": "Cron expression for when the integrity check should run", "type": "string" }, - "permissions": { - "description": "List of permissions", - "items": { - "$ref": "#/components/schemas/Permission" - }, - "minItems": 1, - "type": "array" + "enabled": { + "description": "Enabled", + "type": "boolean" + }, + "percentageLimit": { + "description": "Percentage limit of the integrity checksum job", + "format": "double", + "maximum": 1, + "minimum": 0, + "type": "number" + }, + "timeLimit": { + "description": "How long the integrity checksum job may run for", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" } }, + "required": [ + "cronExpression", + "enabled", + "percentageLimit", + "timeLimit" + ], "type": "object" }, - "AssetBulkDeleteDto": { + "AdminConfigIntegrityJobDto": { + "description": "Integrity job config", "properties": { - "force": { - "description": "Force delete even if in use", - "type": "boolean" + "cronExpression": { + "description": "Cron expression for when the integrity check should run", + "type": "string" }, - "ids": { - "description": "IDs to process", - "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "type": "array" + "enabled": { + "description": "Enabled", + "type": "boolean" } }, "required": [ - "ids" + "cronExpression", + "enabled" ], "type": "object" }, - "AssetBulkUpdateDto": { + "AdminConfigJobDto": { "properties": { - "dateTimeOriginal": { - "description": "Original date and time", - "type": "string" + "backgroundTask": { + "$ref": "#/components/schemas/AdminConfigJobSettingsDto" }, - "dateTimeRelative": { - "description": "Relative time offset in minutes", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "editor": { + "$ref": "#/components/schemas/AdminConfigJobSettingsDto" }, - "description": { - "description": "Asset description", - "type": "string" + "faceDetection": { + "$ref": "#/components/schemas/AdminConfigJobSettingsDto" }, - "duplicateId": { - "description": "Duplicate ID", - "nullable": true, - "type": "string" + "integrityCheck": { + "$ref": "#/components/schemas/AdminConfigJobSettingsDto" }, - "ids": { - "description": "Asset IDs to update", - "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "type": "array" + "library": { + "$ref": "#/components/schemas/AdminConfigJobSettingsDto" }, - "isFavorite": { - "description": "Mark as favorite", - "type": "boolean" + "metadataExtraction": { + "$ref": "#/components/schemas/AdminConfigJobSettingsDto" }, - "latitude": { - "description": "Latitude coordinate", - "maximum": 90, - "minimum": -90, - "type": "number" + "migration": { + "$ref": "#/components/schemas/AdminConfigJobSettingsDto" }, - "longitude": { - "description": "Longitude coordinate", - "maximum": 180, - "minimum": -180, - "type": "number" + "notifications": { + "$ref": "#/components/schemas/AdminConfigJobSettingsDto" }, - "rating": { - "description": "Rating in range [1-5] (starred), -1 (rejected), or null (unrated)", - "maximum": 5, - "minimum": -1, - "nullable": true, - "type": "integer", - "x-immich-history": [ - { - "version": "v1", - "state": "Added" - }, - { - "version": "v2", - "state": "Stable" - }, - { - "version": "v3", - "state": "Updated", - "description": "Using 0 as a rating is no longer valid." - } - ], - "x-immich-state": "Stable" + "ocr": { + "$ref": "#/components/schemas/AdminConfigJobSettingsDto" }, - "timeZone": { - "description": "Time zone (IANA timezone)", - "type": "string" + "search": { + "$ref": "#/components/schemas/AdminConfigJobSettingsDto" }, - "visibility": { - "$ref": "#/components/schemas/AssetVisibility" + "sidecar": { + "$ref": "#/components/schemas/AdminConfigJobSettingsDto" + }, + "smartSearch": { + "$ref": "#/components/schemas/AdminConfigJobSettingsDto" + }, + "thumbnailGeneration": { + "$ref": "#/components/schemas/AdminConfigJobSettingsDto" + }, + "videoConversion": { + "$ref": "#/components/schemas/AdminConfigJobSettingsDto" + }, + "workflow": { + "$ref": "#/components/schemas/AdminConfigJobSettingsDto" } }, "required": [ - "ids" + "backgroundTask", + "editor", + "faceDetection", + "integrityCheck", + "library", + "metadataExtraction", + "migration", + "notifications", + "ocr", + "search", + "sidecar", + "smartSearch", + "thumbnailGeneration", + "videoConversion", + "workflow" ], "type": "object" }, - "AssetBulkUploadCheckDto": { + "AdminConfigJobSettingsDto": { "properties": { - "assets": { - "description": "Assets to check", - "items": { - "$ref": "#/components/schemas/AssetBulkUploadCheckItem" - }, - "type": "array" + "concurrency": { + "description": "Concurrency", + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" } }, "required": [ - "assets" + "concurrency" ], "type": "object" }, - "AssetBulkUploadCheckItem": { + "AdminConfigLibraryDto": { "properties": { - "checksum": { - "description": "Base64 or hex encoded SHA1 hash", - "type": "string" + "scan": { + "$ref": "#/components/schemas/AdminConfigLibraryScanDto" }, - "id": { - "description": "Client-side identifier echoed in the response to match results to inputs (e.g. filename)", - "type": "string" - } - }, - "required": [ - "checksum", - "id" - ], - "type": "object" - }, - "AssetBulkUploadCheckResponseDto": { - "properties": { - "results": { - "description": "Upload check results", - "items": { - "$ref": "#/components/schemas/AssetBulkUploadCheckResult" - }, - "type": "array" + "watch": { + "$ref": "#/components/schemas/AdminConfigLibraryWatchDto" } }, "required": [ - "results" + "scan", + "watch" ], "type": "object" }, - "AssetBulkUploadCheckResult": { + "AdminConfigLibraryScanDto": { "properties": { - "action": { - "$ref": "#/components/schemas/AssetUploadAction" - }, - "assetId": { - "description": "Existing asset ID if duplicate", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "id": { - "description": "Client-side identifier echoed from the request to match results to inputs", + "cronExpression": { + "description": "Cron expression", "type": "string" }, - "isTrashed": { - "description": "Whether existing asset is trashed", + "enabled": { + "description": "Enabled", "type": "boolean" - }, - "reason": { - "$ref": "#/components/schemas/AssetRejectReason" } }, "required": [ - "action", - "id" + "cronExpression", + "enabled" ], "type": "object" }, - "AssetCopyDto": { + "AdminConfigLibraryWatchDto": { "properties": { - "albums": { - "default": true, - "description": "Copy album associations", - "type": "boolean" - }, - "favorite": { - "default": true, - "description": "Copy favorite status", - "type": "boolean" - }, - "sharedLinks": { - "default": true, - "description": "Copy shared links", - "type": "boolean" - }, - "sidecar": { - "default": true, - "description": "Copy sidecar file", - "type": "boolean" - }, - "sourceId": { - "description": "Source asset ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "stack": { - "default": true, - "description": "Copy stack association", + "enabled": { + "description": "Enabled", "type": "boolean" - }, - "targetId": { - "description": "Target asset ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" } }, "required": [ - "sourceId", - "targetId" + "enabled" ], "type": "object" }, - "AssetEditAction": { - "description": "Type of edit action to perform", - "enum": [ - "crop", - "rotate", - "mirror" - ], - "type": "string" - }, - "AssetEditActionItemDto": { + "AdminConfigLoggingDto": { "properties": { - "action": { - "$ref": "#/components/schemas/AssetEditAction" + "enabled": { + "description": "Enabled", + "type": "boolean" }, - "parameters": { - "anyOf": [ - { - "$ref": "#/components/schemas/CropParameters" - }, - { - "$ref": "#/components/schemas/RotateParameters" - }, - { - "$ref": "#/components/schemas/MirrorParameters" - } - ], - "description": "List of edit actions to apply (crop, rotate, or mirror)" + "level": { + "$ref": "#/components/schemas/LogLevel" } }, "required": [ - "action", - "parameters" + "enabled", + "level" ], "type": "object" }, - "AssetEditActionItemResponseDto": { + "AdminConfigMachineLearningAvailabilityChecksDto": { "properties": { - "action": { - "$ref": "#/components/schemas/AssetEditAction" + "enabled": { + "description": "Enabled", + "type": "boolean" }, - "id": { - "description": "Asset edit ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" + "interval": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" }, - "parameters": { - "anyOf": [ - { - "$ref": "#/components/schemas/CropParameters" - }, - { - "$ref": "#/components/schemas/RotateParameters" - }, - { - "$ref": "#/components/schemas/MirrorParameters" - } - ], - "description": "List of edit actions to apply (crop, rotate, or mirror)" + "timeout": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" } }, "required": [ - "action", - "id", - "parameters" + "enabled", + "interval", + "timeout" ], "type": "object" }, - "AssetEditsCreateDto": { + "AdminConfigMachineLearningDto": { "properties": { - "edits": { - "description": "List of edit actions to apply (crop, rotate, or mirror)", + "availabilityChecks": { + "$ref": "#/components/schemas/AdminConfigMachineLearningAvailabilityChecksDto" + }, + "clip": { + "$ref": "#/components/schemas/AdminConfigClipDto" + }, + "duplicateDetection": { + "$ref": "#/components/schemas/AdminConfigDuplicateDetectionDto" + }, + "enabled": { + "description": "Enabled", + "type": "boolean" + }, + "facialRecognition": { + "$ref": "#/components/schemas/AdminConfigFacialRecognitionDto" + }, + "ocr": { + "$ref": "#/components/schemas/AdminConfigOcrDto" + }, + "urls": { + "description": "ML service URLs", "items": { - "$ref": "#/components/schemas/AssetEditActionItemDto" + "type": "string" }, "minItems": 1, "type": "array" } }, "required": [ - "edits" + "availabilityChecks", + "clip", + "duplicateDetection", + "enabled", + "facialRecognition", + "ocr", + "urls" ], "type": "object" }, - "AssetEditsResponseDto": { + "AdminConfigMapDto": { "properties": { - "assetId": { - "description": "Asset ID these edits belong to", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "darkStyle": { + "description": "Dark map style URL", + "format": "uri", "type": "string" }, - "edits": { - "description": "List of edit actions applied to the asset", - "items": { - "$ref": "#/components/schemas/AssetEditActionItemResponseDto" - }, - "type": "array" + "enabled": { + "description": "Enabled", + "type": "boolean" + }, + "lightStyle": { + "description": "Light map style URL", + "format": "uri", + "type": "string" } }, "required": [ - "assetId", - "edits" + "darkStyle", + "enabled", + "lightStyle" ], "type": "object" }, - "AssetFaceCreateDto": { + "AdminConfigMetadataDto": { "properties": { - "assetId": { - "description": "Asset ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "height": { - "description": "Face bounding box height", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "imageHeight": { - "description": "Image height in pixels", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "imageWidth": { - "description": "Image width in pixels", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "personId": { - "description": "Person ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "width": { - "description": "Face bounding box width", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "x": { - "description": "Face bounding box X coordinate", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "y": { - "description": "Face bounding box Y coordinate", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "faces": { + "$ref": "#/components/schemas/AdminConfigFacesDto" } }, "required": [ - "assetId", - "height", - "imageHeight", - "imageWidth", - "personId", - "width", - "x", - "y" + "faces" ], "type": "object" }, - "AssetFaceDeleteDto": { + "AdminConfigNewVersionCheckDto": { "properties": { - "force": { - "description": "Force delete even if person has other faces", + "channel": { + "$ref": "#/components/schemas/ReleaseChannel" + }, + "enabled": { + "description": "Enabled", "type": "boolean" } }, "required": [ - "force" + "channel", + "enabled" ], "type": "object" }, - "AssetFaceResponseDto": { - "description": "Asset face with person", + "AdminConfigNightlyTasksDto": { "properties": { - "boundingBoxX1": { - "description": "Bounding box X1 coordinate", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "clusterNewFaces": { + "description": "Cluster new faces", + "type": "boolean" }, - "boundingBoxX2": { - "description": "Bounding box X2 coordinate", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "databaseCleanup": { + "description": "Database cleanup", + "type": "boolean" }, - "boundingBoxY1": { - "description": "Bounding box Y1 coordinate", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "generateMemories": { + "description": "Generate memories", + "type": "boolean" }, - "boundingBoxY2": { - "description": "Bounding box Y2 coordinate", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "missingThumbnails": { + "description": "Missing thumbnails", + "type": "boolean" }, - "id": { - "description": "Face ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "startTime": { + "description": "Start time (HH:MM)", + "pattern": "^(?:[01]\\d|2[0-3]):[0-5]\\d$", "type": "string" }, - "imageHeight": { - "description": "Image height in pixels", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "imageWidth": { - "description": "Image width in pixels", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "person": { - "allOf": [ - { - "$ref": "#/components/schemas/PersonResponseDto" - } - ], - "nullable": true - }, - "sourceType": { - "$ref": "#/components/schemas/SourceType" + "syncQuotaUsage": { + "description": "Sync quota usage", + "type": "boolean" } }, "required": [ - "boundingBoxX1", - "boundingBoxX2", - "boundingBoxY1", - "boundingBoxY2", - "id", - "imageHeight", - "imageWidth", - "person" + "clusterNewFaces", + "databaseCleanup", + "generateMemories", + "missingThumbnails", + "startTime", + "syncQuotaUsage" ], "type": "object" }, - "AssetFaceUpdateDto": { + "AdminConfigNotificationsDto": { "properties": { - "data": { - "description": "Face update items", - "items": { - "$ref": "#/components/schemas/AssetFaceUpdateItem" - }, - "type": "array" + "smtp": { + "$ref": "#/components/schemas/AdminConfigSmtpDto" } }, "required": [ - "data" + "smtp" ], "type": "object" }, - "AssetFaceUpdateItem": { + "AdminConfigOAuthDto": { "properties": { - "assetId": { - "description": "Asset ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "allowInsecureRequests": { + "description": "Allow insecure requests", + "type": "boolean" + }, + "autoLaunch": { + "description": "Auto launch", + "type": "boolean" + }, + "autoRegister": { + "description": "Auto register", + "type": "boolean" + }, + "buttonText": { + "description": "Button text", "type": "string" }, - "personId": { - "description": "Person ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "clientId": { + "description": "Client ID", "type": "string" - } - }, - "required": [ - "assetId", - "personId" - ], - "type": "object" - }, - "AssetIdErrorReason": { - "description": "Error reason if failed", - "enum": [ - "duplicate", - "no_permission", - "not_found" - ], - "type": "string" - }, - "AssetIdsDto": { - "properties": { - "assetIds": { - "description": "Asset IDs", - "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "assetIds" - ], - "type": "object" - }, - "AssetIdsResponseDto": { - "properties": { - "assetId": { - "description": "Asset ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + }, + "clientSecret": { + "description": "Client secret", "type": "string" }, - "error": { - "$ref": "#/components/schemas/AssetIdErrorReason" + "defaultStorageQuota": { + "description": "Default storage quota", + "maximum": 9007199254740991, + "minimum": 0, + "nullable": true, + "type": "integer" }, - "success": { - "description": "Whether operation succeeded", + "enabled": { + "description": "Enabled", "type": "boolean" - } - }, - "required": [ - "assetId", - "success" - ], - "type": "object" - }, - "AssetJobName": { - "description": "Job name", - "enum": [ - "refresh-faces", - "refresh-metadata", - "regenerate-thumbnail", - "transcode-video" - ], - "type": "string" - }, - "AssetJobsDto": { - "properties": { - "assetIds": { - "description": "Asset IDs", - "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "type": "array" }, - "name": { - "$ref": "#/components/schemas/AssetJobName" - } - }, - "required": [ - "assetIds", - "name" - ], - "type": "object" - }, - "AssetMediaCreateDto": { - "properties": { - "assetData": { - "description": "Asset file data", - "format": "binary", + "endSessionEndpoint": { + "description": "End session endpoint", "type": "string" }, - "duration": { - "description": "Duration in milliseconds (for videos)", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" + "issuerUrl": { + "description": "Issuer URL", + "type": "string" }, - "fileCreatedAt": { - "description": "File creation date", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "mobileOverrideEnabled": { + "description": "Mobile override enabled", + "type": "boolean" + }, + "mobileRedirectUri": { + "description": "Mobile redirect URI (set to empty string to disable)", "type": "string" }, - "fileModifiedAt": { - "description": "File modification date", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "profileSigningAlgorithm": { + "description": "Profile signing algorithm", "type": "string" }, - "filename": { - "description": "Filename", + "prompt": { + "description": "OAuth prompt parameter (e.g. select_account, login, consent)", "type": "string" }, - "isFavorite": { - "description": "Mark as favorite", - "type": "boolean" + "roleClaim": { + "description": "Role claim", + "type": "string" }, - "livePhotoVideoId": { - "description": "Live photo video ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "scope": { + "description": "Scope", "type": "string" }, - "metadata": { - "description": "Asset metadata items", - "items": { - "$ref": "#/components/schemas/AssetMetadataUpsertItemDto" - }, - "type": "array" + "signingAlgorithm": { + "description": "Signing algorithm", + "type": "string" }, - "sidecarData": { - "description": "Sidecar file data", - "format": "binary", + "storageLabelClaim": { + "description": "Storage label claim", "type": "string" }, - "visibility": { - "$ref": "#/components/schemas/AssetVisibility" + "storageQuotaClaim": { + "description": "Storage quota claim", + "type": "string" + }, + "timeout": { + "description": "Timeout", + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + }, + "tokenEndpointAuthMethod": { + "$ref": "#/components/schemas/OAuthTokenEndpointAuthMethod" } }, "required": [ - "assetData", - "fileCreatedAt", - "fileModifiedAt" + "allowInsecureRequests", + "autoLaunch", + "autoRegister", + "buttonText", + "clientId", + "clientSecret", + "defaultStorageQuota", + "enabled", + "endSessionEndpoint", + "issuerUrl", + "mobileOverrideEnabled", + "mobileRedirectUri", + "profileSigningAlgorithm", + "prompt", + "roleClaim", + "scope", + "signingAlgorithm", + "storageLabelClaim", + "storageQuotaClaim", + "timeout", + "tokenEndpointAuthMethod" ], "type": "object" }, - "AssetMediaResponseDto": { + "AdminConfigOcrDto": { "properties": { - "id": { - "description": "Asset media ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" + "enabled": { + "description": "Whether the task is enabled", + "type": "boolean" }, - "status": { - "$ref": "#/components/schemas/AssetMediaStatus" + "maxResolution": { + "description": "Maximum resolution for OCR processing", + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + }, + "minDetectionScore": { + "description": "Minimum confidence score for text detection", + "format": "double", + "maximum": 1, + "minimum": 0.1, + "type": "number" + }, + "minRecognitionScore": { + "description": "Minimum confidence score for text recognition", + "format": "double", + "maximum": 1, + "minimum": 0.1, + "type": "number" + }, + "modelName": { + "description": "Name of the model to use", + "type": "string" } }, "required": [ - "id", - "status" + "enabled", + "maxResolution", + "minDetectionScore", + "minRecognitionScore", + "modelName" ], "type": "object" }, - "AssetMediaSize": { - "description": "Asset media size", - "enum": [ - "original", - "fullsize", - "preview", - "thumbnail" - ], - "type": "string" - }, - "AssetMediaStatus": { - "description": "Upload status", - "enum": [ - "created", - "duplicate" - ], - "type": "string" - }, - "AssetMetadataBulkDeleteDto": { + "AdminConfigPasswordLoginDto": { "properties": { - "items": { - "description": "Metadata items to delete", - "items": { - "$ref": "#/components/schemas/AssetMetadataBulkDeleteItemDto" - }, - "type": "array" + "enabled": { + "description": "Enabled", + "type": "boolean" } }, "required": [ - "items" + "enabled" ], "type": "object" }, - "AssetMetadataBulkDeleteItemDto": { + "AdminConfigReverseGeocodingDto": { "properties": { - "assetId": { - "description": "Asset ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "key": { - "description": "Metadata key", - "type": "string" + "enabled": { + "description": "Enabled", + "type": "boolean" } }, "required": [ - "assetId", - "key" + "enabled" ], "type": "object" }, - "AssetMetadataBulkResponseDto": { + "AdminConfigServerDto": { "properties": { - "assetId": { - "description": "Asset ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "key": { - "description": "Metadata key", + "externalDomain": { + "description": "External domain", "type": "string" }, - "updatedAt": { - "description": "Last update date", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "loginPageMessage": { + "description": "Login page message", "type": "string" }, - "value": { - "additionalProperties": {}, - "description": "Metadata value (object)", - "type": "object" + "publicUsers": { + "description": "Public users", + "type": "boolean" } }, "required": [ - "assetId", - "key", - "updatedAt", - "value" + "externalDomain", + "loginPageMessage", + "publicUsers" ], "type": "object" }, - "AssetMetadataBulkUpsertDto": { + "AdminConfigSmtpDto": { "properties": { - "items": { - "description": "Metadata items to upsert", - "items": { - "$ref": "#/components/schemas/AssetMetadataBulkUpsertItemDto" - }, - "type": "array" + "enabled": { + "description": "Whether SMTP email notifications are enabled", + "type": "boolean" + }, + "from": { + "description": "Email address to send from", + "type": "string" + }, + "replyTo": { + "description": "Email address for replies", + "type": "string" + }, + "transport": { + "$ref": "#/components/schemas/AdminConfigSmtpTransportDto" } }, "required": [ - "items" + "enabled", + "from", + "replyTo", + "transport" ], "type": "object" }, - "AssetMetadataBulkUpsertItemDto": { + "AdminConfigSmtpTransportDto": { "properties": { - "assetId": { - "description": "Asset ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "host": { + "description": "SMTP server hostname", "type": "string" }, - "key": { - "description": "Metadata key", + "ignoreCert": { + "description": "Whether to ignore SSL certificate errors", + "type": "boolean" + }, + "password": { + "description": "SMTP password", "type": "string" }, - "value": { - "additionalProperties": {}, - "description": "Metadata value (object)", - "type": "object" + "port": { + "description": "SMTP server port", + "maximum": 65535, + "minimum": 0, + "type": "integer" + }, + "secure": { + "description": "Whether to use secure connection (TLS/SSL)", + "type": "boolean" + }, + "username": { + "description": "SMTP username", + "type": "string" } }, "required": [ - "assetId", - "key", - "value" + "host", + "ignoreCert", + "password", + "port", + "secure", + "username" ], "type": "object" }, - "AssetMetadataResponseDto": { + "AdminConfigStorageTemplateDto": { "properties": { - "key": { - "description": "Metadata key", + "enabled": { + "description": "Enabled", + "type": "boolean" + }, + "hashVerificationEnabled": { + "description": "Hash verification enabled", + "type": "boolean" + }, + "template": { + "description": "Template", + "type": "string" + } + }, + "required": [ + "enabled", + "hashVerificationEnabled", + "template" + ], + "type": "object" + }, + "AdminConfigTemplateEmailsDto": { + "properties": { + "albumInviteTemplate": { + "description": "Album invite template", "type": "string" }, - "updatedAt": { - "description": "Last update date", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "albumUpdateTemplate": { + "description": "Album update template", "type": "string" }, - "value": { - "additionalProperties": {}, - "description": "Metadata value (object)", - "type": "object" + "welcomeTemplate": { + "description": "Welcome template", + "type": "string" } }, "required": [ - "key", - "updatedAt", - "value" + "albumInviteTemplate", + "albumUpdateTemplate", + "welcomeTemplate" ], "type": "object" }, - "AssetMetadataUpsertDto": { + "AdminConfigTemplatesDto": { "properties": { - "items": { - "description": "Metadata items to upsert", - "items": { - "$ref": "#/components/schemas/AssetMetadataUpsertItemDto" - }, - "type": "array" + "email": { + "$ref": "#/components/schemas/AdminConfigTemplateEmailsDto" } }, "required": [ - "items" + "email" ], "type": "object" }, - "AssetMetadataUpsertItemDto": { + "AdminConfigThemeDto": { "properties": { - "key": { - "description": "Metadata key", + "customCss": { + "description": "Custom CSS for theming", "type": "string" + } + }, + "required": [ + "customCss" + ], + "type": "object" + }, + "AdminConfigTrashDto": { + "properties": { + "days": { + "description": "Days", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" }, - "value": { - "additionalProperties": {}, - "description": "Metadata value (object)", - "type": "object" + "enabled": { + "description": "Enabled", + "type": "boolean" } }, "required": [ - "key", - "value" + "days", + "enabled" ], "type": "object" }, - "AssetOcrResponseDto": { + "AdminConfigUserDto": { "properties": { - "assetId": { + "deleteDelay": { + "description": "Delete delay", + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + } + }, + "required": [ + "deleteDelay" + ], + "type": "object" + }, + "AdminOnboardingUpdateDto": { + "properties": { + "isOnboarded": { + "description": "Is admin onboarded", + "type": "boolean" + } + }, + "required": [ + "isOnboarded" + ], + "type": "object" + }, + "AlbumResponseDto": { + "properties": { + "albumName": { + "description": "Album name", + "type": "string" + }, + "albumThumbnailAssetId": { + "description": "Thumbnail asset ID", "format": "uuid", + "nullable": true, "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "boxScore": { - "description": "Confidence score for text detection box", - "format": "double", - "type": "number" + "albumUsers": { + "description": "First entry is always the album owner. Second entry is the auth user, if it differs from the owner. The rest are ordered alphabetically.", + "items": { + "$ref": "#/components/schemas/AlbumUserResponseDto" + }, + "minItems": 1, + "type": "array" + }, + "assetCount": { + "description": "Number of assets", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "contributorCounts": { + "items": { + "$ref": "#/components/schemas/ContributorCountResponseDto" + }, + "type": "array" + }, + "createdAt": { + "description": "Creation date", + "format": "date-time", + "type": "string" + }, + "description": { + "description": "Album description", + "type": "string", + "x-immich-history": [ + { + "version": "v1", + "state": "Added" + }, + { + "version": "v3", + "state": "Updated", + "description": "An empty string is returned instead of null for backwards compatibility; null will be returned in v4." + } + ] + }, + "endDate": { + "description": "End date (latest asset)", + "format": "date-time", + "type": "string" + }, + "hasSharedLink": { + "description": "Has shared link", + "type": "boolean" }, "id": { + "description": "Album ID", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "text": { - "description": "Recognized text", + "isActivityEnabled": { + "description": "Activity feed enabled", + "type": "boolean" + }, + "lastModifiedAssetTimestamp": { + "description": "Last modified asset timestamp", + "format": "date-time", "type": "string" }, - "textScore": { - "description": "Confidence score for text recognition", - "format": "double", - "type": "number" + "order": { + "$ref": "#/components/schemas/AssetOrder" }, - "x1": { - "description": "Normalized x coordinate of box corner 1 (0-1)", - "format": "double", - "type": "number" - }, - "x2": { - "description": "Normalized x coordinate of box corner 2 (0-1)", - "format": "double", - "type": "number" - }, - "x3": { - "description": "Normalized x coordinate of box corner 3 (0-1)", - "format": "double", - "type": "number" - }, - "x4": { - "description": "Normalized x coordinate of box corner 4 (0-1)", - "format": "double", - "type": "number" - }, - "y1": { - "description": "Normalized y coordinate of box corner 1 (0-1)", - "format": "double", - "type": "number" - }, - "y2": { - "description": "Normalized y coordinate of box corner 2 (0-1)", - "format": "double", - "type": "number" + "shared": { + "description": "Is shared album", + "type": "boolean" }, - "y3": { - "description": "Normalized y coordinate of box corner 3 (0-1)", - "format": "double", - "type": "number" + "startDate": { + "description": "Start date (earliest asset)", + "format": "date-time", + "type": "string" }, - "y4": { - "description": "Normalized y coordinate of box corner 4 (0-1)", - "format": "double", - "type": "number" + "updatedAt": { + "description": "Last update date", + "format": "date-time", + "type": "string" } }, "required": [ - "assetId", - "boxScore", + "albumName", + "albumThumbnailAssetId", + "albumUsers", + "assetCount", + "createdAt", + "description", + "hasSharedLink", "id", - "text", - "textScore", - "x1", - "x2", - "x3", - "x4", - "y1", - "y2", - "y3", - "y4" + "isActivityEnabled", + "shared", + "updatedAt" ], "type": "object" }, - "AssetOrder": { - "description": "Asset sort order", - "enum": [ - "asc", - "desc" - ], - "type": "string" - }, - "AssetOrderBy": { - "description": "Asset sorting property", - "enum": [ - "takenAt", - "createdAt" - ], - "type": "string" - }, - "AssetRejectReason": { - "description": "Rejection reason if rejected", - "enum": [ - "duplicate", - "unsupported-format" - ], - "type": "string" - }, - "AssetResponseDto": { + "AlbumStatisticsResponseDto": { "properties": { - "checksum": { - "description": "Base64 encoded SHA1 hash", - "type": "string" - }, - "createdAt": { - "description": "The UTC timestamp when the asset was originally uploaded to Immich.", - "format": "date-time", - "type": "string" - }, - "duplicateId": { - "description": "Duplicate group ID", - "format": "uuid", - "nullable": true, - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "duration": { - "description": "Video/gif duration in milliseconds (null for static images)", - "maximum": 2147483647, + "notShared": { + "description": "Number of non-shared albums", + "maximum": 9007199254740991, "minimum": 0, - "nullable": true, "type": "integer" }, - "exifInfo": { - "$ref": "#/components/schemas/ExifResponseDto" - }, - "fileCreatedAt": { - "description": "The actual UTC timestamp when the file was created/captured, preserving timezone information. This is the authoritative timestamp for chronological sorting within timeline groups. Combined with timezone data, this can be used to determine the exact moment the photo was taken.", - "format": "date-time", - "type": "string" - }, - "fileModifiedAt": { - "description": "The UTC timestamp when the file was last modified on the filesystem. This reflects the last time the physical file was changed, which may be different from when the photo was originally taken.", - "format": "date-time", - "type": "string" - }, - "hasMetadata": { - "description": "Whether asset has metadata", - "type": "boolean" + "owned": { + "description": "Number of owned albums", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" }, - "height": { - "description": "Asset height", + "shared": { + "description": "Number of shared albums", "maximum": 9007199254740991, "minimum": 0, - "nullable": true, "type": "integer" + } + }, + "required": [ + "notShared", + "owned", + "shared" + ], + "type": "object" + }, + "AlbumUserAddDto": { + "properties": { + "role": { + "$ref": "#/components/schemas/AlbumUserRole", + "default": "editor", + "description": "Album user role" }, - "id": { - "description": "Asset ID", + "userId": { + "description": "User ID", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" + } + }, + "required": [ + "userId" + ], + "type": "object" + }, + "AlbumUserCreateDto": { + "properties": { + "role": { + "$ref": "#/components/schemas/AlbumUserRole" }, - "isArchived": { - "description": "Is archived", - "type": "boolean" - }, - "isEdited": { - "description": "Is edited", - "type": "boolean", - "x-immich-history": [ - { - "version": "v2.5.0", - "state": "Added" - }, - { - "version": "v2.5.0", - "state": "Beta" - } - ], - "x-immich-state": "Beta" - }, - "isFavorite": { - "description": "Is favorite", - "type": "boolean" - }, - "isOffline": { - "description": "Is offline", - "type": "boolean" - }, - "isTrashed": { - "description": "Is trashed", - "type": "boolean" - }, - "libraryId": { - "description": "Library ID", + "userId": { + "description": "User ID", "format": "uuid", - "nullable": true, "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string", - "x-immich-history": [ - { - "version": "v1", - "state": "Added" - }, - { - "version": "v1", - "state": "Deprecated" - } - ], - "x-immich-state": "Deprecated" - }, - "livePhotoVideoId": { - "description": "Live photo video ID", - "nullable": true, - "type": "string" - }, - "localDateTime": { - "description": "The local date and time when the photo/video was taken, derived from EXIF metadata. This represents the photographer's local time regardless of timezone, stored as a timezone-agnostic timestamp. Used for timeline grouping by \"local\" days and months.", - "format": "date-time", - "type": "string" - }, - "originalFileName": { - "description": "Original file name", - "type": "string" - }, - "originalMimeType": { - "description": "Original MIME type", - "type": "string" - }, - "originalPath": { - "description": "Original file path", "type": "string" + } + }, + "required": [ + "role", + "userId" + ], + "type": "object" + }, + "AlbumUserResponseDto": { + "properties": { + "role": { + "$ref": "#/components/schemas/AlbumUserRole" }, - "owner": { + "user": { "$ref": "#/components/schemas/UserResponseDto" + } + }, + "required": [ + "role", + "user" + ], + "type": "object" + }, + "AlbumUserRole": { + "description": "Album user role", + "enum": [ + "editor", + "owner", + "viewer" + ], + "type": "string" + }, + "AlbumsAddAssetsDto": { + "properties": { + "albumIds": { + "description": "Album IDs", + "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "type": "array" }, - "ownerId": { - "description": "Owner user ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "assetIds": { + "description": "Asset IDs", + "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "albumIds", + "assetIds" + ], + "type": "object" + }, + "AlbumsAddAssetsResponseDto": { + "properties": { + "error": { + "$ref": "#/components/schemas/BulkIdErrorReason" + }, + "success": { + "description": "Operation success", + "type": "boolean" + } + }, + "required": [ + "success" + ], + "type": "object" + }, + "AlbumsResponse": { + "properties": { + "defaultAssetOrder": { + "$ref": "#/components/schemas/AssetOrder" + } + }, + "required": [ + "defaultAssetOrder" + ], + "type": "object" + }, + "AlbumsUpdate": { + "description": "Album preferences", + "properties": { + "defaultAssetOrder": { + "$ref": "#/components/schemas/AssetOrder" + } + }, + "type": "object" + }, + "ApiKeyCreateDto": { + "properties": { + "name": { + "description": "API key name", "type": "string" }, - "people": { + "permissions": { + "description": "List of permissions", "items": { - "$ref": "#/components/schemas/PersonResponseDto" + "$ref": "#/components/schemas/Permission" }, + "minItems": 1, "type": "array" - }, - "resized": { - "description": "Is resized", - "type": "boolean", + } + }, + "required": [ + "permissions" + ], + "type": "object" + }, + "ApiKeyCreateResponseDto": { + "properties": { + "apiKey": { + "$ref": "#/components/schemas/ApiKeyResponseDto", "x-immich-history": [ { "version": "v1", "state": "Added" }, { - "version": "v1.113.0", + "version": "v3.2.0", "state": "Deprecated" } ], "x-immich-state": "Deprecated" }, - "stack": { - "allOf": [ - { - "$ref": "#/components/schemas/AssetStackResponseDto" - } - ], - "nullable": true + "createdAt": { + "description": "Creation date", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" }, - "tags": { + "id": { + "description": "API key ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "name": { + "description": "API key name", + "type": "string" + }, + "permissions": { + "description": "List of permissions", "items": { - "$ref": "#/components/schemas/TagResponseDto" + "$ref": "#/components/schemas/Permission" }, "type": "array" }, - "thumbhash": { - "description": "Thumbhash for thumbnail generation (base64) also used as the c query param for thumbnail cache busting.", - "nullable": true, + "secret": { + "description": "API key secret (only shown once)", "type": "string" }, - "type": { - "$ref": "#/components/schemas/AssetTypeEnum" - }, "updatedAt": { - "description": "The UTC timestamp when the asset record was last updated in the database. This is automatically maintained by the database and reflects when any field in the asset was last modified.", + "description": "Last update date", + "example": "2024-01-01T00:00:00.000Z", "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" - }, - "visibility": { - "$ref": "#/components/schemas/AssetVisibility" - }, - "width": { - "description": "Asset width", - "maximum": 9007199254740991, - "minimum": 0, - "nullable": true, - "type": "integer" } }, "required": [ - "checksum", + "apiKey", "createdAt", - "duration", - "fileCreatedAt", - "fileModifiedAt", - "hasMetadata", - "height", "id", - "isArchived", - "isEdited", - "isFavorite", - "isOffline", - "isTrashed", - "localDateTime", - "originalFileName", - "originalPath", - "ownerId", - "thumbhash", - "type", - "updatedAt", - "visibility", - "width" + "name", + "permissions", + "secret", + "updatedAt" ], "type": "object" }, - "AssetStackResponseDto": { + "ApiKeyResponseDto": { "properties": { - "assetCount": { - "description": "Number of assets in stack", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" + "createdAt": { + "description": "Creation date", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" }, "id": { - "description": "Stack ID", + "description": "API key ID", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "primaryAssetId": { - "description": "Primary asset ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "name": { + "description": "API key name", + "type": "string" + }, + "permissions": { + "description": "List of permissions", + "items": { + "$ref": "#/components/schemas/Permission" + }, + "type": "array" + }, + "updatedAt": { + "description": "Last update date", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" } }, "required": [ - "assetCount", + "createdAt", "id", - "primaryAssetId" + "name", + "permissions", + "updatedAt" ], "type": "object" }, - "AssetStatsResponseDto": { + "ApiKeyUpdateDto": { "properties": { - "images": { - "description": "Number of images", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "total": { - "description": "Total number of assets", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "name": { + "description": "API key name", + "type": "string" }, - "videos": { - "description": "Number of videos", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "permissions": { + "description": "List of permissions", + "items": { + "$ref": "#/components/schemas/Permission" + }, + "minItems": 1, + "type": "array" } }, - "required": [ - "images", - "total", - "videos" - ], "type": "object" }, - "AssetTypeEnum": { - "description": "Asset type", - "enum": [ - "IMAGE", - "VIDEO", - "AUDIO", - "OTHER" - ], - "type": "string" - }, - "AssetUploadAction": { - "description": "Upload action", - "enum": [ - "accept", - "reject" - ], - "type": "string" - }, - "AssetVisibility": { - "description": "Asset visibility", - "enum": [ - "archive", - "timeline", - "hidden", - "locked" - ], - "type": "string" - }, - "AudioCodec": { - "description": "Target audio codec", - "enum": [ - "mp3", - "aac", - "opus", - "pcm_s16le" + "AssetBulkDeleteDto": { + "properties": { + "force": { + "description": "Force delete even if in use", + "type": "boolean" + }, + "ids": { + "description": "IDs to process", + "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "ids" ], - "type": "string" + "type": "object" }, - "AuthStatusResponseDto": { + "AssetBulkUpdateDto": { "properties": { - "expiresAt": { - "description": "Session expiration date", + "dateTimeOriginal": { + "description": "Original date and time", "type": "string" }, - "isElevated": { - "description": "Is elevated session", - "type": "boolean" + "dateTimeRelative": { + "description": "Relative time offset in minutes", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" }, - "password": { - "description": "Has password set", - "type": "boolean" + "description": { + "description": "Asset description", + "type": "string" }, - "pinCode": { - "description": "Has PIN code set", + "duplicateId": { + "description": "Duplicate ID", + "nullable": true, + "type": "string" + }, + "ids": { + "description": "Asset IDs to update", + "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "type": "array" + }, + "isFavorite": { + "description": "Mark as favorite", "type": "boolean" }, - "pinExpiresAt": { - "description": "PIN expiration date", + "latitude": { + "description": "Latitude coordinate", + "maximum": 90, + "minimum": -90, + "type": "number" + }, + "longitude": { + "description": "Longitude coordinate", + "maximum": 180, + "minimum": -180, + "type": "number" + }, + "rating": { + "description": "Rating in range [1-5] (starred), -1 (rejected), or null (unrated)", + "maximum": 5, + "minimum": -1, + "nullable": true, + "type": "integer", + "x-immich-history": [ + { + "version": "v1", + "state": "Added" + }, + { + "version": "v2", + "state": "Stable" + }, + { + "version": "v3", + "state": "Updated", + "description": "Using 0 as a rating is no longer valid." + } + ], + "x-immich-state": "Stable" + }, + "timeZone": { + "description": "Time zone (IANA timezone)", "type": "string" + }, + "visibility": { + "$ref": "#/components/schemas/AssetVisibility" } }, "required": [ - "isElevated", - "password", - "pinCode" + "ids" ], "type": "object" }, - "AvatarUpdate": { + "AssetBulkUploadCheckDto": { "properties": { - "color": { - "$ref": "#/components/schemas/UserAvatarColor" + "assets": { + "description": "Assets to check", + "items": { + "$ref": "#/components/schemas/AssetBulkUploadCheckItem" + }, + "type": "array" } }, - "type": "object" - }, - "BulkIdErrorReason": { - "description": "Error reason", - "enum": [ - "duplicate", - "no_permission", - "not_found", - "unknown", - "validation" + "required": [ + "assets" ], - "type": "string" + "type": "object" }, - "BulkIdResponseDto": { + "AssetBulkUploadCheckItem": { "properties": { - "error": { - "$ref": "#/components/schemas/BulkIdErrorReason" - }, - "errorMessage": { + "checksum": { + "description": "Base64 or hex encoded SHA1 hash", "type": "string" }, "id": { - "description": "ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "description": "Client-side identifier echoed in the response to match results to inputs (e.g. filename)", "type": "string" - }, - "success": { - "description": "Whether operation succeeded", - "type": "boolean" } }, "required": [ - "id", - "success" + "checksum", + "id" ], "type": "object" }, - "BulkIdsDto": { + "AssetBulkUploadCheckResponseDto": { "properties": { - "ids": { - "description": "IDs to process", + "results": { + "description": "Upload check results", "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" + "$ref": "#/components/schemas/AssetBulkUploadCheckResult" }, "type": "array" } }, "required": [ - "ids" + "results" ], "type": "object" }, - "CLIPConfig": { + "AssetBulkUploadCheckResult": { "properties": { - "enabled": { - "description": "Whether the task is enabled", - "type": "boolean" + "action": { + "$ref": "#/components/schemas/AssetUploadAction" }, - "modelName": { - "description": "Name of the model to use", + "assetId": { + "description": "Existing asset ID if duplicate", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "id": { + "description": "Client-side identifier echoed from the request to match results to inputs", "type": "string" + }, + "isTrashed": { + "description": "Whether existing asset is trashed", + "type": "boolean" + }, + "reason": { + "$ref": "#/components/schemas/AssetRejectReason" } }, "required": [ - "enabled", - "modelName" + "action", + "id" ], "type": "object" }, - "CQMode": { - "description": "CQ mode", - "enum": [ - "auto", - "cqp", - "icq" - ], - "type": "string" - }, - "CalendarHeatmapResponseDto": { + "AssetCopyDto": { "properties": { - "from": { - "description": "Start date in UTC", - "example": "2024-01-01", - "type": "string" + "albums": { + "default": true, + "description": "Copy album associations", + "type": "boolean" }, - "series": { - "items": { - "properties": { - "count": { - "description": "Activity count", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "date": { - "description": "Date in UTC", - "example": "2024-01-01", - "type": "string" - } - }, - "required": [ - "date", - "count" - ], - "type": "object" - }, - "type": "array" - }, - "to": { - "description": "End date in UTC", - "example": "2024-12-31", - "type": "string" - }, - "totalCount": { - "description": "Total activity count over the period", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "from", - "series", - "to", - "totalCount" - ], - "type": "object" - }, - "CalendarHeatmapType": { - "description": "Type of calendar heatmap", - "enum": [ - "Upload", - "Taken" - ], - "type": "string" - }, - "CastResponse": { - "properties": { - "gCastEnabled": { - "description": "Whether Google Cast is enabled", + "favorite": { + "default": true, + "description": "Copy favorite status", "type": "boolean" - } - }, - "required": [ - "gCastEnabled" - ], - "type": "object" - }, - "CastUpdate": { - "properties": { - "gCastEnabled": { - "description": "Whether Google Cast is enabled", + }, + "sharedLinks": { + "default": true, + "description": "Copy shared links", "type": "boolean" - } - }, - "type": "object" - }, - "ChangePasswordDto": { - "properties": { - "invalidateSessions": { - "default": false, - "description": "Invalidate all other sessions", + }, + "sidecar": { + "default": true, + "description": "Copy sidecar file", "type": "boolean" }, - "newPassword": { - "description": "New password (min 8 characters)", - "example": "password", - "minLength": 8, + "sourceId": { + "description": "Source asset ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "password": { - "description": "Current password", - "example": "password", + "stack": { + "default": true, + "description": "Copy stack association", + "type": "boolean" + }, + "targetId": { + "description": "Target asset ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" } }, "required": [ - "newPassword", - "password" + "sourceId", + "targetId" ], "type": "object" }, - "Colorspace": { - "description": "Colorspace", + "AssetEditAction": { + "description": "Type of edit action to perform", "enum": [ - "srgb", - "p3" + "crop", + "rotate", + "mirror" ], "type": "string" }, - "ContributorCountResponseDto": { + "AssetEditActionItemDto": { "properties": { - "assetCount": { - "description": "Number of assets contributed", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" + "action": { + "$ref": "#/components/schemas/AssetEditAction" }, - "userId": { - "description": "User ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" + "parameters": { + "anyOf": [ + { + "$ref": "#/components/schemas/CropParameters" + }, + { + "$ref": "#/components/schemas/RotateParameters" + }, + { + "$ref": "#/components/schemas/MirrorParameters" + } + ], + "description": "List of edit actions to apply (crop, rotate, or mirror)" } }, "required": [ - "assetCount", - "userId" + "action", + "parameters" ], "type": "object" }, - "CreateAlbumDto": { + "AssetEditActionItemResponseDto": { "properties": { - "albumName": { - "description": "Album name", - "type": "string" - }, - "albumUsers": { - "description": "Album users", - "items": { - "$ref": "#/components/schemas/AlbumUserCreateDto" - }, - "type": "array" + "action": { + "$ref": "#/components/schemas/AssetEditAction" }, - "assetIds": { - "description": "Initial asset IDs", - "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "type": "array" + "id": { + "description": "Asset edit ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" }, - "description": { - "description": "Album description", - "nullable": true, - "type": "string", - "x-immich-history": [ + "parameters": { + "anyOf": [ { - "version": "v1", - "state": "Added" + "$ref": "#/components/schemas/CropParameters" }, { - "version": "v3", - "state": "Updated", - "description": "Sending an empty string is deprecated; send null instead. Empty strings will no longer be coerced to null in v4." + "$ref": "#/components/schemas/RotateParameters" + }, + { + "$ref": "#/components/schemas/MirrorParameters" } - ] + ], + "description": "List of edit actions to apply (crop, rotate, or mirror)" } }, "required": [ - "albumName" + "action", + "id", + "parameters" ], "type": "object" }, - "CreateLibraryDto": { + "AssetEditsCreateDto": { "properties": { - "exclusionPatterns": { - "description": "Exclusion patterns (max 128)", - "items": { - "type": "string" - }, - "maxItems": 128, - "type": "array" - }, - "importPaths": { - "description": "Import paths (max 128)", + "edits": { + "description": "List of edit actions to apply (crop, rotate, or mirror)", "items": { - "type": "string" + "$ref": "#/components/schemas/AssetEditActionItemDto" }, - "maxItems": 128, + "minItems": 1, "type": "array" - }, - "name": { - "description": "Library name", - "minLength": 1, - "type": "string" - }, - "ownerId": { - "description": "Owner user ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" } }, "required": [ - "ownerId" + "edits" ], "type": "object" }, - "CreateProfileImageDto": { + "AssetEditsResponseDto": { "properties": { - "file": { - "description": "Profile image file", - "format": "binary", + "assetId": { + "description": "Asset ID these edits belong to", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" + }, + "edits": { + "description": "List of edit actions applied to the asset", + "items": { + "$ref": "#/components/schemas/AssetEditActionItemResponseDto" + }, + "type": "array" } }, "required": [ - "file" + "assetId", + "edits" ], "type": "object" }, - "CreateProfileImageResponseDto": { + "AssetFaceCreateDto": { "properties": { - "profileChangedAt": { - "description": "Profile image change date", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" - }, - "profileImagePath": { - "description": "Profile image file path", - "type": "string" - }, - "userId": { - "description": "User ID", + "assetId": { + "description": "Asset ID", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" - } - }, - "required": [ - "profileChangedAt", - "profileImagePath", - "userId" - ], - "type": "object" - }, - "CropParameters": { - "properties": { + }, "height": { - "description": "Height of the crop", + "description": "Face bounding box height", "maximum": 9007199254740991, - "minimum": 1, + "minimum": -9007199254740991, + "type": "integer" + }, + "imageHeight": { + "description": "Image height in pixels", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "imageWidth": { + "description": "Image width in pixels", + "maximum": 9007199254740991, + "minimum": -9007199254740991, "type": "integer" }, + "personId": { + "description": "Person ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, "width": { - "description": "Width of the crop", + "description": "Face bounding box width", "maximum": 9007199254740991, - "minimum": 1, + "minimum": -9007199254740991, "type": "integer" }, "x": { - "description": "Top-Left X coordinate of crop", + "description": "Face bounding box X coordinate", "maximum": 9007199254740991, - "minimum": 0, + "minimum": -9007199254740991, "type": "integer" }, "y": { - "description": "Top-Left Y coordinate of crop", + "description": "Face bounding box Y coordinate", "maximum": 9007199254740991, - "minimum": 0, + "minimum": -9007199254740991, "type": "integer" } }, "required": [ + "assetId", "height", + "imageHeight", + "imageWidth", + "personId", "width", "x", "y" ], "type": "object" }, - "DatabaseBackupConfig": { + "AssetFaceDeleteDto": { "properties": { - "cronExpression": { - "description": "Cron expression", - "type": "string" - }, - "enabled": { - "description": "Enabled", + "force": { + "description": "Force delete even if person has other faces", "type": "boolean" - }, - "keepLastAmount": { - "description": "Keep last amount", - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" } }, "required": [ - "cronExpression", - "enabled", - "keepLastAmount" - ], - "type": "object" - }, - "DatabaseBackupDeleteDto": { - "properties": { - "backups": { - "description": "Backup filenames to delete", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "backups" + "force" ], "type": "object" }, - "DatabaseBackupDto": { + "AssetFaceResponseDto": { + "description": "Asset face with person", "properties": { - "filename": { - "description": "Backup filename", - "type": "string" + "boundingBoxX1": { + "description": "Bounding box X1 coordinate", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" }, - "filesize": { - "description": "Backup file size", + "boundingBoxX2": { + "description": "Bounding box X2 coordinate", "maximum": 9007199254740991, "minimum": -9007199254740991, "type": "integer" }, - "timezone": { - "description": "Backup timezone", + "boundingBoxY1": { + "description": "Bounding box Y1 coordinate", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "boundingBoxY2": { + "description": "Bounding box Y2 coordinate", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "id": { + "description": "Face ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" + }, + "imageHeight": { + "description": "Image height in pixels", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "imageWidth": { + "description": "Image width in pixels", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "person": { + "allOf": [ + { + "$ref": "#/components/schemas/PersonResponseDto" + } + ], + "nullable": true + }, + "sourceType": { + "$ref": "#/components/schemas/SourceType" } }, "required": [ - "filename", - "filesize", - "timezone" + "boundingBoxX1", + "boundingBoxX2", + "boundingBoxY1", + "boundingBoxY2", + "id", + "imageHeight", + "imageWidth", + "person" ], "type": "object" }, - "DatabaseBackupListResponseDto": { + "AssetFaceUpdateDto": { "properties": { - "backups": { - "description": "List of backups", + "data": { + "description": "Face update items", "items": { - "$ref": "#/components/schemas/DatabaseBackupDto" + "$ref": "#/components/schemas/AssetFaceUpdateItem" }, "type": "array" } }, "required": [ - "backups" + "data" ], "type": "object" }, - "DatabaseBackupUploadDto": { + "AssetFaceUpdateItem": { "properties": { - "file": { - "description": "Database backup file", - "format": "binary", + "assetId": { + "description": "Asset ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "personId": { + "description": "Person ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" } }, + "required": [ + "assetId", + "personId" + ], "type": "object" }, - "DownloadArchiveDto": { + "AssetIdErrorReason": { + "description": "Error reason if failed", + "enum": [ + "duplicate", + "no_permission", + "not_found" + ], + "type": "string" + }, + "AssetIdsDto": { "properties": { "assetIds": { "description": "Asset IDs", @@ -18879,21 +19190,49 @@ "type": "string" }, "type": "array" + } + }, + "required": [ + "assetIds" + ], + "type": "object" + }, + "AssetIdsResponseDto": { + "properties": { + "assetId": { + "description": "Asset ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" }, - "edited": { - "description": "Download edited asset if available", + "error": { + "$ref": "#/components/schemas/AssetIdErrorReason" + }, + "success": { + "description": "Whether operation succeeded", "type": "boolean" } }, "required": [ - "assetIds" + "assetId", + "success" ], "type": "object" }, - "DownloadArchiveInfo": { + "AssetJobName": { + "description": "Job name", + "enum": [ + "refresh-faces", + "refresh-metadata", + "regenerate-thumbnail", + "transcode-video" + ], + "type": "string" + }, + "AssetJobsDto": { "properties": { "assetIds": { - "description": "Asset IDs in this archive", + "description": "Asset IDs", "items": { "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", @@ -18901,705 +19240,1002 @@ }, "type": "array" }, - "size": { - "description": "Archive size in bytes", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "name": { + "$ref": "#/components/schemas/AssetJobName" } }, "required": [ "assetIds", - "size" + "name" ], "type": "object" }, - "DownloadInfoDto": { + "AssetMediaCreateDto": { "properties": { - "albumId": { - "description": "Album ID to download", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "assetData": { + "description": "Asset file data", + "format": "binary", "type": "string" }, - "archiveSize": { - "description": "Archive size limit in bytes", + "duration": { + "description": "Duration in milliseconds (for videos)", "maximum": 9007199254740991, - "minimum": 1, + "minimum": 0, "type": "integer" }, - "assetIds": { - "description": "Asset IDs to download", - "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "type": "array" + "fileCreatedAt": { + "description": "File creation date", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" }, - "userId": { - "description": "User ID to download assets from", + "fileModifiedAt": { + "description": "File modification date", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "filename": { + "description": "Filename", + "type": "string" + }, + "isFavorite": { + "description": "Mark as favorite", + "type": "boolean" + }, + "livePhotoVideoId": { + "description": "Live photo video ID", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" + }, + "metadata": { + "description": "Asset metadata items", + "items": { + "$ref": "#/components/schemas/AssetMetadataUpsertItemDto" + }, + "type": "array" + }, + "sidecarData": { + "description": "Sidecar file data", + "format": "binary", + "type": "string" + }, + "visibility": { + "$ref": "#/components/schemas/AssetVisibility" } }, + "required": [ + "assetData", + "fileCreatedAt", + "fileModifiedAt" + ], "type": "object" }, - "DownloadResponse": { + "AssetMediaResponseDto": { "properties": { - "archiveSize": { - "description": "Maximum archive size in bytes", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "id": { + "description": "Asset media ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" }, - "includeEmbeddedVideos": { - "description": "Whether to include embedded videos in downloads", - "type": "boolean" + "status": { + "$ref": "#/components/schemas/AssetMediaStatus" } }, "required": [ - "archiveSize", - "includeEmbeddedVideos" + "id", + "status" ], "type": "object" }, - "DownloadResponseDto": { + "AssetMediaSize": { + "description": "Asset media size", + "enum": [ + "original", + "fullsize", + "preview", + "thumbnail" + ], + "type": "string" + }, + "AssetMediaStatus": { + "description": "Upload status", + "enum": [ + "created", + "duplicate" + ], + "type": "string" + }, + "AssetMetadataBulkDeleteDto": { "properties": { - "archives": { - "description": "Archive information", + "items": { + "description": "Metadata items to delete", "items": { - "$ref": "#/components/schemas/DownloadArchiveInfo" + "$ref": "#/components/schemas/AssetMetadataBulkDeleteItemDto" }, "type": "array" - }, - "totalSize": { - "description": "Total size in bytes", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" } }, "required": [ - "archives", - "totalSize" + "items" ], "type": "object" }, - "DownloadUpdate": { + "AssetMetadataBulkDeleteItemDto": { "properties": { - "archiveSize": { - "description": "Maximum archive size in bytes", - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" + "assetId": { + "description": "Asset ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" }, - "includeEmbeddedVideos": { - "description": "Whether to include embedded videos in downloads", - "type": "boolean" + "key": { + "description": "Metadata key", + "type": "string" } }, + "required": [ + "assetId", + "key" + ], "type": "object" }, - "DuplicateDetectionConfig": { + "AssetMetadataBulkResponseDto": { "properties": { - "enabled": { - "description": "Whether the task is enabled", - "type": "boolean" + "assetId": { + "description": "Asset ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" }, - "maxDistance": { - "description": "Maximum distance threshold for duplicate detection", - "format": "double", - "maximum": 0.1, - "minimum": 0.001, - "type": "number" + "key": { + "description": "Metadata key", + "type": "string" + }, + "updatedAt": { + "description": "Last update date", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "value": { + "additionalProperties": {}, + "description": "Metadata value (object)", + "type": "object" } }, "required": [ - "enabled", - "maxDistance" + "assetId", + "key", + "updatedAt", + "value" ], "type": "object" }, - "DuplicateResolveDto": { + "AssetMetadataBulkUpsertDto": { "properties": { - "groups": { - "description": "List of duplicate groups to resolve", + "items": { + "description": "Metadata items to upsert", "items": { - "$ref": "#/components/schemas/DuplicateResolveGroupDto" + "$ref": "#/components/schemas/AssetMetadataBulkUpsertItemDto" }, - "minItems": 1, "type": "array" } }, "required": [ - "groups" + "items" ], "type": "object" }, - "DuplicateResolveGroupDto": { + "AssetMetadataBulkUpsertItemDto": { "properties": { - "duplicateId": { + "assetId": { + "description": "Asset ID", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "keepAssetIds": { - "description": "Asset IDs to keep", - "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "type": "array" + "key": { + "description": "Metadata key", + "type": "string" }, - "trashAssetIds": { - "description": "Asset IDs to trash or delete", - "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "type": "array" + "value": { + "additionalProperties": {}, + "description": "Metadata value (object)", + "type": "object" } }, "required": [ - "duplicateId", - "keepAssetIds", - "trashAssetIds" + "assetId", + "key", + "value" ], "type": "object" }, - "DuplicateResponseDto": { + "AssetMetadataResponseDto": { "properties": { - "assets": { - "description": "Duplicate assets", - "items": { - "$ref": "#/components/schemas/AssetResponseDto" - }, - "type": "array" + "key": { + "description": "Metadata key", + "type": "string" }, - "duplicateId": { - "description": "Duplicate group ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "updatedAt": { + "description": "Last update date", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "suggestedKeepAssetIds": { - "description": "Suggested asset IDs to keep based on file size and EXIF data", + "value": { + "additionalProperties": {}, + "description": "Metadata value (object)", + "type": "object" + } + }, + "required": [ + "key", + "updatedAt", + "value" + ], + "type": "object" + }, + "AssetMetadataUpsertDto": { + "properties": { + "items": { + "description": "Metadata items to upsert", "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" + "$ref": "#/components/schemas/AssetMetadataUpsertItemDto" }, "type": "array" } }, "required": [ - "assets", - "duplicateId", - "suggestedKeepAssetIds" + "items" ], "type": "object" }, - "EmailNotificationsResponse": { + "AssetMetadataUpsertItemDto": { "properties": { - "albumInvite": { - "description": "Whether to receive email notifications for album invites", - "type": "boolean" - }, - "albumUpdate": { - "description": "Whether to receive email notifications for album updates", - "type": "boolean" + "key": { + "description": "Metadata key", + "type": "string" }, - "enabled": { - "description": "Whether email notifications are enabled", - "type": "boolean" + "value": { + "additionalProperties": {}, + "description": "Metadata value (object)", + "type": "object" } }, "required": [ - "albumInvite", - "albumUpdate", - "enabled" + "key", + "value" ], "type": "object" }, - "EmailNotificationsUpdate": { - "properties": { - "albumInvite": { - "description": "Whether to receive email notifications for album invites", - "type": "boolean" - }, - "albumUpdate": { - "description": "Whether to receive email notifications for album updates", - "type": "boolean" - }, - "enabled": { - "description": "Whether email notifications are enabled", - "type": "boolean" - } - }, - "type": "object" - }, - "ExifResponseDto": { - "description": "EXIF response", + "AssetOcrResponseDto": { "properties": { - "city": { - "default": null, - "description": "City name", - "nullable": true, + "assetId": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "country": { - "default": null, - "description": "Country name", - "nullable": true, - "type": "string" + "boxScore": { + "description": "Confidence score for text detection box", + "format": "double", + "type": "number" }, - "dateTimeOriginal": { - "default": null, - "description": "Original date/time", - "format": "date-time", - "nullable": true, + "id": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "description": { - "default": null, - "description": "Image description", - "nullable": true, + "text": { + "description": "Recognized text", "type": "string" }, - "exifImageHeight": { - "default": null, - "description": "Image height in pixels", - "maximum": 9007199254740991, - "minimum": 0, - "nullable": true, - "type": "integer" + "textScore": { + "description": "Confidence score for text recognition", + "format": "double", + "type": "number" }, - "exifImageWidth": { - "default": null, - "description": "Image width in pixels", - "maximum": 9007199254740991, - "minimum": 0, - "nullable": true, - "type": "integer" + "x1": { + "description": "Normalized x coordinate of box corner 1 (0-1)", + "format": "double", + "type": "number" }, - "exposureTime": { - "default": null, - "description": "Exposure time", - "nullable": true, - "type": "string" + "x2": { + "description": "Normalized x coordinate of box corner 2 (0-1)", + "format": "double", + "type": "number" }, - "fNumber": { - "default": null, - "description": "F-number (aperture)", - "nullable": true, + "x3": { + "description": "Normalized x coordinate of box corner 3 (0-1)", + "format": "double", "type": "number" }, - "fileSizeInByte": { - "default": null, - "description": "File size in bytes", - "maximum": 9007199254740991, - "minimum": 0, - "nullable": true, - "type": "integer" + "x4": { + "description": "Normalized x coordinate of box corner 4 (0-1)", + "format": "double", + "type": "number" }, - "focalLength": { - "default": null, - "description": "Focal length in mm", - "nullable": true, + "y1": { + "description": "Normalized y coordinate of box corner 1 (0-1)", + "format": "double", "type": "number" }, - "iso": { - "default": null, - "description": "ISO sensitivity", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "nullable": true, - "type": "integer" + "y2": { + "description": "Normalized y coordinate of box corner 2 (0-1)", + "format": "double", + "type": "number" }, - "latitude": { - "default": null, - "description": "GPS latitude", - "nullable": true, + "y3": { + "description": "Normalized y coordinate of box corner 3 (0-1)", + "format": "double", "type": "number" }, - "lensModel": { - "default": null, - "description": "Lens model", - "nullable": true, + "y4": { + "description": "Normalized y coordinate of box corner 4 (0-1)", + "format": "double", + "type": "number" + } + }, + "required": [ + "assetId", + "boxScore", + "id", + "text", + "textScore", + "x1", + "x2", + "x3", + "x4", + "y1", + "y2", + "y3", + "y4" + ], + "type": "object" + }, + "AssetOrder": { + "description": "Asset sort order", + "enum": [ + "asc", + "desc" + ], + "type": "string" + }, + "AssetOrderBy": { + "description": "Asset sorting property", + "enum": [ + "takenAt", + "createdAt" + ], + "type": "string" + }, + "AssetRejectReason": { + "description": "Rejection reason if rejected", + "enum": [ + "duplicate", + "unsupported-format" + ], + "type": "string" + }, + "AssetResponseDto": { + "properties": { + "checksum": { + "description": "Base64 encoded SHA1 hash", "type": "string" }, - "longitude": { - "default": null, - "description": "GPS longitude", - "nullable": true, - "type": "number" + "createdAt": { + "description": "The UTC timestamp when the asset was originally uploaded to Immich.", + "format": "date-time", + "type": "string" }, - "make": { - "default": null, - "description": "Camera make", + "duplicateId": { + "description": "Duplicate group ID", + "format": "uuid", "nullable": true, + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "model": { - "default": null, - "description": "Camera model", + "duration": { + "description": "Video/gif duration in milliseconds (null for static images)", + "maximum": 2147483647, + "minimum": 0, "nullable": true, - "type": "string" + "type": "integer" }, - "modifyDate": { - "default": null, - "description": "Modification date/time", + "exifInfo": { + "$ref": "#/components/schemas/ExifResponseDto" + }, + "fileCreatedAt": { + "description": "The actual UTC timestamp when the file was created/captured, preserving timezone information. This is the authoritative timestamp for chronological sorting within timeline groups. Combined with timezone data, this can be used to determine the exact moment the photo was taken.", "format": "date-time", - "nullable": true, "type": "string" }, - "orientation": { - "default": null, - "description": "Image orientation", - "nullable": true, + "fileModifiedAt": { + "description": "The UTC timestamp when the file was last modified on the filesystem. This reflects the last time the physical file was changed, which may be different from when the photo was originally taken.", + "format": "date-time", "type": "string" }, - "projectionType": { - "default": null, - "description": "Projection type", + "hasMetadata": { + "description": "Whether asset has metadata", + "type": "boolean" + }, + "height": { + "description": "Asset height", + "maximum": 9007199254740991, + "minimum": 0, "nullable": true, - "type": "string" + "type": "integer" }, - "rating": { - "default": null, - "description": "Rating", - "maximum": 5, - "minimum": 1, + "id": { + "description": "Asset ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "isArchived": { + "description": "Is archived", + "type": "boolean" + }, + "isEdited": { + "description": "Is edited", + "type": "boolean", + "x-immich-history": [ + { + "version": "v2.5.0", + "state": "Added" + }, + { + "version": "v2.5.0", + "state": "Beta" + } + ], + "x-immich-state": "Beta" + }, + "isFavorite": { + "description": "Is favorite", + "type": "boolean" + }, + "isOffline": { + "description": "Is offline", + "type": "boolean" + }, + "isTrashed": { + "description": "Is trashed", + "type": "boolean" + }, + "libraryId": { + "description": "Library ID", + "format": "uuid", "nullable": true, - "type": "integer" + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string", + "x-immich-history": [ + { + "version": "v1", + "state": "Added" + }, + { + "version": "v1", + "state": "Deprecated" + } + ], + "x-immich-state": "Deprecated" }, - "state": { - "default": null, - "description": "State/province name", + "livePhotoVideoId": { + "description": "Live photo video ID", "nullable": true, "type": "string" }, - "timeZone": { - "default": null, - "description": "Time zone", - "nullable": true, + "localDateTime": { + "description": "The local date and time when the photo/video was taken, derived from EXIF metadata. This represents the photographer's local time regardless of timezone, stored as a timezone-agnostic timestamp. Used for timeline grouping by \"local\" days and months.", + "format": "date-time", "type": "string" - } - }, - "type": "object" - }, - "FaceDto": { - "properties": { - "id": { - "description": "Face ID", + }, + "originalFileName": { + "description": "Original file name", + "type": "string" + }, + "originalMimeType": { + "description": "Original MIME type", + "type": "string" + }, + "originalPath": { + "description": "Original file path", + "type": "string" + }, + "owner": { + "$ref": "#/components/schemas/UserResponseDto" + }, + "ownerId": { + "description": "Owner user ID", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" + }, + "people": { + "items": { + "$ref": "#/components/schemas/PersonResponseDto" + }, + "type": "array" + }, + "resized": { + "description": "Is resized", + "type": "boolean", + "x-immich-history": [ + { + "version": "v1", + "state": "Added" + }, + { + "version": "v1.113.0", + "state": "Deprecated" + } + ], + "x-immich-state": "Deprecated" + }, + "stack": { + "allOf": [ + { + "$ref": "#/components/schemas/AssetStackResponseDto" + } + ], + "nullable": true + }, + "tags": { + "items": { + "$ref": "#/components/schemas/TagResponseDto" + }, + "type": "array" + }, + "thumbhash": { + "description": "Thumbhash for thumbnail generation (base64) also used as the c query param for thumbnail cache busting.", + "nullable": true, + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/AssetTypeEnum" + }, + "updatedAt": { + "description": "The UTC timestamp when the asset record was last updated in the database. This is automatically maintained by the database and reflects when any field in the asset was last modified.", + "format": "date-time", + "type": "string" + }, + "visibility": { + "$ref": "#/components/schemas/AssetVisibility" + }, + "width": { + "description": "Asset width", + "maximum": 9007199254740991, + "minimum": 0, + "nullable": true, + "type": "integer" } }, "required": [ - "id" + "checksum", + "createdAt", + "duration", + "fileCreatedAt", + "fileModifiedAt", + "hasMetadata", + "height", + "id", + "isArchived", + "isEdited", + "isFavorite", + "isOffline", + "isTrashed", + "localDateTime", + "originalFileName", + "originalPath", + "ownerId", + "thumbhash", + "type", + "updatedAt", + "visibility", + "width" ], "type": "object" }, - "FacialRecognitionConfig": { + "AssetStackResponseDto": { "properties": { - "enabled": { - "description": "Whether the task is enabled", - "type": "boolean" - }, - "maxDistance": { - "description": "Maximum distance threshold for face recognition", - "format": "double", - "maximum": 2, - "minimum": 0.1, - "type": "number" - }, - "minFaces": { - "description": "Minimum number of faces required for recognition", + "assetCount": { + "description": "Number of assets in stack", "maximum": 9007199254740991, - "minimum": 1, + "minimum": 0, "type": "integer" }, - "minScore": { - "description": "Minimum confidence score for face detection", - "format": "double", - "maximum": 1, - "minimum": 0.1, - "type": "number" + "id": { + "description": "Stack ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" }, - "modelName": { - "description": "Name of the model to use", + "primaryAssetId": { + "description": "Primary asset ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" } }, "required": [ - "enabled", - "maxDistance", - "minFaces", - "minScore", - "modelName" + "assetCount", + "id", + "primaryAssetId" ], "type": "object" }, - "FoldersResponse": { + "AssetStatsResponseDto": { "properties": { - "enabled": { - "description": "Whether folders are enabled", - "type": "boolean" + "images": { + "description": "Number of images", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" }, - "sidebarWeb": { - "description": "Whether folders appear in web sidebar", - "type": "boolean" + "total": { + "description": "Total number of assets", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "videos": { + "description": "Number of videos", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" } }, "required": [ - "enabled", - "sidebarWeb" + "images", + "total", + "videos" ], "type": "object" }, - "FoldersUpdate": { - "properties": { - "enabled": { - "description": "Whether folders are enabled", - "type": "boolean" - }, - "sidebarWeb": { - "description": "Whether folders appear in web sidebar", + "AssetTypeEnum": { + "description": "Asset type", + "enum": [ + "IMAGE", + "VIDEO", + "AUDIO", + "OTHER" + ], + "type": "string" + }, + "AssetUploadAction": { + "description": "Upload action", + "enum": [ + "accept", + "reject" + ], + "type": "string" + }, + "AssetVisibility": { + "description": "Asset visibility", + "enum": [ + "archive", + "timeline", + "hidden", + "locked" + ], + "type": "string" + }, + "AudioCodec": { + "description": "Target audio codec", + "enum": [ + "mp3", + "aac", + "opus", + "pcm_s16le" + ], + "type": "string" + }, + "AuthStatusResponseDto": { + "properties": { + "expiresAt": { + "description": "Session expiration date", + "type": "string" + }, + "isElevated": { + "description": "Is elevated session", "type": "boolean" + }, + "password": { + "description": "Has password set", + "type": "boolean" + }, + "pinCode": { + "description": "Has PIN code set", + "type": "boolean" + }, + "pinExpiresAt": { + "description": "PIN expiration date", + "type": "string" + } + }, + "required": [ + "isElevated", + "password", + "pinCode" + ], + "type": "object" + }, + "AvatarUpdate": { + "properties": { + "color": { + "$ref": "#/components/schemas/UserAvatarColor" } }, "type": "object" }, - "HlsVideoResolution": { - "description": "HLS video resolution", + "BulkIdErrorReason": { + "description": "Error reason", "enum": [ - 480, - 720, - 1080, - 1440, - 2160 + "duplicate", + "no_permission", + "not_found", + "unknown", + "validation" ], - "type": "integer" + "type": "string" }, - "ImageFormat": { - "description": "Image format", - "enum": [ - "jpeg", - "webp" + "BulkIdResponseDto": { + "properties": { + "error": { + "$ref": "#/components/schemas/BulkIdErrorReason" + }, + "errorMessage": { + "type": "string" + }, + "id": { + "description": "ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "success": { + "description": "Whether operation succeeded", + "type": "boolean" + } + }, + "required": [ + "id", + "success" ], - "type": "string" + "type": "object" }, - "IntegrityReport": { - "description": "Integrity report type", + "BulkIdsDto": { + "properties": { + "ids": { + "description": "IDs to process", + "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "ids" + ], + "type": "object" + }, + "CQMode": { + "description": "CQ mode", "enum": [ - "untracked_file", - "missing_file", - "checksum_mismatch" + "auto", + "cqp", + "icq" ], "type": "string" }, - "IntegrityReportResponseDto": { + "CalendarHeatmapResponseDto": { "properties": { - "items": { + "from": { + "description": "Start date in UTC", + "example": "2024-01-01", + "type": "string" + }, + "series": { "items": { "properties": { - "id": { - "description": "Integrity report item id", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" + "count": { + "description": "Activity count", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" }, - "path": { - "description": "Integrity report item path", + "date": { + "description": "Date in UTC", + "example": "2024-01-01", "type": "string" - }, - "type": { - "$ref": "#/components/schemas/IntegrityReport" } }, "required": [ - "id", - "type", - "path" + "date", + "count" ], "type": "object" }, "type": "array" }, - "nextCursor": { + "to": { + "description": "End date in UTC", + "example": "2024-12-31", "type": "string" + }, + "totalCount": { + "description": "Total activity count over the period", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" } }, "required": [ - "items" + "from", + "series", + "to", + "totalCount" ], "type": "object" }, - "IntegrityReportSummaryResponseDto": { + "CalendarHeatmapType": { + "description": "Type of calendar heatmap", + "enum": [ + "Upload", + "Taken" + ], + "type": "string" + }, + "CastResponse": { "properties": { - "checksum_mismatch": { - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "missing_file": { - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "untracked_file": { - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" + "gCastEnabled": { + "description": "Whether Google Cast is enabled", + "type": "boolean" } }, "required": [ - "checksum_mismatch", - "missing_file", - "untracked_file" + "gCastEnabled" ], "type": "object" }, - "JobCreateDto": { + "CastUpdate": { "properties": { - "name": { - "$ref": "#/components/schemas/ManualJobName" + "gCastEnabled": { + "description": "Whether Google Cast is enabled", + "type": "boolean" + } + }, + "type": "object" + }, + "ChangePasswordDto": { + "properties": { + "invalidateSessions": { + "default": false, + "description": "Invalidate all other sessions", + "type": "boolean" + }, + "newPassword": { + "description": "New password (min 8 characters)", + "example": "password", + "minLength": 8, + "type": "string" + }, + "password": { + "description": "Current password", + "example": "password", + "type": "string" } }, "required": [ - "name" + "newPassword", + "password" ], "type": "object" }, - "JobName": { - "description": "Job name", + "Colorspace": { + "description": "Colorspace", "enum": [ - "AssetDelete", - "AssetDeleteCheck", - "AssetDetectFacesQueueAll", - "AssetDetectFaces", - "AssetDetectDuplicatesQueueAll", - "AssetDetectDuplicates", - "AssetEditThumbnailGeneration", - "AssetEncodeVideoQueueAll", - "AssetEncodeVideo", - "AssetEmptyTrash", - "AssetExtractMetadataQueueAll", - "AssetExtractMetadata", - "AssetFileMigration", - "AssetGenerateThumbnailsQueueAll", - "AssetGenerateThumbnails", - "AuditTableCleanup", - "DatabaseBackup", - "FacialRecognitionQueueAll", - "FacialRecognition", - "FileDelete", - "FileMigrationQueueAll", - "LibraryDeleteCheck", - "LibraryDelete", - "LibraryRemoveAsset", - "LibraryScanAssetsQueueAll", - "LibrarySyncAssets", - "LibrarySyncFilesQueueAll", - "LibrarySyncFiles", - "LibraryScanQueueAll", - "HlsSessionCleanup", - "MemoryCleanup", - "MemoryGenerate", - "NotificationsCleanup", - "NotifyUserSignup", - "NotifyAlbumInvite", - "NotifyAlbumUpdate", - "UserDelete", - "UserDeleteCheck", - "UserSyncUsage", - "PersonCleanup", - "PersonFileMigration", - "PersonGenerateThumbnail", - "SessionCleanup", - "SendMail", - "SidecarQueueAll", - "SidecarCheck", - "SidecarWrite", - "SmartSearchQueueAll", - "SmartSearch", - "StorageTemplateMigration", - "StorageTemplateMigrationSingle", - "TagCleanup", - "VersionCheck", - "OcrQueueAll", - "Ocr", - "WorkflowAssetTrigger", - "IntegrityUntrackedFilesQueueAll", - "IntegrityUntrackedFiles", - "IntegrityUntrackedRefresh", - "IntegrityMissingFilesQueueAll", - "IntegrityMissingFiles", - "IntegrityMissingFilesRefresh", - "IntegrityChecksumFiles", - "IntegrityChecksumFilesRefresh", - "IntegrityDeleteReportType", - "IntegrityDeleteReports" + "srgb", + "p3" ], "type": "string" }, - "JobSettingsDto": { + "ContributorCountResponseDto": { "properties": { - "concurrency": { - "description": "Concurrency", + "assetCount": { + "description": "Number of assets contributed", "maximum": 9007199254740991, - "minimum": 1, + "minimum": 0, "type": "integer" + }, + "userId": { + "description": "User ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" } }, "required": [ - "concurrency" + "assetCount", + "userId" ], "type": "object" }, - "LibraryResponseDto": { + "CreateAlbumDto": { "properties": { - "assetCount": { - "description": "Number of assets", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "createdAt": { - "description": "Creation date", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "albumName": { + "description": "Album name", "type": "string" }, - "exclusionPatterns": { - "description": "Exclusion patterns", + "albumUsers": { + "description": "Album users", + "items": { + "$ref": "#/components/schemas/AlbumUserCreateDto" + }, + "type": "array" + }, + "assetIds": { + "description": "Initial asset IDs", "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, "type": "array" }, - "id": { - "description": "Library ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" + "description": { + "description": "Album description", + "nullable": true, + "type": "string", + "x-immich-history": [ + { + "version": "v1", + "state": "Added" + }, + { + "version": "v3", + "state": "Updated", + "description": "Sending an empty string is deprecated; send null instead. Empty strings will no longer be coerced to null in v4." + } + ] + } + }, + "required": [ + "albumName" + ], + "type": "object" + }, + "CreateLibraryDto": { + "properties": { + "exclusionPatterns": { + "description": "Exclusion patterns (max 128)", + "items": { + "type": "string" + }, + "maxItems": 128, + "type": "array" }, "importPaths": { - "description": "Import paths", + "description": "Import paths (max 128)", "items": { "type": "string" }, + "maxItems": 128, "type": "array" }, "name": { "description": "Library name", + "minLength": 1, "type": "string" }, "ownerId": { @@ -19607,5672 +20243,5901 @@ "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" - }, - "refreshedAt": { - "description": "Last refresh date", + } + }, + "required": [ + "ownerId" + ], + "type": "object" + }, + "CreateProfileImageDto": { + "properties": { + "file": { + "description": "Profile image file", + "format": "binary", + "type": "string" + } + }, + "required": [ + "file" + ], + "type": "object" + }, + "CreateProfileImageResponseDto": { + "properties": { + "profileChangedAt": { + "description": "Profile image change date", "example": "2024-01-01T00:00:00.000Z", "format": "date-time", - "nullable": true, "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "updatedAt": { - "description": "Last update date", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "profileImagePath": { + "description": "Profile image file path", + "type": "string" + }, + "userId": { + "description": "User ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" } }, "required": [ - "assetCount", - "createdAt", - "exclusionPatterns", - "id", - "importPaths", - "name", - "ownerId", - "refreshedAt", - "updatedAt" + "profileChangedAt", + "profileImagePath", + "userId" ], "type": "object" }, - "LibraryStatsResponseDto": { + "CropParameters": { "properties": { - "photos": { - "description": "Number of photos", + "height": { + "description": "Height of the crop", "maximum": 9007199254740991, - "minimum": -9007199254740991, + "minimum": 1, "type": "integer" }, - "total": { - "description": "Total number of assets", + "width": { + "description": "Width of the crop", "maximum": 9007199254740991, - "minimum": -9007199254740991, + "minimum": 1, "type": "integer" }, - "usage": { - "description": "Storage usage in bytes", + "x": { + "description": "Top-Left X coordinate of crop", "maximum": 9007199254740991, - "minimum": -9007199254740991, + "minimum": 0, "type": "integer" }, - "videos": { - "description": "Number of videos", + "y": { + "description": "Top-Left Y coordinate of crop", "maximum": 9007199254740991, - "minimum": -9007199254740991, + "minimum": 0, "type": "integer" } }, "required": [ - "photos", - "total", - "usage", - "videos" + "height", + "width", + "x", + "y" ], "type": "object" }, - "LicenseKeyDto": { + "DatabaseBackupDeleteDto": { "properties": { - "activationKey": { - "description": "Activation key", - "type": "string" - }, - "licenseKey": { - "description": "License key (format: /^IM(SV|CL)(-[\\dA-Za-z]{4}){8}$/)", - "pattern": "^IM(SV|CL)(-[\\dA-Za-z]{4}){8}$", - "type": "string" - } - }, - "required": [ - "activationKey", - "licenseKey" + "backups": { + "description": "Backup filenames to delete", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "backups" ], "type": "object" }, - "LicenseResponseDto": { - "$ref": "#/components/schemas/UserLicense" - }, - "LogLevel": { - "description": "Log level", - "enum": [ - "verbose", - "debug", - "log", - "warn", - "error", - "fatal" - ], - "type": "string" - }, - "LoginCredentialDto": { + "DatabaseBackupDto": { "properties": { - "email": { - "description": "User email", - "example": "testuser@email.com", - "format": "email", - "pattern": "^[\\p{L}\\p{M}\\p{N}.!#$%&'*+/=?^_`{|}~-]+@[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?(?:\\.[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?)*$", + "filename": { + "description": "Backup filename", "type": "string" }, - "password": { - "description": "User password", - "example": "password", + "filesize": { + "description": "Backup file size", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "timezone": { + "description": "Backup timezone", "type": "string" } }, "required": [ - "email", - "password" + "filename", + "filesize", + "timezone" ], "type": "object" }, - "LoginResponseDto": { + "DatabaseBackupListResponseDto": { "properties": { - "accessToken": { - "description": "Access token", - "type": "string" - }, - "isAdmin": { - "description": "Is admin user", - "type": "boolean" - }, - "isOnboarded": { - "description": "Is onboarded", - "type": "boolean" - }, - "name": { - "description": "User name", - "type": "string" - }, - "profileImagePath": { - "description": "Profile image path", - "type": "string" - }, - "shouldChangePassword": { - "description": "Should change password", - "type": "boolean" - }, - "userEmail": { - "description": "User email", - "format": "email", - "pattern": "^[\\p{L}\\p{M}\\p{N}.!#$%&'*+/=?^_`{|}~-]+@[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?(?:\\.[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?)*$", - "type": "string" - }, - "userId": { - "description": "User ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" + "backups": { + "description": "List of backups", + "items": { + "$ref": "#/components/schemas/DatabaseBackupDto" + }, + "type": "array" } }, "required": [ - "accessToken", - "isAdmin", - "isOnboarded", - "name", - "profileImagePath", - "shouldChangePassword", - "userEmail", - "userId" + "backups" ], "type": "object" }, - "LogoutResponseDto": { + "DatabaseBackupUploadDto": { "properties": { - "redirectUri": { - "description": "Redirect URI", + "file": { + "description": "Database backup file", + "format": "binary", "type": "string" + } + }, + "type": "object" + }, + "DownloadArchiveDto": { + "properties": { + "assetIds": { + "description": "Asset IDs", + "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "type": "array" }, - "successful": { - "description": "Logout successful", + "edited": { + "description": "Download edited asset if available", "type": "boolean" } }, "required": [ - "redirectUri", - "successful" + "assetIds" ], "type": "object" }, - "MachineLearningAvailabilityChecksDto": { + "DownloadArchiveInfo": { "properties": { - "enabled": { - "description": "Enabled", - "type": "boolean" - }, - "interval": { - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "assetIds": { + "description": "Asset IDs in this archive", + "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "type": "array" }, - "timeout": { + "size": { + "description": "Archive size in bytes", "maximum": 9007199254740991, "minimum": -9007199254740991, "type": "integer" } }, "required": [ - "enabled", - "interval", - "timeout" + "assetIds", + "size" ], "type": "object" }, - "MaintenanceAction": { - "description": "Maintenance action", - "enum": [ - "start", - "end", - "select_database_restore", - "restore_database" - ], - "type": "string" - }, - "MaintenanceAuthDto": { + "DownloadInfoDto": { "properties": { - "username": { - "description": "Maintenance username", + "albumId": { + "description": "Album ID to download", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "archiveSize": { + "description": "Archive size limit in bytes", + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + }, + "assetIds": { + "description": "Asset IDs to download", + "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "type": "array" + }, + "userId": { + "description": "User ID to download assets from", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" } }, + "type": "object" + }, + "DownloadResponse": { + "properties": { + "archiveSize": { + "description": "Maximum archive size in bytes", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "includeEmbeddedVideos": { + "description": "Whether to include embedded videos in downloads", + "type": "boolean" + } + }, "required": [ - "username" + "archiveSize", + "includeEmbeddedVideos" ], "type": "object" }, - "MaintenanceDetectInstallResponseDto": { + "DownloadResponseDto": { "properties": { - "storage": { + "archives": { + "description": "Archive information", "items": { - "$ref": "#/components/schemas/MaintenanceDetectInstallStorageFolderDto" + "$ref": "#/components/schemas/DownloadArchiveInfo" }, "type": "array" + }, + "totalSize": { + "description": "Total size in bytes", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" } }, "required": [ - "storage" + "archives", + "totalSize" ], "type": "object" }, - "MaintenanceDetectInstallStorageFolderDto": { + "DownloadUpdate": { "properties": { - "files": { - "description": "Number of files in the folder", + "archiveSize": { + "description": "Maximum archive size in bytes", "maximum": 9007199254740991, - "minimum": -9007199254740991, + "minimum": 1, "type": "integer" }, - "folder": { - "$ref": "#/components/schemas/StorageFolder" - }, - "readable": { - "description": "Whether the folder is readable", - "type": "boolean" - }, - "writable": { - "description": "Whether the folder is writable", + "includeEmbeddedVideos": { + "description": "Whether to include embedded videos in downloads", "type": "boolean" } }, - "required": [ - "files", - "folder", - "readable", - "writable" - ], "type": "object" }, - "MaintenanceLoginDto": { + "DuplicateResolveDto": { "properties": { - "token": { - "description": "Maintenance token", - "type": "string" - } - }, - "type": "object" - }, - "MaintenanceStatusResponseDto": { - "properties": { - "action": { - "$ref": "#/components/schemas/MaintenanceAction" - }, - "active": { - "type": "boolean" - }, - "error": { - "type": "string" - }, - "progress": { - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "task": { - "type": "string" + "groups": { + "description": "List of duplicate groups to resolve", + "items": { + "$ref": "#/components/schemas/DuplicateResolveGroupDto" + }, + "minItems": 1, + "type": "array" } }, "required": [ - "action", - "active" + "groups" ], "type": "object" }, - "ManualJobName": { - "description": "Manual job name", - "enum": [ - "person-cleanup", - "tag-cleanup", - "user-cleanup", - "memory-cleanup", - "memory-create", - "backup-database", - "integrity-missing-files", - "integrity-untracked-files", - "integrity-checksum-mismatch", - "integrity-missing-files-refresh", - "integrity-untracked-files-refresh", - "integrity-checksum-mismatch-refresh", - "integrity-missing-files-delete-all", - "integrity-untracked-files-delete-all", - "integrity-checksum-mismatch-delete-all" - ], - "type": "string" - }, - "MapMarkerResponseDto": { + "DuplicateResolveGroupDto": { "properties": { - "city": { - "description": "City name", - "nullable": true, - "type": "string" - }, - "country": { - "description": "Country name", - "nullable": true, - "type": "string" - }, - "id": { - "description": "Asset ID", + "duplicateId": { "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "lat": { - "description": "Latitude", - "format": "double", - "type": "number" - }, - "lon": { - "description": "Longitude", - "format": "double", - "type": "number" + "keepAssetIds": { + "description": "Asset IDs to keep", + "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "type": "array" }, - "state": { - "description": "State/Province name", - "nullable": true, - "type": "string" + "trashAssetIds": { + "description": "Asset IDs to trash or delete", + "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "type": "array" } }, "required": [ - "city", - "country", - "id", - "lat", - "lon", - "state" + "duplicateId", + "keepAssetIds", + "trashAssetIds" ], "type": "object" }, - "MapReverseGeocodeResponseDto": { + "DuplicateResponseDto": { "properties": { - "city": { - "description": "City name", - "nullable": true, - "type": "string" + "assets": { + "description": "Duplicate assets", + "items": { + "$ref": "#/components/schemas/AssetResponseDto" + }, + "type": "array" }, - "country": { - "description": "Country name", - "nullable": true, + "duplicateId": { + "description": "Duplicate group ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "state": { - "description": "State/Province name", - "nullable": true, - "type": "string" + "suggestedKeepAssetIds": { + "description": "Suggested asset IDs to keep based on file size and EXIF data", + "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "type": "array" } }, "required": [ - "city", - "country", - "state" + "assets", + "duplicateId", + "suggestedKeepAssetIds" ], "type": "object" }, - "MemoriesResponse": { + "EmailNotificationsResponse": { "properties": { - "duration": { - "description": "Memory duration in seconds", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "albumInvite": { + "description": "Whether to receive email notifications for album invites", + "type": "boolean" + }, + "albumUpdate": { + "description": "Whether to receive email notifications for album updates", + "type": "boolean" }, "enabled": { - "description": "Whether memories are enabled", + "description": "Whether email notifications are enabled", "type": "boolean" } }, "required": [ - "duration", + "albumInvite", + "albumUpdate", "enabled" ], "type": "object" }, - "MemoriesUpdate": { + "EmailNotificationsUpdate": { "properties": { - "duration": { - "description": "Memory duration in seconds", - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" + "albumInvite": { + "description": "Whether to receive email notifications for album invites", + "type": "boolean" + }, + "albumUpdate": { + "description": "Whether to receive email notifications for album updates", + "type": "boolean" }, "enabled": { - "description": "Whether memories are enabled", + "description": "Whether email notifications are enabled", "type": "boolean" } }, "type": "object" }, - "MemoryCreateDto": { + "ExifResponseDto": { + "description": "EXIF response", "properties": { - "assetIds": { - "description": "Asset IDs to associate with memory", - "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "type": "array" - }, - "data": { - "$ref": "#/components/schemas/OnThisDayDto" - }, - "hideAt": { - "description": "Date when memory should be hidden", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string", - "x-immich-history": [ - { - "version": "v2.6.0", - "state": "Added" - }, - { - "version": "v2.6.0", - "state": "Stable" - } - ], - "x-immich-state": "Stable" + "city": { + "default": null, + "description": "City name", + "nullable": true, + "type": "string" }, - "isSaved": { - "description": "Is memory saved", - "type": "boolean" + "country": { + "default": null, + "description": "Country name", + "nullable": true, + "type": "string" }, - "memoryAt": { - "description": "Memory date", - "example": "2024-01-01T00:00:00.000Z", + "dateTimeOriginal": { + "default": null, + "description": "Original date/time", "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "nullable": true, "type": "string" }, - "seenAt": { - "description": "Date when memory was seen", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "description": { + "default": null, + "description": "Image description", + "nullable": true, "type": "string" }, - "showAt": { - "description": "Date when memory should be shown", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string", - "x-immich-history": [ - { - "version": "v2.6.0", - "state": "Added" - }, - { - "version": "v2.6.0", - "state": "Stable" - } - ], - "x-immich-state": "Stable" + "exifImageHeight": { + "default": null, + "description": "Image height in pixels", + "maximum": 9007199254740991, + "minimum": 0, + "nullable": true, + "type": "integer" }, - "type": { - "$ref": "#/components/schemas/MemoryType" - } - }, - "required": [ - "data", - "memoryAt", - "type" - ], - "type": "object" - }, - "MemoryResponseDto": { - "properties": { - "assets": { - "items": { - "$ref": "#/components/schemas/AssetResponseDto" - }, - "type": "array" + "exifImageWidth": { + "default": null, + "description": "Image width in pixels", + "maximum": 9007199254740991, + "minimum": 0, + "nullable": true, + "type": "integer" }, - "createdAt": { - "description": "Creation date", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "exposureTime": { + "default": null, + "description": "Exposure time", + "nullable": true, "type": "string" }, - "data": { - "$ref": "#/components/schemas/OnThisDayDto" + "fNumber": { + "default": null, + "description": "F-number (aperture)", + "nullable": true, + "type": "number" }, - "deletedAt": { - "description": "Deletion date", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" + "fileSizeInByte": { + "default": null, + "description": "File size in bytes", + "maximum": 9007199254740991, + "minimum": 0, + "nullable": true, + "type": "integer" }, - "hideAt": { - "description": "Date when memory should be hidden", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" + "focalLength": { + "default": null, + "description": "Focal length in mm", + "nullable": true, + "type": "number" }, - "id": { - "description": "Memory ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "iso": { + "default": null, + "description": "ISO sensitivity", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "nullable": true, + "type": "integer" + }, + "latitude": { + "default": null, + "description": "GPS latitude", + "nullable": true, + "type": "number" + }, + "lensModel": { + "default": null, + "description": "Lens model", + "nullable": true, "type": "string" }, - "isSaved": { - "description": "Is memory saved", - "type": "boolean" + "longitude": { + "default": null, + "description": "GPS longitude", + "nullable": true, + "type": "number" }, - "memoryAt": { - "description": "Memory date", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "make": { + "default": null, + "description": "Camera make", + "nullable": true, "type": "string" }, - "ownerId": { - "description": "Owner user ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "model": { + "default": null, + "description": "Camera model", + "nullable": true, "type": "string" }, - "seenAt": { - "description": "Date when memory was seen", - "example": "2024-01-01T00:00:00.000Z", + "modifyDate": { + "default": null, + "description": "Modification date/time", "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "nullable": true, "type": "string" }, - "showAt": { - "description": "Date when memory should be shown", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "orientation": { + "default": null, + "description": "Image orientation", + "nullable": true, "type": "string" }, - "type": { - "$ref": "#/components/schemas/MemoryType" + "projectionType": { + "default": null, + "description": "Projection type", + "nullable": true, + "type": "string" }, - "updatedAt": { - "description": "Last update date", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "rating": { + "default": null, + "description": "Rating", + "maximum": 5, + "minimum": 1, + "nullable": true, + "type": "integer" + }, + "state": { + "default": null, + "description": "State/province name", + "nullable": true, + "type": "string" + }, + "timeZone": { + "default": null, + "description": "Time zone", + "nullable": true, "type": "string" } }, - "required": [ - "assets", - "createdAt", - "data", - "id", - "isSaved", - "memoryAt", - "ownerId", - "type", - "updatedAt" - ], "type": "object" }, - "MemorySearchOrder": { - "description": "Sort order", - "enum": [ - "asc", - "desc", - "random" - ], - "type": "string" - }, - "MemoryStatisticsResponseDto": { + "FaceDto": { "properties": { - "total": { - "description": "Total number of memories", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "id": { + "description": "Face ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" } }, "required": [ - "total" + "id" ], "type": "object" }, - "MemoryType": { - "description": "Memory type", - "enum": [ - "on_this_day" - ], - "type": "string" - }, - "MemoryUpdateDto": { + "FoldersResponse": { "properties": { - "isSaved": { - "description": "Is memory saved", + "enabled": { + "description": "Whether folders are enabled", "type": "boolean" }, - "memoryAt": { - "description": "Memory date", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" - }, - "seenAt": { - "description": "Date when memory was seen", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" + "sidebarWeb": { + "description": "Whether folders appear in web sidebar", + "type": "boolean" } }, + "required": [ + "enabled", + "sidebarWeb" + ], "type": "object" }, - "MergePersonDto": { + "FoldersUpdate": { "properties": { - "ids": { - "description": "Person IDs to merge", - "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "type": "array" + "enabled": { + "description": "Whether folders are enabled", + "type": "boolean" + }, + "sidebarWeb": { + "description": "Whether folders appear in web sidebar", + "type": "boolean" } }, - "required": [ - "ids" - ], "type": "object" }, - "MetadataSearchDto": { + "HlsVideoResolution": { + "description": "HLS video resolution", + "enum": [ + 480, + 720, + 1080, + 1440, + 2160 + ], + "type": "integer" + }, + "ImageFormat": { + "description": "Image format", + "enum": [ + "jpeg", + "webp" + ], + "type": "string" + }, + "IntegrityReport": { + "description": "Integrity report type", + "enum": [ + "untracked_file", + "missing_file", + "checksum_mismatch" + ], + "type": "string" + }, + "IntegrityReportResponseDto": { "properties": { - "albumIds": { - "description": "Filter by album IDs", + "items": { "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" + "properties": { + "id": { + "description": "Integrity report item id", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "path": { + "description": "Integrity report item path", + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/IntegrityReport" + } + }, + "required": [ + "id", + "type", + "path" + ], + "type": "object" }, "type": "array" }, - "checksum": { - "description": "Filter by file checksum", - "type": "string" - }, - "city": { - "description": "Filter by city name", - "nullable": true, + "nextCursor": { "type": "string" + } + }, + "required": [ + "items" + ], + "type": "object" + }, + "IntegrityReportSummaryResponseDto": { + "properties": { + "checksum_mismatch": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" }, - "country": { - "description": "Filter by country name", - "nullable": true, - "type": "string" + "missing_file": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" }, - "createdAfter": { - "description": "Filter by creation date (after)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" + "untracked_file": { + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "checksum_mismatch", + "missing_file", + "untracked_file" + ], + "type": "object" + }, + "JobCreateDto": { + "properties": { + "name": { + "$ref": "#/components/schemas/ManualJobName" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "JobName": { + "description": "Job name", + "enum": [ + "AssetDelete", + "AssetDeleteCheck", + "AssetDetectFacesQueueAll", + "AssetDetectFaces", + "AssetDetectDuplicatesQueueAll", + "AssetDetectDuplicates", + "AssetEditThumbnailGeneration", + "AssetEncodeVideoQueueAll", + "AssetEncodeVideo", + "AssetEmptyTrash", + "AssetExtractMetadataQueueAll", + "AssetExtractMetadata", + "AssetFileMigration", + "AssetGenerateThumbnailsQueueAll", + "AssetGenerateThumbnails", + "AuditTableCleanup", + "DatabaseBackup", + "FacialRecognitionQueueAll", + "FacialRecognition", + "FileDelete", + "FileMigrationQueueAll", + "LibraryDeleteCheck", + "LibraryDelete", + "LibraryRemoveAsset", + "LibraryScanAssetsQueueAll", + "LibrarySyncAssets", + "LibrarySyncFilesQueueAll", + "LibrarySyncFiles", + "LibraryScanQueueAll", + "HlsSessionCleanup", + "MemoryCleanup", + "MemoryGenerate", + "NotificationsCleanup", + "NotifyUserSignup", + "NotifyAlbumInvite", + "NotifyAlbumUpdate", + "UserDelete", + "UserDeleteCheck", + "UserSyncUsage", + "PersonCleanup", + "PersonFileMigration", + "PersonGenerateThumbnail", + "SessionCleanup", + "SendMail", + "SidecarQueueAll", + "SidecarCheck", + "SidecarWrite", + "SmartSearchQueueAll", + "SmartSearch", + "StorageTemplateMigration", + "StorageTemplateMigrationSingle", + "TagCleanup", + "VersionCheck", + "OcrQueueAll", + "Ocr", + "WorkflowAssetTrigger", + "IntegrityUntrackedFilesQueueAll", + "IntegrityUntrackedFiles", + "IntegrityUntrackedRefresh", + "IntegrityMissingFilesQueueAll", + "IntegrityMissingFiles", + "IntegrityMissingFilesRefresh", + "IntegrityChecksumFiles", + "IntegrityChecksumFilesRefresh", + "IntegrityDeleteReportType", + "IntegrityDeleteReports" + ], + "type": "string" + }, + "LibraryResponseDto": { + "properties": { + "assetCount": { + "description": "Number of assets", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" }, - "createdBefore": { - "description": "Filter by creation date (before)", + "createdAt": { + "description": "Creation date", "example": "2024-01-01T00:00:00.000Z", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "description": { - "description": "Filter by description text", - "type": "string" - }, - "encodedVideoPath": { - "description": "Filter by encoded video file path", - "type": "string" + "exclusionPatterns": { + "description": "Exclusion patterns", + "items": { + "type": "string" + }, + "type": "array" }, "id": { - "description": "Filter by asset ID", + "description": "Library ID", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "isEncoded": { - "description": "Filter by encoded status", - "type": "boolean" - }, - "isFavorite": { - "description": "Filter by favorite status", - "type": "boolean" - }, - "isMotion": { - "description": "Filter by motion photo status", - "type": "boolean" - }, - "isNotInAlbum": { - "description": "Filter assets not in any album", - "type": "boolean" - }, - "isOffline": { - "description": "Filter by offline status", - "type": "boolean" + "importPaths": { + "description": "Import paths", + "items": { + "type": "string" + }, + "type": "array" }, - "lensModel": { - "description": "Filter by lens model", - "nullable": true, + "name": { + "description": "Library name", "type": "string" }, - "libraryId": { - "description": "Library ID to filter by", + "ownerId": { + "description": "Owner user ID", "format": "uuid", - "nullable": true, "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "make": { - "description": "Filter by camera make", + "refreshedAt": { + "description": "Last refresh date", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", "nullable": true, + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "model": { - "description": "Filter by camera model", - "nullable": true, - "type": "string" - }, - "ocr": { - "description": "Filter by OCR text content", - "type": "string" - }, - "order": { - "$ref": "#/components/schemas/AssetOrder", - "default": "desc", - "description": "Sort order" - }, - "originalFileName": { - "description": "Filter by original file name", - "type": "string" - }, - "originalPath": { - "description": "Filter by original file path", - "type": "string" - }, - "page": { - "description": "Page number", - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" - }, - "personIds": { - "description": "Filter by person IDs", - "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "type": "array" - }, - "previewPath": { - "description": "Filter by preview file path", - "type": "string" - }, - "rating": { - "description": "Filter by rating [1-5], or null for unrated", - "maximum": 5, - "minimum": 1, - "nullable": true, - "type": "integer", - "x-immich-history": [ - { - "version": "v1", - "state": "Added" - }, - { - "version": "v2", - "state": "Stable" - }, - { - "version": "v2.6.0", - "state": "Updated", - "description": "Using -1 as a rating is deprecated and will be removed in the next major version." - }, - { - "version": "v3", - "state": "Updated", - "description": "Using -1 as a rating is no longer valid." - } - ], - "x-immich-state": "Stable" - }, - "size": { - "description": "Number of results to return", - "maximum": 1000, - "minimum": 1, - "type": "integer" - }, - "state": { - "description": "Filter by state/province name", - "nullable": true, - "type": "string" - }, - "tagIds": { - "description": "Filter by tag IDs", - "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "nullable": true, - "type": "array" - }, - "takenAfter": { - "description": "Filter by taken date (after)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" - }, - "takenBefore": { - "description": "Filter by taken date (before)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" - }, - "thumbnailPath": { - "description": "Filter by thumbnail file path", - "type": "string" - }, - "trashedAfter": { - "description": "Filter by trash date (after)", + "updatedAt": { + "description": "Last update date", "example": "2024-01-01T00:00:00.000Z", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" + } + }, + "required": [ + "assetCount", + "createdAt", + "exclusionPatterns", + "id", + "importPaths", + "name", + "ownerId", + "refreshedAt", + "updatedAt" + ], + "type": "object" + }, + "LibraryStatsResponseDto": { + "properties": { + "photos": { + "description": "Number of photos", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" }, - "trashedBefore": { - "description": "Filter by trash date (before)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" + "total": { + "description": "Total number of assets", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" }, - "type": { - "$ref": "#/components/schemas/AssetTypeEnum" + "usage": { + "description": "Storage usage in bytes", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" }, - "updatedAfter": { - "description": "Filter by update date (after)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "videos": { + "description": "Number of videos", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "photos", + "total", + "usage", + "videos" + ], + "type": "object" + }, + "LicenseKeyDto": { + "properties": { + "activationKey": { + "description": "Activation key", "type": "string" }, - "updatedBefore": { - "description": "Filter by update date (before)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "licenseKey": { + "description": "License key (format: /^IM(SV|CL)(-[\\dA-Za-z]{4}){8}$/)", + "pattern": "^IM(SV|CL)(-[\\dA-Za-z]{4}){8}$", "type": "string" - }, - "visibility": { - "$ref": "#/components/schemas/AssetVisibility" - }, - "withDeleted": { - "description": "Include deleted assets", - "type": "boolean" - }, - "withExif": { - "description": "Include EXIF data in response", - "type": "boolean" - }, - "withPeople": { - "description": "Include people data in response", - "type": "boolean" - }, - "withStacked": { - "description": "Include stacked assets", - "type": "boolean" } }, + "required": [ + "activationKey", + "licenseKey" + ], "type": "object" }, - "MirrorAxis": { - "description": "Axis to mirror along", + "LicenseResponseDto": { + "$ref": "#/components/schemas/UserLicense" + }, + "LogLevel": { + "description": "Log level", "enum": [ - "horizontal", - "vertical" + "verbose", + "debug", + "log", + "warn", + "error", + "fatal" ], "type": "string" }, - "MirrorParameters": { + "LoginCredentialDto": { "properties": { - "axis": { - "$ref": "#/components/schemas/MirrorAxis" + "email": { + "description": "User email", + "example": "testuser@email.com", + "format": "email", + "pattern": "^[\\p{L}\\p{M}\\p{N}.!#$%&'*+/=?^_`{|}~-]+@[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?(?:\\.[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?)*$", + "type": "string" + }, + "password": { + "description": "User password", + "example": "password", + "type": "string" } }, "required": [ - "axis" + "email", + "password" ], "type": "object" }, - "NotificationCreateDto": { + "LoginResponseDto": { "properties": { - "data": { - "additionalProperties": {}, - "description": "Additional notification data", - "type": "object" - }, - "description": { - "description": "Notification description", - "nullable": true, + "accessToken": { + "description": "Access token", "type": "string" }, - "level": { - "$ref": "#/components/schemas/NotificationLevel" - }, - "readAt": { - "description": "Date when notification was read", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "nullable": true, - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "isAdmin": { + "description": "Is admin user", + "type": "boolean" + }, + "isOnboarded": { + "description": "Is onboarded", + "type": "boolean" + }, + "name": { + "description": "User name", "type": "string" }, - "title": { - "description": "Notification title", + "profileImagePath": { + "description": "Profile image path", "type": "string" }, - "type": { - "$ref": "#/components/schemas/NotificationType" + "shouldChangePassword": { + "description": "Should change password", + "type": "boolean" + }, + "userEmail": { + "description": "User email", + "format": "email", + "pattern": "^[\\p{L}\\p{M}\\p{N}.!#$%&'*+/=?^_`{|}~-]+@[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?(?:\\.[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?)*$", + "type": "string" }, "userId": { - "description": "User ID to send notification to", + "description": "User ID", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" } }, "required": [ - "title", + "accessToken", + "isAdmin", + "isOnboarded", + "name", + "profileImagePath", + "shouldChangePassword", + "userEmail", "userId" ], "type": "object" }, - "NotificationDeleteAllDto": { - "properties": { - "ids": { - "description": "Notification IDs to delete", - "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "minItems": 1, - "type": "array" - } - }, - "required": [ - "ids" - ], - "type": "object" - }, - "NotificationDto": { + "LogoutResponseDto": { "properties": { - "createdAt": { - "description": "Creation date", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" - }, - "data": { - "additionalProperties": {}, - "description": "Additional notification data", - "type": "object" - }, - "description": { - "description": "Notification description", - "type": "string" - }, - "id": { - "description": "Notification ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "level": { - "$ref": "#/components/schemas/NotificationLevel" - }, - "readAt": { - "description": "Date when notification was read", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" - }, - "title": { - "description": "Notification title", + "redirectUri": { + "description": "Redirect URI", "type": "string" }, - "type": { - "$ref": "#/components/schemas/NotificationType" + "successful": { + "description": "Logout successful", + "type": "boolean" } }, "required": [ - "createdAt", - "id", - "level", - "title", - "type" + "redirectUri", + "successful" ], "type": "object" }, - "NotificationLevel": { - "description": "Notification level", - "enum": [ - "success", - "error", - "warning", - "info" - ], - "type": "string" - }, - "NotificationType": { - "description": "Notification type", + "MaintenanceAction": { + "description": "Maintenance action", "enum": [ - "JobFailed", - "BackupFailed", - "SystemMessage", - "AlbumInvite", - "AlbumUpdate", - "Custom" + "start", + "end", + "select_database_restore", + "restore_database" ], "type": "string" }, - "NotificationUpdateAllDto": { + "MaintenanceAuthDto": { "properties": { - "ids": { - "description": "Notification IDs to update", - "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "minItems": 1, - "type": "array" - }, - "readAt": { - "description": "Date when notifications were read", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "nullable": true, - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "username": { + "description": "Maintenance username", "type": "string" } }, "required": [ - "ids" + "username" ], "type": "object" }, - "NotificationUpdateDto": { - "properties": { - "readAt": { - "description": "Date when notification was read", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "nullable": true, - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" - } - }, - "type": "object" - }, - "OAuthAuthorizeResponseDto": { + "MaintenanceDetectInstallResponseDto": { "properties": { - "url": { - "description": "OAuth authorization URL", - "type": "string" + "storage": { + "items": { + "$ref": "#/components/schemas/MaintenanceDetectInstallStorageFolderDto" + }, + "type": "array" } }, "required": [ - "url" + "storage" ], "type": "object" }, - "OAuthBackchannelLogoutDto": { + "MaintenanceDetectInstallStorageFolderDto": { "properties": { - "logout_token": { - "description": "OAuth logout token", - "type": "string" + "files": { + "description": "Number of files in the folder", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "folder": { + "$ref": "#/components/schemas/StorageFolder" + }, + "readable": { + "description": "Whether the folder is readable", + "type": "boolean" + }, + "writable": { + "description": "Whether the folder is writable", + "type": "boolean" } }, "required": [ - "logout_token" + "files", + "folder", + "readable", + "writable" ], "type": "object" }, - "OAuthCallbackDto": { + "MaintenanceLoginDto": { "properties": { - "codeVerifier": { - "description": "OAuth code verifier (PKCE)", - "type": "string" - }, - "state": { - "description": "OAuth state parameter", - "type": "string" - }, - "url": { - "description": "OAuth callback URL", - "minLength": 1, + "token": { + "description": "Maintenance token", "type": "string" } }, - "required": [ - "url" - ], "type": "object" }, - "OAuthConfigDto": { + "MaintenanceStatusResponseDto": { "properties": { - "codeChallenge": { - "description": "OAuth code challenge (PKCE)", - "type": "string" + "action": { + "$ref": "#/components/schemas/MaintenanceAction" }, - "redirectUri": { - "description": "OAuth redirect URI", + "active": { + "type": "boolean" + }, + "error": { "type": "string" }, - "state": { - "description": "OAuth state parameter", + "progress": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "task": { "type": "string" } }, "required": [ - "redirectUri" + "action", + "active" ], "type": "object" }, - "OAuthTokenEndpointAuthMethod": { - "description": "OAuth token endpoint auth method", + "ManualJobName": { + "description": "Manual job name", "enum": [ - "client_secret_post", - "client_secret_basic" - ], - "type": "string" - }, - "OcrConfig": { + "person-cleanup", + "tag-cleanup", + "user-cleanup", + "memory-cleanup", + "memory-create", + "backup-database", + "integrity-missing-files", + "integrity-untracked-files", + "integrity-checksum-mismatch", + "integrity-missing-files-refresh", + "integrity-untracked-files-refresh", + "integrity-checksum-mismatch-refresh", + "integrity-missing-files-delete-all", + "integrity-untracked-files-delete-all", + "integrity-checksum-mismatch-delete-all" + ], + "type": "string" + }, + "MapMarkerResponseDto": { "properties": { - "enabled": { - "description": "Whether the task is enabled", - "type": "boolean" + "city": { + "description": "City name", + "nullable": true, + "type": "string" }, - "maxResolution": { - "description": "Maximum resolution for OCR processing", - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" + "country": { + "description": "Country name", + "nullable": true, + "type": "string" }, - "minDetectionScore": { - "description": "Minimum confidence score for text detection", + "id": { + "description": "Asset ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "lat": { + "description": "Latitude", "format": "double", - "maximum": 1, - "minimum": 0.1, "type": "number" }, - "minRecognitionScore": { - "description": "Minimum confidence score for text recognition", + "lon": { + "description": "Longitude", "format": "double", - "maximum": 1, - "minimum": 0.1, "type": "number" }, - "modelName": { - "description": "Name of the model to use", - "type": "string" - } - }, - "required": [ - "enabled", - "maxResolution", - "minDetectionScore", - "minRecognitionScore", - "modelName" - ], - "type": "object" - }, - "OnThisDayDto": { - "properties": { - "year": { - "description": "Year for on this day memory", - "maximum": 9999, - "minimum": 1000, - "type": "integer" - } - }, - "required": [ - "year" - ], - "type": "object" - }, - "OnboardingDto": { - "properties": { - "isOnboarded": { - "description": "Is user onboarded", - "type": "boolean" - } - }, - "required": [ - "isOnboarded" - ], - "type": "object" - }, - "OnboardingResponseDto": { - "properties": { - "isOnboarded": { - "description": "Is user onboarded", - "type": "boolean" - } - }, - "required": [ - "isOnboarded" - ], - "type": "object" - }, - "PartnerCreateDto": { - "properties": { - "sharedWithId": { - "description": "User ID to share with", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "state": { + "description": "State/Province name", + "nullable": true, "type": "string" } }, "required": [ - "sharedWithId" + "city", + "country", + "id", + "lat", + "lon", + "state" ], "type": "object" }, - "PartnerDirection": { - "description": "Partner direction", - "enum": [ - "shared-by", - "shared-with" - ], - "type": "string" - }, - "PartnerResponseDto": { - "description": "Partner response", + "MapReverseGeocodeResponseDto": { "properties": { - "avatarColor": { - "$ref": "#/components/schemas/UserAvatarColor" - }, - "email": { - "description": "User email", - "format": "email", - "pattern": "^[\\p{L}\\p{M}\\p{N}.!#$%&'*+/=?^_`{|}~-]+@[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?(?:\\.[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?)*$", - "type": "string" - }, - "id": { - "description": "User ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "inTimeline": { - "description": "Show in timeline", - "type": "boolean" - }, - "name": { - "description": "User name", + "city": { + "description": "City name", + "nullable": true, "type": "string" }, - "profileChangedAt": { - "description": "Profile change date", - "format": "date-time", + "country": { + "description": "Country name", + "nullable": true, "type": "string" }, - "profileImagePath": { - "description": "Profile image path", + "state": { + "description": "State/Province name", + "nullable": true, "type": "string" } }, "required": [ - "avatarColor", - "email", - "id", - "name", - "profileChangedAt", - "profileImagePath" + "city", + "country", + "state" ], "type": "object" }, - "PartnerUpdateDto": { + "MemoriesResponse": { "properties": { - "inTimeline": { - "description": "Show partner assets in timeline", + "duration": { + "description": "Memory duration in seconds", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "enabled": { + "description": "Whether memories are enabled", "type": "boolean" } }, "required": [ - "inTimeline" + "duration", + "enabled" ], "type": "object" }, - "PeopleResponse": { + "MemoriesUpdate": { "properties": { - "enabled": { - "description": "Whether people are enabled", - "type": "boolean" - }, - "minimumFaces": { - "description": "People face threshold", + "duration": { + "description": "Memory duration in seconds", "maximum": 9007199254740991, "minimum": 1, "type": "integer" }, - "sidebarWeb": { - "description": "Whether people appear in web sidebar", + "enabled": { + "description": "Whether memories are enabled", "type": "boolean" } }, - "required": [ - "enabled", - "sidebarWeb" - ], "type": "object" }, - "PeopleResponseDto": { - "description": "People response", + "MemoryCreateDto": { "properties": { - "hasNextPage": { - "description": "Whether there are more pages", - "type": "boolean", + "assetIds": { + "description": "Asset IDs to associate with memory", + "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "type": "array" + }, + "data": { + "$ref": "#/components/schemas/OnThisDayDto" + }, + "hideAt": { + "description": "Date when memory should be hidden", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string", "x-immich-history": [ { - "version": "v1.110.0", + "version": "v2.6.0", "state": "Added" }, { - "version": "v2", + "version": "v2.6.0", "state": "Stable" } ], "x-immich-state": "Stable" }, - "hidden": { - "description": "Number of hidden people", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" + "isSaved": { + "description": "Is memory saved", + "type": "boolean" }, - "people": { - "items": { - "$ref": "#/components/schemas/PersonResponseDto" - }, - "type": "array" + "memoryAt": { + "description": "Memory date", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" }, + "seenAt": { + "description": "Date when memory was seen", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "showAt": { + "description": "Date when memory should be shown", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string", + "x-immich-history": [ + { + "version": "v2.6.0", + "state": "Added" + }, + { + "version": "v2.6.0", + "state": "Stable" + } + ], + "x-immich-state": "Stable" + }, + "type": { + "$ref": "#/components/schemas/MemoryType" + } + }, + "required": [ + "data", + "memoryAt", + "type" + ], + "type": "object" + }, + "MemoryResponseDto": { + "properties": { + "assets": { + "items": { + "$ref": "#/components/schemas/AssetResponseDto" + }, + "type": "array" + }, + "createdAt": { + "description": "Creation date", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "data": { + "$ref": "#/components/schemas/OnThisDayDto" + }, + "deletedAt": { + "description": "Deletion date", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "hideAt": { + "description": "Date when memory should be hidden", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "id": { + "description": "Memory ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "isSaved": { + "description": "Is memory saved", + "type": "boolean" + }, + "memoryAt": { + "description": "Memory date", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "ownerId": { + "description": "Owner user ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "seenAt": { + "description": "Date when memory was seen", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "showAt": { + "description": "Date when memory should be shown", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/MemoryType" + }, + "updatedAt": { + "description": "Last update date", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + } + }, + "required": [ + "assets", + "createdAt", + "data", + "id", + "isSaved", + "memoryAt", + "ownerId", + "type", + "updatedAt" + ], + "type": "object" + }, + "MemorySearchOrder": { + "description": "Sort order", + "enum": [ + "asc", + "desc", + "random" + ], + "type": "string" + }, + "MemoryStatisticsResponseDto": { + "properties": { "total": { - "description": "Total number of people", + "description": "Total number of memories", "maximum": 9007199254740991, - "minimum": 0, + "minimum": -9007199254740991, "type": "integer" } }, "required": [ - "hidden", - "people", "total" ], "type": "object" }, - "PeopleUpdate": { + "MemoryType": { + "description": "Memory type", + "enum": [ + "on_this_day" + ], + "type": "string" + }, + "MemoryUpdateDto": { "properties": { - "enabled": { - "description": "Whether people are enabled", + "isSaved": { + "description": "Is memory saved", "type": "boolean" }, - "minimumFaces": { - "description": "People face threshold", - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" + "memoryAt": { + "description": "Memory date", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" }, - "sidebarWeb": { - "description": "Whether people appear in web sidebar", - "type": "boolean" + "seenAt": { + "description": "Date when memory was seen", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" } }, "type": "object" }, - "PeopleUpdateDto": { + "MergePersonDto": { "properties": { - "people": { - "description": "People to update", + "ids": { + "description": "Person IDs to merge", "items": { - "$ref": "#/components/schemas/PeopleUpdateItem" + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" }, "type": "array" } }, "required": [ - "people" + "ids" ], "type": "object" }, - "PeopleUpdateItem": { + "MetadataSearchDto": { "properties": { - "birthDate": { - "description": "Person date of birth", - "format": "date", + "albumIds": { + "description": "Filter by album IDs", + "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "type": "array" + }, + "checksum": { + "description": "Filter by file checksum", + "type": "string" + }, + "city": { + "description": "Filter by city name", "nullable": true, "type": "string" }, - "color": { - "description": "Person color (hex)", + "country": { + "description": "Filter by country name", "nullable": true, - "pattern": "^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$", "type": "string" }, - "featureFaceAssetId": { - "description": "Asset ID used for feature face thumbnail", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "createdAfter": { + "description": "Filter by creation date (after)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "createdBefore": { + "description": "Filter by creation date (before)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "description": { + "description": "Filter by description text", + "type": "string" + }, + "encodedVideoPath": { + "description": "Filter by encoded video file path", "type": "string" }, "id": { - "description": "Person ID", + "description": "Filter by asset ID", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, + "isEncoded": { + "description": "Filter by encoded status", + "type": "boolean" + }, "isFavorite": { - "description": "Mark as favorite", + "description": "Filter by favorite status", "type": "boolean" }, - "isHidden": { - "description": "Person visibility (hidden)", + "isMotion": { + "description": "Filter by motion photo status", "type": "boolean" }, - "name": { - "description": "Person name", - "type": "string" - } - }, - "required": [ - "id" - ], - "type": "object" - }, - "Permission": { - "description": "List of permissions", - "enum": [ - "all", - "activity.create", - "activity.read", - "activity.update", - "activity.delete", - "activity.statistics", - "apiKey.create", - "apiKey.read", - "apiKey.update", - "apiKey.delete", - "apiKey.rotate", - "asset.read", - "asset.update", - "asset.delete", - "asset.statistics", - "asset.share", - "asset.view", - "asset.download", - "asset.upload", - "asset.copy", - "asset.derive", - "asset.edit.get", - "asset.edit.create", - "asset.edit.delete", - "album.create", - "album.read", - "album.update", - "album.delete", - "album.statistics", - "album.share", - "album.download", - "albumAsset.create", - "albumAsset.delete", - "albumUser.create", - "albumUser.update", - "albumUser.delete", - "auth.changePassword", - "authDevice.delete", - "archive.read", - "backup.list", - "backup.download", - "backup.upload", - "backup.delete", - "duplicate.read", - "duplicate.delete", - "face.create", - "face.read", - "face.update", - "face.delete", - "folder.read", - "job.create", - "job.read", - "library.create", - "library.read", - "library.update", - "library.delete", - "library.statistics", - "timeline.read", - "timeline.download", - "maintenance", - "map.read", - "map.search", - "memory.create", - "memory.read", - "memory.update", - "memory.delete", - "memory.statistics", - "memoryAsset.create", - "memoryAsset.delete", - "notification.create", - "notification.read", - "notification.update", - "notification.delete", - "partner.create", - "partner.read", - "partner.update", - "partner.delete", - "person.create", - "person.read", - "person.update", - "person.delete", - "person.statistics", - "person.merge", - "person.reassign", - "pinCode.create", - "pinCode.update", - "pinCode.delete", - "plugin.create", - "plugin.read", - "plugin.update", - "plugin.delete", - "server.about", - "server.apkLinks", - "server.storage", - "server.statistics", - "server.versionCheck", - "serverLicense.read", - "serverLicense.update", - "serverLicense.delete", - "session.create", - "session.read", - "session.update", - "session.delete", - "session.lock", - "sharedLink.create", - "sharedLink.read", - "sharedLink.update", - "sharedLink.delete", - "stack.create", - "stack.read", - "stack.update", - "stack.delete", - "sync.stream", - "syncCheckpoint.read", - "syncCheckpoint.update", - "syncCheckpoint.delete", - "systemConfig.read", - "systemConfig.update", - "systemMetadata.read", - "systemMetadata.update", - "tag.create", - "tag.read", - "tag.update", - "tag.delete", - "tag.asset", - "user.read", - "user.update", - "userLicense.create", - "userLicense.read", - "userLicense.update", - "userLicense.delete", - "userOnboarding.read", - "userOnboarding.update", - "userOnboarding.delete", - "userPreference.read", - "userPreference.update", - "userProfileImage.create", - "userProfileImage.read", - "userProfileImage.update", - "userProfileImage.delete", - "queue.read", - "queue.update", - "queueJob.create", - "queueJob.read", - "queueJob.update", - "queueJob.delete", - "workflow.create", - "workflow.read", - "workflow.update", - "workflow.delete", - "workflow.logs", - "adminUser.create", - "adminUser.read", - "adminUser.update", - "adminUser.delete", - "adminSession.read", - "adminAuth.unlinkAll" - ], - "type": "string" - }, - "PersonCreateDto": { - "properties": { - "birthDate": { - "description": "Person date of birth", - "format": "date", - "nullable": true, - "type": "string" - }, - "color": { - "description": "Person color (hex)", - "nullable": true, - "pattern": "^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$", - "type": "string" - }, - "isFavorite": { - "description": "Mark as favorite", + "isNotInAlbum": { + "description": "Filter assets not in any album", "type": "boolean" }, - "isHidden": { - "description": "Person visibility (hidden)", + "isOffline": { + "description": "Filter by offline status", "type": "boolean" }, - "name": { - "description": "Person name", - "type": "string" - } - }, - "type": "object" - }, - "PersonResponseDto": { - "properties": { - "birthDate": { - "description": "Person date of birth", - "format": "date", + "lensModel": { + "description": "Filter by lens model", "nullable": true, "type": "string" }, - "color": { - "description": "Person color (hex)", - "type": "string", - "x-immich-history": [ - { - "version": "v1.126.0", - "state": "Added" - }, - { - "version": "v2", - "state": "Stable" - } - ], - "x-immich-state": "Stable" - }, - "id": { - "description": "Person ID", + "libraryId": { + "description": "Library ID to filter by", "format": "uuid", + "nullable": true, "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "isFavorite": { - "description": "Is favorite", - "type": "boolean", - "x-immich-history": [ - { - "version": "v1.126.0", - "state": "Added" - }, - { - "version": "v2", - "state": "Stable" - } - ], - "x-immich-state": "Stable" - }, - "isHidden": { - "description": "Is hidden", - "type": "boolean" + "make": { + "description": "Filter by camera make", + "nullable": true, + "type": "string" }, - "name": { - "description": "Person name", + "model": { + "description": "Filter by camera model", + "nullable": true, "type": "string" }, - "thumbnailPath": { - "description": "Thumbnail path", + "ocr": { + "description": "Filter by OCR text content", "type": "string" }, - "updatedAt": { - "description": "Last update date", - "format": "date-time", - "type": "string", + "order": { + "$ref": "#/components/schemas/AssetOrder", + "default": "desc", + "description": "Sort order" + }, + "originalFileName": { + "description": "Filter by original file name", + "type": "string" + }, + "originalPath": { + "description": "Filter by original file path", + "type": "string" + }, + "page": { + "description": "Page number", + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + }, + "personIds": { + "description": "Filter by person IDs", + "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "type": "array" + }, + "previewPath": { + "description": "Filter by preview file path", + "type": "string" + }, + "rating": { + "description": "Filter by rating [1-5], or null for unrated", + "maximum": 5, + "minimum": 1, + "nullable": true, + "type": "integer", "x-immich-history": [ { - "version": "v1.107.0", + "version": "v1", "state": "Added" }, { "version": "v2", "state": "Stable" + }, + { + "version": "v2.6.0", + "state": "Updated", + "description": "Using -1 as a rating is deprecated and will be removed in the next major version." + }, + { + "version": "v3", + "state": "Updated", + "description": "Using -1 as a rating is no longer valid." } ], "x-immich-state": "Stable" - } - }, - "required": [ - "birthDate", - "id", - "isHidden", - "name", - "thumbnailPath" - ], - "type": "object" - }, - "PersonStatisticsResponseDto": { - "properties": { - "assets": { - "description": "Number of assets", - "maximum": 9007199254740991, - "minimum": -9007199254740991, + }, + "size": { + "description": "Number of results to return", + "maximum": 1000, + "minimum": 1, "type": "integer" - } - }, - "required": [ - "assets" - ], - "type": "object" - }, - "PersonUpdateDto": { - "properties": { - "birthDate": { - "description": "Person date of birth", - "format": "date", + }, + "state": { + "description": "Filter by state/province name", "nullable": true, "type": "string" }, - "color": { - "description": "Person color (hex)", + "tagIds": { + "description": "Filter by tag IDs", + "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, "nullable": true, - "pattern": "^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$", + "type": "array" + }, + "takenAfter": { + "description": "Filter by taken date (after)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "featureFaceAssetId": { - "description": "Asset ID used for feature face thumbnail", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "takenBefore": { + "description": "Filter by taken date (before)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "isFavorite": { - "description": "Mark as favorite", + "thumbnailPath": { + "description": "Filter by thumbnail file path", + "type": "string" + }, + "trashedAfter": { + "description": "Filter by trash date (after)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "trashedBefore": { + "description": "Filter by trash date (before)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/AssetTypeEnum" + }, + "updatedAfter": { + "description": "Filter by update date (after)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "updatedBefore": { + "description": "Filter by update date (before)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "visibility": { + "$ref": "#/components/schemas/AssetVisibility" + }, + "withDeleted": { + "description": "Include deleted assets", "type": "boolean" }, - "isHidden": { - "description": "Person visibility (hidden)", + "withExif": { + "description": "Include EXIF data in response", "type": "boolean" }, - "name": { - "description": "Person name", - "type": "string" + "withPeople": { + "description": "Include people data in response", + "type": "boolean" + }, + "withStacked": { + "description": "Include stacked assets", + "type": "boolean" } }, "type": "object" }, - "PinCodeChangeDto": { + "MirrorAxis": { + "description": "Axis to mirror along", + "enum": [ + "horizontal", + "vertical" + ], + "type": "string" + }, + "MirrorParameters": { "properties": { - "newPinCode": { - "description": "New PIN code (4-6 digits)", - "pattern": "^\\d{6}$", - "type": "string" - }, - "password": { - "description": "User password (required if PIN code is not provided)", - "example": "password", - "type": "string" - }, - "pinCode": { - "description": "New PIN code (4-6 digits)", - "example": "123456", - "pattern": "^\\d{6}$", - "type": "string" + "axis": { + "$ref": "#/components/schemas/MirrorAxis" } }, "required": [ - "newPinCode" + "axis" ], "type": "object" }, - "PinCodeResetDto": { + "NotificationCreateDto": { "properties": { - "password": { - "description": "User password (required if PIN code is not provided)", - "example": "password", + "data": { + "additionalProperties": {}, + "description": "Additional notification data", + "type": "object" + }, + "description": { + "description": "Notification description", + "nullable": true, "type": "string" }, - "pinCode": { - "description": "New PIN code (4-6 digits)", - "example": "123456", - "pattern": "^\\d{6}$", + "level": { + "$ref": "#/components/schemas/NotificationLevel" + }, + "readAt": { + "description": "Date when notification was read", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "nullable": true, + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" - } - }, - "type": "object" - }, - "PinCodeSetupDto": { - "properties": { - "pinCode": { - "description": "PIN code (4-6 digits)", - "example": "123456", - "pattern": "^\\d{6}$", + }, + "title": { + "description": "Notification title", + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/NotificationType" + }, + "userId": { + "description": "User ID to send notification to", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" } }, "required": [ - "pinCode" + "title", + "userId" ], "type": "object" }, - "PlacesResponseDto": { + "NotificationDeleteAllDto": { "properties": { - "admin1name": { - "description": "Administrative level 1 name (state/province)", - "type": "string" - }, - "admin2name": { - "description": "Administrative level 2 name (county/district)", - "type": "string" - }, - "latitude": { - "description": "Latitude coordinate", - "type": "number" - }, - "longitude": { - "description": "Longitude coordinate", - "type": "number" - }, - "name": { - "description": "Place name", - "type": "string" - } - }, - "required": [ - "latitude", - "longitude", - "name" - ], - "type": "object" - }, - "PluginMethodResponseDto": { - "properties": { - "description": { - "description": "Description", - "type": "string" - }, - "hostFunctions": { - "type": "boolean" - }, - "key": { - "description": "Key", - "type": "string" - }, - "name": { - "description": "Name", - "type": "string" - }, - "schema": { - "properties": {}, - "type": "object" - }, - "title": { - "description": "Title", - "type": "string" - }, - "types": { - "description": "Workflow types", - "items": { - "$ref": "#/components/schemas/WorkflowType" - }, - "type": "array" - }, - "uiHints": { - "description": "Ui hints", + "ids": { + "description": "Notification IDs to delete", "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, + "minItems": 1, "type": "array" } }, "required": [ - "description", - "hostFunctions", - "key", - "name", - "title", - "types", - "uiHints" + "ids" ], "type": "object" }, - "PluginResponseDto": { + "NotificationDto": { "properties": { - "author": { - "description": "Plugin author", - "type": "string" - }, "createdAt": { "description": "Creation date", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, + "data": { + "additionalProperties": {}, + "description": "Additional notification data", + "type": "object" + }, "description": { - "description": "Plugin description", + "description": "Notification description", "type": "string" }, "id": { - "description": "Plugin ID", + "description": "Notification ID", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "methods": { - "description": "Plugin methods", - "items": { - "$ref": "#/components/schemas/PluginMethodResponseDto" - }, - "type": "array" + "level": { + "$ref": "#/components/schemas/NotificationLevel" }, - "name": { - "description": "Plugin name", + "readAt": { + "description": "Date when notification was read", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, "title": { - "description": "Plugin title", - "type": "string" - }, - "updatedAt": { - "description": "Last update date", + "description": "Notification title", "type": "string" }, - "version": { - "description": "Plugin version", - "type": "string" + "type": { + "$ref": "#/components/schemas/NotificationType" } }, "required": [ - "author", "createdAt", - "description", "id", - "methods", - "name", + "level", "title", - "updatedAt", - "version" + "type" ], "type": "object" }, - "PluginTemplateResponseDto": { + "NotificationLevel": { + "description": "Notification level", + "enum": [ + "success", + "error", + "warning", + "info" + ], + "type": "string" + }, + "NotificationType": { + "description": "Notification type", + "enum": [ + "JobFailed", + "BackupFailed", + "SystemMessage", + "AlbumInvite", + "AlbumUpdate", + "Custom" + ], + "type": "string" + }, + "NotificationUpdateAllDto": { "properties": { - "description": { - "description": "Template description", - "type": "string" - }, - "key": { - "description": "Template key (unique across all templates)", - "type": "string" - }, - "steps": { - "description": "Workflow steps", + "ids": { + "description": "Notification IDs to update", "items": { - "$ref": "#/components/schemas/PluginTemplateStepResponseDto" + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" }, + "minItems": 1, "type": "array" }, - "title": { - "description": "Template title", + "readAt": { + "description": "Date when notifications were read", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "nullable": true, + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" - }, - "trigger": { - "$ref": "#/components/schemas/WorkflowTrigger", - "description": "Workflow trigger" - }, - "uiHints": { - "description": "Ui hints, for example \"smart-album\"", - "items": { - "type": "string" - }, - "type": "array" } }, "required": [ - "description", - "key", - "steps", - "title", - "trigger", - "uiHints" + "ids" ], "type": "object" }, - "PluginTemplateStepResponseDto": { + "NotificationUpdateDto": { "properties": { - "config": { - "additionalProperties": {}, - "description": "Step configuration", + "readAt": { + "description": "Date when notification was read", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", "nullable": true, - "type": "object" - }, - "enabled": { - "description": "Whether the step is enabled", - "type": "boolean" - }, - "method": { - "description": "Step plugin method", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + } + }, + "type": "object" + }, + "OAuthAuthorizeResponseDto": { + "properties": { + "url": { + "description": "OAuth authorization URL", "type": "string" } }, "required": [ - "config", - "method" + "url" ], "type": "object" }, - "PurchaseResponse": { + "OAuthBackchannelLogoutDto": { "properties": { - "hideBuyButtonUntil": { - "description": "Date until which to hide buy button", + "logout_token": { + "description": "OAuth logout token", "type": "string" - }, - "showSupportBadge": { - "description": "Whether to show support badge", - "type": "boolean" } }, "required": [ - "hideBuyButtonUntil", - "showSupportBadge" + "logout_token" ], "type": "object" }, - "PurchaseUpdate": { + "OAuthCallbackDto": { "properties": { - "hideBuyButtonUntil": { - "description": "Date until which to hide buy button", + "codeVerifier": { + "description": "OAuth code verifier (PKCE)", "type": "string" }, - "showSupportBadge": { - "description": "Whether to show support badge", - "type": "boolean" + "state": { + "description": "OAuth state parameter", + "type": "string" + }, + "url": { + "description": "OAuth callback URL", + "minLength": 1, + "type": "string" } }, + "required": [ + "url" + ], "type": "object" }, - "QueueCommand": { - "description": "Queue command to execute", + "OAuthConfigDto": { + "properties": { + "codeChallenge": { + "description": "OAuth code challenge (PKCE)", + "type": "string" + }, + "redirectUri": { + "description": "OAuth redirect URI", + "type": "string" + }, + "state": { + "description": "OAuth state parameter", + "type": "string" + } + }, + "required": [ + "redirectUri" + ], + "type": "object" + }, + "OAuthTokenEndpointAuthMethod": { + "description": "OAuth token endpoint auth method", "enum": [ - "start", - "pause", - "resume", - "empty", - "clear-failed" + "client_secret_post", + "client_secret_basic" ], "type": "string" }, - "QueueCommandDto": { + "OnThisDayDto": { "properties": { - "command": { - "$ref": "#/components/schemas/QueueCommand" - }, - "force": { - "description": "Force the command execution (if applicable)", - "type": "boolean" + "year": { + "description": "Year for on this day memory", + "maximum": 9999, + "minimum": 1000, + "type": "integer" } }, "required": [ - "command" + "year" ], "type": "object" }, - "QueueDeleteDto": { + "OnboardingDto": { "properties": { - "failed": { - "description": "If true, will also remove failed jobs from the queue.", - "type": "boolean", - "x-immich-history": [ - { - "version": "v2.4.0", - "state": "Added" - }, - { - "version": "v2.4.0", - "state": "Alpha" - } - ], - "x-immich-state": "Alpha" + "isOnboarded": { + "description": "Is user onboarded", + "type": "boolean" } }, + "required": [ + "isOnboarded" + ], "type": "object" }, - "QueueJobResponseDto": { + "OnboardingResponseDto": { "properties": { - "data": { - "additionalProperties": {}, - "description": "Job data payload", - "type": "object" - }, - "id": { - "description": "Job ID", - "type": "string" - }, - "name": { - "$ref": "#/components/schemas/JobName" - }, - "timestamp": { - "description": "Job creation timestamp", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "isOnboarded": { + "description": "Is user onboarded", + "type": "boolean" } }, "required": [ - "data", - "name", - "timestamp" + "isOnboarded" ], "type": "object" }, - "QueueJobStatus": { - "description": "Queue job status", - "enum": [ - "active", - "failed", - "completed", - "delayed", - "waiting", - "paused" + "PartnerCreateDto": { + "properties": { + "sharedWithId": { + "description": "User ID to share with", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + } + }, + "required": [ + "sharedWithId" ], - "type": "string" + "type": "object" }, - "QueueName": { - "description": "Queue name", + "PartnerDirection": { + "description": "Partner direction", "enum": [ - "thumbnailGeneration", - "metadataExtraction", - "videoConversion", - "faceDetection", - "facialRecognition", - "smartSearch", - "duplicateDetection", - "backgroundTask", - "storageTemplateMigration", - "migration", - "search", - "sidecar", - "library", - "notifications", - "backupDatabase", - "ocr", - "workflow", - "integrityCheck", - "editor" + "shared-by", + "shared-with" ], "type": "string" }, - "QueueResponseDto": { + "PartnerResponseDto": { + "description": "Partner response", "properties": { - "isPaused": { - "description": "Whether the queue is paused", + "avatarColor": { + "$ref": "#/components/schemas/UserAvatarColor" + }, + "email": { + "description": "User email", + "format": "email", + "pattern": "^[\\p{L}\\p{M}\\p{N}.!#$%&'*+/=?^_`{|}~-]+@[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?(?:\\.[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?)*$", + "type": "string" + }, + "id": { + "description": "User ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "inTimeline": { + "description": "Show in timeline", "type": "boolean" }, "name": { - "$ref": "#/components/schemas/QueueName" + "description": "User name", + "type": "string" }, - "statistics": { - "$ref": "#/components/schemas/QueueStatisticsDto" + "profileChangedAt": { + "description": "Profile change date", + "format": "date-time", + "type": "string" + }, + "profileImagePath": { + "description": "Profile image path", + "type": "string" } }, "required": [ - "isPaused", + "avatarColor", + "email", + "id", "name", - "statistics" + "profileChangedAt", + "profileImagePath" ], "type": "object" }, - "QueueResponseLegacyDto": { + "PartnerUpdateDto": { "properties": { - "jobCounts": { - "$ref": "#/components/schemas/QueueStatisticsDto" - }, - "queueStatus": { - "$ref": "#/components/schemas/QueueStatusLegacyDto" + "inTimeline": { + "description": "Show partner assets in timeline", + "type": "boolean" } }, "required": [ - "jobCounts", - "queueStatus" + "inTimeline" ], "type": "object" }, - "QueueStatisticsDto": { + "PeopleResponse": { "properties": { - "active": { - "description": "Number of active jobs", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "completed": { - "description": "Number of completed jobs", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "delayed": { - "description": "Number of delayed jobs", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "failed": { - "description": "Number of failed jobs", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "enabled": { + "description": "Whether people are enabled", + "type": "boolean" }, - "paused": { - "description": "Number of paused jobs", + "minimumFaces": { + "description": "People face threshold", "maximum": 9007199254740991, - "minimum": -9007199254740991, + "minimum": 1, "type": "integer" }, - "waiting": { - "description": "Number of waiting jobs", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "sidebarWeb": { + "description": "Whether people appear in web sidebar", + "type": "boolean" } }, "required": [ - "active", - "completed", - "delayed", - "failed", - "paused", - "waiting" + "enabled", + "sidebarWeb" ], "type": "object" }, - "QueueStatusLegacyDto": { + "PeopleResponseDto": { + "description": "People response", "properties": { - "isActive": { - "description": "Whether the queue is currently active (has running jobs)", - "type": "boolean" - }, - "isPaused": { - "description": "Whether the queue is paused", - "type": "boolean" + "hasNextPage": { + "description": "Whether there are more pages", + "type": "boolean", + "x-immich-history": [ + { + "version": "v1.110.0", + "state": "Added" + }, + { + "version": "v2", + "state": "Stable" + } + ], + "x-immich-state": "Stable" + }, + "hidden": { + "description": "Number of hidden people", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "people": { + "items": { + "$ref": "#/components/schemas/PersonResponseDto" + }, + "type": "array" + }, + "total": { + "description": "Total number of people", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" } }, "required": [ - "isActive", - "isPaused" + "hidden", + "people", + "total" ], "type": "object" }, - "QueueUpdateDto": { + "PeopleUpdate": { "properties": { - "isPaused": { - "description": "Whether to pause the queue", + "enabled": { + "description": "Whether people are enabled", + "type": "boolean" + }, + "minimumFaces": { + "description": "People face threshold", + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + }, + "sidebarWeb": { + "description": "Whether people appear in web sidebar", "type": "boolean" } }, "type": "object" }, - "QueuesResponseLegacyDto": { + "PeopleUpdateDto": { "properties": { - "backgroundTask": { - "$ref": "#/components/schemas/QueueResponseLegacyDto" - }, - "backupDatabase": { - "$ref": "#/components/schemas/QueueResponseLegacyDto" - }, - "duplicateDetection": { - "$ref": "#/components/schemas/QueueResponseLegacyDto" - }, - "editor": { - "$ref": "#/components/schemas/QueueResponseLegacyDto" - }, - "faceDetection": { - "$ref": "#/components/schemas/QueueResponseLegacyDto" - }, - "facialRecognition": { - "$ref": "#/components/schemas/QueueResponseLegacyDto" - }, - "integrityCheck": { - "$ref": "#/components/schemas/QueueResponseLegacyDto" - }, - "library": { - "$ref": "#/components/schemas/QueueResponseLegacyDto" - }, - "metadataExtraction": { - "$ref": "#/components/schemas/QueueResponseLegacyDto" - }, - "migration": { - "$ref": "#/components/schemas/QueueResponseLegacyDto" - }, - "notifications": { - "$ref": "#/components/schemas/QueueResponseLegacyDto" - }, - "ocr": { - "$ref": "#/components/schemas/QueueResponseLegacyDto" - }, - "search": { - "$ref": "#/components/schemas/QueueResponseLegacyDto" - }, - "sidecar": { - "$ref": "#/components/schemas/QueueResponseLegacyDto" - }, - "smartSearch": { - "$ref": "#/components/schemas/QueueResponseLegacyDto" - }, - "storageTemplateMigration": { - "$ref": "#/components/schemas/QueueResponseLegacyDto" - }, - "thumbnailGeneration": { - "$ref": "#/components/schemas/QueueResponseLegacyDto" - }, - "videoConversion": { - "$ref": "#/components/schemas/QueueResponseLegacyDto" - }, - "workflow": { - "$ref": "#/components/schemas/QueueResponseLegacyDto" + "people": { + "description": "People to update", + "items": { + "$ref": "#/components/schemas/PeopleUpdateItem" + }, + "type": "array" } }, "required": [ - "backgroundTask", - "backupDatabase", - "duplicateDetection", - "editor", - "faceDetection", - "facialRecognition", - "integrityCheck", - "library", - "metadataExtraction", - "migration", - "notifications", - "ocr", - "search", - "sidecar", - "smartSearch", - "storageTemplateMigration", - "thumbnailGeneration", - "videoConversion", - "workflow" + "people" ], "type": "object" }, - "RandomSearchDto": { + "PeopleUpdateItem": { "properties": { - "albumIds": { - "description": "Filter by album IDs", - "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "type": "array" - }, - "city": { - "description": "Filter by city name", + "birthDate": { + "description": "Person date of birth", + "format": "date", "nullable": true, "type": "string" }, - "country": { - "description": "Filter by country name", + "color": { + "description": "Person color (hex)", "nullable": true, + "pattern": "^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$", "type": "string" }, - "createdAfter": { - "description": "Filter by creation date (after)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "featureFaceAssetId": { + "description": "Asset ID used for feature face thumbnail", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "id": { + "description": "Person ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "isFavorite": { + "description": "Mark as favorite", + "type": "boolean" + }, + "isHidden": { + "description": "Person visibility (hidden)", + "type": "boolean" + }, + "name": { + "description": "Person name", + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + "Permission": { + "description": "List of permissions", + "enum": [ + "all", + "activity.create", + "activity.read", + "activity.update", + "activity.delete", + "activity.statistics", + "apiKey.create", + "apiKey.read", + "apiKey.update", + "apiKey.delete", + "apiKey.rotate", + "asset.read", + "asset.update", + "asset.delete", + "asset.statistics", + "asset.share", + "asset.view", + "asset.download", + "asset.upload", + "asset.copy", + "asset.derive", + "asset.edit.get", + "asset.edit.create", + "asset.edit.delete", + "album.create", + "album.read", + "album.update", + "album.delete", + "album.statistics", + "album.share", + "album.download", + "albumAsset.create", + "albumAsset.delete", + "albumUser.create", + "albumUser.update", + "albumUser.delete", + "auth.changePassword", + "authDevice.delete", + "archive.read", + "backup.list", + "backup.download", + "backup.upload", + "backup.delete", + "adminConfig.read", + "adminConfig.update", + "userConfig.read", + "duplicate.read", + "duplicate.delete", + "face.create", + "face.read", + "face.update", + "face.delete", + "folder.read", + "job.create", + "job.read", + "library.create", + "library.read", + "library.update", + "library.delete", + "library.statistics", + "timeline.read", + "timeline.download", + "maintenance", + "map.read", + "map.search", + "memory.create", + "memory.read", + "memory.update", + "memory.delete", + "memory.statistics", + "memoryAsset.create", + "memoryAsset.delete", + "notification.create", + "notification.read", + "notification.update", + "notification.delete", + "partner.create", + "partner.read", + "partner.update", + "partner.delete", + "person.create", + "person.read", + "person.update", + "person.delete", + "person.statistics", + "person.merge", + "person.reassign", + "pinCode.create", + "pinCode.update", + "pinCode.delete", + "plugin.create", + "plugin.read", + "plugin.update", + "plugin.delete", + "server.about", + "server.apkLinks", + "server.storage", + "server.statistics", + "server.versionCheck", + "serverLicense.read", + "serverLicense.update", + "serverLicense.delete", + "session.create", + "session.read", + "session.update", + "session.delete", + "session.lock", + "sharedLink.create", + "sharedLink.read", + "sharedLink.update", + "sharedLink.delete", + "stack.create", + "stack.read", + "stack.update", + "stack.delete", + "sync.stream", + "syncCheckpoint.read", + "syncCheckpoint.update", + "syncCheckpoint.delete", + "systemConfig.read", + "systemConfig.update", + "systemMetadata.read", + "systemMetadata.update", + "tag.create", + "tag.read", + "tag.update", + "tag.delete", + "tag.asset", + "user.read", + "user.update", + "userLicense.create", + "userLicense.read", + "userLicense.update", + "userLicense.delete", + "userOnboarding.read", + "userOnboarding.update", + "userOnboarding.delete", + "userPreference.read", + "userPreference.update", + "userProfileImage.create", + "userProfileImage.read", + "userProfileImage.update", + "userProfileImage.delete", + "queue.read", + "queue.update", + "queueJob.create", + "queueJob.read", + "queueJob.update", + "queueJob.delete", + "workflow.create", + "workflow.read", + "workflow.update", + "workflow.delete", + "workflow.logs", + "adminUser.create", + "adminUser.read", + "adminUser.update", + "adminUser.delete", + "adminSession.read", + "adminAuth.unlinkAll" + ], + "type": "string" + }, + "PersonCreateDto": { + "properties": { + "birthDate": { + "description": "Person date of birth", + "format": "date", + "nullable": true, "type": "string" }, - "createdBefore": { - "description": "Filter by creation date (before)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "color": { + "description": "Person color (hex)", + "nullable": true, + "pattern": "^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$", "type": "string" }, - "isEncoded": { - "description": "Filter by encoded status", - "type": "boolean" - }, "isFavorite": { - "description": "Filter by favorite status", - "type": "boolean" - }, - "isMotion": { - "description": "Filter by motion photo status", - "type": "boolean" - }, - "isNotInAlbum": { - "description": "Filter assets not in any album", + "description": "Mark as favorite", "type": "boolean" }, - "isOffline": { - "description": "Filter by offline status", + "isHidden": { + "description": "Person visibility (hidden)", "type": "boolean" }, - "lensModel": { - "description": "Filter by lens model", + "name": { + "description": "Person name", + "type": "string" + } + }, + "type": "object" + }, + "PersonResponseDto": { + "properties": { + "birthDate": { + "description": "Person date of birth", + "format": "date", "nullable": true, "type": "string" }, - "libraryId": { - "description": "Library ID to filter by", + "color": { + "description": "Person color (hex)", + "type": "string", + "x-immich-history": [ + { + "version": "v1.126.0", + "state": "Added" + }, + { + "version": "v2", + "state": "Stable" + } + ], + "x-immich-state": "Stable" + }, + "id": { + "description": "Person ID", "format": "uuid", - "nullable": true, "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "make": { - "description": "Filter by camera make", - "nullable": true, - "type": "string" + "isFavorite": { + "description": "Is favorite", + "type": "boolean", + "x-immich-history": [ + { + "version": "v1.126.0", + "state": "Added" + }, + { + "version": "v2", + "state": "Stable" + } + ], + "x-immich-state": "Stable" }, - "model": { - "description": "Filter by camera model", - "nullable": true, - "type": "string" + "isHidden": { + "description": "Is hidden", + "type": "boolean" }, - "ocr": { - "description": "Filter by OCR text content", + "name": { + "description": "Person name", "type": "string" }, - "personIds": { - "description": "Filter by person IDs", - "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "type": "array" + "thumbnailPath": { + "description": "Thumbnail path", + "type": "string" }, - "rating": { - "description": "Filter by rating [1-5], or null for unrated", - "maximum": 5, - "minimum": 1, - "nullable": true, - "type": "integer", + "updatedAt": { + "description": "Last update date", + "format": "date-time", + "type": "string", "x-immich-history": [ { - "version": "v1", + "version": "v1.107.0", "state": "Added" }, { "version": "v2", "state": "Stable" - }, - { - "version": "v2.6.0", - "state": "Updated", - "description": "Using -1 as a rating is deprecated and will be removed in the next major version." - }, - { - "version": "v3", - "state": "Updated", - "description": "Using -1 as a rating is no longer valid." } ], "x-immich-state": "Stable" - }, - "size": { - "description": "Number of results to return", - "maximum": 1000, - "minimum": 1, + } + }, + "required": [ + "birthDate", + "id", + "isHidden", + "name", + "thumbnailPath" + ], + "type": "object" + }, + "PersonStatisticsResponseDto": { + "properties": { + "assets": { + "description": "Number of assets", + "maximum": 9007199254740991, + "minimum": -9007199254740991, "type": "integer" - }, - "state": { - "description": "Filter by state/province name", + } + }, + "required": [ + "assets" + ], + "type": "object" + }, + "PersonUpdateDto": { + "properties": { + "birthDate": { + "description": "Person date of birth", + "format": "date", "nullable": true, "type": "string" }, - "tagIds": { - "description": "Filter by tag IDs", - "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, + "color": { + "description": "Person color (hex)", "nullable": true, - "type": "array" - }, - "takenAfter": { - "description": "Filter by taken date (after)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" - }, - "takenBefore": { - "description": "Filter by taken date (before)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" - }, - "trashedAfter": { - "description": "Filter by trash date (after)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" - }, - "trashedBefore": { - "description": "Filter by trash date (before)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" - }, - "type": { - "$ref": "#/components/schemas/AssetTypeEnum" - }, - "updatedAfter": { - "description": "Filter by update date (after)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "pattern": "^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$", "type": "string" }, - "updatedBefore": { - "description": "Filter by update date (before)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "featureFaceAssetId": { + "description": "Asset ID used for feature face thumbnail", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "visibility": { - "$ref": "#/components/schemas/AssetVisibility" - }, - "withDeleted": { - "description": "Include deleted assets", - "type": "boolean" - }, - "withExif": { - "description": "Include EXIF data in response", + "isFavorite": { + "description": "Mark as favorite", "type": "boolean" }, - "withPeople": { - "description": "Include people data in response", + "isHidden": { + "description": "Person visibility (hidden)", "type": "boolean" }, - "withStacked": { - "description": "Include stacked assets", - "type": "boolean" + "name": { + "description": "Person name", + "type": "string" } }, "type": "object" }, - "RatingsResponse": { + "PinCodeChangeDto": { "properties": { - "enabled": { - "description": "Whether ratings are enabled", - "type": "boolean" + "newPinCode": { + "description": "New PIN code (4-6 digits)", + "pattern": "^\\d{6}$", + "type": "string" + }, + "password": { + "description": "User password (required if PIN code is not provided)", + "example": "password", + "type": "string" + }, + "pinCode": { + "description": "New PIN code (4-6 digits)", + "example": "123456", + "pattern": "^\\d{6}$", + "type": "string" } }, "required": [ - "enabled" + "newPinCode" ], "type": "object" }, - "RatingsUpdate": { + "PinCodeResetDto": { "properties": { - "enabled": { - "description": "Whether ratings are enabled", - "type": "boolean" + "password": { + "description": "User password (required if PIN code is not provided)", + "example": "password", + "type": "string" + }, + "pinCode": { + "description": "New PIN code (4-6 digits)", + "example": "123456", + "pattern": "^\\d{6}$", + "type": "string" } }, "type": "object" }, - "ReactionLevel": { - "description": "Reaction level", - "enum": [ - "album", - "asset" - ], - "type": "string" - }, - "ReactionType": { - "description": "Reaction type", - "enum": [ - "comment", - "like" - ], - "type": "string" - }, - "RecentlyAddedResponse": { + "PinCodeSetupDto": { "properties": { - "sidebarWeb": { - "description": "Whether the recently added page appears in the web sidebar", - "type": "boolean" + "pinCode": { + "description": "PIN code (4-6 digits)", + "example": "123456", + "pattern": "^\\d{6}$", + "type": "string" } }, "required": [ - "sidebarWeb" + "pinCode" ], "type": "object" }, - "RecentlyAddedUpdate": { - "properties": { - "sidebarWeb": { - "description": "Whether the recently added page appears in the web sidebar", - "type": "boolean" - } - }, - "type": "object" - }, - "ReleaseChannel": { - "description": "Release channel", - "enum": [ - "stable", - "releaseCandidate" - ], - "type": "string" - }, - "ReleaseEventV1": { + "PlacesResponseDto": { "properties": { - "checkedAt": { - "description": "When the server last checked for a latest version. As an ISO timestamp", + "admin1name": { + "description": "Administrative level 1 name (state/province)", "type": "string" }, - "isAvailable": { - "description": "Whether a new version is available", - "type": "boolean" + "admin2name": { + "description": "Administrative level 2 name (county/district)", + "type": "string" }, - "releaseVersion": { - "$ref": "#/components/schemas/ServerVersionResponseDto" + "latitude": { + "description": "Latitude coordinate", + "type": "number" }, - "serverVersion": { - "$ref": "#/components/schemas/ServerVersionResponseDto" + "longitude": { + "description": "Longitude coordinate", + "type": "number" }, - "type": { - "$ref": "#/components/schemas/ReleaseType", - "description": "Release type", - "nullable": true + "name": { + "description": "Place name", + "type": "string" } }, "required": [ - "checkedAt", - "isAvailable", - "releaseVersion", - "serverVersion", - "type" + "latitude", + "longitude", + "name" ], "type": "object" }, - "ReleaseType": { - "enum": [ - "major", - "premajor", - "minor", - "preminor", - "patch", - "prepatch", - "prerelease" - ], - "type": "string" - }, - "ReverseGeocodingStateResponseDto": { + "PluginMethodResponseDto": { "properties": { - "lastImportFileName": { - "description": "Last import file name", - "nullable": true, + "description": { + "description": "Description", "type": "string" }, - "lastUpdate": { - "description": "Last update timestamp", - "nullable": true, + "hostFunctions": { + "type": "boolean" + }, + "key": { + "description": "Key", "type": "string" + }, + "name": { + "description": "Name", + "type": "string" + }, + "schema": { + "properties": {}, + "type": "object" + }, + "title": { + "description": "Title", + "type": "string" + }, + "types": { + "description": "Workflow types", + "items": { + "$ref": "#/components/schemas/WorkflowType" + }, + "type": "array" + }, + "uiHints": { + "description": "Ui hints", + "items": { + "type": "string" + }, + "type": "array" } }, "required": [ - "lastImportFileName", - "lastUpdate" - ], - "type": "object" - }, - "RotateParameters": { - "properties": { - "angle": { - "description": "Rotation angle in degrees", - "type": "number" - } - }, - "required": [ - "angle" + "description", + "hostFunctions", + "key", + "name", + "title", + "types", + "uiHints" ], "type": "object" }, - "SearchAlbumResponseDto": { + "PluginResponseDto": { "properties": { - "count": { - "description": "Number of albums in this page", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" + "author": { + "description": "Plugin author", + "type": "string" }, - "facets": { - "items": { - "$ref": "#/components/schemas/SearchFacetResponseDto" - }, - "type": "array" + "createdAt": { + "description": "Creation date", + "type": "string" }, - "items": { + "description": { + "description": "Plugin description", + "type": "string" + }, + "id": { + "description": "Plugin ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "methods": { + "description": "Plugin methods", "items": { - "$ref": "#/components/schemas/AlbumResponseDto" + "$ref": "#/components/schemas/PluginMethodResponseDto" }, "type": "array" }, - "total": { - "description": "Total number of matching albums", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" + "name": { + "description": "Plugin name", + "type": "string" + }, + "title": { + "description": "Plugin title", + "type": "string" + }, + "updatedAt": { + "description": "Last update date", + "type": "string" + }, + "version": { + "description": "Plugin version", + "type": "string" } }, "required": [ - "count", - "facets", - "items", - "total" + "author", + "createdAt", + "description", + "id", + "methods", + "name", + "title", + "updatedAt", + "version" ], "type": "object" }, - "SearchAssetResponseDto": { + "PluginTemplateResponseDto": { "properties": { - "count": { - "description": "Number of assets in this page", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" + "description": { + "description": "Template description", + "type": "string" + }, + "key": { + "description": "Template key (unique across all templates)", + "type": "string" }, - "facets": { + "steps": { + "description": "Workflow steps", "items": { - "$ref": "#/components/schemas/SearchFacetResponseDto" + "$ref": "#/components/schemas/PluginTemplateStepResponseDto" }, "type": "array" }, - "items": { + "title": { + "description": "Template title", + "type": "string" + }, + "trigger": { + "$ref": "#/components/schemas/WorkflowTrigger", + "description": "Workflow trigger" + }, + "uiHints": { + "description": "Ui hints, for example \"smart-album\"", "items": { - "$ref": "#/components/schemas/AssetResponseDto" + "type": "string" }, "type": "array" - }, - "nextPage": { - "description": "Next page token", - "nullable": true, - "type": "string" - }, - "total": { - "description": "Total number of matching assets", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer", - "x-immich-history": [ - { - "version": "v3.0.0", - "state": "Deprecated" - } - ], - "x-immich-state": "Deprecated" } }, "required": [ - "count", - "facets", - "items", - "nextPage", - "total" + "description", + "key", + "steps", + "title", + "trigger", + "uiHints" ], "type": "object" }, - "SearchExploreItem": { + "PluginTemplateStepResponseDto": { "properties": { - "data": { - "$ref": "#/components/schemas/AssetResponseDto" + "config": { + "additionalProperties": {}, + "description": "Step configuration", + "nullable": true, + "type": "object" }, - "value": { - "description": "Explore value", + "enabled": { + "description": "Whether the step is enabled", + "type": "boolean" + }, + "method": { + "description": "Step plugin method", "type": "string" } }, "required": [ - "data", - "value" + "config", + "method" ], "type": "object" }, - "SearchExploreResponseDto": { + "PublicConfigDto": { + "description": "Configuration properties that are visible to everyone", "properties": { - "fieldName": { - "description": "Explore field name", - "type": "string" + "oauth": { + "$ref": "#/components/schemas/PublicConfigOAuthDto" }, - "items": { - "items": { - "$ref": "#/components/schemas/SearchExploreItem" - }, - "type": "array" + "passwordLogin": { + "$ref": "#/components/schemas/PublicConfigPasswordLoginDto" + }, + "server": { + "$ref": "#/components/schemas/PublicConfigServerDto" + }, + "theme": { + "$ref": "#/components/schemas/PublicConfigThemeDto" } }, "required": [ - "fieldName", - "items" + "oauth", + "passwordLogin", + "server", + "theme" ], "type": "object" }, - "SearchFacetCountResponseDto": { + "PublicConfigOAuthDto": { "properties": { - "count": { - "description": "Number of assets with this facet value", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" + "autoLaunch": { + "description": "Auto launch", + "type": "boolean" }, - "value": { - "description": "Facet value", + "buttonText": { + "description": "Button text", "type": "string" + }, + "enabled": { + "description": "Enabled", + "type": "boolean" } }, "required": [ - "count", - "value" + "autoLaunch", + "buttonText", + "enabled" ], "type": "object" }, - "SearchFacetResponseDto": { + "PublicConfigPasswordLoginDto": { "properties": { - "counts": { - "items": { - "$ref": "#/components/schemas/SearchFacetCountResponseDto" - }, - "type": "array" - }, - "fieldName": { - "description": "Facet field name", - "type": "string" + "enabled": { + "description": "Enabled", + "type": "boolean" } }, "required": [ - "counts", - "fieldName" + "enabled" ], "type": "object" }, - "SearchResponseDto": { + "PublicConfigServerDto": { "properties": { - "albums": { - "$ref": "#/components/schemas/SearchAlbumResponseDto" - }, - "assets": { - "$ref": "#/components/schemas/SearchAssetResponseDto" + "loginPageMessage": { + "description": "Login page message", + "type": "string" } }, "required": [ - "albums", - "assets" + "loginPageMessage" ], "type": "object" }, - "SearchStatisticsResponseDto": { + "PublicConfigThemeDto": { "properties": { - "total": { - "description": "Total number of matching assets", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "customCss": { + "description": "Custom CSS for theming", + "type": "string" } }, "required": [ - "total" + "customCss" ], "type": "object" }, - "SearchSuggestionType": { - "description": "Suggestion type", - "enum": [ - "country", - "state", - "city", - "camera-make", - "camera-model", - "camera-lens-model" - ], - "type": "string" - }, - "ServerAboutResponseDto": { + "PurchaseResponse": { "properties": { - "build": { - "description": "Build identifier", - "type": "string" - }, - "buildImage": { - "description": "Build image name", - "type": "string" - }, - "buildImageUrl": { - "description": "Build image URL", - "type": "string" - }, - "buildUrl": { - "description": "Build URL", - "type": "string" - }, - "exiftool": { - "description": "ExifTool version", - "type": "string" - }, - "ffmpeg": { - "description": "FFmpeg version", - "type": "string" - }, - "imagemagick": { - "description": "ImageMagick version", - "type": "string" - }, - "libvips": { - "description": "libvips version", + "hideBuyButtonUntil": { + "description": "Date until which to hide buy button", "type": "string" }, - "licensed": { - "description": "Whether the server is licensed", + "showSupportBadge": { + "description": "Whether to show support badge", "type": "boolean" - }, - "nodejs": { - "description": "Node.js version", - "type": "string" - }, - "repository": { - "description": "Repository name", - "type": "string" - }, - "repositoryUrl": { - "description": "Repository URL", - "type": "string" - }, - "sourceCommit": { - "description": "Source commit hash", - "type": "string" - }, - "sourceRef": { - "description": "Source reference (branch/tag)", - "type": "string" - }, - "sourceUrl": { - "description": "Source URL", - "type": "string" - }, - "thirdPartyBugFeatureUrl": { - "description": "Third-party bug/feature URL", - "type": "string" - }, - "thirdPartyDocumentationUrl": { - "description": "Third-party documentation URL", - "type": "string" - }, - "thirdPartySourceUrl": { - "description": "Third-party source URL", - "type": "string" - }, - "thirdPartySupportUrl": { - "description": "Third-party support URL", + } + }, + "required": [ + "hideBuyButtonUntil", + "showSupportBadge" + ], + "type": "object" + }, + "PurchaseUpdate": { + "properties": { + "hideBuyButtonUntil": { + "description": "Date until which to hide buy button", "type": "string" }, - "version": { - "description": "Server version", - "type": "string" + "showSupportBadge": { + "description": "Whether to show support badge", + "type": "boolean" + } + }, + "type": "object" + }, + "QueueCommand": { + "description": "Queue command to execute", + "enum": [ + "start", + "pause", + "resume", + "empty", + "clear-failed" + ], + "type": "string" + }, + "QueueCommandDto": { + "properties": { + "command": { + "$ref": "#/components/schemas/QueueCommand" }, - "versionUrl": { - "description": "URL to version information", - "type": "string" + "force": { + "description": "Force the command execution (if applicable)", + "type": "boolean" } }, "required": [ - "licensed", - "version", - "versionUrl" + "command" ], "type": "object" }, - "ServerApkLinksDto": { + "QueueDeleteDto": { "properties": { - "arm64v8a": { - "description": "APK download link for ARM64 v8a architecture", - "type": "string" + "failed": { + "description": "If true, will also remove failed jobs from the queue.", + "type": "boolean", + "x-immich-history": [ + { + "version": "v2.4.0", + "state": "Added" + }, + { + "version": "v2.4.0", + "state": "Alpha" + } + ], + "x-immich-state": "Alpha" + } + }, + "type": "object" + }, + "QueueJobResponseDto": { + "properties": { + "data": { + "additionalProperties": {}, + "description": "Job data payload", + "type": "object" }, - "armeabiv7a": { - "description": "APK download link for ARM EABI v7a architecture", + "id": { + "description": "Job ID", "type": "string" }, - "universal": { - "description": "APK download link for universal architecture", - "type": "string" + "name": { + "$ref": "#/components/schemas/JobName" }, - "x86_64": { - "description": "APK download link for x86_64 architecture", - "type": "string" + "timestamp": { + "description": "Job creation timestamp", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" } }, "required": [ - "arm64v8a", - "armeabiv7a", - "universal", - "x86_64" + "data", + "name", + "timestamp" ], "type": "object" }, - "ServerConfigDto": { + "QueueJobStatus": { + "description": "Queue job status", + "enum": [ + "active", + "failed", + "completed", + "delayed", + "waiting", + "paused" + ], + "type": "string" + }, + "QueueName": { + "description": "Queue name", + "enum": [ + "thumbnailGeneration", + "metadataExtraction", + "videoConversion", + "faceDetection", + "facialRecognition", + "smartSearch", + "duplicateDetection", + "backgroundTask", + "storageTemplateMigration", + "migration", + "search", + "sidecar", + "library", + "notifications", + "backupDatabase", + "ocr", + "workflow", + "integrityCheck", + "editor" + ], + "type": "string" + }, + "QueueResponseDto": { "properties": { - "externalDomain": { - "description": "External domain URL", - "type": "string" - }, - "isInitialized": { - "description": "Whether the server has been initialized", - "type": "boolean" - }, - "isOnboarded": { - "description": "Whether the admin has completed onboarding", + "isPaused": { + "description": "Whether the queue is paused", "type": "boolean" }, - "loginPageMessage": { - "description": "Login page message", - "type": "string" - }, - "maintenanceMode": { - "description": "Whether maintenance mode is active", - "type": "boolean" + "name": { + "$ref": "#/components/schemas/QueueName" }, - "mapDarkStyleUrl": { - "description": "Map dark style URL", - "type": "string" + "statistics": { + "$ref": "#/components/schemas/QueueStatisticsDto" + } + }, + "required": [ + "isPaused", + "name", + "statistics" + ], + "type": "object" + }, + "QueueResponseLegacyDto": { + "properties": { + "jobCounts": { + "$ref": "#/components/schemas/QueueStatisticsDto" }, - "mapLightStyleUrl": { - "description": "Map light style URL", - "type": "string" + "queueStatus": { + "$ref": "#/components/schemas/QueueStatusLegacyDto" + } + }, + "required": [ + "jobCounts", + "queueStatus" + ], + "type": "object" + }, + "QueueStatisticsDto": { + "properties": { + "active": { + "description": "Number of active jobs", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" }, - "minFaces": { - "description": "People min faces server default", + "completed": { + "description": "Number of completed jobs", "maximum": 9007199254740991, "minimum": -9007199254740991, "type": "integer" }, - "oauthButtonText": { - "description": "OAuth button text", - "type": "string" + "delayed": { + "description": "Number of delayed jobs", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" }, - "publicUsers": { - "description": "Whether public user registration is enabled", - "type": "boolean" + "failed": { + "description": "Number of failed jobs", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" }, - "trashDays": { - "description": "Number of days before trashed assets are permanently deleted", + "paused": { + "description": "Number of paused jobs", "maximum": 9007199254740991, "minimum": -9007199254740991, "type": "integer" }, - "userDeleteDelay": { - "description": "Delay in days before deleted users are permanently removed", + "waiting": { + "description": "Number of waiting jobs", "maximum": 9007199254740991, "minimum": -9007199254740991, "type": "integer" } }, "required": [ - "externalDomain", - "isInitialized", - "isOnboarded", - "loginPageMessage", - "maintenanceMode", - "mapDarkStyleUrl", - "mapLightStyleUrl", - "minFaces", - "oauthButtonText", - "publicUsers", - "trashDays", - "userDeleteDelay" + "active", + "completed", + "delayed", + "failed", + "paused", + "waiting" ], "type": "object" }, - "ServerFeaturesDto": { + "QueueStatusLegacyDto": { "properties": { - "configFile": { - "description": "Whether config file is available", + "isActive": { + "description": "Whether the queue is currently active (has running jobs)", + "type": "boolean" + }, + "isPaused": { + "description": "Whether the queue is paused", + "type": "boolean" + } + }, + "required": [ + "isActive", + "isPaused" + ], + "type": "object" + }, + "QueueUpdateDto": { + "properties": { + "isPaused": { + "description": "Whether to pause the queue", "type": "boolean" + } + }, + "type": "object" + }, + "QueuesResponseLegacyDto": { + "properties": { + "backgroundTask": { + "$ref": "#/components/schemas/QueueResponseLegacyDto" }, - "duplicateDetection": { - "description": "Whether duplicate detection is enabled", - "type": "boolean" + "backupDatabase": { + "$ref": "#/components/schemas/QueueResponseLegacyDto" }, - "email": { - "description": "Whether email notifications are enabled", - "type": "boolean" + "duplicateDetection": { + "$ref": "#/components/schemas/QueueResponseLegacyDto" }, - "facialRecognition": { - "description": "Whether facial recognition is enabled", - "type": "boolean" + "editor": { + "$ref": "#/components/schemas/QueueResponseLegacyDto" }, - "importFaces": { - "description": "Whether face import is enabled", - "type": "boolean" + "faceDetection": { + "$ref": "#/components/schemas/QueueResponseLegacyDto" }, - "map": { - "description": "Whether map feature is enabled", - "type": "boolean" + "facialRecognition": { + "$ref": "#/components/schemas/QueueResponseLegacyDto" }, - "oauth": { - "description": "Whether OAuth is enabled", - "type": "boolean" + "integrityCheck": { + "$ref": "#/components/schemas/QueueResponseLegacyDto" }, - "oauthAutoLaunch": { - "description": "Whether OAuth auto-launch is enabled", - "type": "boolean" + "library": { + "$ref": "#/components/schemas/QueueResponseLegacyDto" }, - "ocr": { - "description": "Whether OCR is enabled", - "type": "boolean" + "metadataExtraction": { + "$ref": "#/components/schemas/QueueResponseLegacyDto" }, - "passwordLogin": { - "description": "Whether password login is enabled", - "type": "boolean" + "migration": { + "$ref": "#/components/schemas/QueueResponseLegacyDto" }, - "realtimeTranscoding": { - "description": "Whether real-time transcoding is enabled", - "type": "boolean" + "notifications": { + "$ref": "#/components/schemas/QueueResponseLegacyDto" }, - "reverseGeocoding": { - "description": "Whether reverse geocoding is enabled", - "type": "boolean" + "ocr": { + "$ref": "#/components/schemas/QueueResponseLegacyDto" }, "search": { - "description": "Whether search is enabled", - "type": "boolean" + "$ref": "#/components/schemas/QueueResponseLegacyDto" }, "sidecar": { - "description": "Whether sidecar files are supported", - "type": "boolean" + "$ref": "#/components/schemas/QueueResponseLegacyDto" }, "smartSearch": { - "description": "Whether smart search is enabled", - "type": "boolean" + "$ref": "#/components/schemas/QueueResponseLegacyDto" }, - "trash": { - "description": "Whether trash feature is enabled", - "type": "boolean" + "storageTemplateMigration": { + "$ref": "#/components/schemas/QueueResponseLegacyDto" + }, + "thumbnailGeneration": { + "$ref": "#/components/schemas/QueueResponseLegacyDto" + }, + "videoConversion": { + "$ref": "#/components/schemas/QueueResponseLegacyDto" + }, + "workflow": { + "$ref": "#/components/schemas/QueueResponseLegacyDto" } }, "required": [ - "configFile", + "backgroundTask", + "backupDatabase", "duplicateDetection", - "email", + "editor", + "faceDetection", "facialRecognition", - "importFaces", - "map", - "oauth", - "oauthAutoLaunch", + "integrityCheck", + "library", + "metadataExtraction", + "migration", + "notifications", "ocr", - "passwordLogin", - "realtimeTranscoding", - "reverseGeocoding", "search", "sidecar", "smartSearch", - "trash" + "storageTemplateMigration", + "thumbnailGeneration", + "videoConversion", + "workflow" ], "type": "object" }, - "ServerMediaTypesResponseDto": { + "RandomSearchDto": { "properties": { - "image": { - "description": "Supported image MIME types", + "albumIds": { + "description": "Filter by album IDs", "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, "type": "array" }, - "sidecar": { - "description": "Supported sidecar MIME types", - "items": { - "type": "string" - }, - "type": "array" + "city": { + "description": "Filter by city name", + "nullable": true, + "type": "string" }, - "video": { - "description": "Supported video MIME types", + "country": { + "description": "Filter by country name", + "nullable": true, + "type": "string" + }, + "createdAfter": { + "description": "Filter by creation date (after)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "createdBefore": { + "description": "Filter by creation date (before)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "isEncoded": { + "description": "Filter by encoded status", + "type": "boolean" + }, + "isFavorite": { + "description": "Filter by favorite status", + "type": "boolean" + }, + "isMotion": { + "description": "Filter by motion photo status", + "type": "boolean" + }, + "isNotInAlbum": { + "description": "Filter assets not in any album", + "type": "boolean" + }, + "isOffline": { + "description": "Filter by offline status", + "type": "boolean" + }, + "lensModel": { + "description": "Filter by lens model", + "nullable": true, + "type": "string" + }, + "libraryId": { + "description": "Library ID to filter by", + "format": "uuid", + "nullable": true, + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "make": { + "description": "Filter by camera make", + "nullable": true, + "type": "string" + }, + "model": { + "description": "Filter by camera model", + "nullable": true, + "type": "string" + }, + "ocr": { + "description": "Filter by OCR text content", + "type": "string" + }, + "personIds": { + "description": "Filter by person IDs", "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, "type": "array" - } - }, - "required": [ - "image", - "sidecar", - "video" - ], - "type": "object" - }, - "ServerPingResponse": { - "properties": { - "res": { - "example": "pong", - "type": "string" - } - }, - "required": [ - "res" - ], - "type": "object" - }, - "ServerStatsResponseDto": { - "properties": { - "photos": { - "description": "Total number of photos", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" }, - "usage": { - "description": "Total storage usage in bytes", - "maximum": 9007199254740991, - "minimum": -9007199254740991, + "rating": { + "description": "Filter by rating [1-5], or null for unrated", + "maximum": 5, + "minimum": 1, + "nullable": true, + "type": "integer", + "x-immich-history": [ + { + "version": "v1", + "state": "Added" + }, + { + "version": "v2", + "state": "Stable" + }, + { + "version": "v2.6.0", + "state": "Updated", + "description": "Using -1 as a rating is deprecated and will be removed in the next major version." + }, + { + "version": "v3", + "state": "Updated", + "description": "Using -1 as a rating is no longer valid." + } + ], + "x-immich-state": "Stable" + }, + "size": { + "description": "Number of results to return", + "maximum": 1000, + "minimum": 1, "type": "integer" }, - "usageByUser": { - "description": "Array of usage for each user", + "state": { + "description": "Filter by state/province name", + "nullable": true, + "type": "string" + }, + "tagIds": { + "description": "Filter by tag IDs", "items": { - "$ref": "#/components/schemas/UsageByUserDto" + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" }, + "nullable": true, "type": "array" }, - "usagePhotos": { - "description": "Storage usage for photos in bytes", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "usageVideos": { - "description": "Storage usage for videos in bytes", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "videos": { - "description": "Total number of videos", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - } - }, - "required": [ - "photos", - "usage", - "usageByUser", - "usagePhotos", - "usageVideos", - "videos" - ], - "type": "object" - }, - "ServerStorageResponseDto": { - "properties": { - "diskAvailable": { - "description": "Available disk space (human-readable format)", + "takenAfter": { + "description": "Filter by taken date (after)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "diskAvailableRaw": { - "description": "Available disk space in bytes", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "takenBefore": { + "description": "Filter by taken date (before)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" }, - "diskSize": { - "description": "Total disk size (human-readable format)", + "trashedAfter": { + "description": "Filter by trash date (after)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "diskSizeRaw": { - "description": "Total disk size in bytes", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "trashedBefore": { + "description": "Filter by trash date (before)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" }, - "diskUsagePercentage": { - "description": "Disk usage percentage (0-100)", - "format": "double", - "type": "number" + "type": { + "$ref": "#/components/schemas/AssetTypeEnum" }, - "diskUse": { - "description": "Used disk space (human-readable format)", + "updatedAfter": { + "description": "Filter by update date (after)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "diskUseRaw": { - "description": "Used disk space in bytes", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - } - }, - "required": [ - "diskAvailable", - "diskAvailableRaw", - "diskSize", - "diskSizeRaw", - "diskUsagePercentage", - "diskUse", - "diskUseRaw" - ], - "type": "object" - }, - "ServerVersionHistoryResponseDto": { - "properties": { - "createdAt": { - "description": "When this version was first seen", + "updatedBefore": { + "description": "Filter by update date (before)", "example": "2024-01-01T00:00:00.000Z", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "id": { - "description": "Version history entry ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" + "visibility": { + "$ref": "#/components/schemas/AssetVisibility" }, - "version": { - "description": "Version string", - "type": "string" + "withDeleted": { + "description": "Include deleted assets", + "type": "boolean" + }, + "withExif": { + "description": "Include EXIF data in response", + "type": "boolean" + }, + "withPeople": { + "description": "Include people data in response", + "type": "boolean" + }, + "withStacked": { + "description": "Include stacked assets", + "type": "boolean" } }, - "required": [ - "createdAt", - "id", - "version" - ], "type": "object" }, - "ServerVersionResponseDto": { + "RatingsResponse": { "properties": { - "major": { - "description": "Major version number", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "minor": { - "description": "Minor version number", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "patch": { - "description": "Patch version number", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "prerelease": { - "description": "Pre-release version number", - "maximum": 9007199254740991, - "minimum": 0, - "nullable": true, - "type": "integer", - "x-immich-history": [ - { - "version": "v3.0.0", - "state": "Added" - } - ] + "enabled": { + "description": "Whether ratings are enabled", + "type": "boolean" } }, "required": [ - "major", - "minor", - "patch", - "prerelease" + "enabled" ], "type": "object" }, - "SessionCreateDto": { + "RatingsUpdate": { "properties": { - "deviceOS": { - "description": "Device OS", - "type": "string" - }, - "deviceType": { - "description": "Device type", - "type": "string" - }, - "duration": { - "description": "Session duration in seconds", - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" + "enabled": { + "description": "Whether ratings are enabled", + "type": "boolean" } }, "type": "object" }, - "SessionCreateResponseDto": { + "ReactionLevel": { + "description": "Reaction level", + "enum": [ + "album", + "asset" + ], + "type": "string" + }, + "ReactionType": { + "description": "Reaction type", + "enum": [ + "comment", + "like" + ], + "type": "string" + }, + "RecentlyAddedResponse": { "properties": { - "appVersion": { - "description": "App version", - "nullable": true, - "type": "string" - }, - "createdAt": { - "description": "Creation date", - "type": "string" - }, - "current": { - "description": "Is current session", - "type": "boolean" - }, - "deviceOS": { - "description": "Device OS", - "type": "string" - }, - "deviceType": { - "description": "Device type", - "type": "string" - }, - "expiresAt": { - "description": "Expiration date", - "type": "string" - }, - "id": { - "description": "Session ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "isPendingSyncReset": { - "description": "Is pending sync reset", + "sidebarWeb": { + "description": "Whether the recently added page appears in the web sidebar", "type": "boolean" - }, - "token": { - "description": "Session token", - "type": "string" - }, - "updatedAt": { - "description": "Last update date", - "type": "string" } }, "required": [ - "appVersion", - "createdAt", - "current", - "deviceOS", - "deviceType", - "id", - "isPendingSyncReset", - "token", - "updatedAt" + "sidebarWeb" ], "type": "object" }, - "SessionResponseDto": { + "RecentlyAddedUpdate": { "properties": { - "appVersion": { - "description": "App version", - "nullable": true, - "type": "string" - }, - "createdAt": { - "description": "Creation date", - "type": "string" - }, - "current": { - "description": "Is current session", + "sidebarWeb": { + "description": "Whether the recently added page appears in the web sidebar", "type": "boolean" - }, - "deviceOS": { - "description": "Device OS", - "type": "string" - }, - "deviceType": { - "description": "Device type", + } + }, + "type": "object" + }, + "ReleaseChannel": { + "description": "Release channel", + "enum": [ + "stable", + "releaseCandidate" + ], + "type": "string" + }, + "ReleaseEventV1": { + "properties": { + "checkedAt": { + "description": "When the server last checked for a latest version. As an ISO timestamp", "type": "string" }, - "expiresAt": { - "description": "Expiration date", - "type": "string" + "isAvailable": { + "description": "Whether a new version is available", + "type": "boolean" }, - "id": { - "description": "Session ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" + "releaseVersion": { + "$ref": "#/components/schemas/ServerVersionResponseDto" }, - "isPendingSyncReset": { - "description": "Is pending sync reset", - "type": "boolean" + "serverVersion": { + "$ref": "#/components/schemas/ServerVersionResponseDto" }, - "updatedAt": { - "description": "Last update date", - "type": "string" + "type": { + "$ref": "#/components/schemas/ReleaseType", + "description": "Release type", + "nullable": true } }, "required": [ - "appVersion", - "createdAt", - "current", - "deviceOS", - "deviceType", - "id", - "isPendingSyncReset", - "updatedAt" + "checkedAt", + "isAvailable", + "releaseVersion", + "serverVersion", + "type" ], "type": "object" }, - "SessionUnlockDto": { + "ReleaseType": { + "enum": [ + "major", + "premajor", + "minor", + "preminor", + "patch", + "prepatch", + "prerelease" + ], + "type": "string" + }, + "ReverseGeocodingStateResponseDto": { "properties": { - "password": { - "description": "User password (required if PIN code is not provided)", - "example": "password", + "lastImportFileName": { + "description": "Last import file name", + "nullable": true, "type": "string" }, - "pinCode": { - "description": "New PIN code (4-6 digits)", - "example": "123456", - "pattern": "^\\d{6}$", + "lastUpdate": { + "description": "Last update timestamp", + "nullable": true, "type": "string" } }, + "required": [ + "lastImportFileName", + "lastUpdate" + ], "type": "object" }, - "SessionUpdateDto": { + "RotateParameters": { "properties": { - "isPendingSyncReset": { - "description": "Reset pending sync state", - "type": "boolean" + "angle": { + "description": "Rotation angle in degrees", + "type": "number" } }, + "required": [ + "angle" + ], "type": "object" }, - "SetMaintenanceModeDto": { + "SearchAlbumResponseDto": { "properties": { - "action": { - "$ref": "#/components/schemas/MaintenanceAction" + "count": { + "description": "Number of albums in this page", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" }, - "restoreBackupFilename": { - "description": "Restore backup filename", - "type": "string" + "facets": { + "items": { + "$ref": "#/components/schemas/SearchFacetResponseDto" + }, + "type": "array" + }, + "items": { + "items": { + "$ref": "#/components/schemas/AlbumResponseDto" + }, + "type": "array" + }, + "total": { + "description": "Total number of matching albums", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" } }, "required": [ - "action" + "count", + "facets", + "items", + "total" ], "type": "object" }, - "SharedLinkCreateDto": { + "SearchAssetResponseDto": { "properties": { - "albumId": { - "description": "Album ID (for album sharing)", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "allowDownload": { - "default": true, - "description": "Allow downloads", - "type": "boolean" + "count": { + "description": "Number of assets in this page", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" }, - "allowUpload": { - "description": "Allow uploads", - "type": "boolean" + "facets": { + "items": { + "$ref": "#/components/schemas/SearchFacetResponseDto" + }, + "type": "array" }, - "assetIds": { - "description": "Asset IDs (for individual assets)", + "items": { "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" + "$ref": "#/components/schemas/AssetResponseDto" }, "type": "array" }, - "description": { - "description": "Link description", + "nextPage": { + "description": "Next page token", "nullable": true, "type": "string" }, - "expiresAt": { - "default": null, - "description": "Expiration date", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "nullable": true, - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" + "total": { + "description": "Total number of matching assets", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer", + "x-immich-history": [ + { + "version": "v3.0.0", + "state": "Deprecated" + } + ], + "x-immich-state": "Deprecated" + } + }, + "required": [ + "count", + "facets", + "items", + "nextPage", + "total" + ], + "type": "object" + }, + "SearchExploreItem": { + "properties": { + "data": { + "$ref": "#/components/schemas/AssetResponseDto" }, - "password": { - "description": "Link password", - "nullable": true, + "value": { + "description": "Explore value", "type": "string" - }, - "showMetadata": { - "default": true, - "description": "Show metadata", - "type": "boolean" - }, - "slug": { - "description": "Custom URL slug", - "nullable": true, + } + }, + "required": [ + "data", + "value" + ], + "type": "object" + }, + "SearchExploreResponseDto": { + "properties": { + "fieldName": { + "description": "Explore field name", "type": "string" }, - "type": { - "$ref": "#/components/schemas/SharedLinkType" + "items": { + "items": { + "$ref": "#/components/schemas/SearchExploreItem" + }, + "type": "array" } }, "required": [ - "type" + "fieldName", + "items" ], "type": "object" }, - "SharedLinkEditDto": { + "SearchFacetCountResponseDto": { "properties": { - "allowDownload": { - "description": "Allow downloads", - "type": "boolean" - }, - "allowUpload": { - "description": "Allow uploads", - "type": "boolean" - }, - "description": { - "description": "Link description", - "nullable": true, - "type": "string" + "count": { + "description": "Number of assets with this facet value", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" }, - "expiresAt": { - "description": "Expiration date", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "nullable": true, - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "value": { + "description": "Facet value", "type": "string" + } + }, + "required": [ + "count", + "value" + ], + "type": "object" + }, + "SearchFacetResponseDto": { + "properties": { + "counts": { + "items": { + "$ref": "#/components/schemas/SearchFacetCountResponseDto" + }, + "type": "array" }, - "password": { - "description": "Link password", - "nullable": true, + "fieldName": { + "description": "Facet field name", "type": "string" + } + }, + "required": [ + "counts", + "fieldName" + ], + "type": "object" + }, + "SearchResponseDto": { + "properties": { + "albums": { + "$ref": "#/components/schemas/SearchAlbumResponseDto" }, - "showMetadata": { - "description": "Show metadata", - "type": "boolean" - }, - "slug": { - "description": "Custom URL slug", - "nullable": true, - "type": "string" + "assets": { + "$ref": "#/components/schemas/SearchAssetResponseDto" } }, + "required": [ + "albums", + "assets" + ], "type": "object" }, - "SharedLinkLoginDto": { + "SearchStatisticsResponseDto": { "properties": { - "password": { - "description": "Shared link password", - "example": "password", - "type": "string" + "total": { + "description": "Total number of matching assets", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" } }, "required": [ - "password" + "total" ], "type": "object" }, - "SharedLinkResponseDto": { - "description": "Shared link response", + "SearchSuggestionType": { + "description": "Suggestion type", + "enum": [ + "country", + "state", + "city", + "camera-make", + "camera-model", + "camera-lens-model" + ], + "type": "string" + }, + "ServerAboutResponseDto": { "properties": { - "album": { - "$ref": "#/components/schemas/AlbumResponseDto" + "build": { + "description": "Build identifier", + "type": "string" }, - "allowDownload": { - "description": "Allow downloads", - "type": "boolean" + "buildImage": { + "description": "Build image name", + "type": "string" }, - "allowUpload": { - "description": "Allow uploads", - "type": "boolean" + "buildImageUrl": { + "description": "Build image URL", + "type": "string" }, - "assets": { - "items": { - "$ref": "#/components/schemas/AssetResponseDto" - }, - "type": "array" + "buildUrl": { + "description": "Build URL", + "type": "string" }, - "createdAt": { - "description": "Creation date", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "exiftool": { + "description": "ExifTool version", "type": "string" }, - "description": { - "description": "Link description", - "nullable": true, + "ffmpeg": { + "description": "FFmpeg version", "type": "string" }, - "expiresAt": { - "description": "Expiration date", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "nullable": true, - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "imagemagick": { + "description": "ImageMagick version", "type": "string" }, - "id": { - "description": "Shared link ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "libvips": { + "description": "libvips version", "type": "string" }, - "key": { - "description": "Encryption key (base64url)", + "licensed": { + "description": "Whether the server is licensed", + "type": "boolean" + }, + "nodejs": { + "description": "Node.js version", "type": "string" }, - "password": { - "description": "Has password", - "nullable": true, + "repository": { + "description": "Repository name", "type": "string" }, - "showMetadata": { - "description": "Show metadata", - "type": "boolean" + "repositoryUrl": { + "description": "Repository URL", + "type": "string" }, - "slug": { - "description": "Custom URL slug", - "nullable": true, + "sourceCommit": { + "description": "Source commit hash", "type": "string" }, - "type": { - "$ref": "#/components/schemas/SharedLinkType" + "sourceRef": { + "description": "Source reference (branch/tag)", + "type": "string" }, - "userId": { - "description": "Owner user ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "sourceUrl": { + "description": "Source URL", + "type": "string" + }, + "thirdPartyBugFeatureUrl": { + "description": "Third-party bug/feature URL", + "type": "string" + }, + "thirdPartyDocumentationUrl": { + "description": "Third-party documentation URL", + "type": "string" + }, + "thirdPartySourceUrl": { + "description": "Third-party source URL", + "type": "string" + }, + "thirdPartySupportUrl": { + "description": "Third-party support URL", "type": "string" - } - }, - "required": [ - "allowDownload", - "allowUpload", - "assets", - "createdAt", - "description", - "expiresAt", - "id", - "key", - "password", - "showMetadata", - "slug", - "type", - "userId" - ], - "type": "object" - }, - "SharedLinkType": { - "description": "Shared link type", - "enum": [ - "ALBUM", - "INDIVIDUAL" - ], - "type": "string" - }, - "SharedLinksResponse": { - "properties": { - "enabled": { - "description": "Whether shared links are enabled", - "type": "boolean" }, - "sidebarWeb": { - "description": "Whether shared links appear in web sidebar", - "type": "boolean" + "version": { + "description": "Server version", + "type": "string" + }, + "versionUrl": { + "description": "URL to version information", + "type": "string" } }, "required": [ - "enabled", - "sidebarWeb" + "licensed", + "version", + "versionUrl" ], "type": "object" }, - "SharedLinksUpdate": { + "ServerApkLinksDto": { "properties": { - "enabled": { - "description": "Whether shared links are enabled", - "type": "boolean" + "arm64v8a": { + "description": "APK download link for ARM64 v8a architecture", + "type": "string" }, - "sidebarWeb": { - "description": "Whether shared links appear in web sidebar", - "type": "boolean" - } - }, - "type": "object" - }, - "SignUpDto": { - "properties": { - "email": { - "description": "User email", - "example": "testuser@email.com", - "format": "email", - "pattern": "^[\\p{L}\\p{M}\\p{N}.!#$%&'*+/=?^_`{|}~-]+@[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?(?:\\.[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?)*$", + "armeabiv7a": { + "description": "APK download link for ARM EABI v7a architecture", "type": "string" }, - "name": { - "description": "User name", - "example": "Admin", + "universal": { + "description": "APK download link for universal architecture", "type": "string" }, - "password": { - "description": "User password", - "example": "password", + "x86_64": { + "description": "APK download link for x86_64 architecture", "type": "string" } }, "required": [ - "email", - "name", - "password" + "arm64v8a", + "armeabiv7a", + "universal", + "x86_64" ], "type": "object" }, - "SmartSearchDto": { + "ServerConfigDto": { "properties": { - "albumIds": { - "description": "Filter by album IDs", - "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "type": "array" - }, - "city": { - "description": "Filter by city name", - "nullable": true, - "type": "string" - }, - "country": { - "description": "Filter by country name", - "nullable": true, - "type": "string" - }, - "createdAfter": { - "description": "Filter by creation date (after)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" - }, - "createdBefore": { - "description": "Filter by creation date (before)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "externalDomain": { + "description": "External domain URL", "type": "string" }, - "isEncoded": { - "description": "Filter by encoded status", - "type": "boolean" - }, - "isFavorite": { - "description": "Filter by favorite status", - "type": "boolean" - }, - "isMotion": { - "description": "Filter by motion photo status", - "type": "boolean" - }, - "isNotInAlbum": { - "description": "Filter assets not in any album", + "isInitialized": { + "description": "Whether the server has been initialized", "type": "boolean" }, - "isOffline": { - "description": "Filter by offline status", + "isOnboarded": { + "description": "Whether the admin has completed onboarding", "type": "boolean" }, - "language": { - "description": "Search language code", - "type": "string" - }, - "lensModel": { - "description": "Filter by lens model", - "nullable": true, - "type": "string" - }, - "libraryId": { - "description": "Library ID to filter by", - "format": "uuid", - "nullable": true, - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "loginPageMessage": { + "description": "Login page message", "type": "string" }, - "make": { - "description": "Filter by camera make", - "nullable": true, - "type": "string" + "maintenanceMode": { + "description": "Whether maintenance mode is active", + "type": "boolean" }, - "model": { - "description": "Filter by camera model", - "nullable": true, + "mapDarkStyleUrl": { + "description": "Map dark style URL", "type": "string" }, - "ocr": { - "description": "Filter by OCR text content", + "mapLightStyleUrl": { + "description": "Map light style URL", "type": "string" }, - "page": { - "description": "Page number", + "minFaces": { + "description": "People min faces server default", "maximum": 9007199254740991, - "minimum": 1, + "minimum": -9007199254740991, "type": "integer" }, - "personIds": { - "description": "Filter by person IDs", - "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "type": "array" - }, - "query": { - "description": "Natural language search query", + "oauthButtonText": { + "description": "OAuth button text", "type": "string" }, - "queryAssetId": { - "description": "Asset ID to use as search reference", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" + "publicUsers": { + "description": "Whether public user registration is enabled", + "type": "boolean" }, - "rating": { - "description": "Filter by rating [1-5], or null for unrated", - "maximum": 5, - "minimum": 1, - "nullable": true, - "type": "integer", - "x-immich-history": [ - { - "version": "v1", - "state": "Added" - }, - { - "version": "v2", - "state": "Stable" - }, - { - "version": "v2.6.0", - "state": "Updated", - "description": "Using -1 as a rating is deprecated and will be removed in the next major version." - }, - { - "version": "v3", - "state": "Updated", - "description": "Using -1 as a rating is no longer valid." - } - ], - "x-immich-state": "Stable" + "trashDays": { + "description": "Number of days before trashed assets are permanently deleted", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" }, - "size": { - "description": "Number of results to return", - "maximum": 1000, - "minimum": 1, + "userDeleteDelay": { + "description": "Delay in days before deleted users are permanently removed", + "maximum": 9007199254740991, + "minimum": -9007199254740991, "type": "integer" + } + }, + "required": [ + "externalDomain", + "isInitialized", + "isOnboarded", + "loginPageMessage", + "maintenanceMode", + "mapDarkStyleUrl", + "mapLightStyleUrl", + "minFaces", + "oauthButtonText", + "publicUsers", + "trashDays", + "userDeleteDelay" + ], + "type": "object" + }, + "ServerFeaturesDto": { + "properties": { + "configFile": { + "description": "Whether config file is available", + "type": "boolean" }, - "state": { - "description": "Filter by state/province name", - "nullable": true, - "type": "string" + "duplicateDetection": { + "description": "Whether duplicate detection is enabled", + "type": "boolean" }, - "tagIds": { - "description": "Filter by tag IDs", - "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "nullable": true, - "type": "array" + "email": { + "description": "Whether email notifications are enabled", + "type": "boolean" + }, + "facialRecognition": { + "description": "Whether facial recognition is enabled", + "type": "boolean" }, - "takenAfter": { - "description": "Filter by taken date (after)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" + "importFaces": { + "description": "Whether face import is enabled", + "type": "boolean" }, - "takenBefore": { - "description": "Filter by taken date (before)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" + "map": { + "description": "Whether map feature is enabled", + "type": "boolean" }, - "trashedAfter": { - "description": "Filter by trash date (after)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" + "oauth": { + "description": "Whether OAuth is enabled", + "type": "boolean" }, - "trashedBefore": { - "description": "Filter by trash date (before)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" + "oauthAutoLaunch": { + "description": "Whether OAuth auto-launch is enabled", + "type": "boolean" }, - "type": { - "$ref": "#/components/schemas/AssetTypeEnum" + "ocr": { + "description": "Whether OCR is enabled", + "type": "boolean" }, - "updatedAfter": { - "description": "Filter by update date (after)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" + "passwordLogin": { + "description": "Whether password login is enabled", + "type": "boolean" }, - "updatedBefore": { - "description": "Filter by update date (before)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" + "realtimeTranscoding": { + "description": "Whether real-time transcoding is enabled", + "type": "boolean" }, - "visibility": { - "$ref": "#/components/schemas/AssetVisibility" + "reverseGeocoding": { + "description": "Whether reverse geocoding is enabled", + "type": "boolean" }, - "withDeleted": { - "description": "Include deleted assets", + "search": { + "description": "Whether search is enabled", "type": "boolean" }, - "withExif": { - "description": "Include EXIF data in response", + "sidecar": { + "description": "Whether sidecar files are supported", + "type": "boolean" + }, + "smartSearch": { + "description": "Whether smart search is enabled", + "type": "boolean" + }, + "trash": { + "description": "Whether trash feature is enabled", "type": "boolean" } }, - "type": "object" - }, - "SourceType": { - "description": "Face detection source type", - "enum": [ - "machine-learning", - "exif", - "manual" + "required": [ + "configFile", + "duplicateDetection", + "email", + "facialRecognition", + "importFaces", + "map", + "oauth", + "oauthAutoLaunch", + "ocr", + "passwordLogin", + "realtimeTranscoding", + "reverseGeocoding", + "search", + "sidecar", + "smartSearch", + "trash" ], - "type": "string" + "type": "object" }, - "StackCreateDto": { + "ServerMediaTypesResponseDto": { "properties": { - "assetIds": { - "description": "Asset IDs (first becomes primary, min 2)", + "image": { + "description": "Supported image MIME types", "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "minItems": 2, "type": "array" - } - }, - "required": [ - "assetIds" - ], - "type": "object" - }, - "StackResponseDto": { - "description": "Stack response", - "properties": { - "assets": { + }, + "sidecar": { + "description": "Supported sidecar MIME types", "items": { - "$ref": "#/components/schemas/AssetResponseDto" + "type": "string" }, "type": "array" }, - "id": { - "description": "Stack ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "primaryAssetId": { - "description": "Primary asset ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" + "video": { + "description": "Supported video MIME types", + "items": { + "type": "string" + }, + "type": "array" } }, "required": [ - "assets", - "id", - "primaryAssetId" + "image", + "sidecar", + "video" ], "type": "object" }, - "StackUpdateDto": { + "ServerPingResponse": { "properties": { - "primaryAssetId": { - "description": "Primary asset ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "res": { + "example": "pong", "type": "string" } }, + "required": [ + "res" + ], "type": "object" }, - "StatisticsSearchDto": { + "ServerStatsResponseDto": { "properties": { - "albumIds": { - "description": "Filter by album IDs", - "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "type": "array" - }, - "city": { - "description": "Filter by city name", - "nullable": true, - "type": "string" - }, - "country": { - "description": "Filter by country name", - "nullable": true, - "type": "string" - }, - "createdAfter": { - "description": "Filter by creation date (after)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" - }, - "createdBefore": { - "description": "Filter by creation date (before)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" - }, - "description": { - "description": "Filter by description text", - "type": "string" - }, - "isEncoded": { - "description": "Filter by encoded status", - "type": "boolean" - }, - "isFavorite": { - "description": "Filter by favorite status", - "type": "boolean" - }, - "isMotion": { - "description": "Filter by motion photo status", - "type": "boolean" - }, - "isNotInAlbum": { - "description": "Filter assets not in any album", - "type": "boolean" - }, - "isOffline": { - "description": "Filter by offline status", - "type": "boolean" - }, - "lensModel": { - "description": "Filter by lens model", - "nullable": true, - "type": "string" - }, - "libraryId": { - "description": "Library ID to filter by", - "format": "uuid", - "nullable": true, - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "make": { - "description": "Filter by camera make", - "nullable": true, - "type": "string" - }, - "model": { - "description": "Filter by camera model", - "nullable": true, - "type": "string" - }, - "ocr": { - "description": "Filter by OCR text content", - "type": "string" + "photos": { + "description": "Total number of photos", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" }, - "personIds": { - "description": "Filter by person IDs", + "usage": { + "description": "Total storage usage in bytes", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "usageByUser": { + "description": "Array of usage for each user", "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" + "$ref": "#/components/schemas/UsageByUserDto" }, "type": "array" }, - "rating": { - "description": "Filter by rating [1-5], or null for unrated", - "maximum": 5, - "minimum": 1, - "nullable": true, - "type": "integer", - "x-immich-history": [ - { - "version": "v1", - "state": "Added" - }, - { - "version": "v2", - "state": "Stable" - }, - { - "version": "v2.6.0", - "state": "Updated", - "description": "Using -1 as a rating is deprecated and will be removed in the next major version." - }, - { - "version": "v3", - "state": "Updated", - "description": "Using -1 as a rating is no longer valid." - } - ], - "x-immich-state": "Stable" + "usagePhotos": { + "description": "Storage usage for photos in bytes", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" }, - "state": { - "description": "Filter by state/province name", - "nullable": true, + "usageVideos": { + "description": "Storage usage for videos in bytes", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "videos": { + "description": "Total number of videos", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "photos", + "usage", + "usageByUser", + "usagePhotos", + "usageVideos", + "videos" + ], + "type": "object" + }, + "ServerStorageResponseDto": { + "properties": { + "diskAvailable": { + "description": "Available disk space (human-readable format)", "type": "string" }, - "tagIds": { - "description": "Filter by tag IDs", - "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "nullable": true, - "type": "array" + "diskAvailableRaw": { + "description": "Available disk space in bytes", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" }, - "takenAfter": { - "description": "Filter by taken date (after)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "diskSize": { + "description": "Total disk size (human-readable format)", "type": "string" }, - "takenBefore": { - "description": "Filter by taken date (before)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" + "diskSizeRaw": { + "description": "Total disk size in bytes", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" }, - "trashedAfter": { - "description": "Filter by trash date (after)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" + "diskUsagePercentage": { + "description": "Disk usage percentage (0-100)", + "format": "double", + "type": "number" }, - "trashedBefore": { - "description": "Filter by trash date (before)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "diskUse": { + "description": "Used disk space (human-readable format)", "type": "string" }, - "type": { - "$ref": "#/components/schemas/AssetTypeEnum" - }, - "updatedAfter": { - "description": "Filter by update date (after)", + "diskUseRaw": { + "description": "Used disk space in bytes", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "diskAvailable", + "diskAvailableRaw", + "diskSize", + "diskSizeRaw", + "diskUsagePercentage", + "diskUse", + "diskUseRaw" + ], + "type": "object" + }, + "ServerVersionHistoryResponseDto": { + "properties": { + "createdAt": { + "description": "When this version was first seen", "example": "2024-01-01T00:00:00.000Z", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "updatedBefore": { - "description": "Filter by update date (before)", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "id": { + "description": "Version history entry ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "visibility": { - "$ref": "#/components/schemas/AssetVisibility" + "version": { + "description": "Version string", + "type": "string" } }, - "type": "object" - }, - "StorageFolder": { - "description": "Storage folder", - "enum": [ - "encoded-video", - "library", - "upload", - "profile", - "thumbs", - "backups" + "required": [ + "createdAt", + "id", + "version" ], - "type": "string" - }, - "SyncAckDeleteDto": { - "properties": { - "types": { - "description": "Sync entity types to delete acks for", - "items": { - "$ref": "#/components/schemas/SyncEntityType" - }, - "type": "array" - } - }, "type": "object" }, - "SyncAckDto": { + "ServerVersionResponseDto": { "properties": { - "ack": { - "description": "Acknowledgment ID", - "type": "string" + "major": { + "description": "Major version number", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" }, - "type": { - "$ref": "#/components/schemas/SyncEntityType" + "minor": { + "description": "Minor version number", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "patch": { + "description": "Patch version number", + "maximum": 9007199254740991, + "minimum": 0, + "type": "integer" + }, + "prerelease": { + "description": "Pre-release version number", + "maximum": 9007199254740991, + "minimum": 0, + "nullable": true, + "type": "integer", + "x-immich-history": [ + { + "version": "v3.0.0", + "state": "Added" + } + ] } }, "required": [ - "ack", - "type" + "major", + "minor", + "patch", + "prerelease" ], "type": "object" }, - "SyncAckSetDto": { + "SessionCreateDto": { "properties": { - "acks": { - "description": "Acknowledgment IDs (max 1000)", - "items": { - "type": "string" - }, - "maxItems": 1000, - "type": "array" + "deviceOS": { + "description": "Device OS", + "type": "string" + }, + "deviceType": { + "description": "Device type", + "type": "string" + }, + "duration": { + "description": "Session duration in seconds", + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" } }, - "required": [ - "acks" - ], - "type": "object" - }, - "SyncAckV1": { - "properties": {}, "type": "object" }, - "SyncAlbumDeleteV1": { + "SessionCreateResponseDto": { "properties": { - "albumId": { - "description": "Album ID", + "appVersion": { + "description": "App version", + "nullable": true, + "type": "string" + }, + "createdAt": { + "description": "Creation date", + "type": "string" + }, + "current": { + "description": "Is current session", + "type": "boolean" + }, + "deviceOS": { + "description": "Device OS", + "type": "string" + }, + "deviceType": { + "description": "Device type", + "type": "string" + }, + "expiresAt": { + "description": "Expiration date", + "type": "string" + }, + "id": { + "description": "Session ID", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" + }, + "isPendingSyncReset": { + "description": "Is pending sync reset", + "type": "boolean" + }, + "token": { + "description": "Session token", + "type": "string" + }, + "updatedAt": { + "description": "Last update date", + "type": "string" } }, "required": [ - "albumId" + "appVersion", + "createdAt", + "current", + "deviceOS", + "deviceType", + "id", + "isPendingSyncReset", + "token", + "updatedAt" ], "type": "object" }, - "SyncAlbumToAssetDeleteV1": { + "SessionResponseDto": { "properties": { - "albumId": { - "description": "Album ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "appVersion": { + "description": "App version", + "nullable": true, "type": "string" }, - "assetId": { - "description": "Asset ID", + "createdAt": { + "description": "Creation date", + "type": "string" + }, + "current": { + "description": "Is current session", + "type": "boolean" + }, + "deviceOS": { + "description": "Device OS", + "type": "string" + }, + "deviceType": { + "description": "Device type", + "type": "string" + }, + "expiresAt": { + "description": "Expiration date", + "type": "string" + }, + "id": { + "description": "Session ID", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" + }, + "isPendingSyncReset": { + "description": "Is pending sync reset", + "type": "boolean" + }, + "updatedAt": { + "description": "Last update date", + "type": "string" } }, "required": [ - "albumId", - "assetId" + "appVersion", + "createdAt", + "current", + "deviceOS", + "deviceType", + "id", + "isPendingSyncReset", + "updatedAt" ], "type": "object" }, - "SyncAlbumToAssetV1": { + "SessionUnlockDto": { "properties": { - "albumId": { - "description": "Album ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "password": { + "description": "User password (required if PIN code is not provided)", + "example": "password", "type": "string" }, - "assetId": { - "description": "Asset ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "pinCode": { + "description": "New PIN code (4-6 digits)", + "example": "123456", + "pattern": "^\\d{6}$", "type": "string" } }, - "required": [ - "albumId", - "assetId" - ], "type": "object" }, - "SyncAlbumUserDeleteV1": { + "SessionUpdateDto": { "properties": { - "albumId": { - "description": "Album ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" + "isPendingSyncReset": { + "description": "Reset pending sync state", + "type": "boolean" + } + }, + "type": "object" + }, + "SetMaintenanceModeDto": { + "properties": { + "action": { + "$ref": "#/components/schemas/MaintenanceAction" }, - "userId": { - "description": "User ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "restoreBackupFilename": { + "description": "Restore backup filename", "type": "string" } }, "required": [ - "albumId", - "userId" + "action" ], "type": "object" }, - "SyncAlbumUserV1": { + "SharedLinkCreateDto": { "properties": { "albumId": { - "description": "Album ID", + "description": "Album ID (for album sharing)", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "role": { - "$ref": "#/components/schemas/AlbumUserRole" + "allowDownload": { + "default": true, + "description": "Allow downloads", + "type": "boolean" }, - "userId": { - "description": "User ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "allowUpload": { + "description": "Allow uploads", + "type": "boolean" + }, + "assetIds": { + "description": "Asset IDs (for individual assets)", + "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "type": "array" + }, + "description": { + "description": "Link description", + "nullable": true, "type": "string" - } - }, - "required": [ - "albumId", - "role", - "userId" - ], - "type": "object" - }, - "SyncAlbumV1": { - "properties": { - "createdAt": { - "description": "Created at", + }, + "expiresAt": { + "default": null, + "description": "Expiration date", "example": "2024-01-01T00:00:00.000Z", "format": "date-time", + "nullable": true, "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "description": { - "description": "Album description", - "type": "string" - }, - "id": { - "description": "Album ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "password": { + "description": "Link password", + "nullable": true, "type": "string" }, - "isActivityEnabled": { - "description": "Is activity enabled", + "showMetadata": { + "default": true, + "description": "Show metadata", "type": "boolean" }, - "name": { - "description": "Album name", + "slug": { + "description": "Custom URL slug", + "nullable": true, "type": "string" }, - "order": { - "$ref": "#/components/schemas/AssetOrder" + "type": { + "$ref": "#/components/schemas/SharedLinkType" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + "SharedLinkEditDto": { + "properties": { + "allowDownload": { + "description": "Allow downloads", + "type": "boolean" }, - "ownerId": { - "description": "Owner ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" + "allowUpload": { + "description": "Allow uploads", + "type": "boolean" }, - "thumbnailAssetId": { - "description": "Thumbnail asset ID", + "description": { + "description": "Link description", "nullable": true, "type": "string" }, - "updatedAt": { - "description": "Updated at", + "expiresAt": { + "description": "Expiration date", "example": "2024-01-01T00:00:00.000Z", "format": "date-time", + "nullable": true, "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" + }, + "password": { + "description": "Link password", + "nullable": true, + "type": "string" + }, + "showMetadata": { + "description": "Show metadata", + "type": "boolean" + }, + "slug": { + "description": "Custom URL slug", + "nullable": true, + "type": "string" + } + }, + "type": "object" + }, + "SharedLinkLoginDto": { + "properties": { + "password": { + "description": "Shared link password", + "example": "password", + "type": "string" } }, "required": [ - "createdAt", - "description", - "id", - "isActivityEnabled", - "name", - "order", - "ownerId", - "thumbnailAssetId", - "updatedAt" + "password" ], "type": "object" }, - "SyncAlbumV2": { + "SharedLinkResponseDto": { + "description": "Shared link response", "properties": { + "album": { + "$ref": "#/components/schemas/AlbumResponseDto" + }, + "allowDownload": { + "description": "Allow downloads", + "type": "boolean" + }, + "allowUpload": { + "description": "Allow uploads", + "type": "boolean" + }, + "assets": { + "items": { + "$ref": "#/components/schemas/AssetResponseDto" + }, + "type": "array" + }, "createdAt": { - "description": "Created at", + "description": "Creation date", "example": "2024-01-01T00:00:00.000Z", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, "description": { - "description": "Album description", + "description": "Link description", + "nullable": true, + "type": "string" + }, + "expiresAt": { + "description": "Expiration date", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "nullable": true, + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, "id": { - "description": "Album ID", + "description": "Shared link ID", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "isActivityEnabled": { - "description": "Is activity enabled", - "type": "boolean" + "key": { + "description": "Encryption key (base64url)", + "type": "string" }, - "name": { - "description": "Album name", + "password": { + "description": "Has password", + "nullable": true, "type": "string" }, - "order": { - "$ref": "#/components/schemas/AssetOrder" + "showMetadata": { + "description": "Show metadata", + "type": "boolean" }, - "thumbnailAssetId": { - "description": "Thumbnail asset ID", + "slug": { + "description": "Custom URL slug", "nullable": true, "type": "string" }, - "updatedAt": { - "description": "Updated at", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": { + "$ref": "#/components/schemas/SharedLinkType" + }, + "userId": { + "description": "Owner user ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" } }, "required": [ + "allowDownload", + "allowUpload", + "assets", "createdAt", "description", + "expiresAt", "id", - "isActivityEnabled", - "name", - "order", - "thumbnailAssetId", - "updatedAt" + "key", + "password", + "showMetadata", + "slug", + "type", + "userId" ], "type": "object" }, - "SyncAssetDeleteV1": { + "SharedLinkType": { + "description": "Shared link type", + "enum": [ + "ALBUM", + "INDIVIDUAL" + ], + "type": "string" + }, + "SharedLinksResponse": { "properties": { - "assetId": { - "description": "Asset ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" + "enabled": { + "description": "Whether shared links are enabled", + "type": "boolean" + }, + "sidebarWeb": { + "description": "Whether shared links appear in web sidebar", + "type": "boolean" } }, "required": [ - "assetId" + "enabled", + "sidebarWeb" ], "type": "object" }, - "SyncAssetEditDeleteV1": { + "SharedLinksUpdate": { "properties": { - "editId": { - "description": "Edit ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" + "enabled": { + "description": "Whether shared links are enabled", + "type": "boolean" + }, + "sidebarWeb": { + "description": "Whether shared links appear in web sidebar", + "type": "boolean" } }, - "required": [ - "editId" - ], "type": "object" }, - "SyncAssetEditV1": { + "SignUpDto": { "properties": { - "action": { - "$ref": "#/components/schemas/AssetEditAction" - }, - "assetId": { - "description": "Asset ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "email": { + "description": "User email", + "example": "testuser@email.com", + "format": "email", + "pattern": "^[\\p{L}\\p{M}\\p{N}.!#$%&'*+/=?^_`{|}~-]+@[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?(?:\\.[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?)*$", "type": "string" }, - "id": { - "description": "Edit ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "name": { + "description": "User name", + "example": "Admin", "type": "string" }, - "parameters": { - "additionalProperties": {}, - "description": "Edit parameters", - "type": "object" - }, - "sequence": { - "description": "Edit sequence", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "password": { + "description": "User password", + "example": "password", + "type": "string" } }, "required": [ - "action", - "assetId", - "id", - "parameters", - "sequence" + "email", + "name", + "password" ], "type": "object" }, - "SyncAssetExifV1": { + "SmartSearchDto": { "properties": { - "assetId": { - "description": "Asset ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "albumIds": { + "description": "Filter by album IDs", + "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "type": "array" + }, + "city": { + "description": "Filter by city name", + "nullable": true, + "type": "string" + }, + "country": { + "description": "Filter by country name", + "nullable": true, + "type": "string" + }, + "createdAfter": { + "description": "Filter by creation date (after)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "createdBefore": { + "description": "Filter by creation date (before)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "isEncoded": { + "description": "Filter by encoded status", + "type": "boolean" + }, + "isFavorite": { + "description": "Filter by favorite status", + "type": "boolean" + }, + "isMotion": { + "description": "Filter by motion photo status", + "type": "boolean" + }, + "isNotInAlbum": { + "description": "Filter assets not in any album", + "type": "boolean" + }, + "isOffline": { + "description": "Filter by offline status", + "type": "boolean" + }, + "language": { + "description": "Search language code", "type": "string" }, - "city": { - "description": "City", + "lensModel": { + "description": "Filter by lens model", "nullable": true, "type": "string" }, - "country": { - "description": "Country", + "libraryId": { + "description": "Library ID to filter by", + "format": "uuid", "nullable": true, + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "dateTimeOriginal": { - "description": "Date time original", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", + "make": { + "description": "Filter by camera make", "nullable": true, - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "description": { - "description": "Description", + "model": { + "description": "Filter by camera model", "nullable": true, "type": "string" }, - "exifImageHeight": { - "description": "Exif image height", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "nullable": true, - "type": "integer" + "ocr": { + "description": "Filter by OCR text content", + "type": "string" }, - "exifImageWidth": { - "description": "Exif image width", + "page": { + "description": "Page number", "maximum": 9007199254740991, - "minimum": -9007199254740991, - "nullable": true, + "minimum": 1, "type": "integer" }, - "exposureTime": { - "description": "Exposure time", - "nullable": true, - "type": "string" - }, - "fNumber": { - "description": "F number", - "format": "double", - "nullable": true, - "type": "number" + "personIds": { + "description": "Filter by person IDs", + "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "type": "array" }, - "fileSizeInByte": { - "description": "File size in byte", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "nullable": true, - "type": "integer" + "query": { + "description": "Natural language search query", + "type": "string" }, - "focalLength": { - "description": "Focal length", - "format": "double", - "nullable": true, - "type": "number" + "queryAssetId": { + "description": "Asset ID to use as search reference", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" }, - "fps": { - "description": "FPS", - "format": "double", + "rating": { + "description": "Filter by rating [1-5], or null for unrated", + "maximum": 5, + "minimum": 1, "nullable": true, - "type": "number" + "type": "integer", + "x-immich-history": [ + { + "version": "v1", + "state": "Added" + }, + { + "version": "v2", + "state": "Stable" + }, + { + "version": "v2.6.0", + "state": "Updated", + "description": "Using -1 as a rating is deprecated and will be removed in the next major version." + }, + { + "version": "v3", + "state": "Updated", + "description": "Using -1 as a rating is no longer valid." + } + ], + "x-immich-state": "Stable" }, - "iso": { - "description": "ISO", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "nullable": true, + "size": { + "description": "Number of results to return", + "maximum": 1000, + "minimum": 1, "type": "integer" }, - "latitude": { - "description": "Latitude", - "format": "double", - "nullable": true, - "type": "number" - }, - "lensModel": { - "description": "Lens model", + "state": { + "description": "Filter by state/province name", "nullable": true, "type": "string" }, - "longitude": { - "description": "Longitude", - "format": "double", + "tagIds": { + "description": "Filter by tag IDs", + "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, "nullable": true, - "type": "number" + "type": "array" }, - "make": { - "description": "Make", - "nullable": true, + "takenAfter": { + "description": "Filter by taken date (after)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "model": { - "description": "Model", - "nullable": true, + "takenBefore": { + "description": "Filter by taken date (before)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "modifyDate": { - "description": "Modify date", + "trashedAfter": { + "description": "Filter by trash date (after)", "example": "2024-01-01T00:00:00.000Z", "format": "date-time", - "nullable": true, "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "orientation": { - "description": "Orientation", - "nullable": true, + "trashedBefore": { + "description": "Filter by trash date (before)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "profileDescription": { - "description": "Profile description", - "nullable": true, + "type": { + "$ref": "#/components/schemas/AssetTypeEnum" + }, + "updatedAfter": { + "description": "Filter by update date (after)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "projectionType": { - "description": "Projection type", - "nullable": true, + "updatedBefore": { + "description": "Filter by update date (before)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "rating": { - "description": "Rating", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "nullable": true, - "type": "integer" + "visibility": { + "$ref": "#/components/schemas/AssetVisibility" }, - "state": { - "description": "State", - "nullable": true, + "withDeleted": { + "description": "Include deleted assets", + "type": "boolean" + }, + "withExif": { + "description": "Include EXIF data in response", + "type": "boolean" + } + }, + "type": "object" + }, + "SourceType": { + "description": "Face detection source type", + "enum": [ + "machine-learning", + "exif", + "manual" + ], + "type": "string" + }, + "StackCreateDto": { + "properties": { + "assetIds": { + "description": "Asset IDs (first becomes primary, min 2)", + "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "minItems": 2, + "type": "array" + } + }, + "required": [ + "assetIds" + ], + "type": "object" + }, + "StackResponseDto": { + "description": "Stack response", + "properties": { + "assets": { + "items": { + "$ref": "#/components/schemas/AssetResponseDto" + }, + "type": "array" + }, + "id": { + "description": "Stack ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "timeZone": { - "description": "Time zone", - "nullable": true, + "primaryAssetId": { + "description": "Primary asset ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" } }, "required": [ - "assetId", - "city", - "country", - "dateTimeOriginal", - "description", - "exifImageHeight", - "exifImageWidth", - "exposureTime", - "fNumber", - "fileSizeInByte", - "focalLength", - "fps", - "iso", - "latitude", - "lensModel", - "longitude", - "make", - "model", - "modifyDate", - "orientation", - "profileDescription", - "projectionType", - "rating", - "state", - "timeZone" + "assets", + "id", + "primaryAssetId" ], "type": "object" }, - "SyncAssetFaceDeleteV1": { + "StackUpdateDto": { "properties": { - "assetFaceId": { - "description": "Asset face ID", + "primaryAssetId": { + "description": "Primary asset ID", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" } }, - "required": [ - "assetFaceId" - ], "type": "object" }, - "SyncAssetFaceV1": { + "StatisticsSearchDto": { "properties": { - "assetId": { - "description": "Asset ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" + "albumIds": { + "description": "Filter by album IDs", + "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "type": "array" }, - "boundingBoxX1": { - "description": "Bounding box X1", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "city": { + "description": "Filter by city name", + "nullable": true, + "type": "string" }, - "boundingBoxX2": { - "description": "Bounding box X2", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "country": { + "description": "Filter by country name", + "nullable": true, + "type": "string" }, - "boundingBoxY1": { - "description": "Bounding box Y1", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "createdAfter": { + "description": "Filter by creation date (after)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" }, - "boundingBoxY2": { - "description": "Bounding box Y2", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "createdBefore": { + "description": "Filter by creation date (before)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" }, - "id": { - "description": "Asset face ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "description": { + "description": "Filter by description text", "type": "string" }, - "imageHeight": { - "description": "Image height", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "isEncoded": { + "description": "Filter by encoded status", + "type": "boolean" }, - "imageWidth": { - "description": "Image width", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "isFavorite": { + "description": "Filter by favorite status", + "type": "boolean" }, - "personId": { - "description": "Person ID", + "isMotion": { + "description": "Filter by motion photo status", + "type": "boolean" + }, + "isNotInAlbum": { + "description": "Filter assets not in any album", + "type": "boolean" + }, + "isOffline": { + "description": "Filter by offline status", + "type": "boolean" + }, + "lensModel": { + "description": "Filter by lens model", "nullable": true, "type": "string" }, - "sourceType": { - "description": "Source type", - "type": "string" - } - }, - "required": [ - "assetId", - "boundingBoxX1", - "boundingBoxX2", - "boundingBoxY1", - "boundingBoxY2", - "id", - "imageHeight", - "imageWidth", - "personId", - "sourceType" - ], - "type": "object" - }, - "SyncAssetFaceV2": { - "properties": { - "assetId": { - "description": "Asset ID", + "libraryId": { + "description": "Library ID to filter by", "format": "uuid", + "nullable": true, "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "boundingBoxX1": { - "description": "Bounding box X1", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "make": { + "description": "Filter by camera make", + "nullable": true, + "type": "string" }, - "boundingBoxX2": { - "description": "Bounding box X2", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "model": { + "description": "Filter by camera model", + "nullable": true, + "type": "string" }, - "boundingBoxY1": { - "description": "Bounding box Y1", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "ocr": { + "description": "Filter by OCR text content", + "type": "string" }, - "boundingBoxY2": { - "description": "Bounding box Y2", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "personIds": { + "description": "Filter by person IDs", + "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "type": "array" }, - "deletedAt": { - "description": "Face deleted at", + "rating": { + "description": "Filter by rating [1-5], or null for unrated", + "maximum": 5, + "minimum": 1, + "nullable": true, + "type": "integer", + "x-immich-history": [ + { + "version": "v1", + "state": "Added" + }, + { + "version": "v2", + "state": "Stable" + }, + { + "version": "v2.6.0", + "state": "Updated", + "description": "Using -1 as a rating is deprecated and will be removed in the next major version." + }, + { + "version": "v3", + "state": "Updated", + "description": "Using -1 as a rating is no longer valid." + } + ], + "x-immich-state": "Stable" + }, + "state": { + "description": "Filter by state/province name", + "nullable": true, + "type": "string" + }, + "tagIds": { + "description": "Filter by tag IDs", + "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "takenAfter": { + "description": "Filter by taken date (after)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "takenBefore": { + "description": "Filter by taken date (before)", "example": "2024-01-01T00:00:00.000Z", "format": "date-time", - "nullable": true, "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "id": { - "description": "Asset face ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "trashedAfter": { + "description": "Filter by trash date (after)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "imageHeight": { - "description": "Image height", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "trashedBefore": { + "description": "Filter by trash date (before)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" }, - "imageWidth": { - "description": "Image width", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "type": { + "$ref": "#/components/schemas/AssetTypeEnum" }, - "isVisible": { - "description": "Is the face visible in the asset", - "type": "boolean" + "updatedAfter": { + "description": "Filter by update date (after)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" }, - "personId": { - "description": "Person ID", - "nullable": true, + "updatedBefore": { + "description": "Filter by update date (before)", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "sourceType": { - "description": "Source type", + "visibility": { + "$ref": "#/components/schemas/AssetVisibility" + } + }, + "type": "object" + }, + "StorageFolder": { + "description": "Storage folder", + "enum": [ + "encoded-video", + "library", + "upload", + "profile", + "thumbs", + "backups" + ], + "type": "string" + }, + "SyncAckDeleteDto": { + "properties": { + "types": { + "description": "Sync entity types to delete acks for", + "items": { + "$ref": "#/components/schemas/SyncEntityType" + }, + "type": "array" + } + }, + "type": "object" + }, + "SyncAckDto": { + "properties": { + "ack": { + "description": "Acknowledgment ID", "type": "string" + }, + "type": { + "$ref": "#/components/schemas/SyncEntityType" } }, "required": [ - "assetId", - "boundingBoxX1", - "boundingBoxX2", - "boundingBoxY1", - "boundingBoxY2", - "deletedAt", - "id", - "imageHeight", - "imageWidth", - "isVisible", - "personId", - "sourceType" + "ack", + "type" ], "type": "object" }, - "SyncAssetMetadataDeleteV1": { + "SyncAckSetDto": { "properties": { - "assetId": { - "description": "Asset ID", + "acks": { + "description": "Acknowledgment IDs (max 1000)", + "items": { + "type": "string" + }, + "maxItems": 1000, + "type": "array" + } + }, + "required": [ + "acks" + ], + "type": "object" + }, + "SyncAckV1": { + "properties": {}, + "type": "object" + }, + "SyncAlbumDeleteV1": { + "properties": { + "albumId": { + "description": "Album ID", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" - }, - "key": { - "description": "Key", - "type": "string" } }, "required": [ - "assetId", - "key" + "albumId" ], "type": "object" }, - "SyncAssetMetadataV1": { + "SyncAlbumToAssetDeleteV1": { "properties": { - "assetId": { - "description": "Asset ID", + "albumId": { + "description": "Album ID", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "key": { - "description": "Key", + "assetId": { + "description": "Asset ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" - }, - "value": { - "additionalProperties": {}, - "description": "Value", - "type": "object" } }, "required": [ - "assetId", - "key", - "value" + "albumId", + "assetId" ], "type": "object" }, - "SyncAssetOcrDeleteV1": { + "SyncAlbumToAssetV1": { "properties": { - "assetId": { - "description": "Original asset ID of the deleted OCR entry", - "type": "string" - }, - "deletedAt": { - "description": "Timestamp when the OCR entry was deleted", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "albumId": { + "description": "Album ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "id": { - "description": "Audit row ID of the deleted OCR entry", + "assetId": { + "description": "Asset ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" } }, "required": [ - "assetId", - "deletedAt", - "id" + "albumId", + "assetId" ], "type": "object" }, - "SyncAssetOcrV1": { + "SyncAlbumUserDeleteV1": { "properties": { - "assetId": { - "description": "Asset ID", + "albumId": { + "description": "Album ID", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "boxScore": { - "description": "Confidence score of the bounding box", - "format": "double", - "type": "number" - }, - "id": { - "description": "OCR entry ID", + "userId": { + "description": "User ID", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" - }, - "isVisible": { - "description": "Whether the OCR entry is visible", - "type": "boolean" - }, - "text": { - "description": "Recognized text content", + } + }, + "required": [ + "albumId", + "userId" + ], + "type": "object" + }, + "SyncAlbumUserV1": { + "properties": { + "albumId": { + "description": "Album ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "textScore": { - "description": "Confidence score of the recognized text", - "format": "double", - "type": "number" - }, - "x1": { - "description": "Top-left X coordinate (normalized 0–1)", - "format": "double", - "type": "number" - }, - "x2": { - "description": "Top-right X coordinate (normalized 0–1)", - "format": "double", - "type": "number" - }, - "x3": { - "description": "Bottom-right X coordinate (normalized 0–1)", - "format": "double", - "type": "number" - }, - "x4": { - "description": "Bottom-left X coordinate (normalized 0–1)", - "format": "double", - "type": "number" - }, - "y1": { - "description": "Top-left Y coordinate (normalized 0–1)", - "format": "double", - "type": "number" - }, - "y2": { - "description": "Top-right Y coordinate (normalized 0–1)", - "format": "double", - "type": "number" - }, - "y3": { - "description": "Bottom-right Y coordinate (normalized 0–1)", - "format": "double", - "type": "number" + "role": { + "$ref": "#/components/schemas/AlbumUserRole" }, - "y4": { - "description": "Bottom-left Y coordinate (normalized 0–1)", - "format": "double", - "type": "number" + "userId": { + "description": "User ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" } }, "required": [ - "assetId", - "boxScore", - "id", - "isVisible", - "text", - "textScore", - "x1", - "x2", - "x3", - "x4", - "y1", - "y2", - "y3", - "y4" + "albumId", + "role", + "userId" ], "type": "object" }, - "SyncAssetV1": { + "SyncAlbumV1": { "properties": { - "checksum": { - "description": "Checksum", - "type": "string" - }, "createdAt": { - "description": "Uploaded to Immich at", + "description": "Created at", "example": "2024-01-01T00:00:00.000Z", "format": "date-time", - "nullable": true, "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "deletedAt": { - "description": "Deleted at", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "nullable": true, - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "description": { + "description": "Album description", "type": "string" }, - "duration": { - "description": "Duration", + "id": { + "description": "Album ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "isActivityEnabled": { + "description": "Is activity enabled", + "type": "boolean" + }, + "name": { + "description": "Album name", + "type": "string" + }, + "order": { + "$ref": "#/components/schemas/AssetOrder" + }, + "ownerId": { + "description": "Owner ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "thumbnailAssetId": { + "description": "Thumbnail asset ID", "nullable": true, "type": "string" }, - "fileCreatedAt": { - "description": "File created at", + "updatedAt": { + "description": "Updated at", "example": "2024-01-01T00:00:00.000Z", "format": "date-time", - "nullable": true, "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" - }, - "fileModifiedAt": { - "description": "File modified at", + } + }, + "required": [ + "createdAt", + "description", + "id", + "isActivityEnabled", + "name", + "order", + "ownerId", + "thumbnailAssetId", + "updatedAt" + ], + "type": "object" + }, + "SyncAlbumV2": { + "properties": { + "createdAt": { + "description": "Created at", "example": "2024-01-01T00:00:00.000Z", "format": "date-time", - "nullable": true, "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "height": { - "description": "Asset height", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "nullable": true, - "type": "integer" + "description": { + "description": "Album description", + "type": "string" }, "id": { - "description": "Asset ID", + "description": "Album ID", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "isEdited": { - "description": "Is edited", - "type": "boolean" - }, - "isFavorite": { - "description": "Is favorite", + "isActivityEnabled": { + "description": "Is activity enabled", "type": "boolean" }, - "libraryId": { - "description": "Library ID", - "nullable": true, + "name": { + "description": "Album name", "type": "string" }, - "livePhotoVideoId": { - "description": "Live photo video ID", + "order": { + "$ref": "#/components/schemas/AssetOrder" + }, + "thumbnailAssetId": { + "description": "Thumbnail asset ID", "nullable": true, "type": "string" }, - "localDateTime": { - "description": "Local date time", + "updatedAt": { + "description": "Updated at", "example": "2024-01-01T00:00:00.000Z", "format": "date-time", - "nullable": true, "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" - }, - "originalFileName": { - "description": "Original file name", + } + }, + "required": [ + "createdAt", + "description", + "id", + "isActivityEnabled", + "name", + "order", + "thumbnailAssetId", + "updatedAt" + ], + "type": "object" + }, + "SyncAssetDeleteV1": { + "properties": { + "assetId": { + "description": "Asset ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" - }, - "ownerId": { - "description": "Owner ID", + } + }, + "required": [ + "assetId" + ], + "type": "object" + }, + "SyncAssetEditDeleteV1": { + "properties": { + "editId": { + "description": "Edit ID", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" + } + }, + "required": [ + "editId" + ], + "type": "object" + }, + "SyncAssetEditV1": { + "properties": { + "action": { + "$ref": "#/components/schemas/AssetEditAction" }, - "stackId": { - "description": "Stack ID", - "nullable": true, + "assetId": { + "description": "Asset ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "thumbhash": { - "description": "Thumbhash", - "nullable": true, + "id": { + "description": "Edit ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "type": { - "$ref": "#/components/schemas/AssetTypeEnum" - }, - "visibility": { - "$ref": "#/components/schemas/AssetVisibility" + "parameters": { + "additionalProperties": {}, + "description": "Edit parameters", + "type": "object" }, - "width": { - "description": "Asset width", + "sequence": { + "description": "Edit sequence", "maximum": 9007199254740991, "minimum": -9007199254740991, - "nullable": true, "type": "integer" } }, "required": [ - "checksum", - "createdAt", - "deletedAt", - "duration", - "fileCreatedAt", - "fileModifiedAt", - "height", + "action", + "assetId", "id", - "isEdited", - "isFavorite", - "libraryId", - "livePhotoVideoId", - "localDateTime", - "originalFileName", - "ownerId", - "stackId", - "thumbhash", - "type", - "visibility", - "width" + "parameters", + "sequence" ], "type": "object" }, - "SyncAssetV2": { + "SyncAssetExifV1": { "properties": { - "checksum": { - "description": "Checksum", + "assetId": { + "description": "Asset ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "createdAt": { - "description": "Uploaded to Immich at", + "city": { + "description": "City", + "nullable": true, + "type": "string" + }, + "country": { + "description": "Country", + "nullable": true, + "type": "string" + }, + "dateTimeOriginal": { + "description": "Date time original", "example": "2024-01-01T00:00:00.000Z", "format": "date-time", "nullable": true, "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "deletedAt": { - "description": "Deleted at", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", + "description": { + "description": "Description", "nullable": true, - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "duration": { - "description": "Duration", - "maximum": 2147483647, - "minimum": 0, + "exifImageHeight": { + "description": "Exif image height", + "maximum": 9007199254740991, + "minimum": -9007199254740991, "nullable": true, "type": "integer" }, - "fileCreatedAt": { - "description": "File created at", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", + "exifImageWidth": { + "description": "Exif image width", + "maximum": 9007199254740991, + "minimum": -9007199254740991, "nullable": true, - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" + "type": "integer" }, - "fileModifiedAt": { - "description": "File modified at", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", + "exposureTime": { + "description": "Exposure time", "nullable": true, - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "height": { - "description": "Asset height", + "fNumber": { + "description": "F number", + "format": "double", + "nullable": true, + "type": "number" + }, + "fileSizeInByte": { + "description": "File size in byte", "maximum": 9007199254740991, "minimum": -9007199254740991, "nullable": true, "type": "integer" }, - "id": { - "description": "Asset ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" + "focalLength": { + "description": "Focal length", + "format": "double", + "nullable": true, + "type": "number" }, - "isEdited": { - "description": "Is edited", - "type": "boolean" + "fps": { + "description": "FPS", + "format": "double", + "nullable": true, + "type": "number" }, - "isFavorite": { - "description": "Is favorite", - "type": "boolean" + "iso": { + "description": "ISO", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "nullable": true, + "type": "integer" }, - "libraryId": { - "description": "Library ID", + "latitude": { + "description": "Latitude", + "format": "double", + "nullable": true, + "type": "number" + }, + "lensModel": { + "description": "Lens model", "nullable": true, "type": "string" }, - "livePhotoVideoId": { - "description": "Live photo video ID", + "longitude": { + "description": "Longitude", + "format": "double", + "nullable": true, + "type": "number" + }, + "make": { + "description": "Make", "nullable": true, "type": "string" }, - "localDateTime": { - "description": "Local date time", + "model": { + "description": "Model", + "nullable": true, + "type": "string" + }, + "modifyDate": { + "description": "Modify date", "example": "2024-01-01T00:00:00.000Z", "format": "date-time", "nullable": true, "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "originalFileName": { - "description": "Original file name", - "type": "string" - }, - "ownerId": { - "description": "Owner ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "orientation": { + "description": "Orientation", + "nullable": true, "type": "string" }, - "stackId": { - "description": "Stack ID", + "profileDescription": { + "description": "Profile description", "nullable": true, "type": "string" }, - "thumbhash": { - "description": "Thumbhash", + "projectionType": { + "description": "Projection type", "nullable": true, "type": "string" }, - "type": { - "$ref": "#/components/schemas/AssetTypeEnum" - }, - "visibility": { - "$ref": "#/components/schemas/AssetVisibility" - }, - "width": { - "description": "Asset width", + "rating": { + "description": "Rating", "maximum": 9007199254740991, "minimum": -9007199254740991, "nullable": true, "type": "integer" + }, + "state": { + "description": "State", + "nullable": true, + "type": "string" + }, + "timeZone": { + "description": "Time zone", + "nullable": true, + "type": "string" } }, "required": [ - "checksum", - "createdAt", - "deletedAt", - "duration", - "fileCreatedAt", - "fileModifiedAt", - "height", - "id", - "isEdited", - "isFavorite", - "libraryId", - "livePhotoVideoId", - "localDateTime", - "originalFileName", - "ownerId", - "stackId", - "thumbhash", - "type", - "visibility", - "width" + "assetId", + "city", + "country", + "dateTimeOriginal", + "description", + "exifImageHeight", + "exifImageWidth", + "exposureTime", + "fNumber", + "fileSizeInByte", + "focalLength", + "fps", + "iso", + "latitude", + "lensModel", + "longitude", + "make", + "model", + "modifyDate", + "orientation", + "profileDescription", + "projectionType", + "rating", + "state", + "timeZone" ], "type": "object" }, - "SyncAuthUserV1": { + "SyncAssetFaceDeleteV1": { "properties": { - "avatarColor": { - "allOf": [ - { - "$ref": "#/components/schemas/UserAvatarColor" - } - ], - "nullable": true + "assetFaceId": { + "description": "Asset face ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + } + }, + "required": [ + "assetFaceId" + ], + "type": "object" + }, + "SyncAssetFaceV1": { + "properties": { + "assetId": { + "description": "Asset ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" }, - "deletedAt": { - "description": "User deleted at", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", + "boundingBoxX1": { + "description": "Bounding box X1", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "boundingBoxX2": { + "description": "Bounding box X2", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "boundingBoxY1": { + "description": "Bounding box Y1", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "boundingBoxY2": { + "description": "Bounding box Y2", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "id": { + "description": "Asset face ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "imageHeight": { + "description": "Image height", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "imageWidth": { + "description": "Image width", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "personId": { + "description": "Person ID", "nullable": true, - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "email": { - "description": "User email", + "sourceType": { + "description": "Source type", "type": "string" - }, - "hasProfileImage": { - "description": "User has profile image", - "type": "boolean" - }, - "id": { - "description": "User ID", + } + }, + "required": [ + "assetId", + "boundingBoxX1", + "boundingBoxX2", + "boundingBoxY1", + "boundingBoxY2", + "id", + "imageHeight", + "imageWidth", + "personId", + "sourceType" + ], + "type": "object" + }, + "SyncAssetFaceV2": { + "properties": { + "assetId": { + "description": "Asset ID", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "isAdmin": { - "description": "User is admin", - "type": "boolean" + "boundingBoxX1": { + "description": "Bounding box X1", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" }, - "name": { - "description": "User name", - "type": "string" + "boundingBoxX2": { + "description": "Bounding box X2", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" }, - "oauthId": { - "description": "User OAuth ID", - "type": "string" + "boundingBoxY1": { + "description": "Bounding box Y1", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" }, - "pinCode": { - "description": "User pin code", - "nullable": true, - "type": "string" + "boundingBoxY2": { + "description": "Bounding box Y2", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" }, - "profileChangedAt": { - "description": "User profile changed at", + "deletedAt": { + "description": "Face deleted at", "example": "2024-01-01T00:00:00.000Z", "format": "date-time", + "nullable": true, "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "quotaSizeInBytes": { - "description": "Quota size in bytes", + "id": { + "description": "Asset face ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "imageHeight": { + "description": "Image height", "maximum": 9007199254740991, "minimum": -9007199254740991, - "nullable": true, "type": "integer" }, - "quotaUsageInBytes": { - "description": "Quota usage in bytes", + "imageWidth": { + "description": "Image width", "maximum": 9007199254740991, "minimum": -9007199254740991, "type": "integer" }, - "storageLabel": { - "description": "User storage label", + "isVisible": { + "description": "Is the face visible in the asset", + "type": "boolean" + }, + "personId": { + "description": "Person ID", "nullable": true, "type": "string" + }, + "sourceType": { + "description": "Source type", + "type": "string" } }, "required": [ + "assetId", + "boundingBoxX1", + "boundingBoxX2", + "boundingBoxY1", + "boundingBoxY2", "deletedAt", - "email", - "hasProfileImage", "id", - "isAdmin", - "name", - "oauthId", - "pinCode", - "profileChangedAt", - "quotaSizeInBytes", - "quotaUsageInBytes", - "storageLabel" + "imageHeight", + "imageWidth", + "isVisible", + "personId", + "sourceType" ], "type": "object" }, - "SyncCompleteV1": { - "properties": {}, - "type": "object" - }, - "SyncEntityType": { - "description": "Sync entity type", - "enum": [ - "AuthUserV1", - "UserV1", - "UserDeleteV1", - "AssetV1", - "AssetV2", - "AssetDeleteV1", - "AssetExifV1", - "AssetEditV1", - "AssetEditDeleteV1", - "AssetMetadataV1", - "AssetMetadataDeleteV1", - "AssetOcrV1", - "AssetOcrDeleteV1", - "PartnerV1", - "PartnerDeleteV1", - "PartnerAssetV1", - "PartnerAssetV2", - "PartnerAssetBackfillV1", - "PartnerAssetBackfillV2", - "PartnerAssetDeleteV1", - "PartnerAssetExifV1", - "PartnerAssetExifBackfillV1", - "PartnerStackBackfillV1", - "PartnerStackDeleteV1", - "PartnerStackV1", - "AlbumV1", - "AlbumV2", - "AlbumDeleteV1", - "AlbumUserV1", - "AlbumUserBackfillV1", - "AlbumUserDeleteV1", - "AlbumAssetCreateV1", - "AlbumAssetCreateV2", - "AlbumAssetUpdateV1", - "AlbumAssetUpdateV2", - "AlbumAssetBackfillV1", - "AlbumAssetBackfillV2", - "AlbumAssetExifCreateV1", - "AlbumAssetExifUpdateV1", - "AlbumAssetExifBackfillV1", - "AlbumToAssetV1", - "AlbumToAssetDeleteV1", - "AlbumToAssetBackfillV1", - "MemoryV1", - "MemoryDeleteV1", - "MemoryToAssetV1", - "MemoryToAssetDeleteV1", - "StackV1", - "StackDeleteV1", - "PersonV1", - "PersonDeleteV1", - "AssetFaceV1", - "AssetFaceV2", - "AssetFaceDeleteV1", - "UserMetadataV1", - "UserMetadataDeleteV1", - "SyncAckV1", - "SyncResetV1", - "SyncCompleteV1" - ], - "type": "string" - }, - "SyncMemoryAssetDeleteV1": { + "SyncAssetMetadataDeleteV1": { "properties": { "assetId": { "description": "Asset ID", @@ -25280,20 +26145,68 @@ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "memoryId": { - "description": "Memory ID", + "key": { + "description": "Key", + "type": "string" + } + }, + "required": [ + "assetId", + "key" + ], + "type": "object" + }, + "SyncAssetMetadataV1": { + "properties": { + "assetId": { + "description": "Asset ID", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" + }, + "key": { + "description": "Key", + "type": "string" + }, + "value": { + "additionalProperties": {}, + "description": "Value", + "type": "object" } }, "required": [ "assetId", - "memoryId" + "key", + "value" ], "type": "object" }, - "SyncMemoryAssetV1": { + "SyncAssetOcrDeleteV1": { + "properties": { + "assetId": { + "description": "Original asset ID of the deleted OCR entry", + "type": "string" + }, + "deletedAt": { + "description": "Timestamp when the OCR entry was deleted", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "id": { + "description": "Audit row ID of the deleted OCR entry", + "type": "string" + } + }, + "required": [ + "assetId", + "deletedAt", + "id" + ], + "type": "object" + }, + "SyncAssetOcrV1": { "properties": { "assetId": { "description": "Asset ID", @@ -25301,47 +26214,103 @@ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "memoryId": { - "description": "Memory ID", + "boxScore": { + "description": "Confidence score of the bounding box", + "format": "double", + "type": "number" + }, + "id": { + "description": "OCR entry ID", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" + }, + "isVisible": { + "description": "Whether the OCR entry is visible", + "type": "boolean" + }, + "text": { + "description": "Recognized text content", + "type": "string" + }, + "textScore": { + "description": "Confidence score of the recognized text", + "format": "double", + "type": "number" + }, + "x1": { + "description": "Top-left X coordinate (normalized 0–1)", + "format": "double", + "type": "number" + }, + "x2": { + "description": "Top-right X coordinate (normalized 0–1)", + "format": "double", + "type": "number" + }, + "x3": { + "description": "Bottom-right X coordinate (normalized 0–1)", + "format": "double", + "type": "number" + }, + "x4": { + "description": "Bottom-left X coordinate (normalized 0–1)", + "format": "double", + "type": "number" + }, + "y1": { + "description": "Top-left Y coordinate (normalized 0–1)", + "format": "double", + "type": "number" + }, + "y2": { + "description": "Top-right Y coordinate (normalized 0–1)", + "format": "double", + "type": "number" + }, + "y3": { + "description": "Bottom-right Y coordinate (normalized 0–1)", + "format": "double", + "type": "number" + }, + "y4": { + "description": "Bottom-left Y coordinate (normalized 0–1)", + "format": "double", + "type": "number" } }, "required": [ "assetId", - "memoryId" + "boxScore", + "id", + "isVisible", + "text", + "textScore", + "x1", + "x2", + "x3", + "x4", + "y1", + "y2", + "y3", + "y4" ], "type": "object" }, - "SyncMemoryDeleteV1": { + "SyncAssetV1": { "properties": { - "memoryId": { - "description": "Memory ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "checksum": { + "description": "Checksum", "type": "string" - } - }, - "required": [ - "memoryId" - ], - "type": "object" - }, - "SyncMemoryV1": { - "properties": { + }, "createdAt": { - "description": "Created at", + "description": "Uploaded to Immich at", "example": "2024-01-01T00:00:00.000Z", "format": "date-time", + "nullable": true, "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "data": { - "additionalProperties": {}, - "description": "Data", - "type": "object" - }, "deletedAt": { "description": "Deleted at", "example": "2024-01-01T00:00:00.000Z", @@ -25350,278 +26319,210 @@ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "hideAt": { - "description": "Hide at", + "duration": { + "description": "Duration", + "nullable": true, + "type": "string" + }, + "fileCreatedAt": { + "description": "File created at", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "nullable": true, + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "fileModifiedAt": { + "description": "File modified at", "example": "2024-01-01T00:00:00.000Z", "format": "date-time", "nullable": true, "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, + "height": { + "description": "Asset height", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "nullable": true, + "type": "integer" + }, "id": { - "description": "Memory ID", + "description": "Asset ID", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "isSaved": { - "description": "Is saved", + "isEdited": { + "description": "Is edited", "type": "boolean" }, - "memoryAt": { - "description": "Memory at", + "isFavorite": { + "description": "Is favorite", + "type": "boolean" + }, + "libraryId": { + "description": "Library ID", + "nullable": true, + "type": "string" + }, + "livePhotoVideoId": { + "description": "Live photo video ID", + "nullable": true, + "type": "string" + }, + "localDateTime": { + "description": "Local date time", "example": "2024-01-01T00:00:00.000Z", "format": "date-time", + "nullable": true, "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, + "originalFileName": { + "description": "Original file name", + "type": "string" + }, "ownerId": { "description": "Owner ID", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "seenAt": { - "description": "Seen at", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", + "stackId": { + "description": "Stack ID", "nullable": true, - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "showAt": { - "description": "Show at", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", + "thumbhash": { + "description": "Thumbhash", "nullable": true, - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, "type": { - "$ref": "#/components/schemas/MemoryType" + "$ref": "#/components/schemas/AssetTypeEnum" }, - "updatedAt": { - "description": "Updated at", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" + "visibility": { + "$ref": "#/components/schemas/AssetVisibility" + }, + "width": { + "description": "Asset width", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "nullable": true, + "type": "integer" } }, "required": [ + "checksum", "createdAt", - "data", "deletedAt", - "hideAt", + "duration", + "fileCreatedAt", + "fileModifiedAt", + "height", "id", - "isSaved", - "memoryAt", - "ownerId", - "seenAt", - "showAt", - "type", - "updatedAt" - ], - "type": "object" - }, - "SyncPartnerDeleteV1": { - "properties": { - "sharedById": { - "description": "Shared by ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "sharedWithId": { - "description": "Shared with ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - } - }, - "required": [ - "sharedById", - "sharedWithId" - ], - "type": "object" - }, - "SyncPartnerV1": { - "properties": { - "inTimeline": { - "description": "In timeline", - "type": "boolean" - }, - "sharedById": { - "description": "Shared by ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "sharedWithId": { - "description": "Shared with ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - } - }, - "required": [ - "inTimeline", - "sharedById", - "sharedWithId" - ], - "type": "object" - }, - "SyncPersonDeleteV1": { - "properties": { - "personId": { - "description": "Person ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - } - }, - "required": [ - "personId" + "isEdited", + "isFavorite", + "libraryId", + "livePhotoVideoId", + "localDateTime", + "originalFileName", + "ownerId", + "stackId", + "thumbhash", + "type", + "visibility", + "width" ], "type": "object" }, - "SyncPersonV1": { + "SyncAssetV2": { "properties": { - "birthDate": { - "description": "Birth date", + "checksum": { + "description": "Checksum", + "type": "string" + }, + "createdAt": { + "description": "Uploaded to Immich at", "example": "2024-01-01T00:00:00.000Z", "format": "date-time", "nullable": true, "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "color": { - "description": "Color", + "deletedAt": { + "description": "Deleted at", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", "nullable": true, + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "createdAt": { - "description": "Created at", + "duration": { + "description": "Duration", + "maximum": 2147483647, + "minimum": 0, + "nullable": true, + "type": "integer" + }, + "fileCreatedAt": { + "description": "File created at", "example": "2024-01-01T00:00:00.000Z", "format": "date-time", + "nullable": true, "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "faceAssetId": { - "description": "Face asset ID", + "fileModifiedAt": { + "description": "File modified at", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", "nullable": true, + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, + "height": { + "description": "Asset height", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "nullable": true, + "type": "integer" + }, "id": { - "description": "Person ID", + "description": "Asset ID", "format": "uuid", "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "isFavorite": { - "description": "Is favorite", + "isEdited": { + "description": "Is edited", "type": "boolean" }, - "isHidden": { - "description": "Is hidden", + "isFavorite": { + "description": "Is favorite", "type": "boolean" }, - "name": { - "description": "Person name", + "libraryId": { + "description": "Library ID", + "nullable": true, "type": "string" }, - "ownerId": { - "description": "Owner ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "livePhotoVideoId": { + "description": "Live photo video ID", + "nullable": true, "type": "string" }, - "updatedAt": { - "description": "Updated at", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" - } - }, - "required": [ - "birthDate", - "color", - "createdAt", - "faceAssetId", - "id", - "isFavorite", - "isHidden", - "name", - "ownerId", - "updatedAt" - ], - "type": "object" - }, - "SyncRequestType": { - "description": "Sync request type", - "enum": [ - "AlbumsV1", - "AlbumsV2", - "AlbumUsersV1", - "AlbumToAssetsV1", - "AlbumAssetsV1", - "AlbumAssetsV2", - "AlbumAssetExifsV1", - "AssetsV1", - "AssetsV2", - "AssetExifsV1", - "AssetEditsV1", - "AssetMetadataV1", - "AssetOcrV1", - "AuthUsersV1", - "MemoriesV1", - "MemoryToAssetsV1", - "PartnersV1", - "PartnerAssetsV1", - "PartnerAssetsV2", - "PartnerAssetExifsV1", - "PartnerStacksV1", - "StacksV1", - "UsersV1", - "PeopleV1", - "AssetFacesV1", - "AssetFacesV2", - "UserMetadataV1" - ], - "type": "string" - }, - "SyncResetV1": { - "properties": {}, - "type": "object" - }, - "SyncStackDeleteV1": { - "properties": { - "stackId": { - "description": "Stack ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - } - }, - "required": [ - "stackId" - ], - "type": "object" - }, - "SyncStackV1": { - "properties": { - "createdAt": { - "description": "Created at", + "localDateTime": { + "description": "Local date time", "example": "2024-01-01T00:00:00.000Z", "format": "date-time", + "nullable": true, "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "id": { - "description": "Stack ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "originalFileName": { + "description": "Original file name", "type": "string" }, "ownerId": { @@ -25630,105 +26531,55 @@ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "primaryAssetId": { - "description": "Primary asset ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "updatedAt": { - "description": "Updated at", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "stackId": { + "description": "Stack ID", + "nullable": true, "type": "string" - } - }, - "required": [ - "createdAt", - "id", - "ownerId", - "primaryAssetId", - "updatedAt" - ], - "type": "object" - }, - "SyncStreamDto": { - "properties": { - "reset": { - "description": "Reset sync state", - "type": "boolean" }, - "types": { - "description": "Sync request types", - "items": { - "$ref": "#/components/schemas/SyncRequestType" - }, - "type": "array" - } - }, - "required": [ - "types" - ], - "type": "object" - }, - "SyncUserDeleteV1": { - "properties": { - "userId": { - "description": "User ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "thumbhash": { + "description": "Thumbhash", + "nullable": true, "type": "string" - } - }, - "required": [ - "userId" - ], - "type": "object" - }, - "SyncUserMetadataDeleteV1": { - "properties": { - "key": { - "$ref": "#/components/schemas/UserMetadataKey" }, - "userId": { - "description": "User ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - } - }, - "required": [ - "key", - "userId" - ], - "type": "object" - }, - "SyncUserMetadataV1": { - "properties": { - "key": { - "$ref": "#/components/schemas/UserMetadataKey" + "type": { + "$ref": "#/components/schemas/AssetTypeEnum" }, - "userId": { - "description": "User ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" + "visibility": { + "$ref": "#/components/schemas/AssetVisibility" }, - "value": { - "additionalProperties": {}, - "description": "User metadata value", - "type": "object" + "width": { + "description": "Asset width", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "nullable": true, + "type": "integer" } }, "required": [ - "key", - "userId", - "value" + "checksum", + "createdAt", + "deletedAt", + "duration", + "fileCreatedAt", + "fileModifiedAt", + "height", + "id", + "isEdited", + "isFavorite", + "libraryId", + "livePhotoVideoId", + "localDateTime", + "originalFileName", + "ownerId", + "stackId", + "thumbhash", + "type", + "visibility", + "width" ], "type": "object" }, - "SyncUserV1": { + "SyncAuthUserV1": { "properties": { "avatarColor": { "allOf": [ @@ -25760,16 +26611,47 @@ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, + "isAdmin": { + "description": "User is admin", + "type": "boolean" + }, "name": { "description": "User name", "type": "string" }, + "oauthId": { + "description": "User OAuth ID", + "type": "string" + }, + "pinCode": { + "description": "User pin code", + "nullable": true, + "type": "string" + }, "profileChangedAt": { "description": "User profile changed at", "example": "2024-01-01T00:00:00.000Z", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" + }, + "quotaSizeInBytes": { + "description": "Quota size in bytes", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "nullable": true, + "type": "integer" + }, + "quotaUsageInBytes": { + "description": "Quota usage in bytes", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "storageLabel": { + "description": "User storage label", + "nullable": true, + "type": "string" } }, "required": [ @@ -25777,1139 +26659,1101 @@ "email", "hasProfileImage", "id", + "isAdmin", "name", - "profileChangedAt" - ], - "type": "object" - }, - "SystemConfigBackupsDto": { - "properties": { - "database": { - "$ref": "#/components/schemas/DatabaseBackupConfig" - } - }, - "required": [ - "database" + "oauthId", + "pinCode", + "profileChangedAt", + "quotaSizeInBytes", + "quotaUsageInBytes", + "storageLabel" ], "type": "object" }, - "SystemConfigDto": { - "description": "System configuration", - "properties": { - "backup": { - "$ref": "#/components/schemas/SystemConfigBackupsDto" - }, - "ffmpeg": { - "$ref": "#/components/schemas/SystemConfigFFmpegDto" - }, - "image": { - "$ref": "#/components/schemas/SystemConfigImageDto" - }, - "integrityChecks": { - "$ref": "#/components/schemas/SystemConfigIntegrityChecks" - }, - "job": { - "$ref": "#/components/schemas/SystemConfigJobDto" - }, - "library": { - "$ref": "#/components/schemas/SystemConfigLibraryDto" - }, - "logging": { - "$ref": "#/components/schemas/SystemConfigLoggingDto" - }, - "machineLearning": { - "$ref": "#/components/schemas/SystemConfigMachineLearningDto" - }, - "map": { - "$ref": "#/components/schemas/SystemConfigMapDto" - }, - "metadata": { - "$ref": "#/components/schemas/SystemConfigMetadataDto" - }, - "newVersionCheck": { - "$ref": "#/components/schemas/SystemConfigNewVersionCheckDto" - }, - "nightlyTasks": { - "$ref": "#/components/schemas/SystemConfigNightlyTasksDto" - }, - "notifications": { - "$ref": "#/components/schemas/SystemConfigNotificationsDto" - }, - "oauth": { - "$ref": "#/components/schemas/SystemConfigOAuthDto" - }, - "passwordLogin": { - "$ref": "#/components/schemas/SystemConfigPasswordLoginDto" - }, - "reverseGeocoding": { - "$ref": "#/components/schemas/SystemConfigReverseGeocodingDto" - }, - "server": { - "$ref": "#/components/schemas/SystemConfigServerDto" - }, - "storageTemplate": { - "$ref": "#/components/schemas/SystemConfigStorageTemplateDto" - }, - "templates": { - "$ref": "#/components/schemas/SystemConfigTemplatesDto" - }, - "theme": { - "$ref": "#/components/schemas/SystemConfigThemeDto" - }, - "trash": { - "$ref": "#/components/schemas/SystemConfigTrashDto" - }, - "user": { - "$ref": "#/components/schemas/SystemConfigUserDto" - } - }, - "required": [ - "backup", - "ffmpeg", - "image", - "integrityChecks", - "job", - "library", - "logging", - "machineLearning", - "map", - "metadata", - "newVersionCheck", - "nightlyTasks", - "notifications", - "oauth", - "passwordLogin", - "reverseGeocoding", - "server", - "storageTemplate", - "templates", - "theme", - "trash", - "user" - ], + "SyncCompleteV1": { + "properties": {}, "type": "object" }, - "SystemConfigFFmpegDto": { - "properties": { - "accel": { - "$ref": "#/components/schemas/TranscodeHWAccel" - }, - "accelDecode": { - "description": "Accelerated decode", - "type": "boolean" - }, - "acceptedAudioCodecs": { - "description": "Accepted audio codecs", - "items": { - "$ref": "#/components/schemas/AudioCodec" - }, - "type": "array" - }, - "acceptedContainers": { - "description": "Accepted containers", - "items": { - "$ref": "#/components/schemas/VideoContainer" - }, - "type": "array" - }, - "acceptedVideoCodecs": { - "description": "Accepted video codecs", - "items": { - "$ref": "#/components/schemas/VideoCodec" - }, - "type": "array" - }, - "bframes": { - "description": "B-frames", - "maximum": 16, - "minimum": -1, - "type": "integer" - }, - "cqMode": { - "$ref": "#/components/schemas/CQMode" - }, - "crf": { - "description": "CRF", - "maximum": 51, - "minimum": 0, - "type": "integer" - }, - "gopSize": { - "description": "GOP size", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "maxBitrate": { - "description": "Max bitrate", - "type": "string" - }, - "preferredHwDevice": { - "description": "Preferred hardware device", - "type": "string" - }, - "preset": { - "description": "Preset", + "SyncEntityType": { + "description": "Sync entity type", + "enum": [ + "AuthUserV1", + "UserV1", + "UserDeleteV1", + "AssetV1", + "AssetV2", + "AssetDeleteV1", + "AssetExifV1", + "AssetEditV1", + "AssetEditDeleteV1", + "AssetMetadataV1", + "AssetMetadataDeleteV1", + "AssetOcrV1", + "AssetOcrDeleteV1", + "PartnerV1", + "PartnerDeleteV1", + "PartnerAssetV1", + "PartnerAssetV2", + "PartnerAssetBackfillV1", + "PartnerAssetBackfillV2", + "PartnerAssetDeleteV1", + "PartnerAssetExifV1", + "PartnerAssetExifBackfillV1", + "PartnerStackBackfillV1", + "PartnerStackDeleteV1", + "PartnerStackV1", + "AlbumV1", + "AlbumV2", + "AlbumDeleteV1", + "AlbumUserV1", + "AlbumUserBackfillV1", + "AlbumUserDeleteV1", + "AlbumAssetCreateV1", + "AlbumAssetCreateV2", + "AlbumAssetUpdateV1", + "AlbumAssetUpdateV2", + "AlbumAssetBackfillV1", + "AlbumAssetBackfillV2", + "AlbumAssetExifCreateV1", + "AlbumAssetExifUpdateV1", + "AlbumAssetExifBackfillV1", + "AlbumToAssetV1", + "AlbumToAssetDeleteV1", + "AlbumToAssetBackfillV1", + "MemoryV1", + "MemoryDeleteV1", + "MemoryToAssetV1", + "MemoryToAssetDeleteV1", + "StackV1", + "StackDeleteV1", + "PersonV1", + "PersonDeleteV1", + "AssetFaceV1", + "AssetFaceV2", + "AssetFaceDeleteV1", + "UserMetadataV1", + "UserMetadataDeleteV1", + "SyncAckV1", + "SyncResetV1", + "SyncCompleteV1" + ], + "type": "string" + }, + "SyncMemoryAssetDeleteV1": { + "properties": { + "assetId": { + "description": "Asset ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "realtime": { - "$ref": "#/components/schemas/SystemConfigFFmpegRealtimeDto" - }, - "refs": { - "description": "References", - "maximum": 6, - "minimum": 0, - "type": "integer" - }, - "targetAudioCodec": { - "$ref": "#/components/schemas/AudioCodec" - }, - "targetResolution": { - "description": "Target resolution", + "memoryId": { + "description": "Memory ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" - }, - "targetVideoCodec": { - "$ref": "#/components/schemas/VideoCodec" - }, - "temporalAQ": { - "description": "Temporal AQ", - "type": "boolean" - }, - "threads": { - "description": "Threads", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" - }, - "tonemap": { - "$ref": "#/components/schemas/ToneMapping" - }, - "transcode": { - "$ref": "#/components/schemas/TranscodePolicy" - }, - "twoPass": { - "description": "Two pass", - "type": "boolean" } }, "required": [ - "accel", - "accelDecode", - "acceptedAudioCodecs", - "acceptedContainers", - "acceptedVideoCodecs", - "bframes", - "cqMode", - "crf", - "gopSize", - "maxBitrate", - "preferredHwDevice", - "preset", - "realtime", - "refs", - "targetAudioCodec", - "targetResolution", - "targetVideoCodec", - "temporalAQ", - "threads", - "tonemap", - "transcode", - "twoPass" + "assetId", + "memoryId" ], "type": "object" }, - "SystemConfigFFmpegRealtimeDto": { + "SyncMemoryAssetV1": { "properties": { - "enabled": { - "description": "Enable real-time HLS transcoding (alpha)", - "type": "boolean" - }, - "resolutions": { - "description": "Resolutions to use for real-time HLS transcoding", - "items": { - "$ref": "#/components/schemas/HlsVideoResolution" - }, - "type": "array" + "assetId": { + "description": "Asset ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" }, - "videoCodecs": { - "description": "Video codecs to use for real-time HLS transcoding", - "items": { - "$ref": "#/components/schemas/VideoCodec" - }, - "type": "array" + "memoryId": { + "description": "Memory ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" } }, "required": [ - "enabled", - "resolutions", - "videoCodecs" + "assetId", + "memoryId" ], "type": "object" }, - "SystemConfigFacesDto": { + "SyncMemoryDeleteV1": { "properties": { - "import": { - "description": "Import", - "type": "boolean" + "memoryId": { + "description": "Memory ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" } }, "required": [ - "import" + "memoryId" ], "type": "object" }, - "SystemConfigGeneratedFullsizeImageDto": { + "SyncMemoryV1": { "properties": { - "enabled": { - "description": "Enabled", - "type": "boolean" + "createdAt": { + "description": "Created at", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" }, - "format": { - "$ref": "#/components/schemas/ImageFormat" + "data": { + "additionalProperties": {}, + "description": "Data", + "type": "object" }, - "progressive": { - "description": "Progressive", + "deletedAt": { + "description": "Deleted at", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "nullable": true, + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "hideAt": { + "description": "Hide at", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "nullable": true, + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "id": { + "description": "Memory ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "isSaved": { + "description": "Is saved", "type": "boolean" }, - "quality": { - "description": "Quality", - "maximum": 100, - "minimum": 1, - "type": "integer" + "memoryAt": { + "description": "Memory at", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "ownerId": { + "description": "Owner ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "seenAt": { + "description": "Seen at", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "nullable": true, + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "showAt": { + "description": "Show at", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "nullable": true, + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/MemoryType" + }, + "updatedAt": { + "description": "Updated at", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" } }, "required": [ - "enabled", - "format", - "quality" + "createdAt", + "data", + "deletedAt", + "hideAt", + "id", + "isSaved", + "memoryAt", + "ownerId", + "seenAt", + "showAt", + "type", + "updatedAt" ], "type": "object" }, - "SystemConfigGeneratedImageDto": { + "SyncPartnerDeleteV1": { "properties": { - "format": { - "$ref": "#/components/schemas/ImageFormat" - }, - "progressive": { - "description": "Progressive", - "type": "boolean" - }, - "quality": { - "description": "Quality", - "maximum": 100, - "minimum": 1, - "type": "integer" + "sharedById": { + "description": "Shared by ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" }, - "size": { - "description": "Size", - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" + "sharedWithId": { + "description": "Shared with ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" } }, "required": [ - "format", - "quality", - "size" + "sharedById", + "sharedWithId" ], "type": "object" }, - "SystemConfigImageDto": { + "SyncPartnerV1": { "properties": { - "colorspace": { - "$ref": "#/components/schemas/Colorspace" - }, - "extractEmbedded": { - "description": "Extract embedded", + "inTimeline": { + "description": "In timeline", "type": "boolean" }, - "fullsize": { - "$ref": "#/components/schemas/SystemConfigGeneratedFullsizeImageDto" - }, - "preview": { - "$ref": "#/components/schemas/SystemConfigGeneratedImageDto" + "sharedById": { + "description": "Shared by ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" }, - "thumbnail": { - "$ref": "#/components/schemas/SystemConfigGeneratedImageDto" + "sharedWithId": { + "description": "Shared with ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" } }, "required": [ - "colorspace", - "extractEmbedded", - "fullsize", - "preview", - "thumbnail" + "inTimeline", + "sharedById", + "sharedWithId" ], "type": "object" }, - "SystemConfigIntegrityChecks": { - "description": "Integrity checks config", + "SyncPersonDeleteV1": { "properties": { - "checksumFiles": { - "$ref": "#/components/schemas/SystemConfigIntegrityChecksumJob" - }, - "missingFiles": { - "$ref": "#/components/schemas/SystemConfigIntegrityJob" - }, - "untrackedFiles": { - "$ref": "#/components/schemas/SystemConfigIntegrityJob" + "personId": { + "description": "Person ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" } }, "required": [ - "checksumFiles", - "missingFiles", - "untrackedFiles" + "personId" ], "type": "object" }, - "SystemConfigIntegrityChecksumJob": { - "description": "Integrity checksum job config", + "SyncPersonV1": { "properties": { - "cronExpression": { - "description": "Cron expression for when the integrity check should run", + "birthDate": { + "description": "Birth date", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "nullable": true, + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "enabled": { - "description": "Enabled", + "color": { + "description": "Color", + "nullable": true, + "type": "string" + }, + "createdAt": { + "description": "Created at", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" + }, + "faceAssetId": { + "description": "Face asset ID", + "nullable": true, + "type": "string" + }, + "id": { + "description": "Person ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "isFavorite": { + "description": "Is favorite", "type": "boolean" }, - "percentageLimit": { - "description": "Percentage limit of the integrity checksum job", - "format": "double", - "maximum": 1, - "minimum": 0, - "type": "number" + "isHidden": { + "description": "Is hidden", + "type": "boolean" }, - "timeLimit": { - "description": "How long the integrity checksum job may run for", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" + "name": { + "description": "Person name", + "type": "string" + }, + "ownerId": { + "description": "Owner ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "updatedAt": { + "description": "Updated at", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" } }, "required": [ - "cronExpression", - "enabled", - "percentageLimit", - "timeLimit" + "birthDate", + "color", + "createdAt", + "faceAssetId", + "id", + "isFavorite", + "isHidden", + "name", + "ownerId", + "updatedAt" ], "type": "object" }, - "SystemConfigIntegrityJob": { - "description": "Integrity job config", + "SyncRequestType": { + "description": "Sync request type", + "enum": [ + "AlbumsV1", + "AlbumsV2", + "AlbumUsersV1", + "AlbumToAssetsV1", + "AlbumAssetsV1", + "AlbumAssetsV2", + "AlbumAssetExifsV1", + "AssetsV1", + "AssetsV2", + "AssetExifsV1", + "AssetEditsV1", + "AssetMetadataV1", + "AssetOcrV1", + "AuthUsersV1", + "MemoriesV1", + "MemoryToAssetsV1", + "PartnersV1", + "PartnerAssetsV1", + "PartnerAssetsV2", + "PartnerAssetExifsV1", + "PartnerStacksV1", + "StacksV1", + "UsersV1", + "PeopleV1", + "AssetFacesV1", + "AssetFacesV2", + "UserMetadataV1" + ], + "type": "string" + }, + "SyncResetV1": { + "properties": {}, + "type": "object" + }, + "SyncStackDeleteV1": { "properties": { - "cronExpression": { - "description": "Cron expression for when the integrity check should run", + "stackId": { + "description": "Stack ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" - }, - "enabled": { - "description": "Enabled", - "type": "boolean" } }, "required": [ - "cronExpression", - "enabled" + "stackId" ], "type": "object" }, - "SystemConfigJobDto": { + "SyncStackV1": { "properties": { - "backgroundTask": { - "$ref": "#/components/schemas/JobSettingsDto" - }, - "editor": { - "$ref": "#/components/schemas/JobSettingsDto" - }, - "faceDetection": { - "$ref": "#/components/schemas/JobSettingsDto" - }, - "integrityCheck": { - "$ref": "#/components/schemas/JobSettingsDto" - }, - "library": { - "$ref": "#/components/schemas/JobSettingsDto" - }, - "metadataExtraction": { - "$ref": "#/components/schemas/JobSettingsDto" - }, - "migration": { - "$ref": "#/components/schemas/JobSettingsDto" - }, - "notifications": { - "$ref": "#/components/schemas/JobSettingsDto" - }, - "ocr": { - "$ref": "#/components/schemas/JobSettingsDto" - }, - "search": { - "$ref": "#/components/schemas/JobSettingsDto" - }, - "sidecar": { - "$ref": "#/components/schemas/JobSettingsDto" - }, - "smartSearch": { - "$ref": "#/components/schemas/JobSettingsDto" + "createdAt": { + "description": "Created at", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" }, - "thumbnailGeneration": { - "$ref": "#/components/schemas/JobSettingsDto" + "id": { + "description": "Stack ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" }, - "videoConversion": { - "$ref": "#/components/schemas/JobSettingsDto" + "ownerId": { + "description": "Owner ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" }, - "workflow": { - "$ref": "#/components/schemas/JobSettingsDto" - } - }, - "required": [ - "backgroundTask", - "editor", - "faceDetection", - "integrityCheck", - "library", - "metadataExtraction", - "migration", - "notifications", - "ocr", - "search", - "sidecar", - "smartSearch", - "thumbnailGeneration", - "videoConversion", - "workflow" - ], - "type": "object" - }, - "SystemConfigLibraryDto": { - "properties": { - "scan": { - "$ref": "#/components/schemas/SystemConfigLibraryScanDto" + "primaryAssetId": { + "description": "Primary asset ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" }, - "watch": { - "$ref": "#/components/schemas/SystemConfigLibraryWatchDto" + "updatedAt": { + "description": "Updated at", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" } }, "required": [ - "scan", - "watch" + "createdAt", + "id", + "ownerId", + "primaryAssetId", + "updatedAt" ], "type": "object" }, - "SystemConfigLibraryScanDto": { + "SyncStreamDto": { "properties": { - "cronExpression": { - "description": "Cron expression", - "type": "string" - }, - "enabled": { - "description": "Enabled", + "reset": { + "description": "Reset sync state", "type": "boolean" + }, + "types": { + "description": "Sync request types", + "items": { + "$ref": "#/components/schemas/SyncRequestType" + }, + "type": "array" } }, "required": [ - "cronExpression", - "enabled" + "types" ], "type": "object" }, - "SystemConfigLibraryWatchDto": { + "SyncUserDeleteV1": { "properties": { - "enabled": { - "description": "Enabled", - "type": "boolean" + "userId": { + "description": "User ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" } }, "required": [ - "enabled" + "userId" ], "type": "object" }, - "SystemConfigLoggingDto": { + "SyncUserMetadataDeleteV1": { "properties": { - "enabled": { - "description": "Enabled", - "type": "boolean" + "key": { + "$ref": "#/components/schemas/UserMetadataKey" }, - "level": { - "$ref": "#/components/schemas/LogLevel" + "userId": { + "description": "User ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" } }, "required": [ - "enabled", - "level" + "key", + "userId" ], "type": "object" }, - "SystemConfigMachineLearningDto": { + "SyncUserMetadataV1": { "properties": { - "availabilityChecks": { - "$ref": "#/components/schemas/MachineLearningAvailabilityChecksDto" - }, - "clip": { - "$ref": "#/components/schemas/CLIPConfig" - }, - "duplicateDetection": { - "$ref": "#/components/schemas/DuplicateDetectionConfig" - }, - "enabled": { - "description": "Enabled", - "type": "boolean" - }, - "facialRecognition": { - "$ref": "#/components/schemas/FacialRecognitionConfig" + "key": { + "$ref": "#/components/schemas/UserMetadataKey" }, - "ocr": { - "$ref": "#/components/schemas/OcrConfig" + "userId": { + "description": "User ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" }, - "urls": { - "description": "ML service URLs", - "items": { - "type": "string" - }, - "minItems": 1, - "type": "array" + "value": { + "additionalProperties": {}, + "description": "User metadata value", + "type": "object" } }, "required": [ - "availabilityChecks", - "clip", - "duplicateDetection", - "enabled", - "facialRecognition", - "ocr", - "urls" + "key", + "userId", + "value" ], "type": "object" }, - "SystemConfigMapDto": { + "SyncUserV1": { "properties": { - "darkStyle": { - "description": "Dark map style URL", - "format": "uri", + "avatarColor": { + "allOf": [ + { + "$ref": "#/components/schemas/UserAvatarColor" + } + ], + "nullable": true + }, + "deletedAt": { + "description": "User deleted at", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "nullable": true, + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" }, - "enabled": { - "description": "Enabled", + "email": { + "description": "User email", + "type": "string" + }, + "hasProfileImage": { + "description": "User has profile image", "type": "boolean" }, - "lightStyle": { - "description": "Light map style URL", - "format": "uri", + "id": { + "description": "User ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "name": { + "description": "User name", + "type": "string" + }, + "profileChangedAt": { + "description": "User profile changed at", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", "type": "string" } }, "required": [ - "darkStyle", - "enabled", - "lightStyle" - ], - "type": "object" - }, - "SystemConfigMetadataDto": { - "properties": { - "faces": { - "$ref": "#/components/schemas/SystemConfigFacesDto" - } - }, - "required": [ - "faces" + "deletedAt", + "email", + "hasProfileImage", + "id", + "name", + "profileChangedAt" ], "type": "object" }, - "SystemConfigNewVersionCheckDto": { + "SystemConfigTemplateStorageOptionDto": { "properties": { - "channel": { - "$ref": "#/components/schemas/ReleaseChannel" + "dayOptions": { + "description": "Available day format options for storage template", + "items": { + "type": "string" + }, + "type": "array" }, - "enabled": { - "description": "Enabled", - "type": "boolean" + "hourOptions": { + "description": "Available hour format options for storage template", + "items": { + "type": "string" + }, + "type": "array" + }, + "minuteOptions": { + "description": "Available minute format options for storage template", + "items": { + "type": "string" + }, + "type": "array" + }, + "monthOptions": { + "description": "Available month format options for storage template", + "items": { + "type": "string" + }, + "type": "array" + }, + "presetOptions": { + "description": "Available preset template options", + "items": { + "type": "string" + }, + "type": "array" + }, + "secondOptions": { + "description": "Available second format options for storage template", + "items": { + "type": "string" + }, + "type": "array" + }, + "weekOptions": { + "description": "Available week format options for storage template", + "items": { + "type": "string" + }, + "type": "array" + }, + "yearOptions": { + "description": "Available year format options for storage template", + "items": { + "type": "string" + }, + "type": "array" } }, "required": [ - "channel", - "enabled" + "dayOptions", + "hourOptions", + "minuteOptions", + "monthOptions", + "presetOptions", + "secondOptions", + "weekOptions", + "yearOptions" ], "type": "object" }, - "SystemConfigNightlyTasksDto": { + "TagBulkAssetsDto": { "properties": { - "clusterNewFaces": { - "description": "Cluster new faces", - "type": "boolean" - }, - "databaseCleanup": { - "description": "Database cleanup", - "type": "boolean" - }, - "generateMemories": { - "description": "Generate memories", - "type": "boolean" - }, - "missingThumbnails": { - "description": "Missing thumbnails", - "type": "boolean" - }, - "startTime": { - "description": "Start time (HH:MM)", - "pattern": "^(?:[01]\\d|2[0-3]):[0-5]\\d$", - "type": "string" + "assetIds": { + "description": "Asset IDs", + "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "type": "array" }, - "syncQuotaUsage": { - "description": "Sync quota usage", - "type": "boolean" + "tagIds": { + "description": "Tag IDs", + "items": { + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "type": "array" } }, "required": [ - "clusterNewFaces", - "databaseCleanup", - "generateMemories", - "missingThumbnails", - "startTime", - "syncQuotaUsage" + "assetIds", + "tagIds" ], "type": "object" }, - "SystemConfigNotificationsDto": { + "TagBulkAssetsResponseDto": { "properties": { - "smtp": { - "$ref": "#/components/schemas/SystemConfigSmtpDto" + "count": { + "description": "Number of assets tagged", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" } }, "required": [ - "smtp" + "count" ], "type": "object" }, - "SystemConfigOAuthDto": { + "TagCreateDto": { "properties": { - "allowInsecureRequests": { - "description": "Allow insecure requests", - "type": "boolean" - }, - "autoLaunch": { - "description": "Auto launch", - "type": "boolean" - }, - "autoRegister": { - "description": "Auto register", - "type": "boolean" - }, - "buttonText": { - "description": "Button text", - "type": "string" - }, - "clientId": { - "description": "Client ID", - "type": "string" - }, - "clientSecret": { - "description": "Client secret", - "type": "string" - }, - "defaultStorageQuota": { - "description": "Default storage quota", - "maximum": 9007199254740991, - "minimum": 0, + "color": { + "description": "Tag color (hex)", "nullable": true, - "type": "integer" - }, - "enabled": { - "description": "Enabled", - "type": "boolean" - }, - "endSessionEndpoint": { - "description": "End session endpoint", + "pattern": "^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$", "type": "string" }, - "issuerUrl": { - "description": "Issuer URL", + "name": { + "description": "Tag name", "type": "string" }, - "mobileOverrideEnabled": { - "description": "Mobile override enabled", - "type": "boolean" - }, - "mobileRedirectUri": { - "description": "Mobile redirect URI (set to empty string to disable)", + "parentId": { + "description": "Parent tag ID", + "format": "uuid", + "nullable": true, + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" - }, - "profileSigningAlgorithm": { - "description": "Profile signing algorithm", + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "TagResponseDto": { + "properties": { + "color": { + "description": "Tag color (hex)", "type": "string" }, - "prompt": { - "description": "OAuth prompt parameter (e.g. select_account, login, consent)", + "createdAt": { + "description": "Creation date", + "format": "date-time", "type": "string" }, - "roleClaim": { - "description": "Role claim", + "id": { + "description": "Tag ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "scope": { - "description": "Scope", + "name": { + "description": "Tag name", "type": "string" }, - "signingAlgorithm": { - "description": "Signing algorithm", + "parentId": { + "description": "Parent tag ID", "type": "string" }, - "storageLabelClaim": { - "description": "Storage label claim", + "updatedAt": { + "description": "Last update date", + "format": "date-time", "type": "string" }, - "storageQuotaClaim": { - "description": "Storage quota claim", + "value": { + "description": "Tag value (full path)", "type": "string" - }, - "timeout": { - "description": "Timeout", - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" - }, - "tokenEndpointAuthMethod": { - "$ref": "#/components/schemas/OAuthTokenEndpointAuthMethod" - } - }, - "required": [ - "allowInsecureRequests", - "autoLaunch", - "autoRegister", - "buttonText", - "clientId", - "clientSecret", - "defaultStorageQuota", - "enabled", - "endSessionEndpoint", - "issuerUrl", - "mobileOverrideEnabled", - "mobileRedirectUri", - "profileSigningAlgorithm", - "prompt", - "roleClaim", - "scope", - "signingAlgorithm", - "storageLabelClaim", - "storageQuotaClaim", - "timeout", - "tokenEndpointAuthMethod" - ], - "type": "object" - }, - "SystemConfigPasswordLoginDto": { - "properties": { - "enabled": { - "description": "Enabled", - "type": "boolean" - } - }, - "required": [ - "enabled" - ], - "type": "object" - }, - "SystemConfigReverseGeocodingDto": { - "properties": { - "enabled": { - "description": "Enabled", - "type": "boolean" } }, "required": [ - "enabled" + "createdAt", + "id", + "name", + "updatedAt", + "value" ], "type": "object" }, - "SystemConfigServerDto": { + "TagUpdateDto": { "properties": { - "externalDomain": { - "description": "External domain", - "type": "string" - }, - "loginPageMessage": { - "description": "Login page message", + "color": { + "description": "Tag color (hex)", + "nullable": true, + "pattern": "^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$", "type": "string" - }, - "publicUsers": { - "description": "Public users", - "type": "boolean" } }, - "required": [ - "externalDomain", - "loginPageMessage", - "publicUsers" - ], "type": "object" }, - "SystemConfigSmtpDto": { + "TagUpsertDto": { "properties": { - "enabled": { - "description": "Whether SMTP email notifications are enabled", - "type": "boolean" - }, - "from": { - "description": "Email address to send from", - "type": "string" - }, - "replyTo": { - "description": "Email address for replies", - "type": "string" - }, - "transport": { - "$ref": "#/components/schemas/SystemConfigSmtpTransportDto" + "tags": { + "description": "Tag names to upsert", + "items": { + "type": "string" + }, + "type": "array" } }, "required": [ - "enabled", - "from", - "replyTo", - "transport" + "tags" ], "type": "object" }, - "SystemConfigSmtpTransportDto": { - "properties": { - "host": { - "description": "SMTP server hostname", - "type": "string" - }, - "ignoreCert": { - "description": "Whether to ignore SSL certificate errors", - "type": "boolean" - }, - "password": { - "description": "SMTP password", - "type": "string" - }, - "port": { - "description": "SMTP server port", - "maximum": 65535, - "minimum": 0, - "type": "integer" - }, - "secure": { - "description": "Whether to use secure connection (TLS/SSL)", + "TagsResponse": { + "properties": { + "enabled": { + "description": "Whether tags are enabled", "type": "boolean" }, - "username": { - "description": "SMTP username", - "type": "string" + "sidebarWeb": { + "description": "Whether tags appear in web sidebar", + "type": "boolean" } }, "required": [ - "host", - "ignoreCert", - "password", - "port", - "secure", - "username" + "enabled", + "sidebarWeb" ], "type": "object" }, - "SystemConfigStorageTemplateDto": { + "TagsUpdate": { "properties": { "enabled": { - "description": "Enabled", + "description": "Whether tags are enabled", "type": "boolean" }, - "hashVerificationEnabled": { - "description": "Hash verification enabled", + "sidebarWeb": { + "description": "Whether tags appear in web sidebar", "type": "boolean" - }, + } + }, + "type": "object" + }, + "TemplateDto": { + "properties": { "template": { - "description": "Template", + "description": "Template name", "type": "string" } }, "required": [ - "enabled", - "hashVerificationEnabled", "template" ], "type": "object" }, - "SystemConfigTemplateEmailsDto": { + "TemplateResponseDto": { "properties": { - "albumInviteTemplate": { - "description": "Album invite template", + "html": { + "description": "Template HTML content", "type": "string" }, - "albumUpdateTemplate": { - "description": "Album update template", + "name": { + "description": "Template name", "type": "string" - }, - "welcomeTemplate": { - "description": "Welcome template", + } + }, + "required": [ + "html", + "name" + ], + "type": "object" + }, + "TestEmailResponseDto": { + "properties": { + "messageId": { + "description": "Email message ID", "type": "string" } }, "required": [ - "albumInviteTemplate", - "albumUpdateTemplate", - "welcomeTemplate" + "messageId" ], "type": "object" }, - "SystemConfigTemplateStorageOptionDto": { + "TimeBucketAssetResponseDto": { "properties": { - "dayOptions": { - "description": "Available day format options for storage template", + "city": { + "description": "Array of city names extracted from EXIF GPS data", "items": { + "nullable": true, "type": "string" }, "type": "array" }, - "hourOptions": { - "description": "Available hour format options for storage template", + "country": { + "description": "Array of country names extracted from EXIF GPS data", "items": { + "nullable": true, "type": "string" }, "type": "array" }, - "minuteOptions": { - "description": "Available minute format options for storage template", + "createdAt": { + "description": "Array of UTC timestamps when each asset was originally uploaded to Immich", "items": { "type": "string" }, "type": "array" }, - "monthOptions": { - "description": "Available month format options for storage template", + "duration": { + "description": "Array of video/gif durations in milliseconds (null for static images)", + "items": { + "maximum": 2147483647, + "minimum": 0, + "nullable": true, + "type": "integer" + }, + "type": "array" + }, + "fileCreatedAt": { + "description": "Array of file creation timestamps in UTC", "items": { "type": "string" }, "type": "array" }, - "presetOptions": { - "description": "Available preset template options", + "id": { + "description": "Array of asset IDs in the time bucket", "items": { "type": "string" }, "type": "array" }, - "secondOptions": { - "description": "Available second format options for storage template", + "isFavorite": { + "description": "Array indicating whether each asset is favorited", + "items": { + "type": "boolean" + }, + "type": "array" + }, + "isImage": { + "description": "Array indicating whether each asset is an image (false for videos)", + "items": { + "type": "boolean" + }, + "type": "array" + }, + "isTrashed": { + "description": "Array indicating whether each asset is in the trash", + "items": { + "type": "boolean" + }, + "type": "array" + }, + "latitude": { + "description": "Array of latitude coordinates extracted from EXIF GPS data", + "items": { + "nullable": true, + "type": "number" + }, + "type": "array" + }, + "livePhotoVideoId": { + "description": "Array of live photo video asset IDs (null for non-live photos)", "items": { + "nullable": true, "type": "string" }, "type": "array" }, - "weekOptions": { - "description": "Available week format options for storage template", + "localOffsetHours": { + "description": "Array of UTC offset hours at the time each photo was taken. Positive values are east of UTC, negative values are west of UTC. Values may be fractional (e.g., 5.5 for +05:30, -9.75 for -09:45). Applying this offset to 'fileCreatedAt' will give you the time the photo was taken from the photographer's perspective.", + "items": { + "type": "number" + }, + "type": "array" + }, + "longitude": { + "description": "Array of longitude coordinates extracted from EXIF GPS data", + "items": { + "nullable": true, + "type": "number" + }, + "type": "array" + }, + "ownerId": { + "description": "Array of owner IDs for each asset", "items": { "type": "string" }, "type": "array" }, - "yearOptions": { - "description": "Available year format options for storage template", + "projectionType": { + "description": "Array of projection types for 360° content (e.g., \"EQUIRECTANGULAR\", \"CUBEFACE\", \"CYLINDRICAL\")", "items": { + "nullable": true, "type": "string" }, "type": "array" - } - }, - "required": [ - "dayOptions", - "hourOptions", - "minuteOptions", - "monthOptions", - "presetOptions", - "secondOptions", - "weekOptions", - "yearOptions" - ], - "type": "object" - }, - "SystemConfigTemplatesDto": { - "properties": { - "email": { - "$ref": "#/components/schemas/SystemConfigTemplateEmailsDto" - } - }, - "required": [ - "email" - ], - "type": "object" - }, - "SystemConfigThemeDto": { - "properties": { - "customCss": { - "description": "Custom CSS for theming", - "type": "string" - } - }, - "required": [ - "customCss" - ], - "type": "object" - }, - "SystemConfigTrashDto": { - "properties": { - "days": { - "description": "Days", - "maximum": 9007199254740991, - "minimum": 0, - "type": "integer" }, - "enabled": { - "description": "Enabled", - "type": "boolean" + "ratio": { + "description": "Array of aspect ratios (width/height) for each asset", + "items": { + "type": "number" + }, + "type": "array" + }, + "stack": { + "description": "Array of stack information as [stackId, assetCount] tuples (null for non-stacked assets)", + "items": { + "items": { + "type": "string" + }, + "maxItems": 2, + "minItems": 2, + "nullable": true, + "type": "array" + }, + "type": "array" + }, + "thumbhash": { + "description": "Array of BlurHash strings for generating asset previews (base64 encoded)", + "items": { + "nullable": true, + "type": "string" + }, + "type": "array" + }, + "visibility": { + "description": "Array of visibility statuses for each asset (e.g., ARCHIVE, TIMELINE, HIDDEN, LOCKED)", + "items": { + "$ref": "#/components/schemas/AssetVisibility" + }, + "type": "array" } }, "required": [ - "days", - "enabled" + "createdAt", + "duration", + "fileCreatedAt", + "id", + "isFavorite", + "isImage", + "isTrashed", + "livePhotoVideoId", + "localOffsetHours", + "ownerId", + "projectionType", + "ratio", + "thumbhash", + "visibility" ], "type": "object" }, - "SystemConfigUserDto": { + "TimeBucketsResponseDto": { "properties": { - "deleteDelay": { - "description": "Delete delay", + "count": { + "description": "Number of assets in this time bucket", + "example": 42, "maximum": 9007199254740991, - "minimum": 1, + "minimum": -9007199254740991, "type": "integer" + }, + "timeBucket": { + "description": "Time bucket identifier in YYYY-MM-DD format representing the start of the time period", + "example": "2024-01-01", + "type": "string" } }, "required": [ - "deleteDelay" + "count", + "timeBucket" ], "type": "object" }, - "TagBulkAssetsDto": { - "properties": { - "assetIds": { - "description": "Asset IDs", - "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "type": "array" - }, - "tagIds": { - "description": "Tag IDs", - "items": { - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "assetIds", - "tagIds" + "ToneMapping": { + "description": "Tone mapping", + "enum": [ + "hable", + "mobius", + "reinhard", + "disabled" ], - "type": "object" + "type": "string" }, - "TagBulkAssetsResponseDto": { + "TranscodeHWAccel": { + "description": "Transcode hardware acceleration", + "enum": [ + "nvenc", + "qsv", + "vaapi", + "rkmpp", + "disabled" + ], + "type": "string" + }, + "TranscodePolicy": { + "description": "Transcode policy", + "enum": [ + "all", + "optimal", + "bitrate", + "required", + "disabled" + ], + "type": "string" + }, + "TrashResponseDto": { "properties": { "count": { - "description": "Number of assets tagged", + "description": "Number of items in trash", "maximum": 9007199254740991, "minimum": -9007199254740991, "type": "integer" @@ -26920,865 +27764,818 @@ ], "type": "object" }, - "TagCreateDto": { + "UpdateAlbumDto": { "properties": { - "color": { - "description": "Tag color (hex)", - "nullable": true, - "pattern": "^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$", - "type": "string" - }, - "name": { - "description": "Tag name", + "albumName": { + "description": "Album name", "type": "string" }, - "parentId": { - "description": "Parent tag ID", + "albumThumbnailAssetId": { + "description": "Album thumbnail asset ID", "format": "uuid", - "nullable": true, "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" + }, + "description": { + "description": "Album description", + "nullable": true, + "type": "string", + "x-immich-history": [ + { + "version": "v1", + "state": "Added" + }, + { + "version": "v3", + "state": "Updated", + "description": "Sending an empty string is deprecated; send null instead. Empty strings will no longer be coerced to null in v4." + } + ] + }, + "isActivityEnabled": { + "description": "Enable activity feed", + "type": "boolean" + }, + "order": { + "$ref": "#/components/schemas/AssetOrder" + } + }, + "type": "object" + }, + "UpdateAlbumUserDto": { + "properties": { + "role": { + "$ref": "#/components/schemas/AlbumUserRole" } }, "required": [ - "name" + "role" ], "type": "object" }, - "TagResponseDto": { + "UpdateAssetDto": { "properties": { - "color": { - "description": "Tag color (hex)", + "dateTimeOriginal": { + "description": "Original date and time", "type": "string" }, - "createdAt": { - "description": "Creation date", - "format": "date-time", + "description": { + "description": "Asset description", "type": "string" }, - "id": { - "description": "Tag ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" + "isFavorite": { + "description": "Mark as favorite", + "type": "boolean" }, - "name": { - "description": "Tag name", - "type": "string" + "latitude": { + "description": "Latitude coordinate", + "maximum": 90, + "minimum": -90, + "type": "number" }, - "parentId": { - "description": "Parent tag ID", + "livePhotoVideoId": { + "description": "Live photo video ID", + "format": "uuid", + "nullable": true, + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", "type": "string" }, - "updatedAt": { - "description": "Last update date", - "format": "date-time", - "type": "string" + "longitude": { + "description": "Longitude coordinate", + "maximum": 180, + "minimum": -180, + "type": "number" }, - "value": { - "description": "Tag value (full path)", - "type": "string" - } - }, - "required": [ - "createdAt", - "id", - "name", - "updatedAt", - "value" - ], - "type": "object" - }, - "TagUpdateDto": { - "properties": { - "color": { - "description": "Tag color (hex)", + "rating": { + "description": "Rating in range [1-5] (starred), -1 (rejected), or null (unrated)", + "maximum": 5, + "minimum": -1, "nullable": true, - "pattern": "^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{4}|[0-9A-Fa-f]{6}|[0-9A-Fa-f]{8})$", - "type": "string" + "type": "integer", + "x-immich-history": [ + { + "version": "v1", + "state": "Added" + }, + { + "version": "v2", + "state": "Stable" + }, + { + "version": "v3", + "state": "Updated", + "description": "Using 0 as a rating is no longer valid." + } + ], + "x-immich-state": "Stable" + }, + "visibility": { + "$ref": "#/components/schemas/AssetVisibility" } }, "type": "object" }, - "TagUpsertDto": { + "UpdateLibraryDto": { "properties": { - "tags": { - "description": "Tag names to upsert", + "exclusionPatterns": { + "description": "Exclusion patterns (max 128)", + "items": { + "type": "string" + }, + "maxItems": 128, + "type": "array" + }, + "importPaths": { + "description": "Import paths (max 128)", "items": { "type": "string" }, + "maxItems": 128, "type": "array" + }, + "name": { + "description": "Library name", + "minLength": 1, + "type": "string" } }, - "required": [ - "tags" - ], "type": "object" }, - "TagsResponse": { + "UsageByUserDto": { "properties": { - "enabled": { - "description": "Whether tags are enabled", - "type": "boolean" + "photos": { + "description": "Number of photos", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "quotaSizeInBytes": { + "description": "User quota size in bytes (null if unlimited)", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "nullable": true, + "type": "integer" + }, + "usage": { + "description": "Total storage usage in bytes", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "usagePhotos": { + "description": "Storage usage for photos in bytes", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" }, - "sidebarWeb": { - "description": "Whether tags appear in web sidebar", - "type": "boolean" + "usageVideos": { + "description": "Storage usage for videos in bytes", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "userId": { + "description": "User ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" + }, + "userName": { + "description": "User name", + "type": "string" + }, + "videos": { + "description": "Number of videos", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" } }, "required": [ - "enabled", - "sidebarWeb" + "photos", + "quotaSizeInBytes", + "usage", + "usagePhotos", + "usageVideos", + "userId", + "userName", + "videos" ], "type": "object" }, - "TagsUpdate": { + "UserAdminCreateDto": { "properties": { - "enabled": { - "description": "Whether tags are enabled", + "avatarColor": { + "allOf": [ + { + "$ref": "#/components/schemas/UserAvatarColor" + } + ], + "nullable": true + }, + "email": { + "description": "User email", + "format": "email", + "pattern": "^[\\p{L}\\p{M}\\p{N}.!#$%&'*+/=?^_`{|}~-]+@[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?(?:\\.[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?)*$", + "type": "string" + }, + "isAdmin": { + "description": "Grant admin privileges", "type": "boolean" }, - "sidebarWeb": { - "description": "Whether tags appear in web sidebar", + "name": { + "description": "User name", + "type": "string" + }, + "notify": { + "description": "Send notification email", "type": "boolean" - } - }, - "type": "object" - }, - "TemplateDto": { - "properties": { - "template": { - "description": "Template name", + }, + "password": { + "description": "User password", "type": "string" - } - }, - "required": [ - "template" - ], - "type": "object" - }, - "TemplateResponseDto": { - "properties": { - "html": { - "description": "Template HTML content", + }, + "pinCode": { + "description": "PIN code", + "example": "123456", + "nullable": true, + "pattern": "^\\d{6}$", "type": "string" }, - "name": { - "description": "Template name", + "quotaSizeInBytes": { + "description": "Storage quota in bytes", + "maximum": 9007199254740991, + "minimum": 0, + "nullable": true, + "type": "integer" + }, + "shouldChangePassword": { + "description": "Require password change on next login", + "type": "boolean" + }, + "storageLabel": { + "description": "Storage label", + "nullable": true, "type": "string" } }, "required": [ - "html", - "name" + "email", + "name", + "password" ], "type": "object" }, - "TestEmailResponseDto": { + "UserAdminDeleteDto": { "properties": { - "messageId": { - "description": "Email message ID", - "type": "string" + "force": { + "description": "Force delete even if user has assets", + "type": "boolean" } }, - "required": [ - "messageId" - ], "type": "object" }, - "TimeBucketAssetResponseDto": { + "UserAdminResponseDto": { "properties": { - "city": { - "description": "Array of city names extracted from EXIF GPS data", - "items": { - "nullable": true, - "type": "string" - }, - "type": "array" - }, - "country": { - "description": "Array of country names extracted from EXIF GPS data", - "items": { - "nullable": true, - "type": "string" - }, - "type": "array" + "avatarColor": { + "$ref": "#/components/schemas/UserAvatarColor" }, "createdAt": { - "description": "Array of UTC timestamps when each asset was originally uploaded to Immich", - "items": { - "type": "string" - }, - "type": "array" + "description": "Creation date", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" }, - "duration": { - "description": "Array of video/gif durations in milliseconds (null for static images)", - "items": { - "maximum": 2147483647, - "minimum": 0, - "nullable": true, - "type": "integer" - }, - "type": "array" + "deletedAt": { + "description": "Deletion date", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "nullable": true, + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" }, - "fileCreatedAt": { - "description": "Array of file creation timestamps in UTC", - "items": { - "type": "string" - }, - "type": "array" + "email": { + "description": "User email", + "format": "email", + "pattern": "^[\\p{L}\\p{M}\\p{N}.!#$%&'*+/=?^_`{|}~-]+@[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?(?:\\.[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?)*$", + "type": "string" }, "id": { - "description": "Array of asset IDs in the time bucket", - "items": { - "type": "string" - }, - "type": "array" - }, - "isFavorite": { - "description": "Array indicating whether each asset is favorited", - "items": { - "type": "boolean" - }, - "type": "array" + "description": "User ID", + "format": "uuid", + "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", + "type": "string" }, - "isImage": { - "description": "Array indicating whether each asset is an image (false for videos)", - "items": { - "type": "boolean" - }, - "type": "array" + "isAdmin": { + "description": "Is admin user", + "type": "boolean" }, - "isTrashed": { - "description": "Array indicating whether each asset is in the trash", - "items": { - "type": "boolean" - }, - "type": "array" + "license": { + "allOf": [ + { + "$ref": "#/components/schemas/UserLicense" + } + ], + "nullable": true }, - "latitude": { - "description": "Array of latitude coordinates extracted from EXIF GPS data", - "items": { - "nullable": true, - "type": "number" - }, - "type": "array" + "name": { + "description": "User name", + "type": "string" }, - "livePhotoVideoId": { - "description": "Array of live photo video asset IDs (null for non-live photos)", - "items": { - "nullable": true, - "type": "string" - }, - "type": "array" + "oauthId": { + "description": "OAuth ID", + "type": "string" }, - "localOffsetHours": { - "description": "Array of UTC offset hours at the time each photo was taken. Positive values are east of UTC, negative values are west of UTC. Values may be fractional (e.g., 5.5 for +05:30, -9.75 for -09:45). Applying this offset to 'fileCreatedAt' will give you the time the photo was taken from the photographer's perspective.", - "items": { - "type": "number" - }, - "type": "array" + "profileChangedAt": { + "description": "Profile change date", + "format": "date-time", + "type": "string" }, - "longitude": { - "description": "Array of longitude coordinates extracted from EXIF GPS data", - "items": { - "nullable": true, - "type": "number" - }, - "type": "array" + "profileImagePath": { + "description": "Profile image path", + "type": "string" }, - "ownerId": { - "description": "Array of owner IDs for each asset", - "items": { - "type": "string" - }, - "type": "array" + "quotaSizeInBytes": { + "description": "Storage quota in bytes", + "maximum": 9007199254740991, + "minimum": 0, + "nullable": true, + "type": "integer" }, - "projectionType": { - "description": "Array of projection types for 360° content (e.g., \"EQUIRECTANGULAR\", \"CUBEFACE\", \"CYLINDRICAL\")", - "items": { - "nullable": true, - "type": "string" - }, - "type": "array" + "quotaUsageInBytes": { + "description": "Storage usage in bytes", + "maximum": 9007199254740991, + "minimum": 0, + "nullable": true, + "type": "integer" }, - "ratio": { - "description": "Array of aspect ratios (width/height) for each asset", - "items": { - "type": "number" - }, - "type": "array" + "shouldChangePassword": { + "description": "Require password change on next login", + "type": "boolean" }, - "stack": { - "description": "Array of stack information as [stackId, assetCount] tuples (null for non-stacked assets)", - "items": { - "items": { - "type": "string" - }, - "maxItems": 2, - "minItems": 2, - "nullable": true, - "type": "array" - }, - "type": "array" + "status": { + "$ref": "#/components/schemas/UserStatus" }, - "thumbhash": { - "description": "Array of BlurHash strings for generating asset previews (base64 encoded)", - "items": { - "nullable": true, - "type": "string" - }, - "type": "array" + "storageLabel": { + "description": "Storage label", + "nullable": true, + "type": "string" }, - "visibility": { - "description": "Array of visibility statuses for each asset (e.g., ARCHIVE, TIMELINE, HIDDEN, LOCKED)", - "items": { - "$ref": "#/components/schemas/AssetVisibility" - }, - "type": "array" + "updatedAt": { + "description": "Last update date", + "example": "2024-01-01T00:00:00.000Z", + "format": "date-time", + "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", + "type": "string" } }, "required": [ + "avatarColor", "createdAt", - "duration", - "fileCreatedAt", + "deletedAt", + "email", "id", - "isFavorite", - "isImage", - "isTrashed", - "livePhotoVideoId", - "localOffsetHours", - "ownerId", - "projectionType", - "ratio", - "thumbhash", - "visibility" + "isAdmin", + "license", + "name", + "oauthId", + "profileChangedAt", + "profileImagePath", + "quotaSizeInBytes", + "quotaUsageInBytes", + "shouldChangePassword", + "status", + "storageLabel", + "updatedAt" ], "type": "object" }, - "TimeBucketsResponseDto": { + "UserAdminUpdateDto": { "properties": { - "count": { - "description": "Number of assets in this time bucket", - "example": 42, + "avatarColor": { + "allOf": [ + { + "$ref": "#/components/schemas/UserAvatarColor" + } + ], + "nullable": true + }, + "email": { + "description": "User email", + "format": "email", + "pattern": "^[\\p{L}\\p{M}\\p{N}.!#$%&'*+/=?^_`{|}~-]+@[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?(?:\\.[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?)*$", + "type": "string" + }, + "isAdmin": { + "description": "Grant admin privileges", + "type": "boolean" + }, + "name": { + "description": "User name", + "type": "string" + }, + "password": { + "description": "User password", + "type": "string" + }, + "pinCode": { + "description": "PIN code", + "example": "123456", + "nullable": true, + "pattern": "^\\d{6}$", + "type": "string" + }, + "quotaSizeInBytes": { + "description": "Storage quota in bytes", "maximum": 9007199254740991, - "minimum": -9007199254740991, + "minimum": 0, + "nullable": true, "type": "integer" }, - "timeBucket": { - "description": "Time bucket identifier in YYYY-MM-DD format representing the start of the time period", - "example": "2024-01-01", + "shouldChangePassword": { + "description": "Require password change on next login", + "type": "boolean" + }, + "storageLabel": { + "description": "Storage label", + "nullable": true, "type": "string" } }, - "required": [ - "count", - "timeBucket" - ], "type": "object" }, - "ToneMapping": { - "description": "Tone mapping", - "enum": [ - "hable", - "mobius", - "reinhard", - "disabled" - ], - "type": "string" - }, - "TranscodeHWAccel": { - "description": "Transcode hardware acceleration", - "enum": [ - "nvenc", - "qsv", - "vaapi", - "rkmpp", - "disabled" - ], - "type": "string" - }, - "TranscodePolicy": { - "description": "Transcode policy", + "UserAvatarColor": { + "description": "User avatar color", "enum": [ - "all", - "optimal", - "bitrate", - "required", - "disabled" + "primary", + "pink", + "red", + "yellow", + "blue", + "green", + "purple", + "orange", + "gray", + "amber" ], "type": "string" }, - "TrashResponseDto": { + "UserConfigClipDto": { "properties": { - "count": { - "description": "Number of items in trash", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "enabled": { + "description": "Whether the task is enabled", + "type": "boolean" } }, "required": [ - "count" + "enabled" ], "type": "object" }, - "UpdateAlbumDto": { + "UserConfigDto": { + "description": "Configuration properties that are visible to a logged user", "properties": { - "albumName": { - "description": "Album name", - "type": "string" + "ffmpeg": { + "$ref": "#/components/schemas/UserConfigFFmpegDto" }, - "albumThumbnailAssetId": { - "description": "Album thumbnail asset ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" + "image": { + "$ref": "#/components/schemas/UserConfigImageDto" }, - "description": { - "description": "Album description", - "nullable": true, - "type": "string", - "x-immich-history": [ - { - "version": "v1", - "state": "Added" - }, - { - "version": "v3", - "state": "Updated", - "description": "Sending an empty string is deprecated; send null instead. Empty strings will no longer be coerced to null in v4." - } - ] + "machineLearning": { + "$ref": "#/components/schemas/UserConfigMachineLearningDto" }, - "isActivityEnabled": { - "description": "Enable activity feed", - "type": "boolean" + "map": { + "$ref": "#/components/schemas/UserConfigMapDto" }, - "order": { - "$ref": "#/components/schemas/AssetOrder" + "oauth": { + "$ref": "#/components/schemas/UserConfigOAuthDto" + }, + "passwordLogin": { + "$ref": "#/components/schemas/UserConfigPasswordLoginDto" + }, + "reverseGeocoding": { + "$ref": "#/components/schemas/UserConfigReverseGeocodingDto" + }, + "server": { + "$ref": "#/components/schemas/UserConfigServerDto" + }, + "theme": { + "$ref": "#/components/schemas/UserConfigThemeDto" + }, + "trash": { + "$ref": "#/components/schemas/UserConfigTrashDto" + }, + "user": { + "$ref": "#/components/schemas/UserConfigUserDto" } }, + "required": [ + "ffmpeg", + "image", + "machineLearning", + "map", + "oauth", + "passwordLogin", + "reverseGeocoding", + "server", + "theme", + "trash", + "user" + ], "type": "object" }, - "UpdateAlbumUserDto": { + "UserConfigDuplicateDetectionDto": { "properties": { - "role": { - "$ref": "#/components/schemas/AlbumUserRole" + "enabled": { + "description": "Whether the task is enabled", + "type": "boolean" } }, "required": [ - "role" + "enabled" ], "type": "object" }, - "UpdateAssetDto": { + "UserConfigFFmpegDto": { "properties": { - "dateTimeOriginal": { - "description": "Original date and time", - "type": "string" - }, - "description": { - "description": "Asset description", - "type": "string" - }, - "isFavorite": { - "description": "Mark as favorite", - "type": "boolean" - }, - "latitude": { - "description": "Latitude coordinate", - "maximum": 90, - "minimum": -90, - "type": "number" - }, - "livePhotoVideoId": { - "description": "Live photo video ID", - "format": "uuid", - "nullable": true, - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "longitude": { - "description": "Longitude coordinate", - "maximum": 180, - "minimum": -180, - "type": "number" - }, - "rating": { - "description": "Rating in range [1-5] (starred), -1 (rejected), or null (unrated)", - "maximum": 5, - "minimum": -1, - "nullable": true, - "type": "integer", - "x-immich-history": [ - { - "version": "v1", - "state": "Added" - }, - { - "version": "v2", - "state": "Stable" - }, - { - "version": "v3", - "state": "Updated", - "description": "Using 0 as a rating is no longer valid." - } - ], - "x-immich-state": "Stable" - }, - "visibility": { - "$ref": "#/components/schemas/AssetVisibility" + "realtime": { + "$ref": "#/components/schemas/UserConfigFFmpegRealtimeDto" } }, + "required": [ + "realtime" + ], "type": "object" }, - "UpdateLibraryDto": { + "UserConfigFFmpegRealtimeDto": { "properties": { - "exclusionPatterns": { - "description": "Exclusion patterns (max 128)", + "enabled": { + "description": "Enable real-time HLS transcoding (alpha)", + "type": "boolean" + }, + "resolutions": { + "description": "Resolutions to use for real-time HLS transcoding", "items": { - "type": "string" + "$ref": "#/components/schemas/HlsVideoResolution" }, - "maxItems": 128, "type": "array" }, - "importPaths": { - "description": "Import paths (max 128)", + "videoCodecs": { + "description": "Video codecs to use for real-time HLS transcoding", "items": { - "type": "string" + "$ref": "#/components/schemas/VideoCodec" }, - "maxItems": 128, "type": "array" - }, - "name": { - "description": "Library name", - "minLength": 1, - "type": "string" } }, + "required": [ + "enabled", + "resolutions", + "videoCodecs" + ], "type": "object" }, - "UsageByUserDto": { + "UserConfigFacialRecognitionDto": { "properties": { - "photos": { - "description": "Number of photos", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "quotaSizeInBytes": { - "description": "User quota size in bytes (null if unlimited)", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "nullable": true, - "type": "integer" - }, - "usage": { - "description": "Total storage usage in bytes", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "enabled": { + "description": "Whether the task is enabled", + "type": "boolean" }, - "usagePhotos": { - "description": "Storage usage for photos in bytes", + "minFaces": { + "description": "Minimum number of faces required for recognition", "maximum": 9007199254740991, - "minimum": -9007199254740991, + "minimum": 1, "type": "integer" - }, - "usageVideos": { - "description": "Storage usage for videos in bytes", + } + }, + "required": [ + "enabled", + "minFaces" + ], + "type": "object" + }, + "UserConfigGeneratedFullsizeImageDto": { + "properties": { + "enabled": { + "description": "Enabled", + "type": "boolean" + } + }, + "required": [ + "enabled" + ], + "type": "object" + }, + "UserConfigGeneratedImageDto": { + "properties": { + "size": { + "description": "Size", "maximum": 9007199254740991, - "minimum": -9007199254740991, + "minimum": 1, "type": "integer" + } + }, + "required": [ + "size" + ], + "type": "object" + }, + "UserConfigImageDto": { + "properties": { + "fullsize": { + "$ref": "#/components/schemas/UserConfigGeneratedFullsizeImageDto" }, - "userId": { - "description": "User ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "userName": { - "description": "User name", - "type": "string" + "preview": { + "$ref": "#/components/schemas/UserConfigGeneratedImageDto" }, - "videos": { - "description": "Number of videos", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" + "thumbnail": { + "$ref": "#/components/schemas/UserConfigGeneratedImageDto" } }, "required": [ - "photos", - "quotaSizeInBytes", - "usage", - "usagePhotos", - "usageVideos", - "userId", - "userName", - "videos" + "fullsize", + "preview", + "thumbnail" ], "type": "object" }, - "UserAdminCreateDto": { + "UserConfigMachineLearningDto": { "properties": { - "avatarColor": { - "allOf": [ - { - "$ref": "#/components/schemas/UserAvatarColor" - } - ], - "nullable": true + "clip": { + "$ref": "#/components/schemas/UserConfigClipDto" }, - "email": { - "description": "User email", - "format": "email", - "pattern": "^[\\p{L}\\p{M}\\p{N}.!#$%&'*+/=?^_`{|}~-]+@[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?(?:\\.[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?)*$", - "type": "string" + "duplicateDetection": { + "$ref": "#/components/schemas/UserConfigDuplicateDetectionDto" }, - "isAdmin": { - "description": "Grant admin privileges", + "enabled": { + "description": "Enabled", "type": "boolean" }, - "name": { - "description": "User name", + "facialRecognition": { + "$ref": "#/components/schemas/UserConfigFacialRecognitionDto" + }, + "ocr": { + "$ref": "#/components/schemas/UserConfigOcrDto" + } + }, + "required": [ + "clip", + "duplicateDetection", + "enabled", + "facialRecognition", + "ocr" + ], + "type": "object" + }, + "UserConfigMapDto": { + "properties": { + "darkStyle": { + "description": "Dark map style URL", + "format": "uri", "type": "string" }, - "notify": { - "description": "Send notification email", + "enabled": { + "description": "Enabled", "type": "boolean" }, - "password": { - "description": "User password", + "lightStyle": { + "description": "Light map style URL", + "format": "uri", "type": "string" + } + }, + "required": [ + "darkStyle", + "enabled", + "lightStyle" + ], + "type": "object" + }, + "UserConfigOAuthDto": { + "properties": { + "autoLaunch": { + "description": "Auto launch", + "type": "boolean" }, - "pinCode": { - "description": "PIN code", - "example": "123456", - "nullable": true, - "pattern": "^\\d{6}$", + "buttonText": { + "description": "Button text", "type": "string" }, - "quotaSizeInBytes": { - "description": "Storage quota in bytes", - "maximum": 9007199254740991, - "minimum": 0, - "nullable": true, - "type": "integer" - }, - "shouldChangePassword": { - "description": "Require password change on next login", + "enabled": { + "description": "Enabled", "type": "boolean" - }, - "storageLabel": { - "description": "Storage label", - "nullable": true, - "type": "string" } }, "required": [ - "email", - "name", - "password" + "autoLaunch", + "buttonText", + "enabled" ], "type": "object" }, - "UserAdminDeleteDto": { + "UserConfigOcrDto": { "properties": { - "force": { - "description": "Force delete even if user has assets", + "enabled": { + "description": "Whether the task is enabled", "type": "boolean" } }, + "required": [ + "enabled" + ], "type": "object" }, - "UserAdminResponseDto": { + "UserConfigPasswordLoginDto": { "properties": { - "avatarColor": { - "$ref": "#/components/schemas/UserAvatarColor" - }, - "createdAt": { - "description": "Creation date", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" - }, - "deletedAt": { - "description": "Deletion date", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "nullable": true, - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" - }, - "email": { - "description": "User email", - "format": "email", - "pattern": "^[\\p{L}\\p{M}\\p{N}.!#$%&'*+/=?^_`{|}~-]+@[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?(?:\\.[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?)*$", - "type": "string" - }, - "id": { - "description": "User ID", - "format": "uuid", - "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$", - "type": "string" - }, - "isAdmin": { - "description": "Is admin user", - "type": "boolean" - }, - "license": { - "allOf": [ - { - "$ref": "#/components/schemas/UserLicense" - } - ], - "nullable": true - }, - "name": { - "description": "User name", - "type": "string" - }, - "oauthId": { - "description": "OAuth ID", - "type": "string" - }, - "profileChangedAt": { - "description": "Profile change date", - "format": "date-time", - "type": "string" - }, - "profileImagePath": { - "description": "Profile image path", - "type": "string" - }, - "quotaSizeInBytes": { - "description": "Storage quota in bytes", - "maximum": 9007199254740991, - "minimum": 0, - "nullable": true, - "type": "integer" - }, - "quotaUsageInBytes": { - "description": "Storage usage in bytes", - "maximum": 9007199254740991, - "minimum": 0, - "nullable": true, - "type": "integer" - }, - "shouldChangePassword": { - "description": "Require password change on next login", + "enabled": { + "description": "Enabled", "type": "boolean" - }, - "status": { - "$ref": "#/components/schemas/UserStatus" - }, - "storageLabel": { - "description": "Storage label", - "nullable": true, - "type": "string" - }, - "updatedAt": { - "description": "Last update date", - "example": "2024-01-01T00:00:00.000Z", - "format": "date-time", - "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$", - "type": "string" } }, "required": [ - "avatarColor", - "createdAt", - "deletedAt", - "email", - "id", - "isAdmin", - "license", - "name", - "oauthId", - "profileChangedAt", - "profileImagePath", - "quotaSizeInBytes", - "quotaUsageInBytes", - "shouldChangePassword", - "status", - "storageLabel", - "updatedAt" + "enabled" ], "type": "object" }, - "UserAdminUpdateDto": { + "UserConfigReverseGeocodingDto": { "properties": { - "avatarColor": { - "allOf": [ - { - "$ref": "#/components/schemas/UserAvatarColor" - } - ], - "nullable": true - }, - "email": { - "description": "User email", - "format": "email", - "pattern": "^[\\p{L}\\p{M}\\p{N}.!#$%&'*+/=?^_`{|}~-]+@[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?(?:\\.[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?)*$", - "type": "string" - }, - "isAdmin": { - "description": "Grant admin privileges", + "enabled": { + "description": "Enabled", "type": "boolean" - }, - "name": { - "description": "User name", + } + }, + "required": [ + "enabled" + ], + "type": "object" + }, + "UserConfigServerDto": { + "properties": { + "externalDomain": { + "description": "External domain", "type": "string" }, - "password": { - "description": "User password", + "loginPageMessage": { + "description": "Login page message", "type": "string" }, - "pinCode": { - "description": "PIN code", - "example": "123456", - "nullable": true, - "pattern": "^\\d{6}$", + "publicUsers": { + "description": "Public users", + "type": "boolean" + } + }, + "required": [ + "externalDomain", + "loginPageMessage", + "publicUsers" + ], + "type": "object" + }, + "UserConfigThemeDto": { + "properties": { + "customCss": { + "description": "Custom CSS for theming", "type": "string" - }, - "quotaSizeInBytes": { - "description": "Storage quota in bytes", + } + }, + "required": [ + "customCss" + ], + "type": "object" + }, + "UserConfigTrashDto": { + "properties": { + "days": { + "description": "Days", "maximum": 9007199254740991, "minimum": 0, - "nullable": true, "type": "integer" }, - "shouldChangePassword": { - "description": "Require password change on next login", + "enabled": { + "description": "Enabled", "type": "boolean" - }, - "storageLabel": { - "description": "Storage label", - "nullable": true, - "type": "string" } }, + "required": [ + "days", + "enabled" + ], "type": "object" }, - "UserAvatarColor": { - "description": "User avatar color", - "enum": [ - "primary", - "pink", - "red", - "yellow", - "blue", - "green", - "purple", - "orange", - "gray", - "amber" + "UserConfigUserDto": { + "properties": { + "deleteDelay": { + "description": "Delete delay", + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + } + }, + "required": [ + "deleteDelay" ], - "type": "string" + "type": "object" }, "UserLicense": { "properties": { diff --git a/packages/sdk/src/fetch-client.ts b/packages/sdk/src/fetch-client.ts index 7e0fb963f79fc9..2d20f9c5d2fe0f 100644 --- a/packages/sdk/src/fetch-client.ts +++ b/packages/sdk/src/fetch-client.ts @@ -54,118 +54,239 @@ export type ActivityStatisticsResponseDto = { /** Number of likes */ likes: number; }; -export type DatabaseBackupDeleteDto = { - /** Backup filenames to delete */ - backups: string[]; +export type AdminConfigDatabaseBackupDto = { + /** Cron expression */ + cronExpression: string; + /** Enabled */ + enabled: boolean; + /** Keep last amount */ + keepLastAmount: number; }; -export type DatabaseBackupDto = { - /** Backup filename */ - filename: string; - /** Backup file size */ - filesize: number; - /** Backup timezone */ - timezone: string; +export type AdminConfigBackupsDto = { + database: AdminConfigDatabaseBackupDto; }; -export type DatabaseBackupListResponseDto = { - /** List of backups */ - backups: DatabaseBackupDto[]; +export type AdminConfigFFmpegRealtimeDto = { + /** Enable real-time HLS transcoding (alpha) */ + enabled: boolean; + /** Resolutions to use for real-time HLS transcoding */ + resolutions: HlsVideoResolution[]; + /** Video codecs to use for real-time HLS transcoding */ + videoCodecs: VideoCodec[]; }; -export type DatabaseBackupUploadDto = { - /** Database backup file */ - file?: Blob; +export type AdminConfigFFmpegDto = { + accel: TranscodeHWAccel; + /** Accelerated decode */ + accelDecode: boolean; + /** Accepted audio codecs */ + acceptedAudioCodecs: AudioCodec[]; + /** Accepted containers */ + acceptedContainers: VideoContainer[]; + /** Accepted video codecs */ + acceptedVideoCodecs: VideoCodec[]; + /** B-frames */ + bframes: number; + cqMode: CQMode; + /** CRF */ + crf: number; + /** GOP size */ + gopSize: number; + /** Max bitrate */ + maxBitrate: string; + /** Preferred hardware device */ + preferredHwDevice: string; + /** Preset */ + preset: string; + realtime: AdminConfigFFmpegRealtimeDto; + /** References */ + refs: number; + targetAudioCodec: AudioCodec; + /** Target resolution */ + targetResolution: string; + targetVideoCodec: VideoCodec; + /** Temporal AQ */ + temporalAQ: boolean; + /** Threads */ + threads: number; + tonemap: ToneMapping; + transcode: TranscodePolicy; + /** Two pass */ + twoPass: boolean; }; -export type IntegrityReportResponseDto = { - items: { - /** Integrity report item id */ - id: string; - /** Integrity report item path */ - path: string; - "type": IntegrityReport; - }[]; - nextCursor?: string; +export type AdminConfigGeneratedFullsizeImageDto = { + /** Enabled */ + enabled: boolean; + format: ImageFormat; + /** Progressive */ + progressive?: boolean; + /** Quality */ + quality: number; }; -export type IntegrityReportSummaryResponseDto = { - checksum_mismatch: number; - missing_file: number; - untracked_file: number; +export type AdminConfigGeneratedImageDto = { + format: ImageFormat; + /** Progressive */ + progressive?: boolean; + /** Quality */ + quality: number; + /** Size */ + size: number; }; -export type SetMaintenanceModeDto = { - action: MaintenanceAction; - /** Restore backup filename */ - restoreBackupFilename?: string; +export type AdminConfigImageDto = { + colorspace: Colorspace; + /** Extract embedded */ + extractEmbedded: boolean; + fullsize: AdminConfigGeneratedFullsizeImageDto; + preview: AdminConfigGeneratedImageDto; + thumbnail: AdminConfigGeneratedImageDto; }; -export type MaintenanceDetectInstallStorageFolderDto = { - /** Number of files in the folder */ - files: number; - folder: StorageFolder; - /** Whether the folder is readable */ - readable: boolean; - /** Whether the folder is writable */ - writable: boolean; +export type AdminConfigIntegrityChecksumJobDto = { + /** Cron expression for when the integrity check should run */ + cronExpression: string; + /** Enabled */ + enabled: boolean; + /** Percentage limit of the integrity checksum job */ + percentageLimit: number; + /** How long the integrity checksum job may run for */ + timeLimit: number; }; -export type MaintenanceDetectInstallResponseDto = { - storage: MaintenanceDetectInstallStorageFolderDto[]; +export type AdminConfigIntegrityJobDto = { + /** Cron expression for when the integrity check should run */ + cronExpression: string; + /** Enabled */ + enabled: boolean; }; -export type MaintenanceLoginDto = { - /** Maintenance token */ - token?: string; +export type AdminConfigIntegrityChecksDto = { + checksumFiles: AdminConfigIntegrityChecksumJobDto; + missingFiles: AdminConfigIntegrityJobDto; + untrackedFiles: AdminConfigIntegrityJobDto; }; -export type MaintenanceAuthDto = { - /** Maintenance username */ - username: string; +export type AdminConfigJobSettingsDto = { + /** Concurrency */ + concurrency: number; }; -export type MaintenanceStatusResponseDto = { - action: MaintenanceAction; - active: boolean; - error?: string; - progress?: number; - task?: string; +export type AdminConfigJobDto = { + backgroundTask: AdminConfigJobSettingsDto; + editor: AdminConfigJobSettingsDto; + faceDetection: AdminConfigJobSettingsDto; + integrityCheck: AdminConfigJobSettingsDto; + library: AdminConfigJobSettingsDto; + metadataExtraction: AdminConfigJobSettingsDto; + migration: AdminConfigJobSettingsDto; + notifications: AdminConfigJobSettingsDto; + ocr: AdminConfigJobSettingsDto; + search: AdminConfigJobSettingsDto; + sidecar: AdminConfigJobSettingsDto; + smartSearch: AdminConfigJobSettingsDto; + thumbnailGeneration: AdminConfigJobSettingsDto; + videoConversion: AdminConfigJobSettingsDto; + workflow: AdminConfigJobSettingsDto; +}; +export type AdminConfigLibraryScanDto = { + /** Cron expression */ + cronExpression: string; + /** Enabled */ + enabled: boolean; }; -export type NotificationCreateDto = { - /** Additional notification data */ - data?: { - [key: string]: any; - }; - /** Notification description */ - description?: string | null; - level?: NotificationLevel; - /** Date when notification was read */ - readAt?: string | null; - /** Notification title */ - title: string; - "type"?: NotificationType; - /** User ID to send notification to */ - userId: string; +export type AdminConfigLibraryWatchDto = { + /** Enabled */ + enabled: boolean; }; -export type NotificationDto = { - /** Creation date */ - createdAt: string; - /** Additional notification data */ - data?: { - [key: string]: any; - }; - /** Notification description */ - description?: string; - /** Notification ID */ - id: string; - level: NotificationLevel; - /** Date when notification was read */ - readAt?: string; - /** Notification title */ - title: string; - "type": NotificationType; +export type AdminConfigLibraryDto = { + scan: AdminConfigLibraryScanDto; + watch: AdminConfigLibraryWatchDto; }; -export type TemplateDto = { - /** Template name */ - template: string; +export type AdminConfigLoggingDto = { + /** Enabled */ + enabled: boolean; + level: LogLevel; }; -export type TemplateResponseDto = { - /** Template HTML content */ - html: string; - /** Template name */ - name: string; +export type AdminConfigMachineLearningAvailabilityChecksDto = { + /** Enabled */ + enabled: boolean; + interval: number; + timeout: number; +}; +export type AdminConfigClipDto = { + /** Whether the task is enabled */ + enabled: boolean; + /** Name of the model to use */ + modelName: string; }; -export type SystemConfigSmtpTransportDto = { +export type AdminConfigDuplicateDetectionDto = { + /** Whether the task is enabled */ + enabled: boolean; + /** Maximum distance threshold for duplicate detection */ + maxDistance: number; +}; +export type AdminConfigFacialRecognitionDto = { + /** Whether the task is enabled */ + enabled: boolean; + /** Maximum distance threshold for face recognition */ + maxDistance: number; + /** Minimum number of faces required for recognition */ + minFaces: number; + /** Minimum confidence score for face detection */ + minScore: number; + /** Name of the model to use */ + modelName: string; +}; +export type AdminConfigOcrDto = { + /** Whether the task is enabled */ + enabled: boolean; + /** Maximum resolution for OCR processing */ + maxResolution: number; + /** Minimum confidence score for text detection */ + minDetectionScore: number; + /** Minimum confidence score for text recognition */ + minRecognitionScore: number; + /** Name of the model to use */ + modelName: string; +}; +export type AdminConfigMachineLearningDto = { + availabilityChecks: AdminConfigMachineLearningAvailabilityChecksDto; + clip: AdminConfigClipDto; + duplicateDetection: AdminConfigDuplicateDetectionDto; + /** Enabled */ + enabled: boolean; + facialRecognition: AdminConfigFacialRecognitionDto; + ocr: AdminConfigOcrDto; + /** ML service URLs */ + urls: string[]; +}; +export type AdminConfigMapDto = { + /** Dark map style URL */ + darkStyle: string; + /** Enabled */ + enabled: boolean; + /** Light map style URL */ + lightStyle: string; +}; +export type AdminConfigFacesDto = { + /** Import */ + "import": boolean; +}; +export type AdminConfigMetadataDto = { + faces: AdminConfigFacesDto; +}; +export type AdminConfigNewVersionCheckDto = { + channel: ReleaseChannel; + /** Enabled */ + enabled: boolean; +}; +export type AdminConfigNightlyTasksDto = { + /** Cluster new faces */ + clusterNewFaces: boolean; + /** Database cleanup */ + databaseCleanup: boolean; + /** Generate memories */ + generateMemories: boolean; + /** Missing thumbnails */ + missingThumbnails: boolean; + /** Start time (HH:MM) */ + startTime: string; + /** Sync quota usage */ + syncQuotaUsage: boolean; +}; +export type AdminConfigSmtpTransportDto = { /** SMTP server hostname */ host: string; /** Whether to ignore SSL certificate errors */ @@ -179,20 +300,250 @@ export type SystemConfigSmtpTransportDto = { /** SMTP username */ username: string; }; -export type SystemConfigSmtpDto = { +export type AdminConfigSmtpDto = { /** Whether SMTP email notifications are enabled */ enabled: boolean; /** Email address to send from */ "from": string; /** Email address for replies */ replyTo: string; - transport: SystemConfigSmtpTransportDto; + transport: AdminConfigSmtpTransportDto; }; -export type TestEmailResponseDto = { - /** Email message ID */ - messageId: string; +export type AdminConfigNotificationsDto = { + smtp: AdminConfigSmtpDto; }; -export type UserLicense = { +export type AdminConfigOAuthDto = { + /** Allow insecure requests */ + allowInsecureRequests: boolean; + /** Auto launch */ + autoLaunch: boolean; + /** Auto register */ + autoRegister: boolean; + /** Button text */ + buttonText: string; + /** Client ID */ + clientId: string; + /** Client secret */ + clientSecret: string; + /** Default storage quota */ + defaultStorageQuota: number | null; + /** Enabled */ + enabled: boolean; + /** End session endpoint */ + endSessionEndpoint: string; + /** Issuer URL */ + issuerUrl: string; + /** Mobile override enabled */ + mobileOverrideEnabled: boolean; + /** Mobile redirect URI (set to empty string to disable) */ + mobileRedirectUri: string; + /** Profile signing algorithm */ + profileSigningAlgorithm: string; + /** OAuth prompt parameter (e.g. select_account, login, consent) */ + prompt: string; + /** Role claim */ + roleClaim: string; + /** Scope */ + scope: string; + /** Signing algorithm */ + signingAlgorithm: string; + /** Storage label claim */ + storageLabelClaim: string; + /** Storage quota claim */ + storageQuotaClaim: string; + /** Timeout */ + timeout: number; + tokenEndpointAuthMethod: OAuthTokenEndpointAuthMethod; +}; +export type AdminConfigPasswordLoginDto = { + /** Enabled */ + enabled: boolean; +}; +export type AdminConfigReverseGeocodingDto = { + /** Enabled */ + enabled: boolean; +}; +export type AdminConfigServerDto = { + /** External domain */ + externalDomain: string; + /** Login page message */ + loginPageMessage: string; + /** Public users */ + publicUsers: boolean; +}; +export type AdminConfigStorageTemplateDto = { + /** Enabled */ + enabled: boolean; + /** Hash verification enabled */ + hashVerificationEnabled: boolean; + /** Template */ + template: string; +}; +export type AdminConfigTemplateEmailsDto = { + /** Album invite template */ + albumInviteTemplate: string; + /** Album update template */ + albumUpdateTemplate: string; + /** Welcome template */ + welcomeTemplate: string; +}; +export type AdminConfigTemplatesDto = { + email: AdminConfigTemplateEmailsDto; +}; +export type AdminConfigThemeDto = { + /** Custom CSS for theming */ + customCss: string; +}; +export type AdminConfigTrashDto = { + /** Days */ + days: number; + /** Enabled */ + enabled: boolean; +}; +export type AdminConfigUserDto = { + /** Delete delay */ + deleteDelay: number; +}; +export type AdminConfigDto = { + backup: AdminConfigBackupsDto; + ffmpeg: AdminConfigFFmpegDto; + image: AdminConfigImageDto; + integrityChecks: AdminConfigIntegrityChecksDto; + job: AdminConfigJobDto; + library: AdminConfigLibraryDto; + logging: AdminConfigLoggingDto; + machineLearning: AdminConfigMachineLearningDto; + map: AdminConfigMapDto; + metadata: AdminConfigMetadataDto; + newVersionCheck: AdminConfigNewVersionCheckDto; + nightlyTasks: AdminConfigNightlyTasksDto; + notifications: AdminConfigNotificationsDto; + oauth: AdminConfigOAuthDto; + passwordLogin: AdminConfigPasswordLoginDto; + reverseGeocoding: AdminConfigReverseGeocodingDto; + server: AdminConfigServerDto; + storageTemplate: AdminConfigStorageTemplateDto; + templates: AdminConfigTemplatesDto; + theme: AdminConfigThemeDto; + trash: AdminConfigTrashDto; + user: AdminConfigUserDto; +}; +export type DatabaseBackupDeleteDto = { + /** Backup filenames to delete */ + backups: string[]; +}; +export type DatabaseBackupDto = { + /** Backup filename */ + filename: string; + /** Backup file size */ + filesize: number; + /** Backup timezone */ + timezone: string; +}; +export type DatabaseBackupListResponseDto = { + /** List of backups */ + backups: DatabaseBackupDto[]; +}; +export type DatabaseBackupUploadDto = { + /** Database backup file */ + file?: Blob; +}; +export type IntegrityReportResponseDto = { + items: { + /** Integrity report item id */ + id: string; + /** Integrity report item path */ + path: string; + "type": IntegrityReport; + }[]; + nextCursor?: string; +}; +export type IntegrityReportSummaryResponseDto = { + checksum_mismatch: number; + missing_file: number; + untracked_file: number; +}; +export type SetMaintenanceModeDto = { + action: MaintenanceAction; + /** Restore backup filename */ + restoreBackupFilename?: string; +}; +export type MaintenanceDetectInstallStorageFolderDto = { + /** Number of files in the folder */ + files: number; + folder: StorageFolder; + /** Whether the folder is readable */ + readable: boolean; + /** Whether the folder is writable */ + writable: boolean; +}; +export type MaintenanceDetectInstallResponseDto = { + storage: MaintenanceDetectInstallStorageFolderDto[]; +}; +export type MaintenanceLoginDto = { + /** Maintenance token */ + token?: string; +}; +export type MaintenanceAuthDto = { + /** Maintenance username */ + username: string; +}; +export type MaintenanceStatusResponseDto = { + action: MaintenanceAction; + active: boolean; + error?: string; + progress?: number; + task?: string; +}; +export type NotificationCreateDto = { + /** Additional notification data */ + data?: { + [key: string]: any; + }; + /** Notification description */ + description?: string | null; + level?: NotificationLevel; + /** Date when notification was read */ + readAt?: string | null; + /** Notification title */ + title: string; + "type"?: NotificationType; + /** User ID to send notification to */ + userId: string; +}; +export type NotificationDto = { + /** Creation date */ + createdAt: string; + /** Additional notification data */ + data?: { + [key: string]: any; + }; + /** Notification description */ + description?: string; + /** Notification ID */ + id: string; + level: NotificationLevel; + /** Date when notification was read */ + readAt?: string; + /** Notification title */ + title: string; + "type": NotificationType; +}; +export type TemplateDto = { + /** Template name */ + template: string; +}; +export type TemplateResponseDto = { + /** Template HTML content */ + html: string; + /** Template name */ + name: string; +}; +export type TestEmailResponseDto = { + /** Email message ID */ + messageId: string; +}; +export type UserLicense = { /** Activation date */ activatedAt: string; /** Activation key */ @@ -1123,54 +1474,163 @@ export type ValidateAccessTokenResponseDto = { /** Authentication status */ authStatus: boolean; }; -export type DownloadArchiveDto = { - /** Asset IDs */ - assetIds: string[]; - /** Download edited asset if available */ - edited?: boolean; +export type UserConfigFFmpegRealtimeDto = { + /** Enable real-time HLS transcoding (alpha) */ + enabled: boolean; + /** Resolutions to use for real-time HLS transcoding */ + resolutions: HlsVideoResolution[]; + /** Video codecs to use for real-time HLS transcoding */ + videoCodecs: VideoCodec[]; }; -export type DownloadInfoDto = { - /** Album ID to download */ - albumId?: string; - /** Archive size limit in bytes */ - archiveSize?: number; - /** Asset IDs to download */ - assetIds?: string[]; - /** User ID to download assets from */ - userId?: string; +export type UserConfigFFmpegDto = { + realtime: UserConfigFFmpegRealtimeDto; }; -export type DownloadArchiveInfo = { - /** Asset IDs in this archive */ - assetIds: string[]; - /** Archive size in bytes */ +export type UserConfigGeneratedFullsizeImageDto = { + /** Enabled */ + enabled: boolean; +}; +export type UserConfigGeneratedImageDto = { + /** Size */ size: number; }; -export type DownloadResponseDto = { - /** Archive information */ - archives: DownloadArchiveInfo[]; - /** Total size in bytes */ - totalSize: number; +export type UserConfigImageDto = { + fullsize: UserConfigGeneratedFullsizeImageDto; + preview: UserConfigGeneratedImageDto; + thumbnail: UserConfigGeneratedImageDto; }; -export type DuplicateResponseDto = { - /** Duplicate assets */ - assets: AssetResponseDto[]; - /** Duplicate group ID */ - duplicateId: string; - /** Suggested asset IDs to keep based on file size and EXIF data */ - suggestedKeepAssetIds: string[]; +export type UserConfigClipDto = { + /** Whether the task is enabled */ + enabled: boolean; }; -export type DuplicateResolveGroupDto = { - duplicateId: string; - /** Asset IDs to keep */ - keepAssetIds: string[]; - /** Asset IDs to trash or delete */ - trashAssetIds: string[]; +export type UserConfigDuplicateDetectionDto = { + /** Whether the task is enabled */ + enabled: boolean; }; -export type DuplicateResolveDto = { - /** List of duplicate groups to resolve */ - groups: DuplicateResolveGroupDto[]; +export type UserConfigFacialRecognitionDto = { + /** Whether the task is enabled */ + enabled: boolean; + /** Minimum number of faces required for recognition */ + minFaces: number; }; -export type AssetFaceResponseDto = { +export type UserConfigOcrDto = { + /** Whether the task is enabled */ + enabled: boolean; +}; +export type UserConfigMachineLearningDto = { + clip: UserConfigClipDto; + duplicateDetection: UserConfigDuplicateDetectionDto; + /** Enabled */ + enabled: boolean; + facialRecognition: UserConfigFacialRecognitionDto; + ocr: UserConfigOcrDto; +}; +export type UserConfigMapDto = { + /** Dark map style URL */ + darkStyle: string; + /** Enabled */ + enabled: boolean; + /** Light map style URL */ + lightStyle: string; +}; +export type UserConfigOAuthDto = { + /** Auto launch */ + autoLaunch: boolean; + /** Button text */ + buttonText: string; + /** Enabled */ + enabled: boolean; +}; +export type UserConfigPasswordLoginDto = { + /** Enabled */ + enabled: boolean; +}; +export type UserConfigReverseGeocodingDto = { + /** Enabled */ + enabled: boolean; +}; +export type UserConfigServerDto = { + /** External domain */ + externalDomain: string; + /** Login page message */ + loginPageMessage: string; + /** Public users */ + publicUsers: boolean; +}; +export type UserConfigThemeDto = { + /** Custom CSS for theming */ + customCss: string; +}; +export type UserConfigTrashDto = { + /** Days */ + days: number; + /** Enabled */ + enabled: boolean; +}; +export type UserConfigUserDto = { + /** Delete delay */ + deleteDelay: number; +}; +export type UserConfigDto = { + ffmpeg: UserConfigFFmpegDto; + image: UserConfigImageDto; + machineLearning: UserConfigMachineLearningDto; + map: UserConfigMapDto; + oauth: UserConfigOAuthDto; + passwordLogin: UserConfigPasswordLoginDto; + reverseGeocoding: UserConfigReverseGeocodingDto; + server: UserConfigServerDto; + theme: UserConfigThemeDto; + trash: UserConfigTrashDto; + user: UserConfigUserDto; +}; +export type DownloadArchiveDto = { + /** Asset IDs */ + assetIds: string[]; + /** Download edited asset if available */ + edited?: boolean; +}; +export type DownloadInfoDto = { + /** Album ID to download */ + albumId?: string; + /** Archive size limit in bytes */ + archiveSize?: number; + /** Asset IDs to download */ + assetIds?: string[]; + /** User ID to download assets from */ + userId?: string; +}; +export type DownloadArchiveInfo = { + /** Asset IDs in this archive */ + assetIds: string[]; + /** Archive size in bytes */ + size: number; +}; +export type DownloadResponseDto = { + /** Archive information */ + archives: DownloadArchiveInfo[]; + /** Total size in bytes */ + totalSize: number; +}; +export type DuplicateResponseDto = { + /** Duplicate assets */ + assets: AssetResponseDto[]; + /** Duplicate group ID */ + duplicateId: string; + /** Suggested asset IDs to keep based on file size and EXIF data */ + suggestedKeepAssetIds: string[]; +}; +export type DuplicateResolveGroupDto = { + duplicateId: string; + /** Asset IDs to keep */ + keepAssetIds: string[]; + /** Asset IDs to trash or delete */ + trashAssetIds: string[]; +}; +export type DuplicateResolveDto = { + /** List of duplicate groups to resolve */ + groups: DuplicateResolveGroupDto[]; +}; +export type AssetFaceResponseDto = { /** Bounding box X1 coordinate */ boundingBoxX1: number; /** Bounding box X2 coordinate */ @@ -1592,6 +2052,32 @@ export type PluginTemplateResponseDto = { /** Ui hints, for example "smart-album" */ uiHints: string[]; }; +export type PublicConfigOAuthDto = { + /** Auto launch */ + autoLaunch: boolean; + /** Button text */ + buttonText: string; + /** Enabled */ + enabled: boolean; +}; +export type PublicConfigPasswordLoginDto = { + /** Enabled */ + enabled: boolean; +}; +export type PublicConfigServerDto = { + /** Login page message */ + loginPageMessage: string; +}; +export type PublicConfigThemeDto = { + /** Custom CSS for theming */ + customCss: string; +}; +export type PublicConfigDto = { + oauth: PublicConfigOAuthDto; + passwordLogin: PublicConfigPasswordLoginDto; + server: PublicConfigServerDto; + theme: PublicConfigThemeDto; +}; export type QueueResponseDto = { /** Whether the queue is paused */ isPaused: boolean; @@ -2253,401 +2739,50 @@ export type SharedLinkEditDto = { /** Custom URL slug */ slug?: string | null; }; -export type AssetIdsDto = { - /** Asset IDs */ - assetIds: string[]; -}; -export type AssetIdsResponseDto = { - /** Asset ID */ - assetId: string; - error?: AssetIdErrorReason; - /** Whether operation succeeded */ - success: boolean; -}; -export type StackResponseDto = { - assets: AssetResponseDto[]; - /** Stack ID */ - id: string; - /** Primary asset ID */ - primaryAssetId: string; -}; -export type StackCreateDto = { - /** Asset IDs (first becomes primary, min 2) */ - assetIds: string[]; -}; -export type StackUpdateDto = { - /** Primary asset ID */ - primaryAssetId?: string; -}; -export type SyncAckDeleteDto = { - /** Sync entity types to delete acks for */ - types?: SyncEntityType[]; -}; -export type SyncAckDto = { - /** Acknowledgment ID */ - ack: string; - "type": SyncEntityType; -}; -export type SyncAckSetDto = { - /** Acknowledgment IDs (max 1000) */ - acks: string[]; -}; -export type SyncStreamDto = { - /** Reset sync state */ - reset?: boolean; - /** Sync request types */ - types: SyncRequestType[]; -}; -export type DatabaseBackupConfig = { - /** Cron expression */ - cronExpression: string; - /** Enabled */ - enabled: boolean; - /** Keep last amount */ - keepLastAmount: number; -}; -export type SystemConfigBackupsDto = { - database: DatabaseBackupConfig; -}; -export type SystemConfigFFmpegRealtimeDto = { - /** Enable real-time HLS transcoding (alpha) */ - enabled: boolean; - /** Resolutions to use for real-time HLS transcoding */ - resolutions: HlsVideoResolution[]; - /** Video codecs to use for real-time HLS transcoding */ - videoCodecs: VideoCodec[]; -}; -export type SystemConfigFFmpegDto = { - accel: TranscodeHWAccel; - /** Accelerated decode */ - accelDecode: boolean; - /** Accepted audio codecs */ - acceptedAudioCodecs: AudioCodec[]; - /** Accepted containers */ - acceptedContainers: VideoContainer[]; - /** Accepted video codecs */ - acceptedVideoCodecs: VideoCodec[]; - /** B-frames */ - bframes: number; - cqMode: CQMode; - /** CRF */ - crf: number; - /** GOP size */ - gopSize: number; - /** Max bitrate */ - maxBitrate: string; - /** Preferred hardware device */ - preferredHwDevice: string; - /** Preset */ - preset: string; - realtime: SystemConfigFFmpegRealtimeDto; - /** References */ - refs: number; - targetAudioCodec: AudioCodec; - /** Target resolution */ - targetResolution: string; - targetVideoCodec: VideoCodec; - /** Temporal AQ */ - temporalAQ: boolean; - /** Threads */ - threads: number; - tonemap: ToneMapping; - transcode: TranscodePolicy; - /** Two pass */ - twoPass: boolean; -}; -export type SystemConfigGeneratedFullsizeImageDto = { - /** Enabled */ - enabled: boolean; - format: ImageFormat; - /** Progressive */ - progressive?: boolean; - /** Quality */ - quality: number; -}; -export type SystemConfigGeneratedImageDto = { - format: ImageFormat; - /** Progressive */ - progressive?: boolean; - /** Quality */ - quality: number; - /** Size */ - size: number; -}; -export type SystemConfigImageDto = { - colorspace: Colorspace; - /** Extract embedded */ - extractEmbedded: boolean; - fullsize: SystemConfigGeneratedFullsizeImageDto; - preview: SystemConfigGeneratedImageDto; - thumbnail: SystemConfigGeneratedImageDto; -}; -export type SystemConfigIntegrityChecksumJob = { - /** Cron expression for when the integrity check should run */ - cronExpression: string; - /** Enabled */ - enabled: boolean; - /** Percentage limit of the integrity checksum job */ - percentageLimit: number; - /** How long the integrity checksum job may run for */ - timeLimit: number; -}; -export type SystemConfigIntegrityJob = { - /** Cron expression for when the integrity check should run */ - cronExpression: string; - /** Enabled */ - enabled: boolean; -}; -export type SystemConfigIntegrityChecks = { - checksumFiles: SystemConfigIntegrityChecksumJob; - missingFiles: SystemConfigIntegrityJob; - untrackedFiles: SystemConfigIntegrityJob; -}; -export type JobSettingsDto = { - /** Concurrency */ - concurrency: number; -}; -export type SystemConfigJobDto = { - backgroundTask: JobSettingsDto; - editor: JobSettingsDto; - faceDetection: JobSettingsDto; - integrityCheck: JobSettingsDto; - library: JobSettingsDto; - metadataExtraction: JobSettingsDto; - migration: JobSettingsDto; - notifications: JobSettingsDto; - ocr: JobSettingsDto; - search: JobSettingsDto; - sidecar: JobSettingsDto; - smartSearch: JobSettingsDto; - thumbnailGeneration: JobSettingsDto; - videoConversion: JobSettingsDto; - workflow: JobSettingsDto; -}; -export type SystemConfigLibraryScanDto = { - /** Cron expression */ - cronExpression: string; - /** Enabled */ - enabled: boolean; -}; -export type SystemConfigLibraryWatchDto = { - /** Enabled */ - enabled: boolean; -}; -export type SystemConfigLibraryDto = { - scan: SystemConfigLibraryScanDto; - watch: SystemConfigLibraryWatchDto; -}; -export type SystemConfigLoggingDto = { - /** Enabled */ - enabled: boolean; - level: LogLevel; -}; -export type MachineLearningAvailabilityChecksDto = { - /** Enabled */ - enabled: boolean; - interval: number; - timeout: number; -}; -export type ClipConfig = { - /** Whether the task is enabled */ - enabled: boolean; - /** Name of the model to use */ - modelName: string; -}; -export type DuplicateDetectionConfig = { - /** Whether the task is enabled */ - enabled: boolean; - /** Maximum distance threshold for duplicate detection */ - maxDistance: number; -}; -export type FacialRecognitionConfig = { - /** Whether the task is enabled */ - enabled: boolean; - /** Maximum distance threshold for face recognition */ - maxDistance: number; - /** Minimum number of faces required for recognition */ - minFaces: number; - /** Minimum confidence score for face detection */ - minScore: number; - /** Name of the model to use */ - modelName: string; -}; -export type OcrConfig = { - /** Whether the task is enabled */ - enabled: boolean; - /** Maximum resolution for OCR processing */ - maxResolution: number; - /** Minimum confidence score for text detection */ - minDetectionScore: number; - /** Minimum confidence score for text recognition */ - minRecognitionScore: number; - /** Name of the model to use */ - modelName: string; -}; -export type SystemConfigMachineLearningDto = { - availabilityChecks: MachineLearningAvailabilityChecksDto; - clip: ClipConfig; - duplicateDetection: DuplicateDetectionConfig; - /** Enabled */ - enabled: boolean; - facialRecognition: FacialRecognitionConfig; - ocr: OcrConfig; - /** ML service URLs */ - urls: string[]; -}; -export type SystemConfigMapDto = { - /** Dark map style URL */ - darkStyle: string; - /** Enabled */ - enabled: boolean; - /** Light map style URL */ - lightStyle: string; -}; -export type SystemConfigFacesDto = { - /** Import */ - "import": boolean; -}; -export type SystemConfigMetadataDto = { - faces: SystemConfigFacesDto; -}; -export type SystemConfigNewVersionCheckDto = { - channel: ReleaseChannel; - /** Enabled */ - enabled: boolean; -}; -export type SystemConfigNightlyTasksDto = { - /** Cluster new faces */ - clusterNewFaces: boolean; - /** Database cleanup */ - databaseCleanup: boolean; - /** Generate memories */ - generateMemories: boolean; - /** Missing thumbnails */ - missingThumbnails: boolean; - /** Start time (HH:MM) */ - startTime: string; - /** Sync quota usage */ - syncQuotaUsage: boolean; -}; -export type SystemConfigNotificationsDto = { - smtp: SystemConfigSmtpDto; -}; -export type SystemConfigOAuthDto = { - /** Allow insecure requests */ - allowInsecureRequests: boolean; - /** Auto launch */ - autoLaunch: boolean; - /** Auto register */ - autoRegister: boolean; - /** Button text */ - buttonText: string; - /** Client ID */ - clientId: string; - /** Client secret */ - clientSecret: string; - /** Default storage quota */ - defaultStorageQuota: number | null; - /** Enabled */ - enabled: boolean; - /** End session endpoint */ - endSessionEndpoint: string; - /** Issuer URL */ - issuerUrl: string; - /** Mobile override enabled */ - mobileOverrideEnabled: boolean; - /** Mobile redirect URI (set to empty string to disable) */ - mobileRedirectUri: string; - /** Profile signing algorithm */ - profileSigningAlgorithm: string; - /** OAuth prompt parameter (e.g. select_account, login, consent) */ - prompt: string; - /** Role claim */ - roleClaim: string; - /** Scope */ - scope: string; - /** Signing algorithm */ - signingAlgorithm: string; - /** Storage label claim */ - storageLabelClaim: string; - /** Storage quota claim */ - storageQuotaClaim: string; - /** Timeout */ - timeout: number; - tokenEndpointAuthMethod: OAuthTokenEndpointAuthMethod; -}; -export type SystemConfigPasswordLoginDto = { - /** Enabled */ - enabled: boolean; -}; -export type SystemConfigReverseGeocodingDto = { - /** Enabled */ - enabled: boolean; -}; -export type SystemConfigServerDto = { - /** External domain */ - externalDomain: string; - /** Login page message */ - loginPageMessage: string; - /** Public users */ - publicUsers: boolean; +export type AssetIdsDto = { + /** Asset IDs */ + assetIds: string[]; }; -export type SystemConfigStorageTemplateDto = { - /** Enabled */ - enabled: boolean; - /** Hash verification enabled */ - hashVerificationEnabled: boolean; - /** Template */ - template: string; +export type AssetIdsResponseDto = { + /** Asset ID */ + assetId: string; + error?: AssetIdErrorReason; + /** Whether operation succeeded */ + success: boolean; }; -export type SystemConfigTemplateEmailsDto = { - /** Album invite template */ - albumInviteTemplate: string; - /** Album update template */ - albumUpdateTemplate: string; - /** Welcome template */ - welcomeTemplate: string; +export type StackResponseDto = { + assets: AssetResponseDto[]; + /** Stack ID */ + id: string; + /** Primary asset ID */ + primaryAssetId: string; }; -export type SystemConfigTemplatesDto = { - email: SystemConfigTemplateEmailsDto; +export type StackCreateDto = { + /** Asset IDs (first becomes primary, min 2) */ + assetIds: string[]; }; -export type SystemConfigThemeDto = { - /** Custom CSS for theming */ - customCss: string; +export type StackUpdateDto = { + /** Primary asset ID */ + primaryAssetId?: string; }; -export type SystemConfigTrashDto = { - /** Days */ - days: number; - /** Enabled */ - enabled: boolean; +export type SyncAckDeleteDto = { + /** Sync entity types to delete acks for */ + types?: SyncEntityType[]; }; -export type SystemConfigUserDto = { - /** Delete delay */ - deleteDelay: number; +export type SyncAckDto = { + /** Acknowledgment ID */ + ack: string; + "type": SyncEntityType; }; -export type SystemConfigDto = { - backup: SystemConfigBackupsDto; - ffmpeg: SystemConfigFFmpegDto; - image: SystemConfigImageDto; - integrityChecks: SystemConfigIntegrityChecks; - job: SystemConfigJobDto; - library: SystemConfigLibraryDto; - logging: SystemConfigLoggingDto; - machineLearning: SystemConfigMachineLearningDto; - map: SystemConfigMapDto; - metadata: SystemConfigMetadataDto; - newVersionCheck: SystemConfigNewVersionCheckDto; - nightlyTasks: SystemConfigNightlyTasksDto; - notifications: SystemConfigNotificationsDto; - oauth: SystemConfigOAuthDto; - passwordLogin: SystemConfigPasswordLoginDto; - reverseGeocoding: SystemConfigReverseGeocodingDto; - server: SystemConfigServerDto; - storageTemplate: SystemConfigStorageTemplateDto; - templates: SystemConfigTemplatesDto; - theme: SystemConfigThemeDto; - trash: SystemConfigTrashDto; - user: SystemConfigUserDto; +export type SyncAckSetDto = { + /** Acknowledgment IDs (max 1000) */ + acks: string[]; +}; +export type SyncStreamDto = { + /** Reset sync state */ + reset?: boolean; + /** Sync request types */ + types: SyncRequestType[]; }; export type SystemConfigTemplateStorageOptionDto = { /** Available day format options for storage template */ @@ -3452,6 +3587,43 @@ export function unlinkAllOAuthAccountsAdmin(opts?: Oazapfts.RequestOpts) { method: "POST" })); } +/** + * Get the admin configuration + */ +export function getAdminConfig(opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: AdminConfigDto; + }>("/admin/config", { + ...opts + })); +} +/** + * Update the system configuration + */ +export function updateAdminConfig({ adminConfigDto }: { + adminConfigDto: AdminConfigDto; +}, opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: AdminConfigDto; + }>("/admin/config", oazapfts.json({ + ...opts, + method: "PUT", + body: adminConfigDto + }))); +} +/** + * Get the system configuration defaults + */ +export function getAdminConfigDefaults(opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: AdminConfigDto; + }>("/admin/config/defaults", { + ...opts + })); +} /** * Delete database backup */ @@ -3659,8 +3831,8 @@ export function getNotificationTemplateAdmin({ name, templateDto }: { /** * Send test email */ -export function sendTestEmailAdmin({ systemConfigSmtpDto }: { - systemConfigSmtpDto: SystemConfigSmtpDto; +export function sendTestEmailAdmin({ adminConfigSmtpDto }: { + adminConfigSmtpDto: AdminConfigSmtpDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -3668,7 +3840,7 @@ export function sendTestEmailAdmin({ systemConfigSmtpDto }: { }>("/admin/notifications/test-email", oazapfts.json({ ...opts, method: "POST", - body: systemConfigSmtpDto + body: adminConfigSmtpDto }))); } /** @@ -4695,6 +4867,28 @@ export function validateAccessToken(opts?: Oazapfts.RequestOpts) { method: "POST" })); } +/** + * Get the configuration with user visibility + */ +export function getUserConfig(opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: UserConfigDto; + }>("/config", { + ...opts + })); +} +/** + * Get the default configuration with user visibility + */ +export function getUserConfigDefaults(opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: UserConfigDto; + }>("/config/defaults", { + ...opts + })); +} /** * Download asset archive */ @@ -5640,6 +5834,28 @@ export function getPlugin({ id }: { ...opts })); } +/** + * Get the public configuration + */ +export function getPublicConfig(opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: PublicConfigDto; + }>("/public/config", { + ...opts + })); +} +/** + * Get the public configuration defaults + */ +export function getPublicConfigDefaults(opts?: Oazapfts.RequestOpts) { + return oazapfts.ok(oazapfts.fetchJson<{ + status: 200; + data: PublicConfigDto; + }>("/public/config/defaults", { + ...opts + })); +} /** * List all queues */ @@ -6450,7 +6666,7 @@ export function getSyncStream({ syncStreamDto }: { export function getConfig(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; - data: SystemConfigDto; + data: AdminConfigDto; }>("/system-config", { ...opts })); @@ -6458,16 +6674,16 @@ export function getConfig(opts?: Oazapfts.RequestOpts) { /** * Update system configuration */ -export function updateConfig({ systemConfigDto }: { - systemConfigDto: SystemConfigDto; +export function updateConfig({ adminConfigDto }: { + adminConfigDto: AdminConfigDto; }, opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; - data: SystemConfigDto; + data: AdminConfigDto; }>("/system-config", oazapfts.json({ ...opts, method: "PUT", - body: systemConfigDto + body: adminConfigDto }))); } /** @@ -6476,7 +6692,7 @@ export function updateConfig({ systemConfigDto }: { export function getConfigDefaults(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; - data: SystemConfigDto; + data: AdminConfigDto; }>("/system-config/defaults", { ...opts })); @@ -7164,6 +7380,80 @@ export enum UserAvatarColor { Gray = "gray", Amber = "amber" } +export enum TranscodeHWAccel { + Nvenc = "nvenc", + Qsv = "qsv", + Vaapi = "vaapi", + Rkmpp = "rkmpp", + Disabled = "disabled" +} +export enum AudioCodec { + Mp3 = "mp3", + Aac = "aac", + Opus = "opus", + PcmS16Le = "pcm_s16le" +} +export enum VideoContainer { + Mov = "mov", + Mp4 = "mp4", + Ogg = "ogg", + Webm = "webm" +} +export enum VideoCodec { + H264 = "h264", + Hevc = "hevc", + Vp9 = "vp9", + Av1 = "av1" +} +export enum CQMode { + Auto = "auto", + Cqp = "cqp", + Icq = "icq" +} +export enum HlsVideoResolution { + $480 = 480, + $720 = 720, + $1080 = 1080, + $1440 = 1440, + $2160 = 2160 +} +export enum ToneMapping { + Hable = "hable", + Mobius = "mobius", + Reinhard = "reinhard", + Disabled = "disabled" +} +export enum TranscodePolicy { + All = "all", + Optimal = "optimal", + Bitrate = "bitrate", + Required = "required", + Disabled = "disabled" +} +export enum Colorspace { + Srgb = "srgb", + P3 = "p3" +} +export enum ImageFormat { + Jpeg = "jpeg", + Webp = "webp" +} +export enum LogLevel { + Verbose = "verbose", + Debug = "debug", + Log = "log", + Warn = "warn", + Error = "error", + Fatal = "fatal" +} +export enum ReleaseChannel { + Stable = "stable", + ReleaseCandidate = "releaseCandidate" +} +export enum OAuthTokenEndpointAuthMethod { + ClientSecretPost = "client_secret_post", + ClientSecretBasic = "client_secret_basic" +} export enum IntegrityReport { UntrackedFile = "untracked_file", MissingFile = "missing_file", @@ -7272,6 +7562,9 @@ export enum Permission { BackupDownload = "backup.download", BackupUpload = "backup.upload", BackupDelete = "backup.delete", + AdminConfigRead = "adminConfig.read", + AdminConfigUpdate = "adminConfig.update", + UserConfigRead = "userConfig.read", DuplicateRead = "duplicate.read", DuplicateDelete = "duplicate.delete", FaceCreate = "face.create", @@ -7679,80 +7972,6 @@ export enum SyncRequestType { AssetFacesV2 = "AssetFacesV2", UserMetadataV1 = "UserMetadataV1" } -export enum TranscodeHWAccel { - Nvenc = "nvenc", - Qsv = "qsv", - Vaapi = "vaapi", - Rkmpp = "rkmpp", - Disabled = "disabled" -} -export enum AudioCodec { - Mp3 = "mp3", - Aac = "aac", - Opus = "opus", - PcmS16Le = "pcm_s16le" -} -export enum VideoContainer { - Mov = "mov", - Mp4 = "mp4", - Ogg = "ogg", - Webm = "webm" -} -export enum VideoCodec { - H264 = "h264", - Hevc = "hevc", - Vp9 = "vp9", - Av1 = "av1" -} -export enum CQMode { - Auto = "auto", - Cqp = "cqp", - Icq = "icq" -} -export enum HlsVideoResolution { - $480 = 480, - $720 = 720, - $1080 = 1080, - $1440 = 1440, - $2160 = 2160 -} -export enum ToneMapping { - Hable = "hable", - Mobius = "mobius", - Reinhard = "reinhard", - Disabled = "disabled" -} -export enum TranscodePolicy { - All = "all", - Optimal = "optimal", - Bitrate = "bitrate", - Required = "required", - Disabled = "disabled" -} -export enum Colorspace { - Srgb = "srgb", - P3 = "p3" -} -export enum ImageFormat { - Jpeg = "jpeg", - Webp = "webp" -} -export enum LogLevel { - Verbose = "verbose", - Debug = "debug", - Log = "log", - Warn = "warn", - Error = "error", - Fatal = "fatal" -} -export enum ReleaseChannel { - Stable = "stable", - ReleaseCandidate = "releaseCandidate" -} -export enum OAuthTokenEndpointAuthMethod { - ClientSecretPost = "client_secret_post", - ClientSecretBasic = "client_secret_basic" -} export enum AssetOrderBy { TakenAt = "takenAt", CreatedAt = "createdAt" diff --git a/server/src/config.ts b/server/src/config.ts deleted file mode 100644 index 55304080a3deaa..00000000000000 --- a/server/src/config.ts +++ /dev/null @@ -1,449 +0,0 @@ -import { CronExpression } from '@nestjs/schedule'; -import { ReleaseChannel } from 'src/dtos/system-config.dto'; -import { - AudioCodec, - Colorspace, - CQMode, - HlsVideoResolution, - ImageFormat, - LogLevel, - OAuthTokenEndpointAuthMethod, - QueueName, - ToneMapping, - TranscodeHardwareAcceleration, - TranscodePolicy, - VideoCodec, - VideoContainer, -} from 'src/enum'; -import { ConcurrentQueueName, FullsizeImageOptions, ImageOptions } from 'src/types'; - -export type SystemConfig = { - backup: { - database: { - enabled: boolean; - cronExpression: string; - keepLastAmount: number; - }; - }; - ffmpeg: { - crf: number; - threads: number; - preset: string; - targetVideoCodec: VideoCodec; - acceptedVideoCodecs: VideoCodec[]; - targetAudioCodec: AudioCodec; - acceptedAudioCodecs: AudioCodec[]; - acceptedContainers: VideoContainer[]; - targetResolution: string; - maxBitrate: string; - bframes: number; - refs: number; - gopSize: number; - temporalAQ: boolean; - cqMode: CQMode; - twoPass: boolean; - preferredHwDevice: string; - transcode: TranscodePolicy; - accel: TranscodeHardwareAcceleration; - accelDecode: boolean; - tonemap: ToneMapping; - realtime: { - enabled: boolean; - videoCodecs: VideoCodec[]; - resolutions: HlsVideoResolution[]; - }; - }; - integrityChecks: { - missingFiles: { - enabled: boolean; - cronExpression: string; - }; - untrackedFiles: { - enabled: boolean; - cronExpression: string; - }; - checksumFiles: { - enabled: boolean; - cronExpression: string; - timeLimit: number; - percentageLimit: number; - }; - }; - job: Record; - logging: { - enabled: boolean; - level: LogLevel; - }; - machineLearning: { - enabled: boolean; - urls: string[]; - availabilityChecks: { - enabled: boolean; - timeout: number; - interval: number; - }; - clip: { - enabled: boolean; - modelName: string; - }; - duplicateDetection: { - enabled: boolean; - maxDistance: number; - }; - facialRecognition: { - enabled: boolean; - modelName: string; - minScore: number; - minFaces: number; - maxDistance: number; - }; - ocr: { - enabled: boolean; - modelName: string; - minDetectionScore: number; - minRecognitionScore: number; - maxResolution: number; - }; - }; - map: { - enabled: boolean; - lightStyle: string; - darkStyle: string; - }; - reverseGeocoding: { - enabled: boolean; - }; - metadata: { - faces: { - import: boolean; - }; - }; - oauth: { - autoLaunch: boolean; - autoRegister: boolean; - buttonText: string; - clientId: string; - clientSecret: string; - defaultStorageQuota: number | null; - enabled: boolean; - issuerUrl: string; - endSessionEndpoint: string; - mobileOverrideEnabled: boolean; - mobileRedirectUri: string; - prompt: string; - scope: string; - signingAlgorithm: string; - profileSigningAlgorithm: string; - tokenEndpointAuthMethod: OAuthTokenEndpointAuthMethod; - timeout: number; - allowInsecureRequests: boolean; - storageLabelClaim: string; - storageQuotaClaim: string; - roleClaim: string; - }; - passwordLogin: { - enabled: boolean; - }; - storageTemplate: { - enabled: boolean; - hashVerificationEnabled: boolean; - template: string; - }; - image: { - thumbnail: ImageOptions; - preview: ImageOptions; - colorspace: Colorspace; - extractEmbedded: boolean; - fullsize: FullsizeImageOptions; - }; - newVersionCheck: { - enabled: boolean; - channel: ReleaseChannel; - }; - nightlyTasks: { - startTime: string; - databaseCleanup: boolean; - missingThumbnails: boolean; - clusterNewFaces: boolean; - generateMemories: boolean; - syncQuotaUsage: boolean; - }; - trash: { - enabled: boolean; - days: number; - }; - theme: { - customCss: string; - }; - library: { - scan: { - enabled: boolean; - cronExpression: string; - }; - watch: { - enabled: boolean; - }; - }; - notifications: { - smtp: { - enabled: boolean; - from: string; - replyTo: string; - transport: { - ignoreCert: boolean; - host: string; - port: number; - secure: boolean; - username: string; - password: string; - }; - }; - }; - templates: { - email: { - welcomeTemplate: string; - albumInviteTemplate: string; - albumUpdateTemplate: string; - }; - }; - server: { - externalDomain: string; - loginPageMessage: string; - publicUsers: boolean; - }; - user: { - deleteDelay: number; - }; -}; - -export type MachineLearningConfig = SystemConfig['machineLearning']; - -export const defaults = Object.freeze({ - backup: { - database: { - enabled: true, - cronExpression: CronExpression.EVERY_DAY_AT_2AM, - keepLastAmount: 14, - }, - }, - ffmpeg: { - crf: 23, - threads: 0, - preset: 'ultrafast', - targetVideoCodec: VideoCodec.H264, - acceptedVideoCodecs: [VideoCodec.H264], - targetAudioCodec: AudioCodec.Aac, - acceptedAudioCodecs: [AudioCodec.Aac, AudioCodec.Mp3, AudioCodec.Opus], - acceptedContainers: [VideoContainer.Mov, VideoContainer.Ogg, VideoContainer.Webm], - targetResolution: '720', - maxBitrate: '0', - bframes: -1, - refs: 0, - gopSize: 0, - temporalAQ: false, - cqMode: CQMode.Auto, - twoPass: false, - preferredHwDevice: 'auto', - transcode: TranscodePolicy.Required, - tonemap: ToneMapping.Hable, - accel: TranscodeHardwareAcceleration.Disabled, - accelDecode: true, - realtime: { - enabled: false, - videoCodecs: [VideoCodec.H264, VideoCodec.Hevc], - resolutions: [HlsVideoResolution.p480, HlsVideoResolution.p720, HlsVideoResolution.p1080], - }, - }, - integrityChecks: { - missingFiles: { - enabled: true, - cronExpression: CronExpression.EVERY_DAY_AT_3AM, - }, - untrackedFiles: { - enabled: true, - cronExpression: CronExpression.EVERY_DAY_AT_3AM, - }, - checksumFiles: { - enabled: true, - cronExpression: CronExpression.EVERY_DAY_AT_3AM, - timeLimit: 60 * 60 * 1000, // 1 hour - percentageLimit: 1, // 100% of assets - }, - }, - job: { - [QueueName.BackgroundTask]: { concurrency: 5 }, - [QueueName.SmartSearch]: { concurrency: 2 }, - [QueueName.MetadataExtraction]: { concurrency: 5 }, - [QueueName.FaceDetection]: { concurrency: 2 }, - [QueueName.Search]: { concurrency: 5 }, - [QueueName.Sidecar]: { concurrency: 5 }, - [QueueName.Library]: { concurrency: 5 }, - [QueueName.Migration]: { concurrency: 5 }, - [QueueName.ThumbnailGeneration]: { concurrency: 3 }, - [QueueName.VideoConversion]: { concurrency: 1 }, - [QueueName.Notification]: { concurrency: 5 }, - [QueueName.Ocr]: { concurrency: 1 }, - [QueueName.Workflow]: { concurrency: 5 }, - [QueueName.IntegrityCheck]: { concurrency: 1 }, - [QueueName.Editor]: { concurrency: 2 }, - }, - logging: { - enabled: true, - level: LogLevel.Log, - }, - machineLearning: { - enabled: process.env.IMMICH_MACHINE_LEARNING_ENABLED !== 'false', - urls: [process.env.IMMICH_MACHINE_LEARNING_URL || 'http://immich-machine-learning:3003'], - availabilityChecks: { - enabled: true, - timeout: 2000, - interval: 30_000, - }, - clip: { - enabled: true, - modelName: 'ViT-B-32__openai', - }, - duplicateDetection: { - enabled: true, - maxDistance: 0.01, - }, - facialRecognition: { - enabled: true, - modelName: 'buffalo_l', - minScore: 0.7, - maxDistance: 0.5, - minFaces: 3, - }, - ocr: { - enabled: true, - modelName: 'PP-OCRv5_mobile', - minDetectionScore: 0.5, - minRecognitionScore: 0.8, - maxResolution: 736, - }, - }, - map: { - enabled: true, - lightStyle: 'https://tiles.immich.cloud/v1/style/light.json', - darkStyle: 'https://tiles.immich.cloud/v1/style/dark.json', - }, - reverseGeocoding: { - enabled: true, - }, - metadata: { - faces: { - import: false, - }, - }, - oauth: { - autoLaunch: false, - autoRegister: true, - buttonText: 'Login with OAuth', - clientId: '', - clientSecret: '', - defaultStorageQuota: null, - enabled: false, - issuerUrl: '', - endSessionEndpoint: '', - mobileOverrideEnabled: false, - mobileRedirectUri: '', - prompt: '', - scope: 'openid email profile', - signingAlgorithm: 'RS256', - profileSigningAlgorithm: 'none', - storageLabelClaim: 'preferred_username', - storageQuotaClaim: 'immich_quota', - roleClaim: 'immich_role', - tokenEndpointAuthMethod: OAuthTokenEndpointAuthMethod.ClientSecretPost, - timeout: 30_000, - allowInsecureRequests: false, - }, - passwordLogin: { - enabled: true, - }, - storageTemplate: { - enabled: false, - hashVerificationEnabled: true, - template: '{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}', - }, - image: { - thumbnail: { - format: ImageFormat.Webp, - size: 250, - quality: 80, - progressive: false, - }, - preview: { - format: ImageFormat.Jpeg, - size: 1440, - quality: 80, - progressive: false, - }, - colorspace: Colorspace.P3, - extractEmbedded: false, - fullsize: { - enabled: false, - format: ImageFormat.Jpeg, - quality: 80, - progressive: false, - }, - }, - newVersionCheck: { - enabled: true, - channel: ReleaseChannel.Stable, - }, - nightlyTasks: { - startTime: '00:00', - databaseCleanup: true, - generateMemories: true, - syncQuotaUsage: true, - missingThumbnails: true, - clusterNewFaces: true, - }, - trash: { - enabled: true, - days: 30, - }, - theme: { - customCss: '', - }, - library: { - scan: { - enabled: true, - cronExpression: CronExpression.EVERY_DAY_AT_MIDNIGHT, - }, - watch: { - enabled: false, - }, - }, - server: { - externalDomain: '', - loginPageMessage: '', - publicUsers: true, - }, - notifications: { - smtp: { - enabled: false, - from: '', - replyTo: '', - transport: { - ignoreCert: false, - host: '', - port: 587, - secure: false, - username: '', - password: '', - }, - }, - }, - templates: { - email: { - welcomeTemplate: '', - albumInviteTemplate: '', - albumUpdateTemplate: '', - }, - }, - user: { - deleteDelay: 7, - }, -}); diff --git a/server/src/constants.ts b/server/src/constants.ts index 815a400ed80ae5..a8b2216b32e229 100644 --- a/server/src/constants.ts +++ b/server/src/constants.ts @@ -150,6 +150,9 @@ export const endpointTags: Record = { [ApiTag.Assets]: 'An asset is an image or video that has been uploaded to Immich.', [ApiTag.Authentication]: 'Endpoints related to user authentication, including OAuth.', [ApiTag.AuthenticationAdmin]: 'Administrative endpoints related to authentication.', + [ApiTag.ConfigUser]: 'The system configuration properties that are visible to logged in users.', + [ApiTag.ConfigAdmin]: 'Endpoints to view and modify the full system configuration.', + [ApiTag.ConfigPublic]: 'The system configuration properties that are visible to everyone.', [ApiTag.DatabaseBackups]: 'Manage backups of the Immich database.', [ApiTag.Deprecated]: 'Deprecated endpoints that are planned for removal in the next major release.', [ApiTag.Download]: 'Endpoints for downloading assets or collections of assets.', diff --git a/server/src/controllers/config-admin.controller.ts b/server/src/controllers/config-admin.controller.ts new file mode 100644 index 00000000000000..b135b0e62e1091 --- /dev/null +++ b/server/src/controllers/config-admin.controller.ts @@ -0,0 +1,46 @@ +import { Body, Controller, Get, Put } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { Endpoint, HistoryBuilder } from 'src/decorators'; +import { AdminConfigDto } from 'src/dtos/config.dto'; +import { ApiTag, Permission } from 'src/enum'; +import { Authenticated } from 'src/middleware/auth.guard'; +import { SystemConfigService } from 'src/services/system-config.service'; + +@ApiTags(ApiTag.ConfigAdmin) +@Controller('admin/config') +export class ConfigAdminController { + constructor(private service: SystemConfigService) {} + + @Get() + @Authenticated({ permission: Permission.AdminConfigRead, admin: true }) + @Endpoint({ + summary: 'Get the admin configuration', + description: 'Retrieve admin configuration.', + history: new HistoryBuilder().added('v3.2.0').alpha('v3.2.0'), + }) + getAdminConfig(): Promise { + return this.service.getAdminConfig(); + } + + @Get('defaults') + @Authenticated({ permission: Permission.AdminConfigRead, admin: true }) + @Endpoint({ + summary: 'Get the system configuration defaults', + description: 'Retrieve the default value of every system configuration property.', + history: new HistoryBuilder().added('v3.2.0').alpha('v3.2.0'), + }) + getAdminConfigDefaults(): AdminConfigDto { + return this.service.getAdminConfigDefaults(); + } + + @Put() + @Authenticated({ permission: Permission.AdminConfigUpdate, admin: true }) + @Endpoint({ + summary: 'Update the system configuration', + description: 'Update the system configuration with a new system configuration.', + history: new HistoryBuilder().added('v3.2.0').alpha('v3.2.0'), + }) + updateAdminConfig(@Body() dto: AdminConfigDto): Promise { + return this.service.updateAdminConfig(dto); + } +} diff --git a/server/src/controllers/config-public.controller.ts b/server/src/controllers/config-public.controller.ts new file mode 100644 index 00000000000000..87f7bb14c817dc --- /dev/null +++ b/server/src/controllers/config-public.controller.ts @@ -0,0 +1,35 @@ +import { Controller, Get } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { Endpoint, HistoryBuilder } from 'src/decorators'; +import { PublicConfigDto } from 'src/dtos/config.dto'; +import { ApiTag } from 'src/enum'; +import { Authenticated } from 'src/middleware/auth.guard'; +import { SystemConfigService } from 'src/services/system-config.service'; + +@ApiTags(ApiTag.ConfigPublic) +@Controller('public/config') +export class ConfigPublicController { + constructor(private service: SystemConfigService) {} + + @Get() + @Authenticated({ public: true }) + @Endpoint({ + summary: 'Get the public configuration', + description: 'Retrieve the system configuration properties that are visible to everyone.', + history: new HistoryBuilder().added('v3.2.0').alpha('v3.2.0'), + }) + getPublicConfig(): Promise { + return this.service.getPublicConfig(); + } + + @Get('defaults') + @Authenticated({ public: true }) + @Endpoint({ + summary: 'Get the public configuration defaults', + description: 'Retrieve the default value of the configuration properties that are visible to everyone.', + history: new HistoryBuilder().added('v3.2.0').alpha('v3.2.0'), + }) + getPublicConfigDefaults(): PublicConfigDto { + return this.service.getPublicConfigDefaults(); + } +} diff --git a/server/src/controllers/config-user.controller.ts b/server/src/controllers/config-user.controller.ts new file mode 100644 index 00000000000000..8f49d119fd68d5 --- /dev/null +++ b/server/src/controllers/config-user.controller.ts @@ -0,0 +1,35 @@ +import { Controller, Get } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { Endpoint, HistoryBuilder } from 'src/decorators'; +import { UserConfigDto } from 'src/dtos/config.dto'; +import { ApiTag, Permission } from 'src/enum'; +import { Authenticated } from 'src/middleware/auth.guard'; +import { SystemConfigService } from 'src/services/system-config.service'; + +@ApiTags(ApiTag.ConfigUser) +@Controller('config') +export class ConfigUserController { + constructor(private service: SystemConfigService) {} + + @Get() + @Authenticated({ permission: Permission.UserConfigRead }) + @Endpoint({ + summary: 'Get the configuration with user visibility', + description: 'Retrieve the system configuration properties that are visible to logged in users.', + history: new HistoryBuilder().added('v3.2.0').alpha('v3.2.0'), + }) + getUserConfig(): Promise { + return this.service.getUserConfig(); + } + + @Get('defaults') + @Authenticated({ permission: Permission.UserConfigRead }) + @Endpoint({ + summary: 'Get the default configuration with user visibility', + description: 'Retrieve the default value of the configuration properties that are visible to logged in users.', + history: new HistoryBuilder().added('v3.2.0').alpha('v3.2.0'), + }) + getUserConfigDefaults(): UserConfigDto { + return this.service.getUserConfigDefaults(); + } +} diff --git a/server/src/controllers/config.controller.spec.ts b/server/src/controllers/config.controller.spec.ts new file mode 100644 index 00000000000000..fdd6a76e744dbf --- /dev/null +++ b/server/src/controllers/config.controller.spec.ts @@ -0,0 +1,116 @@ +import _ from 'lodash'; +import { ConfigAdminController } from 'src/controllers/config-admin.controller'; +import { ConfigPublicController } from 'src/controllers/config-public.controller'; +import { ConfigUserController } from 'src/controllers/config-user.controller'; +import { defaults, mapPublicConfig, mapUserConfig } from 'src/dtos/config.dto'; +import { SystemConfigService } from 'src/services/system-config.service'; +import request from 'supertest'; +import { errorDto } from 'test/medium/responses'; +import { ControllerContext, controllerSetup, mockBaseService } from 'test/utils'; + +/** Returns a full config that passes Zod validation (required URLs and min lengths). */ +function validConfig() { + const config = _.cloneDeep(defaults) as typeof defaults & { + oauth: { mobileRedirectUri: string }; + notifications: { smtp: { from: string; transport: { host: string } } }; + server: { externalDomain: string }; + }; + config.oauth.mobileRedirectUri ||= 'https://example.com'; + config.server.externalDomain ||= 'https://example.com'; + config.notifications.smtp.from ||= 'noreply@example.com'; + config.notifications.smtp.transport.host ||= 'localhost'; + return config; +} + +describe('config controllers', () => { + let ctx: ControllerContext; + const service = mockBaseService(SystemConfigService); + + beforeAll(async () => { + ctx = await controllerSetup( + [ConfigAdminController, ConfigUserController, ConfigPublicController], + [{ provide: SystemConfigService, useValue: service }], + ); + return () => ctx.close(); + }); + + beforeEach(() => { + service.resetAllMocks(); + ctx.reset(); + }); + + describe('GET /admin/config', () => { + it('should return the full config', async () => { + service.getAdminConfig.mockResolvedValue(validConfig()); + + const { status, body } = await request(ctx.getHttpServer()).get('/admin/config'); + + expect(status).toBe(200); + expect(body.oauth.clientSecret).toBeDefined(); + expect(body.job.thumbnailGeneration).toBeDefined(); + }); + }); + + describe('PUT /admin/config', () => { + it('should accept a valid config', async () => { + service.updateAdminConfig.mockImplementation((dto) => Promise.resolve(dto)); + + const { status } = await request(ctx.getHttpServer()).put('/admin/config').send(validConfig()); + + expect(status).toBe(200); + }); + + it('should reject an invalid config', async () => { + const config = validConfig(); + config.nightlyTasks.startTime = 'invalid'; + + const { status, body } = await request(ctx.getHttpServer()).put('/admin/config').send(config); + + expect(status).toBe(400); + expect(body).toEqual( + errorDto.validationError([ + { + path: ['nightlyTasks', 'startTime'], + message: 'Invalid input: expected string in HH:MM format, received string', + }, + ]), + ); + expect(service.updateAdminConfig).not.toHaveBeenCalled(); + }); + }); + + describe('GET /config', () => { + it('should return the properties visible to logged in users', async () => { + service.getUserConfig.mockResolvedValue(mapUserConfig(validConfig())); + + const { status, body } = await request(ctx.getHttpServer()).get('/config'); + + expect(status).toBe(200); + expect(body.image).toEqual({ + thumbnail: { size: defaults.image.thumbnail.size }, + preview: { size: defaults.image.preview.size }, + fullsize: { enabled: defaults.image.fullsize.enabled }, + }); + expect(body.oauth.clientSecret).toBeUndefined(); + expect(body.job).toBeUndefined(); + }); + }); + + describe('GET /public/config', () => { + it('should return the properties visible to everyone', async () => { + service.getPublicConfig.mockResolvedValue(mapPublicConfig(validConfig())); + + const { status, body } = await request(ctx.getHttpServer()).get('/public/config'); + + expect(status).toBe(200); + expect(body.server).toEqual({ loginPageMessage: defaults.server.loginPageMessage }); + expect(body.oauth).toEqual({ + autoLaunch: defaults.oauth.autoLaunch, + buttonText: defaults.oauth.buttonText, + enabled: defaults.oauth.enabled, + }); + expect(body.image).toBeUndefined(); + expect(body.trash).toBeUndefined(); + }); + }); +}); diff --git a/server/src/controllers/index.ts b/server/src/controllers/index.ts index e7a01643abdc3c..f6ea6d4248c3a4 100644 --- a/server/src/controllers/index.ts +++ b/server/src/controllers/index.ts @@ -6,6 +6,9 @@ import { AssetMediaController } from 'src/controllers/asset-media.controller'; import { AssetController } from 'src/controllers/asset.controller'; import { AuthAdminController } from 'src/controllers/auth-admin.controller'; import { AuthController } from 'src/controllers/auth.controller'; +import { ConfigAdminController } from 'src/controllers/config-admin.controller'; +import { ConfigPublicController } from 'src/controllers/config-public.controller'; +import { ConfigUserController } from 'src/controllers/config-user.controller'; import { DatabaseBackupController } from 'src/controllers/database-backup.controller'; import { DownloadController } from 'src/controllers/download.controller'; import { DuplicateController } from 'src/controllers/duplicate.controller'; @@ -49,6 +52,9 @@ export const controllers = [ AssetMediaController, AuthController, AuthAdminController, + ConfigUserController, + ConfigAdminController, + ConfigPublicController, DatabaseBackupController, DownloadController, DuplicateController, diff --git a/server/src/controllers/notification-admin.controller.ts b/server/src/controllers/notification-admin.controller.ts index c322c5a2b609e6..7a5585fad4ffc0 100644 --- a/server/src/controllers/notification-admin.controller.ts +++ b/server/src/controllers/notification-admin.controller.ts @@ -2,6 +2,7 @@ import { Body, Controller, HttpCode, HttpStatus, Param, Post } from '@nestjs/com import { ApiTags } from '@nestjs/swagger'; import { Endpoint, HistoryBuilder } from 'src/decorators'; import { AuthDto } from 'src/dtos/auth.dto'; +import { SystemConfigSmtpDto } from 'src/dtos/config.dto'; import { NotificationCreateDto, NotificationDto, @@ -9,7 +10,6 @@ import { TemplateResponseDto, TestEmailResponseDto, } from 'src/dtos/notification.dto'; -import { SystemConfigSmtpDto } from 'src/dtos/system-config.dto'; import { ApiTag } from 'src/enum'; import { Auth, Authenticated } from 'src/middleware/auth.guard'; import { EmailTemplate } from 'src/repositories/email.repository'; diff --git a/server/src/controllers/server.controller.ts b/server/src/controllers/server.controller.ts index 6407155492ee4c..ef9f408e8c3ea0 100644 --- a/server/src/controllers/server.controller.ts +++ b/server/src/controllers/server.controller.ts @@ -101,7 +101,11 @@ export class ServerController { @Endpoint({ summary: 'Get features', description: 'Retrieve available features supported by this server.', - history: new HistoryBuilder().added('v1').beta('v1').stable('v2'), + history: new HistoryBuilder() + .added('v1') + .beta('v1') + .stable('v2') + .deprecated('v3.2.0', { replacementId: 'getPublicConfig' }), }) getServerFeatures(): Promise { return this.service.getFeatures(); @@ -112,7 +116,11 @@ export class ServerController { @Endpoint({ summary: 'Get config', description: 'Retrieve the current server configuration.', - history: new HistoryBuilder().added('v1').beta('v1').stable('v2'), + history: new HistoryBuilder() + .added('v1') + .beta('v1') + .stable('v2') + .deprecated('v3.2.0', { replacementId: 'getPublicConfig' }), }) getServerConfig(): Promise { return this.service.getSystemConfig(); diff --git a/server/src/controllers/system-config.controller.spec.ts b/server/src/controllers/system-config.controller.spec.ts index 7d40f125836af7..0ae8741c61e674 100644 --- a/server/src/controllers/system-config.controller.spec.ts +++ b/server/src/controllers/system-config.controller.spec.ts @@ -1,6 +1,6 @@ import _ from 'lodash'; -import { defaults } from 'src/config'; import { SystemConfigController } from 'src/controllers/system-config.controller'; +import { defaults } from 'src/dtos/config.dto'; import { StorageTemplateService } from 'src/services/storage-template.service'; import { SystemConfigService } from 'src/services/system-config.service'; import request from 'supertest'; diff --git a/server/src/controllers/system-config.controller.ts b/server/src/controllers/system-config.controller.ts index 6b79b38d98ba52..b7c5f0099669f6 100644 --- a/server/src/controllers/system-config.controller.ts +++ b/server/src/controllers/system-config.controller.ts @@ -1,7 +1,7 @@ import { Body, Controller, Get, Put } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { Endpoint, HistoryBuilder } from 'src/decorators'; -import { SystemConfigDto, SystemConfigTemplateStorageOptionDto } from 'src/dtos/system-config.dto'; +import { AdminConfigDto, ConfigTemplateStorageOptionDto } from 'src/dtos/config.dto'; import { ApiTag, Permission } from 'src/enum'; import { Authenticated } from 'src/middleware/auth.guard'; import { StorageTemplateService } from 'src/services/storage-template.service'; @@ -20,10 +20,14 @@ export class SystemConfigController { @Endpoint({ summary: 'Get system configuration', description: 'Retrieve the current system configuration.', - history: new HistoryBuilder().added('v1').beta('v1').stable('v2'), + history: new HistoryBuilder() + .added('v1') + .beta('v1') + .stable('v2') + .deprecated('v3.2.0', { replacementId: 'getAdminConfig' }), }) - getConfig(): Promise { - return this.service.getSystemConfig(); + getConfig(): Promise { + return this.service.getAdminConfig(); } @Get('defaults') @@ -31,10 +35,14 @@ export class SystemConfigController { @Endpoint({ summary: 'Get system configuration defaults', description: 'Retrieve the default values for the system configuration.', - history: new HistoryBuilder().added('v1').beta('v1').stable('v2'), + history: new HistoryBuilder() + .added('v1') + .beta('v1') + .stable('v2') + .deprecated('v3.2.0', { replacementId: 'getAdminConfigDefaults' }), }) - getConfigDefaults(): SystemConfigDto { - return this.service.getDefaults(); + getConfigDefaults(): AdminConfigDto { + return this.service.getAdminConfigDefaults(); } @Put() @@ -42,10 +50,14 @@ export class SystemConfigController { @Endpoint({ summary: 'Update system configuration', description: 'Update the system configuration with a new system configuration.', - history: new HistoryBuilder().added('v1').beta('v1').stable('v2'), + history: new HistoryBuilder() + .added('v1') + .beta('v1') + .stable('v2') + .deprecated('v3.2.0', { replacementId: 'updateAdminConfig' }), }) - updateConfig(@Body() dto: SystemConfigDto): Promise { - return this.service.updateSystemConfig(dto); + updateConfig(@Body() dto: AdminConfigDto): Promise { + return this.service.updateAdminConfig(dto); } @Get('storage-template-options') @@ -55,7 +67,7 @@ export class SystemConfigController { description: 'Retrieve exemplary storage template options.', history: new HistoryBuilder().added('v1').beta('v1').stable('v2'), }) - getStorageTemplateOptions(): SystemConfigTemplateStorageOptionDto { + getStorageTemplateOptions(): ConfigTemplateStorageOptionDto { return this.storageTemplateService.getStorageTemplateOptions(); } } diff --git a/server/src/dtos/config.dto.spec.ts b/server/src/dtos/config.dto.spec.ts new file mode 100644 index 00000000000000..ce4f5a5d06827f --- /dev/null +++ b/server/src/dtos/config.dto.spec.ts @@ -0,0 +1,68 @@ +import { + AdminConfigDto, + defaults, + mapPublicConfig, + mapUserConfig, + PublicConfigDto, + UserConfigDto, +} from 'src/dtos/config.dto'; +import { getKeysDeep } from 'src/utils/misc'; +import z from 'zod'; + +const PUBLIC_PROPERTIES = [ + 'oauth.autoLaunch', + 'oauth.buttonText', + 'oauth.enabled', + 'passwordLogin.enabled', + 'server.loginPageMessage', + 'theme.customCss', +]; + +describe('config visibility', () => { + it('should expose every property to admins', () => { + const paths = getKeysDeep(defaults); + + expect(paths).toEqual(expect.arrayContaining(PUBLIC_PROPERTIES)); + expect(paths).toContain('oauth.clientSecret'); + expect(paths.length).toBeGreaterThan(100); + }); + + it('should expose the public properties to everyone', () => { + expect(getKeysDeep(mapPublicConfig(defaults)).sort()).toEqual(PUBLIC_PROPERTIES); + }); + + it('should expose everything public to logged in users as well', () => { + expect(getKeysDeep(mapUserConfig(defaults))).toEqual(expect.arrayContaining(PUBLIC_PROPERTIES)); + }); + + it('should accept the defaults with the admin schema', () => { + expect(AdminConfigDto.schema.safeParse(defaults)).toEqual(expect.objectContaining({ success: true })); + }); + + it('should map the defaults onto the user and public schemas', () => { + expect(UserConfigDto.schema.safeParse(mapUserConfig(defaults))).toEqual(expect.objectContaining({ success: true })); + expect(PublicConfigDto.schema.safeParse(mapPublicConfig(defaults))).toEqual( + expect.objectContaining({ success: true }), + ); + }); + + it('should not leak admin properties into the public config', () => { + const config = mapPublicConfig(defaults) as Record; + + expect(config.oauth).toEqual({ + autoLaunch: defaults.oauth.autoLaunch, + buttonText: defaults.oauth.buttonText, + enabled: defaults.oauth.enabled, + }); + expect(config.job).toBeUndefined(); + expect(config.image).toBeUndefined(); + expect(config.notifications).toBeUndefined(); + }); + + it('should keep the visibility metadata out of the schemas', () => { + for (const schema of [AdminConfigDto.schema, UserConfigDto.schema, PublicConfigDto.schema]) { + const json = JSON.stringify(z.toJSONSchema(schema, { io: 'input', unrepresentable: 'any' })); + expect(json).not.toContain('visibility'); + } + }); +}); diff --git a/server/src/dtos/config.dto.ts b/server/src/dtos/config.dto.ts new file mode 100644 index 00000000000000..a7dbaf0cc64807 --- /dev/null +++ b/server/src/dtos/config.dto.ts @@ -0,0 +1,773 @@ +import { CronExpression } from '@nestjs/schedule'; +import { validateCronExpression } from 'cron'; +import { createZodDto } from 'nestjs-zod'; +import { + AudioCodec, + AudioCodecSchema, + Colorspace, + ColorspaceSchema, + ConfigVisibility, + CQMode, + CQModeSchema, + HlsVideoResolution, + HlsVideoResolutionSchema, + ImageFormat, + ImageFormatSchema, + LogLevel, + LogLevelSchema, + OAuthTokenEndpointAuthMethod, + OAuthTokenEndpointAuthMethodSchema, + ReleaseChannel, + ReleaseChannelSchema, + ToneMapping, + ToneMappingSchema, + TranscodeHardwareAcceleration, + TranscodeHardwareAccelerationSchema, + TranscodePolicy, + TranscodePolicySchema, + VideoCodec, + VideoCodecSchema, + VideoContainer, + VideoContainerSchema, +} from 'src/enum'; +import { DeepPartial } from 'src/types'; +import z from 'zod'; + +const { Admin, User, Public } = ConfigVisibility; + +const configBool = z + .preprocess((val) => { + if (val === 'true') { + return true; + } + if (val === 'false') { + return false; + } + return val; + }, z.boolean()) + .meta({ type: 'boolean' }); + +const cronExpressionSchema = z + .string() + .superRefine((value, ctx) => { + const validated = validateCronExpression(value); + if (!validated.valid) { + ctx.addIssue({ + code: 'custom', + message: `Invalid cron expression. ${validated.error?.message ?? ''}`, + input: value, + }); + } + }) + .describe('Cron expression'); + +const emptyOrUrl = (error: string) => + z.string().refine((url) => url.length === 0 || z.url().safeParse(url).success, { error }); + +const AdminConfigIntegrityJobSchema = z + .object({ + enabled: z.boolean().describe('Enabled'), + cronExpression: cronExpressionSchema.describe('Cron expression for when the integrity check should run'), + }) + .describe('Integrity job config') + .meta({ id: 'AdminConfigIntegrityJobDto' }); + +const AdminConfigJobSettingsSchema = z + .object({ concurrency: z.int().min(1).describe('Concurrency') }) + .meta({ id: 'AdminConfigJobSettingsDto' }); + +const AdminConfigMachineLearningTaskSchema = z.object({ + enabled: z.boolean().describe('Whether the task is enabled').meta({ visibility: User }), +}); + +const AdminConfigMachineLearningModelSchema = AdminConfigMachineLearningTaskSchema.extend({ + modelName: z.string().describe('Name of the model to use'), +}); + +const AdminConfigGeneratedImageSchema = z + .object({ + format: ImageFormatSchema, + quality: z.int().min(1).max(100).describe('Quality'), + size: z.int().min(1).describe('Size').meta({ visibility: User }), + progressive: configBool.default(false).optional().describe('Progressive'), + }) + .meta({ id: 'AdminConfigGeneratedImageDto' }); + +const AdminConfigFFmpegSchema = z + .object({ + crf: z.coerce.number().int().min(0).max(51).describe('CRF'), + threads: z.coerce.number().int().min(0).describe('Threads'), + preset: z.string().describe('Preset'), + targetVideoCodec: VideoCodecSchema, + acceptedVideoCodecs: z.array(VideoCodecSchema).describe('Accepted video codecs'), + targetAudioCodec: AudioCodecSchema, + acceptedAudioCodecs: z.array(AudioCodecSchema).describe('Accepted audio codecs'), + acceptedContainers: z.array(VideoContainerSchema).describe('Accepted containers'), + targetResolution: z.string().describe('Target resolution'), + maxBitrate: z.string().describe('Max bitrate'), + bframes: z.coerce.number().int().min(-1).max(16).describe('B-frames'), + refs: z.coerce.number().int().min(0).max(6).describe('References'), + gopSize: z.coerce.number().int().min(0).describe('GOP size'), + temporalAQ: configBool.describe('Temporal AQ'), + cqMode: CQModeSchema, + twoPass: configBool.describe('Two pass'), + preferredHwDevice: z.string().describe('Preferred hardware device'), + transcode: TranscodePolicySchema, + accel: TranscodeHardwareAccelerationSchema, + accelDecode: configBool.describe('Accelerated decode'), + tonemap: ToneMappingSchema, + realtime: z + .object({ + enabled: configBool.describe('Enable real-time HLS transcoding (alpha)').meta({ visibility: User }), + videoCodecs: z + .array(VideoCodecSchema) + .describe('Video codecs to use for real-time HLS transcoding') + .meta({ visibility: User }), + resolutions: z + .array(HlsVideoResolutionSchema) + .describe('Resolutions to use for real-time HLS transcoding') + .meta({ visibility: User }), + }) + .meta({ id: 'AdminConfigFFmpegRealtimeDto' }), + }) + .meta({ id: 'AdminConfigFFmpegDto' }); + +const AdminConfigSmtpSchema = z + .object({ + enabled: configBool.describe('Whether SMTP email notifications are enabled'), + from: z.string().describe('Email address to send from'), + replyTo: z.string().describe('Email address for replies'), + transport: z + .object({ + ignoreCert: configBool.describe('Whether to ignore SSL certificate errors'), + host: z.string().describe('SMTP server hostname'), + port: z.int().min(0).max(65_535).describe('SMTP server port'), + secure: configBool.describe('Whether to use secure connection (TLS/SSL)'), + username: z.string().describe('SMTP username'), + password: z.string().describe('SMTP password'), + }) + .meta({ id: 'AdminConfigSmtpTransportDto' }), + }) + .meta({ id: 'AdminConfigSmtpDto' }); + +const AdminConfigSchemaWithVisibility = z + .object({ + backup: z + .object({ + database: z + .object({ + enabled: configBool.describe('Enabled'), + cronExpression: cronExpressionSchema, + keepLastAmount: z.int().min(1).describe('Keep last amount'), + }) + .meta({ id: 'AdminConfigDatabaseBackupDto' }), + }) + .meta({ id: 'AdminConfigBackupsDto' }), + ffmpeg: AdminConfigFFmpegSchema, + integrityChecks: z + .object({ + missingFiles: AdminConfigIntegrityJobSchema, + untrackedFiles: AdminConfigIntegrityJobSchema, + checksumFiles: AdminConfigIntegrityJobSchema.extend({ + timeLimit: z.int().nonnegative().describe('How long the integrity checksum job may run for'), + percentageLimit: z + .float32() + .nonnegative() + .max(1) + .describe('Percentage limit of the integrity checksum job') + .meta({ format: 'double' }), + }) + .describe('Integrity checksum job config') + .meta({ id: 'AdminConfigIntegrityChecksumJobDto' }), + }) + .describe('Integrity checks config') + .meta({ id: 'AdminConfigIntegrityChecksDto' }), + job: z + .object({ + thumbnailGeneration: AdminConfigJobSettingsSchema, + metadataExtraction: AdminConfigJobSettingsSchema, + videoConversion: AdminConfigJobSettingsSchema, + faceDetection: AdminConfigJobSettingsSchema, + smartSearch: AdminConfigJobSettingsSchema, + backgroundTask: AdminConfigJobSettingsSchema, + migration: AdminConfigJobSettingsSchema, + search: AdminConfigJobSettingsSchema, + sidecar: AdminConfigJobSettingsSchema, + library: AdminConfigJobSettingsSchema, + notifications: AdminConfigJobSettingsSchema, + ocr: AdminConfigJobSettingsSchema, + workflow: AdminConfigJobSettingsSchema, + editor: AdminConfigJobSettingsSchema, + integrityCheck: AdminConfigJobSettingsSchema, + }) + .meta({ id: 'AdminConfigJobDto' }), + logging: z + .object({ + enabled: configBool.describe('Enabled'), + level: LogLevelSchema, + }) + .meta({ id: 'AdminConfigLoggingDto' }), + machineLearning: z + .object({ + enabled: configBool.describe('Enabled').meta({ visibility: User }), + urls: z.array(z.string()).min(1).describe('ML service URLs'), + availabilityChecks: z + .object({ + enabled: configBool.describe('Enabled'), + timeout: z.int(), + interval: z.int(), + }) + .meta({ id: 'AdminConfigMachineLearningAvailabilityChecksDto' }), + clip: AdminConfigMachineLearningModelSchema.meta({ id: 'AdminConfigClipDto' }), + duplicateDetection: AdminConfigMachineLearningTaskSchema.extend({ + maxDistance: z + .number() + .min(0.001) + .max(0.1) + .describe('Maximum distance threshold for duplicate detection') + .meta({ format: 'double' }), + }).meta({ id: 'AdminConfigDuplicateDetectionDto' }), + facialRecognition: AdminConfigMachineLearningModelSchema.extend({ + minScore: z + .number() + .min(0.1) + .max(1) + .describe('Minimum confidence score for face detection') + .meta({ format: 'double' }), + maxDistance: z + .number() + .min(0.1) + .max(2) + .describe('Maximum distance threshold for face recognition') + .meta({ format: 'double' }), + minFaces: z + .int() + .min(1) + .describe('Minimum number of faces required for recognition') + .meta({ visibility: User }), + }).meta({ id: 'AdminConfigFacialRecognitionDto' }), + ocr: AdminConfigMachineLearningModelSchema.extend({ + maxResolution: z.int().min(1).describe('Maximum resolution for OCR processing'), + minDetectionScore: z + .number() + .min(0.1) + .max(1) + .describe('Minimum confidence score for text detection') + .meta({ format: 'double' }), + minRecognitionScore: z + .number() + .min(0.1) + .max(1) + .describe('Minimum confidence score for text recognition') + .meta({ format: 'double' }), + }).meta({ id: 'AdminConfigOcrDto' }), + }) + .meta({ id: 'AdminConfigMachineLearningDto' }), + map: z + .object({ + enabled: configBool.describe('Enabled').meta({ visibility: User }), + lightStyle: z.url().describe('Light map style URL').meta({ visibility: User }), + darkStyle: z.url().describe('Dark map style URL').meta({ visibility: User }), + }) + .meta({ id: 'AdminConfigMapDto' }), + reverseGeocoding: z + .object({ enabled: configBool.describe('Enabled').meta({ visibility: User }) }) + .meta({ id: 'AdminConfigReverseGeocodingDto' }), + metadata: z + .object({ + faces: z.object({ import: configBool.describe('Import') }).meta({ id: 'AdminConfigFacesDto' }), + }) + .meta({ id: 'AdminConfigMetadataDto' }), + oauth: z + .object({ + autoLaunch: configBool.describe('Auto launch').meta({ visibility: Public }), + autoRegister: configBool.describe('Auto register'), + buttonText: z.string().describe('Button text').meta({ visibility: Public }), + clientId: z.string().describe('Client ID'), + clientSecret: z.string().describe('Client secret'), + tokenEndpointAuthMethod: OAuthTokenEndpointAuthMethodSchema, + timeout: z.int().min(1).describe('Timeout'), + allowInsecureRequests: configBool.describe('Allow insecure requests'), + defaultStorageQuota: z.int().min(0).nullable().describe('Default storage quota'), + enabled: configBool.describe('Enabled').meta({ visibility: Public }), + issuerUrl: emptyOrUrl('Issuer URL must be an empty string or a valid URL').describe('Issuer URL'), + scope: z.string().describe('Scope'), + prompt: z.string().describe('OAuth prompt parameter (e.g. select_account, login, consent)'), + endSessionEndpoint: emptyOrUrl('endSessionEndpoint must be an empty string or a valid URL').describe( + 'End session endpoint', + ), + signingAlgorithm: z.string().describe('Signing algorithm'), + profileSigningAlgorithm: z.string().describe('Profile signing algorithm'), + storageLabelClaim: z.string().describe('Storage label claim'), + storageQuotaClaim: z.string().describe('Storage quota claim'), + roleClaim: z.string().describe('Role claim'), + mobileOverrideEnabled: configBool.describe('Mobile override enabled'), + mobileRedirectUri: z.string().describe('Mobile redirect URI (set to empty string to disable)'), + }) + .transform((value, ctx) => { + if (!value.mobileOverrideEnabled || value.mobileRedirectUri === '') { + return value; + } + + if (!z.url().safeParse(value.mobileRedirectUri).success) { + ctx.issues.push({ + code: 'custom', + message: 'Mobile redirect URI must be an empty string or a valid URL', + input: value.mobileRedirectUri, + }); + return z.NEVER; + } + + return value; + }) + .meta({ id: 'AdminConfigOAuthDto' }), + passwordLogin: z + .object({ enabled: configBool.describe('Enabled').meta({ visibility: Public }) }) + .meta({ id: 'AdminConfigPasswordLoginDto' }), + storageTemplate: z + .object({ + enabled: configBool.describe('Enabled'), + hashVerificationEnabled: configBool.describe('Hash verification enabled'), + template: z.string().describe('Template'), + }) + .meta({ id: 'AdminConfigStorageTemplateDto' }), + image: z + .object({ + thumbnail: AdminConfigGeneratedImageSchema, + preview: AdminConfigGeneratedImageSchema, + fullsize: z + .object({ + enabled: configBool.describe('Enabled').meta({ visibility: User }), + format: ImageFormatSchema, + quality: z.int().min(1).max(100).describe('Quality'), + progressive: configBool.default(false).optional().describe('Progressive'), + }) + .meta({ id: 'AdminConfigGeneratedFullsizeImageDto' }), + colorspace: ColorspaceSchema, + extractEmbedded: configBool.describe('Extract embedded'), + }) + .meta({ id: 'AdminConfigImageDto' }), + newVersionCheck: z + .object({ enabled: configBool.describe('Enabled'), channel: ReleaseChannelSchema }) + .meta({ id: 'AdminConfigNewVersionCheckDto' }), + nightlyTasks: z + .object({ + startTime: z.iso + .time({ + precision: -1, + error: (iss) => `Invalid input: expected string in HH:MM format, received ${typeof iss.input}`, + }) + .describe('Start time (HH:MM)'), + databaseCleanup: configBool.describe('Database cleanup'), + missingThumbnails: configBool.describe('Missing thumbnails'), + clusterNewFaces: configBool.describe('Cluster new faces'), + generateMemories: configBool.describe('Generate memories'), + syncQuotaUsage: configBool.describe('Sync quota usage'), + }) + .meta({ id: 'AdminConfigNightlyTasksDto' }), + trash: z + .object({ + enabled: configBool.describe('Enabled').meta({ visibility: User }), + days: z.int().min(0).describe('Days').meta({ visibility: User }), + }) + .meta({ id: 'AdminConfigTrashDto' }), + theme: z + .object({ customCss: z.string().describe('Custom CSS for theming').meta({ visibility: Public }) }) + .meta({ id: 'AdminConfigThemeDto' }), + library: z + .object({ + scan: z + .object({ + enabled: configBool.describe('Enabled'), + cronExpression: cronExpressionSchema, + }) + .meta({ id: 'AdminConfigLibraryScanDto' }), + watch: z.object({ enabled: configBool.describe('Enabled') }).meta({ id: 'AdminConfigLibraryWatchDto' }), + }) + .meta({ id: 'AdminConfigLibraryDto' }), + notifications: z.object({ smtp: AdminConfigSmtpSchema }).meta({ id: 'AdminConfigNotificationsDto' }), + templates: z + .object({ + email: z + .object({ + welcomeTemplate: z.string().describe('Welcome template'), + albumInviteTemplate: z.string().describe('Album invite template'), + albumUpdateTemplate: z.string().describe('Album update template'), + }) + .meta({ id: 'AdminConfigTemplateEmailsDto' }), + }) + .meta({ id: 'AdminConfigTemplatesDto' }), + server: z + .object({ + externalDomain: emptyOrUrl('External domain must be an empty string or a valid URL') + .describe('External domain') + .meta({ visibility: User }), + loginPageMessage: z.string().describe('Login page message').meta({ visibility: Public }), + publicUsers: configBool.describe('Public users').meta({ visibility: User }), + }) + .meta({ id: 'AdminConfigServerDto' }), + user: z + .object({ deleteDelay: z.int().min(1).describe('Delete delay').meta({ visibility: User }) }) + .meta({ id: 'AdminConfigUserDto' }), + }) + .describe('Configuration properties that are visible to the admin') + .meta({ id: 'AdminConfigDto' }); + +export type SystemConfig = z.infer; +export type MachineLearningConfig = SystemConfig['machineLearning']; + +const visibilities = [Public, User, Admin]; + +const isVisible = (property: ConfigVisibility, visibility: ConfigVisibility) => + visibilities.indexOf(property) <= visibilities.indexOf(visibility); + +const getMeta = (schema: z.ZodType) => + (z.globalRegistry.get(schema) ?? {}) as { id?: string; description?: string; visibility?: ConfigVisibility }; + +const unwrap = (schema: z.ZodType) => (schema instanceof z.ZodPipe ? (schema.def.in as z.ZodType) : schema); + +const visibleSchemas = new Map>(); + +const applyVisibility = (visibility: ConfigVisibility): z.ZodType | undefined => { + const map: Record = { + [Admin]: 'Configuration properties that are visible to the admin', + [User]: 'Configuration properties that are visible to a logged user', + [Public]: 'Configuration properties that are visible to everyone', + }; + + return applyVisibilityRecursive(AdminConfigSchemaWithVisibility, visibility, map[visibility]); +}; + +const applyVisibilityRecursive = ( + schema: z.ZodType, + visibility: ConfigVisibility, + override?: string, +): z.ZodType | undefined => { + const object = unwrap(schema); + const { id, description, visibility: property } = getMeta(schema); + + if (!(object instanceof z.ZodObject)) { + return isVisible(property ?? Admin, visibility) ? schema : undefined; + } + + let cache = visibleSchemas.get(schema); + if (!cache) { + cache = new Map(); + visibleSchemas.set(schema, cache); + } + + if (cache.has(visibility)) { + return cache.get(visibility); + } + + const shape: Record = {}; + for (const [key, value] of Object.entries(object.shape as Record)) { + const visible = applyVisibilityRecursive(value, visibility); + if (visible) { + shape[key] = visible; + } + } + + let visible: z.ZodType | undefined; + if (Object.keys(shape).length > 0) { + visible = z.object(shape).meta({ + ...(id && { id: `${visibility}${id.slice(Admin.length)}` }), + ...((override ?? description) && { description: override ?? description }), + }); + } + + cache.set(visibility, visible); + + return visible; +}; + +const stripVisibilityMetadata = (schema: T): T => { + const object = unwrap(schema); + if (object instanceof z.ZodObject) { + for (const value of Object.values(object.shape as Record)) { + stripVisibilityMetadata(value); + } + + return schema; + } + + const { visibility, ...meta } = getMeta(schema); + if (visibility) { + z.globalRegistry.add(schema, meta); + } + + return schema; +}; + +const AdminConfigSchema = applyVisibility(Admin)! as z.ZodType; +const UserConfigSchema = applyVisibility(User)! as z.ZodType>; +const PublicConfigSchema = applyVisibility(Public)! as z.ZodType>; + +// prevent visibility metadata from leaking to openapi spec +// eslint-disable-next-line unicorn/no-top-level-side-effects +stripVisibilityMetadata(AdminConfigSchemaWithVisibility); + +const ConfigTemplateStorageOptionSchema = z + .object({ + yearOptions: z.array(z.string()).describe('Available year format options for storage template'), + monthOptions: z.array(z.string()).describe('Available month format options for storage template'), + weekOptions: z.array(z.string()).describe('Available week format options for storage template'), + dayOptions: z.array(z.string()).describe('Available day format options for storage template'), + hourOptions: z.array(z.string()).describe('Available hour format options for storage template'), + minuteOptions: z.array(z.string()).describe('Available minute format options for storage template'), + secondOptions: z.array(z.string()).describe('Available second format options for storage template'), + presetOptions: z.array(z.string()).describe('Available preset template options'), + }) + .meta({ id: 'SystemConfigTemplateStorageOptionDto' }); + +export class AdminConfigDto extends createZodDto(AdminConfigSchema) {} +export class UserConfigDto extends createZodDto(UserConfigSchema) {} +export class PublicConfigDto extends createZodDto(PublicConfigSchema) {} +export class ConfigFFmpegDto extends createZodDto(AdminConfigFFmpegSchema) {} +export class ConfigSmtpDto extends createZodDto(AdminConfigSmtpSchema) {} +export class ConfigTemplateStorageOptionDto extends createZodDto(ConfigTemplateStorageOptionSchema) {} + +/** @deprecated the `/system-config` endpoints these are named after are on their way out */ +export { AdminConfigDto as SystemConfigDto, ConfigSmtpDto as SystemConfigSmtpDto }; + +export function mapAdminConfig(config: SystemConfig): AdminConfigDto { + return config; +} + +export function mapUserConfig(config: SystemConfig): UserConfigDto { + return UserConfigSchema.parse(config); +} + +export function mapPublicConfig(config: SystemConfig): PublicConfigDto { + return PublicConfigSchema.parse(config); +} + +export const defaults = Object.freeze({ + backup: { + database: { + enabled: true, + cronExpression: CronExpression.EVERY_DAY_AT_2AM, + keepLastAmount: 14, + }, + }, + ffmpeg: { + crf: 23, + threads: 0, + preset: 'ultrafast', + targetVideoCodec: VideoCodec.H264, + acceptedVideoCodecs: [VideoCodec.H264], + targetAudioCodec: AudioCodec.Aac, + acceptedAudioCodecs: [AudioCodec.Aac, AudioCodec.Mp3, AudioCodec.Opus], + acceptedContainers: [VideoContainer.Mov, VideoContainer.Ogg, VideoContainer.Webm], + targetResolution: '720', + maxBitrate: '0', + bframes: -1, + refs: 0, + gopSize: 0, + temporalAQ: false, + cqMode: CQMode.Auto, + twoPass: false, + preferredHwDevice: 'auto', + transcode: TranscodePolicy.Required, + tonemap: ToneMapping.Hable, + accel: TranscodeHardwareAcceleration.Disabled, + accelDecode: true, + realtime: { + enabled: false, + videoCodecs: [VideoCodec.H264, VideoCodec.Hevc], + resolutions: [HlsVideoResolution.p480, HlsVideoResolution.p720, HlsVideoResolution.p1080], + }, + }, + integrityChecks: { + missingFiles: { + enabled: true, + cronExpression: CronExpression.EVERY_DAY_AT_3AM, + }, + untrackedFiles: { + enabled: true, + cronExpression: CronExpression.EVERY_DAY_AT_3AM, + }, + checksumFiles: { + enabled: true, + cronExpression: CronExpression.EVERY_DAY_AT_3AM, + timeLimit: 60 * 60 * 1000, // 1 hour + percentageLimit: 1, // 100% of assets + }, + }, + job: { + thumbnailGeneration: { concurrency: 3 }, + metadataExtraction: { concurrency: 5 }, + videoConversion: { concurrency: 1 }, + faceDetection: { concurrency: 2 }, + smartSearch: { concurrency: 2 }, + backgroundTask: { concurrency: 5 }, + migration: { concurrency: 5 }, + search: { concurrency: 5 }, + sidecar: { concurrency: 5 }, + library: { concurrency: 5 }, + notifications: { concurrency: 5 }, + ocr: { concurrency: 1 }, + workflow: { concurrency: 5 }, + editor: { concurrency: 2 }, + integrityCheck: { concurrency: 1 }, + }, + logging: { + enabled: true, + level: LogLevel.Log, + }, + machineLearning: { + enabled: process.env.IMMICH_MACHINE_LEARNING_ENABLED !== 'false', + urls: [process.env.IMMICH_MACHINE_LEARNING_URL || 'http://immich-machine-learning:3003'], + availabilityChecks: { + enabled: true, + timeout: 2000, + interval: 30_000, + }, + clip: { + enabled: true, + modelName: 'ViT-B-32__openai', + }, + duplicateDetection: { + enabled: true, + maxDistance: 0.01, + }, + facialRecognition: { + enabled: true, + modelName: 'buffalo_l', + minScore: 0.7, + maxDistance: 0.5, + minFaces: 3, + }, + ocr: { + enabled: true, + modelName: 'PP-OCRv5_mobile', + minDetectionScore: 0.5, + minRecognitionScore: 0.8, + maxResolution: 736, + }, + }, + map: { + enabled: true, + lightStyle: 'https://tiles.immich.cloud/v1/style/light.json', + darkStyle: 'https://tiles.immich.cloud/v1/style/dark.json', + }, + reverseGeocoding: { + enabled: true, + }, + metadata: { + faces: { + import: false, + }, + }, + oauth: { + autoLaunch: false, + autoRegister: true, + buttonText: 'Login with OAuth', + clientId: '', + clientSecret: '', + defaultStorageQuota: null, + enabled: false, + issuerUrl: '', + endSessionEndpoint: '', + mobileOverrideEnabled: false, + mobileRedirectUri: '', + prompt: '', + scope: 'openid email profile', + signingAlgorithm: 'RS256', + profileSigningAlgorithm: 'none', + storageLabelClaim: 'preferred_username', + storageQuotaClaim: 'immich_quota', + roleClaim: 'immich_role', + tokenEndpointAuthMethod: OAuthTokenEndpointAuthMethod.ClientSecretPost, + timeout: 30_000, + allowInsecureRequests: false, + }, + passwordLogin: { + enabled: true, + }, + storageTemplate: { + enabled: false, + hashVerificationEnabled: true, + template: '{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}', + }, + image: { + thumbnail: { + format: ImageFormat.Webp, + size: 250, + quality: 80, + progressive: false, + }, + preview: { + format: ImageFormat.Jpeg, + size: 1440, + quality: 80, + progressive: false, + }, + colorspace: Colorspace.P3, + extractEmbedded: false, + fullsize: { + enabled: false, + format: ImageFormat.Jpeg, + quality: 80, + progressive: false, + }, + }, + newVersionCheck: { + enabled: true, + channel: ReleaseChannel.Stable, + }, + nightlyTasks: { + startTime: '00:00', + databaseCleanup: true, + generateMemories: true, + syncQuotaUsage: true, + missingThumbnails: true, + clusterNewFaces: true, + }, + trash: { + enabled: true, + days: 30, + }, + theme: { + customCss: '', + }, + library: { + scan: { + enabled: true, + cronExpression: CronExpression.EVERY_DAY_AT_MIDNIGHT, + }, + watch: { + enabled: false, + }, + }, + server: { + externalDomain: '', + loginPageMessage: '', + publicUsers: true, + }, + notifications: { + smtp: { + enabled: false, + from: '', + replyTo: '', + transport: { + ignoreCert: false, + host: '', + port: 587, + secure: false, + username: '', + password: '', + }, + }, + }, + templates: { + email: { + welcomeTemplate: '', + albumInviteTemplate: '', + albumUpdateTemplate: '', + }, + }, + user: { + deleteDelay: 7, + }, +}); diff --git a/server/src/dtos/model-config.dto.ts b/server/src/dtos/model-config.dto.ts deleted file mode 100644 index 2ba6f0c365abc8..00000000000000 --- a/server/src/dtos/model-config.dto.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { createZodDto } from 'nestjs-zod'; -import z from 'zod'; - -const TaskConfigSchema = z - .object({ - enabled: z.boolean().describe('Whether the task is enabled'), - }) - .meta({ id: 'TaskConfig' }); - -const ModelConfigSchema = TaskConfigSchema.extend({ - modelName: z.string().describe('Name of the model to use'), -}); - -export const CLIPConfigSchema = ModelConfigSchema.meta({ id: 'CLIPConfig' }); - -export const DuplicateDetectionConfigSchema = TaskConfigSchema.extend({ - maxDistance: z - .number() - .meta({ format: 'double' }) - .min(0.001) - .max(0.1) - .describe('Maximum distance threshold for duplicate detection'), -}).meta({ id: 'DuplicateDetectionConfig' }); - -export const FacialRecognitionConfigSchema = ModelConfigSchema.extend({ - minScore: z - .number() - .meta({ format: 'double' }) - .min(0.1) - .max(1) - .describe('Minimum confidence score for face detection'), - maxDistance: z - .number() - .meta({ format: 'double' }) - .min(0.1) - .max(2) - .describe('Maximum distance threshold for face recognition'), - minFaces: z.int().min(1).describe('Minimum number of faces required for recognition'), -}).meta({ id: 'FacialRecognitionConfig' }); - -export const OcrConfigSchema = ModelConfigSchema.extend({ - maxResolution: z.int().min(1).describe('Maximum resolution for OCR processing'), - minDetectionScore: z - .number() - .meta({ format: 'double' }) - .min(0.1) - .max(1) - .describe('Minimum confidence score for text detection'), - minRecognitionScore: z - .number() - .meta({ format: 'double' }) - .min(0.1) - .max(1) - .describe('Minimum confidence score for text recognition'), -}).meta({ id: 'OcrConfig' }); - -export class CLIPConfig extends createZodDto(CLIPConfigSchema) {} diff --git a/server/src/dtos/system-config.dto.ts b/server/src/dtos/system-config.dto.ts deleted file mode 100644 index a50b7abe87b459..00000000000000 --- a/server/src/dtos/system-config.dto.ts +++ /dev/null @@ -1,444 +0,0 @@ -import { validateCronExpression } from 'cron'; -import { createZodDto } from 'nestjs-zod'; -import { SystemConfig } from 'src/config'; -import { - CLIPConfigSchema, - DuplicateDetectionConfigSchema, - FacialRecognitionConfigSchema, - OcrConfigSchema, -} from 'src/dtos/model-config.dto'; -import { - AudioCodecSchema, - ColorspaceSchema, - CQModeSchema, - HlsVideoResolutionSchema, - ImageFormatSchema, - LogLevelSchema, - OAuthTokenEndpointAuthMethodSchema, - ToneMappingSchema, - TranscodeHardwareAccelerationSchema, - TranscodePolicySchema, - VideoCodecSchema, - VideoContainerSchema, -} from 'src/enum'; -import z from 'zod'; - -/** Coerces 'true'/'false' strings to boolean, but also allows booleans. */ -const configBool = z - .preprocess((val) => { - if (val === 'true') { - return true; - } - if (val === 'false') { - return false; - } - return val; - }, z.boolean()) - .meta({ type: 'boolean' }); - -const JobSettingsSchema = z - .object({ - concurrency: z.int().min(1).describe('Concurrency'), - }) - .meta({ id: 'JobSettingsDto' }); - -const cronExpressionSchema = z - .string() - .superRefine((value, ctx) => { - const validated = validateCronExpression(value); - if (!validated.valid) { - ctx.addIssue({ - code: 'custom', - message: `Invalid cron expression. ${validated.error?.message ?? ''}`, - input: value, - }); - } - }) - .describe('Cron expression'); - -const DatabaseBackupSchema = z - .object({ - enabled: configBool.describe('Enabled'), - cronExpression: cronExpressionSchema, - keepLastAmount: z.int().min(1).describe('Keep last amount'), - }) - .meta({ id: 'DatabaseBackupConfig' }); - -const SystemConfigIntegrityJobSchema = z - .object({ - enabled: z.boolean().describe('Enabled'), - cronExpression: cronExpressionSchema.describe('Cron expression for when the integrity check should run'), - }) - .describe('Integrity job config') - .meta({ id: 'SystemConfigIntegrityJob' }); - -const SystemConfigIntegrityChecksumJobSchema = SystemConfigIntegrityJobSchema.extend({ - timeLimit: z.int().nonnegative().describe('How long the integrity checksum job may run for'), - percentageLimit: z - .float32() - .nonnegative() - .max(1) - .describe('Percentage limit of the integrity checksum job') - .meta({ format: 'double' }), -}) - .describe('Integrity checksum job config') - .meta({ id: 'SystemConfigIntegrityChecksumJob' }); - -const SystemConfigIntegrityChecksSchema = z - .object({ - missingFiles: SystemConfigIntegrityJobSchema, - untrackedFiles: SystemConfigIntegrityJobSchema, - checksumFiles: SystemConfigIntegrityChecksumJobSchema, - }) - .describe('Integrity checks config') - .meta({ id: 'SystemConfigIntegrityChecks' }); - -const SystemConfigBackupsSchema = z.object({ database: DatabaseBackupSchema }).meta({ id: 'SystemConfigBackupsDto' }); - -const SystemConfigFFmpegSchema = z - .object({ - crf: z.coerce.number().int().min(0).max(51).describe('CRF'), - threads: z.coerce.number().int().min(0).describe('Threads'), - preset: z.string().describe('Preset'), - targetVideoCodec: VideoCodecSchema, - acceptedVideoCodecs: z.array(VideoCodecSchema).describe('Accepted video codecs'), - targetAudioCodec: AudioCodecSchema, - acceptedAudioCodecs: z.array(AudioCodecSchema).describe('Accepted audio codecs'), - acceptedContainers: z.array(VideoContainerSchema).describe('Accepted containers'), - targetResolution: z.string().describe('Target resolution'), - maxBitrate: z.string().describe('Max bitrate'), - bframes: z.coerce.number().int().min(-1).max(16).describe('B-frames'), - refs: z.coerce.number().int().min(0).max(6).describe('References'), - gopSize: z.coerce.number().int().min(0).describe('GOP size'), - temporalAQ: configBool.describe('Temporal AQ'), - cqMode: CQModeSchema, - twoPass: configBool.describe('Two pass'), - preferredHwDevice: z.string().describe('Preferred hardware device'), - transcode: TranscodePolicySchema, - accel: TranscodeHardwareAccelerationSchema, - accelDecode: configBool.describe('Accelerated decode'), - tonemap: ToneMappingSchema, - realtime: z - .object({ - enabled: configBool.describe('Enable real-time HLS transcoding (alpha)'), - videoCodecs: z.array(VideoCodecSchema).describe('Video codecs to use for real-time HLS transcoding'), - resolutions: z.array(HlsVideoResolutionSchema).describe('Resolutions to use for real-time HLS transcoding'), - }) - .meta({ id: 'SystemConfigFFmpegRealtimeDto' }), - }) - .meta({ id: 'SystemConfigFFmpegDto' }); - -const SystemConfigJobSchema = z - .object({ - thumbnailGeneration: JobSettingsSchema, - metadataExtraction: JobSettingsSchema, - videoConversion: JobSettingsSchema, - faceDetection: JobSettingsSchema, - smartSearch: JobSettingsSchema, - backgroundTask: JobSettingsSchema, - migration: JobSettingsSchema, - search: JobSettingsSchema, - sidecar: JobSettingsSchema, - library: JobSettingsSchema, - notifications: JobSettingsSchema, - ocr: JobSettingsSchema, - workflow: JobSettingsSchema, - editor: JobSettingsSchema, - integrityCheck: JobSettingsSchema, - }) - .meta({ id: 'SystemConfigJobDto' }); - -const SystemConfigLibraryScanSchema = z - .object({ - enabled: configBool.describe('Enabled'), - cronExpression: cronExpressionSchema, - }) - .meta({ id: 'SystemConfigLibraryScanDto' }); - -const SystemConfigLibraryWatchSchema = z - .object({ enabled: configBool.describe('Enabled') }) - .meta({ id: 'SystemConfigLibraryWatchDto' }); - -const SystemConfigLibrarySchema = z - .object({ scan: SystemConfigLibraryScanSchema, watch: SystemConfigLibraryWatchSchema }) - .meta({ id: 'SystemConfigLibraryDto' }); - -const SystemConfigLoggingSchema = z - .object({ - enabled: configBool.describe('Enabled'), - level: LogLevelSchema, - }) - .meta({ id: 'SystemConfigLoggingDto' }); - -const MachineLearningAvailabilityChecksSchema = z - .object({ - enabled: configBool.describe('Enabled'), - timeout: z.int(), - interval: z.int(), - }) - .meta({ id: 'MachineLearningAvailabilityChecksDto' }); - -const SystemConfigMachineLearningSchema = z - .object({ - enabled: configBool.describe('Enabled'), - urls: z.array(z.string()).min(1).describe('ML service URLs'), - availabilityChecks: MachineLearningAvailabilityChecksSchema, - clip: CLIPConfigSchema, - duplicateDetection: DuplicateDetectionConfigSchema, - facialRecognition: FacialRecognitionConfigSchema, - ocr: OcrConfigSchema, - }) - .meta({ id: 'SystemConfigMachineLearningDto' }); - -const SystemConfigMapSchema = z - .object({ - enabled: configBool.describe('Enabled'), - lightStyle: z.url().describe('Light map style URL'), - darkStyle: z.url().describe('Dark map style URL'), - }) - .meta({ id: 'SystemConfigMapDto' }); - -export enum ReleaseChannel { - Stable = 'stable', - ReleaseCandidate = 'releaseCandidate', -} - -const ReleaseChannelSchema = z.enum(ReleaseChannel).describe('Release channel').meta({ id: 'ReleaseChannel' }); - -const SystemConfigNewVersionCheckSchema = z - .object({ enabled: configBool.describe('Enabled'), channel: ReleaseChannelSchema }) - .meta({ id: 'SystemConfigNewVersionCheckDto' }); - -const SystemConfigNightlyTasksSchema = z - .object({ - startTime: z.iso - .time({ - precision: -1, - error: (iss) => `Invalid input: expected string in HH:MM format, received ${typeof iss.input}`, - }) - .describe('Start time (HH:MM)'), - databaseCleanup: configBool.describe('Database cleanup'), - missingThumbnails: configBool.describe('Missing thumbnails'), - clusterNewFaces: configBool.describe('Cluster new faces'), - generateMemories: configBool.describe('Generate memories'), - syncQuotaUsage: configBool.describe('Sync quota usage'), - }) - .meta({ id: 'SystemConfigNightlyTasksDto' }); - -const SystemConfigOAuthSchema = z - .object({ - autoLaunch: configBool.describe('Auto launch'), - autoRegister: configBool.describe('Auto register'), - buttonText: z.string().describe('Button text'), - clientId: z.string().describe('Client ID'), - clientSecret: z.string().describe('Client secret'), - tokenEndpointAuthMethod: OAuthTokenEndpointAuthMethodSchema, - timeout: z.int().min(1).describe('Timeout'), - allowInsecureRequests: configBool.describe('Allow insecure requests'), - defaultStorageQuota: z.int().min(0).nullable().describe('Default storage quota'), - enabled: configBool.describe('Enabled'), - issuerUrl: z - .string() - .refine((url) => url.length === 0 || z.url().safeParse(url).success, { - error: 'Issuer URL must be an empty string or a valid URL', - }) - .describe('Issuer URL'), - scope: z.string().describe('Scope'), - prompt: z.string().describe('OAuth prompt parameter (e.g. select_account, login, consent)'), - endSessionEndpoint: z - .string() - .refine((url) => url.length === 0 || z.url().safeParse(url).success, { - error: 'endSessionEndpoint must be an empty string or a valid URL', - }) - .describe('End session endpoint'), - signingAlgorithm: z.string().describe('Signing algorithm'), - profileSigningAlgorithm: z.string().describe('Profile signing algorithm'), - storageLabelClaim: z.string().describe('Storage label claim'), - storageQuotaClaim: z.string().describe('Storage quota claim'), - roleClaim: z.string().describe('Role claim'), - mobileOverrideEnabled: configBool.describe('Mobile override enabled'), - mobileRedirectUri: z.string().describe('Mobile redirect URI (set to empty string to disable)'), - }) - .transform((value, ctx) => { - if (!value.mobileOverrideEnabled || value.mobileRedirectUri === '') { - return value; - } - - if (!z.url().safeParse(value.mobileRedirectUri).success) { - ctx.issues.push({ - code: 'custom', - message: 'Mobile redirect URI must be an empty string or a valid URL', - input: value.mobileRedirectUri, - }); - return z.NEVER; - } - - return value; - }) - .meta({ - id: 'SystemConfigOAuthDto', - }); - -const SystemConfigPasswordLoginSchema = z - .object({ enabled: configBool.describe('Enabled') }) - .meta({ id: 'SystemConfigPasswordLoginDto' }); - -const SystemConfigReverseGeocodingSchema = z - .object({ enabled: configBool.describe('Enabled') }) - .meta({ id: 'SystemConfigReverseGeocodingDto' }); - -const SystemConfigFacesSchema = z - .object({ import: configBool.describe('Import') }) - .meta({ id: 'SystemConfigFacesDto' }); -const SystemConfigMetadataSchema = z.object({ faces: SystemConfigFacesSchema }).meta({ id: 'SystemConfigMetadataDto' }); - -const SystemConfigServerSchema = z - .object({ - externalDomain: z - .string() - .refine((url) => url.length === 0 || z.url().safeParse(url).success, { - error: 'External domain must be an empty string or a valid URL', - }) - .describe('External domain'), - loginPageMessage: z.string().describe('Login page message'), - publicUsers: configBool.describe('Public users'), - }) - .meta({ id: 'SystemConfigServerDto' }); - -const SystemConfigSmtpTransportSchema = z - .object({ - ignoreCert: configBool.describe('Whether to ignore SSL certificate errors'), - host: z.string().describe('SMTP server hostname'), - port: z.int().min(0).max(65_535).describe('SMTP server port'), - secure: configBool.describe('Whether to use secure connection (TLS/SSL)'), - username: z.string().describe('SMTP username'), - password: z.string().describe('SMTP password'), - }) - .meta({ id: 'SystemConfigSmtpTransportDto' }); - -const SystemConfigSmtpSchema = z - .object({ - enabled: configBool.describe('Whether SMTP email notifications are enabled'), - from: z.string().describe('Email address to send from'), - replyTo: z.string().describe('Email address for replies'), - transport: SystemConfigSmtpTransportSchema, - }) - .meta({ id: 'SystemConfigSmtpDto' }); - -const SystemConfigNotificationsSchema = z - .object({ smtp: SystemConfigSmtpSchema }) - .meta({ id: 'SystemConfigNotificationsDto' }); - -const SystemConfigTemplateEmailsSchema = z - .object({ - albumInviteTemplate: z.string().describe('Album invite template'), - welcomeTemplate: z.string().describe('Welcome template'), - albumUpdateTemplate: z.string().describe('Album update template'), - }) - .meta({ id: 'SystemConfigTemplateEmailsDto' }); -const SystemConfigTemplatesSchema = z - .object({ email: SystemConfigTemplateEmailsSchema }) - .meta({ id: 'SystemConfigTemplatesDto' }); - -const SystemConfigStorageTemplateSchema = z - .object({ - enabled: configBool.describe('Enabled'), - hashVerificationEnabled: configBool.describe('Hash verification enabled'), - template: z.string().describe('Template'), - }) - .meta({ id: 'SystemConfigStorageTemplateDto' }); - -const SystemConfigTemplateStorageOptionSchema = z - .object({ - yearOptions: z.array(z.string()).describe('Available year format options for storage template'), - monthOptions: z.array(z.string()).describe('Available month format options for storage template'), - weekOptions: z.array(z.string()).describe('Available week format options for storage template'), - dayOptions: z.array(z.string()).describe('Available day format options for storage template'), - hourOptions: z.array(z.string()).describe('Available hour format options for storage template'), - minuteOptions: z.array(z.string()).describe('Available minute format options for storage template'), - secondOptions: z.array(z.string()).describe('Available second format options for storage template'), - presetOptions: z.array(z.string()).describe('Available preset template options'), - }) - .meta({ id: 'SystemConfigTemplateStorageOptionDto' }); - -const SystemConfigThemeSchema = z - .object({ customCss: z.string().describe('Custom CSS for theming') }) - .meta({ id: 'SystemConfigThemeDto' }); - -const SystemConfigGeneratedImageSchema = z - .object({ - format: ImageFormatSchema, - quality: z.int().min(1).max(100).describe('Quality'), - size: z.int().min(1).describe('Size'), - progressive: configBool.default(false).optional().describe('Progressive'), - }) - .meta({ id: 'SystemConfigGeneratedImageDto' }); - -const SystemConfigGeneratedFullsizeImageSchema = z - .object({ - enabled: configBool.describe('Enabled'), - format: ImageFormatSchema, - quality: z.int().min(1).max(100).describe('Quality'), - progressive: configBool.default(false).optional().describe('Progressive'), - }) - .meta({ id: 'SystemConfigGeneratedFullsizeImageDto' }); - -const SystemConfigImageSchema = z - .object({ - thumbnail: SystemConfigGeneratedImageSchema, - preview: SystemConfigGeneratedImageSchema, - fullsize: SystemConfigGeneratedFullsizeImageSchema, - colorspace: ColorspaceSchema, - extractEmbedded: configBool.describe('Extract embedded'), - }) - .meta({ id: 'SystemConfigImageDto' }); - -const SystemConfigTrashSchema = z - .object({ - enabled: configBool.describe('Enabled'), - days: z.int().min(0).describe('Days'), - }) - .meta({ id: 'SystemConfigTrashDto' }); - -const SystemConfigUserSchema = z - .object({ - deleteDelay: z.int().min(1).describe('Delete delay'), - }) - .meta({ id: 'SystemConfigUserDto' }); - -export const SystemConfigSchema = z - .object({ - backup: SystemConfigBackupsSchema, - ffmpeg: SystemConfigFFmpegSchema, - logging: SystemConfigLoggingSchema, - machineLearning: SystemConfigMachineLearningSchema, - map: SystemConfigMapSchema, - newVersionCheck: SystemConfigNewVersionCheckSchema, - nightlyTasks: SystemConfigNightlyTasksSchema, - oauth: SystemConfigOAuthSchema, - passwordLogin: SystemConfigPasswordLoginSchema, - reverseGeocoding: SystemConfigReverseGeocodingSchema, - metadata: SystemConfigMetadataSchema, - storageTemplate: SystemConfigStorageTemplateSchema, - job: SystemConfigJobSchema, - image: SystemConfigImageSchema, - trash: SystemConfigTrashSchema, - theme: SystemConfigThemeSchema, - library: SystemConfigLibrarySchema, - notifications: SystemConfigNotificationsSchema, - templates: SystemConfigTemplatesSchema, - server: SystemConfigServerSchema, - user: SystemConfigUserSchema, - integrityChecks: SystemConfigIntegrityChecksSchema, - }) - .describe('System configuration') - .meta({ id: 'SystemConfigDto' }); - -export class SystemConfigFFmpegDto extends createZodDto(SystemConfigFFmpegSchema) {} -export class SystemConfigSmtpDto extends createZodDto(SystemConfigSmtpSchema) {} -export class SystemConfigTemplateStorageOptionDto extends createZodDto(SystemConfigTemplateStorageOptionSchema) {} -export class SystemConfigDto extends createZodDto(SystemConfigSchema) {} - -export function mapConfig(config: SystemConfig): SystemConfigDto { - return config; -} diff --git a/server/src/enum.ts b/server/src/enum.ts index b6e2f6e4686ce6..3402afbc08ec21 100644 --- a/server/src/enum.ts +++ b/server/src/enum.ts @@ -161,6 +161,11 @@ export enum Permission { BackupUpload = 'backup.upload', BackupDelete = 'backup.delete', + AdminConfigRead = 'adminConfig.read', + AdminConfigUpdate = 'adminConfig.update', + + UserConfigRead = 'userConfig.read', + DuplicateRead = 'duplicate.read', DuplicateDelete = 'duplicate.delete', @@ -1165,12 +1170,25 @@ export const AssetVisibilitySchema = z .describe('Asset visibility') .meta({ id: 'AssetVisibility' }); +export enum ReleaseChannel { + Stable = 'stable', + ReleaseCandidate = 'releaseCandidate', +} + +export const ReleaseChannelSchema = z.enum(ReleaseChannel).describe('Release channel').meta({ id: 'ReleaseChannel' }); + export enum CronJob { LibraryScan = 'LibraryScan', NightlyJobs = 'NightlyJobs', VersionCheck = 'VersionCheck', } +export enum ConfigVisibility { + Public = 'Public', + User = 'User', + Admin = 'Admin', +} + export enum ApiTag { Activities = 'Activities', Albums = 'Albums', @@ -1178,6 +1196,9 @@ export enum ApiTag { Authentication = 'Authentication', AuthenticationAdmin = 'Authentication (admin)', Assets = 'Assets', + ConfigUser = 'Config (user)', + ConfigAdmin = 'Config (admin)', + ConfigPublic = 'Config (public)', DatabaseBackups = 'Database Backups (admin)', Deprecated = 'Deprecated', Download = 'Download', diff --git a/server/src/repositories/event.repository.ts b/server/src/repositories/event.repository.ts index 7fedc4eb3aed0b..d377215e5b3296 100644 --- a/server/src/repositories/event.repository.ts +++ b/server/src/repositories/event.repository.ts @@ -2,10 +2,10 @@ import { Injectable } from '@nestjs/common'; import { ModuleRef, Reflector } from '@nestjs/core'; import _ from 'lodash'; import { Socket } from 'socket.io'; -import { SystemConfig } from 'src/config'; import { Asset } from 'src/database'; import { EventConfig } from 'src/decorators'; import { AuthDto } from 'src/dtos/auth.dto'; +import { SystemConfig } from 'src/dtos/config.dto'; import { ImmichWorker, JobStatus, MetadataKey, QueueName, UserAvatarColor, UserStatus } from 'src/enum'; import { ConfigRepository } from 'src/repositories/config.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; diff --git a/server/src/repositories/machine-learning.repository.ts b/server/src/repositories/machine-learning.repository.ts index a05ddbc866baa7..c1b14fae0ab981 100644 --- a/server/src/repositories/machine-learning.repository.ts +++ b/server/src/repositories/machine-learning.repository.ts @@ -1,8 +1,7 @@ import { Injectable } from '@nestjs/common'; import { Duration } from 'luxon'; import { readFile } from 'node:fs/promises'; -import { MachineLearningConfig } from 'src/config'; -import { CLIPConfig } from 'src/dtos/model-config.dto'; +import { MachineLearningConfig } from 'src/dtos/config.dto'; import { LoggingRepository } from 'src/repositories/logging.repository'; export interface BoundingBox { @@ -205,7 +204,7 @@ export class MachineLearningRepository { }; } - async encodeImage(imagePath: string, { modelName }: CLIPConfig) { + async encodeImage(imagePath: string, { modelName }: MachineLearningConfig['clip']) { const request = { [ModelTask.SEARCH]: { [ModelType.VISUAL]: { modelName } } }; const response = await this.predict({ imagePath }, request); return response[ModelTask.SEARCH]; diff --git a/server/src/repositories/server-info.repository.ts b/server/src/repositories/server-info.repository.ts index 5cfded148d2ad3..4b0e13356d5f8c 100644 --- a/server/src/repositories/server-info.repository.ts +++ b/server/src/repositories/server-info.repository.ts @@ -4,7 +4,7 @@ import { exec as execCallback } from 'node:child_process'; import { readFile } from 'node:fs/promises'; import { promisify } from 'node:util'; import sharp from 'sharp'; -import { ReleaseChannel } from 'src/dtos/system-config.dto'; +import { ReleaseChannel } from 'src/enum'; import { ConfigRepository } from 'src/repositories/config.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; diff --git a/server/src/services/base.service.ts b/server/src/services/base.service.ts index 6ed8ce925a23cf..9204ccea426cc1 100644 --- a/server/src/services/base.service.ts +++ b/server/src/services/base.service.ts @@ -1,10 +1,10 @@ import { BadRequestException, Injectable } from '@nestjs/common'; import { Insertable } from 'kysely'; import sanitize from 'sanitize-filename'; -import { SystemConfig } from 'src/config'; import { SALT_ROUNDS } from 'src/constants'; import { StorageCore } from 'src/cores/storage.core'; import { UserAdmin } from 'src/database'; +import { SystemConfig } from 'src/dtos/config.dto'; import { AccessRepository } from 'src/repositories/access.repository'; import { ActivityRepository } from 'src/repositories/activity.repository'; import { AlbumUserRepository } from 'src/repositories/album-user.repository'; diff --git a/server/src/services/database-backup.service.spec.ts b/server/src/services/database-backup.service.spec.ts index 50263862523099..ac808af8ae34a6 100644 --- a/server/src/services/database-backup.service.spec.ts +++ b/server/src/services/database-backup.service.spec.ts @@ -1,8 +1,8 @@ import { BadRequestException } from '@nestjs/common'; import { DateTime } from 'luxon'; import { PassThrough, Readable } from 'node:stream'; -import { defaults, SystemConfig } from 'src/config'; import { StorageCore } from 'src/cores/storage.core'; +import { defaults, SystemConfig } from 'src/dtos/config.dto'; import { ImmichWorker, JobStatus, StorageFolder } from 'src/enum'; import { MaintenanceHealthRepository } from 'src/maintenance/maintenance-health.repository'; import { DatabaseBackupService } from 'src/services/database-backup.service'; diff --git a/server/src/services/hls.service.ts b/server/src/services/hls.service.ts index 94a8a46acea849..d2f7133c0da46f 100644 --- a/server/src/services/hls.service.ts +++ b/server/src/services/hls.service.ts @@ -5,7 +5,7 @@ import { HLS_SEGMENT_DURATION, HLS_SEGMENT_FILENAME_REGEX, HLS_VARIANTS, HLS_VER import { StorageCore } from 'src/cores/storage.core'; import { OnEvent } from 'src/decorators'; import { AuthDto } from 'src/dtos/auth.dto'; -import { SystemConfigFFmpegDto } from 'src/dtos/system-config.dto'; +import { ConfigFFmpegDto } from 'src/dtos/config.dto'; import { CacheControl, ImmichWorker, Permission } from 'src/enum'; import { ArgOf } from 'src/repositories/event.repository'; import { BaseService } from 'src/services/base.service'; @@ -124,7 +124,7 @@ export class HlsService extends BaseService { this.websocketRepository.serverSend('HlsSessionEnd', { sessionId }); } - private generateMainPlaylist(sessionId: string, ffmpeg: SystemConfigFFmpegDto, asset: AssetWithStreamInfo) { + private generateMainPlaylist(sessionId: string, ffmpeg: ConfigFFmpegDto, asset: AssetWithStreamInfo) { const fps = (asset.packets.packetCount * asset.videoStream.timeBase) / asset.packets.totalDuration; const roundedFps = fps.toFixed(3); const sourceResolution = Math.min(asset.videoStream.height, asset.videoStream.width); diff --git a/server/src/services/library.service.spec.ts b/server/src/services/library.service.spec.ts index ae06232605521a..575de87598d93e 100644 --- a/server/src/services/library.service.spec.ts +++ b/server/src/services/library.service.spec.ts @@ -1,7 +1,7 @@ import { BadRequestException } from '@nestjs/common'; import { Stats } from 'node:fs'; -import { defaults, SystemConfig } from 'src/config'; import { JOBS_LIBRARY_PAGINATION_SIZE } from 'src/constants'; +import { defaults, SystemConfig } from 'src/dtos/config.dto'; import { mapLibrary } from 'src/dtos/library.dto'; import { AssetType, CronJob, ImmichWorker, JobName, JobStatus } from 'src/enum'; import { LibraryService } from 'src/services/library.service'; diff --git a/server/src/services/media.service.spec.ts b/server/src/services/media.service.spec.ts index d64e0088ebd8f3..c9cd395ccc53eb 100644 --- a/server/src/services/media.service.spec.ts +++ b/server/src/services/media.service.spec.ts @@ -1,7 +1,7 @@ import { ShallowDehydrateObject } from 'kysely'; import { OutputInfo } from 'sharp'; -import { SystemConfig } from 'src/config'; import { Exif } from 'src/database'; +import { SystemConfig } from 'src/dtos/config.dto'; import { AssetEditAction } from 'src/dtos/editing.dto'; import { AssetFileType, diff --git a/server/src/services/media.service.ts b/server/src/services/media.service.ts index 19b1688e881a37..0839eba6ada7a6 100644 --- a/server/src/services/media.service.ts +++ b/server/src/services/media.service.ts @@ -1,11 +1,10 @@ import { Injectable } from '@nestjs/common'; -import { SystemConfig } from 'src/config'; import { FACE_THUMBNAIL_SIZE } from 'src/constants'; import { ImagePathOptions, StorageCore, ThumbnailPathEntity } from 'src/cores/storage.core'; import { AssetFile } from 'src/database'; import { OnEvent, OnJob } from 'src/decorators'; +import { ConfigFFmpegDto, SystemConfig } from 'src/dtos/config.dto'; import { AssetEditAction, CropParameters } from 'src/dtos/editing.dto'; -import { SystemConfigFFmpegDto } from 'src/dtos/system-config.dto'; import { AssetFileType, AssetType, @@ -626,7 +625,7 @@ export class MediaService extends BaseService { } private getTranscodeTarget( - config: SystemConfigFFmpegDto, + config: ConfigFFmpegDto, videoStream: VideoStreamInfo, audioStream?: AudioStreamInfo, ): TranscodeTarget { @@ -648,7 +647,7 @@ export class MediaService extends BaseService { return TranscodeTarget.None; } - private isAudioTranscodeRequired(ffmpegConfig: SystemConfigFFmpegDto, stream?: AudioStreamInfo): boolean { + private isAudioTranscodeRequired(ffmpegConfig: ConfigFFmpegDto, stream?: AudioStreamInfo): boolean { if (!stream) { return false; } @@ -671,7 +670,7 @@ export class MediaService extends BaseService { } } - private isVideoTranscodeRequired(ffmpegConfig: SystemConfigFFmpegDto, stream: VideoStreamInfo): boolean { + private isVideoTranscodeRequired(ffmpegConfig: ConfigFFmpegDto, stream: VideoStreamInfo): boolean { const isScalingEnabled = ffmpegConfig.targetResolution !== 'original'; const targetRes = Number.parseInt(ffmpegConfig.targetResolution); const isLargerThanTargetRes = isScalingEnabled && Math.min(stream.height, stream.width) > targetRes; @@ -703,7 +702,7 @@ export class MediaService extends BaseService { } } - private isRemuxRequired(ffmpegConfig: SystemConfigFFmpegDto, { formatName, formatLongName }: VideoFormat): boolean { + private isRemuxRequired(ffmpegConfig: ConfigFFmpegDto, { formatName, formatLongName }: VideoFormat): boolean { if (ffmpegConfig.transcode === TranscodePolicy.Disabled) { return false; } diff --git a/server/src/services/metadata.service.spec.ts b/server/src/services/metadata.service.spec.ts index 57c029961e4aa8..bcd347c56081ac 100644 --- a/server/src/services/metadata.service.spec.ts +++ b/server/src/services/metadata.service.spec.ts @@ -2,7 +2,7 @@ import { BinaryField, ExifDateTime } from 'exiftool-vendored'; import { DateTime } from 'luxon'; import { randomBytes } from 'node:crypto'; import { Stats } from 'node:fs'; -import { defaults } from 'src/config'; +import { defaults } from 'src/dtos/config.dto'; import { AssetFileType, AssetType, diff --git a/server/src/services/notification-admin.service.spec.ts b/server/src/services/notification-admin.service.spec.ts index c2008977194b61..12fb3e71fb2ce9 100644 --- a/server/src/services/notification-admin.service.spec.ts +++ b/server/src/services/notification-admin.service.spec.ts @@ -1,4 +1,4 @@ -import { defaults, SystemConfig } from 'src/config'; +import { defaults, SystemConfig } from 'src/dtos/config.dto'; import { EmailTemplate } from 'src/repositories/email.repository'; import { NotificationService } from 'src/services/notification.service'; import { userStub } from 'test/fixtures/user.stub'; diff --git a/server/src/services/notification-admin.service.ts b/server/src/services/notification-admin.service.ts index 2fc4584dcadad3..63c1da0d2ef24b 100644 --- a/server/src/services/notification-admin.service.ts +++ b/server/src/services/notification-admin.service.ts @@ -1,7 +1,7 @@ import { BadRequestException, Injectable } from '@nestjs/common'; import { AuthDto } from 'src/dtos/auth.dto'; +import { SystemConfigSmtpDto } from 'src/dtos/config.dto'; import { mapNotification, NotificationCreateDto } from 'src/dtos/notification.dto'; -import { SystemConfigSmtpDto } from 'src/dtos/system-config.dto'; import { NotificationLevel, NotificationType } from 'src/enum'; import { EmailTemplate } from 'src/repositories/email.repository'; import { BaseService } from 'src/services/base.service'; diff --git a/server/src/services/notification.service.spec.ts b/server/src/services/notification.service.spec.ts index d6fcefbe6a65cd..47b9b7257ab091 100644 --- a/server/src/services/notification.service.spec.ts +++ b/server/src/services/notification.service.spec.ts @@ -1,5 +1,4 @@ -import { defaults, SystemConfig } from 'src/config'; -import { SystemConfigDto } from 'src/dtos/system-config.dto'; +import { AdminConfigDto, defaults, SystemConfig } from 'src/dtos/config.dto'; import { AssetFileType, JobName, JobStatus, UserMetadataKey } from 'src/enum'; import { NotificationService } from 'src/services/notification.service'; import { AlbumFactory } from 'test/factories/album.factory'; @@ -100,7 +99,7 @@ describe(NotificationService.name, () => { it('skips smtp validation with DTO when there are no changes', async () => { const oldConfig = { ...configs.smtpEnabled }; - const newConfig = configs.smtpEnabled as SystemConfigDto; + const newConfig = configs.smtpEnabled as AdminConfigDto; await expect(sut.onConfigValidate({ oldConfig, newConfig })).resolves.not.toThrow(); expect(mocks.email.verifySmtp).not.toHaveBeenCalled(); diff --git a/server/src/services/notification.service.ts b/server/src/services/notification.service.ts index a650b466baab1c..a341468bb452d2 100644 --- a/server/src/services/notification.service.ts +++ b/server/src/services/notification.service.ts @@ -3,6 +3,7 @@ import { OnEvent, OnJob } from 'src/decorators'; import { MapAlbumDto } from 'src/dtos/album.dto'; import { mapAsset } from 'src/dtos/asset-response.dto'; import { AuthDto } from 'src/dtos/auth.dto'; +import { SystemConfigSmtpDto } from 'src/dtos/config.dto'; import { mapNotification, NotificationDeleteAllDto, @@ -11,7 +12,6 @@ import { NotificationUpdateAllDto, NotificationUpdateDto, } from 'src/dtos/notification.dto'; -import { SystemConfigSmtpDto } from 'src/dtos/system-config.dto'; import { AssetFileType, JobName, diff --git a/server/src/services/queue.service.spec.ts b/server/src/services/queue.service.spec.ts index 5643c5eced84b1..3490c634e6e23f 100644 --- a/server/src/services/queue.service.spec.ts +++ b/server/src/services/queue.service.spec.ts @@ -1,5 +1,5 @@ import { BadRequestException } from '@nestjs/common'; -import { defaults, SystemConfig } from 'src/config'; +import { defaults, SystemConfig } from 'src/dtos/config.dto'; import { ImmichWorker, JobName, QueueCommand, QueueName } from 'src/enum'; import { QueueService } from 'src/services/queue.service'; import { factory } from 'test/small.factory'; diff --git a/server/src/services/queue.service.ts b/server/src/services/queue.service.ts index 1f0bf6c76b7c4e..7130146c15405f 100644 --- a/server/src/services/queue.service.ts +++ b/server/src/services/queue.service.ts @@ -1,7 +1,7 @@ import { BadRequestException, Injectable } from '@nestjs/common'; -import { SystemConfig } from 'src/config'; import { OnEvent } from 'src/decorators'; import { AuthDto } from 'src/dtos/auth.dto'; +import { SystemConfig } from 'src/dtos/config.dto'; import { mapQueueLegacy, mapQueuesLegacy, diff --git a/server/src/services/smart-info.service.spec.ts b/server/src/services/smart-info.service.spec.ts index 6bd0a3c9b2064f..354672a6e59458 100644 --- a/server/src/services/smart-info.service.spec.ts +++ b/server/src/services/smart-info.service.spec.ts @@ -1,4 +1,4 @@ -import { SystemConfig } from 'src/config'; +import { SystemConfig } from 'src/dtos/config.dto'; import { AssetFileType, AssetVisibility, ImmichWorker, JobName, JobStatus } from 'src/enum'; import { SmartInfoService } from 'src/services/smart-info.service'; import { getCLIPModelInfo } from 'src/utils/misc'; diff --git a/server/src/services/smart-info.service.ts b/server/src/services/smart-info.service.ts index 19a17744928f3c..e147d3da7fc3b1 100644 --- a/server/src/services/smart-info.service.ts +++ b/server/src/services/smart-info.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { SystemConfig } from 'src/config'; +import { SystemConfig } from 'src/dtos/config.dto'; import { OnEvent, OnJob } from 'src/decorators'; import { AssetVisibility, DatabaseLock, ImmichWorker, JobName, JobStatus, QueueName } from 'src/enum'; diff --git a/server/src/services/storage-template.service.spec.ts b/server/src/services/storage-template.service.spec.ts index 8f11a1dfa2ee1a..72d8802e1b15c3 100644 --- a/server/src/services/storage-template.service.spec.ts +++ b/server/src/services/storage-template.service.spec.ts @@ -1,5 +1,5 @@ import { Stats } from 'node:fs'; -import { defaults, SystemConfig } from 'src/config'; +import { defaults, SystemConfig } from 'src/dtos/config.dto'; import { AssetPathType, AssetType, JobStatus } from 'src/enum'; import { StorageTemplateService } from 'src/services/storage-template.service'; import { AlbumFactory } from 'test/factories/album.factory'; diff --git a/server/src/services/storage-template.service.ts b/server/src/services/storage-template.service.ts index de731f46c66972..c2aaf986eb738b 100644 --- a/server/src/services/storage-template.service.ts +++ b/server/src/services/storage-template.service.ts @@ -5,7 +5,7 @@ import path from 'node:path'; import sanitize from 'sanitize-filename'; import { StorageCore } from 'src/cores/storage.core'; import { OnEvent, OnJob } from 'src/decorators'; -import { SystemConfigTemplateStorageOptionDto } from 'src/dtos/system-config.dto'; +import { ConfigTemplateStorageOptionDto } from 'src/dtos/config.dto'; import { AssetFileType, AssetPathType, @@ -129,7 +129,7 @@ export class StorageTemplateService extends BaseService { } } - getStorageTemplateOptions(): SystemConfigTemplateStorageOptionDto { + getStorageTemplateOptions(): ConfigTemplateStorageOptionDto { return { ...storageTokens, presetOptions: storagePresets }; } diff --git a/server/src/services/system-config.service.spec.ts b/server/src/services/system-config.service.spec.ts index 08851da96aff94..90d91ccf48cd93 100644 --- a/server/src/services/system-config.service.spec.ts +++ b/server/src/services/system-config.service.spec.ts @@ -1,6 +1,5 @@ import { BadRequestException } from '@nestjs/common'; -import { defaults, SystemConfig } from 'src/config'; -import { ReleaseChannel } from 'src/dtos/system-config.dto'; +import { defaults, SystemConfig } from 'src/dtos/config.dto'; import { AudioCodec, Colorspace, @@ -10,6 +9,7 @@ import { LogLevel, OAuthTokenEndpointAuthMethod, QueueName, + ReleaseChannel, ToneMapping, TranscodeHardwareAcceleration, TranscodePolicy, @@ -269,7 +269,7 @@ describe(SystemConfigService.name, () => { it('should return the default config', () => { mocks.systemMetadata.get.mockResolvedValue(partialConfig); - expect(sut.getDefaults()).toEqual(defaults); + expect(sut.getAdminConfigDefaults()).toEqual(defaults); expect(mocks.systemMetadata.get).not.toHaveBeenCalled(); }); }); @@ -278,7 +278,7 @@ describe(SystemConfigService.name, () => { it('should return the default config', async () => { mocks.systemMetadata.get.mockResolvedValue({}); - await expect(sut.getSystemConfig()).resolves.toEqual(defaults); + await expect(sut.getAdminConfig()).resolves.toEqual(defaults); }); it('should merge the overrides', async () => { @@ -289,14 +289,14 @@ describe(SystemConfigService.name, () => { user: { deleteDelay: 15 }, }); - await expect(sut.getSystemConfig()).resolves.toEqual(updatedConfig); + await expect(sut.getAdminConfig()).resolves.toEqual(updatedConfig); }); it('should load the config from a json file', async () => { mocks.config.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' })); mocks.systemMetadata.readFile.mockResolvedValue(JSON.stringify(partialConfig)); - await expect(sut.getSystemConfig()).resolves.toEqual(updatedConfig); + await expect(sut.getAdminConfig()).resolves.toEqual(updatedConfig); expect(mocks.systemMetadata.readFile).toHaveBeenCalledWith('immich-config.json'); }); @@ -305,7 +305,7 @@ describe(SystemConfigService.name, () => { mocks.config.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' })); mocks.systemMetadata.readFile.mockResolvedValue(JSON.stringify({ ffmpeg: { twoPass: 'false' } })); - await expect(sut.getSystemConfig()).resolves.toMatchObject({ + await expect(sut.getAdminConfig()).resolves.toMatchObject({ ffmpeg: expect.objectContaining({ twoPass: false }), }); }); @@ -314,7 +314,7 @@ describe(SystemConfigService.name, () => { mocks.config.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' })); mocks.systemMetadata.readFile.mockResolvedValue(JSON.stringify({ ffmpeg: { threads: '42' } })); - await expect(sut.getSystemConfig()).resolves.toMatchObject({ + await expect(sut.getAdminConfig()).resolves.toMatchObject({ ffmpeg: expect.objectContaining({ threads: 42 }), }); }); @@ -325,7 +325,7 @@ describe(SystemConfigService.name, () => { JSON.stringify({ library: { scan: { cronExpression: '0 0 */3 * *' } } }), ); - await expect(sut.getSystemConfig()).resolves.toMatchObject({ + await expect(sut.getAdminConfig()).resolves.toMatchObject({ library: { scan: { enabled: true, @@ -339,7 +339,7 @@ describe(SystemConfigService.name, () => { mocks.config.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' })); mocks.systemMetadata.readFile.mockResolvedValue(JSON.stringify({ oauth: { issuerUrl: 'accounts.google.com' } })); - await expect(sut.getSystemConfig()).rejects.toThrow( + await expect(sut.getAdminConfig()).rejects.toThrow( '[oauth.issuerUrl] Issuer URL must be an empty string or a valid URL', ); }); @@ -348,7 +348,7 @@ describe(SystemConfigService.name, () => { mocks.config.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' })); mocks.systemMetadata.readFile.mockResolvedValue(JSON.stringify({ library: { scan: { cronExpression: 'foo' } } })); - await expect(sut.getSystemConfig()).rejects.toThrow('[library.scan.cronExpression] Invalid cron expression'); + await expect(sut.getAdminConfig()).rejects.toThrow('[library.scan.cronExpression] Invalid cron expression'); }); it('should log errors with the config file', async () => { @@ -356,7 +356,7 @@ describe(SystemConfigService.name, () => { mocks.systemMetadata.readFile.mockResolvedValue(`{ "ffmpeg2": true, "ffmpeg2": true }`); - await expect(sut.getSystemConfig()).rejects.toBeInstanceOf(Error); + await expect(sut.getAdminConfig()).rejects.toBeInstanceOf(Error); expect(mocks.systemMetadata.readFile).toHaveBeenCalledWith('immich-config.json'); expect(mocks.logger.error).toHaveBeenCalledTimes(2); @@ -380,7 +380,7 @@ describe(SystemConfigService.name, () => { `; mocks.systemMetadata.readFile.mockResolvedValue(partialConfig); - await expect(sut.getSystemConfig()).resolves.toEqual(updatedConfig); + await expect(sut.getAdminConfig()).resolves.toEqual(updatedConfig); expect(mocks.systemMetadata.readFile).toHaveBeenCalledWith('immich-config.yaml'); }); @@ -389,7 +389,7 @@ describe(SystemConfigService.name, () => { mocks.config.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' })); mocks.systemMetadata.readFile.mockResolvedValue(JSON.stringify({})); - await expect(sut.getSystemConfig()).resolves.toEqual(defaults); + await expect(sut.getAdminConfig()).resolves.toEqual(defaults); expect(mocks.systemMetadata.readFile).toHaveBeenCalledWith('immich-config.json'); }); @@ -399,7 +399,7 @@ describe(SystemConfigService.name, () => { const partialConfig = { machineLearning: { urls: ['immich_machine_learning'] } }; mocks.systemMetadata.readFile.mockResolvedValue(JSON.stringify(partialConfig)); - const config = await sut.getSystemConfig(); + const config = await sut.getAdminConfig(); expect(config.machineLearning.urls).toEqual(['immich_machine_learning']); }); @@ -420,7 +420,7 @@ describe(SystemConfigService.name, () => { const partialConfig = { server: { externalDomain } }; mocks.systemMetadata.readFile.mockResolvedValue(JSON.stringify(partialConfig)); - const config = await sut.getSystemConfig(); + const config = await sut.getAdminConfig(); expect(config.server.externalDomain).toEqual(result ?? 'https://demo.immich.app'); }); } @@ -432,7 +432,7 @@ describe(SystemConfigService.name, () => { `; mocks.systemMetadata.readFile.mockResolvedValue(partialConfig); - await sut.getSystemConfig(); + await sut.getAdminConfig(); expect(mocks.logger.warn).toHaveBeenCalled(); }); @@ -467,12 +467,12 @@ describe(SystemConfigService.name, () => { mocks.systemMetadata.readFile.mockResolvedValue(JSON.stringify(test.config)); if (test.throws) { - await expect(sut.getSystemConfig()).rejects.toThrow(test.throws); + await expect(sut.getAdminConfig()).rejects.toThrow(test.throws); } else if (test.warn) { - await sut.getSystemConfig(); + await sut.getAdminConfig(); expect(mocks.logger.warn).toHaveBeenCalled(); } else { - const config = await sut.getSystemConfig(); + const config = await sut.getAdminConfig(); test.check!(config); } }); @@ -482,7 +482,7 @@ describe(SystemConfigService.name, () => { describe('updateConfig', () => { it('should update the config and emit an event', async () => { mocks.systemMetadata.get.mockResolvedValue(partialConfig); - await expect(sut.updateSystemConfig(updatedConfig)).resolves.toEqual(updatedConfig); + await expect(sut.updateAdminConfig(updatedConfig)).resolves.toEqual(updatedConfig); expect(mocks.event.emit).toHaveBeenCalledWith( 'ConfigUpdate', expect.objectContaining({ oldConfig: expect.any(Object), newConfig: updatedConfig }), @@ -492,7 +492,7 @@ describe(SystemConfigService.name, () => { it('should throw an error if a config file is in use', async () => { mocks.config.getEnv.mockReturnValue(mockEnvData({ configFile: 'immich-config.json' })); mocks.systemMetadata.readFile.mockResolvedValue(JSON.stringify({})); - await expect(sut.updateSystemConfig(defaults)).rejects.toBeInstanceOf(BadRequestException); + await expect(sut.updateAdminConfig(defaults)).rejects.toBeInstanceOf(BadRequestException); expect(mocks.systemMetadata.set).not.toHaveBeenCalled(); }); }); diff --git a/server/src/services/system-config.service.ts b/server/src/services/system-config.service.ts index faa4f8d423bab9..055a21f1c5044f 100644 --- a/server/src/services/system-config.service.ts +++ b/server/src/services/system-config.service.ts @@ -1,8 +1,15 @@ import { BadRequestException, Injectable } from '@nestjs/common'; import _ from 'lodash'; -import { defaults } from 'src/config'; import { OnEvent } from 'src/decorators'; -import { mapConfig, SystemConfigDto } from 'src/dtos/system-config.dto'; +import { + AdminConfigDto, + defaults, + mapAdminConfig, + mapPublicConfig, + mapUserConfig, + PublicConfigDto, + UserConfigDto, +} from 'src/dtos/config.dto'; import { BootstrapEventPriority } from 'src/enum'; import { ArgOf } from 'src/repositories/event.repository'; import { BaseService } from 'src/services/base.service'; @@ -22,13 +29,31 @@ export class SystemConfigService extends BaseService { this.machineLearningRepository.teardown(); } - async getSystemConfig(): Promise { + async getAdminConfig(): Promise { const config = await this.getConfig({ withCache: false }); - return mapConfig(config); + return mapAdminConfig(config); } - getDefaults(): SystemConfigDto { - return mapConfig(defaults); + getAdminConfigDefaults(): AdminConfigDto { + return mapAdminConfig(defaults); + } + + async getUserConfig(): Promise { + const config = await this.getConfig({ withCache: false }); + return mapUserConfig(config); + } + + getUserConfigDefaults(): UserConfigDto { + return mapUserConfig(defaults); + } + + async getPublicConfig(): Promise { + const config = await this.getConfig({ withCache: false }); + return mapPublicConfig(config); + } + + getPublicConfigDefaults(): PublicConfigDto { + return mapPublicConfig(defaults); } @OnEvent({ name: 'ConfigInit', priority: -100 }) @@ -56,7 +81,7 @@ export class SystemConfigService extends BaseService { } } - async updateSystemConfig(dto: SystemConfigDto): Promise { + async updateAdminConfig(dto: AdminConfigDto): Promise { const { configFile } = this.configRepository.getEnv(); if (configFile) { throw new BadRequestException('Cannot update configuration while IMMICH_CONFIG_FILE is in use'); @@ -75,7 +100,7 @@ export class SystemConfigService extends BaseService { await this.eventRepository.emit('ConfigUpdate', { newConfig, oldConfig }); - return mapConfig(newConfig); + return mapAdminConfig(newConfig); } async getCustomCss(): Promise { diff --git a/server/src/services/version.service.spec.ts b/server/src/services/version.service.spec.ts index 0044730ceeb227..b5fc6764f21944 100644 --- a/server/src/services/version.service.spec.ts +++ b/server/src/services/version.service.spec.ts @@ -1,8 +1,7 @@ import { DateTime } from 'luxon'; import { SemVer } from 'semver'; -import { defaults } from 'src/config'; -import { ReleaseChannel } from 'src/dtos/system-config.dto'; -import { CronJob, JobName, JobStatus, SystemMetadataKey } from 'src/enum'; +import { defaults } from 'src/dtos/config.dto'; +import { CronJob, JobName, JobStatus, ReleaseChannel, SystemMetadataKey } from 'src/enum'; import { VersionService } from 'src/services/version.service'; import { factory } from 'test/small.factory'; import { newTestService, ServiceMocks } from 'test/utils'; diff --git a/server/src/services/version.service.ts b/server/src/services/version.service.ts index f1abeedb30ab0e..85bf579fbdb217 100644 --- a/server/src/services/version.service.ts +++ b/server/src/services/version.service.ts @@ -4,8 +4,16 @@ import semver, { SemVer } from 'semver'; import { serverVersion } from 'src/constants'; import { OnEvent, OnJob } from 'src/decorators'; import { ReleaseEventV1, ReleaseType, ServerVersionResponseDto } from 'src/dtos/server.dto'; -import { ReleaseChannel } from 'src/dtos/system-config.dto'; -import { CronJob, DatabaseLock, ImmichWorker, JobName, JobStatus, QueueName, SystemMetadataKey } from 'src/enum'; +import { + CronJob, + DatabaseLock, + ImmichWorker, + JobName, + JobStatus, + QueueName, + ReleaseChannel, + SystemMetadataKey, +} from 'src/enum'; import { ArgOf } from 'src/repositories/event.repository'; import { BaseService } from 'src/services/base.service'; import { VersionCheckMetadata } from 'src/types'; diff --git a/server/src/types.ts b/server/src/types.ts index d31841ffd320a4..3b5a937245dbac 100644 --- a/server/src/types.ts +++ b/server/src/types.ts @@ -1,9 +1,9 @@ import { ShallowDehydrateObject } from 'kysely'; -import { SystemConfig } from 'src/config'; import { VECTOR_EXTENSIONS } from 'src/constants'; import { AssetFile } from 'src/database'; import { UploadFieldName } from 'src/dtos/asset-media.dto'; import { AuthDto } from 'src/dtos/auth.dto'; +import { SystemConfig } from 'src/dtos/config.dto'; import { AssetEditActionItem } from 'src/dtos/editing.dto'; import { SetMaintenanceModeDto } from 'src/dtos/maintenance.dto'; import { @@ -34,10 +34,10 @@ import { Mocked } from 'vitest'; export type DeepPartial = T extends Date ? T - : T extends Record - ? { [K in keyof T]?: DeepPartial } - : T extends Array - ? DeepPartial[] + : T extends Array + ? DeepPartial[] + : T extends object + ? { [K in keyof T]?: DeepPartial } : T; export type RepositoryInterface = Pick; diff --git a/server/src/utils/config.ts b/server/src/utils/config.ts index a6073471d16fe4..38e5facb03daca 100644 --- a/server/src/utils/config.ts +++ b/server/src/utils/config.ts @@ -1,8 +1,7 @@ import AsyncLock from 'async-lock'; import { load as loadYaml } from 'js-yaml'; import * as _ from 'lodash'; -import { SystemConfig, defaults } from 'src/config'; -import { SystemConfigSchema } from 'src/dtos/system-config.dto'; +import { AdminConfigDto, SystemConfig, defaults } from 'src/dtos/config.dto'; import { DatabaseLock, SystemMetadataKey } from 'src/enum'; import { ConfigRepository } from 'src/repositories/config.repository'; import { LoggingRepository } from 'src/repositories/logging.repository'; @@ -100,7 +99,7 @@ const buildConfig = async (repos: RepoDeps) => { } // validate with Zod schema - const result = SystemConfigSchema.safeParse(rawConfig); + const result = AdminConfigDto.schema.safeParse(rawConfig); if (!result.success) { const messages = ['Invalid system config: ']; for (const issue of result.error.issues) { diff --git a/server/src/utils/media.ts b/server/src/utils/media.ts index 656e5bf441050c..49877904ec1246 100644 --- a/server/src/utils/media.ts +++ b/server/src/utils/media.ts @@ -1,5 +1,5 @@ import { AUDIO_ENCODER, AV1_LEVELS, CodecLevel, H264_LEVELS, HEVC_LEVELS, SUPPORTED_HWA_CODECS } from 'src/constants'; -import { SystemConfigFFmpegDto } from 'src/dtos/system-config.dto'; +import { ConfigFFmpegDto } from 'src/dtos/config.dto'; import { ColorMatrix, ColorPrimaries, @@ -62,18 +62,18 @@ export const getCodecString = (codec: VideoCodec, width: number, height: number, export class BaseConfig implements VideoCodecSWConfig { readonly presets = ['veryslow', 'slower', 'slow', 'medium', 'fast', 'faster', 'veryfast', 'superfast', 'ultrafast']; protected constructor( - protected config: SystemConfigFFmpegDto, + protected config: ConfigFFmpegDto, protected tune: VideoTuning = { strictGop: false, lowLatency: false }, ) {} - static create(config: SystemConfigFFmpegDto, interfaces: VideoInterfaces, tune?: VideoTuning) { + static create(config: ConfigFFmpegDto, interfaces: VideoInterfaces, tune?: VideoTuning) { if (config.accel === TranscodeHardwareAcceleration.Disabled) { return BaseConfig.getSWCodecConfig(config, tune); } return BaseConfig.getHWCodecConfig(config, interfaces, tune); } - private static getSWCodecConfig(config: SystemConfigFFmpegDto, tune?: VideoTuning): VideoCodecSWConfig { + private static getSWCodecConfig(config: ConfigFFmpegDto, tune?: VideoTuning): VideoCodecSWConfig { switch (config.targetVideoCodec) { case VideoCodec.H264: { return new H264Config(config, tune); @@ -93,7 +93,7 @@ export class BaseConfig implements VideoCodecSWConfig { } } - private static getHWCodecConfig(config: SystemConfigFFmpegDto, interfaces: VideoInterfaces, tune?: VideoTuning) { + private static getHWCodecConfig(config: ConfigFFmpegDto, interfaces: VideoInterfaces, tune?: VideoTuning) { if (!SUPPORTED_HWA_CODECS[config.accel].includes(config.targetVideoCodec)) { throw new Error( `${config.accel.toUpperCase()} acceleration does not support codec '${config.targetVideoCodec.toUpperCase()}'. Supported codecs: ${SUPPORTED_HWA_CODECS[config.accel]}`, @@ -424,7 +424,7 @@ export class BaseHWConfig extends BaseConfig { protected device: string; constructor( - protected config: SystemConfigFFmpegDto, + protected config: ConfigFFmpegDto, protected interfaces: VideoInterfaces, tune?: VideoTuning, ) { @@ -471,7 +471,7 @@ export class BaseHWConfig extends BaseConfig { } export class ThumbnailConfig extends BaseConfig { - static create(config: SystemConfigFFmpegDto): VideoCodecSWConfig { + static create(config: ConfigFFmpegDto): VideoCodecSWConfig { return new ThumbnailConfig(config); } diff --git a/server/src/utils/misc.ts b/server/src/utils/misc.ts index a514007987620d..84ebc7e980a654 100644 --- a/server/src/utils/misc.ts +++ b/server/src/utils/misc.ts @@ -13,9 +13,9 @@ import { writeFileSync } from 'node:fs'; import path from 'node:path'; import picomatch from 'picomatch'; import parse from 'picomatch/lib/parse'; -import { SystemConfig } from 'src/config'; import { CLIP_MODEL_INFO, JOBS_ASSET_PAGINATION_SIZE, endpointTags, serverVersion } from 'src/constants'; import { extraModels } from 'src/decorators'; +import { SystemConfig } from 'src/dtos/config.dto'; import { ApiCustomExtension, ImmichCookie, ImmichHeader, MetadataKey } from 'src/enum'; import { LoggingRepository } from 'src/repositories/logging.repository'; diff --git a/server/src/utils/profile-image.ts b/server/src/utils/profile-image.ts index ee94dd898625a9..ea2c0d3b6b063c 100644 --- a/server/src/utils/profile-image.ts +++ b/server/src/utils/profile-image.ts @@ -1,6 +1,6 @@ import { join } from 'node:path'; -import { SystemConfig } from 'src/config'; import { StorageCore } from 'src/cores/storage.core'; +import { SystemConfig } from 'src/dtos/config.dto'; import { StorageFolder } from 'src/enum'; import { CryptoRepository } from 'src/repositories/crypto.repository'; import { MediaRepository } from 'src/repositories/media.repository'; diff --git a/server/test/fixtures/system-config.stub.ts b/server/test/fixtures/system-config.stub.ts index 355f2cc1a39c3c..6ba65569210e04 100644 --- a/server/test/fixtures/system-config.stub.ts +++ b/server/test/fixtures/system-config.stub.ts @@ -1,4 +1,4 @@ -import { SystemConfig } from 'src/config'; +import { SystemConfig } from 'src/dtos/config.dto'; import { DeepPartial } from 'src/types'; export const systemConfigStub = { diff --git a/server/test/medium.factory.ts b/server/test/medium.factory.ts index 1fbe41d964e69e..09cef8947cf5fa 100644 --- a/server/test/medium.factory.ts +++ b/server/test/medium.factory.ts @@ -4,9 +4,9 @@ import { createHash, randomBytes } from 'node:crypto'; import { Stats } from 'node:fs'; import { resolve } from 'node:path'; import { Writable } from 'node:stream'; -import { SystemConfig } from 'src/config'; import { AssetFace } from 'src/database'; import { AuthDto, LoginResponseDto } from 'src/dtos/auth.dto'; +import { SystemConfig } from 'src/dtos/config.dto'; import { AssetEditActionItem, AssetEditsCreateDto } from 'src/dtos/editing.dto'; import { AlbumUserRole, diff --git a/server/test/utils.ts b/server/test/utils.ts index 3bdbe1f5d511f0..f8d3c15e221428 100644 --- a/server/test/utils.ts +++ b/server/test/utils.ts @@ -95,7 +95,9 @@ export type ControllerContext = { close: () => Promise; }; -export const controllerSetup = async (controller: new (...args: any[]) => unknown, providers: Provider[]) => { +type ControllerClass = new (...args: any[]) => unknown; + +export const controllerSetup = async (controller: ControllerClass | ControllerClass[], providers: Provider[]) => { const noopInterceptor = { intercept: (ctx: never, next: CallHandler) => next.handle() }; const upload = multer({ storage: multer.memoryStorage() }); const memoryFileInterceptor = { @@ -117,7 +119,7 @@ export const controllerSetup = async (controller: new (...args: any[]) => unknow }, }; const moduleRef = await Test.createTestingModule({ - controllers: [controller], + controllers: Array.isArray(controller) ? controller : [controller], providers: [ { provide: APP_FILTER, useClass: GlobalExceptionFilter }, { provide: APP_PIPE, useClass: ZodValidationPipe }, diff --git a/web/src/lib/components/shared-components/settings/SystemConfigButtonRow.svelte b/web/src/lib/components/shared-components/settings/SystemConfigButtonRow.svelte index da58d779e9768c..3948b6bbc2d85c 100644 --- a/web/src/lib/components/shared-components/settings/SystemConfigButtonRow.svelte +++ b/web/src/lib/components/shared-components/settings/SystemConfigButtonRow.svelte @@ -1,15 +1,15 @@ -{#if !authManager.isSharedLink && isOwner} +{#if !authManager.isSharedLink}
-
- {$t('people')} -
- {#if people.some((person) => person.isHidden)} - assetViewerManager.toggleHiddenPeople()} - /> - {/if} - assetViewerManager.toggleFaceEditMode()} - /> + {#if isOwner || visiblePeople.length > 0} +
+ {$t('people')} +
+ {#if isOwner} + {#if people.some((person) => person.isHidden)} + assetViewerManager.toggleHiddenPeople()} + /> + {/if} + assetViewerManager.toggleFaceEditMode()} + /> - {#if faceManager.data.length > 0} - assetViewerManager.openEditFacesPanel()} - /> - {/if} + {#if faceManager.data.length > 0} + assetViewerManager.openEditFacesPanel()} + /> + {/if} + {/if} +
-
+ {/if}
{#each visiblePeople as person (person.id)} diff --git a/web/src/lib/components/shared-components/navigation-bar/NotificationPanel.svelte b/web/src/lib/components/shared-components/navigation-bar/NotificationPanel.svelte index 79cfff858a5bf4..4e6929a87ef522 100644 --- a/web/src/lib/components/shared-components/navigation-bar/NotificationPanel.svelte +++ b/web/src/lib/components/shared-components/navigation-bar/NotificationPanel.svelte @@ -2,6 +2,8 @@ import { goto } from '$app/navigation'; import { focusTrap } from '$lib/actions/focus-trap'; import NotificationItem from '$lib/components/shared-components/navigation-bar/NotificationItem.svelte'; + import { OpenQueryParam } from '$lib/constants'; + import { Route } from '$lib/route'; import { notificationManager } from '$lib/stores/notification-manager.svelte'; import { handleError } from '$lib/utils/handle-error'; import { NotificationType, type NotificationDto } from '@immich/sdk'; @@ -50,6 +52,11 @@ break; } + case NotificationType.ClusterGroupRequest: { + await goto(Route.userSettings({ isOpen: OpenQueryParam.SHARING })); + break; + } + default: { break; } diff --git a/web/src/lib/constants.ts b/web/src/lib/constants.ts index 802f3bc63be246..530db1e8d58676 100644 --- a/web/src/lib/constants.ts +++ b/web/src/lib/constants.ts @@ -67,6 +67,7 @@ export enum OpenQueryParam { STORAGE_TEMPLATE = 'storage-template', NOTIFICATIONS = 'notifications', PURCHASE_SETTINGS = 'user-purchase-settings', + SHARING = 'sharing', } export const maximumLengthSearchPeople = 100; diff --git a/web/src/lib/modals/ClusterGroupUserSelectionModal.svelte b/web/src/lib/modals/ClusterGroupUserSelectionModal.svelte new file mode 100644 index 00000000000000..7ee46adfbee851 --- /dev/null +++ b/web/src/lib/modals/ClusterGroupUserSelectionModal.svelte @@ -0,0 +1,60 @@ + + + + + {#await loadUsers()} +
+ +
+ {:then _} + {#if availableUsers.length > 0} +
+ {#each availableUsers as user (user.id)} + selectUser(user)} selected={selectedUsers.some(({ id }) => id === user.id)}> + +
+ {user.name} + {user.email} +
+
+ {/each} +
+ + + + + {:else} + {$t('partner_page_no_more_users')} + {/if} + {/await} +
+
diff --git a/web/src/lib/modals/ClusterGroupUsersModal.svelte b/web/src/lib/modals/ClusterGroupUsersModal.svelte new file mode 100644 index 00000000000000..2bfb61257c17d8 --- /dev/null +++ b/web/src/lib/modals/ClusterGroupUsersModal.svelte @@ -0,0 +1,54 @@ + + + + + {#await loadUsers()} +
+ +
+ {:then _} + {$t('cluster_group_invite_description')} + +
+ {$t('users')} +
+ +
+ {#each users as user (user.id)} +
+ +
+ {user.name} + {user.email} +
+
+ {/each} +
+ {/await} +
+ + + + + + + +
diff --git a/web/src/routes/(user)/user-settings/PartnerSettings.svelte b/web/src/routes/(user)/user-settings/PartnerSettings.svelte deleted file mode 100644 index af1eff38bfb64d..00000000000000 --- a/web/src/routes/(user)/user-settings/PartnerSettings.svelte +++ /dev/null @@ -1,194 +0,0 @@ - - -
- {#if partners.length > 0} - {#each partners as partner (partner.user.id)} -
-
-
- -
-

- {partner.user.name} -

-

- {partner.user.email} -

-
-
- - {#if partner.sharedByMe} - handleRemovePartner(partner.user)} - icon={mdiClose} - size="small" - aria-label={$t('stop_sharing_photos_with_user')} - /> - {/if} -
- -
- - {#if partner.sharedByMe} -
- - {$t('shared_with_partner', { values: { partner: partner.user.name } })} - - {$t('partner_can_access', { values: { partner: partner.user.name } })} -
    -
  • - - {$t('partner_can_access_assets')} -
  • -
  • - - {$t('partner_can_access_location')} -
  • -
- {/if} - - - {#if partner.sharedWithMe} -
- - {$t('shared_from_partner', { values: { partner: partner.user.name } })} - - - handleShowOnTimelineChanged(partner, isChecked)} - /> - {/if} -
-
- {/each} - {/if} - -
- -
-
diff --git a/web/src/routes/(user)/user-settings/SharingSettings.svelte b/web/src/routes/(user)/user-settings/SharingSettings.svelte new file mode 100644 index 00000000000000..8e0ea599385480 --- /dev/null +++ b/web/src/routes/(user)/user-settings/SharingSettings.svelte @@ -0,0 +1,379 @@ + + +
+ {$t('cluster_group')} + {$t('cluster_group_description')} + + + + {#each users as user, index (user.id)} +
0}> +
+ +
+

+ {user.name} + {#if user.id === authManager.user.id} + ({$t('you')}) + {/if} +

+

{user.email}

+
+
+ + {#if user.id === authManager.user.id && canLeave} + + {/if} +
+ {/each} +
+
+ + {#if sentRequests.length > 0 || receivedRequests.length > 0} +
+ {$t('pending')} +
+ + + + {#each receivedRequests as request, index (request.id)} +
0}> + {$t('request_received_description')} +
+ +
+
+ {/each} + + {#each sentRequests as request, index (request.id)} + {@const user = candidates[request.userId]} +
0 || receivedRequests.length > 0}> +
+ {#if user} + + {/if} +
+

{user?.name ?? request.userId}

+

{user?.email ?? ''}

+
+
+ + +
+ {/each} +
+
+ {/if} + +
+ +
+
+ +
+ {$t('partners')} + + {#if partners.length > 0} + {#each partners as partner (partner.user.id)} +
+
+
+ +
+

+ {partner.user.name} +

+

+ {partner.user.email} +

+
+
+ + {#if partner.sharedByMe} + handleRemovePartner(partner.user)} + icon={mdiClose} + size="small" + aria-label={$t('stop_sharing_photos_with_user')} + /> + {/if} +
+ +
+ + {#if partner.sharedByMe} +
+ + {$t('shared_with_partner', { values: { partner: partner.user.name } })} + + {$t('partner_can_access', { values: { partner: partner.user.name } })} +
    +
  • + + {$t('partner_can_access_assets')} +
  • +
  • + + {$t('partner_can_access_location')} +
  • +
+ {/if} + + + {#if partner.sharedWithMe} +
+ + {$t('shared_from_partner', { values: { partner: partner.user.name } })} + + + handleShowOnTimelineChanged(partner, isChecked)} + /> + {/if} +
+
+ {/each} + {/if} + +
+ +
+
diff --git a/web/src/routes/(user)/user-settings/UserSettingsList.svelte b/web/src/routes/(user)/user-settings/UserSettingsList.svelte index 91c32560d8479c..b6595ed60f8a87 100644 --- a/web/src/routes/(user)/user-settings/UserSettingsList.svelte +++ b/web/src/routes/(user)/user-settings/UserSettingsList.svelte @@ -31,7 +31,7 @@ import ChangePasswordSettings from './ChangePasswordSettings.svelte'; import DeviceList from './DeviceList.svelte'; import OauthSettings from './OauthSettings.svelte'; - import PartnerSettings from './PartnerSettings.svelte'; + import SharingSettings from './SharingSettings.svelte'; import UserApiKeyList from './UserApiKeyList.svelte'; import UserProfileSettings from './UserProfileSettings.svelte'; @@ -129,15 +129,6 @@ - - - - + + + + diff --git a/web/src/test-data/factories/user-factory.ts b/web/src/test-data/factories/user-factory.ts index 7b56c275e25f3f..edc1ead1ce9109 100644 --- a/web/src/test-data/factories/user-factory.ts +++ b/web/src/test-data/factories/user-factory.ts @@ -4,6 +4,7 @@ import { Sync } from 'factory.ts'; export const userAdminFactory = Sync.makeFactory({ id: Sync.each(() => faker.string.uuid()), + clusterGroupId: Sync.each(() => faker.string.uuid()), email: Sync.each(() => faker.internet.email()), name: Sync.each(() => faker.person.fullName()), profileImagePath: '', From c8199ef32fe68fa481b557624f32c021b26dd9f3 Mon Sep 17 00:00:00 2001 From: Adam Gastineau Date: Thu, 20 Aug 2026 11:17:05 -0700 Subject: [PATCH 6/6] chore(mobile): update flutter-maplibre-gl to 0.27.0 (#30892) --- .../xcshareddata/swiftpm/Package.resolved | 4 ++-- mobile/pubspec.lock | 12 ++++++------ mobile/pubspec.yaml | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/mobile/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved b/mobile/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved index da0f59f87df580..4cf0fac8900e51 100644 --- a/mobile/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/mobile/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/maplibre/maplibre-gl-native-distribution.git", "state" : { - "revision" : "84a79bc375a301169390ac110c868f06c857b83f", - "version" : "6.27.0" + "revision" : "5ee345ca5d65238a6fce29bba87816204be7df20", + "version" : "6.28.0" } }, { diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 34676069dd4596..65cd03c3ae3406 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -1072,26 +1072,26 @@ packages: dependency: "direct main" description: name: maplibre_gl - sha256: b676f124a2fcf88c4dafedc7b462155e6396d61ce7af46354b4de11b45c9812f + sha256: "8b24dce6a050ba44779154c550e0318654ee12c572fa5fd1ad0c12e90d061356" url: "https://pub.dev" source: hosted - version: "0.26.2" + version: "0.27.0" maplibre_gl_platform_interface: dependency: transitive description: name: maplibre_gl_platform_interface - sha256: "1f0ca8a99f03fa9434618ee21f4e42dd615830a7dd973e632b11c73ffec993a8" + sha256: "8a1952b77ce841162fcd2601dc857f5b3f8eea46d34025b901ef803bb15d2adc" url: "https://pub.dev" source: hosted - version: "0.26.2" + version: "0.27.0" maplibre_gl_web: dependency: transitive description: name: maplibre_gl_web - sha256: bbf022f29ceef26d73f63e584819fbd02fbaf4eb1facf5234206c78936cf8f1c + sha256: c9c7bb1183cc9c6baafcef0b5b741420db869ca45e474ca80edf732168a286ee url: "https://pub.dev" source: hosted - version: "0.26.2" + version: "0.27.0" matcher: dependency: transitive description: diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 5a83ad7556e9db..9bda1b20ffd52d 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -44,7 +44,7 @@ dependencies: intl: ^0.20.2 local_auth: ^2.3.0 logging: ^1.3.0 - maplibre_gl: ^0.26.0 + maplibre_gl: ^0.27.0 native_video_player: git: url: https://github.com/immich-app/native_video_player