package seo import ( "encoding/json" "encoding/xml" "fmt" "net/http" "path" "time" "github.com/pocketbase/pocketbase" "github.com/pocketbase/pocketbase/core" "github.com/pocketbase/pocketbase/tools/router" ) type MenuRecord struct { Active bool `json:"active"` CollectionID string `json:"collectionId"` CollectionName string `json:"collectionName"` Created string `json:"created"` ID string `json:"id"` IDLang string `json:"id_lang"` IDPage string `json:"id_page"` IDParent string `json:"id_parent"` IsDefault bool `json:"is_default"` IsRoot bool `json:"is_root"` LinkRewrite string `json:"link_rewrite"` LinkTitle string `json:"link_title"` MetaDescription string `json:"meta_description"` MetaTitle string `json:"meta_title"` Name string `json:"name"` PageName string `json:"page_name"` PositionID int `json:"position_id"` Updated string `json:"updated"` Url string `json:"url"` } type UrlSet struct { XMLName xml.Name `xml:"urlset"` Xmlns string `xml:"xmlns,attr"` Urls []Url `xml:"url"` } type Url struct { Loc string `xml:"loc"` LastMod string `xml:"lastmod,omitempty"` ChangeFreq string `xml:"changefreq,omitempty"` Priority string `xml:"priority,omitempty"` } func ServeFeeds(app *pocketbase.PocketBase, se *core.ServeEvent) *router.Route[*core.RequestEvent] { return se.Router.GET("/feeds/{lang}/sitemap.xml", func(e *core.RequestEvent) error { lang := e.Request.PathValue("lang") urls, err := getLocations(app, lang) if err != nil { return err } xx := UrlSet{ Xmlns: "http://www.sitemaps.org/schemas/sitemap/0.9", Urls: urls, } bytes, err := xml.MarshalIndent(xx, "", " ") if err != nil { return err } e.Response.Header().Add("content-type", "text/xml") return e.String(http.StatusOK, xml.Header+string(bytes)) }) } func getLocations(app *pocketbase.PocketBase, lang string) ([]Url, error) { records, err := app.FindRecordsByFilter("menu_view", fmt.Sprintf("id_lang='%s'&&active=true", lang), "position_id", 200, 0) if err != nil { return nil, err } baseUrl, err := getBaseUrl(app) if err != nil { return nil, err } locations := []Url{} lastMod := time.Now().Add(time.Hour * 24 * 7 * -1).Format("2006-01-02") for _, r := range records { rec := MenuRecord{} x, _ := r.MarshalJSON() json.Unmarshal(x, &rec) if rec.IsRoot { continue } if len(rec.Url) > 0 { continue } if rec.IsDefault { locations = append(locations, Url{ Loc: baseUrl + path.Join(lang), LastMod: lastMod, ChangeFreq: "weekly", Priority: "1.0", }) } locations = append(locations, Url{ Loc: baseUrl + path.Join(lang, rec.IDPage, rec.LinkRewrite), LastMod: lastMod, ChangeFreq: "weekly", Priority: "1.0", }) } return locations, nil } type BaseUrl struct { BaseURL string `json:"baseUrl"` } func getBaseUrl(app *pocketbase.PocketBase) (string, error) { record, err := app.FindFirstRecordByFilter("settings", "key='baseUrl'", nil) if err != nil { return "", err } settings := BaseUrl{} json.Unmarshal([]byte(record.GetString("value")), &settings) if err != nil { return "", err } return settings.BaseURL, nil } type SitemapIndex struct { XMLName xml.Name `xml:"sitemapindex"` Xmlns string `xml:"xmlns,attr"` Sitemaps []Sitemap `xml:"sitemap"` } // Sitemap represents each entry type Sitemap struct { Loc string `xml:"loc"` LastMod string `xml:"lastmod"` } func ServeFeedsIndex(app *pocketbase.PocketBase, se *core.ServeEvent) *router.Route[*core.RequestEvent] { return se.Router.GET("/feeds/index.xml", func(e *core.RequestEvent) error { index, err := makeSiteMapIndex(app) if err != nil { return err } bytes, err := xml.MarshalIndent(index, "", " ") if err != nil { return err } e.Response.Header().Add("content-type", "text/xml") return e.String(http.StatusOK, xml.Header+string(bytes)) }) } func makeSiteMapIndex(app *pocketbase.PocketBase) (*SitemapIndex, error) { index := &SitemapIndex{ Xmlns: "http://www.sitemaps.org/schemas/sitemap/0.9", } bseUrl, err := getBaseUrl(app) if err != nil { return nil, err } langs, err := app.FindRecordsByFilter("lang", "active=true", "", 200, 0) if err != nil { return index, err } lastMod := time.Now().Add(time.Hour * 24 * 7 * -1).Format("2006-01-02") for _, l := range langs { index.Sitemaps = append(index.Sitemaps, Sitemap{ Loc: bseUrl + path.Join("feeds/", l.Id, "sitemap.xml"), LastMod: lastMod, }) } return index, nil }