浏览代码

fix: switch js-cookies to cookies-next for a more reliable client/server value

Nicolas Meienberger 2 年之前
父节点
当前提交
8a86cfd072

+ 1 - 1
package.json

@@ -50,12 +50,12 @@
     "argon2": "^0.30.3",
     "argon2": "^0.30.3",
     "clsx": "^1.1.1",
     "clsx": "^1.1.1",
     "connect-redis": "^7.1.0",
     "connect-redis": "^7.1.0",
+    "cookies-next": "^2.1.1",
     "drizzle-orm": "^0.26.0",
     "drizzle-orm": "^0.26.0",
     "express": "^4.17.3",
     "express": "^4.17.3",
     "express-session": "^1.17.3",
     "express-session": "^1.17.3",
     "fs-extra": "^11.1.1",
     "fs-extra": "^11.1.1",
     "isomorphic-fetch": "^3.0.0",
     "isomorphic-fetch": "^3.0.0",
-    "js-cookie": "^3.0.5",
     "lodash.merge": "^4.6.2",
     "lodash.merge": "^4.6.2",
     "next": "13.4.4",
     "next": "13.4.4",
     "next-intl": "^2.14.2",
     "next-intl": "^2.14.2",

+ 15 - 9
pnpm-lock.yaml

@@ -64,6 +64,9 @@ dependencies:
   connect-redis:
   connect-redis:
     specifier: ^7.1.0
     specifier: ^7.1.0
     version: 7.1.0(express-session@1.17.3)
     version: 7.1.0(express-session@1.17.3)
+  cookies-next:
+    specifier: ^2.1.1
+    version: 2.1.1
   drizzle-orm:
   drizzle-orm:
     specifier: ^0.26.0
     specifier: ^0.26.0
     version: 0.26.0(@types/pg@8.6.6)(pg@8.11.0)
     version: 0.26.0(@types/pg@8.6.6)(pg@8.11.0)
@@ -79,9 +82,6 @@ dependencies:
   isomorphic-fetch:
   isomorphic-fetch:
     specifier: ^3.0.0
     specifier: ^3.0.0
     version: 3.0.0
     version: 3.0.0
-  js-cookie:
-    specifier: ^3.0.5
-    version: 3.0.5
   lodash.merge:
   lodash.merge:
     specifier: ^4.6.2
     specifier: ^4.6.2
     version: 4.6.2
     version: 4.6.2
@@ -2886,7 +2886,6 @@ packages:
 
 
   /@types/cookie@0.4.1:
   /@types/cookie@0.4.1:
     resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==}
     resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==}
-    dev: true
 
 
   /@types/cookiejar@2.1.2:
   /@types/cookiejar@2.1.2:
     resolution: {integrity: sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==}
     resolution: {integrity: sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==}
@@ -3023,6 +3022,10 @@ packages:
     resolution: {integrity: sha512-9PuLtBboc/+JJ7FshmJWv769gDonTpItN0Ol5TMwclpSQNjVyB2SRxSKBcTtbSysSL5R7Oea06kTTFNciCoYwA==}
     resolution: {integrity: sha512-9PuLtBboc/+JJ7FshmJWv769gDonTpItN0Ol5TMwclpSQNjVyB2SRxSKBcTtbSysSL5R7Oea06kTTFNciCoYwA==}
     dev: true
     dev: true
 
 
+  /@types/node@16.18.35:
+    resolution: {integrity: sha512-yqU2Rf94HFZqgHf6Tuyc/IqVD0l3U91KjvypSr1GtJKyrnl6L/kfnxVqN4QOwcF5Zx9tO/HKK+fozGr5AtqA+g==}
+    dev: false
+
   /@types/node@20.2.5:
   /@types/node@20.2.5:
     resolution: {integrity: sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==}
     resolution: {integrity: sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==}
 
 
@@ -4113,6 +4116,14 @@ packages:
     resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==}
     resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==}
     dev: true
     dev: true
 
 
