- Aggiunge src/db.js con better-sqlite3: tabella partite con nomi, modalità, set, formazione di partenza per set, punteggi e vincitore - Salvataggio automatico al termine della partita (websocket-handler.js) - Aggiunge formInizioSet in gameState per tracciare la formazione iniziale di ogni set - Aggiunge storico.html: pagina vanilla dark-theme con lista partite espandibili (set, punteggio, formazioni) - Aggiunge server storico su porta 3002 in dev (vite-plugin-websocket.js) - Aggiunge endpoint /api/partite su displayApp (server.js) - Migliora banner di avvio con URL storico locale e da rete - Migliora log WebSocket: connessione aperta, ruolo unregistered, client rimanenti - Aggiorna .gitignore: ignora tutta la cartella data/
199 lines
6.0 KiB
JavaScript
199 lines
6.0 KiB
JavaScript
import { WebSocketServer } from 'ws'
|
|
import { createServer as createHttpServer, request as httpRequest } from 'http'
|
|
import { readFile } from 'fs'
|
|
import { join, dirname } from 'path'
|
|
import { fileURLToPath } from 'url'
|
|
import { setupWebSocketHandler } from './src/websocket-handler.js'
|
|
import { printServerInfo } from './src/server-utils.js'
|
|
import { getPartite, getPartita } from './src/db.js'
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
const CONTROLLER_PORT = 3001
|
|
const STORICO_PORT = process.env.STORICO_PORT || 3002
|
|
const DEV_PROXY_HOST = process.env.DEV_PROXY_HOST || '127.0.0.1'
|
|
|
|
/**
|
|
* Plugin Vite che integra un server WebSocket per la gestione dello stato di gioco
|
|
* e un server separato sulla porta 3001 per il controller.
|
|
* @returns {import('vite').Plugin}
|
|
*/
|
|
export default function websocketPlugin() {
|
|
return {
|
|
name: 'vite-plugin-websocket',
|
|
configureServer(server) {
|
|
// Inizializza un server WebSocket collegato al server HTTP di Vite.
|
|
const wss = new WebSocketServer({ noServer: true })
|
|
|
|
// Registra i gestori WebSocket con la logica di gioco.
|
|
setupWebSocketHandler(wss)
|
|
|
|
// Intercetta le richieste di upgrade WebSocket solo sul path /ws.
|
|
server.httpServer.on('upgrade', (request, socket, head) => {
|
|
const pathname = new URL(request.url, `http://${request.headers.host}`).pathname
|
|
|
|
if (pathname === '/ws') {
|
|
wss.handleUpgrade(request, socket, head, (ws) => {
|
|
wss.emit('connection', ws, request)
|
|
})
|
|
}
|
|
})
|
|
|
|
// Avvia un server separato per il controller sulla porta 3001.
|
|
server.httpServer.once('listening', () => {
|
|
const viteAddr = server.httpServer.address()
|
|
const vitePort = viteAddr.port
|
|
|
|
startControllerDevServer(vitePort, wss)
|
|
startStoricoDevServer()
|
|
|
|
setTimeout(() => printServerInfo(vitePort, CONTROLLER_PORT, STORICO_PORT), 100)
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Avvia il server di sviluppo per il controller.
|
|
* Fa da proxy verso il dev server di Vite per moduli ES, HMR, e asset.
|
|
*/
|
|
function startControllerDevServer(vitePort, wss) {
|
|
const controllerServer = createHttpServer((req, res) => {
|
|
// Se richiesta alla root, riscrive verso controller.html
|
|
let targetPath = req.url
|
|
if (targetPath === '/' || targetPath === '') {
|
|
targetPath = '/controller.html'
|
|
}
|
|
|
|
// Proxy verso il dev server di Vite
|
|
const proxyReq = httpRequest(
|
|
{
|
|
hostname: DEV_PROXY_HOST,
|
|
port: vitePort,
|
|
path: targetPath,
|
|
method: req.method,
|
|
headers: {
|
|
...req.headers,
|
|
host: `${DEV_PROXY_HOST}:${vitePort}`,
|
|
},
|
|
},
|
|
(proxyRes) => {
|
|
res.writeHead(proxyRes.statusCode, proxyRes.headers)
|
|
proxyRes.pipe(res, { end: true })
|
|
}
|
|
)
|
|
|
|
proxyReq.on('error', (err) => {
|
|
console.error('[Controller Proxy] Error:', err.message)
|
|
if (!res.headersSent) {
|
|
res.writeHead(502)
|
|
res.end('Proxy error')
|
|
}
|
|
})
|
|
|
|
req.pipe(proxyReq, { end: true })
|
|
})
|
|
|
|
// Gestisce l'upgrade WebSocket anche sulla porta del controller
|
|
controllerServer.on('upgrade', (request, socket, head) => {
|
|
const pathname = new URL(request.url, `http://${request.headers.host}`).pathname
|
|
|
|
if (pathname === '/ws') {
|
|
wss.handleUpgrade(request, socket, head, (ws) => {
|
|
wss.emit('connection', ws, request)
|
|
})
|
|
} else {
|
|
// Per l'HMR di Vite, proxare l'upgrade WebSocket verso Vite
|
|
const proxyReq = httpRequest({
|
|
hostname: DEV_PROXY_HOST,
|
|
port: vitePort,
|
|
path: request.url,
|
|
method: 'GET',
|
|
headers: request.headers,
|
|
})
|
|
|
|
proxyReq.on('upgrade', (proxyRes, proxySocket, proxyHead) => {
|
|
socket.write(
|
|
`HTTP/1.1 101 Switching Protocols\r\n` +
|
|
Object.entries(proxyRes.headers)
|
|
.map(([k, v]) => `${k}: ${v}`)
|
|
.join('\r\n') +
|
|
'\r\n\r\n'
|
|
)
|
|
proxySocket.pipe(socket)
|
|
socket.pipe(proxySocket)
|
|
})
|
|
|
|
proxyReq.on('error', (err) => {
|
|
console.error('[Controller Proxy] WS upgrade error:', err.message)
|
|
socket.destroy()
|
|
})
|
|
|
|
proxyReq.end()
|
|
}
|
|
})
|
|
|
|
controllerServer.listen(CONTROLLER_PORT, '0.0.0.0', () => {
|
|
console.log(`[Controller] Dev server running on port ${CONTROLLER_PORT}`)
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Avvia il server di sviluppo per lo storico sulla porta 3002.
|
|
* Serve storico.html e gli endpoint /api/partite.
|
|
*/
|
|
function startStoricoDevServer() {
|
|
const storicoServer = createHttpServer((req, res) => {
|
|
const url = new URL(req.url, `http://${req.headers.host}`)
|
|
const pathname = url.pathname
|
|
|
|
if (pathname === '/api/partite') {
|
|
try {
|
|
res.writeHead(200, { 'Content-Type': 'application/json' })
|
|
res.end(JSON.stringify(getPartite()))
|
|
} catch (err) {
|
|
res.writeHead(500, { 'Content-Type': 'application/json' })
|
|
res.end(JSON.stringify({ error: err.message }))
|
|
}
|
|
return
|
|
}
|
|
|
|
const matchId = pathname.match(/^\/api\/partite\/(\d+)$/)
|
|
if (matchId) {
|
|
try {
|
|
const p = getPartita(Number(matchId[1]))
|
|
if (!p) {
|
|
res.writeHead(404, { 'Content-Type': 'application/json' })
|
|
res.end(JSON.stringify({ error: 'Not found' }))
|
|
} else {
|
|
res.writeHead(200, { 'Content-Type': 'application/json' })
|
|
res.end(JSON.stringify(p))
|
|
}
|
|
} catch (err) {
|
|
res.writeHead(500, { 'Content-Type': 'application/json' })
|
|
res.end(JSON.stringify({ error: err.message }))
|
|
}
|
|
return
|
|
}
|
|
|
|
if (pathname === '/' || pathname === '') {
|
|
readFile(join(__dirname, 'storico.html'), (err, data) => {
|
|
if (err) {
|
|
res.writeHead(500)
|
|
res.end('Error loading storico.html')
|
|
} else {
|
|
res.writeHead(200, { 'Content-Type': 'text/html' })
|
|
res.end(data)
|
|
}
|
|
})
|
|
return
|
|
}
|
|
|
|
res.writeHead(404)
|
|
res.end('Not found')
|
|
})
|
|
|
|
storicoServer.listen(STORICO_PORT, '0.0.0.0', () => {
|
|
console.log(`[Storico] http://localhost:${STORICO_PORT}`)
|
|
})
|
|
}
|