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",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "concurrently -n vite,server -c cyan,yellow \"npm run dev:vite\" \"npm run dev:server\"",
|
"dev": "vite",
|
||||||
"dev:vite": "vite --clearScreen false",
|
|
||||||
"dev:server": "PORT=5173 node server.js",
|
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"start": "node server.js",
|
"start": "node server.js",
|
||||||
@@ -25,4 +23,4 @@
|
|||||||
"vite": "^4.3.9",
|
"vite": "^4.3.9",
|
||||||
"vite-plugin-pwa": "^0.16.0"
|
"vite-plugin-pwa": "^0.16.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
70
server.js
70
server.js
@@ -11,24 +11,72 @@ const __dirname = dirname(__filename)
|
|||||||
|
|
||||||
// --- Configurazione del server ---
|
// --- Configurazione del server ---
|
||||||
|
|
||||||
const app = express()
|
const DISPLAY_PORT = process.env.PORT || 3000
|
||||||
const 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.
|
// 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.
|
// Fallback per SPA: restituisce `index.html` per tutte le route.
|
||||||
app.get('/{*splat}', (_req, res) => {
|
displayApp.get('/{*splat}', (_req, res) => {
|
||||||
res.sendFile(join(__dirname, 'dist', 'index.html'))
|
res.sendFile(join(__dirname, 'dist', 'index.html'))
|
||||||
})
|
})
|
||||||
|
|
||||||
const server = createServer(app)
|
const displayServer = createServer(displayApp)
|
||||||
|
|
||||||
// Inizializza il server WebSocket con la logica di gioco.
|
// Inizializza il server WebSocket condiviso.
|
||||||
const wss = new WebSocketServer({ server })
|
const wss = new WebSocketServer({ noServer: true })
|
||||||
setupWebSocketHandler(wss)
|
setupWebSocketHandler(wss)
|
||||||
|
|
||||||
// Avvia il server HTTP.
|
displayServer.on('upgrade', (request, socket, head) => {
|
||||||
server.listen(PORT, '0.0.0.0', () => {
|
const pathname = new URL(request.url, `http://${request.headers.host}`).pathname
|
||||||
printServerInfo(PORT)
|
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
|
this.isConnecting = true
|
||||||
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:'
|
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:'
|
||||||
const wsUrl = `${protocol}//${location.host}`
|
const wsUrl = `${protocol}//${location.host}/ws`
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.ws = new WebSocket(wsUrl)
|
this.ws = new WebSocket(wsUrl)
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ export default {
|
|||||||
|
|
||||||
this.isConnecting = true
|
this.isConnecting = true
|
||||||
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:'
|
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:'
|
||||||
const wsUrl = `${protocol}//${location.host}`
|
const wsUrl = `${protocol}//${location.host}/ws`
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.ws = new WebSocket(wsUrl)
|
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 { createApp } from 'vue'
|
||||||
import { createRouter, createWebHistory } from 'vue-router'
|
|
||||||
import './style.css'
|
import './style.css'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import WaveUI from 'wave-ui'
|
import WaveUI from 'wave-ui'
|
||||||
import 'wave-ui/dist/wave-ui.css'
|
import 'wave-ui/dist/wave-ui.css'
|
||||||
import DisplayPage from './components/DisplayPage.vue'
|
import DisplayPage from './components/DisplayPage.vue'
|
||||||
import ControllerPage from './components/ControllerPage.vue'
|
|
||||||
|
|
||||||
const router = createRouter({
|
// In modalità display-only, non serve il router.
|
||||||
history: createWebHistory(),
|
// Il display viene montato direttamente.
|
||||||
routes: [
|
const app = createApp(DisplayPage)
|
||||||
{ path: '/', component: DisplayPage },
|
|
||||||
{ path: '/controller', component: ControllerPage },
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
const app = createApp(App)
|
|
||||||
app.use(router)
|
|
||||||
app.use(WaveUI)
|
app.use(WaveUI)
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ export function getNetworkIPs() {
|
|||||||
for (const net of nets[name]) {
|
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).
|
// Esclude loopback (127.0.0.1), indirizzi non IPv4 e bridge Docker (172.17.x.x, 172.18.x.x).
|
||||||
if (net.family === 'IPv4' &&
|
if (net.family === 'IPv4' &&
|
||||||
!net.internal &&
|
!net.internal &&
|
||||||
!net.address.startsWith('172.17.') &&
|
!net.address.startsWith('172.17.') &&
|
||||||
!net.address.startsWith('172.18.')) {
|
!net.address.startsWith('172.18.')) {
|
||||||
networkIPs.push(net.address)
|
networkIPs.push(net.address)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,19 +25,20 @@ export function getNetworkIPs() {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Stampa il riepilogo di avvio del server con gli URL di accesso.
|
* 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()
|
const networkIPs = getNetworkIPs()
|
||||||
|
|
||||||
console.log(`\nSegnapunti Server`)
|
console.log(`\nSegnapunti Server`)
|
||||||
console.log(` Display: http://localhost:${port}/`)
|
console.log(` Display: http://localhost:${displayPort}/`)
|
||||||
console.log(` Controller: http://localhost:${port}/controller`)
|
console.log(` Controller: http://localhost:${controllerPort}/`)
|
||||||
|
|
||||||
if (networkIPs.length > 0) {
|
if (networkIPs.length > 0) {
|
||||||
console.log(`\n Da dispositivi remoti:`)
|
console.log(`\n Controller da dispositivi remoti:`)
|
||||||
networkIPs.forEach(ip => {
|
networkIPs.forEach(ip => {
|
||||||
console.log(` http://${ip}:${port}/controller`)
|
console.log(` http://${ip}:${controllerPort}/`)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
import { WebSocketServer } from 'ws'
|
import { WebSocketServer } from 'ws'
|
||||||
|
import { createServer as createHttpServer, request as httpRequest } from 'http'
|
||||||
import { setupWebSocketHandler } from './src/websocket-handler.js'
|
import { setupWebSocketHandler } from './src/websocket-handler.js'
|
||||||
import { printServerInfo } from './src/server-utils.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}
|
* @returns {import('vite').Plugin}
|
||||||
*/
|
*/
|
||||||
export default function websocketPlugin() {
|
export default function websocketPlugin() {
|
||||||
@@ -11,29 +15,116 @@ export default function websocketPlugin() {
|
|||||||
name: 'vite-plugin-websocket',
|
name: 'vite-plugin-websocket',
|
||||||
configureServer(server) {
|
configureServer(server) {
|
||||||
// Inizializza un server WebSocket collegato al server HTTP di Vite.
|
// 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 })
|
const wss = new WebSocketServer({ noServer: true })
|
||||||
|
|
||||||
// Registra i gestori WebSocket con la logica di gioco.
|
// Registra i gestori WebSocket con la logica di gioco.
|
||||||
setupWebSocketHandler(wss)
|
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) => {
|
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
|
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.handleUpgrade(request, socket, head, (ws) => {
|
||||||
wss.emit('connection', ws, request)
|
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', () => {
|
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 { defineConfig } from 'vite'
|
||||||
|
import { resolve, dirname } from 'path'
|
||||||
|
import { fileURLToPath } from 'url'
|
||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue'
|
||||||
import { VitePWA } from 'vite-plugin-pwa'
|
import { VitePWA } from 'vite-plugin-pwa'
|
||||||
|
import websocketPlugin from './vite-plugin-websocket.js'
|
||||||
|
|
||||||
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||||
|
|
||||||
// Configurazione principale di Vite
|
// Configurazione principale di Vite
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
base: '/',
|
base: '/',
|
||||||
|
build: {
|
||||||
|
rollupOptions: {
|
||||||
|
input: {
|
||||||
|
main: resolve(__dirname, 'index.html'),
|
||||||
|
controller: resolve(__dirname, 'controller.html'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
vue(),
|
vue(),
|
||||||
|
websocketPlugin(),
|
||||||
VitePWA({
|
VitePWA({
|
||||||
registerType: 'autoUpdate',
|
registerType: 'autoUpdate',
|
||||||
manifest: {
|
manifest: {
|
||||||
@@ -35,12 +49,5 @@ export default defineConfig({
|
|||||||
server: {
|
server: {
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0',
|
||||||
port: 5173,
|
port: 5173,
|
||||||
proxy: {
|
|
||||||
'/': {
|
|
||||||
target: 'http://127.0.0.1:5174',
|
|
||||||
ws: true,
|
|
||||||
changeOrigin: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user