Create Files unit to manage filesystem implementations defined in config

This commit is contained in:
Garrett Mills 2021-03-29 11:12:16 -05:00
parent 6f73df3465
commit e772d12f20
Signed by: garrettmills
GPG Key ID: D2BF5FBA8298F246
3 changed files with 159 additions and 0 deletions

View File

@ -55,6 +55,7 @@ export * from './service/CanonicalStatic'
export * from './service/FakeCanonical' export * from './service/FakeCanonical'
export * from './service/Config' export * from './service/Config'
export * from './service/Controllers' export * from './service/Controllers'
export * from './service/Files'
export * from './service/HTTPServer' export * from './service/HTTPServer'
export * from './service/Routing' export * from './service/Routing'
export * from './service/Middlewares' export * from './service/Middlewares'

View File

@ -28,6 +28,22 @@ export function env(key: string, defaultValue?: any): any {
return Application.getApplication().env(key, defaultValue) return Application.getApplication().env(key, defaultValue)
} }
/**
* Helper function for fetching a universal path relative to the root of the application.
* @param parts
*/
export function basePath(...parts: PathLike[]): UniversalPath {
return Application.getApplication().path(...parts)
}
/**
* Helper function for fetching a universal path relative to the `app/` directory.
* @param parts
*/
export function appPath(...parts: PathLike[]): UniversalPath {
return Application.getApplication().appPath(...parts)
}
/** /**
* The main application container. * The main application container.
*/ */

142
src/service/Files.ts Normal file
View File

@ -0,0 +1,142 @@
import {Unit} from "../lifecycle/Unit"
import {Inject, Singleton} from "@extollo/di"
import {Config} from "./Config"
import {Logging} from "./Logging"
import {Filesystem, ErrorWithContext} from "@extollo/util"
/**
* Error thrown when a function is called on a filesystem that does not exists in code.
*/
export class FilesystemDoesNotExist extends ErrorWithContext {
constructor(
message: string = 'The specified filesystem does not exist.',
context: {[key: string]: any} = {}
) {
super(message, context)
}
}
/**
* Unit service that loads and creates Filesystem drivers from config. The filesystems
* will automatically be opened when the app starts, and closed when it stops.
*
* @example
* Filesystems can be defined in the `server.filesystems` config. For example:
*
* ```typescript
* import {basePath} from "@extollo/lib"
* import {LocalFilesystem, LocalFilesystemConfig} from "@extollo/util"
*
* export default {
* // ... other configs ...
* filesystems: {
* default: {
* driver: LocalFilesystem,
* config: {
* baseDir: basePath('..', 'uploads').toLocal,
* } as LocalFilesystemConfig,
* },
* },
* }
* ```
*
* The `config` key should be an instance of the config interface for the driver
* in question.
*
* @example
* Filesystems can then be accessed from the Files service:
*
* ```typescript
* if ( files.hasFilesystem('default') ) {
* const filesystem = files.getFilesystem('default')
* // ... do something with the filesystem ...
* }
* ```
*
*/
@Singleton()
export class Files extends Unit {
protected filesystems: {[key: string]: Filesystem} = {}
protected defaultFilesystem?: Filesystem
@Inject()
protected readonly config!: Config
@Inject()
protected readonly logging!: Logging
async up() {
const config = this.config.get('server.filesystems', {})
const promises = []
for ( const key in config ) {
if ( !config.hasOwnProperty(key) ) continue;
if ( config[key]?.driver?.prototype instanceof Filesystem ) {
this.logging.verbose(`Registering filesystem '${key}' with driver ${config[key].driver.name}...`)
const inst = <Filesystem> this.make(config[key].driver, config[key].config || {})
promises.push(inst.open())
if ( this.filesystems[key] ) {
this.logging.warn(`Overwriting filesystem with duplicate name: ${key}`)
}
this.filesystems[key] = inst
if ( config[key]?.isDefault ) {
this.defaultFilesystem = inst
}
}
}
await Promise.all(promises)
}
async down() {
await Promise.all(Object.values(this.filesystems).map(fs => fs.close()))
}
/**
* Returns true if a filesystem with the given name exists.
* @param key
*/
hasFilesystem(key?: string) {
if ( !key ) {
return !!this.defaultFilesystem
}
return !!this.filesystems[key]
}
/**
* Given the name of a filesystem registered in the system, get that filesystem.
* @param key
*/
getFilesystem(key?: string): Filesystem {
if ( !key ) {
if ( !this.defaultFilesystem ) {
throw new FilesystemDoesNotExist()
}
return this.defaultFilesystem
}
if ( !this.hasFilesystem(key) ) {
throw new FilesystemDoesNotExist()
}
return this.filesystems[key]
}
/**
* Register the given filesystem with this service by name.
* @param key
* @param fs
*/
registerFilesystem(key: string, fs: Filesystem) {
if ( this.hasFilesystem(key) ) {
this.logging.warn(`Overwriting filesystem with duplicate name: ${key}`)
}
this.filesystems[key] = fs
}
}