SSE in working state

This commit is contained in:
Maximilian Wagner
2025-12-26 01:37:23 +01:00
parent aa76d9c721
commit a6214e022f
11 changed files with 116 additions and 71 deletions

View File

@@ -3,6 +3,7 @@
}
.left-sidebar {
padding: 15px;
height: 100%;
}
.pdf-frame {
width: 100%;

View File

@@ -12,26 +12,23 @@
<body>
<div class="container-fluid h-100 d-flex flex-column">
<div class="container-fluid h-100 d-flex flex-column" hx-ext="sse" sse-connect="/sse">
<div class="row output flex-grow-1">
<div class="col-3 left-sidebar" hx-sse="connect:/sse on:status" hx-swap="innerHTML">
Status <br>
Last Compilation <br>
Such Info <br>
Much wow
<div class="col-3 left-sidebar d-flex flex-column justify-content-between">
<div>
<h4>ServTeX Status</h4> <br>
<div sse-swap="status">
Compilation has not run yet.
</div>
</div>
<br>
<button class="btn btn-primary mt-2" hx-post="/compile" hx-trigger="click" hx-swap="none">Recompile</button>
</div>
<div class="col-9 p-0">
<iframe class="pdf-frame" src="/pdf?ts=0" hx-sse="connect:/sse on:pdf" hx-swap="outerHTML"></iframe>
<div class="col-9 p-0" sse-swap="pdf">
<iframe class="pdf-frame" src="/pdf?ts=0"></iframe>
</div>
</div>
<div class="command-out" hx-sse="connect:/sse on:output" hx-swap="innerHTML">
compile start<br>
compile file-x<br>
compile file-y<br>
example output<br>
warning but who cares<br>
finished compilation<br>
output file created<br>
<div class="command-out" sse-swap="output">
</div>
</div>
</div>

View File

@@ -1,12 +1,15 @@
package frontend
import (
"fmt"
"time"
"embed"
"strings"
"fmt"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
"time"
"git.noctra.dev/noctra/servtex/backend"
"git.noctra.dev/noctra/servtex/globals"
)
@@ -18,17 +21,8 @@ import (
//go:embed templates/main.html
var WebFiles embed.FS
// Adds necessary Headers for SSE
func sseHeadersAdd(writer *http.ResponseWriter) {
(*writer).Header().Set("Content-Type", "text/event-stream")
(*writer).Header().Set("Cache-Control", "no-cache")
(*writer).Header().Set("Connection", "keep-alive")
}
// Sends a Ping to keep the connection alive
func ssePing(writer *http.ResponseWriter) {
sseHeadersAdd(writer)
fmt.Fprintf((*writer), ": ping\n\n")
(*writer).(http.Flusher).Flush()
@@ -38,52 +32,52 @@ func ssePing(writer *http.ResponseWriter) {
//
// Reads globals.LatexExec
func sseStatusSend(writer *http.ResponseWriter) {
sseHeadersAdd(writer)
fmt.Fprintf((*writer), "event: status\n")
fmt.Fprintf((*writer), "data: Execution Time: %s\n", globals.LatexExec.Timestamp)
fmt.Fprintf((*writer), "data: Execution State: %s\n\n", globals.LatexExec.ExecutionState)
fmt.Fprintf((*writer), "data: Execution Time: %s<br>\n", globals.LatexExec.Timestamp)
fmt.Fprintf((*writer), "data: Execution State: %s<br>\n\n", globals.LatexExec.ExecutionState)
(*writer).(http.Flusher).Flush()
backend.LogLine("Status Event has been sent", 1)
}
// Sends new Event
//
// Reads globals.LatexExec
func ssePDFSend(writer *http.ResponseWriter) {
sseHeadersAdd(writer)
fmt.Fprintf((*writer), "event: pdf\n")
iframe := fmt.Sprintf(
`<iframe class="pdf-frame" src="servetex.pdf?ts=%s" hx-sse="connect:/sse/pdf" hx-swap="outerHTML"></iframe>`,
`<iframe class="pdf-frame" src="/pdf?ts=%s"></iframe>`,
url.QueryEscape(backend.GetLocalTimeRFC()),
)
fmt.Fprintf((*writer), "data: %s\n\n", iframe)
(*writer).(http.Flusher).Flush()
backend.LogLine("PDF Event has been sent", 1)
}
// Sends new Event
//
// Reads globals.LatexExec
func sseOutputSend(writer *http.ResponseWriter) {
sseHeadersAdd(writer)
fmt.Fprintf((*writer), "event: output\n")
lines := strings.SplitSeq(string(globals.LatexExec.Output), "\n")
for line := range lines {
fmt.Fprintf((*writer), "data: %s\n", line)
fmt.Fprintf((*writer), "data: %s<br>\n", line)
}
fmt.Fprintf((*writer), "\n")
(*writer).(http.Flusher).Flush()
backend.LogLine("Output Event has been sent", 1)
}
// Server Side Event Handler
//
// Sends a Ping instead of actual data when no new data available to save bandwidth
func SSEventHandler(writer http.ResponseWriter, request *http.Request) {
lastExecution := ""
writer.Header().Set("Content-Type", "text/event-stream")
writer.Header().Set("Cache-Control", "no-cache")
writer.Header().Set("Connection", "keep-alive")
lastExecution := "startup"
for range time.Tick(time.Second) {
if lastExecution == globals.LatexExec.Timestamp {
ssePing(&writer)
@@ -97,9 +91,19 @@ func SSEventHandler(writer http.ResponseWriter, request *http.Request) {
}
}
// Serves the compiled PDF file
func PDFHandler(writer http.ResponseWriter, request *http.Request) {
http.NotFound(writer, request)
pdfPath := filepath.Join(globals.AppConfig.LatexOutputPath, "servtex.pdf")
pdf, err := os.Open(pdfPath)
if err != nil {
backend.LogLine("PDF file could not be found or read", 1)
http.NotFound(writer, request)
return
}
defer pdf.Close()
writer.Header().Set("Content-Type", "application/pdf")
http.ServeFile(writer, request, pdfPath)
}
// Serves the main page of ServTeX
@@ -109,3 +113,7 @@ func MainHandler(writer http.ResponseWriter, request *http.Request) {
writer.Write(main)
}
func PDFCompile(writer http.ResponseWriter, request *http.Request) {
backend.LatexCompile(globals.AppConfig, &globals.LatexExec)
}