item-34 - Serve every /public/ asset under a content-versioned URL with an immutable Cache-Control

This commit is contained in:
2026-10-31 10:00:03 +01:00
parent 910ac18798
commit 3d757d4726
18 changed files with 340 additions and 88 deletions
+1 -1
View File
@@ -7,7 +7,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title><%= t("error.403.docTitle") %></title>
<link rel="stylesheet" href="/public/css/styles.css" />
<link rel="stylesheet" href="<%= assetHref("/public/css/styles.css") %>" />
</head>
<body>
<main>
+1 -1
View File
@@ -7,7 +7,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title><%= t("error.404.docTitle") %></title>
<link rel="stylesheet" href="/public/css/styles.css" />
<link rel="stylesheet" href="<%= assetHref("/public/css/styles.css") %>" />
</head>
<body>
<main>
+1 -1
View File
@@ -7,7 +7,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title><%= t("error.500.docTitle") %></title>
<link rel="stylesheet" href="/public/css/styles.css" />
<link rel="stylesheet" href="<%= assetHref("/public/css/styles.css") %>" />
</head>
<body>
<main>
+1 -1
View File
@@ -7,7 +7,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title><%= t("error.503.docTitle") %></title>
<link rel="stylesheet" href="/public/css/styles.css" />
<link rel="stylesheet" href="<%= assetHref("/public/css/styles.css") %>" />
</head>
<body>
<main>
+1 -1
View File
@@ -7,7 +7,7 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title><%= t("error.flow.docTitle") %></title>
<link rel="stylesheet" href="/public/css/styles.css" />
<link rel="stylesheet" href="<%= assetHref("/public/css/styles.css") %>" />
</head>
<body>
<main>
+3 -3
View File
@@ -11,9 +11,9 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title><%= t("consent.title", { client: consent.client }) %></title>
<link rel="stylesheet" href="/public/css/styles.css" />
<link rel="stylesheet" href="/public/css/auth.css" />
<link rel="icon" href="/public/favicon.svg" />
<link rel="stylesheet" href="<%= assetHref("/public/css/styles.css") %>" />
<link rel="stylesheet" href="<%= assetHref("/public/css/auth.css") %>" />
<link rel="icon" href="<%= assetHref("/public/favicon.svg") %>" />
</head>
<body>
<%- include("partials/icons") %>
+6 -5
View File
@@ -10,7 +10,8 @@
(theme-switch default), `user`, `breadcrumbs`, `csrfToken` (the Sign-out form's hidden field),
`signInHref` (anonymous "Sign in" target; default /login). `menu` (default true) — set false to
drop the sidebar and render a focused single-column page. `t`, `locale`, `dir` and `localeSwitch`
come from the host with every render (README → Languages).
come from the host with every render (README → Languages), as does `assetHref`, which versions a
/public/ href so the browser caches it (src/http/static.ts).
%><%
const brand = locals.brand || { name: "Plainpages" };
const title = locals.title || ""; // topbar heading; empty ⇒ no topbar <h1> (the body owns it)
@@ -29,9 +30,9 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title><%= docTitle %></title>
<link rel="stylesheet" href="/public/css/styles.css" />
<% styles.forEach((href) => { %><link rel="stylesheet" href="<%= href %>" />
<% }) %><link rel="icon" href="/public/favicon.svg" />
<link rel="stylesheet" href="<%= assetHref("/public/css/styles.css") %>" />
<% styles.forEach((href) => { %><link rel="stylesheet" href="<%= assetHref(href) %>" />
<% }) %><link rel="icon" href="<%= assetHref("/public/favicon.svg") %>" />
</head>
<body>
<a class="skip-link" href="#main-content"><%= t("shell.skipToContent") %></a>
@@ -45,7 +46,7 @@
<% if (menu) { %>
<aside class="sidebar" aria-label="<%= t("shell.sidebar") %>">
<div class="brand">
<% if (brand.logo) { %><img class="brand-logo" src="<%= brand.logo %>" alt="" /><% } else { %><span class="brand-mark"><svg class="ico ico-sm"><use href="#i-box" /></svg></span><% } %>
<% if (brand.logo) { %><img class="brand-logo" src="<%= assetHref(brand.logo) %>" alt="" /><% } else { %><span class="brand-mark"><svg class="ico ico-sm"><use href="#i-box" /></svg></span><% } %>
<span class="brand-name"><%= brand.name %></span>
<% if (brand.sub) { %><span class="brand-sub"><%= brand.sub %></span><% } %>
</div>