mirror of
https://github.com/BobbyWibowo/lolisafe.git
synced 2025-01-18 17:21:33 +00:00
Init account-manager branch
This commit is contained in:
parent
0745456cac
commit
c3d4c237cb
@ -204,9 +204,12 @@ albumsController.edit = async (req, res, next) => {
|
||||
})
|
||||
.first()
|
||||
|
||||
if (album && (album.id !== id)) {
|
||||
if (!album) {
|
||||
return res.json({ success: false, description: 'Could not get album with the specified ID.' })
|
||||
} else if (album.id !== id) {
|
||||
return res.json({ success: false, description: 'Name already in use.' })
|
||||
} else if (req._old && (album.id === id)) {
|
||||
// Old rename API
|
||||
return res.json({ success: false, description: 'You did not specify a new name.' })
|
||||
}
|
||||
|
||||
@ -266,7 +269,7 @@ albumsController.rename = async (req, res, next) => {
|
||||
}
|
||||
|
||||
albumsController.get = async (req, res, next) => {
|
||||
// TODO:
|
||||
// TODO: ... what was i supposed to do here?
|
||||
const identifier = req.params.identifier
|
||||
if (identifier === undefined) {
|
||||
return res.status(401).json({ success: false, description: 'No identifier provided.' })
|
||||
|
@ -6,6 +6,35 @@ const utils = require('./utilsController')
|
||||
|
||||
const authController = {}
|
||||
|
||||
authController.permissions = {
|
||||
user: 0, // upload & delete own files, create & delete albums
|
||||
moderator: 50, // delete other user's files
|
||||
admin: 80, // manage users (disable accounts) & create moderators
|
||||
superadmin: 100 // create admins
|
||||
// groups will inherit permissions from groups which have lower value
|
||||
}
|
||||
|
||||
authController.is = (user, group) => {
|
||||
// root bypass
|
||||
if (user.username === 'root') { return true }
|
||||
const permission = user.permission || 0
|
||||
return permission >= authController.permissions[group]
|
||||
}
|
||||
|
||||
authController.higher = (user, target) => {
|
||||
const userPermission = user.permission || 0
|
||||
const targetPermission = target.permission || 0
|
||||
return userPermission > targetPermission
|
||||
}
|
||||
|
||||
authController.mapPermissions = user => {
|
||||
const map = {}
|
||||
Object.keys(authController.permissions).forEach(group => {
|
||||
map[group] = authController.is(user, group)
|
||||
})
|
||||
return map
|
||||
}
|
||||
|
||||
authController.verify = async (req, res, next) => {
|
||||
const username = req.body.username
|
||||
const password = req.body.password
|
||||
@ -14,7 +43,9 @@ authController.verify = async (req, res, next) => {
|
||||
if (password === undefined) { return res.json({ success: false, description: 'No password provided.' }) }
|
||||
|
||||
const user = await db.table('users').where('username', username).first()
|
||||
if (!user) { return res.json({ success: false, description: 'Username doesn\'t exist.' }) }
|
||||
if (!user) {
|
||||
return res.json({ success: false, description: 'Username doesn\'t exist.' })
|
||||
}
|
||||
if (user.enabled === false || user.enabled === 0) {
|
||||
return res.json({ success: false, description: 'This account has been disabled.' })
|
||||
}
|
||||
@ -60,7 +91,8 @@ authController.register = async (req, res, next) => {
|
||||
username,
|
||||
password: hash,
|
||||
token,
|
||||
enabled: 1
|
||||
enabled: 1,
|
||||
permission: authController.permissions.user
|
||||
})
|
||||
return res.json({ success: true, token })
|
||||
})
|
||||
@ -94,23 +126,43 @@ authController.changePassword = async (req, res, next) => {
|
||||
authController.getFileLengthConfig = async (req, res, next) => {
|
||||
const user = await utils.authorize(req, res)
|
||||
if (!user) { return }
|
||||
return res.json({ success: true, fileLength: user.fileLength, config: config.uploads.fileLength })
|
||||
return res.json({
|
||||
success: true,
|
||||
fileLength: user.fileLength,
|
||||
config: config.uploads.fileLength
|
||||
})
|
||||
}
|
||||
|
||||
authController.changeFileLength = async (req, res, next) => {
|
||||
if (config.uploads.fileLength.userChangeable === false) {
|
||||
return res.json({ success: false, description: 'Changing file name length is disabled at the moment.' })
|
||||
return res.json({
|
||||
success: false,
|
||||
description: 'Changing file name length is disabled at the moment.'
|
||||
})
|
||||
}
|
||||
|
||||
const user = await utils.authorize(req, res)
|
||||
if (!user) { return }
|
||||
|
||||
const fileLength = parseInt(req.body.fileLength)
|
||||
if (fileLength === undefined) { return res.json({ success: false, description: 'No file name length provided.' }) }
|
||||
if (isNaN(fileLength)) { return res.json({ success: false, description: 'File name length is not a valid number.' }) }
|
||||
if (fileLength === undefined) {
|
||||
return res.json({
|
||||
success: false,
|
||||
description: 'No file name length provided.'
|
||||
})
|
||||
}
|
||||
if (isNaN(fileLength)) {
|
||||
return res.json({
|
||||
success: false,
|
||||
description: 'File name length is not a valid number.'
|
||||
})
|
||||
}
|
||||
|
||||
if (fileLength < config.uploads.fileLength.min || fileLength > config.uploads.fileLength.max) {
|
||||
return res.json({ success: false, description: `File name length must be ${config.uploads.fileLength.min} to ${config.uploads.fileLength.max} characters.` })
|
||||
return res.json({
|
||||
success: false,
|
||||
description: `File name length must be ${config.uploads.fileLength.min} to ${config.uploads.fileLength.max} characters.`
|
||||
})
|
||||
}
|
||||
|
||||
if (fileLength === user.fileLength) {
|
||||
@ -124,4 +176,80 @@ authController.changeFileLength = async (req, res, next) => {
|
||||
return res.json({ success: true })
|
||||
}
|
||||
|
||||
authController.editUser = async (req, res, next) => {
|
||||
const user = await utils.authorize(req, res)
|
||||
if (!user) { return }
|
||||
|
||||
const id = parseInt(req.body.id)
|
||||
if (isNaN(id)) {
|
||||
return res.json({ success: false, description: 'No user specified.' })
|
||||
}
|
||||
|
||||
const target = await db.table('users')
|
||||
.where('id', id)
|
||||
.first()
|
||||
|
||||
if (!target) {
|
||||
return res.json({ success: false, description: 'Could not get user with the specified ID.' })
|
||||
} else if (!authController.higher(user, target)) {
|
||||
return res.json({ success: false, description: 'The user is in the same or higher group as you.' })
|
||||
} else if (target.username === 'root') {
|
||||
return res.json({ success: false, description: 'Root user may not be edited.' })
|
||||
}
|
||||
|
||||
const username = String(req.body.username)
|
||||
if (username.length < 4 || username.length > 32) {
|
||||
return res.json({ success: false, description: 'Username must have 4-32 characters.' })
|
||||
}
|
||||
|
||||
await db.table('users')
|
||||
.where('id', id)
|
||||
.update({
|
||||
username,
|
||||
enabled: Boolean(req.body.enabled)
|
||||
})
|
||||
|
||||
if (!req.body.resetPassword) {
|
||||
return res.json({ success: true, username })
|
||||
}
|
||||
|
||||
const password = randomstring.generate(16)
|
||||
bcrypt.hash(password, 10, async (error, hash) => {
|
||||
if (error) {
|
||||
console.error(error)
|
||||
return res.json({ success: false, description: 'Error generating password hash (╯°□°)╯︵ ┻━┻.' })
|
||||
}
|
||||
|
||||
await db.table('users')
|
||||
.where('id', id)
|
||||
.update('password', hash)
|
||||
|
||||
return res.json({ success: true, password })
|
||||
})
|
||||
}
|
||||
|
||||
authController.listUsers = async (req, res, next) => {
|
||||
const user = await utils.authorize(req, res)
|
||||
if (!user) { return }
|
||||
|
||||
const isadmin = authController.is(user, 'admin')
|
||||
if (!isadmin) { return res.status(403) }
|
||||
|
||||
let offset = req.params.page
|
||||
if (offset === undefined) { offset = 0 }
|
||||
|
||||
const users = await db.table('users')
|
||||
// .orderBy('id', 'DESC')
|
||||
.limit(25)
|
||||
.offset(25 * offset)
|
||||
.select('id', 'username', 'enabled', 'fileLength', 'permission')
|
||||
|
||||
for (const user of users) {
|
||||
user.groups = authController.mapPermissions(user)
|
||||
delete user.permission
|
||||
}
|
||||
|
||||
return res.json({ success: true, users })
|
||||
}
|
||||
|
||||
module.exports = authController
|
||||
|
@ -1,3 +1,4 @@
|
||||
const auth = require('./authController')
|
||||
const config = require('./../config')
|
||||
const db = require('knex')(config.database)
|
||||
const randomstring = require('randomstring')
|
||||
@ -7,17 +8,35 @@ const tokenController = {}
|
||||
|
||||
tokenController.verify = async (req, res, next) => {
|
||||
const token = req.body.token
|
||||
if (token === undefined) { return res.status(401).json({ success: false, description: 'No token provided.' }) }
|
||||
if (token === undefined) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
description: 'No token provided.'
|
||||
})
|
||||
}
|
||||
|
||||
const user = await db.table('users').where('token', token).first()
|
||||
if (!user) { return res.status(401).json({ success: false, description: 'Invalid token.' }) }
|
||||
return res.json({ success: true, username: user.username })
|
||||
if (!user) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
description: 'Invalid token.'
|
||||
})
|
||||
}
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
username: user.username,
|
||||
permissions: auth.mapPermissions(user)
|
||||
})
|
||||
}
|
||||
|
||||
tokenController.list = async (req, res, next) => {
|
||||
const user = await utils.authorize(req, res)
|
||||
if (!user) { return }
|
||||
return res.json({ success: true, token: user.token })
|
||||
return res.json({
|
||||
success: true,
|
||||
token: user.token
|
||||
})
|
||||
}
|
||||
|
||||
tokenController.change = async (req, res, next) => {
|
||||
@ -30,7 +49,10 @@ tokenController.change = async (req, res, next) => {
|
||||
timestamp: Math.floor(Date.now() / 1000)
|
||||
})
|
||||
|
||||
res.json({ success: true, token: newtoken })
|
||||
res.json({
|
||||
success: true,
|
||||
token: newtoken
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = tokenController
|
||||
|
@ -1,3 +1,4 @@
|
||||
const auth = require('./authController')
|
||||
const config = require('./../config')
|
||||
const crypto = require('crypto')
|
||||
const db = require('knex')(config.database)
|
||||
@ -649,6 +650,11 @@ uploadsController.list = async (req, res) => {
|
||||
let offset = req.params.page
|
||||
if (offset === undefined) { offset = 0 }
|
||||
|
||||
// Headers is string-only, this seem to be the safest and lightest
|
||||
const all = req.headers.all === '1'
|
||||
const ismoderator = auth.is(user, 'moderator')
|
||||
if (all && !ismoderator) { return res.json(403) }
|
||||
|
||||
const files = await db.table('files')
|
||||
.where(function () {
|
||||
if (req.params.id === undefined) {
|
||||
@ -658,7 +664,9 @@ uploadsController.list = async (req, res) => {
|
||||
}
|
||||
})
|
||||
.where(function () {
|
||||
if (user.username !== 'root') { this.where('userid', user.id) }
|
||||
if (!all || !ismoderator) {
|
||||
this.where('userid', user.id)
|
||||
}
|
||||
})
|
||||
.orderBy('id', 'DESC')
|
||||
.limit(25)
|
||||
@ -668,7 +676,7 @@ uploadsController.list = async (req, res) => {
|
||||
const albums = await db.table('albums')
|
||||
.where(function () {
|
||||
this.where('enabled', 1)
|
||||
if (user.username !== 'root') {
|
||||
if (!all || !ismoderator) {
|
||||
this.where('userid', user.id)
|
||||
}
|
||||
})
|
||||
@ -688,8 +696,8 @@ uploadsController.list = async (req, res) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Only push usernames if we are root
|
||||
if (user.username === 'root') {
|
||||
// Only push usernames if we are a moderator
|
||||
if (all && ismoderator) {
|
||||
if (file.userid !== undefined && file.userid !== null && file.userid !== '') {
|
||||
userids.push(file.userid)
|
||||
}
|
||||
@ -702,13 +710,12 @@ uploadsController.list = async (req, res) => {
|
||||
}
|
||||
|
||||
// If we are a normal user, send response
|
||||
if (user.username !== 'root') { return res.json({ success: true, files }) }
|
||||
if (!ismoderator) { return res.json({ success: true, files }) }
|
||||
|
||||
// If we are root but there are no uploads attached to a user, send response
|
||||
// If we are a moderator but there are no uploads attached to a user, send response
|
||||
if (userids.length === 0) { return res.json({ success: true, files }) }
|
||||
|
||||
const users = await db.table('users')
|
||||
.whereIn('id', userids)
|
||||
const users = await db.table('users').whereIn('id', userids)
|
||||
for (const dbUser of users) {
|
||||
for (const file of files) {
|
||||
if (file.userid === dbUser.id) {
|
||||
|
@ -57,9 +57,18 @@ utilsController.authorize = async (req, res) => {
|
||||
}
|
||||
|
||||
const user = await db.table('users').where('token', token).first()
|
||||
if (user) { return user }
|
||||
if (user) {
|
||||
if (user.enabled === false || user.enabled === 0) {
|
||||
res.json({ success: false, description: 'This account has been disabled.' })
|
||||
return
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
res.status(401).json({ success: false, description: 'Invalid token.' })
|
||||
res.status(401).json({
|
||||
success: false,
|
||||
description: 'Invalid token.'
|
||||
})
|
||||
}
|
||||
|
||||
utilsController.generateThumbs = (name, force) => {
|
||||
|
@ -1,3 +1,5 @@
|
||||
const { permissions } = require('./../controllers/authController')
|
||||
|
||||
const init = function (db) {
|
||||
// Create the tables we need to store galleries and files
|
||||
db.schema.hasTable('albums').then(exists => {
|
||||
@ -44,6 +46,7 @@ const init = function (db) {
|
||||
table.integer('enabled')
|
||||
table.integer('timestamp')
|
||||
table.integer('fileLength')
|
||||
table.integer('permission')
|
||||
}).then(() => {
|
||||
db.table('users').where({ username: 'root' }).then((user) => {
|
||||
if (user.length > 0) { return }
|
||||
@ -55,7 +58,8 @@ const init = function (db) {
|
||||
username: 'root',
|
||||
password: hash,
|
||||
token: require('randomstring').generate(64),
|
||||
timestamp: Math.floor(Date.now() / 1000)
|
||||
timestamp: Math.floor(Date.now() / 1000),
|
||||
permission: permissions.superadmin
|
||||
}).then(() => {})
|
||||
})
|
||||
})
|
||||
|
@ -1,5 +1,6 @@
|
||||
const config = require('./../config')
|
||||
const db = require('knex')(config.database)
|
||||
const { permissions } = require('./../controllers/authController')
|
||||
|
||||
const map = {
|
||||
albums: {
|
||||
@ -10,7 +11,8 @@ const map = {
|
||||
},
|
||||
users: {
|
||||
enabled: 'integer',
|
||||
fileLength: 'integer'
|
||||
fileLength: 'integer',
|
||||
permission: 'integer'
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,6 +32,14 @@ migration.start = async () => {
|
||||
}))
|
||||
}))
|
||||
|
||||
await db.table('users')
|
||||
.where('username', 'root')
|
||||
.first()
|
||||
.update({
|
||||
permission: permissions.superadmin
|
||||
})
|
||||
.then(() => console.log(`Updated root's permission to ${permissions.superadmin} (superadmin).`))
|
||||
|
||||
console.log('Migration finished! Now start lolisafe normally')
|
||||
process.exit(0)
|
||||
}
|
||||
|
13
lolisafe.js
13
lolisafe.js
@ -32,9 +32,12 @@ fs.existsSync(`./${config.uploads.folder}/zips`) || fs.mkdirSync(`./${config.upl
|
||||
safe.use(helmet())
|
||||
safe.set('trust proxy', 1)
|
||||
|
||||
// https://mozilla.github.io/nunjucks/api.html#configure
|
||||
nunjucks.configure('views', {
|
||||
autoescape: true,
|
||||
express: safe
|
||||
express: safe,
|
||||
// watch: true, // will this be fine in production?
|
||||
noCache: process.env.DEV === '1'
|
||||
})
|
||||
safe.set('view engine', 'njk')
|
||||
safe.enable('view cache')
|
||||
@ -127,7 +130,13 @@ const start = async () => {
|
||||
if (!created) { return process.exit(1) }
|
||||
}
|
||||
|
||||
safe.listen(config.port, () => console.log(`lolisafe started on port ${config.port}`))
|
||||
safe.listen(config.port, () => {
|
||||
console.log(`lolisafe started on port ${config.port}`)
|
||||
if (process.env.DEV === '1') {
|
||||
// DEV=1 yarn start
|
||||
console.log('lolisafe is in development mode, nunjucks caching disabled')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
start()
|
||||
|
@ -9,6 +9,11 @@
|
||||
|
||||
.menu-list a {
|
||||
color: #3794d2;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.menu-list a:hover {
|
||||
@ -21,6 +26,15 @@
|
||||
background-color: #3794d2;
|
||||
}
|
||||
|
||||
.menu-list a[disabled] {
|
||||
color: #7a7a7a;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.menu-list a[disabled]:hover {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.pagination a {
|
||||
color: #eff0f1;
|
||||
border-color: #4d4d4d;
|
||||
@ -71,7 +85,7 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.image-container .file-checkbox {
|
||||
.image-container .checkbox {
|
||||
position: absolute;
|
||||
top: .75rem;
|
||||
left: .75rem;
|
||||
@ -118,7 +132,7 @@
|
||||
|
||||
/* Make extra info appear on hover only on non-touch devices */
|
||||
|
||||
.no-touch .image-container .file-checkbox {
|
||||
.no-touch .image-container .checkbox {
|
||||
opacity: .25;
|
||||
-webkit-transition: opacity .25s;
|
||||
transition: opacity .25s;
|
||||
@ -131,7 +145,7 @@
|
||||
transition: opacity .25s;
|
||||
}
|
||||
|
||||
.no-touch .image-container:hover .file-checkbox,
|
||||
.no-touch .image-container:hover .checkbox,
|
||||
.no-touch .image-container:hover .controls,
|
||||
.no-touch .image-container:hover .details {
|
||||
opacity: 1;
|
||||
@ -180,6 +194,6 @@
|
||||
height: 2.25em;
|
||||
}
|
||||
|
||||
.table td a:not([href]) {
|
||||
text-decoration: line-through;
|
||||
.is-linethrough {
|
||||
text-decoration: line-through
|
||||
}
|
||||
|
@ -18,6 +18,13 @@
|
||||
color: #bdc3c7;
|
||||
}
|
||||
|
||||
.swal-content .is-code {
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
border: 1px dashed #eff0f1;
|
||||
border-radius: 5px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.swal-button {
|
||||
background-color: #3794d2;
|
||||
color: #eff0f1;
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"root": true,
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 5
|
||||
"ecmaVersion": 6
|
||||
},
|
||||
"env": {
|
||||
"browser": true
|
||||
@ -17,6 +17,17 @@
|
||||
"quotes": [
|
||||
"error",
|
||||
"single"
|
||||
],
|
||||
"object-shorthand": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"prefer-const": [
|
||||
"error",
|
||||
{
|
||||
"destructuring": "any",
|
||||
"ignoreReadBeforeAssign": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* global LazyLoad */
|
||||
|
||||
var page = {}
|
||||
const page = {}
|
||||
|
||||
window.onload = function () {
|
||||
page.lazyLoad = new LazyLoad()
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* global swal, axios */
|
||||
|
||||
var page = {
|
||||
const page = {
|
||||
// user token
|
||||
token: localStorage.token,
|
||||
|
||||
@ -10,33 +10,31 @@ var page = {
|
||||
}
|
||||
|
||||
page.do = function (dest) {
|
||||
var user = page.user.value
|
||||
var pass = page.pass.value
|
||||
const user = page.user.value
|
||||
const pass = page.pass.value
|
||||
|
||||
if (!user) {
|
||||
return swal('Error', 'You need to specify a username', 'error')
|
||||
return swal('An error occurred!', 'You need to specify a username', 'error')
|
||||
}
|
||||
|
||||
if (!pass) {
|
||||
return swal('Error', 'You need to specify a username', 'error')
|
||||
return swal('An error occurred!', 'You need to specify a username', 'error')
|
||||
}
|
||||
|
||||
axios.post('api/' + dest, {
|
||||
axios.post(`api/${dest}`, {
|
||||
username: user,
|
||||
password: pass
|
||||
})
|
||||
.then(function (response) {
|
||||
if (response.data.success === false) {
|
||||
return swal('Error', response.data.description, 'error')
|
||||
}
|
||||
}).then(function (response) {
|
||||
if (response.data.success === false) {
|
||||
return swal('An error occurred!', response.data.description, 'error')
|
||||
}
|
||||
|
||||
localStorage.token = response.data.token
|
||||
window.location = 'dashboard'
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.error(error)
|
||||
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
|
||||
})
|
||||
localStorage.token = response.data.token
|
||||
window.location = 'dashboard'
|
||||
}).catch(function (error) {
|
||||
console.error(error)
|
||||
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
|
||||
})
|
||||
}
|
||||
|
||||
page.verify = function () {
|
||||
@ -44,18 +42,16 @@ page.verify = function () {
|
||||
|
||||
axios.post('api/tokens/verify', {
|
||||
token: page.token
|
||||
})
|
||||
.then(function (response) {
|
||||
if (response.data.success === false) {
|
||||
return swal('Error', response.data.description, 'error')
|
||||
}
|
||||
}).then(function (response) {
|
||||
if (response.data.success === false) {
|
||||
return swal('An error occurred!', response.data.description, 'error')
|
||||
}
|
||||
|
||||
window.location = 'dashboard'
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.error(error)
|
||||
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
|
||||
})
|
||||
window.location = 'dashboard'
|
||||
}).catch(function (error) {
|
||||
console.error(error)
|
||||
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
|
||||
})
|
||||
}
|
||||
|
||||
window.onload = function () {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
/* global swal, axios, Dropzone, ClipboardJS, LazyLoad */
|
||||
|
||||
var page = {
|
||||
const page = {
|
||||
// user token
|
||||
token: localStorage.token,
|
||||
|
||||
@ -21,24 +21,22 @@ var page = {
|
||||
lazyLoad: null
|
||||
}
|
||||
|
||||
var imageExtensions = ['.webp', '.jpg', '.jpeg', '.bmp', '.gif', '.png']
|
||||
const imageExtensions = ['.webp', '.jpg', '.jpeg', '.bmp', '.gif', '.png']
|
||||
|
||||
page.checkIfPublic = function () {
|
||||
axios.get('api/check')
|
||||
.then(function (response) {
|
||||
page.private = response.data.private
|
||||
page.enableUserAccounts = response.data.enableUserAccounts
|
||||
page.maxFileSize = response.data.maxFileSize
|
||||
page.chunkSize = response.data.chunkSize
|
||||
page.preparePage()
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.log(error)
|
||||
var button = document.getElementById('loginToUpload')
|
||||
button.classList.remove('is-loading')
|
||||
button.innerText = 'Error occurred. Reload the page?'
|
||||
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
|
||||
})
|
||||
axios.get('api/check').then(function (response) {
|
||||
page.private = response.data.private
|
||||
page.enableUserAccounts = response.data.enableUserAccounts
|
||||
page.maxFileSize = response.data.maxFileSize
|
||||
page.chunkSize = response.data.chunkSize
|
||||
page.preparePage()
|
||||
}).catch(function (error) {
|
||||
console.log(error)
|
||||
const button = document.getElementById('loginToUpload')
|
||||
button.classList.remove('is-loading')
|
||||
button.innerText = 'Error occurred. Reload the page?'
|
||||
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
|
||||
})
|
||||
}
|
||||
|
||||
page.preparePage = function () {
|
||||
@ -46,7 +44,7 @@ page.preparePage = function () {
|
||||
if (page.token) {
|
||||
return page.verifyToken(page.token, true)
|
||||
} else {
|
||||
var button = document.getElementById('loginToUpload')
|
||||
const button = document.getElementById('loginToUpload')
|
||||
button.href = 'auth'
|
||||
button.classList.remove('is-loading')
|
||||
|
||||
@ -64,29 +62,26 @@ page.preparePage = function () {
|
||||
page.verifyToken = function (token, reloadOnError) {
|
||||
if (reloadOnError === undefined) { reloadOnError = false }
|
||||
|
||||
axios.post('api/tokens/verify', { token: token })
|
||||
.then(function (response) {
|
||||
if (response.data.success === false) {
|
||||
return swal({
|
||||
title: 'An error occurred!',
|
||||
text: response.data.description,
|
||||
icon: 'error'
|
||||
})
|
||||
.then(function () {
|
||||
if (!reloadOnError) { return }
|
||||
localStorage.removeItem('token')
|
||||
location.reload()
|
||||
})
|
||||
}
|
||||
axios.post('api/tokens/verify', { token }).then(function (response) {
|
||||
if (response.data.success === false) {
|
||||
return swal({
|
||||
title: 'An error occurred!',
|
||||
text: response.data.description,
|
||||
icon: 'error'
|
||||
}).then(function () {
|
||||
if (!reloadOnError) { return }
|
||||
localStorage.removeItem('token')
|
||||
location.reload()
|
||||
})
|
||||
}
|
||||
|
||||
localStorage.token = token
|
||||
page.token = token
|
||||
return page.prepareUpload()
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.log(error)
|
||||
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
|
||||
})
|
||||
localStorage.token = token
|
||||
page.token = token
|
||||
return page.prepareUpload()
|
||||
}).catch(function (error) {
|
||||
console.log(error)
|
||||
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
|
||||
})
|
||||
}
|
||||
|
||||
page.prepareUpload = function () {
|
||||
@ -104,24 +99,24 @@ page.prepareUpload = function () {
|
||||
document.getElementById('albumDiv').style.display = 'flex'
|
||||
}
|
||||
|
||||
document.getElementById('maxFileSize').innerHTML = 'Maximum upload size per file is ' + page.maxFileSize
|
||||
document.getElementById('maxFileSize').innerHTML = `Maximum upload size per file is ${page.maxFileSize}`
|
||||
document.getElementById('loginToUpload').style.display = 'none'
|
||||
|
||||
if (!page.token && page.enableUserAccounts) {
|
||||
document.getElementById('loginLinkText').innerHTML = 'Create an account and keep track of your uploads'
|
||||
}
|
||||
|
||||
var previewNode = document.querySelector('#tpl')
|
||||
const previewNode = document.querySelector('#tpl')
|
||||
page.previewTemplate = previewNode.innerHTML
|
||||
previewNode.parentNode.removeChild(previewNode)
|
||||
|
||||
page.prepareDropzone()
|
||||
|
||||
var tabs = document.getElementById('tabs')
|
||||
const tabs = document.getElementById('tabs')
|
||||
if (tabs) {
|
||||
tabs.style.display = 'flex'
|
||||
var items = tabs.getElementsByTagName('li')
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
const items = tabs.getElementsByTagName('li')
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
items[i].addEventListener('click', function () {
|
||||
page.setActiveTab(this.dataset.id)
|
||||
})
|
||||
@ -136,42 +131,44 @@ page.prepareUpload = function () {
|
||||
}
|
||||
|
||||
page.prepareAlbums = function () {
|
||||
var option = document.createElement('option')
|
||||
const option = document.createElement('option')
|
||||
option.value = ''
|
||||
option.innerHTML = 'Upload to album'
|
||||
option.disabled = true
|
||||
option.selected = true
|
||||
page.albumSelect.appendChild(option)
|
||||
|
||||
axios.get('api/albums', { headers: { token: page.token } })
|
||||
.then(function (response) {
|
||||
if (response.data.success === false) {
|
||||
return swal('An error occurred!', response.data.description, 'error')
|
||||
}
|
||||
axios.get('api/albums', {
|
||||
headers: {
|
||||
token: page.token
|
||||
}
|
||||
}).then(function (response) {
|
||||
if (response.data.success === false) {
|
||||
return swal('An error occurred!', response.data.description, 'error')
|
||||
}
|
||||
|
||||
// If the user doesn't have any albums we don't really need to display
|
||||
// an album selection
|
||||
if (!response.data.albums.length) { return }
|
||||
// If the user doesn't have any albums we don't really need to display
|
||||
// an album selection
|
||||
if (!response.data.albums.length) { return }
|
||||
|
||||
// Loop through the albums and create an option for each album
|
||||
for (var i = 0; i < response.data.albums.length; i++) {
|
||||
var album = response.data.albums[i]
|
||||
var option = document.createElement('option')
|
||||
option.value = album.id
|
||||
option.innerHTML = album.name
|
||||
page.albumSelect.appendChild(option)
|
||||
}
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.log(error)
|
||||
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
|
||||
})
|
||||
// Loop through the albums and create an option for each album
|
||||
for (let i = 0; i < response.data.albums.length; i++) {
|
||||
const album = response.data.albums[i]
|
||||
const option = document.createElement('option')
|
||||
option.value = album.id
|
||||
option.innerHTML = album.name
|
||||
page.albumSelect.appendChild(option)
|
||||
}
|
||||
}).catch(function (error) {
|
||||
console.log(error)
|
||||
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
|
||||
})
|
||||
}
|
||||
|
||||
page.setActiveTab = function (activeId) {
|
||||
var items = document.getElementById('tabs').getElementsByTagName('li')
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
var tabId = items[i].dataset.id
|
||||
const items = document.getElementById('tabs').getElementsByTagName('li')
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const tabId = items[i].dataset.id
|
||||
if (tabId === activeId) {
|
||||
items[i].classList.add('is-active')
|
||||
document.getElementById(tabId).style.display = 'block'
|
||||
@ -183,27 +180,28 @@ page.setActiveTab = function (activeId) {
|
||||
}
|
||||
|
||||
page.prepareDropzone = function () {
|
||||
var tabDiv = document.getElementById('tab-files')
|
||||
var div = document.createElement('div')
|
||||
const tabDiv = document.getElementById('tab-files')
|
||||
const div = document.createElement('div')
|
||||
div.className = 'control is-expanded'
|
||||
div.innerHTML =
|
||||
'<div id="dropzone" class="button is-danger is-fullwidth is-unselectable">\n' +
|
||||
' <span class="icon">\n' +
|
||||
' <i class="icon-upload-cloud"></i>\n' +
|
||||
' </span>\n' +
|
||||
' <span>Click here or drag and drop files</span>\n' +
|
||||
'</div>'
|
||||
div.innerHTML = `
|
||||
<div id="dropzone" class="button is-danger is-fullwidth is-unselectable">
|
||||
<span class="icon">
|
||||
<i class="icon-upload-cloud"></i>
|
||||
</span>
|
||||
<span>Click here or drag and drop files</span>
|
||||
</div>
|
||||
`
|
||||
|
||||
tabDiv.getElementsByClassName('dz-container')[0].appendChild(div)
|
||||
|
||||
var previewsContainer = tabDiv.getElementsByClassName('uploads')[0]
|
||||
const previewsContainer = tabDiv.getElementsByClassName('uploads')[0]
|
||||
page.dropzone = new Dropzone('#dropzone', {
|
||||
url: 'api/upload',
|
||||
paramName: 'files[]',
|
||||
maxFilesize: parseInt(page.maxFileSize),
|
||||
parallelUploads: 2,
|
||||
uploadMultiple: false,
|
||||
previewsContainer: previewsContainer,
|
||||
previewsContainer,
|
||||
previewTemplate: page.previewTemplate,
|
||||
createImageThumbnails: false,
|
||||
maxFiles: 1000,
|
||||
@ -212,45 +210,41 @@ page.prepareDropzone = function () {
|
||||
chunking: Boolean(page.chunkSize),
|
||||
chunkSize: parseInt(page.chunkSize) * 1000000, // 1000000 B = 1 MB,
|
||||
parallelChunkUploads: false, // when set to true, sometimes it often hangs with hundreds of parallel uploads
|
||||
chunksUploaded: function (file, done) {
|
||||
chunksUploaded (file, done) {
|
||||
file.previewElement.querySelector('.progress').setAttribute('value', 100)
|
||||
file.previewElement.querySelector('.progress').innerHTML = '100%'
|
||||
|
||||
// The API supports an array of multiple files
|
||||
return axios.post('api/upload/finishchunks',
|
||||
{
|
||||
files: [{
|
||||
uuid: file.upload.uuid,
|
||||
original: file.name,
|
||||
size: file.size,
|
||||
type: file.type,
|
||||
count: file.upload.totalChunkCount,
|
||||
albumid: page.album
|
||||
}]
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
token: page.token
|
||||
}
|
||||
})
|
||||
.then(function (response) {
|
||||
file.previewElement.querySelector('.progress').style.display = 'none'
|
||||
return axios.post('api/upload/finishchunks', {
|
||||
// The API supports an array of multiple files
|
||||
files: [{
|
||||
uuid: file.upload.uuid,
|
||||
original: file.name,
|
||||
size: file.size,
|
||||
type: file.type,
|
||||
count: file.upload.totalChunkCount,
|
||||
albumid: page.album
|
||||
}]
|
||||
}, {
|
||||
headers: {
|
||||
token: page.token
|
||||
}
|
||||
}).then(function (response) {
|
||||
file.previewElement.querySelector('.progress').style.display = 'none'
|
||||
|
||||
if (response.data.success === false) {
|
||||
file.previewElement.querySelector('.error').innerHTML = response.data.description
|
||||
}
|
||||
if (response.data.success === false) {
|
||||
file.previewElement.querySelector('.error').innerHTML = response.data.description
|
||||
}
|
||||
|
||||
if (response.data.files && response.data.files[0]) {
|
||||
page.updateTemplate(file, response.data.files[0])
|
||||
}
|
||||
return done()
|
||||
})
|
||||
.catch(function (error) {
|
||||
return {
|
||||
success: false,
|
||||
description: error.toString()
|
||||
}
|
||||
})
|
||||
if (response.data.files && response.data.files[0]) {
|
||||
page.updateTemplate(file, response.data.files[0])
|
||||
}
|
||||
return done()
|
||||
}).catch(function (error) {
|
||||
return {
|
||||
success: false,
|
||||
description: error.toString()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@ -269,7 +263,7 @@ page.prepareDropzone = function () {
|
||||
page.dropzone.on('uploadprogress', function (file, progress) {
|
||||
if (file.upload.chunked && progress === 100) { return }
|
||||
file.previewElement.querySelector('.progress').setAttribute('value', progress)
|
||||
file.previewElement.querySelector('.progress').innerHTML = progress + '%'
|
||||
file.previewElement.querySelector('.progress').innerHTML = `${progress}%`
|
||||
})
|
||||
|
||||
page.dropzone.on('success', function (file, response) {
|
||||
@ -296,7 +290,7 @@ page.prepareDropzone = function () {
|
||||
}
|
||||
|
||||
page.uploadUrls = function (button) {
|
||||
var tabDiv = document.getElementById('tab-urls')
|
||||
const tabDiv = document.getElementById('tab-urls')
|
||||
if (!tabDiv) { return }
|
||||
|
||||
if (button.classList.contains('is-loading')) { return }
|
||||
@ -308,9 +302,9 @@ page.uploadUrls = function (button) {
|
||||
}
|
||||
|
||||
function run () {
|
||||
var albumid = page.album
|
||||
var previewsContainer = tabDiv.getElementsByClassName('uploads')[0]
|
||||
var urls = document.getElementById('urls').value
|
||||
const albumid = page.album
|
||||
const previewsContainer = tabDiv.getElementsByClassName('uploads')[0]
|
||||
const urls = document.getElementById('urls').value
|
||||
.split(/\r?\n/)
|
||||
.filter(function (url) { return url.trim().length })
|
||||
document.getElementById('urls').value = urls.join('\n')
|
||||
@ -321,22 +315,22 @@ page.uploadUrls = function (button) {
|
||||
}
|
||||
|
||||
tabDiv.getElementsByClassName('uploads')[0].style.display = 'block'
|
||||
var files = urls.map(function (url) {
|
||||
var previewTemplate = document.createElement('template')
|
||||
const files = urls.map(function (url) {
|
||||
const previewTemplate = document.createElement('template')
|
||||
previewTemplate.innerHTML = page.previewTemplate.trim()
|
||||
var previewElement = previewTemplate.content.firstChild
|
||||
const previewElement = previewTemplate.content.firstChild
|
||||
previewElement.querySelector('.name').innerHTML = url
|
||||
previewsContainer.appendChild(previewElement)
|
||||
return {
|
||||
url: url,
|
||||
previewElement: previewElement
|
||||
url,
|
||||
previewElement
|
||||
}
|
||||
})
|
||||
|
||||
function post (i) {
|
||||
if (i === files.length) { return done() }
|
||||
|
||||
var file = files[i]
|
||||
const file = files[i]
|
||||
|
||||
function posted (result) {
|
||||
file.previewElement.querySelector('.progress').style.display = 'none'
|
||||
@ -348,25 +342,21 @@ page.uploadUrls = function (button) {
|
||||
return post(i + 1)
|
||||
}
|
||||
|
||||
axios.post('api/upload',
|
||||
{
|
||||
urls: [file.url]
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
token: page.token,
|
||||
albumid: albumid
|
||||
}
|
||||
})
|
||||
.then(function (response) {
|
||||
return posted(response.data)
|
||||
})
|
||||
.catch(function (error) {
|
||||
return posted({
|
||||
success: false,
|
||||
description: error.toString()
|
||||
})
|
||||
axios.post('api/upload', {
|
||||
urls: [file.url]
|
||||
}, {
|
||||
headers: {
|
||||
token: page.token,
|
||||
albumid
|
||||
}
|
||||
}).then(function (response) {
|
||||
return posted(response.data)
|
||||
}).catch(function (error) {
|
||||
return posted({
|
||||
success: false,
|
||||
description: error.toString()
|
||||
})
|
||||
})
|
||||
}
|
||||
return post(0)
|
||||
}
|
||||
@ -376,14 +366,14 @@ page.uploadUrls = function (button) {
|
||||
page.updateTemplate = function (file, response) {
|
||||
if (!response.url) { return }
|
||||
|
||||
var a = file.previewElement.querySelector('.link > a')
|
||||
var clipboard = file.previewElement.querySelector('.clipboard-mobile > .clipboard-js')
|
||||
const a = file.previewElement.querySelector('.link > a')
|
||||
const clipboard = file.previewElement.querySelector('.clipboard-mobile > .clipboard-js')
|
||||
a.href = a.innerHTML = clipboard.dataset['clipboardText'] = response.url
|
||||
clipboard.parentElement.style.display = 'block'
|
||||
|
||||
var exec = /.[\w]+(\?|$)/.exec(response.url)
|
||||
const exec = /.[\w]+(\?|$)/.exec(response.url)
|
||||
if (exec && exec[0] && imageExtensions.includes(exec[0].toLowerCase())) {
|
||||
var img = file.previewElement.querySelector('img')
|
||||
const img = file.previewElement.querySelector('img')
|
||||
img.setAttribute('alt', response.name || '')
|
||||
img.dataset['src'] = response.url
|
||||
img.onerror = function () { this.style.display = 'none' } // hide webp in firefox and ie
|
||||
@ -392,30 +382,31 @@ page.updateTemplate = function (file, response) {
|
||||
}
|
||||
|
||||
page.createAlbum = function () {
|
||||
var div = document.createElement('div')
|
||||
div.innerHTML =
|
||||
'<div class="field">\n' +
|
||||
' <label class="label">Album name</label>\n' +
|
||||
' <div class="controls">\n' +
|
||||
' <input id="_name" class="input" type="text" placeholder="My super album">\n' +
|
||||
' </div>\n' +
|
||||
'</div>\n' +
|
||||
'<div class="field">\n' +
|
||||
' <div class="control">\n' +
|
||||
' <label class="checkbox">\n' +
|
||||
' <input id="_download" type="checkbox" checked>\n' +
|
||||
' Enable download\n' +
|
||||
' </label>\n' +
|
||||
' </div>\n' +
|
||||
'</div>\n' +
|
||||
'<div class="field">\n' +
|
||||
' <div class="control">\n' +
|
||||
' <label class="checkbox">\n' +
|
||||
' <input id="_public" type="checkbox" checked>\n' +
|
||||
' Enable public link\n' +
|
||||
' </label>\n' +
|
||||
' </div>\n' +
|
||||
'</div>'
|
||||
const div = document.createElement('div')
|
||||
div.innerHTML = `
|
||||
<div class="field">
|
||||
<label class="label">Album name</label>
|
||||
<div class="controls">
|
||||
<input id="swalName" class="input" type="text" placeholder="My super album">
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<label class="checkbox">
|
||||
<input id="swalDownload" type="checkbox" checked>
|
||||
Enable download
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<label class="checkbox">
|
||||
<input id="swalPublic" type="checkbox" checked>
|
||||
Enable public link
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
swal({
|
||||
title: 'Create new album',
|
||||
@ -427,43 +418,44 @@ page.createAlbum = function () {
|
||||
closeModal: false
|
||||
}
|
||||
}
|
||||
})
|
||||
.then(function (value) {
|
||||
if (!value) { return }
|
||||
}).then(function (value) {
|
||||
if (!value) { return }
|
||||
|
||||
var name = document.getElementById('_name').value
|
||||
axios.post('api/albums', {
|
||||
name: name,
|
||||
download: document.getElementById('_download').checked,
|
||||
public: document.getElementById('_public').checked
|
||||
}, { headers: { token: page.token } })
|
||||
.then(function (response) {
|
||||
if (response.data.success === false) {
|
||||
return swal('An error occurred!', response.data.description, 'error')
|
||||
}
|
||||
const name = document.getElementById('swalName').value
|
||||
axios.post('api/albums', {
|
||||
name,
|
||||
download: document.getElementById('swalDownload').checked,
|
||||
public: document.getElementById('swalPublic').checked
|
||||
}, {
|
||||
headers: {
|
||||
token: page.token
|
||||
}
|
||||
}).then(function (response) {
|
||||
if (response.data.success === false) {
|
||||
return swal('An error occurred!', response.data.description, 'error')
|
||||
}
|
||||
|
||||
var option = document.createElement('option')
|
||||
option.value = response.data.id
|
||||
option.innerHTML = name
|
||||
page.albumSelect.appendChild(option)
|
||||
const option = document.createElement('option')
|
||||
option.value = response.data.id
|
||||
option.innerHTML = name
|
||||
page.albumSelect.appendChild(option)
|
||||
|
||||
swal('Woohoo!', 'Album was created successfully', 'success')
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.log(error)
|
||||
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
|
||||
})
|
||||
swal('Woohoo!', 'Album was created successfully', 'success')
|
||||
}).catch(function (error) {
|
||||
console.log(error)
|
||||
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Handle image paste event
|
||||
window.addEventListener('paste', function (event) {
|
||||
var items = (event.clipboardData || event.originalEvent.clipboardData).items
|
||||
for (var index in items) {
|
||||
var item = items[index]
|
||||
const items = (event.clipboardData || event.originalEvent.clipboardData).items
|
||||
for (const index in items) {
|
||||
const item = items[index]
|
||||
if (item.kind === 'file') {
|
||||
var blob = item.getAsFile()
|
||||
var file = new File([blob], 'pasted-image.' + blob.type.match(/(?:[^/]*\/)([^;]*)/)[1])
|
||||
const blob = item.getAsFile()
|
||||
const file = new File([blob], `pasted-image.${blob.type.match(/(?:[^/]*\/)([^;]*)/)[1]}`)
|
||||
file.type = blob.type
|
||||
page.dropzone.addFile(file)
|
||||
}
|
||||
|
@ -2,24 +2,23 @@
|
||||
|
||||
page.prepareShareX = function () {
|
||||
if (!page.token) { return }
|
||||
var origin = (location.hostname + location.pathname).replace(/\/(dashboard)?$/, '')
|
||||
var originClean = origin.replace(/\//g, '_')
|
||||
var sharexElement = document.getElementById('ShareX')
|
||||
var sharexFile =
|
||||
'{\r\n' +
|
||||
' "Name": "' + originClean + '",\r\n' +
|
||||
' "DestinationType": "ImageUploader, FileUploader",\r\n' +
|
||||
' "RequestType": "POST",\r\n' +
|
||||
' "RequestURL": "' + location.protocol + '//' + origin + '/api/upload",\r\n' +
|
||||
' "FileFormName": "files[]",\r\n' +
|
||||
' "Headers": {\r\n' +
|
||||
' "token": "' + page.token + '"\r\n' +
|
||||
' },\r\n' +
|
||||
' "ResponseType": "Text",\r\n' +
|
||||
' "URL": "$json:files[0].url$",\r\n' +
|
||||
' "ThumbnailURL": "$json:files[0].url$"\r\n' +
|
||||
'}'
|
||||
var sharexBlob = new Blob([sharexFile], { type: 'application/octet-binary' })
|
||||
const origin = (location.hostname + location.pathname).replace(/\/(dashboard)?$/, '')
|
||||
const originClean = origin.replace(/\//g, '_')
|
||||
const sharexElement = document.getElementById('ShareX')
|
||||
const sharexFile = `{
|
||||
"Name": "${originClean}",
|
||||
"DestinationType": "ImageUploader, FileUploader",
|
||||
"RequestType": "POST",
|
||||
"RequestURL": "${location.protocol}//${origin}/api/upload",
|
||||
"FileFormName": "files[]",
|
||||
"Headers": {
|
||||
"token": "${page.token}"
|
||||
},
|
||||
"ResponseType": "Text",
|
||||
"URL": "$json:files[0].url$",
|
||||
"ThumbnailURL": "$json:files[0].url$"
|
||||
}\n`
|
||||
const sharexBlob = new Blob([sharexFile], { type: 'application/octet-binary' })
|
||||
sharexElement.setAttribute('href', URL.createObjectURL(sharexBlob))
|
||||
sharexElement.setAttribute('download', originClean + '.sxcu')
|
||||
sharexElement.setAttribute('download', `${originClean}.sxcu`)
|
||||
}
|
||||
|
29
public/libs/fontello/fontello.css
vendored
29
public/libs/fontello/fontello.css
vendored
@ -1,11 +1,11 @@
|
||||
@font-face {
|
||||
font-family: 'fontello';
|
||||
src: url('fontello.eot?61363773');
|
||||
src: url('fontello.eot?61363773#iefix') format('embedded-opentype'),
|
||||
url('fontello.woff2?61363773') format('woff2'),
|
||||
url('fontello.woff?61363773') format('woff'),
|
||||
url('fontello.ttf?61363773') format('truetype'),
|
||||
url('fontello.svg?61363773#fontello') format('svg');
|
||||
src: url('fontello.eot?54767678');
|
||||
src: url('fontello.eot?54767678#iefix') format('embedded-opentype'),
|
||||
url('fontello.woff2?54767678') format('woff2'),
|
||||
url('fontello.woff?54767678') format('woff'),
|
||||
url('fontello.ttf?54767678') format('truetype'),
|
||||
url('fontello.svg?54767678#fontello') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@ -33,6 +33,10 @@
|
||||
text-align: center;
|
||||
/* opacity: .8; */
|
||||
|
||||
/* For safety - reset parent styles, that can break glyph codes*/
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
|
||||
/* fix buttons height, for twitter bootstrap */
|
||||
/* line-height: 1em; */
|
||||
|
||||
@ -55,12 +59,12 @@
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.icon-sharex:before { content: '\e800'; } /* '' */
|
||||
.icon-upload-cloud:before { content: '\e801'; } /* '' */
|
||||
.icon-picture-1:before { content: '\e802'; } /* '' */
|
||||
.icon-th-list:before { content: '\e803'; } /* '' */
|
||||
.icon-trash:before { content: '\e804'; } /* '' */
|
||||
.icon-pencil-1:before { content: '\e805'; } /* '' */
|
||||
.icon-pencil-1:before { content: '\e800'; } /* '' */
|
||||
.icon-sharex:before { content: '\e801'; } /* '' */
|
||||
.icon-upload-cloud:before { content: '\e802'; } /* '' */
|
||||
.icon-picture-1:before { content: '\e803'; } /* '' */
|
||||
.icon-th-list:before { content: '\e804'; } /* '' */
|
||||
.icon-trash:before { content: '\e805'; } /* '' */
|
||||
.icon-th-large:before { content: '\e806'; } /* '' */
|
||||
.icon-arrows-cw:before { content: '\e807'; } /* '' */
|
||||
.icon-plus:before { content: '\e808'; } /* '' */
|
||||
@ -72,6 +76,7 @@
|
||||
.icon-download:before { content: '\e80e'; } /* '' */
|
||||
.icon-help-circled:before { content: '\e80f'; } /* '' */
|
||||
.icon-terminal:before { content: '\e810'; } /* '' */
|
||||
.icon-hammer:before { content: '\e811'; } /* '' */
|
||||
.icon-github-circled:before { content: '\f09b'; } /* '' */
|
||||
.icon-gauge:before { content: '\f0e4'; } /* '' */
|
||||
.icon-paper-plane-empty:before { content: '\f1d9'; } /* '' */
|
||||
|
Binary file not shown.
@ -6,17 +6,17 @@
|
||||
<font id="fontello" horiz-adv-x="1000" >
|
||||
<font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
|
||||
<missing-glyph horiz-adv-x="1000" />
|
||||
<glyph glyph-name="sharex" unicode="" d="M328-73c-98 10-183 74-229 171-52 110-43 246 24 346 44 66 107 110 181 126 21 4 70 4 91 0 45-10 86-30 120-59 7-5 12-10 12-10-1 0-10-2-20-4-122-17-223-98-257-205-10-29-12-45-12-79 0-34 2-50 12-80 14-41 35-75 70-109 32-33 64-55 107-72l16-7-7-3c-12-5-45-13-65-15-20-2-25-2-43 0z m248-11c-64 5-121 24-170 56-66 44-110 108-126 182-4 21-4 70 1 91 9 45 29 86 58 120 5 7 10 12 10 12 1-1 2-10 4-20 17-122 98-223 205-257 29-10 45-12 79-12 46 0 74 7 117 28 62 31 115 90 144 161l7 16 3-7c10-23 18-74 16-99-8-103-72-190-172-238-53-25-120-38-176-33z m42 212c-45 7-90 27-127 55-20 16-20 17-9 18 129 16 233 97 268 207 10 29 12 45 12 79 0 46-7 74-28 117-30 61-88 114-157 142-13 6-19 9-17 10 4 3 35 12 50 14 24 5 42 5 62 3 65-7 122-35 170-85 24-25 39-47 55-78 22-43 33-84 37-134 6-77-15-157-56-220-44-65-107-109-180-125-15-4-64-5-80-3z m31 204c-16 129-97 233-207 268-29 10-45 12-79 12-44 0-72-7-112-25-30-15-51-30-77-56-33-33-55-65-72-108l-7-16-3 7c-10 23-18 74-16 99 9 103 72 190 172 238 81 39 178 44 264 16 38-12 67-27 98-50 55-42 95-103 109-169 4-18 6-57 3-76-6-47-25-92-55-131-16-20-16-20-18-9z" horiz-adv-x="1000" />
|
||||
<glyph glyph-name="pencil-1" unicode="" d="M0-143l68 343 274-273z m137 392l422 422 259-260-421-422z m531 494q2 39 31 69t69 31 66-25l131-131q25-26 24-66t-30-69-69-30-66 24l-131 131q-27 27-25 66z" horiz-adv-x="989" />
|
||||
|
||||
<glyph glyph-name="upload-cloud" unicode="" d="M781 506q108 0 184-76t76-184-76-184-184-77l-208 0 0 239 67-66q16-17 38-17 20 0 36 17 15 15 15 36t-15 36l-156 156q-14 14-37 15t-37-15l-156-156q-15-15-15-36t15-36q16-17 37-17 20 0 36 17l68 66 0-239-260 0q-86 0-148 61t-61 148q0 72 44 128t112 73l0 8q0 130 92 221t221 91q100 0 180-58t114-152q2 0 8 1t10 0z" horiz-adv-x="1041" />
|
||||
<glyph glyph-name="sharex" unicode="" d="M328-73c-98 10-183 74-229 171-52 110-43 246 24 346 44 66 107 110 181 126 21 4 70 4 91 0 45-10 86-30 120-59 7-5 12-10 12-10-1 0-10-2-20-4-122-17-223-98-257-205-10-29-12-45-12-79 0-34 2-50 12-80 14-41 35-75 70-109 32-33 64-55 107-72l16-7-7-3c-12-5-45-13-65-15-20-2-25-2-43 0z m248-11c-64 5-121 24-170 56-66 44-110 108-126 182-4 21-4 70 1 91 9 45 29 86 58 120 5 7 10 12 10 12 1-1 2-10 4-20 17-122 98-223 205-257 29-10 45-12 79-12 46 0 74 7 117 28 62 31 115 90 144 161l7 16 3-7c10-23 18-74 16-99-8-103-72-190-172-238-53-25-120-38-176-33z m42 212c-45 7-90 27-127 55-20 16-20 17-9 18 129 16 233 97 268 207 10 29 12 45 12 79 0 46-7 74-28 117-30 61-88 114-157 142-13 6-19 9-17 10 4 3 35 12 50 14 24 5 42 5 62 3 65-7 122-35 170-85 24-25 39-47 55-78 22-43 33-84 37-134 6-77-15-157-56-220-44-65-107-109-180-125-15-4-64-5-80-3z m31 204c-16 129-97 233-207 268-29 10-45 12-79 12-44 0-72-7-112-25-30-15-51-30-77-56-33-33-55-65-72-108l-7-16-3 7c-10 23-18 74-16 99 9 103 72 190 172 238 81 39 178 44 264 16 38-12 67-27 98-50 55-42 95-103 109-169 4-18 6-57 3-76-6-47-25-92-55-131-16-20-16-20-18-9z" horiz-adv-x="1000" />
|
||||
|
||||
<glyph glyph-name="picture-1" unicode="" d="M0-68l0 836 1000 0 0-836-1000 0z m76 78l848 0 0 680-848 0 0-680z m90 80l0 59 150 195 102-86 193 291 223-228 0-231-668 0z m0 416q0 37 24 62t62 24q33 0 58-24t24-62q0-33-24-57t-58-25q-37 0-62 25t-24 57z" horiz-adv-x="1000" />
|
||||
<glyph glyph-name="upload-cloud" unicode="" d="M781 506q108 0 184-76t76-184-76-184-184-77l-208 0 0 239 67-66q16-17 38-17 20 0 36 17 15 15 15 36t-15 36l-156 156q-14 14-37 15t-37-15l-156-156q-15-15-15-36t15-36q16-17 37-17 20 0 36 17l68 66 0-239-260 0q-86 0-148 61t-61 148q0 72 44 128t112 73l0 8q0 130 92 221t221 91q100 0 180-58t114-152q2 0 8 1t10 0z" horiz-adv-x="1041" />
|
||||
|
||||
<glyph glyph-name="th-list" unicode="" d="M860 90q43 0 73-31t31-74q0-43-31-73t-73-31l-365 0q-44 0-74 31t-31 73 31 74 74 31l365 0z m0 364q43 0 73-31t31-73-31-73-73-31l-365 0q-44 0-74 31t-31 73 31 73 74 31l365 0z m0 365q43 0 73-31t31-73q0-44-31-74t-73-31l-365 0q-42 0-74 31t-31 74 31 73 74 31l365 0z m-860-834q0 130 130 130t130-130-130-130-130 130z m0 365q0 130 130 130t130-130-130-130-130 130z m0 365q0 130 130 130t130-130-130-130-130 130z" horiz-adv-x="964" />
|
||||
<glyph glyph-name="picture-1" unicode="" d="M0-68l0 836 1000 0 0-836-1000 0z m76 78l848 0 0 680-848 0 0-680z m90 80l0 59 150 195 102-86 193 291 223-228 0-231-668 0z m0 416q0 37 24 62t62 24q33 0 58-24t24-62q0-33-24-57t-58-25q-37 0-62 25t-24 57z" horiz-adv-x="1000" />
|
||||
|
||||
<glyph glyph-name="trash" unicode="" d="M0 633l0 141 289 0 0 76 246 0 0-76 289 0 0-141-824 0z m43-783l0 676 738 0 0-676-738 0z" horiz-adv-x="824" />
|
||||
<glyph glyph-name="th-list" unicode="" d="M860 90q43 0 73-31t31-74q0-43-31-73t-73-31l-365 0q-44 0-74 31t-31 73 31 74 74 31l365 0z m0 364q43 0 73-31t31-73-31-73-73-31l-365 0q-44 0-74 31t-31 73 31 73 74 31l365 0z m0 365q43 0 73-31t31-73q0-44-31-74t-73-31l-365 0q-42 0-74 31t-31 74 31 73 74 31l365 0z m-860-834q0 130 130 130t130-130-130-130-130 130z m0 365q0 130 130 130t130-130-130-130-130 130z m0 365q0 130 130 130t130-130-130-130-130 130z" horiz-adv-x="964" />
|
||||
|
||||
<glyph glyph-name="pencil-1" unicode="" d="M0-143l68 343 274-273z m137 392l422 422 259-260-421-422z m531 494q2 39 31 69t69 31 66-25l131-131q25-26 24-66t-30-69-69-30-66 24l-131 131q-27 27-25 66z" horiz-adv-x="989" />
|
||||
<glyph glyph-name="trash" unicode="" d="M0 633l0 141 289 0 0 76 246 0 0-76 289 0 0-141-824 0z m43-783l0 676 738 0 0-676-738 0z" horiz-adv-x="824" />
|
||||
|
||||
<glyph glyph-name="th-large" unicode="" d="M0 663q0 65 46 110t110 46l104 0q65 0 111-46t45-110l0-104q0-65-45-111t-111-45l-104 0q-65 0-110 45t-46 111l0 104z m521 0q0 65 46 110t111 46l103 0q65 0 111-46t46-110l0-104q0-65-46-111t-111-45l-103 0q-65 0-111 45t-46 111l0 104z m-521-521q0 65 46 110t110 46l104 0q65 0 111-46t45-110l0-104q0-65-45-111t-111-46l-104 0q-65 0-110 46t-46 111l0 104z m521 0q0 65 46 110t111 46l103 0q65 0 111-46t46-110l0-104q0-65-46-111t-111-46l-103 0q-65 0-111 46t-46 111l0 104z" horiz-adv-x="938" />
|
||||
|
||||
@ -40,6 +40,8 @@
|
||||
|
||||
<glyph glyph-name="terminal" unicode="" d="M929 779h-858c-39 0-71-32-71-72v-714c0-40 32-72 71-72h858c39 0 71 32 71 72v714c0 40-32 72-71 72z m-786-500l143 142-143 143 71 72 215-215-215-214-71 72z m571-72h-285v72h285v-72z" horiz-adv-x="1000" />
|
||||
|
||||
<glyph glyph-name="hammer" unicode="" d="M988-7q0-30-20-50l-60-61q-22-20-51-20-29 0-50 20l-203 204q-21 20-21 50 0 29 24 53l-143 143-70-70q-8-8-19-8t-19 8q1-1 7-7t7-7 6-6 5-8 4-8 3-9 0-10q0-21-15-38-2-1-9-10t-11-11-10-9-13-9-12-5-14-3q-23 0-38 16l-228 228q-16 15-16 38 0 7 3 14t5 12 9 13 9 10 11 11 10 9q17 15 38 15 6 0 10 0t9-3 8-4 8-5 6-6 7-7 7-7q-8 8-8 19t8 19l194 194q8 8 19 8t19-8q-1 1-7 7t-7 7-6 7-5 7-3 8-4 9 0 10q0 21 15 38 2 2 9 10t11 11 10 10 13 8 12 5 14 3q23 0 38-16l228-228q16-15 16-38 0-7-3-14t-5-12-8-13-10-10-11-11-10-9q-17-15-38-15-6 0-10 0t-9 4-8 3-7 5-7 6-7 7-7 7q8-8 8-19t-8-19l-70-70 143-143q24 24 53 24 29 0 51-21l203-202q20-22 20-51z" horiz-adv-x="1000" />
|
||||
|
||||
<glyph glyph-name="github-circled" unicode="" d="M429 779q116 0 215-58t156-156 57-215q0-140-82-252t-211-155q-15-3-22 4t-7 17q0 1 0 43t0 75q0 54-29 79 32 3 57 10t53 22 45 37 30 58 11 84q0 67-44 115 21 51-4 114-16 5-46-6t-51-25l-21-13q-52 15-107 15t-108-15q-8 6-23 15t-47 22-47 7q-25-63-5-114-44-48-44-115 0-47 12-83t29-59 45-37 52-22 57-10q-21-20-27-58-12-5-25-8t-32-3-36 12-31 35q-11 18-27 29t-28 14l-11 1q-12 0-16-2t-3-7 5-8 7-6l4-3q12-6 24-21t18-29l6-13q7-21 24-34t37-17 39-3 31 1l13 3q0-22 0-50t1-30q0-10-8-17t-22-4q-129 43-211 155t-82 252q0 117 58 215t155 156 216 58z m-267-616q2 4-3 7-6 1-8-1-1-4 4-7 5-3 7 1z m18-19q4 3-1 9-6 5-9 2-4-3 1-9 5-6 9-2z m16-25q6 4 0 11-4 7-9 3-5-3 0-10t9-4z m24-23q4 4-2 10-7 7-11 2-5-5 2-11 6-6 11-1z m32-14q1 6-8 9-8 2-10-4t7-9q8-3 11 4z m35-3q0 7-10 6-9 0-9-6 0-7 10-6 9 0 9 6z m32 5q-1 7-10 5-9-1-8-8t10-4 8 7z" horiz-adv-x="857.1" />
|
||||
|
||||
<glyph glyph-name="gauge" unicode="" d="M214 207q0 30-21 51t-50 21-51-21-21-51 21-50 51-21 50 21 21 50z m107 250q0 30-20 51t-51 21-50-21-21-51 21-50 50-21 51 21 20 50z m239-268l57 213q3 14-5 27t-21 16-27-3-17-22l-56-213q-33-3-60-25t-35-55q-11-43 11-81t66-50 81 11 50 66q9 33-4 65t-40 51z m369 18q0 30-21 51t-51 21-50-21-21-51 21-50 50-21 51 21 21 50z m-358 357q0 30-20 51t-51 21-50-21-21-51 21-50 50-21 51 21 20 50z m250-107q0 30-20 51t-51 21-50-21-21-51 21-50 50-21 51 21 20 50z m179-250q0-145-79-269-10-17-30-17h-782q-20 0-30 17-79 123-79 269 0 102 40 194t106 160 160 107 194 39 194-39 160-107 106-160 40-194z" horiz-adv-x="1000" />
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 12 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -41,5 +41,8 @@ routes.post('/tokens/verify', (req, res, next) => tokenController.verify(req, re
|
||||
routes.post('/tokens/change', (req, res, next) => tokenController.change(req, res, next))
|
||||
routes.get('/filelength/config', (req, res, next) => authController.getFileLengthConfig(req, res, next))
|
||||
routes.post('/filelength/change', (req, res, next) => authController.changeFileLength(req, res, next))
|
||||
routes.get('/users', (req, res, next) => authController.listUsers(req, res, next))
|
||||
routes.get('/users/:page', (req, res, next) => authController.listUsers(req, res, next))
|
||||
routes.post('/users/edit', (req, res, next) => authController.editUser(req, res, next))
|
||||
|
||||
module.exports = routes
|
||||
|
@ -70,6 +70,15 @@
|
||||
</li>
|
||||
</ul>
|
||||
<p class="menu-label">Administration</p>
|
||||
<ul class="menu-list">
|
||||
<li>
|
||||
<a id="itemManageUploads" disabled>Manage uploads</a>
|
||||
</li>
|
||||
<li>
|
||||
<a id="itemManageUsers" disabled>Manage users</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="menu-label">Configuration</p>
|
||||
<ul class="menu-list">
|
||||
<li>
|
||||
<a id="ShareX">ShareX user profile</a>
|
||||
|
Loading…
Reference in New Issue
Block a user