From 54247ca4f08d03686505d5e1a297d572aaa52230 Mon Sep 17 00:00:00 2001 From: Samir Boulahtit Date: Mon, 18 May 2026 19:35:59 +0200 Subject: [PATCH] feat(static-assets): cache-bust JS/CSS via ?v=, immutable in prod MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a `static_v(request, name, path=...)` Jinja helper that appends ?v= from app.core.build_info, plus a CachedStaticFiles subclass that serves Cache-Control: public, max-age=31536000, immutable in production and no-cache in development. Browsers refetch JS/CSS automatically on every deploy without the user having to hard-reload. - New: app/core/static_files.py (CachedStaticFiles) - Updated: app/templates_config.py (static_v helper) - Updated: main.py (use CachedStaticFiles for *_static mounts) - Codemod: 143 url_for('*_static', path='*.js'|'*.css') → static_v(...) across 123 templates. Images/fonts/JSON locales intentionally unchanged (out of scope). - Arch rule: FE-024 (warning) flags raw url_for on JS/CSS to prevent drift. Note: FE-008 was already taken by the number_stepper rule. - docs/proposals/static-asset-cache-busting.md marked Done. Closes plan from docs/proposals/static-asset-cache-busting.md. Co-Authored-By: Claude Opus 4.7 (1M context) --- .architecture-rules/frontend.yaml | 27 ++++++++++++ app/core/static_files.py | 31 ++++++++++++++ .../templates/analytics/store/analytics.html | 2 +- .../billing/admin/billing-history.html | 2 +- .../billing/admin/subscription-tiers.html | 2 +- .../billing/admin/subscriptions.html | 2 +- .../catalog/admin/store-product-create.html | 4 +- .../catalog/admin/store-product-detail.html | 2 +- .../catalog/admin/store-product-edit.html | 4 +- .../catalog/admin/store-products.html | 2 +- .../catalog/store/product-create.html | 2 +- .../templates/catalog/store/products.html | 2 +- .../cms/admin/content-page-edit.html | 2 +- .../templates/cms/admin/content-pages.html | 2 +- .../cms/templates/cms/admin/store-theme.html | 2 +- .../cms/templates/cms/admin/store-themes.html | 2 +- .../cms/store/content-page-edit.html | 2 +- .../templates/cms/store/content-pages.html | 2 +- .../cms/templates/cms/store/media.html | 2 +- .../core/templates/core/admin/dashboard.html | 2 +- .../core/templates/core/admin/settings.html | 2 +- .../core/templates/core/store/dashboard.html | 2 +- .../templates/customers/admin/customers.html | 2 +- .../customers/store/customer-detail.html | 2 +- .../templates/customers/store/customers.html | 2 +- .../admin/code-quality-dashboard.html | 2 +- .../admin/code-quality-violations.html | 2 +- .../templates/dev_tools/admin/components.html | 2 +- .../templates/dev_tools/admin/icons.html | 2 +- .../templates/dev_tools/admin/sql-query.html | 2 +- .../dev_tools/admin/testing-dashboard.html | 2 +- .../dev_tools/admin/testing-hub.html | 2 +- .../dev_tools/admin/translation-editor.html | 2 +- .../templates/inventory/admin/inventory.html | 2 +- .../templates/inventory/store/inventory.html | 2 +- .../templates/loyalty/admin/analytics.html | 2 +- .../loyalty/admin/merchant-card-detail.html | 4 +- .../loyalty/admin/merchant-cards.html | 4 +- .../loyalty/admin/merchant-detail.html | 2 +- .../loyalty/admin/merchant-devices.html | 4 +- .../loyalty/admin/merchant-pins.html | 4 +- .../loyalty/admin/merchant-settings.html | 2 +- .../loyalty/admin/merchant-transactions.html | 4 +- .../templates/loyalty/admin/program-edit.html | 4 +- .../templates/loyalty/admin/programs.html | 2 +- .../templates/loyalty/merchant/analytics.html | 2 +- .../loyalty/merchant/card-detail.html | 4 +- .../templates/loyalty/merchant/cards.html | 4 +- .../templates/loyalty/merchant/devices.html | 4 +- .../templates/loyalty/merchant/pins.html | 4 +- .../loyalty/merchant/program-edit.html | 4 +- .../templates/loyalty/merchant/settings.html | 2 +- .../loyalty/merchant/transactions.html | 4 +- .../templates/loyalty/store/analytics.html | 2 +- .../templates/loyalty/store/card-detail.html | 2 +- .../templates/loyalty/store/cards.html | 2 +- .../templates/loyalty/store/enroll.html | 2 +- .../loyalty/templates/loyalty/store/pins.html | 4 +- .../templates/loyalty/store/settings.html | 4 +- .../templates/loyalty/store/terminal.html | 2 +- .../loyalty/storefront/dashboard.html | 2 +- .../templates/loyalty/storefront/enroll.html | 2 +- .../templates/loyalty/storefront/history.html | 2 +- .../marketplace/admin/background-tasks.html | 2 +- .../templates/marketplace/admin/imports.html | 2 +- .../admin/letzshop-store-directory.html | 2 +- .../templates/marketplace/admin/letzshop.html | 2 +- .../admin/marketplace-letzshop.html | 2 +- .../admin/marketplace-product-detail.html | 2 +- .../admin/marketplace-products.html | 2 +- .../marketplace/admin/marketplace.html | 2 +- .../marketplace/store/onboarding.html | 2 +- .../templates/messaging/admin/email-logs.html | 2 +- .../messaging/admin/email-templates.html | 2 +- .../templates/messaging/admin/messages.html | 2 +- .../messaging/admin/notifications.html | 2 +- .../messaging/store/email-templates.html | 2 +- .../templates/messaging/store/messages.html | 2 +- .../messaging/store/notifications.html | 2 +- .../templates/monitoring/admin/logs.html | 2 +- .../monitoring/admin/platform-health.html | 2 +- .../orders/templates/orders/admin/orders.html | 2 +- .../templates/orders/store/order-detail.html | 2 +- .../orders/templates/orders/store/orders.html | 2 +- .../prospecting/admin/campaigns.html | 2 +- .../templates/prospecting/admin/capture.html | 2 +- .../prospecting/admin/dashboard.html | 2 +- .../templates/prospecting/admin/leads.html | 2 +- .../prospecting/admin/prospect-detail.html | 2 +- .../prospecting/admin/prospects.html | 2 +- .../prospecting/admin/scan-jobs.html | 2 +- .../tenancy/admin/admin-user-detail.html | 2 +- .../tenancy/admin/admin-user-edit.html | 2 +- .../templates/tenancy/admin/admin-users.html | 2 +- .../templates/tenancy/admin/login.html | 2 +- .../tenancy/admin/merchant-detail.html | 2 +- .../tenancy/admin/merchant-edit.html | 2 +- .../tenancy/admin/merchant-user-detail.html | 2 +- .../tenancy/admin/merchant-user-edit.html | 2 +- .../tenancy/admin/merchant-users.html | 2 +- .../templates/tenancy/admin/merchants.html | 2 +- .../tenancy/admin/platform-create.html | 2 +- .../tenancy/admin/platform-detail.html | 2 +- .../tenancy/admin/platform-edit.html | 2 +- .../tenancy/admin/platform-menu-config.html | 2 +- .../tenancy/admin/platform-modules.html | 2 +- .../templates/tenancy/admin/platforms.html | 2 +- .../tenancy/admin/select-platform.html | 2 +- .../templates/tenancy/admin/store-create.html | 2 +- .../templates/tenancy/admin/store-detail.html | 2 +- .../templates/tenancy/admin/store-edit.html | 2 +- .../templates/tenancy/admin/store-roles.html | 2 +- .../templates/tenancy/admin/stores.html | 2 +- .../templates/tenancy/admin/user-create.html | 2 +- .../templates/tenancy/merchant/login.html | 2 +- .../templates/tenancy/merchant/team.html | 2 +- .../templates/tenancy/store/login.html | 2 +- .../templates/tenancy/store/profile.html | 2 +- .../templates/tenancy/store/roles.html | 2 +- .../templates/tenancy/store/settings.html | 2 +- .../tenancy/templates/tenancy/store/team.html | 2 +- app/templates/admin/base.html | 6 +-- app/templates/merchant/base.html | 2 +- app/templates/store/base.html | 6 +-- app/templates/storefront/base.html | 2 +- app/templates_config.py | 25 +++++++++++ docs/proposals/static-asset-cache-busting.md | 11 ++++- main.py | 5 ++- scripts/validate/validate_architecture.py | 42 +++++++++++++++++++ 129 files changed, 281 insertions(+), 146 deletions(-) create mode 100644 app/core/static_files.py diff --git a/.architecture-rules/frontend.yaml b/.architecture-rules/frontend.yaml index 8ebff8d0..48e64cf8 100644 --- a/.architecture-rules/frontend.yaml +++ b/.architecture-rules/frontend.yaml @@ -936,6 +936,33 @@ frontend_component_rules: encouraged_patterns: - "{% from 'shared/macros/shop/trust-badges.html' import" + - id: "FE-024" + name: "Use static_v() for JS/CSS, not raw url_for()" + severity: "warning" + description: | + Static .js and .css URLs must use the cache-busting `static_v()` helper + so browsers refetch automatically after each deploy. The helper appends + `?v=` from app.core.build_info — same SHA across one deploy, + flips on the next. + + WRONG (browser keeps cached file after deploy): + + + + RIGHT: + + + + Images, fonts, and JSON locale files are intentionally out of scope — + keep using `url_for()` for those. + pattern: + file_pattern: "app/**/templates/**/*.html" + anti_patterns: + - "url_for\\(\\s*['\"]\\w+_static['\"]\\s*,\\s*path=['\"][^'\"]+\\.(?:js|css)['\"]" + exceptions: + - "base.html" + - "partials/" + # ============================================================================ # FRONTEND STYLING RULES # ============================================================================ diff --git a/app/core/static_files.py b/app/core/static_files.py new file mode 100644 index 00000000..a71e898a --- /dev/null +++ b/app/core/static_files.py @@ -0,0 +1,31 @@ +# app/core/static_files.py +"""Static file serving with cache-busting-aware headers. + +In production, JS/CSS/etc. URLs are versioned by `static_v(...)` in +`app/templates_config.py` (appends `?v=`), so the file at a +given URL never changes within a deploy. We can therefore tell browsers +to cache it for a year and skip revalidation. + +In development the version still flips per commit, but the developer often +edits files without committing, so we serve `no-cache` to force conditional +GETs (the browser still gets a 304 when nothing changed). +""" + +from fastapi.staticfiles import StaticFiles +from starlette.responses import FileResponse + +from app.core.environment import is_development + +_IMMUTABLE = "public, max-age=31536000, immutable" +_NO_CACHE = "no-cache" + + +class CachedStaticFiles(StaticFiles): + """StaticFiles that sets Cache-Control based on environment.""" + + def file_response(self, *args, **kwargs) -> FileResponse: + response = super().file_response(*args, **kwargs) + response.headers["Cache-Control"] = ( + _NO_CACHE if is_development() else _IMMUTABLE + ) + return response diff --git a/app/modules/analytics/templates/analytics/store/analytics.html b/app/modules/analytics/templates/analytics/store/analytics.html index 3db0e0bb..5e59dc65 100644 --- a/app/modules/analytics/templates/analytics/store/analytics.html +++ b/app/modules/analytics/templates/analytics/store/analytics.html @@ -227,5 +227,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/billing/templates/billing/admin/billing-history.html b/app/modules/billing/templates/billing/admin/billing-history.html index f9a9b6dc..48e7b738 100644 --- a/app/modules/billing/templates/billing/admin/billing-history.html +++ b/app/modules/billing/templates/billing/admin/billing-history.html @@ -203,5 +203,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/billing/templates/billing/admin/subscription-tiers.html b/app/modules/billing/templates/billing/admin/subscription-tiers.html index 8d87d375..37644c65 100644 --- a/app/modules/billing/templates/billing/admin/subscription-tiers.html +++ b/app/modules/billing/templates/billing/admin/subscription-tiers.html @@ -474,5 +474,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/billing/templates/billing/admin/subscriptions.html b/app/modules/billing/templates/billing/admin/subscriptions.html index 57e49845..ffef8c1f 100644 --- a/app/modules/billing/templates/billing/admin/subscriptions.html +++ b/app/modules/billing/templates/billing/admin/subscriptions.html @@ -316,5 +316,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/catalog/templates/catalog/admin/store-product-create.html b/app/modules/catalog/templates/catalog/admin/store-product-create.html index 3c0c63bf..584de9bc 100644 --- a/app/modules/catalog/templates/catalog/admin/store-product-create.html +++ b/app/modules/catalog/templates/catalog/admin/store-product-create.html @@ -465,6 +465,6 @@ document.head.appendChild(script); })(); - - + + {% endblock %} diff --git a/app/modules/catalog/templates/catalog/admin/store-product-detail.html b/app/modules/catalog/templates/catalog/admin/store-product-detail.html index d753c2c9..39f9da22 100644 --- a/app/modules/catalog/templates/catalog/admin/store-product-detail.html +++ b/app/modules/catalog/templates/catalog/admin/store-product-detail.html @@ -354,5 +354,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/catalog/templates/catalog/admin/store-product-edit.html b/app/modules/catalog/templates/catalog/admin/store-product-edit.html index d965a57f..8857bbdd 100644 --- a/app/modules/catalog/templates/catalog/admin/store-product-edit.html +++ b/app/modules/catalog/templates/catalog/admin/store-product-edit.html @@ -498,6 +498,6 @@ {% endblock %} {% block extra_scripts %} - - + + {% endblock %} diff --git a/app/modules/catalog/templates/catalog/admin/store-products.html b/app/modules/catalog/templates/catalog/admin/store-products.html index b027fbf2..90832cd5 100644 --- a/app/modules/catalog/templates/catalog/admin/store-products.html +++ b/app/modules/catalog/templates/catalog/admin/store-products.html @@ -336,5 +336,5 @@ document.head.appendChild(script); })(); - + {% endblock %} diff --git a/app/modules/catalog/templates/catalog/store/product-create.html b/app/modules/catalog/templates/catalog/store/product-create.html index 034fbef7..df701fff 100644 --- a/app/modules/catalog/templates/catalog/store/product-create.html +++ b/app/modules/catalog/templates/catalog/store/product-create.html @@ -170,5 +170,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/catalog/templates/catalog/store/products.html b/app/modules/catalog/templates/catalog/store/products.html index 686afb57..c42a98ee 100644 --- a/app/modules/catalog/templates/catalog/store/products.html +++ b/app/modules/catalog/templates/catalog/store/products.html @@ -364,5 +364,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/cms/templates/cms/admin/content-page-edit.html b/app/modules/cms/templates/cms/admin/content-page-edit.html index fc71fe9b..45936186 100644 --- a/app/modules/cms/templates/cms/admin/content-page-edit.html +++ b/app/modules/cms/templates/cms/admin/content-page-edit.html @@ -701,5 +701,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/cms/templates/cms/admin/content-pages.html b/app/modules/cms/templates/cms/admin/content-pages.html index 62c487f1..4ac39bdb 100644 --- a/app/modules/cms/templates/cms/admin/content-pages.html +++ b/app/modules/cms/templates/cms/admin/content-pages.html @@ -201,5 +201,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/cms/templates/cms/admin/store-theme.html b/app/modules/cms/templates/cms/admin/store-theme.html index d43caced..f4ed724a 100644 --- a/app/modules/cms/templates/cms/admin/store-theme.html +++ b/app/modules/cms/templates/cms/admin/store-theme.html @@ -459,5 +459,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/cms/templates/cms/admin/store-themes.html b/app/modules/cms/templates/cms/admin/store-themes.html index 40177648..3e5c53b3 100644 --- a/app/modules/cms/templates/cms/admin/store-themes.html +++ b/app/modules/cms/templates/cms/admin/store-themes.html @@ -125,5 +125,5 @@ {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/cms/templates/cms/store/content-page-edit.html b/app/modules/cms/templates/cms/store/content-page-edit.html index 1148e9bf..c040bd64 100644 --- a/app/modules/cms/templates/cms/store/content-page-edit.html +++ b/app/modules/cms/templates/cms/store/content-page-edit.html @@ -333,5 +333,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/cms/templates/cms/store/content-pages.html b/app/modules/cms/templates/cms/store/content-pages.html index 809bb002..019d7446 100644 --- a/app/modules/cms/templates/cms/store/content-pages.html +++ b/app/modules/cms/templates/cms/store/content-pages.html @@ -335,5 +335,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/cms/templates/cms/store/media.html b/app/modules/cms/templates/cms/store/media.html index 447db1ba..4202c845 100644 --- a/app/modules/cms/templates/cms/store/media.html +++ b/app/modules/cms/templates/cms/store/media.html @@ -452,5 +452,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/core/templates/core/admin/dashboard.html b/app/modules/core/templates/core/admin/dashboard.html index 2187915d..848ca9b1 100644 --- a/app/modules/core/templates/core/admin/dashboard.html +++ b/app/modules/core/templates/core/admin/dashboard.html @@ -137,5 +137,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/core/templates/core/admin/settings.html b/app/modules/core/templates/core/admin/settings.html index 80b1b1e1..ae182dc0 100644 --- a/app/modules/core/templates/core/admin/settings.html +++ b/app/modules/core/templates/core/admin/settings.html @@ -816,5 +816,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/core/templates/core/store/dashboard.html b/app/modules/core/templates/core/store/dashboard.html index 23e339e5..370467b5 100644 --- a/app/modules/core/templates/core/store/dashboard.html +++ b/app/modules/core/templates/core/store/dashboard.html @@ -180,5 +180,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/customers/templates/customers/admin/customers.html b/app/modules/customers/templates/customers/admin/customers.html index 40ae0f10..24c3c525 100644 --- a/app/modules/customers/templates/customers/admin/customers.html +++ b/app/modules/customers/templates/customers/admin/customers.html @@ -217,5 +217,5 @@ document.head.appendChild(script); })(); - + {% endblock %} diff --git a/app/modules/customers/templates/customers/store/customer-detail.html b/app/modules/customers/templates/customers/store/customer-detail.html index cb074322..ed52ac8b 100644 --- a/app/modules/customers/templates/customers/store/customer-detail.html +++ b/app/modules/customers/templates/customers/store/customer-detail.html @@ -174,5 +174,5 @@ customerId: {{ customer_id }} }; - + {% endblock %} diff --git a/app/modules/customers/templates/customers/store/customers.html b/app/modules/customers/templates/customers/store/customers.html index c346ffef..c3c86c72 100644 --- a/app/modules/customers/templates/customers/store/customers.html +++ b/app/modules/customers/templates/customers/store/customers.html @@ -210,5 +210,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/dev_tools/templates/dev_tools/admin/code-quality-dashboard.html b/app/modules/dev_tools/templates/dev_tools/admin/code-quality-dashboard.html index 2039cff9..54a2c617 100644 --- a/app/modules/dev_tools/templates/dev_tools/admin/code-quality-dashboard.html +++ b/app/modules/dev_tools/templates/dev_tools/admin/code-quality-dashboard.html @@ -9,7 +9,7 @@ {% block alpine_data %}codeQualityDashboard(){% endblock %} {% block extra_scripts %} - + {% endblock %} {% block content %} diff --git a/app/modules/dev_tools/templates/dev_tools/admin/code-quality-violations.html b/app/modules/dev_tools/templates/dev_tools/admin/code-quality-violations.html index f5c81ed9..410c36b6 100644 --- a/app/modules/dev_tools/templates/dev_tools/admin/code-quality-violations.html +++ b/app/modules/dev_tools/templates/dev_tools/admin/code-quality-violations.html @@ -10,7 +10,7 @@ {% block alpine_data %}codeQualityViolations(){% endblock %} {% block extra_scripts %} - + {% endblock %} {% block content %} diff --git a/app/modules/dev_tools/templates/dev_tools/admin/components.html b/app/modules/dev_tools/templates/dev_tools/admin/components.html index 1ccd421c..9635d716 100644 --- a/app/modules/dev_tools/templates/dev_tools/admin/components.html +++ b/app/modules/dev_tools/templates/dev_tools/admin/components.html @@ -3173,5 +3173,5 @@ new Chart(document.getElementById('barChart'), barConfig); {% block extra_scripts %} {# ✅ CRITICAL: Load JavaScript file #} - + {% endblock %} diff --git a/app/modules/dev_tools/templates/dev_tools/admin/icons.html b/app/modules/dev_tools/templates/dev_tools/admin/icons.html index b221fdab..2062aba2 100644 --- a/app/modules/dev_tools/templates/dev_tools/admin/icons.html +++ b/app/modules/dev_tools/templates/dev_tools/admin/icons.html @@ -318,5 +318,5 @@ {% block extra_scripts %} {# ✅ CRITICAL: Load JavaScript file #} - + {% endblock %} diff --git a/app/modules/dev_tools/templates/dev_tools/admin/sql-query.html b/app/modules/dev_tools/templates/dev_tools/admin/sql-query.html index 28ac9bab..7bb1492a 100644 --- a/app/modules/dev_tools/templates/dev_tools/admin/sql-query.html +++ b/app/modules/dev_tools/templates/dev_tools/admin/sql-query.html @@ -274,5 +274,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/dev_tools/templates/dev_tools/admin/testing-dashboard.html b/app/modules/dev_tools/templates/dev_tools/admin/testing-dashboard.html index df589dbf..e8f07da1 100644 --- a/app/modules/dev_tools/templates/dev_tools/admin/testing-dashboard.html +++ b/app/modules/dev_tools/templates/dev_tools/admin/testing-dashboard.html @@ -8,7 +8,7 @@ {% block alpine_data %}testingDashboard(){% endblock %} {% block extra_scripts %} - + {% endblock %} {% block content %} diff --git a/app/modules/dev_tools/templates/dev_tools/admin/testing-hub.html b/app/modules/dev_tools/templates/dev_tools/admin/testing-hub.html index 47b9888a..69a6a209 100644 --- a/app/modules/dev_tools/templates/dev_tools/admin/testing-hub.html +++ b/app/modules/dev_tools/templates/dev_tools/admin/testing-hub.html @@ -218,5 +218,5 @@ {% block extra_scripts %} {# ✅ CRITICAL: Load JavaScript file #} - + {% endblock %} diff --git a/app/modules/dev_tools/templates/dev_tools/admin/translation-editor.html b/app/modules/dev_tools/templates/dev_tools/admin/translation-editor.html index 4952d5bb..798ac1b6 100644 --- a/app/modules/dev_tools/templates/dev_tools/admin/translation-editor.html +++ b/app/modules/dev_tools/templates/dev_tools/admin/translation-editor.html @@ -159,5 +159,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/inventory/templates/inventory/admin/inventory.html b/app/modules/inventory/templates/inventory/admin/inventory.html index e5d8548f..460ea4bb 100644 --- a/app/modules/inventory/templates/inventory/admin/inventory.html +++ b/app/modules/inventory/templates/inventory/admin/inventory.html @@ -599,5 +599,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/inventory/templates/inventory/store/inventory.html b/app/modules/inventory/templates/inventory/store/inventory.html index 61f2c3e0..b1c8efdf 100644 --- a/app/modules/inventory/templates/inventory/store/inventory.html +++ b/app/modules/inventory/templates/inventory/store/inventory.html @@ -370,5 +370,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/admin/analytics.html b/app/modules/loyalty/templates/loyalty/admin/analytics.html index 8c309400..f86b02ae 100644 --- a/app/modules/loyalty/templates/loyalty/admin/analytics.html +++ b/app/modules/loyalty/templates/loyalty/admin/analytics.html @@ -175,5 +175,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/admin/merchant-card-detail.html b/app/modules/loyalty/templates/loyalty/admin/merchant-card-detail.html index 2d0dd945..b4879950 100644 --- a/app/modules/loyalty/templates/loyalty/admin/merchant-card-detail.html +++ b/app/modules/loyalty/templates/loyalty/admin/merchant-card-detail.html @@ -21,6 +21,6 @@ {% endblock %} {% block extra_scripts %} - - + + {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/admin/merchant-cards.html b/app/modules/loyalty/templates/loyalty/admin/merchant-cards.html index 43ad88bb..369409d4 100644 --- a/app/modules/loyalty/templates/loyalty/admin/merchant-cards.html +++ b/app/modules/loyalty/templates/loyalty/admin/merchant-cards.html @@ -23,6 +23,6 @@ {% endblock %} {% block extra_scripts %} - - + + {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/admin/merchant-detail.html b/app/modules/loyalty/templates/loyalty/admin/merchant-detail.html index 8caa3522..aad96071 100644 --- a/app/modules/loyalty/templates/loyalty/admin/merchant-detail.html +++ b/app/modules/loyalty/templates/loyalty/admin/merchant-detail.html @@ -428,5 +428,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/admin/merchant-devices.html b/app/modules/loyalty/templates/loyalty/admin/merchant-devices.html index cf255d7b..9ba8b5ac 100644 --- a/app/modules/loyalty/templates/loyalty/admin/merchant-devices.html +++ b/app/modules/loyalty/templates/loyalty/admin/merchant-devices.html @@ -22,6 +22,6 @@ {% endblock %} {% block extra_scripts %} - - + + {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/admin/merchant-pins.html b/app/modules/loyalty/templates/loyalty/admin/merchant-pins.html index 3965c0d9..6857e81a 100644 --- a/app/modules/loyalty/templates/loyalty/admin/merchant-pins.html +++ b/app/modules/loyalty/templates/loyalty/admin/merchant-pins.html @@ -22,6 +22,6 @@ {% endblock %} {% block extra_scripts %} - - + + {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/admin/merchant-settings.html b/app/modules/loyalty/templates/loyalty/admin/merchant-settings.html index 8685bcc8..9e0d44bd 100644 --- a/app/modules/loyalty/templates/loyalty/admin/merchant-settings.html +++ b/app/modules/loyalty/templates/loyalty/admin/merchant-settings.html @@ -178,5 +178,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/admin/merchant-transactions.html b/app/modules/loyalty/templates/loyalty/admin/merchant-transactions.html index 7628e7e3..e8308353 100644 --- a/app/modules/loyalty/templates/loyalty/admin/merchant-transactions.html +++ b/app/modules/loyalty/templates/loyalty/admin/merchant-transactions.html @@ -21,6 +21,6 @@ {% endblock %} {% block extra_scripts %} - - + + {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/admin/program-edit.html b/app/modules/loyalty/templates/loyalty/admin/program-edit.html index 5b7fef63..3db801a7 100644 --- a/app/modules/loyalty/templates/loyalty/admin/program-edit.html +++ b/app/modules/loyalty/templates/loyalty/admin/program-edit.html @@ -41,6 +41,6 @@ {% endblock %} {% block extra_scripts %} - - + + {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/admin/programs.html b/app/modules/loyalty/templates/loyalty/admin/programs.html index af420e25..bee9bde7 100644 --- a/app/modules/loyalty/templates/loyalty/admin/programs.html +++ b/app/modules/loyalty/templates/loyalty/admin/programs.html @@ -342,5 +342,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/merchant/analytics.html b/app/modules/loyalty/templates/loyalty/merchant/analytics.html index 86d75ad5..2b8e0ebe 100644 --- a/app/modules/loyalty/templates/loyalty/merchant/analytics.html +++ b/app/modules/loyalty/templates/loyalty/merchant/analytics.html @@ -63,5 +63,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/merchant/card-detail.html b/app/modules/loyalty/templates/loyalty/merchant/card-detail.html index e7f79af2..ad9e803f 100644 --- a/app/modules/loyalty/templates/loyalty/merchant/card-detail.html +++ b/app/modules/loyalty/templates/loyalty/merchant/card-detail.html @@ -25,6 +25,6 @@ {% endblock %} {% block extra_scripts %} - - + + {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/merchant/cards.html b/app/modules/loyalty/templates/loyalty/merchant/cards.html index 6b30dcd9..28cc3580 100644 --- a/app/modules/loyalty/templates/loyalty/merchant/cards.html +++ b/app/modules/loyalty/templates/loyalty/merchant/cards.html @@ -29,6 +29,6 @@ {% endblock %} {% block extra_scripts %} - - + + {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/merchant/devices.html b/app/modules/loyalty/templates/loyalty/merchant/devices.html index e9a56d44..7e584dbf 100644 --- a/app/modules/loyalty/templates/loyalty/merchant/devices.html +++ b/app/modules/loyalty/templates/loyalty/merchant/devices.html @@ -31,6 +31,6 @@ {% endblock %} {% block extra_scripts %} - - + + {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/merchant/pins.html b/app/modules/loyalty/templates/loyalty/merchant/pins.html index b3cf825a..e4d1e496 100644 --- a/app/modules/loyalty/templates/loyalty/merchant/pins.html +++ b/app/modules/loyalty/templates/loyalty/merchant/pins.html @@ -33,6 +33,6 @@ {% endblock %} {% block extra_scripts %} - - + + {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/merchant/program-edit.html b/app/modules/loyalty/templates/loyalty/merchant/program-edit.html index 81e7bd3f..1a7f19c8 100644 --- a/app/modules/loyalty/templates/loyalty/merchant/program-edit.html +++ b/app/modules/loyalty/templates/loyalty/merchant/program-edit.html @@ -39,6 +39,6 @@ {% endblock %} {% block extra_scripts %} - - + + {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/merchant/settings.html b/app/modules/loyalty/templates/loyalty/merchant/settings.html index 19df2218..c7fab47f 100644 --- a/app/modules/loyalty/templates/loyalty/merchant/settings.html +++ b/app/modules/loyalty/templates/loyalty/merchant/settings.html @@ -85,5 +85,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/merchant/transactions.html b/app/modules/loyalty/templates/loyalty/merchant/transactions.html index a116b14d..3bce7866 100644 --- a/app/modules/loyalty/templates/loyalty/merchant/transactions.html +++ b/app/modules/loyalty/templates/loyalty/merchant/transactions.html @@ -27,6 +27,6 @@ {% endblock %} {% block extra_scripts %} - - + + {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/store/analytics.html b/app/modules/loyalty/templates/loyalty/store/analytics.html index a4cb5b2b..7545a019 100644 --- a/app/modules/loyalty/templates/loyalty/store/analytics.html +++ b/app/modules/loyalty/templates/loyalty/store/analytics.html @@ -156,5 +156,5 @@ {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/store/card-detail.html b/app/modules/loyalty/templates/loyalty/store/card-detail.html index a1c77ae6..c2070a80 100644 --- a/app/modules/loyalty/templates/loyalty/store/card-detail.html +++ b/app/modules/loyalty/templates/loyalty/store/card-detail.html @@ -201,5 +201,5 @@ window._cardDetailLabels = { } }; - + {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/store/cards.html b/app/modules/loyalty/templates/loyalty/store/cards.html index 9e8f9543..b748edd5 100644 --- a/app/modules/loyalty/templates/loyalty/store/cards.html +++ b/app/modules/loyalty/templates/loyalty/store/cards.html @@ -167,5 +167,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/store/enroll.html b/app/modules/loyalty/templates/loyalty/store/enroll.html index 73ca1e43..e1a981ef 100644 --- a/app/modules/loyalty/templates/loyalty/store/enroll.html +++ b/app/modules/loyalty/templates/loyalty/store/enroll.html @@ -147,5 +147,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/store/pins.html b/app/modules/loyalty/templates/loyalty/store/pins.html index 83ab9af9..8b4c428d 100644 --- a/app/modules/loyalty/templates/loyalty/store/pins.html +++ b/app/modules/loyalty/templates/loyalty/store/pins.html @@ -44,6 +44,6 @@ {% endblock %} {% block extra_scripts %} - - + + {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/store/settings.html b/app/modules/loyalty/templates/loyalty/store/settings.html index 7280deec..efee098b 100644 --- a/app/modules/loyalty/templates/loyalty/store/settings.html +++ b/app/modules/loyalty/templates/loyalty/store/settings.html @@ -61,6 +61,6 @@ {% endblock %} {% block extra_scripts %} - - + + {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/store/terminal.html b/app/modules/loyalty/templates/loyalty/store/terminal.html index c17f6b2f..638cc773 100644 --- a/app/modules/loyalty/templates/loyalty/store/terminal.html +++ b/app/modules/loyalty/templates/loyalty/store/terminal.html @@ -407,5 +407,5 @@ window._txLabels = { reward_redeemed: {{ _('loyalty.transactions.reward_redeemed')|tojson }}, }; - + {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/storefront/dashboard.html b/app/modules/loyalty/templates/loyalty/storefront/dashboard.html index d2521cfc..8841a414 100644 --- a/app/modules/loyalty/templates/loyalty/storefront/dashboard.html +++ b/app/modules/loyalty/templates/loyalty/storefront/dashboard.html @@ -232,5 +232,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/storefront/enroll.html b/app/modules/loyalty/templates/loyalty/storefront/enroll.html index 62f7fe12..d6538bb6 100644 --- a/app/modules/loyalty/templates/loyalty/storefront/enroll.html +++ b/app/modules/loyalty/templates/loyalty/storefront/enroll.html @@ -170,5 +170,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/loyalty/templates/loyalty/storefront/history.html b/app/modules/loyalty/templates/loyalty/storefront/history.html index 083fa845..d9c0a024 100644 --- a/app/modules/loyalty/templates/loyalty/storefront/history.html +++ b/app/modules/loyalty/templates/loyalty/storefront/history.html @@ -105,5 +105,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/marketplace/templates/marketplace/admin/background-tasks.html b/app/modules/marketplace/templates/marketplace/admin/background-tasks.html index a66ed880..be318a76 100644 --- a/app/modules/marketplace/templates/marketplace/admin/background-tasks.html +++ b/app/modules/marketplace/templates/marketplace/admin/background-tasks.html @@ -8,7 +8,7 @@ {% block alpine_data %}backgroundTasks(){% endblock %} {% block extra_scripts %} - + {% endblock %} {% block content %} diff --git a/app/modules/marketplace/templates/marketplace/admin/imports.html b/app/modules/marketplace/templates/marketplace/admin/imports.html index 720d28bc..9e99181b 100644 --- a/app/modules/marketplace/templates/marketplace/admin/imports.html +++ b/app/modules/marketplace/templates/marketplace/admin/imports.html @@ -11,7 +11,7 @@ {% block alpine_data %}adminImports(){% endblock %} {% block extra_scripts %} - + {% endblock %} {% block content %} diff --git a/app/modules/marketplace/templates/marketplace/admin/letzshop-store-directory.html b/app/modules/marketplace/templates/marketplace/admin/letzshop-store-directory.html index 11caa0c4..5df2041e 100644 --- a/app/modules/marketplace/templates/marketplace/admin/letzshop-store-directory.html +++ b/app/modules/marketplace/templates/marketplace/admin/letzshop-store-directory.html @@ -426,5 +426,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/marketplace/templates/marketplace/admin/letzshop.html b/app/modules/marketplace/templates/marketplace/admin/letzshop.html index dde4cdb0..e85af8aa 100644 --- a/app/modules/marketplace/templates/marketplace/admin/letzshop.html +++ b/app/modules/marketplace/templates/marketplace/admin/letzshop.html @@ -11,7 +11,7 @@ {% block alpine_data %}adminLetzshop(){% endblock %} {% block extra_scripts %} - + {% endblock %} {% block content %} diff --git a/app/modules/marketplace/templates/marketplace/admin/marketplace-letzshop.html b/app/modules/marketplace/templates/marketplace/admin/marketplace-letzshop.html index 8fa6451c..5401634a 100644 --- a/app/modules/marketplace/templates/marketplace/admin/marketplace-letzshop.html +++ b/app/modules/marketplace/templates/marketplace/admin/marketplace-letzshop.html @@ -570,5 +570,5 @@ document.head.appendChild(script); })(); - + {% endblock %} diff --git a/app/modules/marketplace/templates/marketplace/admin/marketplace-product-detail.html b/app/modules/marketplace/templates/marketplace/admin/marketplace-product-detail.html index 1d5f9820..681185ed 100644 --- a/app/modules/marketplace/templates/marketplace/admin/marketplace-product-detail.html +++ b/app/modules/marketplace/templates/marketplace/admin/marketplace-product-detail.html @@ -388,5 +388,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/marketplace/templates/marketplace/admin/marketplace-products.html b/app/modules/marketplace/templates/marketplace/admin/marketplace-products.html index 505056e9..cdc1467b 100644 --- a/app/modules/marketplace/templates/marketplace/admin/marketplace-products.html +++ b/app/modules/marketplace/templates/marketplace/admin/marketplace-products.html @@ -412,5 +412,5 @@ document.head.appendChild(script); })(); - + {% endblock %} diff --git a/app/modules/marketplace/templates/marketplace/admin/marketplace.html b/app/modules/marketplace/templates/marketplace/admin/marketplace.html index 99d0fd95..83cc5eb2 100644 --- a/app/modules/marketplace/templates/marketplace/admin/marketplace.html +++ b/app/modules/marketplace/templates/marketplace/admin/marketplace.html @@ -347,5 +347,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/marketplace/templates/marketplace/store/onboarding.html b/app/modules/marketplace/templates/marketplace/store/onboarding.html index 57f3712a..d0efa7cc 100644 --- a/app/modules/marketplace/templates/marketplace/store/onboarding.html +++ b/app/modules/marketplace/templates/marketplace/store/onboarding.html @@ -378,6 +378,6 @@ - + diff --git a/app/modules/messaging/templates/messaging/admin/email-logs.html b/app/modules/messaging/templates/messaging/admin/email-logs.html index 06dcb887..159590e8 100644 --- a/app/modules/messaging/templates/messaging/admin/email-logs.html +++ b/app/modules/messaging/templates/messaging/admin/email-logs.html @@ -347,5 +347,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/messaging/templates/messaging/admin/email-templates.html b/app/modules/messaging/templates/messaging/admin/email-templates.html index 726e8612..0d96ada6 100644 --- a/app/modules/messaging/templates/messaging/admin/email-templates.html +++ b/app/modules/messaging/templates/messaging/admin/email-templates.html @@ -364,5 +364,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/messaging/templates/messaging/admin/messages.html b/app/modules/messaging/templates/messaging/admin/messages.html index 311f7435..242e2da6 100644 --- a/app/modules/messaging/templates/messaging/admin/messages.html +++ b/app/modules/messaging/templates/messaging/admin/messages.html @@ -335,5 +335,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/messaging/templates/messaging/admin/notifications.html b/app/modules/messaging/templates/messaging/admin/notifications.html index 8f648822..9b195a5b 100644 --- a/app/modules/messaging/templates/messaging/admin/notifications.html +++ b/app/modules/messaging/templates/messaging/admin/notifications.html @@ -361,5 +361,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/messaging/templates/messaging/store/email-templates.html b/app/modules/messaging/templates/messaging/store/email-templates.html index 345f934a..a8102a06 100644 --- a/app/modules/messaging/templates/messaging/store/email-templates.html +++ b/app/modules/messaging/templates/messaging/store/email-templates.html @@ -329,5 +329,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/messaging/templates/messaging/store/messages.html b/app/modules/messaging/templates/messaging/store/messages.html index db1cc559..23b3baf4 100644 --- a/app/modules/messaging/templates/messaging/store/messages.html +++ b/app/modules/messaging/templates/messaging/store/messages.html @@ -278,5 +278,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/messaging/templates/messaging/store/notifications.html b/app/modules/messaging/templates/messaging/store/notifications.html index ed6aabe5..a9b1b4f9 100644 --- a/app/modules/messaging/templates/messaging/store/notifications.html +++ b/app/modules/messaging/templates/messaging/store/notifications.html @@ -229,5 +229,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/monitoring/templates/monitoring/admin/logs.html b/app/modules/monitoring/templates/monitoring/admin/logs.html index 55b2b0d9..7b836ac2 100644 --- a/app/modules/monitoring/templates/monitoring/admin/logs.html +++ b/app/modules/monitoring/templates/monitoring/admin/logs.html @@ -407,5 +407,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/monitoring/templates/monitoring/admin/platform-health.html b/app/modules/monitoring/templates/monitoring/admin/platform-health.html index 33038a11..1db43459 100644 --- a/app/modules/monitoring/templates/monitoring/admin/platform-health.html +++ b/app/modules/monitoring/templates/monitoring/admin/platform-health.html @@ -263,5 +263,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/orders/templates/orders/admin/orders.html b/app/modules/orders/templates/orders/admin/orders.html index 7816d87a..b5989b3b 100644 --- a/app/modules/orders/templates/orders/admin/orders.html +++ b/app/modules/orders/templates/orders/admin/orders.html @@ -580,5 +580,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/orders/templates/orders/store/order-detail.html b/app/modules/orders/templates/orders/store/order-detail.html index ae80a33d..c468503d 100644 --- a/app/modules/orders/templates/orders/store/order-detail.html +++ b/app/modules/orders/templates/orders/store/order-detail.html @@ -451,5 +451,5 @@ orderId: {{ order_id }} }; - + {% endblock %} diff --git a/app/modules/orders/templates/orders/store/orders.html b/app/modules/orders/templates/orders/store/orders.html index 515c4a8a..e569b2b8 100644 --- a/app/modules/orders/templates/orders/store/orders.html +++ b/app/modules/orders/templates/orders/store/orders.html @@ -330,5 +330,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/prospecting/templates/prospecting/admin/campaigns.html b/app/modules/prospecting/templates/prospecting/admin/campaigns.html index fe8be1b2..6e4215ec 100644 --- a/app/modules/prospecting/templates/prospecting/admin/campaigns.html +++ b/app/modules/prospecting/templates/prospecting/admin/campaigns.html @@ -156,5 +156,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/prospecting/templates/prospecting/admin/capture.html b/app/modules/prospecting/templates/prospecting/admin/capture.html index 03bd691b..13563072 100644 --- a/app/modules/prospecting/templates/prospecting/admin/capture.html +++ b/app/modules/prospecting/templates/prospecting/admin/capture.html @@ -148,5 +148,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/prospecting/templates/prospecting/admin/dashboard.html b/app/modules/prospecting/templates/prospecting/admin/dashboard.html index 4c197e62..6643405d 100644 --- a/app/modules/prospecting/templates/prospecting/admin/dashboard.html +++ b/app/modules/prospecting/templates/prospecting/admin/dashboard.html @@ -127,5 +127,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/prospecting/templates/prospecting/admin/leads.html b/app/modules/prospecting/templates/prospecting/admin/leads.html index a38b93b0..4ad6c4e6 100644 --- a/app/modules/prospecting/templates/prospecting/admin/leads.html +++ b/app/modules/prospecting/templates/prospecting/admin/leads.html @@ -139,5 +139,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/prospecting/templates/prospecting/admin/prospect-detail.html b/app/modules/prospecting/templates/prospecting/admin/prospect-detail.html index 4f9b5710..5e970672 100644 --- a/app/modules/prospecting/templates/prospecting/admin/prospect-detail.html +++ b/app/modules/prospecting/templates/prospecting/admin/prospect-detail.html @@ -429,5 +429,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/prospecting/templates/prospecting/admin/prospects.html b/app/modules/prospecting/templates/prospecting/admin/prospects.html index 2c57983f..7ec86be8 100644 --- a/app/modules/prospecting/templates/prospecting/admin/prospects.html +++ b/app/modules/prospecting/templates/prospecting/admin/prospects.html @@ -241,5 +241,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/prospecting/templates/prospecting/admin/scan-jobs.html b/app/modules/prospecting/templates/prospecting/admin/scan-jobs.html index a7d44e5b..bbdc6c8f 100644 --- a/app/modules/prospecting/templates/prospecting/admin/scan-jobs.html +++ b/app/modules/prospecting/templates/prospecting/admin/scan-jobs.html @@ -98,5 +98,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/tenancy/templates/tenancy/admin/admin-user-detail.html b/app/modules/tenancy/templates/tenancy/admin/admin-user-detail.html index b664db80..b57ce90e 100644 --- a/app/modules/tenancy/templates/tenancy/admin/admin-user-detail.html +++ b/app/modules/tenancy/templates/tenancy/admin/admin-user-detail.html @@ -271,5 +271,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/tenancy/templates/tenancy/admin/admin-user-edit.html b/app/modules/tenancy/templates/tenancy/admin/admin-user-edit.html index 905bd446..d3430caa 100644 --- a/app/modules/tenancy/templates/tenancy/admin/admin-user-edit.html +++ b/app/modules/tenancy/templates/tenancy/admin/admin-user-edit.html @@ -353,5 +353,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/tenancy/templates/tenancy/admin/admin-users.html b/app/modules/tenancy/templates/tenancy/admin/admin-users.html index 79aaa5f3..6215d7fc 100644 --- a/app/modules/tenancy/templates/tenancy/admin/admin-users.html +++ b/app/modules/tenancy/templates/tenancy/admin/admin-users.html @@ -283,5 +283,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/tenancy/templates/tenancy/admin/login.html b/app/modules/tenancy/templates/tenancy/admin/login.html index bfbb8e66..45d5b19a 100644 --- a/app/modules/tenancy/templates/tenancy/admin/login.html +++ b/app/modules/tenancy/templates/tenancy/admin/login.html @@ -210,6 +210,6 @@ - + diff --git a/app/modules/tenancy/templates/tenancy/admin/merchant-detail.html b/app/modules/tenancy/templates/tenancy/admin/merchant-detail.html index 9aaaafef..4ed68aff 100644 --- a/app/modules/tenancy/templates/tenancy/admin/merchant-detail.html +++ b/app/modules/tenancy/templates/tenancy/admin/merchant-detail.html @@ -483,5 +483,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/tenancy/templates/tenancy/admin/merchant-edit.html b/app/modules/tenancy/templates/tenancy/admin/merchant-edit.html index 79247305..3144626b 100644 --- a/app/modules/tenancy/templates/tenancy/admin/merchant-edit.html +++ b/app/modules/tenancy/templates/tenancy/admin/merchant-edit.html @@ -490,5 +490,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/tenancy/templates/tenancy/admin/merchant-user-detail.html b/app/modules/tenancy/templates/tenancy/admin/merchant-user-detail.html index 0edeee14..9c1221a9 100644 --- a/app/modules/tenancy/templates/tenancy/admin/merchant-user-detail.html +++ b/app/modules/tenancy/templates/tenancy/admin/merchant-user-detail.html @@ -281,5 +281,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/tenancy/templates/tenancy/admin/merchant-user-edit.html b/app/modules/tenancy/templates/tenancy/admin/merchant-user-edit.html index ca635ac5..77df0311 100644 --- a/app/modules/tenancy/templates/tenancy/admin/merchant-user-edit.html +++ b/app/modules/tenancy/templates/tenancy/admin/merchant-user-edit.html @@ -247,5 +247,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/tenancy/templates/tenancy/admin/merchant-users.html b/app/modules/tenancy/templates/tenancy/admin/merchant-users.html index ce1c0ac7..f2aee269 100644 --- a/app/modules/tenancy/templates/tenancy/admin/merchant-users.html +++ b/app/modules/tenancy/templates/tenancy/admin/merchant-users.html @@ -250,5 +250,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/tenancy/templates/tenancy/admin/merchants.html b/app/modules/tenancy/templates/tenancy/admin/merchants.html index 918d3626..b6f27594 100644 --- a/app/modules/tenancy/templates/tenancy/admin/merchants.html +++ b/app/modules/tenancy/templates/tenancy/admin/merchants.html @@ -257,5 +257,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/tenancy/templates/tenancy/admin/platform-create.html b/app/modules/tenancy/templates/tenancy/admin/platform-create.html index 8138848b..daa6a05c 100644 --- a/app/modules/tenancy/templates/tenancy/admin/platform-create.html +++ b/app/modules/tenancy/templates/tenancy/admin/platform-create.html @@ -233,5 +233,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/tenancy/templates/tenancy/admin/platform-detail.html b/app/modules/tenancy/templates/tenancy/admin/platform-detail.html index 2ae98154..7aff07d2 100644 --- a/app/modules/tenancy/templates/tenancy/admin/platform-detail.html +++ b/app/modules/tenancy/templates/tenancy/admin/platform-detail.html @@ -221,5 +221,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/tenancy/templates/tenancy/admin/platform-edit.html b/app/modules/tenancy/templates/tenancy/admin/platform-edit.html index 72692742..bc8a1f77 100644 --- a/app/modules/tenancy/templates/tenancy/admin/platform-edit.html +++ b/app/modules/tenancy/templates/tenancy/admin/platform-edit.html @@ -342,5 +342,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/tenancy/templates/tenancy/admin/platform-menu-config.html b/app/modules/tenancy/templates/tenancy/admin/platform-menu-config.html index f20df9e6..d9bfbb14 100644 --- a/app/modules/tenancy/templates/tenancy/admin/platform-menu-config.html +++ b/app/modules/tenancy/templates/tenancy/admin/platform-menu-config.html @@ -223,5 +223,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/tenancy/templates/tenancy/admin/platform-modules.html b/app/modules/tenancy/templates/tenancy/admin/platform-modules.html index f63f2766..7280b03d 100644 --- a/app/modules/tenancy/templates/tenancy/admin/platform-modules.html +++ b/app/modules/tenancy/templates/tenancy/admin/platform-modules.html @@ -303,5 +303,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/tenancy/templates/tenancy/admin/platforms.html b/app/modules/tenancy/templates/tenancy/admin/platforms.html index d83f29e8..94ab3f5f 100644 --- a/app/modules/tenancy/templates/tenancy/admin/platforms.html +++ b/app/modules/tenancy/templates/tenancy/admin/platforms.html @@ -123,5 +123,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/tenancy/templates/tenancy/admin/select-platform.html b/app/modules/tenancy/templates/tenancy/admin/select-platform.html index 881d44a7..d38aa9dd 100644 --- a/app/modules/tenancy/templates/tenancy/admin/select-platform.html +++ b/app/modules/tenancy/templates/tenancy/admin/select-platform.html @@ -177,7 +177,7 @@ - + diff --git a/app/modules/tenancy/templates/tenancy/admin/store-create.html b/app/modules/tenancy/templates/tenancy/admin/store-create.html index b459196a..f1fc9a62 100644 --- a/app/modules/tenancy/templates/tenancy/admin/store-create.html +++ b/app/modules/tenancy/templates/tenancy/admin/store-create.html @@ -279,5 +279,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/tenancy/templates/tenancy/admin/store-detail.html b/app/modules/tenancy/templates/tenancy/admin/store-detail.html index bf4a2af2..cd89d636 100644 --- a/app/modules/tenancy/templates/tenancy/admin/store-detail.html +++ b/app/modules/tenancy/templates/tenancy/admin/store-detail.html @@ -661,5 +661,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/tenancy/templates/tenancy/admin/store-edit.html b/app/modules/tenancy/templates/tenancy/admin/store-edit.html index 1653b281..1119c830 100644 --- a/app/modules/tenancy/templates/tenancy/admin/store-edit.html +++ b/app/modules/tenancy/templates/tenancy/admin/store-edit.html @@ -477,5 +477,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/tenancy/templates/tenancy/admin/store-roles.html b/app/modules/tenancy/templates/tenancy/admin/store-roles.html index e4a080ab..6260f7cd 100644 --- a/app/modules/tenancy/templates/tenancy/admin/store-roles.html +++ b/app/modules/tenancy/templates/tenancy/admin/store-roles.html @@ -267,5 +267,5 @@ - + {% endblock %} diff --git a/app/modules/tenancy/templates/tenancy/admin/stores.html b/app/modules/tenancy/templates/tenancy/admin/stores.html index 919e5aaa..3fb2341a 100644 --- a/app/modules/tenancy/templates/tenancy/admin/stores.html +++ b/app/modules/tenancy/templates/tenancy/admin/stores.html @@ -248,5 +248,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/tenancy/templates/tenancy/admin/user-create.html b/app/modules/tenancy/templates/tenancy/admin/user-create.html index 5bcefb1f..de7be40a 100644 --- a/app/modules/tenancy/templates/tenancy/admin/user-create.html +++ b/app/modules/tenancy/templates/tenancy/admin/user-create.html @@ -191,5 +191,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/tenancy/templates/tenancy/merchant/login.html b/app/modules/tenancy/templates/tenancy/merchant/login.html index 4dd9ad67..7e3e974c 100644 --- a/app/modules/tenancy/templates/tenancy/merchant/login.html +++ b/app/modules/tenancy/templates/tenancy/merchant/login.html @@ -211,6 +211,6 @@ - + diff --git a/app/modules/tenancy/templates/tenancy/merchant/team.html b/app/modules/tenancy/templates/tenancy/merchant/team.html index 39ada7b5..8cff160e 100644 --- a/app/modules/tenancy/templates/tenancy/merchant/team.html +++ b/app/modules/tenancy/templates/tenancy/merchant/team.html @@ -534,5 +534,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/tenancy/templates/tenancy/store/login.html b/app/modules/tenancy/templates/tenancy/store/login.html index 692a745c..8549e14e 100644 --- a/app/modules/tenancy/templates/tenancy/store/login.html +++ b/app/modules/tenancy/templates/tenancy/store/login.html @@ -246,7 +246,7 @@ - + diff --git a/app/modules/tenancy/templates/tenancy/store/profile.html b/app/modules/tenancy/templates/tenancy/store/profile.html index 502c645f..00a1b927 100644 --- a/app/modules/tenancy/templates/tenancy/store/profile.html +++ b/app/modules/tenancy/templates/tenancy/store/profile.html @@ -202,5 +202,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/tenancy/templates/tenancy/store/roles.html b/app/modules/tenancy/templates/tenancy/store/roles.html index 31611418..d6fdd339 100644 --- a/app/modules/tenancy/templates/tenancy/store/roles.html +++ b/app/modules/tenancy/templates/tenancy/store/roles.html @@ -163,5 +163,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/tenancy/templates/tenancy/store/settings.html b/app/modules/tenancy/templates/tenancy/store/settings.html index 9f1bde84..229bf7a9 100644 --- a/app/modules/tenancy/templates/tenancy/store/settings.html +++ b/app/modules/tenancy/templates/tenancy/store/settings.html @@ -1328,5 +1328,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/modules/tenancy/templates/tenancy/store/team.html b/app/modules/tenancy/templates/tenancy/store/team.html index 73b5c2b4..1114e9f8 100644 --- a/app/modules/tenancy/templates/tenancy/store/team.html +++ b/app/modules/tenancy/templates/tenancy/store/team.html @@ -299,5 +299,5 @@ {% endblock %} {% block extra_scripts %} - + {% endblock %} diff --git a/app/templates/admin/base.html b/app/templates/admin/base.html index 4bf3dba9..142baf43 100644 --- a/app/templates/admin/base.html +++ b/app/templates/admin/base.html @@ -110,7 +110,7 @@ - + @@ -145,10 +145,10 @@ - + - + diff --git a/app/templates/merchant/base.html b/app/templates/merchant/base.html index d0f83593..7d7abfec 100644 --- a/app/templates/merchant/base.html +++ b/app/templates/merchant/base.html @@ -60,7 +60,7 @@ - + diff --git a/app/templates/store/base.html b/app/templates/store/base.html index cc436ac9..fdc3d007 100644 --- a/app/templates/store/base.html +++ b/app/templates/store/base.html @@ -75,7 +75,7 @@ - + @@ -95,10 +95,10 @@ - + - + {% block extra_scripts %}{% endblock %} diff --git a/app/templates/storefront/base.html b/app/templates/storefront/base.html index 26786518..648ab260 100644 --- a/app/templates/storefront/base.html +++ b/app/templates/storefront/base.html @@ -381,7 +381,7 @@ {# 4. Base Shop Layout (Alpine.js component - must load before Alpine) #} - + {# 5. Utilities #} diff --git a/app/templates_config.py b/app/templates_config.py index 255c047c..f6b3cb43 100644 --- a/app/templates_config.py +++ b/app/templates_config.py @@ -103,3 +103,28 @@ templates.env.globals["config"] = { "version": _settings.version, **get_build_info(), } + + +def _asset_version() -> str: + """Return a stable version string for cache-busting static assets. + + Uses the deploy commit SHA from build_info (written by scripts/deploy.sh, + falls back to git HEAD locally). Same SHA across one deploy → browsers + cache; new deploy flips the SHA → browsers refetch. + """ + info = get_build_info() + return info.get("commit") or "dev" + + +def static_v(request, name: str, *, path: str) -> str: + """Versioned `url_for` for static assets. + + Usage in templates: + + + Emits `?v=` so deploys break browser caches automatically. + """ + return f"{request.url_for(name, path=path)}?v={_asset_version()}" + + +templates.env.globals["static_v"] = static_v diff --git a/docs/proposals/static-asset-cache-busting.md b/docs/proposals/static-asset-cache-busting.md index 825746ee..74a05505 100644 --- a/docs/proposals/static-asset-cache-busting.md +++ b/docs/proposals/static-asset-cache-busting.md @@ -1,9 +1,18 @@ # Static Asset Cache-Busting Plan **Date:** 2026-05-17 -**Status:** Proposed +**Implemented:** 2026-05-18 +**Status:** Done **Motivation:** Users must hard-reload after every release to pick up new JS/CSS. We need versioned asset URLs that flip automatically on deploy so browsers refetch without manual intervention. +## Implementation summary + +- `static_v(request, name, *, path)` Jinja global in `app/templates_config.py` — appends `?v=` from `build_info`. +- `CachedStaticFiles` in `app/core/static_files.py` — subclass of `fastapi.staticfiles.StaticFiles` that sets `Cache-Control: public, max-age=31536000, immutable` in production and `no-cache` in development. Wired into the JS/CSS-bearing mounts in `main.py` (module statics + `/static`). Uploads and module locales keep default headers. +- Codemod migrated 143 `url_for('*_static', path='*.js'|'*.css')` callsites across 123 templates to `static_v(...)`. +- Architecture rule `FE-024` (warning) flags raw `url_for(...)` on JS/CSS so the pattern can't drift back. (Renamed from the proposed `FE-008`, which was already taken by the `number_stepper` rule.) +- Verified: `mkdocs build --strict` clean, `validate-architecture` reports no new findings, smoke test renders `?v=`. + --- ## Existing Infrastructure (Already in Place) diff --git a/main.py b/main.py index 51e5d76b..e2aa3170 100644 --- a/main.py +++ b/main.py @@ -40,6 +40,7 @@ from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware # ConnectionRefusedError at runtime. The isort: split below stops ruff from # alphabetically reordering this back behind api_router. from app.core import celery_config # noqa: F401 # side-effect: set_default() +from app.core.static_files import CachedStaticFiles # isort: split from app.api.main import api_router @@ -235,12 +236,12 @@ if MODULES_DIR.exists(): module_static = module_dir / "static" if module_static.exists(): mount_path = f"/static/modules/{module_name}" - app.mount(mount_path, StaticFiles(directory=str(module_static)), name=f"{module_name}_static") + app.mount(mount_path, CachedStaticFiles(directory=str(module_static)), name=f"{module_name}_static") logger.info(f"Mounted module static files: {mount_path} -> {module_static}") # Mount main static directory AFTER module statics if STATIC_DIR.exists(): - app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static") + app.mount("/static", CachedStaticFiles(directory=str(STATIC_DIR)), name="static") logger.info(f"Mounted static files from: {STATIC_DIR}") else: logger.warning(f"Static directory not found at {STATIC_DIR}") diff --git a/scripts/validate/validate_architecture.py b/scripts/validate/validate_architecture.py index 927bd697..ac670560 100755 --- a/scripts/validate/validate_architecture.py +++ b/scripts/validate/validate_architecture.py @@ -867,6 +867,10 @@ class ArchitectureValidator: if not is_components_page and not is_macro: self._check_number_stepper_macro_usage(file_path, content, lines) + # FE-024: Check for raw url_for(...) on .js/.css (should use static_v) + if not is_macro: + self._check_static_v_usage(file_path, content, lines) + # TPL-004: Check x-text usage for dynamic content self._check_xtext_usage(file_path, content, lines) @@ -1793,6 +1797,40 @@ class ArchitectureValidator: ) return # Only report once per file + _STATIC_V_PATTERN = re.compile( + r"""url_for\(\s*['"]\w+_static['"]\s*,\s*path=['"][^'"]+\.(?:js|css)['"]\s*\)""", + re.IGNORECASE, + ) + + def _check_static_v_usage( + self, file_path: Path, content: str, lines: list[str] + ): + """FE-024: Flag raw url_for() on .js/.css URLs — must use static_v() instead. + + The static_v() Jinja helper appends ?v= from build_info so + browsers refetch JS/CSS automatically after each deploy. + + Skip lines with `noqa: FE-024` or `noqa: FE024`. + """ + for i, line in enumerate(lines, 1): + if not self._STATIC_V_PATTERN.search(line): + continue + if "noqa" in line.lower() and ( + "fe-024" in line.lower() or "fe024" in line.lower() + ): + continue + stripped = line.strip() + self._add_violation( + rule_id="FE-024", + rule_name="Use static_v() for JS/CSS, not raw url_for()", + severity=Severity.WARNING, + file_path=file_path, + line_number=i, + message="Raw url_for() on JS/CSS — browsers won't refetch after deploy", + context=stripped[:80], + suggestion="Replace url_for(...) with static_v(request, ...) so the URL carries ?v=", + ) + def _validate_api_endpoints(self, target_path: Path): """Validate API endpoint rules (API-001, API-002, API-003, API-004)""" print("📡 Validating API endpoints...") @@ -3508,6 +3546,10 @@ class ArchitectureValidator: if not is_base_or_partial and not is_macro and not is_components_page: self._check_number_stepper_macro_usage(file_path, content, lines) + # FE-024: Check for raw url_for(...) on .js/.css (should use static_v) + if not is_base_or_partial and not is_macro: + self._check_static_v_usage(file_path, content, lines) + # TPL-008: Check for call table_header() pattern self._check_table_header_call_pattern(file_path, content, lines)