Support uploaded cover images for media posts
فشلت بعض الفحوصات
Deploy To Ghaymah / deploy (push) Has been cancelled

هذا الالتزام موجود في:
boutmoun123
2026-05-25 21:42:27 +03:00
الأصل 8863f61d00
التزام efd87659b2
4 ملفات معدلة مع 104 إضافات و48 حذوفات

عرض الملف

@@ -87,6 +87,7 @@ export class PostsService {
imageFiles: Array<{ mimetype?: string; size: number; buffer: Buffer; originalname?: string }> = [],
videoFile?: { mimetype?: string; size: number; buffer: Buffer; originalname?: string },
audioFile?: { mimetype?: string; size: number; buffer: Buffer; originalname?: string },
coverImageFile?: { mimetype?: string; size: number; buffer: Buffer; originalname?: string },
): Promise<PostDocument> {
const inputImageUrls = dto.imageUrls ?? [];
if (inputImageUrls.length > 10 || imageFiles.length > 10) {
@@ -104,6 +105,12 @@ export class PostsService {
if (audioFile && dto.audioUrl) {
throw new BadRequestException('Provide either audioFile or audioUrl, not both');
}
if (coverImageFile && dto.thumbnailUrl) {
throw new BadRequestException('Provide either coverImageFile or thumbnailUrl, not both');
}
if (coverImageFile && !(videoFile || audioFile || dto.videoUrl || dto.audioUrl)) {
throw new BadRequestException('coverImageFile is allowed only with video or audio posts');
}
if ((videoFile || dto.videoUrl) && (imageFiles.length || inputImageUrls.length)) {
throw new BadRequestException('Post can contain either images or video, not both');
}
@@ -115,10 +122,15 @@ export class PostsService {
const uploadedImageUrls = savedImageUploads.map((item) => item.primaryUrl);
const uploadedImageVariants = savedImageUploads.map((item) => item.variants);
const savedVideoUpload = videoFile ? await this.saveVideoUpload(videoFile) : null;
const savedCoverImageUpload = coverImageFile
? await this.saveResponsiveImageAsset('thumbnails', coverImageFile)
: null;
const uploadedVideoUrl = savedVideoUpload?.videoUrl ?? '';
const uploadedHlsUrl = savedVideoUpload?.hlsUrl ?? '';
const uploadedThumbnailUrl = savedVideoUpload?.thumbnailUrl ?? '';
const uploadedThumbnailVariants = savedVideoUpload?.thumbnailVariants ?? null;
const generatedThumbnailUrl = savedVideoUpload?.thumbnailUrl ?? '';
const generatedThumbnailVariants = savedVideoUpload?.thumbnailVariants ?? null;
const uploadedThumbnailUrl = savedCoverImageUpload?.primaryUrl ?? generatedThumbnailUrl;
const uploadedThumbnailVariants = savedCoverImageUpload?.variants ?? generatedThumbnailVariants;
const uploadedAudioUrl = audioFile ? await this.saveMediaFile('audio', audioFile) : '';
const finalImageUrls = uploadedImageUrls.length ? uploadedImageUrls : inputImageUrls;
const finalImageVariants = uploadedImageVariants.length ? uploadedImageVariants : [];
@@ -174,11 +186,18 @@ export class PostsService {
uploadedThumbnailUrl
? this.deleteThumbnailAsset(uploadedThumbnailUrl, uploadedThumbnailVariants)
: Promise.resolve(),
generatedThumbnailUrl && generatedThumbnailUrl !== uploadedThumbnailUrl
? this.deleteThumbnailAsset(generatedThumbnailUrl, generatedThumbnailVariants)
: Promise.resolve(),
uploadedAudioUrl ? this.deleteManagedPostMedia(uploadedAudioUrl) : Promise.resolve(),
]);
throw error;
}
if (generatedThumbnailUrl && generatedThumbnailUrl !== uploadedThumbnailUrl) {
await this.deleteThumbnailAsset(generatedThumbnailUrl, generatedThumbnailVariants);
}
await this.usersRepository.incrementPostsCount(userId, 1);
await this.feedVersionService.bumpGlobalVersion();
await this.notifyMentionedUsers(userId, post.id, mentionResolution.mentionedUsers, finalContent);
@@ -192,6 +211,7 @@ export class PostsService {
imageFiles: Array<{ mimetype?: string; size: number; buffer: Buffer; originalname?: string }> = [],
videoFile?: { mimetype?: string; size: number; buffer: Buffer; originalname?: string },
audioFile?: { mimetype?: string; size: number; buffer: Buffer; originalname?: string },
coverImageFile?: { mimetype?: string; size: number; buffer: Buffer; originalname?: string },
): Promise<PostDocument> {
const post = await this.postsRepository.findById(postId);
if (!post) {
@@ -219,6 +239,15 @@ export class PostsService {
if (audioFile && dto.audioUrl) {
throw new BadRequestException('Provide either audioFile or audioUrl, not both');
}
if (coverImageFile && dto.thumbnailUrl) {
throw new BadRequestException('Provide either coverImageFile or thumbnailUrl, not both');
}
if (coverImageFile && (imageFiles.length || inputImageUrls.length)) {
throw new BadRequestException('coverImageFile is allowed only with video or audio posts');
}
if (coverImageFile && !(videoFile || audioFile || dto.videoUrl || dto.audioUrl || post.videoUrl || post.audioUrl)) {
throw new BadRequestException('coverImageFile is allowed only with video or audio posts');
}
if ((videoFile || dto.videoUrl) && (imageFiles.length || inputImageUrls.length)) {
throw new BadRequestException('Post can contain either images or video, not both');
}
@@ -230,10 +259,15 @@ export class PostsService {
const uploadedImageUrls = savedImageUploads.map((item) => item.primaryUrl);
const uploadedImageVariants = savedImageUploads.map((item) => item.variants);
const savedVideoUpload = videoFile ? await this.saveVideoUpload(videoFile) : null;
const savedCoverImageUpload = coverImageFile
? await this.saveResponsiveImageAsset('thumbnails', coverImageFile)
: null;
const uploadedVideoUrl = savedVideoUpload?.videoUrl ?? '';
const uploadedHlsUrl = savedVideoUpload?.hlsUrl ?? '';
const uploadedThumbnailUrl = savedVideoUpload?.thumbnailUrl ?? '';
const uploadedThumbnailVariants = savedVideoUpload?.thumbnailVariants ?? null;
const generatedThumbnailUrl = savedVideoUpload?.thumbnailUrl ?? '';
const generatedThumbnailVariants = savedVideoUpload?.thumbnailVariants ?? null;
const uploadedThumbnailUrl = savedCoverImageUpload?.primaryUrl ?? generatedThumbnailUrl;
const uploadedThumbnailVariants = savedCoverImageUpload?.variants ?? generatedThumbnailVariants;
const uploadedAudioUrl = audioFile ? await this.saveMediaFile('audio', audioFile) : '';
const hasImageUpdate = typeof dto.imageUrls !== 'undefined' || imageFiles.length > 0;
const existingImageVariants = Array.isArray((post as any).imageVariants)
@@ -266,7 +300,7 @@ export class PostsService {
? uploadedAudioUrl
: dto.audioUrl ?? ''
: post.audioUrl ?? '';
const nextThumbnailVariants = videoFile
const nextThumbnailVariants = coverImageFile || videoFile
? uploadedThumbnailVariants
: typeof dto.thumbnailUrl === 'string'
? null
@@ -392,6 +426,9 @@ export class PostsService {
uploadedThumbnailUrl
? this.deleteThumbnailAsset(uploadedThumbnailUrl, uploadedThumbnailVariants)
: Promise.resolve(),
generatedThumbnailUrl && generatedThumbnailUrl !== uploadedThumbnailUrl
? this.deleteThumbnailAsset(generatedThumbnailUrl, generatedThumbnailVariants)
: Promise.resolve(),
uploadedAudioUrl ? this.deleteManagedPostMedia(uploadedAudioUrl) : Promise.resolve(),
]);
throw error;
@@ -404,6 +441,9 @@ export class PostsService {
uploadedThumbnailUrl
? this.deleteThumbnailAsset(uploadedThumbnailUrl, uploadedThumbnailVariants)
: Promise.resolve(),
generatedThumbnailUrl && generatedThumbnailUrl !== uploadedThumbnailUrl
? this.deleteThumbnailAsset(generatedThumbnailUrl, generatedThumbnailVariants)
: Promise.resolve(),
uploadedAudioUrl ? this.deleteManagedPostMedia(uploadedAudioUrl) : Promise.resolve(),
]);
throw new NotFoundException('Post not found');
@@ -418,6 +458,9 @@ export class PostsService {
if ((post.thumbnailUrl ?? '') !== (updated.thumbnailUrl ?? '')) {
await this.deleteThumbnailAsset(post.thumbnailUrl ?? '', existingThumbnailVariants);
}
if (generatedThumbnailUrl && generatedThumbnailUrl !== uploadedThumbnailUrl) {
await this.deleteThumbnailAsset(generatedThumbnailUrl, generatedThumbnailVariants);
}
if (hasAudioUpdate && (post.audioUrl ?? '') !== (updated.audioUrl ?? '')) {
await this.deleteManagedPostMedia(post.audioUrl ?? '');
}
@@ -567,6 +610,7 @@ export class PostsService {
userId: string,
dto: CreateReelDto,
videoFile?: { mimetype?: string; size: number; buffer: Buffer; originalname?: string },
coverImageFile?: { mimetype?: string; size: number; buffer: Buffer; originalname?: string },
): Promise<PostDocument> {
if (!videoFile && !dto.videoUrl) {
throw new BadRequestException('Reel requires videoFile or videoUrl');
@@ -592,6 +636,7 @@ export class PostsService {
[],
videoFile,
undefined,
coverImageFile,
);
}