+  /cookies-next@2.1.1:
+    resolution: {integrity: sha512-AZGZPdL1hU3jCjN2UMJTGhLOYzNUN9Gm+v8BdptYIHUdwz397Et1p+sZRfvAl8pKnnmMdX2Pk9xDRKCGBum6GA==}
+    dependencies:
+      '@types/cookie': 0.4.1
+      '@types/node': 16.18.35
+      cookie: 0.4.2
+    dev: false
+
   /copy-anything@3.0.3:
   /copy-anything@3.0.3:
     resolution: {integrity: sha512-fpW2W/BqEzqPp29QS+MwwfisHCQZtiduTe/m8idFo0xbti9fIZ2WVhAsCv4ggFVH3AgCkVdpoOCtQC6gBrdhjw==}
     resolution: {integrity: sha512-fpW2W/BqEzqPp29QS+MwwfisHCQZtiduTe/m8idFo0xbti9fIZ2WVhAsCv4ggFVH3AgCkVdpoOCtQC6gBrdhjw==}
     engines: {node: '>=12.13'}
     engines: {node: '>=12.13'}
@@ -6891,11 +6902,6 @@ packages:
       - ts-node
       - ts-node
     dev: true
     dev: true
 
 
-  /js-cookie@3.0.5:
-    resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
-    engines: {node: '>=14'}
-    dev: false
-
   /js-levenshtein@1.1.6:
   /js-levenshtein@1.1.6:
     resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==}
     resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==}
     engines: {node: '>=0.10.0'}
     engines: {node: '>=0.10.0'}

+ 1 - 1
src/client/components/ui/Header/Header.tsx

