Initial commit
Some checks failed
Publish Docker image / Push Docker image to Docker Hub (push) Failing after 4m2s

This commit is contained in:
Mathias Wagner 2025-02-27 19:24:14 +01:00
parent 9318052560
commit 62acb290fe
27 changed files with 4873 additions and 0 deletions

33
.github/workflows/deploy_docker.yml vendored Normal file
View File

@ -0,0 +1,33 @@
name: Publish Docker image
on:
push:
branches: [ "main" ]
jobs:
push_to_registry:
name: Push Docker image to Docker Hub
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v4
- name: Set up docker buildx
uses: docker/setup-buildx-action@v1
- name: Log in to Docker Hub
uses: docker/login-action@v1
with:
registry: git.gnm.dev
username: ${{ github.actor }}
password: ${{ secrets.GT_TOKEN }}
- name: Build and push Docker image
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: git.gnm.dev/websiteprojects/toneguessr:latest
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}

134
.gitignore vendored Normal file
View File

@ -0,0 +1,134 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# IDEs
.idea
.vscode
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

16
Dockerfile Normal file
View File

@ -0,0 +1,16 @@
FROM node:18-alpine
WORKDIR /app
COPY --chown=node:node ./server ./server
COPY --chown=node:node ./package.json ./package.json
RUN npm install
RUN chown -R node:node /app
USER node
EXPOSE 5287
CMD ["node", "server"]

15
client/.eslintrc.cjs Normal file
View File

@ -0,0 +1,15 @@
module.exports = {
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:react/jsx-runtime',
'plugin:react-hooks/recommended',
],
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
settings: { react: { version: '18.2' } },
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': 'warn',
},
}

24
client/.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

13
client/index.html Normal file
View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/png" href="/logo.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ToneGuessr</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>

10
client/jsconfig.json Normal file
View File

@ -0,0 +1,10 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": [
"./src/*"
]
}
}
}

32
client/package.json Normal file
View File

@ -0,0 +1,32 @@
{
"name": "webui",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint src --ext js,jsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
"dependencies": {
"@fontsource/bangers": "^5.1.1",
"@fortawesome/fontawesome-svg-core": "^6.7.2",
"@fortawesome/free-solid-svg-icons": "^6.7.2",
"@fortawesome/react-fontawesome": "^0.2.2",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"sass": "^1.85.1",
"socket.io-client": "^4.8.1"
},
"devDependencies": {
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"@vitejs/plugin-react": "^4.3.4",
"eslint": "^9.21.0",
"eslint-plugin-react": "^7.37.4",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-react-refresh": "^0.4.19",
"vite": "^6.2.0"
}
}

3194
client/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

BIN
client/public/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

15
client/src/App.jsx Normal file
View File

@ -0,0 +1,15 @@
import Home from "./pages/Home";
import {useContext} from "react";
import {StateContext} from "@/common/contexts/StateContext";
const App = () => {
const {currentState} = useContext(StateContext);
return (
<>
{currentState === "Home" && <Home />}
</>
)
}
export default App

View File

@ -0,0 +1,30 @@
import {createContext} from "react";
import {io} from "socket.io-client";
export const SocketContext = createContext({});
export const SocketProvider = ({ children }) => {
const socket = io("/", {autoConnect: false});
const connect = () => {
socket.connect();
}
const send = (event, data) => {
socket.emit(event, data);
}
const disconnect = () => {
socket.disconnect();
}
const addListener = (event, callback) => {
socket.on(event, callback);
}
return (
<SocketContext.Provider value={{connect, disconnect, send, addListener}}>
{children}
</SocketContext.Provider>
)
}

View File

@ -0,0 +1 @@
export * from "./SocketContext";

View File

@ -0,0 +1,13 @@
import {createContext, useState} from "react";
export const StateContext = createContext({});
export const StateProvider = ({ children }) => {
const [currentState, setCurrentState] = useState("Home");
return (
<StateContext.Provider value={{currentState, setCurrentState}}>
{children}
</StateContext.Provider>
)
}

View File

@ -0,0 +1 @@
export * from "./StateContext";

View File

View File

@ -0,0 +1,17 @@
@use "@/common/styles/colors" as *
*
box-sizing: border-box
body, html
margin: 0
padding: 0
background-color: #232529
letter-spacing: 0.2rem
color: #E0E0E0
height: 100%
font-family: 'Bangers', sans-serif
::-webkit-scrollbar
width: 10px

21
client/src/main.jsx Normal file
View File

