Server heavy refactoring 3 (not functionnal)

This commit is contained in:
Sebastien Riviere
2026-03-04 18:36:06 +01:00
parent 8046feadb0
commit e1b6c0e0c5
50 changed files with 6576 additions and 687 deletions

View File

@@ -1,15 +1,11 @@
services: services:
proxy: proxy:
build: ./proxy build: ./proxy
restart: always
ports:
- "80:80"
front: front:
build: build:
context: ./traque-front context: ./traque-front
dockerfile: Dockerfile.dev target: dev
restart: always
volumes: volumes:
- ./traque-front:/app - ./traque-front:/app
- /app/node_modules - /app/node_modules
@@ -19,14 +15,7 @@ services:
back: back:
build: build:
context: ./traque-back context: ./traque-back
dockerfile: Dockerfile.dev target: dev
restart: always
ports:
- "3001:3001"
volumes: volumes:
- ./traque-back:/app - ./traque-back:/app
- /app/node_modules - /app/node_modules
environment:
ADMIN_PASSWORD_HASH: '23e3c6886ff8fcba302deac05c46612ed3af99e40a2a14252810f540f3c186aa'
HOST: '0.0.0.0'
PORT: 3001

View File

@@ -0,0 +1,11 @@
services:
proxy:
image: git.rezel.net/ludotech/traque-proxy:latest
front:
image: git.rezel.net/ludotech/traque-front:latest
environment:
NEXT_PUBLIC_SOCKET_HOST: 'traque.rezel.net'
back:
image: git.rezel.net/ludotech/traque-back:latest

View File

@@ -1,22 +1,25 @@
services: services:
proxy: proxy:
image: git.rezel.net/ludotech/traque-proxy:latest restart: always
ports: ports:
- "80:80" - "80:80"
restart: always networks:
- traque-net
front: front:
image: git.rezel.net/ludotech/traque-front:latest
restart: always restart: always
environment: networks:
NEXT_PUBLIC_SOCKET_HOST: 'traque.rezel.net' - traque-net
back: back:
image: git.rezel.net/ludotech/traque-back:latest
restart: always restart: always
ports:
- "3001:3001"
environment: environment:
ADMIN_PASSWORD_HASH: '23e3c6886ff8fcba302deac05c46612ed3af99e40a2a14252810f540f3c186aa' ADMIN_PASSWORD_HASH: '23e3c6886ff8fcba302deac05c46612ed3af99e40a2a14252810f540f3c186aa'
HOST: '0.0.0.0' HOST: '0.0.0.0'
PORT: 3001 PORT: 3001
networks:
- traque-net
networks:
traque-net:
driver: bridge

6
server/package-lock.json generated Normal file
View File

@@ -0,0 +1,6 @@
{
"name": "server",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}

1
server/package.json Normal file
View File

@@ -0,0 +1 @@
{}

View File

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

View File

@@ -1,4 +1,3 @@
# nginx/nginx.conf
events { events {
worker_connections 1024; worker_connections 1024;
} }
@@ -14,15 +13,14 @@ http {
proxy_set_header X-Real-IP $remote_addr; 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;
} }
location /back/ { location /back/ {
proxy_pass http://back:3001/; proxy_pass http://back:3001/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr; 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_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade"; proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
} }
} }
} }

View File

@@ -1,8 +1,7 @@
Dockerfile Dockerfile
.dockerignore .dockerignore
node_modules node_modules
npm-debug.log
README.md
.next .next
.git .git
.vscode .vscode
.env

View File

@@ -1,24 +1,29 @@
# Use Node 22 alpine as parent image # Étape commune
FROM node:22-alpine FROM node:22-slim AS base
# Change the working directory on the Docker image to /app
WORKDIR /app WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
# Copy package.json and package-lock.json to the /app directory # Étape développement
COPY package.json package-lock.json* ./ FROM base AS dev
ENV NODE_ENV=development
# Install dependencies
RUN npm install
# Copy the rest of project files into this image
COPY . . COPY . .
# Create those folders if they don't already exist
RUN if [ ! -d uploads ]; then mkdir uploads; fi
RUN if [ ! -d trajectories ]; then mkdir trajectories; fi
# Expose the port
EXPOSE 3001 EXPOSE 3001
CMD ["npm", "run", "dev"]
# Start the application # Étape builder
CMD ["npm", "run", "start"] FROM base AS builder
ENV NODE_ENV=production
COPY . .
RUN npm run build && npm prune --omit=dev
# Étape production
FROM node:22-slim AS prod
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder --chown=node:node /app/node_modules ./node_modules
COPY --from=builder --chown=node:node /app/dist ./dist
COPY --from=builder --chown=node:node /app/package.json ./package.json
USER node
EXPOSE 3001
CMD ["node", "dist/index.js"]

View File

@@ -1,27 +0,0 @@
# Use Node 22 alpine as parent image
FROM node:22-alpine
# Change the working directory on the Docker image to /app
WORKDIR /app
# Change specified variables
ENV NODE_ENV=development
# Copy package.json and package-lock.json to the /app directory
COPY package.json package-lock.json* ./
# Install dependencies
RUN npm install
# Copy the rest of project files into this image
COPY . .
# Create those folders if they don't already exist
RUN if [ ! -d uploads ]; then mkdir uploads; fi
RUN if [ ! -d trajectories ]; then mkdir trajectories; fi
# Expose the port
EXPOSE 3001
# Start the server in dev mode
CMD ["npm", "run", "dev"]

View File

@@ -6,7 +6,7 @@
"checkJs": true, "checkJs": true,
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"@/*": ["src/*"] "#*": ["src/*"]
} }
}, },
"exclude": ["node_modules", "dist"] "exclude": ["node_modules", "dist"]

View File

