# `MishkaCmsCoreWeb.AssetsController`
[🔗](https://github.com/mishka-group/mishka_cms_core/blob/v0.0.1/lib/mishka_cms_core_web/controllers/assets_controller.ex#L5)

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

# `css`

# `js`

---

*Consult [api-reference.md](api-reference.md) for complete listing*
