Skip to content

Commit

Permalink
code gen cache busting
Browse files Browse the repository at this point in the history
  • Loading branch information
Pineapple217 committed May 9, 2024
1 parent 61c511d commit d528480
Show file tree
Hide file tree
Showing 26 changed files with 427 additions and 126 deletions.
12 changes: 6 additions & 6 deletions .air.toml
Expand Up @@ -5,23 +5,23 @@ tmp_dir = "tmp"
[build]
args_bin = []
bin = "tmp\\main.exe"
cmd = "go build -o ./tmp/main.exe -tags sqlite_math_functions ./cmd/server"
cmd = "make build"
delay = 1000
exclude_dir = ["assets", "tmp", "vendor", "testdata", "data"]
exclude_file = ["pkg\\database\\models.go", "pkg\\database\\query.sql.go"]
exclude_dir = ["assets", "tmp", "vendor", "testdata", "data", "pkg\\static\\bundle"]
exclude_file = ["pkg\\database\\models.go", "pkg\\database\\query.sql.go", "pkg\\static\\file_paths.go"]
exclude_regex = ["_test.go", ".*_templ.go", ".*.db"]
exclude_unchanged = false
exclude_unchanged = true
follow_symlink = false
full_bin = ""
include_dir = [""]
include_ext = ["go", "tpl", "tmpl", "templ", "html", "sql"]
include_ext = ["go", "tpl", "tmpl", "templ", "html", "sql", "png", "svg", "ico", "css", "woff2"]
include_file = ["main.go"]
kill_delay = "0s"
log = "build-errors.log"
poll = false
poll_interval = 0
post_cmd = []
pre_cmd = ["templ generate", "sqlc generate"]
# pre_cmd = ["templ generate", "sqlc generate", "go run ./cmd/gen/"]
rerun = false
rerun_delay = 500
send_interrupt = false
Expand Down
8 changes: 7 additions & 1 deletion Makefile
@@ -1,4 +1,5 @@
docker-build:
@make --no-print-directory codegen
docker build -t pineapple217/mb:latest --build-arg GIT_COMMIT=$(shell git log -1 --format=%h) .

docker-push:
Expand All @@ -11,10 +12,15 @@ docker-update:
codegen:
templ generate
sqlc generate
go run ./cmd/gen/

build:
@make --no-print-directory codegen
go build -o ./tmp/main.exe ./cmd/server

start:
@./tmp/main.exe
@./tmp/main.exe

run:
@make --no-print-directory build
@make --no-print-directory start
183 changes: 183 additions & 0 deletions cmd/gen/main.go
@@ -0,0 +1,183 @@
package main

import (
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"os"
"path/filepath"
"strings"

"github.com/tdewolff/minify/v2"
"github.com/tdewolff/minify/v2/css"
"golang.org/x/text/cases"
"golang.org/x/text/language"
)

const folderPath = "./pkg/static"
const outputPath = folderPath + "/bundle"
const filePath = folderPath + "/file_paths.go"
const staticFiles = "./pkg/static/public"

const fileHeader = `// Code generated by ./cmd/gen - DO NOT EDIT.
package static
`

var extentions = []string{
".css",
".png",
".svg",
".ico",
}

type hashed struct {
fileName string
path string
}

func main() {
if err := os.RemoveAll(outputPath); err != nil {
fmt.Println("Error creating folder:", err)
return
}

// Create folder if it doesn't exist
if _, err := os.Stat(outputPath); os.IsNotExist(err) {
err := os.MkdirAll(outputPath, 0755)
if err != nil {
fmt.Println("Error creating folder:", err)
return
}
}

// Create or overwrite file
file, err := os.Create(filePath)
if err != nil {
fmt.Println("Error creating file:", err)
return
}
defer file.Close()

// Write content to file
_, err = file.WriteString(fileHeader)
if err != nil {
fmt.Println("Error writing to file:", err)
return
}

hashes := []hashed{}

err = filepath.Walk(staticFiles, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}

// If it's a directory, skip it
if info.IsDir() {
return nil
}

// If it's a file, copy it to the destination
if !info.Mode().IsRegular() {
// Skip special files
return nil
}

extend := ""
for _, ext := range extentions {
if strings.HasSuffix(path, ext) {
extend = ext
break
}
}

if extend != "" {
fileName := strings.TrimSuffix(filepath.Base(path), extend) + "-" + hashFile(path) + extend
dstPath := filepath.Join(outputPath, fileName)
hashes = append(hashes, hashed{
fileName: filepath.Base(path),
path: fileName,
})
fmt.Printf("Copying CSS file: %s\n", path)
return copyFile(path, dstPath)
}

fileName := filepath.Base(path)
dstPath := filepath.Join(outputPath, fileName)
fmt.Printf("Copying file: %s\n", path)
return copyFile(path, dstPath)
})
if err != nil {
fmt.Println(err)
}

