Merge branch 'safe.fiery.me' into master

This commit is contained in:
Bobby Wibowo 2018-02-04 03:55:06 +07:00 committed by GitHub
commit 27050d5ac0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
78 changed files with 3661 additions and 2405 deletions

13
.editorconfig Normal file
View File

@ -0,0 +1,13 @@
[*]
end_of_line = lf
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[LICENSE]
insert_final_newline = false

View File

@ -1,9 +1,12 @@
![loli-safe](https://a.safe.moe/jcutlz.png)
![loli-safe](https://s.fiery.me/rlpHkNJejaYKuVesQN1Gxtm43NokI8WQ.png)
[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](https://raw.githubusercontent.com/kanadeko/Kuro/master/LICENSE)
[![Chat / Support](https://img.shields.io/badge/Chat%20%2F%20Support-discord-7289DA.svg?style=flat-square)](https://discord.gg/5g6vgwn)
# lolisafe, a small safe worth protecting.
## What is `safe.fiery.me` branch?
This branch is the one being used at https://safe.fiery.me. If you are looking for the original, head to `master` branch, or even better to [WeebDev/lolisafe](https://github.com/WeebDev/lolisafe).
## What's new in v3.0.0
- Backend rewrite to make it faster, better and easier to extend
- Album downloads (Thanks to [PascalTemel](https://github.com/PascalTemel))

View File

@ -1,58 +1,58 @@
const config = require('../config.js');
const db = require('knex')(config.database);
const randomstring = require('randomstring');
const utils = require('./utilsController.js');
const path = require('path');
const fs = require('fs');
const Zip = require('jszip');
const albumsController = {};
const config = require('../config.js')
const db = require('knex')(config.database)
const randomstring = require('randomstring')
const utils = require('./utilsController.js')
const path = require('path')
const fs = require('fs')
const Zip = require('jszip')
const albumsController = {}
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) {
fields.push('timestamp');
fields.push('identifier');
fields.push('timestamp')
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) {
return res.json({ success: true, albums });
return res.json({ success: true, albums })
}
let ids = [];
let ids = []
for (let album of albums) {
album.date = new Date(album.timestamp * 1000)
album.date = utils.getPrettyDate(album.date)
album.identifier = `${config.domain}/a/${album.identifier}`;
ids.push(album.id);
album.identifier = `${config.domain}/a/${album.identifier}`
ids.push(album.id)
}
const files = await db.table('files').whereIn('albumid', ids).select('albumid');
const albumsCount = {};
const files = await db.table('files').whereIn('albumid', ids).select('albumid')
const albumsCount = {}
for (let id of ids) albumsCount[id] = 0;
for (let file of files) albumsCount[file.albumid] += 1;
for (let album of albums) album.files = albumsCount[album.id];
for (let id of ids) albumsCount[id] = 0
for (let file of files) albumsCount[file.albumid] += 1
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) => {
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 === '') {
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({
name: name,
enabled: 1,
userid: user.id
}).first();
}).first()
if (album) {
return res.json({ success: false, description: 'There\'s already an album with that name' })
@ -64,61 +64,61 @@ albumsController.create = async (req, res, next) => {
userid: user.id,
identifier: randomstring.generate(8),
timestamp: Math.floor(Date.now() / 1000)
});
})
return res.json({ success: true });
};
return res.json({ success: true })
}
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 === '') {
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 });
return res.json({ success: true });
};
await db.table('albums').where({ id: id, userid: user.id }).update({ enabled: 0 })
return res.json({ success: true })
}
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 === '') {
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 === '') {
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) {
return res.json({ success: false, description: 'Name already in use' })
}
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) => {
const identifier = req.params.identifier;
if (identifier === undefined) return res.status(401).json({ success: false, description: 'No identifier provided' });
const identifier = req.params.identifier
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();
if (!album) return res.json({ success: false, description: 'Album not found' });
const album = await db.table('albums').where({ identifier, enabled: 1 }).first()
if (!album) return res.json({ success: false, description: 'Album not found' })
const title = album.name;
const files = await db.table('files').select('name').where('albumid', album.id).orderBy('id', 'DESC');
const title = album.name
const files = await db.table('files').select('name').where('albumid', album.id).orderBy('id', 'DESC')
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)) {
file.thumb = `${config.domain}/thumbs/${file.name.slice(0, -ext.length)}.png`;
file.thumb = `${config.domain}/thumbs/${file.name.slice(0, -ext.length)}.png`
}
}
@ -127,36 +127,35 @@ albumsController.get = async (req, res, next) => {
title: title,
count: files.length,
files
});
};
})
}
albumsController.generateZip = async (req, res, next) => {
const identifier = req.params.identifier;
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' });
const identifier = req.params.identifier
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' })
const album = await db.table('albums').where({ identifier, enabled: 1 }).first();
if (!album) return res.json({ success: false, description: 'Album not found' });
const album = await db.table('albums').where({ identifier, enabled: 1 }).first()
if (!album) return res.json({ success: false, description: 'Album not found' })
if (album.zipGeneratedAt > album.editedAt) {
const filePath = path.join(config.uploads.folder, 'zips', `${identifier}.zip`);
const fileName = `${album.name}.zip`;
return res.download(filePath, fileName);
const filePath = path.join(config.uploads.folder, 'zips', `${identifier}.zip`)
const fileName = `${album.name}.zip`
return res.download(filePath, fileName)
} else {
console.log(`Generating zip for album identifier: ${identifier}`);
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' });
console.log(`Generating zip for album identifier: ${identifier}`)
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' })
const zipPath = path.join(__dirname, '..', config.uploads.folder, 'zips', `${album.identifier}.zip`);
let archive = new Zip();
const zipPath = path.join(__dirname, '..', config.uploads.folder, 'zips', `${album.identifier}.zip`)
let archive = new Zip()
for (let file of files) {
try {
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)));
// 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)))
} catch (err) {
console.log(err);
console.log(err)
}
}
@ -164,16 +163,16 @@ albumsController.generateZip = async (req, res, next) => {
.generateNodeStream({ type: 'nodebuffer', streamFiles: true })
.pipe(fs.createWriteStream(zipPath))
.on('finish', async () => {
console.log(`Generated zip for album identifier: ${identifier}`);
console.log(`Generated zip for album identifier: ${identifier}`)
await db.table('albums')
.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 fileName = `${album.name}.zip`;
return res.download(filePath, fileName);
});
const filePath = path.join(config.uploads.folder, 'zips', `${identifier}.zip`)
const fileName = `${album.name}.zip`
return res.download(filePath, fileName)
})
}
}
};
module.exports = albumsController;
module.exports = albumsController

View File

@ -1,41 +1,41 @@
const config = require('../config.js');
const db = require('knex')(config.database);
const bcrypt = require('bcrypt');
const randomstring = require('randomstring');
const utils = require('./utilsController.js');
const config = require('../config.js')
const db = require('knex')(config.database)
const bcrypt = require('bcrypt')
const randomstring = require('randomstring')
const utils = require('./utilsController.js')
let authController = {};
let authController = {}
authController.verify = async (req, res, next) => {
const username = req.body.username;
const password = req.body.password;
const username = req.body.username
const password = req.body.password
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 (username === undefined) return res.json({ success: false, description: 'No username provided' })
if (password === undefined) return res.json({ success: false, description: 'No password provided' })
const user = await db.table('users').where('username', username).first();
if (!user) return res.json({ success: false, description: 'Username doesn\'t exist' });
const user = await db.table('users').where('username', username).first()
if (!user) return res.json({ success: false, description: 'Username doesn\'t exist' })
bcrypt.compare(password, user.password, (err, result) => {
if (err) {
console.log(err);
return res.json({ success: false, description: 'There was an error' });
console.log(err)
return res.json({ success: false, description: 'There was an error' })
}
if (result === false) return res.json({ success: false, description: 'Wrong password' })
return res.json({ success: true, token: user.token })
})
}
if (result === false) return res.json({ success: false, description: 'Wrong password' });
return res.json({ success: true, token: user.token });
});
};
authController.register = async (req, res, next) => {
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 password = req.body.password;
const username = req.body.username
const password = req.body.password
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 (username === undefined) return res.json({ success: false, description: 'No username provided' })
if (password === undefined) return res.json({ success: false, description: 'No password provided' })
if (username.length < 4 || username.length > 32) {
return res.json({ success: false, description: 'Username must have 4-32 characters' })
@ -44,43 +44,43 @@ authController.register = async (req, res, next) => {
return res.json({ success: false, description: 'Password must have 6-64 characters' })
}
const user = await db.table('users').where('username', username).first();
if (user) return res.json({ success: false, description: 'Username already exists' });
const user = await db.table('users').where('username', username).first()
if (user) return res.json({ success: false, description: 'Username already exists' })
bcrypt.hash(password, 10, async (err, hash) => {
if (err) {
console.log(err);
return res.json({ success: false, description: 'Error generating password hash (╯°□°)╯︵ ┻━┻' });
console.log(err)
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({
username: username,
password: hash,
token: token
});
})
return res.json({ success: true, token: token })
});
};
})
}
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;
if (password === undefined) return res.json({ success: false, description: 'No password provided' });
let password = req.body.password
if (password === undefined) return res.json({ success: false, description: 'No password provided' })
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) => {
if (err) {
console.log(err);
return res.json({ success: false, description: 'Error generating password hash (╯°□°)╯︵ ┻━┻' });
console.log(err)
return res.json({ success: false, description: 'Error generating password hash (╯°□°)╯︵ ┻━┻' })
}
await db.table('users').where('id', user.id).update({ password: hash });
return res.json({ success: true });
});
};
await db.table('users').where('id', user.id).update({ password: hash })
return res.json({ success: true })
})
}
module.exports = authController;
module.exports = authController

View File

