MishkaCmsCoreWeb.AssetsController (Mishka CMS Core v0.0.1)

Copy Markdown View Source

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:

  1. Brotli: Preferred compression for modern browsers (highest efficiency)
  2. Gzip: Fallback compression for broader browser support
  3. 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, immutable

JavaScript Asset Delivery

GET /assets/js/mysite
Accept-Encoding: gzip, deflate

Response:
Content-Type: text/javascript
Content-Encoding: gzip
Cache-Control: public, max-age=31536000, immutable

Route 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}
  ]
end

Asset 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

Summary

Functions

css(conn, params)

js(conn, params)