working as intended

This commit is contained in:
Maximilian Wagner
2025-12-26 02:50:34 +01:00
parent 6fc4ee2716
commit 8dcf9fba11
5 changed files with 110 additions and 15 deletions

View File

@@ -5,11 +5,13 @@ import (
"errors" "errors"
"fmt" "fmt"
"os" "os"
"io/fs"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"strings" "strings"
"time" "time"
"git.noctra.dev/noctra/servtex/globals" "git.noctra.dev/noctra/servtex/globals"
"github.com/fsnotify/fsnotify"
) )
// Returns the current time in the timezone specified in the config file // Returns the current time in the timezone specified in the config file
@@ -111,15 +113,89 @@ func ConfigReader(configFileName string, configOptionStorage *globals.Config) er
return err return err
} }
// Contents copied from https://github.com/fsnotify/fsnotify/blob/main/cmd/fsnotify/watch.go
func watchLoop(watcher *fsnotify.Watcher, callOnChange func() error) {
defer watcher.Close()
for {
select {
// Errors
case err, ok := <-watcher.Errors:
if !ok {
LogLine(fmt.Sprintf("Watcher crashed on error, manual compile only: %s", err), 4)
return
}
LogLine(fmt.Sprintf("Watcher Error: %s", err), 3)
// Events
case event, ok := <-watcher.Events:
if !ok {
LogLine(fmt.Sprintf("Watcher crashed on event, manual compile only: %s", event), 4)
return
}
LogLine(fmt.Sprintf("File change noticed: %s", event.Name), 2)
err := callOnChange()
if err != nil {
LogLine(fmt.Sprintf("Latex compilation failed: %s", err), 4)
}
if err = watcher.Add(event.Name); err != nil {
LogLine(fmt.Sprintf("File could not be re-added to watcher: %s", event.Name), 4)
}
}
}
}
func texFinder(path string) []string {
var texFiles []string
err := filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if !d.IsDir() && filepath.Ext(path) == ".tex" {
texFiles = append(texFiles, path)
}
return nil
})
if err != nil {
LogLine("Some paths could not be traversed while search for .tex files", 3)
}
return texFiles
}
// Watches a specified directory and calls a function on change // Watches a specified directory and calls a function on change
// //
// Optionally, a minimum time to wait for consecutive changes can be specified // Optionally, a minimum time to wait for consecutive changes can be specified
func ChangeWatch(path string, callOnChange func()) { func ChangeWatch(path string, callOnChange func() error) {
LogLine("File change noticed", 2) watcher, err := fsnotify.NewWatcher()
if err != nil {
LogLine("File system watcher could not be initialised, manual compile only", 4)
return
}
for _, file := range texFinder(path) {
if err = watcher.Add(file); err != nil {
LogLine(fmt.Sprintf("File could not be added to watcher: %s", err), 4)
}
LogLine(fmt.Sprintf("File added to watcher: %s", file), 1)
}
go watchLoop(watcher, callOnChange)
LogLine("Watcher initialised", 1)
}
func updateExecutionTimestamp(execution *globals.LatexExecution) {
execution.Timestamp = GetLocalTimePretty()
execution.TimestampRFC = GetLocalTimeRFC()
} }
// Intended to be run as goroutine // Intended to be run as goroutine
func LatexCompile(config globals.Config, execution *globals.LatexExecution) error { func LatexCompile() error {
execution := &globals.LatexExec
config := globals.AppConfig
if execution.ExecutionLock.TryLock() { if execution.ExecutionLock.TryLock() {
defer execution.ExecutionLock.Unlock() defer execution.ExecutionLock.Unlock()
} else { } else {
@@ -143,7 +219,8 @@ func LatexCompile(config globals.Config, execution *globals.LatexExecution) erro
default: default:
execution.ExecutionState = "Unknown Latex Engine" execution.ExecutionState = "Unknown Latex Engine"
execution.Output = []byte{} execution.Output = []byte{}
execution.Timestamp = GetLocalTimePretty() updateExecutionTimestamp(execution)
LogLine("Unsupported LaTeX Engine", 4) LogLine("Unsupported LaTeX Engine", 4)
return errors.New("Unsupported LaTeX Engine") return errors.New("Unsupported LaTeX Engine")
} }
@@ -156,8 +233,9 @@ func LatexCompile(config globals.Config, execution *globals.LatexExecution) erro
} }
execution.Output = stdout execution.Output = stdout
execution.Timestamp = GetLocalTimePretty()
execution.ExecutionState = "Done" execution.ExecutionState = "Done"
updateExecutionTimestamp(execution)
LogLine("LaTeX execution finished", 2) LogLine("LaTeX execution finished", 2)
return nil return nil
} }

