refactor(static): prefix all static asset routes with /static/

Move static asset URLs from root-level paths to /static/ prefix
mirroring the filesystem layout (e.g. /styles.css → /static/css/styles.css,
/components/*.js → /static/js/components/*.js).
This commit is contained in:
Jon Seager 2026-02-08 18:07:10 +00:00
parent a9fd069a3c
commit 0fd4214b6c
No known key found for this signature in database
15 changed files with 34 additions and 31 deletions

View file

@ -44,19 +44,22 @@ pub(super) fn router() -> axum::Router<AppState> {
"/roasters/{roaster_slug}/roasts/{roast_slug}", "/roasters/{roaster_slug}/roasts/{roast_slug}",
get(roasts::roast_detail_page), get(roasts::roast_detail_page),
) )
.route("/styles.css", get(styles)) .route("/static/css/styles.css", get(styles))
.route("/webauthn.js", get(webauthn_js)) .route("/static/js/webauthn.js", get(webauthn_js))
.route("/components/photo-capture.js", get(photo_capture_js))
.route( .route(
"/components/searchable-select.js", "/static/js/components/photo-capture.js",
get(photo_capture_js),
)
.route(
"/static/js/components/searchable-select.js",
get(searchable_select_js), get(searchable_select_js),
) )
.route("/components/chip-scroll.js", get(chip_scroll_js)) .route("/static/js/components/chip-scroll.js", get(chip_scroll_js))
.route("/components/world-map.js", get(world_map_js)) .route("/static/js/components/world-map.js", get(world_map_js))
.route("/components/donut-chart.js", get(donut_chart_js)) .route("/static/js/components/donut-chart.js", get(donut_chart_js))
.route("/favicon-light.svg", get(favicon_light)) .route("/static/favicon-light.svg", get(favicon_light))
.route("/favicon-dark.svg", get(favicon_dark)) .route("/static/favicon-dark.svg", get(favicon_dark))
.route("/og-image.png", get(og_image)) .route("/static/og-image.png", get(og_image))
.route("/health", get(health)) .route("/health", get(health))
} }

View file

@ -13,8 +13,8 @@
<meta property="og:description" content="{% block og_description %}Self-hosted coffee logging — track roasters, roasts, bags, brews, and gear.{% endblock %}" /> <meta property="og:description" content="{% block og_description %}Self-hosted coffee logging — track roasters, roasts, bags, brews, and gear.{% endblock %}" />
<meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:card" content="summary_large_image" />
<title>{% block title %}Brewlog{% endblock %}</title> <title>{% block title %}Brewlog{% endblock %}</title>
<link rel="stylesheet" href="/styles.css" /> <link rel="stylesheet" href="/static/css/styles.css" />
<link rel="icon" id="favicon" type="image/svg+xml" href="/favicon-light.svg" /> <link rel="icon" id="favicon" type="image/svg+xml" href="/static/favicon-light.svg" />
<script> <script>
;(() => { ;(() => {
const stored = localStorage.getItem("theme") const stored = localStorage.getItem("theme")
@ -22,7 +22,7 @@
stored === "dark" || (!stored && matchMedia("(prefers-color-scheme: dark)").matches) stored === "dark" || (!stored && matchMedia("(prefers-color-scheme: dark)").matches)
if (isDark) { if (isDark) {
document.documentElement.setAttribute("data-theme", "dark") document.documentElement.setAttribute("data-theme", "dark")
document.getElementById("favicon").href = "/favicon-dark.svg" document.getElementById("favicon").href = "/static/favicon-dark.svg"
} }
})() })()
</script> </script>
@ -30,11 +30,11 @@
type="module" type="module"
src="https://cdn.jsdelivr.net/gh/starfederation/datastar@1.0.0-RC.6/bundles/datastar.js" src="https://cdn.jsdelivr.net/gh/starfederation/datastar@1.0.0-RC.6/bundles/datastar.js"
></script> ></script>
<script defer src="/components/photo-capture.js"></script> <script defer src="/static/js/components/photo-capture.js"></script>
<script defer src="/components/searchable-select.js"></script> <script defer src="/static/js/components/searchable-select.js"></script>
<script defer src="/components/chip-scroll.js"></script> <script defer src="/static/js/components/chip-scroll.js"></script>
<script defer src="/components/world-map.js"></script> <script defer src="/static/js/components/world-map.js"></script>
<script defer src="/components/donut-chart.js"></script> <script defer src="/static/js/components/donut-chart.js"></script>
{% block head %}{% endblock %} {% block head %}{% endblock %}
<script> <script>
const toggleRow = (event) => { const toggleRow = (event) => {

View file

@ -1,7 +1,7 @@
{% extends "base.html" %} {% import "partials/icons.html" as icons %} {% extends "base.html" %} {% import "partials/icons.html" as icons %}
{% block title %}Brewlog · Admin{% endblock %} {% block title %}Brewlog · Admin{% endblock %}
{% block head %} {% block head %}
<script src="/webauthn.js"></script> <script src="/static/js/webauthn.js"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<header class="flex flex-col gap-2"> <header class="flex flex-col gap-2">

View file

@ -5,7 +5,7 @@
{% block description %}{{ bag.roast_name }} by {{ bag.roaster_name }} — {{ bag.amount }} bag.{% endblock %} {% block description %}{{ bag.roast_name }} by {{ bag.roaster_name }} — {{ bag.amount }} bag.{% endblock %}
{% block og_title %}{{ bag.roast_name }} — Brewlog{% endblock %} {% block og_title %}{{ bag.roast_name }} — Brewlog{% endblock %}
{% block og_description %}{{ bag.roast_name }} by {{ bag.roaster_name }} — {{ bag.amount }} bag.{% endblock %} {% block og_description %}{{ bag.roast_name }} by {{ bag.roaster_name }} — {{ bag.amount }} bag.{% endblock %}
{% block head %}<meta property="og:image" content="{{ base_url }}/og-image.png" />{% endblock %} {% block head %}<meta property="og:image" content="{{ base_url }}/static/og-image.png" />{% endblock %}
{% block content %} {% block content %}
<header class="flex items-start justify-between gap-4"> <header class="flex items-start justify-between gap-4">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">

View file

@ -5,7 +5,7 @@
{% block description %}{{ brew.roast_name }} by {{ brew.roaster_name }} — {{ brew.coffee_weight }} coffee, {{ brew.water_volume }} water.{% endblock %} {% block description %}{{ brew.roast_name }} by {{ brew.roaster_name }} — {{ brew.coffee_weight }} coffee, {{ brew.water_volume }} water.{% endblock %}
{% block og_title %}{{ brew.roast_name }} — Brewlog{% endblock %} {% block og_title %}{{ brew.roast_name }} — Brewlog{% endblock %}
{% block og_description %}{{ brew.roast_name }} by {{ brew.roaster_name }} — {{ brew.coffee_weight }} coffee, {{ brew.water_volume }} water.{% endblock %} {% block og_description %}{{ brew.roast_name }} by {{ brew.roaster_name }} — {{ brew.coffee_weight }} coffee, {{ brew.water_volume }} water.{% endblock %}
{% block head %}<meta property="og:image" content="{{ base_url }}/og-image.png" />{% endblock %} {% block head %}<meta property="og:image" content="{{ base_url }}/static/og-image.png" />{% endblock %}
{% block content %} {% block content %}
<header class="flex items-start justify-between gap-4"> <header class="flex items-start justify-between gap-4">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">

View file

@ -4,7 +4,7 @@
{% block title %}Brewlog · {{ cafe.name }}{% endblock %} {% block title %}Brewlog · {{ cafe.name }}{% endblock %}
{% block og_title %}{{ cafe.name }} — Brewlog{% endblock %} {% block og_title %}{{ cafe.name }} — Brewlog{% endblock %}
{% block og_description %}{{ cafe.name }} — {{ cafe.city }}, {{ cafe.country_flag }} {{ cafe.country }}{% endblock %} {% block og_description %}{{ cafe.name }} — {{ cafe.city }}, {{ cafe.country_flag }} {{ cafe.country }}{% endblock %}
{% block head %}<meta property="og:image" content="{{ base_url }}/og-image.png" />{% endblock %} {% block head %}<meta property="og:image" content="{{ base_url }}/static/og-image.png" />{% endblock %}
{% block content %} {% block content %}
<header class="flex items-start justify-between gap-4"> <header class="flex items-start justify-between gap-4">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">

View file

@ -5,7 +5,7 @@
{% block description %}{{ cup.roast_name }} by {{ cup.roaster_name }} at {{ cup.cafe_name }}, {{ cup.cafe_city }}.{% endblock %} {% block description %}{{ cup.roast_name }} by {{ cup.roaster_name }} at {{ cup.cafe_name }}, {{ cup.cafe_city }}.{% endblock %}
{% block og_title %}{{ cup.roast_name }} at {{ cup.cafe_name }} — Brewlog{% endblock %} {% block og_title %}{{ cup.roast_name }} at {{ cup.cafe_name }} — Brewlog{% endblock %}
{% block og_description %}{{ cup.roast_name }} by {{ cup.roaster_name }} at {{ cup.cafe_name }}, {{ cup.cafe_city }}.{% endblock %} {% block og_description %}{{ cup.roast_name }} by {{ cup.roaster_name }} at {{ cup.cafe_name }}, {{ cup.cafe_city }}.{% endblock %}
{% block head %}<meta property="og:image" content="{{ base_url }}/og-image.png" />{% endblock %} {% block head %}<meta property="og:image" content="{{ base_url }}/static/og-image.png" />{% endblock %}
{% block content %} {% block content %}
<header class="flex items-start justify-between gap-4"> <header class="flex items-start justify-between gap-4">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">

View file

@ -3,7 +3,7 @@
{% block title %}Brewlog · {{ gear.make }} {{ gear.model }}{% endblock %} {% block title %}Brewlog · {{ gear.make }} {{ gear.model }}{% endblock %}
{% block og_title %}{{ gear.make }} {{ gear.model }} — Brewlog{% endblock %} {% block og_title %}{{ gear.make }} {{ gear.model }} — Brewlog{% endblock %}
{% block og_description %}{{ gear.make }} {{ gear.model }} — {{ gear.category_label }}{% endblock %} {% block og_description %}{{ gear.make }} {{ gear.model }} — {{ gear.category_label }}{% endblock %}
{% block head %}<meta property="og:image" content="{{ base_url }}/og-image.png" />{% endblock %} {% block head %}<meta property="og:image" content="{{ base_url }}/static/og-image.png" />{% endblock %}
{% block content %} {% block content %}
<header class="flex items-start justify-between gap-4"> <header class="flex items-start justify-between gap-4">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">

View file

@ -1,6 +1,6 @@
{% extends "base.html" %} {% import "partials/bag_card.html" as bag_card %} {% import {% extends "base.html" %} {% import "partials/bag_card.html" as bag_card %} {% import
"partials/brew_card.html" as brew_card %} {% import "partials/icons.html" as icons %} {% block title %}Brewlog{% endblock %} {% block head %} "partials/brew_card.html" as brew_card %} {% import "partials/icons.html" as icons %} {% block title %}Brewlog{% endblock %} {% block head %}
<meta property="og:image" content="{{ base_url }}/og-image.png" /> <meta property="og:image" content="{{ base_url }}/static/og-image.png" />
{% endblock %} {% block content %} {% endblock %} {% block content %}
<!-- Scan Bag --> <!-- Scan Bag -->
{% if is_authenticated %} {% if is_authenticated %}

View file

@ -1,7 +1,7 @@
{% extends "base.html" %} {% import "partials/icons.html" as icons %} {% extends "base.html" %} {% import "partials/icons.html" as icons %}
{% block title %}Brewlog · Login{% endblock %} {% block title %}Brewlog · Login{% endblock %}
{% block head %} {% block head %}
<script src="/webauthn.js"></script> <script src="/static/js/webauthn.js"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="mx-auto max-w-md"> <div class="mx-auto max-w-md">

View file

@ -1,7 +1,7 @@
{% extends "base.html" %} {% import "partials/icons.html" as icons %} {% extends "base.html" %} {% import "partials/icons.html" as icons %}
{% block title %}Brewlog · Register{% endblock %} {% block title %}Brewlog · Register{% endblock %}
{% block head %} {% block head %}
<script src="/webauthn.js"></script> <script src="/static/js/webauthn.js"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="mx-auto max-w-md"> <div class="mx-auto max-w-md">

View file

@ -4,7 +4,7 @@
{% block title %}Brewlog · {{ roast.name }}{% endblock %} {% block title %}Brewlog · {{ roast.name }}{% endblock %}
{% block og_title %}{{ roast.name }} — Brewlog{% endblock %} {% block og_title %}{{ roast.name }} — Brewlog{% endblock %}
{% block og_description %}{{ roast.name }} by {{ roast.roaster_name }} — {{ roast.origin }}{% endblock %} {% block og_description %}{{ roast.name }} by {{ roast.roaster_name }} — {{ roast.origin }}{% endblock %}
{% block head %}<meta property="og:image" content="{{ base_url }}/og-image.png" />{% endblock %} {% block head %}<meta property="og:image" content="{{ base_url }}/static/og-image.png" />{% endblock %}
{% block content %} {% block content %}
<header class="flex items-start justify-between gap-4"> <header class="flex items-start justify-between gap-4">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">

View file

@ -4,7 +4,7 @@
{% block title %}Brewlog · {{ roaster.name }}{% endblock %} {% block title %}Brewlog · {{ roaster.name }}{% endblock %}
{% block og_title %}{{ roaster.name }} — Brewlog{% endblock %} {% block og_title %}{{ roaster.name }} — Brewlog{% endblock %}
{% block og_description %}{{ roaster.name }} — {{ roaster.country_flag }} {{ roaster.country }}{% if let Some(c) = roaster.city %}, {{ c }}{% endif %}{% endblock %} {% block og_description %}{{ roaster.name }} — {{ roaster.country_flag }} {{ roaster.country }}{% if let Some(c) = roaster.city %}, {{ c }}{% endif %}{% endblock %}
{% block head %}<meta property="og:image" content="{{ base_url }}/og-image.png" />{% endblock %} {% block head %}<meta property="og:image" content="{{ base_url }}/static/og-image.png" />{% endblock %}
{% block content %} {% block content %}
<header class="flex items-start justify-between gap-4"> <header class="flex items-start justify-between gap-4">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">

View file

@ -1,7 +1,7 @@
{% extends "base.html" %} {% import "partials/icons.html" as icons %} {% import "partials/histogram.html" as histogram %} {% block title %}Brewlog · Stats{% endblock %} {% extends "base.html" %} {% import "partials/icons.html" as icons %} {% import "partials/histogram.html" as histogram %} {% block title %}Brewlog · Stats{% endblock %}
{% block og_title %}Stats — Brewlog{% endblock %} {% block og_title %}Stats — Brewlog{% endblock %}
{% block og_description %}Aggregated coffee data across origins, consumption, and brewing.{% endblock %} {% block og_description %}Aggregated coffee data across origins, consumption, and brewing.{% endblock %}
{% block head %}<meta property="og:image" content="{{ base_url }}/og-image.png" />{% endblock %} {% block head %}<meta property="og:image" content="{{ base_url }}/static/og-image.png" />{% endblock %}
{% block content %} {% block content %}
<header class="flex flex-col gap-2"> <header class="flex flex-col gap-2">
<div class="flex items-start justify-between gap-4"> <div class="flex items-start justify-between gap-4">

View file

@ -52,7 +52,7 @@
}); });
const favicon = document.getElementById("favicon"); const favicon = document.getElementById("favicon");
if (favicon) { if (favicon) {
favicon.href = isDark ? "/favicon-dark.svg" : "/favicon-light.svg"; favicon.href = isDark ? "/static/favicon-dark.svg" : "/static/favicon-light.svg";
} }
}; };