Skip to content

Commit

Permalink
chore: linting
Browse files Browse the repository at this point in the history
  • Loading branch information
scolastico committed Oct 25, 2024
1 parent 4c9a54c commit 3a43201
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 130 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ jobs:
- name: Install dependencies with pnpm
run: pnpm install

# Build and generate code
# Lint and build
- name: Run esLint
run: pnpm lint
- name: Run prettier
run: pnpm prettier
- name: Build
run: pnpm build

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"start": "node dist/index.cjs",
"lint": "eslint src",
"lint:fix": "eslint --fix src",
"prettier": "prettier --write src/**/*.ts"
"prettier": "prettier --check src/**/*.ts",
"prettier:fix": "prettier --write src/**/*.ts"
},
"dependencies": {
"@board-bound/sdk": "^0.1.0",
Expand Down
31 changes: 19 additions & 12 deletions src/handler/StatusHandler.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import type { Application } from "express-ws";
import { GameServer } from "..";
import { SimpleEventBus } from "../TypeHelper";
import { ModifiableData } from "@board-bound/sdk";
import type { Application } from 'express-ws'
import { GameServer } from '..'
import { SimpleEventBus } from '../TypeHelper'
import { ModifiableData } from '@board-bound/sdk'

export function installStatusHandler(app: Application, server: GameServer, bus: SimpleEventBus) {
app.get("/status", async (req, res) => {
server.getLogger().info("Status check from " + req.ip);
const response = new ModifiableData({ status: "ok" });
const success = await bus.emit("serverStatusRequest", server, { ip: req.ip, response })
if (!success) res.status(560).json({ error: "Request canceled" });
else res.json(response.get());
});
export function installStatusHandler(
app: Application,
server: GameServer,
bus: SimpleEventBus
) {
app.get('/status', async (req, res) => {
server.getLogger().info('Status check from ' + req.ip)
const response = new ModifiableData({ status: 'ok' })
const success = await bus.emit('serverStatusRequest', server, {
ip: req.ip,
response,
})
if (!success) res.status(560).json({ error: 'Request canceled' })
else res.json(response.get())
})
}
176 changes: 100 additions & 76 deletions src/handler/WsHandler.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { Application } from "express-ws";
import type { WebSocket } from "ws";
import { GameServer } from "..";
import { SimpleEventBus } from "../TypeHelper";
import { LOGO } from "../tools/LogoProvider";
import type { Application } from 'express-ws'
import type { WebSocket } from 'ws'
import { GameServer } from '..'
import { SimpleEventBus } from '../TypeHelper'
import { LOGO } from '../tools/LogoProvider'

import { ConnectedUser, ModifiableData } from "@board-bound/sdk";
import { ConnectedUser, ModifiableData } from '@board-bound/sdk'

export class WsConnectedUser extends ConnectedUser {
constructor(
Expand All @@ -13,116 +13,140 @@ export class WsConnectedUser extends ConnectedUser {
public readonly ip: string,
private readonly server: GameServer,
private readonly ws: WebSocket,
private readonly bus: SimpleEventBus,
private readonly bus: SimpleEventBus
) {
super(id, name, ip);
super(id, name, ip)
}

async sendMessage(message: Record<string, unknown>): Promise<void> {
const data = new ModifiableData(message);
const res = await this.bus.emit('serverUserRawOutput', this.server, { user: this, message: data });
if (!res) return;
this.server.getLogger().debug({ tag: 'ws-send', message: data.get(), id: this.id, ip: this.ip }, 'Sending message to user');
this.ws.send(JSON.stringify(data.get()));
const data = new ModifiableData(message)
const res = await this.bus.emit('serverUserRawOutput', this.server, {
user: this,
message: data,
})
if (!res) return
this.server
.getLogger()
.debug(
{ tag: 'ws-send', message: data.get(), id: this.id, ip: this.ip },
'Sending message to user'
)
this.ws.send(JSON.stringify(data.get()))
}

async disconnect(message: string, code?: number): Promise<void> {
const msg = code ? { error: message, code } : { message };
this.sendMessage(msg);
this.ws.close(code ? 1008 : 1000);
await this.bus.emit('serverUserDisconnect', this.server, { user: this, message, code });
const msg = code ? { error: message, code } : { message }
this.sendMessage(msg)
this.ws.close(code ? 1008 : 1000)
await this.bus.emit('serverUserDisconnect', this.server, {
user: this,
message,
code,
})
}
}

export function installWsHandler(app: Application, server: GameServer, bus: SimpleEventBus) {
const maxConnectionsPerIp = parseInt(process.env.MAX_CONNECTIONS_PER_IP || '10') || 10;
const maxConnectionsDifIp = parseInt(process.env.MAX_CONNECTIONS_DIF_IP || '100') || 100;
const connections = new Map<string, number>();
app.ws("/play", async (ws, req) => {
const ip = req.socket.remoteAddress;
const id = server.getUuid();
const log = server.getLogger().child({tag: 'ws', ip, id});
const send = (msg: Record<string, unknown>) => ws.send(JSON.stringify(msg));
export function installWsHandler(
app: Application,
server: GameServer,
bus: SimpleEventBus
) {
const maxConnectionsPerIp =
parseInt(process.env.MAX_CONNECTIONS_PER_IP || '10') || 10
const maxConnectionsDifIp =
parseInt(process.env.MAX_CONNECTIONS_DIF_IP || '100') || 100
const connections = new Map<string, number>()
app.ws('/play', async (ws, req) => {
const ip = req.socket.remoteAddress
const id = server.getUuid()
const log = server.getLogger().child({ tag: 'ws', ip, id })
const send = (msg: Record<string, unknown>) => ws.send(JSON.stringify(msg))

if (connections.has(ip)) {
const count = connections.get(ip) + 1;
const count = connections.get(ip) + 1
if (count > maxConnectionsPerIp) {
log.warn('Too many connections from this IP');
send({error: 'Too many connections from this IP', code: 429});
ws.close(1008);
return;
log.warn('Too many connections from this IP')
send({ error: 'Too many connections from this IP', code: 429 })
ws.close(1008)
return
}
connections.set(ip, count);
connections.set(ip, count)
} else {
if (connections.size > maxConnectionsDifIp) {
log.warn('Too many different IPs connected');
send({error: 'Too many different IPs connected', code: 429});
ws.close(1008);
return;
log.warn('Too many different IPs connected')
send({ error: 'Too many different IPs connected', code: 429 })
ws.close(1008)
return
}
connections.set(ip, 1);
connections.set(ip, 1)
}

log.info('Received connection, awaiting login payload');
log.info('Received connection, awaiting login payload')
const loginTimeout = setTimeout(() => {
log.warn('Login timeout reached, closing connection');
send({ error: 'Login timeout reached', code: 408 });
ws.close(1008);
}, 5000);
let loggedIn = false;
log.warn('Login timeout reached, closing connection')
send({ error: 'Login timeout reached', code: 408 })
ws.close(1008)
}, 5000)
let loggedIn = false

// We send the logo to the client to display it.
// This is simple way to ensure the server protocol isn't
// reverse-engineered to publish it with a different license,
// as the logo is a trademark and copyrighted.
// Don't tamper with the logo, or clients will refuse to connect.
send({ message: 'Please login', code: 401, logo: LOGO });
send({ message: 'Please login', code: 401, logo: LOGO })

ws.on('close', async () => {
const user = server.getConnectedUsers().find(u => u.id === id);
if (user) server.removeConnectedUser(user);
if (!loggedIn) clearTimeout(loginTimeout);
const count = connections.get(ip) - 1;
if (count <= 0) connections.delete(ip);
else connections.set(ip, count);
log.info('Websocket connection closed');
});
const user = server.getConnectedUsers().find((u) => u.id === id)
if (user) server.removeConnectedUser(user)
if (!loggedIn) clearTimeout(loginTimeout)
const count = connections.get(ip) - 1
if (count <= 0) connections.delete(ip)
else connections.set(ip, count)
log.info('Websocket connection closed')
})

ws.on('message', async (msg) => {
log.debug({ tag: 'ws-message', message: msg }, 'Received message from client');
let payload: Record<string, unknown> = {};
log.debug(
{ tag: 'ws-message', message: msg },
'Received message from client'
)
let payload: Record<string, unknown> = {}
try {
payload = JSON.parse(msg.toString());
payload = JSON.parse(msg.toString())
} catch {
log.warn('Invalid JSON payload received');
send({ error: 'Invalid JSON payload', code: 400 });
return;
log.warn('Invalid JSON payload received')
send({ error: 'Invalid JSON payload', code: 400 })
return
}
if (!loggedIn) {
clearTimeout(loginTimeout);
const name = new ModifiableData<string>('');
clearTimeout(loginTimeout)
const name = new ModifiableData<string>('')
loggedIn = await bus.emit('serverUserPreConnect', server, {
ip: req.socket.remoteAddress,
headers: req.headers as Record<string, string|string[]>,
headers: req.headers as Record<string, string | string[]>,
payload,
name,
});
})
if (!loggedIn) {
log.warn('Preconnect event canceled connection');
send({ error: 'Connection denied', code: 403 });
ws.close(1008);
log.warn('Preconnect event canceled connection')
send({ error: 'Connection denied', code: 403 })
ws.close(1008)
} else {
send({ message: 'Connected', code: 200 });
const user = new WsConnectedUser(id, name.get(), ip, server, ws, bus);
server.addConnectedUser(user);
await bus.emit('serverUserConnect', server, user);
log.info('User connected successfully');
send({ message: 'Connected', code: 200 })
const user = new WsConnectedUser(id, name.get(), ip, server, ws, bus)
server.addConnectedUser(user)
await bus.emit('serverUserConnect', server, user)
log.info('User connected successfully')
}
return;
return
}
const user = server.getConnectedUsers().find(u => u.id === id);
const result = await bus.emit('serverUserRawInput', server, {user, payload});
if (!result) send({error: 'Invalid input', code: 400});
});
});
const user = server.getConnectedUsers().find((u) => u.id === id)
const result = await bus.emit('serverUserRawInput', server, {
user,
payload,
})
if (!result) send({ error: 'Invalid input', code: 400 })
})
})
}
38 changes: 19 additions & 19 deletions src/tools/IpFinder.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,51 @@
import os from 'os';
import axios from 'axios';
import os from 'os'
import axios from 'axios'

