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/helpers/middleware"
"github.com/Theodor-Springmann-Stiftung/lenz-web/server" "github.com/Theodor-Springmann-Stiftung/lenz-web/server"
"github.com/Theodor-Springmann-Stiftung/lenz-web/views" "github.com/Theodor-Springmann-Stiftung/lenz-web/views"
"github.com/gofiber/fiber/v2/middleware/compress"
) )
const ASSETS_URL = "/assets" const ASSETS_URL = "/assets"
func Register(server server.Server) { 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.Use(ASSETS_URL, middleware.StaticHandler(&views.StaticFS))
server.Server.Get("/", GetIndex)
} }

View File

@@ -1,63 +1,67 @@
package functions package functions
type Month struct { import (
Full string "fmt"
Short string "time"
Number string )
No int
}
type Weekday struct { type Weekday struct {
Full string Name string
Short string ShortName string
No int Number int
} }
func (m Month) String() string { type Month struct {
return m.Full Name string
ShortName string
Number int
} }
func (w Weekday) String() string { var Months = []Month{
return w.Full {"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{ var Weekdays = []Weekday{
{"NotAvailable", "NA", "0", 0}, {"Sonntag", "So", 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},
{"Montag", "Mo", 1}, {"Montag", "Mo", 1},
{"Dienstag", "Di", 2}, {"Dienstag", "Di", 2},
{"Mittwoch", "Mi", 3}, {"Mittwoch", "Mi", 3},
{"Donnerstag", "Do", 4}, {"Donnerstag", "Do", 4},
{"Freitag", "Fr", 5}, {"Freitag", "Fr", 5},
{"Samstag", "Sa", 6}, {"Samstag", "Sa", 6},
{"Sonntag", "So", 7}, {"N/A", "N/A", 7},
} }
func MonthName(i int) Month { func Today() time.Time {
if i > 12 || i < 1 { return time.Now()
return TRANSLM[0]
}
return TRANSLM[i]
} }
func WeekdayName(i int) Weekday { func GetMonth(month any) Month {
if i > 7 || i < 1 { if val, ok := month.(int); ok {
return TRANSLD[0] val -= 1
if val < 0 || val > 11 {
val = 12
} }
return TRANSLD[i] return Months[val]
}
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" "time"
"github.com/Theodor-Springmann-Stiftung/lenz-web/config" "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" gitprovider "github.com/Theodor-Springmann-Stiftung/lenz-web/git"
"github.com/Theodor-Springmann-Stiftung/lenz-web/server" "github.com/Theodor-Springmann-Stiftung/lenz-web/server"
"github.com/Theodor-Springmann-Stiftung/lenz-web/templating" "github.com/Theodor-Springmann-Stiftung/lenz-web/templating"
@@ -41,7 +42,7 @@ func main() {
panic(err) 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) err = xmlmodels.New(dir, commit.Hash)
if err != nil { if err != nil {
panic(err) panic(err)
@@ -57,6 +58,7 @@ func main() {
} }
server := server.New(engine, storage, cfg.Debug) server := server.New(engine, storage, cfg.Debug)
controllers.Register(server)
server.Start(cfg.Address + ":" + cfg.Port) 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) { func ResetFunction(storage fiber.Storage, engine *templating.Engine) {
slog.Debug("Resetting storage and reloading engine")
storage.Reset() storage.Reset()
engine.Reload() engine.Reload()
} }
func RefreshFunction(storage fiber.Storage, engine *templating.Engine) { func RefreshFunction(storage fiber.Storage, engine *templating.Engine) {
slog.Debug("Resetting storage and sending refresh signal")
storage.Reset() storage.Reset()
engine.Refresh() engine.Refresh()
} }

View File

@@ -100,7 +100,7 @@ func (e *Engine) setDebugData() {
e.mu.Lock() e.mu.Lock()
defer e.mu.Unlock() defer e.mu.Unlock()
e.debug = true e.debug = true
e.GlobalData["debug"] = true e.GlobalData["isDev"] = true
e.GlobalData["debugport"] = WS_SERVER e.GlobalData["debugport"] = WS_SERVER
} }
@@ -121,6 +121,8 @@ func (e *Engine) funcs() error {
// Passing HTML // Passing HTML
e.AddFunc("Safe", functions.Safe) e.AddFunc("Safe", functions.Safe)
e.AddFunc("Today", functions.Today)
e.AddFunc("GetMonth", functions.GetMonth)
return nil return nil
} }
@@ -159,9 +161,9 @@ func (e *Engine) Load() error {
func (e *Engine) Reload() { func (e *Engine) Reload() {
e.regmu.Lock() e.regmu.Lock()
defer e.regmu.Unlock()
e.LayoutRegistry = e.LayoutRegistry.Reset() e.LayoutRegistry = e.LayoutRegistry.Reset()
e.TemplateRegistry = e.TemplateRegistry.Reset() e.TemplateRegistry = e.TemplateRegistry.Reset()
e.regmu.Unlock()
e.Load() 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>
<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> <a href="https://theodor-springmann-stiftung.de">Theodor Springmann Stiftung</a>
<span>&middot;</span> <span>&middot;</span>
<a href="/datenschutz/">Impressum &amp; Datenschutz</a> <a href="/datenschutz/">Impressum &amp; Datenschutz</a>
<span>&middot;</span> <span>&middot;</span>
<i class="ri-code-line"></i> <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>
</div> </div>
</footer> </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 }} {{ 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> <script type="module" src="/assets/scripts.js"></script>
<link href="/assets/css/remixicon.css" rel="stylesheet" /> <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" /> <link rel="stylesheet" type="text/css" href="/assets/style.css" />
<script type="module"> <script type="module">
@@ -67,17 +61,10 @@
<!-- Default scripts... --> <!-- Default scripts... -->
{{ end }} {{ 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> </div>
</body> </body>
</html> </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 // INFO: We import this so vite processes the stylesheet
import "../public/css/fonts.css";
import "./site.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_ONLOAD = "script[xslt-onload]";
const ATTR_XSLT_TEMPLATE = "xslt-template"; const ATTR_XSLT_TEMPLATE = "xslt-template";

View File

@@ -40,7 +40,7 @@ func New(fn func()) (*Watcher, error) {
reloadTimer.Stop() reloadTimer.Stop()
} }
reloadTimer = time.AfterFunc(WATCHER_DEBOUNCE, func() { reloadTimer = time.AfterFunc(WATCHER_DEBOUNCE, func() {
slog.Debug("Changes detected, reloading templates...") slog.Debug("Changes detected...")
fn() 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)) decoder := xml.NewDecoder(strings.NewReader(xmlData))
stack := []Element{} stack := []Element{}
state := initialState state := initialState
return iter.Seq2[*TokenResult[T], error](func(yield func(*TokenResult[T], error) bool) { return func(yield func(*TokenResult[T], error) bool) {
for { for {
token, err := decoder.Token() token, err := decoder.Token()
if err == io.EOF { if err == io.EOF {
@@ -117,7 +117,7 @@ func Iterate[T any](xmlData string, initialState T) iter.Seq2[*TokenResult[T], e
return return
} }
} }
}) }
} }
// mapAttributes converts xml.Attr to a map[string]string. // 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 // INFO: this runs just once for the first key
ret := make([]Resolved[T], 0)
keys := item.Keys() keys := item.Keys()
for _, key := range 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() a.RLock()
defer a.RUnlock() defer a.RUnlock()
var s string
for _, item := range a.array { for _, item := range a.array {
s += item.String() s += item.String()
} }
return s return
} }
func (p *XMLParser[T]) Info(id string) ItemInfo { func (p *XMLParser[T]) Info(id string) ItemInfo {