stately-files
File upload, versioning, and download management for stately applications.
Overview
This crate provides HTTP endpoints and path types for managing files with automatic UUID-based versioning. It's designed to be mounted as an axum router and pairs with the @statelyjs/files frontend plugin.
Features
- File Uploads - Multipart form and JSON-based uploads with automatic versioning
- File Downloads - Streaming downloads with content-type detection
- Path Types - Configuration property types for referencing files in entities
- Version Resolution - Automatic latest-version resolution using UUID v7
Install
Add to your Cargo.toml:
[]
= { = "../stately-files" }
Quick Start
use Router;
use ;
async
Storage Structure
Uploaded files are stored with automatic versioning:
{data_dir}/
└── uploads/
└── {filename}/
└── __versions__/
├── 01234567-89ab-cdef-0123-456789abcdef
├── 01234567-89ab-cdef-0123-456789abcdf0
└── 01234567-89ab-cdef-0123-456789abcdf1 (latest)
UUID v7 identifiers are time-sortable, so the latest version is always the lexicographically largest UUID in the directory.
API Endpoints
| Method | Path | Description |
|---|---|---|
POST |
/upload |
Upload via multipart form |
POST |
/save |
Save content from JSON body |
GET |
/list |
List files and directories |
GET |
/file/cache/{path} |
Download from cache directory |
GET |
/file/data/{path} |
Download from data directory |
GET |
/file/upload/{path} |
Download uploaded file (with version resolution) |
Upload
Response:
List Files
Response:
Download
# Latest version
# Specific version
Path Types
Use these types in your entity configurations to reference files:
VersionedPath
Logical filename that resolves to the latest version:
use VersionedPath;
let path = new;
let resolved = path.resolve?;
// -> /app/data/uploads/config.json/__versions__/{latest-uuid}
RelativePath
Path relative to cache, data, or uploads directory:
use RelativePath;
// Reference a cached file
let cache_path = Cache;
// Reference a data file
let data_path = Data;
// Reference an uploaded file (with version resolution)
let upload_path = Upload;
UserDefinedPath
Union of managed paths or external (user-provided) paths:
use UserDefinedPath;
// Application-managed path
let managed = Managed;
// User-provided external path
let external = External;
Configuration
Default Directories
Default paths are relative to the current working directory:
- Cache:
.cache - Data:
.data
Custom Directories
Provide custom directories via FileState:
use ;
let state = new;
Or initialize globally before any handlers run:
use Dirs;
init.expect;
OpenAPI
Generate the OpenAPI spec for frontend codegen:
This outputs openapi.json which is consumed by @statelyjs/codegen to generate TypeScript types.
Module Structure
stately-files/
├── error.rs # Error types with axum IntoResponse
├── handlers.rs # HTTP handlers (upload, save, list, download)
├── openapi.rs # OpenAPI documentation
├── path.rs # Path types (VersionedPath, RelativePath, UserDefinedPath)
├── request.rs # Request DTOs
├── response.rs # Response DTOs
├── router.rs # Axum router factory
├── settings.rs # Directory configuration
├── state.rs # FileState extractor
└── utils.rs # Path sanitization, helpers
License
Licensed under the Apache License, Version 2.0. See LICENSE for details.