mirror of
https://github.com/hexastack/hexabot
synced 2025-04-09 15:34:38 +00:00
fix: message attachment id
This commit is contained in:
parent
1d896830cb
commit
d7cb39f9f4
@ -12,6 +12,7 @@ import { HttpModule } from '@nestjs/axios';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { MongooseModule } from '@nestjs/mongoose';
|
||||
|
||||
import { AttachmentModule } from '@/attachment/attachment.module';
|
||||
import { LoggerModule } from '@/logger/logger.module';
|
||||
|
||||
import { MigrationCommand } from './migration.command';
|
||||
@ -23,6 +24,7 @@ import { MigrationService } from './migration.service';
|
||||
MongooseModule.forFeature([MigrationModel]),
|
||||
LoggerModule,
|
||||
HttpModule,
|
||||
AttachmentModule,
|
||||
],
|
||||
providers: [
|
||||
MigrationService,
|
||||
|
@ -14,6 +14,7 @@ import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { getModelToken, MongooseModule } from '@nestjs/mongoose';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { AttachmentService } from '@/attachment/services/attachment.service';
|
||||
import { LoggerService } from '@/logger/logger.service';
|
||||
import { MetadataRepository } from '@/setting/repositories/metadata.repository';
|
||||
import { Metadata, MetadataModel } from '@/setting/schemas/metadata.schema';
|
||||
@ -54,6 +55,10 @@ describe('MigrationService', () => {
|
||||
provide: HttpService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: AttachmentService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: ModuleRef,
|
||||
useValue: {
|
||||
@ -278,6 +283,7 @@ describe('MigrationService', () => {
|
||||
});
|
||||
expect(loadMigrationFileSpy).toHaveBeenCalledWith('v2.1.9');
|
||||
expect(migrationMock.up).toHaveBeenCalledWith({
|
||||
attachmentService: service['attachmentService'],
|
||||
logger: service['logger'],
|
||||
http: service['httpService'],
|
||||
});
|
||||
@ -308,6 +314,7 @@ describe('MigrationService', () => {
|
||||
});
|
||||
expect(loadMigrationFileSpy).toHaveBeenCalledWith('v2.1.9');
|
||||
expect(migrationMock.up).toHaveBeenCalledWith({
|
||||
attachmentService: service['attachmentService'],
|
||||
logger: service['logger'],
|
||||
http: service['httpService'],
|
||||
});
|
||||
@ -338,6 +345,7 @@ describe('MigrationService', () => {
|
||||
});
|
||||
expect(loadMigrationFileSpy).toHaveBeenCalledWith('v2.1.9');
|
||||
expect(migrationMock.up).toHaveBeenCalledWith({
|
||||
attachmentService: service['attachmentService'],
|
||||
logger: service['logger'],
|
||||
http: service['httpService'],
|
||||
});
|
||||
|
@ -19,6 +19,7 @@ import leanDefaults from 'mongoose-lean-defaults';
|
||||
import leanGetters from 'mongoose-lean-getters';
|
||||
import leanVirtuals from 'mongoose-lean-virtuals';
|
||||
|
||||
import { AttachmentService } from '@/attachment/services/attachment.service';
|
||||
import { config } from '@/config';
|
||||
import { LoggerService } from '@/logger/logger.service';
|
||||
import { MetadataService } from '@/setting/services/metadata.service';
|
||||
@ -43,6 +44,7 @@ export class MigrationService implements OnApplicationBootstrap {
|
||||
private readonly logger: LoggerService,
|
||||
private readonly metadataService: MetadataService,
|
||||
private readonly httpService: HttpService,
|
||||
private readonly attachmentService: AttachmentService,
|
||||
@InjectModel(Migration.name)
|
||||
private readonly migrationModel: Model<Migration>,
|
||||
) {}
|
||||
@ -253,6 +255,7 @@ module.exports = {
|
||||
const result = await migration[action]({
|
||||
logger: this.logger,
|
||||
http: this.httpService,
|
||||
attachmentService: this.attachmentService,
|
||||
});
|
||||
|
||||
if (result) {
|
||||
|
@ -10,11 +10,13 @@ import { existsSync } from 'fs';
|
||||
import { join, resolve } from 'path';
|
||||
|
||||
import mongoose, { HydratedDocument } from 'mongoose';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import attachmentSchema, {
|
||||
Attachment,
|
||||
} from '@/attachment/schemas/attachment.schema';
|
||||
import blockSchema, { Block } from '@/chat/schemas/block.schema';
|
||||
import messageSchema, { Message } from '@/chat/schemas/message.schema';
|
||||
import subscriberSchema, { Subscriber } from '@/chat/schemas/subscriber.schema';
|
||||
import { StdOutgoingAttachmentMessage } from '@/chat/schemas/types/message';
|
||||
import contentSchema, { Content } from '@/cms/schemas/content.schema';
|
||||
@ -372,12 +374,114 @@ const migrateAttachmentContents = async (
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates message documents that contain attachment "message.attachment"
|
||||
* to apply one of the following operation:
|
||||
* - Rename 'attachment_id' to 'id'
|
||||
* - Parse internal url for to get the 'id'
|
||||
* - Fetch external url, stores the attachment and store the 'id'
|
||||
*
|
||||
* @returns Resolves when the migration process is complete.
|
||||
*/
|
||||
const migrateAttachmentMessages = async ({
|
||||
logger,
|
||||
http,
|
||||
attachmentService,
|
||||
}: MigrationServices) => {
|
||||
const MessageModel = mongoose.model<Message>(Message.name, messageSchema);
|
||||
|
||||
// Find blocks where "message.attachment" exists
|
||||
const cursor = MessageModel.find({
|
||||
'message.attachment.payload': { $exists: true },
|
||||
'message.attachment.payload.id': { $exists: false },
|
||||
}).cursor();
|
||||
|
||||
// Helper function to update the attachment ID in the database
|
||||
const updateAttachmentId = async (
|
||||
messageId: mongoose.Types.ObjectId,
|
||||
attachmentId: string | null,
|
||||
) => {
|
||||
await MessageModel.updateOne(
|
||||
{ _id: messageId },
|
||||
{ $set: { 'message.attachment.payload.id': attachmentId } },
|
||||
);
|
||||
};
|
||||
|
||||
for await (const msg of cursor) {
|
||||
try {
|
||||
if (
|
||||
'attachment' in msg.message &&
|
||||
'payload' in msg.message.attachment &&
|
||||
msg.message.attachment.payload
|
||||
) {
|
||||
if ('attachment_id' in msg.message.attachment.payload) {
|
||||
await updateAttachmentId(
|
||||
msg._id,
|
||||
msg.message.attachment.payload.attachment_id as string,
|
||||
);
|
||||
} else if ('url' in msg.message.attachment.payload) {
|
||||
const url = msg.message.attachment.payload.url;
|
||||
const regex =
|
||||
/^https?:\/\/[\w.-]+\/attachment\/download\/([a-f\d]{24})\/.+$/;
|
||||
// Test the URL and extract the ID
|
||||
const match = url.match(regex);
|
||||
if (match) {
|
||||
const [, attachmentId] = match;
|
||||
await updateAttachmentId(msg._id, attachmentId);
|
||||
} else if (url) {
|
||||
logger.log(
|
||||
`Migrate message ${msg._id}: Handling an external url ...`,
|
||||
);
|
||||
const response = await http.axiosRef.get(url, {
|
||||
responseType: 'arraybuffer', // Ensures the response is returned as a Buffer
|
||||
});
|
||||
const fileBuffer = Buffer.from(response.data);
|
||||
const attachment = await attachmentService.store(fileBuffer, {
|
||||
name: uuidv4(),
|
||||
size: fileBuffer.length,
|
||||
type: response.headers['content-type'],
|
||||
channel: {},
|
||||
});
|
||||
await updateAttachmentId(msg._id, attachment.id);
|
||||
}
|
||||
} else {
|
||||
logger.warn(
|
||||
`Unable to migrate message ${msg._id}: No ID nor URL was found`,
|
||||
);
|
||||
|
||||
throw new Error(
|
||||
'Unable to process message attachment: No ID or URL to be processed',
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new Error(
|
||||
'Unable to process message attachment: Invalid Payload',
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`Failed to update message ${msg._id}: ${error.message}, defaulting to null`,
|
||||
);
|
||||
try {
|
||||
await updateAttachmentId(msg._id, null);
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
`Failed to update message ${msg._id}: ${error.message}, unable to default to null`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
async up(services: MigrationServices) {
|
||||
await populateSubscriberAvatar(services);
|
||||
await updateOldAvatarsPath(services);
|
||||
await migrateAttachmentBlocks(MigrationAction.UP, services);
|
||||
await migrateAttachmentContents(MigrationAction.UP, services);
|
||||
// Given the complexity and inconsistency data, this method does not have
|
||||
// a revert equivalent, at the same time, thus, it doesn't "unset" any attribute
|
||||
await migrateAttachmentMessages(services);
|
||||
return true;
|
||||
},
|
||||
async down(services: MigrationServices) {
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
import { HttpService } from '@nestjs/axios';
|
||||
|
||||
import { AttachmentService } from '@/attachment/services/attachment.service';
|
||||
import { LoggerService } from '@/logger/logger.service';
|
||||
|
||||
import { MigrationDocument } from './migration.schema';
|
||||
@ -34,4 +35,5 @@ export interface MigrationSuccessCallback extends MigrationRunParams {
|
||||
export type MigrationServices = {
|
||||
logger: LoggerService;
|
||||
http: HttpService;
|
||||
attachmentService: AttachmentService;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user