Add page footer with request time display
This commit is contained in:
@@ -31,6 +31,9 @@
|
|||||||
<main>
|
<main>
|
||||||
{{block "content" .}}{{end}}
|
{{block "content" .}}{{end}}
|
||||||
</main>
|
</main>
|
||||||
|
<footer>
|
||||||
|
<span class="muted">Request time: {{.RenderMS}} ms</span>
|
||||||
|
</footer>
|
||||||
{{block "extras" .}}{{end}}
|
{{block "extras" .}}{{end}}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+16
-1
@@ -37,6 +37,8 @@ body {
|
|||||||
font:
|
font:
|
||||||
1rem "Iosevka Etoile",
|
1rem "Iosevka Etoile",
|
||||||
monospace;
|
monospace;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
@@ -148,6 +150,8 @@ main {
|
|||||||
max-width: 860px;
|
max-width: 860px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 1.5rem 1rem;
|
padding: 1.5rem 1rem;
|
||||||
|
width: 100%;
|
||||||
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* === Markdown content === */
|
/* === Markdown content === */
|
||||||
@@ -439,6 +443,16 @@ textarea {
|
|||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* === Page footer === */
|
||||||
|
footer {
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
border-top: 1px dashed var(--secondary);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
/* === Task lists === */
|
/* === Task lists === */
|
||||||
.content li:has(> input.task-checkbox:checked) {
|
.content li:has(> input.task-checkbox:checked) {
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
@@ -822,7 +836,8 @@ hr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
header {
|
header,
|
||||||
|
footer {
|
||||||
padding: 0.5rem 0.75rem;
|
padding: 0.5rem 0.75rem;
|
||||||
}
|
}
|
||||||
main {
|
main {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"embed"
|
"embed"
|
||||||
"flag"
|
"flag"
|
||||||
"html/template"
|
"html/template"
|
||||||
@@ -12,6 +13,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed assets
|
//go:embed assets
|
||||||
@@ -94,7 +96,23 @@ type handler struct {
|
|||||||
authKey []byte
|
authKey []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reqStartKey marks the request start time stored in the request context
|
||||||
|
// so HTML templates can render total server-side processing time.
|
||||||
|
type reqStartKeyT struct{}
|
||||||
|
|
||||||
|
var reqStartKey = reqStartKeyT{}
|
||||||
|
|
||||||
|
// elapsedMS returns the milliseconds since the request entered ServeHTTP.
|
||||||
|
func elapsedMS(r *http.Request) int64 {
|
||||||
|
if start, ok := r.Context().Value(reqStartKey).(time.Time); ok {
|
||||||
|
return time.Since(start).Milliseconds()
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
r = r.WithContext(context.WithValue(r.Context(), reqStartKey, time.Now()))
|
||||||
|
|
||||||
if !h.checkAuth(w, r) {
|
if !h.checkAuth(w, r) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -234,6 +252,7 @@ func (h *handler) serveDir(w http.ResponseWriter, r *http.Request, urlPath, fsPa
|
|||||||
if editMode {
|
if editMode {
|
||||||
t = editTmpl
|
t = editTmpl
|
||||||
}
|
}
|
||||||
|
data.RenderMS = elapsedMS(r)
|
||||||
if err := t.ExecuteTemplate(w, "layout", data); err != nil {
|
if err := t.ExecuteTemplate(w, "layout", data); err != nil {
|
||||||
log.Printf("template error: %v", err)
|
log.Printf("template error: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ type pageData struct {
|
|||||||
Entries []entry
|
Entries []entry
|
||||||
SpecialContent template.HTML
|
SpecialContent template.HTML
|
||||||
SidebarWidget template.HTML
|
SidebarWidget template.HTML
|
||||||
|
RenderMS int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// pageSettings holds the parsed contents of a .page-settings file.
|
// pageSettings holds the parsed contents of a .page-settings file.
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ type searchPageData struct {
|
|||||||
EditMode bool
|
EditMode bool
|
||||||
Query string
|
Query string
|
||||||
Results []searchResult
|
Results []searchResult
|
||||||
|
RenderMS int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleSearch walks the wiki root and renders a search results page for the
|
// handleSearch walks the wiki root and renders a search results page for the
|
||||||
@@ -43,6 +44,7 @@ func (h *handler) handleSearch(w http.ResponseWriter, r *http.Request) {
|
|||||||
Results: results,
|
Results: results,
|
||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
|
data.RenderMS = elapsedMS(r)
|
||||||
if err := searchTmpl.ExecuteTemplate(w, "layout", data); err != nil {
|
if err := searchTmpl.ExecuteTemplate(w, "layout", data); err != nil {
|
||||||
log.Printf("search template error: %v", err)
|
log.Printf("search template error: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user