Merge pull request #913 from Hexastack/fix/attachment-issues
Some checks are pending
Build and Push Docker API Image / build-and-push (push) Waiting to run
Build and Push Docker Base Image / build-and-push (push) Waiting to run
Build and Push Docker UI Image / build-and-push (push) Waiting to run

Fix: attachment issues
This commit is contained in:
Yassine
2025-04-25 08:01:11 +01:00
committed by GitHub
11 changed files with 244 additions and 56 deletions

View File

@@ -308,21 +308,20 @@ export default abstract class ChannelHandler<
* @return A signed URL string for downloading the specified attachment.
*/
public async getPublicUrl(attachment: AttachmentRef | Attachment) {
const [name, _suffix] = this.getName().split('-');
if ('id' in attachment) {
if (!attachment.id) {
throw new TypeError(
'Attachment ID is empty, unable to generate public URL.',
);
if (!attachment || !attachment.id) {
return buildURL(config.apiBaseUrl, `/webhook/${name}/not-found`);
}
const resource = await this.attachmentService.findOne(attachment.id);
if (!resource) {
throw new NotFoundException('Unable to find attachment');
this.logger.warn('Unable to find attachment sending fallback image');
return buildURL(config.apiBaseUrl, `/webhook/${name}/not-found`);
}
const token = this.jwtService.sign({ ...resource }, this.jwtSignOptions);
const [name, _suffix] = this.getName().split('-');
return buildURL(
config.apiBaseUrl,
`/webhook/${name}/download/${resource.name}?t=${encodeURIComponent(token)}`,
@@ -331,7 +330,7 @@ export default abstract class ChannelHandler<
// In case the url is external
return attachment.url;
} else {
throw new TypeError('Unable to resolve the attachment public URL.');
return buildURL(config.apiBaseUrl, `/webhook/${name}/not-found`);
}
}

View File

@@ -88,4 +88,10 @@ export class WebhookController {
this.logger.log('Channel notification : ', req.method, channel);
return await this.channelService.handle(channel, req, res);
}
@Roles('public')
@Get(':channel/not-found')
async handleNotFound(@Res() res: Response) {
return res.status(404).send({ error: 'Not found!' });
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2024 Hexastack. All rights reserved.
* Copyright © 2025 Hexastack. All rights reserved.
*
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
@@ -11,6 +11,9 @@ import { EventEmitter2 } from '@nestjs/event-emitter';
import { MongooseModule } from '@nestjs/mongoose';
import { AttachmentModule } from '@/attachment/attachment.module';
import { AttachmentRepository } from '@/attachment/repositories/attachment.repository';
import { AttachmentModel } from '@/attachment/schemas/attachment.schema';
import { AttachmentService } from '@/attachment/services/attachment.service';
import { ChannelModule } from '@/channel/channel.module';
import { CmsModule } from '@/cms/cms.module';
import { UserModule } from '@/user/user.module';
@@ -58,6 +61,7 @@ import { SubscriberService } from './services/subscriber.service';
SubscriberModel,
ConversationModel,
SubscriberModel,
AttachmentModel,
]),
forwardRef(() => ChannelModule),
CmsModule,
@@ -92,6 +96,8 @@ import { SubscriberService } from './services/subscriber.service';
ConversationService,
ChatService,
BotService,
AttachmentService,
AttachmentRepository,
],
exports: [
SubscriberService,

View File

@@ -11,8 +11,14 @@ import {
InternalServerErrorException,
Optional,
} from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import { Document, Query } from 'mongoose';
import { Attachment } from '@/attachment/schemas/attachment.schema';
import { AttachmentService } from '@/attachment/services/attachment.service';
import { DeleteResult } from '@/utils/generics/base-repository';
import { BaseService } from '@/utils/generics/base-service';
import { TFilterQuery } from '@/utils/types/filter.types';
import {
SocketGet,
SocketPost,
@@ -39,6 +45,7 @@ export class MessageService extends BaseService<
constructor(
private readonly messageRepository: MessageRepository,
private attachmentService: AttachmentService,
@Optional() gateway?: WebsocketGateway,
) {
super(messageRepository);
@@ -127,4 +134,40 @@ export class MessageService extends BaseService<
return lastMessages.reverse();
}
@OnEvent('hook:attachment:preDelete')
async handleDeleteImage(
_query: Query<
DeleteResult,
Document<Attachment, any, any>,
unknown,
Attachment,
'deleteOne' | 'deleteMany'
>,
criteria: TFilterQuery<Attachment>,
) {
try {
this.logger.log(
'deleting attachment messages containing deleted images',
criteria,
);
const foundAttachments = await this.attachmentService.find(criteria);
for (const attachment of foundAttachments) {
await this.updateMany(
{
'message.attachment.payload.id': attachment.id,
},
{
['message.attachment.payload.id' as any]: null,
},
);
}
} catch (error) {
this.logger.error(
'Unable to cleanup old messages with attachment ids',
error,
);
}
}
}