feat: separazione display e controller su porte distinte (5173/3001)
- Creati entry point separati per il Display (porta 5173) e il Controller (porta 3001). - Aggiunti controller.html e src/controller-main.js per l'app di controllo remoto. - Semplificato src/main.js per montare direttamente DisplayPage, rimuovendo vue-router. - Implementato un server di sviluppo proxy per il controller in vite-plugin-websocket.js. - Aggiornato server.js per gestire due istanze Express (display e controller) in produzione. - Aggiornata la configurazione di Vite per il supporto alla build multi-pagina
This commit is contained in:
13
controller.html
Normal file
13
controller.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Segnapunti - Controller</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/controller-main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -4,9 +4,7 @@
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "concurrently -n vite,server -c cyan,yellow \"npm run dev:vite\" \"npm run dev:server\"",
|
||||
"dev:vite": "vite --clearScreen false",
|
||||
"dev:server": "PORT=5173 node server.js",
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"start": "node server.js",
|
||||
@@ -25,4 +23,4 @@
|
||||
"vite": "^4.3.9",
|
||||
"vite-plugin-pwa": "^0.16.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
70
server.js
70
server.js
@@ -11,24 +11,72 @@ const __dirname = dirname(__filename)
|
||||
|
||||
// --- Configurazione del server ---
|
||||
|
||||
const app = express()
|
||||
const PORT = process.env.PORT || 3000
|
||||
const DISPLAY_PORT = process.env.PORT || 3000
|
||||
const CONTROLLER_PORT = process.env.CONTROLLER_PORT || 3001
|
||||
|
||||
// ========================================
|
||||
// Server Display (porta principale)
|
||||
// ========================================
|
||||
|
||||
const displayApp = express()
|
||||
|
||||
// Espone i file generati dalla build di Vite.
|
||||
app.use(express.static(join(__dirname, 'dist')))
|
||||
displayApp.use(express.static(join(__dirname, 'dist')))
|
||||
|
||||
// Fallback per SPA: restituisce `index.html` per tutte le route che non puntano a file statici.
|
||||
app.get('/{*splat}', (_req, res) => {
|
||||
// Fallback per SPA: restituisce `index.html` per tutte le route.
|
||||
displayApp.get('/{*splat}', (_req, res) => {
|
||||
res.sendFile(join(__dirname, 'dist', 'index.html'))
|
||||
})
|
||||
|
||||
const server = createServer(app)
|
||||
const displayServer = createServer(displayApp)
|
||||
|
||||
// Inizializza il server WebSocket con la logica di gioco.
|
||||
const wss = new WebSocketServer({ server })
|
||||
// Inizializza il server WebSocket condiviso.
|
||||
const wss = new WebSocketServer({ noServer: true })
|
||||
setupWebSocketHandler(wss)
|
||||
|
||||
// Avvia il server HTTP.
|
||||
server.listen(PORT, '0.0.0.0', () => {
|
||||
printServerInfo(PORT)
|
||||
displayServer.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 {
|
||||
socket.destroy()
|
||||
}
|
||||
})
|
||||
|
||||
displayServer.listen(DISPLAY_PORT, '0.0.0.0', () => {
|
||||
console.log(`[Display] Server running on port ${DISPLAY_PORT}`)
|
||||
})
|
||||
|
||||
// ========================================
|
||||
// Server Controller (porta separata)
|
||||
// ========================================
|
||||
|
||||
const controllerApp = express()
|
||||
|
||||
// Espone gli stessi file statici della build.
|
||||
controllerApp.use(express.static(join(__dirname, 'dist')))
|
||||
|
||||
// Fallback: restituisce `controller.html` per tutte le route.
|
||||
controllerApp.get('/{*splat}', (_req, res) => {
|
||||
res.sendFile(join(__dirname, 'dist', 'controller.html'))
|
||||
})
|
||||
|
||||
const controllerServer = createServer(controllerApp)
|
||||
|
||||
// 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 {
|
||||
socket.destroy()
|
||||
}
|
||||
})
|
||||
|
||||
controllerServer.listen(CONTROLLER_PORT, '0.0.0.0', () => {
|
||||
printServerInfo(DISPLAY_PORT, CONTROLLER_PORT)
|
||||
})
|
||||
|
||||
@@ -308,7 +308,7 @@ export default {
|
||||
|
||||
this.isConnecting = true
|
||||
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:'
|
||||
const wsUrl = `${protocol}//${location.host}`
|
||||
const wsUrl = `${protocol}//${location.host}/ws`
|
||||
|
||||
try {
|
||||
this.ws = new WebSocket(wsUrl)
|
||||
|
||||
@@ -221,7 +221,7 @@ export default {
|
||||
|
||||
this.isConnecting = true
|
||||
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:'
|
||||
const wsUrl = `${protocol}//${location.host}`
|
||||
const wsUrl = `${protocol}//${location.host}/ws`
|
||||
|
||||
try {
|
||||
this.ws = new WebSocket(wsUrl)
|
||||
|
||||
9
src/controller-main.js
Normal file
9
src/controller-main.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import { createApp } from 'vue'
|
||||
import './style.css'
|
||||
import WaveUI from 'wave-ui'
|
||||
import 'wave-ui/dist/wave-ui.css'
|
||||
import ControllerPage from './components/ControllerPage.vue'
|
||||
|
||||
const app = createApp(ControllerPage)
|
||||
app.use(WaveUI)
|
||||
app.mount('#app')
|
||||
15
src/main.js
15
src/main.js
@@ -1,21 +1,12 @@
|
||||
import { createApp } from 'vue'
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import './style.css'
|
||||
import App from './App.vue'
|
||||
import WaveUI from 'wave-ui'
|
||||
import 'wave-ui/dist/wave-ui.css'
|
||||
import DisplayPage from './components/DisplayPage.vue'
|
||||
import ControllerPage from './components/ControllerPage.vue'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes: [
|
||||
{ path: '/', component: DisplayPage },
|
||||
{ path: '/controller', component: ControllerPage },
|
||||
],
|
||||
})
|
||||
|
||||
const app = createApp(App)
|
||||
app.use(router)
|
||||
// In modalità display-only, non serve il router.
|
||||
// Il display viene montato direttamente.
|
||||
const app = createApp(DisplayPage)
|
||||
app.use(WaveUI)
|
||||
app.mount('#app')
|
||||
|
||||
@@ -12,9 +12,9 @@ export function getNetworkIPs() {
|
||||
for (const net of nets[name]) {
|
||||
// Esclude loopback (127.0.0.1), indirizzi non IPv4 e bridge Docker (172.17.x.x, 172.18.x.x).
|
||||
if (net.family === 'IPv4' &&
|
||||
!net.internal &&
|
||||
!net.address.startsWith('172.17.') &&
|
||||
!net.address.startsWith('172.18.')) {
|
||||
!net.internal &&
|
||||
!net.address.startsWith('172.17.') &&
|
||||
!net.address.startsWith('172.18.')) {
|
||||
networkIPs.push(net.address)
|
||||
}
|
||||
}
|
||||
@@ -25,19 +25,20 @@ export function getNetworkIPs() {
|
||||
|
||||
/**
|
||||
* Stampa il riepilogo di avvio del server con gli URL di accesso.
|
||||
* @param {number} port - Porta sulla quale il server e in ascolto.
|
||||
* @param {number} displayPort - Porta del display.
|
||||
* @param {number} controllerPort - Porta del controller.
|
||||
*/
|
||||
export function printServerInfo(port = 5173) {
|
||||
export function printServerInfo(displayPort = 5173, controllerPort = 3001) {
|
||||
const networkIPs = getNetworkIPs()
|
||||
|
||||
console.log(`\nSegnapunti Server`)
|
||||
console.log(` Display: http://localhost:${port}/`)
|
||||
console.log(` Controller: http://localhost:${port}/controller`)
|
||||
console.log(` Display: http://localhost:${displayPort}/`)
|
||||
console.log(` Controller: http://localhost:${controllerPort}/`)
|
||||
|
||||
if (networkIPs.length > 0) {
|
||||
console.log(`\n Da dispositivi remoti:`)
|
||||
console.log(`\n Controller da dispositivi remoti:`)
|
||||
networkIPs.forEach(ip => {
|
||||
console.log(` http://${ip}:${port}/controller`)
|
||||
console.log(` http://${ip}:${controllerPort}/`)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { WebSocketServer } from 'ws'
|
||||
import { createServer as createHttpServer, request as httpRequest } from 'http'
|
||||
import { setupWebSocketHandler } from './src/websocket-handler.js'
|
||||
import { printServerInfo } from './src/server-utils.js'
|
||||
|
||||
const CONTROLLER_PORT = 3001
|
||||
|
||||
/**
|
||||
* Plugin Vite che integra un server WebSocket per la gestione dello stato di gioco.
|
||||
* 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() {
|
||||
@@ -11,29 +15,116 @@ export default function websocketPlugin() {
|
||||
name: 'vite-plugin-websocket',
|
||||
configureServer(server) {
|
||||
// Inizializza un server WebSocket collegato al server HTTP di Vite.
|
||||
// Importante: usa `noServer: true` per evitare conflitti con l'HMR 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.
|
||||
// Intercetta le richieste di upgrade WebSocket solo sul path /ws.
|
||||
server.httpServer.on('upgrade', (request, socket, head) => {
|
||||
// Se la richiesta non riguarda l'HMR di Vite (es. /@vite/client),
|
||||
// la gestisce il server WebSocket dell'applicazione.
|
||||
const pathname = new URL(request.url, `http://${request.headers.host}`).pathname
|
||||
|
||||
if (!pathname.startsWith('/@vite') && !pathname.startsWith('/@fs')) {
|
||||
if (pathname === '/ws') {
|
||||
wss.handleUpgrade(request, socket, head, (ws) => {
|
||||
wss.emit('connection', ws, request)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// Stampa le informazioni di accesso dopo l'avvio del server Vite.
|
||||
// Avvia un server separato per il controller sulla porta 3001.
|
||||
server.httpServer.once('listening', () => {
|
||||
setTimeout(() => printServerInfo(5173), 100)
|
||||
const viteAddr = server.httpServer.address()
|
||||
const vitePort = viteAddr.port
|
||||
|
||||
startControllerDevServer(vitePort, wss)
|
||||
|
||||
setTimeout(() => printServerInfo(vitePort, CONTROLLER_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: 'localhost',
|
||||
port: vitePort,
|
||||
path: targetPath,
|
||||
method: req.method,
|
||||
headers: {
|
||||
...req.headers,
|
||||
host: `localhost:${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: 'localhost',
|
||||
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}`)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,12 +1,26 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import { resolve, dirname } from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import { VitePWA } from 'vite-plugin-pwa'
|
||||
import websocketPlugin from './vite-plugin-websocket.js'
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||
|
||||
// Configurazione principale di Vite
|
||||
export default defineConfig({
|
||||
base: '/',
|
||||
build: {
|
||||
rollupOptions: {
|
||||
input: {
|
||||
main: resolve(__dirname, 'index.html'),
|
||||
controller: resolve(__dirname, 'controller.html'),
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
vue(),
|
||||
websocketPlugin(),
|
||||
VitePWA({
|
||||
registerType: 'autoUpdate',
|
||||
manifest: {
|
||||
@@ -35,12 +49,5 @@ export default defineConfig({
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
port: 5173,
|
||||
proxy: {
|
||||
'/': {
|
||||
target: 'http://127.0.0.1:5174',
|
||||
ws: true,
|
||||
changeOrigin: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user