284 lines
7.1 KiB
JavaScript
284 lines
7.1 KiB
JavaScript
const Authentication = require('./authentication')
|
|
const lang = require('./lang')
|
|
|
|
function safeParseReponse(str, status, url) {
|
|
if (status === 0) {
|
|
return new Error(lang.api_down)
|
|
}
|
|
if (str.slice(0, 9) === '<!doctype') {
|
|
if (status === 500) {
|
|
return new Error('Server is temporarily down, try again later.')
|
|
}
|
|
return new Error('Expected JSON but got HTML (' + status + ': ' + url.split('?')[0] + ')')
|
|
}
|
|
if (!str) {
|
|
return {}
|
|
}
|
|
try {
|
|
return JSON.parse(str)
|
|
} catch (err) {
|
|
return new Error('Unexpected non-JSON response: ' + err.message)
|
|
}
|
|
}
|
|
|
|
let requests = new Set()
|
|
let requestIndex = 0
|
|
|
|
function clearLoading(request) {
|
|
requests.delete(request)
|
|
if (!requests.size) {
|
|
api.loading = false
|
|
window.requestAnimationFrame(m.redraw.bind(m))
|
|
}
|
|
}
|
|
|
|
const api = {
|
|
loading: false,
|
|
sendRequest: function(options, isPagination) {
|
|
let request = requestIndex++
|
|
requests.add(request)
|
|
api.loading = true
|
|
let token = Authentication.getToken()
|
|
let pagination = isPagination
|
|
|
|
if (token) {
|
|
options.headers = options.headers || {}
|
|
options.headers['Authorization'] = 'Bearer ' + token
|
|
}
|
|
|
|
options.extract = function(xhr) {
|
|
let out = safeParseReponse(xhr.responseText, xhr.status, this.url)
|
|
|
|
if (out instanceof Error) {
|
|
throw out
|
|
}
|
|
if (xhr.status >= 300) {
|
|
if (out.message) {
|
|
throw out
|
|
}
|
|
console.error('Got error ' + xhr.status + ' but no error message:', out)
|
|
throw new Error('Unknown or empty response from server.')
|
|
}
|
|
if (pagination) {
|
|
let headers = {}
|
|
|
|
xhr.getAllResponseHeaders().split('\r\n').forEach(function(item) {
|
|
let splitted = item.split(': ')
|
|
headers[splitted[0]] = splitted[1]
|
|
})
|
|
|
|
out = {
|
|
headers: headers || {},
|
|
data: out,
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
return m.request(options)
|
|
.then(function(res) {
|
|
clearLoading(request)
|
|
return res
|
|
})
|
|
.catch(function (error) {
|
|
clearLoading(request)
|
|
window.requestAnimationFrame(m.redraw.bind(m))
|
|
if (error.status === 403) {
|
|
Authentication.clearToken()
|
|
m.route.set('/', { redirect: m.route.get() })
|
|
}
|
|
if (error.response && error.response.status) {
|
|
return Promise.reject(error.response)
|
|
}
|
|
return Promise.reject(error)
|
|
})
|
|
},
|
|
|
|
uploadBanner: function(bannerFile, token, reporter) {
|
|
let request = requestIndex++
|
|
requests.add(request)
|
|
api.loading = true
|
|
|
|
var report = reporter || function() {}
|
|
var data = new FormData()
|
|
data.append('file', bannerFile)
|
|
/*data.append('preview', JSON.stringify({
|
|
"out": "base64",
|
|
"format": "avif",
|
|
"resize": {
|
|
"width": 360,
|
|
"height": 203,
|
|
"fit": "cover",
|
|
"withoutEnlargement": true,
|
|
"kernel": "mitchell"
|
|
},
|
|
"avif": {
|
|
"quality": 40,
|
|
"effort": 9
|
|
}
|
|
}))*/
|
|
data.append('medium', JSON.stringify({
|
|
"format": "avif",
|
|
"resize": {
|
|
"width": 1280,
|
|
"height": 720,
|
|
"fit": "cover",
|
|
"withoutEnlargement": true,
|
|
"kernel": "mitchell"
|
|
},
|
|
"avif": {
|
|
"quality": 75,
|
|
"effort": 3
|
|
}
|
|
}))
|
|
|
|
report(lang.api_banner_upload)
|
|
|
|
return api.sendRequest({
|
|
method: 'POST',
|
|
url: token.resize + '?token=' + token.token,
|
|
body: data,
|
|
})
|
|
.then(async (banner) => {
|
|
let preview = null
|
|
let quality = 60
|
|
while (!preview && quality > 10) {
|
|
report(lang.format(lang.api_banner_generate, quality))
|
|
let check = await api.sendRequest({
|
|
method: 'POST',
|
|
url: token.resize + '/' + banner.filename + '?token=' + token.token,
|
|
body: {
|
|
"preview": {
|
|
"out": "base64",
|
|
"format": "avif",
|
|
"resize": {
|
|
"width": 360,
|
|
"height": 203,
|
|
"fit": "cover",
|
|
"withoutEnlargement": true,
|
|
"kernel": "mitchell"
|
|
},
|
|
"avif": {
|
|
"quality": quality,
|
|
"effort": 9
|
|
}
|
|
},
|
|
},
|
|
})
|
|
|
|
if (check.preview.base64.length < 8000) {
|
|
preview = check.preview.base64
|
|
} else {
|
|
quality -= 5
|
|
}
|
|
}
|
|
report(null)
|
|
|
|
api.sendRequest({
|
|
method: 'DELETE',
|
|
url: token.delete + banner.filename + '?token=' + token.token,
|
|
}).catch(err => console.error(err))
|
|
|
|
return {
|
|
file: bannerFile,
|
|
size: bannerFile.size,
|
|
medium: {
|
|
filename: banner.medium.filename,
|
|
path: banner.medium.path,
|
|
},
|
|
preview: {
|
|
base64: preview,
|
|
},
|
|
}
|
|
})
|
|
.then(
|
|
function(res) {
|
|
clearLoading(request)
|
|
return res
|
|
},
|
|
function(err) {
|
|
clearLoading(request)
|
|
return Promise.reject(err)
|
|
}
|
|
)
|
|
},
|
|
|
|
prettyFormatBytes: function(bytes) {
|
|
if (bytes < 1024) {
|
|
return `${bytes} B`
|
|
}
|
|
if (bytes < 1024 * 1024) {
|
|
return `${(bytes / 1024).toFixed(1)} KB`
|
|
}
|
|
if (bytes < 1024 * 1024 * 1024) {
|
|
return `${(bytes / 1024 / 1024).toFixed(1)} MB`
|
|
}
|
|
},
|
|
|
|
uploadFileProgress: function(options, file, reporter) {
|
|
let request = requestIndex++
|
|
requests.add(request)
|
|
api.loading = true
|
|
|
|
return new Promise(function(res, rej) {
|
|
let report = reporter || function() {}
|
|
let formdata = new FormData()
|
|
|
|
formdata.append('file', file)
|
|
|
|
let request = new XMLHttpRequest()
|
|
|
|
let finished = false
|
|
let lastMarker = new Date()
|
|
let lastMarkerLoaded = 0
|
|
let lastMarkerSpeed = '...'
|
|
|
|
request.abortRequest = function() {
|
|
finished = true
|
|
request.abort()
|
|
}
|
|
|
|
request.upload.addEventListener('progress', function (e) {
|
|
let check = new Date()
|
|
if (check - lastMarker >= 1000) {
|
|
let loaded = e.loaded - lastMarkerLoaded
|
|
lastMarkerSpeed = api.prettyFormatBytes(loaded / ((check - lastMarker) / 1000))
|
|
lastMarker = check
|
|
lastMarkerLoaded = e.loaded
|
|
}
|
|
report(request, Math.min(e.loaded / file.size * 100, 100), lastMarkerSpeed)
|
|
})
|
|
request.addEventListener('abort', function(e) {
|
|
finished = true
|
|
window.requestAnimationFrame(m.redraw.bind(m))
|
|
rej()
|
|
})
|
|
|
|
request.addEventListener('readystatechange', function(e) {
|
|
if (finished) return
|
|
if (request.readyState !== 4) return
|
|
|
|
finished = true
|
|
let out = safeParseReponse(request.responseText, request.status, options.url)
|
|
if (out instanceof Error || request.status >= 300) {
|
|
return rej(out)
|
|
}
|
|
return res(out)
|
|
})
|
|
|
|
request.open(options.method || 'POST', options.url)
|
|
request.send(formdata)
|
|
|
|
report(request, 0)
|
|
})
|
|
.then(function(res) {
|
|
clearLoading(request)
|
|
return res
|
|
}, function(err) {
|
|
clearLoading(request)
|
|
return Promise.reject(err)
|
|
})
|
|
},
|
|
}
|
|
|
|
module.exports = api
|