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: services:
reverse_proxy:
build: ./proxy
ports:
- "80:80"
restart: always
front: front:
build: ./traque-front build: ./traque-front
restart: always restart: always
reverse_proxy:
build: ./proxy
ports:
- "3000:443"
volumes:
# reusing the ssl files from the backend
- ./traque-back/ssl:/etc/nginx/ssl:ro
restart: always
back: back:
build: ./traque-back build: ./traque-back
ports:
- "3001:3001"
restart: always restart: always
environment: environment:
ADMIN_PASSWORD: 'traquebdsbanger' ADMIN_PASSWORD: 'traquebdsbanger'
HOST: '0.0.0.0' HOST: '0.0.0.0'
PORT: 3001 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"

View File

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

View File

@@ -1,42 +1,26 @@
# nginx/nginx.conf # nginx/nginx.conf
events { events {
worker_connections 1024;
} }
http { http {
upstream front {
server front:3000;
}
server { server {
# Redirect HTTP requests to HTTPS.
listen 80; 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 / { 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-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https; proxy_http_version 1.1;
proxy_set_header X-Forwarded-Ssl on; proxy_set_header Upgrade $http_upgrade;
proxy_set_header Host $http_host; proxy_set_header Connection "Upgrade";
proxy_redirect off; proxy_set_header Host $host;
proxy_pass http://front;
proxy_cookie_path / "/; HTTPOnly; Secure";
} }
} }
} }

View File

