You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lib/src/util/support/path/HTTPFilesystem.ts

87 lines
2.8 KiB

import * as fs from 'fs'
import * as mime from 'mime-types'
import * as path from 'path'
import {FilesystemOperationNotSupported, ReadOnlyFilesystem} from './ReadOnlyFilesystem'
import {UniversalPath} from '../path'
import {Awaitable, Maybe} from '../types'
import {Readable} from 'stream'
import {FileMetadata, Stat} from './Filesystem'
import {Collection} from '../../collection/Collection'
import {RequestInfo, RequestInit, Response} from 'node-fetch'
import {unsafeESMImport} from '../../unsafe'
const fetch = (url: RequestInfo, init?: RequestInit): Promise<Response> => unsafeESMImport('node-fetch').then(({default: nodeFetch}) => nodeFetch(url, init))
/**
* A filesystem implementation that reads files over HTTPS.
*/
export class HTTPFilesystem extends ReadOnlyFilesystem {
getPrefix(): string {
return 'https://'
}
async getStoreFileAsTemp(args: { storePath: string }): Promise<UniversalPath> {
const temp = this.tempName()
const write = fs.createWriteStream(temp)
const read = await this.getStoreFileAsStream(args)
return new Promise<UniversalPath>((res, rej) => {
write.on('finish', () => {
res(new UniversalPath(temp))
})
write.on('error', rej)
read.on('error', rej)
read.pipe(write)
})
}
async getStoreFileAsStream(args: { storePath: string }): Promise<Readable> {
if ( args.storePath.startsWith('/') ) {
args.storePath = args.storePath.slice(1)
}
const result = await fetch(`${this.getPrefix()}${args.storePath}`)
if ( result.body ) {
return (new Readable()).wrap(result.body)
}
return Readable.from([])
}
async stat(args: { storePath: string }): Promise<Stat> {
if ( args.storePath.startsWith('/') ) {
args.storePath = args.storePath.slice(1)
}
let response: Maybe<Response> = undefined
try {
response = await fetch(`${this.getPrefix()}${args.storePath}`)
} catch (e) {} // eslint-disable-line no-empty
return {
path: new UniversalPath(args.storePath, this),
exists: Boolean(response?.ok),
sizeInBytes: response?.size || 0,
mimeType: mime.lookup(path.basename(args.storePath)) || undefined,
tags: [],
accessed: undefined,
modified: undefined,
created: undefined,
isFile: true,
isDirectory: false,
}
}
getMetadata(storePath: string): Awaitable<FileMetadata> {
const mimeType = mime.lookup(path.basename(storePath))
return {
...(mimeType ? { mimeType } : {}),
}
}
list(): Awaitable<Collection<string>> {
throw new FilesystemOperationNotSupported(this, 'list')
}
}