diff --git a/packages/core/src/services/assets/assets.ts b/packages/core/src/services/assets/assets.ts index ba96cc21..8cd1e335 100644 --- a/packages/core/src/services/assets/assets.ts +++ b/packages/core/src/services/assets/assets.ts @@ -17,6 +17,7 @@ import type { } from '@cio/utils/validation/assets'; import type { TTranscriptResponse, TUpdateTranscript } from '@cio/utils/validation/media'; import { + assetUsageExistsForTarget, createAssetUsage, createHlsAssetPlaceholder, createOrGetAssetByStorageKey, @@ -503,6 +504,12 @@ export async function getAssetUsageGraphService(orgId: string, assetId: string) export async function attachAssetService(orgId: string, assetId: string, profileId: string, input: TAssetAttach) { try { await getAssetService(orgId, assetId); + + const alreadyAttached = await assetUsageExistsForTarget(assetId, orgId, input.targetType, input.targetId); + if (alreadyAttached) { + throw new AppError('Asset is already attached to this target', ErrorCodes.ASSET_ALREADY_ATTACHED, 409); + } + return await createAssetUsage({ organizationId: orgId, assetId, diff --git a/packages/db/src/queries/assets/assets.ts b/packages/db/src/queries/assets/assets.ts index c3864cc2..c73cdd65 100644 --- a/packages/db/src/queries/assets/assets.ts +++ b/packages/db/src/queries/assets/assets.ts @@ -387,8 +387,7 @@ export async function createAssetUsage(values: TNewAssetUsage, dbClient: DbOrTxC schema.assetUsage.targetType, schema.assetUsage.targetId, schema.assetUsage.slotType, - schema.assetUsage.slotKey, - schema.assetUsage.position + schema.assetUsage.slotKey ] }) .returning(); @@ -408,10 +407,7 @@ export async function createAssetUsage(values: TNewAssetUsage, dbClient: DbOrTxC eq(schema.assetUsage.slotType, slotType), values.slotKey == null ? sql`${schema.assetUsage.slotKey} is null` - : eq(schema.assetUsage.slotKey, values.slotKey), - values.position == null - ? sql`${schema.assetUsage.position} is null` - : eq(schema.assetUsage.position, values.position) + : eq(schema.assetUsage.slotKey, values.slotKey) ) ) .limit(1); @@ -512,6 +508,35 @@ export async function listAssetUsagesByAsset(assetId: string, orgId: string): Pr } } +export async function assetUsageExistsForTarget( + assetId: string, + orgId: string, + targetType: string, + targetId: string +): Promise { + try { + const [existing] = await db + .select({ id: schema.assetUsage.id }) + .from(schema.assetUsage) + .where( + and( + eq(schema.assetUsage.assetId, assetId), + eq(schema.assetUsage.organizationId, orgId), + eq(schema.assetUsage.targetType, targetType), + eq(schema.assetUsage.targetId, targetId) + ) + ) + .limit(1); + + return Boolean(existing); + } catch (error) { + console.error('assetUsageExistsForTarget error:', error); + throw new Error( + `Failed to check asset usage for target: ${error instanceof Error ? error.message : 'Unknown error'}` + ); + } +} + export async function countAssetUsagesByAsset(assetId: string, orgId: string): Promise { try { const [result] = await db diff --git a/packages/db/src/schema.ts b/packages/db/src/schema.ts index 1cc6e92f..d881dc16 100644 --- a/packages/db/src/schema.ts +++ b/packages/db/src/schema.ts @@ -1142,8 +1142,7 @@ export const assetUsage = pgTable( table.targetType, table.targetId, table.slotType, - table.slotKey, - table.position + table.slotKey ) ] ); diff --git a/packages/utils/src/constants/error-codes.ts b/packages/utils/src/constants/error-codes.ts index 8eaa4842..69b0ea87 100644 --- a/packages/utils/src/constants/error-codes.ts +++ b/packages/utils/src/constants/error-codes.ts @@ -71,6 +71,7 @@ export const ErrorCodes = { ASSET_UPDATE_FAILED: 'ASSET_UPDATE_FAILED', ASSET_DELETE_FAILED: 'ASSET_DELETE_FAILED', ASSET_ATTACH_FAILED: 'ASSET_ATTACH_FAILED', + ASSET_ALREADY_ATTACHED: 'ASSET_ALREADY_ATTACHED', ASSET_DETACH_FAILED: 'ASSET_DETACH_FAILED', ASSET_USAGE_FETCH_FAILED: 'ASSET_USAGE_FETCH_FAILED', ASSET_EXPORT_FAILED: 'ASSET_EXPORT_FAILED',