@@ -15,9 +15,6 @@
"multer": "^1.4.5-lts.1", "multer": "^1.4.5-lts.1",
"random-location": "^1.1.3", "random-location": "^1.1.3",
"socket.io": "^4.7.5" "socket.io": "^4.7.5"
},
"devDependencies": {
"nodemon": "^3.1.10"
} }
}, },
"node_modules/@socket.io/component-emitter": { "node_modules/@socket.io/component-emitter": {
@@ -2131,20 +2128,6 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dev": true,
"license": "ISC",
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/append-field": { "node_modules/append-field": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
@@ -2165,16 +2148,6 @@
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/balanced-match": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
"dev": true,
"license": "MIT",
"engines": {
"node": "18 || 20 || >=22"
}
},
"node_modules/base64id": { "node_modules/base64id": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
@@ -2193,19 +2166,6 @@
"node": "*" "node": "*"
} }
}, },
"node_modules/binary-extensions": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/body-parser": { "node_modules/body-parser": {
"version": "1.20.4", "version": "1.20.4",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz",
@@ -2230,32 +2190,6 @@
"npm": "1.2.8000 || >= 1.4.16" "npm": "1.2.8000 || >= 1.4.16"
} }
}, },
"node_modules/brace-expansion": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
"integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^4.0.2"
},
"engines": {
"node": "18 || 20 || >=22"
}
},
"node_modules/braces": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
"license": "MIT",
"dependencies": {
"fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/buffer-from": { "node_modules/buffer-from": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@@ -2311,31 +2245,6 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/chokidar": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
"dev": true,
"license": "MIT",
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
},
"engines": {
"node": ">= 8.10.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/commander": { "node_modules/commander": {
"version": "2.20.3", "version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
@@ -2673,19 +2582,6 @@
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"license": "MIT",
"dependencies": {
"to-regex-range": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/finalhandler": { "node_modules/finalhandler": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
@@ -2722,21 +2618,6 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": { "node_modules/function-bind": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@@ -2825,19 +2706,6 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
"license": "ISC",
"dependencies": {
"is-glob": "^4.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/gopd": { "node_modules/gopd": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
@@ -2850,16 +2718,6 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/has-symbols": { "node_modules/has-symbols": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
@@ -2916,13 +2774,6 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/ignore-by-default": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
"integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
"dev": true,
"license": "ISC"
},
"node_modules/inherits": { "node_modules/inherits": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
@@ -2938,52 +2789,6 @@
"node": ">= 0.10" "node": ">= 0.10"
} }
}, },
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dev": true,
"license": "MIT",
"dependencies": {
"binary-extensions": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-extglob": "^2.1.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/isarray": { "node_modules/isarray": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
@@ -3074,22 +2879,6 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/minimatch": {
"version": "10.2.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
"integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"brace-expansion": "^5.0.2"
},
"engines": {
"node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/minimist": { "node_modules/minimist": {
"version": "1.2.8", "version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
@@ -3145,70 +2934,6 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/nodemon": {
"version": "3.1.14",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.14.tgz",
"integrity": "sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==",
"dev": true,
"license": "MIT",
"dependencies": {
"chokidar": "^3.5.2",
"debug": "^4",
"ignore-by-default": "^1.0.1",
"minimatch": "^10.2.1",
"pstree.remy": "^1.1.8",
"semver": "^7.5.3",
"simple-update-notifier": "^2.0.0",
"supports-color": "^5.5.0",
"touch": "^3.1.0",
"undefsafe": "^2.0.5"
},
"bin": {
"nodemon": "bin/nodemon.js"
},
"engines": {
"node": ">=10"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/nodemon"
}
},
"node_modules/nodemon/node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/nodemon/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true,
"license": "MIT"
},
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/object-assign": { "node_modules/object-assign": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -3257,19 +2982,6 @@
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/point-in-polygon": { "node_modules/point-in-polygon": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/point-in-polygon/-/point-in-polygon-1.1.0.tgz", "resolved": "https://registry.npmjs.org/point-in-polygon/-/point-in-polygon-1.1.0.tgz",
@@ -3320,13 +3032,6 @@
"node": ">= 0.10" "node": ">= 0.10"
} }
}, },
"node_modules/pstree.remy": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
"integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
"dev": true,
"license": "MIT"
},
"node_modules/qs": { "node_modules/qs": {
"version": "6.14.2", "version": "6.14.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
@@ -3408,19 +3113,6 @@
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dev": true,
"license": "MIT",
"dependencies": {
"picomatch": "^2.2.1"
},
"engines": {
"node": ">=8.10.0"
}
},
"node_modules/robust-predicates": { "node_modules/robust-predicates": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-2.0.4.tgz", "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-2.0.4.tgz",
@@ -3453,19 +3145,6 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/semver": {
"version": "7.7.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"dev": true,
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/send": { "node_modules/send": {
"version": "0.19.2", "version": "0.19.2",
"resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz",
@@ -3589,19 +3268,6 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/simple-update-notifier": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
"integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
"dev": true,
"license": "MIT",
"dependencies": {
"semver": "^7.5.3"
},
"engines": {
"node": ">=10"
}
},
"node_modules/skmeans": { "node_modules/skmeans": {
"version": "0.9.7", "version": "0.9.7",
"resolved": "https://registry.npmjs.org/skmeans/-/skmeans-0.9.7.tgz", "resolved": "https://registry.npmjs.org/skmeans/-/skmeans-0.9.7.tgz",
@@ -3756,19 +3422,6 @@
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"license": "MIT",
"dependencies": {
"has-flag": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/sweepline-intersections": { "node_modules/sweepline-intersections": {
"version": "1.5.0", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/sweepline-intersections/-/sweepline-intersections-1.5.0.tgz", "resolved": "https://registry.npmjs.org/sweepline-intersections/-/sweepline-intersections-1.5.0.tgz",
@@ -3784,19 +3437,6 @@
"integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==", "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-number": "^7.0.0"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/toidentifier": { "node_modules/toidentifier": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
@@ -3832,16 +3472,6 @@
"geo2topo": "bin/geo2topo" "geo2topo": "bin/geo2topo"
} }
}, },
"node_modules/touch": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz",
"integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==",
"dev": true,
"license": "ISC",
"bin": {
"nodetouch": "bin/nodetouch.js"
}
},
"node_modules/tslib": { "node_modules/tslib": {
"version": "2.8.1", "version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
@@ -3867,13 +3497,6 @@
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/undefsafe": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
"dev": true,
"license": "MIT"
},
"node_modules/undici-types": { "node_modules/undici-types": {
"version": "7.18.2", "version": "7.18.2",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",

View File

@@ -1,14 +1,15 @@
{ {
"name": "traque-back", "name": "traque-back",
"version": "1.0.0", "version": "1.0.0",
"description": "", "private": true,
"main": "src/index.js", "main": "src/index.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node src/index.js", "start": "node src/index.js",
"dev": "nodemon src/index.js" "dev": "node --watch src/index.js"
},
"imports": {
"#*": "./src/*"
}, },
"author": "Quentin Roussel",
"license": "ISC", "license": "ISC",
"type": "module", "type": "module",
"dependencies": { "dependencies": {
@@ -18,8 +19,5 @@
"multer": "^1.4.5-lts.1", "multer": "^1.4.5-lts.1",
"random-location": "^1.1.3", "random-location": "^1.1.3",
"socket.io": "^4.7.5" "socket.io": "^4.7.5"
},
"devDependencies": {
"nodemon": "^3.1.10"
} }
} }

View File

@@ -1,5 +1,5 @@
import { DEFAULT_ZONES_SETTINGS } from "@/config/zone.js"; import { DEFAULT_ZONES_SETTINGS } from "#config/zone.js";
import { DefaultState, PlacementState, PlayingState, FinishedState } from '@/core/states/game/index.js'; import { DefaultState, PlacementState, PlayingState, FinishedState } from '#core/states/game/index.js';
export const STATE_SETTINGS = { export const STATE_SETTINGS = {
ENTRY_STATE_CLASS: DefaultState, ENTRY_STATE_CLASS: DefaultState,

View File

@@ -1,6 +1,6 @@
import * as turf from '@turf/turf'; import * as turf from '@turf/turf';
import { ZoneWithDuration } from '@/core/models/zone.js'; import { ZoneWithDuration } from '#core/models/zone.js';
import { TURF_BUFFER_SIZE, TURF_CIRCLE_STEPS, TURF_DISTANCE_UNIT, ZONE_TYPES } from '@/config/zone.js'; import { TURF_BUFFER_SIZE, TURF_CIRCLE_STEPS, TURF_DISTANCE_UNIT, ZONE_TYPES } from '#config/zone.js';
export const settingsToZoneList = (settings) => { export const settingsToZoneList = (settings) => {
if (!settings) return []; if (!settings) return [];

View File

@@ -1,6 +1,6 @@
import * as turf from '@turf/turf'; import * as turf from '@turf/turf';
import { Zone } from '@/core/models/zone.js'; import { Zone } from '#core/models/zone.js';
import { TURF_CIRCLE_STEPS, TURF_DISTANCE_UNIT } from '@/config/zone.js'; import { TURF_CIRCLE_STEPS, TURF_DISTANCE_UNIT } from '#config/zone.js';
export const settingsToZone = (settings) => { export const settingsToZone = (settings) => {
if (!settings) return null; if (!settings) return null;

View File

@@ -1,7 +1,7 @@
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
import { ZoneManager } from "@/core/managers/zone_manager.js"; import { ZoneManager } from "#core/managers/zone_manager.js";
import { TeamManager } from '@/core/managers/team_manager.js'; import { TeamManager } from '#core/managers/team_manager.js';
import { GAME_MANAGER_EVENTS } from "@/config/events.js"; import { GAME_MANAGER_EVENTS } from "#config/events.js";
export class GameManager extends EventEmitter { export class GameManager extends EventEmitter {

View File

@@ -1,4 +1,4 @@
import { Team } from "@/core/models/team.js"; import { Team } from "#core/models/team.js";
export class TeamManager { export class TeamManager {
constructor() { constructor() {
@@ -36,7 +36,7 @@ export class TeamManager {
const team = new Team(id, teamName); const team = new Team(id, teamName);
if (!this.has(id)) this.order.push(id); if (!this.has(id)) this.order.push(id);
this._map.set(id, team); this._map.set(id, team);
return id; return team;
} }
delete(id) { delete(id) {

View File

@@ -1,5 +1,5 @@
import { Scheduler } from "@/util/scheduler.js"; import { Scheduler } from "#util/scheduler.js";
import { settingsToZoneList } from "@/core/factories/game_zone_factory.js"; import { settingsToZoneList } from "#core/factories/game_zone_factory.js";
export class ZoneManager { export class ZoneManager {
constructor() { constructor() {

View File

@@ -1,5 +1,5 @@
import { CAPTURE_CODE_LENGTH, TEAM_ID_LENGTH } from "@/config/game.js"; import { CAPTURE_CODE_LENGTH, TEAM_ID_LENGTH } from "#config/game.js";
import { randint } from "@/util/random.js"; import { randint } from "#util/random.js";
export class Team { export class Team {
constructor(id, teamName) { constructor(id, teamName) {

View File

@@ -1,15 +1,17 @@
import { DefaultTeam } from "@/core/states/teams/default_team.js"; import { DefaultTeam } from "#core/states/teams/default_team.js";
export class DefaultState { export class DefaultState {
static name = "default";
get name () {
return this.constructor.name;
}
constructor(teams, zoneManager) { constructor(teams, zoneManager) {
this.teams = teams; this.teams = teams;
this.zoneManager = zoneManager; this.zoneManager = zoneManager;
} }
static get name () {
return "default";
}
// --------------- LIFE CYCLE --------------- // // --------------- LIFE CYCLE --------------- //

View File

@@ -1,15 +1,17 @@
import { FinishedTeam } from "@/core/states/teams/finished_team.js"; import { FinishedTeam } from "#core/states/teams/finished_team.js";
export class FinishedState { export class FinishedState {
static name = "finished";
get name () {
return this.constructor.name;
}
constructor(teams, zoneManager) { constructor(teams, zoneManager) {
this.teams = teams; this.teams = teams;
this.zoneManager = zoneManager; this.zoneManager = zoneManager;
} }
static get name () {
return "finished";
}
// --------------- LIFE CYCLE --------------- // // --------------- LIFE CYCLE --------------- //

View File

@@ -1,4 +1,4 @@
export { DefaultState } from '@/core/states/game/default_state.js'; export { DefaultState } from '#core/states/game/default_state.js';
export { PlacementState } from '@/core/states/game/placement_state.js'; export { PlacementState } from '#core/states/game/placement_state.js';
export { PlayingState } from '@/core/states/game/playing_state.js'; export { PlayingState } from '#core/states/game/playing_state.js';
export { FinishedState } from '@/core/states/game/finished_state.js'; export { FinishedState } from '#core/states/game/finished_state.js';

View File

@@ -1,15 +1,17 @@
import { PlacementTeam } from "@/core/states/teams/placement_team.js"; import { PlacementTeam } from "#core/states/teams/placement_team.js";
export class PlacementState { export class PlacementState {
static name = "placement";
get name () {
return this.constructor.name;
}
constructor(teams, zoneManager) { constructor(teams, zoneManager) {
this.teams = teams; this.teams = teams;
this.zoneManager = zoneManager; this.zoneManager = zoneManager;
} }
static get name () {
return "placement";
}
// --------------- LIFE CYCLE --------------- // // --------------- LIFE CYCLE --------------- //

View File

@@ -1,15 +1,17 @@
import { PlayingTeam } from "@/core/states/teams/playing_team.js"; import { PlayingTeam } from "#core/states/teams/playing_team.js";
export class PlayingState { export class PlayingState {
static name = "playing";
get name () {
return this.constructor.name;
}
constructor(teams, zoneManager) { constructor(teams, zoneManager) {
this.teams = teams; this.teams = teams;
this.zoneManager = zoneManager; this.zoneManager = zoneManager;
} }
static get name () {
return "playing";
}
// --------------- LIFE CYCLE --------------- // // --------------- LIFE CYCLE --------------- //

View File

@@ -1,4 +1,4 @@
import { settingsToZone } from "@/core/factories/placement_zone_factory.js"; import { settingsToZone } from "#core/factories/placement_zone_factory.js";
export class PlacementTeam { export class PlacementTeam {
constructor(team) { constructor(team) {

View File

@@ -1,6 +1,6 @@
import { Team } from "@/core/models/team.js"; import { Team } from "#core/models/team.js";
import { ScheduledTask } from "@/util/scheduler.js"; import { ScheduledTask } from "#util/scheduler.js";
import { RESTART_TIMERS } from "@/config/game.js"; import { RESTART_TIMERS } from "#config/game.js";
export class PlayingTeam { export class PlayingTeam {
constructor(team, zoneManager) { constructor(team, zoneManager) {

View File

@@ -1,6 +1,6 @@
import { createHash } from "crypto"; import { createHash } from "crypto";
import { ADMIN_PASSWORD_HASH } from "@/config/server.js"; import { ADMIN_PASSWORD_HASH } from "#config/server.js";
import { ADMIN_HANDLER_EVENTS } from "@/config/events.js"; import { ADMIN_HANDLER_EVENTS } from "#config/events.js";
export class AdminHandler { export class AdminHandler {
constructor(gameManager) { constructor(gameManager) {
@@ -29,7 +29,7 @@ class AdminConnection {
if (this._isLoggedIn) return; if (this._isLoggedIn) return;
const hash = createHash('sha256').update(password).digest('hex'); const hash = createHash('sha256').update(password).digest('hex');
if (hash !== ADMIN_PASSWORD_HASH) return false; if (false && hash !== ADMIN_PASSWORD_HASH) return false; // TODO : temporaire
this._isLoggedIn = true; this._isLoggedIn = true;
this._gameManager.onAdminLogin(this._socket.id); this._gameManager.onAdminLogin(this._socket.id);
@@ -49,9 +49,8 @@ class AdminConnection {
this._logout() this._logout()
}); });
this._socket.on(ADMIN_HANDLER_EVENTS.LOGIN, (password, callback) => { this._socket.on(ADMIN_HANDLER_EVENTS.LOGIN, (password) => {
this._login(password); this._login(password);
callback(this._isLoggedIn);
}); });
this._socket.on(ADMIN_HANDLER_EVENTS.LOGOUT, () => { this._socket.on(ADMIN_HANDLER_EVENTS.LOGOUT, () => {

View File

@@ -1,4 +1,4 @@
import { PLAYER_HANDLER_EVENTS } from "@/config/events.js"; import { PLAYER_HANDLER_EVENTS } from "#config/events.js";
export class PlayerHandler { export class PlayerHandler {
constructor(gameManager) { constructor(gameManager) {

View File

@@ -1,4 +1,4 @@
import { DefaultState, PlacementState, PlayingState, FinishedState } from "@/core/states/game/index.js"; import { DefaultState, PlacementState, PlayingState, FinishedState } from "#core/states/game/index.js";
const TEAM_STATE_MAP = { const TEAM_STATE_MAP = {
[DefaultState.name]: (_team, _gameState) => ({}), [DefaultState.name]: (_team, _gameState) => ({}),
@@ -55,9 +55,8 @@ export class AdminMapper {
} }
return { return {
stateName: stateName, gameState: stateName,
teams: teamsDto, teams: this.gameManager.teams.order.map(teamId => teamsDto[teamId]),
teamsOrder: this.gameManager.teams.order,
zones: zonesDto, zones: zonesDto,
settings: this.gameManager.settings settings: this.gameManager.settings
} }

View File

@@ -1,4 +1,4 @@
import { DefaultState, PlacementState, PlayingState, FinishedState } from "@/core/states/game/index.js"; import { DefaultState, PlacementState, PlayingState, FinishedState } from "#core/states/game/index.js";
const TEAM_STATE_MAP = { const TEAM_STATE_MAP = {
[DefaultState.name]: (_team, _gameState) => ({}), [DefaultState.name]: (_team, _gameState) => ({}),

View File

@@ -1,6 +1,6 @@
import { AdminMapper } from "@/externals/mappers/admin_mapper.js"; import { AdminMapper } from "#externals/mappers/admin_mapper.js";
import { StateTracker } from "@/util/state_tracker.js"; import { StateTracker } from "#util/state_tracker.js";
import { GAME_MANAGER_EVENTS, ADMIN_SYNCHRONIZER_EVENTS } from "@/config/events.js"; import { GAME_MANAGER_EVENTS, ADMIN_SYNCHRONIZER_EVENTS } from "#config/events.js";
export class AdminSynchronizer { export class AdminSynchronizer {
constructor(gameManager) { constructor(gameManager) {
@@ -11,11 +11,16 @@ export class AdminSynchronizer {
init(io) { init(io) {
this.gameManager.on(GAME_MANAGER_EVENTS.INIT_ADMIN, (socketId) => { this.gameManager.on(GAME_MANAGER_EVENTS.INIT_ADMIN, (socketId) => {
const { dto } = this.gameStateTracker.getSyncDto(); const { dto } = this.gameStateTracker.getSyncDto();
console.log("INIT ADMIN");
console.log(dto);
io.to(socketId).emit(ADMIN_SYNCHRONIZER_EVENTS.UPDATE_FULL, dto); io.to(socketId).emit(ADMIN_SYNCHRONIZER_EVENTS.UPDATE_FULL, dto);
}); });
this.gameManager.on(GAME_MANAGER_EVENTS.UPDATE_GAME, () => { this.gameManager.on(GAME_MANAGER_EVENTS.UPDATE_GAME, () => {
const { dto, hasChanged } = this.gameStateTracker.getSyncDto(); const { dto, hasChanged } = this.gameStateTracker.getSyncDto();
console.log("UPDATE ADMIN");
console.log(hasChanged);
console.log(dto);
if (hasChanged) io.emit(ADMIN_SYNCHRONIZER_EVENTS.UPDATE_FULL, dto); if (hasChanged) io.emit(ADMIN_SYNCHRONIZER_EVENTS.UPDATE_FULL, dto);
}); });

View File

@@ -1,6 +1,6 @@
import { GAME_MANAGER_EVENTS, PLAYER_SYNCHRONIZER_EVENTS } from "@/config/events.js"; import { GAME_MANAGER_EVENTS, PLAYER_SYNCHRONIZER_EVENTS } from "#config/events.js";
import { PlayerMapper } from "@/externals/mappers/player_mapper.js"; import { PlayerMapper } from "#externals/mappers/player_mapper.js";
import { StateTracker } from "@/util/state_tracker.js"; import { StateTracker } from "#util/state_tracker.js";
export class PlayerSynchronizer { export class PlayerSynchronizer {
constructor(gameManager) { constructor(gameManager) {

View File

@@ -2,16 +2,16 @@ import { createServer } from "http";
import express from "express"; import express from "express";
import { Server } from "socket.io"; import { Server } from "socket.io";
// Core // Core
import { GameManager } from "@/core/managers/game_manager.js"; import { GameManager } from "#core/managers/game_manager.js";
// Externals // Externals
import { PhotoService } from "@/externals/api/photo.js"; import { PhotoService } from "#externals/api/photo.js";
import { PlayerSynchronizer } from "@/externals/synchronizers/player_synchronizer.js"; import { PlayerSynchronizer } from "#externals/synchronizers/player_synchronizer.js";
import { PlayerHandler } from "@/externals/handlers/playerHandler.js"; import { PlayerHandler } from "#externals/handlers/playerHandler.js";
import { AdminSynchronizer } from "@/externals/synchronizers/admin_synchronizer.js"; import { AdminSynchronizer } from "#externals/synchronizers/admin_synchronizer.js";
import { AdminHandler } from "@/externals/handlers/adminHandler.js"; import { AdminHandler } from "#externals/handlers/adminHandler.js";
// Config // Config
import { PORT, HOST } from "@/config/server.js"; import { PORT, HOST } from "#config/server.js";
import { DEFAULT_GAME_SETTINGS, STATE_SETTINGS } from "@/config/game.js"; import { DEFAULT_GAME_SETTINGS, STATE_SETTINGS } from "#config/game.js";
// Configuration // Configuration
@@ -21,7 +21,6 @@ const io = new Server(httpServer, {
cors: { origin: "*", methods: ["GET", "POST"] } cors: { origin: "*", methods: ["GET", "POST"] }
}); });
// Initialization // Initialization
const gameManager = new GameManager(STATE_SETTINGS, DEFAULT_GAME_SETTINGS); const gameManager = new GameManager(STATE_SETTINGS, DEFAULT_GAME_SETTINGS);

View File

@@ -1,8 +1,7 @@
Dockerfile Dockerfile
.dockerignore .dockerignore
node_modules node_modules
npm-debug.log
README.md
.next .next
.git .git
.vscode .vscode
.env

View File

@@ -1,18 +1,31 @@
# Use Node 22 alpine as parent image # Étape commune
FROM node:22-alpine AS base FROM node:22-slim AS base
WORKDIR /app WORKDIR /app
ENV NEXT_TELEMETRY_DISABLED=1
COPY package.json package-lock.json ./
RUN npm ci
RUN apk add --no-cache libc6-compat # Étape développement
FROM base AS dev
COPY package.json package-lock.json* ./ ENV NODE_ENV=development
RUN npm install
COPY . . COPY . .
ENV NODE_ENV development
ENV NEXT_TELEMETRY_DISABLED 1
EXPOSE 3000 EXPOSE 3000
CMD ["npm", "run", "dev"] CMD ["npm", "run", "dev"]
# Étape builder
FROM base AS builder
ENV NODE_ENV=production
COPY . .
RUN npm run build
# Étape production
FROM node:22-slim AS prod
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
COPY --from=builder --chown=node:node /app/.next/standalone ./
COPY --from=builder --chown=node:node /app/.next/static ./.next/static
COPY --from=builder --chown=node:node /app/public ./public
USER node
EXPOSE 3000
CMD ["node", "server.js"]

View File

@@ -1,27 +0,0 @@
# Use Node 22 alpine as parent image
FROM node:22-alpine
# Change the working directory on the Docker image to /app
WORKDIR /app
# Installs glibc compatibility on Alpine to support native Node.js modules that require glibc
RUN apk add --no-cache libc6-compat
# Change specified variables
ENV NODE_ENV=development
ENV NEXT_TELEMETRY_DISABLED=1
# Copy package.json and package-lock.json to the /app directory
COPY package.json package-lock.json* ./
# Install dependencies
RUN npm install
# Copy the rest of project files into this image
COPY . .
# Expose the port
EXPOSE 3000
# Start the server in dev mode
CMD ["npm", "run", "dev"]

View File

@@ -2,18 +2,18 @@ import { Fragment, useEffect, useState } from "react";
import { Arrow, CircleZone, PolygonZone, Position, Tag } from "@/components/layer"; import { Arrow, CircleZone, PolygonZone, Position, Tag } from "@/components/layer";
import { CustomMapContainer, MapEventListener, MapPan } from "@/components/map"; import { CustomMapContainer, MapEventListener, MapPan } from "@/components/map";
import useAdmin from "@/hook/useAdmin"; import useAdmin from "@/hook/useAdmin";
import { GameState, ZoneTypes } from "@/util/types"; import { GameState } from "@/util/types";
import { mapZooms } from "@/util/configurations"; import { mapZooms } from "@/util/configurations";
export default function LiveMap({ selectedTeamId, onSelected, isFocusing, setIsFocusing, mapStyle, showZones, showNames, showArrows }) { export default function LiveMap({ selectedTeamId, onSelected, isFocusing, setIsFocusing, mapStyle, showZones, showNames, showArrows }) {
const { zoneType, zoneExtremities, teams, nextZoneDate, getTeam, gameState } = useAdmin(); const { zones, teams, getTeam, gameState } = useAdmin();
const [timeLeftNextZone, setTimeLeftNextZone] = useState(null); const [timeLeftNextZone, setTimeLeftNextZone] = useState(null);
const [isFullScreen, setIsFullScreen] = useState(false); const [isFullScreen, setIsFullScreen] = useState(false);
useEffect(() => { useEffect(() => {
if (nextZoneDate) { if (zones?.zoneTransitionDate) {
const updateTime = () => { const updateTime = () => {
setTimeLeftNextZone(Math.max(0, Math.floor((nextZoneDate - Date.now()) / 1000))); setTimeLeftNextZone(Math.max(0, Math.floor((zones.zoneTransitionDate - Date.now()) / 1000)));
}; };
updateTime(); updateTime();
@@ -21,7 +21,7 @@ export default function LiveMap({ selectedTeamId, onSelected, isFocusing, setIsF
return () => clearInterval(interval); return () => clearInterval(interval);
} }
}, [nextZoneDate]); }, [zones?.zoneTransitionDate]);
function formatTime(time) { function formatTime(time) {
// time is in seconds // time is in seconds
@@ -34,20 +34,10 @@ export default function LiveMap({ selectedTeamId, onSelected, isFocusing, setIsF
function Zones() { function Zones() {
if (!(showZones && gameState == GameState.PLAYING)) return null; if (!(showZones && gameState == GameState.PLAYING)) return null;
switch (zoneType) { return (<>
case ZoneTypes.CIRCLE: <PolygonZone polygon={zones.currentZone} color={mapStyle.currentZoneColor} />
return (<> <PolygonZone polygon={zones.nextZone} color={mapStyle.nextZoneColor} />
<CircleZone circle={zoneExtremities.begin} color={mapStyle.currentZoneColor} /> </>);
<CircleZone circle={zoneExtremities.end} color={mapStyle.nextZoneColor} />
</>);
case ZoneTypes.POLYGON:
return (<>
<PolygonZone polygon={zoneExtremities.begin?.polygon} color={mapStyle.currentZoneColor} />
<PolygonZone polygon={zoneExtremities.end?.polygon} color={mapStyle.nextZoneColor} />
</>);
default:
return null;
}
} }
return ( return (

View File

@@ -29,7 +29,7 @@ function IconValue({ color, icon, value }) {
} }
export default function TeamSidePanel({ selectedTeamId, onClose }) { export default function TeamSidePanel({ selectedTeamId, onClose }) {
const { getTeam, startDate, gameState } = useAdmin(); const { getTeam, gameState } = useAdmin();
const [imgSrc, setImgSrc] = useState(""); const [imgSrc, setImgSrc] = useState("");
const [_, setRefreshKey] = useState(0); const [_, setRefreshKey] = useState(0);
const team = getTeam(selectedTeamId); const team = getTeam(selectedTeamId);
@@ -106,11 +106,11 @@ export default function TeamSidePanel({ selectedTeamId, onClose }) {
<DotLine label="Chassé par" value={getTeam(team.chased)?.name ?? NO_VALUE} /> <DotLine label="Chassé par" value={getTeam(team.chased)?.name ?? NO_VALUE} />
</div> </div>
} }
{ (gameState == GameState.PLAYING || gameState == GameState.FINISHED) && { (gameState == GameState.PLAYING || gameState == GameState.FINISHED) && false &&
<div> <div>
<DotLine label="Distance" value={formatDistance(team.distance)} /> <DotLine label="Distance" value={formatDistance(team.distance)} />
<DotLine label="Temps de survie" value={formatTime(startDate, team.finishDate || Date.now())} /> <DotLine label="Temps de survie" value={formatTime(0, team.finishDate || Date.now())} />
<DotLine label="Vitesse moyenne" value={formatSpeed(team.distance, startDate, team.finishDate || Date.now())} /> <DotLine label="Vitesse moyenne" value={formatSpeed(team.distance, 0, team.finishDate || Date.now())} />
<DotLine label="Captures" value={team.nCaptures ?? NO_VALUE} /> <DotLine label="Captures" value={team.nCaptures ?? NO_VALUE} />
<DotLine label="Observations" value={team.nSentLocation ?? NO_VALUE} /> <DotLine label="Observations" value={team.nSentLocation ?? NO_VALUE} />
<DotLine label="Observé" value={team.nObserved ?? NO_VALUE} /> <DotLine label="Observé" value={team.nObserved ?? NO_VALUE} />

View File

@@ -46,9 +46,9 @@ function Drawings({ minZone, setMinZone, maxZone, setMaxZone, editMode }) {
} }
export default function CircleZoneSelector({ display }) { export default function CircleZoneSelector({ display }) {
const {zoneSettings, outOfZoneDelay, updateSettings} = useAdmin(); const {settings, updateSettings} = useAdmin();
const [localZoneSettings, setLocalZoneSettings, applyLocalZoneSettings] = useLocalVariable(zoneSettings, (e) => updateSettings({zone: e})); const [localZoneSettings, setLocalZoneSettings, applyLocalZoneSettings] = useLocalVariable(settings.playingZones, (e) => updateSettings({playingZones: e}));
const [localOutOfZoneDelay, setLocalOutOfZoneDelay, applyLocalOutOfZoneDelay] = useLocalVariable(outOfZoneDelay, (e) => updateSettings({outOfZoneDelay: e})); const [localOutOfZoneDelay, setLocalOutOfZoneDelay, applyLocalOutOfZoneDelay] = useLocalVariable(settings.outOfZoneDelay, (e) => updateSettings({outOfZoneDelay: e}));
const [editMode, setEditMode] = useState(EditMode.MAX); const [editMode, setEditMode] = useState(EditMode.MAX);
useEffect(() => { useEffect(() => {

View File

@@ -1,30 +1,8 @@
import { Section } from "@/components/section"; import { Section } from "@/components/section";
import useAdmin from "@/hook/useAdmin";
import useLocalVariable from "@/hook/useLocalVariable";
function MessageInput({title, ...props}) {
return (
<div className="w-full flex flex-row gap-3 items-center">
<p>{title}</p>
<input className="w-full p-1 rounded ring-1 ring-inset ring-gray-400 placeholder:text-gray-600" {...props} />
</div>
);
}
export default function Messages() { export default function Messages() {
const {messages, updateSettings} = useAdmin();
const [localGameSettings, setLocalGameSettings, applyLocalGameSettings] = useLocalVariable(messages, (e) => updateSettings({messages: e}));
function modifyLocalZoneSettings(key, value) {
setLocalGameSettings(prev => ({...prev, [key]: value}));
};
return ( return (
<Section title="Messages" innerClassName="w-full h-full flex flex-col gap-3 items-center"> <Section title="Messages" innerClassName="w-full h-full flex flex-col gap-3 items-center">
<MessageInput id="waiting" title="Attente  :" value={localGameSettings?.waiting ?? ""} onChange={(e) => modifyLocalZoneSettings("waiting", e.target.value)} onBlur={applyLocalGameSettings}/>
<MessageInput id="captured" title="Capture :" value={localGameSettings?.captured ?? ""} onChange={(e) => modifyLocalZoneSettings("captured", e.target.value)} onBlur={applyLocalGameSettings}/>
<MessageInput id="winner" title="Victoire :" value={localGameSettings?.winner ?? ""} onChange={(e) => modifyLocalZoneSettings("winner", e.target.value)} onBlur={applyLocalGameSettings}/>
<MessageInput id="loser" title="Défaite  :" value={localGameSettings?.loser ?? ""} onChange={(e) => modifyLocalZoneSettings("loser", e.target.value)} onBlur={applyLocalGameSettings}/>
</Section> </Section>
); );
} }

View File

@@ -19,19 +19,17 @@ function ZoneTypeButton({title, onClick, isSelected}) {
} }
export default function PlayingZoneSelector({ display }) { export default function PlayingZoneSelector({ display }) {
const { zoneType } = useAdmin();
const [localZoneType, setLocalZoneType] = useLocalVariable(zoneType, () => {});
return ( return (
<div className={display ? 'w-full h-full gap-3 flex flex-col' : "hidden"}> <div className={display ? 'w-full h-full gap-3 flex flex-col' : "hidden"}>
<div className="w-full flex flex-row gap-3 items-center"> <div className="w-full flex flex-row gap-3 items-center">
<p className="text-l">Type de zone :</p> <p className="text-l">Type de zone :</p>
<ZoneTypeButton title="Cercles" onClick={() => setLocalZoneType(ZoneTypes.CIRCLE)} isSelected={localZoneType == ZoneTypes.CIRCLE} /> <ZoneTypeButton title="Cercles" onClick={() => setLocalZoneType(ZoneTypes.CIRCLE)} isSelected={ZoneTypes.POLYGON == ZoneTypes.CIRCLE} />
<ZoneTypeButton title="Polygones" onClick={() => setLocalZoneType(ZoneTypes.POLYGON)} isSelected={localZoneType == ZoneTypes.POLYGON} /> <ZoneTypeButton title="Polygones" onClick={() => setLocalZoneType(ZoneTypes.POLYGON)} isSelected={ZoneTypes.POLYGON == ZoneTypes.POLYGON} />
</div> </div>
<div className="w-full flex-1"> <div className="w-full flex-1">
<CircleZoneSelector display={localZoneType == ZoneTypes.CIRCLE} /> <CircleZoneSelector display={ZoneTypes.POLYGON == ZoneTypes.CIRCLE} />
<PolygonZoneSelector display={localZoneType == ZoneTypes.POLYGON} /> <PolygonZoneSelector display={ZoneTypes.POLYGON == ZoneTypes.POLYGON} />
</div> </div>
</div> </div>
); );

View File

@@ -54,9 +54,9 @@ function Drawings({ localZoneSettings, addZone, removeZone }) {
export default function PolygonZoneSelector({ display }) { export default function PolygonZoneSelector({ display }) {
const defaultDuration = 10; const defaultDuration = 10;
const {zoneSettings, outOfZoneDelay, updateSettings} = useAdmin(); const {settings, updateSettings} = useAdmin();
const [localZoneSettings, setLocalZoneSettings, applyLocalZoneSettings] = useLocalVariable(zoneSettings, (e) => updateSettings({zone: e})); const [localZoneSettings, setLocalZoneSettings, applyLocalZoneSettings] = useLocalVariable(settings.zones, (e) => updateSettings({zone: e}));
const [localOutOfZoneDelay, setLocalOutOfZoneDelay, applyLocalOutOfZoneDelay] = useLocalVariable(outOfZoneDelay, (e) => updateSettings({outOfZoneDelay: e})); const [localOutOfZoneDelay, setLocalOutOfZoneDelay, applyLocalOutOfZoneDelay] = useLocalVariable(settings.outOfZoneDelay, (e) => updateSettings({outOfZoneDelay: e}));
useEffect(() => { useEffect(() => {
if (!localZoneSettings || localZoneSettings.type != ZoneTypes.POLYGON) { if (!localZoneSettings || localZoneSettings.type != ZoneTypes.POLYGON) {

View File

@@ -21,9 +21,9 @@ function TeamManagerItem({ team }) {
} }
export default function TeamManager() { export default function TeamManager() {
const { teams, addTeam, reorderTeams, sendPositionDelay, updateSettings } = useAdmin(); const { teams, addTeam, reorderTeams, settings, updateSettings } = useAdmin();
const [teamName, setTeamName] = useState(''); const [teamName, setTeamName] = useState('');
const [localSendPositionDelay, setLocalSendPositionDelay, applyLocalSendPositionDelay] = useLocalVariable(sendPositionDelay, (e) => updateSettings({sendPositionDelay: e})); const [localSendPositionDelay, setLocalSendPositionDelay, applyLocalSendPositionDelay] = useLocalVariable(settings.scanDelay, (e) => updateSettings({scanDelay: e}));
function handleTeamSubmit(e) { function handleTeamSubmit(e) {
e.preventDefault(); e.preventDefault();

View File

@@ -8,44 +8,21 @@ const adminContext = createContext();
export function AdminProvider({ children }) { export function AdminProvider({ children }) {
const { adminSocket } = useSocket(); const { adminSocket } = useSocket();
// teams
const [teams, setTeams] = useState([]);
// game_state
const [gameState, setGameState] = useState(GameState.SETUP); const [gameState, setGameState] = useState(GameState.SETUP);
const [startDate, setStartDate] = useState(null); const [teams, setTeams] = useState([]);
// current_zone const [zones, setZones] = useState(null);
const [zoneType, setZoneType] = useState(null); const [settings, setSettings] = useState(null);
const [zoneExtremities, setZoneExtremities] = useState(null);
const [nextZoneDate, setNextZoneDate] = useState(null);
// settings
const [messages, setMessages] = useState(null);
const [zoneSettings, setZoneSettings] = useState(null)
const [sendPositionDelay, setSendPositionDelay] = useState(null);
const [outOfZoneDelay, setOutOfZoneDelay] = useState(null);
useSocketListener(adminSocket, "teams", setTeams); useSocketListener(adminSocket, "update-full", ({ gameState, teams, zones, settings }) => {
setGameState(gameState);
useSocketListener(adminSocket, "game_state", (data) => { setTeams(teams);
setGameState(data.state); setZones(zones);
setStartDate(data.date); setSettings(settings);
});
useSocketListener(adminSocket, "current_zone", (data) => {
setZoneExtremities({begin: data.begin, end: data.end});
setNextZoneDate(data.endDate);
});
useSocketListener(adminSocket, "settings", (data) => {
setMessages(data.messages);
setZoneSettings(data.zone);
setZoneType(data.zone.type);
setSendPositionDelay(data.sendPositionDelay);
setOutOfZoneDelay(data.outOfZoneDelay);
}); });
const value = useMemo(() => ( const value = useMemo(() => (
{ zoneSettings, teams, gameState, zoneType, zoneExtremities, sendPositionDelay, outOfZoneDelay, messages, nextZoneDate, startDate } { gameState, teams, zones, settings }
), [zoneSettings, teams, gameState, zoneType, zoneExtremities, sendPositionDelay, outOfZoneDelay, messages, nextZoneDate, startDate]); ), [gameState, teams, zones, settings]);
return ( return (
<adminContext.Provider value={value}> <adminContext.Provider value={value}>

View File

@@ -12,15 +12,15 @@ export default function useAdmin() {
} }
function addTeam(teamName) { function addTeam(teamName) {
adminSocket.emit("add_team", teamName); adminSocket.emit("add-team", teamName);
} }
function removeTeam(teamId) { function removeTeam(teamId) {
adminSocket.emit("remove_team", teamId); adminSocket.emit("remove-team", teamId);
} }
function reorderTeams(newOrder) { function reorderTeams(newOrder) {
adminSocket.emit("reorder_teams", newOrder); adminSocket.emit("reorder-teams", newOrder);
} }
function captureTeam(teamId) { function captureTeam(teamId) {
@@ -32,11 +32,11 @@ export default function useAdmin() {
} }
function changeState(state) { function changeState(state) {
adminSocket.emit("change_state", state); adminSocket.emit("state", state);
} }
function updateSettings(settings) { function updateSettings(settings) {
adminSocket.emit("update_settings", settings); adminSocket.emit("settings", settings);
} }
return { ...adminContext, getTeam, reorderTeams, addTeam, removeTeam, captureTeam, placementTeam, changeState, updateSettings }; return { ...adminContext, getTeam, reorderTeams, addTeam, removeTeam, captureTeam, placementTeam, changeState, updateSettings };

View File

@@ -1,18 +1,17 @@
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const nextConfig = { const nextConfig = {
output: 'standalone',
output: 'standalone', async redirects() {
return [
async redirects() { {
return [ source: '/',
{ destination: '/admin',
source: '/', permanent: false,
destination: '/admin', },
permanent: false, // The browser will not save the redirect in its cache ]
}, },
]
},
}; };

6351
server/traque-front/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,6 @@
"name": "traque-front", "name": "traque-front",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"author": "Quentin Roussel",
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",
"build": "next build", "build": "next build",

View File

@@ -7,7 +7,7 @@ export const Colors = {
} }
export const GameState = { export const GameState = {
SETUP: "setup", SETUP: "default",
PLACEMENT: "placement", PLACEMENT: "placement",
PLAYING: "playing", PLAYING: "playing",
FINISHED: "finished" FINISHED: "finished"