BUGFIX: race condition in engine; index page controller

This commit is contained in:
Simon Martens
2025-03-24 15:30:07 +01:00
parent 66fedf4baf
commit 4bff7fb17a
39 changed files with 59138 additions and 302 deletions

11
controllers/index.go Normal file
View File

@@ -0,0 +1,11 @@
package controllers
import (
"github.com/Theodor-Springmann-Stiftung/lenz-web/xmlmodels"
"github.com/gofiber/fiber/v2"
)
func GetIndex(fiber *fiber.Ctx) error {
_ = xmlmodels.Get()
return fiber.Render("/", nil)
}

View File

@@ -4,10 +4,16 @@ import (
"github.com/Theodor-Springmann-Stiftung/lenz-web/helpers/middleware"
"github.com/Theodor-Springmann-Stiftung/lenz-web/server"
"github.com/Theodor-Springmann-Stiftung/lenz-web/views"
"github.com/gofiber/fiber/v2/middleware/compress"
)
const ASSETS_URL = "/assets"
func Register(server server.Server) {
server.Server.Use(ASSETS_URL, compress.New(compress.Config{
Level: compress.LevelBestSpeed,
}))
server.Server.Use(ASSETS_URL, middleware.StaticHandler(&views.StaticFS))
server.Server.Get("/", GetIndex)
}

View File

