mirror of
https://github.com/BobbyWibowo/lolisafe.git
synced 2025-01-18 17:21:33 +00:00
Added deletion URL for ShareX or derivatives
For registered users only! This requires adding a basic GET API for file deletion, so that I did. Configs which guests download will not include pattern for delete URL, so they won't get notified of unusable delete URL or anything like that. dev: Improved logger.debug() to support specifying options for node's Util.inspect() if an object is set as its last param (assuming >1 params). Default options now also includes enabling colors. src/js/utils.js: Simplified dynamic ShareX config generator. Among other things, it will now use JSON.stringify(). I don't even remember why we didn't use that in the first place.. Some logic improvements in src/js/home.js. Bumped v1 version string and rebuilt client assets.
This commit is contained in:
parent
e552017bfb
commit
51c8df71bc
@ -320,7 +320,7 @@ self.actuallyUploadFiles = async (req, res, user, albumid, age) => {
|
||||
await self.stripTags(req, infoMap)
|
||||
|
||||
const result = await self.storeFilesToDb(req, res, user, infoMap)
|
||||
await self.sendUploadResponse(req, res, result)
|
||||
await self.sendUploadResponse(req, res, user, result)
|
||||
}
|
||||
|
||||
self.actuallyUploadUrls = async (req, res, user, albumid, age) => {
|
||||
@ -418,7 +418,7 @@ self.actuallyUploadUrls = async (req, res, user, albumid, age) => {
|
||||
}
|
||||
|
||||
const result = await self.storeFilesToDb(req, res, user, infoMap)
|
||||
await self.sendUploadResponse(req, res, result)
|
||||
await self.sendUploadResponse(req, res, user, result)
|
||||
} catch (error) {
|
||||
// Unlink all downloaded files when at least one file threw an error from the for-loop
|
||||
// Should continue even when encountering errors
|
||||
@ -544,7 +544,7 @@ self.actuallyFinishChunks = async (req, res, user) => {
|
||||
await self.stripTags(req, infoMap)
|
||||
|
||||
const result = await self.storeFilesToDb(req, res, user, infoMap)
|
||||
await self.sendUploadResponse(req, res, result)
|
||||
await self.sendUploadResponse(req, res, user, result)
|
||||
} catch (error) {
|
||||
// Dispose unfinished hasher and clean up leftover chunks
|
||||
// Should continue even when encountering errors
|
||||
@ -733,7 +733,7 @@ self.storeFilesToDb = async (req, res, user, infoMap) => {
|
||||
return files.concat(exists)
|
||||
}
|
||||
|
||||
self.sendUploadResponse = async (req, res, result) => {
|
||||
self.sendUploadResponse = async (req, res, user, result) => {
|
||||
// Send response
|
||||
res.json({
|
||||
success: true,
|
||||
@ -751,18 +751,35 @@ self.sendUploadResponse = async (req, res, result) => {
|
||||
if (req.path === '/nojs')
|
||||
map.original = file.original
|
||||
|
||||
// If uploaded by user, add delete URL (intended for ShareX and its derivatives)
|
||||
// Homepage uploader will not use this (use dashboard instead)
|
||||
if (user)
|
||||
map.deleteUrl = `${config.homeDomain}/api/upload/delete/${file.name}`
|
||||
|
||||
return map
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
self.delete = async (req, res) => {
|
||||
// Map /delete requests to /bulkdelete route
|
||||
const id = parseInt(req.body.id)
|
||||
const body = {
|
||||
field: 'id',
|
||||
values: isNaN(id) ? undefined : [id]
|
||||
// Map /api/delete requests to /api/bulkdelete
|
||||
let body
|
||||
if (req.method === 'POST') {
|
||||
// Original lolisafe API (this fork uses /api/bulkdelete immediately)
|
||||
const id = parseInt(req.body.id)
|
||||
body = {
|
||||
field: 'id',
|
||||
values: isNaN(id) ? undefined : [id]
|
||||
}
|
||||
} else if (req.method === 'GET') {
|
||||
// ShareX-compatible API (or other clients that require basic GET-based API)
|
||||
const name = req.params.name
|
||||
body = {
|
||||
field: 'name',
|
||||
values: name ? [name] : undefined
|
||||
}
|
||||
}
|
||||
|
||||
req.body = body
|
||||
return self.bulkDelete(req, res)
|
||||
}
|
||||
|
2
dist/js/home.js
vendored
2
dist/js/home.js
vendored
File diff suppressed because one or more lines are too long
2
dist/js/home.js.map
vendored
2
dist/js/home.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/js/misc/utils.js
vendored
2
dist/js/misc/utils.js
vendored
@ -1,2 +1,2 @@
|
||||
lsKeys.siBytes="siBytes",page.prepareShareX=function(){var e=document.querySelector("#ShareX");if(e){var t=page.token?{token:page.token||"",albumid:page.album||""}:{};t.filelength=page.fileLength||"",t.age=page.uploadAge||"",t.striptags=page.stripTags||"";for(var a=[],n=Object.keys(t),r=0;r<n.length;r++)a.push(' "'+n[r]+'": "'+t[n[r]]+'"');var o=(window.location.hostname+window.location.pathname).replace(/\/(dashboard)?$/,""),i=o.replace(/\//g,"_"),s='{\n "Name": "'+i+'",\n "DestinationType": "ImageUploader, FileUploader",\n "RequestMethod": "POST",\n "RequestURL": "'+window.location.protocol+"//"+o+'/api/upload",\n "Headers": {\n'+a.join(",\n")+'\n },\n "Body": "MultipartFormData",\n "FileFormName": "files[]",\n "URL": "$json:files[0].url$",\n "ThumbnailURL": "$json:files[0].url$"\n}',l=new Blob([s],{type:"application/octet-binary"});e.setAttribute("href",URL.createObjectURL(l)),e.setAttribute("download",i+".sxcu")}},page.getPrettyDate=function(e){return e.getFullYear()+"/"+(e.getMonth()<9?"0":"")+(e.getMonth()+1)+"/"+(e.getDate()<10?"0":"")+e.getDate()+" "+(e.getHours()<10?"0":"")+e.getHours()+":"+(e.getMinutes()<10?"0":"")+e.getMinutes()+":"+(e.getSeconds()<10?"0":"")+e.getSeconds()},page.getPrettyBytes=function(e){if("number"!=typeof e&&!isFinite(e))return e;var t="0"!==localStorage[lsKeys.siBytes],a=e<0?"-":"",n=t?1e3:1024;if(a&&(e=-e),e<n)return""+a+e+" B";var r=Math.min(Math.floor(Math.log(e)*Math.LOG10E/3),8);return""+a+Number((e/Math.pow(n,r)).toPrecision(3))+" "+((t?"kMGTPEZY":"KMGTPEZY").charAt(r-1)+(t?"":"i"))+"B"},page.escape=function(e){if(!e)return e;var t,a=String(e),n=/["'&<>]/.exec(a);if(!n)return a;var r="",o=0,i=0;for(o=n.index;o<a.length;o++){switch(a.charCodeAt(o)){case 34:t=""";break;case 38:t="&";break;case 39:t="'";break;case 60:t="<";break;case 62:t=">";break;default:continue}i!==o&&(r+=a.substring(i,o)),i=o+1,r+=t}return i!==o?r+a.substring(i,o):r};
|
||||
lsKeys.siBytes="siBytes",page.prepareShareX=function(){var e=document.querySelector("#ShareX");if(e){var t={};page.token&&(t.token=page.token||"",t.albumid=page.album||""),t.filelength=page.fileLength||"",t.age=page.uploadAge||"",t.striptags=page.stripTags||"";var a=(window.location.hostname+window.location.pathname).replace(/\/(dashboard)?$/,""),r=a.replace(/\//g,"_"),o={Name:r,DestinationType:"ImageUploader, FileUploader",RequestMethod:"POST",RequestURL:window.location.protocol+"//"+a+"/api/upload",Headers:t,Body:"MultipartFromData",FileFormName:"files[]",URL:"$json:files[0].url$",ThumbnailURL:"$json:files[0].url$"};page.token&&(o.DeletionURL="$json:files[0].deleteUrl$");var n=JSON.stringify(o,null,2),i=new Blob([n],{type:"application/octet-binary"});e.setAttribute("href",URL.createObjectURL(i)),e.setAttribute("download",r+".sxcu")}},page.getPrettyDate=function(e){return e.getFullYear()+"/"+(e.getMonth()<9?"0":"")+(e.getMonth()+1)+"/"+(e.getDate()<10?"0":"")+e.getDate()+" "+(e.getHours()<10?"0":"")+e.getHours()+":"+(e.getMinutes()<10?"0":"")+e.getMinutes()+":"+(e.getSeconds()<10?"0":"")+e.getSeconds()},page.getPrettyBytes=function(e){if("number"!=typeof e&&!isFinite(e))return e;var t="0"!==localStorage[lsKeys.siBytes],a=e<0?"-":"",r=t?1e3:1024;if(a&&(e=-e),e<r)return""+a+e+" B";var o=Math.min(Math.floor(Math.log(e)*Math.LOG10E/3),8);return""+a+Number((e/Math.pow(r,o)).toPrecision(3))+" "+((t?"kMGTPEZY":"KMGTPEZY").charAt(o-1)+(t?"":"i"))+"B"},page.escape=function(e){if(!e)return e;var t,a=String(e),r=/["'&<>]/.exec(a);if(!r)return a;var o="",n=0,i=0;for(n=r.index;n<a.length;n++){switch(a.charCodeAt(n)){case 34:t=""";break;case 38:t="&";break;case 39:t="'";break;case 60:t="<";break;case 62:t=">";break;default:continue}i!==n&&(o+=a.substring(i,n)),i=n+1,o+=t}return i!==n?o+a.substring(i,n):o};
|
||||
//# sourceMappingURL=utils.js.map
|
||||
|
2
dist/js/misc/utils.js.map
vendored
2
dist/js/misc/utils.js.map
vendored
File diff suppressed because one or more lines are too long
10
logger.js
10
logger.js
@ -46,8 +46,16 @@ self.error = (content, options = {}) => {
|
||||
}
|
||||
|
||||
self.debug = (...args) => {
|
||||
const options = {
|
||||
colors: true,
|
||||
depth: Infinity
|
||||
}
|
||||
if (args.length > 1 && typeof args[args.length - 1] === 'object') {
|
||||
Object.assign(options, args[args.length - 1])
|
||||
args.splice(args.length - 1, 1)
|
||||
}
|
||||
for (const arg of args)
|
||||
console.log(inspect(arg, { depth: Infinity }))
|
||||
console.log(inspect(arg, options))
|
||||
}
|
||||
|
||||
module.exports = self
|
||||
|
@ -27,6 +27,7 @@ routes.get('/uploads', (req, res, next) => uploadController.list(req, res, next)
|
||||
routes.get('/uploads/:page', (req, res, next) => uploadController.list(req, res, next))
|
||||
routes.post('/upload', (req, res, next) => uploadController.upload(req, res, next))
|
||||
routes.post('/upload/delete', (req, res, next) => uploadController.delete(req, res, next))
|
||||
routes.get('/upload/delete/:name', (req, res, next) => uploadController.delete(req, res, next))
|
||||
routes.post('/upload/bulkdelete', (req, res, next) => uploadController.bulkDelete(req, res, next))
|
||||
routes.post('/upload/finishchunks', (req, res, next) => uploadController.finishChunks(req, res, next))
|
||||
routes.post('/upload/:albumid', (req, res, next) => uploadController.upload(req, res, next))
|
||||
|
@ -767,6 +767,10 @@ page.createAlbum = () => {
|
||||
}
|
||||
|
||||
page.prepareUploadConfig = () => {
|
||||
// This object should only be used to set fallback values for page[key]
|
||||
// (essentially for page[key] properties that explicitly need to be set as something)
|
||||
// As for default values in the Config tab (which will not set page[key]),
|
||||
// check out number.default property of each config
|
||||
const fallback = {
|
||||
chunkSize: page.chunkSizeConfig.default,
|
||||
parallelUploads: 2
|
||||
@ -794,6 +798,7 @@ page.prepareUploadConfig = () => {
|
||||
number: fileIdentifierLength ? {
|
||||
min: page.fileIdentifierLength.min,
|
||||
max: page.fileIdentifierLength.max,
|
||||
default: page.fileIdentifierLength.default,
|
||||
round: true
|
||||
} : undefined,
|
||||
help: true, // true means auto-generated, for number-based configs only
|
||||
@ -822,6 +827,7 @@ page.prepareUploadConfig = () => {
|
||||
number: {
|
||||
min: 1,
|
||||
max: page.chunkSizeConfig.max,
|
||||
default: fallback.chunkSize,
|
||||
suffix: ' MB',
|
||||
round: true
|
||||
},
|
||||
@ -832,6 +838,7 @@ page.prepareUploadConfig = () => {
|
||||
number: {
|
||||
min: 1,
|
||||
max: 10,
|
||||
default: fallback.parallelUploads,
|
||||
round: true
|
||||
},
|
||||
help: true
|
||||
@ -879,7 +886,6 @@ page.prepareUploadConfig = () => {
|
||||
}
|
||||
|
||||
if (fileIdentifierLength) {
|
||||
fallback.fileLength = page.fileIdentifierLength.default || undefined
|
||||
const stored = parseInt(localStorage[lsKeys.fileLength])
|
||||
if (!page.fileIdentifierLength.force &&
|
||||
!isNaN(stored) &&
|
||||
@ -952,7 +958,7 @@ page.prepareUploadConfig = () => {
|
||||
${opts.join('\n')}
|
||||
</select>
|
||||
`
|
||||
} else if (conf.number !== undefined) {
|
||||
} else if (conf.number) {
|
||||
control = document.createElement('input')
|
||||
control.id = control.name = key
|
||||
control.className = 'input is-fullwidth'
|
||||
@ -964,8 +970,8 @@ page.prepareUploadConfig = () => {
|
||||
control.max = conf.number.max
|
||||
if (typeof value === 'number')
|
||||
control.value = value
|
||||
else if (fallback[key] !== undefined)
|
||||
control.value = fallback[key]
|
||||
else if (conf.number.default !== undefined)
|
||||
control.value = conf.number.default
|
||||
}
|
||||
|
||||
let help
|
||||
@ -980,8 +986,8 @@ page.prepareUploadConfig = () => {
|
||||
} else if (conf.help === true && conf.number !== undefined) {
|
||||
const tmp = []
|
||||
|
||||
if (fallback[key] !== undefined)
|
||||
tmp.push(`Default is ${fallback[key]}${conf.number.suffix || ''}.`)
|
||||
if (conf.number.default !== undefined)
|
||||
tmp.push(`Default is ${conf.number.default}${conf.number.suffix || ''}.`)
|
||||
if (conf.number.min !== undefined)
|
||||
tmp.push(`Min is ${conf.number.min}${conf.number.suffix || ''}.`)
|
||||
if (conf.number.max !== undefined)
|
||||
@ -1037,7 +1043,7 @@ page.prepareUploadConfig = () => {
|
||||
value = Math.min(Math.max(parsed, config[key].number.min), config[key].number.max)
|
||||
}
|
||||
|
||||
if (value !== undefined && value !== fallback[key])
|
||||
if (value !== undefined && config[key].number && value !== config[key].number.default)
|
||||
localStorage[lsKeys[key]] = value
|
||||
else
|
||||
localStorage.removeItem(lsKeys[key])
|
||||
|
@ -7,38 +7,37 @@ page.prepareShareX = () => {
|
||||
const sharexElement = document.querySelector('#ShareX')
|
||||
if (!sharexElement) return
|
||||
|
||||
const values = page.token ? {
|
||||
token: page.token || '',
|
||||
albumid: page.album || ''
|
||||
} : {}
|
||||
values.filelength = page.fileLength || ''
|
||||
values.age = page.uploadAge || ''
|
||||
values.striptags = page.stripTags || ''
|
||||
const headers = {}
|
||||
|
||||
const headers = []
|
||||
const keys = Object.keys(values)
|
||||
for (let i = 0; i < keys.length; i++)
|
||||
// Pad by 4 space
|
||||
headers.push(` "${keys[i]}": "${values[keys[i]]}"`)
|
||||
if (page.token) {
|
||||
headers.token = page.token || ''
|
||||
headers.albumid = page.album || ''
|
||||
}
|
||||
|
||||
headers.filelength = page.fileLength || ''
|
||||
headers.age = page.uploadAge || ''
|
||||
headers.striptags = page.stripTags || ''
|
||||
|
||||
const origin = (window.location.hostname + window.location.pathname).replace(/\/(dashboard)?$/, '')
|
||||
const originClean = origin.replace(/\//g, '_')
|
||||
|
||||
const sharexFile = `{
|
||||
"Name": "${originClean}",
|
||||
"DestinationType": "ImageUploader, FileUploader",
|
||||
"RequestMethod": "POST",
|
||||
"RequestURL": "${window.location.protocol}//${origin}/api/upload",
|
||||
"Headers": {
|
||||
${headers.join(',\n')}
|
||||
},
|
||||
"Body": "MultipartFormData",
|
||||
"FileFormName": "files[]",
|
||||
"URL": "$json:files[0].url$",
|
||||
"ThumbnailURL": "$json:files[0].url$"
|
||||
}`
|
||||
const sharexConfObj = {
|
||||
Name: originClean,
|
||||
DestinationType: 'ImageUploader, FileUploader',
|
||||
RequestMethod: 'POST',
|
||||
RequestURL: `${window.location.protocol}//${origin}/api/upload`,
|
||||
Headers: headers,
|
||||
Body: 'MultipartFromData',
|
||||
FileFormName: 'files[]',
|
||||
URL: '$json:files[0].url$',
|
||||
ThumbnailURL: '$json:files[0].url$'
|
||||
}
|
||||
|
||||
const sharexBlob = new Blob([sharexFile], { type: 'application/octet-binary' })
|
||||
if (page.token)
|
||||
sharexConfObj.DeletionURL = '$json:files[0].deleteUrl$'
|
||||
|
||||
const sharexConfStr = JSON.stringify(sharexConfObj, null, 2)
|
||||
const sharexBlob = new Blob([sharexConfStr], { type: 'application/octet-binary' })
|
||||
/* eslint-disable-next-line compat/compat */
|
||||
sharexElement.setAttribute('href', URL.createObjectURL(sharexBlob))
|
||||
sharexElement.setAttribute('download', `${originClean}.sxcu`)
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"1": "1592210320",
|
||||
"1": "1592591301",
|
||||
"2": "1589010026",
|
||||
"3": "1581416390",
|
||||
"4": "1581416390",
|
||||
|
Loading…
Reference in New Issue
Block a user