routed_storage 0.2.2
routed_storage: ^0.2.2 copied to clipboard
Routed adapter for server_storage.
routed_storage #
Routed adapter for server_storage — StorageManager, local disks, and declarative static mounts.
Wraps server_storage for routed EngineContext.storageManager / storageDisk and storageMiddleware / RoutedStorageProvider.
Install #
dependencies:
routed: ^0.5.0
routed_core: ^0.5.0
routed_storage: ^0.2.0
server_storage: ^0.1.3
Usage #
import 'package:file/memory.dart';
import 'package:routed_core/routed_core.dart';
import 'package:routed_storage/routed_storage.dart';
import 'package:server_storage/server_storage.dart';
void main() async {
final fs = MemoryFileSystem();
final manager = StorageManager(defaultFileSystem: fs)
..registerDisk(
'local',
LocalStorageDisk(root: '/tmp/storage', fileSystem: fs),
)
..setDefault('local');
final engine = await Engine.create(
providers: [
...Engine.defaultProviders,
RoutedStorageProvider(manager: manager),
],
);
engine.use(storageMiddleware(manager));
engine.get('/files/:path', (ctx) {
final disk = ctx.storageDisk();
return ctx.json({'disk': disk.resolve(ctx.param('path')!)});
});
await engine.serve(port: 8080);
}
RoutedStorageProvider binds the manager for application code. Static mounts
are supplied as typed configuration:
final engine = await Engine.create(
providers: [
RoutedStorageProvider(manager: manager),
RoutedStaticProvider(
StaticConfig(
enabled: true,
mounts: [StaticMountConfig(route: '/assets', disk: 'local')],
),
),
],
);
The same mount works for a storage-only filesystem registered with
registerFilesystem(). For imperative composition, mount the asynchronous
filesystem directly:
engine.staticStorage(
'/assets',
manager.storage('public-assets'),
rootPath: 'public',
);
Storage-backed static mounts require public object visibility and fail
closed when visibility is private, unknown, or cannot be read. Private object
stores such as Cloudflare R2 should use a signed mount:
final signer = StorageSignedUrlSigner(environment['STORAGE_SIGNING_KEY']!);
engine.signedStorage(
'/downloads',
manager.storage('r2'),
signer: signer,
rootPath: 'private',
);
// Run authentication and object-level authorization before issuing this URL.
final url = signer.sign(
Uri.parse('https://app.example.com/downloads/report.pdf'),
expiresAt: DateTime.now().add(const Duration(minutes: 5)),
);
The secret must contain at least 32 UTF-8 bytes and belong in the host's
secret manager. Signed downloads return Cache-Control: private, no-store;
unsigned, expired, and tampered URLs are rejected before any storage read.
S3-compatible disks come from server_storage and are re-exported here:
import 'dart:io';
import 'package:routed_storage/routed_storage.dart';
final environment = Platform.environment;
final s3 = S3StorageDisk(
endpoint: 'https://<account-id>.r2.cloudflarestorage.com',
accessKey: environment['S3_ACCESS_KEY']!,
secretKey: environment['S3_SECRET_KEY']!,
bucket: 'assets',
region: 'auto',
pathStyle: true,
);
final manager = StorageManager()
..registerDisk('assets', s3)
..setDefault('assets');
final engine = await Engine.create(
providers: [RoutedStorageProvider(manager: manager)],
);
final signer = StorageSignedUrlSigner(
environment['STORAGE_SIGNING_KEY']!,
);
engine.signedStorage(
'/files',
manager.storage('assets'),
signer: signer,
);
Authenticate and authorize the requested object before issuing a signed URL; the mount itself rejects unsigned, expired, and path-tampered reads.
SFTP uses the same manager and request API:
final sftp = SftpStorageDisk(
config: SftpConfig(
host: environment['SFTP_HOST']!,
username: environment['SFTP_USERNAME']!,
password: environment['SFTP_PASSWORD'],
root: '/srv/uploads',
),
);
manager.registerDisk('archive', sftp);
await manager.storage('archive').put('reports/daily.json', reportJson);
Call await sftp.close() during application shutdown.
With the batteries-included routed package, call
registerRoutedProviders() and include the typed providers you need in the
engine's provider list. An explicitly supplied StorageManager remains
authoritative; otherwise pass StorageConfig as configuration to define
local disks with LocalStorageDiskConfig. Configured providers are immutable
for the lifetime of an engine.
The package includes a runnable storage example under its example directory.
Testing #
dart test packages/routed_storage
dart analyze --fatal-infos packages/routed_storage/lib