mirror of
https://github.com/Theodor-Springmann-Stiftung/kgpz_web.git
synced 2025-10-28 16:45:32 +00:00
index endpoint start
This commit is contained in:
51
.air.toml
Normal file
51
.air.toml
Normal file
@@ -0,0 +1,51 @@
|
||||
root = "."
|
||||
testdata_dir = "testdata"
|
||||
tmp_dir = "tmp"
|
||||
|
||||
[build]
|
||||
args_bin = []
|
||||
bin = "./tmp/main"
|
||||
cmd = "go build -tags=\"dev\" -o ./tmp/main ."
|
||||
delay = 1000
|
||||
exclude_dir = ["assets", "views", "tmp", "vendor", "testdata"]
|
||||
exclude_file = []
|
||||
exclude_regex = ["_test.go"]
|
||||
exclude_unchanged = false
|
||||
follow_symlink = false
|
||||
full_bin = ""
|
||||
include_dir = []
|
||||
include_ext = ["go", "tpl", "tmpl", "html"]
|
||||
include_file = []
|
||||
kill_delay = "0s"
|
||||
log = "build-errors.log"
|
||||
poll = false
|
||||
poll_interval = 0
|
||||
post_cmd = []
|
||||
pre_cmd = []
|
||||
rerun = false
|
||||
rerun_delay = 500
|
||||
send_interrupt = false
|
||||
stop_on_error = false
|
||||
|
||||
[color]
|
||||
app = ""
|
||||
build = "yellow"
|
||||
main = "magenta"
|
||||
runner = "green"
|
||||
watcher = "cyan"
|
||||
|
||||
[log]
|
||||
main_only = false
|
||||
time = false
|
||||
|
||||
[misc]
|
||||
clean_on_exit = false
|
||||
|
||||
[proxy]
|
||||
app_port = 0
|
||||
enabled = false
|
||||
proxy_port = 0
|
||||
|
||||
[screen]
|
||||
clear_on_rebuild = false
|
||||
keep_scroll = true
|
||||
@@ -1,4 +1,4 @@
|
||||
package controller
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
21
controllers/years.go
Normal file
21
controllers/years.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/Theodor-Springmann-Stiftung/kgpz_web/app"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func GetYear(kgpz *app.KGPZ) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
y := c.Params("year", "1764")
|
||||
if len(y) != 4 {
|
||||
return c.SendStatus(fiber.StatusBadRequest)
|
||||
}
|
||||
|
||||
issues := kgpz.Library.Issues.GetYear(y)
|
||||
if len(issues.Issues) == 0 {
|
||||
return c.SendStatus(fiber.StatusNotFound)
|
||||
}
|
||||
return c.Render("/", fiber.Map{"model": issues})
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,6 @@ type FileWatcher struct {
|
||||
}
|
||||
|
||||
func NewFileWatcher() (*FileWatcher, error) {
|
||||
|
||||
fw := &FileWatcher{mu: sync.Mutex{}}
|
||||
fw.Watch()
|
||||
return fw, nil
|
||||
@@ -96,8 +95,8 @@ func (fw *FileWatcher) Watch() error {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
log.Println("event:", event)
|
||||
if !event.Has(fsnotify.Chmod) {
|
||||
log.Println("event:", event)
|
||||
fw.mu.Lock()
|
||||
for _, wf := range fw.wf {
|
||||
wf(event.Name)
|
||||
|
||||
@@ -37,7 +37,7 @@ func main() {
|
||||
kgpz := app.NewKGPZ(cfg)
|
||||
kgpz.Init()
|
||||
|
||||
server := server.Start(kgpz, cfg)
|
||||
server := server.Create(kgpz, cfg)
|
||||
Start(kgpz, server)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# Notes on xml reading
|
||||
|
||||
The models expect certain files and a certain file XML structure and throw quite often if the structure is not as expected.
|
||||
This is by design, as the models are supposed to be used in a controlled environment. Changes in the XML schema should be reflected in the models.
|
||||
All collections in the models should be kept thread-safe.
|
||||
@@ -11,7 +11,31 @@ type IssueProvider struct {
|
||||
|
||||
type Issues struct {
|
||||
XMLName xml.Name `xml:"stuecke"`
|
||||
Issue []Issue `xml:"stueck"`
|
||||
Issues []Issue `xml:"stueck"`
|
||||
}
|
||||
|
||||
func (i *IssueProvider) GetYear(year string) YearViewModel {
|
||||
res := YearViewModel{Year: year}
|
||||
last := ""
|
||||
for _, issue := range i.Items.Issues {
|
||||
if len(issue.Datum.When) < 4 {
|
||||
continue
|
||||
}
|
||||
|
||||
date := issue.Datum.When[0:4]
|
||||
if date != last {
|
||||
res.PushAvailable(date)
|
||||
last = date
|
||||
}
|
||||
|
||||
if date == year {
|
||||
res.PushIssue(issue)
|
||||
}
|
||||
}
|
||||
|
||||
res.SortAvailableYears()
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
type Issue struct {
|
||||
@@ -39,13 +63,13 @@ type Additional struct {
|
||||
}
|
||||
|
||||
func (i Issues) Append(data Issues) Issues {
|
||||
i.Issue = append(i.Issue, data.Issue...)
|
||||
i.Issues = append(i.Issues, data.Issues...)
|
||||
return i
|
||||
}
|
||||
|
||||
func (i Issues) String() string {
|
||||
var res []string
|
||||
for _, issue := range i.Issue {
|
||||
for _, issue := range i.Issues {
|
||||
res = append(res, issue.String())
|
||||
}
|
||||
|
||||
|
||||
@@ -81,5 +81,5 @@ type Reference struct {
|
||||
}
|
||||
|
||||
type Value struct {
|
||||
Value string `xml:",chardata"`
|
||||
Chardata string `xml:",chardata"`
|
||||
}
|
||||
|
||||
84
providers/year.go
Normal file
84
providers/year.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const TLAYOUT = "2006-01-02"
|
||||
|
||||
var TRANSLM = map[string][]string{
|
||||
"January": {"Januar", "Jan"},
|
||||
"February": {"Februar", "Feb"},
|
||||
"March": {"März", "Mär"},
|
||||
"April": {"April", "Apr"},
|
||||
"May": {"Mai", "Mai"},
|
||||
"June": {"Juni", "Jun"},
|
||||
"July": {"Juli", "Jul"},
|
||||
"August": {"August", "Aug"},
|
||||
"September": {"September", "Sep"},
|
||||
"October": {"Oktober", "Okt"},
|
||||
"November": {"November", "Nov"},
|
||||
"December": {"Dezember", "Dez"},
|
||||
}
|
||||
|
||||
var TRANSLD = map[string][]string{
|
||||
"Monday": {"Montag", "Mo"},
|
||||
"Tuesday": {"Dienstag", "Di"},
|
||||
"Wednesday": {"Mittwoch", "Mi"},
|
||||
"Thursday": {"Donnerstag", "Do"},
|
||||
"Friday": {"Freitag", "Fr"},
|
||||
"Saturday": {"Samstag", "Sa"},
|
||||
"Sunday": {"Sonntag", "So"},
|
||||
}
|
||||
|
||||
type IssueViewModel struct {
|
||||
Issue
|
||||
Weekday []string
|
||||
Day int
|
||||
Month []string
|
||||
}
|
||||
|
||||
func NewIssueView(i Issue) (IssueViewModel, error) {
|
||||
t, err := time.Parse(TLAYOUT, i.Datum.When)
|
||||
if err != nil {
|
||||
return IssueViewModel{}, err
|
||||
}
|
||||
|
||||
return IssueViewModel{
|
||||
Issue: i,
|
||||
Weekday: append(TRANSLD[t.Weekday().String()], t.Weekday().String()),
|
||||
Day: t.Day(),
|
||||
Month: append(TRANSLM[t.Month().String()], i.Datum.When[5:7]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type YearViewModel struct {
|
||||
Year string
|
||||
AvailableYears []int
|
||||
Issues []IssueViewModel
|
||||
}
|
||||
|
||||
func (y *YearViewModel) PushIssue(i Issue) {
|
||||
iv, err := NewIssueView(i)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
y.Issues = append(y.Issues, iv)
|
||||
}
|
||||
|
||||
func (y *YearViewModel) PushAvailable(s string) {
|
||||
i, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !slices.Contains(y.AvailableYears, i) {
|
||||
y.AvailableYears = append(y.AvailableYears, i)
|
||||
}
|
||||
}
|
||||
|
||||
func (y *YearViewModel) SortAvailableYears() {
|
||||
slices.Sort(y.AvailableYears)
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/Theodor-Springmann-Stiftung/kgpz_web/app"
|
||||
"github.com/Theodor-Springmann-Stiftung/kgpz_web/controllers"
|
||||
"github.com/Theodor-Springmann-Stiftung/kgpz_web/helpers"
|
||||
"github.com/Theodor-Springmann-Stiftung/kgpz_web/providers"
|
||||
"github.com/Theodor-Springmann-Stiftung/kgpz_web/templating"
|
||||
@@ -40,27 +41,45 @@ const (
|
||||
// - we invalidate all caches if data is valid
|
||||
// - we reload all clients
|
||||
// - if data validity catastrophically fails, we restart the router to map error pages.
|
||||
// TODO: Use fiber
|
||||
type Server struct {
|
||||
Config *providers.ConfigProvider
|
||||
running chan bool
|
||||
shutdown *sync.WaitGroup
|
||||
cache *memory.Storage
|
||||
|
||||
mu sync.Mutex
|
||||
watcher *helpers.FileWatcher
|
||||
|
||||
kgpz *app.KGPZ
|
||||
}
|
||||
|
||||
func Start(k *app.KGPZ, c *providers.ConfigProvider) *Server {
|
||||
func Create(k *app.KGPZ, c *providers.ConfigProvider) *Server {
|
||||
if c == nil || k == nil {
|
||||
log.Println("Error creating server")
|
||||
return nil
|
||||
}
|
||||
|
||||
return &Server{
|
||||
Config: c,
|
||||
kgpz: k,
|
||||
}
|
||||
}
|
||||
|
||||
// INFO: hot reloading for poor people
|
||||
// BUG: unable to close the old file watcher here, since the process gets aborted in the middle of creating the new one.
|
||||
func (s *Server) Watcher() error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
watcher, err := helpers.NewFileWatcher()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.watcher = watcher
|
||||
s.watcher.Append(func(path string) {
|
||||
log.Println("Restarting server")
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
s.Restart()
|
||||
})
|
||||
|
||||
@@ -111,6 +130,7 @@ func (s *Server) Start() {
|
||||
srv.Use(recover.New())
|
||||
|
||||
// TODO: Dont cache static assets, bc storage gets huge
|
||||
// INFO: Maybe fiber does this already?
|
||||
if s.Config.Debug {
|
||||
srv.Use(cache.New(cache.Config{
|
||||
Next: func(c *fiber.Ctx) bool {
|
||||
@@ -133,16 +153,15 @@ func (s *Server) Start() {
|
||||
|
||||
srv.Use(STATIC_PREFIX, static(&views.StaticFS))
|
||||
|
||||
srv.Get("/", func(c *fiber.Ctx) error {
|
||||
return c.Render("/", fiber.Map{})
|
||||
})
|
||||
srv.Get("/:year?", controllers.GetYear(s.kgpz))
|
||||
|
||||
s.runner(srv)
|
||||
|
||||
if s.Config.Debug {
|
||||
err := s.Watcher()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
log.Println("Error watching files")
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -194,8 +213,6 @@ func (s *Server) runner(srv *fiber.App) {
|
||||
defer cleanup.Done()
|
||||
clean := <-s.running
|
||||
|
||||
srv.Server().CloseOnShutdown = true
|
||||
|
||||
if clean {
|
||||
if err := srv.ShutdownWithTimeout(SERVER_TIMEOUT); err != nil {
|
||||
fmt.Println(err)
|
||||
|
||||
@@ -73,9 +73,6 @@
|
||||
- Was ist "Link auf seite teilen"? -> Permalink
|
||||
|
||||
## Überarbeiten
|
||||
- Undifferenzierte Angabe von "Akteur" von Werken in der Kurzangabe, Differenzieren
|
||||
- Jahr
|
||||
- Kurztitel, Titel
|
||||
- Sekundärüberlieferung in Stück/Beitrag
|
||||
- Fehlende Daten in Stück/Beitrag
|
||||
- Kurzinformation Überlieferung
|
||||
|
||||
1
tmp/build-errors.log
Normal file
1
tmp/build-errors.log
Normal file
@@ -0,0 +1 @@
|
||||
exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1
|
||||
@@ -1,3 +1,19 @@
|
||||
{{ define "body" }}
|
||||
<p>Change! Hello from body</p>
|
||||
{{ range $year := .model.AvailableYears }}
|
||||
<a href="/{{ $year }}">{{ $year }}</a>
|
||||
{{ end }}
|
||||
{{ range $issue := .model.Issues }}
|
||||
<div>
|
||||
<div>
|
||||
{{ $issue.Number.Chardata }}
|
||||
</div>
|
||||
<div>
|
||||
{{ index $issue.Month 1 }}
|
||||
</div>
|
||||
<div>
|
||||
{{ index $issue.Weekday 1 }}
|
||||
</div>
|
||||
<div>{{ $issue.Day }}.{{ index $issue.Month 2 }}.</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
Reference in New Issue
Block a user