storage-upload/api/media/routes.mjs

140 lines
3.7 KiB
JavaScript

import path from 'path'
import sharp from 'sharp-lite'
import fs from 'fs/promises'
import { HttpError } from '../error.mjs'
import * as security from './security.mjs'
import * as formidable from './formidable.mjs'
export default class MediaRoutes {
constructor(opts = {}) {
Object.assign(this, {
security: opts.security || security,
formidable: opts.formidable || formidable,
sharp: opts.sharp || sharp,
fs: opts.fs || fs,
})
this.siteCache = new Map()
this.collator = new Intl.Collator('is-IS', { numeric: false, sensitivity: 'accent' })
}
init() {
return fs.readdir('./public').then(folders => {
return Promise.all(folders.map(folder => {
return fs.readdir('./public/' + folder)
.then(files => {
return Promise.all(files.map(file => {
return fs.stat(`./public/${folder}/${file}`)
.then(function(stat) {
return { name: file, size: stat.size }
})
}))
})
.then(files => {
this.siteCache.set(folder, files)
})
.catch(function() {})
}))
})
}
filesCacheGet(site) {
let files = this.siteCache.get(site) || []
return files.sort((a, b) => {
return this.collator.compare(a.name, b.name)
})
}
filesCacheAdd(site, filename, size) {
let arr = this.siteCache.get(site)
if (!arr) {
this.siteCache.set(site, arr = [])
}
arr.push({ name: filename, size: size })
}
async listFiles(ctx) {
let site = await this.security.verifyToken(ctx)
ctx.body = this.filesCacheGet(site)
}
async listPublicFiles(ctx) {
this.security.throwIfNotPublic(ctx.params.site)
ctx.body = this.filesCacheGet(ctx.params.site)
}
async upload(ctx) {
let site = await this.security.verifyToken(ctx)
ctx.state.site = site
let result = await this.formidable.uploadFile(ctx, ctx.state.site)
ctx.log.info(`Uploaded ${result.filename}`)
let stat = await this.fs.stat(`./public/${ctx.state.site}/${result.filename}`)
this.filesCacheAdd(ctx.state.site, result.filename, stat.size)
ctx.body = {
filename: result.filename,
path: `/${ctx.state.site}/${result.filename}`
}
}
async resize(ctx) {
await this.upload(ctx)
this.security.verifyBody(ctx)
let out = {
original: ctx.body,
}
let keys = Object.keys(ctx.req.body)
await Promise.all(keys.map(key => {
return Promise.resolve()
.then(async () => {
let item = ctx.req.body[key]
let sharp = this.sharp(`./public/${ctx.state.site}/${ctx.body.filename}`)
.rotate()
if (item.resize) {
sharp = sharp.resize(item.resize)
}
sharp = sharp[item.format](item[item.format])
let target = ctx.body.filename
if (path.extname(target).length > 0) {
target = target.slice(0, -path.extname(target).length)
}
target += `_${key}.${item.format.replace('jpeg', 'jpg')}`
if (item.out === 'base64') {
let buffer = await sharp.toBuffer()
out[key] = {
base64: `data:image/${item.format};base64,` + buffer.toString('base64'),
}
return
}
await sharp.toFile(`./public/${ctx.state.site}/${target}`)
let stat = await this.fs.stat(`./public/${ctx.state.site}/${target}`)
this.filesCacheAdd(ctx.state.site, target, stat.size)
out[key] = {
filename: target,
path: `/${ctx.state.site}/${target}`,
}
}).then(
function() {},
function(err) {
throw new HttpError(`Error processing ${key}: ${err.message}`, 422)
}
)
}))
ctx.body = out
}
}