mirror of
https://github.com/hexastack/hexabot
synced 2025-06-26 18:27:28 +00:00
Merge pull request #546 from Hexastack/refactor/attachment-payload
feat: Refactor attachment payload + use public signed urls in web channel
This commit is contained in:
@@ -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.
|
||||
@@ -7,10 +7,7 @@
|
||||
*/
|
||||
|
||||
import { Subscriber } from '@/chat/schemas/subscriber.schema';
|
||||
import {
|
||||
AttachmentForeignKey,
|
||||
AttachmentPayload,
|
||||
} from '@/chat/schemas/types/attachment';
|
||||
import { AttachmentPayload } from '@/chat/schemas/types/attachment';
|
||||
import { SubscriberChannelData } from '@/chat/schemas/types/channel';
|
||||
import {
|
||||
IncomingMessageType,
|
||||
@@ -101,7 +98,7 @@ export default abstract class EventWrapper<
|
||||
*
|
||||
* @returns The current instance of the channel handler.
|
||||
*/
|
||||
getHandler(): ChannelHandler {
|
||||
getHandler(): C {
|
||||
return this._handler;
|
||||
}
|
||||
|
||||
@@ -126,6 +123,7 @@ export default abstract class EventWrapper<
|
||||
/**
|
||||
* Sets an event attribute value
|
||||
*
|
||||
* @deprecated
|
||||
* @param attr - Event attribute name
|
||||
* @param value - The value to set for the specified attribute.
|
||||
*/
|
||||
@@ -136,6 +134,7 @@ export default abstract class EventWrapper<
|
||||
/**
|
||||
* Returns an event attribute value, default value if it does exist
|
||||
*
|
||||
* @deprecated
|
||||
* @param attr - Event attribute name
|
||||
* @param otherwise - Default value if attribute does not exist
|
||||
*
|
||||
@@ -190,6 +189,16 @@ export default abstract class EventWrapper<
|
||||
this._profile = profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-Process the message event
|
||||
*
|
||||
* Child class can perform operations such as storing files as attachments.
|
||||
*/
|
||||
preprocess() {
|
||||
// Nothing ...
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns event recipient id
|
||||
*
|
||||
@@ -249,7 +258,7 @@ export default abstract class EventWrapper<
|
||||
*
|
||||
* @returns Received attachments message
|
||||
*/
|
||||
abstract getAttachments(): AttachmentPayload<AttachmentForeignKey>[];
|
||||
abstract getAttachments(): AttachmentPayload[];
|
||||
|
||||
/**
|
||||
* Returns the list of delivered messages
|
||||
@@ -378,7 +387,7 @@ export class GenericEventWrapper extends EventWrapper<
|
||||
* @returns A list of received attachments
|
||||
* @deprecated - This method is deprecated
|
||||
*/
|
||||
getAttachments(): AttachmentPayload<AttachmentForeignKey>[] {
|
||||
getAttachments(): AttachmentPayload[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -21,6 +21,7 @@ import { NextFunction, Request, Response } from 'express';
|
||||
import { Attachment } from '@/attachment/schemas/attachment.schema';
|
||||
import { AttachmentService } from '@/attachment/services/attachment.service';
|
||||
import { SubscriberCreateDto } from '@/chat/dto/subscriber.dto';
|
||||
import { AttachmentRef } from '@/chat/schemas/types/attachment';
|
||||
import {
|
||||
StdOutgoingEnvelope,
|
||||
StdOutgoingMessage,
|
||||
@@ -234,22 +235,32 @@ export default abstract class ChannelHandler<
|
||||
* @param attachment The attachment ID or object to generate a signed URL for.
|
||||
* @return A signed URL string for downloading the specified attachment.
|
||||
*/
|
||||
protected async getPublicUrl(attachment: string | Attachment) {
|
||||
const resource =
|
||||
typeof attachment === 'string'
|
||||
? await this.attachmentService.findOne(attachment)
|
||||
: attachment;
|
||||
public async getPublicUrl(attachment: AttachmentRef | Attachment) {
|
||||
if ('id' in attachment) {
|
||||
if (!attachment.id) {
|
||||
throw new TypeError(
|
||||
'Attachment ID is empty, unable to generate public URL.',
|
||||
);
|
||||
}
|
||||
|
||||
if (!resource) {
|
||||
throw new NotFoundException('Unable to find attachment');
|
||||
const resource = await this.attachmentService.findOne(attachment.id);
|
||||
|
||||
if (!resource) {
|
||||
throw new NotFoundException('Unable to find attachment');
|
||||
}
|
||||
|
||||
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)}`,
|
||||
);
|
||||
} else if ('url' in attachment && attachment.url) {
|
||||
// In case the url is external
|
||||
return attachment.url;
|
||||
} else {
|
||||
throw new TypeError('Unable to resolve the attachment public URL.');
|
||||
}
|
||||
|
||||
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)}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -266,7 +277,11 @@ export default abstract class ChannelHandler<
|
||||
*/
|
||||
public async download(token: string, _req: Request) {
|
||||
try {
|
||||
const result = this.jwtService.verify(token, this.jwtSignOptions);
|
||||
const {
|
||||
exp: _exp,
|
||||
iat: _iat,
|
||||
...result
|
||||
} = this.jwtService.verify(token, this.jwtSignOptions);
|
||||
const attachment = plainToClass(Attachment, result);
|
||||
return await this.attachmentService.download(attachment);
|
||||
} catch (err) {
|
||||
|
||||
@@ -78,25 +78,20 @@ export const urlButtonsMessage: StdOutgoingButtonsMessage = {
|
||||
};
|
||||
|
||||
const attachment: Attachment = {
|
||||
id: '1',
|
||||
id: '1'.repeat(24),
|
||||
name: 'attachment.jpg',
|
||||
type: 'image/jpeg',
|
||||
size: 3539,
|
||||
location: '39991e51-55c6-4a26-9176-b6ba04f180dc.jpg',
|
||||
channel: {
|
||||
['dimelo']: {
|
||||
id: 'attachment-id-dimelo',
|
||||
['any-channel']: {
|
||||
id: 'any-channel-attachment-id',
|
||||
},
|
||||
},
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
|
||||
const attachmentWithUrl: Attachment = {
|
||||
...attachment,
|
||||
url: 'http://localhost:4000/attachment/download/1/attachment.jpg',
|
||||
};
|
||||
|
||||
export const contentMessage: StdOutgoingListMessage = {
|
||||
options: {
|
||||
display: OutgoingMessageFormat.list,
|
||||
@@ -121,7 +116,8 @@ export const contentMessage: StdOutgoingListMessage = {
|
||||
title: 'First',
|
||||
desc: 'About being first',
|
||||
thumbnail: {
|
||||
payload: attachmentWithUrl,
|
||||
type: 'image',
|
||||
payload: { id: attachment.id },
|
||||
},
|
||||
getPayload() {
|
||||
return this.title;
|
||||
@@ -136,7 +132,8 @@ export const contentMessage: StdOutgoingListMessage = {
|
||||
title: 'Second',
|
||||
desc: 'About being second',
|
||||
thumbnail: {
|
||||
payload: attachmentWithUrl,
|
||||
type: 'image',
|
||||
payload: { id: attachment.id },
|
||||
},
|
||||
getPayload() {
|
||||
return this.title;
|
||||
@@ -149,14 +146,14 @@ export const contentMessage: StdOutgoingListMessage = {
|
||||
pagination: {
|
||||
total: 3,
|
||||
skip: 0,
|
||||
limit: 1,
|
||||
limit: 2,
|
||||
},
|
||||
};
|
||||
|
||||
export const attachmentMessage: StdOutgoingAttachmentMessage<Attachment> = {
|
||||
export const attachmentMessage: StdOutgoingAttachmentMessage = {
|
||||
attachment: {
|
||||
type: FileType.image,
|
||||
payload: attachmentWithUrl,
|
||||
payload: { id: attachment.id },
|
||||
},
|
||||
quickReplies: [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user