mirror of
https://github.com/BobbyWibowo/lolisafe.git
synced 2025-01-19 01:31:34 +00:00
refactor: ServeStatic custom middleware (WIP)
currently when enabled will force-close lolisafe i still need to find a decent backend library to make life easier
This commit is contained in:
parent
21d75f71f3
commit
b0913eaf59
121
controllers/middlewares/serveStatic.js
Normal file
121
controllers/middlewares/serveStatic.js
Normal file
@ -0,0 +1,121 @@
|
||||
|
||||
const contentDisposition = require('content-disposition')
|
||||
const SimpleDataStore = require('../utils/SimpleDataStore')
|
||||
const paths = require('../pathsController')
|
||||
const utils = require('../utilsController')
|
||||
const logger = require('../../logger')
|
||||
|
||||
class ServeStatic {
|
||||
directory
|
||||
contentDispositionStore
|
||||
contentTypesMaps
|
||||
|
||||
async #setContentDisposition () {}
|
||||
#setContentType () {}
|
||||
|
||||
constructor (directory, options = {}) {
|
||||
logger.error('new ServeStatic()')
|
||||
if (!directory || typeof directory !== 'string') {
|
||||
throw new TypeError('Root directory must be set')
|
||||
}
|
||||
|
||||
this.directory = directory
|
||||
|
||||
// Init Content-Type overrides
|
||||
if (typeof options.overrideContentTypes === 'object') {
|
||||
this.contentTypesMaps = new Map()
|
||||
|
||||
const types = Object.keys(options.overrideContentTypes)
|
||||
for (const type of types) {
|
||||
const extensions = options.overrideContentTypes[type]
|
||||
if (Array.isArray(extensions)) {
|
||||
for (const extension of extensions) {
|
||||
this.contentTypesMaps.set(extension, type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.contentTypesMaps.size) {
|
||||
this.#setContentType = (res, path, stat) => {
|
||||
// Do only if accessing files from uploads' root directory (i.e. not thumbs, etc.)
|
||||
const relpath = path.replace(paths.uploads, '')
|
||||
if (relpath.indexOf('/', 1) === -1) {
|
||||
const name = relpath.substring(1)
|
||||
const extname = utils.extname(name).substring(1)
|
||||
const contentType = this.contentTypesMaps.get(extname)
|
||||
if (contentType) {
|
||||
res.set('Content-Type', contentType)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.contentTypesMaps = undefined
|
||||
}
|
||||
}
|
||||
|
||||
// Init Content-Disposition store and setHeaders function if required
|
||||
if (options.setContentDisposition) {
|
||||
this.contentDispositionStore = new SimpleDataStore(
|
||||
options.contentDispositionOptions || {
|
||||
limit: 50,
|
||||
strategy: SimpleDataStore.STRATEGIES[0]
|
||||
}
|
||||
)
|
||||
|
||||
this.#setContentDisposition = async (res, path, stat) => {
|
||||
// Do only if accessing files from uploads' root directory (i.e. not thumbs, etc.)
|
||||
const relpath = path.replace(paths.uploads, '')
|
||||
if (relpath.indexOf('/', 1) !== -1) return
|
||||
const name = relpath.substring(1)
|
||||
try {
|
||||
let original = this.contentDispositionStore.get(name)
|
||||
if (original === undefined) {
|
||||
this.contentDispositionStore.hold(name)
|
||||
original = await utils.db.table('files')
|
||||
.where('name', name)
|
||||
.select('original')
|
||||
.first()
|
||||
.then(_file => {
|
||||
this.contentDispositionStore.set(name, _file.original)
|
||||
return _file.original
|
||||
})
|
||||
}
|
||||
if (original) {
|
||||
res.set('Content-Disposition', contentDisposition(original, { type: 'inline' }))
|
||||
}
|
||||
} catch (error) {
|
||||
this.contentDispositionStore.delete(name)
|
||||
logger.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug('Inititated SimpleDataStore for Content-Disposition: ' +
|
||||
`{ limit: ${this.contentDispositionStore.limit}, strategy: "${this.contentDispositionStore.strategy}" }`)
|
||||
}
|
||||
}
|
||||
|
||||
async #setHeaders (req, res) {
|
||||
logger.log('ServeStatic.setHeaders()')
|
||||
|
||||
this.#setContentType(req, res)
|
||||
|
||||
// Only set Content-Disposition on GET requests
|
||||
if (req.method === 'GET') {
|
||||
await this.#setContentDisposition(req, res)
|
||||
}
|
||||
}
|
||||
|
||||
async #middleware (req, res, next) {
|
||||
logger.log(`ServeStatic.middleware(): ${this.directory}, ${req.path}`)
|
||||
|
||||
// TODO
|
||||
|
||||
return next()
|
||||
}
|
||||
|
||||
get middleware () {
|
||||
return this.#middleware.bind(this)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ServeStatic
|
110
lolisafe.js
110
lolisafe.js
@ -10,7 +10,6 @@ process.on('unhandledRejection', error => {
|
||||
})
|
||||
|
||||
// Libraries
|
||||
const contentDisposition = require('content-disposition')
|
||||
const helmet = require('helmet')
|
||||
const HyperExpress = require('hyper-express')
|
||||
const LiveDirectory = require('live-directory')
|
||||
@ -130,74 +129,6 @@ const cdnPages = [...config.pages]
|
||||
// Defaults to no-op
|
||||
let setHeadersForStaticAssets = () => {}
|
||||
|
||||
const contentTypes = typeof config.overrideContentTypes === 'object' &&
|
||||
Object.keys(config.overrideContentTypes)
|
||||
const overrideContentTypes = contentTypes && contentTypes.length && function (res, path) {
|
||||
// Do only if accessing files from uploads' root directory (i.e. not thumbs, etc.)
|
||||
const relpath = path.replace(paths.uploads, '')
|
||||
if (relpath.indexOf('/', 1) === -1) {
|
||||
const name = relpath.substring(1)
|
||||
const extname = utils.extname(name).substring(1)
|
||||
for (const contentType of contentTypes) {
|
||||
if (config.overrideContentTypes[contentType].includes(extname)) {
|
||||
res.set('Content-Type', contentType)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const initServeStaticUploads = (opts = {}) => {
|
||||
if (config.setContentDisposition) {
|
||||
const SimpleDataStore = require('./controllers/utils/SimpleDataStore')
|
||||
utils.contentDispositionStore = new SimpleDataStore(
|
||||
config.contentDispositionOptions || {
|
||||
limit: 50,
|
||||
strategy: SimpleDataStore.STRATEGIES[0]
|
||||
}
|
||||
)
|
||||
opts.preSetHeaders = async (res, req, path, stat) => {
|
||||
// Do only if accessing files from uploads' root directory (i.e. not thumbs, etc.),
|
||||
// AND only if GET requests
|
||||
const relpath = path.replace(paths.uploads, '')
|
||||
if (relpath.indexOf('/', 1) !== -1 || req.method !== 'GET') return
|
||||
const name = relpath.substring(1)
|
||||
try {
|
||||
let original = utils.contentDispositionStore.get(name)
|
||||
if (original === undefined) {
|
||||
utils.contentDispositionStore.hold(name)
|
||||
original = await utils.db.table('files')
|
||||
.where('name', name)
|
||||
.select('original')
|
||||
.first()
|
||||
.then(_file => {
|
||||
utils.contentDispositionStore.set(name, _file.original)
|
||||
return _file.original
|
||||
})
|
||||
}
|
||||
if (original) {
|
||||
res.set('Content-Disposition', contentDisposition(original, { type: 'inline' }))
|
||||
}
|
||||
} catch (error) {
|
||||
utils.contentDispositionStore.delete(name)
|
||||
logger.error(error)
|
||||
}
|
||||
}
|
||||
// serveStatic is provided with @bobbywibowo/serve-static, a fork of express/serve-static.
|
||||
// The fork allows specifying an async function by the name preSetHeaders,
|
||||
// which it will await before creating 'send' stream to client.
|
||||
// This is necessary due to database queries being async tasks,
|
||||
// and express/serve-static not having the functionality by default.
|
||||
// safe.use('/', require('@bobbywibowo/serve-static')(paths.uploads, opts))
|
||||
// logger.debug('Inititated SimpleDataStore for Content-Disposition: ' +
|
||||
// `{ limit: ${utils.contentDispositionStore.limit}, strategy: "${utils.contentDispositionStore.strategy}" }`)
|
||||
logger.error('initServeStaticUploads() was called, but still WIP')
|
||||
} else {
|
||||
// safe.use('/', express.static(paths.uploads, opts))
|
||||
logger.error('initServeStaticUploads() was called, but still WIP')
|
||||
}
|
||||
}
|
||||
|
||||
// Cache control
|
||||
if (config.cacheControl) {
|
||||
const cacheControls = {
|
||||
@ -232,23 +163,6 @@ if (config.cacheControl) {
|
||||
break
|
||||
}
|
||||
|
||||
// If serving uploads with node
|
||||
if (config.serveFilesWithNode) {
|
||||
initServeStaticUploads({
|
||||
setHeaders: (res, path) => {
|
||||
// Override Content-Type header if necessary
|
||||
if (overrideContentTypes) {
|
||||
overrideContentTypes(res, path)
|
||||
}
|
||||
// If using CDN, cache uploads in CDN as well
|
||||
// Use with cloudflare.purgeCache enabled in config file
|
||||
if (config.cacheControl !== 2) {
|
||||
res.set('Cache-Control', cacheControls.cdn)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Function for static assets.
|
||||
// This requires the assets to use version in their query string,
|
||||
// as they will be cached by clients for a very long time.
|
||||
@ -266,13 +180,6 @@ if (config.cacheControl) {
|
||||
}
|
||||
return next()
|
||||
})
|
||||
} else if (config.serveFilesWithNode) {
|
||||
const opts = {}
|
||||
// Override Content-Type header if necessary
|
||||
if (overrideContentTypes) {
|
||||
opts.setHeaders = overrideContentTypes
|
||||
}
|
||||
initServeStaticUploads(opts)
|
||||
}
|
||||
|
||||
// Static assets
|
||||
@ -354,6 +261,23 @@ safe.use('/api', api)
|
||||
}
|
||||
}
|
||||
|
||||
// Init ServerStatic last if serving uploaded files with node
|
||||
/* // TODO
|
||||
if (config.serveFilesWithNode) {
|
||||
const serveStaticInstance = new ServeStatic(paths.uploads, {
|
||||
contentDispositionOptions: config.contentDispositionOptions,
|
||||
overrideContentTypes: config.overrideContentTypes,
|
||||
setContentDisposition: config.setContentDisposition
|
||||
})
|
||||
safe.use('/', serveStaticInstance.middleware)
|
||||
utils.contentDispositionStore = serveStaticInstance.contentDispositionStore
|
||||
}
|
||||
*/
|
||||
if (config.serveFilesWithNode) {
|
||||
logger.error('Serving files with node is currently not available in this branch.')
|
||||
return process.exit(1)
|
||||
}
|
||||
|
||||
// Web server error handlers (must always be set after all routes/middlewares)
|
||||
safe.set_not_found_handler(errors.handleNotFound)
|
||||
safe.set_error_handler(errors.handleError)
|
||||
|
Loading…
Reference in New Issue
Block a user