initial commit

This commit is contained in:
Bhavnoor Singh Saroya 2025-08-25 14:33:52 -07:00
commit abcee32d38
6 changed files with 176 additions and 0 deletions

6
.dockerignore Normal file
View file

@ -0,0 +1,6 @@
/.git
/node_modules
.dockerignore
.env
Dockerfile
fly.toml

39
Dockerfile Normal file
View file

@ -0,0 +1,39 @@
# syntax = docker/dockerfile:1
# Adjust NODE_VERSION as desired
ARG NODE_VERSION=22.13.1
FROM node:${NODE_VERSION}-slim AS base
LABEL fly_launch_runtime="Node.js"
# Node.js app lives here
WORKDIR /app
# Set production environment
ENV NODE_ENV="production"
# Throw-away build stage to reduce size of final image
FROM base AS build
# Install packages needed to build node modules
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y build-essential node-gyp pkg-config python-is-python3
# Install node modules
COPY package-lock.json package.json ./
RUN npm ci
# Copy application code
COPY . .
# Final stage for app image
FROM base
# Copy built application
COPY --from=build /app /app
# Start the server by default, this can be overwritten at runtime
EXPOSE 3000
CMD [ "npm", "run", "start" ]

3
README.md Normal file
View file

@ -0,0 +1,3 @@
## Custom game server
Serve games to the appropriate gameboard service from postgres db
Very basic, threw this together in a day

22
fly.toml Normal file
View file

@ -0,0 +1,22 @@
# fly.toml app configuration file generated for snake-server on 2025-08-18T22:02:23-07:00
#
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
#
app = 'snake-server'
primary_region = 'sea'
[build]
[http_service]
internal_port = 3000
force_https = true
auto_stop_machines = 'suspend'
auto_start_machines = true
min_machines_running = 0
processes = ['app']
[[vm]]
memory = '512mb'
cpu_kind = 'shared'
cpus = 1

85
index.js Normal file
View file

@ -0,0 +1,85 @@
import express from "express";
import http from "http";
import { WebSocketServer } from "ws";
import cors from "cors";
import pkg from "pg";
const { Pool } = pkg;
const app = express();
const PORT = 3000;
// Setup Postgres connection pool
const pool = new Pool({
connectionString: process.env.DATABASE_URL || "neverleavetheconnectionstringhereforproduction",
});
app.use(cors());
// HTTP GET /games/:id → return game info from DB
app.get("/games/:id", async (req, res) => {
const gameId = req.params.id;
try {
const result = await pool.query("SELECT info FROM items WHERE id = $1", [gameId]);
if (result.rows.length === 0) {
return res.redirect(302, "https://engine.battlesnake.com");
// return res.status(404).json({ error: "Game not found" });
}
res.json(result.rows[0].info); // send back the JSONB field
} catch (err) {
console.error(err);
res.status(500).json({ error: "Database error" });
}
});
// Create HTTP server and attach WebSocket server
const server = http.createServer(app);
const wss = new WebSocketServer({ server });
// Helper to match the path pattern
function matchGameEventsPath(url) {
if (!url) return null;
const match = url.match(/^\/games\/([^\/]+)\/events$/);
return match ? match[1] : null;
}
wss.on("connection", async (ws, req) => {
const gameId = matchGameEventsPath(req.url);
if (!gameId) {
ws.close();
return;
}
console.log(`WS client connected for game: ${gameId}`);
try {
// Grab frames array from DB
const result = await pool.query("SELECT frames FROM items WHERE id = $1", [gameId]);
if (result.rows.length === 0) {
ws.send(JSON.stringify({ error: "Game not found" }));
ws.close();
return;
}
const frames = result.rows[0].frames || [];
// Stream frames one by one with delay
frames.forEach((frame, i) => {
setTimeout(() => {
if (ws.readyState === ws.OPEN) {
ws.send(JSON.stringify(frame));
}
}, i * 5); // staggered delay
});
} catch (err) {
console.error(err);
ws.send(JSON.stringify({ error: "Database error" }));
ws.close();
}
});
server.listen(PORT, "0.0.0.0", () => {
console.log(`Server running at http://localhost:${PORT} (HTTP + WebSocket)`);
});

21
package.json Normal file
View file

@ -0,0 +1,21 @@
{
"name": "game-server",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"cors": "^2.8.5",
"express": "^5.1.0",
"pg": "^8.16.3",
"ws": "^8.18.3"
},
"devDependencies": {
"@flydotio/dockerfile": "^0.7.10"
}
}