package classes import ( "context" "strings" "git.golem15.com/golem15/sm-seo-plugin/models" "git.golem15.com/golem15/sm-sitemap-plugin/contract" ) // SiteAnswer is the answer of GET /_seo/api/v1/site (P33-17): the facts // about the whole site the frontend layer needs, all public. SiteURL, // DefaultLocale, Locales and UnprefixedLocale are the resolver's; Links // the settings' new-tab switch; HTMLSitemap the sitemap plugin's HTML // sitemap settings; Routes each content type's layered URL pattern for // the layer's drift check; RootFiles the file names the layer serves at // the site root (verification files, the IndexNow key file); LLMS whether // /llms.txt is served; Pagination the site's page address form and // HomeCrumb the home crumb (33-06, D-40, D-86). type SiteAnswer struct { SiteURL string `json:"site_url"` DefaultLocale string `json:"default_locale"` Locales []string `json:"locales"` UnprefixedLocale string `json:"unprefixed_locale"` Links SiteLinks `json:"links"` HTMLSitemap SiteHTMLSitemap `json:"html_sitemap"` Routes []SiteRoute `json:"routes"` RootFiles []string `json:"root_files"` LLMS bool `json:"llms"` Pagination SitePagination `json:"pagination"` HomeCrumb SiteHomeCrumb `json:"home_crumb"` } // SitePagination is the site answer's pagination block: the form (query // or path), the query parameter and the path segment per locale, so the // layer builds numbered links and checks drift. type SitePagination struct { Form string `json:"form"` Param string `json:"param"` Segments map[string]string `json:"segments"` } // SiteHomeCrumb is the home crumb: its name in the default locale, the // home URL and the name per locale. type SiteHomeCrumb struct { Name string `json:"name"` URL string `json:"url"` Names map[string]string `json:"names"` } // SiteLinks is the site answer's links block. type SiteLinks struct { NewTab bool `json:"new_tab"` } // SiteHTMLSitemap is the site answer's HTML sitemap block: whether the // sitemap plugin serves it, its path per locale and its page size. type SiteHTMLSitemap struct { Enabled bool `json:"enabled"` Paths map[string]string `json:"paths"` PageSize int `json:"page_size"` } // SiteRoute is one content type's URL pattern. type SiteRoute struct { Type string `json:"type"` Pattern string `json:"pattern"` } // listing returns the sitemap plugin's published contract.Listing, nil // when none is published. func (s *Service) listing() contract.Listing { if s == nil || s.app == nil { return nil } l, ok := s.app.Lookup[contract.Listing]() if !ok { return nil } return l } // Site builds the site answer. It names only public facts: no token, no // unpublished data (T-33-19). func (s *Service) Site(ctx context.Context) (SiteAnswer, error) { out := SiteAnswer{ SiteURL: s.SiteURL(), UnprefixedLocale: s.UnprefixedLocale(), HTMLSitemap: SiteHTMLSitemap{Paths: map[string]string{}}, Routes: []SiteRoute{}, RootFiles: []string{}, } db, err := s.DB(ctx) if err != nil { return out, err } settings, err := LoadSettings(db) if err != nil { return out, err } robots, err := LoadRobots(db) if err != nil { return out, err } out.DefaultLocale = s.DefaultLocale(ctx, db) out.Locales = s.Locales(ctx, db) out.Links.NewTab = settings.LinksNewTab out.LLMS = robots.LlmsEnabled pages := s.PageConfigOf(ctx, db, settings) out.Pagination = SitePagination{Form: pages.Form, Param: PageParam, Segments: pages.Segments} out.HomeCrumb = SiteHomeCrumb{URL: out.SiteURL + "/", Names: map[string]string{}} for _, locale := range out.Locales { out.HomeCrumb.Names[locale] = s.HomeCrumbName(ctx, db, settings, locale) } out.HomeCrumb.Name = out.HomeCrumb.Names[out.DefaultLocale] if reg := s.Registry(); reg != nil { for _, typ := range reg.ContentTypes() { if typ.Key == PageTypeKey { continue } out.Routes = append(out.Routes, SiteRoute{Type: typ.Key, Pattern: reg.Pattern(typ.Key, "")}) } } seen := map[string]bool{} addFile := func(name string) { name = strings.TrimSpace(name) if name != "" && verificationName.MatchString(name) && !seen[name] { seen[name] = true out.RootFiles = append(out.RootFiles, name) } } for _, f := range settings.Files() { addFile(f.Filename) } if l := s.listing(); l != nil { st, err := l.Settings(ctx) if err != nil { return out, err } out.HTMLSitemap.Enabled = st.HTMLEnabled for locale, path := range st.HTMLPaths { if strings.TrimSpace(path) != "" { out.HTMLSitemap.Paths[locale] = path } } out.HTMLSitemap.PageSize = st.HTMLPageSize addFile(st.KeyFile) } return out, nil } // SiteName is the site's name in locale: the settings' site name, else // the application name. func (s *Service) SiteName(ctx context.Context, settings models.Settings, locale string) string { db, err := s.DB(ctx) name := settings.SiteName if err == nil { name = s.translatedSetting(ctx, db, &settings, "site_name", settings.SiteName, locale) } if strings.TrimSpace(name) == "" { if cfg := s.Config(); cfg != nil { name = cfg.String("app.name") } } return strings.TrimSpace(name) }