export function getAllLocalIPAddresses(): string[] {
const interfaces = os.networkInterfaces();
const addresses: string[] = [];
const interfaces = os.networkInterfaces()
const addresses: string[] = []

for (const name in interfaces) {
const iface = interfaces[name];
if (!iface) continue;
const iface = interfaces[name]
if (!iface) continue

for (const alias of iface) {
if (alias.family !== 'IPv4' || alias.internal) continue;
addresses.push(alias.address);
if (alias.family !== 'IPv4' || alias.internal) continue
addresses.push(alias.address)
}
}

return addresses;
return addresses
}

export function getLocalIPAddress(): string | null {
const interfaces = os.networkInterfaces();
const interfaces = os.networkInterfaces()

for (const name in interfaces) {
const iface = interfaces[name];
if (!iface) continue;
const iface = interfaces[name]
if (!iface) continue

for (const alias of iface) {
if (alias.family === 'IPv4' && !alias.internal) {
return alias.address;
return alias.address
}
}
}

return null;
return null
}

export async function getPublicIPAddress(): Promise<string> {
try {
const response = await axios.get('https://api.ipify.org', {
params: { format: 'json' },
});
})

const ip = response.data?.ip;
if (!ip) throw new Error('Invalid response from IP service.');
const ip = response.data?.ip
if (!ip) throw new Error('Invalid response from IP service.')

return ip;
return ip
} catch (error) {
throw new Error(`Error fetching public IP address: ${error.message}`);
throw new Error(`Error fetching public IP address: ${error.message}`)
}
}
2 changes: 1 addition & 1 deletion src/tools/LogoProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,4 +404,4 @@ uIdL//7cfn/NCjEqAqjQFe5/7D9jwqbMjqwz1qt5aryaFc9T44mNCMCqmECFBmEQIqLnf/SKfl92
hQoVKlSoUKFChQoVKlTI8P8Dom2th+ClIqcAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjMtMDgtMTZU
MDE6MTM6NTMrMDA6MDBmVd0vAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIzLTA4LTE2VDAxOjEzOjUz
KzAwOjAwFwhlkwAAAABJRU5ErkJggg==
`.trim();
`.trim()
Loading

0 comments on commit 3a43201

Please sign in to comment.