mirror of
https://github.com/MeshTender/MeshTender.git
synced 2026-09-16 08:32:37 +00:00
Static asset template helper
This commit is contained in:
@@ -169,7 +169,7 @@
|
||||
</template>
|
||||
</form>
|
||||
<script nonce="{{.Nonce}}">window.MESHTENDER_LINK_PLATFORMS = {{.PlatformsJS}};</script>
|
||||
<script src="/static/link-editor.js"></script>
|
||||
<script src="{{ asset "/static/link-editor.js" }}"></script>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -290,6 +290,6 @@
|
||||
document.getElementById("rename-name").value = btn.getAttribute("data-name") || "";
|
||||
});
|
||||
</script>
|
||||
<script src="/static/timezone-picker.js"></script>
|
||||
<script src="/static/webauthn.js"></script>
|
||||
<script src="{{ asset "/static/timezone-picker.js" }}"></script>
|
||||
<script src="{{ asset "/static/webauthn.js" }}"></script>
|
||||
{{end}}
|
||||
|
||||
@@ -35,6 +35,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center text-secondary mt-3">Don't have an account yet? <a href="/signup{{if .Next}}?next={{.Next}}{{end}}" tabindex="-1">Create one</a></div>
|
||||
<script src="/static/webauthn.js"></script>
|
||||
<script src="{{ asset "/static/webauthn.js" }}"></script>
|
||||
<script nonce="{{.Nonce}}">document.addEventListener("DOMContentLoaded", initSigninPasskey);</script>
|
||||
{{end}}
|
||||
|
||||
@@ -40,5 +40,5 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center text-secondary mt-3">Already have an account? <a href="/login{{if .Next}}?next={{.Next}}{{end}}" tabindex="-1">Sign in</a></div>
|
||||
<script src="/static/webauthn.js"></script>
|
||||
<script src="{{ asset "/static/webauthn.js" }}"></script>
|
||||
{{end}}
|
||||
|
||||
@@ -277,10 +277,10 @@
|
||||
}
|
||||
document.getElementById("region").addEventListener("change", applyRegion);
|
||||
</script>
|
||||
<link rel="stylesheet" href="/static/leaflet.css">
|
||||
<script src="/static/leaflet.js"></script>
|
||||
<script src="/static/regionmap.js"></script>
|
||||
<script src="/static/serial-setup.js"></script>
|
||||
<link rel="stylesheet" href="{{ asset "/static/leaflet.css" }}">
|
||||
<script src="{{ asset "/static/leaflet.js" }}"></script>
|
||||
<script src="{{ asset "/static/regionmap.js" }}"></script>
|
||||
<script src="{{ asset "/static/serial-setup.js" }}"></script>
|
||||
|
||||
{{else}}
|
||||
{{/* ---- Step 3 (KISS): details for a repeater that's already on the network ---- */}}
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<link rel="stylesheet" href="/static/leaflet.css">
|
||||
<link rel="stylesheet" href="/static/leaflet-geoman.css">
|
||||
<link rel="stylesheet" href="{{ asset "/static/leaflet.css" }}">
|
||||
<link rel="stylesheet" href="{{ asset "/static/leaflet-geoman.css" }}">
|
||||
<style>
|
||||
@media (min-width: 992px) { .region-map-sticky { position: sticky; top: 1rem; } }
|
||||
</style>
|
||||
@@ -114,9 +114,9 @@ function addBlock(kind) {
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<script src="/static/leaflet.js"></script>
|
||||
<script src="/static/leaflet-geoman.js"></script>
|
||||
<script src="/static/regionmap.js"></script>
|
||||
<script src="{{ asset "/static/leaflet.js" }}"></script>
|
||||
<script src="{{ asset "/static/leaflet-geoman.js" }}"></script>
|
||||
<script src="{{ asset "/static/regionmap.js" }}"></script>
|
||||
<script nonce="{{.Nonce}}">initRegionEditor('region-map', 'regions');</script>
|
||||
{{end}}
|
||||
|
||||
|
||||
@@ -135,14 +135,14 @@ swap needs no new markup. The flex row is nested so the [hidden] attribute wins
|
||||
</div>
|
||||
|
||||
{{if .ShowConfig}}
|
||||
<link rel="stylesheet" href="/static/leaflet.css">
|
||||
<script src="/static/leaflet.js"></script>
|
||||
<script src="/static/regionmap.js"></script>
|
||||
<script src="/static/console-config.js"></script>
|
||||
<link rel="stylesheet" href="{{ asset "/static/leaflet.css" }}">
|
||||
<script src="{{ asset "/static/leaflet.js" }}"></script>
|
||||
<script src="{{ asset "/static/regionmap.js" }}"></script>
|
||||
<script src="{{ asset "/static/console-config.js" }}"></script>
|
||||
{{end}}
|
||||
|
||||
<script nonce="{{.Nonce}}">
|
||||
window.MESHTENDER_WS = (location.protocol === "https:" ? "wss://" : "ws://") + location.host + "/repeaters/{{.Repeater.PublicID}}/console/ws{{if .Debug}}?debug=1{{end}}";
|
||||
</script>
|
||||
<script src="/static/console.js"></script>
|
||||
<script src="{{ asset "/static/console.js" }}"></script>
|
||||
{{end}}
|
||||
|
||||
@@ -126,10 +126,10 @@
|
||||
<div class="card-header"><h3 class="card-title">Repeater map</h3></div>
|
||||
{{if .Mapped}}
|
||||
<div class="card-body p-2">
|
||||
<link rel="stylesheet" href="/static/leaflet.css">
|
||||
<link rel="stylesheet" href="{{ asset "/static/leaflet.css" }}">
|
||||
<div id="map" style="height:260px" role="region" aria-label="Map of your repeater locations"></div>
|
||||
<script src="/static/leaflet.js"></script>
|
||||
<script src="/static/meshmap.js"></script>
|
||||
<script src="{{ asset "/static/leaflet.js" }}"></script>
|
||||
<script src="{{ asset "/static/meshmap.js" }}"></script>
|
||||
<script nonce="{{.Nonce}}">
|
||||
meshMap('map', [
|
||||
{{range .Mapped}}{name: {{.Name}}, lat: {{.Latitude}}, lon: {{.Longitude}}},{{end}}
|
||||
|
||||
@@ -171,10 +171,10 @@
|
||||
<div class="card">
|
||||
<div class="card-header"><h3 class="card-title">Repeaters</h3></div>
|
||||
<div class="card-body p-2">
|
||||
<link rel="stylesheet" href="/static/leaflet.css">
|
||||
<link rel="stylesheet" href="{{ asset "/static/leaflet.css" }}">
|
||||
<div id="map" style="height:260px" role="region" aria-label="Map of organization repeater locations"></div>
|
||||
<script src="/static/leaflet.js"></script>
|
||||
<script src="/static/meshmap.js"></script>
|
||||
<script src="{{ asset "/static/leaflet.js" }}"></script>
|
||||
<script src="{{ asset "/static/meshmap.js" }}"></script>
|
||||
<script nonce="{{.Nonce}}">
|
||||
meshMap('map', [
|
||||
{{range .Repeaters}}{{if .HasLocation}}{name: {{.Name}}, lat: {{.Lat}}, lon: {{.Lon}}},{{end}}{{end}}
|
||||
@@ -201,7 +201,7 @@
|
||||
<!-- Per-platform link config for the links editor loaded into the modal above;
|
||||
link-editor.js re-initializes on htmx:afterSwap. -->
|
||||
<script nonce="{{.Nonce}}">window.MESHTENDER_LINK_PLATFORMS = {{.PlatformsJS}};</script>
|
||||
<script src="/static/link-editor.js"></script>
|
||||
<script src="{{ asset "/static/link-editor.js" }}"></script>
|
||||
{{end}}
|
||||
|
||||
<a class="back-link mt-3" href="/orgs">{{template "icon-arrow-left" "me-1"}}All organizations</a>
|
||||
|
||||
@@ -135,10 +135,10 @@
|
||||
<div class="card mt-3">
|
||||
<div class="card-header"><h3 class="card-title">Location</h3></div>
|
||||
<div class="card-body p-2">
|
||||
<link rel="stylesheet" href="/static/leaflet.css">
|
||||
<link rel="stylesheet" href="{{ asset "/static/leaflet.css" }}">
|
||||
<div id="map" style="height:240px" role="region" aria-label="Map of this repeater's location"></div>
|
||||
<script src="/static/leaflet.js"></script>
|
||||
<script src="/static/meshmap.js"></script>
|
||||
<script src="{{ asset "/static/leaflet.js" }}"></script>
|
||||
<script src="{{ asset "/static/meshmap.js" }}"></script>
|
||||
<script nonce="{{.Nonce}}">
|
||||
meshMap('map', [{name: {{.Repeater.Name}}, lat: {{.Repeater.Latitude}}, lon: {{.Repeater.Longitude}}}]);
|
||||
</script>
|
||||
|
||||
@@ -64,10 +64,10 @@
|
||||
<div class="card mt-3">
|
||||
<div class="card-header"><h3 class="card-title">Location</h3></div>
|
||||
<div class="card-body p-2">
|
||||
<link rel="stylesheet" href="/static/leaflet.css">
|
||||
<link rel="stylesheet" href="{{ asset "/static/leaflet.css" }}">
|
||||
<div id="map" style="height:240px" role="region" aria-label="Map of this repeater's location"></div>
|
||||
<script src="/static/leaflet.js"></script>
|
||||
<script src="/static/meshmap.js"></script>
|
||||
<script src="{{ asset "/static/leaflet.js" }}"></script>
|
||||
<script src="{{ asset "/static/meshmap.js" }}"></script>
|
||||
<script nonce="{{.Nonce}}">
|
||||
meshMap('map', [{name: {{.Repeater.Name}}, lat: {{.Repeater.Latitude}}, lon: {{.Repeater.Longitude}}}]);
|
||||
</script>
|
||||
|
||||
@@ -104,6 +104,37 @@ func TestTemplatesHaveNoInlineJS(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestTemplatesUseAssetHelper enforces that every static reference goes through
|
||||
// the {{ asset }} helper (href="{{ asset "/static/x" }}"), never a bare
|
||||
// href="/static/x". The helper rewrites the URL to a content-hashed,
|
||||
// immutably-cached path; a bare reference silently loses that caching.
|
||||
func TestTemplatesUseAssetHelper(t *testing.T) {
|
||||
t.Parallel()
|
||||
root := moduleRoot(t)
|
||||
bare := regexp.MustCompile(`(?i)(href|src)="/static/`)
|
||||
|
||||
err := filepath.WalkDir(filepath.Join(root, "internal"), func(path string, d os.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.IsDir() || !strings.HasSuffix(path, ".html") {
|
||||
return nil
|
||||
}
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rel, _ := filepath.Rel(root, path)
|
||||
if loc := bare.FindIndex(b); loc != nil {
|
||||
t.Errorf("%s: bare static reference — use {{ asset \"/static/...\" }}: %q", rel, snippet(b, loc[0]))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("walk templates: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func snippet(b []byte, at int) string {
|
||||
end := at + 40
|
||||
if end > len(b) {
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<link rel="icon" href="/static/favicon.svg" type="image/svg+xml">
|
||||
<link rel="icon" href="{{ asset "/static/favicon.svg" }}" type="image/svg+xml">
|
||||
<title>{{block "title" .}}MeshTender{{end}}</title>
|
||||
<link rel="stylesheet" href="/static/tabler.min.css">
|
||||
<link rel="stylesheet" href="/static/app.css">
|
||||
<script src="/static/tabler.min.js" defer></script>
|
||||
<script src="/static/htmx.min.js" defer></script>
|
||||
<script src="/static/ui.js" defer></script>
|
||||
<script src="/static/listfilter.js" defer></script>
|
||||
<link rel="stylesheet" href="{{ asset "/static/tabler.min.css" }}">
|
||||
<link rel="stylesheet" href="{{ asset "/static/app.css" }}">
|
||||
<script src="{{ asset "/static/tabler.min.js" }}" defer></script>
|
||||
<script src="{{ asset "/static/htmx.min.js" }}" defer></script>
|
||||
<script src="{{ asset "/static/ui.js" }}" defer></script>
|
||||
<script src="{{ asset "/static/listfilter.js" }}" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<a href="#main-content" class="visually-hidden-focusable btn btn-primary position-absolute top-0 start-0 m-2" style="z-index:1080">Skip to main content</a>
|
||||
@@ -164,14 +164,14 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<link rel="icon" href="/static/favicon.svg" type="image/svg+xml">
|
||||
<link rel="icon" href="{{ asset "/static/favicon.svg" }}" type="image/svg+xml">
|
||||
<title>{{template "title" .}}</title>
|
||||
<link rel="stylesheet" href="/static/tabler.min.css">
|
||||
<link rel="stylesheet" href="/static/app.css">
|
||||
<script src="/static/tabler.min.js" defer></script>
|
||||
<script src="/static/htmx.min.js" defer></script>
|
||||
<script src="/static/ui.js" defer></script>
|
||||
<script src="/static/listfilter.js" defer></script>
|
||||
<link rel="stylesheet" href="{{ asset "/static/tabler.min.css" }}">
|
||||
<link rel="stylesheet" href="{{ asset "/static/app.css" }}">
|
||||
<script src="{{ asset "/static/tabler.min.js" }}" defer></script>
|
||||
<script src="{{ asset "/static/htmx.min.js" }}" defer></script>
|
||||
<script src="{{ asset "/static/ui.js" }}" defer></script>
|
||||
<script src="{{ asset "/static/listfilter.js" }}" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<a href="#main-content" class="visually-hidden-focusable btn btn-primary position-absolute top-0 start-0 m-2" style="z-index:1080">Skip to main content</a>
|
||||
@@ -210,12 +210,12 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<link rel="icon" href="/static/favicon.svg" type="image/svg+xml">
|
||||
<link rel="icon" href="{{ asset "/static/favicon.svg" }}" type="image/svg+xml">
|
||||
<title>{{template "title" .}}</title>
|
||||
<link rel="stylesheet" href="/static/tabler.min.css">
|
||||
<link rel="stylesheet" href="/static/app.css">
|
||||
<script src="/static/tabler.min.js" defer></script>
|
||||
<script src="/static/ui.js" defer></script>
|
||||
<link rel="stylesheet" href="{{ asset "/static/tabler.min.css" }}">
|
||||
<link rel="stylesheet" href="{{ asset "/static/app.css" }}">
|
||||
<script src="{{ asset "/static/tabler.min.js" }}" defer></script>
|
||||
<script src="{{ asset "/static/ui.js" }}" defer></script>
|
||||
</head>
|
||||
<body class="d-flex flex-column">
|
||||
<div class="page page-center">
|
||||
@@ -239,12 +239,12 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<link rel="icon" href="/static/favicon.svg" type="image/svg+xml">
|
||||
<link rel="icon" href="{{ asset "/static/favicon.svg" }}" type="image/svg+xml">
|
||||
<title>{{template "title" .}}</title>
|
||||
<link rel="stylesheet" href="/static/tabler.min.css">
|
||||
<link rel="stylesheet" href="/static/app.css">
|
||||
<script src="/static/tabler.min.js" defer></script>
|
||||
<script src="/static/ui.js" defer></script>
|
||||
<link rel="stylesheet" href="{{ asset "/static/tabler.min.css" }}">
|
||||
<link rel="stylesheet" href="{{ asset "/static/app.css" }}">
|
||||
<script src="{{ asset "/static/tabler.min.js" }}" defer></script>
|
||||
<script src="{{ asset "/static/ui.js" }}" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
{{template "rootnav" .}}
|
||||
|
||||
@@ -55,10 +55,10 @@
|
||||
<div class="card{{if .Config.Profiles}} mt-3{{end}}">
|
||||
<div class="card-header"><h3 class="card-title">Preview a location</h3></div>
|
||||
<div class="card-body p-2">
|
||||
<link rel="stylesheet" href="/static/leaflet.css">
|
||||
<link rel="stylesheet" href="{{ asset "/static/leaflet.css" }}">
|
||||
<div id="region-map" style="height:240px" role="region" aria-label="Click the map to preview a repeater's region config"></div>
|
||||
<script src="/static/leaflet.js"></script>
|
||||
<script src="/static/regionmap.js"></script>
|
||||
<script src="{{ asset "/static/leaflet.js" }}"></script>
|
||||
<script src="{{ asset "/static/regionmap.js" }}"></script>
|
||||
<script nonce="{{.Nonce}}">
|
||||
regionMapView('region-map', {
|
||||
pickURL: '/orgs/{{.Org.Slug}}/config?{{if .Config.Profiles}}profile={{.Config.Selected | urlquery}}&{{end}}',
|
||||
|
||||
@@ -66,12 +66,12 @@
|
||||
<div class="card-header"><h3 class="card-title">Repeaters</h3></div>
|
||||
{{if .HasMap}}
|
||||
<div class="card-body p-2">
|
||||
<link rel="stylesheet" href="/static/leaflet.css">
|
||||
<link rel="stylesheet" href="/static/leaflet.markercluster.css">
|
||||
<link rel="stylesheet" href="{{ asset "/static/leaflet.css" }}">
|
||||
<link rel="stylesheet" href="{{ asset "/static/leaflet.markercluster.css" }}">
|
||||
<div id="map" style="height:260px" role="region" aria-label="Map of organization repeater locations"></div>
|
||||
<script src="/static/leaflet.js"></script>
|
||||
<script src="/static/leaflet.markercluster.js"></script>
|
||||
<script src="/static/meshmap.js"></script>
|
||||
<script src="{{ asset "/static/leaflet.js" }}"></script>
|
||||
<script src="{{ asset "/static/leaflet.markercluster.js" }}"></script>
|
||||
<script src="{{ asset "/static/meshmap.js" }}"></script>
|
||||
<script nonce="{{.Nonce}}">
|
||||
{{if .MapURL}}meshMapFromURL('map', {{.MapURL}});{{else}}meshMap('map', [
|
||||
{{range .Repeaters}}{{if .HasLocation}}{name: {{.Name}}, lat: {{.Lat}}, lon: {{.Lon}}},{{end}}{{end}}
|
||||
|
||||
@@ -64,12 +64,12 @@
|
||||
<div class="card-header"><h3 class="card-title">Map</h3></div>
|
||||
{{if .Reps.HasMap}}
|
||||
<div class="card-body p-2">
|
||||
<link rel="stylesheet" href="/static/leaflet.css">
|
||||
<link rel="stylesheet" href="/static/leaflet.markercluster.css">
|
||||
<link rel="stylesheet" href="{{ asset "/static/leaflet.css" }}">
|
||||
<link rel="stylesheet" href="{{ asset "/static/leaflet.markercluster.css" }}">
|
||||
<div id="map" style="height:300px" role="region" aria-label="Map of organization repeater locations"></div>
|
||||
<script src="/static/leaflet.js"></script>
|
||||
<script src="/static/leaflet.markercluster.js"></script>
|
||||
<script src="/static/meshmap.js"></script>
|
||||
<script src="{{ asset "/static/leaflet.js" }}"></script>
|
||||
<script src="{{ asset "/static/leaflet.markercluster.js" }}"></script>
|
||||
<script src="{{ asset "/static/meshmap.js" }}"></script>
|
||||
<script nonce="{{.Nonce}}">
|
||||
{{if .MapURL}}meshMapFromURL('map', {{.MapURL}});{{else}}meshMap('map', [
|
||||
{{range .Reps.MapPoints}}{name: {{.Name}}, lat: {{.Lat}}, lon: {{.Lon}}},{{end}}
|
||||
|
||||
Reference in New Issue
Block a user