for _, h := range hashes {
_, err = file.WriteString(makeConstant(h))
if err != nil {
fmt.Println("Error writing to file:", err)
return
}

}

}

func hashFile(path string) string {
file, err := os.Open(path)
if err != nil {
os.Exit(1)
}
hash := sha256.New()
if _, err := io.Copy(hash, file); err != nil {
os.Exit(1)
}
sum := hash.Sum(nil)

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

func makeConstant(hash hashed) string {
parts := strings.Split(hash.fileName, ".")
var name string
for _, part := range parts {
name += cases.Title(language.Und, cases.NoLower).String(part)
}
name = strings.ReplaceAll(name, "-", "")

path := "/static/" + hash.path

return fmt.Sprintf("const %s string = \"%s\"\n", name, path)
}

func copyFile(src, dst string) error {
srcFile, err := os.Open(src)
if err != nil {
return err
}
defer srcFile.Close()

dstFile, err := os.Create(dst)
if err != nil {
return err
}
defer dstFile.Close()

if filepath.Ext(src) == ".css" {
m := minify.New()
m.AddFunc("text/css", css.Minify)
err := m.Minify("text/css", dstFile, srcFile)
if err != nil {
panic(err)
}
} else {
_, err = io.Copy(dstFile, srcFile)
if err != nil {
return err
}
}

return nil
}
4 changes: 3 additions & 1 deletion go.mod
Expand Up @@ -8,19 +8,21 @@ require (
github.com/joho/godotenv v1.5.1
github.com/labstack/echo/v4 v4.12.0
github.com/mattn/go-sqlite3 v1.14.22
github.com/tdewolff/minify/v2 v2.20.20
golang.org/x/image v0.15.0
golang.org/x/text v0.14.0
)

require (
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/labstack/gommon v0.4.2 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/tdewolff/parse/v2 v2.7.13 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
)
7 changes: 7 additions & 0 deletions go.sum
Expand Up @@ -25,6 +25,13 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tdewolff/minify/v2 v2.20.20 h1:vhULb+VsW2twkplgsawAoUY957efb+EdiZ7zu5fUhhk=
github.com/tdewolff/minify/v2 v2.20.20/go.mod h1:GYaLXFpIIwsX99apQHXfGdISUdlA98wmaoWxjT9C37k=
github.com/tdewolff/parse/v2 v2.7.13 h1:iSiwOUkCYLNfapHoqdLcqZVgvQ0jrsao8YYKP/UJYTI=
github.com/tdewolff/parse/v2 v2.7.13/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA=
github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739 h1:IkjBCtQOOjIn03u/dMQK9g+Iw9ewps4mCl1nB8Sscbo=
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
Expand Down
35 changes: 35 additions & 0 deletions pkg/handler/manifest.go
@@ -0,0 +1,35 @@
package handler

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

"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"`
}

var manfest = IconSet{
Icons: []Icon{
{Src: static.Icon192Png, Type: "image/png", Sizes: "192x192"},
{Src: static.Icon512Png, Type: "image/png", Sizes: "512x512"},
},
}

func (h *Handler) Manifest(c echo.Context) error {
jsonData, err := json.Marshal(manfest)
if err != nil {
return err
}
c.Response().Header().Set("Content-Type", "application/manifest+json")
return c.String(http.StatusOK, string(jsonData))
}
18 changes: 6 additions & 12 deletions pkg/server/routes.go
@@ -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, "bundle"))

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

e.GET("robot.txt", hdlr.RobotTxt)
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.

File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes
File renamed without changes
1 change: 1 addition & 0 deletions pkg/static/bundle/main-67353a3a089c.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions pkg/static/embed.go
@@ -0,0 +1,10 @@
package static

import (
"embed"
)

var (
//go:embed bundle/*
PublicFS embed.FS
)
10 changes: 10 additions & 0 deletions pkg/static/file_paths.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file added pkg/static/public/apple-touch-icon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Expand Up @@ -15,7 +15,7 @@
}
@font-face {
font-family: "Terminus (TTF)";
src: url("/static/fonts/TerminusTTF.woff2") format("woff2");
src: url("/static/TerminusTTF.woff2") format("woff2");
}

html,
Expand Down
Binary file added pkg/static/public/favicon.ico
Binary file not shown.
Binary file added pkg/static/public/fonts/TerminusTTF.woff2
Binary file not shown.
Binary file added pkg/static/public/icon-192.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added pkg/static/public/icon-512.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit d528480

Please sign in to comment.