@@ -52,7 +52,7 @@ export const Header: React.FC<IProps> = ({ isUpdateAvailable }) => {
           </h1>
           </h1>
         </Link>
         </Link>
         <div className="navbar-nav flex-row order-md-last">
         <div className="navbar-nav flex-row order-md-last">
-          <div className="nav-item d-none d-lg-flex me-3">
+          <div className="nav-item d-none d-xl-flex me-3">
             <div className="btn-list">
             <div className="btn-list">
               <a href="https://github.com/meienberger/runtipi" target="_blank" rel="noreferrer" className="btn btn-dark">
               <a href="https://github.com/meienberger/runtipi" target="_blank" rel="noreferrer" className="btn btn-dark">
                 <IconBrandGithub data-testid="icon-github" className="me-1 icon" size={24} />
                 <IconBrandGithub data-testid="icon-github" className="me-1 icon" size={24} />

+ 5 - 5
src/client/hooks/__tests__/useLocale.test.ts

@@ -1,11 +1,11 @@
 import { getTRPCMock } from '@/client/mocks/getTrpcMock';
 import { getTRPCMock } from '@/client/mocks/getTrpcMock';
 import { server } from '@/client/mocks/server';
 import { server } from '@/client/mocks/server';
-import Cookies from 'js-cookie';
+import { deleteCookie, setCookie, getCookie } from 'cookies-next';
 import { renderHook, waitFor } from '../../../../tests/test-utils';
 import { renderHook, waitFor } from '../../../../tests/test-utils';
 import { useLocale } from '../useLocale';
 import { useLocale } from '../useLocale';
 
 
 beforeEach(() => {
 beforeEach(() => {
-  Cookies.remove('locale');
+  deleteCookie('tipi-locale');
 });
 });
 
 
 describe('test: useLocale()', () => {
 describe('test: useLocale()', () => {
@@ -28,7 +28,7 @@ describe('test: useLocale()', () => {
     it('should return cookie locale if not logged in', async () => {
     it('should return cookie locale if not logged in', async () => {
       // arrange
       // arrange
       const locale = 'fr-FR';
       const locale = 'fr-FR';
-      Cookies.set('locale', locale);
+      setCookie('tipi-locale', locale);
       server.use(getTRPCMock({ path: ['auth', 'me'], response: null }));
       server.use(getTRPCMock({ path: ['auth', 'me'], response: null }));
 
 
       // act
       // act
@@ -82,7 +82,7 @@ describe('test: useLocale()', () => {
 
 
       // assert
       // assert
       await waitFor(() => {
       await waitFor(() => {
-        expect(Cookies.get('locale')).toEqual('fr-FR');
+        expect(getCookie('tipi-locale')).toEqual('fr-FR');
       });
       });
     });
     });
 
 
@@ -102,7 +102,7 @@ describe('test: useLocale()', () => {
 
 
       // assert
       // assert
       await waitFor(() => {
       await waitFor(() => {
-        expect(Cookies.get('locale')).toEqual(locale);
+        expect(getCookie('tipi-locale')).toEqual(locale);
       });
       });
     });
     });
   });
   });

+ 5 - 6
src/client/hooks/useLocale.ts

@@ -1,5 +1,5 @@
+import { setCookie, getCookie } from 'cookies-next';
 import { useRouter } from 'next/router';
 import { useRouter } from 'next/router';
-import Cookies from 'js-cookie';
 import { trpc } from '@/utils/trpc';
 import { trpc } from '@/utils/trpc';
 import { Locale, getLocaleFromString } from '@/shared/internationalization/locales';
 import { Locale, getLocaleFromString } from '@/shared/internationalization/locales';
 
 
@@ -8,9 +8,9 @@ export const useLocale = () => {
   const me = trpc.auth.me.useQuery();
   const me = trpc.auth.me.useQuery();
   const changeUserLocale = trpc.auth.changeLocale.useMutation();
   const changeUserLocale = trpc.auth.changeLocale.useMutation();
   const browserLocale = typeof window !== 'undefined' ? window.navigator.language : undefined;
   const browserLocale = typeof window !== 'undefined' ? window.navigator.language : undefined;
-  const cookieLocale = Cookies.get('locale');
+  const cookieLocale = getCookie('tipi-locale');
 
 
-  const locale = me.data?.locale || cookieLocale || browserLocale || 'en';
+  const locale = String(me.data?.locale || cookieLocale || browserLocale || 'en');
   const ctx = trpc.useContext();
   const ctx = trpc.useContext();
 
 
   const changeLocale = async (l: Locale) => {
   const changeLocale = async (l: Locale) => {
@@ -19,9 +19,8 @@ export const useLocale = () => {
       await ctx.invalidate();
       await ctx.invalidate();
     }
     }
 
 
-    Cookies.set('locale', l, {
-      expires: 30,
-      path: '/',
+    setCookie('tipi-locale', l, {
+      expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
     });
     });
 
 
     router.reload();
     router.reload();

+ 3 - 2
src/client/utils/page-helpers.ts

@@ -1,6 +1,7 @@
 import { GetServerSideProps } from 'next';
 import { GetServerSideProps } from 'next';
 import merge from 'lodash.merge';
 import merge from 'lodash.merge';
 import { getLocaleFromString } from '@/shared/internationalization/locales';
 import { getLocaleFromString } from '@/shared/internationalization/locales';
+import { getCookie } from 'cookies-next';
 
 
 export const getAuthedPageProps: GetServerSideProps = async (ctx) => {
 export const getAuthedPageProps: GetServerSideProps = async (ctx) => {
   const { userId } = ctx.req.session;
   const { userId } = ctx.req.session;
@@ -20,11 +21,11 @@ export const getAuthedPageProps: GetServerSideProps = async (ctx) => {
 };
 };
 
 
 export const getMessagesPageProps: GetServerSideProps = async (ctx) => {
 export const getMessagesPageProps: GetServerSideProps = async (ctx) => {
-  const { cookies } = ctx.req;
   const { locale: sessionLocale } = ctx.req.session;
   const { locale: sessionLocale } = ctx.req.session;
+  const cookieLocale = getCookie('tipi-locale', { req: ctx.req });
   const browserLocale = ctx.req.headers['accept-language']?.split(',')[0];
   const browserLocale = ctx.req.headers['accept-language']?.split(',')[0];
 
 
-  const locale = getLocaleFromString(sessionLocale || cookies?.locale || browserLocale || 'en');
+  const locale = getLocaleFromString(String(sessionLocale || cookieLocale || browserLocale || 'en'));
 
 
   const englishMessages = (await import(`../messages/en.json`)).default;
   const englishMessages = (await import(`../messages/en.json`)).default;
   const messages = (await import(`../messages/${locale}.json`)).default;
   const messages = (await import(`../messages/${locale}.json`)).default;