mirror of
https://github.com/BobbyWibowo/lolisafe.git
synced 2025-02-22 13:19:05 +00:00
Various updates
* Switched ESLint + Aqua to Standard. I'm a big fan of Standard. Updated yarn.lock file too. * Lots of refactors to follow the rules of Standard. * Fixed issue with uploading as a not logged in user.
This commit is contained in:
parent
da40ea71b1
commit
bcdfcd7064
134
config.sample.js
134
config.sample.js
@ -1,84 +1,84 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
If set to true the user will need to specify the auto-generated token
|
If set to true the user will need to specify the auto-generated token
|
||||||
on each API call, meaning random strangers wont be able to use the service
|
on each API call, meaning random strangers wont be able to use the service
|
||||||
unless they have the token loli-safe provides you with.
|
unless they have the token loli-safe provides you with.
|
||||||
If it's set to false, then upload will be public for anyone to use.
|
If it's set to false, then upload will be public for anyone to use.
|
||||||
*/
|
*/
|
||||||
private: true,
|
private: true,
|
||||||
|
|
||||||
// If true, users will be able to create accounts and access their uploaded files
|
// If true, users will be able to create accounts and access their uploaded files
|
||||||
enableUserAccounts: true,
|
enableUserAccounts: true,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Here you can decide if you want lolisafe to serve the files or if you prefer doing so via nginx.
|
Here you can decide if you want lolisafe to serve the files or if you prefer doing so via nginx.
|
||||||
The main difference between the two is the ease of use and the chance of analytics in the future.
|
The main difference between the two is the ease of use and the chance of analytics in the future.
|
||||||
If you set it to `true`, the uploaded files will be located after the host like:
|
If you set it to `true`, the uploaded files will be located after the host like:
|
||||||
https://lolisafe.moe/yourFile.jpg
|
https://lolisafe.moe/yourFile.jpg
|
||||||
|
|
||||||
If you set it to `false`, you need to set nginx to directly serve whatever folder it is you are serving your
|
If you set it to `false`, you need to set nginx to directly serve whatever folder it is you are serving your
|
||||||
downloads in. This also gives you the ability to serve them, for example, like this:
|
downloads in. This also gives you the ability to serve them, for example, like this:
|
||||||
https://files.lolisafe.moe/yourFile.jpg
|
https://files.lolisafe.moe/yourFile.jpg
|
||||||
|
|
||||||
Both cases require you to type the domain where the files will be served on the `domain` key below.
|
Both cases require you to type the domain where the files will be served on the `domain` key below.
|
||||||
Which one you use is ultimately up to you.
|
Which one you use is ultimately up to you.
|
||||||
*/
|
*/
|
||||||
serveFilesWithNode: false,
|
serveFilesWithNode: false,
|
||||||
domain: 'https://lolisafe.moe',
|
domain: 'https://lolisafe.moe',
|
||||||
|
|
||||||
// Port on which to run the server
|
// Port on which to run the server
|
||||||
port: 9999,
|
port: 9999,
|
||||||
|
|
||||||
// Pages to process for the frontend
|
// Pages to process for the frontend
|
||||||
pages: ['home', 'auth', 'dashboard', 'faq'],
|
pages: ['home', 'auth', 'dashboard', 'faq'],
|
||||||
|
|
||||||
// Add file extensions here which should be blocked
|
// Add file extensions here which should be blocked
|
||||||
blockedExtensions: [
|
blockedExtensions: [
|
||||||
'.exe',
|
'.exe',
|
||||||
'.bat',
|
'.bat',
|
||||||
'.cmd',
|
'.cmd',
|
||||||
'.msi',
|
'.msi',
|
||||||
'.sh'
|
'.sh'
|
||||||
],
|
],
|
||||||
|
|
||||||
// Uploads config
|
// Uploads config
|
||||||
uploads: {
|
uploads: {
|
||||||
|
|
||||||
// Folder where images should be stored
|
// Folder where images should be stored
|
||||||
folder: 'uploads',
|
folder: 'uploads',
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Max file size allowed. Needs to be in MB
|
Max file size allowed. Needs to be in MB
|
||||||
Note: When maxSize is greater than 1 MiB, you must set the client_max_body_size to the same as maxSize.
|
Note: When maxSize is greater than 1 MiB, you must set the client_max_body_size to the same as maxSize.
|
||||||
*/
|
*/
|
||||||
maxSize: '512MB',
|
maxSize: '512MB',
|
||||||
|
|
||||||
// The length of the random generated name for the uploaded files
|
// The length of the random generated name for the uploaded files
|
||||||
fileLength: 32,
|
fileLength: 32,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
NOTE: Thumbnails are only for the admin panel and they require you
|
NOTE: Thumbnails are only for the admin panel and they require you
|
||||||
to install a separate binary called graphicsmagick (http://www.graphicsmagick.org)
|
to install a separate binary called graphicsmagick (http://www.graphicsmagick.org)
|
||||||
for images and ffmpeg (https://ffmpeg.org/) for video files
|
for images and ffmpeg (https://ffmpeg.org/) for video files
|
||||||
*/
|
*/
|
||||||
generateThumbnails: false,
|
generateThumbnails: false,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Allows users to download a .zip file of all files in an album.
|
Allows users to download a .zip file of all files in an album.
|
||||||
The file is generated when the user clicks the download button in the view
|
The file is generated when the user clicks the download button in the view
|
||||||
and is re-used if the album has not changed between download requests
|
and is re-used if the album has not changed between download requests
|
||||||
*/
|
*/
|
||||||
generateZips: true
|
generateZips: true
|
||||||
},
|
},
|
||||||
|
|
||||||
// Folder where to store logs
|
// Folder where to store logs
|
||||||
logsFolder: 'logs',
|
logsFolder: 'logs',
|
||||||
|
|
||||||
// The following values shouldn't be touched
|
// The following values shouldn't be touched
|
||||||
database: {
|
database: {
|
||||||
client: 'sqlite3',
|
client: 'sqlite3',
|
||||||
connection: { filename: './database/db' },
|
connection: { filename: './database/db' },
|
||||||
useNullAsDefault: true
|
useNullAsDefault: true
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
@ -1,179 +1,178 @@
|
|||||||
const config = require('../config.js');
|
const config = require('../config.js')
|
||||||
const db = require('knex')(config.database);
|
const db = require('knex')(config.database)
|
||||||
const randomstring = require('randomstring');
|
const randomstring = require('randomstring')
|
||||||
const utils = require('./utilsController.js');
|
const utils = require('./utilsController.js')
|
||||||
const path = require('path');
|
const path = require('path')
|
||||||
const fs = require('fs');
|
const fs = require('fs')
|
||||||
const Zip = require('jszip');
|
const Zip = require('jszip')
|
||||||
const albumsController = {};
|
const albumsController = {}
|
||||||
|
|
||||||
albumsController.list = async (req, res, next) => {
|
albumsController.list = async (req, res, next) => {
|
||||||
const user = await utils.authorize(req, res);
|
const user = await utils.authorize(req, res)
|
||||||
|
|
||||||
const fields = ['id', 'name'];
|
const fields = ['id', 'name']
|
||||||
if (req.params.sidebar === undefined) {
|
if (req.params.sidebar === undefined) {
|
||||||
fields.push('timestamp');
|
fields.push('timestamp')
|
||||||
fields.push('identifier');
|
fields.push('identifier')
|
||||||
}
|
}
|
||||||
|
|
||||||
const albums = await db.table('albums').select(fields).where({ enabled: 1, userid: user.id });
|
const albums = await db.table('albums').select(fields).where({ enabled: 1, userid: user.id })
|
||||||
if (req.params.sidebar !== undefined) {
|
if (req.params.sidebar !== undefined) {
|
||||||
return res.json({ success: true, albums });
|
return res.json({ success: true, albums })
|
||||||
}
|
}
|
||||||
|
|
||||||
let ids = [];
|
let ids = []
|
||||||
for (let album of albums) {
|
for (let album of albums) {
|
||||||
album.date = new Date(album.timestamp * 1000)
|
album.date = new Date(album.timestamp * 1000)
|
||||||
album.date = utils.getPrettyDate(album.date)
|
album.date = utils.getPrettyDate(album.date)
|
||||||
|
|
||||||
album.identifier = `${config.domain}/a/${album.identifier}`;
|
album.identifier = `${config.domain}/a/${album.identifier}`
|
||||||
ids.push(album.id);
|
ids.push(album.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
const files = await db.table('files').whereIn('albumid', ids).select('albumid');
|
const files = await db.table('files').whereIn('albumid', ids).select('albumid')
|
||||||
const albumsCount = {};
|
const albumsCount = {}
|
||||||
|
|
||||||
for (let id of ids) albumsCount[id] = 0;
|
for (let id of ids) albumsCount[id] = 0
|
||||||
for (let file of files) albumsCount[file.albumid] += 1;
|
for (let file of files) albumsCount[file.albumid] += 1
|
||||||
for (let album of albums) album.files = albumsCount[album.id];
|
for (let album of albums) album.files = albumsCount[album.id]
|
||||||
|
|
||||||
return res.json({ success: true, albums });
|
return res.json({ success: true, albums })
|
||||||
};
|
}
|
||||||
|
|
||||||
albumsController.create = async (req, res, next) => {
|
albumsController.create = async (req, res, next) => {
|
||||||
const user = await utils.authorize(req, res);
|
const user = await utils.authorize(req, res)
|
||||||
|
|
||||||
const name = req.body.name;
|
const name = req.body.name
|
||||||
if (name === undefined || name === '') {
|
if (name === undefined || name === '') {
|
||||||
return res.json({ success: false, description: 'No album name specified' });
|
return res.json({ success: false, description: 'No album name specified' })
|
||||||
}
|
}
|
||||||
|
|
||||||
const album = await db.table('albums').where({
|
const album = await db.table('albums').where({
|
||||||
name: name,
|
name: name,
|
||||||
enabled: 1,
|
enabled: 1,
|
||||||
userid: user.id
|
userid: user.id
|
||||||
}).first();
|
}).first()
|
||||||
|
|
||||||
if (album) {
|
if (album) {
|
||||||
return res.json({ success: false, description: 'There\'s already an album with that name' })
|
return res.json({ success: false, description: 'There\'s already an album with that name' })
|
||||||
}
|
}
|
||||||
|
|
||||||
await db.table('albums').insert({
|
await db.table('albums').insert({
|
||||||
name: name,
|
name: name,
|
||||||
enabled: 1,
|
enabled: 1,
|
||||||
userid: user.id,
|
userid: user.id,
|
||||||
identifier: randomstring.generate(8),
|
identifier: randomstring.generate(8),
|
||||||
timestamp: Math.floor(Date.now() / 1000)
|
timestamp: Math.floor(Date.now() / 1000)
|
||||||
});
|
})
|
||||||
|
|
||||||
return res.json({ success: true });
|
return res.json({ success: true })
|
||||||
};
|
}
|
||||||
|
|
||||||
albumsController.delete = async (req, res, next) => {
|
albumsController.delete = async (req, res, next) => {
|
||||||
const user = await utils.authorize(req, res);
|
const user = await utils.authorize(req, res)
|
||||||
|
|
||||||
const id = req.body.id;
|
const id = req.body.id
|
||||||
if (id === undefined || id === '') {
|
if (id === undefined || id === '') {
|
||||||
return res.json({ success: false, description: 'No album specified' });
|
return res.json({ success: false, description: 'No album specified' })
|
||||||
}
|
}
|
||||||
|
|
||||||
await db.table('albums').where({ id: id, userid: user.id }).update({ enabled: 0 });
|
await db.table('albums').where({ id: id, userid: user.id }).update({ enabled: 0 })
|
||||||
return res.json({ success: true });
|
return res.json({ success: true })
|
||||||
};
|
}
|
||||||
|
|
||||||
albumsController.rename = async (req, res, next) => {
|
albumsController.rename = async (req, res, next) => {
|
||||||
const user = await utils.authorize(req, res);
|
const user = await utils.authorize(req, res)
|
||||||
|
|
||||||
const id = req.body.id;
|
const id = req.body.id
|
||||||
if (id === undefined || id === '') {
|
if (id === undefined || id === '') {
|
||||||
return res.json({ success: false, description: 'No album specified' });
|
return res.json({ success: false, description: 'No album specified' })
|
||||||
}
|
}
|
||||||
|
|
||||||
const name = req.body.name;
|
const name = req.body.name
|
||||||
if (name === undefined || name === '') {
|
if (name === undefined || name === '') {
|
||||||
return res.json({ success: false, description: 'No name specified' });
|
return res.json({ success: false, description: 'No name specified' })
|
||||||
}
|
}
|
||||||
|
|
||||||
const album = await db.table('albums').where({ name: name, userid: user.id }).first();
|
const album = await db.table('albums').where({ name: name, userid: user.id }).first()
|
||||||
if (album) {
|
if (album) {
|
||||||
return res.json({ success: false, description: 'Name already in use' })
|
return res.json({ success: false, description: 'Name already in use' })
|
||||||
}
|
}
|
||||||
|
|
||||||
await db.table('albums').where({ id: id, userid: user.id }).update({ name: name })
|
await db.table('albums').where({ id: id, userid: user.id }).update({ name: name })
|
||||||
return res.json({ success: true });
|
return res.json({ success: true })
|
||||||
};
|
}
|
||||||
|
|
||||||
albumsController.get = async (req, res, next) => {
|
albumsController.get = async (req, res, next) => {
|
||||||
const identifier = req.params.identifier;
|
const identifier = req.params.identifier
|
||||||
if (identifier === undefined) return res.status(401).json({ success: false, description: 'No identifier provided' });
|
if (identifier === undefined) return res.status(401).json({ success: false, description: 'No identifier provided' })
|
||||||
|
|
||||||
const album = await db.table('albums').where({ identifier, enabled: 1 }).first();
|
const album = await db.table('albums').where({ identifier, enabled: 1 }).first()
|
||||||
if (!album) return res.json({ success: false, description: 'Album not found' });
|
if (!album) return res.json({ success: false, description: 'Album not found' })
|
||||||
|
|
||||||
const title = album.name;
|
const title = album.name
|
||||||
const files = await db.table('files').select('name').where('albumid', album.id).orderBy('id', 'DESC');
|
const files = await db.table('files').select('name').where('albumid', album.id).orderBy('id', 'DESC')
|
||||||
|
|
||||||
for (let file of files) {
|
for (let file of files) {
|
||||||
file.file = `${config.domain}/${file.name}`;
|
file.file = `${config.domain}/${file.name}`
|
||||||
|
|
||||||
const ext = path.extname(file.name).toLowerCase();
|
const ext = path.extname(file.name).toLowerCase()
|
||||||
if (utils.imageExtensions.includes(ext) || utils.videoExtensions.includes(ext)) {
|
if (utils.imageExtensions.includes(ext) || utils.videoExtensions.includes(ext)) {
|
||||||
file.thumb = `${config.domain}/thumbs/${file.name.slice(0, -ext.length)}.png`;
|
file.thumb = `${config.domain}/thumbs/${file.name.slice(0, -ext.length)}.png`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.json({
|
|
||||||
success: true,
|
|
||||||
title: title,
|
|
||||||
count: files.length,
|
|
||||||
files
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
return res.json({
|
||||||
|
success: true,
|
||||||
|
title: title,
|
||||||
|
count: files.length,
|
||||||
|
files
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
albumsController.generateZip = async (req, res, next) => {
|
albumsController.generateZip = async (req, res, next) => {
|
||||||
const identifier = req.params.identifier;
|
const identifier = req.params.identifier
|
||||||
if (identifier === undefined) return res.status(401).json({ success: false, description: 'No identifier provided' });
|
if (identifier === undefined) return res.status(401).json({ success: false, description: 'No identifier provided' })
|
||||||
if (!config.uploads.generateZips) return res.status(401).json({ success: false, description: 'Zip generation disabled' });
|
if (!config.uploads.generateZips) return res.status(401).json({ success: false, description: 'Zip generation disabled' })
|
||||||
|
|
||||||
const album = await db.table('albums').where({ identifier, enabled: 1 }).first();
|
const album = await db.table('albums').where({ identifier, enabled: 1 }).first()
|
||||||
if (!album) return res.json({ success: false, description: 'Album not found' });
|
if (!album) return res.json({ success: false, description: 'Album not found' })
|
||||||
|
|
||||||
if (album.zipGeneratedAt > album.editedAt) {
|
if (album.zipGeneratedAt > album.editedAt) {
|
||||||
const filePath = path.join(config.uploads.folder, 'zips', `${identifier}.zip`);
|
const filePath = path.join(config.uploads.folder, 'zips', `${identifier}.zip`)
|
||||||
const fileName = `${album.name}.zip`;
|
const fileName = `${album.name}.zip`
|
||||||
return res.download(filePath, fileName);
|
return res.download(filePath, fileName)
|
||||||
} else {
|
} else {
|
||||||
console.log(`Generating zip for album identifier: ${identifier}`);
|
console.log(`Generating zip for album identifier: ${identifier}`)
|
||||||
const files = await db.table('files').select('name').where('albumid', album.id);
|
const files = await db.table('files').select('name').where('albumid', album.id)
|
||||||
if (files.length === 0) return res.json({ success: false, description: 'There are no files in the album' });
|
if (files.length === 0) return res.json({ success: false, description: 'There are no files in the album' })
|
||||||
|
|
||||||
const zipPath = path.join(__dirname, '..', config.uploads.folder, 'zips', `${album.identifier}.zip`);
|
const zipPath = path.join(__dirname, '..', config.uploads.folder, 'zips', `${album.identifier}.zip`)
|
||||||
let archive = new Zip();
|
let archive = new Zip()
|
||||||
|
|
||||||
for (let file of files) {
|
for (let file of files) {
|
||||||
try {
|
try {
|
||||||
const exists = fs.statSync(path.join(__dirname, '..', config.uploads.folder, file.name));
|
// const exists = fs.statSync(path.join(__dirname, '..', config.uploads.folder, file.name))
|
||||||
archive.file(file.name, fs.readFileSync(path.join(__dirname, '..', config.uploads.folder, file.name)));
|
archive.file(file.name, fs.readFileSync(path.join(__dirname, '..', config.uploads.folder, file.name)))
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
archive
|
archive
|
||||||
.generateNodeStream({ type: 'nodebuffer', streamFiles: true })
|
.generateNodeStream({ type: 'nodebuffer', streamFiles: true })
|
||||||
.pipe(fs.createWriteStream(zipPath))
|
.pipe(fs.createWriteStream(zipPath))
|
||||||
.on('finish', async () => {
|
.on('finish', async () => {
|
||||||
console.log(`Generated zip for album identifier: ${identifier}`);
|
console.log(`Generated zip for album identifier: ${identifier}`)
|
||||||
await db.table('albums')
|
await db.table('albums')
|
||||||
.where('id', album.id)
|
.where('id', album.id)
|
||||||
.update({ zipGeneratedAt: Math.floor(Date.now() / 1000) });
|
.update({ zipGeneratedAt: Math.floor(Date.now() / 1000) })
|
||||||
|
|
||||||
const filePath = path.join(config.uploads.folder, 'zips', `${identifier}.zip`);
|
const filePath = path.join(config.uploads.folder, 'zips', `${identifier}.zip`)
|
||||||
const fileName = `${album.name}.zip`;
|
const fileName = `${album.name}.zip`
|
||||||
return res.download(filePath, fileName);
|
return res.download(filePath, fileName)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
module.exports = albumsController;
|
module.exports = albumsController
|
||||||
|
@ -1,86 +1,86 @@
|
|||||||
const config = require('../config.js');
|
const config = require('../config.js')
|
||||||
const db = require('knex')(config.database);
|
const db = require('knex')(config.database)
|
||||||
const bcrypt = require('bcrypt');
|
const bcrypt = require('bcrypt')
|
||||||
const randomstring = require('randomstring');
|
const randomstring = require('randomstring')
|
||||||
const utils = require('./utilsController.js');
|
const utils = require('./utilsController.js')
|
||||||
|
|
||||||
let authController = {};
|
let authController = {}
|
||||||
|
|
||||||
authController.verify = async (req, res, next) => {
|
authController.verify = async (req, res, next) => {
|
||||||
const username = req.body.username;
|
const username = req.body.username
|
||||||
const password = req.body.password;
|
const password = req.body.password
|
||||||
|
|
||||||
if (username === undefined) return res.json({ success: false, description: 'No username provided' });
|
if (username === undefined) return res.json({ success: false, description: 'No username provided' })
|
||||||
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' })
|
||||||
|
|
||||||
bcrypt.compare(password, user.password, (err, result) => {
|
bcrypt.compare(password, user.password, (err, result) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err);
|
console.log(err)
|
||||||
return res.json({ success: false, description: 'There was an error' });
|
return res.json({ success: false, description: 'There was an error' })
|
||||||
}
|
}
|
||||||
if (result === false) return res.json({ success: false, description: 'Wrong password' });
|
if (result === false) return res.json({ success: false, description: 'Wrong password' })
|
||||||
return res.json({ success: true, token: user.token });
|
return res.json({ success: true, token: user.token })
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
authController.register = async (req, res, next) => {
|
authController.register = async (req, res, next) => {
|
||||||
if (config.enableUserAccounts === false) {
|
if (config.enableUserAccounts === false) {
|
||||||
return res.json({ success: false, description: 'Register is disabled at the moment' });
|
return res.json({ success: false, description: 'Register is disabled at the moment' })
|
||||||
}
|
}
|
||||||
|
|
||||||
const username = req.body.username;
|
const username = req.body.username
|
||||||
const password = req.body.password;
|
const password = req.body.password
|
||||||
|
|
||||||
if (username === undefined) return res.json({ success: false, description: 'No username provided' });
|
if (username === undefined) return res.json({ success: false, description: 'No username provided' })
|
||||||
if (password === undefined) return res.json({ success: false, description: 'No password provided' });
|
if (password === undefined) return res.json({ success: false, description: 'No password provided' })
|
||||||
|
|
||||||
if (username.length < 4 || username.length > 32) {
|
if (username.length < 4 || username.length > 32) {
|
||||||
return res.json({ success: false, description: 'Username must have 4-32 characters' })
|
return res.json({ success: false, description: 'Username must have 4-32 characters' })
|
||||||
}
|
}
|
||||||
if (password.length < 6 || password.length > 64) {
|
if (password.length < 6 || password.length > 64) {
|
||||||
return res.json({ success: false, description: 'Password must have 6-64 characters' })
|
return res.json({ success: false, description: 'Password must have 6-64 characters' })
|
||||||
}
|
}
|
||||||
|
|
||||||
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 already exists' });
|
if (user) return res.json({ success: false, description: 'Username already exists' })
|
||||||
|
|
||||||
bcrypt.hash(password, 10, async (err, hash) => {
|
bcrypt.hash(password, 10, async (err, hash) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err);
|
console.log(err)
|
||||||
return res.json({ success: false, description: 'Error generating password hash (╯°□°)╯︵ ┻━┻' });
|
return res.json({ success: false, description: 'Error generating password hash (╯°□°)╯︵ ┻━┻' })
|
||||||
}
|
}
|
||||||
const token = randomstring.generate(64);
|
const token = randomstring.generate(64)
|
||||||
await db.table('users').insert({
|
await db.table('users').insert({
|
||||||
username: username,
|
username: username,
|
||||||
password: hash,
|
password: hash,
|
||||||
token: token
|
token: token
|
||||||
});
|
})
|
||||||
return res.json({ success: true, token: token })
|
return res.json({ success: true, token: token })
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
authController.changePassword = async (req, res, next) => {
|
authController.changePassword = async (req, res, next) => {
|
||||||
const user = await utils.authorize(req, res);
|
const user = await utils.authorize(req, res)
|
||||||
|
|
||||||
let password = req.body.password;
|
let password = req.body.password
|
||||||
if (password === undefined) return res.json({ success: false, description: 'No password provided' });
|
if (password === undefined) return res.json({ success: false, description: 'No password provided' })
|
||||||
|
|
||||||
if (password.length < 6 || password.length > 64) {
|
if (password.length < 6 || password.length > 64) {
|
||||||
return res.json({ success: false, description: 'Password must have 6-64 characters' });
|
return res.json({ success: false, description: 'Password must have 6-64 characters' })
|
||||||
}
|
}
|
||||||
|
|
||||||
bcrypt.hash(password, 10, async (err, hash) => {
|
bcrypt.hash(password, 10, async (err, hash) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err);
|
console.log(err)
|
||||||
return res.json({ success: false, description: 'Error generating password hash (╯°□°)╯︵ ┻━┻' });
|
return res.json({ success: false, description: 'Error generating password hash (╯°□°)╯︵ ┻━┻' })
|
||||||
}
|
}
|
||||||
|
|
||||||
await db.table('users').where('id', user.id).update({ password: hash });
|
await db.table('users').where('id', user.id).update({ password: hash })
|
||||||
return res.json({ success: true });
|
return res.json({ success: true })
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
module.exports = authController;
|
module.exports = authController
|
||||||
|
@ -1,34 +1,34 @@
|
|||||||
const config = require('../config.js');
|
const config = require('../config.js')
|
||||||
const db = require('knex')(config.database);
|
const db = require('knex')(config.database)
|
||||||
const randomstring = require('randomstring');
|
const randomstring = require('randomstring')
|
||||||
const utils = require('./utilsController.js');
|
const utils = require('./utilsController.js')
|
||||||
|
|
||||||
const tokenController = {};
|
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.status(401).json({ success: false, description: 'Invalid token' })
|
||||||
return res.json({ success: true, username: user.username });
|
return res.json({ success: true, username: user.username })
|
||||||
};
|
}
|
||||||
|
|
||||||
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)
|
||||||
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) => {
|
||||||
const user = await utils.authorize(req, res);
|
const user = await utils.authorize(req, res)
|
||||||
const newtoken = randomstring.generate(64);
|
const newtoken = randomstring.generate(64)
|
||||||
|
|
||||||
await db.table('users').where('token', user.token).update({
|
await db.table('users').where('token', user.token).update({
|
||||||
token: newtoken,
|
token: newtoken,
|
||||||
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
|
||||||
|
@ -1,285 +1,285 @@
|
|||||||
const config = require('../config.js');
|
const config = require('../config.js')
|
||||||
const path = require('path');
|
const path = require('path')
|
||||||
const multer = require('multer');
|
const multer = require('multer')
|
||||||
const randomstring = require('randomstring');
|
const randomstring = require('randomstring')
|
||||||
const db = require('knex')(config.database);
|
const db = require('knex')(config.database)
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto')
|
||||||
const fs = require('fs');
|
const fs = require('fs')
|
||||||
const utils = require('./utilsController.js');
|
const utils = require('./utilsController.js')
|
||||||
|
|
||||||
const uploadsController = {};
|
const uploadsController = {}
|
||||||
|
|
||||||
const storage = multer.diskStorage({
|
const storage = multer.diskStorage({
|
||||||
destination: function(req, file, cb) {
|
destination: function (req, file, cb) {
|
||||||
cb(null, path.join(__dirname, '..', config.uploads.folder));
|
cb(null, path.join(__dirname, '..', config.uploads.folder))
|
||||||
},
|
},
|
||||||
filename: function(req, file, cb) {
|
filename: function (req, file, cb) {
|
||||||
cb(null, randomstring.generate(config.uploads.fileLength) + path.extname(file.originalname));
|
cb(null, randomstring.generate(config.uploads.fileLength) + path.extname(file.originalname))
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const upload = multer({
|
const upload = multer({
|
||||||
storage: storage,
|
storage: storage,
|
||||||
limits: { fileSize: config.uploads.maxSize },
|
limits: { fileSize: config.uploads.maxSize },
|
||||||
fileFilter: function(req, file, cb) {
|
fileFilter: function (req, file, cb) {
|
||||||
if (config.blockedExtensions !== undefined) {
|
if (config.blockedExtensions !== undefined) {
|
||||||
if (config.blockedExtensions.some(extension => path.extname(file.originalname).toLowerCase() === extension)) {
|
if (config.blockedExtensions.some(extension => path.extname(file.originalname).toLowerCase() === extension)) {
|
||||||
return cb('This file extension is not allowed');
|
return cb('This file extension is not allowed') // eslint-disable-line standard/no-callback-literal
|
||||||
}
|
}
|
||||||
return cb(null, true);
|
return cb(null, true)
|
||||||
}
|
}
|
||||||
return cb(null, true);
|
return cb(null, true)
|
||||||
}
|
}
|
||||||
}).array('files[]');
|
}).array('files[]')
|
||||||
|
|
||||||
uploadsController.upload = async (req, res, next) => {
|
uploadsController.upload = async (req, res, next) => {
|
||||||
if (config.private === true) {
|
if (config.private === true) {
|
||||||
await utils.authorize(req, res);
|
await utils.authorize(req, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = req.headers.token || '';
|
const token = req.headers.token || ''
|
||||||
const user = await db.table('users').where('token', token).first();
|
const user = await db.table('users').where('token', token).first()
|
||||||
const albumid = req.headers.albumid || req.params.albumid;
|
const albumid = req.headers.albumid || req.params.albumid
|
||||||
|
|
||||||
if (albumid && user) {
|
if (albumid && user) {
|
||||||
const album = await db.table('albums').where({ id: albumid, userid: user.id }).first();
|
const album = await db.table('albums').where({ id: albumid, userid: user.id }).first()
|
||||||
if (!album) {
|
if (!album) {
|
||||||
return res.json({
|
return res.json({
|
||||||
success: false,
|
success: false,
|
||||||
description: 'Album doesn\'t exist or it doesn\'t belong to the user'
|
description: 'Album doesn\'t exist or it doesn\'t belong to the user'
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
return uploadsController.actuallyUpload(req, res, user, albumid);
|
return uploadsController.actuallyUpload(req, res, user, albumid)
|
||||||
}
|
}
|
||||||
return uploadsController.actuallyUpload(req, res, user, albumid);
|
return uploadsController.actuallyUpload(req, res, user, albumid)
|
||||||
};
|
}
|
||||||
|
|
||||||
uploadsController.actuallyUpload = async (req, res, userid, album) => {
|
uploadsController.actuallyUpload = async (req, res, userid, album) => {
|
||||||
upload(req, res, async err => {
|
upload(req, res, async err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err);
|
console.error(err)
|
||||||
return res.json({ success: false, description: err });
|
return res.json({ success: false, description: err })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.files.length === 0) return res.json({ success: false, description: 'no-files' });
|
if (req.files.length === 0) return res.json({ success: false, description: 'no-files' })
|
||||||
|
|
||||||
const files = [];
|
const files = []
|
||||||
const existingFiles = [];
|
const existingFiles = []
|
||||||
let iteration = 1;
|
let iteration = 1
|
||||||
|
|
||||||
req.files.forEach(async file => {
|
req.files.forEach(async file => {
|
||||||
// Check if the file exists by checking hash and size
|
// Check if the file exists by checking hash and size
|
||||||
let hash = crypto.createHash('md5');
|
let hash = crypto.createHash('md5')
|
||||||
let stream = fs.createReadStream(path.join(__dirname, '..', config.uploads.folder, file.filename));
|
let stream = fs.createReadStream(path.join(__dirname, '..', config.uploads.folder, file.filename))
|
||||||
|
|
||||||
stream.on('data', data => {
|
stream.on('data', data => {
|
||||||
hash.update(data, 'utf8');
|
hash.update(data, 'utf8')
|
||||||
});
|
})
|
||||||
|
|
||||||
stream.on('end', async () => {
|
stream.on('end', async () => {
|
||||||
const fileHash = hash.digest('hex');
|
const fileHash = hash.digest('hex')
|
||||||
const dbFile = await db.table('files')
|
const dbFile = await db.table('files')
|
||||||
.where(function() {
|
.where(function () {
|
||||||
if (userid === undefined) this.whereNull('userid');
|
if (userid === undefined) this.whereNull('userid')
|
||||||
else this.where('userid', userid.id);
|
else this.where('userid', userid.id)
|
||||||
})
|
})
|
||||||
.where({
|
.where({
|
||||||
hash: fileHash,
|
hash: fileHash,
|
||||||
size: file.size
|
size: file.size
|
||||||
})
|
})
|
||||||
.first();
|
.first()
|
||||||
|
|
||||||
if (!dbFile) {
|
if (!dbFile) {
|
||||||
files.push({
|
files.push({
|
||||||
name: file.filename,
|
name: file.filename,
|
||||||
original: file.originalname,
|
original: file.originalname,
|
||||||
type: file.mimetype,
|
type: file.mimetype,
|
||||||
size: file.size,
|
size: file.size,
|
||||||
hash: fileHash,
|
hash: fileHash,
|
||||||
ip: req.ip,
|
ip: req.ip,
|
||||||
albumid: album,
|
albumid: album,
|
||||||
userid: userid.id,
|
userid: userid ? userid.id : null,
|
||||||
timestamp: Math.floor(Date.now() / 1000)
|
timestamp: Math.floor(Date.now() / 1000)
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
uploadsController.deleteFile(file.filename).then(() => {}).catch(err => console.error(err));
|
uploadsController.deleteFile(file.filename).then(() => {}).catch(err => console.error(err))
|
||||||
existingFiles.push(dbFile);
|
existingFiles.push(dbFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iteration === req.files.length) {
|
if (iteration === req.files.length) {
|
||||||
return uploadsController.processFilesForDisplay(req, res, files, existingFiles);
|
return uploadsController.processFilesForDisplay(req, res, files, existingFiles)
|
||||||
}
|
}
|
||||||
iteration++;
|
iteration++
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
uploadsController.processFilesForDisplay = async (req, res, files, existingFiles) => {
|
uploadsController.processFilesForDisplay = async (req, res, files, existingFiles) => {
|
||||||
let basedomain = config.domain;
|
let basedomain = config.domain
|
||||||
if (files.length === 0) {
|
if (files.length === 0) {
|
||||||
return res.json({
|
return res.json({
|
||||||
success: true,
|
success: true,
|
||||||
files: existingFiles.map(file => {
|
files: existingFiles.map(file => {
|
||||||
return {
|
return {
|
||||||
name: file.name,
|
name: file.name,
|
||||||
size: file.size,
|
size: file.size,
|
||||||
url: `${basedomain}/${file.name}`
|
url: `${basedomain}/${file.name}`
|
||||||
};
|
}
|
||||||
})
|
})
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
await db.table('files').insert(files);
|
await db.table('files').insert(files)
|
||||||
for (let efile of existingFiles) files.push(efile);
|
for (let efile of existingFiles) files.push(efile)
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
files: files.map(file => {
|
files: files.map(file => {
|
||||||
return {
|
return {
|
||||||
name: file.name,
|
name: file.name,
|
||||||
size: file.size,
|
size: file.size,
|
||||||
url: `${basedomain}/${file.name}`
|
url: `${basedomain}/${file.name}`
|
||||||
};
|
}
|
||||||
})
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
for (let file of files) {
|
for (let file of files) {
|
||||||
let ext = path.extname(file.name).toLowerCase();
|
let ext = path.extname(file.name).toLowerCase()
|
||||||
if (utils.imageExtensions.includes(ext) || utils.videoExtensions.includes(ext)) {
|
if (utils.imageExtensions.includes(ext) || utils.videoExtensions.includes(ext)) {
|
||||||
file.thumb = `${basedomain}/thumbs/${file.name.slice(0, -ext.length)}.png`;
|
file.thumb = `${basedomain}/thumbs/${file.name.slice(0, -ext.length)}.png`
|
||||||
utils.generateThumbs(file);
|
utils.generateThumbs(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file.albumid) {
|
if (file.albumid) {
|
||||||
db.table('albums').where('id', file.albumid).update('editedAt', file.timestamp).then(() => {})
|
db.table('albums').where('id', file.albumid).update('editedAt', file.timestamp).then(() => {})
|
||||||
.catch(error => { console.log(error); res.json({ success: false, description: 'Error updating album' }); });
|
.catch(error => { console.log(error); res.json({ success: false, description: 'Error updating album' }) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
uploadsController.delete = async (req, res) => {
|
uploadsController.delete = async (req, res) => {
|
||||||
const user = await utils.authorize(req, res);
|
const user = await utils.authorize(req, res)
|
||||||
const id = req.body.id;
|
const id = req.body.id
|
||||||
if (id === undefined || id === '') {
|
if (id === undefined || id === '') {
|
||||||
return res.json({ success: false, description: 'No file specified' });
|
return res.json({ success: false, description: 'No file specified' })
|
||||||
}
|
}
|
||||||
|
|
||||||
const file = await db.table('files')
|
const file = await db.table('files')
|
||||||
.where('id', id)
|
.where('id', id)
|
||||||
.where(function() {
|
.where(function () {
|
||||||
if (user.username !== 'root') {
|
if (user.username !== 'root') {
|
||||||
this.where('userid', user.id);
|
this.where('userid', user.id)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.first();
|
.first()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await uploadsController.deleteFile(file.name);
|
await uploadsController.deleteFile(file.name)
|
||||||
await db.table('files').where('id', id).del();
|
await db.table('files').where('id', id).del()
|
||||||
if (file.albumid) {
|
if (file.albumid) {
|
||||||
await db.table('albums').where('id', file.albumid).update('editedAt', Math.floor(Date.now() / 1000));
|
await db.table('albums').where('id', file.albumid).update('editedAt', Math.floor(Date.now() / 1000))
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.json({ success: true });
|
return res.json({ success: true })
|
||||||
};
|
}
|
||||||
|
|
||||||
uploadsController.deleteFile = function(file) {
|
uploadsController.deleteFile = function (file) {
|
||||||
const ext = path.extname(file).toLowerCase();
|
const ext = path.extname(file).toLowerCase()
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fs.stat(path.join(__dirname, '..', config.uploads.folder, file), (err, stats) => {
|
fs.stat(path.join(__dirname, '..', config.uploads.folder, file), (err, stats) => {
|
||||||
if (err) { return reject(err); }
|
if (err) { return reject(err) }
|
||||||
fs.unlink(path.join(__dirname, '..', config.uploads.folder, file), err => {
|
fs.unlink(path.join(__dirname, '..', config.uploads.folder, file), err => {
|
||||||
if (err) { return reject(err); }
|
if (err) { return reject(err) }
|
||||||
if (!utils.imageExtensions.includes(ext) && !utils.videoExtensions.includes(ext)) {
|
if (!utils.imageExtensions.includes(ext) && !utils.videoExtensions.includes(ext)) {
|
||||||
return resolve();
|
return resolve()
|
||||||
}
|
}
|
||||||
file = file.substr(0, file.lastIndexOf('.')) + '.png';
|
file = file.substr(0, file.lastIndexOf('.')) + '.png'
|
||||||
fs.stat(path.join(__dirname, '..', config.uploads.folder, 'thumbs/', file), (err, stats) => {
|
fs.stat(path.join(__dirname, '..', config.uploads.folder, 'thumbs/', file), (err, stats) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err);
|
console.log(err)
|
||||||
return resolve();
|
return resolve()
|
||||||
}
|
}
|
||||||
fs.unlink(path.join(__dirname, '..', config.uploads.folder, 'thumbs/', file), err => {
|
fs.unlink(path.join(__dirname, '..', config.uploads.folder, 'thumbs/', file), err => {
|
||||||
if (err) { return reject(err); }
|
if (err) { return reject(err) }
|
||||||
return resolve();
|
return resolve()
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
uploadsController.list = async (req, res) => {
|
uploadsController.list = async (req, res) => {
|
||||||
const user = await utils.authorize(req, res);
|
const user = await utils.authorize(req, res)
|
||||||
|
|
||||||
let offset = req.params.page;
|
let offset = req.params.page
|
||||||
if (offset === undefined) offset = 0;
|
if (offset === undefined) offset = 0
|
||||||
|
|
||||||
const files = await db.table('files')
|
const files = await db.table('files')
|
||||||
.where(function() {
|
.where(function () {
|
||||||
if (req.params.id === undefined) this.where('id', '<>', '');
|
if (req.params.id === undefined) this.where('id', '<>', '')
|
||||||
else this.where('albumid', req.params.id);
|
else this.where('albumid', req.params.id)
|
||||||
})
|
})
|
||||||
.where(function() {
|
.where(function () {
|
||||||
if (user.username !== 'root') this.where('userid', user.id);
|
if (user.username !== 'root') this.where('userid', user.id)
|
||||||
})
|
})
|
||||||
.orderBy('id', 'DESC')
|
.orderBy('id', 'DESC')
|
||||||
.limit(25)
|
.limit(25)
|
||||||
.offset(25 * offset)
|
.offset(25 * offset)
|
||||||
.select('id', 'albumid', 'timestamp', 'name', 'userid');
|
.select('id', 'albumid', 'timestamp', 'name', 'userid')
|
||||||
|
|
||||||
const albums = await db.table('albums');
|
const albums = await db.table('albums')
|
||||||
let basedomain = config.domain;
|
let basedomain = config.domain
|
||||||
let userids = [];
|
let userids = []
|
||||||
|
|
||||||
for (let file of files) {
|
for (let file of files) {
|
||||||
file.file = `${basedomain}/${file.name}`;
|
file.file = `${basedomain}/${file.name}`
|
||||||
file.date = new Date(file.timestamp * 1000);
|
file.date = new Date(file.timestamp * 1000)
|
||||||
file.date = utils.getPrettyDate(file.date);
|
file.date = utils.getPrettyDate(file.date)
|
||||||
|
|
||||||
file.album = '';
|
file.album = ''
|
||||||
|
|
||||||
if (file.albumid !== undefined) {
|
if (file.albumid !== undefined) {
|
||||||
for (let album of albums) {
|
for (let album of albums) {
|
||||||
if (file.albumid === album.id) {
|
if (file.albumid === album.id) {
|
||||||
file.album = album.name;
|
file.album = album.name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only push usernames if we are root
|
// Only push usernames if we are root
|
||||||
if (user.username === 'root') {
|
if (user.username === 'root') {
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let ext = path.extname(file.name).toLowerCase();
|
let ext = path.extname(file.name).toLowerCase()
|
||||||
if (utils.imageExtensions.includes(ext) || utils.videoExtensions.includes(ext)) {
|
if (utils.imageExtensions.includes(ext) || utils.videoExtensions.includes(ext)) {
|
||||||
file.thumb = `${basedomain}/thumbs/${file.name.slice(0, -ext.length)}.png`;
|
file.thumb = `${basedomain}/thumbs/${file.name.slice(0, -ext.length)}.png`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 (user.username !== 'root') return res.json({ success: true, files })
|
||||||
|
|
||||||
// If we are root but there are no uploads attached to a user, send response
|
// If we are root 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').whereIn('id', userids);
|
const users = await db.table('users').whereIn('id', userids)
|
||||||
for (let dbUser of users) {
|
for (let dbUser of users) {
|
||||||
for (let file of files) {
|
for (let file of files) {
|
||||||
if (file.userid === dbUser.id) {
|
if (file.userid === dbUser.id) {
|
||||||
file.username = dbUser.username;
|
file.username = dbUser.username
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.json({ success: true, files });
|
return res.json({ success: true, files })
|
||||||
};
|
}
|
||||||
|
|
||||||
module.exports = uploadsController;
|
module.exports = uploadsController
|
||||||
|
@ -1,67 +1,67 @@
|
|||||||
const path = require('path');
|
const path = require('path')
|
||||||
const config = require('../config.js');
|
const config = require('../config.js')
|
||||||
const fs = require('fs');
|
const fs = require('fs')
|
||||||
const gm = require('gm');
|
const gm = require('gm')
|
||||||
const ffmpeg = require('fluent-ffmpeg');
|
const ffmpeg = require('fluent-ffmpeg')
|
||||||
const db = require('knex')(config.database);
|
const db = require('knex')(config.database)
|
||||||
|
|
||||||
const utilsController = {};
|
const utilsController = {}
|
||||||
utilsController.imageExtensions = ['.jpg', '.jpeg', '.bmp', '.gif', '.png'];
|
utilsController.imageExtensions = ['.jpg', '.jpeg', '.bmp', '.gif', '.png']
|
||||||
utilsController.videoExtensions = ['.webm', '.mp4', '.wmv', '.avi', '.mov'];
|
utilsController.videoExtensions = ['.webm', '.mp4', '.wmv', '.avi', '.mov']
|
||||||
|
|
||||||
utilsController.getPrettyDate = function(date) {
|
utilsController.getPrettyDate = function (date) {
|
||||||
return date.getFullYear() + '-'
|
return date.getFullYear() + '-' +
|
||||||
+ (date.getMonth() + 1) + '-'
|
(date.getMonth() + 1) + '-' +
|
||||||
+ date.getDate() + ' '
|
date.getDate() + ' ' +
|
||||||
+ (date.getHours() < 10 ? '0' : '')
|
(date.getHours() < 10 ? '0' : '') +
|
||||||
+ date.getHours() + ':'
|
date.getHours() + ':' +
|
||||||
+ (date.getMinutes() < 10 ? '0' : '')
|
(date.getMinutes() < 10 ? '0' : '') +
|
||||||
+ date.getMinutes() + ':'
|
date.getMinutes() + ':' +
|
||||||
+ (date.getSeconds() < 10 ? '0' : '')
|
(date.getSeconds() < 10 ? '0' : '') +
|
||||||
+ date.getSeconds();
|
date.getSeconds()
|
||||||
}
|
}
|
||||||
|
|
||||||
utilsController.authorize = async (req, res) => {
|
utilsController.authorize = async (req, res) => {
|
||||||
const token = req.headers.token;
|
const token = req.headers.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.status(401).json({ success: false, description: 'Invalid token' })
|
||||||
return user;
|
return user
|
||||||
};
|
}
|
||||||
|
|
||||||
utilsController.generateThumbs = function(file, basedomain) {
|
utilsController.generateThumbs = function (file, basedomain) {
|
||||||
if (config.uploads.generateThumbnails !== true) return;
|
if (config.uploads.generateThumbnails !== true) return
|
||||||
const ext = path.extname(file.name).toLowerCase();
|
const ext = path.extname(file.name).toLowerCase()
|
||||||
|
|
||||||
let thumbname = path.join(__dirname, '..', config.uploads.folder, 'thumbs', file.name.slice(0, -ext.length) + '.png');
|
let thumbname = path.join(__dirname, '..', config.uploads.folder, 'thumbs', file.name.slice(0, -ext.length) + '.png')
|
||||||
fs.access(thumbname, err => {
|
fs.access(thumbname, err => {
|
||||||
if (err && err.code === 'ENOENT') {
|
if (err && err.code === 'ENOENT') {
|
||||||
if (utilsController.videoExtensions.includes(ext)) {
|
if (utilsController.videoExtensions.includes(ext)) {
|
||||||
ffmpeg(path.join(__dirname, '..', config.uploads.folder, file.name))
|
ffmpeg(path.join(__dirname, '..', config.uploads.folder, file.name))
|
||||||
.thumbnail({
|
.thumbnail({
|
||||||
timestamps: [0],
|
timestamps: [0],
|
||||||
filename: '%b.png',
|
filename: '%b.png',
|
||||||
folder: path.join(__dirname, '..', config.uploads.folder, 'thumbs'),
|
folder: path.join(__dirname, '..', config.uploads.folder, 'thumbs'),
|
||||||
size: '200x?'
|
size: '200x?'
|
||||||
})
|
})
|
||||||
.on('error', error => console.log('Error - ', error.message));
|
.on('error', error => console.log('Error - ', error.message))
|
||||||
} else {
|
} else {
|
||||||
let size = {
|
let size = {
|
||||||
width: 200,
|
width: 200,
|
||||||
height: 200
|
height: 200
|
||||||
};
|
}
|
||||||
gm(path.join(__dirname, '..', config.uploads.folder, file.name))
|
gm(path.join(__dirname, '..', config.uploads.folder, file.name))
|
||||||
.resize(size.width, size.height + '>')
|
.resize(size.width, size.height + '>')
|
||||||
.gravity('Center')
|
.gravity('Center')
|
||||||
.extent(size.width, size.height)
|
.extent(size.width, size.height)
|
||||||
.background('transparent')
|
.background('transparent')
|
||||||
.write(thumbname, error => {
|
.write(thumbname, error => {
|
||||||
if (error) console.log('Error - ', error);
|
if (error) console.log('Error - ', error)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
module.exports = utilsController;
|
module.exports = utilsController
|
||||||
|
@ -1,50 +1,49 @@
|
|||||||
let init = function(db){
|
let init = function (db) {
|
||||||
|
// Create the tables we need to store galleries and files
|
||||||
|
db.schema.createTableIfNotExists('albums', function (table) {
|
||||||
|
table.increments()
|
||||||
|
table.integer('userid')
|
||||||
|
table.string('name')
|
||||||
|
table.string('identifier')
|
||||||
|
table.integer('enabled')
|
||||||
|
table.integer('timestamp')
|
||||||
|
}).then(() => {})
|
||||||
|
|
||||||
// Create the tables we need to store galleries and files
|
db.schema.createTableIfNotExists('files', function (table) {
|
||||||
db.schema.createTableIfNotExists('albums', function (table) {
|
table.increments()
|
||||||
table.increments()
|
table.integer('userid')
|
||||||
table.integer('userid')
|
table.string('name')
|
||||||
table.string('name')
|
table.string('original')
|
||||||
table.string('identifier')
|
table.string('type')
|
||||||
table.integer('enabled')
|
table.string('size')
|
||||||
table.integer('timestamp')
|
table.string('hash')
|
||||||
}).then(() => {})
|
table.string('ip')
|
||||||
|
table.integer('albumid')
|
||||||
|
table.integer('timestamp')
|
||||||
|
}).then(() => {})
|
||||||
|
|
||||||
db.schema.createTableIfNotExists('files', function (table) {
|
db.schema.createTableIfNotExists('users', function (table) {
|
||||||
table.increments()
|
table.increments()
|
||||||
table.integer('userid')
|
table.string('username')
|
||||||
table.string('name')
|
table.string('password')
|
||||||
table.string('original')
|
table.string('token')
|
||||||
table.string('type')
|
table.integer('timestamp')
|
||||||
table.string('size')
|
}).then(() => {
|
||||||
table.string('hash')
|
db.table('users').where({username: 'root'}).then((user) => {
|
||||||
table.string('ip')
|
if (user.length > 0) return
|
||||||
table.integer('albumid')
|
|
||||||
table.integer('timestamp')
|
|
||||||
}).then(() => {})
|
|
||||||
|
|
||||||
db.schema.createTableIfNotExists('users', function (table) {
|
require('bcrypt').hash('root', 10, function (err, hash) {
|
||||||
table.increments()
|
if (err) console.error('Error generating password hash for root')
|
||||||
table.string('username')
|
|
||||||
table.string('password')
|
|
||||||
table.string('token')
|
|
||||||
table.integer('timestamp')
|
|
||||||
}).then(() => {
|
|
||||||
db.table('users').where({username: 'root'}).then((user) => {
|
|
||||||
if(user.length > 0) return
|
|
||||||
|
|
||||||
require('bcrypt').hash('root', 10, function(err, hash) {
|
db.table('users').insert({
|
||||||
if(err) console.error('Error generating password hash for root')
|
username: 'root',
|
||||||
|
password: hash,
|
||||||
db.table('users').insert({
|
token: require('randomstring').generate(64),
|
||||||
username: 'root',
|
timestamp: Math.floor(Date.now() / 1000)
|
||||||
password: hash,
|
}).then(() => {})
|
||||||
token: require('randomstring').generate(64),
|
})
|
||||||
timestamp: Math.floor(Date.now() / 1000)
|
})
|
||||||
}).then(() => {})
|
})
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = init
|
module.exports = init
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
const config = require('../config.js');
|
const config = require('../config.js')
|
||||||
const db = require('knex')(config.database);
|
const db = require('knex')(config.database)
|
||||||
|
|
||||||
const migration = {};
|
const migration = {}
|
||||||
migration.start = async () => {
|
migration.start = async () => {
|
||||||
await db.schema.table('albums', table => {
|
await db.schema.table('albums', table => {
|
||||||
table.dateTime('editedAt');
|
table.dateTime('editedAt')
|
||||||
table.dateTime('zipGeneratedAt');
|
table.dateTime('zipGeneratedAt')
|
||||||
});
|
})
|
||||||
console.log('Migration finished! Now start lolisafe normally');
|
console.log('Migration finished! Now start lolisafe normally')
|
||||||
};
|
}
|
||||||
|
|
||||||
migration.start();
|
migration.start()
|
||||||
|
94
lolisafe.js
94
lolisafe.js
@ -1,58 +1,66 @@
|
|||||||
const config = require('./config.js');
|
const config = require('./config.js')
|
||||||
const api = require('./routes/api.js');
|
const api = require('./routes/api.js')
|
||||||
const album = require('./routes/album.js');
|
const album = require('./routes/album.js')
|
||||||
const express = require('express');
|
const express = require('express')
|
||||||
const helmet = require('helmet');
|
const helmet = require('helmet')
|
||||||
const bodyParser = require('body-parser');
|
const bodyParser = require('body-parser')
|
||||||
const RateLimit = require('express-rate-limit');
|
const RateLimit = require('express-rate-limit')
|
||||||
const db = require('knex')(config.database);
|
const db = require('knex')(config.database)
|
||||||
const fs = require('fs');
|
const fs = require('fs')
|
||||||
const exphbs = require('express-handlebars');
|
const exphbs = require('express-handlebars')
|
||||||
const safe = express();
|
const safe = express()
|
||||||
|
|
||||||
require('./database/db.js')(db);
|
require('./database/db.js')(db)
|
||||||
|
|
||||||
fs.existsSync('./pages/custom') || fs.mkdirSync('./pages/custom');
|
fs.existsSync('./pages/custom') || fs.mkdirSync('./pages/custom')
|
||||||
fs.existsSync(`./${config.logsFolder}`) || fs.mkdirSync(`./${ config.logsFolder}`);
|
fs.existsSync('./' + config.logsFolder) || fs.mkdirSync('./' + config.logsFolder)
|
||||||
fs.existsSync(`./${config.uploads.folder}`) || fs.mkdirSync(`./${config.uploads.folder}`);
|
fs.existsSync('./' + config.uploads.folder) || fs.mkdirSync('./' + config.uploads.folder)
|
||||||
fs.existsSync(`./${config.uploads.folder}/thumbs`) || fs.mkdirSync(`./${config.uploads.folder }/thumbs`);
|
fs.existsSync('./' + config.uploads.folder + '/thumbs') || fs.mkdirSync('./' + config.uploads.folder + '/thumbs')
|
||||||
fs.existsSync(`./${config.uploads.folder }/zips`) || fs.mkdirSync(`./${config.uploads.folder}/zips`);
|
fs.existsSync('./' + config.uploads.folder + '/zips') || fs.mkdirSync('./' + config.uploads.folder + '/zips')
|
||||||
|
|
||||||
safe.use(helmet());
|
safe.use(helmet())
|
||||||
safe.set('trust proxy', 1);
|
safe.set('trust proxy', 1)
|
||||||
|
|
||||||
safe.engine('handlebars', exphbs({ defaultLayout: 'main' }));
|
safe.engine('handlebars', exphbs({ defaultLayout: 'main' }))
|
||||||
safe.set('view engine', 'handlebars');
|
safe.set('view engine', 'handlebars')
|
||||||
safe.enable('view cache');
|
safe.enable('view cache')
|
||||||
|
|
||||||
let limiter = new RateLimit({ windowMs: 5000, max: 2 });
|
let limiter = new RateLimit({ windowMs: 5000, max: 2 })
|
||||||
safe.use('/api/login/', limiter);
|
safe.use('/api/login/', limiter)
|
||||||
safe.use('/api/register/', limiter);
|
safe.use('/api/register/', limiter)
|
||||||
|
|
||||||
safe.use(bodyParser.urlencoded({ extended: true }));
|
safe.use(bodyParser.urlencoded({ extended: true }))
|
||||||
safe.use(bodyParser.json());
|
safe.use(bodyParser.json())
|
||||||
|
|
||||||
if (config.serveFilesWithNode) {
|
if (config.serveFilesWithNode) {
|
||||||
safe.use('/', express.static(config.uploads.folder));
|
safe.use('/', express.static(config.uploads.folder))
|
||||||
}
|
}
|
||||||
|
|
||||||
safe.use('/', express.static('./public'));
|
safe.use('/', express.static('./public'))
|
||||||
safe.use('/', album);
|
safe.use('/', album)
|
||||||
safe.use('/api', api);
|
safe.use('/api', api)
|
||||||
|
|
||||||
for (let page of config.pages) {
|
for (let page of config.pages) {
|
||||||
let root = './pages/';
|
let root = './pages/'
|
||||||
if (fs.existsSync(`./pages/custom/${page}.html`)) {
|
if (fs.existsSync(`./pages/custom/${page}.html`)) {
|
||||||
root = './pages/custom/';
|
root = './pages/custom/'
|
||||||
}
|
}
|
||||||
if (page === 'home') {
|
if (page === 'home') {
|
||||||
safe.get('/', (req, res, next) => res.sendFile(`${page}.html`, { root: root }));
|
safe.get('/', (req, res, next) => res.sendFile(`${page}.html`, { root: root }))
|
||||||
} else {
|
} else {
|
||||||
safe.get(`/${page}`, (req, res, next) => res.sendFile(`${page}.html`, { root: root }));
|
safe.get(`/${page}`, (req, res, next) => res.sendFile(`${page}.html`, { root: root }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
safe.use((req, res, next) => res.status(404).sendFile('404.html', { root: './pages/error/' }));
|
safe.use((req, res, next) => res.status(404).sendFile('404.html', { root: './pages/error/' }))
|
||||||
safe.use((req, res, next) => res.status(500).sendFile('500.html', { root: './pages/error/' }));
|
safe.use((req, res, next) => res.status(500).sendFile('500.html', { root: './pages/error/' }))
|
||||||
|
|
||||||
safe.listen(config.port, () => console.log(`lolisafe started on port ${config.port}`));
|
safe.listen(config.port, () => console.log(`lolisafe started on port ${config.port}`))
|
||||||
|
|
||||||
|
process.on('uncaughtException', err => {
|
||||||
|
console.error(`Uncaught Exception:\n${err.stack}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
process.on('unhandledRejection', err => {
|
||||||
|
console.error(`Unhandled Rejection (Promise):\n${err.stack}`)
|
||||||
|
})
|
||||||
|
16
package.json
16
package.json
@ -30,19 +30,9 @@
|
|||||||
"sqlite3": "^3.1.13"
|
"sqlite3": "^3.1.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^4.4.1",
|
"standard": "^10.0.3"
|
||||||
"eslint-config-aqua": "^1.5.0"
|
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"standard": {
|
||||||
"extends": [
|
"envs": ["browser", "node"]
|
||||||
"aqua"
|
|
||||||
],
|
|
||||||
"env": {
|
|
||||||
"browser": true,
|
|
||||||
"node": true
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"func-names": 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.15.3/axios.min.js"></script>
|
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.15.3/axios.min.js"></script>
|
||||||
<script type="text/javascript" src="https://use.fontawesome.com/cd26baa9bd.js"></script>
|
<script type="text/javascript" src="https://use.fontawesome.com/cd26baa9bd.js"></script>
|
||||||
<script type="text/javascript" src="/js/auth.js"></script>
|
<script type="text/javascript" src="/js/auth.js"></script>
|
||||||
|
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
/** Based on KDE Breeze Dark **/
|
/** Based on KDE Breeze Dark **/
|
||||||
|
|
||||||
@ -46,6 +47,7 @@
|
|||||||
background-color: #232629;
|
background-color: #232629;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.15.3/axios.min.js"></script>
|
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.15.3/axios.min.js"></script>
|
||||||
<script type="text/javascript" src="https://use.fontawesome.com/cd26baa9bd.js"></script>
|
<script type="text/javascript" src="https://use.fontawesome.com/cd26baa9bd.js"></script>
|
||||||
<script type="text/javascript" src="/js/dashboard.js"></script>
|
<script type="text/javascript" src="/js/dashboard.js"></script>
|
||||||
|
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
/** Based on KDE Breeze Dark **/
|
/** Based on KDE Breeze Dark **/
|
||||||
|
|
||||||
@ -122,6 +123,7 @@
|
|||||||
border-left-color: #31363b;
|
border-left-color: #31363b;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<browserconfig>
|
<browserconfig>
|
||||||
<msapplication>
|
<msapplication>
|
||||||
<tile>
|
<tile>
|
||||||
<square150x150logo src="/images/icons/mstile-150x150.png?v=ZqYs7M3fG4"/>
|
<square150x150logo src="/images/icons/mstile-150x150.png?v=ZqYs7M3fG4"/>
|
||||||
<TileColor>#232629</TileColor>
|
<TileColor>#232629</TileColor>
|
||||||
</tile>
|
</tile>
|
||||||
</msapplication>
|
</msapplication>
|
||||||
</browserconfig>
|
</browserconfig>
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "safe.fiery.me",
|
"name": "safe.fiery.me",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "/images/icons/android-chrome-192x192.png?v=ZqYs7M3fG4",
|
"src": "/images/icons/android-chrome-192x192.png?v=ZqYs7M3fG4",
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image/png"
|
"type": "image/png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/images/icons/android-chrome-384x384.png?v=ZqYs7M3fG4",
|
"src": "/images/icons/android-chrome-384x384.png?v=ZqYs7M3fG4",
|
||||||
"sizes": "384x384",
|
"sizes": "384x384",
|
||||||
"type": "image/png"
|
"type": "image/png"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"theme_color": "#232629",
|
"theme_color": "#232629",
|
||||||
"background_color": "#232629",
|
"background_color": "#232629",
|
||||||
"display": "standalone"
|
"display": "standalone"
|
||||||
}
|
}
|
||||||
|
@ -1,44 +1,56 @@
|
|||||||
var page = {};
|
/* global swal, axios */
|
||||||
|
|
||||||
page.do = function(dest) {
|
var page = {}
|
||||||
var user = document.getElementById('user').value;
|
|
||||||
var pass = document.getElementById('pass').value;
|
|
||||||
|
|
||||||
if (user === undefined || user === null || user === '') { return swal('Error', 'You need to specify a username', 'error'); }
|
page.do = function (dest) {
|
||||||
if (pass === undefined || pass === null || pass === '') { return swal('Error', 'You need to specify a username', 'error'); }
|
var user = document.getElementById('user').value
|
||||||
|
var pass = document.getElementById('pass').value
|
||||||
|
|
||||||
axios.post(`/api/${dest}`, {
|
if (user === undefined || user === null || user === '') {
|
||||||
username: user,
|
return swal('Error', 'You need to specify a username', 'error')
|
||||||
password: pass
|
}
|
||||||
})
|
if (pass === undefined || pass === null || pass === '') {
|
||||||
.then(response => {
|
return swal('Error', 'You need to specify a username', 'error')
|
||||||
if (response.data.success === false) { return swal('Error', response.data.description, 'error'); }
|
}
|
||||||
|
|
||||||
localStorage.token = response.data.token;
|
axios.post('/api/' + dest, {
|
||||||
window.location = '/dashboard';
|
username: user,
|
||||||
})
|
password: pass
|
||||||
.catch(error => {
|
})
|
||||||
return swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error');
|
.then(function (response) {
|
||||||
console.log(error);
|
if (response.data.success === false) {
|
||||||
});
|
return swal('Error', response.data.description, 'error')
|
||||||
};
|
}
|
||||||
|
|
||||||
page.verify = function() {
|
localStorage.token = response.data.token
|
||||||
page.token = localStorage.token;
|
window.location = '/dashboard'
|
||||||
if (page.token === undefined) return;
|
})
|
||||||
|
.catch(function (error) {
|
||||||
|
console.log(error)
|
||||||
|
return swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
axios.post('/api/tokens/verify', { token: page.token })
|
page.verify = function () {
|
||||||
.then(response => {
|
page.token = localStorage.token
|
||||||
if (response.data.success === false) { return swal('Error', response.data.description, 'error'); }
|
if (page.token === undefined) return
|
||||||
|
|
||||||
window.location = '/dashboard';
|
axios.post('/api/tokens/verify', {
|
||||||
})
|
token: page.token
|
||||||
.catch(error => {
|
})
|
||||||
return swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error');
|
.then(function (response) {
|
||||||
console.log(error);
|
if (response.data.success === false) {
|
||||||
});
|
return swal('Error', response.data.description, 'error')
|
||||||
};
|
}
|
||||||
|
|
||||||
window.onload = function() {
|
window.location = '/dashboard'
|
||||||
page.verify();
|
})
|
||||||
};
|
.catch(function (error) {
|
||||||
|
console.log(error)
|
||||||
|
return swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onload = function () {
|
||||||
|
page.verify()
|
||||||
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,172 +1,178 @@
|
|||||||
var upload = {};
|
/* eslint-disable no-unused-expressions */
|
||||||
|
/* global swal, axios, Dropzone */
|
||||||
|
|
||||||
upload.isPrivate = true;
|
var upload = {}
|
||||||
upload.token = localStorage.token;
|
|
||||||
upload.maxFileSize;
|
upload.isPrivate = true
|
||||||
|
upload.token = localStorage.token
|
||||||
|
upload.maxFileSize
|
||||||
// Add the album var to the upload so we can store the album id in there
|
// Add the album var to the upload so we can store the album id in there
|
||||||
upload.album;
|
upload.album
|
||||||
upload.myDropzone;
|
upload.myDropzone
|
||||||
|
|
||||||
upload.checkIfPublic = function() {
|
upload.checkIfPublic = function () {
|
||||||
axios.get('/api/check')
|
axios.get('/api/check')
|
||||||
.then(response => {
|
.then(response => {
|
||||||
upload.isPrivate = response.data.private;
|
upload.isPrivate = response.data.private
|
||||||
upload.maxFileSize = response.data.maxFileSize;
|
upload.maxFileSize = response.data.maxFileSize
|
||||||
upload.preparePage();
|
upload.preparePage()
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error');
|
swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error')
|
||||||
return console.log(error);
|
return console.log(error)
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
upload.preparePage = function() {
|
upload.preparePage = function () {
|
||||||
if (!upload.isPrivate) return upload.prepareUpload();
|
if (!upload.isPrivate) return upload.prepareUpload()
|
||||||
if (!upload.token) return document.getElementById('loginToUpload').style.display = 'inline-flex';
|
if (!upload.token) {
|
||||||
upload.verifyToken(upload.token, true);
|
document.getElementById('loginToUpload').style.display = 'inline-flex'
|
||||||
};
|
return 'inline-flex'
|
||||||
|
}
|
||||||
|
upload.verifyToken(upload.token, true)
|
||||||
|
}
|
||||||
|
|
||||||
upload.verifyToken = function(token, reloadOnError) {
|
upload.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: token })
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if (response.data.success === false) {
|
if (response.data.success === false) {
|
||||||
swal({
|
swal({
|
||||||
title: 'An error ocurred',
|
title: 'An error ocurred',
|
||||||
text: response.data.description,
|
text: response.data.description,
|
||||||
type: 'error'
|
type: 'error'
|
||||||
}, () => {
|
}, () => {
|
||||||
if (reloadOnError) {
|
if (reloadOnError) {
|
||||||
localStorage.removeItem('token');
|
localStorage.removeItem('token')
|
||||||
location.reload();
|
location.reload()
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
localStorage.token = token;
|
localStorage.token = token
|
||||||
upload.token = token;
|
upload.token = token
|
||||||
return upload.prepareUpload();
|
return upload.prepareUpload()
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error');
|
swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error')
|
||||||
return console.log(error);
|
return console.log(error)
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
upload.prepareUpload = function() {
|
upload.prepareUpload = function () {
|
||||||
// I think this fits best here because we need to check for a valid token before we can get the albums
|
// I think this fits best here because we need to check for a valid token before we can get the albums
|
||||||
if (upload.token) {
|
if (upload.token) {
|
||||||
var select = document.getElementById('albumSelect');
|
var select = document.getElementById('albumSelect')
|
||||||
|
|
||||||
select.addEventListener('change', () => {
|
select.addEventListener('change', () => {
|
||||||
upload.album = select.value;
|
upload.album = select.value
|
||||||
});
|
})
|
||||||
|
|
||||||
axios.get('/api/albums', { headers: { token: upload.token } })
|
axios.get('/api/albums', { headers: { token: upload.token } })
|
||||||
.then(res => {
|
.then(res => {
|
||||||
var albums = res.data.albums;
|
var albums = res.data.albums
|
||||||
|
|
||||||
// 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 (albums.length === 0) return;
|
if (albums.length === 0) 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 < albums.length; i++) {
|
for (var i = 0; i < albums.length; i++) {
|
||||||
var opt = document.createElement('option');
|
var opt = document.createElement('option')
|
||||||
opt.value = albums[i].id;
|
opt.value = albums[i].id
|
||||||
opt.innerHTML = albums[i].name;
|
opt.innerHTML = albums[i].name
|
||||||
select.appendChild(opt);
|
select.appendChild(opt)
|
||||||
}
|
}
|
||||||
// Display the album selection
|
// Display the album selection
|
||||||
document.getElementById('albumDiv').style.display = 'block';
|
document.getElementById('albumDiv').style.display = 'block'
|
||||||
})
|
})
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error');
|
swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error')
|
||||||
return console.log(e);
|
return console.log(e)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
div = document.createElement('div');
|
var div = document.createElement('div')
|
||||||
div.id = 'dropzone';
|
div.id = 'dropzone'
|
||||||
div.innerHTML = 'Click here or drag and drop files';
|
div.innerHTML = 'Click here or drag and drop files'
|
||||||
div.style.display = 'flex';
|
div.style.display = 'flex'
|
||||||
|
|
||||||
document.getElementById('maxFileSize').innerHTML = `Maximum upload size per file is ${upload.maxFileSize}`;
|
document.getElementById('maxFileSize').innerHTML = `Maximum upload size per file is ${upload.maxFileSize}`
|
||||||
document.getElementById('loginToUpload').style.display = 'none';
|
document.getElementById('loginToUpload').style.display = 'none'
|
||||||
|
|
||||||
if (upload.token === undefined) { document.getElementById('loginLinkText').innerHTML = 'Create an account and keep track of your uploads'; }
|
if (upload.token === undefined) { document.getElementById('loginLinkText').innerHTML = 'Create an account and keep track of your uploads' }
|
||||||
|
|
||||||
document.getElementById('uploadContainer').appendChild(div);
|
document.getElementById('uploadContainer').appendChild(div)
|
||||||
|
|
||||||
upload.prepareDropzone();
|
upload.prepareDropzone()
|
||||||
};
|
}
|
||||||
|
|
||||||
upload.prepareDropzone = function() {
|
upload.prepareDropzone = function () {
|
||||||
var previewNode = document.querySelector('#template');
|
var previewNode = document.querySelector('#template')
|
||||||
previewNode.id = '';
|
previewNode.id = ''
|
||||||
var previewTemplate = previewNode.parentNode.innerHTML;
|
var previewTemplate = previewNode.parentNode.innerHTML
|
||||||
previewNode.parentNode.removeChild(previewNode);
|
previewNode.parentNode.removeChild(previewNode)
|
||||||
|
|
||||||
var dropzone = new Dropzone('div#dropzone', {
|
var dropzone = new Dropzone('div#dropzone', {
|
||||||
url: '/api/upload',
|
url: '/api/upload',
|
||||||
paramName: 'files[]',
|
paramName: 'files[]',
|
||||||
maxFilesize: upload.maxFileSize.slice(0, -2),
|
maxFilesize: upload.maxFileSize.slice(0, -2),
|
||||||
parallelUploads: 2,
|
parallelUploads: 2,
|
||||||
uploadMultiple: false,
|
uploadMultiple: false,
|
||||||
previewsContainer: 'div#uploads',
|
previewsContainer: 'div#uploads',
|
||||||
previewTemplate: previewTemplate,
|
previewTemplate: previewTemplate,
|
||||||
createImageThumbnails: false,
|
createImageThumbnails: false,
|
||||||
maxFiles: 1000,
|
maxFiles: 1000,
|
||||||
autoProcessQueue: true,
|
autoProcessQueue: true,
|
||||||
headers: { token: upload.token },
|
headers: { token: upload.token },
|
||||||
init: function() {
|
init: function () {
|
||||||
upload.myDropzone = this;
|
upload.myDropzone = this
|
||||||
this.on('addedfile', file => {
|
this.on('addedfile', file => {
|
||||||
document.getElementById('uploads').style.display = 'block';
|
document.getElementById('uploads').style.display = 'block'
|
||||||
});
|
})
|
||||||
// Add the selected albumid, if an album is selected, as a header
|
// Add the selected albumid, if an album is selected, as a header
|
||||||
this.on('sending', (file, xhr) => {
|
this.on('sending', (file, xhr) => {
|
||||||
if (upload.album) {
|
if (upload.album) {
|
||||||
xhr.setRequestHeader('albumid', upload.album);
|
xhr.setRequestHeader('albumid', upload.album)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
// Update the total progress bar
|
// Update the total progress bar
|
||||||
dropzone.on('uploadprogress', (file, progress) => {
|
dropzone.on('uploadprogress', (file, progress) => {
|
||||||
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}%`
|
||||||
});
|
})
|
||||||
|
|
||||||
dropzone.on('success', (file, response) => {
|
dropzone.on('success', (file, response) => {
|
||||||
// Handle the responseText here. For example, add the text to the preview element:
|
// Handle the responseText here. For example, add the text to the preview element:
|
||||||
|
|
||||||
if (response.success === false) {
|
if (response.success === false) {
|
||||||
var span = document.createElement('span');
|
var span = document.createElement('span')
|
||||||
span.innerHTML = response.description;
|
span.innerHTML = response.description
|
||||||
file.previewTemplate.querySelector('.link').appendChild(span);
|
file.previewTemplate.querySelector('.link').appendChild(span)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
a = document.createElement('a');
|
var a = document.createElement('a')
|
||||||
a.href = response.files[0].url;
|
a.href = response.files[0].url
|
||||||
a.target = '_blank';
|
a.target = '_blank'
|
||||||
a.innerHTML = response.files[0].url;
|
a.innerHTML = response.files[0].url
|
||||||
file.previewTemplate.querySelector('.link').appendChild(a);
|
file.previewTemplate.querySelector('.link').appendChild(a)
|
||||||
|
|
||||||
file.previewTemplate.querySelector('.progress').style.display = 'none';
|
file.previewTemplate.querySelector('.progress').style.display = 'none'
|
||||||
});
|
})
|
||||||
|
|
||||||
upload.prepareShareX();
|
upload.prepareShareX()
|
||||||
};
|
}
|
||||||
|
|
||||||
upload.prepareShareX = function() {
|
upload.prepareShareX = function () {
|
||||||
if (upload.token) {
|
if (upload.token) {
|
||||||
var sharex_element = document.getElementById('ShareX');
|
var sharexElement = document.getElementById('ShareX')
|
||||||
var sharex_file = `{\r\n\
|
var sharexFile = `{\r\n\
|
||||||
"Name": "${location.hostname}",\r\n\
|
"Name": "${location.hostname}",\r\n\
|
||||||
"DestinationType": "ImageUploader, FileUploader",\r\n\
|
"DestinationType": "ImageUploader, FileUploader",\r\n\
|
||||||
"RequestType": "POST",\r\n\
|
"RequestType": "POST",\r\n\
|
||||||
@ -178,30 +184,29 @@ upload.prepareShareX = function() {
|
|||||||
"ResponseType": "Text",\r\n\
|
"ResponseType": "Text",\r\n\
|
||||||
"URL": "$json:files[0].url$",\r\n\
|
"URL": "$json:files[0].url$",\r\n\
|
||||||
"ThumbnailURL": "$json:files[0].url$"\r\n\
|
"ThumbnailURL": "$json:files[0].url$"\r\n\
|
||||||
}`;
|
}`
|
||||||
var sharex_blob = new Blob([sharex_file], { type: 'application/octet-binary' });
|
var sharexBlob = new Blob([sharexFile], { type: 'application/octet-binary' })
|
||||||
sharex_element.setAttribute('href', URL.createObjectURL(sharex_blob));
|
sharexElement.setAttribute('href', URL.createObjectURL(sharexBlob))
|
||||||
sharex_element.setAttribute('download', `${location.hostname}.sxcu`);
|
sharexElement.setAttribute('download', `${location.hostname}.sxcu`)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
// Handle image paste event
|
// Handle image paste event
|
||||||
window.addEventListener('paste', event => {
|
window.addEventListener('paste', event => {
|
||||||
var items = (event.clipboardData || event.originalEvent.clipboardData).items;
|
var items = (event.clipboardData || event.originalEvent.clipboardData).items
|
||||||
for (index in items) {
|
for (var index in items) {
|
||||||
var item = items[index];
|
var item = items[index]
|
||||||
if (item.kind === 'file') {
|
if (item.kind === 'file') {
|
||||||
var blob = item.getAsFile();
|
var blob = item.getAsFile()
|
||||||
console.log(blob.type);
|
console.log(blob.type)
|
||||||
var file = new File([blob], `pasted-image.${blob.type.match(/(?:[^\/]*\/)([^;]*)/)[1]}`);
|
var file = new File([blob], `pasted-image.${blob.type.match(/(?:[^/]*\/)([^;]*)/)[1]}`)
|
||||||
file.type = blob.type;
|
file.type = blob.type
|
||||||
console.log(file);
|
console.log(file)
|
||||||
upload.myDropzone.addFile(file);
|
upload.myDropzone.addFile(file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
window.onload = function() {
|
|
||||||
upload.checkIfPublic();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
window.onload = function () {
|
||||||
|
upload.checkIfPublic()
|
||||||
|
}
|
||||||
|
@ -1,56 +1,55 @@
|
|||||||
const config = require('../config.js');
|
const config = require('../config.js')
|
||||||
const routes = require('express').Router();
|
const routes = require('express').Router()
|
||||||
const db = require('knex')(config.database);
|
const db = require('knex')(config.database)
|
||||||
const path = require('path');
|
const path = require('path')
|
||||||
const utils = require('../controllers/utilsController.js');
|
const utils = require('../controllers/utilsController.js')
|
||||||
|
|
||||||
routes.get('/a/:identifier', async (req, res, next) => {
|
routes.get('/a/:identifier', async (req, res, next) => {
|
||||||
let identifier = req.params.identifier;
|
let identifier = req.params.identifier
|
||||||
if (identifier === undefined) return res.status(401).json({ success: false, description: 'No identifier provided' });
|
if (identifier === undefined) return res.status(401).json({ success: false, description: 'No identifier provided' })
|
||||||
|
|
||||||
const album = await db.table('albums').where({ identifier, enabled: 1 }).first();
|
const album = await db.table('albums').where({ identifier, enabled: 1 }).first()
|
||||||
if (!album) return res.status(404).sendFile('404.html', { root: './pages/error/' });
|
if (!album) return res.status(404).sendFile('404.html', { root: './pages/error/' })
|
||||||
|
|
||||||
const files = await db.table('files').select('name').where('albumid', album.id).orderBy('id', 'DESC');
|
const files = await db.table('files').select('name').where('albumid', album.id).orderBy('id', 'DESC')
|
||||||
let thumb = '';
|
let thumb = ''
|
||||||
const basedomain = config.domain;
|
const basedomain = config.domain
|
||||||
|
|
||||||
for (let file of files) {
|
for (let file of files) {
|
||||||
file.file = `${basedomain}/${file.name}`;
|
file.file = `${basedomain}/${file.name}`
|
||||||
|
|
||||||
let ext = path.extname(file.name).toLowerCase();
|
let ext = path.extname(file.name).toLowerCase()
|
||||||
if (utils.imageExtensions.includes(ext) || utils.videoExtensions.includes(ext)) {
|
if (utils.imageExtensions.includes(ext) || utils.videoExtensions.includes(ext)) {
|
||||||
file.thumb = `${basedomain}/thumbs/${file.name.slice(0, -ext.length)}.png`;
|
file.thumb = `${basedomain}/thumbs/${file.name.slice(0, -ext.length)}.png`
|
||||||
|
|
||||||
/*
|
/*
|
||||||
If thumbnail for album is still not set, do it.
|
If thumbnail for album is still not set, do it.
|
||||||
A potential improvement would be to let the user upload a specific image as an album cover
|
A potential improvement would be to let the user upload a specific image as an album cover
|
||||||
since embedding the first image could potentially result in nsfw content when pasting links.
|
since embedding the first image could potentially result in nsfw content when pasting links.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (thumb === '') {
|
if (thumb === '') {
|
||||||
thumb = file.thumb;
|
thumb = file.thumb
|
||||||
}
|
}
|
||||||
|
|
||||||
file.thumb = `<img src="${file.thumb}"/>`;
|
file.thumb = `<img src="${file.thumb}"/>`
|
||||||
} else {
|
} else {
|
||||||
file.thumb = `<h1 class="title">.${ext}</h1>`;
|
file.thumb = `<h1 class="title">.${ext}</h1>`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let enableDownload = false
|
||||||
|
if (config.uploads.generateZips) enableDownload = true
|
||||||
|
|
||||||
let enableDownload = false;
|
return res.render('album', {
|
||||||
if (config.uploads.generateZips) enableDownload = true;
|
layout: false,
|
||||||
|
title: album.name,
|
||||||
|
count: files.length,
|
||||||
|
thumb,
|
||||||
|
files,
|
||||||
|
identifier,
|
||||||
|
enableDownload
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
return res.render('album', {
|
module.exports = routes
|
||||||
layout: false,
|
|
||||||
title: album.name,
|
|
||||||
count: files.length,
|
|
||||||
thumb,
|
|
||||||
files,
|
|
||||||
identifier,
|
|
||||||
enableDownload
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = routes;
|
|
||||||
|
@ -1,37 +1,37 @@
|
|||||||
const config = require('../config.js');
|
const config = require('../config.js')
|
||||||
const routes = require('express').Router();
|
const routes = require('express').Router()
|
||||||
const uploadController = require('../controllers/uploadController');
|
const uploadController = require('../controllers/uploadController')
|
||||||
const albumsController = require('../controllers/albumsController');
|
const albumsController = require('../controllers/albumsController')
|
||||||
const tokenController = require('../controllers/tokenController');
|
const tokenController = require('../controllers/tokenController')
|
||||||
const authController = require('../controllers/authController');
|
const authController = require('../controllers/authController')
|
||||||
|
|
||||||
routes.get('/check', (req, res, next) => {
|
routes.get('/check', (req, res, next) => {
|
||||||
return res.json({
|
return res.json({
|
||||||
private: config.private,
|
private: config.private,
|
||||||
maxFileSize: config.uploads.maxSize
|
maxFileSize: config.uploads.maxSize
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
routes.post('/login', (req, res, next) => authController.verify(req, res, next));
|
routes.post('/login', (req, res, next) => authController.verify(req, res, next))
|
||||||
routes.post('/register', (req, res, next) => authController.register(req, res, next));
|
routes.post('/register', (req, res, next) => authController.register(req, res, next))
|
||||||
routes.post('/password/change', (req, res, next) => authController.changePassword(req, res, next));
|
routes.post('/password/change', (req, res, next) => authController.changePassword(req, res, next))
|
||||||
routes.get('/uploads', (req, res, next) => uploadController.list(req, res, next));
|
routes.get('/uploads', (req, res, next) => uploadController.list(req, res, next))
|
||||||
routes.get('/uploads/:page', (req, res, next) => uploadController.list(req, res, next));
|
routes.get('/uploads/:page', (req, res, next) => uploadController.list(req, res, next))
|
||||||
routes.post('/upload', (req, res, next) => uploadController.upload(req, res, next));
|
routes.post('/upload', (req, res, next) => uploadController.upload(req, res, next))
|
||||||
routes.post('/upload/delete', (req, res, next) => uploadController.delete(req, res, next));
|
routes.post('/upload/delete', (req, res, next) => uploadController.delete(req, res, next))
|
||||||
routes.post('/upload/:albumid', (req, res, next) => uploadController.upload(req, res, next));
|
routes.post('/upload/:albumid', (req, res, next) => uploadController.upload(req, res, next))
|
||||||
routes.get('/album/get/:identifier', (req, res, next) => albumsController.get(req, res, next));
|
routes.get('/album/get/:identifier', (req, res, next) => albumsController.get(req, res, next))
|
||||||
routes.get('/album/zip/:identifier', (req, res, next) => albumsController.generateZip(req, res, next));
|
routes.get('/album/zip/:identifier', (req, res, next) => albumsController.generateZip(req, res, next))
|
||||||
routes.get('/album/:id', (req, res, next) => uploadController.list(req, res, next));
|
routes.get('/album/:id', (req, res, next) => uploadController.list(req, res, next))
|
||||||
routes.get('/album/:id/:page', (req, res, next) => uploadController.list(req, res, next));
|
routes.get('/album/:id/:page', (req, res, next) => uploadController.list(req, res, next))
|
||||||
routes.get('/albums', (req, res, next) => albumsController.list(req, res, next));
|
routes.get('/albums', (req, res, next) => albumsController.list(req, res, next))
|
||||||
routes.get('/albums/:sidebar', (req, res, next) => albumsController.list(req, res, next));
|
routes.get('/albums/:sidebar', (req, res, next) => albumsController.list(req, res, next))
|
||||||
routes.post('/albums', (req, res, next) => albumsController.create(req, res, next));
|
routes.post('/albums', (req, res, next) => albumsController.create(req, res, next))
|
||||||
routes.post('/albums/delete', (req, res, next) => albumsController.delete(req, res, next));
|
routes.post('/albums/delete', (req, res, next) => albumsController.delete(req, res, next))
|
||||||
routes.post('/albums/rename', (req, res, next) => albumsController.rename(req, res, next));
|
routes.post('/albums/rename', (req, res, next) => albumsController.rename(req, res, next))
|
||||||
routes.get('/albums/test', (req, res, next) => albumsController.test(req, res, next));
|
routes.get('/albums/test', (req, res, next) => albumsController.test(req, res, next))
|
||||||
routes.get('/tokens', (req, res, next) => tokenController.list(req, res, next));
|
routes.get('/tokens', (req, res, next) => tokenController.list(req, res, next))
|
||||||
routes.post('/tokens/verify', (req, res, next) => tokenController.verify(req, res, next));
|
routes.post('/tokens/verify', (req, res, next) => tokenController.verify(req, res, next))
|
||||||
routes.post('/tokens/change', (req, res, next) => tokenController.change(req, res, next));
|
routes.post('/tokens/change', (req, res, next) => tokenController.change(req, res, next))
|
||||||
|
|
||||||
module.exports = routes;
|
module.exports = routes
|
||||||
|
Loading…
Reference in New Issue
Block a user