@lucidcms/core
Version:
The core of the Lucid CMS. It's responsible for spinning up the API and serving the CMS.
1 lines • 24.7 kB
Source Map (JSON)
{"version":3,"file":"update-single.mjs","names":["MediaTranslationsRepository","formatter","mediaServices.checks.checkMediaKeyAccess","mediaServices.checks.checkAwaitingSync","mediaServices.strategies.update","constants","mediaServices.strategies.rename","processedImageServices.clearSingle"],"sources":["../../../src/services/media/update-single.ts"],"sourcesContent":["import constants from \"../../constants/constants.js\";\nimport type { LucidMedia, Update } from \"../../libs/db/types.js\";\nimport formatter from \"../../libs/formatters/index.js\";\nimport executeHooks from \"../../libs/hooks/execute-hooks.js\";\nimport { copy } from \"../../libs/i18n/index.js\";\nimport cacheKeys from \"../../libs/kv/cache-keys.js\";\nimport { invalidateHttpCacheTags } from \"../../libs/kv/http-cache.js\";\nimport {\n\tMediaAwaitingSyncRepository,\n\tMediaRepository,\n\tMediaTranslationsRepository,\n} from \"../../libs/repositories/index.js\";\nimport type {\n\tMediaCropInput,\n\tMediaOrigin,\n\tMediaType,\n} from \"../../types/response.js\";\nimport changeKeyVisibility from \"../../utils/media/change-key-visibility.js\";\nimport getKeyVisibility from \"../../utils/media/get-key-visibility.js\";\nimport type { ServiceFn } from \"../../utils/services/types.js\";\nimport { mediaServices, processedImageServices } from \"../index.js\";\nimport checkFolderAccess from \"../media-folders/checks/check-folder-access.js\";\nimport checkFolderTenantCompatibility from \"./helpers/check-folder-tenant-compatibility.js\";\nimport clearClientMediaSingleCache from \"./helpers/clear-client-media-cache.js\";\nimport deactivateCrop from \"./helpers/deactivate-crop.js\";\nimport permanentlyDeleteMedia from \"./helpers/permanently-delete-media.js\";\nimport prepareMediaTranslations from \"./helpers/prepare-media-translations.js\";\nimport resolveAiGeneration from \"./helpers/resolve-ai-generation.js\";\nimport resolvePoster from \"./helpers/resolve-poster.js\";\nimport syncOwnedVisibility from \"./helpers/sync-owned-visibility.js\";\nimport upsertCrop from \"./helpers/upsert-crop.js\";\n\nconst updateSingle: ServiceFn<\n\t[\n\t\t{\n\t\t\tid: number;\n\t\t\tkey?: string;\n\t\t\tfileName?: string;\n\t\t\tpublic?: boolean;\n\t\t\tfolderId?: number | null;\n\t\t\ttitle?: {\n\t\t\t\tlocaleCode: string;\n\t\t\t\tvalue: string | null;\n\t\t\t}[];\n\t\t\talt?: {\n\t\t\t\tlocaleCode: string;\n\t\t\t\tvalue: string | null;\n\t\t\t}[];\n\t\t\tdescription?: {\n\t\t\t\tlocaleCode: string;\n\t\t\t\tvalue: string | null;\n\t\t\t}[];\n\t\t\tsummary?: {\n\t\t\t\tlocaleCode: string;\n\t\t\t\tvalue: string | null;\n\t\t\t}[];\n\t\t\twidth?: number | null;\n\t\t\theight?: number | null;\n\t\t\tfocalPoint?: {\n\t\t\t\tx: number;\n\t\t\t\ty: number;\n\t\t\t} | null;\n\t\t\tblurHash?: string | null;\n\t\t\taverageColor?: string | null;\n\t\t\tbase64?: string | null;\n\t\t\tisDark?: boolean | null;\n\t\t\tisLight?: boolean | null;\n\t\t\tisDeleted?: boolean;\n\t\t\tposterId?: number | null;\n\t\t\tcrop?: MediaCropInput | null;\n\t\t\torigin?: MediaOrigin;\n\t\t\taiGenerationRequestId?: string;\n\t\t\tallowedType?: MediaType;\n\t\t\tuserId: number;\n\t\t},\n\t],\n\tnumber | undefined\n> = async (context, data) => {\n\tconst Media = new MediaRepository(context.db.client, context.config.db);\n\tconst MediaTranslations = new MediaTranslationsRepository(\n\t\tcontext.db.client,\n\t\tcontext.config.db,\n\t);\n\tconst MediaAwaitingSync = new MediaAwaitingSyncRepository(\n\t\tcontext.db.client,\n\t\tcontext.config.db,\n\t);\n\n\tconst folderAccessRes = await checkFolderAccess(context, {\n\t\tfolderId: data.folderId,\n\t});\n\tif (folderAccessRes.error) return folderAccessRes;\n\n\tconst mediaRes = await Media.selectSingleById({\n\t\tid: data.id,\n\t\ttenantKey: context.request.tenantKey,\n\t\tincludeOwned: true,\n\t\tvalidation: {\n\t\t\tenabled: true,\n\t\t\tdefaultError: {\n\t\t\t\tmessage: copy(\"server:core.media.not.found.message\"),\n\t\t\t\tstatus: 404,\n\t\t\t},\n\t\t},\n\t});\n\tif (mediaRes.error) return mediaRes;\n\tif (mediaRes.data.relation_type === \"crop\") {\n\t\treturn {\n\t\t\terror: {\n\t\t\t\ttype: \"basic\",\n\t\t\t\tstatus: 404,\n\t\t\t\tmessage: copy(\"server:core.media.not.found.message\"),\n\t\t\t},\n\t\t\tdata: undefined,\n\t\t};\n\t}\n\tif (data.crop && mediaRes.data.type !== \"image\") {\n\t\treturn {\n\t\t\terror: {\n\t\t\t\ttype: \"basic\",\n\t\t\t\tstatus: 400,\n\t\t\t\tmessage: copy(\"server:core.media.errors.image.only\"),\n\t\t\t},\n\t\t\tdata: undefined,\n\t\t};\n\t}\n\n\tconst folderTenantRes = checkFolderTenantCompatibility({\n\t\tfolderId: data.folderId,\n\t\tfolderTenantKey: folderAccessRes.data?.tenant_key ?? null,\n\t\tmediaTenantKey: mediaRes.data.tenant_key,\n\t});\n\tif (folderTenantRes.error) return folderTenantRes;\n\n\tif (data.focalPoint !== undefined && mediaRes.data.type !== \"image\") {\n\t\treturn {\n\t\t\terror: {\n\t\t\t\ttype: \"basic\",\n\t\t\t\tstatus: 400,\n\t\t\t\terrors: {\n\t\t\t\t\tfocalPoint: {\n\t\t\t\t\t\tcode: \"media_error\",\n\t\t\t\t\t\tmessage: copy(\"server:core.media.errors.focal.point.image.only\"),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tdata: undefined,\n\t\t};\n\t}\n\n\tif (data.posterId !== undefined && data.posterId !== null) {\n\t\tconst posterRes = await resolvePoster(context, {\n\t\t\tposterId: data.posterId,\n\t\t\tparentId: data.id,\n\t\t});\n\t\tif (posterRes.error) return posterRes;\n\t}\n\n\tif (data.key !== undefined && data.fileName === undefined) {\n\t\treturn {\n\t\t\terror: {\n\t\t\t\ttype: \"basic\",\n\t\t\t\tstatus: 400,\n\t\t\t\terrors: {\n\t\t\t\t\tfile: {\n\t\t\t\t\t\tcode: \"media_error\",\n\t\t\t\t\t\tmessage: copy(\"server:core.media.errors.missing.file.name\"),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tdata: undefined,\n\t\t};\n\t}\n\n\tlet updateObjectRes: Awaited<\n\t\tReturnType<typeof mediaServices.strategies.update>\n\t>[\"data\"];\n\n\tlet renamedKey: string | undefined;\n\tconst currentPublic = formatter.formatBoolean(mediaRes.data.public);\n\tconst targetPublic = data.public ?? currentPublic;\n\n\tif (data.key !== undefined && data.fileName !== undefined) {\n\t\tconst keyAccessRes = await mediaServices.checks.checkMediaKeyAccess(\n\t\t\tcontext,\n\t\t\t{\n\t\t\t\tkey: data.key,\n\t\t\t},\n\t\t);\n\t\tif (keyAccessRes.error) return keyAccessRes;\n\n\t\tconst awaitingSync = await mediaServices.checks.checkAwaitingSync(context, {\n\t\t\tkey: data.key,\n\t\t});\n\t\tif (awaitingSync.error) return awaitingSync;\n\n\t\tconst updateRes = await mediaServices.strategies.update(context, {\n\t\t\tpreviousSize: mediaRes.data.file_size,\n\t\t\tpreviousKey: mediaRes.data.key,\n\t\t\ttenantKey: mediaRes.data.tenant_key,\n\t\t\tpreviousType: mediaRes.data.type as MediaType,\n\t\t\tpreviousEtag: mediaRes.data.e_tag,\n\t\t\tupdatedKey: data.key,\n\t\t\tallowedType: data.allowedType,\n\t\t\tfileName: data.fileName,\n\t\t\t//* The final media key remains the existing media key, so replacement uploads cannot reassign tenants.\n\t\t\ttargetKey:\n\t\t\t\ttargetPublic === currentPublic\n\t\t\t\t\t? mediaRes.data.key\n\t\t\t\t\t: changeKeyVisibility({\n\t\t\t\t\t\t\tkey: mediaRes.data.key,\n\t\t\t\t\t\t\tvisibility: targetPublic\n\t\t\t\t\t\t\t\t? constants.media.visibilityKeys.public\n\t\t\t\t\t\t\t\t: constants.media.visibilityKeys.private,\n\t\t\t\t\t\t}),\n\t\t});\n\t\tif (updateRes.error) return updateRes;\n\n\t\tupdateObjectRes = updateRes.data;\n\t}\n\n\tconst finalType = updateObjectRes?.type ?? mediaRes.data.type;\n\tconst translations = prepareMediaTranslations({\n\t\ttitle: data.title || [],\n\t\talt: finalType === \"image\" ? (data.alt ?? []) : [],\n\t\tdescription:\n\t\t\tfinalType === \"video\" || finalType === \"audio\"\n\t\t\t\t? (data.description ?? [])\n\t\t\t\t: [],\n\t\tsummary: finalType === \"document\" ? (data.summary ?? []) : [],\n\t\tmediaId: mediaRes.data.id,\n\t});\n\tif (data.posterId != null && finalType !== \"video\") {\n\t\treturn {\n\t\t\terror: {\n\t\t\t\ttype: \"basic\",\n\t\t\t\tstatus: 400,\n\t\t\t\tmessage: copy(\"server:core.media.poster.video.only\"),\n\t\t\t},\n\t\t\tdata: undefined,\n\t\t};\n\t}\n\tif (data.focalPoint !== undefined && finalType !== \"image\") {\n\t\treturn {\n\t\t\terror: {\n\t\t\t\ttype: \"basic\",\n\t\t\t\tstatus: 400,\n\t\t\t\terrors: {\n\t\t\t\t\tfocalPoint: {\n\t\t\t\t\t\tcode: \"media_error\",\n\t\t\t\t\t\tmessage: copy(\"server:core.media.errors.focal.point.image.only\"),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tdata: undefined,\n\t\t};\n\t}\n\n\t//* if no new key/file provided but public flag differs, rename the key only\n\tif (\n\t\tdata.key === undefined &&\n\t\tdata.public !== undefined &&\n\t\tcurrentPublic !== data.public\n\t) {\n\t\tconst targetVisibility = data.public\n\t\t\t? constants.media.visibilityKeys.public\n\t\t\t: constants.media.visibilityKeys.private;\n\t\tconst newKey = changeKeyVisibility({\n\t\t\tkey: mediaRes.data.key,\n\t\t\tvisibility: targetVisibility,\n\t\t});\n\n\t\tconst renameRes = await mediaServices.strategies.rename(context, {\n\t\t\tfrom: mediaRes.data.key,\n\t\t\tto: newKey,\n\t\t});\n\t\tif (renameRes.error) return renameRes;\n\n\t\trenamedKey = newKey;\n\t}\n\n\t//* key visibility infered from either the new key, or if we're changing the visibility, the renamed key\n\tconst keyVisibility = updateObjectRes?.key\n\t\t? getKeyVisibility(updateObjectRes.key)\n\t\t: renamedKey\n\t\t\t? getKeyVisibility(renamedKey)\n\t\t\t: undefined;\n\n\t//* we infer the public value based on the key so there cannot be drift between the media uploaded via the\n\t//* upload endpoint and this media update endpoint which the SPA calls afterwards\n\tconst isPublic =\n\t\tkeyVisibility !== undefined\n\t\t\t? keyVisibility === constants.media.visibilityKeys.public\n\t\t\t: undefined;\n\n\t//* clear processed images if:\n\t//* - a new file was uploaded (variants of old image are invalid)\n\t//* - visibility changed (variants need to be in new public/private path)\n\tconst activeCrop = mediaRes.data.crop?.[0];\n\n\tconst shouldClearProcessed =\n\t\tmediaRes.data.type === \"image\" &&\n\t\t(updateObjectRes !== undefined ||\n\t\t\trenamedKey !== undefined ||\n\t\t\t(data.focalPoint !== undefined && activeCrop === undefined));\n\tconst shouldClearActiveCropProcessed =\n\t\tactiveCrop !== undefined &&\n\t\tdata.focalPoint !== undefined &&\n\t\tdata.crop === undefined &&\n\t\tupdateObjectRes === undefined;\n\n\tconst aiGenerationRes = await resolveAiGeneration(context, {\n\t\torigin: data.origin,\n\t\taiGenerationRequestId: data.aiGenerationRequestId,\n\t});\n\tif (aiGenerationRes.error) return aiGenerationRes;\n\n\tconst updateData: Partial<Update<LucidMedia>> = {\n\t\tkey: updateObjectRes?.key ?? renamedKey,\n\t\te_tag: updateObjectRes?.etag,\n\t\torigin: data.origin,\n\t\tai_generation_id: aiGenerationRes.data,\n\t\ttype: updateObjectRes?.type,\n\t\tmime_type: updateObjectRes?.mimeType,\n\t\tfile_extension: updateObjectRes?.extension,\n\t\tfile_name: data.fileName,\n\t\tfile_size: updateObjectRes?.size,\n\t\twidth: data.width,\n\t\theight: data.height,\n\t\tfocal_x:\n\t\t\tfinalType !== \"image\"\n\t\t\t\t? null\n\t\t\t\t: activeCrop !== undefined && updateObjectRes === undefined\n\t\t\t\t\t? undefined\n\t\t\t\t\t: data.focalPoint === undefined\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: data.focalPoint === null\n\t\t\t\t\t\t\t? null\n\t\t\t\t\t\t\t: Math.round(data.focalPoint.x * 10000),\n\t\tfocal_y:\n\t\t\tfinalType !== \"image\"\n\t\t\t\t? null\n\t\t\t\t: activeCrop !== undefined && updateObjectRes === undefined\n\t\t\t\t\t? undefined\n\t\t\t\t\t: data.focalPoint === undefined\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: data.focalPoint === null\n\t\t\t\t\t\t\t? null\n\t\t\t\t\t\t\t: Math.round(data.focalPoint.y * 10000),\n\t\tblur_hash: data.blurHash,\n\t\taverage_color: data.averageColor,\n\t\tbase64: finalType !== \"image\" ? null : data.base64,\n\t\tis_dark: data.isDark,\n\t\tis_light: data.isLight,\n\t\tfolder_id: data.folderId,\n\t\tpublic: isPublic ?? data.public,\n\t\tis_deleted: data.isDeleted,\n\t\tis_deleted_at: data.isDeleted\n\t\t\t? new Date().toISOString()\n\t\t\t: data.isDeleted === false\n\t\t\t\t? null\n\t\t\t\t: undefined,\n\t\tdeleted_by: data.isDeleted\n\t\t\t? data.userId\n\t\t\t: data.isDeleted === false\n\t\t\t\t? null\n\t\t\t\t: undefined,\n\t\tupdated_at: new Date().toISOString(),\n\t\tupdated_by: data.userId,\n\t};\n\n\tconst [\n\t\tmediaUpdateRes,\n\t\tmediaTranslationsRes,\n\t\tclearProcessedRes,\n\t\tclearActiveCropProcessedRes,\n\t\tcropFocalRes,\n\t] = await Promise.all([\n\t\tMedia.updateSingle({\n\t\t\twhere: [\n\t\t\t\t{\n\t\t\t\t\tkey: \"id\",\n\t\t\t\t\toperator: \"=\",\n\t\t\t\t\tvalue: data.id,\n\t\t\t\t},\n\t\t\t],\n\t\t\tdata: updateData,\n\t\t\treturning: [\"id\"],\n\t\t\tvalidation: {\n\t\t\t\tenabled: true,\n\t\t\t},\n\t\t}),\n\t\ttranslations.length > 0\n\t\t\t? MediaTranslations.upsertMultiple({\n\t\t\t\t\tdata: translations,\n\t\t\t\t\treturning: [\"id\"],\n\t\t\t\t\tvalidation: {\n\t\t\t\t\t\tenabled: true,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t: Promise.resolve({ error: undefined, data: undefined }),\n\t\tshouldClearProcessed\n\t\t\t? processedImageServices.clearSingle(context, {\n\t\t\t\t\tid: mediaRes.data.id,\n\t\t\t\t\tkey: mediaRes.data.key,\n\t\t\t\t})\n\t\t\t: Promise.resolve({ error: undefined, data: undefined }),\n\t\tshouldClearActiveCropProcessed\n\t\t\t? processedImageServices.clearSingle(context, {\n\t\t\t\t\tkey: activeCrop.key,\n\t\t\t\t})\n\t\t\t: Promise.resolve({ error: undefined, data: undefined }),\n\t\tshouldClearActiveCropProcessed\n\t\t\t? Media.updateSingle({\n\t\t\t\t\twhere: [{ key: \"id\", operator: \"=\", value: activeCrop.id }],\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tfocal_x:\n\t\t\t\t\t\t\tdata.focalPoint === null\n\t\t\t\t\t\t\t\t? null\n\t\t\t\t\t\t\t\t: Math.round((data.focalPoint?.x ?? 0) * 10000),\n\t\t\t\t\t\tfocal_y:\n\t\t\t\t\t\t\tdata.focalPoint === null\n\t\t\t\t\t\t\t\t? null\n\t\t\t\t\t\t\t\t: Math.round((data.focalPoint?.y ?? 0) * 10000),\n\t\t\t\t\t\tupdated_at: new Date().toISOString(),\n\t\t\t\t\t\tupdated_by: data.userId,\n\t\t\t\t\t},\n\t\t\t\t\treturning: [\"id\"],\n\t\t\t\t})\n\t\t\t: Promise.resolve({ error: undefined, data: undefined }),\n\t]);\n\tif (mediaUpdateRes.error) return mediaUpdateRes;\n\tif (mediaTranslationsRes.error) return mediaTranslationsRes;\n\tif (clearProcessedRes.error) return clearProcessedRes;\n\tif (clearActiveCropProcessedRes.error) return clearActiveCropProcessedRes;\n\tif (cropFocalRes.error) return cropFocalRes;\n\n\tconst currentPosterId = mediaRes.data.poster?.[0]?.id ?? null;\n\tconst posterWasRemoved =\n\t\tdata.posterId !== undefined &&\n\t\tcurrentPosterId !== null &&\n\t\tdata.posterId !== currentPosterId;\n\n\tif (posterWasRemoved && currentPosterId !== null) {\n\t\tconst deletePosterRes = await permanentlyDeleteMedia(context, {\n\t\t\tid: currentPosterId,\n\t\t\tinvalidateCache: false,\n\t\t});\n\t\tif (deletePosterRes.error) return deletePosterRes;\n\t}\n\n\tif (data.posterId !== undefined && data.posterId !== null) {\n\t\tconst hidePosterRes = await Media.updateSingle({\n\t\t\twhere: [{ key: \"id\", operator: \"=\", value: data.posterId }],\n\t\t\tdata: {\n\t\t\t\tis_hidden: true,\n\t\t\t\tfolder_id: null,\n\t\t\t\tparent_media_id: mediaRes.data.id,\n\t\t\t\trelation_type: \"poster\",\n\t\t\t\tupdated_at: new Date().toISOString(),\n\t\t\t\tupdated_by: data.userId,\n\t\t\t},\n\t\t\tvalidation: {\n\t\t\t\tenabled: true,\n\t\t\t},\n\t\t});\n\t\tif (hidePosterRes.error) return hidePosterRes;\n\t}\n\n\tconst shouldDeactivateCrop =\n\t\tdata.crop === null ||\n\t\t(updateObjectRes !== undefined && data.crop === undefined);\n\tif (shouldDeactivateCrop) {\n\t\tconst deactivateRes = await deactivateCrop(context, {\n\t\t\tparentId: mediaRes.data.id,\n\t\t\tuserId: data.userId,\n\t\t});\n\t\tif (deactivateRes.error) return deactivateRes;\n\t}\n\n\tif (data.crop) {\n\t\tconst cropRes = await upsertCrop(context, {\n\t\t\tparent: {\n\t\t\t\tid: mediaRes.data.id,\n\t\t\t\tkey: updateObjectRes?.key ?? renamedKey ?? mediaRes.data.key,\n\t\t\t\ttype: finalType,\n\t\t\t\torigin: data.origin ?? mediaRes.data.origin,\n\t\t\t\tpublic: isPublic ?? data.public ?? mediaRes.data.public,\n\t\t\t\ttenant_key: mediaRes.data.tenant_key,\n\t\t\t\trelation_type: mediaRes.data.relation_type,\n\t\t\t},\n\t\t\tcrop: data.crop,\n\t\t\tuserId: data.userId,\n\t\t});\n\t\tif (cropRes.error) return cropRes;\n\t}\n\n\tconst visibilityRes = await syncOwnedVisibility(context, {\n\t\tparentId: mediaRes.data.id,\n\t\tpublic: isPublic ?? data.public ?? currentPublic,\n\t\tuserId: data.userId,\n\t});\n\tif (visibilityRes.error) return visibilityRes;\n\n\tif (\n\t\tupdateObjectRes !== undefined &&\n\t\tupdateObjectRes.sourceDeleted !== false &&\n\t\tdata.key !== undefined\n\t) {\n\t\tconst deleteMediaSyncRes = await MediaAwaitingSync.deleteSingle({\n\t\t\twhere: [{ key: \"key\", operator: \"=\", value: data.key }],\n\t\t\treturning: [\"key\"],\n\t\t\tvalidation: {\n\t\t\t\tenabled: true,\n\t\t\t},\n\t\t});\n\t\tif (deleteMediaSyncRes.error) return deleteMediaSyncRes;\n\t}\n\n\tawait Promise.all([\n\t\tclearClientMediaSingleCache(context, data.id),\n\t\tinvalidateHttpCacheTags(context, [cacheKeys.http.tags.clientMedia]),\n\t]);\n\n\tconst hookRes = await executeHooks(\n\t\tcontext,\n\t\t{\n\t\t\tservice: \"media\",\n\t\t\tevent: \"afterUpdate\",\n\t\t\tconfig: context.config,\n\t\t},\n\t\t{\n\t\t\tmeta: {\n\t\t\t\ttenantKey: context.request.tenantKey ?? null,\n\t\t\t},\n\t\t\tdata: {\n\t\t\t\tid: mediaUpdateRes.data.id,\n\t\t\t\tuserId: data.userId,\n\t\t\t},\n\t\t},\n\t);\n\tif (hookRes.error) return hookRes;\n\n\treturn {\n\t\terror: undefined,\n\t\tdata: mediaUpdateRes.data.id,\n\t};\n};\n\nexport default updateSingle;\n"],"mappings":"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"}