Skip to content

Commit

Permalink
Add caching duration of 1 year + md5 hashing based cach busting
Browse files Browse the repository at this point in the history
  • Loading branch information
Bios-Marcel committed Aug 16, 2023
1 parent 085b80b commit c3f69b8
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 50 deletions.
4 changes: 4 additions & 0 deletions cmd/scribblers/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ func main() {

api.SetupRoutes(cfg.RootPath, router)

if err := frontend.Init(); err != nil {
log.Fatal("error setting up runnign frontend init:", err)
}

// FIXME Global state needs to be deleted.
frontend.SetRootPath(cfg.RootPath)
frontend.SetupRoutes(cfg.RootPath, router)
Expand Down
48 changes: 39 additions & 9 deletions internal/frontend/http.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package frontend

import (
"crypto/md5"
"embed"
"fmt"
"html/template"
"net/http"
"path"
Expand All @@ -19,15 +21,32 @@ var (
frontendResourcesFS embed.FS
)

// In this init hook we initialize all templates that could at some point
// be needed during the server runtime. If any of the templates can't be
// loaded, we panic.
func init() {
var templateParseError error
pageTemplates, templateParseError = template.ParseFS(templateFS, "templates/*")
if templateParseError != nil {
panic(templateParseError)
func Init() error {
var err error
pageTemplates, err = template.ParseFS(templateFS, "templates/*")
if err != nil {
return fmt.Errorf("error loading templates: %w", err)
}

entries, err := frontendResourcesFS.ReadDir("resources")
if err != nil {
return fmt.Errorf("error reading resource directory: %w", err)
}

hash := md5.New()
for _, entry := range entries {
bytes, err := frontendResourcesFS.ReadFile("resources/" + entry.Name())
if err != nil {
return fmt.Errorf("error reading resource %s: %w", entry.Name(), err)
}

if _, err := hash.Write(bytes); err != nil {
return fmt.Errorf("error hashing resource %s: %w", entry.Name(), err)
}
}

currentBasePageConfig.CacheBust = fmt.Sprintf("%x", hash.Sum(nil))
return nil
}

// FIXME Delete global state.
Expand All @@ -47,16 +66,27 @@ type BasePageConfig struct {
// but already host a different website, then your API paths might have to
// look like this: painting.com/scribblers/v1.
RootPath string `json:"rootPath"`
// CacheBust is a string that is appended to all resources to prevent
// browsers from using cached data of a previous version, but still have
// long lived max age values.
CacheBust string `json:"cacheBust"`
}

// SetupRoutes registers the official webclient endpoints with the http package.
func SetupRoutes(rootPath string, router chi.Router) {
router.Get("/"+rootPath, homePage)

fileServer := http.FileServer(http.FS(frontendResourcesFS))
router.Get(
"/"+path.Join(rootPath, "resources/*"),
http.StripPrefix(
"/"+rootPath,
http.FileServer(http.FS(frontendResourcesFS)),
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Duration of 1 year, since we use cachebusting anyway.
w.Header().Set("Cache-Control", "public, max-age=31536000")

fileServer.ServeHTTP(w, r)
}),
).ServeHTTP,
)
router.Get("/"+path.Join(rootPath, "ssrEnterLobby/{lobby_id}"), ssrEnterLobby)
Expand Down
4 changes: 2 additions & 2 deletions internal/frontend/templates/error.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
<title>Scribble.rs - Error</title>
<meta charset="UTF-8" />
{{template "non-static-css-decl" .}}
<link rel="stylesheet" type="text/css" href="{{.RootPath}}/resources/base.css" />
<link rel="stylesheet" type="text/css" href="{{.RootPath}}/resources/error.css" />
<link rel="stylesheet" type="text/css" href="{{.RootPath}}/resources/base.css?cache_bust={{.CacheBust}}" />
<link rel="stylesheet" type="text/css" href="{{.RootPath}}/resources/error.css?cache_bust={{.CacheBust}}" />
{{template "favicon-decl" .}}
</head>

Expand Down
8 changes: 4 additions & 4 deletions internal/frontend/templates/favicon.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{{define "favicon-decl"}}
<link rel="icon" type="image/svg+xml" href="{{.RootPath}}/resources/favicon.svg" sizes="any">
<link rel="icon" type="image/png" sizes="16x16" href="{{.RootPath}}/resources/favicon_16.png">
<link rel="icon" type="image/png" sizes="32x32" href="{{.RootPath}}/resources/favicon_32.png">
<link rel="icon" type="image/png" sizes="92x92" href="{{.RootPath}}/resources/favicon_92.png">
<link rel="icon" type="image/svg+xml" href="{{.RootPath}}/resources/favicon.svg?cache_bust={{.CacheBust}}" sizes="any">
<link rel="icon" type="image/png" sizes="16x16" href="{{.RootPath}}/resources/favicon_16.png?cache_bust={{.CacheBust}}">
<link rel="icon" type="image/png" sizes="32x32" href="{{.RootPath}}/resources/favicon_32.png?cache_bust={{.CacheBust}}">
<link rel="icon" type="image/png" sizes="92x92" href="{{.RootPath}}/resources/favicon_92.png?cache_bust={{.CacheBust}}">
{{end}}
54 changes: 31 additions & 23 deletions internal/frontend/templates/lobby.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=0.75 maximum-scale=0.75, user-scalable=0">
{{template "non-static-css-decl" .}}
<link rel="stylesheet" type="text/css" href="{{.RootPath}}/resources/base.css" />
<link rel="stylesheet" type="text/css" href="{{.RootPath}}/resources/lobby.css" />
<link rel="stylesheet" type="text/css" href="{{.RootPath}}/resources/base.css?cache_bust={{.CacheBust}}" />
<link rel="stylesheet" type="text/css" href="{{.RootPath}}/resources/lobby.css?cache_bust={{.CacheBust}}" />
{{template "favicon-decl" .}}
</head>

Expand Down Expand Up @@ -37,36 +37,42 @@
<button onclick="showNameChangeDialog()" class="dialog-button header-button"
alt="{{.Translation.Get "change-your-name"}}"
title="{{.Translation.Get "change-your-name"}}">
<img src="{{.RootPath}}/resources/user.svg" class="header-button-image" />
<img src="{{.RootPath}}/resources/user.svg?cache_bust={{.CacheBust}}"
class="header-button-image" />
</button>
<button onclick="toggleFullscreen()" class="dialog-button header-button"
alt="{{.Translation.Get "toggle-fullscreen"}}"
title="{{.Translation.Get "toggle-fullscreen"}}">
<img src="{{.RootPath}}/resources/fullscreen.svg" class="header-button-image" />
<img src="{{.RootPath}}/resources/fullscreen.svg?cache_bust={{.CacheBust}}"
class="header-button-image" />
</button>
</div>
<div id="word-container"></div>
<div>
<button id="help-button" onclick="showHelpDialog()" class="dialog-button header-button"
alt="{{.Translation.Get "show-help"}}" title="{{.Translation.Get "show-help"}}">
<img src="{{.RootPath}}/resources/help.svg" class="header-button-image" />
<img src="{{.RootPath}}/resources/help.svg?cache_bust={{.CacheBust}}"
class="header-button-image" />
</button>
<button id="kick-button" style="display: none;" onclick="showKickDialog()"
class="dialog-button header-button" alt="{{.Translation.Get "votekick-a-player"}}"
title="{{.Translation.Get "votekick-a-player"}}">
<img src="{{.RootPath}}/resources/kick.png" class="header-button-image" />
<img src="{{.RootPath}}/resources/kick.png?cache_bust={{.CacheBust}}"
class="header-button-image" />
</button>
<button id="lobby-settings-button" style="display: none;" onclick="showLobbySettingsDialog()"
class="dialog-button header-button"
alt="{{.Translation.Get "change-lobby-settings-tooltip"}}"
title="{{.Translation.Get "change-lobby-settings-tooltip"}}">
<img src="{{.RootPath}}/resources/settings.svg" class="header-button-image" />
<img src="{{.RootPath}}/resources/settings.svg?cache_bust={{.CacheBust}}"
class="header-button-image" />
</button>
</div>
</div>

<div id="time-left">
<img src=" {{.RootPath}}/resources/clock.svg" class="header-button-image" />
<img src=" {{.RootPath}}/resources/clock.svg?cache_bust={{.CacheBust}}"
class="header-button-image" />
<div id="time-left-value"></div>
</div>
</div>
Expand Down Expand Up @@ -296,8 +302,8 @@
onmouseup="document.getElementById('tool-type-pencil').click()"
class="line-width-button-content">
<img title="{{.Translation.Get "use-pencil"}}" alt="{{.Translation.Get "use-pencil"}}"
src="{{.RootPath}}/resources/pencil.svg" style="transform: scaleX(-1)" width="40px"
height="40px" />
src="{{.RootPath}}/resources/pencil.svg?cache_bust={{.CacheBust}}"
style="transform: scaleX(-1)" width="40px" height="40px" />
</div>
</label>
<label for="tool-type-fill">
Expand All @@ -307,8 +313,9 @@
onmouseup="document.getElementById('tool-type-fill').click()"
class="line-width-button-content">
<img alt="{{.Translation.Get "use-fill-bucket"}}"
title="{{.Translation.Get "use-fill-bucket"}}" src="{{.RootPath}}/resources/fill.svg"
width="40px" height="40px" />
title="{{.Translation.Get "use-fill-bucket"}}"
src="{{.RootPath}}/resources/fill.svg?cache_bust={{.CacheBust}}" width="40px"
height="40px" />
</div>
</label>
<label for="tool-type-rubber">
Expand All @@ -318,7 +325,8 @@
onmouseup="document.getElementById('tool-type-rubber').click()"
class="line-width-button-content">
<img alt="{{.Translation.Get "use-eraser"}}" title="{{.Translation.Get "use-eraser"}}"
src="{{.RootPath}}/resources/rubber.svg" width="40px" height="40px" />
src="{{.RootPath}}/resources/rubber.svg?cache_bust={{.CacheBust}}" width="40px"
height="40px" />
</div>
</label>
</div>
Expand Down Expand Up @@ -372,13 +380,13 @@
<button class="canvas-button toolbox-group" onclick="clearCanvasAndSendEvent()"
alt="{{.Translation.Get "clear-canvas"}}" title="{{.Translation.Get "clear-canvas"}}">
<img alt="{{.Translation.Get "clear-canvas"}}" title="{{.Translation.Get "clear-canvas"}}"
src="{{.RootPath}}/resources/trash.svg" width="40px" height="40px" />
src="{{.RootPath}}/resources/trash.svg?cache_bust={{.CacheBust}}" width="40px" height="40px" />
</button>
<!--We won't make this button easier to click, as there's no going back. -->
<button class="canvas-button toolbox-group" onclick="undoAndSendEvent()"
alt="{{.Translation.Get "undo"}}" title="{{.Translation.Get "undo"}}">
<img alt="{{.Translation.Get "undo"}}" title="{{.Translation.Get "undo"}}"
src="{{.RootPath}}/resources/undo.svg" width="40px" height="40px" />
src="{{.RootPath}}/resources/undo.svg?cache_bust={{.CacheBust}}" width="40px" height="40px" />
</button>
</div>

Expand All @@ -394,7 +402,7 @@
{{template "footer" .}}
</div>

<script type="text/javascript" src="{{.RootPath}}/resources/floodfill.js"></script>
<script type="text/javascript" src="{{.RootPath}}/resources/floodfill.js?cache_bust={{.CacheBust}}"></script>
<script type="text/javascript">
String.prototype.format = function () {
return [...arguments].reduce((p, c) => p.replace(/%s/, c), this);
Expand Down Expand Up @@ -712,9 +720,9 @@

function updateSoundIcon() {
if (sound) {
soundToggleLabel.src = "{{.RootPath}}/resources/sound.svg";
soundToggleLabel.src = "{{.RootPath}}/resources/sound.svg?cache_bust={{.CacheBust}}";
} else {
soundToggleLabel.src = "{{.RootPath}}/resources/no-sound.svg";
soundToggleLabel.src = "{{.RootPath}}/resources/no-sound.svg?cache_bust={{.CacheBust}}";
}
}

Expand Down Expand Up @@ -1043,7 +1051,7 @@
waitChooseDrawerSpan.innerText = parsed.data.playerName;
}
} else if (parsed.type === "correct-guess") {
playWav('{{.RootPath}}/resources/plop.wav');
playWav('{{.RootPath}}/resources/plop.wav?cache_bust={{.CacheBust}}');

if (parsed.data === ownID) {
appendMessage("correct-guess-message", null, '{{.Translation.Get "correct-guess"}}');
Expand Down Expand Up @@ -1084,7 +1092,7 @@

//If a player doesn't choose, the dialog will still be up.
wordDialog.style.visibility = "hidden";
playWav('{{.RootPath}}/resources/end-turn.wav');
playWav('{{.RootPath}}/resources/end-turn.wav?cache_bust={{.CacheBust}}');

clear(context);

Expand Down Expand Up @@ -1114,7 +1122,7 @@
gameState = "ongoing";
}
} else if (parsed.type === "your-turn") {
playWav('{{.RootPath}}/resources/your-turn.wav');
playWav('{{.RootPath}}/resources/your-turn.wav?cache_bust={{.CacheBust}}');
//This dialog could potentially stay visible from last
//turn, in case nobody has chosen a word.
waitChooseDialog.style.visibility = "hidden";
Expand Down Expand Up @@ -1427,11 +1435,11 @@
drawerID = player.id;
drawerName = player.name;

const playerStateImage = createPlayerStateImageNode("{{.RootPath}}/resources/pencil.svg");
const playerStateImage = createPlayerStateImageNode("{{.RootPath}}/resources/pencil.svg?cache_bust={{.CacheBust}}");
playerStateImage.style.transform = "scaleX(-1)";
scoreAndStatusDiv.appendChild(playerStateImage);
} else if (player.state === "standby") {
const playerStateImage = createPlayerStateImageNode("{{.RootPath}}/resources/checkmark.svg");
const playerStateImage = createPlayerStateImageNode("{{.RootPath}}/resources/checkmark.svg?cache_bust={{.CacheBust}}");
scoreAndStatusDiv.appendChild(playerStateImage);
}

Expand Down
6 changes: 3 additions & 3 deletions internal/frontend/templates/lobby_create.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
{{template "non-static-css-decl" .}}
<link rel="stylesheet" type="text/css" href="{{.RootPath}}/resources/base.css" />
<link rel="stylesheet" type="text/css" href="{{.RootPath}}/resources/lobby_create.css" />
<link rel="stylesheet" type="text/css" href="{{.RootPath}}/resources/base.css?cache_bust={{.CacheBust}}" />
<link rel="stylesheet" type="text/css" href="{{.RootPath}}/resources/lobby_create.css?cache_bust={{.CacheBust}}" />
{{template "favicon-decl" .}}
</head>

<body>
<div class="content-wrapper">
<noscript><span class="noscript">{{.Translation.Get "requires-js"}}</span></noscript>

<img id="logo" src="{{.RootPath}}/resources/logo.svg">
<img id="logo" src="{{.RootPath}}/resources/logo.svg?cache_bust={{.CacheBust}}">

<div class="tab-header">
<label for="create-lobby-tab-button">
Expand Down
6 changes: 3 additions & 3 deletions internal/frontend/templates/non-static-css.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
font-family: 'Montserrat';
font-weight: normal;
src: local('Monserrat'),
url({{.RootPath}}/resources/Montserrat-Regular.otf) format("opentype");
url({{.RootPath}}/resources/Montserrat-Regular.otf?cache_bust={{.CacheBust}}) format("opentype");
}

@font-face {
font-family: 'Montserrat';
font-weight: bold;
src: local('Monserrat Bold'),
url({{.RootPath}}/resources/Montserrat-Bold.otf) format("opentype");
url({{.RootPath}}/resources/Montserrat-Bold.otf?cache_bust={{.CacheBust}}) format("opentype");
}

.content-wrapper::before {
Expand All @@ -24,7 +24,7 @@
width: 100vw;
height: 100vh;
opacity: 0.20;
background-image: url('{{.RootPath}}/resources/background.png');
background-image: url('{{.RootPath}}/resources/background.png?cache_bust={{.CacheBust}}');
background-size: 400px 400px;
background-repeat: repeat;
z-index: -1;
Expand Down
10 changes: 5 additions & 5 deletions internal/frontend/templates/robot.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
<meta property="og:type" content="website">
<meta property="og:title" content="Scribble.rs - Join Lobby">
<meta property="og:description" content="{{$description}}">
<meta property="og:image" content="{{.RootPath}}/resources/favicon_96.png">
<meta property="og:image" content="{{.RootPath}}/resources/favicon_96.png?cache_bust={{.CacheBust}}">
<meta property="twitter:card" content="summary_large_image">
<meta property="twitter:title" content="Scribble.rs - Join Lobby">
<meta property="twitter:description" content="{{$description}}">
<meta property="twitter:image" content="{{.RootPath}}/resources/favicon_96.png">
<meta property="twitter:image" content="{{.RootPath}}/resources/favicon_96.png?cache_bust={{.CacheBust}}">
{{template "non-static-css-decl" .}}
<link rel="stylesheet" type="text/css" href="{{.RootPath}}/resources/base.css" />
<link rel="stylesheet" type="text/css" href="{{.RootPath}}/resources/error.css" />
<link rel="icon" type="image/png" href="{{.RootPath}}/resources/favicon_96.png" />
<link rel="stylesheet" type="text/css" href="{{.RootPath}}/resources/base.css?cache_bust={{.CacheBust}}" />
<link rel="stylesheet" type="text/css" href="{{.RootPath}}/resources/error.css?cache_bust={{.CacheBust}}" />
<link rel="icon" type="image/png" href="{{.RootPath}}/resources/favicon_96.png?cache_bust={{.CacheBust}}" />
</head>

<body>
Expand Down
10 changes: 9 additions & 1 deletion internal/frontend/templating_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,19 @@ import (
"github.com/scribble-rs/scribble.rs/internal/translations"
)

func init() {
if err := Init(); err != nil {
panic(err)
}
}

func Test_templateLobbyPage(t *testing.T) {
var buffer bytes.Buffer
err := pageTemplates.ExecuteTemplate(&buffer,
"lobby-page", &lobbyPageData{
BasePageConfig: &BasePageConfig{},
BasePageConfig: &BasePageConfig{
CacheBust: "lol",
},
LobbyData: &api.LobbyData{
EditableLobbySettings: &game.EditableLobbySettings{},
SettingBounds: game.LobbySettingBounds,
Expand Down

0 comments on commit c3f69b8

Please sign in to comment.