mirror of
https://github.com/Theodor-Springmann-Stiftung/kgpz_web.git
synced 2025-10-29 17:15:31 +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 (
|
import (
|
||||||
"net/http"
|
"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) {
|
func NewFileWatcher() (*FileWatcher, error) {
|
||||||
|
|
||||||
fw := &FileWatcher{mu: sync.Mutex{}}
|
fw := &FileWatcher{mu: sync.Mutex{}}
|
||||||
fw.Watch()
|
fw.Watch()
|
||||||
return fw, nil
|
return fw, nil
|
||||||
@@ -96,8 +95,8 @@ func (fw *FileWatcher) Watch() error {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Println("event:", event)
|
|
||||||
if !event.Has(fsnotify.Chmod) {
|
if !event.Has(fsnotify.Chmod) {
|
||||||
|
log.Println("event:", event)
|
||||||
fw.mu.Lock()
|
fw.mu.Lock()
|
||||||
for _, wf := range fw.wf {
|
for _, wf := range fw.wf {
|
||||||
wf(event.Name)
|
wf(event.Name)
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ func main() {
|
|||||||
kgpz := app.NewKGPZ(cfg)
|
kgpz := app.NewKGPZ(cfg)
|
||||||
kgpz.Init()
|
kgpz.Init()
|
||||||
|
|
||||||
server := server.Start(kgpz, cfg)
|
server := server.Create(kgpz, cfg)
|
||||||
Start(kgpz, server)
|
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 {
|
type Issues struct {
|
||||||
XMLName xml.Name `xml:"stuecke"`
|
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 {
|
type Issue struct {
|
||||||
@@ -39,13 +63,13 @@ type Additional struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i Issues) Append(data Issues) Issues {
|
func (i Issues) Append(data Issues) Issues {
|
||||||
i.Issue = append(i.Issue, data.Issue...)
|
i.Issues = append(i.Issues, data.Issues...)
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i Issues) String() string {
|
func (i Issues) String() string {
|
||||||
var res []string
|
var res []string
|
||||||
for _, issue := range i.Issue {
|
for _, issue := range i.Issues {
|
||||||
res = append(res, issue.String())
|
res = append(res, issue.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,5 +81,5 @@ type Reference struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Value 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"
|
"time"
|
||||||
|
|
||||||
"github.com/Theodor-Springmann-Stiftung/kgpz_web/app"
|
"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/helpers"
|
||||||
"github.com/Theodor-Springmann-Stiftung/kgpz_web/providers"
|
"github.com/Theodor-Springmann-Stiftung/kgpz_web/providers"
|
||||||
"github.com/Theodor-Springmann-Stiftung/kgpz_web/templating"
|
"github.com/Theodor-Springmann-Stiftung/kgpz_web/templating"
|
||||||
@@ -40,27 +41,45 @@ const (
|
|||||||
// - we invalidate all caches if data is valid
|
// - we invalidate all caches if data is valid
|
||||||
// - we reload all clients
|
// - we reload all clients
|
||||||
// - if data validity catastrophically fails, we restart the router to map error pages.
|
// - if data validity catastrophically fails, we restart the router to map error pages.
|
||||||
// TODO: Use fiber
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
Config *providers.ConfigProvider
|
Config *providers.ConfigProvider
|
||||||
running chan bool
|
running chan bool
|
||||||
shutdown *sync.WaitGroup
|
shutdown *sync.WaitGroup
|
||||||
cache *memory.Storage
|
cache *memory.Storage
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
watcher *helpers.FileWatcher
|
watcher *helpers.FileWatcher
|
||||||
|
|
||||||
|
kgpz *app.KGPZ
|
||||||
|
}
|
||||||
|
|
||||||
|
func Create(k *app.KGPZ, c *providers.ConfigProvider) *Server {
|
||||||
|
if c == nil || k == nil {
|
||||||
|
log.Println("Error creating server")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Start(k *app.KGPZ, c *providers.ConfigProvider) *Server {
|
|
||||||
return &Server{
|
return &Server{
|
||||||
Config: c,
|
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 {
|
func (s *Server) Watcher() error {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
watcher, err := helpers.NewFileWatcher()
|
watcher, err := helpers.NewFileWatcher()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
s.watcher = watcher
|
s.watcher = watcher
|
||||||
s.watcher.Append(func(path string) {
|
s.watcher.Append(func(path string) {
|
||||||
log.Println("Restarting server")
|
log.Println("Restarting server")
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
s.Restart()
|
s.Restart()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -111,6 +130,7 @@ func (s *Server) Start() {
|
|||||||
srv.Use(recover.New())
|
srv.Use(recover.New())
|
||||||
|
|
||||||
// TODO: Dont cache static assets, bc storage gets huge
|
// TODO: Dont cache static assets, bc storage gets huge
|
||||||
|
// INFO: Maybe fiber does this already?
|
||||||
if s.Config.Debug {
|
if s.Config.Debug {
|
||||||
srv.Use(cache.New(cache.Config{
|
srv.Use(cache.New(cache.Config{
|
||||||
Next: func(c *fiber.Ctx) bool {
|
Next: func(c *fiber.Ctx) bool {
|
||||||
@@ -133,16 +153,15 @@ func (s *Server) Start() {
|
|||||||
|
|
||||||
srv.Use(STATIC_PREFIX, static(&views.StaticFS))
|
srv.Use(STATIC_PREFIX, static(&views.StaticFS))
|
||||||
|
|
||||||
srv.Get("/", func(c *fiber.Ctx) error {
|
srv.Get("/:year?", controllers.GetYear(s.kgpz))
|
||||||
return c.Render("/", fiber.Map{})
|
|
||||||
})
|
|
||||||
|
|
||||||
s.runner(srv)
|
s.runner(srv)
|
||||||
|
|
||||||
if s.Config.Debug {
|
if s.Config.Debug {
|
||||||
err := s.Watcher()
|
err := s.Watcher()
|
||||||
if err != nil {
|
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()
|
defer cleanup.Done()
|
||||||
clean := <-s.running
|
clean := <-s.running
|
||||||
|
|
||||||
srv.Server().CloseOnShutdown = true
|
|
||||||
|
|
||||||
if clean {
|
if clean {
|
||||||
if err := srv.ShutdownWithTimeout(SERVER_TIMEOUT); err != nil {
|
if err := srv.ShutdownWithTimeout(SERVER_TIMEOUT); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
|
|||||||
@@ -73,9 +73,6 @@
|
|||||||
- Was ist "Link auf seite teilen"? -> Permalink
|
- Was ist "Link auf seite teilen"? -> Permalink
|
||||||
|
|
||||||
## Überarbeiten
|
## Überarbeiten
|
||||||
- Undifferenzierte Angabe von "Akteur" von Werken in der Kurzangabe, Differenzieren
|
|
||||||
- Jahr
|
|
||||||
- Kurztitel, Titel
|
|
||||||
- Sekundärüberlieferung in Stück/Beitrag
|
- Sekundärüberlieferung in Stück/Beitrag
|
||||||
- Fehlende Daten in Stück/Beitrag
|
- Fehlende Daten in Stück/Beitrag
|
||||||
- Kurzinformation Überlieferung
|
- 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" }}
|
{{ 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 }}
|
{{ end }}
|
||||||
|
|||||||
Reference in New Issue
Block a user