From 0934860474322671849547609f3087713741c7ad Mon Sep 17 00:00:00 2001 From: Greg Slepak Date: Tue, 11 Feb 2025 10:42:04 -0800 Subject: [PATCH 1/4] return no-cache on 404 --- backend/routes.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/backend/routes.js b/backend/routes.js index b61a06c8a..f9e854bb2 100644 --- a/backend/routes.js +++ b/backend/routes.js @@ -71,6 +71,16 @@ const route = new Proxy({}, { } }) +// helper function that returns 404 and prevents client from caching the 404 response +// which can sometimes break things: https://github.com/okTurtles/group-income/issues/2608 +function return404nocache (h) { + return h.response() + .code(404) + .header('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate') + .header('Pragma', 'no-cache') + .header('Expires', '0') +} + // RESTful API routes // TODO: Update this regex once `chel` uses prefixed manifests @@ -277,7 +287,7 @@ route.GET('/latestHEADinfo/{contractID}', { const HEADinfo = await sbp('chelonia/db/latestHEADinfo', contractID) if (!HEADinfo) { console.warn(`[backend] latestHEADinfo not found for ${contractID}`) - return Boom.notFound() + return return404nocache(h) } return HEADinfo } catch (err) { @@ -473,7 +483,7 @@ route.GET('/file/{hash}', { const blobOrString = await sbp('chelonia/db/get', `any:${hash}`) if (!blobOrString) { - return Boom.notFound() + return return404nocache(h) } return h.response(blobOrString).etag(hash) }) @@ -675,7 +685,7 @@ route.GET('/kv/{contractID}/{key}', { const result = await sbp('chelonia/db/get', `_private_kv_${contractID}_${key}`) if (!result) { - return Boom.notFound() + return return404nocache(h) } return h.response(result).etag(createCID(result)) @@ -804,7 +814,7 @@ route.GET('/zkpp/{name}/auth_hash', { try { const challenge = await getChallenge(req.params['name'], req.query['b']) - return challenge || Boom.notFound() + return challenge || return404nocache(h) } catch (e) { e.ip = req.headers['x-real-ip'] || req.info.remoteAddress console.error(e, 'Error at GET /zkpp/{name}/auth_hash: ' + e.message) From de9264cd62a2ab19507a9e7f09e4d64f5800182d Mon Sep 17 00:00:00 2001 From: Greg Slepak Date: Wed, 12 Feb 2025 08:53:09 -0800 Subject: [PATCH 2/4] rename function, remove redundant no-cache --- backend/routes.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/routes.js b/backend/routes.js index f9e854bb2..11a45bccd 100644 --- a/backend/routes.js +++ b/backend/routes.js @@ -73,10 +73,10 @@ const route = new Proxy({}, { // helper function that returns 404 and prevents client from caching the 404 response // which can sometimes break things: https://github.com/okTurtles/group-income/issues/2608 -function return404nocache (h) { +function notFoundNoCache (h) { return h.response() .code(404) - .header('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate') + .header('Cache-Control', 'no-store, must-revalidate, proxy-revalidate') .header('Pragma', 'no-cache') .header('Expires', '0') } @@ -287,7 +287,7 @@ route.GET('/latestHEADinfo/{contractID}', { const HEADinfo = await sbp('chelonia/db/latestHEADinfo', contractID) if (!HEADinfo) { console.warn(`[backend] latestHEADinfo not found for ${contractID}`) - return return404nocache(h) + return notFoundNoCache(h) } return HEADinfo } catch (err) { @@ -483,7 +483,7 @@ route.GET('/file/{hash}', { const blobOrString = await sbp('chelonia/db/get', `any:${hash}`) if (!blobOrString) { - return return404nocache(h) + return notFoundNoCache(h) } return h.response(blobOrString).etag(hash) }) @@ -685,7 +685,7 @@ route.GET('/kv/{contractID}/{key}', { const result = await sbp('chelonia/db/get', `_private_kv_${contractID}_${key}`) if (!result) { - return return404nocache(h) + return notFoundNoCache(h) } return h.response(result).etag(createCID(result)) @@ -814,7 +814,7 @@ route.GET('/zkpp/{name}/auth_hash', { try { const challenge = await getChallenge(req.params['name'], req.query['b']) - return challenge || return404nocache(h) + return challenge || notFoundNoCache(h) } catch (e) { e.ip = req.headers['x-real-ip'] || req.info.remoteAddress console.error(e, 'Error at GET /zkpp/{name}/auth_hash: ' + e.message) From a145c24b689cffe973ae42cf37959d742dc8c0c8 Mon Sep 17 00:00:00 2001 From: Greg Slepak Date: Wed, 12 Feb 2025 09:25:07 -0800 Subject: [PATCH 3/4] feedback --- backend/routes.js | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/backend/routes.js b/backend/routes.js index 11a45bccd..4b21c53db 100644 --- a/backend/routes.js +++ b/backend/routes.js @@ -74,11 +74,7 @@ const route = new Proxy({}, { // helper function that returns 404 and prevents client from caching the 404 response // which can sometimes break things: https://github.com/okTurtles/group-income/issues/2608 function notFoundNoCache (h) { - return h.response() - .code(404) - .header('Cache-Control', 'no-store, must-revalidate, proxy-revalidate') - .header('Pragma', 'no-cache') - .header('Expires', '0') + return h.response().code(404).header('Cache-Control', 'no-store') } // RESTful API routes @@ -468,13 +464,7 @@ route.POST('/file', { // Serve data from Chelonia DB. // Note that a `Last-Modified` header isn't included in the response. -route.GET('/file/{hash}', { - cache: { - // Do not set other cache options here, to make sure the 'otherwise' option - // will be used so that the 'immutable' directive gets included. - otherwise: 'public,max-age=31536000,immutable' - } -}, async function (request, h) { +route.GET('/file/{hash}', {}, async function (request, h) { const { hash } = request.params if (hash.startsWith('_private')) { @@ -485,7 +475,8 @@ route.GET('/file/{hash}', { if (!blobOrString) { return notFoundNoCache(h) } - return h.response(blobOrString).etag(hash) + return h.response(blobOrString).code(200).etag(hash) + .header('Cache-Control', 'public,max-age=31536000,immutable') }) route.POST('/deleteFile/{hash}', { From a44b1e93d1ec00156919df11420137f79c5672fb Mon Sep 17 00:00:00 2001 From: Greg Slepak Date: Wed, 12 Feb 2025 10:02:52 -0800 Subject: [PATCH 4/4] Should close #2572 - french classes heisenbug --- test/cypress/integration/group-contributions.spec.js | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/test/cypress/integration/group-contributions.spec.js b/test/cypress/integration/group-contributions.spec.js index 214e22c8d..a1e4bd4a5 100644 --- a/test/cypress/integration/group-contributions.spec.js +++ b/test/cypress/integration/group-contributions.spec.js @@ -26,15 +26,6 @@ function addNonMonetaryContribution (name) { }) } -function assertNonMonetaryEditableValue (name) { - // Need to wait until the event is processed - cy.giEmptyInvocationQueue() - - cy.getByDT('buttonEditNonMonetaryContribution').click() - cy.getByDT('inputNonMonetaryContribution').should('have.value', name) - cy.getByDT('buttonCancelNonMonetaryContribution').click() -} - function assertGraphicSummary (legendListItems) { cy.getByDT('groupPledgeSummary', 'ul').within(([list]) => { legendListItems.forEach((legendText, index) => { @@ -373,8 +364,7 @@ describe('Contributions', () => { cy.getByDT('buttonEditNonMonetaryContribution').click() cy.getByDT('inputNonMonetaryContribution').clear() cy.getByDT('inputNonMonetaryContribution').type('French classes{enter}') - assertNonMonetaryEditableValue('French classes') - + cy.giEmptyInvocationQueue() // wait for edits to go through cy.getByDT('givingList', 'ul') .get('li.is-editable') .should('have.length', 1)