logger, compiler logic, sse endpoints
This commit is contained in:
@@ -2,26 +2,45 @@ package backend
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"git.noctra.dev/noctra/servtex/globals"
|
"git.noctra.dev/noctra/servtex/globals"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func getLocalTime() time.Time {
|
||||||
|
timezone, _ := time.LoadLocation(globals.AppConfig.Timezone)
|
||||||
|
return time.Now().In(timezone)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLocalTimePretty() string {
|
||||||
|
return getLocalTime().Format("02.01.2006 15:04")
|
||||||
|
}
|
||||||
|
|
||||||
|
func LogLine(message string) {
|
||||||
|
logline := "\n" + getLocalTime().Format(time.RFC3339) + " - " + message
|
||||||
|
globals.LogFile.WriteString(logline)
|
||||||
|
}
|
||||||
|
|
||||||
// Helper for configReader() that does the actual reading
|
// Helper for configReader() that does the actual reading
|
||||||
|
// Also validates the populated fields
|
||||||
func configReaderParse(filePath string, configOptionStorage *globals.Config) error {
|
func configReaderParse(filePath string, configOptionStorage *globals.Config) error {
|
||||||
jsonData, err := os.ReadFile(filePath)
|
jsonData, err := os.ReadFile(filePath)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = json.Unmarshal(jsonData, &configOptionStorage)
|
err = json.Unmarshal(jsonData, &configOptionStorage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// log error unmarshal failure
|
|
||||||
return errors.New("Config file could not be read")
|
return errors.New("Config file could not be read")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return errors.New("Config file does not exist")
|
return errors.New("Config file does not exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return configValidator(*configOptionStorage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func configValidator(config globals.Config) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,34 +67,52 @@ func ConfigReader(configFileName string, configOptionStorage *globals.Config) er
|
|||||||
err = configReaderParse(path, configOptionStorage)
|
err = configReaderParse(path, configOptionStorage)
|
||||||
if err == nil { return nil }
|
if err == nil { return nil }
|
||||||
|
|
||||||
// log error no config file found
|
return err
|
||||||
return errors.New("Configuration file not found or json parsing error")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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()) {
|
||||||
|
LogLine("File change noticed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intended to be run as goroutine
|
// Intended to be run as goroutine
|
||||||
func LatexCompile(config *globals.Config) error {
|
func LatexCompile(config globals.Config, execution *globals.LatexExecution) error {
|
||||||
if !config.ExecutionLock.TryLock() {
|
if !execution.ExecutionLock.TryLock() {
|
||||||
|
LogLine("LaTeX execution already underway")
|
||||||
return errors.New("Execution already in progress")
|
return errors.New("Execution already in progress")
|
||||||
}
|
}
|
||||||
|
LogLine("LaTeX execution started")
|
||||||
|
execution.ExecutionState = "Started"
|
||||||
|
|
||||||
var latexCommand string
|
var latexCommand *exec.Cmd
|
||||||
switch config.LatexEngine {
|
switch config.LatexEngine {
|
||||||
case "lualatex":
|
case "lualatex":
|
||||||
latexCommand = fmt.Sprintf(
|
latexCommand = exec.Command(
|
||||||
"lualatex -output-directory=%s -jobname=servtex %s",
|
"lualatex",
|
||||||
config.LatexOutputPath,
|
"-output-directory=" + config.LatexOutputPath,
|
||||||
config.LatexSourceFilePath,
|
"-jobname=servtex",
|
||||||
)
|
config.LatexSourceFilePath,
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
execution.ExecutionState = "Unknown Latex Engine"
|
||||||
|
execution.Output = []byte{}
|
||||||
|
execution.Timestamp = getLocalTimePretty()
|
||||||
|
execution.ExecutionLock.Unlock()
|
||||||
|
return errors.New("Unknown Latex Engine")
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Sprintf(latexCommand) // placeholder for compilation
|
execution.ExecutionState = "Running"
|
||||||
config.ExecutionLock.Unlock()
|
stdout, err := latexCommand.Output()
|
||||||
|
if err != nil {
|
||||||
|
execution.ExecutionState = "Failed"
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
execution.Output = stdout
|
||||||
|
execution.Timestamp = getLocalTimePretty()
|
||||||
|
execution.ExecutionState = "Done"
|
||||||
|
execution.ExecutionLock.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,5 +9,6 @@
|
|||||||
"webserverDomain": "localhost:9876",
|
"webserverDomain": "localhost:9876",
|
||||||
"webserverSecure": false,
|
"webserverSecure": false,
|
||||||
"certificatePath": "",
|
"certificatePath": "",
|
||||||
"certificateKeyPath": ""
|
"certificateKeyPath": "",
|
||||||
|
"timezone": "Europe/Berlin"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<iframe class="pdf-frame" src="servetex.pdf?ts=0" hx-sse="connect:/sse/pdf" hx-swap="outerHTML"></iframe>
|
<iframe class="pdf-frame" src="servetex.pdf?ts=0" hx-sse="connect:/sse/pdf" hx-swap="outerHTML"></iframe>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="command-out">
|
<div class="command-out" hx-sse="connect:/sse/status" hx-swap="innerHTML">
|
||||||
compile start
|
compile start
|
||||||
compile file-x
|
compile file-x
|
||||||
compile file-y
|
compile file-y
|
||||||
|
|||||||
@@ -1,2 +1,32 @@
|
|||||||
package frontend
|
package frontend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"fmt"
|
||||||
|
"git.noctra.dev/noctra/servtex/globals"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Publishes Meta Information relating to last execution
|
||||||
|
// Does not use contained Mutex, ignore warning
|
||||||
|
func SSEStatus(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
writer.Header().Set("Content-Type", "text/event-stream")
|
||||||
|
writer.Header().Set("Cache-Control", "no-cache")
|
||||||
|
writer.Header().Set("Connection", "keep-alive")
|
||||||
|
|
||||||
|
fmt.Fprintf(writer, "data: Execution Time: %s\n", globals.LatexExec.Timestamp)
|
||||||
|
fmt.Fprintf(writer, "data: Execution State: %s\n\n", globals.LatexExec.ExecutionState)
|
||||||
|
|
||||||
|
writer.(http.Flusher).Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func SSEOutput(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
writer.Header().Set("Content-Type", "text/event-stream")
|
||||||
|
writer.Header().Set("Cache-Control", "no-cache")
|
||||||
|
writer.Header().Set("Connection", "keep-alive")
|
||||||
|
|
||||||
|
fmt.Fprintf(writer, "data: Execution Time: %s\n", globals.LatexExec.Timestamp)
|
||||||
|
fmt.Fprintf(writer, "data: Execution State: %s\n\n", globals.LatexExec.ExecutionState)
|
||||||
|
|
||||||
|
writer.(http.Flusher).Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package globals
|
package globals
|
||||||
|
|
||||||
import "sync"
|
import (
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
// general
|
// general
|
||||||
@@ -17,9 +20,18 @@ type Config struct {
|
|||||||
WebserverSecure bool `json:"webserverSecure"`
|
WebserverSecure bool `json:"webserverSecure"`
|
||||||
CertificatePath string `json:"certificatePath"`
|
CertificatePath string `json:"certificatePath"`
|
||||||
CertificateKeyPath string `json:"certificateKeyPath"`
|
CertificateKeyPath string `json:"certificateKeyPath"`
|
||||||
|
Timezone string `json:"timezone"`
|
||||||
// to prevent competing compiles, not actual configuration
|
|
||||||
ExecutionLock sync.Mutex
|
|
||||||
}
|
}
|
||||||
var AppConfig Config
|
var AppConfig Config
|
||||||
|
|
||||||
|
|
||||||
|
type LatexExecution struct {
|
||||||
|
ExecutionLock sync.Mutex
|
||||||
|
ExecutionState string
|
||||||
|
Timestamp string
|
||||||
|
Output []byte
|
||||||
|
}
|
||||||
|
var LatexExec LatexExecution
|
||||||
|
|
||||||
|
var LogFile *os.File
|
||||||
|
|
||||||
2
go.mod
2
go.mod
@@ -1,3 +1,5 @@
|
|||||||
module git.noctra.dev/noctra/servtex
|
module git.noctra.dev/noctra/servtex
|
||||||
|
|
||||||
go 1.25.5
|
go 1.25.5
|
||||||
|
|
||||||
|
require github.com/tmaxmax/go-sse v0.11.0
|
||||||
|
|||||||
2
go.sum
Normal file
2
go.sum
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
github.com/tmaxmax/go-sse v0.11.0 h1:nogmJM6rJUoOLoAwEKeQe5XlVpt9l7N82SS1jI7lWFg=
|
||||||
|
github.com/tmaxmax/go-sse v0.11.0/go.mod h1:u/2kZQR1tyngo1lKaNCj1mJmhXGZWS1Zs5yiSOD+Eg8=
|
||||||
8
main.go
8
main.go
@@ -2,7 +2,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
//"log/slog"
|
|
||||||
"git.noctra.dev/noctra/servtex/globals"
|
"git.noctra.dev/noctra/servtex/globals"
|
||||||
"git.noctra.dev/noctra/servtex/backend"
|
"git.noctra.dev/noctra/servtex/backend"
|
||||||
//"net/http"
|
//"net/http"
|
||||||
@@ -14,8 +13,9 @@ import (
|
|||||||
// 1 - config file could not be read
|
// 1 - config file could not be read
|
||||||
func main() {
|
func main() {
|
||||||
err := backend.ConfigReader("config.json", &globals.AppConfig)
|
err := backend.ConfigReader("config.json", &globals.AppConfig)
|
||||||
if err != nil {
|
if err != nil { os.Exit(1) }
|
||||||
os.Exit(1)
|
|
||||||
}
|
globals.LogFile, err = os.Open(globals.AppConfig.LogFilePath)
|
||||||
|
if err != nil { os.Exit(2) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user