diff --git a/.gitignore b/.gitignore
index fa07660..3b1931e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,7 +6,6 @@
*.dll
*.so
*.dylib
-servtex
# Test binary, built with `go test -c`
*.test
@@ -14,3 +13,8 @@ servtex
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
+# Custom Additions
+servtex
+servtex.log
+output
+
diff --git a/backend/backend.go b/backend/backend.go
index 9ba9ae4..7b4ad1e 100644
--- a/backend/backend.go
+++ b/backend/backend.go
@@ -1,13 +1,15 @@
package backend
import (
+ "encoding/json"
+ "errors"
+ "fmt"
"os"
"os/exec"
"path/filepath"
- "encoding/json"
- "errors"
- "git.noctra.dev/noctra/servtex/globals"
"time"
+
+ "git.noctra.dev/noctra/servtex/globals"
)
// Returns the current time in the timezone specified in the config file
@@ -21,13 +23,19 @@ func GetLocalTimeRFC() string {
return GetLocalTime().Format(time.RFC3339)
}
+func GetLocalTimeLog() string {
+ return GetLocalTime().Format("2006/01/02 15:04:05")
+}
+
// Returns the current localtime in custom pretty print format
func GetLocalTimePretty() string {
return GetLocalTime().Format("02.01.2006 15:04")
}
+// Writes to log and prints to screen
func LogLine(message string) {
- logline := "\n" + GetLocalTimeRFC() + " - " + message
+ logline := GetLocalTimeLog() + " ServTeX: " + message + "\n"
+ fmt.Print(logline)
globals.LogFile.WriteString(logline)
}
@@ -37,8 +45,7 @@ func LogLine(message string) {
func configReaderParse(filePath string, configOptionStorage *globals.Config) error {
jsonData, err := os.ReadFile(filePath)
if err == nil {
- err = json.Unmarshal(jsonData, &configOptionStorage)
- if err != nil {
+ if err = json.Unmarshal(jsonData, &configOptionStorage); err != nil {
return errors.New("Config file could not be read")
}
} else {
@@ -89,10 +96,13 @@ func ChangeWatch(path string, callOnChange func()) {
// Intended to be run as goroutine
func LatexCompile(config globals.Config, execution *globals.LatexExecution) error {
- if !execution.ExecutionLock.TryLock() {
+ if execution.ExecutionLock.TryLock() {
+ defer execution.ExecutionLock.Unlock()
+ } else {
LogLine("LaTeX execution already underway")
return errors.New("Execution already in progress")
}
+
LogLine("LaTeX execution started")
execution.ExecutionState = "Started"
@@ -109,7 +119,6 @@ func LatexCompile(config globals.Config, execution *globals.LatexExecution) erro
execution.ExecutionState = "Unknown Latex Engine"
execution.Output = []byte{}
execution.Timestamp = GetLocalTimePretty()
- execution.ExecutionLock.Unlock()
return errors.New("Unknown Latex Engine")
}
@@ -123,7 +132,6 @@ func LatexCompile(config globals.Config, execution *globals.LatexExecution) erro
execution.Output = stdout
execution.Timestamp = GetLocalTimePretty()
execution.ExecutionState = "Done"
- execution.ExecutionLock.Unlock()
return nil
}
diff --git a/config.json b/config.json
index 7e4e2b0..3f2632c 100644
--- a/config.json
+++ b/config.json
@@ -6,9 +6,11 @@
"latexSourceFilePath": "",
"latexOutputPath": "./output",
- "webserverDomain": "localhost:9876",
- "webserverSecure": false,
- "certificatePath": "",
- "certificateKeyPath": "",
+ "webserverDomain": "localhost",
+ "webserverPort": "8080",
+ "webserverSecure": true,
+ "webserverPortSecure": "8443",
+ "certificatePath": "./testing.crt",
+ "certificateKeyPath": "./testing.key",
"timezone": "Europe/Berlin"
}
diff --git a/frontend/templates/body.html b/frontend/templates/body.html
deleted file mode 100644
index 5d12c8e..0000000
--- a/frontend/templates/body.html
+++ /dev/null
@@ -1,27 +0,0 @@
-{{ define "body" }}
-
-
-
-
- compile start
- compile file-x
- compile file-y
- example output
- warning but who cares
- finished compilation
- output file created
-
-
-
-
-{{ end }}
diff --git a/frontend/templates/head.html b/frontend/templates/head.html
deleted file mode 100644
index 0e5358e..0000000
--- a/frontend/templates/head.html
+++ /dev/null
@@ -1,14 +0,0 @@
-{{ define "head" }}
-
-
-
- ServTeX
-
-
-
-
-
-
-
-{{ end }}
-
diff --git a/frontend/templates/main.html b/frontend/templates/main.html
index af7c923..c2ce990 100644
--- a/frontend/templates/main.html
+++ b/frontend/templates/main.html
@@ -1 +1,41 @@
-{{ template "header" . }}
+
+
+
+ ServTeX
+
+
+
+
+
+
+
+
+
+
+
+
+
+ compile start
+ compile file-x
+ compile file-y
+ example output
+ warning but who cares
+ finished compilation
+ output file created
+
+
+
+
+
+
+
diff --git a/frontend/templates/sse-pdf.html b/frontend/templates/sse-pdf.html
deleted file mode 100644
index 17cd0b5..0000000
--- a/frontend/templates/sse-pdf.html
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/frontend/webserver.go b/frontend/webserver.go
index 8ae4941..03ce086 100644
--- a/frontend/webserver.go
+++ b/frontend/webserver.go
@@ -3,6 +3,7 @@ package frontend
import (
"fmt"
"time"
+ "embed"
"strings"
"net/http"
"net/url"
@@ -10,6 +11,13 @@ import (
"git.noctra.dev/noctra/servtex/globals"
)
+//go:embed jscss/bootstrap.min.css
+//go:embed jscss/custom-view.css
+//go:embed jscss/htmx.min.js
+//go:embed jscss/htmx-ext-sse.js
+//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")
@@ -74,7 +82,7 @@ func sseOutputSend(writer *http.ResponseWriter) {
// Server Side Event Handler
//
// Sends a Ping instead of actual data when no new data available to save bandwidth
-func SSEEventSender(writer http.ResponseWriter, request *http.Request) {
+func SSEventHandler(writer http.ResponseWriter, request *http.Request) {
lastExecution := ""
for range time.Tick(time.Second) {
if lastExecution == globals.LatexExec.Timestamp {
@@ -89,3 +97,15 @@ func SSEEventSender(writer http.ResponseWriter, request *http.Request) {
}
}
+
+func PDFHandler(writer http.ResponseWriter, request *http.Request) {
+ http.NotFound(writer, request)
+}
+
+// Serves the main page of ServTeX
+func MainHandler(writer http.ResponseWriter, request *http.Request) {
+ writer.Header().Set("Content-Type", "text/html")
+ main, _ := WebFiles.ReadFile("templates/main.html")
+ writer.Write(main)
+}
+
diff --git a/globals/memory.go b/globals/memory.go
index 181d437..f01fedc 100644
--- a/globals/memory.go
+++ b/globals/memory.go
@@ -17,7 +17,9 @@ type Config struct {
// webserver
WebserverDomain string `json:"webserverDomain"`
+ WebserverPort string `json:"webserverPort"`
WebserverSecure bool `json:"webserverSecure"`
+ WebserverPortSecure string `json:"webserverPortSecure"`
CertificatePath string `json:"certificatePath"`
CertificateKeyPath string `json:"certificateKeyPath"`
Timezone string `json:"timezone"`
@@ -35,3 +37,4 @@ var LatexExec LatexExecution
var LogFile *os.File
+
diff --git a/main.go b/main.go
index 4064909..9175d21 100644
--- a/main.go
+++ b/main.go
@@ -2,20 +2,66 @@ package main
import (
"os"
- "git.noctra.dev/noctra/servtex/globals"
+ "os/signal"
+ "syscall"
+ "io/fs"
+ "context"
+ "fmt"
+ "time"
+ "net/http"
"git.noctra.dev/noctra/servtex/backend"
- //"net/http"
+ "git.noctra.dev/noctra/servtex/frontend"
+ "git.noctra.dev/noctra/servtex/globals"
)
-
// Exit Codes:
// 0 - ok
// 1 - config file could not be read
+// 2 - log file could not be accessed
func main() {
+ // application init
err := backend.ConfigReader("config.json", &globals.AppConfig)
if err != nil { os.Exit(1) }
- globals.LogFile, err = os.Open(globals.AppConfig.LogFilePath)
+ globals.LogFile, err = os.OpenFile(globals.AppConfig.LogFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil { os.Exit(2) }
+
+ globals.LatexExec.Timestamp = "mytimestamp"
+
+ // webserver init
+ server := &http.Server{Addr: globals.AppConfig.WebserverDomain + ":" + globals.AppConfig.WebserverPort}
+ serverSecure := &http.Server{Addr: globals.AppConfig.WebserverDomain + ":" + globals.AppConfig.WebserverPortSecure}
+
+ http.HandleFunc("/", frontend.MainHandler)
+ http.HandleFunc("/sse", frontend.SSEventHandler)
+ http.HandleFunc("/pdf", frontend.PDFHandler)
+ jscss, _ := fs.Sub(frontend.WebFiles, "jscss")
+ http.Handle("/jscss/", http.StripPrefix("/jscss/", http.FileServer(http.FS(jscss))))
+
+ go server.ListenAndServe()
+ if globals.AppConfig.WebserverSecure {
+ go serverSecure.ListenAndServeTLS(globals.AppConfig.CertificatePath, globals.AppConfig.CertificateKeyPath)
+ }
+ backend.LogLine("Started")
+
+ // shutdown logic
+ fmt.Println("Press CTRL-C to Exit ServTeX")
+
+ stop := make(chan os.Signal, 1)
+ signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
+
+ <-stop
+
+ context, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ defer cancel()
+
+ fmt.Print("\r")
+ if err = server.Shutdown(context); err != nil {
+ backend.LogLine("Graceful Shutdown failed")
+ }
+ if err = serverSecure.Shutdown(context); err != nil {
+ backend.LogLine("Graceful Shutdown failed")
+ }
+ backend.LogLine("Stopped")
}
diff --git a/testing.crt b/testing.crt
new file mode 100644
index 0000000..ece7049
--- /dev/null
+++ b/testing.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDCTCCAfGgAwIBAgIUfDzMzEHwX7jXT3lKp9+o3MreMNUwDQYJKoZIhvcNAQEL
+BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI1MTIyNTIxNTgzMFoXDTI2MTIy
+NTIxNTgzMFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEAyORa1+z/hdvaFX9IRRiadw51xmuVNWenMslb+hu2+PWz
+yAwYLNm03JAomFV/Qxm3RCUiZ9kPPw+CWjert40BX908k7rb5IeV4pHAsJkvR65T
+ZFCnCpR+e7nOIhy7zDmlilcATq77/AWhyfodR0lUuSTMSO8dYwZRs2rTAkItAo3j
+RnMDom742lBjTn7b4F/FZOstbPXX9j1povIyYQ5k92HqRwVeTG+h7w3njnoMOYeW
+YOWUUB1LyPHdzPJ50ooqvsNdBcYkkAR8C/R5czUaF/KUXHYM0zoz+QPk+KqTRGeJ
+XNVB99viI6QlNVniY0YtJBWl/TEzSQVbt1lEMf3JmwIDAQABo1MwUTAdBgNVHQ4E
+FgQUscO27AhUsPrR0NaYDuWgHvgFMrIwHwYDVR0jBBgwFoAUscO27AhUsPrR0NaY
+DuWgHvgFMrIwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEANDUA
+YrkofOVQ4Y33YGYTpPSf8CKIOMgJuKFihre7fcBevwRqBkhu+QBty3hvfzJSzUEx
+bT9twiO94F8gke1KAWjZQpd05aAtoa3OHf3JPTvRlPqNMpP+g+gCAj2Fm3NT8yBW
+nQyIRFg1DVydc61HxvR49DqiigzV+GgjcSS/WAtEDu6l/WhmvGCTHwYxY6w3LT+R
+3E/JEp/Mp9o55IFR9tz5wL3R5w4R5JwQZOKdbzQPkYjoxXjI3ldfXC9U/h86t5Md
+QmdWYyIfQxsqGoA0LAsUll/zwv1ywudeK1lxNtywXIKIur90a3D81MiTkzbhytVE
+DKD9klpayWzekOXrlA==
+-----END CERTIFICATE-----
diff --git a/testing.key b/testing.key
new file mode 100644
index 0000000..bf389fa
--- /dev/null
+++ b/testing.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDI5FrX7P+F29oV
+f0hFGJp3DnXGa5U1Z6cyyVv6G7b49bPIDBgs2bTckCiYVX9DGbdEJSJn2Q8/D4Ja
+N6u3jQFf3TyTutvkh5XikcCwmS9HrlNkUKcKlH57uc4iHLvMOaWKVwBOrvv8BaHJ
++h1HSVS5JMxI7x1jBlGzatMCQi0CjeNGcwOibvjaUGNOftvgX8Vk6y1s9df2PWmi
+8jJhDmT3YepHBV5Mb6HvDeeOegw5h5Zg5ZRQHUvI8d3M8nnSiiq+w10FxiSQBHwL
+9HlzNRoX8pRcdgzTOjP5A+T4qpNEZ4lc1UH32+IjpCU1WeJjRi0kFaX9MTNJBVu3
+WUQx/cmbAgMBAAECggEAY3JboBlT0rVnS44DPiU0TeyME+ns5o+FvsfcLr8qgCrN
+USHfk9A/zpHUbrigM5zW0raZRhQ3Dm4EhtmZOVdlj0mrM6xkL0iJQ6wIWcOzGoZr
+BLVCQ6QHywLLTqqvsqT01DtGTS0lU3iMQzp75O6hsLdNI2uvPfaCWlFu3Gba9jBb
+ZdT+qd9cca3FQSL0fut3msLNr2STtKLgVTEB7IHz2lEbsioBTcd7IlrsXQKTsR9N
+UOyTbOzDybt5kWmDYvtuQ7cfMfEuv4fWCBtFib1JljswJI2TaE1Gwpb7u8Hcq2w9
+FCAPtxltYnNRO4Xhru+zkxnG2kVNf/3Z6jzsnfIFHQKBgQDqOl4cwiev8oLwyXbR
+UVL7dfXjTGv6PphRAvXHwkOAH8xzndQT6ro7g3ftODDnndN/+/orblB3K4iBMQeU
+GVkmKQb1J2sXhRyf5e60WX3ZrhmW7+n7Fmx+znF98NZNSF6QhoYQmfy2ffXlwped
+FFyZIkD+MrpNn0hpDa+yS/uA9QKBgQDbkLtxZMo1NKBhC8hKLRO4gzNzTt4n3cbm
+psT8bo56fJYjWcr6RGlUJXF+Hol+2cn7MUtYJDpfwT72L6+BF0uv6R6s8eL2R5gE
+/gBkIhvx0fMrP5hR+GPd5Gd7PLudbNL5hVH1uOBOkbscy/XhIon32iD+AZzTkybP
+DOxO5uZGTwKBgGxkRlkYoDUUDPRQxuNmtvgXRorBOta7UNFshUDD7WjFTl/SkeoF
+ndkcpcrpTfhhWRbJDKQ8kJAVXT4r6k3mzRKTudyJOU3RE8YLKcPcBhlOMBlhPO4t
+Glg0QOD/Kqzo6JoJJtFX8VKiR8DjpDXUzmUvLNR1tTFmnKPA6aWg8+phAoGADDww
+dc0sB3L7TO0fKCMC6lFFWLOYZZhSMSAx8e8nOWQf6bBjQzb0t5+uh1ykRNFWFA1X
+KX47UoKuQ4G8wfDOYusWroR8JUUwD3coBmxwKjWM22gb0NWKmx7TNWbY/ZjG2Oi4
+/Hxk43vzdVNYTEdkcM9S71SfrJqSmw8ZS/xJ8LkCgYEA3DaHLR4KNitppL4Tx+5j
+dpIKUlCVCtxp4cOtpyuDSg8ssfHB3ZsX7j3+KFX0hF4imORAfUlUkzayuAAw6spF
+luPjDo+9g8mmVi3FArkbmef67L98A8C947EpoaqAeZWmFpG2w5tOB0vZcn/gFzOp
+hA4tRHe4o26/PRBUb4GFlq8=
+-----END PRIVATE KEY-----