diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000000000000000000000000000000000..321c3475d6f10c524b8cf6e446201d97b84fb6b7 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,49 @@ +# docker +Dockerfile* +docker-compose* + +# git +.git +.gitignore + +# npm & yarn +node_modules +npm-debug.log +package-lock.json +yarn-error.log + +# misc +.devcontainer +.DS_Store +.vscode +app.pid + +# environment files +.env* +*.env + +# app +_build/ +config/local* +data/ +coverage/ +cypress/ +logs/ +stories/ +tmp/ +uploads/ +templates + +# Include README.md and LICENSE.md, exlcude the rest +*.md +!README.md +!LICENSE.md + +# development config files +# .eslintrc.js +.commitlintrc.js +.cz-config.js +.gitlab-ci.yml +.linstagedrc.js +# .prettierrc.js +.stylelintrc.js \ No newline at end of file diff --git a/config/custom-environment-variables.js b/config/custom-environment-variables.js index e121ec6ff70d83cdb7c7e1f21cdd8eaf7fbdfdd5..b73750904cca89cb88a164520ad3a9edbda5536c 100644 --- a/config/custom-environment-variables.js +++ b/config/custom-environment-variables.js @@ -22,6 +22,7 @@ module.exports = { minioConsolePort: 'MINIO_CONSOLE_PORT', maximumWidthForSmallImages: 'MAXIMUM_WIDTH_FOR_SMALL_IMAGES', maximumWidthForMediumImages: 'MAXIMUM_WIDTH_FOR_MEDIUM_IMAGES', + s3SeparateDeleteOperations: 'S3_SEPARATE_DELETE_OPERATIONS', }, chatGPT: { key: 'CHAT_GPT_KEY', diff --git a/config/default.js b/config/default.js index 26e09261e7f8f44c90481eb6362f57f11ecaa12a..247b323512d9293759e4487cf9968049b0c3edcf 100644 --- a/config/default.js +++ b/config/default.js @@ -60,5 +60,6 @@ module.exports = { host: 'localhost', port: '9000', minioConsolePort: '9001', + s3SeparateDeleteOperations: false, }, } diff --git a/src/__tests__/job.test.js b/src/__tests__/job.test.js index 3c95ba162c7c9470c412b07f4c5e03cc0d26023a..0d6aead5726a6cf814156b777e4a20f4d716836d 100644 --- a/src/__tests__/job.test.js +++ b/src/__tests__/job.test.js @@ -1,7 +1,7 @@ const { boss } = require('pubsweet-server/src/jobs') const { subscribeJobsToQueue } = require('../jobs') const { jobs } = require('../services') -const { renewAuthTokens } = require('../utils/tokens') +// const { renewAuthTokens } = require('../utils/tokens') const freezeTime = 1701856542000 const daySeconds = 24 * 3600 @@ -56,6 +56,16 @@ jest.mock('../utils/tokens', () => { } }) +jest.mock('../models/user/user.controller', () => { + const originalModule = jest.requireActual('../models/user/user.controller') + return { + __esModule: true, + ...originalModule, + getUser: jest.fn(async userId => ({ + id: userId, + })), + } +}) // Mock the date and time Date.now = jest.fn(() => freezeTime) @@ -77,42 +87,10 @@ describe('jobs service', () => { it('registers jobs', async () => { expect(Object.keys(boss.subscriptions)).toEqual([ - jobs.RENEW_AUTH_TOKENS_JOB, + jobs.REFRESH_TOKEN_EXPIRED, ]) expect( - typeof boss.subscriptions[jobs.RENEW_AUTH_TOKENS_JOB].callback, + typeof boss.subscriptions[jobs.REFRESH_TOKEN_EXPIRED].callback, ).toEqual('function') }) - - it('reschedules auth token renewal after successfully renewing the refresh token', async () => { - boss.log = [] - - // Run the job callback directly and then verify its behaviour - const renewCallback = - boss.subscriptions[jobs.RENEW_AUTH_TOKENS_JOB].callback - - const job = dummyJob( - { userId: 'fakeUserId', providerLabel: 'fakeProviderLabel' }, - {}, - ) - - await renewCallback(job) - - // renewAuthTokens should have been called - expect(renewAuthTokens.mock.calls.length).toEqual(1) - expect(renewAuthTokens.mock.calls[0]).toEqual([ - 'fakeUserId', - 'fakeProviderLabel', - ]) - - // Job should succeed and be marked done - expect(job.isDone).toBe(true) - - // Job should schedule a future job - expect(boss.log).toEqual([`publish ${jobs.RENEW_AUTH_TOKENS_JOB}`]) - expect(boss.lastJob.data).toEqual(job.data) - expect(Object.keys(boss.lastJob.options).length).toEqual(1) - // Refresh token expires in 7 days and must be renewed in 6 - expect(boss.lastJob.options.startAfter).toEqual(daySeconds * 6) - }) }) diff --git a/src/jobs.js b/src/jobs.js index c073acc4bc831b5e2ea90dea7fad7662bd652f7a..4eb43b22cad1b03b32c0e7bde67ea7044a6d02b7 100644 --- a/src/jobs.js +++ b/src/jobs.js @@ -104,6 +104,7 @@ const defaultJobs = [ pubsub.publish(USER_UPDATED, { userUpdated: updatedUser, }) + job.done() } catch (e) { logger.error(`Job ${jobs.REFRESH_TOKEN_EXPIRED}: defer error:`, e) throw e diff --git a/src/models/__tests__/identity.controller.test.js b/src/models/__tests__/identity.controller.test.js index bd78a43212755e8a706d52127c3761a84282887a..db440358ea1b94a81405f8d9e07296f987ab9e28 100644 --- a/src/models/__tests__/identity.controller.test.js +++ b/src/models/__tests__/identity.controller.test.js @@ -151,8 +151,10 @@ describe('Identity Controller', () => { // Expect renewal job to have been "scheduled" const lastCallIndex = jobs.defer.mock.calls.length - 1 const [name, renewAfter, data] = jobs.defer.mock.calls[lastCallIndex] - expect(name).toEqual('renew-auth-tokens') - expect(renewAfter).toEqual({ seconds: 273600 }) // 360000 - 86400 + expect(name).toEqual('refresh-token-expired') + expect({ seconds: Math.round(renewAfter.seconds) }).toEqual({ + seconds: 360000, + }) // 360000 - 86400 expect(data).toEqual({ providerLabel: 'test', userId: user.id }) }) }) diff --git a/src/services/fileStorage.js b/src/services/fileStorage.js index b6e3bc5f5758478bc3ff7e9cf8398d0f203f19e2..3ec7f01b3e3699d28fd29e3ee39c86903dc3704a 100644 --- a/src/services/fileStorage.js +++ b/src/services/fileStorage.js @@ -479,12 +479,37 @@ const deleteFiles = objectKeys => { ) } - const { bucket } = config.get('fileStorage') + const { bucket, s3SeparateDeleteOperations } = config.get('fileStorage') if (objectKeys.length === 0) { throw new Error('the provided array of keys if empty') } + const separateDeleteOperations = !emptyUndefinedOrNull( + s3SeparateDeleteOperations, + ) + ? JSON.parse(s3SeparateDeleteOperations) + : false + + if (separateDeleteOperations) { + return Promise.all( + objectKeys.map( + async objectKey => + new Promise((resolve, reject) => { + const params = { Bucket: bucket, Key: objectKey } + + s3.deleteObject(params, (err, data) => { + if (err) { + reject(err) + } + + resolve(data) + }) + }), + ), + ) + } + const params = { Bucket: bucket, Delete: { diff --git a/src/services/jobs/jobs.identifiers.js b/src/services/jobs/jobs.identifiers.js index 27f5a2acf88341e749cc97959a119b0a00d34625..ab7fdd68485d2e5a25125836ff995c7953b05dd7 100644 --- a/src/services/jobs/jobs.identifiers.js +++ b/src/services/jobs/jobs.identifiers.js @@ -1,4 +1,4 @@ module.exports = { - RENEW_AUTH_TOKENS_JOB: 'renew-auth-tokens', + // RENEW_AUTH_TOKENS_JOB: 'renew-auth-tokens', REFRESH_TOKEN_EXPIRED: 'refresh-token-expired', }