Compare commits
12 Commits
Author | SHA1 | Date |
---|---|---|
Jonatan Nilsson | d1bab7a929 | |
Jonatan Nilsson | afde7fb89a | |
Jonatan Nilsson | bf522833a8 | |
Jonatan Nilsson | 5d57db6f32 | |
Jonatan Nilsson | 8d0265d0fe | |
Jonatan Nilsson | 81c9603ea0 | |
Jonatan Nilsson | ec81950c42 | |
Jonatan Nilsson | 9e6d6f505d | |
Jonatan Nilsson | a578e2fd80 | |
Jonatan Nilsson | 75929e7c17 | |
Jonatan Nilsson | b931dfb784 | |
Jonatan Nilsson | f1be7e0d79 |
|
@ -60,9 +60,6 @@ typings/
|
||||||
# Local development config file
|
# Local development config file
|
||||||
config/*.json
|
config/*.json
|
||||||
|
|
||||||
# Public folder should be ignored
|
|
||||||
public/*
|
|
||||||
|
|
||||||
# lol
|
# lol
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
|
||||||
|
|
|
@ -39,10 +39,21 @@ export function uploadFile(ctx, siteName, noprefix = false) {
|
||||||
form.uploadDir = `${config.get('uploadFolder')}/${siteName}`
|
form.uploadDir = `${config.get('uploadFolder')}/${siteName}`
|
||||||
form.maxFileSize = config.get('fileSize')
|
form.maxFileSize = config.get('fileSize')
|
||||||
|
|
||||||
|
let siteSize = config.get(`sites:${siteName}:fileSize`)
|
||||||
|
|
||||||
|
if (siteSize && typeof(siteSize) === 'number') {
|
||||||
|
form.maxFileSize = siteSize
|
||||||
|
}
|
||||||
|
|
||||||
form.parse(ctx.req, function(err, fields, files) {
|
form.parse(ctx.req, function(err, fields, files) {
|
||||||
if (err) return rej(err)
|
if (err) return rej(err)
|
||||||
if (!files || !files.file) return rej(new HttpError(422, 'File in body was missing'))
|
if (!files || !files.file) return rej(new HttpError(422, 'File in body was missing'))
|
||||||
let file = files.file
|
let file = files.file
|
||||||
|
let filename = file.name.replace(/ /g, '_')
|
||||||
|
.replace(/&/g, 'and')
|
||||||
|
.replace(/'/g, '')
|
||||||
|
.replace(/"/g, '')
|
||||||
|
.replace(/\?/g, '')
|
||||||
|
|
||||||
Object.keys(fields).forEach(function(key) {
|
Object.keys(fields).forEach(function(key) {
|
||||||
try {
|
try {
|
||||||
|
@ -51,14 +62,14 @@ export function uploadFile(ctx, siteName, noprefix = false) {
|
||||||
})
|
})
|
||||||
ctx.req.body = fields
|
ctx.req.body = fields
|
||||||
|
|
||||||
if (!noprefix || fs.existsSync(`${config.get('uploadFolder')}/${siteName}/${prefix}${file.name}`)) {
|
if (!noprefix || fs.existsSync(`${config.get('uploadFolder')}/${siteName}/${prefix}${filename}`)) {
|
||||||
prefix = getPrefix()
|
prefix = getPrefix()
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.rename(files.file.path, `${config.get('uploadFolder')}/${siteName}/${prefix}${file.name}`, function(err) {
|
fs.rename(files.file.path, `${config.get('uploadFolder')}/${siteName}/${prefix}${filename}`, function(err) {
|
||||||
if (err) return rej(err)
|
if (err) return rej(err)
|
||||||
file.path = `${config.get('uploadFolder')}/${siteName}/${prefix}${file.name}`
|
file.path = `${config.get('uploadFolder')}/${siteName}/${prefix}${filename}`
|
||||||
file.filename = `${prefix}${file.name}`
|
file.filename = `${prefix}${filename}`
|
||||||
|
|
||||||
return res(file)
|
return res(file)
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,7 +2,7 @@ import path from 'path'
|
||||||
import sharp from 'sharp'
|
import sharp from 'sharp'
|
||||||
import fs from 'fs/promises'
|
import fs from 'fs/promises'
|
||||||
import config from '../config.mjs'
|
import config from '../config.mjs'
|
||||||
import { HttpError } from 'flaska'
|
import { HttpError, CorsHandler } from 'flaska'
|
||||||
import * as security from './security.mjs'
|
import * as security from './security.mjs'
|
||||||
import * as formidable from './formidable.mjs'
|
import * as formidable from './formidable.mjs'
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ export default class MediaRoutes {
|
||||||
})
|
})
|
||||||
this.siteCache = new Map()
|
this.siteCache = new Map()
|
||||||
this.collator = new Intl.Collator('is-IS', { numeric: false, sensitivity: 'accent' })
|
this.collator = new Intl.Collator('is-IS', { numeric: false, sensitivity: 'accent' })
|
||||||
|
this.cors = CorsHandler({ allowedOrigins: ['*'] })
|
||||||
}
|
}
|
||||||
|
|
||||||
register(server) {
|
register(server) {
|
||||||
|
@ -30,6 +31,7 @@ export default class MediaRoutes {
|
||||||
server.flaska.post('/media/resize', [server.queryHandler()], this.resize.bind(this))
|
server.flaska.post('/media/resize', [server.queryHandler()], this.resize.bind(this))
|
||||||
server.flaska.post('/media/resize/:filename', [server.queryHandler(), server.jsonHandler()], this.resizeExisting.bind(this))
|
server.flaska.post('/media/resize/:filename', [server.queryHandler(), server.jsonHandler()], this.resizeExisting.bind(this))
|
||||||
server.flaska.delete('/media/:filename', [server.queryHandler()], this.remove.bind(this))
|
server.flaska.delete('/media/:filename', [server.queryHandler()], this.remove.bind(this))
|
||||||
|
server.flaska.options('/::path', [server.queryHandler(), this.security.verifyCorsEnabled], this.cors)
|
||||||
}
|
}
|
||||||
|
|
||||||
init(server) {
|
init(server) {
|
||||||
|
@ -89,20 +91,32 @@ export default class MediaRoutes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getSite(ctx) {
|
||||||
|
let site = this.security.verifyToken(ctx)
|
||||||
|
if (this.security.hasCors(site)) {
|
||||||
|
this.cors(ctx)
|
||||||
|
}
|
||||||
|
return site
|
||||||
|
}
|
||||||
|
|
||||||
async listFiles(ctx) {
|
async listFiles(ctx) {
|
||||||
let site = await this.security.verifyToken(ctx)
|
let site = this.getSite(ctx)
|
||||||
|
|
||||||
ctx.body = this.filesCacheGet(site)
|
ctx.body = this.filesCacheGet(site)
|
||||||
}
|
}
|
||||||
|
|
||||||
async listPublicFiles(ctx) {
|
async listPublicFiles(ctx) {
|
||||||
this.security.throwIfNotPublic(ctx.params.site)
|
this.security.throwIfNotPublic(ctx.params.site)
|
||||||
|
|
||||||
|
if (this.security.hasCors(ctx.params.site)) {
|
||||||
|
this.cors(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
ctx.body = this.filesCacheGet(ctx.params.site)
|
ctx.body = this.filesCacheGet(ctx.params.site)
|
||||||
}
|
}
|
||||||
|
|
||||||
async upload(ctx, noprefix = false) {
|
async upload(ctx, noprefix = false) {
|
||||||
let site = await this.security.verifyToken(ctx)
|
let site = this.getSite(ctx)
|
||||||
ctx.state.site = site
|
ctx.state.site = site
|
||||||
|
|
||||||
let result = await this.formidable.uploadFile(ctx, ctx.state.site, noprefix)
|
let result = await this.formidable.uploadFile(ctx, ctx.state.site, noprefix)
|
||||||
|
@ -135,7 +149,7 @@ export default class MediaRoutes {
|
||||||
'extend',
|
'extend',
|
||||||
]
|
]
|
||||||
|
|
||||||
await Promise.all(keys.map(key => {
|
await Promise.all(keys.filter(key => ctx.req.body[key]).map(key => {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
let item = ctx.req.body[key]
|
let item = ctx.req.body[key]
|
||||||
|
@ -181,7 +195,7 @@ export default class MediaRoutes {
|
||||||
}
|
}
|
||||||
|
|
||||||
async resizeExisting(ctx) {
|
async resizeExisting(ctx) {
|
||||||
let site = await this.security.verifyToken(ctx)
|
let site = this.getSite(ctx)
|
||||||
ctx.state.site = site
|
ctx.state.site = site
|
||||||
|
|
||||||
ctx.body = {}
|
ctx.body = {}
|
||||||
|
@ -195,13 +209,20 @@ export default class MediaRoutes {
|
||||||
}
|
}
|
||||||
|
|
||||||
async remove(ctx) {
|
async remove(ctx) {
|
||||||
let site = await this.security.verifyToken(ctx)
|
let site = this.getSite(ctx)
|
||||||
|
|
||||||
this.filesCacheRemove(site, ctx.params.filename)
|
this.filesCacheRemove(site, ctx.params.filename)
|
||||||
|
|
||||||
await this.fs.unlink(`${config.get('uploadFolder')}/${site}/${ctx.params.filename}`)
|
let root = path.join(config.get('uploadFolder'), site)
|
||||||
|
var unlinkPath = path.join(root, decodeURIComponent(ctx.params.filename))
|
||||||
|
|
||||||
|
if (unlinkPath.indexOf(root) !== 0) {
|
||||||
|
throw new HttpError(403, `Error removing ${unlinkPath}: Traversing folder is not allowed`)
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.fs.unlink(unlinkPath)
|
||||||
.catch(function(err) {
|
.catch(function(err) {
|
||||||
throw new HttpError(422, `Error removing ${site}/${ctx.params.filename}: ${err.message}`)
|
throw new HttpError(422, `Error removing ${unlinkPath}: ${err.message}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx.status = 204
|
ctx.status = 204
|
||||||
|
|
|
@ -25,6 +25,24 @@ export function verifyToken(ctx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function hasCors(site) {
|
||||||
|
let sites = config.get('sites')
|
||||||
|
return sites[site]?.cors === true
|
||||||
|
}
|
||||||
|
|
||||||
|
export function verifyCorsEnabled(ctx) {
|
||||||
|
let site
|
||||||
|
try {
|
||||||
|
site = verifyToken(ctx)
|
||||||
|
} catch (err) {
|
||||||
|
throw new HttpError(404)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasCors(site)) {
|
||||||
|
throw new HttpError(404)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function throwIfNotPublic(site) {
|
export function throwIfNotPublic(site) {
|
||||||
let sites = config.get('sites')
|
let sites = config.get('sites')
|
||||||
if (!sites[site] || sites[site].public !== true) {
|
if (!sites[site] || sites[site].public !== true) {
|
||||||
|
@ -52,8 +70,9 @@ export function verifyBody(ctx) {
|
||||||
}
|
}
|
||||||
let item = ctx.req.body[key]
|
let item = ctx.req.body[key]
|
||||||
|
|
||||||
|
if (item == null) continue
|
||||||
|
|
||||||
if (typeof(item) !== 'object'
|
if (typeof(item) !== 'object'
|
||||||
|| !item
|
|
||||||
|| Array.isArray(item)) {
|
|| Array.isArray(item)) {
|
||||||
throw new HttpError(422, `Body item ${key} was not valid`)
|
throw new HttpError(422, `Body item ${key} was not valid`)
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ export default class Server {
|
||||||
})
|
})
|
||||||
|
|
||||||
let healthChecks = 0
|
let healthChecks = 0
|
||||||
let healthCollectLimit = 10
|
let healthCollectLimit = 60 * 60 * 12
|
||||||
|
|
||||||
this.flaska.after(function(ctx) {
|
this.flaska.after(function(ctx) {
|
||||||
let ended = performance.now() - ctx.__started
|
let ended = performance.now() - ctx.__started
|
||||||
|
@ -60,7 +60,7 @@ export default class Server {
|
||||||
|
|
||||||
if (ctx.url === '/health') {
|
if (ctx.url === '/health') {
|
||||||
healthChecks++
|
healthChecks++
|
||||||
if (healthChecks > healthCollectLimit) {
|
if (healthChecks >= healthCollectLimit) {
|
||||||
ctx.log[level]({
|
ctx.log[level]({
|
||||||
duration: Math.round(ended),
|
duration: Math.round(ended),
|
||||||
status: ctx.status,
|
status: ctx.status,
|
||||||
|
|
|
@ -66,7 +66,7 @@ on_success:
|
||||||
https://git.nfp.is/api/v1/repos/$APPVEYOR_REPO_NAME/releases/$RELEASE_ID/assets
|
https://git.nfp.is/api/v1/repos/$APPVEYOR_REPO_NAME/releases/$RELEASE_ID/assets
|
||||||
|
|
||||||
echo "Deplying to production"
|
echo "Deplying to production"
|
||||||
curl -X POST http://192.168.93.50:4010/update/storageupload
|
curl -X POST http://192.168.93.51:4010/update/storageupload
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# on build failure
|
# on build failure
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "storage-upload",
|
"name": "storage-upload",
|
||||||
"version": "2.2.4",
|
"version": "2.2.12",
|
||||||
"description": "Micro service for uploading and image resizing files to a storage server.",
|
"description": "Micro service for uploading and image resizing files to a storage server.",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/nfp-projects/storage-upload#readme",
|
"homepage": "https://github.com/nfp-projects/storage-upload#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"flaska": "^1.2.3",
|
"flaska": "^1.3.5",
|
||||||
"formidable": "^1.2.2",
|
"formidable": "^1.2.2",
|
||||||
"nconf-lite": "^2.0.0",
|
"nconf-lite": "^2.0.0",
|
||||||
"sharp": "^0.30.3"
|
"sharp": "^0.30.3"
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
|
@ -26,6 +26,9 @@ Client.prototype.customRequest = function(method = 'GET', path, body, options) {
|
||||||
host: urlObj.hostname,
|
host: urlObj.hostname,
|
||||||
port: Number(urlObj.port),
|
port: Number(urlObj.port),
|
||||||
path: urlObj.pathname + urlObj.search,
|
path: urlObj.pathname + urlObj.search,
|
||||||
|
headers: {
|
||||||
|
origin: 'http://localhost'
|
||||||
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const req = http.request(opts)
|
const req = http.request(opts)
|
||||||
|
@ -44,7 +47,9 @@ Client.prototype.customRequest = function(method = 'GET', path, body, options) {
|
||||||
})
|
})
|
||||||
|
|
||||||
res.on('end', function () {
|
res.on('end', function () {
|
||||||
if (!output) return resolve(null)
|
let headers = opts.includeHeaders ? res.headers : null
|
||||||
|
|
||||||
|
if (!output) return resolve(headers ? { headers } : null)
|
||||||
try {
|
try {
|
||||||
output = JSON.parse(output)
|
output = JSON.parse(output)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -56,29 +61,29 @@ Client.prototype.customRequest = function(method = 'GET', path, body, options) {
|
||||||
err.status = output.status
|
err.status = output.status
|
||||||
return reject(err)
|
return reject(err)
|
||||||
}
|
}
|
||||||
resolve(output)
|
resolve(headers ? { headers, output } : output)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
req.end()
|
req.end()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
Client.prototype.get = function(url = '/') {
|
Client.prototype.get = function(url = '/', options) {
|
||||||
return this.customRequest('GET', url, null)
|
return this.customRequest('GET', url, null, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
Client.prototype.post = function(url = '/', body = {}) {
|
Client.prototype.post = function(url = '/', body = {}, options) {
|
||||||
let parsed = JSON.stringify(body)
|
let parsed = JSON.stringify(body)
|
||||||
return this.customRequest('POST', url, parsed, {
|
return this.customRequest('POST', url, parsed, defaults(options, {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Content-Length': parsed.length,
|
'Content-Length': parsed.length,
|
||||||
},
|
},
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
Client.prototype.del = function(url = '/', body = {}) {
|
Client.prototype.del = function(url = '/', body = {}, options) {
|
||||||
return this.customRequest('DELETE', url, JSON.stringify(body))
|
return this.customRequest('DELETE', url, JSON.stringify(body), options)
|
||||||
}
|
}
|
||||||
|
|
||||||
const random = (length = 8) => {
|
const random = (length = 8) => {
|
||||||
|
@ -92,7 +97,7 @@ const random = (length = 8) => {
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
Client.prototype.upload = function(url, file, method = 'POST', body = {}) {
|
Client.prototype.upload = function(url, file, method = 'POST', body = {}, options) {
|
||||||
return fs.readFile(file).then(data => {
|
return fs.readFile(file).then(data => {
|
||||||
const crlf = '\r\n'
|
const crlf = '\r\n'
|
||||||
const filename = path.basename(file)
|
const filename = path.basename(file)
|
||||||
|
@ -114,12 +119,12 @@ Client.prototype.upload = function(url, file, method = 'POST', body = {}) {
|
||||||
Buffer.from(`${crlf}--${boundary}--`),
|
Buffer.from(`${crlf}--${boundary}--`),
|
||||||
])
|
])
|
||||||
|
|
||||||
return this.customRequest(method, url, multipartBody, {
|
return this.customRequest(method, url, multipartBody, defaults(options, {
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'multipart/form-data; boundary=' + boundary,
|
'Content-Type': 'multipart/form-data; boundary=' + boundary,
|
||||||
'Content-Length': multipartBody.length,
|
'Content-Length': multipartBody.length,
|
||||||
},
|
},
|
||||||
})
|
}))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,12 +35,25 @@ export function startServer() {
|
||||||
"default@HS256": "asdf1234"
|
"default@HS256": "asdf1234"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"development_cors": {
|
||||||
|
"keys": {
|
||||||
|
"default@HS256": "asdf1234"
|
||||||
|
},
|
||||||
|
"cors": true
|
||||||
|
},
|
||||||
"existing": {
|
"existing": {
|
||||||
"public": true,
|
"public": true,
|
||||||
"keys": {
|
"keys": {
|
||||||
"default@HS256": "asdf1234"
|
"default@HS256": "asdf1234"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"existing_cors": {
|
||||||
|
"public": true,
|
||||||
|
"keys": {
|
||||||
|
"default@HS256": "asdf1234"
|
||||||
|
},
|
||||||
|
"cors": true
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.0 MiB |
|
@ -15,7 +15,7 @@ function resolve(file) {
|
||||||
|
|
||||||
const currYear = new Date().getFullYear().toString()
|
const currYear = new Date().getFullYear().toString()
|
||||||
|
|
||||||
t.describe('Media (API)', () => {
|
t.timeout(10000).describe('Media (API)', () => {
|
||||||
let client
|
let client
|
||||||
let secret = 'asdf1234'
|
let secret = 'asdf1234'
|
||||||
let testFiles = []
|
let testFiles = []
|
||||||
|
@ -34,7 +34,82 @@ t.describe('Media (API)', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.timeout(10000).describe('POST /media', function temp() {
|
t.describe('OPTIONS', function() {
|
||||||
|
t.test('should fail options for every single route', async () => {
|
||||||
|
const testPaths = [
|
||||||
|
'/media',
|
||||||
|
'/media/:site',
|
||||||
|
'/media',
|
||||||
|
'/media/noprefix',
|
||||||
|
'/media/resize',
|
||||||
|
'/media/resize/:filename',
|
||||||
|
'/media/:filename',
|
||||||
|
]
|
||||||
|
|
||||||
|
for (let path of testPaths) {
|
||||||
|
let err = await assert.isRejected(
|
||||||
|
client.customRequest('OPTIONS', path, null)
|
||||||
|
)
|
||||||
|
assert.strictEqual(err.status, 404)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('should fail options for every single route even with token', async () => {
|
||||||
|
let token = encode(null, { iss: 'development' }, secret)
|
||||||
|
const testPaths = [
|
||||||
|
'/media',
|
||||||
|
'/media/:site',
|
||||||
|
'/media',
|
||||||
|
'/media/noprefix',
|
||||||
|
'/media/resize',
|
||||||
|
'/media/resize/:filename',
|
||||||
|
'/media/:filename',
|
||||||
|
]
|
||||||
|
|
||||||
|
for (let path of testPaths) {
|
||||||
|
let err = await assert.isRejected(
|
||||||
|
client.customRequest('OPTIONS', path + `?token=${token}`, null)
|
||||||
|
)
|
||||||
|
assert.strictEqual(err.status, 404)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('should work if specified has cors enabled', async () => {
|
||||||
|
const assertOrigin = 'http://localhost:9999'
|
||||||
|
let token = encode(null, { iss: 'development_cors' }, secret)
|
||||||
|
const testPaths = [
|
||||||
|
'/media',
|
||||||
|
'/media/:site',
|
||||||
|
'/media',
|
||||||
|
'/media/noprefix',
|
||||||
|
'/media/resize',
|
||||||
|
'/media/resize/:filename',
|
||||||
|
'/media/:filename',
|
||||||
|
]
|
||||||
|
|
||||||
|
for (let path of testPaths) {
|
||||||
|
let res = await client.customRequest('OPTIONS', path + `?token=${token}`, null, {
|
||||||
|
includeHeaders: true,
|
||||||
|
headers: { origin: assertOrigin, 'access-control-request-method': 'POST' },
|
||||||
|
})
|
||||||
|
assert.strictEqual(res.headers['access-control-allow-origin'], assertOrigin)
|
||||||
|
assert.match(res.headers['access-control-allow-methods'], /post/i)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.describe('POST /media', function temp() {
|
||||||
|
let config
|
||||||
|
|
||||||
|
t.before(async () => {
|
||||||
|
config = (await import('../../api/config.mjs')).default
|
||||||
|
})
|
||||||
|
|
||||||
|
t.afterEach(function() {
|
||||||
|
config.sources[2].store.fileSize = 524288000
|
||||||
|
delete config.sources[1].store.sites.development.fileSize
|
||||||
|
})
|
||||||
|
|
||||||
t.test('should require authentication', async () => {
|
t.test('should require authentication', async () => {
|
||||||
resetLog()
|
resetLog()
|
||||||
assert.strictEqual(log.error.callCount, 0)
|
assert.strictEqual(log.error.callCount, 0)
|
||||||
|
@ -85,6 +160,108 @@ t.describe('Media (API)', () => {
|
||||||
t.test('should upload file and create file', async () => {
|
t.test('should upload file and create file', async () => {
|
||||||
let token = encode(null, { iss: 'development' }, secret)
|
let token = encode(null, { iss: 'development' }, secret)
|
||||||
|
|
||||||
|
let data = await assert.isFulfilled(
|
||||||
|
client.upload(
|
||||||
|
`/media?token=${token}`,
|
||||||
|
resolve('test.png'),
|
||||||
|
'POST',
|
||||||
|
{ },
|
||||||
|
{ includeHeaders: true }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
testFiles.push(data?.output?.path)
|
||||||
|
|
||||||
|
assert.ok(data.output)
|
||||||
|
assert.ok(data.output.filename)
|
||||||
|
assert.ok(data.output.filename.startsWith(currYear))
|
||||||
|
assert.ok(data.output.path)
|
||||||
|
|
||||||
|
let stats = await Promise.all([
|
||||||
|
fs.stat(resolve('test.png')),
|
||||||
|
fs.stat(resolve(`../../public/${data.output.path}`)),
|
||||||
|
])
|
||||||
|
assert.strictEqual(stats[0].size, stats[1].size)
|
||||||
|
|
||||||
|
let img = await sharp(resolve(`../../public/${data.output.path}`)).metadata()
|
||||||
|
assert.strictEqual(img.width, 600)
|
||||||
|
assert.strictEqual(img.height, 700)
|
||||||
|
assert.strictEqual(img.format, 'png')
|
||||||
|
assert.notOk(data.headers['access-control-allow-origin'])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('should upload file and create file and return cors if site has cors', async () => {
|
||||||
|
const assertOrigin = 'http://localhost:9000'
|
||||||
|
let token = encode(null, { iss: 'development_cors' }, secret)
|
||||||
|
|
||||||
|
let data = await assert.isFulfilled(
|
||||||
|
client.upload(
|
||||||
|
`/media?token=${token}`,
|
||||||
|
resolve('test.png'),
|
||||||
|
'POST',
|
||||||
|
{ },
|
||||||
|
{
|
||||||
|
includeHeaders: true,
|
||||||
|
headers: { origin: assertOrigin, 'access-control-request-method': 'POST' },
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
testFiles.push(data.output?.path)
|
||||||
|
|
||||||
|
assert.ok(data.output)
|
||||||
|
assert.ok(data.output.filename)
|
||||||
|
assert.ok(data.output.filename.startsWith(currYear))
|
||||||
|
assert.ok(data.output.path)
|
||||||
|
|
||||||
|
let stats = await Promise.all([
|
||||||
|
fs.stat(resolve('test.png')),
|
||||||
|
fs.stat(resolve(`../../public/${data.output.path}`)),
|
||||||
|
])
|
||||||
|
assert.strictEqual(stats[0].size, stats[1].size)
|
||||||
|
|
||||||
|
let img = await sharp(resolve(`../../public/${data.output.path}`)).metadata()
|
||||||
|
assert.strictEqual(img.width, 600)
|
||||||
|
assert.strictEqual(img.height, 700)
|
||||||
|
assert.strictEqual(img.format, 'png')
|
||||||
|
assert.strictEqual(data.headers['access-control-allow-origin'], assertOrigin)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('should properly replace spaces and other stuff', async () => {
|
||||||
|
let token = encode(null, { iss: 'development' }, secret)
|
||||||
|
|
||||||
|
let data = await assert.isFulfilled(
|
||||||
|
client.upload(
|
||||||
|
`/media?token=${token}`,
|
||||||
|
resolve('A stray\'d cat asked to go & inside a shop during heatwave [oBv38cS-MbM].mp4_snapshot_00.00.164.png')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
testFiles.push(data.path)
|
||||||
|
|
||||||
|
assert.ok(data)
|
||||||
|
assert.ok(data.filename)
|
||||||
|
assert.ok(data.filename.startsWith(currYear))
|
||||||
|
assert.ok(data.path)
|
||||||
|
|
||||||
|
assert.notOk(data.filename.includes(' '))
|
||||||
|
assert.ok(data.filename.includes('A_strayd_cat_asked_to_go_and_inside_a_shop_during_heatwave_[oBv38cS-MbM].mp4_snapshot_00.00.164.png'))
|
||||||
|
|
||||||
|
let stats = await Promise.all([
|
||||||
|
fs.stat(resolve('A stray\'d cat asked to go & inside a shop during heatwave [oBv38cS-MbM].mp4_snapshot_00.00.164.png')),
|
||||||
|
fs.stat(resolve(`../../public/${data.path}`)),
|
||||||
|
])
|
||||||
|
assert.strictEqual(stats[0].size, stats[1].size)
|
||||||
|
|
||||||
|
let img = await sharp(resolve(`../../public/${data.path}`)).metadata()
|
||||||
|
assert.strictEqual(img.width, 1280)
|
||||||
|
assert.strictEqual(img.height, 720)
|
||||||
|
assert.strictEqual(img.format, 'png')
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('should return cors for cors-enabled sites', async () => {
|
||||||
|
let token = encode(null, { iss: 'development' }, secret)
|
||||||
|
|
||||||
let data = await assert.isFulfilled(
|
let data = await assert.isFulfilled(
|
||||||
client.upload(
|
client.upload(
|
||||||
`/media?token=${token}`,
|
`/media?token=${token}`,
|
||||||
|
@ -110,9 +287,38 @@ t.describe('Media (API)', () => {
|
||||||
assert.strictEqual(img.height, 700)
|
assert.strictEqual(img.height, 700)
|
||||||
assert.strictEqual(img.format, 'png')
|
assert.strictEqual(img.format, 'png')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.test('should support site-specific fileSize', async () => {
|
||||||
|
let token = encode(null, { iss: 'development' }, secret)
|
||||||
|
|
||||||
|
config.sources[2].store.fileSize = 1000
|
||||||
|
|
||||||
|
let err = await assert.isRejected(
|
||||||
|
client.upload(
|
||||||
|
`/media?token=${token}`,
|
||||||
|
resolve('test.png')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert.match(err.message, /maxFileSize exceeded/)
|
||||||
|
|
||||||
|
config.sources[1].store.sites.development.fileSize = 524288000
|
||||||
|
|
||||||
|
let data = await assert.isFulfilled(
|
||||||
|
client.upload(
|
||||||
|
`/media?token=${token}`,
|
||||||
|
resolve('test.png')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
testFiles.push(data.path)
|
||||||
|
|
||||||
|
assert.ok(data)
|
||||||
|
assert.ok(data.filename)
|
||||||
|
assert.ok(data.filename.startsWith(currYear))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.timeout(10000).describe('POST /media/noprefix', function temp() {
|
t.describe('POST /media/noprefix', function temp() {
|
||||||
t.test('should require authentication', async () => {
|
t.test('should require authentication', async () => {
|
||||||
resetLog()
|
resetLog()
|
||||||
assert.strictEqual(log.error.callCount, 0)
|
assert.strictEqual(log.error.callCount, 0)
|
||||||
|
@ -166,15 +372,18 @@ t.describe('Media (API)', () => {
|
||||||
let data = await assert.isFulfilled(
|
let data = await assert.isFulfilled(
|
||||||
client.upload(
|
client.upload(
|
||||||
`/media/noprefix?token=${token}`,
|
`/media/noprefix?token=${token}`,
|
||||||
resolve('test.png')
|
resolve('test.png'),
|
||||||
|
'POST',
|
||||||
|
{ },
|
||||||
|
{ includeHeaders: true }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
testFiles.push(data.path)
|
testFiles.push(data?.output?.path)
|
||||||
|
|
||||||
assert.ok(data)
|
assert.ok(data.output)
|
||||||
assert.strictEqual(data.filename, 'test.png')
|
assert.strictEqual(data.output.filename, 'test.png')
|
||||||
assert.ok(data.path)
|
assert.ok(data.output.path)
|
||||||
|
|
||||||
let stats = await Promise.all([
|
let stats = await Promise.all([
|
||||||
fs.stat(resolve('test.png')),
|
fs.stat(resolve('test.png')),
|
||||||
|
@ -186,6 +395,44 @@ t.describe('Media (API)', () => {
|
||||||
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')
|
||||||
|
assert.notOk(data.headers['access-control-allow-origin'])
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
t.test('should upload and create file with no prefix', async () => {
|
||||||
|
const assertOrigin = 'http://localhost:9000'
|
||||||
|
let token = encode(null, { iss: 'development_cors' }, secret)
|
||||||
|
|
||||||
|
let data = await assert.isFulfilled(
|
||||||
|
client.upload(
|
||||||
|
`/media/noprefix?token=${token}`,
|
||||||
|
resolve('test.png'),
|
||||||
|
'POST',
|
||||||
|
{ },
|
||||||
|
{
|
||||||
|
includeHeaders: true,
|
||||||
|
headers: { origin: assertOrigin, 'access-control-request-method': 'POST' },
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
testFiles.push(data?.output?.path)
|
||||||
|
|
||||||
|
assert.ok(data.output)
|
||||||
|
assert.strictEqual(data.output.filename, 'test.png')
|
||||||
|
assert.ok(data.output.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')
|
||||||
|
assert.strictEqual(data.headers['access-control-allow-origin'], assertOrigin)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('should upload and create file with prefix if exists', async () => {
|
t.test('should upload and create file with prefix if exists', async () => {
|
||||||
|
@ -228,7 +475,7 @@ t.describe('Media (API)', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.timeout(10000).describe('POST /media/resize', function temp() {
|
t.describe('POST /media/resize', function temp() {
|
||||||
t.test('should require authentication', async () => {
|
t.test('should require authentication', async () => {
|
||||||
resetLog()
|
resetLog()
|
||||||
assert.strictEqual(log.error.callCount, 0)
|
assert.strictEqual(log.error.callCount, 0)
|
||||||
|
@ -284,27 +531,66 @@ t.describe('Media (API)', () => {
|
||||||
`/media/resize?token=${token}`,
|
`/media/resize?token=${token}`,
|
||||||
resolve('test.png'),
|
resolve('test.png'),
|
||||||
'POST',
|
'POST',
|
||||||
{ }
|
{ },
|
||||||
|
{ includeHeaders: true }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
testFiles.push(data.path)
|
testFiles.push(data?.output?.path)
|
||||||
|
|
||||||
assert.ok(data)
|
assert.ok(data.output)
|
||||||
assert.ok(data.filename)
|
assert.ok(data.output.filename)
|
||||||
assert.ok(data.filename.startsWith(currYear))
|
assert.ok(data.output.filename.startsWith(currYear))
|
||||||
assert.ok(data.path)
|
assert.ok(data.output.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.output.path}`)),
|
||||||
])
|
])
|
||||||
assert.strictEqual(stats[0].size, stats[1].size)
|
assert.strictEqual(stats[0].size, stats[1].size)
|
||||||
|
|
||||||
let img = await sharp(resolve(`../../public/${data.path}`)).metadata()
|
let img = await sharp(resolve(`../../public/${data.output.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')
|
||||||
|
assert.notOk(data.headers['access-control-allow-origin'])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('should upload file and create file and return cors if site has cors', async () => {
|
||||||
|
const assertOrigin = 'http://localhost:9000'
|
||||||
|
let token = encode(null, { iss: 'development_cors' }, secret)
|
||||||
|
|
||||||
|
let data = await assert.isFulfilled(
|
||||||
|
client.upload(
|
||||||
|
`/media/resize?token=${token}`,
|
||||||
|
resolve('test.png'),
|
||||||
|
'POST',
|
||||||
|
{ },
|
||||||
|
{
|
||||||
|
includeHeaders: true,
|
||||||
|
headers: { origin: assertOrigin, 'access-control-request-method': 'POST' },
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
testFiles.push(data?.output?.path)
|
||||||
|
|
||||||
|
assert.ok(data.output)
|
||||||
|
assert.ok(data.output.filename)
|
||||||
|
assert.ok(data.output.filename.startsWith(currYear))
|
||||||
|
assert.ok(data.output.path)
|
||||||
|
|
||||||
|
let stats = await Promise.all([
|
||||||
|
fs.stat(resolve('test.png')),
|
||||||
|
fs.stat(resolve(`../../public/${data.output.path}`)),
|
||||||
|
])
|
||||||
|
assert.strictEqual(stats[0].size, stats[1].size)
|
||||||
|
|
||||||
|
let img = await sharp(resolve(`../../public/${data.output.path}`)).metadata()
|
||||||
|
assert.strictEqual(img.width, 600)
|
||||||
|
assert.strictEqual(img.height, 700)
|
||||||
|
assert.strictEqual(img.format, 'png')
|
||||||
|
assert.strictEqual(data.headers['access-control-allow-origin'], assertOrigin)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('should upload file and create multiple sizes for file', async () => {
|
t.test('should upload file and create multiple sizes for file', async () => {
|
||||||
|
@ -431,13 +717,8 @@ t.describe('Media (API)', () => {
|
||||||
assert.strictEqual(img.height, 12)
|
assert.strictEqual(img.height, 12)
|
||||||
assert.strictEqual(img.format, 'jpeg')
|
assert.strictEqual(img.format, 'jpeg')
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
t.timeout(10000).describe('POST /media/resize/:filename', function temp() {
|
t.test('should upload file and filter out null sizes', async () => {
|
||||||
let sourceFilename
|
|
||||||
let sourcePath
|
|
||||||
|
|
||||||
t.before(async function() {
|
|
||||||
let token = encode(null, { iss: 'development' }, secret)
|
let token = encode(null, { iss: 'development' }, secret)
|
||||||
|
|
||||||
let data = await assert.isFulfilled(
|
let data = await assert.isFulfilled(
|
||||||
|
@ -445,19 +726,70 @@ t.describe('Media (API)', () => {
|
||||||
`/media/resize?token=${token}`,
|
`/media/resize?token=${token}`,
|
||||||
resolve('test.png'),
|
resolve('test.png'),
|
||||||
'POST',
|
'POST',
|
||||||
{ }
|
{
|
||||||
|
outtest: null,
|
||||||
|
bla: null,
|
||||||
|
test: null,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
sourceFilename = data.filename
|
|
||||||
sourcePath = data.path
|
testFiles.push(data.path)
|
||||||
|
|
||||||
|
let stats = await Promise.all([
|
||||||
|
fs.stat(resolve('test.png')),
|
||||||
|
fs.stat(resolve(`../../public/${data.path}`)),
|
||||||
|
])
|
||||||
|
assert.strictEqual(stats[0].size, stats[1].size)
|
||||||
|
|
||||||
|
let img = await sharp(resolve(`../../public/${data.path}`)).metadata()
|
||||||
|
assert.strictEqual(img.width, 600)
|
||||||
|
assert.strictEqual(img.height, 700)
|
||||||
|
assert.strictEqual(img.format, 'png')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.describe('POST /media/resize/:filename', function temp() {
|
||||||
|
let sourceFilename
|
||||||
|
let sourceFilenameCors
|
||||||
|
let sourcePath
|
||||||
|
let sourcePathCors
|
||||||
|
|
||||||
|
t.before(async function() {
|
||||||
|
let issuers = ['development', 'development_cors']
|
||||||
|
|
||||||
|
for (let iss of issuers) {
|
||||||
|
let token = encode(null, { iss }, secret)
|
||||||
|
|
||||||
|
let data = await assert.isFulfilled(
|
||||||
|
client.upload(
|
||||||
|
`/media/resize?token=${token}`,
|
||||||
|
resolve('test.png'),
|
||||||
|
'POST',
|
||||||
|
{ }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
testFiles.push(data.path)
|
||||||
|
|
||||||
|
assert.ok(data)
|
||||||
|
assert.ok(data.filename)
|
||||||
|
assert.ok(data.filename.startsWith(currYear))
|
||||||
|
assert.ok(data.path)
|
||||||
|
|
||||||
|
if (iss === 'development') {
|
||||||
|
sourceFilename = data.filename
|
||||||
|
sourcePath = data.path
|
||||||
|
} else {
|
||||||
|
sourceFilenameCors = data.filename
|
||||||
|
sourcePathCors = data.path
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('should require authentication', async () => {
|
t.test('should require authentication', async () => {
|
||||||
|
@ -533,34 +865,75 @@ t.describe('Media (API)', () => {
|
||||||
compressionLevel: 9,
|
compressionLevel: 9,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
|
{ includeHeaders: true }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
testFiles.push(data.test1.path)
|
testFiles.push(data.output.test1.path)
|
||||||
testFiles.push(data.test2.path)
|
testFiles.push(data.output.test2.path)
|
||||||
|
|
||||||
assert.ok(data.test1.filename)
|
assert.ok(data.output.test1.filename)
|
||||||
assert.ok(data.test1.filename.startsWith(currYear))
|
assert.ok(data.output.test1.filename.startsWith(currYear))
|
||||||
assert.ok(data.test1.path)
|
assert.ok(data.output.test1.path)
|
||||||
assert.ok(data.test2.filename)
|
assert.ok(data.output.test2.filename)
|
||||||
assert.ok(data.test2.filename.startsWith(currYear))
|
assert.ok(data.output.test2.filename.startsWith(currYear))
|
||||||
assert.ok(data.test2.path)
|
assert.ok(data.output.test2.path)
|
||||||
|
|
||||||
let img = await sharp(resolve(`../../public/${sourcePath}`)).metadata()
|
let img = await sharp(resolve(`../../public/${sourcePath}`)).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')
|
||||||
|
|
||||||
img = await sharp(resolve(`../../public/${data.test1.path}`)).metadata()
|
img = await sharp(resolve(`../../public/${data.output.test1.path}`)).metadata()
|
||||||
assert.strictEqual(img.width, 320)
|
assert.strictEqual(img.width, 320)
|
||||||
assert.strictEqual(img.height, 413)
|
assert.strictEqual(img.height, 413)
|
||||||
assert.strictEqual(img.format, 'jpeg')
|
assert.strictEqual(img.format, 'jpeg')
|
||||||
|
|
||||||
img = await sharp(resolve(`../../public/${data.test2.path}`)).metadata()
|
img = await sharp(resolve(`../../public/${data.output.test2.path}`)).metadata()
|
||||||
assert.strictEqual(img.width, 150)
|
assert.strictEqual(img.width, 150)
|
||||||
assert.strictEqual(img.height, 175)
|
assert.strictEqual(img.height, 175)
|
||||||
assert.strictEqual(img.format, 'png')
|
assert.strictEqual(img.format, 'png')
|
||||||
|
assert.notOk(data.headers['access-control-allow-origin'])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('should create sizes for existing file and return cors if site has cors', async () => {
|
||||||
|
const assertOrigin = 'http://localhost:9000'
|
||||||
|
let token = encode(null, { iss: 'development_cors' }, secret)
|
||||||
|
|
||||||
|
let data = await assert.isFulfilled(
|
||||||
|
client.post(
|
||||||
|
`/media/resize/${sourceFilenameCors}?token=${token}`,
|
||||||
|
{
|
||||||
|
test2: {
|
||||||
|
format: 'png',
|
||||||
|
resize: { width: 150 },
|
||||||
|
png: { compressionLevel: 9 }
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
includeHeaders: true,
|
||||||
|
headers: { origin: assertOrigin, 'access-control-request-method': 'POST' },
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
testFiles.push(data.output.test2.path)
|
||||||
|
|
||||||
|
assert.ok(data.output.test2.filename)
|
||||||
|
assert.ok(data.output.test2.filename.startsWith(currYear))
|
||||||
|
assert.ok(data.output.test2.path)
|
||||||
|
|
||||||
|
let img = await sharp(resolve(`../../public/${sourcePath}`)).metadata()
|
||||||
|
assert.strictEqual(img.width, 600)
|
||||||
|
assert.strictEqual(img.height, 700)
|
||||||
|
assert.strictEqual(img.format, 'png')
|
||||||
|
|
||||||
|
img = await sharp(resolve(`../../public/${data.output.test2.path}`)).metadata()
|
||||||
|
assert.strictEqual(img.width, 150)
|
||||||
|
assert.strictEqual(img.height, 175)
|
||||||
|
assert.strictEqual(img.format, 'png')
|
||||||
|
assert.strictEqual(data.headers['access-control-allow-origin'], assertOrigin)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('should base64 output of existing file', async () => {
|
t.test('should base64 output of existing file', async () => {
|
||||||
|
@ -597,7 +970,7 @@ t.describe('Media (API)', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.timeout(10000).describe('DELETE /media/:filename', function temp() {
|
t.describe('DELETE /media/:filename', function temp() {
|
||||||
t.test('should require authentication', async () => {
|
t.test('should require authentication', async () => {
|
||||||
resetLog()
|
resetLog()
|
||||||
assert.strictEqual(log.error.callCount, 0)
|
assert.strictEqual(log.error.callCount, 0)
|
||||||
|
@ -646,7 +1019,7 @@ t.describe('Media (API)', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('should remove the file', async () => {
|
t.test('should remove the file', async () => {
|
||||||
let token = encode(null, { iss: 'existing' }, secret)
|
let token = encode(null, { iss: 'development' }, secret)
|
||||||
|
|
||||||
let data = await client.upload(
|
let data = await client.upload(
|
||||||
`/media/noprefix?token=${token}`,
|
`/media/noprefix?token=${token}`,
|
||||||
|
@ -656,11 +1029,11 @@ t.describe('Media (API)', () => {
|
||||||
let filepath = data.path
|
let filepath = data.path
|
||||||
testFiles.push(filepath)
|
testFiles.push(filepath)
|
||||||
|
|
||||||
let files = await client.get('/media/existing')
|
let files = await client.get('/media?token=' + token)
|
||||||
|
|
||||||
let found = false
|
let found = false
|
||||||
for (let file of files) {
|
for (let file of files) {
|
||||||
if (file.filename === 'test.png') {
|
if (file.filename === data.filename) {
|
||||||
found = true
|
found = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -670,17 +1043,19 @@ t.describe('Media (API)', () => {
|
||||||
fs.stat(resolve(`../../public/${filepath}`))
|
fs.stat(resolve(`../../public/${filepath}`))
|
||||||
)
|
)
|
||||||
|
|
||||||
await assert.isFulfilled(
|
let res = await assert.isFulfilled(
|
||||||
client.del(`/media/test.png?token=${token}`)
|
client.del(`/media/${data.filename}?token=${token}`, {}, {
|
||||||
|
includeHeaders: true
|
||||||
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
testFiles.splice(testFiles.length - 1)
|
testFiles.splice(testFiles.length - 1)
|
||||||
|
|
||||||
files = await client.get('/media/existing')
|
files = await client.get('/media?token=' + token)
|
||||||
|
|
||||||
found = false
|
found = false
|
||||||
for (let file of files) {
|
for (let file of files) {
|
||||||
if (file.filename === 'test.png') {
|
if (file.filename === data.filename) {
|
||||||
found = true
|
found = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -689,6 +1064,58 @@ t.describe('Media (API)', () => {
|
||||||
await assert.isRejected(
|
await assert.isRejected(
|
||||||
fs.stat(resolve(`../../public/${filepath}`))
|
fs.stat(resolve(`../../public/${filepath}`))
|
||||||
)
|
)
|
||||||
|
assert.notOk(res.headers['access-control-allow-origin'])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('should remove the file and return cors if site has cors', async () => {
|
||||||
|
const assertOrigin = 'http://localhost:9000'
|
||||||
|
let token = encode(null, { iss: 'development_cors' }, 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?token=' + token)
|
||||||
|
|
||||||
|
let found = false
|
||||||
|
for (let file of files) {
|
||||||
|
if (file.filename === data.filename) {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.ok(found)
|
||||||
|
|
||||||
|
await assert.isFulfilled(
|
||||||
|
fs.stat(resolve(`../../public/${filepath}`))
|
||||||
|
)
|
||||||
|
|
||||||
|
let res = await assert.isFulfilled(
|
||||||
|
client.del(`/media/${data.filename}?token=${token}`, {}, {
|
||||||
|
includeHeaders: true,
|
||||||
|
headers: { origin: assertOrigin, 'access-control-request-method': 'POST' },
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
testFiles.splice(testFiles.length - 1)
|
||||||
|
|
||||||
|
files = await client.get('/media?token=' + token)
|
||||||
|
|
||||||
|
found = false
|
||||||
|
for (let file of files) {
|
||||||
|
if (file.filename === data.filename) {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.notOk(found)
|
||||||
|
|
||||||
|
await assert.isRejected(
|
||||||
|
fs.stat(resolve(`../../public/${filepath}`))
|
||||||
|
)
|
||||||
|
assert.strictEqual(res.headers['access-control-allow-origin'], assertOrigin)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -735,17 +1162,39 @@ t.describe('Media (API)', () => {
|
||||||
t.test('should return list of files in specified folder', async () => {
|
t.test('should return list of files in specified folder', async () => {
|
||||||
let token = encode(null, { iss: 'development' }, secret)
|
let token = encode(null, { iss: 'development' }, secret)
|
||||||
|
|
||||||
let data = await client.get('/media?token=' + token)
|
let data = await client.get('/media?token=' + token, { includeHeaders: true })
|
||||||
|
|
||||||
assert.ok(data.length)
|
assert.ok(data.output.length)
|
||||||
let found = false
|
let found = false
|
||||||
for (let file of data) {
|
for (let file of data.output) {
|
||||||
if (file.filename === '.gitkeep') {
|
if (file.filename === '.gitkeep') {
|
||||||
found = true
|
found = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert.ok(found)
|
assert.ok(found)
|
||||||
|
assert.notOk(data.headers['access-control-allow-origin'])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('should return list of files in specified folder and return cors if site has cors', async () => {
|
||||||
|
const assertOrigin = 'http://localhost:9000'
|
||||||
|
let token = encode(null, { iss: 'development_cors' }, secret)
|
||||||
|
|
||||||
|
let data = await client.get('/media?token=' + token, {
|
||||||
|
includeHeaders: true,
|
||||||
|
headers: { origin: assertOrigin, 'access-control-request-method': 'POST' },
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.ok(data.output.length)
|
||||||
|
let found = false
|
||||||
|
for (let file of data.output) {
|
||||||
|
if (file.filename === '.gitkeep') {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.ok(found)
|
||||||
|
assert.strictEqual(data.headers['access-control-allow-origin'], assertOrigin)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -765,11 +1214,25 @@ 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', { includeHeaders: true })
|
||||||
assert.strictEqual(files[0].filename, '20220105_101610_test1.jpg')
|
assert.strictEqual(files.output[0].filename, '20220105_101610_test1.jpg')
|
||||||
assert.strictEqual(files[0].size, null)
|
assert.strictEqual(files.output[0].size, null)
|
||||||
assert.strictEqual(files[1].filename, '20220105_101610_test2.png')
|
assert.strictEqual(files.output[1].filename, '20220105_101610_test2.png')
|
||||||
assert.strictEqual(files[1].size, null)
|
assert.strictEqual(files.output[1].size, null)
|
||||||
|
assert.notOk(files.headers['access-control-allow-origin'])
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('should otherwise return list of files for a public site and cors if site has cors', async () => {
|
||||||
|
const assertOrigin = 'http://localhost:9000'
|
||||||
|
let files = await client.get('/media/existing_cors', {
|
||||||
|
includeHeaders: true,
|
||||||
|
headers: { origin: assertOrigin, 'access-control-request-method': 'POST' },
|
||||||
|
})
|
||||||
|
assert.strictEqual(files.output[0].filename, '20220105_101610_test1.jpg')
|
||||||
|
assert.strictEqual(files.output[0].size, null)
|
||||||
|
assert.strictEqual(files.output[1].filename, '20220105_101610_test2.png')
|
||||||
|
assert.strictEqual(files.output[1].size, null)
|
||||||
|
assert.strictEqual(files.headers['access-control-allow-origin'], assertOrigin)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import fs from 'fs/promises'
|
import fs from 'fs/promises'
|
||||||
|
import path from 'path'
|
||||||
import { Eltro as t, assert, stub } from 'eltro'
|
import { Eltro as t, assert, stub } from 'eltro'
|
||||||
import { HttpError } from 'flaska'
|
import { HttpError } from 'flaska'
|
||||||
import { createContext } from '../helper.server.mjs'
|
import { createContext } from '../helper.server.mjs'
|
||||||
|
@ -72,7 +73,7 @@ t.describe('#listFiles()', function() {
|
||||||
const stubGetCache = stub()
|
const stubGetCache = stub()
|
||||||
|
|
||||||
const routes = new MediaRoutes({
|
const routes = new MediaRoutes({
|
||||||
security: { verifyToken: stubVerify },
|
security: { verifyToken: stubVerify, hasCors: stub() },
|
||||||
})
|
})
|
||||||
routes.filesCacheGet = stubGetCache
|
routes.filesCacheGet = stubGetCache
|
||||||
|
|
||||||
|
@ -86,7 +87,7 @@ t.describe('#listFiles()', function() {
|
||||||
|
|
||||||
let ctx = createContext()
|
let ctx = createContext()
|
||||||
const assertError = new Error('temp')
|
const assertError = new Error('temp')
|
||||||
stubVerify.rejects(assertError)
|
stubVerify.throws(assertError)
|
||||||
|
|
||||||
let err = await assert.isRejected(routes.listFiles(ctx))
|
let err = await assert.isRejected(routes.listFiles(ctx))
|
||||||
|
|
||||||
|
@ -101,7 +102,7 @@ t.describe('#listFiles()', function() {
|
||||||
let ctx = createContext()
|
let ctx = createContext()
|
||||||
const assertSiteName = 'benshapiro'
|
const assertSiteName = 'benshapiro'
|
||||||
const assertResult = { a: 1 }
|
const assertResult = { a: 1 }
|
||||||
stubVerify.resolves(assertSiteName)
|
stubVerify.returns(assertSiteName)
|
||||||
stubGetCache.returns(assertResult)
|
stubGetCache.returns(assertResult)
|
||||||
|
|
||||||
await routes.listFiles(ctx)
|
await routes.listFiles(ctx)
|
||||||
|
@ -117,7 +118,7 @@ t.describe('#listPublicFiles()', function() {
|
||||||
const stubGetCache = stub()
|
const stubGetCache = stub()
|
||||||
|
|
||||||
const routes = new MediaRoutes({
|
const routes = new MediaRoutes({
|
||||||
security: { throwIfNotPublic: stubSitePublic },
|
security: { throwIfNotPublic: stubSitePublic, hasCors: stub() },
|
||||||
})
|
})
|
||||||
routes.filesCacheGet = stubGetCache
|
routes.filesCacheGet = stubGetCache
|
||||||
|
|
||||||
|
@ -249,6 +250,7 @@ t.describe('#uploadNoPrefix', function() {
|
||||||
security: {
|
security: {
|
||||||
verifyToken: stubVerify,
|
verifyToken: stubVerify,
|
||||||
verifyBody: stub(),
|
verifyBody: stub(),
|
||||||
|
hasCors: stub(),
|
||||||
},
|
},
|
||||||
formidable: { uploadFile: stubUpload, },
|
formidable: { uploadFile: stubUpload, },
|
||||||
fs: { stat: stubStat },
|
fs: { stat: stubStat },
|
||||||
|
@ -266,7 +268,7 @@ t.describe('#uploadNoPrefix', function() {
|
||||||
let ctx = createContext()
|
let ctx = createContext()
|
||||||
const assertSiteName = 'benshapiro'
|
const assertSiteName = 'benshapiro'
|
||||||
const assertError = new Error('hello')
|
const assertError = new Error('hello')
|
||||||
stubVerify.resolves(assertSiteName)
|
stubVerify.returns(assertSiteName)
|
||||||
stubUpload.rejects(assertError)
|
stubUpload.rejects(assertError)
|
||||||
|
|
||||||
let err = await assert.isRejected(routes.uploadNoPrefix(ctx))
|
let err = await assert.isRejected(routes.uploadNoPrefix(ctx))
|
||||||
|
@ -299,6 +301,7 @@ t.describe('#resizeExisting', function() {
|
||||||
security: {
|
security: {
|
||||||
verifyToken: stubVerifyToken,
|
verifyToken: stubVerifyToken,
|
||||||
verifyBody: stubVerifyBody,
|
verifyBody: stubVerifyBody,
|
||||||
|
hasCors: stub(),
|
||||||
},
|
},
|
||||||
fs: { stat: stubStat },
|
fs: { stat: stubStat },
|
||||||
sharp: stubSharp,
|
sharp: stubSharp,
|
||||||
|
@ -337,7 +340,7 @@ t.describe('#resizeExisting', function() {
|
||||||
|
|
||||||
let ctx = createContext({ req: { body: { } } })
|
let ctx = createContext({ req: { body: { } } })
|
||||||
const assertError = new Error('temp')
|
const assertError = new Error('temp')
|
||||||
stubVerifyToken.rejects(assertError)
|
stubVerifyToken.throws(assertError)
|
||||||
|
|
||||||
let err = await assert.isRejected(routes.resizeExisting(ctx))
|
let err = await assert.isRejected(routes.resizeExisting(ctx))
|
||||||
|
|
||||||
|
@ -366,7 +369,7 @@ t.describe('#resizeExisting', function() {
|
||||||
const assertJpeg = { a: 1 }
|
const assertJpeg = { a: 1 }
|
||||||
const assertFilename = 'asdfsafd'
|
const assertFilename = 'asdfsafd'
|
||||||
const assertSite = 'mario'
|
const assertSite = 'mario'
|
||||||
stubVerifyToken.resolves(assertSite)
|
stubVerifyToken.returns(assertSite)
|
||||||
|
|
||||||
let ctx = createContext({ req: { body: {
|
let ctx = createContext({ req: { body: {
|
||||||
[assertKey]: {
|
[assertKey]: {
|
||||||
|
@ -417,7 +420,7 @@ t.describe('#resizeExisting', function() {
|
||||||
const assertPayload = { a: 1 }
|
const assertPayload = { a: 1 }
|
||||||
const assertFilename = 'asdfsafd'
|
const assertFilename = 'asdfsafd'
|
||||||
const assertSite = 'mario'
|
const assertSite = 'mario'
|
||||||
stubVerifyToken.resolves(assertSite)
|
stubVerifyToken.returns(assertSite)
|
||||||
|
|
||||||
let called = 0
|
let called = 0
|
||||||
stubStat.returnWith(function() {
|
stubStat.returnWith(function() {
|
||||||
|
@ -466,7 +469,7 @@ t.describe('#resizeExisting', function() {
|
||||||
const assertValidKey2 = 'derp'
|
const assertValidKey2 = 'derp'
|
||||||
const assertErrorKey = 'throwmyerr'
|
const assertErrorKey = 'throwmyerr'
|
||||||
const assertErrorMessage = 'some message here'
|
const assertErrorMessage = 'some message here'
|
||||||
stubVerifyToken.resolves('asdf')
|
stubVerifyToken.returns('asdf')
|
||||||
|
|
||||||
let called = 0
|
let called = 0
|
||||||
stubStat.returnWith(function() {
|
stubStat.returnWith(function() {
|
||||||
|
@ -511,7 +514,7 @@ t.describe('#resizeExisting', function() {
|
||||||
const assertFilename = 'asdfsafd.png'
|
const assertFilename = 'asdfsafd.png'
|
||||||
const assertSite = 'mario'
|
const assertSite = 'mario'
|
||||||
const assertBase64Data = 'asdf1234'
|
const assertBase64Data = 'asdf1234'
|
||||||
stubVerifyToken.resolves(assertSite)
|
stubVerifyToken.returns(assertSite)
|
||||||
|
|
||||||
let ctx = createContext({ req: { body: {
|
let ctx = createContext({ req: { body: {
|
||||||
[assertKey]: {
|
[assertKey]: {
|
||||||
|
@ -561,6 +564,7 @@ t.describe('#resize', function() {
|
||||||
security: {
|
security: {
|
||||||
verifyToken: stubVerifyToken,
|
verifyToken: stubVerifyToken,
|
||||||
verifyBody: stubVerifyBody,
|
verifyBody: stubVerifyBody,
|
||||||
|
hasCors: stub(),
|
||||||
},
|
},
|
||||||
fs: { stat: stubStat },
|
fs: { stat: stubStat },
|
||||||
formidable: { uploadFile: stubUpload },
|
formidable: { uploadFile: stubUpload },
|
||||||
|
@ -617,7 +621,7 @@ t.describe('#resize', function() {
|
||||||
const assertJpeg = { a: 1 }
|
const assertJpeg = { a: 1 }
|
||||||
const assertFilename = 'asdfsafd'
|
const assertFilename = 'asdfsafd'
|
||||||
const assertSite = 'mario'
|
const assertSite = 'mario'
|
||||||
stubVerifyToken.resolves(assertSite)
|
stubVerifyToken.returns(assertSite)
|
||||||
stubUpload.resolves({ filename: assertFilename + '.png' })
|
stubUpload.resolves({ filename: assertFilename + '.png' })
|
||||||
|
|
||||||
let ctx = createContext({ req: { body: {
|
let ctx = createContext({ req: { body: {
|
||||||
|
@ -668,7 +672,7 @@ t.describe('#resize', function() {
|
||||||
const assertPayload = { a: 1 }
|
const assertPayload = { a: 1 }
|
||||||
const assertFilename = 'asdfsafd'
|
const assertFilename = 'asdfsafd'
|
||||||
const assertSite = 'mario'
|
const assertSite = 'mario'
|
||||||
stubVerifyToken.resolves(assertSite)
|
stubVerifyToken.returns(assertSite)
|
||||||
stubUpload.resolves({ filename: assertFilename + '.png' })
|
stubUpload.resolves({ filename: assertFilename + '.png' })
|
||||||
|
|
||||||
let called = 0
|
let called = 0
|
||||||
|
@ -719,7 +723,7 @@ t.describe('#resize', function() {
|
||||||
const assertValidKey2 = 'derp'
|
const assertValidKey2 = 'derp'
|
||||||
const assertErrorKey = 'throwmyerr'
|
const assertErrorKey = 'throwmyerr'
|
||||||
const assertErrorMessage = 'some message here'
|
const assertErrorMessage = 'some message here'
|
||||||
stubVerifyToken.resolves('asdf')
|
stubVerifyToken.returns('asdf')
|
||||||
stubUpload.resolves({ filename: 'file.png' })
|
stubUpload.resolves({ filename: 'file.png' })
|
||||||
|
|
||||||
let called = 0
|
let called = 0
|
||||||
|
@ -764,7 +768,7 @@ t.describe('#resize', function() {
|
||||||
const assertFilename = 'asdfsafd.png'
|
const assertFilename = 'asdfsafd.png'
|
||||||
const assertSite = 'mario'
|
const assertSite = 'mario'
|
||||||
const assertBase64Data = 'asdf1234'
|
const assertBase64Data = 'asdf1234'
|
||||||
stubVerifyToken.resolves(assertSite)
|
stubVerifyToken.returns(assertSite)
|
||||||
stubUpload.resolves({ filename: assertFilename })
|
stubUpload.resolves({ filename: assertFilename })
|
||||||
|
|
||||||
let ctx = createContext({ req: { body: {
|
let ctx = createContext({ req: { body: {
|
||||||
|
@ -810,6 +814,7 @@ basicUploadTestRoutes.forEach(function(name) {
|
||||||
security: {
|
security: {
|
||||||
verifyToken: stubVerify,
|
verifyToken: stubVerify,
|
||||||
verifyBody: stub(),
|
verifyBody: stub(),
|
||||||
|
hasCors: stub(),
|
||||||
},
|
},
|
||||||
formidable: { uploadFile: stubUpload, },
|
formidable: { uploadFile: stubUpload, },
|
||||||
fs: { stat: stubStat },
|
fs: { stat: stubStat },
|
||||||
|
@ -826,7 +831,7 @@ basicUploadTestRoutes.forEach(function(name) {
|
||||||
|
|
||||||
let ctx = createContext({ req: { body: { } } })
|
let ctx = createContext({ req: { body: { } } })
|
||||||
const assertError = new Error('temp')
|
const assertError = new Error('temp')
|
||||||
stubVerify.rejects(assertError)
|
stubVerify.throws(assertError)
|
||||||
|
|
||||||
let err = await assert.isRejected(routes[name](ctx))
|
let err = await assert.isRejected(routes[name](ctx))
|
||||||
|
|
||||||
|
@ -841,7 +846,7 @@ basicUploadTestRoutes.forEach(function(name) {
|
||||||
let ctx = createContext({ req: { body: { } } })
|
let ctx = createContext({ req: { body: { } } })
|
||||||
const assertSiteName = 'benshapiro'
|
const assertSiteName = 'benshapiro'
|
||||||
const assertError = new Error('hello')
|
const assertError = new Error('hello')
|
||||||
stubVerify.resolves(assertSiteName)
|
stubVerify.returns(assertSiteName)
|
||||||
stubUpload.rejects(assertError)
|
stubUpload.rejects(assertError)
|
||||||
|
|
||||||
let err = await assert.isRejected(routes[name](ctx))
|
let err = await assert.isRejected(routes[name](ctx))
|
||||||
|
@ -859,7 +864,7 @@ basicUploadTestRoutes.forEach(function(name) {
|
||||||
const assertSize = 1241412
|
const assertSize = 1241412
|
||||||
const assertFilename = 'asdfsafd'
|
const assertFilename = 'asdfsafd'
|
||||||
const assertSite = 'mario'
|
const assertSite = 'mario'
|
||||||
stubVerify.resolves(assertSite)
|
stubVerify.returns(assertSite)
|
||||||
stubUpload.resolves({ filename: assertFilename })
|
stubUpload.resolves({ filename: assertFilename })
|
||||||
stubStat.resolves({ size: assertSize })
|
stubStat.resolves({ size: assertSize })
|
||||||
await routes[name](ctx)
|
await routes[name](ctx)
|
||||||
|
@ -886,6 +891,7 @@ t.describe('#remove()', function() {
|
||||||
const routes = new MediaRoutes({
|
const routes = new MediaRoutes({
|
||||||
security: {
|
security: {
|
||||||
verifyToken: stubVerify,
|
verifyToken: stubVerify,
|
||||||
|
hasCors: stub(),
|
||||||
},
|
},
|
||||||
fs: { unlink: stubUnlink },
|
fs: { unlink: stubUnlink },
|
||||||
})
|
})
|
||||||
|
@ -904,7 +910,7 @@ t.describe('#remove()', function() {
|
||||||
|
|
||||||
let ctx = createContext({ req: { body: { } } })
|
let ctx = createContext({ req: { body: { } } })
|
||||||
const assertError = new Error('temp')
|
const assertError = new Error('temp')
|
||||||
stubVerify.rejects(assertError)
|
stubVerify.throws(assertError)
|
||||||
|
|
||||||
let err = await assert.isRejected(routes.remove(ctx))
|
let err = await assert.isRejected(routes.remove(ctx))
|
||||||
|
|
||||||
|
@ -923,7 +929,7 @@ t.describe('#remove()', function() {
|
||||||
|
|
||||||
let ctx = createContext({ req: { body: { } } })
|
let ctx = createContext({ req: { body: { } } })
|
||||||
ctx.params.filename = assertFilename
|
ctx.params.filename = assertFilename
|
||||||
stubVerify.resolves(assertSiteName)
|
stubVerify.returns(assertSiteName)
|
||||||
stubUnlink.rejects(assertError)
|
stubUnlink.rejects(assertError)
|
||||||
|
|
||||||
let err = await assert.isRejected(routes.remove(ctx))
|
let err = await assert.isRejected(routes.remove(ctx))
|
||||||
|
@ -936,7 +942,7 @@ t.describe('#remove()', function() {
|
||||||
assert.match(err.message, new RegExp(assertFilename))
|
assert.match(err.message, new RegExp(assertFilename))
|
||||||
assert.match(err.message, new RegExp(assertErrorMessage))
|
assert.match(err.message, new RegExp(assertErrorMessage))
|
||||||
|
|
||||||
assert.strictEqual(stubUnlink.firstCall[0], `./public/${assertSiteName}/${assertFilename}`)
|
assert.strictEqual(stubUnlink.firstCall[0], path.join(`./public/${assertSiteName}/`, assertFilename))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('should otherwise return 204 status and remove from array', async function() {
|
t.test('should otherwise return 204 status and remove from array', async function() {
|
||||||
|
@ -947,7 +953,7 @@ t.describe('#remove()', function() {
|
||||||
|
|
||||||
let ctx = createContext({ req: { body: { } } })
|
let ctx = createContext({ req: { body: { } } })
|
||||||
ctx.params.filename = assertFilename
|
ctx.params.filename = assertFilename
|
||||||
stubVerify.resolves(assertSiteName)
|
stubVerify.returns(assertSiteName)
|
||||||
|
|
||||||
await routes.remove(ctx)
|
await routes.remove(ctx)
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Eltro as t, assert} from 'eltro'
|
||||||
import { HttpError } from 'flaska'
|
import { HttpError } from 'flaska'
|
||||||
|
|
||||||
import { createContext } from '../helper.server.mjs'
|
import { createContext } from '../helper.server.mjs'
|
||||||
import { verifyToken, verifyBody, throwIfNotPublic } from '../../api/media/security.mjs'
|
import { verifyToken, verifyBody, throwIfNotPublic, verifyCorsEnabled } from '../../api/media/security.mjs'
|
||||||
import encode from '../../api/jwt/encode.mjs'
|
import encode from '../../api/jwt/encode.mjs'
|
||||||
import config from '../../api/config.mjs'
|
import config from '../../api/config.mjs'
|
||||||
|
|
||||||
|
@ -61,6 +61,98 @@ t.describe('#throwIfNotPublic()', function() {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.describe('#verifyCorsEnabled()', function() {
|
||||||
|
let ctx
|
||||||
|
let backup = {}
|
||||||
|
|
||||||
|
t.beforeEach(function() {
|
||||||
|
ctx = createContext({ })
|
||||||
|
})
|
||||||
|
|
||||||
|
t.before(function() {
|
||||||
|
backup = config.sources[1].store
|
||||||
|
config.sources[1].store = {
|
||||||
|
sites: {
|
||||||
|
justatest: {
|
||||||
|
keys: {
|
||||||
|
'default@HS512': 'mysharedkey',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
justatest2: {
|
||||||
|
keys: {
|
||||||
|
'default@HS512': 'mysharedkey',
|
||||||
|
},
|
||||||
|
cors: false,
|
||||||
|
},
|
||||||
|
justatest3: {
|
||||||
|
keys: {
|
||||||
|
'default@HS512': 'mysharedkey',
|
||||||
|
},
|
||||||
|
cors: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.after(function() {
|
||||||
|
config.sources[1].store = backup
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('should throw 404 for sites that do not exist or are null', function() {
|
||||||
|
let tests = [
|
||||||
|
'justatest',
|
||||||
|
'justatest2',
|
||||||
|
'nonexisting1',
|
||||||
|
null,
|
||||||
|
]
|
||||||
|
|
||||||
|
tests.forEach(function(test) {
|
||||||
|
ctx.query.set('token', encode({ typ: 'JWT', alg: 'HS512' }, { iss: test }, 'mysharedkey'))
|
||||||
|
|
||||||
|
assert.throws(function() { verifyCorsEnabled(ctx) }, function(err) {
|
||||||
|
assert.ok(err instanceof HttpError)
|
||||||
|
assert.ok(err instanceof Error)
|
||||||
|
assert.strictEqual(err.status, 404)
|
||||||
|
assert.notOk(err.message)
|
||||||
|
return true
|
||||||
|
}, `should throw with site ${test}`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('should throw 404 for sites that use wrong token', function() {
|
||||||
|
let tests = [
|
||||||
|
'justatest',
|
||||||
|
'justatest2',
|
||||||
|
'nonexisting1',
|
||||||
|
null,
|
||||||
|
]
|
||||||
|
|
||||||
|
tests.forEach(function(test) {
|
||||||
|
ctx.query.set('token', encode({ typ: 'JWT', alg: 'HS512' }, { iss: test }, 'herp'))
|
||||||
|
|
||||||
|
assert.throws(function() { verifyCorsEnabled(ctx) }, function(err) {
|
||||||
|
assert.ok(err instanceof HttpError)
|
||||||
|
assert.ok(err instanceof Error)
|
||||||
|
assert.strictEqual(err.status, 404)
|
||||||
|
assert.notOk(err.message)
|
||||||
|
return true
|
||||||
|
}, `should throw with site ${test}`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.test('should pass for sites that have cors enabled', function() {
|
||||||
|
let tests = [
|
||||||
|
'justatest3',
|
||||||
|
]
|
||||||
|
|
||||||
|
tests.forEach(function(test) {
|
||||||
|
ctx.query.set('token', encode({ typ: 'JWT', alg: 'HS512' }, { iss: test }, 'mysharedkey'))
|
||||||
|
|
||||||
|
assert.doesNotThrow(function() { verifyCorsEnabled(ctx) }, `should not throw with site ${test}`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
t.describe('#verifyToken()', function() {
|
t.describe('#verifyToken()', function() {
|
||||||
let backup = {}
|
let backup = {}
|
||||||
t.before(function() {
|
t.before(function() {
|
||||||
|
@ -160,6 +252,12 @@ t.describe('#verifyBody()', function() {
|
||||||
|
|
||||||
verifyBody(ctx)
|
verifyBody(ctx)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.test('should succeed with null values in body', function() {
|
||||||
|
let ctx = createContext({ req: { body: { test: null } } })
|
||||||
|
|
||||||
|
verifyBody(ctx)
|
||||||
|
})
|
||||||
|
|
||||||
t.test('should fail with invalid body', function() {
|
t.test('should fail with invalid body', function() {
|
||||||
let ctx = createContext({ req: { body: {
|
let ctx = createContext({ req: { body: {
|
||||||
|
@ -167,7 +265,7 @@ t.describe('#verifyBody()', function() {
|
||||||
} } })
|
} } })
|
||||||
|
|
||||||
let tests = [
|
let tests = [
|
||||||
[null, 'null'],
|
// [null, 'null'],
|
||||||
['', 'empty string'],
|
['', 'empty string'],
|
||||||
['asdf', 'string'],
|
['asdf', 'string'],
|
||||||
[0, 'empty number'],
|
[0, 'empty number'],
|
||||||
|
|
Loading…
Reference in New Issue