diff --git a/package-lock.json b/package-lock.json index b769105abd..d6794e2859 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,7 @@ "wrangler": "^2.0.23" }, "engines": { - "node": "16.x", + "node": "18.x", "npm": ">=7.x" } }, @@ -24202,7 +24202,7 @@ }, "packages/api": { "name": "@web3-storage/api", - "version": "7.20.0", + "version": "7.22.0", "license": "(Apache-2.0 OR MIT)", "dependencies": { "@aws-sdk/client-s3": "^3.53.1", @@ -27048,7 +27048,7 @@ }, "packages/website": { "name": "@web3-storage/website", - "version": "2.37.0", + "version": "2.38.2", "dependencies": { "@docsearch/react": "^3.0.0", "@fortawesome/free-brands-svg-icons": "^6.1.2", diff --git a/package.json b/package.json index ad2d478481..1724eeec65 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ ] }, "engines": { - "node": "16.x", + "node": "18.x", "npm": ">=7.x" } } diff --git a/packages/api/src/errors.js b/packages/api/src/errors.js index 234418d88a..c38c5cc7c3 100644 --- a/packages/api/src/errors.js +++ b/packages/api/src/errors.js @@ -193,6 +193,24 @@ export class MaintenanceError extends Error { } MaintenanceError.CODE = 'ERROR_MAINTENANCE' +export class FeatureHasBeenSunsetError extends Error { + /** + * @param {string} reason + */ + constructor (reason) { + super(reason) + this.name = 'FeatureHasBeenSunset' + /** + * The 410 (Gone) status code indicates that access to the target resource + * is no longer available at the origin server and that this condition is likely + * to be permanent. + */ + this.status = 410 // Gone + this.code = FeatureHasBeenSunsetError.CODE + } +} +FeatureHasBeenSunsetError.CODE = 'ERROR_FEATURE_HAS_BEEN_SUNSET' + export class PSAErrorInvalidData extends PinningServiceApiError { /** * @param {string} message diff --git a/packages/api/src/maintenance.js b/packages/api/src/maintenance.js index af60f47de1..e4425c73fd 100644 --- a/packages/api/src/maintenance.js +++ b/packages/api/src/maintenance.js @@ -1,4 +1,4 @@ -import { HTTPError, MaintenanceError } from './errors.js' +import { FeatureHasBeenSunsetError, HTTPError, MaintenanceError } from './errors.js' import { getTokenFromRequest } from './auth.js' /** @@ -50,10 +50,10 @@ export function withMode (mode) { * @returns {Response|undefined} */ return (request, env, ctx) => { - const enabled = () => { - const currentMode = env.MODE - const currentModeBits = modeBits(currentMode) + const currentMode = env.MODE + const currentModeBits = modeBits(currentMode) + const enabled = () => { return modeBits(mode).every((bit, i) => { if (bit === '-') { return true @@ -78,6 +78,10 @@ export function withMode (mode) { // Not enabled, use maintenance handler. if (!enabled() && !modeSkip()) { + const isAfterSunsetStart = env.NEXT_PUBLIC_W3UP_LAUNCH_SUNSET_START ? (env.NEXT_PUBLIC_W3UP_LAUNCH_SUNSET_START < new Date().toISOString()) : false + if (isAfterSunsetStart && (currentMode === READ_ONLY)) { + throw new FeatureHasBeenSunsetError('This API feature has been sunset, and is no longer available. To continue uploading, use the new web3.storage API: https://web3.storage/docs.') + } return maintenanceHandler() } } diff --git a/packages/api/test/maintenance.spec.js b/packages/api/test/maintenance.spec.js index 27e0131223..d849165f17 100644 --- a/packages/api/test/maintenance.spec.js +++ b/packages/api/test/maintenance.spec.js @@ -62,6 +62,12 @@ describe('maintenance middleware', () => { assert.throws(() => block(() => { }, { MODE: NO_READ_OR_WRITE }), /API undergoing maintenance/) + + // after product sunset, READ_ONLY means FeatureHasBeenSunset + assert.throws(() => block(() => { }, { + MODE: READ_ONLY, + NEXT_PUBLIC_W3UP_LAUNCH_SUNSET_START: (new Date(0)).toISOString() + }), /FeatureHasBeenSunset/) }) it('should bypass maintenance mode with a allowed token', async () => { diff --git a/packages/website/components/w3up-launch.js b/packages/website/components/w3up-launch.js index 078b22ca36..7e5a304a9c 100644 --- a/packages/website/components/w3up-launch.js +++ b/packages/website/components/w3up-launch.js @@ -19,8 +19,8 @@ export const W3upMigrationRecommendationCopy = ({ sunsetStartDate }) => { const sunsetDateFormatter = new Intl.DateTimeFormat(undefined, { dateStyle: 'long' }); return ( <> - This web3.storage product will sunset on {sunsetDateFormatter.format(sunsetStartDate)}. We recommend migrating - your usage of web3.storage to the new web3.storage. + This web3.storage product sunset for new uploads on {sunsetDateFormatter.format(sunsetStartDate)}. To continue + uploading, migrate to the new web3.storage API.
Click here to create a new account and  here to read about what’s awesome about the new web3.storage experience.