Merge pull request 'mathieu' (#25) from mathieu into main

Reviewed-on: https://git.rezel.net/LudoTech/traque/pulls/25
This commit is contained in:
moriol
2024-09-15 18:11:28 +02:00
24 changed files with 234 additions and 206 deletions

View File

@@ -1,26 +1,18 @@
services:
front:
build: ./traque-front
restart: always
reverse_proxy:
build: ./proxy
ports:
- "3000:443"
volumes:
# reusing the ssl files from the backend
- ./traque-back/ssl:/etc/nginx/ssl:ro
- "80:80"
restart: always
front:
build: ./traque-front
restart: always
back:
build: ./traque-back
ports:
- "3001:3001"
restart: always
environment:
ADMIN_PASSWORD: 'traquebdsbanger'
HOST: '0.0.0.0'
PORT: 3001
#Those files need to exist in traque-back/ssl for https to work
SSL_KEY: "ssl/privkey.pem"
SSL_CERT: "ssl/cert.pem"
PORT: 3001

View File

@@ -1,8 +1,7 @@
# nginx/Dockerfile
FROM nginx:1.23.3-alpine
FROM nginx:alpine
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
EXPOSE 443

View File

@@ -1,42 +1,26 @@
# nginx/nginx.conf
events {
worker_connections 1024;
}
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;
proxy_pass http://front:3000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location @front {
location /back/ {
proxy_pass http://back:3001/;
proxy_set_header X-Real-IP $remote_addr;
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";
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
}
}
}

View File

