Diffstat (limited to 'internal/router/router.go')
| -rw-r--r-- | internal/router/router.go | 152 |
1 files changed, 45 insertions, 107 deletions
diff --git a/internal/router/router.go b/internal/router/router.go index a11a934..5230e74 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -7,11 +7,8 @@ import ( "fmt" "html/template" "log/slog" - "net" "net/url" - "os" "slices" - "strconv" "strings" "time" @@ -23,6 +20,7 @@ import ( "github.com/SayaAndy/saya-today-web/internal/mailer" "github.com/SayaAndy/saya-today-web/internal/tailwind" "github.com/SayaAndy/saya-today-web/internal/templatemanager" + "github.com/SayaAndy/saya-today-web/locale" "github.com/dgraph-io/ristretto/v2" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/compress" @@ -94,6 +92,7 @@ type Route interface { type Supplements struct { DB *sql.DB BlogClient blog.Client + Localization map[string]*locale.LocaleConfig AvailableLanguages []config.AvailableLanguageConfig ClientCache *ClientCache PageCache *ristretto.Cache[string, []byte] @@ -102,9 +101,9 @@ type Supplements struct { BlogTrigger *blogtrigger.BlogTriggerScheduler TemplateManager *templatemanager.TemplateManager MarkdownRenderer goldmark.Markdown - Meta []config.MetaConfig + Meta config.MetaConfig PhotoStorage config.PhotoStorageConfig - StaticStorage config.StaticStorageConfig + StaticStorage config.PhotoTypeConfig } type Router struct { @@ -113,7 +112,6 @@ type Router struct { templatedRoutes map[string]map[string]Route templatedPathMatcher *PathMatcher canonicalEndpoint string - endpoint config.EndpointConfig } func NewRouter(cfg *config.Config) (*Router, error) { @@ -147,7 +145,16 @@ func NewRouter(cfg *config.Config) (*Router, error) { supplements.BlogClient, err = blog.NewClientMap[cfg.BlogPages.Storage.Type](&cfg.BlogPages.Storage) if err != nil { - return nil, fmt.Errorf("fail to initialize blog client: type %s: %w", cfg.BlogPages.Storage.Type, err) + return nil, fmt.Errorf("fail to initialize b2 client: %w", err) + } + + supplements.Localization = make(map[string]*locale.LocaleConfig, len(cfg.AvailableLanguages)) + for _, lang := range cfg.AvailableLanguages { + localeCfg, err := locale.InitConfig(cfg.LocalePath + lang.LocFile) + if err != nil { + return nil, fmt.Errorf("fail to initialize a locale: %w", err) + } + supplements.Localization[lang.Name] = localeCfg } supplements.MarkdownRenderer = goldmark.New( @@ -189,7 +196,7 @@ func NewRouter(cfg *config.Config) (*Router, error) { } supplements.Mailer, err = mailer.NewMailer(supplements.DB, cfg.Mail.ClientHost, cfg.Mail.MailHost, - cfg.Mail.PublicName, cfg.Mail.MailAddress, cfg.Mail.Username, cfg.Mail.Password, []byte(cfg.Mail.Salt)) + cfg.Mail.PublicName, cfg.Mail.MailAddress, cfg.Mail.Username, cfg.Mail.Password, []byte(cfg.Mail.Salt), supplements.Localization) if err != nil { return nil, fmt.Errorf("fail to initialize mailer: %w", err) } @@ -249,7 +256,7 @@ func NewRouter(cfg *config.Config) (*Router, error) { templatedRoutes := make(map[string]map[string]Route) templatedPathMatcher := NewPathMatcher() - return &Router{supplements, app, templatedRoutes, templatedPathMatcher, cfg.CanonicalEndpoint, cfg.Endpoint}, nil + return &Router{supplements, app, templatedRoutes, templatedPathMatcher, cfg.CanonicalEndpoint}, nil } func (r *Router) InitRoutes() (err error) { @@ -298,17 +305,16 @@ func (r *Router) InitRoutes() (err error) { cacheKey = fmt.Sprintf("%s.full-page.%s", method, trimmedPath) case ByUrlAndQuery: cacheKey = fmt.Sprintf("%s.full-page.%s.%s", method, trimmedPath, queryString) - case Disabled: - c.Set("Cache-Control", "no-store, no-cache, must-revalidate") } defaultMap := fiber.Map{ - "Lang": lang, - "Path": trimmedPath, - "QueryString": queryString, - "CanonicalEndpoint": r.canonicalEndpoint, - "StaticStorage": r.supplements.StaticStorage, - "PhotoStorage": r.supplements.PhotoStorage, + "L": r.supplements.Localization[lang], + "Lang": lang, + "Path": trimmedPath, + "QueryString": queryString, + "CanonicalEndpoint": r.canonicalEndpoint, + "StaticStorageBaseUrl": r.supplements.StaticStorage.BaseUrl, + "ThumbnailBaseUrl": r.supplements.PhotoStorage.Thumbnail320p.BaseUrl, } statusCode, err := currentRoute.Render(c, r.supplements, lang, defaultMap) @@ -361,7 +367,6 @@ func (r *Router) InitRoutes() (err error) { c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8) return c.Status(fiber.ErrBadRequest.Code).SendString(fmt.Sprintf("unknown segment '%s'", part)) } - c.Set("Cache-Control", "no-store, no-cache, must-revalidate") err := r.generalPageSegment(c, part) return err }) @@ -377,35 +382,10 @@ func (r *Router) InitRoutes() (err error) { return nil } -func (r *Router) Listen() error { - switch r.endpoint.Type { - case "unix": - unixConfig := r.endpoint.Config.(*config.UnixConfig) - endpoint, _ := strings.CutPrefix(unixConfig.Path, "unix://") - - if err := os.Remove(endpoint); err != nil && !errors.Is(err, os.ErrNotExist) { - return fmt.Errorf("error while cleaning up existing unix socket: %w", err) - } - - ln, err := net.Listen("unix", endpoint) - if err != nil { - return fmt.Errorf("error while initializing unix listener: %w", err) - } - chmod, _ := strconv.ParseUint(unixConfig.Chmod[1:], 8, 32) - os.Chmod(unixConfig.Path, os.FileMode(chmod)) - if err := r.app.Listener(ln); err != nil { - return fmt.Errorf("error while running fiber server: %w", err) - } - case "http": - httpConfig := r.endpoint.Config.(*config.HttpConfig) - fmt.Print(httpConfig) - if err := r.app.Listen(httpConfig.ListenOn); err != nil { - return fmt.Errorf("error while running fiber server: %w", err) - } - default: - return fmt.Errorf("error with initializing fiber server: invalid endpoint type (supported are unix and http)") +func (r *Router) Listen(endpoint string) error { + if err := r.app.Listen(endpoint); err != nil { + return fmt.Errorf("error while running fiber server: %w", err) } - return nil } @@ -429,7 +409,6 @@ func (r *Router) Close() (err error) { } slog.Debug("closing page cache") r.supplements.PageCache.Close() - return errors.Join(allErrors...) } @@ -439,8 +418,8 @@ func (r *Router) generalPage(c *fiber.Ctx, route Route, lang string) error { path := c.Path() method := c.Method() trimmedPath := strings.Trim(path, "/") - queryString := c.Request().URI().QueryString() cacheKey := "" + queryString := c.Request().URI().QueryString() switch route.ToCache() { case ByUrlOnly: @@ -455,12 +434,12 @@ func (r *Router) generalPage(c *fiber.Ctx, route Route, lang string) error { } valueMap := fiber.Map{ - "Lang": lang, - "Path": trimmedPath, - "QueryString": queryString, - "CanonicalEndpoint": r.canonicalEndpoint, - "StaticStorage": r.supplements.StaticStorage, - "PhotoStorage": r.supplements.PhotoStorage, + "L": r.supplements.Localization[lang], + "Lang": lang, + "QueryString": queryString, + "CanonicalEndpoint": r.canonicalEndpoint, + "StaticStorageBaseUrl": r.supplements.StaticStorage.BaseUrl, + "ThumbnailBaseUrl": r.supplements.PhotoStorage.Thumbnail320p.BaseUrl, } var err error @@ -477,52 +456,10 @@ func (r *Router) generalPage(c *fiber.Ctx, route Route, lang string) error { valueMap["LinkedData"] = template.JS(ldBytes) } - syntheticReferer := path - if len(queryString) > 0 { - syntheticReferer += "?" + string(queryString) - } - c.Request().Header.Set("Referer", syntheticReferer) - - parts := []struct { - name string - key string - render func(c *fiber.Ctx, supplements *Supplements, lang string, templateMap fiber.Map) (int, error) - }{ - {"top-embeds", "RenderedTopEmbeds", route.RenderTopEmbeds}, - {"header", "RenderedHeader", route.RenderHeader}, - {"body", "RenderedBody", route.RenderBody}, - {"footer", "RenderedFooter", route.RenderFooter}, - {"bottom-embeds", "RenderedBottomEmbeds", route.RenderBottomEmbeds}, - } - - for _, p := range parts { - statusCode, err := p.render(c, r.supplements, lang, valueMap) - if err != nil { - slog.Error("failed to render segment for full page", - slog.String("path", path), - slog.String("segment", p.name), - slog.String("error", err.Error()), - ) - c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8) - return c.Status(statusCode).SendString(err.Error()) - } - segContent, err := r.supplements.TemplateManager.Render("general-page-"+p.name, valueMap, route.TemplatesToInject()...) - if err != nil { - slog.Error("failed to render segment template for full page", - slog.String("path", path), - slog.String("segment", p.name), - slog.String("error", err.Error()), - ) - c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8) - return c.Status(fiber.ErrInternalServerError.Code).SendString("failed to generate segment") - } - valueMap[p.key] = template.HTML(segContent) - } - content, err := r.supplements.TemplateManager.Render("general-page", valueMap) if err != nil { - slog.Warn("failed to generate full page", slog.String("path", path), slog.String("error", err.Error())) - return c.Status(fiber.ErrInternalServerError.Code).SendString("failed to generate full page") + slog.Warn("failed to generate div", slog.String("path", path), slog.String("error", err.Error())) + return c.Status(fiber.ErrInternalServerError.Code).SendString("failed to generate div") } go r.supplements.PageCache.SetWithTTL(cacheKey, content, int64(len(content)), route.CacheDuration()) @@ -563,12 +500,11 @@ func (r *Router) generalPageSegment(c *fiber.Ctx, part string) error { cacheKey := "" trimmedPath := strings.Trim(path, "/") - requestQuery := string(c.Request().URI().QueryString()) switch route.ToCache() { case ByUrlOnly: cacheKey = fmt.Sprintf("%s.%s.%s", method, part, trimmedPath) case ByUrlAndQuery: - cacheKey = fmt.Sprintf("%s.%s.%s.%s.%s", method, part, trimmedPath, queryString, requestQuery) + cacheKey = fmt.Sprintf("%s.%s.%s.%s", method, part, trimmedPath, queryString) } if route.ToCache() != Disabled { @@ -580,12 +516,13 @@ func (r *Router) generalPageSegment(c *fiber.Ctx, part string) error { var statusCode int defaultMap := fiber.Map{ - "Lang": lang, - "Path": strings.Trim(path, "/"), - "QueryString": queryString, - "CanonicalEndpoint": r.canonicalEndpoint, - "StaticStorage": r.supplements.StaticStorage, - "PhotoStorage": r.supplements.PhotoStorage, + "L": r.supplements.Localization[lang], + "Lang": lang, + "Path": strings.Trim(path, "/"), + "QueryString": queryString, + "CanonicalEndpoint": r.canonicalEndpoint, + "StaticStorageBaseUrl": r.supplements.StaticStorage.BaseUrl, + "ThumbnailBaseUrl": r.supplements.PhotoStorage.Thumbnail320p.BaseUrl, } switch part { @@ -677,7 +614,8 @@ func (r *Router) getAndValidateLang(c *fiber.Ctx, langSetting LangSetting, defau return lang, nil } } - return "", fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("lang value is invalid: '%s' is not considered an available language", lang)) + c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8) + return "", c.Status(fiber.ErrBadRequest.Code).SendString(fmt.Sprintf("lang value is invalid: '%s' is not considered an available language", lang)) } func GetPathFromReferer(c *fiber.Ctx) (path string, pathParts []string, queryString string, err error) { |