@ -1,34 +1,34 @@
const config = require('../config.js');
const db = require('knex')(config.database);
const randomstring = require('randomstring');
const utils = require('./utilsController.js');
const config = require('../config.js')
const db = require('knex')(config.database)
const randomstring = require('randomstring')
const utils = require('./utilsController.js')
const tokenController = {};
const tokenController = {}
tokenController.verify = async (req, res, next) => {
const token = req.body.token;
if (token === undefined) return res.status(401).json({ success: false, description: 'No token provided' });
const token = req.body.token
if (token === undefined) return res.status(401).json({ success: false, description: 'No token provided' })
const user = await db.table('users').where('token', token).first();
if (!user) return res.status(401).json({ success: false, description: 'Invalid token' });
return res.json({ success: true, username: user.username });
};
const user = await db.table('users').where('token', token).first()
if (!user) return res.status(401).json({ success: false, description: 'Invalid token' })
return res.json({ success: true, username: user.username })
}
tokenController.list = async (req, res, next) => {
const user = await utils.authorize(req, res);
return res.json({ success: true, token: user.token });
};
const user = await utils.authorize(req, res)
return res.json({ success: true, token: user.token })
}
tokenController.change = async (req, res, next) => {
const user = await utils.authorize(req, res);
const newtoken = randomstring.generate(64);
const user = await utils.authorize(req, res)
const newtoken = randomstring.generate(64)
await db.table('users').where('token', user.token).update({
token: newtoken,
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

@ -1,22 +1,22 @@
const config = require('../config.js');
const path = require('path');
const multer = require('multer');
const randomstring = require('randomstring');
const db = require('knex')(config.database);
const crypto = require('crypto');
const fs = require('fs');
const utils = require('./utilsController.js');
const config = require('../config.js')
const path = require('path')
const multer = require('multer')
const randomstring = require('randomstring')
const db = require('knex')(config.database)
const crypto = require('crypto')
const fs = require('fs')
const utils = require('./utilsController.js')
const uploadsController = {};
const uploadsController = {}
const storage = multer.diskStorage({
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) {
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({
storage: storage,
@ -24,70 +24,70 @@ const upload = multer({
fileFilter: function (req, file, cb) {
if (config.blockedExtensions !== undefined) {
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) => {
if (config.private === true) {
await utils.authorize(req, res);
await utils.authorize(req, res)
}
const token = req.headers.token || '';
const user = await db.table('users').where('token', token).first();
const albumid = req.headers.albumid || req.params.albumid;
const token = req.headers.token || ''
const user = await db.table('users').where('token', token).first()
const albumid = req.headers.albumid || req.params.albumid
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) {
return res.json({
success: false,
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) => {
upload(req, res, async err => {
if (err) {
console.error(err);
return res.json({ success: false, description: err });
console.error(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 existingFiles = [];
let iteration = 1;
const files = []
const existingFiles = []
let iteration = 1
req.files.forEach(async file => {
// Check if the file exists by checking hash and size
let hash = crypto.createHash('md5');
let stream = fs.createReadStream(path.join(__dirname, '..', config.uploads.folder, file.filename));
let hash = crypto.createHash('md5')
let stream = fs.createReadStream(path.join(__dirname, '..', config.uploads.folder, file.filename))
stream.on('data', data => {
hash.update(data, 'utf8');
});
hash.update(data, 'utf8')
})
stream.on('end', async () => {
const fileHash = hash.digest('hex');
const fileHash = hash.digest('hex')
const dbFile = await db.table('files')
.where(function () {
if (userid === undefined) this.whereNull('userid');
else this.where('userid', userid.id);
if (userid === undefined) this.whereNull('userid')
else this.where('userid', userid.id)
})
.where({
hash: fileHash,
size: file.size
})
.first();
.first()
if (!dbFile) {
files.push({
@ -100,23 +100,23 @@ uploadsController.actuallyUpload = async (req, res, userid, album) => {
albumid: album,
userid: userid !== undefined ? userid.id : null,
timestamp: Math.floor(Date.now() / 1000)
});
})
} else {
uploadsController.deleteFile(file.filename).then(() => {}).catch(err => console.error(err));
existingFiles.push(dbFile);
uploadsController.deleteFile(file.filename).then(() => {}).catch(err => console.error(err))
existingFiles.push(dbFile)
}
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) => {
let basedomain = config.domain;
let basedomain = config.domain
if (files.length === 0) {
return res.json({
success: true,
@ -125,13 +125,13 @@ uploadsController.processFilesForDisplay = async (req, res, files, existingFiles
name: file.name,
size: file.size,
url: `${basedomain}/${file.name}`
};
}
})
})
});
}
await db.table('files').insert(files);
for (let efile of existingFiles) files.push(efile);
await db.table('files').insert(files)
for (let efile of existingFiles) files.push(efile)
res.json({
success: true,
@ -140,113 +140,113 @@ uploadsController.processFilesForDisplay = async (req, res, files, existingFiles
name: file.name,
size: file.size,
url: `${basedomain}/${file.name}`
};
}
})
})
});
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)) {
file.thumb = `${basedomain}/thumbs/${file.name.slice(0, -ext.length)}.png`;
utils.generateThumbs(file);
file.thumb = `${basedomain}/thumbs/${file.name.slice(0, -ext.length)}.png`
utils.generateThumbs(file)
}
if (file.albumid) {
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) => {
const user = await utils.authorize(req, res);
const id = req.body.id;
const user = await utils.authorize(req, res)
const id = req.body.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')
.where('id', id)
.where(function () {
if (user.username !== 'root') {
this.where('userid', user.id);
this.where('userid', user.id)
}
})
.first();
.first()
try {
await uploadsController.deleteFile(file.name);
await db.table('files').where('id', id).del();
await uploadsController.deleteFile(file.name)
await db.table('files').where('id', id).del()
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) {
console.log(err);
console.log(err)
}
return res.json({ success: true });
};
return res.json({ success: true })
}
uploadsController.deleteFile = function (file) {
const ext = path.extname(file).toLowerCase();
const ext = path.extname(file).toLowerCase()
return new Promise((resolve, reject) => {
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 => {
if (err) { return reject(err); }
if (err) { return reject(err) }
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) => {
if (err) {
console.log(err);
return resolve();
console.log(err)
return resolve()
}
fs.unlink(path.join(__dirname, '..', config.uploads.folder, 'thumbs/', file), err => {
if (err) { return reject(err); }
return resolve();
});
});
});
});
});
};
if (err) { return reject(err) }
return resolve()
})
})
})
})
})
}
uploadsController.list = async (req, res) => {
const user = await utils.authorize(req, res);
const user = await utils.authorize(req, res)
let offset = req.params.page;
if (offset === undefined) offset = 0;
let offset = req.params.page
if (offset === undefined) offset = 0
const files = await db.table('files')
.where(function () {
if (req.params.id === undefined) this.where('id', '<>', '');
else this.where('albumid', req.params.id);
if (req.params.id === undefined) this.where('id', '<>', '')
else this.where('albumid', req.params.id)
})
.where(function () {
if (user.username !== 'root') this.where('userid', user.id);
if (user.username !== 'root') this.where('userid', user.id)
})
.orderBy('id', 'DESC')
.limit(25)
.offset(25 * offset)
.select('id', 'albumid', 'timestamp', 'name', 'userid');
.select('id', 'albumid', 'timestamp', 'name', 'userid')
const albums = await db.table('albums');
let basedomain = config.domain;
let userids = [];
const albums = await db.table('albums')
let basedomain = config.domain
let userids = []
for (let file of files) {
file.file = `${basedomain}/${file.name}`;
file.date = new Date(file.timestamp * 1000);
file.date = utils.getPrettyDate(file.date);
file.file = `${basedomain}/${file.name}`
file.date = new Date(file.timestamp * 1000)
file.date = utils.getPrettyDate(file.date)
file.album = '';
file.album = ''
if (file.albumid !== undefined) {
for (let album of albums) {
if (file.albumid === album.id) {
file.album = album.name;
file.album = album.name
}
}
}
@ -254,32 +254,32 @@ uploadsController.list = async (req, res) => {
// Only push usernames if we are root
if (user.username === 'root') {
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)) {
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 (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 (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 file of files) {
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

View File

@ -1,40 +1,40 @@
const path = require('path');
const config = require('../config.js');
const fs = require('fs');
const gm = require('gm');
const ffmpeg = require('fluent-ffmpeg');
const db = require('knex')(config.database);
const path = require('path')
const config = require('../config.js')
const fs = require('fs')
const gm = require('gm')
const ffmpeg = require('fluent-ffmpeg')
const db = require('knex')(config.database)
const utilsController = {};
utilsController.imageExtensions = ['.jpg', '.jpeg', '.bmp', '.gif', '.png'];
utilsController.videoExtensions = ['.webm', '.mp4', '.wmv', '.avi', '.mov'];
const utilsController = {}
utilsController.imageExtensions = ['.jpg', '.jpeg', '.bmp', '.gif', '.png']
utilsController.videoExtensions = ['.webm', '.mp4', '.wmv', '.avi', '.mov']
utilsController.getPrettyDate = function (date) {
return date.getFullYear() + '-'
+ (date.getMonth() + 1) + '-'
+ date.getDate() + ' '
+ (date.getHours() < 10 ? '0' : '')
+ date.getHours() + ':'
+ (date.getMinutes() < 10 ? '0' : '')
+ date.getMinutes() + ':'
+ (date.getSeconds() < 10 ? '0' : '')
+ date.getSeconds();
return date.getFullYear() + '-' +
(date.getMonth() + 1) + '-' +
date.getDate() + ' ' +
(date.getHours() < 10 ? '0' : '') +
date.getHours() + ':' +
(date.getMinutes() < 10 ? '0' : '') +
date.getMinutes() + ':' +
(date.getSeconds() < 10 ? '0' : '') +
date.getSeconds()
}
utilsController.authorize = async (req, res) => {
const token = req.headers.token;
if (token === undefined) return res.status(401).json({ success: false, description: 'No token provided' });
const token = req.headers.token
if (token === undefined) return res.status(401).json({ success: false, description: 'No token provided' })
const user = await db.table('users').where('token', token).first();
if (!user) return res.status(401).json({ success: false, description: 'Invalid token' });
return user;
};
const user = await db.table('users').where('token', token).first()
if (!user) return res.status(401).json({ success: false, description: 'Invalid token' })
return user
}
utilsController.generateThumbs = function (file, basedomain) {
if (config.uploads.generateThumbnails !== true) return;
const ext = path.extname(file.name).toLowerCase();
if (config.uploads.generateThumbnails !== true) return
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 => {
if (err && err.code === 'ENOENT') {
if (utilsController.videoExtensions.includes(ext)) {
@ -45,23 +45,23 @@ utilsController.generateThumbs = function(file, basedomain) {
folder: path.join(__dirname, '..', config.uploads.folder, 'thumbs'),
size: '200x?'
})
.on('error', error => console.log('Error - ', error.message));
.on('error', error => console.log('Error - ', error.message))
} else {
let size = {
width: 200,
height: 200
};
}
gm(path.join(__dirname, '..', config.uploads.folder, file.name))
.resize(size.width, size.height + '>')
.gravity('Center')
.extent(size.width, size.height)
.background('transparent')
.write(thumbname, error => {
if (error) console.log('Error - ', error);
});
if (error) console.log('Error - ', error)
})
}
}
});
};
})
}
module.exports = utilsController;
module.exports = utilsController

View File

@ -1,5 +1,4 @@
let init = function (db) {
// Create the tables we need to store galleries and files
db.schema.createTableIfNotExists('albums', function (table) {
table.increments()

View File

@ -1,13 +1,13 @@
const config = require('../config.js');
const db = require('knex')(config.database);
const config = require('../config.js')
const db = require('knex')(config.database)
const migration = {};
const migration = {}
migration.start = async () => {
await db.schema.table('albums', table => {
table.dateTime('editedAt');
table.dateTime('zipGeneratedAt');
});
console.log('Migration finished! Now start lolisafe normally');
};
table.dateTime('editedAt')
table.dateTime('zipGeneratedAt')
})
console.log('Migration finished! Now start lolisafe normally')
}
migration.start();
migration.start()

View File

