feat: allow specifying api token in nojs uploader

This commit is contained in:
Bobby 2022-09-21 08:03:28 +07:00
parent 7147afc309
commit 47727f3ca8
No known key found for this signature in database
GPG Key ID: 941839794CBF5A09
3 changed files with 44 additions and 24 deletions

View File

@ -4,6 +4,7 @@ const fs = require('fs')
const path = require('path') const path = require('path')
const randomstring = require('randomstring') const randomstring = require('randomstring')
const searchQuery = require('search-query-parser') const searchQuery = require('search-query-parser')
const auth = require('./authController')
const paths = require('./pathsController') const paths = require('./pathsController')
const perms = require('./permissionController') const perms = require('./permissionController')
const utils = require('./utilsController') const utils = require('./utilsController')
@ -263,12 +264,16 @@ self.parseStripTags = stripTags => {
/** File uploads */ /** File uploads */
self.upload = async (req, res) => { self.upload = async (req, res) => {
// Assert Request type // Assert Request type (skip for POST /nojs requests)
// Multipart for regular uploads, JSON for URL uploads let isMultipart = req.locals.nojs
const isMultipart = req.is('multipart/form-data') let isJson
const isJson = req.is('application/json') if (!req.locals.nojs) {
if (!isMultipart && !isJson) { // Multipart for regular uploads, JSON for URL uploads
throw new ClientError('Request Content-Type must be either multipart/form-data or application/json.') isMultipart = req.is('multipart/form-data')
isJson = req.is('application/json')
if (!isMultipart && !isJson) {
throw new ClientError('Request Content-Type must be either multipart/form-data or application/json.')
}
} }
if (config.privateUploadGroup) { if (config.privateUploadGroup) {
@ -513,6 +518,18 @@ self.actuallyUpload = async (req, res, data = {}) => {
return res.json({ success: true }) return res.json({ success: true })
} }
// If POST /nojs requests, additionally attempt to parse token from form input
if (req.locals.nojs) {
await new Promise((resolve, reject) => {
auth.optionalUser(req, res, error => {
if (error) return reject(error)
return resolve()
}, {
token: req.body.token
})
})
}
const filesData = req.files const filesData = req.files
if (utils.scan.instance) { if (utils.scan.instance) {

View File

@ -11,9 +11,16 @@ routes.get('/nojs', async (req, res) => {
}) })
// HyperExpress defaults to 250kb // HyperExpress defaults to 250kb
// https://github.com/kartikk221/hyper-express/blob/6.4.4/docs/Server.md#server-constructor-options // https://github.com/kartikk221/hyper-express/blob/6.4.8/docs/Server.md#server-constructor-options
const maxBodyLength = parseInt(config.uploads.maxSize) * 1e6 routes.post('/nojs', {
routes.post('/nojs', { max_body_length: maxBodyLength }, async (req, res) => { max_body_length: parseInt(config.uploads.maxSize) * 1e6,
middlewares: [
async (req, res) => {
// Assert Request type early
utils.assertRequestType(req, 'multipart/form-data')
}
]
}, async (req, res) => {
// Map built-in Response.json() function into Response.render() accordingly // Map built-in Response.json() function into Response.render() accordingly
// Since NoJS uploader needs to reply with a complete HTML page // Since NoJS uploader needs to reply with a complete HTML page
res._json = res.json res._json = res.json
@ -27,6 +34,11 @@ routes.post('/nojs', { max_body_length: maxBodyLength }, async (req, res) => {
files: result.files || [{}] files: result.files || [{}]
}) })
} }
// Indicate uploadController.js to additionally process this request further
// (skip request type assertion, parse token from form input, etc.)
req.locals.nojs = true
return upload.upload(req, res) return upload.upload(req, res)
}) })

View File

@ -3,14 +3,6 @@
{% extends "_layout.njk" %} {% extends "_layout.njk" %}
{% set private = config.private %}
{% set disabledMessage -%}
{%- if config.enableUserAccounts -%}
Anonymous upload is disabled. Log in to upload.
{%- else -%}
Running in private mode. Log in to upload.
{%- endif %}
{%- endset %}
{% set maxSizeInt = config.uploads.maxSize | int %} {% set maxSizeInt = config.uploads.maxSize | int %}
{% set noJsMaxSizeInt = config.cloudflare.noJsMaxSize | int %} {% set noJsMaxSizeInt = config.cloudflare.noJsMaxSize | int %}
@ -37,27 +29,26 @@
<div class="columns is-gapless"> <div class="columns is-gapless">
<div class="column is-hidden-mobile"></div> <div class="column is-hidden-mobile"></div>
<div class="column"> <div class="column">
{% if private -%}
<a class="button is-danger is-outlined is-fullwidth" href="auth">
{{ disabledMessage }}
</a>
{%- else -%}
<form id="form" class="field" action="" method="post" enctype="multipart/form-data"> <form id="form" class="field" action="" method="post" enctype="multipart/form-data">
<div class="field"> <div class="field">
<p class="control"> <p class="control">
<input type="file" class="is-fullwidth" name="files[]" multiple="multiple" required="required"> <input type="file" class="is-fullwidth" name="files[]" multiple="multiple" required="required">
</p> </p>
</div> </div>
<div class="field">
<p class="control">
<input type="text" class="input is-fullwidth" name="token" minLength="64" maxLength="64" placeholder="API token ({{ "required" if config.private else "optional"}})"{% if config.private %} required="required"{% endif %}>
</p>
</div>
<div class="field"> <div class="field">
<p class="control"> <p class="control">
<input type="submit" class="button is-danger is-outlined is-fullwidth" value="Upload"> <input type="submit" class="button is-danger is-outlined is-fullwidth" value="Upload">
</p> </p>
<p class="help"> <p class="help">
Files uploaded through this form will not be associated with your account, if you have any. Files uploaded through this form will only be associated with your account if you specify your API token.
</p> </p>
</div> </div>
</form> </form>
{%- endif %}
{% if files -%} {% if files -%}
<div class="field uploads nojs"> <div class="field uploads nojs">
{% for file in files -%} {% for file in files -%}