3
SSE
fmatte edited this page 2025-09-02 01:34:29 -04:00
./server/index.js
import command from './admin/command/command.js'
server.use(servu.get('/admin/command/', redirect('/admin/command')))
server.use(servu.get('/admin/command', command.index))
server.use(servu.get('/admin/command/run/:id', command.run))
./admin/command/command.js
import { nameChanged } from '#comico/commands/healthNameChanged'
import { infoCount } from '#comico/commands/infoCount'
import { importFromLibraries } from '#comico/commands/importFromLibraries'
const commands = {
1: { name: "Info: Count", func: infoCount },
2: { name: "Health: Name Changed", func: nameChanged },
3: { name: "Import", func: importFromLibraries }
}
const index = async (context) => {
const list = Object.keys(commands).map(id => ({ id, name: commands[id].name }))
await context.html(import.meta.dirname + '/command.mustache', { list, url: context.req.url }, {})
}
const run = async (context) => {
const { id } = context.req.params
const command = commands[id]
await context.sse(async (log) => {
log({ event: 'start', data: command.name })
await comico.command(command.func, (data) => log({ event: 'log', data }))
log({ event: 'stop', data: command.name })
})
}
export default { index, run }
./server/context.js
const context = (req, res) => {
return {
sse: async (callback) => {
res.setHeader('Content-Type', 'text/event-stream')
res.setHeader('Cache-Control', 'no-cache')
res.setHeader('Connection', 'keep-alive')
await callback((log) => {
res.write(`event: ${log.event}\ndata:${log.data}\n\n`)
})
res.end()
}
}
}
./comico/commands/infoCount.js
const infoCount = async (comico, event = () => { }) => {
event(comico.comicsRepo.values.length + ' comics')
}
export { infoCount }
./server/admin/command/command.mustache
<style>
#commands {
display: flex;
flex-wrap: wrap;
gap: 1em;
}
button {
padding: 5px;
}
#result {
margin-block: 1em;
width: 100%;
height: 45vh;
}
</style>
<main id="container">
<div id="commands">
{{#list}}
<button data-id="{{id}}">{{name}}</button>
{{/list}}
</div>
<textarea id="result"></textarea>
</main>
<script type="module">
const result = document.getElementById('result')
const commands = document.getElementById('commands')
const clear = () => {
result.value = ''
}
const add = (msg) => {
result.value += msg + '\n'
}
const disabled = () => {
commands.querySelectorAll('button').forEach(b => b.disabled = true)
}
const enabled = () => {
commands.querySelectorAll('button').forEach(b => b.disabled = false)
}
const commandOnClick = (evt) => {
const { target } = evt
const { id } = target.dataset
disabled()
clear()
const url = `{{{url}}}/run/${id}`
const eventSource = new EventSource(url)
eventSource.addEventListener('start', (e) => {
add('Running: ' + e.data)
})
eventSource.addEventListener('log', (e) => {
add(e.data)
})
eventSource.addEventListener('stop', (e) => {
add('DONE')
eventSource.close()
enabled()
})
eventSource.onerror = function (event) {
console.log('Error occurred:', event)
eventSource.close()
enabled()
}
}
commands.querySelectorAll('button').forEach(button => button.addEventListener('click', commandOnClick))
clear()
</script>