Many updates. Added ability to upload prefix-less files. Added ability to remove files.
Breaking: Renamed name to filename when listing files in folder. Breaking: Fixed the schema response for /media/resize. It is now consistent with other upload methods.
This commit is contained in:
parent
92338b94ca
commit
58553f3e34
8 changed files with 566 additions and 182 deletions
|
@ -6,24 +6,28 @@ import config from '../config.mjs'
|
||||||
let lastDateString = ''
|
let lastDateString = ''
|
||||||
let incrementor = 1
|
let incrementor = 1
|
||||||
|
|
||||||
export function uploadFile(ctx, siteName) {
|
export function uploadFile(ctx, siteName, noprefix = false) {
|
||||||
return new Promise((res, rej) => {
|
return new Promise((res, rej) => {
|
||||||
const date = new Date()
|
const date = new Date()
|
||||||
|
|
||||||
// Generate 'YYYYMMDD_HHMMSS_' prefix
|
let prefix = ''
|
||||||
let prefix = date
|
|
||||||
.toISOString()
|
|
||||||
.replace(/-/g, '')
|
|
||||||
.replace('T', '_')
|
|
||||||
.replace(/:/g, '')
|
|
||||||
.replace(/\..+/, '_')
|
|
||||||
|
|
||||||
// Append xx_ if same date is hit multiple times
|
if (!noprefix) {
|
||||||
if (prefix === lastDateString) {
|
// Generate 'YYYYMMDD_HHMMSS_' prefix
|
||||||
prefix += incrementor.toString().padStart('2', '0') + '_'
|
prefix = date
|
||||||
incrementor++
|
.toISOString()
|
||||||
} else {
|
.replace(/-/g, '')
|
||||||
lastDateString = prefix
|
.replace('T', '_')
|
||||||
|
.replace(/:/g, '')
|
||||||
|
.replace(/\..+/, '_')
|
||||||
|
|
||||||
|
// Append xx_ if same date is hit multiple times
|
||||||
|
if (prefix === lastDateString) {
|
||||||
|
prefix += incrementor.toString().padStart('2', '0') + '_'
|
||||||
|
incrementor++
|
||||||
|
} else {
|
||||||
|
lastDateString = prefix
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var form = new formidable.IncomingForm()
|
var form = new formidable.IncomingForm()
|
||||||
|
|
|
@ -25,7 +25,7 @@ export default class MediaRoutes {
|
||||||
return Promise.all(files.map(file => {
|
return Promise.all(files.map(file => {
|
||||||
return fs.stat(`./public/${folder}/${file}`)
|
return fs.stat(`./public/${folder}/${file}`)
|
||||||
.then(function(stat) {
|
.then(function(stat) {
|
||||||
return { name: file, size: stat.size }
|
return { filename: file, size: stat.size }
|
||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
@ -40,7 +40,7 @@ export default class MediaRoutes {
|
||||||
filesCacheGet(site) {
|
filesCacheGet(site) {
|
||||||
let files = this.siteCache.get(site) || []
|
let files = this.siteCache.get(site) || []
|
||||||
return files.sort((a, b) => {
|
return files.sort((a, b) => {
|
||||||
return this.collator.compare(a.name, b.name)
|
return this.collator.compare(a.filename, b.filename)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +49,29 @@ export default class MediaRoutes {
|
||||||
if (!arr) {
|
if (!arr) {
|
||||||
this.siteCache.set(site, arr = [])
|
this.siteCache.set(site, arr = [])
|
||||||
}
|
}
|
||||||
arr.push({ name: filename, size: size })
|
let found = false
|
||||||
|
for (let file of arr) {
|
||||||
|
if (file.filename === filename) {
|
||||||
|
found = true
|
||||||
|
file.size = size
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
arr.push({ filename: filename, size: size })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filesCacheRemove(site, filename) {
|
||||||
|
let arr = this.siteCache.get(site)
|
||||||
|
if (!arr) return
|
||||||
|
|
||||||
|
for (let i = 0; i < arr.length; i++) {
|
||||||
|
if (arr[i].filename === filename) {
|
||||||
|
arr.splice(i, 1)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async listFiles(ctx) {
|
async listFiles(ctx) {
|
||||||
|
@ -64,11 +86,11 @@ export default class MediaRoutes {
|
||||||
ctx.body = this.filesCacheGet(ctx.params.site)
|
ctx.body = this.filesCacheGet(ctx.params.site)
|
||||||
}
|
}
|
||||||
|
|
||||||
async upload(ctx) {
|
async upload(ctx, noprefix = false) {
|
||||||
let site = await this.security.verifyToken(ctx)
|
let site = await this.security.verifyToken(ctx)
|
||||||
ctx.state.site = site
|
ctx.state.site = site
|
||||||
|
|
||||||
let result = await this.formidable.uploadFile(ctx, ctx.state.site)
|
let result = await this.formidable.uploadFile(ctx, ctx.state.site, noprefix)
|
||||||
|
|
||||||
ctx.log.info(`Uploaded ${result.filename}`)
|
ctx.log.info(`Uploaded ${result.filename}`)
|
||||||
|
|
||||||
|
@ -81,15 +103,15 @@ export default class MediaRoutes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uploadNoPrefix(ctx) {
|
||||||
|
return this.upload(ctx, true)
|
||||||
|
}
|
||||||
|
|
||||||
async resize(ctx) {
|
async resize(ctx) {
|
||||||
await this.upload(ctx)
|
await this.upload(ctx)
|
||||||
|
|
||||||
this.security.verifyBody(ctx)
|
this.security.verifyBody(ctx)
|
||||||
|
|
||||||
let out = {
|
|
||||||
original: ctx.body,
|
|
||||||
}
|
|
||||||
|
|
||||||
let keys = Object.keys(ctx.req.body)
|
let keys = Object.keys(ctx.req.body)
|
||||||
|
|
||||||
await Promise.all(keys.map(key => {
|
await Promise.all(keys.map(key => {
|
||||||
|
@ -112,7 +134,7 @@ export default class MediaRoutes {
|
||||||
|
|
||||||
if (item.out === 'base64') {
|
if (item.out === 'base64') {
|
||||||
let buffer = await sharp.toBuffer()
|
let buffer = await sharp.toBuffer()
|
||||||
out[key] = {
|
ctx.body[key] = {
|
||||||
base64: `data:image/${item.format};base64,` + buffer.toString('base64'),
|
base64: `data:image/${item.format};base64,` + buffer.toString('base64'),
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -122,7 +144,7 @@ export default class MediaRoutes {
|
||||||
let stat = await this.fs.stat(`./public/${ctx.state.site}/${target}`)
|
let stat = await this.fs.stat(`./public/${ctx.state.site}/${target}`)
|
||||||
this.filesCacheAdd(ctx.state.site, target, stat.size)
|
this.filesCacheAdd(ctx.state.site, target, stat.size)
|
||||||
|
|
||||||
out[key] = {
|
ctx.body[key] = {
|
||||||
filename: target,
|
filename: target,
|
||||||
path: `/${ctx.state.site}/${target}`,
|
path: `/${ctx.state.site}/${target}`,
|
||||||
}
|
}
|
||||||
|
@ -133,7 +155,18 @@ export default class MediaRoutes {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
ctx.body = out
|
async remove(ctx) {
|
||||||
|
let site = await this.security.verifyToken(ctx)
|
||||||
|
|
||||||
|
this.filesCacheRemove(site, ctx.params.filename)
|
||||||
|
|
||||||
|
await this.fs.unlink(`./public/${site}/${ctx.params.filename}`)
|
||||||
|
.catch(function(err) {
|
||||||
|
throw new HttpError(`Error removing ${site}/${ctx.params.filename}: ${err.message}`, 422)
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx.status = 204
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,8 +36,8 @@ export function verifyBody(ctx) {
|
||||||
let keys = Object.keys(ctx.req.body)
|
let keys = Object.keys(ctx.req.body)
|
||||||
|
|
||||||
for (let key of keys) {
|
for (let key of keys) {
|
||||||
if (key === 'original') {
|
if (key === 'filename' || key === 'path') {
|
||||||
throw new HttpError('Body item with name original is not allowed', 422)
|
throw new HttpError('Body item with name filename or path is not allowed', 422)
|
||||||
}
|
}
|
||||||
let item = ctx.req.body[key]
|
let item = ctx.req.body[key]
|
||||||
|
|
||||||
|
|
|
@ -53,9 +53,11 @@ media.init().then(function() {}, function(err) {
|
||||||
log.error(err, 'Error initing media')
|
log.error(err, 'Error initing media')
|
||||||
})
|
})
|
||||||
app.get('/media', [QueryHandler()], media.listFiles.bind(media))
|
app.get('/media', [QueryHandler()], media.listFiles.bind(media))
|
||||||
app.get('/media/:site', [QueryHandler()], media.listPublicFiles.bind(media))
|
app.get('/media/:site', media.listPublicFiles.bind(media))
|
||||||
app.post('/media', [QueryHandler()], media.upload.bind(media))
|
app.post('/media', [QueryHandler()], media.upload.bind(media))
|
||||||
|
app.post('/media/noprefix', [QueryHandler()], media.uploadNoPrefix.bind(media))
|
||||||
app.post('/media/resize', [QueryHandler()], media.resize.bind(media))
|
app.post('/media/resize', [QueryHandler()], media.resize.bind(media))
|
||||||
|
app.delete('/media/:filename', [QueryHandler()], media.remove.bind(media))
|
||||||
|
|
||||||
app.listen(config.get('server:port'), function(a,b) {
|
app.listen(config.get('server:port'), function(a,b) {
|
||||||
log.info(`Server listening at ${config.get('server:port')}`)
|
log.info(`Server listening at ${config.get('server:port')}`)
|
||||||
|
|
|
@ -44,6 +44,7 @@ Client.prototype.customRequest = function(method = 'GET', path, body, options) {
|
||||||
})
|
})
|
||||||
|
|
||||||
res.on('end', function () {
|
res.on('end', function () {
|
||||||
|
if (!output) return resolve(null)
|
||||||
try {
|
try {
|
||||||
output = JSON.parse(output)
|
output = JSON.parse(output)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -66,6 +67,10 @@ Client.prototype.get = function(url = '/') {
|
||||||
return this.customRequest('GET', url, null)
|
return this.customRequest('GET', url, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Client.prototype.del = function(url = '/', body = {}) {
|
||||||
|
return this.customRequest('DELETE', url, JSON.stringify(body))
|
||||||
|
}
|
||||||
|
|
||||||
const random = (length = 8) => {
|
const random = (length = 8) => {
|
||||||
// Declare all characters
|
// Declare all characters
|
||||||
let chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
let chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
|
|
|
@ -14,6 +14,8 @@ function resolve(file) {
|
||||||
return path.resolve(path.join(__dirname, file))
|
return path.resolve(path.join(__dirname, file))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const currYear = new Date().getFullYear().toString()
|
||||||
|
|
||||||
t.describe('Media (API)', () => {
|
t.describe('Media (API)', () => {
|
||||||
const client = new Client()
|
const client = new Client()
|
||||||
const secret = 'asdf1234'
|
const secret = 'asdf1234'
|
||||||
|
@ -23,6 +25,9 @@ t.describe('Media (API)', () => {
|
||||||
return Promise.all(testFiles.map(function(file) {
|
return Promise.all(testFiles.map(function(file) {
|
||||||
return fs.unlink(resolve(`../../public/${file}`)).catch(function() {})
|
return fs.unlink(resolve(`../../public/${file}`)).catch(function() {})
|
||||||
}))
|
}))
|
||||||
|
.then(function() {
|
||||||
|
// return fs.unlink(resolve('../../public/existing/test.png')).catch(function() {})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.timeout(10000).describe('POST /media', function temp() {
|
t.timeout(10000).describe('POST /media', function temp() {
|
||||||
|
@ -83,12 +88,13 @@ t.describe('Media (API)', () => {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
testFiles.push(data.path)
|
||||||
|
|
||||||
assert.ok(data)
|
assert.ok(data)
|
||||||
assert.ok(data.filename)
|
assert.ok(data.filename)
|
||||||
|
assert.ok(data.filename.startsWith(currYear))
|
||||||
assert.ok(data.path)
|
assert.ok(data.path)
|
||||||
|
|
||||||
testFiles.push(data.path)
|
|
||||||
|
|
||||||
let stats = await Promise.all([
|
let stats = await Promise.all([
|
||||||
fs.stat(resolve('test.png')),
|
fs.stat(resolve('test.png')),
|
||||||
fs.stat(resolve(`../../public/${data.path}`)),
|
fs.stat(resolve(`../../public/${data.path}`)),
|
||||||
|
@ -102,6 +108,83 @@ t.describe('Media (API)', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.timeout(10000).describe('POST /media/noprefix', function temp() {
|
||||||
|
t.test('should require authentication', async () => {
|
||||||
|
resetLog()
|
||||||
|
assert.strictEqual(server.log.error.callCount, 0)
|
||||||
|
assert.strictEqual(server.log.warn.callCount, 0)
|
||||||
|
let err = await assert.isRejected(
|
||||||
|
client.upload('/media/noprefix',
|
||||||
|
resolve('test.png')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.strictEqual(err.status, 422)
|
||||||
|
assert.match(err.message, /[Tt]oken/)
|
||||||
|
assert.match(err.message, /[Mm]issing/)
|
||||||
|
|
||||||
|
assert.strictEqual(server.log.error.callCount, 0)
|
||||||
|
assert.strictEqual(server.log.warn.callCount, 2)
|
||||||
|
assert.strictEqual(typeof(server.log.warn.firstCall[0]), 'string')
|
||||||
|
assert.match(server.log.warn.firstCall[0], /[Tt]oken/)
|
||||||
|
assert.match(server.log.warn.firstCall[0], /[Mm]issing/)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('should verify token correctly', async () => {
|
||||||
|
const assertToken = 'asdf.asdf.asdf'
|
||||||
|
resetLog()
|
||||||
|
assert.strictEqual(server.log.error.callCount, 0)
|
||||||
|
assert.strictEqual(server.log.warn.callCount, 0)
|
||||||
|
assert.strictEqual(server.log.info.callCount, 0)
|
||||||
|
|
||||||
|
let err = await assert.isRejected(
|
||||||
|
client.upload('/media/noprefix?token=' + assertToken,
|
||||||
|
resolve('test.png')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.strictEqual(err.status, 422)
|
||||||
|
assert.match(err.message, /[Tt]oken/)
|
||||||
|
assert.match(err.message, /[Ii]nvalid/)
|
||||||
|
|
||||||
|
assert.strictEqual(server.log.error.callCount, 1)
|
||||||
|
assert.strictEqual(server.log.warn.callCount, 2)
|
||||||
|
assert.strictEqual(typeof(server.log.warn.firstCall[0]), 'string')
|
||||||
|
assert.match(server.log.warn.firstCall[0], /[Tt]oken/)
|
||||||
|
assert.match(server.log.warn.firstCall[0], /[Ii]nvalid/)
|
||||||
|
assert.ok(server.log.error.lastCall[0] instanceof Error)
|
||||||
|
assert.match(server.log.error.lastCall[1], new RegExp(assertToken))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('should upload and create file with no prefix', async () => {
|
||||||
|
let token = encode(null, { iss: 'development' }, secret)
|
||||||
|
|
||||||
|
let data = await assert.isFulfilled(
|
||||||
|
client.upload(
|
||||||
|
`/media/noprefix?token=${token}`,
|
||||||
|
resolve('test.png')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
testFiles.push(data.path)
|
||||||
|
|
||||||
|
assert.ok(data)
|
||||||
|
assert.strictEqual(data.filename, 'test.png')
|
||||||
|
assert.ok(data.path)
|
||||||
|
|
||||||
|
let stats = await Promise.all([
|
||||||
|
fs.stat(resolve('test.png')),
|
||||||
|
fs.stat(resolve('../../public/development/test.png')),
|
||||||
|
])
|
||||||
|
assert.strictEqual(stats[0].size, stats[1].size)
|
||||||
|
|
||||||
|
let img = await sharp(resolve('../../public/development/test.png')).metadata()
|
||||||
|
assert.strictEqual(img.width, 600)
|
||||||
|
assert.strictEqual(img.height, 700)
|
||||||
|
assert.strictEqual(img.format, 'png')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
t.timeout(10000).describe('POST /media/resize', function temp() {
|
t.timeout(10000).describe('POST /media/resize', function temp() {
|
||||||
t.test('should require authentication', async () => {
|
t.test('should require authentication', async () => {
|
||||||
resetLog()
|
resetLog()
|
||||||
|
@ -162,20 +245,20 @@ t.describe('Media (API)', () => {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
assert.ok(data)
|
testFiles.push(data.path)
|
||||||
assert.ok(data.original)
|
|
||||||
assert.ok(data.original.filename)
|
|
||||||
assert.ok(data.original.path)
|
|
||||||
|
|
||||||
testFiles.push(data.original.path)
|
assert.ok(data)
|
||||||
|
assert.ok(data.filename)
|
||||||
|
assert.ok(data.filename.startsWith(currYear))
|
||||||
|
assert.ok(data.path)
|
||||||
|
|
||||||
let stats = await Promise.all([
|
let stats = await Promise.all([
|
||||||
fs.stat(resolve('test.png')),
|
fs.stat(resolve('test.png')),
|
||||||
fs.stat(resolve(`../../public/${data.original.path}`)),
|
fs.stat(resolve(`../../public/${data.path}`)),
|
||||||
])
|
])
|
||||||
assert.strictEqual(stats[0].size, stats[1].size)
|
assert.strictEqual(stats[0].size, stats[1].size)
|
||||||
|
|
||||||
let img = await sharp(resolve(`../../public/${data.original.path}`)).metadata()
|
let img = await sharp(resolve(`../../public/${data.path}`)).metadata()
|
||||||
assert.strictEqual(img.width, 600)
|
assert.strictEqual(img.width, 600)
|
||||||
assert.strictEqual(img.height, 700)
|
assert.strictEqual(img.height, 700)
|
||||||
assert.strictEqual(img.format, 'png')
|
assert.strictEqual(img.format, 'png')
|
||||||
|
@ -213,27 +296,29 @@ t.describe('Media (API)', () => {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
assert.ok(data)
|
testFiles.push(data.path)
|
||||||
assert.ok(data.original)
|
|
||||||
assert.ok(data.original.filename)
|
|
||||||
assert.ok(data.original.path)
|
|
||||||
assert.ok(data.test1.filename)
|
|
||||||
assert.ok(data.test1.path)
|
|
||||||
assert.ok(data.test2.filename)
|
|
||||||
assert.ok(data.test2.path)
|
|
||||||
|
|
||||||
|
|
||||||
testFiles.push(data.original.path)
|
|
||||||
testFiles.push(data.test1.path)
|
testFiles.push(data.test1.path)
|
||||||
testFiles.push(data.test2.path)
|
testFiles.push(data.test2.path)
|
||||||
|
|
||||||
|
assert.ok(data)
|
||||||
|
assert.ok(data.filename)
|
||||||
|
assert.ok(data.filename.startsWith(currYear))
|
||||||
|
assert.ok(data.path)
|
||||||
|
assert.ok(data.test1.filename)
|
||||||
|
assert.ok(data.test1.filename.startsWith(currYear))
|
||||||
|
assert.ok(data.test1.path)
|
||||||
|
assert.ok(data.test2.filename)
|
||||||
|
assert.ok(data.test2.filename.startsWith(currYear))
|
||||||
|
assert.ok(data.test2.path)
|
||||||
|
|
||||||
|
|
||||||
let stats = await Promise.all([
|
let stats = await Promise.all([
|
||||||
fs.stat(resolve('test.png')),
|
fs.stat(resolve('test.png')),
|
||||||
fs.stat(resolve(`../../public/${data.original.path}`)),
|
fs.stat(resolve(`../../public/${data.path}`)),
|
||||||
])
|
])
|
||||||
assert.strictEqual(stats[0].size, stats[1].size)
|
assert.strictEqual(stats[0].size, stats[1].size)
|
||||||
|
|
||||||
let img = await sharp(resolve(`../../public/${data.original.path}`)).metadata()
|
let img = await sharp(resolve(`../../public/${data.path}`)).metadata()
|
||||||
assert.strictEqual(img.width, 600)
|
assert.strictEqual(img.width, 600)
|
||||||
assert.strictEqual(img.height, 700)
|
assert.strictEqual(img.height, 700)
|
||||||
assert.strictEqual(img.format, 'png')
|
assert.strictEqual(img.format, 'png')
|
||||||
|
@ -249,7 +334,6 @@ t.describe('Media (API)', () => {
|
||||||
assert.strictEqual(img.format, 'png')
|
assert.strictEqual(img.format, 'png')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
t.test('should upload file and support base64 output', async () => {
|
t.test('should upload file and support base64 output', async () => {
|
||||||
let token = encode(null, { iss: 'development' }, secret)
|
let token = encode(null, { iss: 'development' }, secret)
|
||||||
|
|
||||||
|
@ -275,21 +359,20 @@ t.describe('Media (API)', () => {
|
||||||
)
|
)
|
||||||
|
|
||||||
assert.ok(data)
|
assert.ok(data)
|
||||||
assert.ok(data.original)
|
assert.ok(data.filename)
|
||||||
assert.ok(data.original.filename)
|
assert.ok(data.path)
|
||||||
assert.ok(data.original.path)
|
|
||||||
assert.ok(data.outtest.base64)
|
assert.ok(data.outtest.base64)
|
||||||
|
|
||||||
|
|
||||||
testFiles.push(data.original.path)
|
testFiles.push(data.path)
|
||||||
|
|
||||||
let stats = await Promise.all([
|
let stats = await Promise.all([
|
||||||
fs.stat(resolve('test.png')),
|
fs.stat(resolve('test.png')),
|
||||||
fs.stat(resolve(`../../public/${data.original.path}`)),
|
fs.stat(resolve(`../../public/${data.path}`)),
|
||||||
])
|
])
|
||||||
assert.strictEqual(stats[0].size, stats[1].size)
|
assert.strictEqual(stats[0].size, stats[1].size)
|
||||||
|
|
||||||
let img = await sharp(resolve(`../../public/${data.original.path}`)).metadata()
|
let img = await sharp(resolve(`../../public/${data.path}`)).metadata()
|
||||||
assert.strictEqual(img.width, 600)
|
assert.strictEqual(img.width, 600)
|
||||||
assert.strictEqual(img.height, 700)
|
assert.strictEqual(img.height, 700)
|
||||||
assert.strictEqual(img.format, 'png')
|
assert.strictEqual(img.format, 'png')
|
||||||
|
@ -303,6 +386,101 @@ t.describe('Media (API)', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.timeout(10000).describe('DELETE /media/:filename', function temp() {
|
||||||
|
t.test('should require authentication', async () => {
|
||||||
|
resetLog()
|
||||||
|
assert.strictEqual(server.log.error.callCount, 0)
|
||||||
|
assert.strictEqual(server.log.warn.callCount, 0)
|
||||||
|
let err = await assert.isRejected(
|
||||||
|
client.del('/media/20220105_101610_test1.jpg',
|
||||||
|
resolve('test.png')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.strictEqual(err.status, 422)
|
||||||
|
assert.match(err.message, /[Tt]oken/)
|
||||||
|
assert.match(err.message, /[Mm]issing/)
|
||||||
|
|
||||||
|
assert.strictEqual(server.log.error.callCount, 0)
|
||||||
|
assert.strictEqual(server.log.warn.callCount, 2)
|
||||||
|
assert.strictEqual(typeof(server.log.warn.firstCall[0]), 'string')
|
||||||
|
assert.match(server.log.warn.firstCall[0], /[Tt]oken/)
|
||||||
|
assert.match(server.log.warn.firstCall[0], /[Mm]issing/)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('should verify token correctly', async () => {
|
||||||
|
const assertToken = 'asdf.asdf.asdf'
|
||||||
|
resetLog()
|
||||||
|
assert.strictEqual(server.log.error.callCount, 0)
|
||||||
|
assert.strictEqual(server.log.warn.callCount, 0)
|
||||||
|
assert.strictEqual(server.log.info.callCount, 0)
|
||||||
|
|
||||||
|
let err = await assert.isRejected(
|
||||||
|
client.del('/media/20220105_101610_test1.jpg?token=' + assertToken,
|
||||||
|
resolve('test.png')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
assert.strictEqual(err.status, 422)
|
||||||
|
assert.match(err.message, /[Tt]oken/)
|
||||||
|
assert.match(err.message, /[Ii]nvalid/)
|
||||||
|
|
||||||
|
assert.strictEqual(server.log.error.callCount, 1)
|
||||||
|
assert.strictEqual(server.log.warn.callCount, 2)
|
||||||
|
assert.strictEqual(typeof(server.log.warn.firstCall[0]), 'string')
|
||||||
|
assert.match(server.log.warn.firstCall[0], /[Tt]oken/)
|
||||||
|
assert.match(server.log.warn.firstCall[0], /[Ii]nvalid/)
|
||||||
|
assert.ok(server.log.error.lastCall[0] instanceof Error)
|
||||||
|
assert.match(server.log.error.lastCall[1], new RegExp(assertToken))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('should remove the file', async () => {
|
||||||
|
let token = encode(null, { iss: 'existing' }, secret)
|
||||||
|
|
||||||
|
let data = await client.upload(
|
||||||
|
`/media/noprefix?token=${token}`,
|
||||||
|
resolve('test.png')
|
||||||
|
)
|
||||||
|
|
||||||
|
let filepath = data.path
|
||||||
|
testFiles.push(filepath)
|
||||||
|
|
||||||
|
let files = await client.get('/media/existing')
|
||||||
|
|
||||||
|
let found = false
|
||||||
|
for (let file of files) {
|
||||||
|
if (file.filename === 'test.png') {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.ok(found)
|
||||||
|
|
||||||
|
await assert.isFulfilled(
|
||||||
|
fs.stat(resolve(`../../public/${filepath}`))
|
||||||
|
)
|
||||||
|
|
||||||
|
await assert.isFulfilled(
|
||||||
|
client.del(`/media/test.png?token=${token}`)
|
||||||
|
)
|
||||||
|
|
||||||
|
testFiles.splice(testFiles.length - 1)
|
||||||
|
|
||||||
|
files = await client.get('/media/existing')
|
||||||
|
|
||||||
|
found = false
|
||||||
|
for (let file of files) {
|
||||||
|
if (file.filename === 'test.png') {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.notOk(found)
|
||||||
|
|
||||||
|
await assert.isRejected(
|
||||||
|
fs.stat(resolve(`../../public/${filepath}`))
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
t.describe('GET /media', function() {
|
t.describe('GET /media', function() {
|
||||||
t.test('should require authentication', async () => {
|
t.test('should require authentication', async () => {
|
||||||
resetLog()
|
resetLog()
|
||||||
|
@ -351,7 +529,7 @@ t.describe('Media (API)', () => {
|
||||||
assert.ok(data.length)
|
assert.ok(data.length)
|
||||||
let found = false
|
let found = false
|
||||||
for (let file of data) {
|
for (let file of data) {
|
||||||
if (file.name === '.gitkeep' && file.size === 0) {
|
if (file.filename === '.gitkeep' && file.size === 0) {
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -377,9 +555,9 @@ t.describe('Media (API)', () => {
|
||||||
|
|
||||||
t.test('should otherwise return list of files for a public site', async () => {
|
t.test('should otherwise return list of files for a public site', async () => {
|
||||||
let files = await client.get('/media/existing')
|
let files = await client.get('/media/existing')
|
||||||
assert.strictEqual(files[0].name, '20220105_101610_test1.jpg')
|
assert.strictEqual(files[0].filename, '20220105_101610_test1.jpg')
|
||||||
assert.strictEqual(files[0].size, 15079)
|
assert.strictEqual(files[0].size, 15079)
|
||||||
assert.strictEqual(files[1].name, '20220105_101610_test2.png')
|
assert.strictEqual(files[1].filename, '20220105_101610_test2.png')
|
||||||
assert.strictEqual(files[1].size, 31705)
|
assert.strictEqual(files[1].size, 31705)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,9 +1,23 @@
|
||||||
|
import fs from 'fs/promises'
|
||||||
import { Eltro as t, assert, stub } from 'eltro'
|
import { Eltro as t, assert, stub } from 'eltro'
|
||||||
import { createContext } from '../helper.server.mjs'
|
import { createContext } from '../helper.server.mjs'
|
||||||
|
|
||||||
import MediaRoutes from '../../api/media/routes.mjs'
|
import MediaRoutes from '../../api/media/routes.mjs'
|
||||||
import { HttpError } from '../../api/error.mjs'
|
import { HttpError } from '../../api/error.mjs'
|
||||||
|
|
||||||
|
t.before(function() {
|
||||||
|
return Promise.all([
|
||||||
|
fs.readdir('./public/development').then(files => {
|
||||||
|
return Promise.all(files.map(function(file) {
|
||||||
|
if (file !== '.gitkeep') {
|
||||||
|
return fs.unlink(`./public/development/${file}`).catch(function() {})
|
||||||
|
}
|
||||||
|
return Promise.resolve()
|
||||||
|
}))
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
t.describe('#filesCacheGet', function() {
|
t.describe('#filesCacheGet', function() {
|
||||||
t.test('should return all files in public folder', async function() {
|
t.test('should return all files in public folder', async function() {
|
||||||
const routes = new MediaRoutes()
|
const routes = new MediaRoutes()
|
||||||
|
@ -21,11 +35,11 @@ t.describe('#filesCacheGet', function() {
|
||||||
assert.strictEqual(routes.filesCacheGet('existing').length, 2)
|
assert.strictEqual(routes.filesCacheGet('existing').length, 2)
|
||||||
assert.strictEqual(routes.filesCacheGet('nonexisting').length, 0)
|
assert.strictEqual(routes.filesCacheGet('nonexisting').length, 0)
|
||||||
|
|
||||||
assert.strictEqual(routes.filesCacheGet('development')[0].name, '.gitkeep')
|
assert.strictEqual(routes.filesCacheGet('development')[0].filename, '.gitkeep')
|
||||||
assert.strictEqual(routes.filesCacheGet('development')[0].size, 0)
|
assert.strictEqual(routes.filesCacheGet('development')[0].size, 0)
|
||||||
assert.strictEqual(routes.filesCacheGet('existing')[0].name, '20220105_101610_test1.jpg')
|
assert.strictEqual(routes.filesCacheGet('existing')[0].filename, '20220105_101610_test1.jpg')
|
||||||
assert.strictEqual(routes.filesCacheGet('existing')[0].size, 15079)
|
assert.strictEqual(routes.filesCacheGet('existing')[0].size, 15079)
|
||||||
assert.strictEqual(routes.filesCacheGet('existing')[1].name, '20220105_101610_test2.png')
|
assert.strictEqual(routes.filesCacheGet('existing')[1].filename, '20220105_101610_test2.png')
|
||||||
assert.strictEqual(routes.filesCacheGet('existing')[1].size, 31705)
|
assert.strictEqual(routes.filesCacheGet('existing')[1].size, 31705)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -36,19 +50,19 @@ t.describe('#filesCacheGet', function() {
|
||||||
|
|
||||||
let files = routes.filesCacheGet('existing')
|
let files = routes.filesCacheGet('existing')
|
||||||
|
|
||||||
assert.strictEqual(files[0].name, '20220105_101610_test1.jpg')
|
assert.strictEqual(files[0].filename, '20220105_101610_test1.jpg')
|
||||||
assert.strictEqual(files[0].size, 15079)
|
assert.strictEqual(files[0].size, 15079)
|
||||||
assert.strictEqual(files[1].name, '20220105_101610_test2.png')
|
assert.strictEqual(files[1].filename, '20220105_101610_test2.png')
|
||||||
assert.strictEqual(files[1].size, 31705)
|
assert.strictEqual(files[1].size, 31705)
|
||||||
|
|
||||||
routes.siteCache.get('existing').push({ name: '0000.png', size: 0 })
|
routes.siteCache.get('existing').push({ filename: '0000.png', size: 0 })
|
||||||
|
|
||||||
files = routes.filesCacheGet('existing')
|
files = routes.filesCacheGet('existing')
|
||||||
assert.strictEqual(files[0].name, '0000.png')
|
assert.strictEqual(files[0].filename, '0000.png')
|
||||||
assert.strictEqual(files[0].size, 0)
|
assert.strictEqual(files[0].size, 0)
|
||||||
assert.strictEqual(files[1].name, '20220105_101610_test1.jpg')
|
assert.strictEqual(files[1].filename, '20220105_101610_test1.jpg')
|
||||||
assert.strictEqual(files[1].size, 15079)
|
assert.strictEqual(files[1].size, 15079)
|
||||||
assert.strictEqual(files[2].name, '20220105_101610_test2.png')
|
assert.strictEqual(files[2].filename, '20220105_101610_test2.png')
|
||||||
assert.strictEqual(files[2].size, 31705)
|
assert.strictEqual(files[2].size, 31705)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -160,19 +174,83 @@ t.describe('#filesCacheAdd', function() {
|
||||||
routes.filesCacheAdd('nonexisting', assertName, assertSize)
|
routes.filesCacheAdd('nonexisting', assertName, assertSize)
|
||||||
|
|
||||||
assert.strictEqual(routes.filesCacheGet('nonexisting').length, 1)
|
assert.strictEqual(routes.filesCacheGet('nonexisting').length, 1)
|
||||||
assert.strictEqual(routes.filesCacheGet('nonexisting')[0].name, assertName)
|
assert.strictEqual(routes.filesCacheGet('nonexisting')[0].filename, assertName)
|
||||||
assert.strictEqual(routes.filesCacheGet('nonexisting')[0].size, assertSize)
|
assert.strictEqual(routes.filesCacheGet('nonexisting')[0].size, assertSize)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.test('should not add but update size if file already exists', function() {
|
||||||
|
const routes = new MediaRoutes()
|
||||||
|
const assertName = 'asdf.png'
|
||||||
|
const assertSize = 1234
|
||||||
|
const assertNewSize = 5678
|
||||||
|
|
||||||
|
assert.ok(routes.filesCacheGet('nonexisting'))
|
||||||
|
assert.ok(Array.isArray(routes.filesCacheGet('nonexisting')))
|
||||||
|
assert.strictEqual(routes.filesCacheGet('nonexisting').length, 0)
|
||||||
|
|
||||||
|
routes.filesCacheAdd('nonexisting', assertName, assertSize)
|
||||||
|
|
||||||
|
assert.strictEqual(routes.filesCacheGet('nonexisting').length, 1)
|
||||||
|
assert.strictEqual(routes.filesCacheGet('nonexisting')[0].filename, assertName)
|
||||||
|
assert.strictEqual(routes.filesCacheGet('nonexisting')[0].size, assertSize)
|
||||||
|
|
||||||
|
routes.filesCacheAdd('nonexisting', assertName, assertNewSize)
|
||||||
|
|
||||||
|
assert.strictEqual(routes.filesCacheGet('nonexisting').length, 1)
|
||||||
|
assert.strictEqual(routes.filesCacheGet('nonexisting')[0].filename, assertName)
|
||||||
|
assert.strictEqual(routes.filesCacheGet('nonexisting')[0].size, assertNewSize)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.describe('#upload', function() {
|
t.describe('#filesCacheRemove', function() {
|
||||||
|
t.test('should default not do anything if nothing is found', async function() {
|
||||||
|
const routes = new MediaRoutes()
|
||||||
|
|
||||||
|
await routes.init()
|
||||||
|
|
||||||
|
assert.ok(routes.filesCacheGet('existing'))
|
||||||
|
assert.ok(Array.isArray(routes.filesCacheGet('existing')))
|
||||||
|
assert.strictEqual(routes.filesCacheGet('existing').length, 2)
|
||||||
|
|
||||||
|
routes.filesCacheRemove('nonexisting', 'bla.text')
|
||||||
|
routes.filesCacheRemove('nonexisting', 'herp.derp')
|
||||||
|
routes.filesCacheRemove('existing', 'bla.text')
|
||||||
|
routes.filesCacheRemove('existing', 'herp.derp')
|
||||||
|
|
||||||
|
assert.ok(routes.filesCacheGet('existing'))
|
||||||
|
assert.ok(Array.isArray(routes.filesCacheGet('existing')))
|
||||||
|
assert.strictEqual(routes.filesCacheGet('existing').length, 2)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('should otherwise remove entries that match filename', async function() {
|
||||||
|
const routes = new MediaRoutes()
|
||||||
|
|
||||||
|
await routes.init()
|
||||||
|
|
||||||
|
assert.ok(routes.filesCacheGet('existing'))
|
||||||
|
assert.ok(Array.isArray(routes.filesCacheGet('existing')))
|
||||||
|
assert.strictEqual(routes.filesCacheGet('existing').length, 2)
|
||||||
|
|
||||||
|
routes.filesCacheRemove('existing', '20220105_101610_test1.jpg')
|
||||||
|
|
||||||
|
assert.ok(routes.filesCacheGet('existing'))
|
||||||
|
assert.ok(Array.isArray(routes.filesCacheGet('existing')))
|
||||||
|
assert.strictEqual(routes.filesCacheGet('existing').length, 1)
|
||||||
|
assert.strictEqual(routes.filesCacheGet('existing')[0].filename, '20220105_101610_test2.png')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.describe('#uploadNoPrefix', function() {
|
||||||
const stubVerify = stub()
|
const stubVerify = stub()
|
||||||
const stubUpload = stub()
|
const stubUpload = stub()
|
||||||
const stubStat = stub()
|
const stubStat = stub()
|
||||||
|
|
||||||
const routes = new MediaRoutes({
|
const routes = new MediaRoutes({
|
||||||
security: { verifyToken: stubVerify },
|
security: {
|
||||||
formidable: { uploadFile: stubUpload },
|
verifyToken: stubVerify,
|
||||||
|
verifyBody: stub(),
|
||||||
|
},
|
||||||
|
formidable: { uploadFile: stubUpload, },
|
||||||
fs: { stat: stubStat },
|
fs: { stat: stubStat },
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -182,20 +260,6 @@ t.describe('#upload', function() {
|
||||||
stubStat.reset()
|
stubStat.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
t.test('should call security correctly', async function() {
|
|
||||||
reset()
|
|
||||||
|
|
||||||
let ctx = createContext()
|
|
||||||
const assertError = new Error('temp')
|
|
||||||
stubVerify.rejects(assertError)
|
|
||||||
|
|
||||||
let err = await assert.isRejected(routes.upload(ctx))
|
|
||||||
|
|
||||||
assert.ok(stubVerify.called)
|
|
||||||
assert.strictEqual(err, assertError)
|
|
||||||
assert.strictEqual(stubVerify.firstCall[0], ctx)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should call upload correctly', async function() {
|
t.test('should call upload correctly', async function() {
|
||||||
reset()
|
reset()
|
||||||
|
|
||||||
|
@ -205,36 +269,13 @@ t.describe('#upload', function() {
|
||||||
stubVerify.resolves(assertSiteName)
|
stubVerify.resolves(assertSiteName)
|
||||||
stubUpload.rejects(assertError)
|
stubUpload.rejects(assertError)
|
||||||
|
|
||||||
let err = await assert.isRejected(routes.upload(ctx))
|
let err = await assert.isRejected(routes.uploadNoPrefix(ctx))
|
||||||
|
|
||||||
assert.ok(stubUpload.called)
|
assert.ok(stubUpload.called)
|
||||||
assert.strictEqual(err, assertError)
|
assert.strictEqual(err, assertError)
|
||||||
assert.strictEqual(stubUpload.firstCall[0], ctx)
|
assert.strictEqual(stubUpload.firstCall[0], ctx)
|
||||||
assert.strictEqual(stubUpload.firstCall[1], assertSiteName)
|
assert.strictEqual(stubUpload.firstCall[1], assertSiteName)
|
||||||
})
|
assert.strictEqual(stubUpload.firstCall[2], true)
|
||||||
|
|
||||||
t.test('should otherwise set context status to 204 and file in result', async function() {
|
|
||||||
reset()
|
|
||||||
|
|
||||||
let ctx = createContext()
|
|
||||||
const assertSize = 1241412
|
|
||||||
const assertFilename = 'asdfsafd'
|
|
||||||
const assertSite = 'mario'
|
|
||||||
stubVerify.resolves(assertSite)
|
|
||||||
stubUpload.resolves({ filename: assertFilename })
|
|
||||||
stubStat.resolves({ size: assertSize })
|
|
||||||
await routes.upload(ctx)
|
|
||||||
|
|
||||||
assert.strictEqual(ctx.body.filename, assertFilename)
|
|
||||||
assert.strictEqual(ctx.body.path, `/${assertSite}/${assertFilename}`)
|
|
||||||
|
|
||||||
assert.ok(stubStat.called)
|
|
||||||
assert.strictEqual(stubStat.firstCall[0], `./public/${assertSite}/${assertFilename}`)
|
|
||||||
|
|
||||||
let filesFromCache = routes.filesCacheGet(assertSite)
|
|
||||||
assert.strictEqual(filesFromCache.length, 1)
|
|
||||||
assert.strictEqual(filesFromCache[0].name, assertFilename)
|
|
||||||
assert.strictEqual(filesFromCache[0].size, assertSize)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -292,37 +333,6 @@ t.describe('#resize', function() {
|
||||||
routes.siteCache.clear()
|
routes.siteCache.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
t.test('should call security verifyToken correctly', async function() {
|
|
||||||
reset()
|
|
||||||
|
|
||||||
let ctx = createContext({ req: { body: { }}})
|
|
||||||
const assertError = new Error('temp')
|
|
||||||
stubVerifyToken.rejects(assertError)
|
|
||||||
|
|
||||||
let err = await assert.isRejected(routes.resize(ctx))
|
|
||||||
|
|
||||||
assert.ok(stubVerifyToken.called)
|
|
||||||
assert.strictEqual(err, assertError)
|
|
||||||
assert.strictEqual(stubVerifyToken.firstCall[0], ctx)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should call upload correctly', async function() {
|
|
||||||
reset()
|
|
||||||
|
|
||||||
let ctx = createContext({ req: { body: { }}})
|
|
||||||
const assertSiteName = 'benshapiro'
|
|
||||||
const assertError = new Error('hello')
|
|
||||||
stubVerifyToken.resolves(assertSiteName)
|
|
||||||
stubUpload.rejects(assertError)
|
|
||||||
|
|
||||||
let err = await assert.isRejected(routes.resize(ctx))
|
|
||||||
|
|
||||||
assert.strictEqual(err, assertError)
|
|
||||||
assert.ok(stubUpload.called)
|
|
||||||
assert.strictEqual(stubUpload.firstCall[0], ctx)
|
|
||||||
assert.strictEqual(stubUpload.firstCall[1], assertSiteName)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should call security verifyBody correctly', async function() {
|
t.test('should call security verifyBody correctly', async function() {
|
||||||
reset()
|
reset()
|
||||||
|
|
||||||
|
@ -338,22 +348,6 @@ t.describe('#resize', function() {
|
||||||
assert.strictEqual(stubVerifyBody.firstCall[0], ctx)
|
assert.strictEqual(stubVerifyBody.firstCall[0], ctx)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('should otherwise return original in result', async function() {
|
|
||||||
reset()
|
|
||||||
|
|
||||||
let ctx = createContext({ req: { body: { }}})
|
|
||||||
const assertFilename = 'asdfsafd.png'
|
|
||||||
const assertSite = 'mario'
|
|
||||||
stubVerifyToken.resolves(assertSite)
|
|
||||||
stubUpload.resolves({ filename: assertFilename })
|
|
||||||
|
|
||||||
await routes.resize(ctx)
|
|
||||||
|
|
||||||
assert.notOk(stubSharp.called)
|
|
||||||
assert.strictEqual(ctx.body.original.filename, assertFilename)
|
|
||||||
assert.strictEqual(ctx.body.original.path, `/${assertSite}/${assertFilename}`)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.test('should call sharp correctly if items are specified', async function() {
|
t.test('should call sharp correctly if items are specified', async function() {
|
||||||
reset()
|
reset()
|
||||||
const assertKey = 'asdf'
|
const assertKey = 'asdf'
|
||||||
|
@ -381,8 +375,8 @@ t.describe('#resize', function() {
|
||||||
assert.match(stubSharpToFile.firstCall[0], new RegExp(`\/${assertSite}\/${assertFilename}_${assertKey}\.jpg`))
|
assert.match(stubSharpToFile.firstCall[0], new RegExp(`\/${assertSite}\/${assertFilename}_${assertKey}\.jpg`))
|
||||||
assert.notOk(stubSharpPng.called)
|
assert.notOk(stubSharpPng.called)
|
||||||
assert.notOk(stubSharpResize.called)
|
assert.notOk(stubSharpResize.called)
|
||||||
assert.strictEqual(ctx.body.original.filename, assertFilename + '.png')
|
assert.strictEqual(ctx.body.filename, assertFilename + '.png')
|
||||||
assert.strictEqual(ctx.body.original.path, `/${assertSite}/${assertFilename}.png`)
|
assert.strictEqual(ctx.body.path, `/${assertSite}/${assertFilename}.png`)
|
||||||
assert.strictEqual(ctx.body[assertKey].filename, `${assertFilename}_${assertKey}\.jpg`)
|
assert.strictEqual(ctx.body[assertKey].filename, `${assertFilename}_${assertKey}\.jpg`)
|
||||||
assert.strictEqual(ctx.body[assertKey].path, `/${assertSite}/${assertFilename}_${assertKey}.jpg`)
|
assert.strictEqual(ctx.body[assertKey].path, `/${assertSite}/${assertFilename}_${assertKey}.jpg`)
|
||||||
})
|
})
|
||||||
|
@ -423,16 +417,16 @@ t.describe('#resize', function() {
|
||||||
assert.notOk(stubSharpJpeg.called)
|
assert.notOk(stubSharpJpeg.called)
|
||||||
assert.ok(stubSharpResize.called)
|
assert.ok(stubSharpResize.called)
|
||||||
assert.strictEqual(stubSharpResize.firstCall[0], assertResize)
|
assert.strictEqual(stubSharpResize.firstCall[0], assertResize)
|
||||||
assert.strictEqual(ctx.body.original.filename, assertFilename + '.png')
|
assert.strictEqual(ctx.body.filename, assertFilename + '.png')
|
||||||
assert.strictEqual(ctx.body.original.path, `/${assertSite}/${assertFilename}.png`)
|
assert.strictEqual(ctx.body.path, `/${assertSite}/${assertFilename}.png`)
|
||||||
assert.strictEqual(ctx.body[assertKey].filename, `${assertFilename}_${assertKey}\.png`)
|
assert.strictEqual(ctx.body[assertKey].filename, `${assertFilename}_${assertKey}\.png`)
|
||||||
assert.strictEqual(ctx.body[assertKey].path, `/${assertSite}/${assertFilename}_${assertKey}.png`)
|
assert.strictEqual(ctx.body[assertKey].path, `/${assertSite}/${assertFilename}_${assertKey}.png`)
|
||||||
|
|
||||||
let filesFromCache = routes.filesCacheGet(assertSite)
|
let filesFromCache = routes.filesCacheGet(assertSite)
|
||||||
assert.strictEqual(filesFromCache.length, 2)
|
assert.strictEqual(filesFromCache.length, 2)
|
||||||
assert.strictEqual(filesFromCache[0].name, `${assertFilename}_${assertKey}\.png`)
|
assert.strictEqual(filesFromCache[0].filename, `${assertFilename}_${assertKey}\.png`)
|
||||||
assert.strictEqual(filesFromCache[0].size, 20)
|
assert.strictEqual(filesFromCache[0].size, 20)
|
||||||
assert.strictEqual(filesFromCache[1].name, assertFilename + '.png')
|
assert.strictEqual(filesFromCache[1].filename, assertFilename + '.png')
|
||||||
assert.strictEqual(filesFromCache[1].size, 10)
|
assert.strictEqual(filesFromCache[1].size, 10)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -513,6 +507,170 @@ t.describe('#resize', function() {
|
||||||
|
|
||||||
let filesFromCache = routes.filesCacheGet(assertSite)
|
let filesFromCache = routes.filesCacheGet(assertSite)
|
||||||
assert.strictEqual(filesFromCache.length, 1)
|
assert.strictEqual(filesFromCache.length, 1)
|
||||||
assert.strictEqual(filesFromCache[0].name, assertFilename)
|
assert.strictEqual(filesFromCache[0].filename, assertFilename)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
let basicUploadTestRoutes = [
|
||||||
|
'upload',
|
||||||
|
'resize',
|
||||||
|
'uploadNoPrefix'
|
||||||
|
]
|
||||||
|
|
||||||
|
basicUploadTestRoutes.forEach(function(name) {
|
||||||
|
t.describe(`#${name}() Base`, function() {
|
||||||
|
const stubVerify = stub()
|
||||||
|
const stubUpload = stub()
|
||||||
|
const stubStat = stub()
|
||||||
|
|
||||||
|
const routes = new MediaRoutes({
|
||||||
|
security: {
|
||||||
|
verifyToken: stubVerify,
|
||||||
|
verifyBody: stub(),
|
||||||
|
},
|
||||||
|
formidable: { uploadFile: stubUpload, },
|
||||||
|
fs: { stat: stubStat },
|
||||||
|
})
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
stubVerify.reset()
|
||||||
|
stubUpload.reset()
|
||||||
|
stubStat.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
t.test('should call security correctly', async function() {
|
||||||
|
reset()
|
||||||
|
|
||||||
|
let ctx = createContext({ req: { body: { } } })
|
||||||
|
const assertError = new Error('temp')
|
||||||
|
stubVerify.rejects(assertError)
|
||||||
|
|
||||||
|
let err = await assert.isRejected(routes[name](ctx))
|
||||||
|
|
||||||
|
assert.ok(stubVerify.called)
|
||||||
|
assert.strictEqual(err, assertError)
|
||||||
|
assert.strictEqual(stubVerify.firstCall[0], ctx)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('should call upload correctly', async function() {
|
||||||
|
reset()
|
||||||
|
|
||||||
|
let ctx = createContext({ req: { body: { } } })
|
||||||
|
const assertSiteName = 'benshapiro'
|
||||||
|
const assertError = new Error('hello')
|
||||||
|
stubVerify.resolves(assertSiteName)
|
||||||
|
stubUpload.rejects(assertError)
|
||||||
|
|
||||||
|
let err = await assert.isRejected(routes[name](ctx))
|
||||||
|
|
||||||
|
assert.ok(stubUpload.called)
|
||||||
|
assert.strictEqual(err, assertError)
|
||||||
|
assert.strictEqual(stubUpload.firstCall[0], ctx)
|
||||||
|
assert.strictEqual(stubUpload.firstCall[1], assertSiteName)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('should otherwise return the file in result', async function() {
|
||||||
|
reset()
|
||||||
|
|
||||||
|
let ctx = createContext({ req: { body: { } } })
|
||||||
|
const assertSize = 1241412
|
||||||
|
const assertFilename = 'asdfsafd'
|
||||||
|
const assertSite = 'mario'
|
||||||
|
stubVerify.resolves(assertSite)
|
||||||
|
stubUpload.resolves({ filename: assertFilename })
|
||||||
|
stubStat.resolves({ size: assertSize })
|
||||||
|
await routes[name](ctx)
|
||||||
|
|
||||||
|
assert.strictEqual(ctx.body.filename, assertFilename)
|
||||||
|
assert.strictEqual(ctx.body.path, `/${assertSite}/${assertFilename}`)
|
||||||
|
|
||||||
|
assert.ok(stubStat.called)
|
||||||
|
assert.strictEqual(stubStat.firstCall[0], `./public/${assertSite}/${assertFilename}`)
|
||||||
|
|
||||||
|
let filesFromCache = routes.filesCacheGet(assertSite)
|
||||||
|
assert.strictEqual(filesFromCache.length, 1)
|
||||||
|
assert.strictEqual(filesFromCache[0].filename, assertFilename)
|
||||||
|
assert.strictEqual(filesFromCache[0].size, assertSize)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.describe('#remove()', function() {
|
||||||
|
const stubVerify = stub()
|
||||||
|
const stubUnlink = stub()
|
||||||
|
const stubCacheRemove = stub()
|
||||||
|
|
||||||
|
const routes = new MediaRoutes({
|
||||||
|
security: {
|
||||||
|
verifyToken: stubVerify,
|
||||||
|
},
|
||||||
|
fs: { unlink: stubUnlink },
|
||||||
|
})
|
||||||
|
|
||||||
|
routes.filesCacheRemove = stubCacheRemove
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
stubVerify.reset()
|
||||||
|
stubUnlink.reset()
|
||||||
|
stubCacheRemove.reset()
|
||||||
|
stubUnlink.resolves(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.test('should call security correctly', async function() {
|
||||||
|
reset()
|
||||||
|
|
||||||
|
let ctx = createContext({ req: { body: { } } })
|
||||||
|
const assertError = new Error('temp')
|
||||||
|
stubVerify.rejects(assertError)
|
||||||
|
|
||||||
|
let err = await assert.isRejected(routes.remove(ctx))
|
||||||
|
|
||||||
|
assert.ok(stubVerify.called)
|
||||||
|
assert.strictEqual(err, assertError)
|
||||||
|
assert.strictEqual(stubVerify.firstCall[0], ctx)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('should call unlink correctly', async function() {
|
||||||
|
const assertSiteName = 'benshapiro'
|
||||||
|
const assertFilename = 'somefilename.png'
|
||||||
|
const assertErrorMessage = 'Noriyasu Agematsu Tensei'
|
||||||
|
const assertError = new Error(assertErrorMessage)
|
||||||
|
|
||||||
|
reset()
|
||||||
|
|
||||||
|
let ctx = createContext({ req: { body: { } } })
|
||||||
|
ctx.params.filename = assertFilename
|
||||||
|
stubVerify.resolves(assertSiteName)
|
||||||
|
stubUnlink.rejects(assertError)
|
||||||
|
|
||||||
|
let err = await assert.isRejected(routes.remove(ctx))
|
||||||
|
|
||||||
|
assert.ok(stubUnlink.called)
|
||||||
|
assert.ok(err instanceof HttpError)
|
||||||
|
assert.ok(err instanceof Error)
|
||||||
|
assert.strictEqual(err.status, 422)
|
||||||
|
assert.match(err.message, new RegExp(assertSiteName))
|
||||||
|
assert.match(err.message, new RegExp(assertFilename))
|
||||||
|
assert.match(err.message, new RegExp(assertErrorMessage))
|
||||||
|
|
||||||
|
assert.strictEqual(stubUnlink.firstCall[0], `./public/${assertSiteName}/${assertFilename}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('should otherwise return 204 status and remove from array', async function() {
|
||||||
|
const assertSiteName = 'benshapiro'
|
||||||
|
const assertFilename = 'somefilename.png'
|
||||||
|
|
||||||
|
reset()
|
||||||
|
|
||||||
|
let ctx = createContext({ req: { body: { } } })
|
||||||
|
ctx.params.filename = assertFilename
|
||||||
|
stubVerify.resolves(assertSiteName)
|
||||||
|
|
||||||
|
await routes.remove(ctx)
|
||||||
|
|
||||||
|
assert.strictEqual(ctx.status, 204)
|
||||||
|
assert.ok(stubCacheRemove.called)
|
||||||
|
assert.strictEqual(stubCacheRemove.firstCall[0], assertSiteName)
|
||||||
|
assert.strictEqual(stubCacheRemove.firstCall[1], assertFilename)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -173,21 +173,25 @@ t.describe('#verifyBody()', function() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('should fail if an item has the name original', function() {
|
let testInvalidNames = ['filename', 'path']
|
||||||
let ctx = createContext({ req: { body: {
|
|
||||||
original: {}
|
|
||||||
} } })
|
|
||||||
|
|
||||||
assert.throws(function() { verifyBody(ctx) }, function(err) {
|
testInvalidNames.forEach(function(invalidName) {
|
||||||
assert.ok(err instanceof HttpError)
|
t.test(`should fail if an item has the name ${invalidName}`, function() {
|
||||||
assert.ok(err instanceof Error)
|
let ctx = createContext({ req: { body: {
|
||||||
assert.strictEqual(err.status, 422)
|
[invalidName]: {}
|
||||||
assert.match(err.message, /body/i)
|
} } })
|
||||||
assert.match(err.message, /name/i)
|
|
||||||
assert.match(err.message, /original/i)
|
assert.throws(function() { verifyBody(ctx) }, function(err) {
|
||||||
assert.match(err.message, /allowed/i)
|
assert.ok(err instanceof HttpError)
|
||||||
return true
|
assert.ok(err instanceof Error)
|
||||||
}, 'should fail if body item has the name original')
|
assert.strictEqual(err.status, 422)
|
||||||
|
assert.match(err.message, /body/i)
|
||||||
|
assert.match(err.message, /name/i)
|
||||||
|
assert.match(err.message, new RegExp(invalidName))
|
||||||
|
assert.match(err.message, /allowed/i)
|
||||||
|
return true
|
||||||
|
}, 'should fail if body item has the name original')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('should require format string present in item', function() {
|
t.test('should require format string present in item', function() {
|
||||||
|
|
Loading…
Reference in a new issue