diff --git a/docker-compose.yaml b/docker-compose.yaml index a4b92e3..1662c82 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,22 +1,18 @@ services: front: build: ./traque-front + restart: always + + reverse_proxy: + build: ./proxy ports: - - "3000:3000" - environment: - - NEXT_PUBLIC_SOCKET_HOST = 'quentinrsl.com' - - NEXT_PUBLIC_SOCKET_PORT = 3001 + - "3000:443" + volumes: + - ./traque-back/ssl:/etc/nginx/ssl:ro + restart: always back: build: ./traque-back ports: - "3001:3001" - environment: - - HOST = 'quentinrsl.com' - - PORT = 3001 - - SSL_KEY = "/etc/letsencrypt/live/quentinrsl.com/privkey.pem" - - SSL_CERT = "/etc/letsencrypt/live/quentinrsl.com/cert.pem" - - ADMIN_PASSWORD = 'admin' - - MAX_PENALTIES = 3 - - ALLOWED_TIME_OUT_OF_ZONE_IN_MINUTES = 10 - - ALLOWED_TIME_BETWEEN_POSITION_UPDATE_IN_MINUTES = 10 \ No newline at end of file + restart: always \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..3877974 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "traque", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/proxy/Dockerfile b/proxy/Dockerfile new file mode 100644 index 0000000..3772885 --- /dev/null +++ b/proxy/Dockerfile @@ -0,0 +1,8 @@ +# nginx/Dockerfile + +FROM nginx:1.23.3-alpine + +COPY nginx.conf /etc/nginx/nginx.conf + +EXPOSE 80 +EXPOSE 443 diff --git a/proxy/nginx.conf b/proxy/nginx.conf new file mode 100644 index 0000000..eb5699f --- /dev/null +++ b/proxy/nginx.conf @@ -0,0 +1,42 @@ +# nginx/nginx.conf + +events { +} + +http { + upstream front { + server front:3000; + } + server { + # Redirect HTTP requests to HTTPS. + listen 80; + server_name localhost; + root /srv/public; + return 301 https://$host$request_uri; + } + + server { + listen 443 ssl; + + server_name localhost; + root /srv/public; + server_tokens off; + + ssl_certificate /etc/nginx/ssl/cert.pem; + ssl_certificate_key /etc/nginx/ssl/privkey.pem; + + location / { + try_files $uri $uri/ @front; + } + + location @front { + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto https; + proxy_set_header X-Forwarded-Ssl on; + proxy_set_header Host $http_host; + proxy_redirect off; + proxy_pass http://front; + proxy_cookie_path / "/; HTTPOnly; Secure"; + } + } +} \ No newline at end of file diff --git a/readme.md b/readme.md index 62e3fff..49525f3 100644 --- a/readme.md +++ b/readme.md @@ -4,7 +4,7 @@ Edit the .env file in traque-front and add specify the following values: ``` NEXT_PUBLIC_SOCKET_HOST = 'example.com' -NEXT_PUBLIC_SOCKET_PORT = 3000 +NEXT_PUBLIC_SOCKET_PORT = 3001 ``` Where NEXT_PUBLIC_SOCKET_HOST is the host of the socket server and NEXT_PUBLIC_SOCKET_PORT is the port of the socket server. ### Back end configuration @@ -25,6 +25,8 @@ MAX_PENALTIES is the maximum number of penalties a user can have before loosing ALLOWED_TIME_OUT_OF_ZONE_IN_MINUTES is the time a user can be out of the zone before being penalized. ALLOWED_TIME_BETWEEN_POSITION_UPDATE_IN_MINUTES is the maximum amount of time a user can wait before updating their position, after this period they will recieve a pennalty. +Note : make sure PORT and NEXT_PUBLIC_SOCKET_PORT are the same + ### Running the project #### Front end To run the front end, navigate to the traque-front directory and run the following commands: @@ -43,5 +45,9 @@ Then navigate to the host and port specified in the .env file to access the appl https://example.com:3000 ``` -## Depployment +## Deployment + +### SSL certificates +Put your certificate and private key in the traque-back/ssl folder. They need to be named `cert.pem` and `privkey.pem`. +You can then depploy the docker application with `docker compose up` diff --git a/traque-back/.gitignore b/traque-back/.gitignore index 410b041..800f3e6 100644 --- a/traque-back/.gitignore +++ b/traque-back/.gitignore @@ -1,8 +1,6 @@ uploads/* #https dev certificates -csr.pem -key.pem -server.crt +*.pem # Logs logs *.log diff --git a/traque-back/admin_socket.js b/traque-back/admin_socket.js index 3be25d6..93c7b2d 100644 --- a/traque-back/admin_socket.js +++ b/traque-back/admin_socket.js @@ -122,6 +122,7 @@ export function initAdminSocketHandler() { } if (game.reorderTeams(newOrder)) { secureAdminBroadcast("teams", game.teams); + game.teams.forEach(t => sendUpdatedTeamInformations(t.id)) } else { socket.emit("error", "Error reordering teams"); } diff --git a/traque-back/game.js b/traque-back/game.js index 7c381cf..411f3da 100644 --- a/traque-back/game.js +++ b/traque-back/game.js @@ -24,6 +24,7 @@ export default class Game { } //The game has started if (newState == GameState.PLAYING) { + penaltyController.start(); if (!this.zone.ready()) { return false; } diff --git a/traque-back/index.js b/traque-back/index.js index 2607865..88b2a4d 100644 --- a/traque-back/index.js +++ b/traque-back/index.js @@ -49,7 +49,6 @@ function onUpdateZone(zone) { export const game = new Game(onUpdateZone, onUpdateNewZone); export const penaltyController = new PenaltyController(); -penaltyController.init() initAdminSocketHandler(); diff --git a/traque-back/penalty_controller.js b/traque-back/penalty_controller.js index 843e1c5..926d3c2 100644 --- a/traque-back/penalty_controller.js +++ b/traque-back/penalty_controller.js @@ -14,7 +14,7 @@ export class PenaltyController { this.checkIntervalId = null; } - init() { + start() { this.outOfBoundsSince = {}; if(this.checkIntervalId) { clearInterval(this.checkIntervalId) @@ -62,6 +62,7 @@ export class PenaltyController { } watchZone() { + console.log("watching zone") this.game.teams.forEach((team) => { if (team.captured) { return } //All the informations are not ready yet @@ -69,9 +70,11 @@ export class PenaltyController { return; } if (!isInCircle({lat: team.currentLocation[0], lng: team.currentLocation[1]}, this.game.zone.currentZone.center, this.game.zone.currentZone.radius)) { + console.log("tema " + team.name + " out of zone") //The team was not previously out of the zone if (!this.outOfBoundsSince[team.id]) { this.outOfBoundsSince[team.id] = new Date(); + console.log("tema " + team.name + " warned") teamBroadcast(team.id, "warning", `You left the zone, you have ${process.env.ALLOWED_TIME_OUT_OF_ZONE_IN_MINUTES} minutes to get back in the marked area.`) } else { if (new Date() - this.outOfBoundsSince[team.id] > process.env.ALLOWED_TIME_OUT_OF_ZONE_IN_MINUTES * 60 * 1000) { diff --git a/traque-front/Dockerfile b/traque-front/Dockerfile new file mode 100644 index 0000000..3d1b852 --- /dev/null +++ b/traque-front/Dockerfile @@ -0,0 +1,67 @@ +FROM node:18-alpine AS base + +# Install dependencies only when needed +FROM base AS deps +# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. +RUN apk add --no-cache libc6-compat +WORKDIR /app + +# Install dependencies based on the preferred package manager +COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./ +RUN \ + if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ + elif [ -f package-lock.json ]; then npm ci; \ + elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \ + else echo "Lockfile not found." && exit 1; \ + fi + + +# Rebuild the source code only when needed +FROM base AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . + +# Next.js collects completely anonymous telemetry data about general usage. +# Learn more here: https://nextjs.org/telemetry +# Uncomment the following line in case you want to disable telemetry during the build. +# ENV NEXT_TELEMETRY_DISABLED 1 + +RUN \ + if [ -f yarn.lock ]; then yarn run build; \ + elif [ -f package-lock.json ]; then npm run build; \ + elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \ + else echo "Lockfile not found." && exit 1; \ + fi + +# Production image, copy all the files and run next +FROM base AS runner +WORKDIR /app + +ENV NODE_ENV production +# Uncomment the following line in case you want to disable telemetry during runtime. +# ENV NEXT_TELEMETRY_DISABLED 1 + +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs + +COPY --from=builder /app/public ./public + +# Set the correct permission for prerender cache +RUN mkdir .next +RUN chown nextjs:nodejs .next + +# Automatically leverage output traces to reduce image size +# https://nextjs.org/docs/advanced-features/output-file-tracing +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + +USER nextjs + +EXPOSE 3000 + +ENV PORT 3000 + +# server.js is created by next build from the standalone output +# https://nextjs.org/docs/pages/api-reference/next-config-js/output +CMD HOSTNAME="0.0.0.0" node server.js diff --git a/traque-front/components/team/actionDrawer.jsx b/traque-front/components/team/actionDrawer.jsx index 13c00a3..ef96c2b 100644 --- a/traque-front/components/team/actionDrawer.jsx +++ b/traque-front/components/team/actionDrawer.jsx @@ -4,6 +4,7 @@ import BlueButton, { GreenButton, RedButton } from "../util/button"; import TextInput from "../util/textInput"; import { useTeamConnexion } from "@/context/teamConnexionContext"; import { EnemyTeamModal } from "./enemyTeamModal"; +import Image from "next/image"; export default function ActionDrawer() { const [visible, setVisible] = useState(false); @@ -29,10 +30,10 @@ export default function ActionDrawer() { return (
- setVisible(!visible)} /> + setVisible(!visible)} /> {visible &&
- +
diff --git a/traque-front/components/team/enemyTeamModal.jsx b/traque-front/components/team/enemyTeamModal.jsx index b4eda3b..84a9f32 100644 --- a/traque-front/components/team/enemyTeamModal.jsx +++ b/traque-front/components/team/enemyTeamModal.jsx @@ -1,6 +1,7 @@ import useGame from "@/hook/useGame"; import { RedButton } from "../util/button"; import { useEffect, useRef } from "react"; +import Image from "next/image"; export function EnemyTeamModal({ visible, onClose }) { const { teamId, enemyName } = useGame(); @@ -22,7 +23,7 @@ export function EnemyTeamModal({ visible, onClose }) {

{enemyName}

- Close
diff --git a/traque-front/components/team/map.jsx b/traque-front/components/team/map.jsx index c2deb01..7d219aa 100644 --- a/traque-front/components/team/map.jsx +++ b/traque-front/components/team/map.jsx @@ -71,7 +71,7 @@ export function LiveMap({ ...props }) { shadowSize: [41, 41] })}> - Position de l'ennemi + Position de l&aposennemi } diff --git a/traque-front/components/team/notification.jsx b/traque-front/components/team/notification.jsx index 9d2f01d..e24a95a 100644 --- a/traque-front/components/team/notification.jsx +++ b/traque-front/components/team/notification.jsx @@ -41,7 +41,7 @@ export function Notification({socket }) { return ( Object.keys(bgColorMap).map((key) => notification?.type == key && -
setVisible(false)}> +
setVisible(false)}>

