#!/usr/bin/env node
/**
* Mini web UI example for hyper-deploy
*
* Usage:
* node examples/web.js <dir> [--port 9090] [--topic <hex>]
*
* Starts a deploy server and an HTTP dashboard showing live status.
*/
import http from 'http'
import { DeployServer } from '../index.js'
const args = process.argv.slice(2)
const dir = args[0]
if (!dir) { console.error('Usage: web.js <dir> [--port N]'); process.exit(1) }
let port = 9090
let topic
for (let i = 1; i < args.length; i++) {
if (args[i] === '--port' && args[i + 1]) port = parseInt(args[++i])
if (args[i] === '--topic' && args[i + 1]) topic = Buffer.from(args[++i], 'hex')
}
function fmt (b) {
if (b < 1024) return b + ' B'
if (b < 1048576) return (b / 1024).toFixed(1) + ' KB'
if (b < 1073741824) return (b / 1048576).toFixed(1) + ' MB'
return (b / 1073741824).toFixed(2) + ' GB'
}
const server = new DeployServer(dir, { topic })
const sseClients = new Set()
function broadcast (event, data) {
const msg = 'event: ' + event + '\ndata: ' + JSON.stringify(data) + '\n\n'
for (const res of sseClients) {
try { res.write(msg) } catch { sseClients.delete(res) }
}
}
server.on('peer', (info) => broadcast('peer', info))
server.on('progress', (info) => broadcast('progress', info))
function getStatus () {
return {
dir: server.dir,
topic: server.topicHex,
files: server.manifest ? server.manifest.files.length : 0,
totalSize: server.manifest ? fmt(server.manifest.totalSize) : '0',
connections: server.connections,
filesServed: server.stats.filesServed,
bytesServed: fmt(server.stats.bytesServed)
}
}
const httpServer = http.createServer((req, res) => {
const url = req.url.split('?')[0]
if (url === '/api/status') {
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify(getStatus()))
return
}
if (url === '/events') {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
Connection: 'keep-alive'
})
res.write('data: ' + JSON.stringify(getStatus()) + '\n\n')
sseClients.add(res)
req.on('close', () => sseClients.delete(res))
return
}
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' })
res.end(`<!DOCTYPE html>
<html><head><title>hyper-deploy</title>
<style>
body { font-family: monospace; background: #0d1117; color: #c9d1d9; padding: 2rem; max-width: 640px; margin: 0 auto; }
h1 { color: #58a6ff; }
.card { background: #161b22; border: 1px solid #30363d; border-radius: 6px; padding: 1rem; margin: 1rem 0; }
.row { display: flex; justify-content: space-between; padding: 0.3rem 0; }
.label { color: #8b949e; }
.topic { font-size: 0.75rem; word-break: break-all; color: #79c0ff; }
#log { max-height: 300px; overflow-y: auto; font-size: 0.8rem; color: #8b949e; }
#log div { padding: 2px 0; border-bottom: 1px solid #21262d; }
</style></head><body>
<h1>hyper-deploy</h1>
<div class="card">
<div class="topic" id="topic">...</div>
</div>
<div class="card">
<div class="row"><span class="label">Files</span><span id="files">-</span></div>
<div class="row"><span class="label">Size</span><span id="size">-</span></div>
<div class="row"><span class="label">Peers</span><span id="peers">0</span></div>
<div class="row"><span class="label">Served</span><span id="served">0 files</span></div>
<div class="row"><span class="label">Bytes</span><span id="bytes">0 B</span></div>
</div>
<div class="card"><h3 style="color:#8b949e;margin:0 0 0.5rem">Log</h3><div id="log"></div></div>
<script>
const $ = id => document.getElementById(id)
function log(msg) {
const d = document.createElement('div'); d.textContent = msg
$('log').prepend(d)
while ($('log').children.length > 100) $('log').lastChild.remove()
}
function update(d) {
if (d.topic) $('topic').textContent = d.topic
if (d.files !== undefined) $('files').textContent = d.files
if (d.totalSize) $('size').textContent = d.totalSize
if (d.connections !== undefined) $('peers').textContent = d.connections
if (d.filesServed !== undefined) $('served').textContent = d.filesServed + ' files'
if (d.bytesServed) $('bytes').textContent = d.bytesServed
}
const es = new EventSource('/events')
es.onmessage = e => update(JSON.parse(e.data))
es.addEventListener('peer', e => { const d = JSON.parse(e.data); log('Peer ' + d.key + ': ' + d.state) })
es.addEventListener('progress', e => { const d = JSON.parse(e.data); if (d.file) log('sent ' + d.file) })
</script></body></html>`)
})
server.ready().then(() => {
httpServer.listen(port, () => {
console.log('Serving: %s', server.dir)
console.log('Topic: %s', server.topicHex)
console.log('Files: %d (%s)', server.manifest.files.length, fmt(server.manifest.totalSize))
console.log('Web UI: http://localhost:%d', port)
})
}).catch((err) => { console.error(err.message); process.exit(1) })