@@ -1,3 +1,7 @@
---
lang: en-GB
---
# The game # The game
## General principle ## General principle
La traque is a IRL team game where the goal is to catch another team without being catched by another team. 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 ## Deployment
Put your certificate and private key in the proxy/ssl folder. They need to be named `cert.pem` and `privkey.pem`. 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`. 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 # Authors
- [Quentin Roussel](mailto:quentin.roussel11@gmail.com) (initial version) - [Quentin Roussel](mailto:quentin.roussel11@gmail.com) (initial version)

View File

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

View File

@@ -20,9 +20,9 @@ const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD;
* @param {String} data The data to send * @param {String} data The data to send
*/ */
export function secureAdminBroadcast(event, data) { export function secureAdminBroadcast(event, data) {
loggedInSockets.forEach(s => { loggedInSockets.forEach(s => {
io.of("admin").to(s).emit(event, data); io.of("admin").to(s).emit(event, data);
}); });
} }
//Array of logged in sockets //Array of logged in sockets
@@ -73,10 +73,10 @@ export function initAdminSocketHandler() {
socket.emit("error", "Not logged in"); socket.emit("error", "Not logged in");
return; return;
} }
if(!game.changeSettings(settings)) { if (!game.changeSettings(settings)) {
socket.emit("error", "Invalid settings"); socket.emit("error", "Invalid settings");
} }
secureAdminBroadcast("game_settings",game.settings); secureAdminBroadcast("game_settings", game.settings);
playersBroadcast("game_settings", game.settings); playersBroadcast("game_settings", game.settings);
}) })
@@ -99,10 +99,10 @@ export function initAdminSocketHandler() {
socket.emit("error", "Not logged in"); socket.emit("error", "Not logged in");
return; return;
} }
if(!penaltyController.updateSettings(settings)) { if (!penaltyController.updateSettings(settings)) {
socket.emit("error", "Invalid settings"); socket.emit("error", "Invalid settings");
socket.emit("penalty_settings", penaltyController.settings) socket.emit("penalty_settings", penaltyController.settings)
}else { } else {
secureAdminBroadcast("penalty_settings", penaltyController.settings) 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 penaltyController from "./penalty_controller.js";
import zoneManager from "./zone_manager.js"; import zoneManager from "./zone_manager.js";
import { getDistanceFromLatLon } from "./map_utils.js";
/** /**
* The possible states of the game * The possible states of the game
*/ */
@@ -20,11 +22,11 @@ export const GameState = {
export default { export default {
//List of teams, as objects. To see the fields see the addTeam methods //List of teams, as objects. To see the fields see the addTeam methods
teams : [], teams: [],
//Current state of the game //Current state of the game
state : GameState.SETUP, state: GameState.SETUP,
//Settings of the game //Settings of the game
settings : { settings: {
loserEndGameMessage: "", loserEndGameMessage: "",
winnerEndGameMessage: "", winnerEndGameMessage: "",
capturedMessage: "", capturedMessage: "",
@@ -37,7 +39,7 @@ export default {
* @returns true if the settings are applied * @returns true if the settings are applied
*/ */
changeSettings(newSettings) { changeSettings(newSettings) {
this.settings = {...this.settings, ...newSettings}; this.settings = { ...this.settings, ...newSettings };
return true; 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 //The location sent by the team will be null if the browser call API dooes not succeed
//See issue #19 //See issue #19
if(location == null) { if (location == null) {
return false; return false;
} }
team.currentLocation = location; team.currentLocation = location;
@@ -264,7 +266,7 @@ export default {
if (team == undefined) { if (team == undefined) {
return false; return false;
} }
if(team.currentLocation == null) { if (team.currentLocation == null) {
return false; return false;
} }
@@ -329,6 +331,13 @@ export default {
if (this.state == GameState.PLAYING || this.state == GameState.FINISHED) { if (this.state == GameState.PLAYING || this.state == GameState.FINISHED) {
return false; 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) return zoneManager.udpateSettings(newSettings)
}, },

View File

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

View File

@@ -287,9 +287,9 @@
} }
}, },
"node_modules/engine.io": { "node_modules/engine.io": {
"version": "6.5.4", "version": "6.5.5",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.4.tgz", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz",
"integrity": "sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==", "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==",
"dependencies": { "dependencies": {
"@types/cookie": "^0.4.1", "@types/cookie": "^0.4.1",
"@types/cors": "^2.8.12", "@types/cors": "^2.8.12",
@@ -300,7 +300,7 @@
"cors": "~2.8.5", "cors": "~2.8.5",
"debug": "~4.3.1", "debug": "~4.3.1",
"engine.io-parser": "~5.2.1", "engine.io-parser": "~5.2.1",
"ws": "~8.11.0" "ws": "~8.17.1"
}, },
"engines": { "engines": {
"node": ">=10.2.0" "node": ">=10.2.0"
@@ -931,12 +931,12 @@
} }
}, },
"node_modules/socket.io-adapter": { "node_modules/socket.io-adapter": {
"version": "2.5.4", "version": "2.5.5",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.4.tgz", "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz",
"integrity": "sha512-wDNHGXGewWAjQPt3pyeYBtpWSq9cLE5UW1ZUPL/2eGK9jtse/FpXib7epSTsz0Q0m+6sg6Y4KtcFTlah1bdOVg==", "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==",
"dependencies": { "dependencies": {
"debug": "~4.3.4", "debug": "~4.3.4",
"ws": "~8.11.0" "ws": "~8.17.1"
} }
}, },
"node_modules/socket.io-parser": { "node_modules/socket.io-parser": {
@@ -1040,15 +1040,15 @@
} }
}, },
"node_modules/ws": { "node_modules/ws": {
"version": "8.11.0", "version": "8.17.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
"engines": { "engines": {
"node": ">=10.0.0" "node": ">=10.0.0"
}, },
"peerDependencies": { "peerDependencies": {
"bufferutil": "^4.0.1", "bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2" "utf-8-validate": ">=5.0.2"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"bufferutil": { "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 { isInCircle } from "./map_utils.js";
import { sendUpdatedTeamInformations, teamBroadcast } from "./team_socket.js"; import { sendUpdatedTeamInformations, teamBroadcast } from "./team_socket.js";
import { secureAdminBroadcast } from "./admin_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"; import zone from "./zone_manager.js";
export default { 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 This module also exposes functions to send messages via socket to all teams
*/ */
import { secureAdminBroadcast } from "./admin_socket.js"; import { secureAdminBroadcast } from "./admin_socket.js";
import { io} from "./index.js"; import { io } from "./index.js";
import game from "./game.js"; import game from "./game.js";
import zone from "./zone_manager.js"; import zone from "./zone_manager.js";
@@ -43,7 +43,7 @@ function logoutPlayer(id) {
export function sendUpdatedTeamInformations(teamId) { export function sendUpdatedTeamInformations(teamId) {
let team = game.getTeam(teamId) let team = game.getTeam(teamId)
if(!team) { if (!team) {
return false; return false;
} }
team.sockets.forEach(socketId => { team.sockets.forEach(socketId => {
@@ -106,7 +106,7 @@ export function initTeamSocket() {
return; return;
} }
let team = game.getTeam(teamId) let team = game.getTeam(teamId)
if(team == undefined) { if (team == undefined) {
logoutPlayer(socket.id); logoutPlayer(socket.id);
return; return;
} }
@@ -125,8 +125,8 @@ export function initTeamSocket() {
return; return;
} }
game.updateTeamChasing(); game.updateTeamChasing();
teamBroadcast(teamId, "update_team", { enemyLocation: team.enemyLocation,locationSendDeadline: team.locationSendDeadline }); teamBroadcast(teamId, "update_team", { enemyLocation: team.enemyLocation, locationSendDeadline: team.locationSendDeadline });
teamBroadcast(teamId,"success", "Position udpated") teamBroadcast(teamId, "success", "Position udpated")
secureAdminBroadcast("teams", game.teams) secureAdminBroadcast("teams", game.teams)
}); });

View File

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

View File

@@ -1,4 +1,4 @@
FROM node:18-alpine AS base FROM node:20-alpine AS base
# Install dependencies only when needed # Install dependencies only when needed
FROM base AS deps FROM base AS deps
@@ -7,13 +7,8 @@ RUN apk add --no-cache libc6-compat
WORKDIR /app WORKDIR /app
# Install dependencies based on the preferred package manager # Install dependencies based on the preferred package manager
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./ COPY package.json package-lock.json* ./
RUN \ RUN npm ci
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 # Rebuild the source code only when needed
@@ -25,14 +20,9 @@ COPY . .
# Next.js collects completely anonymous telemetry data about general usage. # Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry # Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the build. # 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 \ RUN npm run build
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 # Production image, copy all the files and run next
FROM base AS runner FROM base AS runner
@@ -40,7 +30,7 @@ WORKDIR /app
ENV NODE_ENV production ENV NODE_ENV production
# Uncomment the following line in case you want to disable telemetry during runtime. # 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 addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs RUN adduser --system --uid 1001 nextjs

View File

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

View File

@@ -80,17 +80,26 @@ export function ZonePicker({ minZone, setMinZone, maxZone, setMaxZone, editMode,
minHover(e); minHover(e);
} }
} }
return ( return (
<MapContainer {...props} className='min-h-full w-full ' center={[0, 0]} zoom={0} scrollWheelZoom={true}> <div>
<TileLayer <div className='h-96'>
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' <MapContainer {...props} className='min-h-full w-full ' center={[0, 0]} zoom={0} scrollWheelZoom={true}>
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" <TileLayer
/> attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
{minCenter && minRadius && <Circle center={minCenter} radius={minRadius} color="blue" fillColor="blue" />} url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
{maxCenter && maxRadius && <Circle center={maxCenter} radius={maxRadius} color="red" fillColor="red" />} />
<MapPan center={location} zoom={DEFAULT_ZOOM} /> {minCenter && minRadius && <Circle center={minCenter} radius={minRadius} color="blue" fillColor="blue" />}
<MapEventListener onClick={handleClick} onMouseMove={handleMouseMove} /> {maxCenter && maxRadius && <Circle center={maxCenter} radius={maxRadius} color="red" fillColor="red" />}
</MapContainer> <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 teamImage = useRef(null);
const [newTeamName, setNewTeamName] = React.useState(''); const [newTeamName, setNewTeamName] = React.useState('');
const { updateTeam, getTeamName, removeTeam, getTeam, teams } = useAdmin(); const { updateTeam, getTeamName, removeTeam, getTeam, teams } = useAdmin();
const [team, setTeam] = useState({}) const [team, setTeam] = useState({});
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);
useEffect(() => { useEffect(() => {
let team = getTeam(selectedTeamId); let team = getTeam(selectedTeamId);

View File

@@ -41,9 +41,7 @@ export function ZoneSelector() {
<h2 className="text-2xl">Edit zones</h2> <h2 className="text-2xl">Edit zones</h2>
{editMode == EditMode.MIN && <RedButton onClick={() => setEditMode(EditMode.MAX)}>Edit end zone</RedButton>} {editMode == EditMode.MIN && <RedButton onClick={() => setEditMode(EditMode.MAX)}>Edit end zone</RedButton>}
{editMode == EditMode.MAX && <BlueButton onClick={() => setEditMode(EditMode.MIN)}>Edit start zone</BlueButton>} {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} />
<ZonePicker minZone={minZone} maxZone={maxZone} editMode={editMode} setMinZone={setMinZone} setMaxZone={setMaxZone} />
</div>
<div> <div>
<p>Number of reductions</p> <p>Number of reductions</p>
<TextInput value={reductionCount} onChange={(e) => setReductionCount(e.target.value)}></TextInput> <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(); 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 && return (visible &&
<> <>
<div className='fixed w-screen h-screen bg-black bg-opacity-50 z-10 text-center'></div> <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 { name, teamId } = useGame();
const { gameSettings } = useTeamContext(); const { gameSettings } = useTeamContext();
const imageRef = useRef(null); 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() { function sendImage() {
let data = new FormData(); let data = new FormData();

View File

@@ -3,12 +3,21 @@ import { createContext, useContext, useMemo } from "react";
const { io } = require("socket.io-client"); 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 USER_SOCKET_URL = SOCKET_URL + "/player";
const ADMIN_SOCKET_URL = SOCKET_URL + "/admin"; const ADMIN_SOCKET_URL = SOCKET_URL + "/admin";
export const teamSocket = io(USER_SOCKET_URL); export const teamSocket = io(USER_SOCKET_URL, {
export const adminSocket = io(ADMIN_SOCKET_URL); path: "/back/socket.io",
});
export const adminSocket = io(ADMIN_SOCKET_URL, {
path: "/back/socket.io",
});
export const SocketContext = createContext(); export const SocketContext = createContext();
export default function SocketProvider({ children }) { export default function SocketProvider({ children }) {

View File

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

View File

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

View File

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