@@ -1,3 +1,7 @@
---
lang: en-GB
---
# The game
## General principle
La traque is a IRL team game where the goal is to catch another team without being catched by another team.
@@ -63,10 +67,10 @@ https://example.com:3000
```
## Deployment
Put your certificate and private key in the proxy/ssl folder. They need to be named `cert.pem` and `privkey.pem`.
You can then depploy the docker application with `docker compose up`.
Put your certificate and private key in the `proxy/ssl` folder. They need to be named `cert.pem` and `privkey.pem`.
You can then deploy the docker application with `docker compose up`.
You can change the production environment varialbes for the backend in the docker-compose.yml file. The frontend environment variables can be changed in the .env file in the traque-front directory.
You can change the production environment variables for the backend in the `docker-compose.yml` file. The frontend environment variables can be changed in the `.env` file in the `traque-front` directory.
# Authors
- [Quentin Roussel](mailto:quentin.roussel11@gmail.com) (initial version)

View File

@@ -1,5 +1,5 @@
# Use Node 16 alpine as parent image
FROM node:16-alpine
# Use Node 20 alpine as parent image
FROM node:20-alpine
# Change the working directory on the Docker image to /app
WORKDIR /app

View File

@@ -20,9 +20,9 @@ const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD;
* @param {String} data The data to send
*/
export function secureAdminBroadcast(event, data) {
loggedInSockets.forEach(s => {
io.of("admin").to(s).emit(event, data);
});
loggedInSockets.forEach(s => {
io.of("admin").to(s).emit(event, data);
});
}
//Array of logged in sockets
@@ -73,10 +73,10 @@ export function initAdminSocketHandler() {
socket.emit("error", "Not logged in");
return;
}
if(!game.changeSettings(settings)) {
if (!game.changeSettings(settings)) {
socket.emit("error", "Invalid settings");
}
secureAdminBroadcast("game_settings",game.settings);
secureAdminBroadcast("game_settings", game.settings);
playersBroadcast("game_settings", game.settings);
})
@@ -99,10 +99,10 @@ export function initAdminSocketHandler() {
socket.emit("error", "Not logged in");
return;
}
if(!penaltyController.updateSettings(settings)) {
if (!penaltyController.updateSettings(settings)) {
socket.emit("error", "Invalid settings");
socket.emit("penalty_settings", penaltyController.settings)
}else {
} else {
secureAdminBroadcast("penalty_settings", penaltyController.settings)
}

View File

@@ -8,6 +8,8 @@ import { playersBroadcast, sendUpdatedTeamInformations } from "./team_socket.js"
import penaltyController from "./penalty_controller.js";
import zoneManager from "./zone_manager.js";
import { getDistanceFromLatLon } from "./map_utils.js";
/**
* The possible states of the game
*/
@@ -20,11 +22,11 @@ export const GameState = {
export default {
//List of teams, as objects. To see the fields see the addTeam methods
teams : [],
teams: [],
//Current state of the game
state : GameState.SETUP,
state: GameState.SETUP,
//Settings of the game
settings : {
settings: {
loserEndGameMessage: "",
winnerEndGameMessage: "",
capturedMessage: "",
@@ -37,7 +39,7 @@ export default {
* @returns true if the settings are applied
*/
changeSettings(newSettings) {
this.settings = {...this.settings, ...newSettings};
this.settings = { ...this.settings, ...newSettings };
return true;
},
@@ -232,7 +234,7 @@ export default {
}
//The location sent by the team will be null if the browser call API dooes not succeed
//See issue #19
if(location == null) {
if (location == null) {
return false;
}
team.currentLocation = location;
@@ -264,7 +266,7 @@ export default {
if (team == undefined) {
return false;
}
if(team.currentLocation == null) {
if (team.currentLocation == null) {
return false;
}
@@ -329,6 +331,13 @@ export default {
if (this.state == GameState.PLAYING || this.state == GameState.FINISHED) {
return false;
}
var min = newSettings.min;
var max = newSettings.max;
// The end zone must be included in the start zone
var dist = getDistanceFromLatLon(min.center, max.center);
if (min.radius + dist >= max.radius) {
return false;
}
return zoneManager.udpateSettings(newSettings)
},

View File

@@ -1,4 +1,4 @@
import { createServer } from "https";
import { createServer } from "http";
import express from "express";
import { Server } from "socket.io";
@@ -14,19 +14,16 @@ const PORT = process.env.PORT;
export const app = express()
const httpsServer = createServer({
key: readFileSync(process.env.SSL_KEY, 'utf-8'),
cert: readFileSync(process.env.SSL_CERT, 'utf-8')
}, app);
const httpServer = createServer({}, app);
httpsServer.listen(PORT, HOST, () => {
console.log(`Server running`);
httpServer.listen(PORT, HOST, () => {
console.log("Server running on http://" + HOST + ":" + PORT);
});
//set cors to allow all origins
export const io = new Server(httpsServer, {
export const io = new Server(httpServer, {
cors: {
origin: "*",
methods: ["GET", "POST"]
@@ -35,4 +32,4 @@ export const io = new Server(httpsServer, {
initAdminSocketHandler();
initTeamSocket();
initPhotoUpload();
initPhotoUpload();

View File

@@ -287,9 +287,9 @@
}
},
"node_modules/engine.io": {
"version": "6.5.4",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.4.tgz",
"integrity": "sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==",
"version": "6.5.5",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz",
"integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==",
"dependencies": {
"@types/cookie": "^0.4.1",
"@types/cors": "^2.8.12",
@@ -300,7 +300,7 @@
"cors": "~2.8.5",
"debug": "~4.3.1",
"engine.io-parser": "~5.2.1",
"ws": "~8.11.0"
"ws": "~8.17.1"
},
"engines": {
"node": ">=10.2.0"
@@ -931,12 +931,12 @@
}
},
"node_modules/socket.io-adapter": {
"version": "2.5.4",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.4.tgz",
"integrity": "sha512-wDNHGXGewWAjQPt3pyeYBtpWSq9cLE5UW1ZUPL/2eGK9jtse/FpXib7epSTsz0Q0m+6sg6Y4KtcFTlah1bdOVg==",
"version": "2.5.5",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz",
"integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==",
"dependencies": {
"debug": "~4.3.4",
"ws": "~8.11.0"
"ws": "~8.17.1"
}
},
"node_modules/socket.io-parser": {
@@ -1040,15 +1040,15 @@
}
},
"node_modules/ws": {
"version": "8.11.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {

View File

@@ -4,7 +4,7 @@ This module manages the verification of the game rules and the penalties.
import { isInCircle } from "./map_utils.js";
import { sendUpdatedTeamInformations, teamBroadcast } from "./team_socket.js";
import { secureAdminBroadcast } from "./admin_socket.js";
import game, {GameState} from "./game.js";
import game, { GameState } from "./game.js";
import zone from "./zone_manager.js";
export default {

View File

@@ -4,7 +4,7 @@ It receives messages, checks permissions, manages authentication and performs ac
This module also exposes functions to send messages via socket to all teams
*/
import { secureAdminBroadcast } from "./admin_socket.js";
import { io} from "./index.js";
import { io } from "./index.js";
import game from "./game.js";
import zone from "./zone_manager.js";
@@ -43,7 +43,7 @@ function logoutPlayer(id) {
export function sendUpdatedTeamInformations(teamId) {
let team = game.getTeam(teamId)
if(!team) {
if (!team) {
return false;
}
team.sockets.forEach(socketId => {
@@ -106,7 +106,7 @@ export function initTeamSocket() {
return;
}
let team = game.getTeam(teamId)
if(team == undefined) {
if (team == undefined) {
logoutPlayer(socket.id);
return;
}
@@ -125,8 +125,8 @@ export function initTeamSocket() {
return;
}
game.updateTeamChasing();
teamBroadcast(teamId, "update_team", { enemyLocation: team.enemyLocation,locationSendDeadline: team.locationSendDeadline });
teamBroadcast(teamId,"success", "Position udpated")
teamBroadcast(teamId, "update_team", { enemyLocation: team.enemyLocation, locationSendDeadline: team.locationSendDeadline });
teamBroadcast(teamId, "success", "Position udpated")
secureAdminBroadcast("teams", game.teams)
});

View File

@@ -10,4 +10,4 @@
*/
export function map(value, oldMin, oldMax, newMin, newMax) {
return ((value - oldMin) / (oldMax - oldMin)) * (newMax - newMin) + newMin;
}
}

View File

@@ -200,8 +200,8 @@ export default {
onNextZoneUpdate(newZone) {
playersBroadcast("new_zone", newZone)
secureAdminBroadcast("new_zone", newZone)
},
},
//a call to onZoneUpdate will be made every updateIntervalSeconds when the zone is changing
onZoneUpdate(zone) {
playersBroadcast("zone", zone)

View File

@@ -1,4 +1,4 @@
FROM node:18-alpine AS base
FROM node:20-alpine AS base
# Install dependencies only when needed
FROM base AS deps
@@ -7,13 +7,8 @@ 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
COPY package.json package-lock.json* ./
RUN npm ci
# Rebuild the source code only when needed
@@ -25,14 +20,9 @@ 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
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
RUN npm run build
# Production image, copy all the files and run next
FROM base AS runner
@@ -40,7 +30,7 @@ WORKDIR /app
ENV NODE_ENV production
# Uncomment the following line in case you want to disable telemetry during runtime.
# ENV NEXT_TELEMETRY_DISABLED 1
ENV NEXT_TELEMETRY_DISABLED 1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

View File

@@ -25,12 +25,12 @@ export default function AdminPage() {
<h2 className="text-2xl">Game state </h2>
<strong className="p-5 bg-gray-900 text-white text-xl rounded">Current : {gameState}</strong>
<div className="flex flex-row">
<RedButton onClick={() => changeState(GameState.SETUP)}>Reset game</RedButton>
<GreenButton onClick={() => changeState(GameState.PLACEMENT)}>Start placement</GreenButton>
<BlueButton onClick={() => changeState(GameState.PLAYING)}>Start game</BlueButton>
<RedButton onClick={() => changeState(GameState.SETUP)}>Reset game</RedButton>
<GreenButton onClick={() => changeState(GameState.PLACEMENT)}>Start placement</GreenButton>
<BlueButton onClick={() => changeState(GameState.PLAYING)}>Start game</BlueButton>
</div>
</div>
<GameSettings />
<GameSettings />
</div>
{gameState == GameState.PLACEMENT && <div className="max-h-5/6"><TeamReady /></div>}
{(gameState == GameState.SETUP || gameState == GameState.PLACEMENT) && <ZoneSelector />}

View File

@@ -80,17 +80,26 @@ export function ZonePicker({ minZone, setMinZone, maxZone, setMaxZone, editMode,
minHover(e);
}
}
return (
<MapContainer {...props} className='min-h-full w-full ' center={[0, 0]} zoom={0} scrollWheelZoom={true}>
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{minCenter && minRadius && <Circle center={minCenter} radius={minRadius} color="blue" fillColor="blue" />}
{maxCenter && maxRadius && <Circle center={maxCenter} radius={maxRadius} color="red" fillColor="red" />}
<MapPan center={location} zoom={DEFAULT_ZOOM} />
<MapEventListener onClick={handleClick} onMouseMove={handleMouseMove} />
</MapContainer>
<div>
<div className='h-96'>
<MapContainer {...props} className='min-h-full w-full ' center={[0, 0]} zoom={0} scrollWheelZoom={true}>
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{minCenter && minRadius && <Circle center={minCenter} radius={minRadius} color="blue" fillColor="blue" />}
{maxCenter && maxRadius && <Circle center={maxCenter} radius={maxRadius} color="red" fillColor="red" />}
<MapPan center={location} zoom={DEFAULT_ZOOM} />
<MapEventListener onClick={handleClick} onMouseMove={handleMouseMove} />
</MapContainer>
</div>
{ maxCenter && minCenter && typeof maxCenter.distanceTo === 'function'
&& maxRadius + maxCenter.distanceTo(minCenter) >= minRadius
&& <p className="text-red-500">La zone de fin doit être incluse dans celle de départ</p>}
</div>
)
}

View File

@@ -12,8 +12,13 @@ export default function TeamEdit({ selectedTeamId, setSelectedTeamId }) {
const teamImage = useRef(null);
const [newTeamName, setNewTeamName] = React.useState('');
const { updateTeam, getTeamName, removeTeam, getTeam, teams } = useAdmin();
const [team, setTeam] = useState({})
const SERVER_URL = "https://" + process.env.NEXT_PUBLIC_SOCKET_HOST + ":" + process.env.NEXT_PUBLIC_SOCKET_PORT;
const [team, setTeam] = useState({});
var protocol = "https://";
if (process.env.NEXT_PUBLIC_SOCKET_HOST == "localhost") {
protocol = "http://";
}
const SERVER_URL = protocol + process.env.NEXT_PUBLIC_SOCKET_HOST + "/back";
console.log(SERVER_URL);
useEffect(() => {
let team = getTeam(selectedTeamId);

View File

@@ -41,9 +41,7 @@ export function ZoneSelector() {
<h2 className="text-2xl">Edit zones</h2>
{editMode == EditMode.MIN && <RedButton onClick={() => setEditMode(EditMode.MAX)}>Edit end zone</RedButton>}
{editMode == EditMode.MAX && <BlueButton onClick={() => setEditMode(EditMode.MIN)}>Edit start zone</BlueButton>}
<div className='h-96'>
<ZonePicker minZone={minZone} maxZone={maxZone} editMode={editMode} setMinZone={setMinZone} setMaxZone={setMaxZone} />
</div>
<ZonePicker minZone={minZone} maxZone={maxZone} editMode={editMode} setMinZone={setMinZone} setMaxZone={setMaxZone} />
<div>
<p>Number of reductions</p>
<TextInput value={reductionCount} onChange={(e) => setReductionCount(e.target.value)}></TextInput>

View File

@@ -17,7 +17,12 @@ export function EnemyTeamModal({ visible, onClose }) {
imageRef.current.src = SERVER_URL + "/photo/enemy?team=" + teamId.toString() + "&t=" + new Date().getTime();
}
const SERVER_URL = "https://" + process.env.NEXT_PUBLIC_SOCKET_HOST + ":" + process.env.NEXT_PUBLIC_SOCKET_PORT;
var protocol = "https://";
if (process.env.NEXT_PUBLIC_SOCKET_HOST == "localhost") {
protocol = "http://";
}
const SERVER_URL = protocol + process.env.NEXT_PUBLIC_SOCKET_HOST + "/back";
console.log(SERVER_URL);
return (visible &&
<>
<div className='fixed w-screen h-screen bg-black bg-opacity-50 z-10 text-center'></div>

View File

@@ -8,7 +8,12 @@ export function WaitingScreen() {
const { name, teamId } = useGame();
const { gameSettings } = useTeamContext();
const imageRef = useRef(null);
const SERVER_URL = "https://" + process.env.NEXT_PUBLIC_SOCKET_HOST + ":" + process.env.NEXT_PUBLIC_SOCKET_PORT;
var protocol = "https://";
if (process.env.NEXT_PUBLIC_SOCKET_HOST == "localhost") {
protocol = "http://";
}
const SERVER_URL = protocol + process.env.NEXT_PUBLIC_SOCKET_HOST + "/back";
console.log(SERVER_URL);
function sendImage() {
let data = new FormData();

View File

@@ -3,12 +3,21 @@ import { createContext, useContext, useMemo } from "react";
const { io } = require("socket.io-client");
const SOCKET_URL = 'wss://' + process.env.NEXT_PUBLIC_SOCKET_HOST + ':' + process.env.NEXT_PUBLIC_SOCKET_PORT;
var proto = "wss://";
if (process.env.NEXT_PUBLIC_SOCKET_HOST == "localhost") {
proto = "ws://";
}
const SOCKET_URL = proto + process.env.NEXT_PUBLIC_SOCKET_HOST;
const USER_SOCKET_URL = SOCKET_URL + "/player";
const ADMIN_SOCKET_URL = SOCKET_URL + "/admin";
export const teamSocket = io(USER_SOCKET_URL);
export const adminSocket = io(ADMIN_SOCKET_URL);
export const teamSocket = io(USER_SOCKET_URL, {
path: "/back/socket.io",
});
export const adminSocket = io(ADMIN_SOCKET_URL, {
path: "/back/socket.io",
});
export const SocketContext = createContext();
export default function SocketProvider({ children }) {

View File

@@ -1,8 +1,20 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
outputStandalone: true
}
output: 'standalone',
async redirects() {
return [
{
source: '/',
destination: '/team',
permanent: true,
},
]
},
};
export default nextConfig;

View File

@@ -10,7 +10,7 @@
"dependencies": {
"@hello-pangea/dnd": "^16.6.0",
"leaflet-defaulticon-compatibility": "^0.1.2",
"next": "14.1.4",
"next": "^14.2.9",
"react": "^18",
"react-dom": "^18",
"react-leaflet": "^4.2.1",
@@ -256,9 +256,9 @@
}
},
"node_modules/@next/env": {
"version": "14.1.4",
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.4.tgz",
"integrity": "sha512-e7X7bbn3Z6DWnDi75UWn+REgAbLEqxI8Tq2pkFOFAMpWAWApz/YCUhtWMWn410h8Q2fYiYL7Yg5OlxMOCfFjJQ=="
"version": "14.2.9",
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.9.tgz",
"integrity": "sha512-hnDAoDPMii31V0ivibI8p6b023jOF1XblWTVjsDUoZKwnZlaBtJFZKDwFqi22R8r9i6W08dThUWU7Bsh2Rg8Ww=="
},
"node_modules/@next/eslint-plugin-next": {
"version": "14.1.4",
@@ -270,9 +270,9 @@
}
},
"node_modules/@next/swc-darwin-arm64": {
"version": "14.1.4",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.4.tgz",
"integrity": "sha512-ubmUkbmW65nIAOmoxT1IROZdmmJMmdYvXIe8211send9ZYJu+SqxSnJM4TrPj9wmL6g9Atvj0S/2cFmMSS99jg==",
"version": "14.2.9",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.9.tgz",
"integrity": "sha512-/kfQifl3uLYi3DlwFlzCkgxe6fprJNLzzTUFknq3M5wGYicDIbdGlxUl6oHpVLJpBB/CBY3Y//gO6alz/K4NWA==",
"cpu": [
"arm64"
],
@@ -285,9 +285,9 @@
}
},
"node_modules/@next/swc-darwin-x64": {
"version": "14.1.4",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.4.tgz",
"integrity": "sha512-b0Xo1ELj3u7IkZWAKcJPJEhBop117U78l70nfoQGo4xUSvv0PJSTaV4U9xQBLvZlnjsYkc8RwQN1HoH/oQmLlQ==",
"version": "14.2.9",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.9.tgz",
"integrity": "sha512-tK/RyhCmOCiXQ9IVdFrBbZOf4/1+0RSuJkebXU2uMEsusS51TjIJO4l8ZmEijH9gZa0pJClvmApRHi7JuBqsRw==",
"cpu": [
"x64"
],
@@ -300,9 +300,9 @@
}
},
"node_modules/@next/swc-linux-arm64-gnu": {
"version": "14.1.4",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.4.tgz",
"integrity": "sha512-457G0hcLrdYA/u1O2XkRMsDKId5VKe3uKPvrKVOyuARa6nXrdhJOOYU9hkKKyQTMru1B8qEP78IAhf/1XnVqKA==",
"version": "14.2.9",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.9.tgz",
"integrity": "sha512-tS5eqwsp2nO7mzywRUuFYmefNZsUKM/mTG3exK2jIHv9TEVklE1SByB1KMhFkqlit1PxS9YK1tV8BOV90Wpbrw==",
"cpu": [
"arm64"
],
@@ -315,9 +315,9 @@
}
},
"node_modules/@next/swc-linux-arm64-musl": {
"version": "14.1.4",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.4.tgz",
"integrity": "sha512-l/kMG+z6MB+fKA9KdtyprkTQ1ihlJcBh66cf0HvqGP+rXBbOXX0dpJatjZbHeunvEHoBBS69GYQG5ry78JMy3g==",
"version": "14.2.9",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.9.tgz",
"integrity": "sha512-8svpeTFNAMTUMKQbEzE8qRAwl9o7mNBv7LR1bmSkQvo1oy4WrNyZbhWsldOiKrc4mZ5dfQkGYsI9T75mIFMfeA==",
"cpu": [
"arm64"
],
@@ -330,9 +330,9 @@
}
},
"node_modules/@next/swc-linux-x64-gnu": {
"version": "14.1.4",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.4.tgz",
"integrity": "sha512-BapIFZ3ZRnvQ1uWbmqEGJuPT9cgLwvKtxhK/L2t4QYO7l+/DxXuIGjvp1x8rvfa/x1FFSsipERZK70pewbtJtw==",
"version": "14.2.9",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.9.tgz",
"integrity": "sha512-0HNulLWpKTB7H5BhHCkEhcRAnWUHeAYCftrrGw3QC18+ZywTdAoPv/zEqKy/0adqt+ks4JDdlgSQ1lNKOKjo0A==",
"cpu": [
"x64"
],
@@ -345,9 +345,9 @@
}
},
"node_modules/@next/swc-linux-x64-musl": {
"version": "14.1.4",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.4.tgz",
"integrity": "sha512-mqVxTwk4XuBl49qn2A5UmzFImoL1iLm0KQQwtdRJRKl21ylQwwGCxJtIYo2rbfkZHoSKlh/YgztY0qH3wG1xIg==",
"version": "14.2.9",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.9.tgz",
"integrity": "sha512-hhVFViPHLAVUJRNtwwm609p9ozWajOmRvzOZzzKXgiVGwx/CALxlMUeh+M+e0Zj6orENhWLZeilOPHpptuENsA==",
"cpu": [
"x64"
],
@@ -360,9 +360,9 @@
}
},
"node_modules/@next/swc-win32-arm64-msvc": {
"version": "14.1.4",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.4.tgz",
"integrity": "sha512-xzxF4ErcumXjO2Pvg/wVGrtr9QQJLk3IyQX1ddAC/fi6/5jZCZ9xpuL9Tzc4KPWMFq8GGWFVDMshZOdHGdkvag==",
"version": "14.2.9",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.9.tgz",
"integrity": "sha512-p/v6XlOdrk06xfN9z4evLNBqftVQUWiyduQczCwSj7hNh8fWTbzdVxsEiNOcajMXJbQiaX/ZzZdFgKVmmJnnGQ==",
"cpu": [
"arm64"
],
@@ -375,9 +375,9 @@
}
},
"node_modules/@next/swc-win32-ia32-msvc": {
"version": "14.1.4",
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.4.tgz",
"integrity": "sha512-WZiz8OdbkpRw6/IU/lredZWKKZopUMhcI2F+XiMAcPja0uZYdMTZQRoQ0WZcvinn9xZAidimE7tN9W5v9Yyfyw==",
"version": "14.2.9",
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.9.tgz",
"integrity": "sha512-IcW9dynWDjMK/0M05E3zopbRen7v0/yEaMZbHFOSS1J/w+8YG3jKywOGZWNp/eCUVtUUXs0PW+7Lpz8uLu+KQA==",
"cpu": [
"ia32"
],
@@ -390,9 +390,9 @@
}
},
"node_modules/@next/swc-win32-x64-msvc": {
"version": "14.1.4",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.4.tgz",
"integrity": "sha512-4Rto21sPfw555sZ/XNLqfxDUNeLhNYGO2dlPqsnuCg8N8a2a9u1ltqBOPQ4vj1Gf7eJC0W2hHG2eYUHuiXgY2w==",
"version": "14.2.9",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.9.tgz",
"integrity": "sha512-gcbpoXyWZdVOBgNa5BRzynrL5UR1nb2ZT38yKgnphYU9UHjeecnylMHntrQiMg/QtONDcJPFC/PmsS47xIRYoA==",
"cpu": [
"x64"
],
@@ -470,11 +470,17 @@
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.1.tgz",
"integrity": "sha512-dzJtaDAAoXx4GCOJpbB2eG/Qj8VDpdwkLsWGzGm+0L7E8/434RyMbAHmk9ubXWVAb9nXmc44jUf8GKqVDiKezg=="
},
"node_modules/@swc/counter": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
"integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="
},
"node_modules/@swc/helpers": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz",
"integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==",
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz",
"integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==",
"dependencies": {
"@swc/counter": "^0.1.3",
"tslib": "^2.4.0"
}
},
@@ -1019,12 +1025,12 @@
}
},
"node_modules/braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
"dependencies": {
"fill-range": "^7.0.1"
"fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
@@ -1435,14 +1441,14 @@
"dev": true
},
"node_modules/engine.io-client": {
"version": "6.5.3",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz",
"integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==",
"version": "6.5.4",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.4.tgz",
"integrity": "sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ==",
"dependencies": {
"@socket.io/component-emitter": "~3.1.0",
"debug": "~4.3.1",
"engine.io-parser": "~5.2.1",
"ws": "~8.11.0",
"ws": "~8.17.1",
"xmlhttprequest-ssl": "~2.0.0"
}
},
@@ -2126,9 +2132,9 @@
}
},
"node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"dependencies": {
"to-regex-range": "^5.0.1"
@@ -3202,12 +3208,12 @@
}
},
"node_modules/micromatch": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true,
"dependencies": {
"braces": "^3.0.2",
"braces": "^3.0.3",
"picomatch": "^2.3.1"
},
"engines": {
@@ -3284,12 +3290,12 @@
"dev": true
},
"node_modules/next": {
"version": "14.1.4",
"resolved": "https://registry.npmjs.org/next/-/next-14.1.4.tgz",
"integrity": "sha512-1WTaXeSrUwlz/XcnhGTY7+8eiaFvdet5z9u3V2jb+Ek1vFo0VhHKSAIJvDWfQpttWjnyw14kBeq28TPq7bTeEQ==",
"version": "14.2.9",
"resolved": "https://registry.npmjs.org/next/-/next-14.2.9.tgz",
"integrity": "sha512-3CzBNo6BuJnRjcQvRw+irnU1WiuJNZEp+dkzkt91y4jeIDN/Emg95F+takSYiLpJ/HkxClVQRyqiTwYce5IVqw==",
"dependencies": {
"@next/env": "14.1.4",
"@swc/helpers": "0.5.2",
"@next/env": "14.2.9",
"@swc/helpers": "0.5.5",
"busboy": "1.6.0",
"caniuse-lite": "^1.0.30001579",
"graceful-fs": "^4.2.11",
@@ -3303,18 +3309,19 @@
"node": ">=18.17.0"
},
"optionalDependencies": {
"@next/swc-darwin-arm64": "14.1.4",
"@next/swc-darwin-x64": "14.1.4",
"@next/swc-linux-arm64-gnu": "14.1.4",
"@next/swc-linux-arm64-musl": "14.1.4",
"@next/swc-linux-x64-gnu": "14.1.4",
"@next/swc-linux-x64-musl": "14.1.4",
"@next/swc-win32-arm64-msvc": "14.1.4",
"@next/swc-win32-ia32-msvc": "14.1.4",
"@next/swc-win32-x64-msvc": "14.1.4"
"@next/swc-darwin-arm64": "14.2.9",
"@next/swc-darwin-x64": "14.2.9",
"@next/swc-linux-arm64-gnu": "14.2.9",
"@next/swc-linux-arm64-musl": "14.2.9",
"@next/swc-linux-x64-gnu": "14.2.9",
"@next/swc-linux-x64-musl": "14.2.9",
"@next/swc-win32-arm64-msvc": "14.2.9",
"@next/swc-win32-ia32-msvc": "14.2.9",
"@next/swc-win32-x64-msvc": "14.2.9"
},
"peerDependencies": {
"@opentelemetry/api": "^1.1.0",
"@playwright/test": "^1.41.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"sass": "^1.3.0"
@@ -3323,6 +3330,9 @@
"@opentelemetry/api": {
"optional": true
},
"@playwright/test": {
"optional": true
},
"sass": {
"optional": true
}
@@ -4725,9 +4735,9 @@
}
},
"node_modules/tslib": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
"integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA=="
},
"node_modules/type-check": {
"version": "0.4.0",
@@ -5111,15 +5121,15 @@
"dev": true
},
"node_modules/ws": {
"version": "8.11.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {

View File

@@ -12,7 +12,7 @@
"dependencies": {
"@hello-pangea/dnd": "^16.6.0",
"leaflet-defaulticon-compatibility": "^0.1.2",
"next": "14.1.4",
"next": "^14.2.9",
"react": "^18",
"react-dom": "^18",
"react-leaflet": "^4.2.1",