High-performance asset delivery controller for CSS and JavaScript resources.
Provides optimized asset serving with intelligent compression selection, aggressive caching strategies, and multi-site support. Designed for production-grade performance with minimal latency and maximum throughput.
Key Features
- Multi-site Asset Management: Site-specific asset delivery and isolation
- Intelligent Compression: Automatic Brotli/Gzip selection based on client capabilities
- Aggressive Caching: Long-term browser caching with immutable cache directives
- CSRF Protection: Automatic CSRF bypass for JavaScript assets
- Graceful Fallbacks: Custom error handling with informative fallback content
- Optimal Headers: Production-optimized HTTP headers for performance
Asset Types Supported
- CSS Assets: Stylesheets with appropriate content-type headers
- JavaScript Assets: Scripts with CSRF protection disabled
Compression Strategy
The controller implements intelligent compression selection:
- Brotli: Preferred compression for modern browsers (highest efficiency)
- Gzip: Fallback compression for broader browser support
- Uncompressed: Raw assets for clients without compression support
Who gets a bundle
A bundle is answered only at its current hash, and only to whom may see its site, read from ETS:
- on the address of its own site: anyone while the site is up; while it is offline, its stylesheet to anyone, since the offline page its visitors get links it, and its script to its admins and the masters;
- on any other address: a master, as the page builder's canvas asks for it, and whoever holds
a preview link of that site (
?preview=<token>), by the same rule as its own address; - the content editor's stylesheet for a site (
MishkaCmsCore.Runtime.ContentEditorStyles): a master, and an admin of that site; __shared_base__, the base every site's bundle starts with: anyone.
A lazy JS hook's module (MishkaCmsCore.Runtime.Compilers.JavaScriptCompiler.lazy_type/1) is
answered at /assets/js/<site>/<hash>.js too, by its own hash, to whom may see that site's script,
with the same headers — and only while the site's current bundle loads it.
Anything else — a stale or unknown hash, another site's bundle, a site that is down — is a 404
that no cache keeps. A plugin on web:request:asset_unavailable hears it first, with
%{site: site_name, type: "css" | "js", reason: reason} as its meta, and may answer instead by
halting the conn.
Caching Strategy
The URL carries the bundle's hash, so a bundle never changes at a given URL, and no answer sends the session cookie:
- Site up:
public, max-age=31536000, immutable - Site down, to its admins or a master, or its stylesheet while it is offline:
private, max-age=31536000, immutable
Usage Examples
CSS Asset Delivery
GET /assets/css/mysite
Accept-Encoding: br, gzip, deflate
Response:
Content-Type: text/css
Content-Encoding: br
Cache-Control: public, max-age=31536000, immutableJavaScript Asset Delivery
GET /assets/js/mysite
Accept-Encoding: gzip, deflate
Response:
Content-Type: text/javascript
Content-Encoding: gzip
Cache-Control: public, max-age=31536000, immutableRoute Integration
Registered via MishkaCmsCoreWeb.Routes under the :public extension route type:
def routes(:public) do
[
{:get, "/assets/css/:site/:hash", MishkaCmsCoreWeb.AssetsController, :css},
{:get, "/assets/js/:site/:hash", MishkaCmsCoreWeb.AssetsController, :js}
]
endAsset Storage Integration
Integrates with MishkaCmsCore.Runtime.States.AssetsState for:
- Site-specific asset retrieval
- Compressed asset variants (brotli_css, gzip_css, brotli_js, gzip_js)
- Raw asset fallbacks
- High-performance ETS-based lookups
Error Handling
A missing bundle, or a hash that is not the current one, is a 404 with cache-control: no-store.
A missing compressed variant falls back to the uncompressed bundle.
Performance Optimizations
- Zero Database Queries: Direct ETS state access
- Minimal Processing: Streamlined request handling
- Header Optimization: Production-tuned response headers
- Compression Selection: Client-optimized encoding selection
Security Considerations
- CSRF Protection: Automatically disabled for JavaScript assets
- Site Isolation: Assets scoped to requesting site
- Content-Type Validation: Proper MIME types for asset security
- Cache Security: Immutable caching prevents cache poisoning
CDN Integration
Optimized for CDN deployment:
- Long-term caching headers for edge caching
- Immutable directive for aggressive caching
- Optional CORS headers for cross-domain assets (commented)
Production Considerations
- Monitor asset hit rates and cache performance
- Implement asset versioning for cache invalidation
- Consider CDN integration for global distribution
- Monitor compression ratios and client capabilities
- Implement asset preloading strategies
- Consider HTTP/2 server push for critical assets
Multi-site Architecture
Assets are isolated per site:
- Site parameter determines asset scope
- Independent asset compilation per site
- Site-specific fallback messages
- Tenant-isolated asset storage
Extension Points
The controller includes commented extension points for:
- CORS header configuration for external assets
- Additional asset types (fonts, images, etc.)
- Custom compression algorithms
- Asset fingerprinting and versioning