import Middleware from '../Middleware.ts'
import { Request } from '../Request.ts'
import { Injectable } from '../../../../di/src/decorator/Injection.ts'
import { file , http } from '../response/helpers.ts'
import { HTTPStatus } from '../../const/http.ts'
import { Logging } from '../../service/logging/Logging.ts'
import { UniversalPath } from "../../support/UniversalPath.ts" ;
/ * *
* Daton - provided middleware that serves files from a static directory .
* @extends Middleware
* /
@Injectable ( )
export default class StaticServer extends Middleware {
constructor (
protected readonly logger : Logging ,
) { super ( ) }
/ * *
* Handle an incoming request . Get the path to the file from the route params , and
* resolve it using the asset_dir argument . If the file exists , serve it .
* @param { Request } request
* @param { string } asset_dir
* /
public async handleRequest ( request : Request , asset_dir? : string ) {
if ( ! asset_dir ) {
throw new Error ( ` This static server is mis-configured. You must provide, as an argument in the route definition, the relative path to the base directory of the static files to be served. ` )
}
const params = request . route . params
const rel_file = params . $1
if ( ! rel_file || ! rel_file . trim ( ) || rel_file . trim ( ) . startsWith ( '/' ) || rel_file . trim ( ) . startsWith ( '.' ) ) {
this . logger . info ( ` Blocked attempt to access invalid static file path: " ${ rel_file } " ` )
return http ( HTTPStatus . NOT_FOUND )
}
const abs_file = this . resolve ( asset_dir , rel_file )
if ( ! ( await this . fileExists ( abs_file ) ) ) {
this . logger . debug ( ` File does not exist: ${ abs_file } ` )
return http ( HTTPStatus . NOT_FOUND )
}
return file ( abs_file )
}
/ * *
* Given the asset base dir and a relative path , resolve the fully - qualified
* path to the file .
* @param { string } asset_dir
* @param { string } rel_file
* @return string
* /
protected resolve ( asset_dir : string , rel_file : string ) : UniversalPath {
return this . app . app_path ( asset_dir , rel_file )
}
/ * *
* Resolves true if the given path exists , and is a file .
* @param { string } path
* @return Promise < boolean >
* /
protected async fileExists ( path : UniversalPath ) : Promise < boolean > {
try {
const stat = await Deno . lstat ( path . to_local )
return stat && stat . isFile
} catch ( e ) {
if ( e && e instanceof Deno . errors . NotFound ) {
return false
} else {
throw e
}
}
}
}