Skip to content

Commit

Permalink
Implement deletion of attachments through interface
Browse files Browse the repository at this point in the history
Signed-off-by: Jonathan Mezach <[email protected]>
  • Loading branch information
jmezach committed Jan 6, 2025
1 parent b70ad25 commit b24d460
Show file tree
Hide file tree
Showing 6 changed files with 36 additions and 34 deletions.
35 changes: 2 additions & 33 deletions plugins/qeta-backend/src/service/routes/attachments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@ import AzureBlobStorageEngine from '../upload/azureBlobStorage';
import fs from 'fs';
import FileType from 'file-type';
import { File, RouteOptions } from '../types';
import {
DeleteObjectCommand,
DeleteObjectCommandOutput,
} from '@aws-sdk/client-s3';
import { getS3Client } from '../util';
import { AttachmentStorageEngine, AttachmentStorageEngineOptions } from '../upload/attachmentStorageEngine';

const DEFAULT_IMAGE_SIZE_LIMIT = 2500000;
Expand Down Expand Up @@ -157,34 +152,8 @@ export const attachmentsRoutes = (router: Router, options: RouteOptions) => {
return;
}

const deleteS3Image = async () => {
const bucket = config.getOptionalString('qeta.storage.bucket');
if (!bucket) {
throw new Error('Bucket name is required for S3 storage');
}
const s3 = getS3Client(config);
const output: DeleteObjectCommandOutput = await s3.send(
new DeleteObjectCommand({
Bucket: bucket,
Key: attachment.path,
}),
);
if (output.$metadata.httpStatusCode !== 204) {
throw new Error('Failed to delete object');
}
};

switch (attachment.locationType) {
case 's3':
await deleteS3Image();
break;
case 'filesystem':
await fs.promises.rm(attachment.path);
break;
default:
case 'database':
break;
}
const engine = getStorageEngine(attachment.locationType, options);
await engine.deleteAttachment(attachment);

const result = await database.deleteAttachment(uuid);
if (!result) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ export type AttachmentStorageEngineOptions = {
export interface AttachmentStorageEngine {
handleFile: (file: File, options?: { postId?: number; answerId?: number; collectionId?: number }) => Promise<Attachment>;
getAttachmentBuffer: (attachment: Attachment) => Promise<Buffer | undefined>;
deleteAttachment(attachment: Attachment): Promise<void>;
}
7 changes: 7 additions & 0 deletions plugins/qeta-backend/src/service/upload/azureBlobStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ class AzureBlobStorageEngine implements AttachmentStorageEngine {
const blob = container.getBlockBlobClient(attachment.path);
return blob.downloadToBuffer();
}

deleteAttachment = async (attachment: Attachment) => {
const client = getAzureBlobServiceClient(this.config);
const container = client.getContainerClient(this.container);
const blob = container.getBlockBlobClient(attachment.path);
await blob.delete();
}
}

export default (opts: AttachmentStorageEngineOptions) => {
Expand Down
4 changes: 4 additions & 0 deletions plugins/qeta-backend/src/service/upload/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ class DatabaseStoreEngine implements AttachmentStorageEngine {
getAttachmentBuffer = async (attachment: Attachment) => {
return attachment.binaryImage;
};

deleteAttachment = async (_attachment: Attachment) => {
// Nothing to do here, since the attachment is stored in the database
}
}

export default (opts: AttachmentStorageEngineOptions) => {
Expand Down
4 changes: 4 additions & 0 deletions plugins/qeta-backend/src/service/upload/filesystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ class FilesystemStoreEngine implements AttachmentStorageEngine {
getAttachmentBuffer = async (attachment: Attachment) => {
return await fs.promises.readFile(attachment.path);
};

deleteAttachment = async (attachment: Attachment) => {
await fs.promises.rm(attachment.path);
}
}

export default (opts: AttachmentStorageEngineOptions) => {
Expand Down
19 changes: 18 additions & 1 deletion plugins/qeta-backend/src/service/upload/s3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Config } from '@backstage/config';
import { QetaStore } from '../../database/QetaStore';
import { Attachment } from '@drodil/backstage-plugin-qeta-common';
import { File } from '../types';
import { GetObjectCommand, GetObjectCommandOutput, PutObjectCommand } from '@aws-sdk/client-s3';
import { DeleteObjectCommand, DeleteObjectCommandOutput, GetObjectCommand, GetObjectCommandOutput, PutObjectCommand } from '@aws-sdk/client-s3';
import { getS3Client } from '../util';
import { AttachmentStorageEngine, AttachmentStorageEngineOptions } from './attachmentStorageEngine';

Expand Down Expand Up @@ -81,6 +81,23 @@ class S3StoreEngine implements AttachmentStorageEngine {
const bytes = await object.Body.transformToByteArray();
return Buffer.from(bytes);
};

deleteAttachment = async (attachment: Attachment) => {
const bucket = this.config.getOptionalString('qeta.storage.bucket');
if (!bucket) {
throw new Error('Bucket name is required for S3 storage');
}
const s3 = getS3Client(this.config);
const output: DeleteObjectCommandOutput = await s3.send(
new DeleteObjectCommand({
Bucket: bucket,
Key: attachment.path,
}),
);
if (output.$metadata.httpStatusCode !== 204) {
throw new Error('Failed to delete object');
}
}
}

export default (opts: AttachmentStorageEngineOptions) => {
Expand Down

0 comments on commit b24d460

Please sign in to comment.