@@ -1,63 +1,67 @@
package functions
type Month struct {
Full string
Short string
Number string
No int
}
import (
"fmt"
"time"
)
type Weekday struct {
Full string
Short string
No int
Name string
ShortName string
Number int
}
func (m Month) String() string {
return m.Full
type Month struct {
Name string
ShortName string
Number int
}
func (w Weekday) String() string {
return w.Full
var Months = []Month{
{"Januar", "Jan", 1},
{"Februar", "Feb", 2},
{"März", "Mär", 3},
{"April", "Apr", 4},
{"Mai", "Mai", 5},
{"Juni", "Jun", 6},
{"Juli", "Jul", 7},
{"August", "Aug", 8},
{"September", "Sep", 9},
{"Oktober", "Okt", 10},
{"November", "Nov", 11},
{"Dezember", "Dez", 12},
{"N/A", "N/A", 0},
}
var TRANSLM = []Month{
{"NotAvailable", "NA", "0", 0},
{"Januar", "Jan", "1", 1},
{"Februar", "Feb", "2", 2},
{"März", "Mär", "3", 3},
{"April", "Apr", "4", 4},
{"Mai", "Mai", "5", 5},
{"Juni", "Jun", "6", 6},
{"Juli", "Jul", "7", 7},
{"August", "Aug", "8", 8},
{"September", "Sep", "9", 9},
{"Oktober", "Okt", "10", 10},
{"November", "Nov", "11", 11},
{"Dezember", "Dez", "12", 12},
}
var TRANSLD = []Weekday{
{"NotAvailable", "NA", 0},
var Weekdays = []Weekday{
{"Sonntag", "So", 0},
{"Montag", "Mo", 1},
{"Dienstag", "Di", 2},
{"Mittwoch", "Mi", 3},
{"Donnerstag", "Do", 4},
{"Freitag", "Fr", 5},
{"Samstag", "Sa", 6},
{"Sonntag", "So", 7},
{"N/A", "N/A", 7},
}
func MonthName(i int) Month {
if i > 12 || i < 1 {
return TRANSLM[0]
}
return TRANSLM[i]
func Today() time.Time {
return time.Now()
}
func WeekdayName(i int) Weekday {
if i > 7 || i < 1 {
return TRANSLD[0]
func GetMonth(month any) Month {
if val, ok := month.(int); ok {
val -= 1
if val < 0 || val > 11 {
val = 12
}
return Months[val]
}
return TRANSLD[i]
if val, ok := month.(time.Time); ok {
m := val.Month() - 1
return Months[m]
}
fmt.Println("Invalid month value", month)
return Months[12]
}

View File

@@ -6,6 +6,7 @@ import (
"time"
"github.com/Theodor-Springmann-Stiftung/lenz-web/config"
"github.com/Theodor-Springmann-Stiftung/lenz-web/controllers"
gitprovider "github.com/Theodor-Springmann-Stiftung/lenz-web/git"
"github.com/Theodor-Springmann-Stiftung/lenz-web/server"
"github.com/Theodor-Springmann-Stiftung/lenz-web/templating"
@@ -41,7 +42,7 @@ func main() {
panic(err)
}
// INFO: the lib, engine and storage objects passed to the server are not made to be recreated.
// INFO: the lib, engine and storage objects passed to the server should never be recreated.
err = xmlmodels.New(dir, commit.Hash)
if err != nil {
panic(err)
@@ -57,6 +58,7 @@ func main() {
}
server := server.New(engine, storage, cfg.Debug)
controllers.Register(server)
server.Start(cfg.Address + ":" + cfg.Port)
}
@@ -92,11 +94,13 @@ func SetupReloadWatcher(storage fiber.Storage, engine *templating.Engine) {
}
func ResetFunction(storage fiber.Storage, engine *templating.Engine) {
slog.Debug("Resetting storage and reloading engine")
storage.Reset()
engine.Reload()
}
func RefreshFunction(storage fiber.Storage, engine *templating.Engine) {
slog.Debug("Resetting storage and sending refresh signal")
storage.Reset()
engine.Refresh()
}

View File

@@ -100,7 +100,7 @@ func (e *Engine) setDebugData() {
e.mu.Lock()
defer e.mu.Unlock()
e.debug = true
e.GlobalData["debug"] = true
e.GlobalData["isDev"] = true
e.GlobalData["debugport"] = WS_SERVER
}
@@ -121,6 +121,8 @@ func (e *Engine) funcs() error {
// Passing HTML
e.AddFunc("Safe", functions.Safe)
e.AddFunc("Today", functions.Today)
e.AddFunc("GetMonth", functions.GetMonth)
return nil
}
@@ -159,9 +161,9 @@ func (e *Engine) Load() error {
func (e *Engine) Reload() {
e.regmu.Lock()
defer e.regmu.Unlock()
e.LayoutRegistry = e.LayoutRegistry.Reset()
e.TemplateRegistry = e.TemplateRegistry.Reset()
e.regmu.Unlock()
e.Load()
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 2.6 MiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -10,13 +10,13 @@
</div>
<div>
<span>{{- .site.title }} &ndash; ein Projekt der</span>
<span>{{- .title }} &ndash; ein Projekt der</span>
<a href="https://theodor-springmann-stiftung.de">Theodor Springmann Stiftung</a>
<span>&middot;</span>
<a href="/datenschutz/">Impressum &amp; Datenschutz</a>
<span>&middot;</span>
<i class="ri-code-line"></i>
<a href="https://github.com/Theodor-Springmann-Stiftung/musenalm">Code</a>
<a href="https://github.com/Theodor-Springmann-Stiftung/lenz-web">Code</a>
</div>
</div>
</footer>

View File

@@ -0,0 +1,52 @@
<!-- Mark selected anchor location for CSS & Assistive Technology -->
<script type="module">
const hash = window.location.hash;
if (hash) {
const stripped = hash.slice(1);
const element = document.getElementById(stripped);
if (element) {
element.setAttribute("aria-current", "location");
}
}
</script>
{{ if .isDev }}
<!-- Reload script for development -->
<script type="module">
(function () {
let relto = -1;
const scheme = location.protocol === "https:" ? "wss" : "ws";
// Hardcode port 9000 here:
const url = scheme + "://" + location.hostname + ":9000/pb/reload";
function connect() {
const socket = new WebSocket(url);
socket.addEventListener("open", function () {
console.log("Reload socket connected (port 9000).");
});
socket.addEventListener("message", function (evt) {
if (evt.data === "reload") {
console.log("Received reload signal. Reloading...");
if (relto !== -1) clearTimeout(relto);
relto = setTimeout(() => location.reload(), 0);
}
});
socket.addEventListener("close", function () {
console.log("Reload socket closed. Reconnecting in 3 seconds...");
setTimeout(connect, 3000);
});
socket.addEventListener("error", function (err) {
console.error("Reload socket error:", err);
// We'll let onclose handle reconnection.
});
}
// Initiate the first connection attempt.
connect();
})();
</script>
{{ end }}

View File

@@ -21,14 +21,8 @@
{{ end }}
<script src="/assets/js/alpine.min.js" defer></script>
<script src="/assets/js/htmx.min.js" defer></script>
<script src="/assets/js/htmx-response-targets.js" defer></script>
<script src="/assets/js/mark.min.js" defer></script>
<script type="module" src="/assets/scripts.js"></script>
<link href="/assets/css/remixicon.css" rel="stylesheet" />
<link rel="stylesheet" type="text/css" href="/assets/css/fonts.css" />
<link rel="stylesheet" type="text/css" href="/assets/style.css" />
<script type="module">
@@ -67,17 +61,10 @@
<!-- Default scripts... -->
{{ end }}
{{ block "_globalscripts" . }}
<!-- Default global scripts... -->
{{ end }}
<script type="module">
const hash = window.location.hash;
if (hash) {
const stripped = hash.slice(1);
const element = document.getElementById(stripped);
if (element) {
element.setAttribute("aria-current", "location");
}
}
</script>
</div>
</body>
</html>

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 2.6 MiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
Helo from body!

View File

@@ -1,5 +1,8 @@
// INFO: We import this so vite processes the stylesheet
import "../public/css/fonts.css";
import "./site.css";
import "../public/js/alpine.min.js";
import "../public/js/htmx.min.js";
const ATTR_XSLT_ONLOAD = "script[xslt-onload]";
const ATTR_XSLT_TEMPLATE = "xslt-template";

View File

@@ -40,7 +40,7 @@ func New(fn func()) (*Watcher, error) {
reloadTimer.Stop()
}
reloadTimer = time.AfterFunc(WATCHER_DEBOUNCE, func() {
slog.Debug("Changes detected, reloading templates...")
slog.Debug("Changes detected...")
fn()
})
}

View File

@@ -42,7 +42,7 @@ func Iterate[T any](xmlData string, initialState T) iter.Seq2[*TokenResult[T], e
decoder := xml.NewDecoder(strings.NewReader(xmlData))
stack := []Element{}
state := initialState
return iter.Seq2[*TokenResult[T], error](func(yield func(*TokenResult[T], error) bool) {
return func(yield func(*TokenResult[T], error) bool) {
for {
token, err := decoder.Token()
if err == io.EOF {
@@ -117,7 +117,7 @@ func Iterate[T any](xmlData string, initialState T) iter.Seq2[*TokenResult[T], e
return
}
}
})
}
}
// mapAttributes converts xml.Attr to a map[string]string.

View File

@@ -134,9 +134,8 @@ func (p *XMLParser[T]) addResolvable(item T) {
}
}
func (p *XMLParser[T]) ReverseLookup(item IXMLItem) []Resolved[T] {
func (p *XMLParser[T]) ReverseLookup(item IXMLItem) (ret []Resolved[T]) {
// INFO: this runs just once for the first key
ret := make([]Resolved[T], 0)
keys := item.Keys()
for _, key := range keys {
@@ -146,17 +145,16 @@ func (p *XMLParser[T]) ReverseLookup(item IXMLItem) []Resolved[T] {
}
}
return ret
return
}
func (a *XMLParser[T]) String() string {
func (a *XMLParser[T]) String() (s string) {
a.RLock()
defer a.RUnlock()
var s string
for _, item := range a.array {
s += item.String()
}
return s
return
}
func (p *XMLParser[T]) Info(id string) ItemInfo {