From a7002cd925d0fc951f015c13e0352d09286ae5c4 Mon Sep 17 00:00:00 2001 From: Sergio Gutierrez Villalba Date: Fri, 10 Nov 2023 14:32:35 +0100 Subject: [PATCH 01/18] refactor: simplify entrypoint --- index.ts | 106 ++--------- package.json | 9 +- .../file-deletion/deleted-files.iterator.ts | 36 ++++ .../deleted-folders.iterator.ts | 0 src/tasks/folder-deletion/index.ts | 72 ++++++++ src/tasks/index.ts | 11 ++ src/tasks/process.ts | 4 + src/tasks/task.ts | 11 ++ yarn.lock | 173 ++++++++++++++++++ 9 files changed, 332 insertions(+), 90 deletions(-) create mode 100644 src/tasks/file-deletion/deleted-files.iterator.ts rename src/tasks/{process-folder-deletion => folder-deletion}/deleted-folders.iterator.ts (100%) create mode 100644 src/tasks/folder-deletion/index.ts create mode 100644 src/tasks/index.ts create mode 100644 src/tasks/process.ts create mode 100644 src/tasks/task.ts diff --git a/index.ts b/index.ts index c4734a1..0f0ff69 100644 --- a/index.ts +++ b/index.ts @@ -3,50 +3,34 @@ import amqp from 'amqplib'; import { v4 } from 'uuid'; import { createLogger } from './src/utils'; -import { Consumer } from './src/consumer'; -import { Producer } from './src/producer'; import { DriveDatabase } from './src/drive'; -import { DeletedFoldersIterator } from './src/tasks/process-folder-deletion/deleted-folders.iterator'; +import { taskTypes, tasks } from './src/tasks'; +import { ProcessType } from './src/tasks/process'; -const [,, ...args] = process.argv; -const [type] = args; +config(); -if (!type) { - console.error('Missing argument: type'); - process.exit(1); -} +const taskType = process.env.TASK_TYPE as undefined | string; -if (type !== 'producer' && type !== 'consumer') { - console.error('Invalid argument: type. Accepted values are "producer" or "consumer"'); +if (!taskType || !taskTypes.includes(taskType)) { + console.error(`Invalid or missing task type. Expected ${ + taskTypes.map(t => `'${t}'`).join(', ') + } but got '${taskType}'`); process.exit(1); } -const processId = v4(); -const logger = createLogger(processId); -config(); - -const amqpServer = process.env.AMQP_SERVER; -const queueName = process.env.MARK_DELETED_ITEMS_QUEUE_NAME; -const maxEnqueuedItems = process.env.TASK_MARK_DELETED_ITEMS_PRODUCER_MAX_ENQUEUED_ITEMS; -const maxConcurrentItems = process.env.TASK_MARK_DELETED_ITEMS_CONSUMER_MAX_CONCURRENT_ITEMS; +const processType = process.env.PROCESS_TYPE as undefined | ProcessType; -if (!maxEnqueuedItems) { - logger.log('Missing env var: TASK_MARK_DELETED_ITEMS_PRODUCER_MAX_ENQUEUED_ITEMS'); +if (!processType || !Object.values(ProcessType).includes(processType)) { + console.error(`Invalid or missing process type. Expected ${ + Object.values(ProcessType).map(t => `'${t}'`).join(', ') + } but got '${processType}'`); process.exit(1); } -if (!maxConcurrentItems) { - logger.log('Missing env var: TASK_MARK_DELETED_ITEMS_CONSUMER_MAX_CONCURRENT_ITEMS'); - process.exit(1); -} +const processId = v4(); +const logger = createLogger(processId); -logger.log(`params: process_type -> ${type}, env -> ${ - JSON.stringify({ - maxConcurrentItems, - maxEnqueuedItems, - queueName - }) -}`); +const amqpServer = process.env.QUEUE_SERVER; let db: DriveDatabase; let connection: amqp.Connection; @@ -75,7 +59,7 @@ function handleStop() { } } -async function start(): Promise<{ connection: amqp.Connection, db: DriveDatabase }> { +async function start(): Promise { db = new DriveDatabase(); logger.log('(drive-db) connecting ...'); @@ -86,63 +70,13 @@ async function start(): Promise<{ connection: amqp.Connection, db: DriveDatabase connection = await amqp.connect(amqpServer as string); logger.log('(rabbit) connected !'); - return { connection, db }; + await tasks[taskType as string](processType as 'consumer' | 'producer', { db }, connection); } -start().then(({ connection, db }) => { - if (type === 'producer') { - const deletedFoldersIterator = new DeletedFoldersIterator(db); - - return connection.createChannel().then((channel) => { - const producer = new Producer( - channel, - queueName as string, - deletedFoldersIterator, - maxEnqueuedItems ? parseInt(maxEnqueuedItems as string) : undefined, - ); - - producer.on('enqueue', (item) => { - logger.log(`enqueued item: + ${JSON.stringify(item)}`, 'producer'); - }); - - producer.on('queue-full', () => { - logger.log(`queue full, waiting 1s...`, 'producer'); - }); - - return producer.run(); - }); - } else { - connection.createChannel().then((channel) => { - const consumer = new Consumer<{ - folder_id: string, - processed: boolean, - created_at: Date, - updated_at: Date, - processed_at: Date, - }>( - channel, - queueName as string, - async (taskPayload) => { - logger.log(`received item: + ${JSON.stringify(taskPayload)}`, 'consumer'); - - await db.markChildrenFilesAsDeleted(taskPayload.folder_id); - await db.markChildrenFoldersAsDeleted(taskPayload.folder_id); - await db.markDeletedFolderAsProcessed([taskPayload.folder_id]); - }, - maxConcurrentItems ? parseInt(maxConcurrentItems as string) : undefined, - ); - - consumer.on('error', ({ err, msg }) => { - logger.error(`error processing item: ${JSON.stringify(msg.content)}`, err, 'consumer'); - }); - - consumer.run(); - }); - } -}).catch((err) => { +start().catch((err) => { logger.error('Error starting', err); process.exit(1); -}) +}); process.on('uncaughtException', (err) => { logger.error('Uncaught exception', err); diff --git a/package.json b/package.json index 4def583..17bb998 100644 --- a/package.json +++ b/package.json @@ -4,15 +4,15 @@ "main": "index.js", "license": "MIT", "scripts": { - "start:dev:producer": "ts-node index.ts producer", - "start:dev:consumer": "ts-node index.ts consumer", - "start:prod:producer": "node dist/index.js producer", - "start:prod:consumer": "node dist/index.js consumer", + "start:dev": "ts-node index.ts", + "start:prod": "node dist/index.js", "build": "tsc" }, "dependencies": { "amqplib": "^0.10.3", + "axios": "^1.6.1", "dotenv": "^16.3.1", + "jsonwebtoken": "^9.0.2", "pg": "^8.11.3", "ts-node": "^10.9.1", "typescript": "^5.2.2", @@ -20,6 +20,7 @@ }, "devDependencies": { "@types/amqplib": "^0.10.3", + "@types/jsonwebtoken": "^9.0.5", "@types/node": "^20.8.9", "@types/pg": "^8.10.7", "@types/uuid": "^9.0.6" diff --git a/src/tasks/file-deletion/deleted-files.iterator.ts b/src/tasks/file-deletion/deleted-files.iterator.ts new file mode 100644 index 0000000..ba12380 --- /dev/null +++ b/src/tasks/file-deletion/deleted-files.iterator.ts @@ -0,0 +1,36 @@ +import { DriveDatabase } from "../../drive"; + +export class DeletedFilesIterator { + constructor(private readonly db: DriveDatabase) {} + + async * [Symbol.asyncIterator]() { + let rows : { + file_id: string, + network_file_id: string; + processed: boolean, + created_at: Date, + updated_at: Date, + processed_at: Date, + }[] = []; + let n = 10; + + do { + const rows = await this.db.getDeletedFiles(); + if (rows.length === 0) { + // Wait for a short period before checking for new data. + console.log('No data to process, waiting 1s...'); + await new Promise(resolve => setTimeout(resolve, 1000)); + } else { + await this.db.setFilesAsEnqueued(rows.map(row => row.fileId)); + while (rows.length >= n) { + const chunk = rows.splice(0, n); + yield chunk; + } + + if (rows.length > 0) { + yield rows; + } + } + } while (true); + } +} diff --git a/src/tasks/process-folder-deletion/deleted-folders.iterator.ts b/src/tasks/folder-deletion/deleted-folders.iterator.ts similarity index 100% rename from src/tasks/process-folder-deletion/deleted-folders.iterator.ts rename to src/tasks/folder-deletion/deleted-folders.iterator.ts diff --git a/src/tasks/folder-deletion/index.ts b/src/tasks/folder-deletion/index.ts new file mode 100644 index 0000000..19a4b1e --- /dev/null +++ b/src/tasks/folder-deletion/index.ts @@ -0,0 +1,72 @@ +import { v4 } from 'uuid'; + +import { DeletedFoldersIterator } from './deleted-folders.iterator'; +import { Producer } from '../../producer'; +import { Consumer } from '../../consumer'; +import { createLogger } from '../../utils'; +import { TaskFunction } from '../task'; + +const task: TaskFunction = async( + processType, + drive, + connection, +) => { + const processId = v4(); + const logger = createLogger(processId); + + const queueName = process.env.MARK_DELETED_ITEMS_QUEUE_NAME; + const maxEnqueuedItems = process.env.TASK_MARK_DELETED_ITEMS_PRODUCER_MAX_ENQUEUED_ITEMS; + const maxConcurrentItems = process.env.TASK_MARK_DELETED_ITEMS_CONSUMER_MAX_CONCURRENT_ITEMS; + + if (processType === 'producer') { + const deletedFoldersIterator = new DeletedFoldersIterator(drive.db); + + return connection.createChannel().then((channel) => { + const producer = new Producer( + channel, + queueName as string, + deletedFoldersIterator, + maxEnqueuedItems ? parseInt(maxEnqueuedItems as string) : undefined, + ); + + producer.on('enqueue', (item) => { + logger.log(`enqueued item: + ${JSON.stringify(item)}`, 'producer'); + }); + + producer.on('queue-full', () => { + logger.log(`queue full, waiting 1s...`, 'producer'); + }); + + return producer.run(); + }); + } else { + connection.createChannel().then((channel) => { + const consumer = new Consumer<{ + folder_id: string, + processed: boolean, + created_at: Date, + updated_at: Date, + processed_at: Date, + }>( + channel, + queueName as string, + async (taskPayload) => { + logger.log(`received item: + ${JSON.stringify(taskPayload)}`, 'consumer'); + + await drive.db.markChildrenFilesAsDeleted(taskPayload.folder_id); + await drive.db.markChildrenFoldersAsDeleted(taskPayload.folder_id); + await drive.db.markDeletedFolderAsProcessed([taskPayload.folder_id]); + }, + maxConcurrentItems ? parseInt(maxConcurrentItems as string) : undefined, + ); + + consumer.on('error', ({ err, msg }) => { + logger.error(`error processing item: ${JSON.stringify(msg.content)}`, err, 'consumer'); + }); + + consumer.run(); + }); + } +} + +export default task; diff --git a/src/tasks/index.ts b/src/tasks/index.ts new file mode 100644 index 0000000..73952ff --- /dev/null +++ b/src/tasks/index.ts @@ -0,0 +1,11 @@ +import { TaskFunction } from './task'; + +import fileDeletion from './file-deletion'; +import folderDeletion from './folder-deletion'; + +export const tasks: Record = { + 'delete-files': fileDeletion, + 'delete-folders': folderDeletion, +}; + +export const taskTypes = Object.keys(tasks); diff --git a/src/tasks/process.ts b/src/tasks/process.ts new file mode 100644 index 0000000..9f14b86 --- /dev/null +++ b/src/tasks/process.ts @@ -0,0 +1,4 @@ +export enum ProcessType { + Consumer = 'consumer', + Producer = 'producer', +} diff --git a/src/tasks/task.ts b/src/tasks/task.ts new file mode 100644 index 0000000..c499332 --- /dev/null +++ b/src/tasks/task.ts @@ -0,0 +1,11 @@ +import amqp from 'amqplib'; + +import { DriveDatabase } from '../drive'; + +export type TaskFunction = ( + processType: 'producer' | 'consumer', + drive: { + db: DriveDatabase + }, + connection: amqp.Connection, +) => Promise; diff --git a/yarn.lock b/yarn.lock index 6646fdd..08cef29 100644 --- a/yarn.lock +++ b/yarn.lock @@ -63,6 +63,13 @@ dependencies: "@types/node" "*" +"@types/jsonwebtoken@^9.0.5": + version "9.0.5" + resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz#0bd9b841c9e6c5a937c17656e2368f65da025588" + integrity sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA== + dependencies: + "@types/node" "*" + "@types/node@*", "@types/node@^20.8.9": version "20.8.9" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.8.9.tgz#646390b4fab269abce59c308fc286dcd818a2b08" @@ -109,6 +116,25 @@ arg@^4.1.0: resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +axios@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.1.tgz#76550d644bf0a2d469a01f9244db6753208397d7" + integrity sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g== + dependencies: + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== + buffer-more-ints@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-more-ints/-/buffer-more-ints-1.0.0.tgz#ef4f8e2dddbad429ed3828a9c55d44f05c611422" @@ -119,6 +145,13 @@ buffer-writer@2.0.0: resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04" integrity sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw== +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" @@ -136,6 +169,11 @@ debug@^4.3.4: dependencies: ms "2.1.2" +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" @@ -146,6 +184,27 @@ dotenv@^16.3.1: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + +follow-redirects@^1.15.0: + version "1.15.3" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" + integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + inherits@~2.0.1: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" @@ -156,16 +215,108 @@ isarray@0.0.1: resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== +jsonwebtoken@^9.0.2: + version "9.0.2" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz#65ff91f4abef1784697d40952bb1998c504caaf3" + integrity sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ== + dependencies: + jws "^3.2.2" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^7.5.4" + +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg== + +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA== + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw== + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== + +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + obuf@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" @@ -301,6 +452,11 @@ postgres-range@^1.1.1: resolved "https://registry.yarnpkg.com/postgres-range/-/postgres-range-1.1.3.tgz#9ccd7b01ca2789eb3c2e0888b3184225fa859f76" integrity sha512-VdlZoocy5lCP0c/t66xAfclglEapXPCIVhqqJRncYpvbCgImF0w67aPKfbqUMr72tO2k5q0TdTZwCLjPTI6C9g== +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + querystringify@^2.1.1: version "2.2.0" resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" @@ -321,11 +477,23 @@ requires-port@^1.0.0: resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== +safe-buffer@^5.0.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + safe-buffer@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +semver@^7.5.4: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + split2@^4.1.0: version "4.2.0" resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4" @@ -388,6 +556,11 @@ xtend@^4.0.0: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" From 3767c8fc03c5290484bfca81cb538034fcd4bd94 Mon Sep 17 00:00:00 2001 From: Sergio Gutierrez Villalba Date: Fri, 10 Nov 2023 14:33:05 +0100 Subject: [PATCH 02/18] refactor(cd): unify Dockerfile --- consumer.Dockerfile => Dockerfile | 2 +- producer.Dockerfile | 12 ------------ 2 files changed, 1 insertion(+), 13 deletions(-) rename consumer.Dockerfile => Dockerfile (85%) delete mode 100644 producer.Dockerfile diff --git a/consumer.Dockerfile b/Dockerfile similarity index 85% rename from consumer.Dockerfile rename to Dockerfile index 7bb64c3..b38d2fa 100644 --- a/consumer.Dockerfile +++ b/Dockerfile @@ -9,4 +9,4 @@ COPY . . RUN yarn && yarn build && yarn --production && yarn cache clean # Start server -CMD yarn start:prod:consumer \ No newline at end of file +CMD yarn start:prod diff --git a/producer.Dockerfile b/producer.Dockerfile deleted file mode 100644 index 25903e5..0000000 --- a/producer.Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM node:iron-slim -LABEL author="internxt" - -WORKDIR /app - -COPY . . - -# Install deps -RUN yarn && yarn build && yarn --production && yarn cache clean - -# Start server -CMD yarn start:prod:producer \ No newline at end of file From 4ac7962918c499f3df13628b1dd94e316b23448d Mon Sep 17 00:00:00 2001 From: Sergio Gutierrez Villalba Date: Fri, 10 Nov 2023 14:33:35 +0100 Subject: [PATCH 03/18] feat: delete-files task --- src/drive.ts | 41 ++++++++++ src/producer.ts | 2 +- src/tasks/file-deletion/index.ts | 135 +++++++++++++++++++++++++++++++ 3 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 src/tasks/file-deletion/index.ts diff --git a/src/drive.ts b/src/drive.ts index 56f44ab..522adc0 100644 --- a/src/drive.ts +++ b/src/drive.ts @@ -49,6 +49,47 @@ export class DriveDatabase { await this.client.end(); } + async getDeletedFiles(): Promise<{ + fileId: string; + processed: boolean, + createdAt: Date, + updatedAt: Date, + processedAt: Date, + }[]> { + const query = 'SELECT * FROM deleted_files WHERE processed = false AND enqueued = false LIMIT 100'; + + const result = await this.client.query(query); + + return result.rows.map(r => ({ + fileId: r.file_id, + processed: r.processed, + createdAt: r.created_at, + updatedAt: r.updated_at, + processedAt: r.processed_at, + networkFileId: r.network_file_id, + })); + } + + async setFilesAsEnqueued(fileIds: string[]): Promise { + const query = ` + UPDATE deleted_files + SET enqueued = true, enqueued_at = NOW(), updated_at = NOW() + WHERE file_id IN (${fileIds.map((fileIds) => `'${fileIds}'`).join(', ')}) + `; + + await this.client.query(query); + } + + async markDeletedFilesAsProcessed(uuids: string[]): Promise { + const query = ` + UPDATE deleted_files + SET processed = true, processed_at = NOW(), updated_at = NOW() + WHERE file_id IN (${uuids.map((uuid) => `'${uuid}'`).join(', ')}) + `; + + await this.client.query(query); + } + async getChildrenFoldersOfDeletedFolders(): Promise<{ folder_id: string, processed: boolean, diff --git a/src/producer.ts b/src/producer.ts index 271ce57..e38ef53 100644 --- a/src/producer.ts +++ b/src/producer.ts @@ -21,7 +21,7 @@ export class Producer extends EventEmitter { this.channel.sendToQueue( this.queueName, Buffer.from( - JSON.stringify(item), + (item as any[]).length ? JSON.stringify({ payload: item }) : JSON.stringify(item), ), ); diff --git a/src/tasks/file-deletion/index.ts b/src/tasks/file-deletion/index.ts new file mode 100644 index 0000000..5f37cd9 --- /dev/null +++ b/src/tasks/file-deletion/index.ts @@ -0,0 +1,135 @@ +import { v4 } from 'uuid'; +import axios, { AxiosRequestConfig } from 'axios'; +import { sign } from 'jsonwebtoken' + +import { createLogger } from '../../utils'; +import { Consumer } from '../../consumer'; +import { Producer } from '../../producer'; +import { DeletedFilesIterator } from './deleted-files.iterator'; +import { TaskFunction } from '../task'; + +const task: TaskFunction = async ( + processType, + drive, + connection, +) => { + const processId = v4(); + const logger = createLogger(processId); + + type DeleteFilesResponse = { + message: { + confirmed: string[], + notConfirmed: string[] + } + } + + function signToken(duration: string, secret: string) { + return sign( + {}, + Buffer.from(secret, 'base64').toString('utf8'), + { + algorithm: 'RS256', + expiresIn: duration + } + ); + } + + function deleteFiles(endpoint: string, fileIds: string[]): Promise { + const params: AxiosRequestConfig = { + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${signToken( + '5m', + process.env.NETWORK_GATEWAY_DELETE_FILES_SECRET as string + )}` + }, + data: { + files: fileIds + } + }; + + return axios.delete(endpoint, params) + .then((res) => res.data); + } + + const queueName = `${process.env.TASK_TYPE}-${process.env.NODE_ENV}`; + const maxEnqueuedItems = process.env.TASK_DELETE_FILES_PRODUCER_MAX_ENQUEUED_ITEMS; + const maxConcurrentItems = process.env.TASK_DELETE_FILES_CONSUMER_MAX_CONCURRENT_ITEMS; + + if (!maxEnqueuedItems) { + logger.log('Missing env var: TASK_MARK_DELETED_ITEMS_PRODUCER_MAX_ENQUEUED_ITEMS'); + process.exit(1); + } + + if (!maxConcurrentItems) { + logger.log('Missing env var: TASK_MARK_DELETED_ITEMS_CONSUMER_MAX_CONCURRENT_ITEMS'); + process.exit(1); + } + + logger.log(`params: process_type -> ${processType}, env -> ${ + JSON.stringify({ + maxConcurrentItems, + maxEnqueuedItems, + queueName + }) + }`); + + + if (processType === 'producer') { + const deletedFilesIterator = new DeletedFilesIterator(drive.db); + + return connection.createChannel().then((channel) => { + const producer = new Producer( + channel, + queueName as string, + deletedFilesIterator, + maxEnqueuedItems ? parseInt(maxEnqueuedItems as string) : undefined, + ); + + producer.on('enqueue', (item) => { + logger.log(`enqueued item: + ${JSON.stringify(item)}`, 'producer'); + }); + + producer.on('queue-full', () => { + logger.log(`queue full, waiting 1s...`, 'producer'); + }); + + return producer.run(); + }); + } else { + connection.createChannel().then((channel) => { + const consumer = new Consumer<{ + payload: { + fileId: string, + processed: boolean, + createdAt: Date, + updatedAt: Date, + processedAt: Date, + networkFileId: string, + }[] + }>( + channel, + queueName as string, + async (task) => { + logger.log(`received item: + ${JSON.stringify(task)}`, 'consumer'); + + const networkFileIdsToDelete = task.payload.map((file) => file.networkFileId); + const res = await deleteFiles(process.env.NETWORK_GATEWAY_DELETE_FILES_ENDPOINT as string, networkFileIdsToDelete); + const fileIdsDeletedSuccesfully = res.message.confirmed; + const filesToMarkAsDeleted = task.payload.filter((file) => fileIdsDeletedSuccesfully.includes(file.networkFileId)); + + await drive.db.markDeletedFilesAsProcessed(filesToMarkAsDeleted.map(f => f.fileId)); + }, + maxConcurrentItems ? parseInt(maxConcurrentItems as string) : undefined, + ); + + consumer.on('error', ({ err, msg }) => { + logger.error(`error processing item: ${JSON.stringify(msg.content)}`, err, 'consumer'); + }); + + consumer.run(); + }); + } +} + +export default task; From d4d5ae1bcb3c52cb5d9cba01ea4ecda287ecba05 Mon Sep 17 00:00:00 2001 From: Andres Pinto Date: Wed, 24 Dec 2025 13:47:48 +0100 Subject: [PATCH 04/18] feat: delete file versions --- src/drive.ts | 50 ++++++++++++++++++++++++++++++++ src/tasks/file-deletion/index.ts | 10 ++++++- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/drive.ts b/src/drive.ts index 522adc0..cabc93d 100644 --- a/src/drive.ts +++ b/src/drive.ts @@ -183,4 +183,54 @@ export class DriveDatabase { count = result.rowCount; } while (count === 1000); } + + /** + * Gets network file IDs for existing file versions in batches + * @param fileIds + */ + async getFileVersionsByFileId(fileIds: string[]): Promise< + { + id: string; + fileId: string; + networkFileId: string; + }[] + > { + const placeholders = fileIds.map((_, i) => `$${i + 1}`).join(", "); + const query = ` + SELECT network_file_id, file_id, id + FROM file_versions + WHERE file_id IN (${placeholders}) + AND status = 'EXISTS' + `; + + const result = await this.client.query(query, fileIds); + + return result.rows.map((r) => ({ + id: r.id, + networkFileId: r.network_file_id, + fileId: r.file_id, + })); + } + + + /** + * Mark file versions as deleted + * @param versionIds + */ + async markFileVersionsAsDeleted(versionIds: string[]): Promise { + const placeholders = versionIds.map((_, i) => `$${i + 1}`).join(", "); + if (placeholders.length === 0) { + return 0; + } + + const query = ` + UPDATE file_versions + SET status = 'DELETED', updated_at = NOW() + WHERE id IN (${placeholders}) + AND status = 'EXISTS' + `; + const result = await this.client.query(query, versionIds); + + return result.rowCount; + } } \ No newline at end of file diff --git a/src/tasks/file-deletion/index.ts b/src/tasks/file-deletion/index.ts index 5f37cd9..dc195e3 100644 --- a/src/tasks/file-deletion/index.ts +++ b/src/tasks/file-deletion/index.ts @@ -114,11 +114,19 @@ const task: TaskFunction = async ( logger.log(`received item: + ${JSON.stringify(task)}`, 'consumer'); const networkFileIdsToDelete = task.payload.map((file) => file.networkFileId); - const res = await deleteFiles(process.env.NETWORK_GATEWAY_DELETE_FILES_ENDPOINT as string, networkFileIdsToDelete); + const fileIdsToDelete = task.payload.map((file) => file.fileId); + const fileVersionsData = await drive.db.getFileVersionsByFileId(fileIdsToDelete); + const networkVersionFileIdsToDelete = fileVersionsData.map(fv => fv.networkFileId); + + const aggregatedNetworkFileIdsToDelete = networkFileIdsToDelete.concat(networkVersionFileIdsToDelete); + + const res = await deleteFiles(process.env.NETWORK_GATEWAY_DELETE_FILES_ENDPOINT as string, aggregatedNetworkFileIdsToDelete); const fileIdsDeletedSuccesfully = res.message.confirmed; const filesToMarkAsDeleted = task.payload.filter((file) => fileIdsDeletedSuccesfully.includes(file.networkFileId)); + const fileVersionsToMarkAsDeleted = fileVersionsData.filter((fileNetwork) => fileIdsDeletedSuccesfully.includes(fileNetwork.networkFileId)); await drive.db.markDeletedFilesAsProcessed(filesToMarkAsDeleted.map(f => f.fileId)); + await drive.db.markFileVersionsAsDeleted(fileVersionsToMarkAsDeleted.map(fv => fv.id)); }, maxConcurrentItems ? parseInt(maxConcurrentItems as string) : undefined, ); From 57e402cd2f7750bdc21438bfdded028f082e4b4d Mon Sep 17 00:00:00 2001 From: Andres Pinto Date: Fri, 26 Dec 2025 15:24:48 +0100 Subject: [PATCH 05/18] refactor: change name --- src/tasks/file-deletion/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tasks/file-deletion/index.ts b/src/tasks/file-deletion/index.ts index dc195e3..0ea0b79 100644 --- a/src/tasks/file-deletion/index.ts +++ b/src/tasks/file-deletion/index.ts @@ -123,7 +123,7 @@ const task: TaskFunction = async ( const res = await deleteFiles(process.env.NETWORK_GATEWAY_DELETE_FILES_ENDPOINT as string, aggregatedNetworkFileIdsToDelete); const fileIdsDeletedSuccesfully = res.message.confirmed; const filesToMarkAsDeleted = task.payload.filter((file) => fileIdsDeletedSuccesfully.includes(file.networkFileId)); - const fileVersionsToMarkAsDeleted = fileVersionsData.filter((fileNetwork) => fileIdsDeletedSuccesfully.includes(fileNetwork.networkFileId)); + const fileVersionsToMarkAsDeleted = fileVersionsData.filter((fileVersion) => fileIdsDeletedSuccesfully.includes(fileVersion.networkFileId)); await drive.db.markDeletedFilesAsProcessed(filesToMarkAsDeleted.map(f => f.fileId)); await drive.db.markFileVersionsAsDeleted(fileVersionsToMarkAsDeleted.map(fv => fv.id)); From b2bc2fb5012e22b9b9caee918441222ac1e3959a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Guti=C3=A9rrez?= <34506328+sg-gs@users.noreply.github.com> Date: Fri, 26 Dec 2025 15:53:47 +0100 Subject: [PATCH 06/18] Add 'feat/delete-files' branch to workflow triggers --- .github/workflows/deploy-and-deploy-consumer.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-and-deploy-consumer.yaml b/.github/workflows/deploy-and-deploy-consumer.yaml index 34b005a..97636e0 100644 --- a/.github/workflows/deploy-and-deploy-consumer.yaml +++ b/.github/workflows/deploy-and-deploy-consumer.yaml @@ -1,7 +1,7 @@ name: build & deploy on: push: - branches: ["master", "feature/cd"] + branches: ["master", "feature/cd", "feat/delete-files"] jobs: build: runs-on: ubuntu-latest @@ -40,4 +40,4 @@ jobs: with: config: ${{ secrets.KUBE_CONFIG_DATA }} version: v1.27.4 - command: rollout status deployment/background-tasks-consumer \ No newline at end of file + command: rollout status deployment/background-tasks-consumer From cd19c0985683d62426ecc26b7ba035f50013c732 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Guti=C3=A9rrez?= <34506328+sg-gs@users.noreply.github.com> Date: Fri, 26 Dec 2025 15:58:09 +0100 Subject: [PATCH 07/18] Fix Dockerfile path in GitHub Actions workflow --- .github/workflows/deploy-and-deploy-consumer.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-and-deploy-consumer.yaml b/.github/workflows/deploy-and-deploy-consumer.yaml index 97636e0..555fff6 100644 --- a/.github/workflows/deploy-and-deploy-consumer.yaml +++ b/.github/workflows/deploy-and-deploy-consumer.yaml @@ -18,8 +18,8 @@ jobs: - name: Build and push to DockerHub uses: docker/build-push-action@v5 with: - context: ./ - file: ./consumer.Dockerfile + context: . + file: consumer.Dockerfile push: true tags: ${{ secrets.DOCKERHUB_USERNAME }}/drive-background-tasks-consumer:${{ github.sha }} deploy: From 0913e9c2936b129388465a6aa1a054ecaaea48aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Guti=C3=A9rrez?= <34506328+sg-gs@users.noreply.github.com> Date: Fri, 26 Dec 2025 15:59:42 +0100 Subject: [PATCH 08/18] Fix Dockerfile path in GitHub Actions workflow --- .github/workflows/deploy-and-deploy-consumer.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-and-deploy-consumer.yaml b/.github/workflows/deploy-and-deploy-consumer.yaml index 555fff6..723bf50 100644 --- a/.github/workflows/deploy-and-deploy-consumer.yaml +++ b/.github/workflows/deploy-and-deploy-consumer.yaml @@ -19,7 +19,7 @@ jobs: uses: docker/build-push-action@v5 with: context: . - file: consumer.Dockerfile + file: ./consumer.Dockerfile push: true tags: ${{ secrets.DOCKERHUB_USERNAME }}/drive-background-tasks-consumer:${{ github.sha }} deploy: From 0c92621ffe7a716050dac83c94b7c614bd0bf0d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Guti=C3=A9rrez?= <34506328+sg-gs@users.noreply.github.com> Date: Fri, 26 Dec 2025 16:01:52 +0100 Subject: [PATCH 09/18] Add Dockerfile for consumer application --- consumer.Dockerfile | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 consumer.Dockerfile diff --git a/consumer.Dockerfile b/consumer.Dockerfile new file mode 100644 index 0000000..614e329 --- /dev/null +++ b/consumer.Dockerfile @@ -0,0 +1,12 @@ +FROM node:24 +LABEL author="internxt" + +WORKDIR /app + +COPY . . + +# Install deps +RUN yarn && yarn build && yarn --production && yarn cache clean + +# Start server +CMD yarn start:prod:consumer From b96d128cbfe61accea3a5847a62617f58572ccf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Guti=C3=A9rrez?= <34506328+sg-gs@users.noreply.github.com> Date: Fri, 26 Dec 2025 16:16:39 +0100 Subject: [PATCH 10/18] Rename workflow and update Docker image references --- ...eploy-consumer.yaml => build-and-deploy-consumer.yaml} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename .github/workflows/{deploy-and-deploy-consumer.yaml => build-and-deploy-consumer.yaml} (74%) diff --git a/.github/workflows/deploy-and-deploy-consumer.yaml b/.github/workflows/build-and-deploy-consumer.yaml similarity index 74% rename from .github/workflows/deploy-and-deploy-consumer.yaml rename to .github/workflows/build-and-deploy-consumer.yaml index 723bf50..9ca7df8 100644 --- a/.github/workflows/deploy-and-deploy-consumer.yaml +++ b/.github/workflows/build-and-deploy-consumer.yaml @@ -1,4 +1,4 @@ -name: build & deploy +name: Build and Deploy to Production on: push: branches: ["master", "feature/cd", "feat/delete-files"] @@ -21,7 +21,7 @@ jobs: context: . file: ./consumer.Dockerfile push: true - tags: ${{ secrets.DOCKERHUB_USERNAME }}/drive-background-tasks-consumer:${{ github.sha }} + tags: ${{ secrets.DOCKERHUB_USERNAME }}/background-tasks:${{ github.sha }} deploy: needs: build runs-on: ubuntu-latest @@ -34,10 +34,10 @@ jobs: with: config: ${{ secrets.KUBE_CONFIG_DATA }} version: v1.27.4 - command: set image --record deployment/background-tasks-consumer background-tasks-consumer=${{ secrets.DOCKERHUB_USERNAME }}/drive-background-tasks-consumer:${{ github.sha }} + command: set image --record deployment/delete-files-consumer delete-files-consumer=${{ secrets.DOCKERHUB_USERNAME }}/background-tasks:${{ github.sha }} - name: Verify successful deployment uses: steebchen/kubectl@v2.0.0 with: config: ${{ secrets.KUBE_CONFIG_DATA }} version: v1.27.4 - command: rollout status deployment/background-tasks-consumer + command: rollout status deployment/delete-files-consumer From 0a84f853ab7f9072080a8aa5db6750d99bafd6a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Guti=C3=A9rrez?= <34506328+sg-gs@users.noreply.github.com> Date: Fri, 26 Dec 2025 16:19:05 +0100 Subject: [PATCH 11/18] Update Dockerfile path in CI workflow --- .github/workflows/build-and-deploy-consumer.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-and-deploy-consumer.yaml b/.github/workflows/build-and-deploy-consumer.yaml index 9ca7df8..d8bddcb 100644 --- a/.github/workflows/build-and-deploy-consumer.yaml +++ b/.github/workflows/build-and-deploy-consumer.yaml @@ -19,7 +19,7 @@ jobs: uses: docker/build-push-action@v5 with: context: . - file: ./consumer.Dockerfile + file: ./Dockerfile push: true tags: ${{ secrets.DOCKERHUB_USERNAME }}/background-tasks:${{ github.sha }} deploy: From 7efc58a3cc655ae569a16de0f837efc547004109 Mon Sep 17 00:00:00 2001 From: dougama Date: Wed, 14 Jan 2026 15:40:06 +0100 Subject: [PATCH 12/18] refactor(delete-files): remove version deletion from network --- src/tasks/file-deletion/index.ts | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/tasks/file-deletion/index.ts b/src/tasks/file-deletion/index.ts index 0ea0b79..287afdd 100644 --- a/src/tasks/file-deletion/index.ts +++ b/src/tasks/file-deletion/index.ts @@ -115,18 +115,15 @@ const task: TaskFunction = async ( const networkFileIdsToDelete = task.payload.map((file) => file.networkFileId); const fileIdsToDelete = task.payload.map((file) => file.fileId); - const fileVersionsData = await drive.db.getFileVersionsByFileId(fileIdsToDelete); - const networkVersionFileIdsToDelete = fileVersionsData.map(fv => fv.networkFileId); - const aggregatedNetworkFileIdsToDelete = networkFileIdsToDelete.concat(networkVersionFileIdsToDelete); - - const res = await deleteFiles(process.env.NETWORK_GATEWAY_DELETE_FILES_ENDPOINT as string, aggregatedNetworkFileIdsToDelete); - const fileIdsDeletedSuccesfully = res.message.confirmed; - const filesToMarkAsDeleted = task.payload.filter((file) => fileIdsDeletedSuccesfully.includes(file.networkFileId)); - const fileVersionsToMarkAsDeleted = fileVersionsData.filter((fileVersion) => fileIdsDeletedSuccesfully.includes(fileVersion.networkFileId)); + const res = await deleteFiles(process.env.NETWORK_GATEWAY_DELETE_FILES_ENDPOINT as string, networkFileIdsToDelete); + const fileIdsDeletedSuccessfully = res.message.confirmed; + const filesToMarkAsProcessed = task.payload.filter((file) => fileIdsDeletedSuccessfully.includes(file.networkFileId)); + + await drive.db.markDeletedFilesAsProcessed(filesToMarkAsProcessed.map(f => f.fileId)); - await drive.db.markDeletedFilesAsProcessed(filesToMarkAsDeleted.map(f => f.fileId)); - await drive.db.markFileVersionsAsDeleted(fileVersionsToMarkAsDeleted.map(fv => fv.id)); + const fileVersionsData = await drive.db.getFileVersionsByFileId(fileIdsToDelete); + await drive.db.markFileVersionsAsDeleted(fileVersionsData.map(fv => fv.id)); }, maxConcurrentItems ? parseInt(maxConcurrentItems as string) : undefined, ); From ac9953b66e9a6df2a70fca70c3a93e65019a3b98 Mon Sep 17 00:00:00 2001 From: dougama Date: Wed, 14 Jan 2026 21:10:16 +0100 Subject: [PATCH 13/18] refactor: extract network deletion logic --- src/network.ts | 38 +++++++++++++++++++++++++++++++ src/tasks/file-deletion/index.ts | 39 +------------------------------- 2 files changed, 39 insertions(+), 38 deletions(-) create mode 100644 src/network.ts diff --git a/src/network.ts b/src/network.ts new file mode 100644 index 0000000..695c724 --- /dev/null +++ b/src/network.ts @@ -0,0 +1,38 @@ +import axios, { AxiosRequestConfig } from 'axios'; +import { sign } from 'jsonwebtoken'; + +export type DeleteFilesResponse = { + message: { + confirmed: string[], + notConfirmed: string[] + } +} + +export function signToken(duration: string, secret: string): string { + return sign( + {}, + Buffer.from(secret, 'base64').toString('utf8'), + { + algorithm: 'RS256', + expiresIn: duration + } + ); +} + +export function deleteFiles(endpoint: string, fileIds: string[]): Promise { + const params: AxiosRequestConfig = { + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${signToken( + '5m', + process.env.NETWORK_GATEWAY_DELETE_FILES_SECRET as string + )}` + }, + data: { + files: fileIds + } + }; + + return axios.delete(endpoint, params) + .then((res) => res.data); +} diff --git a/src/tasks/file-deletion/index.ts b/src/tasks/file-deletion/index.ts index 287afdd..6221eca 100644 --- a/src/tasks/file-deletion/index.ts +++ b/src/tasks/file-deletion/index.ts @@ -1,12 +1,11 @@ import { v4 } from 'uuid'; -import axios, { AxiosRequestConfig } from 'axios'; -import { sign } from 'jsonwebtoken' import { createLogger } from '../../utils'; import { Consumer } from '../../consumer'; import { Producer } from '../../producer'; import { DeletedFilesIterator } from './deleted-files.iterator'; import { TaskFunction } from '../task'; +import { deleteFiles } from '../../network'; const task: TaskFunction = async ( processType, @@ -16,42 +15,6 @@ const task: TaskFunction = async ( const processId = v4(); const logger = createLogger(processId); - type DeleteFilesResponse = { - message: { - confirmed: string[], - notConfirmed: string[] - } - } - - function signToken(duration: string, secret: string) { - return sign( - {}, - Buffer.from(secret, 'base64').toString('utf8'), - { - algorithm: 'RS256', - expiresIn: duration - } - ); - } - - function deleteFiles(endpoint: string, fileIds: string[]): Promise { - const params: AxiosRequestConfig = { - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${signToken( - '5m', - process.env.NETWORK_GATEWAY_DELETE_FILES_SECRET as string - )}` - }, - data: { - files: fileIds - } - }; - - return axios.delete(endpoint, params) - .then((res) => res.data); - } - const queueName = `${process.env.TASK_TYPE}-${process.env.NODE_ENV}`; const maxEnqueuedItems = process.env.TASK_DELETE_FILES_PRODUCER_MAX_ENQUEUED_ITEMS; const maxConcurrentItems = process.env.TASK_DELETE_FILES_CONSUMER_MAX_CONCURRENT_ITEMS; From 0a1f40580f2f367c72bf1f10556fa1a8609c4d31 Mon Sep 17 00:00:00 2001 From: dougama Date: Thu, 15 Jan 2026 00:58:18 +0100 Subject: [PATCH 14/18] feat: add task for file version deletion --- src/drive.ts | 74 +++++++++++++ .../deleted-file-versions.iterator.ts | 38 +++++++ src/tasks/file-version-deletion/index.ts | 102 ++++++++++++++++++ src/tasks/index.ts | 2 + 4 files changed, 216 insertions(+) create mode 100644 src/tasks/file-version-deletion/deleted-file-versions.iterator.ts create mode 100644 src/tasks/file-version-deletion/index.ts diff --git a/src/drive.ts b/src/drive.ts index cabc93d..6339628 100644 --- a/src/drive.ts +++ b/src/drive.ts @@ -233,4 +233,78 @@ export class DriveDatabase { return result.rowCount; } + + /** + * Gets deleted file versions pending processing + * @returns Array of deleted file versions + */ + async getDeletedFileVersions(): Promise<{ + fileVersionId: string; + fileId: string; + networkFileId: string; + size: bigint; + processed: boolean; + enqueued: boolean; + createdAt: Date; + updatedAt: Date; + processedAt: Date; + }[]> { + const query = ` + SELECT + file_version_id, + file_id, + network_file_id, + size, + processed, + enqueued, + created_at, + updated_at, + processed_at + FROM deleted_file_versions + WHERE processed = false AND enqueued = false + LIMIT 100 + `; + + const result = await this.client.query(query); + + return result.rows.map(r => ({ + fileVersionId: r.file_version_id, + fileId: r.file_id, + networkFileId: r.network_file_id, + size: r.size, + processed: r.processed, + enqueued: r.enqueued, + createdAt: r.created_at, + updatedAt: r.updated_at, + processedAt: r.processed_at, + })); + } + + /** + * Mark file versions as enqueued for deletion + * @param versionIds + */ + async setFileVersionsAsEnqueued(versionIds: string[]): Promise { + const placeholders = versionIds.map((_, i) => `$${i + 1}`).join(", "); + const query = ` + UPDATE deleted_file_versions + SET enqueued = true, enqueued_at = NOW(), updated_at = NOW() + WHERE file_version_id IN (${placeholders}) + `; + await this.client.query(query, versionIds); + } + + /** + * Mark deleted file versions as processed after successful deletion from network + * @param versionIds + */ + async markDeletedFileVersionsAsProcessed(versionIds: string[]): Promise { + const placeholders = versionIds.map((_, i) => `$${i + 1}`).join(", "); + const query = ` + UPDATE deleted_file_versions + SET processed = true, processed_at = NOW(), updated_at = NOW() + WHERE file_version_id IN (${placeholders}) + `; + await this.client.query(query, versionIds); + } } \ No newline at end of file diff --git a/src/tasks/file-version-deletion/deleted-file-versions.iterator.ts b/src/tasks/file-version-deletion/deleted-file-versions.iterator.ts new file mode 100644 index 0000000..70b776c --- /dev/null +++ b/src/tasks/file-version-deletion/deleted-file-versions.iterator.ts @@ -0,0 +1,38 @@ +import { DriveDatabase } from "../../drive"; + +export class DeletedFileVersionsIterator { + constructor(private readonly db: DriveDatabase) {} + + async * [Symbol.asyncIterator]() { + let rows : { + fileVersionId: string, + fileId: string, + networkFileId: string; + size: bigint, + processed: boolean, + enqueued: boolean, + createdAt: Date, + updatedAt: Date, + processedAt: Date, + }[] = []; + let n = 50; + + do { + const rows = await this.db.getDeletedFileVersions(); + if (rows.length === 0) { + console.log('No file versions to process, waiting 1s...'); + await new Promise(resolve => setTimeout(resolve, 1000)); + } else { + await this.db.setFileVersionsAsEnqueued(rows.map(row => row.fileVersionId)); + while (rows.length >= n) { + const chunk = rows.splice(0, n); + yield chunk; + } + + if (rows.length > 0) { + yield rows; + } + } + } while (true); + } +} diff --git a/src/tasks/file-version-deletion/index.ts b/src/tasks/file-version-deletion/index.ts new file mode 100644 index 0000000..badc4a2 --- /dev/null +++ b/src/tasks/file-version-deletion/index.ts @@ -0,0 +1,102 @@ +import { v4 } from 'uuid'; + +import { createLogger } from '../../utils'; +import { Consumer } from '../../consumer'; +import { Producer } from '../../producer'; +import { DeletedFileVersionsIterator } from './deleted-file-versions.iterator'; +import { TaskFunction } from '../task'; +import { deleteFiles } from '../../network'; + +const task: TaskFunction = async ( + processType, + drive, + connection, +) => { + const processId = v4(); + const logger = createLogger(processId); + + const queueName = `${process.env.TASK_TYPE}-${process.env.NODE_ENV}`; + const maxEnqueuedItems = process.env.TASK_DELETE_FILE_VERSIONS_PRODUCER_MAX_ENQUEUED_ITEMS; + const maxConcurrentItems = process.env.TASK_DELETE_FILE_VERSIONS_CONSUMER_MAX_CONCURRENT_ITEMS; + + if (!maxEnqueuedItems) { + logger.log('Missing env var: TASK_DELETE_FILE_VERSIONS_PRODUCER_MAX_ENQUEUED_ITEMS'); + process.exit(1); + } + + if (!maxConcurrentItems) { + logger.log('Missing env var: TASK_DELETE_FILE_VERSIONS_CONSUMER_MAX_CONCURRENT_ITEMS'); + process.exit(1); + } + + logger.log(`params: process_type -> ${processType}, env -> ${ + JSON.stringify({ + maxConcurrentItems, + maxEnqueuedItems, + queueName + }) + }`); + + + if (processType === 'producer') { + const deletedFileVersionsIterator = new DeletedFileVersionsIterator(drive.db); + + return connection.createChannel().then((channel) => { + const producer = new Producer( + channel, + queueName as string, + deletedFileVersionsIterator, + maxEnqueuedItems ? parseInt(maxEnqueuedItems as string) : undefined, + ); + + producer.on('enqueue', (item) => { + logger.log(`enqueued item: + ${JSON.stringify(item)}`, 'producer'); + }); + + producer.on('queue-full', () => { + logger.log(`queue full, waiting 1s...`, 'producer'); + }); + + return producer.run(); + }); + } else { + connection.createChannel().then((channel) => { + const consumer = new Consumer<{ + payload: { + fileVersionId: string, + fileId: string, + networkFileId: string, + size: bigint, + processed: boolean, + enqueued: boolean, + createdAt: Date, + updatedAt: Date, + processedAt: Date, + }[] + }>( + channel, + queueName as string, + async (task) => { + logger.log(`received item: + ${JSON.stringify(task)}`, 'consumer'); + + const networkFileIdsToDelete = task.payload.map((version) => version.networkFileId); + + const res = await deleteFiles(process.env.NETWORK_GATEWAY_DELETE_FILES_ENDPOINT as string, networkFileIdsToDelete); + const versionIdsDeletedSuccessfully = res.message.confirmed; + const versionsToMarkAsProcessed = task.payload.filter((version) => versionIdsDeletedSuccessfully.includes(version.networkFileId)); + + await drive.db.markDeletedFileVersionsAsProcessed(versionsToMarkAsProcessed.map(v => v.fileVersionId)); + }, + maxConcurrentItems ? parseInt(maxConcurrentItems as string) : undefined, + ); + + consumer.on('error', ({ err, msg }) => { + logger.error(`error processing item: ${JSON.stringify(msg.content)}`, err, 'consumer'); + }); + + consumer.run(); + }); + } +} + +export default task; diff --git a/src/tasks/index.ts b/src/tasks/index.ts index 73952ff..356eb8e 100644 --- a/src/tasks/index.ts +++ b/src/tasks/index.ts @@ -2,10 +2,12 @@ import { TaskFunction } from './task'; import fileDeletion from './file-deletion'; import folderDeletion from './folder-deletion'; +import fileVersionDeletion from './file-version-deletion'; export const tasks: Record = { 'delete-files': fileDeletion, 'delete-folders': folderDeletion, + 'delete-file-versions': fileVersionDeletion, }; export const taskTypes = Object.keys(tasks); From 58f26308b58913732b5092d5ec48bb639048f97d Mon Sep 17 00:00:00 2001 From: dougama Date: Mon, 2 Feb 2026 15:21:39 +0100 Subject: [PATCH 15/18] refactor(drive): simplify query in getDeletedFileVersions --- src/drive.ts | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/drive.ts b/src/drive.ts index 6339628..5bf7d3d 100644 --- a/src/drive.ts +++ b/src/drive.ts @@ -249,21 +249,7 @@ export class DriveDatabase { updatedAt: Date; processedAt: Date; }[]> { - const query = ` - SELECT - file_version_id, - file_id, - network_file_id, - size, - processed, - enqueued, - created_at, - updated_at, - processed_at - FROM deleted_file_versions - WHERE processed = false AND enqueued = false - LIMIT 100 - `; + const query = 'SELECT * FROM deleted_file_versions WHERE processed = false AND enqueued = false LIMIT 100'; const result = await this.client.query(query); From 11e9aa82dee2c121cce58449393650b9672a793c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Guti=C3=A9rrez?= <34506328+sg-gs@users.noreply.github.com> Date: Wed, 4 Feb 2026 13:17:44 +0100 Subject: [PATCH 16/18] Add delete file versions deployment --- .../workflows/build-and-deploy-producer.yaml | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-and-deploy-producer.yaml b/.github/workflows/build-and-deploy-producer.yaml index fb2531f..26e49c4 100644 --- a/.github/workflows/build-and-deploy-producer.yaml +++ b/.github/workflows/build-and-deploy-producer.yaml @@ -1,7 +1,7 @@ name: build & deploy on: push: - branches: ["master", "feature/cd"] + branches: ["master", "feature/cd", "feat/delete-files"] jobs: build: runs-on: ubuntu-latest @@ -29,15 +29,27 @@ jobs: name: production steps: - uses: actions/checkout@master - - name: Update deployment image + - name: Update delete files/folders deployment image uses: steebchen/kubectl@v2.0.0 with: config: ${{ secrets.KUBE_CONFIG_DATA }} version: v1.27.4 - command: set image --record deployment/background-tasks-producer background-tasks-producer=${{ secrets.DOCKERHUB_USERNAME }}/drive-background-tasks-producer:${{ github.sha }} + command: set image --record deployment/background-tasks-producer background-tasks-producer=${{ secrets.DOCKERHUB_USERNAME }}/drive-background-tasks:${{ github.sha }} - name: Verify succesful deployment uses: steebchen/kubectl@v2.0.0 with: config: ${{ secrets.KUBE_CONFIG_DATA }} version: v1.27.4 - command: rollout status deployment/background-tasks-producer \ No newline at end of file + command: rollout status deployment/background-tasks-producer + - name: Update delete file versions deployment image + uses: steebchen/kubectl@v2.0.0 + with: + config: ${{ secrets.KUBE_CONFIG_DATA }} + version: v1.27.4 + command: set image --record deployment/delete-file-versions-producer delete-file-versions-producer=${{ secrets.DOCKERHUB_USERNAME }}/background-tasks:${{ github.sha }} + - name: Verify succesful deployment + uses: steebchen/kubectl@v2.0.0 + with: + config: ${{ secrets.KUBE_CONFIG_DATA }} + version: v1.27.4 + command: rollout status deployment/delete-file-versions-producer From 62c61b077251bf96d7ecf90aabf3d1cd0a4cbe9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Guti=C3=A9rrez?= <34506328+sg-gs@users.noreply.github.com> Date: Wed, 4 Feb 2026 13:22:52 +0100 Subject: [PATCH 17/18] Change Dockerfile reference and adjust deployment steps Updated Dockerfile path and modified deployment steps. --- .../workflows/build-and-deploy-producer.yaml | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build-and-deploy-producer.yaml b/.github/workflows/build-and-deploy-producer.yaml index 26e49c4..4d95698 100644 --- a/.github/workflows/build-and-deploy-producer.yaml +++ b/.github/workflows/build-and-deploy-producer.yaml @@ -19,28 +19,28 @@ jobs: uses: docker/build-push-action@v5 with: context: ./ - file: ./producer.Dockerfile + file: ./Dockerfile push: true - tags: ${{ secrets.DOCKERHUB_USERNAME }}/drive-background-tasks-producer:${{ github.sha }} + tags: ${{ secrets.DOCKERHUB_USERNAME }}/background-tasks:${{ github.sha }} deploy: needs: build runs-on: ubuntu-latest environment: name: production steps: - - uses: actions/checkout@master - - name: Update delete files/folders deployment image - uses: steebchen/kubectl@v2.0.0 - with: - config: ${{ secrets.KUBE_CONFIG_DATA }} - version: v1.27.4 - command: set image --record deployment/background-tasks-producer background-tasks-producer=${{ secrets.DOCKERHUB_USERNAME }}/drive-background-tasks:${{ github.sha }} - - name: Verify succesful deployment - uses: steebchen/kubectl@v2.0.0 - with: - config: ${{ secrets.KUBE_CONFIG_DATA }} - version: v1.27.4 - command: rollout status deployment/background-tasks-producer + # - uses: actions/checkout@master + # - name: Update delete files/folders deployment image + # uses: steebchen/kubectl@v2.0.0 + # with: + # config: ${{ secrets.KUBE_CONFIG_DATA }} + # version: v1.27.4 + # command: set image --record deployment/background-tasks-producer background-tasks-producer=${{ secrets.DOCKERHUB_USERNAME }}/drive-background-tasks:${{ github.sha }} + # - name: Verify succesful deployment + # uses: steebchen/kubectl@v2.0.0 + # with: + # config: ${{ secrets.KUBE_CONFIG_DATA }} + # version: v1.27.4 + # command: rollout status deployment/background-tasks-producer - name: Update delete file versions deployment image uses: steebchen/kubectl@v2.0.0 with: From 8ffa6da6c74bd5775a3c47c73cea449478f0f6cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Guti=C3=A9rrez?= <34506328+sg-gs@users.noreply.github.com> Date: Wed, 4 Feb 2026 13:28:51 +0100 Subject: [PATCH 18/18] Rename workflow and update deployment steps Including file versions consumer deployment update --- .github/workflows/build-and-deploy-consumer.yaml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-and-deploy-consumer.yaml b/.github/workflows/build-and-deploy-consumer.yaml index d8bddcb..09964e6 100644 --- a/.github/workflows/build-and-deploy-consumer.yaml +++ b/.github/workflows/build-and-deploy-consumer.yaml @@ -1,4 +1,4 @@ -name: Build and Deploy to Production +name: Build and Deploy Consumer to Production on: push: branches: ["master", "feature/cd", "feat/delete-files"] @@ -29,7 +29,7 @@ jobs: name: production steps: - uses: actions/checkout@master - - name: Update deployment image + - name: Update delete items deployment image uses: steebchen/kubectl@v2.0.0 with: config: ${{ secrets.KUBE_CONFIG_DATA }} @@ -41,3 +41,15 @@ jobs: config: ${{ secrets.KUBE_CONFIG_DATA }} version: v1.27.4 command: rollout status deployment/delete-files-consumer + - name: Update delete file versions deployment image + uses: steebchen/kubectl@v2.0.0 + with: + config: ${{ secrets.KUBE_CONFIG_DATA }} + version: v1.27.4 + command: set image --record deployment/delete-file-versions-consumer delete-file-versions-consumer=${{ secrets.DOCKERHUB_USERNAME }}/background-tasks:${{ github.sha }} + - name: Verify successful deployment + uses: steebchen/kubectl@v2.0.0 + with: + config: ${{ secrets.KUBE_CONFIG_DATA }} + version: v1.27.4 + command: rollout status deployment/delete-file-versions-consumer