From 8ce48b3a24e0882ee463b8ab8db5b5cdb93a1e46 Mon Sep 17 00:00:00 2001 From: Selwyn Date: Thu, 22 Aug 2024 16:59:19 +0200 Subject: [PATCH] Move Netlify redirects to Nuxt redirects --- .npmrc | 1 + netlify.toml | 21 ------- nuxt.config.ts | 5 ++ package-lock.json | 59 +++++++++++++++++++ package.json | 3 +- .../language-switcher/language-switcher.vue | 10 ++-- src/lib/i18n.js | 3 + src/plugins/i18n.ts | 3 +- src/server/routes/index.ts | 28 +++++++++ src/server/tsconfig.json | 3 + 10 files changed, 107 insertions(+), 29 deletions(-) create mode 100644 .npmrc create mode 100644 src/server/routes/index.ts create mode 100644 src/server/tsconfig.json diff --git a/.npmrc b/.npmrc new file mode 100644 index 000000000..41583e36c --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +@jsr:registry=https://npm.jsr.io diff --git a/netlify.toml b/netlify.toml index 2a4f1503f..0fb92c547 100644 --- a/netlify.toml +++ b/netlify.toml @@ -16,24 +16,3 @@ for = "/*" Strict-Transport-Security = "max-age=63072000; includeSubDomains; preload" Referrer-Policy = "no-referrer-when-downgrade" X-Frame-Options = "SAMEORIGIN" - -[[redirects]] -from = "/" -to = "/nl/" -status = 302 -conditions = { Language = ["nl"] } - -[[redirects]] -from = "/" -to = "/en/" -status = 302 - -[[redirects]] -from = "/blog-feed.xml" -to = "/blog/feed.json" -status = 301 - -[[redirects]] -from = "/mogelijk/api/event" -to = "https://plausible.io/api/event" -status = 200 diff --git a/nuxt.config.ts b/nuxt.config.ts index f77b90b02..dc11b6e6e 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -18,11 +18,16 @@ export default defineNuxtConfig({ '@/components/app-core/index.css', ], nitro: { + preset: 'netlify-edge', prerender: { crawlLinks: false, }, routeRules: { '/': { prerender: false }, + '/blog-feed.xml': { + redirect: { to: '/blog/feed.json', statusCode: 301 }, + }, + '/mogelijk/api/event': { proxy: { to: 'https://plausible.io/api/event' } }, }, }, runtimeConfig: { diff --git a/package-lock.json b/package-lock.json index d35813180..6e0eda95b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "dependencies": { "@json-feed-types/1_1": "^1.0.2", "@nuxtjs/plausible": "^0.2.0", + "@std/http": "npm:@jsr/std__http@^1.0.10", "@voorhoede/vue-dato-video": "^3.1.0", "datocms-listen": "^0.1.14", "datocms-structured-text-utils": "^2.0.4", @@ -1533,6 +1534,49 @@ "integrity": "sha512-IWcSEjI+2bjl1T0R4cIPHu2DZ6PF/unRdEl9VZ1vTtLNaybzirGCywat+KLIpbmxrg48kRNgasH4CBYkq99NDw==", "license": "ISC" }, + "node_modules/@jsr/std__bytes": { + "version": "1.0.4", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__bytes/1.0.4.tgz", + "integrity": "sha512-3RJxZsSHd9AhTIbJDYDigSTlElYkkU/NilUHtgLGxlXgNPxmb+wapntsxmgqQACsykhdCOUSW9SH/NinTBJ/XQ==" + }, + "node_modules/@jsr/std__cli": { + "version": "1.0.6", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__cli/1.0.6.tgz", + "integrity": "sha512-aWfDn9Hsr8wGI1D0nX4o1ADCt93gerYy3bU7zXrlymxD1HXTWj3EfgOq2/cKmpcTmR/PWBensX4AnenprWb9cA==" + }, + "node_modules/@jsr/std__encoding": { + "version": "1.0.5", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__encoding/1.0.5.tgz", + "integrity": "sha512-blLadT0G3SiwsneatWsUwH5s+Sb+zSsQCgtHZlT+nQkoFci2OiErHx4XqdoFcwuz3IH6wOdPZqjhcSrbNS0jqA==" + }, + "node_modules/@jsr/std__fmt": { + "version": "1.0.3", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__fmt/1.0.3.tgz", + "integrity": "sha512-Nf8EywAPLhDV6k0Nbsqid3slskDji4xG2wHxyLM/ojG75NtxY3mdeFov2RVj9fsyBV+qQcoqlR/9RX5lkZJLMQ==" + }, + "node_modules/@jsr/std__media-types": { + "version": "1.1.0", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__media-types/1.1.0.tgz", + "integrity": "sha512-dHvaxHL7ENWnltgL653uo3KnKFse3ZbopZop2gqsT7yrscx7irZEClu5Cba7gMPPRk4Lg1FbriNcaBViM2RSBw==" + }, + "node_modules/@jsr/std__net": { + "version": "1.0.4", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__net/1.0.4.tgz", + "integrity": "sha512-KJGU8ZpQ70sMW2Zk+wU3wFUkggS9lTLfRFBygnV9VaK8KI+1ggiqtB06rH4a14CNRGM9y46Mn/ZCbQUd4Q45Jg==" + }, + "node_modules/@jsr/std__path": { + "version": "1.0.8", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__path/1.0.8.tgz", + "integrity": "sha512-eNBGlh/8ZVkMxtFH4bwIzlAeKoHYk5in4wrBZhi20zMdOiuX4QozP4+19mIXBT2lzHDjhuVLyECbhFeR304iDg==" + }, + "node_modules/@jsr/std__streams": { + "version": "1.0.8", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__streams/1.0.8.tgz", + "integrity": "sha512-CTpO3/SIGGsA8i4qWmplPiUxJKxtZ6bx4A7piYioFlt5BDUf1fUtNs/120NPEW0b97I8Z+nolCx5SdY32sNOfw==", + "dependencies": { + "@jsr/std__bytes": "^1.0.3" + } + }, "node_modules/@kamilkisiela/fast-url-parser": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@kamilkisiela/fast-url-parser/-/fast-url-parser-1.1.4.tgz", @@ -3369,6 +3413,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@std/http": { + "name": "@jsr/std__http", + "version": "1.0.10", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__http/1.0.10.tgz", + "integrity": "sha512-RUDKP5qyHMA7wWl0rOXSdaFnRDK66PX2PeYH6aAVABC+R6i4NDcV9V8yY7D3j6rH8yuXRQFELhfrTy014xMP3w==", + "dependencies": { + "@jsr/std__cli": "^1.0.6", + "@jsr/std__encoding": "^1.0.5", + "@jsr/std__fmt": "^1.0.3", + "@jsr/std__media-types": "^1.1.0", + "@jsr/std__net": "^1.0.4", + "@jsr/std__path": "^1.0.8", + "@jsr/std__streams": "^1.0.8" + } + }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", diff --git a/package.json b/package.json index 7fdfb7761..e8d7daed7 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "private": true, "scripts": { "dev": "nuxt dev", - "build": "nuxt generate", + "build": "nuxt build", "lint": "eslint '**/*.vue'", "lint-staged": "nano-staged", "prepare": "husky install" @@ -11,6 +11,7 @@ "dependencies": { "@json-feed-types/1_1": "^1.0.2", "@nuxtjs/plausible": "^0.2.0", + "@std/http": "npm:@jsr/std__http@^1.0.10", "@voorhoede/vue-dato-video": "^3.1.0", "datocms-listen": "^0.1.14", "datocms-structured-text-utils": "^2.0.4", diff --git a/src/components/language-switcher/language-switcher.vue b/src/components/language-switcher/language-switcher.vue index c17463f17..71e822674 100644 --- a/src/components/language-switcher/language-switcher.vue +++ b/src/components/language-switcher/language-switcher.vue @@ -32,6 +32,7 @@ diff --git a/src/lib/i18n.js b/src/lib/i18n.js index e84227733..a8bc55dcd 100644 --- a/src/lib/i18n.js +++ b/src/lib/i18n.js @@ -1,3 +1,6 @@ +export const defaultLanguage = 'en'; +export const cookieName = 'chosen-language'; + export const locales = [ { code: 'en', diff --git a/src/plugins/i18n.ts b/src/plugins/i18n.ts index 360a9ddd2..5369bece9 100644 --- a/src/plugins/i18n.ts +++ b/src/plugins/i18n.ts @@ -1,10 +1,9 @@ import rosetta from 'rosetta'; import { joinURL, withTrailingSlash } from 'ufo'; -import { locales } from '../lib/i18n'; +import { locales, defaultLanguage } from '../lib/i18n'; import messages from '../../.cache/ui-translations.json'; const i18n = rosetta(messages); -const defaultLanguage = 'en'; export default defineNuxtPlugin((nuxtApp) => { i18n.locale(nuxtApp._route.params.language); diff --git a/src/server/routes/index.ts b/src/server/routes/index.ts new file mode 100644 index 000000000..6bb12d1da --- /dev/null +++ b/src/server/routes/index.ts @@ -0,0 +1,28 @@ +import { acceptsLanguages } from '@std/http/negotiation'; +import { cookieName, defaultLanguage, locales } from '~/lib/i18n.js'; + +const localeCodes = locales.map(({ code }) => code); + +export default defineEventHandler((event) => { + const request = toWebRequest(event); + const chosenLanguage = getCookie(event, cookieName); + + if (localeCodes.includes(chosenLanguage)) { + return sendRedirect( + event, + `/${chosenLanguage}/`, + 302, + ); + } + + const clientLocale = acceptsLanguages( + request, + ...localeCodes, + ); + + sendRedirect( + event, + `/${clientLocale || defaultLanguage}/`, + 302, + ); +}); diff --git a/src/server/tsconfig.json b/src/server/tsconfig.json new file mode 100644 index 000000000..b9ed69c19 --- /dev/null +++ b/src/server/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../.nuxt/tsconfig.server.json" +}