Skip to content

Commit

Permalink
added cache busting for static files
Browse files Browse the repository at this point in the history
  • Loading branch information
Pineapple217 committed May 9, 2024
1 parent 141438c commit b0e0997
Show file tree
Hide file tree
Showing 15 changed files with 293 additions and 119 deletions.
7 changes: 5 additions & 2 deletions cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/Pineapple217/mb/pkg/media"
"github.com/Pineapple217/mb/pkg/scheduler"
"github.com/Pineapple217/mb/pkg/server"
"github.com/Pineapple217/mb/pkg/static"
)

// TODO: banner does not get printed first
Expand All @@ -21,7 +22,7 @@ const banner = `
·██ ▐███▪▐█ ▀█▪
▐█ ▌▐▌▐█·▐█▀▀█▄
██ ██▌▐█▌██▄▪▐█
▀▀ █▪▀▀▀·▀▀▀▀ v0.7.1
▀▀ █▪▀▀▀·▀▀▀▀ v0.7.2
Minimal blog with no JavaScript
https://github.com/Pineapple217/mb
-----------------------------------------------------------------------------`
Expand All @@ -35,12 +36,14 @@ func main() {

config.Load()

rr := static.HashPublicFS()

q := database.NewQueries("file:" + config.DataDir + "/database.db")
h := handler.NewHandler(q)

server := server.NewServer()
server.RegisterRoutes(h)
server.ApplyMiddleware(q)
server.ApplyMiddleware(q, rr)

server.Start()

Expand Down
35 changes: 35 additions & 0 deletions pkg/handler/manifest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package handler

import (
"encoding/json"
"net/http"

s "github.com/Pineapple217/mb/pkg/static"
"github.com/labstack/echo/v4"
)

type Icon struct {
Src string `json:"src"`
Type string `json:"type"`
Sizes string `json:"sizes"`
}

type IconSet struct {
Icons []Icon `json:"icons"`
}

func (h *Handler) Manifest(c echo.Context) error {
// TODO: could cache the man
var manifest = IconSet{
Icons: []Icon{
{Src: s.StaticMap["/static/icon-192.png"], Type: "image/png", Sizes: "192x192"},
{Src: s.StaticMap["/static/icon-512.png"], Type: "image/png", Sizes: "512x512"},
},
}
jsonData, err := json.Marshal(manifest)
if err != nil {
return err
}
c.Response().Header().Set("Content-Type", "application/manifest+json")
return c.String(http.StatusOK, string(jsonData))
}
3 changes: 2 additions & 1 deletion pkg/server/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ import (
echoMw "github.com/labstack/echo/v4/middleware"
)

func (s *Server) ApplyMiddleware(q *database.Queries) {
func (s *Server) ApplyMiddleware(q *database.Queries, reRoutes map[string]string) {
slog.Info("Applying middlewares")
s.e.Pre(echoMw.Rewrite(reRoutes))
s.e.Use(echoMw.RequestLoggerWithConfig(echoMw.RequestLoggerConfig{
LogStatus: true,
LogURI: true,
Expand Down
16 changes: 5 additions & 11 deletions pkg/server/routes.go
Original file line number Diff line number Diff line change
@@ -1,39 +1,33 @@
package server

import (
"embed"
"log/slog"
"time"

"github.com/Pineapple217/mb/pkg/config"
"github.com/Pineapple217/mb/pkg/handler"
"github.com/Pineapple217/mb/pkg/middleware"
"github.com/Pineapple217/mb/pkg/static"
"github.com/labstack/echo/v4"
)

var (
//go:embed static/public/*
publicFS embed.FS
)

func (server *Server) RegisterRoutes(hdlr *handler.Handler) {
slog.Info("Registering routes")
e := server.e

s := e.Group("/static")
// TODO: post issue, StaticFS not getting cached
bootTime := time.Now().Add(-2 * time.Hour)

s.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
c.Response().Header().Add("Last-Modified", bootTime.Local().UTC().Format("Mon, 2 Jan 2006 15:04:05 GMT"))
c.Response().Header().Add("Cache-Control", "public, max-age=31536000, immutable")
return next(c)
}
})
s.StaticFS("/", echo.MustSubFS(publicFS, "static/public"))
s.StaticFS("/", echo.MustSubFS(static.PublicFS, "public"))

e.GET("/index.xml", hdlr.RSSFeed)

e.GET("robot.txt", hdlr.RobotTxt)
e.GET("/site.webmanifest", hdlr.Manifest)

//TODO better caching with http headers

Expand Down
6 changes: 0 additions & 6 deletions pkg/server/static/public/site.webmanifest

This file was deleted.

93 changes: 93 additions & 0 deletions pkg/static/embed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package static

import (
"crypto/sha256"
"embed"
"encoding/hex"
"fmt"
"io"
"io/fs"
"log/slog"
"path/filepath"
"strings"
)

var (
//go:embed public/*
PublicFS embed.FS
excludeExts = []string{".woff2"}
StaticMap map[string]string
)

func hashFile(file fs.File) (string, error) {
hash := sha256.New()
if _, err := io.Copy(hash, file); err != nil {
return "", err
}
sum := hash.Sum(nil)

return hex.EncodeToString(sum)[:12], nil
}

func hashFiles(f embed.FS) (map[string]string, error) {
filesDetails := map[string]string{}

err := fs.WalkDir(f, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if !d.IsDir() {
ext := filepath.Ext(path)
for _, e := range excludeExts {
if ext == e {
return nil
}
}

file, err := f.Open(path)
if err != nil {
return err
}
defer file.Close()

hash, err := hashFile(file)
if err != nil {
return err
}

fileName := strings.TrimSuffix(d.Name(), ext)

newFilename := fmt.Sprintf("%s-%s%s", fileName, hash, ext)
basePath := strings.TrimSuffix(path, d.Name())
basePath = strings.ReplaceAll(basePath, "public", "/static")

p := strings.ReplaceAll(path, "public", "/static")
filesDetails[p] = basePath + newFilename
}
return nil
})

if err != nil {
return nil, err
}

return filesDetails, nil
}

func HashPublicFS() map[string]string {
slog.Info("Hashing static files")
files, err := hashFiles(PublicFS)
if err != nil {
panic(err)
}

for k, v := range files {
slog.Info("file hashed", "old", k, "new", v)
}
StaticMap = files
swappedMap := make(map[string]string)
for k, v := range files {
swappedMap[v] = k
}
return swappedMap
}
File renamed without changes
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes
File renamed without changes
11 changes: 6 additions & 5 deletions pkg/view/base.templ
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/Pineapple217/mb/pkg/database"
ct "github.com/Pineapple217/mb/pkg/context"
"github.com/Pineapple217/mb/pkg/config"
s "github.com/Pineapple217/mb/pkg/static"
)

templ boiler(desc string) {
Expand All @@ -21,11 +22,11 @@ templ boiler(desc string) {
if desc != "" {
<meta name="description" content={ desc }/>
}
<link rel="icon" href="/static/favicon.ico" sizes="32x32"/>
<link rel="icon" href="/static/icon.svg" type="image/svg+xml"/>
<link rel="apple-touch-icon" href="/static/apple-touch-icon.png"/>
<link rel="manifest" href="/static/site.webmanifest"/>
<link rel="stylesheet" href="/static/css/main.css"/>
<link rel="icon" href={ s.StaticMap["/static/favicon.ico"] } sizes="32x32"/>
<link rel="icon" href={ s.StaticMap["/static/icon.svg"] } type="image/svg+xml"/>
<link rel="apple-touch-icon" href={ s.StaticMap["/static/apple-touch-icon.png"] }/>
<link rel="manifest" href="/site.webmanifest"/>
<link rel="stylesheet" href={ s.StaticMap["/static/css/main.css"] }/>
<title>mb</title>
</head>
<a href="/auth" accesskey="a" aria-hidden="true" tabindex="-1"></a>
Expand Down
Loading

0 comments on commit b0e0997

Please sign in to comment.