@ -0,0 +1,21 @@
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.jsx";
import "@fontsource/bangers";
import "@/common/styles/main.sass";
import {StateProvider} from "@/common/contexts/StateContext";
import {SocketProvider} from "@/common/contexts/SocketContext";
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<div className="app">
<StateProvider>
<SocketProvider>
<App/>
</SocketProvider>
</StateProvider>
</div>
</React.StrictMode>,
);

View File

@ -0,0 +1,13 @@
import "./styles.sass";
import {StateContext} from "@/common/contexts/StateContext";
import {useContext} from "react";
export const Home = () => {
const {setCurrentState} = useContext(StateContext);
return (
<div className="home-page">
<h2>ToneGuessr</h2>
</div>
);
}

View File

@ -0,0 +1 @@
export {Home as default} from "./Home";

View File

@ -0,0 +1,8 @@
.home-page
display: flex
align-items: center
justify-content: center
height: 100vh
h2
font-size: 48pt

21
client/vite.config.js Normal file
View File

@ -0,0 +1,21 @@
import {defineConfig} from "vite";
import react from "@vitejs/plugin-react";
import * as path from "path";
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
server: {
proxy: {
"/socket.io": {
target: "http://localhost:5287",
changeOrigin: true,
ws: true,
},
},
},
})

22
package.json Normal file
View File

@ -0,0 +1,22 @@
{
"name": "toneguessr",
"version": "1.0.0-ALPHA",
"description": "The server of the ToneGuessr game",
"main": "server/index.js",
"repository": "https://git.gnm.dev/WebsiteProjects/ToneGuessr",
"author": "Mathias Wagner",
"license": "MIT",
"scripts": {
"webui": "cd client && yarn dev",
"server": "nodemon server",
"dev": "concurrently --kill-others \"yarn server\" \"yarn webui\""
},
"dependencies": {
"express": "^4.21.2",
"socket.io": "^4.8.1"
},
"devDependencies": {
"concurrently": "^9.1.2",
"nodemon": "^3.1.9"
}
}

1149
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

39
server/controller/room.js Normal file
View File

@ -0,0 +1,39 @@
let rooms = {};
module.exports.roomExists = (roomId) => rooms[roomId] !== undefined;
module.exports.connectUserToRoom = (roomId, user) => {
if (rooms[roomId]) {
rooms[roomId].members.push({...user, creator: false});
} else {
rooms[roomId] = {members: [{...user, creator: true}], settings: {}};
}
console.log(JSON.stringify(rooms));
}
module.exports.getUserRoom = (userId) => {
for (const roomId in rooms) {
const room = rooms[roomId];
const memberIndex = room.members.findIndex(member => member.id === userId);
if (memberIndex !== -1) return roomId;
}
}
module.exports.disconnectUser = (userId) => {
for (const roomId in rooms) {
const room = rooms[roomId];
const memberIndex = room.members.findIndex(member => member.id === userId);
if (memberIndex !== -1) {
if (room.members[memberIndex].creator) {
if (room.members.length > 1) room.members[1].creator = true;
}
room.members.splice(memberIndex, 1);
if (room.members.length === 0) delete rooms[roomId];
break;
}
}
}

View File

@ -0,0 +1,36 @@
const {connectUserToRoom, roomExists, disconnectUser, getUserRoom} = require("../controller/room");
module.exports = (socket) => {
let currentRoomId = null;
socket.on("disconnect", () => {
const roomId = getUserRoom(socket.id);
if (roomId) socket.to(roomId).emit("user-disconnected", socket.id);
disconnectUser(socket.id);
});
socket.on("join-room", ({roomId, name}) => {
if (currentRoomId) return socket.emit("already-in-room", currentRoomId);
if (roomExists(roomId.toString())) {
connectUserToRoom(roomId, {id: socket.id, name: name.toString()});
socket.join(roomId);
socket.to(roomId).emit("user-connected", {name: name.toString(), id: socket.id});
socket.emit("room-joined", roomId);
currentRoomId = roomId;
} else {
socket.emit("room-not-found", roomId.toString());
}
});
socket.on("create-room", ({name}) => {
if (!name) return socket.emit("room-name-required");
const roomId = Math.random().toString(36).substring(7);
connectUserToRoom(roomId, {id: socket.id, name: name?.toString()});
socket.join(roomId);
socket.emit("room-created", roomId);
currentRoomId = roomId;
});
}

15
server/index.js Normal file
View File

@ -0,0 +1,15 @@
const express = require("express");
const { Server } = require("socket.io");
const http = require("http");
const app = express();
app.disable("x-powered-by");
const server = http.createServer(app);
const io = new Server(server, {cors: {origin: "*"}});
io.on("connection", require("./handler/connection"));
server.listen(5287, () => {
console.log("Server running on port 5287");
});