{notification?.text}

)); diff --git a/traque-front/components/team/placementOverlay.jsx b/traque-front/components/team/placementOverlay.jsx index c4bb5a6..4e77b90 100644 --- a/traque-front/components/team/placementOverlay.jsx +++ b/traque-front/components/team/placementOverlay.jsx @@ -1,12 +1,13 @@ import { useTeamConnexion } from "@/context/teamConnexionContext"; import useGame from "@/hook/useGame" +import Image from "next/image"; export default function PlacementOverlay() { const { name, ready } = useGame(); const {logout} = useTeamConnexion(); return ( <> - +
Placement
{name}
diff --git a/traque-front/components/team/waitingScreen.jsx b/traque-front/components/team/waitingScreen.jsx index db58d46..e06b322 100644 --- a/traque-front/components/team/waitingScreen.jsx +++ b/traque-front/components/team/waitingScreen.jsx @@ -1,6 +1,7 @@ import useGame from "@/hook/useGame" import { GreenButton, LogoutButton } from "../util/button"; import { useRef } from "react"; +import Image from "next/image"; export function WaitingScreen() { const { name, teamId } = useGame(); @@ -35,13 +36,13 @@ export function WaitingScreen() { Jeu en préparation, veuillez patienter...
-

Uploadez une photo où tous les membres de l'équipe sont visibles

+

Uploadez une photo où tous les membres de l&aposéquipe sont visibles

Envoyer
- {teamId && @@ -20,5 +21,5 @@ export function GreenButton({ children, ...props }) { export function LogoutButton() { const { logout } = useTeamConnexion(); - return + return } \ No newline at end of file diff --git a/traque-front/next.config.mjs b/traque-front/next.config.mjs index 4678774..7ea2c19 100644 --- a/traque-front/next.config.mjs +++ b/traque-front/next.config.mjs @@ -1,4 +1,8 @@ /** @type {import('next').NextConfig} */ -const nextConfig = {}; +const nextConfig = { + experimental: { + outputStandalone: true + } +}; export default nextConfig; diff --git a/traque-front/package-lock.json b/traque-front/package-lock.json index e74d06a..c48a574 100644 --- a/traque-front/package-lock.json +++ b/traque-front/package-lock.json @@ -46,9 +46,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz", - "integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.4.tgz", + "integrity": "sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -130,54 +130,6 @@ "react-dom": "^16.8.5 || ^17.0.0 || ^18.0.0" } }, - "node_modules/@hello-pangea/dnd/node_modules/memoize-one": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", - "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" - }, - "node_modules/@hello-pangea/dnd/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" - }, - "node_modules/@hello-pangea/dnd/node_modules/react-redux": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz", - "integrity": "sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==", - "dependencies": { - "@babel/runtime": "^7.12.1", - "@types/hoist-non-react-statics": "^3.3.1", - "@types/use-sync-external-store": "^0.0.3", - "hoist-non-react-statics": "^3.3.2", - "react-is": "^18.0.0", - "use-sync-external-store": "^1.0.0" - }, - "peerDependencies": { - "@types/react": "^16.8 || ^17.0 || ^18.0", - "@types/react-dom": "^16.8 || ^17.0 || ^18.0", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0", - "react-native": ">=0.59", - "redux": "^4 || ^5.0.0-beta.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - }, - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - }, - "redux": { - "optional": true - } - } - }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -206,9 +158,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", - "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, "node_modules/@isaacs/cliui": { @@ -508,15 +460,15 @@ } }, "node_modules/@rushstack/eslint-patch": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.8.0.tgz", - "integrity": "sha512-0HejFckBN2W+ucM6cUOlwsByTKt9/+0tWhqUffNIcHqCXkthY/mZ7AuYPK/2IIaGWhdl0h+tICDO0ssLMd6XMQ==", + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.2.tgz", + "integrity": "sha512-hw437iINopmQuxWPSUEvqE56NCPsiU8N4AYtfHmJFckclktzK9YQJieD3XkDCDH4OjL+C7zgPUh73R/nrcHrqw==", "dev": true }, "node_modules/@socket.io/component-emitter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", - "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.1.tgz", + "integrity": "sha512-dzJtaDAAoXx4GCOJpbB2eG/Qj8VDpdwkLsWGzGm+0L7E8/434RyMbAHmk9ubXWVAb9nXmc44jUf8GKqVDiKezg==" }, "node_modules/@swc/helpers": { "version": "0.5.2", @@ -547,20 +499,14 @@ "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" }, "node_modules/@types/react": { - "version": "18.2.69", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.69.tgz", - "integrity": "sha512-W1HOMUWY/1Yyw0ba5TkCV+oqynRjG7BnteBB+B7JmAK7iw3l2SW+VGOxL+akPweix6jk2NNJtyJKpn4TkpfK3Q==", + "version": "18.2.79", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.79.tgz", + "integrity": "sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w==", "dependencies": { "@types/prop-types": "*", - "@types/scheduler": "*", "csstype": "^3.0.2" } }, - "node_modules/@types/scheduler": { - "version": "0.16.8", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", - "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==" - }, "node_modules/@types/use-sync-external-store": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", @@ -1165,9 +1111,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001600", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001600.tgz", - "integrity": "sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==", + "version": "1.0.30001612", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001612.tgz", + "integrity": "sha512-lFgnZ07UhaCcsSZgWW0K5j4e69dK1u/ltrL9lTUiFOwNHs12S3UMIEYgBV0Z6C6hRDev7iRnMzzYmKabYdXF9g==", "funding": [ { "type": "opencollective", @@ -1477,9 +1423,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.715", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.715.tgz", - "integrity": "sha512-XzWNH4ZSa9BwVUQSDorPWAUQ5WGuYz7zJUNpNif40zFCiCl20t8zgylmreNmn26h5kiyw2lg7RfTmeMBsDklqg==", + "version": "1.4.747", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.747.tgz", + "integrity": "sha512-+FnSWZIAvFHbsNVmUxhEqWiaOiPMcfum1GQzlWCg/wLigVtshOsjXHyEFfmt6cFK6+HkS3QOJBv6/3OPumbBfw==", "dev": true }, "node_modules/emoji-regex": { @@ -1522,9 +1468,9 @@ } }, "node_modules/es-abstract": { - "version": "1.23.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.2.tgz", - "integrity": "sha512-60s3Xv2T2p1ICykc7c+DNDPLDMm9t4QxCOUU0K9JxiLjM3C1zB9YVdN7tjxrFd4+AkZ8CdX1ovUga4P2+1e+/w==", + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.1", @@ -1566,11 +1512,11 @@ "safe-regex-test": "^1.0.3", "string.prototype.trim": "^1.2.9", "string.prototype.trimend": "^1.0.8", - "string.prototype.trimstart": "^1.0.7", + "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.2", "typed-array-byte-length": "^1.0.1", "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.5", + "typed-array-length": "^1.0.6", "unbox-primitive": "^1.0.2", "which-typed-array": "^1.1.15" }, @@ -2413,9 +2359,9 @@ } }, "node_modules/glob/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -3241,6 +3187,11 @@ "node": "14 || >=16.14" } }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -3664,12 +3615,12 @@ "dev": true }, "node_modules/path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", + "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", "dev": true, "dependencies": { - "lru-cache": "^9.1.1 || ^10.0.0", + "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { @@ -3976,6 +3927,49 @@ "react-dom": "^18.0.0" } }, + "node_modules/react-redux": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz", + "integrity": "sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==", + "dependencies": { + "@babel/runtime": "^7.12.1", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/use-sync-external-store": "^0.0.3", + "hoist-non-react-statics": "^3.3.2", + "react-is": "^18.0.0", + "use-sync-external-store": "^1.0.0" + }, + "peerDependencies": { + "@types/react": "^16.8 || ^17.0 || ^18.0", + "@types/react-dom": "^16.8 || ^17.0 || ^18.0", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0", + "react-native": ">=0.59", + "redux": "^4 || ^5.0.0-beta.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "node_modules/react-redux/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -4611,9 +4605,9 @@ } }, "node_modules/tailwindcss": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz", - "integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz", + "integrity": "sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==", "dev": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", @@ -4624,7 +4618,7 @@ "fast-glob": "^3.3.0", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", - "jiti": "^1.19.1", + "jiti": "^1.21.0", "lilconfig": "^2.1.0", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", @@ -4833,9 +4827,9 @@ } }, "node_modules/typescript": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", - "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, "peer": true, "bin": {