View File

@@ -77,16 +77,17 @@ func SSEventHandler(writer http.ResponseWriter, request *http.Request) {
writer.Header().Set("Content-Type", "text/event-stream") writer.Header().Set("Content-Type", "text/event-stream")
writer.Header().Set("Cache-Control", "no-cache") writer.Header().Set("Cache-Control", "no-cache")
writer.Header().Set("Connection", "keep-alive") writer.Header().Set("Connection", "keep-alive")
ssePing(&writer)
lastExecution := "startup" lastExecution := "startup"
for range time.Tick(time.Second) { for range time.Tick(time.Second) {
if lastExecution == globals.LatexExec.Timestamp { if lastExecution == globals.LatexExec.TimestampRFC {
ssePing(&writer) ssePing(&writer)
} else { } else {
sseStatusSend(&writer) sseStatusSend(&writer)
ssePDFSend(&writer) ssePDFSend(&writer)
sseOutputSend(&writer) sseOutputSend(&writer)
// comment out for testing purposes lastExecution = globals.LatexExec.TimestampRFC
lastExecution = globals.LatexExec.Timestamp
} }
} }
} }
@@ -114,6 +115,6 @@ func MainHandler(writer http.ResponseWriter, request *http.Request) {
} }
func PDFCompile(writer http.ResponseWriter, request *http.Request) { func PDFCompile(writer http.ResponseWriter, request *http.Request) {
backend.LatexCompile(globals.AppConfig, &globals.LatexExec) backend.LatexCompile()
} }

View File

@@ -32,6 +32,7 @@ type LatexExecution struct {
ExecutionLock sync.Mutex ExecutionLock sync.Mutex
ExecutionState string ExecutionState string
Timestamp string Timestamp string
TimestampRFC string
Output []byte Output []byte
} }
var LatexExec LatexExecution var LatexExec LatexExecution

23
main.go
View File

@@ -1,14 +1,16 @@
package main package main
import ( import (
"os"
"os/signal"
"syscall"
"io/fs"
"context" "context"
"fmt" "fmt"
"time" "io/fs"
"net/http" "net/http"
"os"
"os/signal"
"path/filepath"
"syscall"
"time"
"git.noctra.dev/noctra/servtex/backend" "git.noctra.dev/noctra/servtex/backend"
"git.noctra.dev/noctra/servtex/frontend" "git.noctra.dev/noctra/servtex/frontend"
"git.noctra.dev/noctra/servtex/globals" "git.noctra.dev/noctra/servtex/globals"
@@ -49,6 +51,15 @@ func main() {
if globals.AppConfig.WebserverSecure { if globals.AppConfig.WebserverSecure {
go serverSecure.ListenAndServeTLS(globals.AppConfig.CertificatePath, globals.AppConfig.CertificateKeyPath) go serverSecure.ListenAndServeTLS(globals.AppConfig.CertificatePath, globals.AppConfig.CertificateKeyPath)
} }
// file system change watch
absTexPath, err := filepath.Abs(globals.AppConfig.LatexSourceFilePath)
if err == nil {
backend.ChangeWatch(filepath.Dir(absTexPath), backend.LatexCompile)
} else {
backend.LogLine("Absolute TeX file path could not be gotten", 4)
}
backend.LogLine("Started", 2) backend.LogLine("Started", 2)
fmt.Println("Press CTRL-C to Exit ServTeX") fmt.Println("Press CTRL-C to Exit ServTeX")
@@ -61,7 +72,7 @@ func main() {
fmt.Print("\r") fmt.Print("\r")
// shutdown // shutdown
context, cancel := context.WithTimeout(context.Background(), 15*time.Second) context, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel() defer cancel()
if err = server.Shutdown(context); err != nil { if err = server.Shutdown(context); err != nil {
backend.LogLine("Graceful Shutdown failed", 4) backend.LogLine("Graceful Shutdown failed", 4)

View File

@@ -1,4 +1,8 @@
\documentclass{article} \documentclass{article}
\begin{document} \begin{document}
(This is an Example Document) (This is an Example Document)
Filewatcher Test Document
Filewatcher Test Document
Filewatcher Test Document
Filewatcher Test Document
\end{document} \end{document}