@ -1,58 +1,80 @@
const config = require('./config.js');
const api = require('./routes/api.js');
const album = require('./routes/album.js');
const express = require('express');
const helmet = require('helmet');
const bodyParser = require('body-parser');
const RateLimit = require('express-rate-limit');
const db = require('knex')(config.database);
const fs = require('fs');
const exphbs = require('express-handlebars');
const safe = express();
const config = require('./config.js')
const api = require('./routes/api.js')
const album = require('./routes/album.js')
const express = require('express')
const helmet = require('helmet')
const bodyParser = require('body-parser')
const RateLimit = require('express-rate-limit')
const db = require('knex')(config.database)
const fs = require('fs')
const exphbs = require('express-handlebars')
const safe = express()
require('./database/db.js')(db);
require('./database/db.js')(db)
fs.existsSync('./pages/custom' ) || fs.mkdirSync('./pages/custom');
fs.existsSync('./' + config.logsFolder) || fs.mkdirSync('./' + config.logsFolder);
fs.existsSync('./' + config.uploads.folder) || fs.mkdirSync('./' + config.uploads.folder);
fs.existsSync('./' + config.uploads.folder + '/thumbs') || fs.mkdirSync('./' + config.uploads.folder + '/thumbs');
fs.existsSync('./pages/custom') || fs.mkdirSync('./pages/custom')
fs.existsSync('./' + config.logsFolder) || fs.mkdirSync('./' + config.logsFolder)
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 + '/zips') || fs.mkdirSync('./' + config.uploads.folder + '/zips')
safe.use(helmet());
safe.set('trust proxy', 1);
safe.use(helmet())
safe.set('trust proxy', 1)
safe.engine('handlebars', exphbs({ defaultLayout: 'main' }));
safe.set('view engine', 'handlebars');
safe.enable('view cache');
safe.engine('handlebars', exphbs({ defaultLayout: 'main' }))
safe.set('view engine', 'handlebars')
safe.enable('view cache')
let limiter = new RateLimit({ windowMs: 5000, max: 2 });
safe.use('/api/login/', limiter);
safe.use('/api/register/', limiter);
let limiter = new RateLimit({ windowMs: 5000, max: 2 })
safe.use('/api/login/', limiter)
safe.use('/api/register/', limiter)
safe.use(bodyParser.urlencoded({ extended: true }));
safe.use(bodyParser.json());
safe.use(bodyParser.urlencoded({ extended: true }))
safe.use(bodyParser.json())
const setHeaders = (res, path, stat) => {
if (/\.(3gp|gif|jpg|jpeg|png|ico|wmv|avi|asf|asx|mpg|mpeg|mp4|pls|mp3|mid|wav|swf|flv|exe|zip|tar|rar|gz|tgz|bz2|uha|7z|doc|docx|xls|xlsx|pdf|iso|js|css|eot|svg|ttf|woff|woff2)$/.test(path)) {
const day = 86400
res.set('Access-Control-Allow-Origin', '*')
res.set('Cache-Control', `public, max-age=${day * 30}, must-revalidate, proxy-revalidate, immutable, stale-while-revalidate=86400, stale-if-error=604800`) // 30 days
}
}
if (config.serveFilesWithNode) {
safe.use('/', express.static(config.uploads.folder));
safe.use('/', express.static(config.uploads.folder, { setHeaders }))
}
safe.use('/', express.static('./public'));
safe.use('/', album);
safe.use('/api', api);
safe.use('/', express.static('./public', { setHeaders }))
safe.use('/', album)
safe.use('/api', api)
for (let page of config.pages) {
let root = './pages/';
let root = './pages/'
if (fs.existsSync(`./pages/custom/${page}.html`)) {
root = './pages/custom/';
root = './pages/custom/'
}
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 {
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(500).sendFile('500.html', { root: './pages/error/' }));
// NOTE: Uses fiery-me branch of https://github.com/BobbyWibowo/HttpErrorPages
safe.use((req, res, next) => {
res.status(404).sendFile('HTTP404.html', { root: '../HttpErrorPages/dist/' })
})
safe.use((err, req, res, next) => {
console.error(err)
res.status(500).sendFile('HTTP505.html', { root: '../HttpErrorPages/dist/' })
})
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}`)
})

View File

@ -14,35 +14,32 @@
"node": ">=7.0.0"
},
"license": "MIT",
"scripts": {
"start": "node ./lolisafe.js",
"pm2": "pm2 start --name lolisafe ./lolisafe.js "
},
"dependencies": {
"bcrypt": "^1.0.3",
"body-parser": "^1.16.0",
"express": "^4.14.0",
"body-parser": "^1.18.2",
"express": "^4.16.2",
"express-handlebars": "^3.0.0",
"express-rate-limit": "^2.6.0",
"express-rate-limit": "^2.11.0",
"fluent-ffmpeg": "^2.1.0",
"gm": "^1.23.0",
"helmet": "^3.5.0",
"gm": "^1.23.1",
"helmet": "^3.10.0",
"jszip": "^3.1.4",
"knex": "^0.12.6",
"knex": "^0.14.2",
"multer": "^1.2.1",
"randomstring": "^1.1.5",
"sqlite3": "^3.1.11"
"sqlite3": "^3.1.13"
},
"devDependencies": {
"eslint": "^4.4.1",
"eslint-config-aqua": "^1.4.1"
"standard": "^10.0.3"
},
"eslintConfig": {
"extends": [
"aqua"
],
"env": {
"browser": true,
"node": true
},
"rules": {
"func-names": 0
}
"standard": {
"envs": [
"browser",
"node"
]
}
}

View File

@ -1,42 +1,54 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="description" content="A pomf-like file uploading service that doesn't suck.">
<meta name="keywords" content="upload,lolisafe,file,images,hosting">
<meta name="keywords" content="upload,lolisafe,file,images,hosting,bobby,fiery">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>safe.fiery.me &#8211; A small safe worth protecting.</title>
<link rel="apple-touch-icon" sizes="180x180" href="https://lolisafe.moe/images/icons/apple-touch-icon.png?v=XBreOJMe24">
<link rel="icon" type="image/png" href="https://lolisafe.moe/images/icons/favicon-32x32.png?v=XBreOJMe24" sizes="32x32">
<link rel="icon" type="image/png" href="https://lolisafe.moe/images/icons/favicon-16x16.png?v=XBreOJMe24" sizes="16x16">
<link rel="manifest" href="https://lolisafe.moe/images/icons/manifest.json?v=XBreOJMe24">
<link rel="mask-icon" href="https://lolisafe.moe/images/icons/safari-pinned-tab.svg?v=XBreOJMe24" color="#5bbad5">
<link rel="shortcut icon" href="https://lolisafe.moe/images/icons/favicon.ico?v=XBreOJMe24">
<meta name="apple-mobile-web-app-title" content="lolisafe">
<meta name="application-name" content="lolisafe">
<meta name="msapplication-config" content="https://lolisafe.moe/images/icons/browserconfig.xml?v=XBreOJMe24">
<meta name="theme-color" content="#ffffff">
<!-- Stylesheets and scripts -->
<link rel="stylesheet" type="text/css" href="libs/bulma/bulma.min.css">
<link rel="stylesheet" type="text/css" href="libs/sweetalert/sweetalert.min.css">
<link rel="stylesheet" type="text/css" href="css/style.css">
<script type="text/javascript" src="libs/sweetalert/sweetalert.min.js"></script>
<script type="text/javascript" src="libs/axios/axios.min.js"></script>
<script type="text/javascript" src="js/album.js"></script>
<meta property="og:url" content="https://lolisafe.moe" />
<!-- Open Graph tags -->
<meta property="og:type" content="website" />
<meta property="og:title" content="lolisafe.moe | A small safe worth protecting." />
<meta property="og:title" content="safe.fiery.me &#8211; A small safe worth protecting." />
<meta property="og:url" content="https://safe.fiery.me/" />
<meta property="og:description" content="A pomf-like file uploading service that doesn't suck." />
<meta property="og:image" content="http://lolisafe.moe/images/logo_square.png" />
<meta property="og:image:secure_url" content="https://lolisafe.moe/images/logo_square.png" />
<meta property="og:image" content="https://safe.fiery.me/icons/600px.png" />
<meta property="og:image:width" content="600" />
<meta property="og:image:height" content="600" />
<meta property="og:image" content="https://safe.fiery.me/images/fb_share.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:locale" content="en_US" />
<!-- Twitter Card tags -->
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="lolisafe.moe | A small safe worth protecting.">
<meta name="twitter:title" content="safe.fiery.me &#8211; A small safe worth protecting.">
<meta name="twitter:description" content="A pomf-like file uploading service that doesn't suck.">
<meta name="twitter:image" content="https://listen.moe/files/images/logo_square.png">
<meta name="twitter:image:src" content="https://lolisafe.moe/images/logo_square.png">
<meta name="twitter:image" content="https://safe.fiery.me/icons/600px.png">
<!-- Icons and configs -->
<link rel="icon" type="image/png" href="https://safe.fiery.me/icons/32pxr.png" sizes="32x32">
<link rel="icon" type="image/png" href="https://safe.fiery.me/icons/96pxr.png" sizes="96x96">
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/120px.png" sizes="120x120">
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/152px.png" sizes="152x152">
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/167px.png" sizes="167x167">
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/180px.png" sizes="180x180">
<link rel="manifest" href="https://safe.fiery.me/icons/manifest.json?v=hEjFGFcHY8">
<meta name="apple-mobile-web-app-title" content="safe.fiery.me">
<meta name="application-name" content="safe.fiery.me">
<meta name="msapplication-config" content="https://safe.fiery.me/icons/browserconfig.xml?v=hEjFGFcHY8">
<meta name="theme-color" content="#232629">
<title>lolisafe - A small safe worth protecting.</title>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.3.0/css/bulma.min.css">
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.css">
<link rel="stylesheet" type="text/css" href="/css/style.css">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.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="/js/album.js"></script>
</head>
<body>

View File

@ -2,52 +2,65 @@
<html>
<head>
<meta charset="utf-8" />
<meta name="description" content="A pomf-like file uploading service that doesn't suck.">
<meta name="keywords" content="upload,lolisafe,file,images,hosting">
<meta name="keywords" content="upload,lolisafe,file,images,hosting,bobby,fiery">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>safe.fiery.me &#8211; A small safe worth protecting.</title>
<link rel="apple-touch-icon" sizes="180x180" href="https://lolisafe.moe/images/icons/apple-touch-icon.png?v=XBreOJMe24">
<link rel="icon" type="image/png" href="https://lolisafe.moe/images/icons/favicon-32x32.png?v=XBreOJMe24" sizes="32x32">
<link rel="icon" type="image/png" href="https://lolisafe.moe/images/icons/favicon-16x16.png?v=XBreOJMe24" sizes="16x16">
<link rel="manifest" href="https://lolisafe.moe/images/icons/manifest.json?v=XBreOJMe24">
<link rel="mask-icon" href="https://lolisafe.moe/images/icons/safari-pinned-tab.svg?v=XBreOJMe24" color="#5bbad5">
<link rel="shortcut icon" href="https://lolisafe.moe/images/icons/favicon.ico?v=XBreOJMe24">
<meta name="apple-mobile-web-app-title" content="lolisafe">
<meta name="application-name" content="lolisafe">
<meta name="msapplication-config" content="https://lolisafe.moe/images/icons/browserconfig.xml?v=XBreOJMe24">
<meta name="theme-color" content="#ffffff">
<!-- Stylesheets and scripts -->
<link rel="stylesheet" type="text/css" href="libs/bulma/bulma.min.css">
<link rel="stylesheet" type="text/css" href="libs/sweetalert/sweetalert.min.css">
<link rel="stylesheet" type="text/css" href="libs/fontello/fontello.css">
<link rel="stylesheet" type="text/css" href="css/style.css">
<script type="text/javascript" src="libs/sweetalert/sweetalert.min.js"></script>
<script type="text/javascript" src="libs/axios/axios.min.js"></script>
<script type="text/javascript" src="js/auth.js"></script>
<meta property="og:url" content="https://lolisafe.moe" />
<!-- Open Graph tags -->
<meta property="og:type" content="website" />
<meta property="og:title" content="lolisafe.moe | A small safe worth protecting." />
<meta property="og:title" content="safe.fiery.me &#8211; A small safe worth protecting." />
<meta property="og:url" content="https://safe.fiery.me/" />
<meta property="og:description" content="A pomf-like file uploading service that doesn't suck." />
<meta property="og:image" content="http://lolisafe.moe/images/logo_square.png" />
<meta property="og:image:secure_url" content="https://lolisafe.moe/images/logo_square.png" />
<meta property="og:image" content="https://safe.fiery.me/icons/600px.png" />
<meta property="og:image:width" content="600" />
<meta property="og:image:height" content="600" />
<meta property="og:image" content="https://safe.fiery.me/images/fb_share.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:locale" content="en_US" />
<!-- Twitter Card tags -->
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="lolisafe.moe | A small safe worth protecting.">
<meta name="twitter:title" content="safe.fiery.me &#8211; A small safe worth protecting.">
<meta name="twitter:description" content="A pomf-like file uploading service that doesn't suck.">
<meta name="twitter:image" content="https://listen.moe/files/images/logo_square.png">
<meta name="twitter:image:src" content="https://lolisafe.moe/images/logo_square.png">
<meta name="twitter:image" content="https://safe.fiery.me/icons/600px.png">
<title>lolisafe - A small safe worth protecting.</title>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.3.0/css/bulma.min.css">
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.css">
<link rel="stylesheet" type="text/css" href="/css/style.css">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.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="/js/auth.js"></script>
</head>
<body>
<!-- Icons and configs -->
<link rel="icon" type="image/png" href="https://safe.fiery.me/icons/32pxr.png" sizes="32x32">
<link rel="icon" type="image/png" href="https://safe.fiery.me/icons/96pxr.png" sizes="96x96">
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/120px.png" sizes="120x120">
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/152px.png" sizes="152x152">
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/167px.png" sizes="167x167">
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/180px.png" sizes="180x180">
<link rel="manifest" href="https://safe.fiery.me/icons/manifest.json?v=hEjFGFcHY8">
<meta name="apple-mobile-web-app-title" content="safe.fiery.me">
<meta name="application-name" content="safe.fiery.me">
<meta name="msapplication-config" content="https://safe.fiery.me/icons/browserconfig.xml?v=hEjFGFcHY8">
<meta name="theme-color" content="#232629">
<style type="text/css">
<style>
/** Colors based on KDE Breeze Dark **/
section#login {
background-color: #f5f6f8;
background-color: #232629;
}
</style>
</head>
<body>
<section id='login' class="hero is-fullheight">
<div class="hero-body">
<div class="container">

View File

@ -2,44 +2,57 @@
<html>
<head>
<meta charset="utf-8" />
<meta name="description" content="A pomf-like file uploading service that doesn't suck.">
<meta name="keywords" content="upload,lolisafe,file,images,hosting">
<meta name="keywords" content="upload,lolisafe,file,images,hosting,bobby,fiery">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>safe.fiery.me &#8211; A small safe worth protecting.</title>
<link rel="apple-touch-icon" sizes="180x180" href="https://lolisafe.moe/images/icons/apple-touch-icon.png?v=XBreOJMe24">
<link rel="icon" type="image/png" href="https://lolisafe.moe/images/icons/favicon-32x32.png?v=XBreOJMe24" sizes="32x32">
<link rel="icon" type="image/png" href="https://lolisafe.moe/images/icons/favicon-16x16.png?v=XBreOJMe24" sizes="16x16">
<link rel="manifest" href="https://lolisafe.moe/images/icons/manifest.json?v=XBreOJMe24">
<link rel="mask-icon" href="https://lolisafe.moe/images/icons/safari-pinned-tab.svg?v=XBreOJMe24" color="#5bbad5">
<link rel="shortcut icon" href="https://lolisafe.moe/images/icons/favicon.ico?v=XBreOJMe24">
<meta name="apple-mobile-web-app-title" content="lolisafe">
<meta name="application-name" content="lolisafe">
<meta name="msapplication-config" content="https://lolisafe.moe/images/icons/browserconfig.xml?v=XBreOJMe24">
<meta name="theme-color" content="#ffffff">
<!-- Stylesheets and scripts -->
<link rel="stylesheet" type="text/css" href="libs/bulma/bulma.min.css">
<link rel="stylesheet" type="text/css" href="libs/sweetalert/sweetalert.min.css">
<link rel="stylesheet" type="text/css" href="libs/fontello/fontello.css">
<link rel="stylesheet" type="text/css" href="css/style.css">
<link rel="stylesheet" type="text/css" href="css/dashboard.css">
<script type="text/javascript" src="libs/sweetalert/sweetalert.min.js"></script>
<script type="text/javascript" src="libs/axios/axios.min.js"></script>
<script type="text/javascript" src="js/dashboard.js"></script>
<meta property="og:url" content="https://lolisafe.moe" />
<!-- Open Graph tags -->
<meta property="og:type" content="website" />
<meta property="og:title" content="lolisafe.moe | A small safe worth protecting." />
<meta property="og:title" content="safe.fiery.me &#8211; A small safe worth protecting." />
<meta property="og:url" content="https://safe.fiery.me/" />
<meta property="og:description" content="A pomf-like file uploading service that doesn't suck." />
<meta property="og:image" content="http://lolisafe.moe/images/logo_square.png" />
<meta property="og:image:secure_url" content="https://lolisafe.moe/images/logo_square.png" />
<meta property="og:image" content="https://safe.fiery.me/icons/600px.png" />
<meta property="og:image:width" content="600" />
<meta property="og:image:height" content="600" />
<meta property="og:image" content="https://safe.fiery.me/images/fb_share.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:locale" content="en_US" />
<!-- Twitter Card tags -->
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="lolisafe.moe | A small safe worth protecting.">
<meta name="twitter:title" content="safe.fiery.me &#8211; A small safe worth protecting.">
<meta name="twitter:description" content="A pomf-like file uploading service that doesn't suck.">
<meta name="twitter:image" content="https://listen.moe/files/images/logo_square.png">
<meta name="twitter:image:src" content="https://lolisafe.moe/images/logo_square.png">
<meta name="twitter:image" content="https://safe.fiery.me/icons/600px.png">
<!-- Icons and configs -->
<link rel="icon" type="image/png" href="https://safe.fiery.me/icons/32pxr.png" sizes="32x32">
<link rel="icon" type="image/png" href="https://safe.fiery.me/icons/96pxr.png" sizes="96x96">
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/120px.png" sizes="120x120">
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/152px.png" sizes="152x152">
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/167px.png" sizes="167x167">
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/180px.png" sizes="180x180">
<link rel="manifest" href="https://safe.fiery.me/icons/manifest.json?v=hEjFGFcHY8">
<meta name="apple-mobile-web-app-title" content="safe.fiery.me">
<meta name="application-name" content="safe.fiery.me">
<meta name="msapplication-config" content="https://safe.fiery.me/icons/browserconfig.xml?v=hEjFGFcHY8">
<meta name="theme-color" content="#232629">
<title>lolisafe - A small safe worth protecting.</title>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.3.0/css/bulma.min.css">
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.css">
<link rel="stylesheet" type="text/css" href="/css/style.css">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.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="/js/dashboard.js"></script>
</head>
<body>
<section id='auth' class="hero is-light is-fullheight">
@ -71,7 +84,7 @@
<aside class="menu" id="menu">
<p class="menu-label">General</p>
<ul class="menu-list">
<li><a href="/">Frontpage</a></li>
<li><a href=".">Frontpage</a></li>
<li><a id="itemUploads" onclick="panel.getUploads()">Uploads</a></li>
</ul>
<p class="menu-label">Albums</p>
@ -90,7 +103,7 @@
</aside>
</div>
<div class="column has-text-centered" id='page'>
<img src="/images/logo.png">
<img src="images/logo.png">
</div>
</div>
</div>

View File

@ -1,47 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>loli-safe</title>
<link href='//fonts.googleapis.com/css?family=Lato:100' rel='stylesheet' type='text/css'>
<style>
html, body {
height: 100%;
}
body {
margin: 0;
padding: 0;
width: 100%;
color: #B0BEC5;
display: table;
font-weight: 100;
font-family: 'Lato';
}
.container {
text-align: center;
display: table-cell;
vertical-align: middle;
}
.content {
text-align: center;
display: inline-block;
}
.title {
font-size: 72px;
margin-bottom: 40px;
}
</style>
</head>
<body>
<div class="container">
<div class="content">
<div class="title">Page not found.</div>
</div>
</div>
</body>
</html>

View File

@ -1,47 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>loli-safe</title>
<link href='//fonts.googleapis.com/css?family=Lato:100' rel='stylesheet' type='text/css'>
<style>
html, body {
height: 100%;
}
body {
margin: 0;
padding: 0;
width: 100%;
color: #B0BEC5;
display: table;
font-weight: 100;
font-family: 'Lato';
}
.container {
text-align: center;
display: table-cell;
vertical-align: middle;
}
.content {
text-align: center;
display: inline-block;
}
.title {
font-size: 72px;
margin-bottom: 40px;
}
</style>
</head>
<body>
<div class="container">
<div class="content">
<div class="title">Internal server error.</div>
</div>
</div>
</body>
</html>

View File

@ -1,49 +1,71 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="description" content="A pomf-like file uploading service that doesn't suck.">
<meta name="keywords" content="upload,lolisafe,file,images,hosting">
<meta name="keywords" content="upload,lolisafe,file,images,hosting,bobby,fiery">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>safe.fiery.me &#8211; A small safe worth protecting.</title>
<link rel="apple-touch-icon" sizes="180x180" href="https://lolisafe.moe/images/icons/apple-touch-icon.png?v=XBreOJMe24">
<link rel="icon" type="image/png" href="https://lolisafe.moe/images/icons/favicon-32x32.png?v=XBreOJMe24" sizes="32x32">
<link rel="icon" type="image/png" href="https://lolisafe.moe/images/icons/favicon-16x16.png?v=XBreOJMe24" sizes="16x16">
<link rel="manifest" href="https://lolisafe.moe/images/icons/manifest.json?v=XBreOJMe24">
<link rel="mask-icon" href="https://lolisafe.moe/images/icons/safari-pinned-tab.svg?v=XBreOJMe24" color="#5bbad5">
<link rel="shortcut icon" href="https://lolisafe.moe/images/icons/favicon.ico?v=XBreOJMe24">
<meta name="apple-mobile-web-app-title" content="lolisafe">
<meta name="application-name" content="lolisafe">
<meta name="msapplication-config" content="https://lolisafe.moe/images/icons/browserconfig.xml?v=XBreOJMe24">
<meta name="theme-color" content="#ffffff">
<!-- Stylesheets and scripts -->
<link rel="stylesheet" type="text/css" href="libs/bulma/bulma.min.css">
<link rel="stylesheet" type="text/css" href="css/style.css">
<meta property="og:url" content="https://lolisafe.moe" />
<!-- Open Graph tags -->
<meta property="og:type" content="website" />
<meta property="og:title" content="lolisafe.moe | A small safe worth protecting." />
<meta property="og:title" content="safe.fiery.me &#8211; A small safe worth protecting." />
<meta property="og:url" content="https://safe.fiery.me/" />
<meta property="og:description" content="A pomf-like file uploading service that doesn't suck." />
<meta property="og:image" content="http://lolisafe.moe/images/logo_square.png" />
<meta property="og:image:secure_url" content="https://lolisafe.moe/images/logo_square.png" />
<meta property="og:image" content="https://safe.fiery.me/icons/600px.png" />
<meta property="og:image:width" content="600" />
<meta property="og:image:height" content="600" />
<meta property="og:image" content="https://safe.fiery.me/images/fb_share.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:locale" content="en_US" />
<!-- Twitter Card tags -->
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="lolisafe.moe | A small safe worth protecting.">
<meta name="twitter:title" content="safe.fiery.me &#8211; A small safe worth protecting.">
<meta name="twitter:description" content="A pomf-like file uploading service that doesn't suck.">
<meta name="twitter:image" content="https://listen.moe/files/images/logo_square.png">
<meta name="twitter:image:src" content="https://lolisafe.moe/images/logo_square.png">
<meta name="twitter:image" content="https://safe.fiery.me/icons/600px.png">
<!-- Icons and configs -->
<link rel="icon" type="image/png" href="https://safe.fiery.me/icons/32pxr.png" sizes="32x32">
<link rel="icon" type="image/png" href="https://safe.fiery.me/icons/96pxr.png" sizes="96x96">
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/120px.png" sizes="120x120">
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/152px.png" sizes="152x152">
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/167px.png" sizes="167x167">
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/180px.png" sizes="180x180">
<link rel="manifest" href="https://safe.fiery.me/icons/manifest.json?v=hEjFGFcHY8">
<meta name="apple-mobile-web-app-title" content="safe.fiery.me">
<meta name="application-name" content="safe.fiery.me">
<meta name="msapplication-config" content="https://safe.fiery.me/icons/browserconfig.xml?v=hEjFGFcHY8">
<meta name="theme-color" content="#232629">
<style>
/** Colors based on KDE Breeze Dark **/
.message-body {
color: #eff0f1;
background-color: #31363b;
border-color: #898b8d;
}
</style>
<title>lolisafe - A small safe worth protecting.</title>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.3.0/css/bulma.min.css">
<link rel="stylesheet" type="text/css" href="/css/style.css">
</head>
<body>
<section class="hero is-fullheight has-text-centered" id="home">
<div class="hero-body">
<div class="container has-text-left">
<h2 class='subtitle'>What is lolisafe?</h2>
<h2 class='subtitle'>What is safe.fiery.me?</h2>
<article class="message">
<div class="message-body">
lolisafe is an easy to use, open source and completely free file upload service. We accept your files, photos, documents, anything, and give you back a shareable link for you to send to others.
safe.fiery.me is merely another clone of lolisafe. lolisafe itself is an easy to use, open source and completely free file upload service. We accept your files, photos, documents, anything, and give you back a shareable link for you to send to others. On a side note, this site was originally made for personal usage but I disabled private mode in the end, so yeah, feel free..
</div>
</article>
@ -64,14 +86,23 @@
<h2 class='subtitle'>What are albums?</h2>
<article class="message">
<div class="message-body">
Albums are a simple way of sorting uploads together. Right now you can create albums through the dashboard and use them only with <a target="_blank" href="https://chrome.google.com/webstore/detail/loli-safe-uploader/enkkmplljfjppcdaancckgilmgoiofnj">our chrome extension</a> which will enable you to <strong>right click -> send to lolisafe</strong> or to a desired album if you have any.
Albums are a simple way of sorting uploads together. Right now you can create albums through the dashboard and use them <s>only</s>* with <a target="_blank" href="https://chrome.google.com/webstore/detail/loli-safe-uploader/enkkmplljfjppcdaancckgilmgoiofnj">our chrome extension</a> which will enable you to <strong>right click -> send to lolisafe</strong> or to a desired album if you have any. You will probably have to change some things involving https://safe.fiery.me/api/upload.<br>
<br>
* You are no longer required to use the chrome extension to utilize albums. As long as you are logged in, you will be able to choose your albums from the upload page (of course the albums had to be created first from the dashboard).
</div>
</article>
<h2 class='subtitle'>Why should I use this?</h2>
<article class="message">
<div class="message-body">
There are too many file upload services out there, and a lot of them rely on the foundations of pomf which is ancient. In a desperate and unsuccessful attempt of finding a good file uploader that's easily extendable, lolisafe was born. We give you control over your files, we give you a way to sort your uploads into albums for ease of access and we give you an api to use with ShareX or any other thing that let's you make POST requests. Awesome isn't it? Just like you.
I don't know ¯\_(ツ)_/¯.
</div>
</article>
<h2 class='subtitle'>I saw something too illegal for my tastes here, what do?</h2>
<article class="message">
<div class="message-body">
Send a strongly worded email to <a href="mailto:bobby@fiery.me">bobby@fiery.me</a> and I will <i>try</i> to get back to you within 24 hours.
</div>
</article>

View File

@ -1,60 +1,73 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="description" content="A pomf-like file uploading service that doesn't suck.">
<meta name="keywords" content="upload,lolisafe,file,images,hosting">
<meta name="keywords" content="upload,lolisafe,file,images,hosting,bobby,fiery">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>safe.fiery.me &#8211; A small safe worth protecting.</title>
<link rel="apple-touch-icon" sizes="180x180" href="https://lolisafe.moe/images/icons/apple-touch-icon.png?v=XBreOJMe24">
<link rel="icon" type="image/png" href="https://lolisafe.moe/images/icons/favicon-32x32.png?v=XBreOJMe24" sizes="32x32">
<link rel="icon" type="image/png" href="https://lolisafe.moe/images/icons/favicon-16x16.png?v=XBreOJMe24" sizes="16x16">
<link rel="manifest" href="https://lolisafe.moe/images/icons/manifest.json?v=XBreOJMe24">
<link rel="mask-icon" href="https://lolisafe.moe/images/icons/safari-pinned-tab.svg?v=XBreOJMe24" color="#5bbad5">
<link rel="shortcut icon" href="https://lolisafe.moe/images/icons/favicon.ico?v=XBreOJMe24">
<meta name="apple-mobile-web-app-title" content="lolisafe">
<meta name="application-name" content="lolisafe">
<meta name="msapplication-config" content="https://lolisafe.moe/images/icons/browserconfig.xml?v=XBreOJMe24">
<meta name="theme-color" content="#ffffff">
<!-- Stylesheets and scripts -->
<link rel="stylesheet" type="text/css" href="libs/bulma/bulma.min.css">
<link rel="stylesheet" type="text/css" href="libs/sweetalert/sweetalert.min.css">
<link rel="stylesheet" type="text/css" href="css/style.css">
<script type="text/javascript" src="libs/sweetalert/sweetalert.min.js"></script>
<script type="text/javascript" src="libs/dropzone/dropzone.min.js"></script>
<script type="text/javascript" src="libs/axios/axios.min.js"></script>
<script type="text/javascript" src="js/home.js"></script>
<meta property="og:url" content="https://lolisafe.moe" />
<!-- Open Graph tags -->
<meta property="og:type" content="website" />
<meta property="og:title" content="lolisafe.moe | A small safe worth protecting." />
<meta property="og:title" content="safe.fiery.me &#8211; A small safe worth protecting." />
<meta property="og:url" content="https://safe.fiery.me/" />
<meta property="og:description" content="A pomf-like file uploading service that doesn't suck." />
<meta property="og:image" content="http://lolisafe.moe/images/logo_square.png" />
<meta property="og:image:secure_url" content="https://lolisafe.moe/images/logo_square.png" />
<meta property="og:image" content="https://safe.fiery.me/icons/600px.png" />
<meta property="og:image:width" content="600" />
<meta property="og:image:height" content="600" />
<meta property="og:image" content="https://safe.fiery.me/images/fb_share.png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:locale" content="en_US" />
<!-- Twitter Card tags -->
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="lolisafe.moe | A small safe worth protecting.">
<meta name="twitter:title" content="safe.fiery.me &#8211; A small safe worth protecting.">
<meta name="twitter:description" content="A pomf-like file uploading service that doesn't suck.">
<meta name="twitter:image" content="https://listen.moe/files/images/logo_square.png">
<meta name="twitter:image:src" content="https://lolisafe.moe/images/logo_square.png">
<meta name="twitter:image" content="https://safe.fiery.me/icons/600px.png">
<!-- Icons and configs -->
<link rel="icon" type="image/png" href="https://safe.fiery.me/icons/32pxr.png" sizes="32x32">
<link rel="icon" type="image/png" href="https://safe.fiery.me/icons/96pxr.png" sizes="96x96">
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/120px.png" sizes="120x120">
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/152px.png" sizes="152x152">
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/167px.png" sizes="167x167">
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/180px.png" sizes="180x180">
<link rel="manifest" href="https://safe.fiery.me/icons/manifest.json?v=hEjFGFcHY8">
<meta name="apple-mobile-web-app-title" content="safe.fiery.me">
<meta name="application-name" content="safe.fiery.me">
<meta name="msapplication-config" content="https://safe.fiery.me/icons/browserconfig.xml?v=hEjFGFcHY8">
<meta name="theme-color" content="#232629">
<title>lolisafe - A small safe worth protecting.</title>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.3.0/css/bulma.min.css">
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.css">
<link rel="stylesheet" type="text/css" href="/css/style.css">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/4.3.0/min/dropzone.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="/js/home.js"></script>
</head>
<body>
<section class="hero is-fullheight has-text-centered" id="home">
<div class="hero-body">
<div class="container">
<p id="b">
<img class="logo" src="/images/logo_smol.png">
<img class="logo" src="images/logo_smol.png">
</p>
<h1 class="title">lolisafe</h1>
<h1 class="title">safe.fiery.me</h1>
<h2 class="subtitle">A <strong>modern</strong> self-hosted file upload service</h2>
<h3 class="subtitle" id="maxFileSize"></h3>
<div class="columns">
<div class="column is-hidden-mobile"></div>
<div class="column" id="uploadContainer">
<a id="loginToUpload" href="/auth" class="button is-danger">Running in private mode. Log in to upload.</a>
<a id="loginToUpload" href="auth" class="button is-danger">Loading&hellip;</a>
<div class="field" id="albumDiv" style="display: none">
<p class="control select-wrapper">
<span class="select">
@ -80,9 +93,9 @@
</div>
</div>
<h3 class="subtitle"><a href="/auth" id="loginLinkText"></a></h3>
<h3 class="subtitle"><a href="auth" id="loginLinkText"></a></h3>
<h3 id="links">
<a href="https://github.com/kanadeko/loli-safe" target="_blank" class="is-danger">View on GitHub</a><span>|</span><a id="ShareX" href="https://lolisafe.moe/sharex.txt">ShareX</a><span>|</span><a href="https://chrome.google.com/webstore/detail/loli-safe-uploader/enkkmplljfjppcdaancckgilmgoiofnj" target="_blank" class="is-danger">Chrome extension</a><span>|</span><a href="/faq" class="is-danger">FAQ</a><span>|</span><a href="/auth" target="_blank" class="is-danger">Dashboard</a>
<a href="https://fiery.me" class="is-danger">Home</a><span>|</span><a href="https://blog.fiery.me" class="is-danger">Blog</a><span>|</span><a id="ShareX" href="https://safe.fiery.me/sharex.txt">ShareX</a><span>|</span><a href="faq" class="is-danger">FAQ</a><span>|</span><a href="auth" class="is-danger">Dashboard</a><span>|</span><a href="https://github.com/BobbyWibowo/lolisafe" target="_blank" class="is-danger">View on GitHub</a>
</h3>
</div>

80
public/css/dashboard.css Normal file
View File

@ -0,0 +1,80 @@
/** Colors based on KDE Breeze Dark **/
html {
background-color: #232629;
}
.section {
background: none;
}
.menu-list a {
color: #2980b9;
}
.menu-list a:hover {
color: #3daee9;
background-color: #4d4d4d;
}
.menu-list a.is-active {
color: #eff0f1;
background-color: #2980b9;
}
.button.is-primary {
background-color: #2980b9;
}
.button.is-primary.is-hovered, .button.is-primary:hover {
background-color: #2980b9;
}
.pagination a {
color: #eff0f1;
border-color: #4d4d4d;
background-color: #31363b;
}
.pagination-link:hover, .pagination-next:hover, .pagination-previous:hover {
color: #eff0f1;
border-color: #3daee9;
background-color: #31363b;
}
.label {
color: #bdc3c7;
}
.table {
color: #bdc3c7;
background-color: #31363b;
}
.table tr:hover, .table.is-striped tbody tr:nth-child(2n) {
background: none;
}
.table.is-striped tbody tr:hover, .table.is-striped tbody tr:nth-child(2n):hover, .tag {
background-color: #4d4d4d;
}
.table thead td, .table thead th {
color: #eff0f1;
background-color: #ff3860;
}
.table th {
color: #eff0f1;
}
.table td, .table th {
border: 0;
}
section#dashboard div#table div.column {
background-color: #31363b;
}
.menu-list li ul {
border-left-color: #898b8d;
}

View File

@ -110,3 +110,48 @@ section#dashboard div#table div.column a img { width:200px; }
text-align: center;
margin-bottom: 10px;
}
/** Colors based on KDE Breeze Dark **/
.hero {
background-color: #232629;
color: #eff0f1;
}
.title {
color: #eff0f1;
}
.subtitle {
color: #bdc3c7;
}
.subtitle strong {
color: #bdc3c7;
}
a {
color: #2980b9;
}
a:hover {
color: #3daee9;
}
section#home h3#links span {
color: #898b8d;
}
section#home #b {
width: 200px;
height: 200px;
border-radius: 100%;
}
hr {
background-color: #898b8d;
}
.table-container {
overflow-x: auto;
}

BIN
public/icons/120px.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
public/icons/152px.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
public/icons/167px.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

BIN
public/icons/180px.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
public/icons/192px.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

BIN
public/icons/270px.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

BIN
public/icons/310px.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

BIN
public/icons/310pxw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
public/icons/32pxr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
public/icons/384px.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

BIN
public/icons/600px.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 KiB

BIN
public/icons/600pxr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 KiB

BIN
public/icons/70px.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

BIN
public/icons/96pxr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square70x70logo src="https://safe.fiery.me/icons/70px.png"/>
<square150x150logo src="https://safe.fiery.me/icons/270px.png"/>
<square310x310logo src="https://safe.fiery.me/icons/310px.png"/>
<wide310x150logo src="https://safe.fiery.me/icons/310pxw.png"/>
<TileColor>#232629</TileColor>
</tile>
</msapplication>
</browserconfig>

View File

@ -0,0 +1,21 @@
{
"name": "safe.fiery.me",
"description": "A pomf-like file uploading service that doesn't suck.",
"icons": [
{
"src": "/icons/192px.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/384px.png",
"sizes": "384x384",
"type": "image/png"
}
],
"theme_color": "#232629",
"background_color": "#232629",
"display": "standalone",
"orientation": "any",
"lang": "en-US"
}

BIN
public/icons/xcf/600px.xcf Normal file

Binary file not shown.

BIN
public/icons/xcf/600pxr.xcf Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/images/icons/mstile-150x150.png?v=XBreOJMe24"/>
<TileColor>#00aba9</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 920 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,18 +0,0 @@
{
"name": "lolisafe",
"icons": [
{
"src": "/images/icons/android-chrome-192x192.png?v=XBreOJMe24",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/images/icons/android-chrome-384x384.png?v=XBreOJMe24",
"sizes": "384x384",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -1,47 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="16.000000pt" height="16.000000pt" viewBox="0 0 16.000000 16.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.11, written by Peter Selinger 2001-2013
</metadata>
<g transform="translate(0.000000,16.000000) scale(0.003765,-0.003765)"
fill="#000000" stroke="none">
<path d="M2397 4143 c-3 -3 -19 -7 -34 -8 -162 -16 -259 -56 -351 -144 -65
-64 -123 -152 -156 -239 -22 -61 -27 -80 -40 -148 -6 -29 -18 -161 -20 -209
-1 -40 -2 -40 -81 -80 l-81 -40 58 -50 c32 -28 70 -55 85 -62 l28 -11 -25 -10
-25 -10 29 -13 28 -13 -6 -106 c-3 -58 -7 -114 -9 -125 -3 -11 -8 -45 -11 -76
-4 -31 -9 -67 -11 -80 -3 -13 -12 -60 -20 -104 -21 -108 -99 -327 -177 -497
-29 -64 -32 -67 -62 -64 -17 2 -36 4 -42 5 -25 2 -95 171 -108 259 -9 62 -14
55 -73 -98 -35 -89 -48 -133 -59 -190 -2 -14 -6 -34 -9 -45 -21 -99 -22 -255
-2 -370 8 -44 14 -81 13 -82 -5 -6 -97 76 -131 117 -47 57 -97 151 -111 205
-30 123 -36 217 -19 300 8 41 -6 24 -48 -60 -36 -72 -72 -169 -81 -221 -3 -16
-10 -44 -16 -63 -10 -36 -7 -255 4 -304 4 -16 15 -51 26 -79 11 -28 17 -54 15
-58 -14 -22 -177 11 -235 48 l-25 16 16 -25 c27 -42 141 -127 204 -153 33 -14
86 -32 117 -41 48 -14 58 -20 63 -44 3 -15 31 -65 61 -112 31 -46 68 -109 84
-139 15 -31 41 -67 57 -80 16 -14 45 -45 64 -70 19 -25 37 -47 40 -50 3 -3 34
-43 69 -90 109 -146 240 -259 412 -353 25 -14 65 -38 89 -53 48 -30 175 -79
229 -88 117 -19 211 -37 250 -49 47 -14 85 -14 160 -3 131 21 171 87 171 281
-1 73 -3 93 -12 110 -4 6 -9 22 -13 37 -6 26 -54 109 -159 278 -26 41 -55 85
-63 97 -14 20 -14 23 -2 23 9 0 32 -21 52 -47 99 -128 366 -377 506 -473 12
-8 43 -30 68 -49 38 -29 57 -35 116 -41 128 -13 166 5 214 103 81 161 76 270
-19 438 -23 41 -45 76 -48 79 -3 3 -31 34 -61 70 -30 36 -57 67 -61 70 -15 13
-169 222 -169 231 0 6 7 23 15 38 26 51 56 185 61 271 3 47 7 93 9 103 5 27
-8 20 -38 -22 -15 -22 -45 -54 -67 -71 l-40 -32 -1 89 c0 48 -3 81 -7 73 -11
-25 -41 -64 -46 -59 -2 2 -7 33 -11 69 -7 65 -7 66 22 80 15 7 30 14 33 14 3
1 26 13 52 28 56 34 121 97 157 153 22 35 30 41 47 34 47 -17 103 -115 91
-160 -6 -22 8 -26 17 -4 3 8 8 36 12 63 10 79 -28 143 -98 165 -15 5 -15 10
-4 49 31 104 15 279 -35 384 -19 40 -29 78 -29 107 0 48 -12 61 -43 45 -9 -5
-23 -5 -31 -1 -31 19 -74 61 -91 91 -13 21 -35 38 -62 48 -24 10 -42 22 -40
28 3 6 9 10 15 9 9 -2 52 84 67 137 4 14 25 42 47 62 22 20 37 42 33 48 -3 5
-11 7 -17 4 -6 -4 -6 1 1 14 6 12 16 24 22 27 6 4 23 25 39 47 29 40 29 41 10
57 -17 13 -18 22 -11 54 5 21 7 50 6 65 -2 15 -6 52 -9 82 -10 114 -77 279
-158 387 -46 63 -167 162 -247 202 -97 49 -263 91 -276 70 -4 -5 -13 -5 -24 1
-10 5 -21 7 -24 3z"/>
<path d="M3111 3011 c-13 -13 -21 -35 -22 -58 0 -21 -1 -53 -2 -71 -1 -17 2
-32 6 -32 4 0 29 11 55 24 74 37 87 86 27 98 -12 2 -19 10 -17 20 1 8 -4 21
-12 27 -12 10 -19 8 -35 -8z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 KiB

After

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 MiB

After

Width:  |  Height:  |  Size: 466 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

After

Width:  |  Height:  |  Size: 269 KiB

View File

@ -1,56 +1,56 @@
var page = {};
/* global swal, axios */
var page = {}
page.do = function (dest) {
var user = document.getElementById('user').value
var pass = document.getElementById('pass').value
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')
}
if (pass === undefined || pass === null || pass === '') {
return swal('Error', 'You need to specify a username', 'error')
}
if(user === undefined || user === null || user === '')
return swal('Error', 'You need to specify a username', 'error');
if(pass === undefined || pass === null || pass === '')
return swal('Error', 'You need to specify a username', 'error');
axios.post('/api/' + dest, {
axios.post('api/' + dest, {
username: user,
password: pass
})
.then(function (response) {
if (response.data.success === false) {
return swal('Error', response.data.description, 'error')
}
if(response.data.success === false)
return swal('Error', response.data.description, 'error');
localStorage.token = response.data.token;
window.location = '/dashboard';
localStorage.token = response.data.token
window.location = 'dashboard'
})
.catch(function (error) {
return swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error');
console.log(error);
});
console.log(error)
return swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error')
})
}
page.verify = function () {
page.token = localStorage.token;
if(page.token === undefined) return;
page.token = localStorage.token
if (page.token === undefined) return
axios.post('/api/tokens/verify', {
axios.post('api/tokens/verify', {
token: page.token
})
.then(function (response) {
if (response.data.success === false) {
return swal('Error', response.data.description, 'error')
}
if(response.data.success === false)
return swal('Error', response.data.description, 'error');
window.location = '/dashboard';
window.location = 'dashboard'
})
.catch(function (error) {
return swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error');
console.log(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();
page.verify()
}

View File

@ -1,129 +1,129 @@
/* eslint-disable no-unused-expressions */
/* global swal, axios */
let panel = {}
panel.page;
panel.username;
panel.token = localStorage.token;
panel.filesView = localStorage.filesView;
panel.page
panel.username
panel.token = localStorage.token
panel.filesView = localStorage.filesView
panel.preparePage = function () {
if(!panel.token) return window.location = '/auth';
panel.verifyToken(panel.token, true);
if (!panel.token) {
window.location = 'auth'
}
panel.verifyToken(panel.token, true)
}
panel.verifyToken = function (token, reloadOnError) {
if(reloadOnError === undefined)
reloadOnError = false;
if (reloadOnError === undefined) {
reloadOnError = false
}
axios.post('/api/tokens/verify', {
axios.post('api/tokens/verify', {
token: token
})
.then(function (response) {
if (response.data.success === false) {
swal({
title: "An error ocurred",
title: 'An error ocurred',
text: response.data.description,
type: "error"
type: 'error'
}, function () {
if (reloadOnError) {
localStorage.removeItem("token");
location.location = '/auth';
localStorage.removeItem('token')
location.location = 'auth'
}
})
return;
return
}
axios.defaults.headers.common['token'] = token;
localStorage.token = token;
panel.token = token;
panel.username = response.data.username;
return panel.prepareDashboard();
axios.defaults.headers.common.token = token
localStorage.token = token
panel.token = token
panel.username = response.data.username
return panel.prepareDashboard()
})
.catch(function (error) {
return swal("An error ocurred", 'There was an error with the request, please check the console for more information.', "error");
console.log(error);
});
console.log(error)
return swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error')
})
}
panel.prepareDashboard = function () {
panel.page = document.getElementById('page');
document.getElementById('auth').style.display = 'none';
document.getElementById('dashboard').style.display = 'block';
panel.page = document.getElementById('page')
document.getElementById('auth').style.display = 'none'
document.getElementById('dashboard').style.display = 'block'
document.getElementById('itemUploads').addEventListener('click', function () {
panel.setActiveMenu(this);
});
panel.setActiveMenu(this)
})
document.getElementById('itemManageGallery').addEventListener('click', function () {
panel.setActiveMenu(this);
});
panel.setActiveMenu(this)
})
document.getElementById('itemTokens').addEventListener('click', function () {
panel.setActiveMenu(this);
});
panel.setActiveMenu(this)
})
document.getElementById('itemPassword').addEventListener('click', function () {
panel.setActiveMenu(this);
});
panel.setActiveMenu(this)
})
document.getElementById('itemLogout').innerHTML = `Logout ( ${panel.username} )`;
document.getElementById('itemLogout').innerHTML = `Logout ( ${panel.username} )`
panel.getAlbumsSidebar();
panel.getAlbumsSidebar()
}
panel.logout = function () {
localStorage.removeItem("token");
location.reload('/');
localStorage.removeItem('token')
location.reload('.')
}
panel.getUploads = function (album = undefined, page = undefined) {
if (page === undefined) page = 0
if(page === undefined) page = 0;
let url = '/api/uploads/' + page
if(album !== undefined)
url = '/api/album/' + album + '/' + page
let url = 'api/uploads/' + page
if (album !== undefined) { url = 'api/album/' + album + '/' + page }
axios.get(url).then(function (response) {
if (response.data.success === false) {
if(response.data.description === 'No token provided') return panel.verifyToken(panel.token);
else return swal("An error ocurred", response.data.description, "error");
if (response.data.description === 'No token provided') return panel.verifyToken(panel.token)
else return swal('An error ocurred', response.data.description, 'error')
}
var prevPage = 0;
var nextPage = page + 1;
var prevPage = 0
var nextPage = page + 1
if(response.data.files.length < 25)
nextPage = page;
if (response.data.files.length < 25) { nextPage = page }
if(page > 0) prevPage = page - 1;
if (page > 0) prevPage = page - 1
panel.page.innerHTML = '';
var container = document.createElement('div');
panel.page.innerHTML = ''
var container = document.createElement('div')
var pagination = `<nav class="pagination is-centered">
<a class="pagination-previous" onclick="panel.getUploads(${album}, ${prevPage} )">Previous</a>
<a class="pagination-next" onclick="panel.getUploads(${album}, ${nextPage} )">Next page</a>
</nav>`;
</nav>`
var listType = `
<div class="columns">
<div class="column">
<a class="button is-small is-outlined is-danger" title="List view" onclick="panel.setFilesView('list', ${album}, ${page})">
<span class="icon is-small">
<i class="fa fa-list-ul"></i>
<i class="fa icon-list-bullet"></i>
</span>
</a>
<a class="button is-small is-outlined is-danger" title="List view" onclick="panel.setFilesView('thumbs', ${album}, ${page})">
<span class="icon is-small">
<i class="fa fa-th-large"></i>
<i class="fa icon-th-large"></i>
</span>
</a>
</div>
</div>`
var table, item
if (panel.filesView === 'thumbs') {
container.innerHTML = `
${pagination}
<hr>
@ -132,33 +132,30 @@ panel.getUploads = function(album = undefined, page = undefined){
</div>
${pagination}
`;
`
panel.page.appendChild(container);
var table = document.getElementById('table');
for(var item of response.data.files){
var div = document.createElement('div');
div.className = "column is-2";
if(item.thumb !== undefined)
div.innerHTML = `<a href="${item.file}" target="_blank"><img src="${item.thumb}"/></a>`;
else
div.innerHTML = `<a href="${item.file}" target="_blank"><h1 class="title">.${item.file.split('.').pop()}</h1></a>`;
table.appendChild(div);
}
panel.page.appendChild(container)
table = document.getElementById('table')
for (item of response.data.files) {
var div = document.createElement('div')
div.className = 'column is-2'
if (item.thumb !== undefined) {
div.innerHTML = `<a href="${item.file}" target="_blank"><img src="${item.thumb}"/></a>`
} else {
var albumOrUser = 'Album';
if(panel.username === 'root')
albumOrUser = 'User';
div.innerHTML = `<a href="${item.file}" target="_blank"><h1 class="title">.${item.file.split('.').pop()}</h1></a>`
}
table.appendChild(div)
}
} else {
var albumOrUser = 'Album'
if (panel.username === 'root') { albumOrUser = 'User' }
container.innerHTML = `
${pagination}
<hr>
${listType}
<div class="table-container">
<table class="table is-striped is-narrow is-left">
<thead>
<tr>
@ -171,22 +168,22 @@ panel.getUploads = function(album = undefined, page = undefined){
<tbody id="table">
</tbody>
</table>
</div>
<hr>
${pagination}
`;
`
panel.page.appendChild(container);
var table = document.getElementById('table');
panel.page.appendChild(container)
table = document.getElementById('table')
for(var item of response.data.files){
for (item of response.data.files) {
var tr = document.createElement('tr')
var tr = document.createElement('tr');
var displayAlbumOrUser = item.album;
var displayAlbumOrUser = item.album
console.log(item)
if (panel.username === 'root') {
displayAlbumOrUser = '';
if(item.username !== undefined)
displayAlbumOrUser = item.username;
displayAlbumOrUser = ''
if (item.username !== undefined) { displayAlbumOrUser = item.username }
}
tr.innerHTML = `
@ -197,77 +194,70 @@ panel.getUploads = function(album = undefined, page = undefined){
<td>
<a class="button is-small is-danger is-outlined" title="Delete album" onclick="panel.deleteFile(${item.id})">
<span class="icon is-small">
<i class="fa fa-trash-o"></i>
<i class="fa icon-trash"></i>
</span>
</a>
</td>
</tr>
`;
`
table.appendChild(tr);
table.appendChild(tr)
}
}
})
.catch(function (error) {
return swal("An error ocurred", 'There was an error with the request, please check the console for more information.', "error");
console.log(error);
});
console.log(error)
return swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error')
})
}
panel.setFilesView = function (view, album, page) {
localStorage.filesView = view;
panel.filesView = view;
panel.getUploads(album, page);
localStorage.filesView = view
panel.filesView = view
panel.getUploads(album, page)
}
panel.deleteFile = function (id) {
swal({
title: "Are you sure?",
text: "You wont be able to recover the file!",
type: "warning",
title: 'Are you sure?',
text: 'You wont be able to recover the file!',
type: 'warning',
showCancelButton: true,
confirmButtonColor: "#ff3860",
confirmButtonText: "Yes, delete it!",
confirmButtonColor: '#ff3860',
confirmButtonText: 'Yes, delete it!',
closeOnConfirm: false
},
function () {
axios.post('/api/upload/delete', {
axios.post('api/upload/delete', {
id: id
})
.then(function (response) {
if (response.data.success === false) {
if(response.data.description === 'No token provided') return panel.verifyToken(panel.token);
else return swal("An error ocurred", response.data.description, "error");
if (response.data.description === 'No token provided') return panel.verifyToken(panel.token)
else return swal('An error ocurred', response.data.description, 'error')
}
swal("Deleted!", "The file has been deleted.", "success");
panel.getUploads();
return;
swal('Deleted!', 'The file has been deleted.', 'success')
panel.getUploads()
})
.catch(function (error) {
return swal("An error ocurred", 'There was an error with the request, please check the console for more information.', "error");
console.log(error);
});
console.log(error)
return swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error')
})
}
);
)
}
panel.getAlbums = function () {
axios.get('/api/albums').then(function (response) {
axios.get('api/albums').then(function (response) {
if (response.data.success === false) {
if(response.data.description === 'No token provided') return panel.verifyToken(panel.token);
else return swal("An error ocurred", response.data.description, "error");
if (response.data.description === 'No token provided') return panel.verifyToken(panel.token)
else return swal('An error ocurred', response.data.description, 'error')
}
panel.page.innerHTML = '';
var container = document.createElement('div');
container.className = "container";
panel.page.innerHTML = ''
var container = document.createElement('div')
container.className = 'container'
container.innerHTML = `
<h2 class="subtitle">Create new album</h2>
@ -290,14 +280,13 @@ panel.getAlbums = function(){
</thead>
<tbody id="table">
</tbody>
</table>`;
</table>`
panel.page.appendChild(container);
var table = document.getElementById('table');
panel.page.appendChild(container)
var table = document.getElementById('table')
for (var item of response.data.albums) {
var tr = document.createElement('tr');
var tr = document.createElement('tr')
tr.innerHTML = `
<tr>
<th>${item.name}</th>
@ -307,196 +296,172 @@ panel.getAlbums = function(){
<td>
<a class="button is-small is-primary is-outlined" title="Edit name" onclick="panel.renameAlbum(${item.id})">
<span class="icon is-small">
<i class="fa fa-pencil"></i>
<i class="fa icon-pencil"></i>
</span>
</a>
<a class="button is-small is-danger is-outlined" title="Delete album" onclick="panel.deleteAlbum(${item.id})">
<span class="icon is-small">
<i class="fa fa-trash-o"></i>
<i class="fa icon-trash"></i>
</span>
</a>
</td>
</tr>
`;
`
table.appendChild(tr);
table.appendChild(tr)
}
document.getElementById('submitAlbum').addEventListener('click', function () {
panel.submitAlbum();
});
panel.submitAlbum()
})
})
.catch(function (error) {
return swal("An error ocurred", 'There was an error with the request, please check the console for more information.', "error");
console.log(error);
});
console.log(error)
return swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error')
})
}
panel.renameAlbum = function (id) {
swal({
title: "Rename album",
text: "New name you want to give the album:",
type: "input",
title: 'Rename album',
text: 'New name you want to give the album:',
type: 'input',
showCancelButton: true,
closeOnConfirm: false,
animation: "slide-from-top",
inputPlaceholder: "My super album"
animation: 'slide-from-top',
inputPlaceholder: 'My super album'
}, function (inputValue) {
if (inputValue === false) return false;
if (inputValue === "") {
swal.showInputError("You need to write something!");
if (inputValue === false) return false
if (inputValue === '') {
swal.showInputError('You need to write something!')
return false
}
axios.post('/api/albums/rename', {
axios.post('api/albums/rename', {
id: id,
name: inputValue
})
.then(function (response) {
if (response.data.success === false) {
if(response.data.description === 'No token provided') return panel.verifyToken(panel.token);
else if(response.data.description === 'Name already in use') swal.showInputError("That name is already in use!");
else swal("An error ocurred", response.data.description, "error");
return;
if (response.data.description === 'No token provided') return panel.verifyToken(panel.token)
else if (response.data.description === 'Name already in use') swal.showInputError('That name is already in use!')
else swal('An error ocurred', response.data.description, 'error')
return
}
swal("Success!", "Your album was renamed to: " + inputValue, "success");
panel.getAlbumsSidebar();
panel.getAlbums();
return;
swal('Success!', 'Your album was renamed to: ' + inputValue, 'success')
panel.getAlbumsSidebar()
panel.getAlbums()
})
.catch(function (error) {
return swal("An error ocurred", 'There was an error with the request, please check the console for more information.', "error");
console.log(error);
});
});
console.log(error)
return swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error')
})
})
}
panel.deleteAlbum = function (id) {
swal({
title: "Are you sure?",
title: 'Are you sure?',
text: "This won't delete your files, only the album!",
type: "warning",
type: 'warning',
showCancelButton: true,
confirmButtonColor: "#ff3860",
confirmButtonText: "Yes, delete it!",
confirmButtonColor: '#ff3860',
confirmButtonText: 'Yes, delete it!',
closeOnConfirm: false
},
function () {
axios.post('/api/albums/delete', {
axios.post('api/albums/delete', {
id: id
})
.then(function (response) {
if (response.data.success === false) {
if(response.data.description === 'No token provided') return panel.verifyToken(panel.token);
else return swal("An error ocurred", response.data.description, "error");
if (response.data.description === 'No token provided') return panel.verifyToken(panel.token)
else return swal('An error ocurred', response.data.description, 'error')
}
swal("Deleted!", "Your album has been deleted.", "success");
panel.getAlbumsSidebar();
panel.getAlbums();
return;
swal('Deleted!', 'Your album has been deleted.', 'success')
panel.getAlbumsSidebar()
panel.getAlbums()
})
.catch(function (error) {
return swal("An error ocurred", 'There was an error with the request, please check the console for more information.', "error");
console.log(error);
});
console.log(error)
return swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error')
})
}
);
)
}
panel.submitAlbum = function () {
axios.post('/api/albums', {
axios.post('api/albums', {
name: document.getElementById('albumName').value
})
.then(function (response) {
if (response.data.success === false) {
if(response.data.description === 'No token provided') return panel.verifyToken(panel.token);
else return swal("An error ocurred", response.data.description, "error");
if (response.data.description === 'No token provided') return panel.verifyToken(panel.token)
else return swal('An error ocurred', response.data.description, 'error')
}
swal("Woohoo!", "Album was added successfully", "success");
panel.getAlbumsSidebar();
panel.getAlbums();
return;
swal('Woohoo!', 'Album was added successfully', 'success')
panel.getAlbumsSidebar()
panel.getAlbums()
})
.catch(function (error) {
return swal("An error ocurred", 'There was an error with the request, please check the console for more information.', "error");
console.log(error);
});
console.log(error)
return swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error')
})
}
panel.getAlbumsSidebar = function () {
axios.get('/api/albums/sidebar')
axios.get('api/albums/sidebar')
.then(function (response) {
if (response.data.success === false) {
if(response.data.description === 'No token provided') return panel.verifyToken(panel.token);
else return swal("An error ocurred", response.data.description, "error");
if (response.data.description === 'No token provided') return panel.verifyToken(panel.token)
else return swal('An error ocurred', response.data.description, 'error')
}
var albumsContainer = document.getElementById('albumsContainer');
albumsContainer.innerHTML = '';
var albumsContainer = document.getElementById('albumsContainer')
albumsContainer.innerHTML = ''
if(response.data.albums === undefined) return;
if (response.data.albums === undefined) return
var li, a
for (var album of response.data.albums) {
li = document.createElement('li');
a = document.createElement('a');
a.id = album.id;
a.innerHTML = album.name;
li = document.createElement('li')
a = document.createElement('a')
a.id = album.id
a.innerHTML = album.name
a.addEventListener('click', function () {
panel.getAlbum(this);
});
panel.getAlbum(this)
})
li.appendChild(a);
albumsContainer.appendChild(li);
li.appendChild(a)
albumsContainer.appendChild(li)
}
})
.catch(function (error) {
return swal("An error ocurred", 'There was an error with the request, please check the console for more information.', "error");
console.log(error);
});
console.log(error)
return swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error')
})
}
panel.getAlbum = function (item) {
panel.setActiveMenu(item);
panel.getUploads(item.id);
panel.setActiveMenu(item)
panel.getUploads(item.id)
}
panel.changeToken = function () {
axios.get('/api/tokens')
axios.get('api/tokens')
.then(function (response) {
if (response.data.success === false) {
if(response.data.description === 'No token provided') return panel.verifyToken(panel.token);
else return swal("An error ocurred", response.data.description, "error");
if (response.data.description === 'No token provided') return panel.verifyToken(panel.token)
else return swal('An error ocurred', response.data.description, 'error')
}
panel.page.innerHTML = '';
var container = document.createElement('div');
container.className = "container";
panel.page.innerHTML = ''
var container = document.createElement('div')
container.className = 'container'
container.innerHTML = `
<h2 class="subtitle">Manage your token</h2>
@ -505,54 +470,47 @@ panel.changeToken = function(){
<input id="token" readonly class="input is-expanded" type="text" placeholder="Your token" value="${response.data.token}">
<a id="getNewToken" class="button is-primary">Request new token</a>
</p>
`;
`
panel.page.appendChild(container);
panel.page.appendChild(container)
document.getElementById('getNewToken').addEventListener('click', function () {
panel.getNewToken();
});
panel.getNewToken()
})
})
.catch(function (error) {
return swal("An error ocurred", 'There was an error with the request, please check the console for more information.', "error");
console.log(error);
});
console.log(error)
return swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error')
})
}
panel.getNewToken = function () {
axios.post('/api/tokens/change')
axios.post('api/tokens/change')
.then(function (response) {
if (response.data.success === false) {
if(response.data.description === 'No token provided') return panel.verifyToken(panel.token);
else return swal("An error ocurred", response.data.description, "error");
if (response.data.description === 'No token provided') return panel.verifyToken(panel.token)
else return swal('An error ocurred', response.data.description, 'error')
}
swal({
title: "Woohoo!",
title: 'Woohoo!',
text: 'Your token was changed successfully.',
type: "success"
type: 'success'
}, function () {
localStorage.token = response.data.token;
location.reload();
localStorage.token = response.data.token
location.reload()
})
})
.catch(function (error) {
return swal("An error ocurred", 'There was an error with the request, please check the console for more information.', "error");
console.log(error);
});
console.log(error)
return swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error')
})
}
panel.changePassword = function () {
panel.page.innerHTML = '';
var container = document.createElement('div');
container.className = "container";
panel.page.innerHTML = ''
var container = document.createElement('div')
container.className = 'container'
container.innerHTML = `
<h2 class="subtitle">Change your password</h2>
@ -565,60 +523,55 @@ panel.changePassword = function(){
<input id="passwordConfirm" class="input is-expanded" type="password" placeholder="Verify your new password">
<a id="sendChangePassword" class="button is-primary">Set new password</a>
</p>
`;
`
panel.page.appendChild(container);
panel.page.appendChild(container)
document.getElementById('sendChangePassword').addEventListener('click', function () {
if (document.getElementById('password').value === document.getElementById('passwordConfirm').value) {
panel.sendNewPassword(document.getElementById('password').value);
panel.sendNewPassword(document.getElementById('password').value)
} else {
swal({
title: "Password mismatch!",
title: 'Password mismatch!',
text: 'Your passwords do not match, please try again.',
type: "error"
type: 'error'
}, function () {
panel.changePassword();
});
panel.changePassword()
})
}
});
})
}
panel.sendNewPassword = function (pass) {
axios.post('/api/password/change', {password: pass})
axios.post('api/password/change', {password: pass})
.then(function (response) {
if (response.data.success === false) {
if(response.data.description === 'No token provided') return panel.verifyToken(panel.token);
else return swal("An error ocurred", response.data.description, "error");
if (response.data.description === 'No token provided') return panel.verifyToken(panel.token)
else return swal('An error ocurred', response.data.description, 'error')
}
swal({
title: "Woohoo!",
title: 'Woohoo!',
text: 'Your password was changed successfully.',
type: "success"
type: 'success'
}, function () {
location.reload();
location.reload()
})
})
.catch(function (error) {
return swal("An error ocurred", 'There was an error with the request, please check the console for more information.', "error");
console.log(error);
});
console.log(error)
return swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error')
})
}
panel.setActiveMenu = function (item) {
var menu = document.getElementById('menu');
var items = menu.getElementsByTagName('a');
for(var i = 0; i < items.length; i++)
items[i].className = "";
var menu = document.getElementById('menu')
var items = menu.getElementsByTagName('a')
for (var i = 0; i < items.length; i++) { items[i].className = '' }
item.className = 'is-active';
item.className = 'is-active'
}
window.onload = function () {
panel.preparePage();
panel.preparePage()
}

View File

@ -1,124 +1,126 @@
var upload = {};
/* eslint-disable no-unused-expressions */
/* global swal, axios, Dropzone */
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
upload.album;
upload.myDropzone;
var upload = {}
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
upload.album
upload.myDropzone
upload.checkIfPublic = function () {
axios.get('/api/check')
.then(function (response) {
upload.isPrivate= response.data.private;
upload.maxFileSize = response.data.maxFileSize;
upload.preparePage();
axios.get('api/check')
.then(response => {
upload.isPrivate = response.data.private
upload.maxFileSize = response.data.maxFileSize
upload.preparePage()
})
.catch(error => {
console.log(error)
return swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error')
})
.catch(function (error) {
swal("An error ocurred", 'There was an error with the request, please check the console for more information.', "error");
return console.log(error);
});
}
upload.preparePage = function () {
if(!upload.isPrivate) return upload.prepareUpload();
if(!upload.token) return document.getElementById('loginToUpload').style.display = 'inline-flex';
upload.verifyToken(upload.token, true);
if (upload.isPrivate) {
if (upload.token) {
return upload.verifyToken(upload.token, true)
} else {
document.getElementById('loginToUpload').innerText = 'Running in private mode. Log in to upload.'
document.getElementById('loginToUpload').style.display = 'inline-flex' // ???
}
} else {
return upload.prepareUpload()
}
}
upload.verifyToken = function (token, reloadOnError) {
if(reloadOnError === undefined)
reloadOnError = false;
axios.post('/api/tokens/verify', {
token: token
})
.then(function (response) {
if (reloadOnError === undefined) { reloadOnError = false }
axios.post('api/tokens/verify', { token: token })
.then(response => {
if (response.data.success === false) {
swal({
title: "An error ocurred",
title: 'An error ocurred',
text: response.data.description,
type: "error"
}, function(){
type: 'error'
}, () => {
if (reloadOnError) {
localStorage.removeItem("token");
location.reload();
localStorage.removeItem('token')
location.reload()
}
})
return;
return
}
localStorage.token = token;
upload.token = token;
return upload.prepareUpload();
localStorage.token = token
upload.token = token
return upload.prepareUpload()
})
.catch(error => {
console.log(error)
return swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error')
})
.catch(function (error) {
swal("An error ocurred", 'There was an error with the request, please check the console for more information.', "error");
return console.log(error);
});
}
upload.prepareUpload = function () {
// I think this fits best here because we need to check for a valid token before we can get the albums
if (upload.token) {
var select = document.getElementById('albumSelect');
var select = document.getElementById('albumSelect')
select.addEventListener('change', function() {
upload.album = select.value;
});
select.addEventListener('change', () => {
upload.album = select.value
})
axios.get('/api/albums', { headers: { token: upload.token }})
.then(function(res) {
var albums = res.data.albums;
axios.get('api/albums', { headers: { token: upload.token } })
.then(res => {
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
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++) {
var opt = document.createElement('option');
opt.value = albums[i].id;
opt.innerHTML = albums[i].name;
select.appendChild(opt);
var opt = document.createElement('option')
opt.value = albums[i].id
opt.innerHTML = albums[i].name
select.appendChild(opt)
}
// display the album selection
document.getElementById('albumDiv').style.display = 'block';
// Display the album selection
document.getElementById('albumDiv').style.display = 'block'
})
.catch(function(e) {
swal("An error ocurred", 'There was an error with the request, please check the console for more information.', "error");
return console.log(e);
.catch(e => {
console.log(e)
return swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error')
})
}
div = document.createElement('div');
div.id = 'dropzone';
div.innerHTML = 'Click here or drag and drop files';
div.style.display = 'flex';
var div = document.createElement('div')
div.id = 'dropzone'
div.innerHTML = 'Click here or drag and drop files'
div.style.display = 'flex'
document.getElementById('maxFileSize').innerHTML = 'Maximum upload size per file is ' + upload.maxFileSize;
document.getElementById('loginToUpload').style.display = 'none';
document.getElementById('maxFileSize').innerHTML = `Maximum upload size per file is ${upload.maxFileSize}`
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);
upload.prepareDropzone();
document.getElementById('uploadContainer').appendChild(div)
upload.prepareDropzone()
}
upload.prepareDropzone = function () {
var previewNode = document.querySelector('#template');
previewNode.id = '';
var previewTemplate = previewNode.parentNode.innerHTML;
previewNode.parentNode.removeChild(previewNode);
var previewNode = document.querySelector('#template')
previewNode.id = ''
var previewTemplate = previewNode.parentNode.innerHTML
previewNode.parentNode.removeChild(previewNode)
var dropzone = new Dropzone('div#dropzone', {
url: '/api/upload',
url: 'api/upload',
paramName: 'files[]',
maxFilesize: upload.maxFileSize.slice(0, -2),
parallelUploads: 2,
@ -128,92 +130,87 @@ upload.prepareDropzone = function(){
createImageThumbnails: false,
maxFiles: 1000,
autoProcessQueue: true,
headers: {
'token': upload.token
},
headers: { token: upload.token },
init: function () {
upload.myDropzone = this;
this.on('addedfile', function(file) {
document.getElementById('uploads').style.display = 'block';
});
// add the selected albumid, if an album is selected, as a header
this.on('sending', function(file, xhr) {
upload.myDropzone = this
this.on('addedfile', file => {
document.getElementById('uploads').style.display = 'block'
})
// Add the selected albumid, if an album is selected, as a header
this.on('sending', (file, xhr) => {
if (upload.album) {
xhr.setRequestHeader('albumid', upload.album)
}
});
})
}
});
})
// Update the total progress bar
dropzone.on('uploadprogress', function(file, progress) {
file.previewElement.querySelector('.progress').setAttribute('value', progress);
file.previewElement.querySelector('.progress').innerHTML = progress + '%';
});
dropzone.on('success', function(file, response) {
dropzone.on('uploadprogress', (file, progress) => {
file.previewElement.querySelector('.progress').setAttribute('value', progress)
file.previewElement.querySelector('.progress').innerHTML = `${progress}%`
})
dropzone.on('success', (file, response) => {
// Handle the responseText here. For example, add the text to the preview element:
if (response.success === false) {
var span = document.createElement('span');
span.innerHTML = response.description;
file.previewTemplate.querySelector('.link').appendChild(span);
return;
var span = document.createElement('span')
span.innerHTML = response.description
file.previewTemplate.querySelector('.link').appendChild(span)
return
}
a = document.createElement('a');
a.href = response.files[0].url;
a.target = '_blank';
a.innerHTML = response.files[0].url;
file.previewTemplate.querySelector('.link').appendChild(a);
var a = document.createElement('a')
a.href = response.files[0].url
a.target = '_blank'
a.innerHTML = response.files[0].url
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 () {
if (upload.token) {
var sharex_element = document.getElementById("ShareX");
var sharex_file = "{\r\n\
\"Name\": \"" + location.hostname + "\",\r\n\
\"DestinationType\": \"ImageUploader, FileUploader\",\r\n\
\"RequestType\": \"POST\",\r\n\
\"RequestURL\": \"" + location.origin + "/api/upload\",\r\n\
\"FileFormName\": \"files[]\",\r\n\
\"Headers\": {\r\n\
\"token\": \"" + upload.token + "\"\r\n\
var sharexElement = document.getElementById('ShareX')
var sharexFile = `{\r\n\
"Name": "${location.hostname}",\r\n\
"DestinationType": "ImageUploader, FileUploader",\r\n\
"RequestType": "POST",\r\n\
"RequestURL": "${location.origin}/api/upload",\r\n\
"FileFormName": "files[]",\r\n\
"Headers": {\r\n\
"token": "${upload.token}"\r\n\
},\r\n\
\"ResponseType\": \"Text\",\r\n\
\"URL\": \"$json:files[0].url$\",\r\n\
\"ThumbnailURL\": \"$json:files[0].url$\"\r\n\
}";
var sharex_blob = new Blob([sharex_file], {type: "application/octet-binary"});
sharex_element.setAttribute("href", URL.createObjectURL(sharex_blob))
sharex_element.setAttribute("download", location.hostname + ".sxcu");
"ResponseType": "Text",\r\n\
"URL": "$json:files[0].url$",\r\n\
"ThumbnailURL": "$json:files[0].url$"\r\n\
}`
var sharexBlob = new Blob([sharexFile], { type: 'application/octet-binary' })
sharexElement.setAttribute('href', URL.createObjectURL(sharexBlob))
sharexElement.setAttribute('download', `${location.hostname}.sxcu`)
}
}
// Handle image paste event
window.addEventListener('paste', function(event) {
var items = (event.clipboardData || event.originalEvent.clipboardData).items;
for (index in items) {
var item = items[index];
window.addEventListener('paste', event => {
var items = (event.clipboardData || event.originalEvent.clipboardData).items
for (var index in items) {
var item = items[index]
if (item.kind === 'file') {
var blob = item.getAsFile();
console.log(blob.type);
var file = new File([blob], "pasted-image."+blob.type.match(/(?:[^\/]*\/)([^;]*)/)[1]);
file.type = blob.type;
console.log(file);
upload.myDropzone.addFile(file);
var blob = item.getAsFile()
console.log(blob.type)
var file = new File([blob], `pasted-image.${blob.type.match(/(?:[^/]*\/)([^;]*)/)[1]}`)
file.type = blob.type
console.log(file)
upload.myDropzone.addFile(file)
}
}
});
})
window.onload = function () {
upload.checkIfPublic();
};
upload.checkIfPublic()
}

19
public/libs/axios/LICENSE Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2014-present Matt Zabriskie
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

3
public/libs/axios/axios.min.js vendored Normal file

File diff suppressed because one or more lines are too long

21
public/libs/bulma/LICENSE Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2017 Jeremy Thomas
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

1
public/libs/bulma/bulma.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,12 @@
License
(The MIT License)
Copyright (c) 2012 Matias Meno <m@tias.me>
Logo & Website Design (c) 2015 "1910" www.weare1910.com
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

2
public/libs/dropzone/dropzone.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,12 @@
Font license info
## Font Awesome
Copyright (C) 2016 by Dave Gandy
Author: Dave Gandy
License: SIL ()
Homepage: http://fortawesome.github.com/Font-Awesome/

46
public/libs/fontello/fontello.css vendored Normal file
View File

@ -0,0 +1,46 @@
@font-face {
font-family: 'fontello';
src: url('fontello.eot?90492172');
src: url('fontello.eot?90492172#iefix') format('embedded-opentype'),
url('fontello.woff2?90492172') format('woff2'),
url('fontello.woff?90492172') format('woff'),
url('fontello.ttf?90492172') format('truetype'),
url('fontello.svg?90492172#fontello') format('svg');
font-weight: normal;
font-style: normal;
}
/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */
/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */
/*
@media screen and (-webkit-min-device-pixel-ratio:0) {
@font-face {
font-family: 'fontello';
src: url('fontello.svg?90492172#fontello') format('svg');
}
}
*/
[class^="fa icon-"]:before, [class*=" icon-"]:before {
font: normal normal 14px/1 fontello;
display: inline-block;
text-decoration: inherit;
width: 1em;
text-align: center;
/* For safety - reset parent styles, that can break glyph codes*/
font-variant: normal;
text-transform: none;
/* fix buttons height, for twitter bootstrap */
line-height: 1em;
/* Font smoothing. That was taken from TWBS */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-th-large:before { content: '\e800'; } /* '' */
.icon-pencil:before { content: '\e802'; } /* '' */
.icon-list-bullet:before { content: '\f0ca'; } /* '' */
.icon-trash:before { content: '\f1f8'; } /* '' */

Binary file not shown.

View File

@ -0,0 +1,18 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Copyright (C) 2018 by original authors @ fontello.com</metadata>
<defs>
<font id="fontello" horiz-adv-x="1000" >
<font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
<missing-glyph horiz-adv-x="1000" />
<glyph glyph-name="th-large" unicode="&#xe800;" d="M429 279v-215q0-29-22-50t-50-21h-286q-29 0-50 21t-21 50v215q0 29 21 50t50 21h286q29 0 50-21t22-50z m0 428v-214q0-29-22-50t-50-22h-286q-29 0-50 22t-21 50v214q0 29 21 50t50 22h286q29 0 50-22t22-50z m500-428v-215q0-29-22-50t-50-21h-286q-29 0-50 21t-21 50v215q0 29 21 50t50 21h286q29 0 50-21t22-50z m0 428v-214q0-29-22-50t-50-22h-286q-29 0-50 22t-21 50v214q0 29 21 50t50 22h286q29 0 50-22t22-50z" horiz-adv-x="928.6" />
<glyph glyph-name="pencil" unicode="&#xe802;" d="M203-7l50 51-131 131-51-51v-60h72v-71h60z m291 518q0 12-12 12-5 0-9-4l-303-302q-4-4-4-10 0-12 13-12 5 0 9 4l303 302q3 4 3 10z m-30 107l232-232-464-465h-232v233z m381-54q0-29-20-50l-93-93-232 233 93 92q20 21 50 21 29 0 51-21l131-131q20-22 20-51z" horiz-adv-x="857.1" />
<glyph glyph-name="list-bullet" unicode="&#xf0ca;" d="M214 64q0-44-31-76t-76-31-76 31-31 76 31 76 76 31 76-31 31-76z m0 286q0-45-31-76t-76-31-76 31-31 76 31 76 76 31 76-31 31-76z m786-232v-107q0-7-5-13t-13-5h-678q-8 0-13 5t-5 13v107q0 7 5 12t13 6h678q7 0 13-6t5-12z m-786 518q0-45-31-76t-76-31-76 31-31 76 31 76 76 31 76-31 31-76z m786-232v-108q0-7-5-12t-13-5h-678q-8 0-13 5t-5 12v108q0 7 5 12t13 5h678q7 0 13-5t5-12z m0 285v-107q0-7-5-12t-13-6h-678q-8 0-13 6t-5 12v107q0 8 5 13t13 5h678q7 0 13-5t5-13z" horiz-adv-x="1000" />
<glyph glyph-name="trash" unicode="&#xf1f8;" d="M286 82v393q0 8-5 13t-13 5h-36q-8 0-13-5t-5-13v-393q0-8 5-13t13-5h36q8 0 13 5t5 13z m143 0v393q0 8-5 13t-13 5h-36q-8 0-13-5t-5-13v-393q0-8 5-13t13-5h36q8 0 13 5t5 13z m142 0v393q0 8-5 13t-12 5h-36q-8 0-13-5t-5-13v-393q0-8 5-13t13-5h36q7 0 12 5t5 13z m-303 554h250l-27 65q-4 5-9 6h-177q-6-1-10-6z m518-18v-36q0-8-5-13t-13-5h-54v-529q0-46-26-80t-63-34h-464q-37 0-63 33t-27 79v531h-53q-8 0-13 5t-5 13v36q0 8 5 13t13 5h172l39 93q9 21 31 35t44 15h178q23 0 44-15t30-35l39-93h173q8 0 13-5t5-13z" horiz-adv-x="785.7" />
</font>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,9 @@
The MIT License (MIT)
Copyright (c) 2014-present Tristan Edwards
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,26 +1,26 @@
const config = require('../config.js');
const routes = require('express').Router();
const db = require('knex')(config.database);
const path = require('path');
const utils = require('../controllers/utilsController.js');
const config = require('../config.js')
const routes = require('express').Router()
const db = require('knex')(config.database)
const path = require('path')
const utils = require('../controllers/utilsController.js')
routes.get('/a/:identifier', async (req, res, next) => {
let identifier = req.params.identifier;
if (identifier === undefined) return res.status(401).json({ success: false, description: 'No identifier provided' });
let identifier = req.params.identifier
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();
if (!album) return res.status(404).sendFile('404.html', { root: './pages/error/' });
const album = await db.table('albums').where({ identifier, enabled: 1 }).first()
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');
let thumb = '';
const basedomain = config.domain;
const files = await db.table('files').select('name').where('albumid', album.id).orderBy('id', 'DESC')
let thumb = ''
const basedomain = config.domain
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)) {
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.
@ -29,18 +29,17 @@ routes.get('/a/:identifier', async (req, res, next) => {
*/
if (thumb === '') {
thumb = file.thumb;
thumb = file.thumb
}
file.thumb = `<img src="${file.thumb}"/>`;
file.thumb = `<img src="${file.thumb}"/>`
} 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
if (config.uploads.generateZips) enableDownload = true
return res.render('album', {
layout: false,
@ -50,7 +49,7 @@ routes.get('/a/:identifier', async (req, res, next) => {
files,
identifier,
enableDownload
});
});
})
})
module.exports = routes;
module.exports = routes

View File

@ -1,37 +1,37 @@
const config = require('../config.js');
const routes = require('express').Router();
const uploadController = require('../controllers/uploadController');
const albumsController = require('../controllers/albumsController');
const tokenController = require('../controllers/tokenController');
const authController = require('../controllers/authController');
const config = require('../config.js')
const routes = require('express').Router()
const uploadController = require('../controllers/uploadController')
const albumsController = require('../controllers/albumsController')
const tokenController = require('../controllers/tokenController')
const authController = require('../controllers/authController')
routes.get('/check', (req, res, next) => {
return res.json({
private: config.private,
maxFileSize: config.uploads.maxSize
});
});
})
})
routes.post('/login', (req, res, next) => authController.verify(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.get('/uploads', (req, res, next) => uploadController.list(req, res, next));
routes.get('/uploads/:page', (req, res, next) => uploadController.list(req, res, next));
routes.post('/upload', (req, res, next) => uploadController.upload(req, res, next));
routes.post('/upload/delete', (req, res, next) => uploadController.delete(req, res, next));
routes.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/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/:page', (req, res, next) => uploadController.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.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/rename', (req, res, next) => albumsController.rename(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.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('/login', (req, res, next) => authController.verify(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.get('/uploads', (req, res, next) => uploadController.list(req, res, next))
routes.get('/uploads/:page', (req, res, next) => uploadController.list(req, res, next))
routes.post('/upload', (req, res, next) => uploadController.upload(req, res, next))
routes.post('/upload/delete', (req, res, next) => uploadController.delete(req, res, next))
routes.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/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/:page', (req, res, next) => uploadController.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.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/rename', (req, res, next) => albumsController.rename(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.post('/tokens/verify', (req, res, next) => tokenController.verify(req, res, next))
routes.post('/tokens/change', (req, res, next) => tokenController.change(req, res, next))
module.exports = routes;
module.exports = routes

View File

@ -1,41 +1,50 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="description" content="A pomf-like file uploading service that doesn't suck.">
<meta name="keywords" content="upload,lolisafe,file,images,hosting">
<meta name="keywords" content="upload,lolisafe,file,images,hosting,bobby,fiery">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>{{ title }}</title>
<link rel="apple-touch-icon" sizes="180x180" href="https://lolisafe.moe/images/icons/apple-touch-icon.png?v=XBreOJMe24">
<link rel="icon" type="image/png" href="https://lolisafe.moe/images/icons/favicon-32x32.png?v=XBreOJMe24" sizes="32x32">
<link rel="icon" type="image/png" href="https://lolisafe.moe/images/icons/favicon-16x16.png?v=XBreOJMe24" sizes="16x16">
<link rel="manifest" href="https://lolisafe.moe/images/icons/manifest.json?v=XBreOJMe24">
<link rel="mask-icon" href="https://lolisafe.moe/images/icons/safari-pinned-tab.svg?v=XBreOJMe24" color="#5bbad5">
<link rel="shortcut icon" href="https://lolisafe.moe/images/icons/favicon.ico?v=XBreOJMe24">
<meta name="apple-mobile-web-app-title" content="lolisafe">
<meta name="application-name" content="lolisafe">
<meta name="msapplication-config" content="https://lolisafe.moe/images/icons/browserconfig.xml?v=XBreOJMe24">
<meta name="theme-color" content="#ffffff">
<!-- Stylesheets and scripts -->
<link rel="stylesheet" type="text/css" href="libs/bulma/bulma.min.css">
<link rel="stylesheet" type="text/css" href="libs/sweetalert/sweetalert.min.css">
<link rel="stylesheet" type="text/css" href="css/style.css">
<script type="text/javascript" src="libs/sweetalert/sweetalert.min.js"></script>
<script type="text/javascript" src="libs/dropzone/dropzone.min.js"></script>
<script type="text/javascript" src="libs/axios/axios.min.js"></script>
<script type="text/javascript" src="js/home.js"></script>
<meta property="og:url" content="https://lolisafe.moe" />
<!-- Open Graph tags -->
<meta property="og:type" content="website" />
<meta property="og:title" content="{{ title }} | {{ count }} files" />
<meta property="og:description" content="lolisafe.moe | A small safe worth protecting." />
<meta property="og:url" content="https://safe.fiery.me/" />
<meta property="og:description" content="A pomf-like file uploading service that doesn't suck." />
<meta property="og:image" content="{{ thumb }}" />
<meta property="og:image:secure_url" content="{{ thumb }}" />
<meta property="og:locale" content="en_US" />
<!-- Twitter Card tags -->
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="{{ title }} | {{ count }} files">
<meta name="twitter:description" content="lolisafe.moe | A small safe worth protecting.">
<meta name="twitter:description" content="A pomf-like file uploading service that doesn't suck.">
<meta name="twitter:image" content="{{ thumb }}">
<meta name="twitter:image:src" content="{{ thumb }}">
<title>{{ title }}</title>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.3.0/css/bulma.min.css">
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.css">
<link rel="stylesheet" type="text/css" href="/css/style.css">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.15.3/axios.min.js"></script>
<!-- Icons and configs -->
<link rel="icon" type="image/png" href="https://safe.fiery.me/icons/32pxr.png" sizes="32x32">
<link rel="icon" type="image/png" href="https://safe.fiery.me/icons/96pxr.png" sizes="96x96">
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/120px.png" sizes="120x120">
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/152px.png" sizes="152x152">
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/167px.png" sizes="167x167">
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/180px.png" sizes="180x180">
<link rel="manifest" href="https://safe.fiery.me/icons/manifest.json?v=hEjFGFcHY8">
<meta name="apple-mobile-web-app-title" content="safe.fiery.me">
<meta name="application-name" content="safe.fiery.me">
<meta name="msapplication-config" content="https://safe.fiery.me/icons/browserconfig.xml?v=hEjFGFcHY8">
<meta name="theme-color" content="#232629">
</head>
<body>
@ -50,7 +59,7 @@
</div>
<div class="column is-3" style="text-align: right; padding-top: 45px;">
{{#if enableDownload}}
<a class="button is-primary is-outlined" title="Download album" href="/api/album/zip/{{ identifier }}">Download Album</a>
<a class="button is-primary is-outlined" title="Download album" href="api/album/zip/{{ identifier }}">Download Album</a>
{{/if}}
</div>
</div>

1556
yarn.lock

File diff suppressed because it is too large Load Diff