Merge branch 'account-manager' into safe.fiery.me

This commit is contained in:
Bobby Wibowo 2018-11-18 02:22:30 +07:00
commit dc7214cb38
No known key found for this signature in database
GPG Key ID: 51C3A1E1E22D26CF
52 changed files with 2053 additions and 1219 deletions

2
.gitignore vendored
View File

@ -12,3 +12,5 @@ migrate.js
yarn.lock yarn.lock
yarn-error.log yarn-error.log
package-lock.json package-lock.json
public/render/**/original
public/render/**/wip

View File

@ -204,9 +204,12 @@ albumsController.edit = async (req, res, next) => {
}) })
.first() .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.' }) return res.json({ success: false, description: 'Name already in use.' })
} else if (req._old && (album.id === id)) { } else if (req._old && (album.id === id)) {
// Old rename API
return res.json({ success: false, description: 'You did not specify a new name.' }) 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) => { albumsController.get = async (req, res, next) => {
// TODO: // TODO: ... what was i supposed to do here?
const identifier = req.params.identifier const identifier = req.params.identifier
if (identifier === undefined) { if (identifier === undefined) {
return res.status(401).json({ success: false, description: 'No identifier provided.' }) return res.status(401).json({ success: false, description: 'No identifier provided.' })

View File

@ -1,6 +1,7 @@
const bcrypt = require('bcrypt') const bcrypt = require('bcrypt')
const config = require('./../config') const config = require('./../config')
const db = require('knex')(config.database) const db = require('knex')(config.database)
const perms = require('./permissionController')
const randomstring = require('randomstring') const randomstring = require('randomstring')
const utils = require('./utilsController') const utils = require('./utilsController')
@ -14,7 +15,9 @@ authController.verify = async (req, res, next) => {
if (password === undefined) { return res.json({ success: false, description: 'No password provided.' }) } if (password === undefined) { return res.json({ success: false, description: 'No password provided.' }) }
const user = await db.table('users').where('username', username).first() 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) { if (user.enabled === false || user.enabled === 0) {
return res.json({ success: false, description: 'This account has been disabled.' }) return res.json({ success: false, description: 'This account has been disabled.' })
} }
@ -60,7 +63,8 @@ authController.register = async (req, res, next) => {
username, username,
password: hash, password: hash,
token, token,
enabled: 1 enabled: 1,
permission: perms.permissions.user
}) })
return res.json({ success: true, token }) return res.json({ success: true, token })
}) })
@ -94,23 +98,43 @@ authController.changePassword = async (req, res, next) => {
authController.getFileLengthConfig = async (req, res, next) => { authController.getFileLengthConfig = async (req, res, next) => {
const user = await utils.authorize(req, res) const user = await utils.authorize(req, res)
if (!user) { return } 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) => { authController.changeFileLength = async (req, res, next) => {
if (config.uploads.fileLength.userChangeable === false) { 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) const user = await utils.authorize(req, res)
if (!user) { return } if (!user) { return }
const fileLength = parseInt(req.body.fileLength) const fileLength = parseInt(req.body.fileLength)
if (fileLength === undefined) { return res.json({ success: false, description: 'No file name length provided.' }) } if (fileLength === undefined) {
if (isNaN(fileLength)) { return res.json({ success: false, description: 'File name length is not a valid number.' }) } 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) { 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) { if (fileLength === user.fileLength) {
@ -124,4 +148,108 @@ authController.changeFileLength = async (req, res, next) => {
return res.json({ success: true }) 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 (!perms.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.' })
}
let permission = req.body.group ? perms.permissions[req.body.group] : target.permission
if (typeof permission !== 'number' || permission < 0) { permission = target.permission }
await db.table('users')
.where('id', id)
.update({
username,
enabled: Boolean(req.body.enabled),
permission
})
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 = perms.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')
if (!users.length) { return res.json({ success: true, users }) }
const userids = []
for (const user of users) {
user.groups = perms.mapPermissions(user)
delete user.permission
userids.push(user.id)
user.uploadsCount = 0
user.diskUsage = 0
}
const maps = {}
const uploads = await db.table('files').whereIn('userid', userids)
for (const upload of uploads) {
// This is the fastest method that I can think of
if (maps[upload.userid] === undefined) { maps[upload.userid] = { count: 0, size: 0 } }
maps[upload.userid].count++
maps[upload.userid].size += parseInt(upload.size)
}
for (const user of users) {
if (!maps[user.id]) { continue }
user.uploadsCount = maps[user.id].count
user.diskUsage = maps[user.id].size
}
return res.json({ success: true, users })
}
module.exports = authController module.exports = authController

View File

@ -0,0 +1,32 @@
const permissionController = {}
permissionController.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
}
permissionController.is = (user, group) => {
// root bypass
if (user.username === 'root') { return true }
const permission = user.permission || 0
return permission >= permissionController.permissions[group]
}
permissionController.higher = (user, target) => {
const userPermission = user.permission || 0
const targetPermission = target.permission || 0
return userPermission > targetPermission
}
permissionController.mapPermissions = user => {
const map = {}
Object.keys(permissionController.permissions).forEach(group => {
map[group] = permissionController.is(user, group)
})
return map
}
module.exports = permissionController

View File

@ -1,5 +1,6 @@
const config = require('./../config') const config = require('./../config')
const db = require('knex')(config.database) const db = require('knex')(config.database)
const perms = require('./permissionController')
const randomstring = require('randomstring') const randomstring = require('randomstring')
const utils = require('./utilsController') const utils = require('./utilsController')
@ -7,17 +8,35 @@ const tokenController = {}
tokenController.verify = async (req, res, next) => { tokenController.verify = async (req, res, next) => {
const token = req.body.token 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() const user = await db.table('users').where('token', token).first()
if (!user) { return res.status(401).json({ success: false, description: 'Invalid token.' }) } if (!user) {
return res.json({ success: true, username: user.username }) return res.status(401).json({
success: false,
description: 'Invalid token.'
})
}
return res.json({
success: true,
username: user.username,
permissions: perms.mapPermissions(user)
})
} }
tokenController.list = async (req, res, next) => { tokenController.list = async (req, res, next) => {
const user = await utils.authorize(req, res) const user = await utils.authorize(req, res)
if (!user) { return } 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) => { tokenController.change = async (req, res, next) => {
@ -30,7 +49,10 @@ tokenController.change = async (req, res, next) => {
timestamp: Math.floor(Date.now() / 1000) timestamp: Math.floor(Date.now() / 1000)
}) })
res.json({ success: true, token: newtoken }) res.json({
success: true,
token: newtoken
})
} }
module.exports = tokenController module.exports = tokenController

View File

@ -5,6 +5,7 @@ const fetch = require('node-fetch')
const fs = require('fs') const fs = require('fs')
const multer = require('multer') const multer = require('multer')
const path = require('path') const path = require('path')
const perms = require('./permissionController')
const randomstring = require('randomstring') const randomstring = require('randomstring')
const utils = require('./utilsController') const utils = require('./utilsController')
@ -649,6 +650,11 @@ uploadsController.list = async (req, res) => {
let offset = req.params.page let offset = req.params.page
if (offset === undefined) { offset = 0 } 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 = perms.is(user, 'moderator')
if (all && !ismoderator) { return res.json(403) }
const files = await db.table('files') const files = await db.table('files')
.where(function () { .where(function () {
if (req.params.id === undefined) { if (req.params.id === undefined) {
@ -658,7 +664,9 @@ uploadsController.list = async (req, res) => {
} }
}) })
.where(function () { .where(function () {
if (user.username !== 'root') { this.where('userid', user.id) } if (!all || !ismoderator) {
this.where('userid', user.id)
}
}) })
.orderBy('id', 'DESC') .orderBy('id', 'DESC')
.limit(25) .limit(25)
@ -668,7 +676,7 @@ uploadsController.list = async (req, res) => {
const albums = await db.table('albums') const albums = await db.table('albums')
.where(function () { .where(function () {
this.where('enabled', 1) this.where('enabled', 1)
if (user.username !== 'root') { if (!all || !ismoderator) {
this.where('userid', user.id) this.where('userid', user.id)
} }
}) })
@ -688,8 +696,8 @@ uploadsController.list = async (req, res) => {
} }
} }
// Only push usernames if we are root // Only push usernames if we are a moderator
if (user.username === 'root') { if (all && ismoderator) {
if (file.userid !== undefined && file.userid !== null && file.userid !== '') { if (file.userid !== undefined && file.userid !== null && file.userid !== '') {
userids.push(file.userid) userids.push(file.userid)
} }
@ -702,13 +710,12 @@ uploadsController.list = async (req, res) => {
} }
// If we are a normal user, send response // 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 }) } if (userids.length === 0) { return res.json({ success: true, files }) }
const users = await db.table('users') const users = await db.table('users').whereIn('id', userids)
.whereIn('id', userids)
for (const dbUser of users) { for (const dbUser of users) {
for (const file of files) { for (const file of files) {
if (file.userid === dbUser.id) { if (file.userid === dbUser.id) {

View File

@ -5,6 +5,7 @@ const ffmpeg = require('fluent-ffmpeg')
const fs = require('fs') const fs = require('fs')
const gm = require('gm') const gm = require('gm')
const path = require('path') const path = require('path')
const perms = require('./permissionController')
const utilsController = {} const utilsController = {}
const uploadsDir = path.join(__dirname, '..', config.uploads.folder) const uploadsDir = path.join(__dirname, '..', config.uploads.folder)
@ -57,9 +58,18 @@ utilsController.authorize = async (req, res) => {
} }
const user = await db.table('users').where('token', token).first() 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) => { utilsController.generateThumbs = (name, force) => {
@ -158,10 +168,11 @@ utilsController.deleteFile = file => {
utilsController.bulkDeleteFiles = async (field, values, user) => { utilsController.bulkDeleteFiles = async (field, values, user) => {
if (!user || !['id', 'name'].includes(field)) { return } if (!user || !['id', 'name'].includes(field)) { return }
const ismoderator = perms.is(user, 'moderator')
const files = await db.table('files') const files = await db.table('files')
.whereIn(field, values) .whereIn(field, values)
.where(function () { .where(function () {
if (user.username !== 'root') { if (!ismoderator) {
this.where('userid', user.id) this.where('userid', user.id)
} }
}) })

View File

@ -1,3 +1,5 @@
const perms = require('./../controllers/permissionController')
const init = function (db) { const init = function (db) {
// Create the tables we need to store galleries and files // Create the tables we need to store galleries and files
db.schema.hasTable('albums').then(exists => { db.schema.hasTable('albums').then(exists => {
@ -44,6 +46,7 @@ const init = function (db) {
table.integer('enabled') table.integer('enabled')
table.integer('timestamp') table.integer('timestamp')
table.integer('fileLength') table.integer('fileLength')
table.integer('permission')
}).then(() => { }).then(() => {
db.table('users').where({ username: 'root' }).then((user) => { db.table('users').where({ username: 'root' }).then((user) => {
if (user.length > 0) { return } if (user.length > 0) { return }
@ -55,7 +58,8 @@ const init = function (db) {
username: 'root', username: 'root',
password: hash, password: hash,
token: require('randomstring').generate(64), token: require('randomstring').generate(64),
timestamp: Math.floor(Date.now() / 1000) timestamp: Math.floor(Date.now() / 1000),
permission: perms.permissions.superadmin
}).then(() => {}) }).then(() => {})
}) })
}) })

View File

@ -1,5 +1,6 @@
const config = require('./../config') const config = require('./../config')
const db = require('knex')(config.database) const db = require('knex')(config.database)
const perms = require('./../controllers/permissionController')
const map = { const map = {
albums: { albums: {
@ -10,7 +11,8 @@ const map = {
}, },
users: { users: {
enabled: 'integer', enabled: 'integer',
fileLength: 'integer' fileLength: 'integer',
permission: 'integer'
} }
} }
@ -30,6 +32,17 @@ migration.start = async () => {
})) }))
})) }))
await db.table('users')
.where('username', 'root')
.first()
.update({
permission: perms.permissions.superadmin
})
.then(rows => {
if (!rows) { return console.log('Unable to update root\'s permission into superadmin.') }
console.log(`Updated root's permission to ${perms.permissions.superadmin} (superadmin).`)
})
console.log('Migration finished! Now start lolisafe normally') console.log('Migration finished! Now start lolisafe normally')
process.exit(0) process.exit(0)
} }

View File

@ -32,9 +32,12 @@ fs.existsSync(`./${config.uploads.folder}/zips`) || fs.mkdirSync(`./${config.upl
safe.use(helmet()) safe.use(helmet())
safe.set('trust proxy', 1) safe.set('trust proxy', 1)
// https://mozilla.github.io/nunjucks/api.html#configure
nunjucks.configure('views', { nunjucks.configure('views', {
autoescape: true, 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.set('view engine', 'njk')
safe.enable('view cache') safe.enable('view cache')
@ -127,7 +130,13 @@ const start = async () => {
if (!created) { return process.exit(1) } 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() start()

View File

@ -9,6 +9,11 @@
.menu-list a { .menu-list a {
color: #3794d2; 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 { .menu-list a:hover {
@ -21,6 +26,15 @@
background-color: #3794d2; background-color: #3794d2;
} }
.menu-list a[disabled] {
color: #7a7a7a;
cursor: not-allowed;
}
.menu-list a[disabled]:hover {
background: none;
}
.pagination a { .pagination a {
color: #eff0f1; color: #eff0f1;
border-color: #4d4d4d; border-color: #4d4d4d;
@ -71,7 +85,7 @@
width: 100%; width: 100%;
} }
.image-container .file-checkbox { .image-container .checkbox {
position: absolute; position: absolute;
top: .75rem; top: .75rem;
left: .75rem; left: .75rem;
@ -118,7 +132,7 @@
/* Make extra info appear on hover only on non-touch devices */ /* Make extra info appear on hover only on non-touch devices */
.no-touch .image-container .file-checkbox { .no-touch .image-container .checkbox {
opacity: .25; opacity: .25;
-webkit-transition: opacity .25s; -webkit-transition: opacity .25s;
transition: opacity .25s; transition: opacity .25s;
@ -131,7 +145,7 @@
transition: opacity .25s; 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 .controls,
.no-touch .image-container:hover .details { .no-touch .image-container:hover .details {
opacity: 1; opacity: 1;
@ -180,6 +194,6 @@
height: 2.25em; height: 2.25em;
} }
.table td a:not([href]) { .is-linethrough {
text-decoration: line-through; text-decoration: line-through
} }

View File

@ -23,6 +23,10 @@ a {
color: #3794d2; color: #3794d2;
} }
a.is-dotted {
border-bottom: 1px dotted #3794d2;
}
a:hover { a:hover {
color: #60a8dc; color: #60a8dc;
} }
@ -96,3 +100,24 @@ hr {
border-color: transparent; border-color: transparent;
color: #fff; color: #fff;
} }
.render {
position: fixed;
right: 0;
bottom: 0;
font-size: 1rem;
color: #bdc3c7;
cursor: pointer;
}
.render.button {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
right: 1%;
opacity: .25;
transition: opacity .25s;
}
.render.button:hover {
opacity: 1;
}

View File

@ -2,11 +2,24 @@
background-color: #31363b; background-color: #31363b;
} }
.swal-modal .field {
text-align: initial;
}
.swal-modal.is-expanded {
width: auto;
max-width: 90%;
}
.swal-title, .swal-title,
.swal-text { .swal-text {
color: #eff0f1; color: #eff0f1;
} }
.swal-text {
text-align: center;
}
.swal-content .label, .swal-content .label,
.swal-content .checkbox, .swal-content .checkbox,
.swal-content .radio { .swal-content .radio {
@ -18,6 +31,13 @@
color: #bdc3c7; color: #bdc3c7;
} }
.swal-content .is-code {
font-family: 'Courier New', Courier, monospace;
border: 1px dashed #eff0f1;
border-radius: 5px;
margin-top: 5px;
}
.swal-button { .swal-button {
background-color: #3794d2; background-color: #3794d2;
color: #eff0f1; color: #eff0f1;

View File

@ -1,7 +1,7 @@
{ {
"root": true, "root": true,
"parserOptions": { "parserOptions": {
"ecmaVersion": 5 "ecmaVersion": 6
}, },
"env": { "env": {
"browser": true "browser": true
@ -17,6 +17,17 @@
"quotes": [ "quotes": [
"error", "error",
"single" "single"
],
"object-shorthand": [
"error",
"always"
],
"prefer-const": [
"error",
{
"destructuring": "any",
"ignoreReadBeforeAssign": false
}
] ]
} }
} }

View File

@ -1,6 +1,6 @@
/* global LazyLoad */ /* global LazyLoad */
var page = { const page = {
lazyLoad: null, lazyLoad: null,
// byte units for getPrettyBytes() // byte units for getPrettyBytes()
@ -12,20 +12,20 @@ page.getPrettyBytes = num => {
// Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com) // Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
if (!Number.isFinite(num)) { return num } if (!Number.isFinite(num)) { return num }
var neg = num < 0 const neg = num < 0
if (neg) { num = -num } if (neg) { num = -num }
if (num < 1) { return (neg ? '-' : '') + num + ' B' } if (num < 1) { return (neg ? '-' : '') + num + ' B' }
var exponent = Math.min(Math.floor(Math.log10(num) / 3), page.byteUnits.length - 1) const exponent = Math.min(Math.floor(Math.log10(num) / 3), page.byteUnits.length - 1)
var numStr = Number((num / Math.pow(1000, exponent)).toPrecision(3)) const numStr = Number((num / Math.pow(1000, exponent)).toPrecision(3))
var unit = page.byteUnits[exponent] const unit = page.byteUnits[exponent]
return (neg ? '-' : '') + numStr + ' ' + unit return (neg ? '-' : '') + numStr + ' ' + unit
} }
window.onload = function () { window.onload = function () {
var elements = document.getElementsByClassName('file-size') const elements = document.getElementsByClassName('file-size')
for (var i = 0; i < elements.length; i++) { for (let i = 0; i < elements.length; i++) {
elements[i].innerHTML = page.getPrettyBytes(parseInt(elements[i].innerHTML)) elements[i].innerHTML = page.getPrettyBytes(parseInt(elements[i].innerHTML))
} }

View File

@ -1,6 +1,6 @@
/* global swal, axios */ /* global swal, axios */
var page = { const page = {
// user token // user token
token: localStorage.token, token: localStorage.token,
@ -10,33 +10,31 @@ var page = {
} }
page.do = function (dest) { page.do = function (dest) {
var user = page.user.value const user = page.user.value
var pass = page.pass.value const pass = page.pass.value
if (!user) { 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) { 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, username: user,
password: pass password: pass
}) }).then(function (response) {
.then(function (response) { if (response.data.success === false) {
if (response.data.success === false) { return swal('An error occurred!', response.data.description, 'error')
return swal('Error', response.data.description, 'error') }
}
localStorage.token = response.data.token localStorage.token = response.data.token
window.location = 'dashboard' window.location = 'dashboard'
}) }).catch(function (error) {
.catch(function (error) { console.error(error)
console.error(error) return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error') })
})
} }
page.verify = function () { page.verify = function () {
@ -44,18 +42,16 @@ page.verify = function () {
axios.post('api/tokens/verify', { axios.post('api/tokens/verify', {
token: page.token token: page.token
}) }).then(function (response) {
.then(function (response) { if (response.data.success === false) {
if (response.data.success === false) { return swal('An error occurred!', response.data.description, 'error')
return swal('Error', response.data.description, 'error') }
}
window.location = 'dashboard' window.location = 'dashboard'
}) }).catch(function (error) {
.catch(function (error) { console.error(error)
console.error(error) return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error') })
})
} }
window.onload = function () { window.onload = function () {

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/* global swal, axios, Dropzone, ClipboardJS, LazyLoad */ /* global swal, axios, Dropzone, ClipboardJS, LazyLoad */
var page = { const page = {
// user token // user token
token: localStorage.token, token: localStorage.token,
@ -18,27 +18,25 @@ var page = {
dropzone: null, dropzone: null,
clipboardJS: null, clipboardJS: null,
lazyLoad: null lazyLoad: null,
imageExtensions: ['.webp', '.jpg', '.jpeg', '.bmp', '.gif', '.png']
} }
var imageExtensions = ['.webp', '.jpg', '.jpeg', '.bmp', '.gif', '.png']
page.checkIfPublic = function () { page.checkIfPublic = function () {
axios.get('api/check') axios.get('api/check').then(function (response) {
.then(function (response) { page.private = response.data.private
page.private = response.data.private page.enableUserAccounts = response.data.enableUserAccounts
page.enableUserAccounts = response.data.enableUserAccounts page.maxFileSize = response.data.maxFileSize
page.maxFileSize = response.data.maxFileSize page.chunkSize = response.data.chunkSize
page.chunkSize = response.data.chunkSize page.preparePage()
page.preparePage() }).catch(function (error) {
}) console.log(error)
.catch(function (error) { const button = document.getElementById('loginToUpload')
console.log(error) button.classList.remove('is-loading')
var button = document.getElementById('loginToUpload') button.innerText = 'Error occurred. Reload the page?'
button.classList.remove('is-loading') return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
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 () { page.preparePage = function () {
@ -46,7 +44,7 @@ page.preparePage = function () {
if (page.token) { if (page.token) {
return page.verifyToken(page.token, true) return page.verifyToken(page.token, true)
} else { } else {
var button = document.getElementById('loginToUpload') const button = document.getElementById('loginToUpload')
button.href = 'auth' button.href = 'auth'
button.classList.remove('is-loading') button.classList.remove('is-loading')
@ -64,29 +62,26 @@ page.preparePage = function () {
page.verifyToken = function (token, reloadOnError) { page.verifyToken = function (token, reloadOnError) {
if (reloadOnError === undefined) { reloadOnError = false } if (reloadOnError === undefined) { reloadOnError = false }
axios.post('api/tokens/verify', { token: token }) axios.post('api/tokens/verify', { token }).then(function (response) {
.then(function (response) { if (response.data.success === false) {
if (response.data.success === false) { return swal({
return swal({ title: 'An error occurred!',
title: 'An error occurred!', text: response.data.description,
text: response.data.description, icon: 'error'
icon: 'error' }).then(function () {
}) if (!reloadOnError) { return }
.then(function () { localStorage.removeItem('token')
if (!reloadOnError) { return } location.reload()
localStorage.removeItem('token') })
location.reload() }
})
}
localStorage.token = token localStorage.token = token
page.token = token page.token = token
return page.prepareUpload() return page.prepareUpload()
}) }).catch(function (error) {
.catch(function (error) { console.log(error)
console.log(error) return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')
return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error') })
})
} }
page.prepareUpload = function () { page.prepareUpload = function () {
@ -104,24 +99,24 @@ page.prepareUpload = function () {
document.getElementById('albumDiv').style.display = 'flex' 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' document.getElementById('loginToUpload').style.display = 'none'
if (!page.token && page.enableUserAccounts) { if (!page.token && page.enableUserAccounts) {
document.getElementById('loginLinkText').innerHTML = 'Create an account and keep track of your uploads' 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 page.previewTemplate = previewNode.innerHTML
previewNode.parentNode.removeChild(previewNode) previewNode.parentNode.removeChild(previewNode)
page.prepareDropzone() page.prepareDropzone()
var tabs = document.getElementById('tabs') const tabs = document.getElementById('tabs')
if (tabs) { if (tabs) {
tabs.style.display = 'flex' tabs.style.display = 'flex'
var items = tabs.getElementsByTagName('li') const items = tabs.getElementsByTagName('li')
for (var i = 0; i < items.length; i++) { for (let i = 0; i < items.length; i++) {
items[i].addEventListener('click', function () { items[i].addEventListener('click', function () {
page.setActiveTab(this.dataset.id) page.setActiveTab(this.dataset.id)
}) })
@ -136,42 +131,44 @@ page.prepareUpload = function () {
} }
page.prepareAlbums = function () { page.prepareAlbums = function () {
var option = document.createElement('option') const option = document.createElement('option')
option.value = '' option.value = ''
option.innerHTML = 'Upload to album' option.innerHTML = 'Upload to album'
option.disabled = true option.disabled = true
option.selected = true option.selected = true
page.albumSelect.appendChild(option) page.albumSelect.appendChild(option)
axios.get('api/albums', { headers: { token: page.token } }) axios.get('api/albums', {
.then(function (response) { headers: {
if (response.data.success === false) { token: page.token
return swal('An error occurred!', response.data.description, 'error') }
} }).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 // If the user doesn't have any albums we don't really need to display
// an album selection // an album selection
if (!response.data.albums.length) { return } if (!response.data.albums.length) { return }
// Loop through the albums and create an option for each album // Loop through the albums and create an option for each album
for (var i = 0; i < response.data.albums.length; i++) { for (let i = 0; i < response.data.albums.length; i++) {
var album = response.data.albums[i] const album = response.data.albums[i]
var option = document.createElement('option') const option = document.createElement('option')
option.value = album.id option.value = album.id
option.innerHTML = album.name option.innerHTML = album.name
page.albumSelect.appendChild(option) page.albumSelect.appendChild(option)
} }
}) }).catch(function (error) {
.catch(function (error) { console.log(error)
console.log(error) return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', '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) { page.setActiveTab = function (activeId) {
var items = document.getElementById('tabs').getElementsByTagName('li') const items = document.getElementById('tabs').getElementsByTagName('li')
for (var i = 0; i < items.length; i++) { for (let i = 0; i < items.length; i++) {
var tabId = items[i].dataset.id const tabId = items[i].dataset.id
if (tabId === activeId) { if (tabId === activeId) {
items[i].classList.add('is-active') items[i].classList.add('is-active')
document.getElementById(tabId).style.display = 'block' document.getElementById(tabId).style.display = 'block'
@ -183,27 +180,27 @@ page.setActiveTab = function (activeId) {
} }
page.prepareDropzone = function () { page.prepareDropzone = function () {
var tabDiv = document.getElementById('tab-files') const tabDiv = document.getElementById('tab-files')
var div = document.createElement('div') const div = document.createElement('div')
div.className = 'control is-expanded' div.className = 'control is-expanded'
div.innerHTML = div.innerHTML = `
'<div id="dropzone" class="button is-danger is-fullwidth is-unselectable">\n' + <div id="dropzone" class="button is-danger is-fullwidth is-unselectable">
' <span class="icon">\n' + <span class="icon">
' <i class="icon-upload-cloud"></i>\n' + <i class="icon-upload-cloud"></i>
' </span>\n' + </span>
' <span>Click here or drag and drop files</span>\n' + <span>Click here or drag and drop files</span>
'</div>' </div>
`
tabDiv.querySelector('.dz-container').appendChild(div)
tabDiv.getElementsByClassName('dz-container')[0].appendChild(div) const previewsContainer = tabDiv.querySelector('#tab-files .field.uploads')
var previewsContainer = tabDiv.getElementsByClassName('uploads')[0]
page.dropzone = new Dropzone('#dropzone', { page.dropzone = new Dropzone('#dropzone', {
url: 'api/upload', url: 'api/upload',
paramName: 'files[]', paramName: 'files[]',
maxFilesize: parseInt(page.maxFileSize), maxFilesize: parseInt(page.maxFileSize),
parallelUploads: 2, parallelUploads: 2,
uploadMultiple: false, uploadMultiple: false,
previewsContainer: previewsContainer, previewsContainer,
previewTemplate: page.previewTemplate, previewTemplate: page.previewTemplate,
createImageThumbnails: false, createImageThumbnails: false,
maxFiles: 1000, maxFiles: 1000,
@ -212,45 +209,41 @@ page.prepareDropzone = function () {
chunking: Boolean(page.chunkSize), chunking: Boolean(page.chunkSize),
chunkSize: parseInt(page.chunkSize) * 1000000, // 1000000 B = 1 MB, chunkSize: parseInt(page.chunkSize) * 1000000, // 1000000 B = 1 MB,
parallelChunkUploads: false, // when set to true, sometimes it often hangs with hundreds of parallel uploads 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').setAttribute('value', 100)
file.previewElement.querySelector('.progress').innerHTML = '100%' file.previewElement.querySelector('.progress').innerHTML = '100%'
// The API supports an array of multiple files return axios.post('api/upload/finishchunks', {
return axios.post('api/upload/finishchunks', // The API supports an array of multiple files
{ files: [{
files: [{ uuid: file.upload.uuid,
uuid: file.upload.uuid, original: file.name,
original: file.name, size: file.size,
size: file.size, type: file.type,
type: file.type, count: file.upload.totalChunkCount,
count: file.upload.totalChunkCount, albumid: page.album
albumid: page.album }]
}] }, {
}, headers: {
{ token: page.token
headers: { }
token: page.token }).then(function (response) {
} file.previewElement.querySelector('.progress').style.display = 'none'
})
.then(function (response) {
file.previewElement.querySelector('.progress').style.display = 'none'
if (response.data.success === false) { if (response.data.success === false) {
file.previewElement.querySelector('.error').innerHTML = response.data.description file.previewElement.querySelector('.error').innerHTML = response.data.description
} }
if (response.data.files && response.data.files[0]) { if (response.data.files && response.data.files[0]) {
page.updateTemplate(file, response.data.files[0]) page.updateTemplate(file, response.data.files[0])
} }
return done() return done()
}) }).catch(function (error) {
.catch(function (error) { return {
return { success: false,
success: false, description: error.toString()
description: error.toString() }
} })
})
} }
}) })
@ -269,7 +262,7 @@ page.prepareDropzone = function () {
page.dropzone.on('uploadprogress', function (file, progress) { page.dropzone.on('uploadprogress', function (file, progress) {
if (file.upload.chunked && progress === 100) { return } if (file.upload.chunked && progress === 100) { return }
file.previewElement.querySelector('.progress').setAttribute('value', progress) 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) { page.dropzone.on('success', function (file, response) {
@ -296,7 +289,7 @@ page.prepareDropzone = function () {
} }
page.uploadUrls = function (button) { page.uploadUrls = function (button) {
var tabDiv = document.getElementById('tab-urls') const tabDiv = document.getElementById('tab-urls')
if (!tabDiv) { return } if (!tabDiv) { return }
if (button.classList.contains('is-loading')) { return } if (button.classList.contains('is-loading')) { return }
@ -308,9 +301,9 @@ page.uploadUrls = function (button) {
} }
function run () { function run () {
var albumid = page.album const albumid = page.album
var previewsContainer = tabDiv.getElementsByClassName('uploads')[0] const previewsContainer = tabDiv.getElementsByClassName('uploads')[0]
var urls = document.getElementById('urls').value const urls = document.getElementById('urls').value
.split(/\r?\n/) .split(/\r?\n/)
.filter(function (url) { return url.trim().length }) .filter(function (url) { return url.trim().length })
document.getElementById('urls').value = urls.join('\n') document.getElementById('urls').value = urls.join('\n')
@ -321,22 +314,22 @@ page.uploadUrls = function (button) {
} }
tabDiv.getElementsByClassName('uploads')[0].style.display = 'block' tabDiv.getElementsByClassName('uploads')[0].style.display = 'block'
var files = urls.map(function (url) { const files = urls.map(function (url) {
var previewTemplate = document.createElement('template') const previewTemplate = document.createElement('template')
previewTemplate.innerHTML = page.previewTemplate.trim() previewTemplate.innerHTML = page.previewTemplate.trim()
var previewElement = previewTemplate.content.firstChild const previewElement = previewTemplate.content.firstChild
previewElement.querySelector('.name').innerHTML = url previewElement.querySelector('.name').innerHTML = url
previewsContainer.appendChild(previewElement) previewsContainer.appendChild(previewElement)
return { return {
url: url, url,
previewElement: previewElement previewElement
} }
}) })
function post (i) { function post (i) {
if (i === files.length) { return done() } if (i === files.length) { return done() }
var file = files[i] const file = files[i]
function posted (result) { function posted (result) {
file.previewElement.querySelector('.progress').style.display = 'none' file.previewElement.querySelector('.progress').style.display = 'none'
@ -348,25 +341,21 @@ page.uploadUrls = function (button) {
return post(i + 1) return post(i + 1)
} }
axios.post('api/upload', axios.post('api/upload', {
{ urls: [file.url]
urls: [file.url] }, {
}, headers: {
{ token: page.token,
headers: { albumid
token: page.token, }
albumid: albumid }).then(function (response) {
} return posted(response.data)
}) }).catch(function (error) {
.then(function (response) { return posted({
return posted(response.data) success: false,
}) description: error.toString()
.catch(function (error) {
return posted({
success: false,
description: error.toString()
})
}) })
})
} }
return post(0) return post(0)
} }
@ -376,14 +365,14 @@ page.uploadUrls = function (button) {
page.updateTemplate = function (file, response) { page.updateTemplate = function (file, response) {
if (!response.url) { return } if (!response.url) { return }
var a = file.previewElement.querySelector('.link > a') const a = file.previewElement.querySelector('.link > a')
var clipboard = file.previewElement.querySelector('.clipboard-mobile > .clipboard-js') const clipboard = file.previewElement.querySelector('.clipboard-mobile > .clipboard-js')
a.href = a.innerHTML = clipboard.dataset['clipboardText'] = response.url a.href = a.innerHTML = clipboard.dataset['clipboardText'] = response.url
clipboard.parentElement.style.display = 'block' 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())) { if (exec && exec[0] && page.imageExtensions.includes(exec[0].toLowerCase())) {
var img = file.previewElement.querySelector('img') const img = file.previewElement.querySelector('img')
img.setAttribute('alt', response.name || '') img.setAttribute('alt', response.name || '')
img.dataset['src'] = response.url img.dataset['src'] = response.url
img.onerror = function () { this.style.display = 'none' } // hide webp in firefox and ie img.onerror = function () { this.style.display = 'none' } // hide webp in firefox and ie
@ -392,30 +381,31 @@ page.updateTemplate = function (file, response) {
} }
page.createAlbum = function () { page.createAlbum = function () {
var div = document.createElement('div') const div = document.createElement('div')
div.innerHTML = div.innerHTML = `
'<div class="field">\n' + <div class="field">
' <label class="label">Album name</label>\n' + <label class="label">Album name</label>
' <div class="controls">\n' + <div class="controls">
' <input id="_name" class="input" type="text" placeholder="My super album">\n' + <input id="swalName" class="input" type="text" placeholder="My super album">
' </div>\n' + </div>
'</div>\n' + </div>
'<div class="field">\n' + <div class="field">
' <div class="control">\n' + <div class="control">
' <label class="checkbox">\n' + <label class="checkbox">
' <input id="_download" type="checkbox" checked>\n' + <input id="swalDownload" type="checkbox" checked>
' Enable download\n' + Enable download
' </label>\n' + </label>
' </div>\n' + </div>
'</div>\n' + </div>
'<div class="field">\n' + <div class="field">
' <div class="control">\n' + <div class="control">
' <label class="checkbox">\n' + <label class="checkbox">
' <input id="_public" type="checkbox" checked>\n' + <input id="swalPublic" type="checkbox" checked>
' Enable public link\n' + Enable public link
' </label>\n' + </label>
' </div>\n' + </div>
'</div>' </div>
`
swal({ swal({
title: 'Create new album', title: 'Create new album',
@ -427,43 +417,44 @@ page.createAlbum = function () {
closeModal: false closeModal: false
} }
} }
}) }).then(function (value) {
.then(function (value) { if (!value) { return }
if (!value) { return }
var name = document.getElementById('_name').value const name = document.getElementById('swalName').value
axios.post('api/albums', { axios.post('api/albums', {
name: name, name,
download: document.getElementById('_download').checked, download: document.getElementById('swalDownload').checked,
public: document.getElementById('_public').checked public: document.getElementById('swalPublic').checked
}, { headers: { token: page.token } }) }, {
.then(function (response) { headers: {
if (response.data.success === false) { token: page.token
return swal('An error occurred!', response.data.description, 'error') }
} }).then(function (response) {
if (response.data.success === false) {
return swal('An error occurred!', response.data.description, 'error')
}
var option = document.createElement('option') const option = document.createElement('option')
option.value = response.data.id option.value = response.data.id
option.innerHTML = name option.innerHTML = name
page.albumSelect.appendChild(option) page.albumSelect.appendChild(option)
swal('Woohoo!', 'Album was created successfully', 'success') swal('Woohoo!', 'Album was created successfully', 'success')
}) }).catch(function (error) {
.catch(function (error) { console.log(error)
console.log(error) return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', '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 // Handle image paste event
window.addEventListener('paste', function (event) { window.addEventListener('paste', function (event) {
var items = (event.clipboardData || event.originalEvent.clipboardData).items const items = (event.clipboardData || event.originalEvent.clipboardData).items
for (var index in items) { for (const index in items) {
var item = items[index] const item = items[index]
if (item.kind === 'file') { if (item.kind === 'file') {
var blob = item.getAsFile() const blob = item.getAsFile()
var file = new File([blob], 'pasted-image.' + blob.type.match(/(?:[^/]*\/)([^;]*)/)[1]) const file = new File([blob], `pasted-image.${blob.type.match(/(?:[^/]*\/)([^;]*)/)[1]}`)
file.type = blob.type file.type = blob.type
page.dropzone.addFile(file) page.dropzone.addFile(file)
} }
@ -484,7 +475,9 @@ window.onload = function () {
return swal('An error occurred!', 'There was an error when trying to copy the link to clipboard, please check the console for more information.', 'error') return swal('An error occurred!', 'There was an error when trying to copy the link to clipboard, please check the console for more information.', 'error')
}) })
page.lazyLoad = new LazyLoad() page.lazyLoad = new LazyLoad({
elements_selector: '.field.uploads img'
})
document.getElementById('createAlbum').addEventListener('click', function () { document.getElementById('createAlbum').addEventListener('click', function () {
page.createAlbum() page.createAlbum()

92
public/js/render.js Normal file
View File

@ -0,0 +1,92 @@
/* global page, swal */
page.renderRoot = 'render/al/'
page.renderArray = [
'atago_1.png',
'atago_2.png',
'belfast_1.png',
'belfast_2.png',
'belfast_3.png',
'eldridge_1.png',
'hammann_1.png',
'hammann_2.png',
'javelin_1.png',
'kaga_1.png',
'laffey_1.png',
'prinz_eugen_1.png',
'prinz_eugen_2.png',
'takao_1.png',
'takao_2.png',
'unicorn_1.png',
'unicorn_2.png',
'unicorn_3.png',
'unicorn_4.png',
'unicorn_5.png',
'yamashiro_1.png'
]
page.render = null
page.doRenderSwal = function () {
const div = document.createElement('div')
div.innerHTML = `
<div class="field">
<div class="control">
<label class="checkbox">
<input id="swalRender" type="checkbox" ${localStorage.render === '0' ? '' : 'checked'}>
Enable random render of ship waifu~
</label>
</div>
<p class="help">If disabled, you will still be able to see a small button on the bottom right corner of the screen to re-enable it.</p>
</div>
`
swal({
content: div,
buttons: {
confirm: true
}
}).then(function (value) {
if (!value) { return }
const newValue = div.querySelector('#swalRender').checked ? undefined : '0'
if (newValue !== localStorage.render) {
newValue ? localStorage.render = newValue : localStorage.removeItem('render')
swal('Success!', `Render is now ${newValue ? 'disabled' : 'enabled'}.`, 'success')
const element = document.querySelector('body > .render')
element.remove()
page.doRender()
}
})
}
page.getRenderVersion = function () {
const renderScript = document.getElementById('renderScript')
if (!renderScript) { return '' }
const match = renderScript.src.match(/\?v=\w*$/)
if (!match) { return '' }
return match[0]
}
page.doRender = function () {
if (!page.renderRoot || !page.renderArray || !page.renderArray.length) { return }
let element
if (localStorage.render === '0') {
element = document.createElement('a')
element.className = 'button is-breeze is-hidden-mobile'
element.title = 'ship waifu~'
element.innerHTML = '<i class="icon-picture-1"></i>'
} else {
// Let us just allow people to get new render when toggling the option
page.render = page.renderArray[Math.floor(Math.random() * page.renderArray.length)]
element = document.createElement('img')
element.alt = element.title = 'ship waifu~'
element.className = 'is-hidden-mobile'
element.src = `${page.renderRoot}${page.render}${page.getRenderVersion()}`
}
element.classList.add('render')
element.addEventListener('click', page.doRenderSwal)
document.body.appendChild(element)
}
page.doRender()

View File

@ -2,24 +2,23 @@
page.prepareShareX = function () { page.prepareShareX = function () {
if (!page.token) { return } if (!page.token) { return }
var origin = (location.hostname + location.pathname).replace(/\/(dashboard)?$/, '') const origin = (location.hostname + location.pathname).replace(/\/(dashboard)?$/, '')
var originClean = origin.replace(/\//g, '_') const originClean = origin.replace(/\//g, '_')
var sharexElement = document.getElementById('ShareX') const sharexElement = document.getElementById('ShareX')
var sharexFile = const sharexFile = `{
'{\r\n' + "Name": "${originClean}",
' "Name": "' + originClean + '",\r\n' + "DestinationType": "ImageUploader, FileUploader",
' "DestinationType": "ImageUploader, FileUploader",\r\n' + "RequestType": "POST",
' "RequestType": "POST",\r\n' + "RequestURL": "${location.protocol}//${origin}/api/upload",
' "RequestURL": "' + location.protocol + '//' + origin + '/api/upload",\r\n' + "FileFormName": "files[]",
' "FileFormName": "files[]",\r\n' + "Headers": {
' "Headers": {\r\n' + "token": "${page.token}"
' "token": "' + page.token + '"\r\n' + },
' },\r\n' + "ResponseType": "Text",
' "ResponseType": "Text",\r\n' + "URL": "$json:files[0].url$",
' "URL": "$json:files[0].url$",\r\n' + "ThumbnailURL": "$json:files[0].url$"
' "ThumbnailURL": "$json:files[0].url$"\r\n' + }\n`
'}' const sharexBlob = new Blob([sharexFile], { type: 'application/octet-binary' })
var sharexBlob = new Blob([sharexFile], { type: 'application/octet-binary' })
sharexElement.setAttribute('href', URL.createObjectURL(sharexBlob)) sharexElement.setAttribute('href', URL.createObjectURL(sharexBlob))
sharexElement.setAttribute('download', originClean + '.sxcu') sharexElement.setAttribute('download', `${originClean}.sxcu`)
} }

View File

@ -1,11 +1,11 @@
@font-face { @font-face {
font-family: 'fontello'; font-family: 'fontello';
src: url('fontello.eot?61363773'); src: url('fontello.eot?54767678');
src: url('fontello.eot?61363773#iefix') format('embedded-opentype'), src: url('fontello.eot?54767678#iefix') format('embedded-opentype'),
url('fontello.woff2?61363773') format('woff2'), url('fontello.woff2?54767678') format('woff2'),
url('fontello.woff?61363773') format('woff'), url('fontello.woff?54767678') format('woff'),
url('fontello.ttf?61363773') format('truetype'), url('fontello.ttf?54767678') format('truetype'),
url('fontello.svg?61363773#fontello') format('svg'); url('fontello.svg?54767678#fontello') format('svg');
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
} }
@ -33,6 +33,10 @@
text-align: center; text-align: center;
/* opacity: .8; */ /* opacity: .8; */
/* For safety - reset parent styles, that can break glyph codes*/
font-variant: normal;
text-transform: none;
/* fix buttons height, for twitter bootstrap */ /* fix buttons height, for twitter bootstrap */
/* line-height: 1em; */ /* line-height: 1em; */
@ -55,12 +59,12 @@
font-size: 2rem; font-size: 2rem;
} }
.icon-sharex:before { content: '\e800'; } /* '' */ .icon-pencil-1:before { content: '\e800'; } /* '' */
.icon-upload-cloud:before { content: '\e801'; } /* '' */ .icon-sharex:before { content: '\e801'; } /* '' */
.icon-picture-1:before { content: '\e802'; } /* '' */ .icon-upload-cloud:before { content: '\e802'; } /* '' */
.icon-th-list:before { content: '\e803'; } /* '' */ .icon-picture-1:before { content: '\e803'; } /* '' */
.icon-trash:before { content: '\e804'; } /* '' */ .icon-th-list:before { content: '\e804'; } /* '' */
.icon-pencil-1:before { content: '\e805'; } /* '' */ .icon-trash:before { content: '\e805'; } /* '' */
.icon-th-large:before { content: '\e806'; } /* '' */ .icon-th-large:before { content: '\e806'; } /* '' */
.icon-arrows-cw:before { content: '\e807'; } /* '' */ .icon-arrows-cw:before { content: '\e807'; } /* '' */
.icon-plus:before { content: '\e808'; } /* '' */ .icon-plus:before { content: '\e808'; } /* '' */
@ -72,6 +76,7 @@
.icon-download:before { content: '\e80e'; } /* '' */ .icon-download:before { content: '\e80e'; } /* '' */
.icon-help-circled:before { content: '\e80f'; } /* '' */ .icon-help-circled:before { content: '\e80f'; } /* '' */
.icon-terminal:before { content: '\e810'; } /* '' */ .icon-terminal:before { content: '\e810'; } /* '' */
.icon-hammer:before { content: '\e811'; } /* '' */
.icon-github-circled:before { content: '\f09b'; } /* '' */ .icon-github-circled:before { content: '\f09b'; } /* '' */
.icon-gauge:before { content: '\f0e4'; } /* '' */ .icon-gauge:before { content: '\f0e4'; } /* '' */
.icon-paper-plane-empty:before { content: '\f1d9'; } /* '' */ .icon-paper-plane-empty:before { content: '\f1d9'; } /* '' */

Binary file not shown.

View File

@ -6,17 +6,17 @@
<font id="fontello" horiz-adv-x="1000" > <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" /> <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" /> <missing-glyph horiz-adv-x="1000" />
<glyph glyph-name="sharex" unicode="&#xe800;" 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="&#xe800;" 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="&#xe801;" 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="&#xe801;" 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="&#xe802;" 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="&#xe802;" 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="&#xe803;" 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="&#xe803;" 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="&#xe804;" 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="&#xe804;" 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="&#xe805;" 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="&#xe805;" 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="&#xe806;" 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" /> <glyph glyph-name="th-large" unicode="&#xe806;" 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="&#xe810;" 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="terminal" unicode="&#xe810;" 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="&#xe811;" 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="&#xf09b;" 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="github-circled" unicode="&#xf09b;" 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="&#xf0e4;" 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" /> <glyph glyph-name="gauge" unicode="&#xf0e4;" 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.

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

BIN
public/render/al/kaga_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

View File

@ -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.post('/tokens/change', (req, res, next) => tokenController.change(req, res, next))
routes.get('/filelength/config', (req, res, next) => authController.getFileLengthConfig(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.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 module.exports = routes

View File

@ -15,9 +15,9 @@
v2: Images and config files (manifest.json, browserconfig.xml, etc). v2: Images and config files (manifest.json, browserconfig.xml, etc).
v3: CSS and JS files (libs such as bulma, lazyload, etc). v3: CSS and JS files (libs such as bulma, lazyload, etc).
#} #}
{% set v1 = "UW9mVRyFee" %} {% set v1 = "LZ9JN4pnIf" %}
{% set v2 = "Ii3JYKIhb0" %} {% set v2 = "Ii3JYKIhb0" %}
{% set v3 = "HrvcYD3KTh" %} {% set v3 = "6MfcbDZldp" %}
{# {#
These will be the links in the homepage and the No-JS uploader. These will be the links in the homepage and the No-JS uploader.

View File

@ -70,6 +70,15 @@
</li> </li>
</ul> </ul>
<p class="menu-label">Administration</p> <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"> <ul class="menu-list">
<li> <li>
<a id="ShareX">ShareX user profile</a> <a id="ShareX">ShareX user profile</a>

View File

@ -23,6 +23,8 @@
<script type="text/javascript" src="libs/lazyload/lazyload.min.js?v={{ globals.v3 }}"></script> <script type="text/javascript" src="libs/lazyload/lazyload.min.js?v={{ globals.v3 }}"></script>
<script type="text/javascript" src="js/home.js?v={{ globals.v1 }}"></script> <script type="text/javascript" src="js/home.js?v={{ globals.v1 }}"></script>
<script type="text/javascript" src="js/sharex.js?v={{ globals.v1 }}"></script> <script type="text/javascript" src="js/sharex.js?v={{ globals.v1 }}"></script>
<!-- We assign an ID for this so that the script can find out its own version string -->
<script id="renderScript" type="text/javascript" src="js/render.js?v={{ globals.v1 }}"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@ -126,7 +128,7 @@
{% include "_partial/links.njk" %} {% include "_partial/links.njk" %}
{% if gitHash -%} {% if gitHash -%}
<p>Git commit: <a href="https://github.com/BobbyWibowo/lolisafe/commit/{{ gitHash }}" target="_blank" rel="noopener">{{ gitHash }}</a></p> <p class="git-commit">Git commit: <a class="is-dotted" href="https://github.com/BobbyWibowo/lolisafe/commit/{{ gitHash }}" target="_blank" rel="noopener">{{ gitHash }}</a></p>
{%- endif %} {%- endif %}
</div> </div>
</div> </div>