diff --git a/app/pb.go b/app/pb.go index 7d92338..abc1af6 100644 --- a/app/pb.go +++ b/app/pb.go @@ -38,6 +38,8 @@ type App struct { dataMutex sync.RWMutex htmlCache *PrefixCache htmlMutex sync.RWMutex + pagesCache map[string]PageMetaData + pagesMutex sync.RWMutex imagesCache map[string]*dbmodels.Image imagesMutex sync.RWMutex } @@ -205,48 +207,39 @@ func (app *App) createEngine() (*templating.Engine, error) { } return template.HTML(value) }) + app.ResetPagesCache() engine.AddFunc("pageMeta", func(name string) map[string]any { - app.ensureDataCache() - key := "page." + name - app.dataMutex.RLock() - value := app.dataCache.Get(key) - app.dataMutex.RUnlock() - if value == nil { + app.ensurePagesCache() + app.pagesMutex.RLock() + meta, ok := app.pagesCache[name] + app.pagesMutex.RUnlock() + if !ok { return map[string]any{} } - if meta, ok := value.(map[string]any); ok { - return meta + return map[string]any{ + "title": meta.Title, + "description": meta.Description, + "keywords": meta.Keywords, } - if meta, ok := value.(map[string]interface{}); ok { - return meta - } - return map[string]any{} }) engine.AddFunc("pageMetaField", func(name, field string) string { - app.ensureDataCache() - key := "page." + name - app.dataMutex.RLock() - value := app.dataCache.Get(key) - app.dataMutex.RUnlock() - if value == nil { - return "" - } - meta, ok := value.(map[string]any) + app.ensurePagesCache() + app.pagesMutex.RLock() + meta, ok := app.pagesCache[name] + app.pagesMutex.RUnlock() if !ok { - if metaAlt, ok := value.(map[string]interface{}); ok { - meta = map[string]any(metaAlt) - } else { - return "" - } - } - fieldValue, ok := meta[field] - if !ok || fieldValue == nil { return "" } - if s, ok := fieldValue.(string); ok { - return s + switch field { + case "title": + return meta.Title + case "description": + return meta.Description + case "keywords": + return meta.Keywords + default: + return "" } - return fmt.Sprint(fieldValue) }) engine.AddFunc("pageHtml", func(name string, section ...string) template.HTML { app.ensureHtmlCache() @@ -305,11 +298,23 @@ func (app *App) ResetImagesCache() { app.imagesCache = make(map[string]*dbmodels.Image) } +func (app *App) ResetPagesCache() { + app.pagesMutex.Lock() + defer app.pagesMutex.Unlock() + app.pagesCache = make(map[string]PageMetaData) +} + type PrefixCache struct { data map[string]any keys []string } +type PageMetaData struct { + Title string + Description string + Keywords string +} + func NewPrefixCache() *PrefixCache { return &PrefixCache{ data: make(map[string]any), @@ -418,6 +423,43 @@ func (app *App) ensureHtmlCache() { app.htmlMutex.Unlock() } +func (app *App) ensurePagesCache() { + app.pagesMutex.RLock() + if app.pagesCache != nil && len(app.pagesCache) > 0 { + app.pagesMutex.RUnlock() + return + } + app.pagesMutex.RUnlock() + + pages, err := dbmodels.Pages_All(app.PB.App) + if err != nil { + app.PB.Logger().Error("Failed to fetch pages cache: %v", err) + return + } + + cache := make(map[string]PageMetaData, len(pages)) + for _, page := range pages { + meta := PageMetaData{ + Title: page.Title(), + } + if data := page.Data(); data != nil { + if value, ok := data["description"]; ok && value != nil { + meta.Description = fmt.Sprint(value) + } + if value, ok := data["keywords"]; ok && value != nil { + meta.Keywords = fmt.Sprint(value) + } + } + cache[page.Key()] = meta + } + + app.pagesMutex.Lock() + if app.pagesCache == nil || len(app.pagesCache) == 0 { + app.pagesCache = cache + } + app.pagesMutex.Unlock() +} + func (app *App) ensureImagesCache() { app.imagesMutex.RLock() if app.imagesCache != nil && len(app.imagesCache) > 0 { diff --git a/dbmodels/page.go b/dbmodels/page.go index 7d2e20c..16caab7 100644 --- a/dbmodels/page.go +++ b/dbmodels/page.go @@ -18,6 +18,14 @@ func (p *Page) URL() string { return p.GetString(URL_FIELD) } +func (p *Page) Title() string { + return p.GetString(TITLE_FIELD) +} + +func (p *Page) SetTitle(title string) { + p.Set(TITLE_FIELD, title) +} + func (p *Page) SetURL(url string) { p.Set(URL_FIELD, url) } diff --git a/dbmodels/queries.go b/dbmodels/queries.go index 9772a80..61a3874 100644 --- a/dbmodels/queries.go +++ b/dbmodels/queries.go @@ -155,6 +155,12 @@ func Data_All(app core.App) ([]*Data, error) { return data, err } +func Pages_All(app core.App) ([]*Page, error) { + pages := make([]*Page, 0) + err := app.RecordQuery(PAGES_TABLE).All(&pages) + return pages, err +} + func Html_All(app core.App) ([]*HTML, error) { html := make([]*HTML, 0) err := app.RecordQuery(HTML_TABLE).All(&html) diff --git a/migrations/01_tables.go b/migrations/01_tables.go index cc27233..ee10edf 100644 --- a/migrations/01_tables.go +++ b/migrations/01_tables.go @@ -582,6 +582,7 @@ func pagesTable() *core.Collection { func pagesTableFields() core.FieldsList { fields := core.NewFieldsList( &core.TextField{Name: dbmodels.KEY_FIELD, Required: true, Presentable: true}, + &core.TextField{Name: dbmodels.TITLE_FIELD, Required: false}, &core.TextField{Name: dbmodels.URL_FIELD, Required: false}, &core.TextField{Name: dbmodels.TEMPLATE_FIELD, Required: false}, &core.TextField{Name: dbmodels.LAYOUT_FIELD, Required: false}, diff --git a/migrations/1768403945_page_seed.go b/migrations/1768403945_page_seed.go index 6c2ede0..bbbc6a9 100644 --- a/migrations/1768403945_page_seed.go +++ b/migrations/1768403945_page_seed.go @@ -325,42 +325,42 @@ const ( ) var pageMetaSeed = map[string]PageMeta{ - pageDataKey(pagemodels.P_INDEX_NAME): { + pagemodels.P_INDEX_NAME: { Title: INDEX_TITLE, Description: INDEX_DESCRIPTION, Keywords: "", }, - pageDataKey(pagemodels.P_REIHEN_NAME): { + pagemodels.P_REIHEN_NAME: { Title: REIHEN_TITLE, Description: REIHEN_DESCRIPTION, Keywords: "", }, - pageDataKey(pagemodels.P_DANK_NAME): { + pagemodels.P_DANK_NAME: { Title: "Danksagungen", Description: DANKSAGUNGEN_DESCRIPTION, Keywords: "", }, - pageDataKey(pagemodels.P_EINFUEHRUNG_NAME): { + pagemodels.P_EINFUEHRUNG_NAME: { Title: EINLEITUNG_TITLE, Description: EINLEITUNG_DESCRIPTION, Keywords: "", }, - pageDataKey(pagemodels.P_KONTAKT_NAME): { + pagemodels.P_KONTAKT_NAME: { Title: KONTAKT_TITLE, Description: KONTAKT_DESCRIPTION, Keywords: "", }, - pageDataKey(pagemodels.P_LIT_NAME): { + pagemodels.P_LIT_NAME: { Title: LITERATUR_TITLE, Description: LITERATUR_DESCRIPTION, Keywords: "", }, - pageDataKey(pagemodels.P_DOK_NAME): { + pagemodels.P_DOK_NAME: { Title: DOKUMENTATION_TITLE, Description: DOKUMENTATION_DESCRIPTION, Keywords: "", }, - pageDataKey(pagemodels.P_KABINETT_NAME): { + pagemodels.P_KABINETT_NAME: { Title: KABINETT_TITLE, Description: KABINETT_DESCRIPTION, Keywords: "", @@ -382,7 +382,7 @@ var pageHTMLSeed = map[string]string{ func init() { m.Register(func(app core.App) error { for key, meta := range pageMetaSeed { - if err := upsertData(app, key, meta); err != nil { + if err := upsertPageMeta(app, key, meta); err != nil { return err } } @@ -413,7 +413,7 @@ func init() { } for key := range pageMetaSeed { - if err := deleteByKey(app, dbmodels.DATA_TABLE, key); err != nil { + if err := deleteByKey(app, dbmodels.PAGES_TABLE, key); err != nil { return err } } diff --git a/migrations/page_content_helpers.go b/migrations/page_content_helpers.go index 6d0ecfc..d206024 100644 --- a/migrations/page_content_helpers.go +++ b/migrations/page_content_helpers.go @@ -11,10 +11,6 @@ type PageMeta struct { Keywords string `json:"keywords"` } -func pageDataKey(name string) string { - return "page." + name -} - func pageHTMLKey(name, section string) string { if section == "" { return "page." + name @@ -22,6 +18,33 @@ func pageHTMLKey(name, section string) string { return "page." + name + "." + section } +func upsertPageMeta(app core.App, key string, meta PageMeta) error { + collection, err := app.FindCollectionByNameOrId(dbmodels.PAGES_TABLE) + if err != nil { + return err + } + + record, _ := app.FindFirstRecordByData(collection.Id, dbmodels.KEY_FIELD, key) + if record == nil { + record = core.NewRecord(collection) + record.Set(dbmodels.KEY_FIELD, key) + } + record.Set(dbmodels.TITLE_FIELD, meta.Title) + + data := map[string]any{} + if existing := record.Get(dbmodels.DATA_FIELD); existing != nil { + if existingMap, ok := existing.(map[string]any); ok { + data = existingMap + } else if existingMap, ok := existing.(map[string]interface{}); ok { + data = map[string]any(existingMap) + } + } + data["description"] = meta.Description + data["keywords"] = meta.Keywords + record.Set(dbmodels.DATA_FIELD, data) + return app.Save(record) +} + func upsertHTML(app core.App, key, value string) error { collection, err := app.FindCollectionByNameOrId(dbmodels.HTML_TABLE) if err != nil {