Merge branch 'safe.fiery.me' into master
13
.editorconfig
Normal 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
|
2
LICENSE
@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
@ -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)
|
[![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)
|
[![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.
|
# 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
|
## What's new in v3.0.0
|
||||||
- Backend rewrite to make it faster, better and easier to extend
|
- Backend rewrite to make it faster, better and easier to extend
|
||||||
- Album downloads (Thanks to [PascalTemel](https://github.com/PascalTemel))
|
- Album downloads (Thanks to [PascalTemel](https://github.com/PascalTemel))
|
||||||
|
132
config.sample.js
@ -1,84 +1,84 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
If set to true the user will need to specify the auto-generated token
|
If set to true the user will need to specify the auto-generated token
|
||||||
on each API call, meaning random strangers wont be able to use the service
|
on each API call, meaning random strangers wont be able to use the service
|
||||||
unless they have the token loli-safe provides you with.
|
unless they have the token loli-safe provides you with.
|
||||||
If it's set to false, then upload will be public for anyone to use.
|
If it's set to false, then upload will be public for anyone to use.
|
||||||
*/
|
*/
|
||||||
private: true,
|
private: true,
|
||||||
|
|
||||||
// If true, users will be able to create accounts and access their uploaded files
|
// If true, users will be able to create accounts and access their uploaded files
|
||||||
enableUserAccounts: true,
|
enableUserAccounts: true,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Here you can decide if you want lolisafe to serve the files or if you prefer doing so via nginx.
|
Here you can decide if you want lolisafe to serve the files or if you prefer doing so via nginx.
|
||||||
The main difference between the two is the ease of use and the chance of analytics in the future.
|
The main difference between the two is the ease of use and the chance of analytics in the future.
|
||||||
If you set it to `true`, the uploaded files will be located after the host like:
|
If you set it to `true`, the uploaded files will be located after the host like:
|
||||||
https://lolisafe.moe/yourFile.jpg
|
https://lolisafe.moe/yourFile.jpg
|
||||||
|
|
||||||
If you set it to `false`, you need to set nginx to directly serve whatever folder it is you are serving your
|
If you set it to `false`, you need to set nginx to directly serve whatever folder it is you are serving your
|
||||||
downloads in. This also gives you the ability to serve them, for example, like this:
|
downloads in. This also gives you the ability to serve them, for example, like this:
|
||||||
https://files.lolisafe.moe/yourFile.jpg
|
https://files.lolisafe.moe/yourFile.jpg
|
||||||
|
|
||||||
Both cases require you to type the domain where the files will be served on the `domain` key below.
|
Both cases require you to type the domain where the files will be served on the `domain` key below.
|
||||||
Which one you use is ultimately up to you.
|
Which one you use is ultimately up to you.
|
||||||
*/
|
*/
|
||||||
serveFilesWithNode: false,
|
serveFilesWithNode: false,
|
||||||
domain: 'https://lolisafe.moe',
|
domain: 'https://lolisafe.moe',
|
||||||
|
|
||||||
// Port on which to run the server
|
// Port on which to run the server
|
||||||
port: 9999,
|
port: 9999,
|
||||||
|
|
||||||
// Pages to process for the frontend
|
// Pages to process for the frontend
|
||||||
pages: ['home', 'auth', 'dashboard', 'faq'],
|
pages: ['home', 'auth', 'dashboard', 'faq'],
|
||||||
|
|
||||||
// Add file extensions here which should be blocked
|
// Add file extensions here which should be blocked
|
||||||
blockedExtensions: [
|
blockedExtensions: [
|
||||||
'.exe',
|
'.exe',
|
||||||
'.bat',
|
'.bat',
|
||||||
'.cmd',
|
'.cmd',
|
||||||
'.msi',
|
'.msi',
|
||||||
'.sh'
|
'.sh'
|
||||||
],
|
],
|
||||||
|
|
||||||
// Uploads config
|
// Uploads config
|
||||||
uploads: {
|
uploads: {
|
||||||
|
|
||||||
// Folder where images should be stored
|
// Folder where images should be stored
|
||||||
folder: 'uploads',
|
folder: 'uploads',
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Max file size allowed. Needs to be in MB
|
Max file size allowed. Needs to be in MB
|
||||||
Note: When maxSize is greater than 1 MiB, you must set the client_max_body_size to the same as maxSize.
|
Note: When maxSize is greater than 1 MiB, you must set the client_max_body_size to the same as maxSize.
|
||||||
*/
|
*/
|
||||||
maxSize: '512MB',
|
maxSize: '512MB',
|
||||||
|
|
||||||
// The length of the random generated name for the uploaded files
|
// The length of the random generated name for the uploaded files
|
||||||
fileLength: 32,
|
fileLength: 32,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
NOTE: Thumbnails are only for the admin panel and they require you
|
NOTE: Thumbnails are only for the admin panel and they require you
|
||||||
to install a separate binary called graphicsmagick (http://www.graphicsmagick.org)
|
to install a separate binary called graphicsmagick (http://www.graphicsmagick.org)
|
||||||
for images and ffmpeg (https://ffmpeg.org/) for video files
|
for images and ffmpeg (https://ffmpeg.org/) for video files
|
||||||
*/
|
*/
|
||||||
generateThumbnails: false,
|
generateThumbnails: false,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Allows users to download a .zip file of all files in an album.
|
Allows users to download a .zip file of all files in an album.
|
||||||
The file is generated when the user clicks the download button in the view
|
The file is generated when the user clicks the download button in the view
|
||||||
and is re-used if the album has not changed between download requests
|
and is re-used if the album has not changed between download requests
|
||||||
*/
|
*/
|
||||||
generateZips: true
|
generateZips: true
|
||||||
},
|
},
|
||||||
|
|
||||||
// Folder where to store logs
|
// Folder where to store logs
|
||||||
logsFolder: 'logs',
|
logsFolder: 'logs',
|
||||||
|
|
||||||
// The following values shouldn't be touched
|
// The following values shouldn't be touched
|
||||||
database: {
|
database: {
|
||||||
client: 'sqlite3',
|
client: 'sqlite3',
|
||||||
connection: { filename: './database/db' },
|
connection: { filename: './database/db' },
|
||||||
useNullAsDefault: true
|
useNullAsDefault: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,179 +1,178 @@
|
|||||||
const config = require('../config.js');
|
const config = require('../config.js')
|
||||||
const db = require('knex')(config.database);
|
const db = require('knex')(config.database)
|
||||||
const randomstring = require('randomstring');
|
const randomstring = require('randomstring')
|
||||||
const utils = require('./utilsController.js');
|
const utils = require('./utilsController.js')
|
||||||
const path = require('path');
|
const path = require('path')
|
||||||
const fs = require('fs');
|
const fs = require('fs')
|
||||||
const Zip = require('jszip');
|
const Zip = require('jszip')
|
||||||
const albumsController = {};
|
const albumsController = {}
|
||||||
|
|
||||||
albumsController.list = async (req, res, next) => {
|
albumsController.list = async (req, res, next) => {
|
||||||
const user = await utils.authorize(req, res);
|
const user = await utils.authorize(req, res)
|
||||||
|
|
||||||
const fields = ['id', 'name'];
|
const fields = ['id', 'name']
|
||||||
if (req.params.sidebar === undefined) {
|
if (req.params.sidebar === undefined) {
|
||||||
fields.push('timestamp');
|
fields.push('timestamp')
|
||||||
fields.push('identifier');
|
fields.push('identifier')
|
||||||
}
|
}
|
||||||
|
|
||||||
const albums = await db.table('albums').select(fields).where({ enabled: 1, userid: user.id });
|
const albums = await db.table('albums').select(fields).where({ enabled: 1, userid: user.id })
|
||||||
if (req.params.sidebar !== undefined) {
|
if (req.params.sidebar !== undefined) {
|
||||||
return res.json({ success: true, albums });
|
return res.json({ success: true, albums })
|
||||||
}
|
}
|
||||||
|
|
||||||
let ids = [];
|
let ids = []
|
||||||
for (let album of albums) {
|
for (let album of albums) {
|
||||||
album.date = new Date(album.timestamp * 1000)
|
album.date = new Date(album.timestamp * 1000)
|
||||||
album.date = utils.getPrettyDate(album.date)
|
album.date = utils.getPrettyDate(album.date)
|
||||||
|
|
||||||
album.identifier = `${config.domain}/a/${album.identifier}`;
|
album.identifier = `${config.domain}/a/${album.identifier}`
|
||||||
ids.push(album.id);
|
ids.push(album.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
const files = await db.table('files').whereIn('albumid', ids).select('albumid');
|
const files = await db.table('files').whereIn('albumid', ids).select('albumid')
|
||||||
const albumsCount = {};
|
const albumsCount = {}
|
||||||
|
|
||||||
for (let id of ids) albumsCount[id] = 0;
|
for (let id of ids) albumsCount[id] = 0
|
||||||
for (let file of files) albumsCount[file.albumid] += 1;
|
for (let file of files) albumsCount[file.albumid] += 1
|
||||||
for (let album of albums) album.files = albumsCount[album.id];
|
for (let album of albums) album.files = albumsCount[album.id]
|
||||||
|
|
||||||
return res.json({ success: true, albums });
|
return res.json({ success: true, albums })
|
||||||
};
|
}
|
||||||
|
|
||||||
albumsController.create = async (req, res, next) => {
|
albumsController.create = async (req, res, next) => {
|
||||||
const user = await utils.authorize(req, res);
|
const user = await utils.authorize(req, res)
|
||||||
|
|
||||||
const name = req.body.name;
|
const name = req.body.name
|
||||||
if (name === undefined || name === '') {
|
if (name === undefined || name === '') {
|
||||||
return res.json({ success: false, description: 'No album name specified' });
|
return res.json({ success: false, description: 'No album name specified' })
|
||||||
}
|
}
|
||||||
|
|
||||||
const album = await db.table('albums').where({
|
const album = await db.table('albums').where({
|
||||||
name: name,
|
name: name,
|
||||||
enabled: 1,
|
enabled: 1,
|
||||||
userid: user.id
|
userid: user.id
|
||||||
}).first();
|
}).first()
|
||||||
|
|
||||||
if (album) {
|
if (album) {
|
||||||
return res.json({ success: false, description: 'There\'s already an album with that name' })
|
return res.json({ success: false, description: 'There\'s already an album with that name' })
|
||||||
}
|
}
|
||||||
|
|
||||||
await db.table('albums').insert({
|
await db.table('albums').insert({
|
||||||
name: name,
|
name: name,
|
||||||
enabled: 1,
|
enabled: 1,
|
||||||
userid: user.id,
|
userid: user.id,
|
||||||
identifier: randomstring.generate(8),
|
identifier: randomstring.generate(8),
|
||||||
timestamp: Math.floor(Date.now() / 1000)
|
timestamp: Math.floor(Date.now() / 1000)
|
||||||
});
|
})
|
||||||
|
|
||||||
return res.json({ success: true });
|
return res.json({ success: true })
|
||||||
};
|
}
|
||||||
|
|
||||||
albumsController.delete = async (req, res, next) => {
|
albumsController.delete = async (req, res, next) => {
|
||||||
const user = await utils.authorize(req, res);
|
const user = await utils.authorize(req, res)
|
||||||
|
|
||||||
const id = req.body.id;
|
const id = req.body.id
|
||||||
if (id === undefined || id === '') {
|
if (id === undefined || id === '') {
|
||||||
return res.json({ success: false, description: 'No album specified' });
|
return res.json({ success: false, description: 'No album specified' })
|
||||||
}
|
}
|
||||||
|
|
||||||
await db.table('albums').where({ id: id, userid: user.id }).update({ enabled: 0 });
|
await db.table('albums').where({ id: id, userid: user.id }).update({ enabled: 0 })
|
||||||
return res.json({ success: true });
|
return res.json({ success: true })
|
||||||
};
|
}
|
||||||
|
|
||||||
albumsController.rename = async (req, res, next) => {
|
albumsController.rename = async (req, res, next) => {
|
||||||
const user = await utils.authorize(req, res);
|
const user = await utils.authorize(req, res)
|
||||||
|
|
||||||
const id = req.body.id;
|
const id = req.body.id
|
||||||
if (id === undefined || id === '') {
|
if (id === undefined || id === '') {
|
||||||
return res.json({ success: false, description: 'No album specified' });
|
return res.json({ success: false, description: 'No album specified' })
|
||||||
}
|
}
|
||||||
|
|
||||||
const name = req.body.name;
|
const name = req.body.name
|
||||||
if (name === undefined || name === '') {
|
if (name === undefined || name === '') {
|
||||||
return res.json({ success: false, description: 'No name specified' });
|
return res.json({ success: false, description: 'No name specified' })
|
||||||
}
|
}
|
||||||
|
|
||||||
const album = await db.table('albums').where({ name: name, userid: user.id }).first();
|
const album = await db.table('albums').where({ name: name, userid: user.id }).first()
|
||||||
if (album) {
|
if (album) {
|
||||||
return res.json({ success: false, description: 'Name already in use' })
|
return res.json({ success: false, description: 'Name already in use' })
|
||||||
}
|
}
|
||||||
|
|
||||||
await db.table('albums').where({ id: id, userid: user.id }).update({ name: name })
|
await db.table('albums').where({ id: id, userid: user.id }).update({ name: name })
|
||||||
return res.json({ success: true });
|
return res.json({ success: true })
|
||||||
};
|
}
|
||||||
|
|
||||||
albumsController.get = async (req, res, next) => {
|
albumsController.get = async (req, res, next) => {
|
||||||
const identifier = req.params.identifier;
|
const identifier = req.params.identifier
|
||||||
if (identifier === undefined) return res.status(401).json({ success: false, description: 'No identifier provided' });
|
if (identifier === undefined) return res.status(401).json({ success: false, description: 'No identifier provided' })
|
||||||
|
|
||||||
const album = await db.table('albums').where({ identifier, enabled: 1 }).first();
|
const album = await db.table('albums').where({ identifier, enabled: 1 }).first()
|
||||||
if (!album) return res.json({ success: false, description: 'Album not found' });
|
if (!album) return res.json({ success: false, description: 'Album not found' })
|
||||||
|
|
||||||
const title = album.name;
|
const title = album.name
|
||||||
const files = await db.table('files').select('name').where('albumid', album.id).orderBy('id', 'DESC');
|
const files = await db.table('files').select('name').where('albumid', album.id).orderBy('id', 'DESC')
|
||||||
|
|
||||||
for (let file of files) {
|
for (let file of files) {
|
||||||
file.file = `${config.domain}/${file.name}`;
|
file.file = `${config.domain}/${file.name}`
|
||||||
|
|
||||||
const ext = path.extname(file.name).toLowerCase();
|
const ext = path.extname(file.name).toLowerCase()
|
||||||
if (utils.imageExtensions.includes(ext) || utils.videoExtensions.includes(ext)) {
|
if (utils.imageExtensions.includes(ext) || utils.videoExtensions.includes(ext)) {
|
||||||
file.thumb = `${config.domain}/thumbs/${file.name.slice(0, -ext.length)}.png`;
|
file.thumb = `${config.domain}/thumbs/${file.name.slice(0, -ext.length)}.png`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.json({
|
|
||||||
success: true,
|
|
||||||
title: title,
|
|
||||||
count: files.length,
|
|
||||||
files
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
return res.json({
|
||||||
|
success: true,
|
||||||
|
title: title,
|
||||||
|
count: files.length,
|
||||||
|
files
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
albumsController.generateZip = async (req, res, next) => {
|
albumsController.generateZip = async (req, res, next) => {
|
||||||
const identifier = req.params.identifier;
|
const identifier = req.params.identifier
|
||||||
if (identifier === undefined) return res.status(401).json({ success: false, description: 'No identifier provided' });
|
if (identifier === undefined) return res.status(401).json({ success: false, description: 'No identifier provided' })
|
||||||
if (!config.uploads.generateZips) return res.status(401).json({ success: false, description: 'Zip generation disabled' });
|
if (!config.uploads.generateZips) return res.status(401).json({ success: false, description: 'Zip generation disabled' })
|
||||||
|
|
||||||
const album = await db.table('albums').where({ identifier, enabled: 1 }).first();
|
const album = await db.table('albums').where({ identifier, enabled: 1 }).first()
|
||||||
if (!album) return res.json({ success: false, description: 'Album not found' });
|
if (!album) return res.json({ success: false, description: 'Album not found' })
|
||||||
|
|
||||||
if (album.zipGeneratedAt > album.editedAt) {
|
if (album.zipGeneratedAt > album.editedAt) {
|
||||||
const filePath = path.join(config.uploads.folder, 'zips', `${identifier}.zip`);
|
const filePath = path.join(config.uploads.folder, 'zips', `${identifier}.zip`)
|
||||||
const fileName = `${album.name}.zip`;
|
const fileName = `${album.name}.zip`
|
||||||
return res.download(filePath, fileName);
|
return res.download(filePath, fileName)
|
||||||
} else {
|
} else {
|
||||||
console.log(`Generating zip for album identifier: ${identifier}`);
|
console.log(`Generating zip for album identifier: ${identifier}`)
|
||||||
const files = await db.table('files').select('name').where('albumid', album.id);
|
const files = await db.table('files').select('name').where('albumid', album.id)
|
||||||
if (files.length === 0) return res.json({ success: false, description: 'There are no files in the album' });
|
if (files.length === 0) return res.json({ success: false, description: 'There are no files in the album' })
|
||||||
|
|
||||||
const zipPath = path.join(__dirname, '..', config.uploads.folder, 'zips', `${album.identifier}.zip`);
|
const zipPath = path.join(__dirname, '..', config.uploads.folder, 'zips', `${album.identifier}.zip`)
|
||||||
let archive = new Zip();
|
let archive = new Zip()
|
||||||
|
|
||||||
for (let file of files) {
|
for (let file of files) {
|
||||||
try {
|
try {
|
||||||
const exists = fs.statSync(path.join(__dirname, '..', config.uploads.folder, file.name));
|
// const exists = fs.statSync(path.join(__dirname, '..', config.uploads.folder, file.name))
|
||||||
archive.file(file.name, fs.readFileSync(path.join(__dirname, '..', config.uploads.folder, file.name)));
|
archive.file(file.name, fs.readFileSync(path.join(__dirname, '..', config.uploads.folder, file.name)))
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
archive
|
archive
|
||||||
.generateNodeStream({ type: 'nodebuffer', streamFiles: true })
|
.generateNodeStream({ type: 'nodebuffer', streamFiles: true })
|
||||||
.pipe(fs.createWriteStream(zipPath))
|
.pipe(fs.createWriteStream(zipPath))
|
||||||
.on('finish', async () => {
|
.on('finish', async () => {
|
||||||
console.log(`Generated zip for album identifier: ${identifier}`);
|
console.log(`Generated zip for album identifier: ${identifier}`)
|
||||||
await db.table('albums')
|
await db.table('albums')
|
||||||
.where('id', album.id)
|
.where('id', album.id)
|
||||||
.update({ zipGeneratedAt: Math.floor(Date.now() / 1000) });
|
.update({ zipGeneratedAt: Math.floor(Date.now() / 1000) })
|
||||||
|
|
||||||
const filePath = path.join(config.uploads.folder, 'zips', `${identifier}.zip`);
|
const filePath = path.join(config.uploads.folder, 'zips', `${identifier}.zip`)
|
||||||
const fileName = `${album.name}.zip`;
|
const fileName = `${album.name}.zip`
|
||||||
return res.download(filePath, fileName);
|
return res.download(filePath, fileName)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
module.exports = albumsController;
|
module.exports = albumsController
|
||||||
|
@ -1,86 +1,86 @@
|
|||||||
const config = require('../config.js');
|
const config = require('../config.js')
|
||||||
const db = require('knex')(config.database);
|
const db = require('knex')(config.database)
|
||||||
const bcrypt = require('bcrypt');
|
const bcrypt = require('bcrypt')
|
||||||
const randomstring = require('randomstring');
|
const randomstring = require('randomstring')
|
||||||
const utils = require('./utilsController.js');
|
const utils = require('./utilsController.js')
|
||||||
|
|
||||||
let authController = {};
|
let authController = {}
|
||||||
|
|
||||||
authController.verify = async (req, res, next) => {
|
authController.verify = async (req, res, next) => {
|
||||||
const username = req.body.username;
|
const username = req.body.username
|
||||||
const password = req.body.password;
|
const password = req.body.password
|
||||||
|
|
||||||
if (username === undefined) return res.json({ success: false, description: 'No username provided' });
|
if (username === undefined) return res.json({ success: false, description: 'No username provided' })
|
||||||
if (password === undefined) return res.json({ success: false, description: 'No password provided' });
|
if (password === undefined) return res.json({ success: false, description: 'No password provided' })
|
||||||
|
|
||||||
const user = await db.table('users').where('username', username).first();
|
const user = await db.table('users').where('username', username).first()
|
||||||
if (!user) return res.json({ success: false, description: 'Username doesn\'t exist' });
|
if (!user) return res.json({ success: false, description: 'Username doesn\'t exist' })
|
||||||
|
|
||||||
bcrypt.compare(password, user.password, (err, result) => {
|
bcrypt.compare(password, user.password, (err, result) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err);
|
console.log(err)
|
||||||
return res.json({ success: false, description: 'There was an error' });
|
return res.json({ success: false, description: 'There was an error' })
|
||||||
}
|
}
|
||||||
if (result === false) return res.json({ success: false, description: 'Wrong password' });
|
if (result === false) return res.json({ success: false, description: 'Wrong password' })
|
||||||
return res.json({ success: true, token: user.token });
|
return res.json({ success: true, token: user.token })
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
authController.register = async (req, res, next) => {
|
authController.register = async (req, res, next) => {
|
||||||
if (config.enableUserAccounts === false) {
|
if (config.enableUserAccounts === false) {
|
||||||
return res.json({ success: false, description: 'Register is disabled at the moment' });
|
return res.json({ success: false, description: 'Register is disabled at the moment' })
|
||||||
}
|
}
|
||||||
|
|
||||||
const username = req.body.username;
|
const username = req.body.username
|
||||||
const password = req.body.password;
|
const password = req.body.password
|
||||||
|
|
||||||
if (username === undefined) return res.json({ success: false, description: 'No username provided' });
|
if (username === undefined) return res.json({ success: false, description: 'No username provided' })
|
||||||
if (password === undefined) return res.json({ success: false, description: 'No password provided' });
|
if (password === undefined) return res.json({ success: false, description: 'No password provided' })
|
||||||
|
|
||||||
if (username.length < 4 || username.length > 32) {
|
if (username.length < 4 || username.length > 32) {
|
||||||
return res.json({ success: false, description: 'Username must have 4-32 characters' })
|
return res.json({ success: false, description: 'Username must have 4-32 characters' })
|
||||||
}
|
}
|
||||||
if (password.length < 6 || password.length > 64) {
|
if (password.length < 6 || password.length > 64) {
|
||||||
return res.json({ success: false, description: 'Password must have 6-64 characters' })
|
return res.json({ success: false, description: 'Password must have 6-64 characters' })
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await db.table('users').where('username', username).first();
|
const user = await db.table('users').where('username', username).first()
|
||||||
if (user) return res.json({ success: false, description: 'Username already exists' });
|
if (user) return res.json({ success: false, description: 'Username already exists' })
|
||||||
|
|
||||||
bcrypt.hash(password, 10, async (err, hash) => {
|
bcrypt.hash(password, 10, async (err, hash) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err);
|
console.log(err)
|
||||||
return res.json({ success: false, description: 'Error generating password hash (╯°□°)╯︵ ┻━┻' });
|
return res.json({ success: false, description: 'Error generating password hash (╯°□°)╯︵ ┻━┻' })
|
||||||
}
|
}
|
||||||
const token = randomstring.generate(64);
|
const token = randomstring.generate(64)
|
||||||
await db.table('users').insert({
|
await db.table('users').insert({
|
||||||
username: username,
|
username: username,
|
||||||
password: hash,
|
password: hash,
|
||||||
token: token
|
token: token
|
||||||
});
|
})
|
||||||
return res.json({ success: true, token: token })
|
return res.json({ success: true, token: token })
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
authController.changePassword = async (req, res, next) => {
|
authController.changePassword = async (req, res, next) => {
|
||||||
const user = await utils.authorize(req, res);
|
const user = await utils.authorize(req, res)
|
||||||
|
|
||||||
let password = req.body.password;
|
let password = req.body.password
|
||||||
if (password === undefined) return res.json({ success: false, description: 'No password provided' });
|
if (password === undefined) return res.json({ success: false, description: 'No password provided' })
|
||||||
|
|
||||||
if (password.length < 6 || password.length > 64) {
|
if (password.length < 6 || password.length > 64) {
|
||||||
return res.json({ success: false, description: 'Password must have 6-64 characters' });
|
return res.json({ success: false, description: 'Password must have 6-64 characters' })
|
||||||
}
|
}
|
||||||
|
|
||||||
bcrypt.hash(password, 10, async (err, hash) => {
|
bcrypt.hash(password, 10, async (err, hash) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err);
|
console.log(err)
|
||||||
return res.json({ success: false, description: 'Error generating password hash (╯°□°)╯︵ ┻━┻' });
|
return res.json({ success: false, description: 'Error generating password hash (╯°□°)╯︵ ┻━┻' })
|
||||||
}
|
}
|
||||||
|
|
||||||
await db.table('users').where('id', user.id).update({ password: hash });
|
await db.table('users').where('id', user.id).update({ password: hash })
|
||||||
return res.json({ success: true });
|
return res.json({ success: true })
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
module.exports = authController;
|
module.exports = authController
|
||||||
|
@ -1,34 +1,34 @@
|
|||||||
const config = require('../config.js');
|
const config = require('../config.js')
|
||||||
const db = require('knex')(config.database);
|
const db = require('knex')(config.database)
|
||||||
const randomstring = require('randomstring');
|
const randomstring = require('randomstring')
|
||||||
const utils = require('./utilsController.js');
|
const utils = require('./utilsController.js')
|
||||||
|
|
||||||
const tokenController = {};
|
const tokenController = {}
|
||||||
|
|
||||||
tokenController.verify = async (req, res, next) => {
|
tokenController.verify = async (req, res, next) => {
|
||||||
const token = req.body.token;
|
const token = req.body.token
|
||||||
if (token === undefined) return res.status(401).json({ success: false, description: 'No token provided' });
|
if (token === undefined) return res.status(401).json({ success: false, description: 'No token provided' })
|
||||||
|
|
||||||
const user = await db.table('users').where('token', token).first();
|
const user = await db.table('users').where('token', token).first()
|
||||||
if (!user) return res.status(401).json({ success: false, description: 'Invalid token' });
|
if (!user) return res.status(401).json({ success: false, description: 'Invalid token' })
|
||||||
return res.json({ success: true, username: user.username });
|
return res.json({ success: true, username: user.username })
|
||||||
};
|
}
|
||||||
|
|
||||||
tokenController.list = async (req, res, next) => {
|
tokenController.list = async (req, res, next) => {
|
||||||
const user = await utils.authorize(req, res);
|
const user = await utils.authorize(req, res)
|
||||||
return res.json({ success: true, token: user.token });
|
return res.json({ success: true, token: user.token })
|
||||||
};
|
}
|
||||||
|
|
||||||
tokenController.change = async (req, res, next) => {
|
tokenController.change = async (req, res, next) => {
|
||||||
const user = await utils.authorize(req, res);
|
const user = await utils.authorize(req, res)
|
||||||
const newtoken = randomstring.generate(64);
|
const newtoken = randomstring.generate(64)
|
||||||
|
|
||||||
await db.table('users').where('token', user.token).update({
|
await db.table('users').where('token', user.token).update({
|
||||||
token: newtoken,
|
token: newtoken,
|
||||||
timestamp: Math.floor(Date.now() / 1000)
|
timestamp: Math.floor(Date.now() / 1000)
|
||||||
});
|
})
|
||||||
|
|
||||||
res.json({ success: true, token: newtoken });
|
res.json({ success: true, token: newtoken })
|
||||||
};
|
}
|
||||||
|
|
||||||
module.exports = tokenController;
|
module.exports = tokenController
|
||||||
|
@ -1,285 +1,285 @@
|
|||||||
const config = require('../config.js');
|
const config = require('../config.js')
|
||||||
const path = require('path');
|
const path = require('path')
|
||||||
const multer = require('multer');
|
const multer = require('multer')
|
||||||
const randomstring = require('randomstring');
|
const randomstring = require('randomstring')
|
||||||
const db = require('knex')(config.database);
|
const db = require('knex')(config.database)
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto')
|
||||||
const fs = require('fs');
|
const fs = require('fs')
|
||||||
const utils = require('./utilsController.js');
|
const utils = require('./utilsController.js')
|
||||||
|
|
||||||
const uploadsController = {};
|
const uploadsController = {}
|
||||||
|
|
||||||
const storage = multer.diskStorage({
|
const storage = multer.diskStorage({
|
||||||
destination: function(req, file, cb) {
|
destination: function (req, file, cb) {
|
||||||
cb(null, path.join(__dirname, '..', config.uploads.folder));
|
cb(null, path.join(__dirname, '..', config.uploads.folder))
|
||||||
},
|
},
|
||||||
filename: function(req, file, cb) {
|
filename: function (req, file, cb) {
|
||||||
cb(null, randomstring.generate(config.uploads.fileLength) + path.extname(file.originalname));
|
cb(null, randomstring.generate(config.uploads.fileLength) + path.extname(file.originalname))
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const upload = multer({
|
const upload = multer({
|
||||||
storage: storage,
|
storage: storage,
|
||||||
limits: { fileSize: config.uploads.maxSize },
|
limits: { fileSize: config.uploads.maxSize },
|
||||||
fileFilter: function(req, file, cb) {
|
fileFilter: function (req, file, cb) {
|
||||||
if (config.blockedExtensions !== undefined) {
|
if (config.blockedExtensions !== undefined) {
|
||||||
if (config.blockedExtensions.some(extension => path.extname(file.originalname).toLowerCase() === extension)) {
|
if (config.blockedExtensions.some(extension => path.extname(file.originalname).toLowerCase() === extension)) {
|
||||||
return cb('This file extension is not allowed');
|
return cb('This file extension is not allowed') // eslint-disable-line standard/no-callback-literal
|
||||||
}
|
}
|
||||||
return cb(null, true);
|
return cb(null, true)
|
||||||
}
|
}
|
||||||
return cb(null, true);
|
return cb(null, true)
|
||||||
}
|
}
|
||||||
}).array('files[]');
|
}).array('files[]')
|
||||||
|
|
||||||
uploadsController.upload = async (req, res, next) => {
|
uploadsController.upload = async (req, res, next) => {
|
||||||
if (config.private === true) {
|
if (config.private === true) {
|
||||||
await utils.authorize(req, res);
|
await utils.authorize(req, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = req.headers.token || '';
|
const token = req.headers.token || ''
|
||||||
const user = await db.table('users').where('token', token).first();
|
const user = await db.table('users').where('token', token).first()
|
||||||
const albumid = req.headers.albumid || req.params.albumid;
|
const albumid = req.headers.albumid || req.params.albumid
|
||||||
|
|
||||||
if (albumid && user) {
|
if (albumid && user) {
|
||||||
const album = await db.table('albums').where({ id: albumid, userid: user.id }).first();
|
const album = await db.table('albums').where({ id: albumid, userid: user.id }).first()
|
||||||
if (!album) {
|
if (!album) {
|
||||||
return res.json({
|
return res.json({
|
||||||
success: false,
|
success: false,
|
||||||
description: 'Album doesn\'t exist or it doesn\'t belong to the user'
|
description: 'Album doesn\'t exist or it doesn\'t belong to the user'
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
return uploadsController.actuallyUpload(req, res, user, albumid);
|
return uploadsController.actuallyUpload(req, res, user, albumid)
|
||||||
}
|
}
|
||||||
return uploadsController.actuallyUpload(req, res, user, albumid);
|
return uploadsController.actuallyUpload(req, res, user, albumid)
|
||||||
};
|
}
|
||||||
|
|
||||||
uploadsController.actuallyUpload = async (req, res, userid, album) => {
|
uploadsController.actuallyUpload = async (req, res, userid, album) => {
|
||||||
upload(req, res, async err => {
|
upload(req, res, async err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err);
|
console.error(err)
|
||||||
return res.json({ success: false, description: err });
|
return res.json({ success: false, description: err })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (req.files.length === 0) return res.json({ success: false, description: 'no-files' });
|
if (req.files.length === 0) return res.json({ success: false, description: 'no-files' })
|
||||||
|
|
||||||
const files = [];
|
const files = []
|
||||||
const existingFiles = [];
|
const existingFiles = []
|
||||||
let iteration = 1;
|
let iteration = 1
|
||||||
|
|
||||||
req.files.forEach(async file => {
|
req.files.forEach(async file => {
|
||||||
// Check if the file exists by checking hash and size
|
// Check if the file exists by checking hash and size
|
||||||
let hash = crypto.createHash('md5');
|
let hash = crypto.createHash('md5')
|
||||||
let stream = fs.createReadStream(path.join(__dirname, '..', config.uploads.folder, file.filename));
|
let stream = fs.createReadStream(path.join(__dirname, '..', config.uploads.folder, file.filename))
|
||||||
|
|
||||||
stream.on('data', data => {
|
stream.on('data', data => {
|
||||||
hash.update(data, 'utf8');
|
hash.update(data, 'utf8')
|
||||||
});
|
})
|
||||||
|
|
||||||
stream.on('end', async () => {
|
stream.on('end', async () => {
|
||||||
const fileHash = hash.digest('hex');
|
const fileHash = hash.digest('hex')
|
||||||
const dbFile = await db.table('files')
|
const dbFile = await db.table('files')
|
||||||
.where(function() {
|
.where(function () {
|
||||||
if (userid === undefined) this.whereNull('userid');
|
if (userid === undefined) this.whereNull('userid')
|
||||||
else this.where('userid', userid.id);
|
else this.where('userid', userid.id)
|
||||||
})
|
})
|
||||||
.where({
|
.where({
|
||||||
hash: fileHash,
|
hash: fileHash,
|
||||||
size: file.size
|
size: file.size
|
||||||
})
|
})
|
||||||
.first();
|
.first()
|
||||||
|
|
||||||
if (!dbFile) {
|
if (!dbFile) {
|
||||||
files.push({
|
files.push({
|
||||||
name: file.filename,
|
name: file.filename,
|
||||||
original: file.originalname,
|
original: file.originalname,
|
||||||
type: file.mimetype,
|
type: file.mimetype,
|
||||||
size: file.size,
|
size: file.size,
|
||||||
hash: fileHash,
|
hash: fileHash,
|
||||||
ip: req.ip,
|
ip: req.ip,
|
||||||
albumid: album,
|
albumid: album,
|
||||||
userid: userid !== undefined ? userid.id : null,
|
userid: userid !== undefined ? userid.id : null,
|
||||||
timestamp: Math.floor(Date.now() / 1000)
|
timestamp: Math.floor(Date.now() / 1000)
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
uploadsController.deleteFile(file.filename).then(() => {}).catch(err => console.error(err));
|
uploadsController.deleteFile(file.filename).then(() => {}).catch(err => console.error(err))
|
||||||
existingFiles.push(dbFile);
|
existingFiles.push(dbFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iteration === req.files.length) {
|
if (iteration === req.files.length) {
|
||||||
return uploadsController.processFilesForDisplay(req, res, files, existingFiles);
|
return uploadsController.processFilesForDisplay(req, res, files, existingFiles)
|
||||||
}
|
}
|
||||||
iteration++;
|
iteration++
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
uploadsController.processFilesForDisplay = async (req, res, files, existingFiles) => {
|
uploadsController.processFilesForDisplay = async (req, res, files, existingFiles) => {
|
||||||
let basedomain = config.domain;
|
let basedomain = config.domain
|
||||||
if (files.length === 0) {
|
if (files.length === 0) {
|
||||||
return res.json({
|
return res.json({
|
||||||
success: true,
|
success: true,
|
||||||
files: existingFiles.map(file => {
|
files: existingFiles.map(file => {
|
||||||
return {
|
return {
|
||||||
name: file.name,
|
name: file.name,
|
||||||
size: file.size,
|
size: file.size,
|
||||||
url: `${basedomain}/${file.name}`
|
url: `${basedomain}/${file.name}`
|
||||||
};
|
}
|
||||||
})
|
})
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
await db.table('files').insert(files);
|
await db.table('files').insert(files)
|
||||||
for (let efile of existingFiles) files.push(efile);
|
for (let efile of existingFiles) files.push(efile)
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
files: files.map(file => {
|
files: files.map(file => {
|
||||||
return {
|
return {
|
||||||
name: file.name,
|
name: file.name,
|
||||||
size: file.size,
|
size: file.size,
|
||||||
url: `${basedomain}/${file.name}`
|
url: `${basedomain}/${file.name}`
|
||||||
};
|
}
|
||||||
})
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
for (let file of files) {
|
for (let file of files) {
|
||||||
let ext = path.extname(file.name).toLowerCase();
|
let ext = path.extname(file.name).toLowerCase()
|
||||||
if (utils.imageExtensions.includes(ext) || utils.videoExtensions.includes(ext)) {
|
if (utils.imageExtensions.includes(ext) || utils.videoExtensions.includes(ext)) {
|
||||||
file.thumb = `${basedomain}/thumbs/${file.name.slice(0, -ext.length)}.png`;
|
file.thumb = `${basedomain}/thumbs/${file.name.slice(0, -ext.length)}.png`
|
||||||
utils.generateThumbs(file);
|
utils.generateThumbs(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file.albumid) {
|
if (file.albumid) {
|
||||||
db.table('albums').where('id', file.albumid).update('editedAt', file.timestamp).then(() => {})
|
db.table('albums').where('id', file.albumid).update('editedAt', file.timestamp).then(() => {})
|
||||||
.catch(error => { console.log(error); res.json({ success: false, description: 'Error updating album' }); });
|
.catch(error => { console.log(error); res.json({ success: false, description: 'Error updating album' }) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
uploadsController.delete = async (req, res) => {
|
uploadsController.delete = async (req, res) => {
|
||||||
const user = await utils.authorize(req, res);
|
const user = await utils.authorize(req, res)
|
||||||
const id = req.body.id;
|
const id = req.body.id
|
||||||
if (id === undefined || id === '') {
|
if (id === undefined || id === '') {
|
||||||
return res.json({ success: false, description: 'No file specified' });
|
return res.json({ success: false, description: 'No file specified' })
|
||||||
}
|
}
|
||||||
|
|
||||||
const file = await db.table('files')
|
const file = await db.table('files')
|
||||||
.where('id', id)
|
.where('id', id)
|
||||||
.where(function() {
|
.where(function () {
|
||||||
if (user.username !== 'root') {
|
if (user.username !== 'root') {
|
||||||
this.where('userid', user.id);
|
this.where('userid', user.id)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.first();
|
.first()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await uploadsController.deleteFile(file.name);
|
await uploadsController.deleteFile(file.name)
|
||||||
await db.table('files').where('id', id).del();
|
await db.table('files').where('id', id).del()
|
||||||
if (file.albumid) {
|
if (file.albumid) {
|
||||||
await db.table('albums').where('id', file.albumid).update('editedAt', Math.floor(Date.now() / 1000));
|
await db.table('albums').where('id', file.albumid).update('editedAt', Math.floor(Date.now() / 1000))
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.json({ success: true });
|
return res.json({ success: true })
|
||||||
};
|
}
|
||||||
|
|
||||||
uploadsController.deleteFile = function(file) {
|
uploadsController.deleteFile = function (file) {
|
||||||
const ext = path.extname(file).toLowerCase();
|
const ext = path.extname(file).toLowerCase()
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fs.stat(path.join(__dirname, '..', config.uploads.folder, file), (err, stats) => {
|
fs.stat(path.join(__dirname, '..', config.uploads.folder, file), (err, stats) => {
|
||||||
if (err) { return reject(err); }
|
if (err) { return reject(err) }
|
||||||
fs.unlink(path.join(__dirname, '..', config.uploads.folder, file), err => {
|
fs.unlink(path.join(__dirname, '..', config.uploads.folder, file), err => {
|
||||||
if (err) { return reject(err); }
|
if (err) { return reject(err) }
|
||||||
if (!utils.imageExtensions.includes(ext) && !utils.videoExtensions.includes(ext)) {
|
if (!utils.imageExtensions.includes(ext) && !utils.videoExtensions.includes(ext)) {
|
||||||
return resolve();
|
return resolve()
|
||||||
}
|
}
|
||||||
file = file.substr(0, file.lastIndexOf('.')) + '.png';
|
file = file.substr(0, file.lastIndexOf('.')) + '.png'
|
||||||
fs.stat(path.join(__dirname, '..', config.uploads.folder, 'thumbs/', file), (err, stats) => {
|
fs.stat(path.join(__dirname, '..', config.uploads.folder, 'thumbs/', file), (err, stats) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err);
|
console.log(err)
|
||||||
return resolve();
|
return resolve()
|
||||||
}
|
}
|
||||||
fs.unlink(path.join(__dirname, '..', config.uploads.folder, 'thumbs/', file), err => {
|
fs.unlink(path.join(__dirname, '..', config.uploads.folder, 'thumbs/', file), err => {
|
||||||
if (err) { return reject(err); }
|
if (err) { return reject(err) }
|
||||||
return resolve();
|
return resolve()
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
uploadsController.list = async (req, res) => {
|
uploadsController.list = async (req, res) => {
|
||||||
const user = await utils.authorize(req, res);
|
const user = await utils.authorize(req, res)
|
||||||
|
|
||||||
let offset = req.params.page;
|
let offset = req.params.page
|
||||||
if (offset === undefined) offset = 0;
|
if (offset === undefined) offset = 0
|
||||||
|
|
||||||
const files = await db.table('files')
|
const files = await db.table('files')
|
||||||
.where(function() {
|
.where(function () {
|
||||||
if (req.params.id === undefined) this.where('id', '<>', '');
|
if (req.params.id === undefined) this.where('id', '<>', '')
|
||||||
else this.where('albumid', req.params.id);
|
else this.where('albumid', req.params.id)
|
||||||
})
|
})
|
||||||
.where(function() {
|
.where(function () {
|
||||||
if (user.username !== 'root') this.where('userid', user.id);
|
if (user.username !== 'root') this.where('userid', user.id)
|
||||||
})
|
})
|
||||||
.orderBy('id', 'DESC')
|
.orderBy('id', 'DESC')
|
||||||
.limit(25)
|
.limit(25)
|
||||||
.offset(25 * offset)
|
.offset(25 * offset)
|
||||||
.select('id', 'albumid', 'timestamp', 'name', 'userid');
|
.select('id', 'albumid', 'timestamp', 'name', 'userid')
|
||||||
|
|
||||||
const albums = await db.table('albums');
|
const albums = await db.table('albums')
|
||||||
let basedomain = config.domain;
|
let basedomain = config.domain
|
||||||
let userids = [];
|
let userids = []
|
||||||
|
|
||||||
for (let file of files) {
|
for (let file of files) {
|
||||||
file.file = `${basedomain}/${file.name}`;
|
file.file = `${basedomain}/${file.name}`
|
||||||
file.date = new Date(file.timestamp * 1000);
|
file.date = new Date(file.timestamp * 1000)
|
||||||
file.date = utils.getPrettyDate(file.date);
|
file.date = utils.getPrettyDate(file.date)
|
||||||
|
|
||||||
file.album = '';
|
file.album = ''
|
||||||
|
|
||||||
if (file.albumid !== undefined) {
|
if (file.albumid !== undefined) {
|
||||||
for (let album of albums) {
|
for (let album of albums) {
|
||||||
if (file.albumid === album.id) {
|
if (file.albumid === album.id) {
|
||||||
file.album = album.name;
|
file.album = album.name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only push usernames if we are root
|
// Only push usernames if we are root
|
||||||
if (user.username === 'root') {
|
if (user.username === 'root') {
|
||||||
if (file.userid !== undefined && file.userid !== null && file.userid !== '') {
|
if (file.userid !== undefined && file.userid !== null && file.userid !== '') {
|
||||||
userids.push(file.userid);
|
userids.push(file.userid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let ext = path.extname(file.name).toLowerCase();
|
let ext = path.extname(file.name).toLowerCase()
|
||||||
if (utils.imageExtensions.includes(ext) || utils.videoExtensions.includes(ext)) {
|
if (utils.imageExtensions.includes(ext) || utils.videoExtensions.includes(ext)) {
|
||||||
file.thumb = `${basedomain}/thumbs/${file.name.slice(0, -ext.length)}.png`;
|
file.thumb = `${basedomain}/thumbs/${file.name.slice(0, -ext.length)}.png`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we are a normal user, send response
|
// If we are a normal user, send response
|
||||||
if (user.username !== 'root') return res.json({ success: true, files });
|
if (user.username !== 'root') return res.json({ success: true, files })
|
||||||
|
|
||||||
// If we are root but there are no uploads attached to a user, send response
|
// If we are root but there are no uploads attached to a user, send response
|
||||||
if (userids.length === 0) return res.json({ success: true, files });
|
if (userids.length === 0) return res.json({ success: true, files })
|
||||||
|
|
||||||
const users = await db.table('users').whereIn('id', userids);
|
const users = await db.table('users').whereIn('id', userids)
|
||||||
for (let dbUser of users) {
|
for (let dbUser of users) {
|
||||||
for (let file of files) {
|
for (let file of files) {
|
||||||
if (file.userid === dbUser.id) {
|
if (file.userid === dbUser.id) {
|
||||||
file.username = dbUser.username;
|
file.username = dbUser.username
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.json({ success: true, files });
|
return res.json({ success: true, files })
|
||||||
};
|
}
|
||||||
|
|
||||||
module.exports = uploadsController;
|
module.exports = uploadsController
|
||||||
|
@ -1,67 +1,67 @@
|
|||||||
const path = require('path');
|
const path = require('path')
|
||||||
const config = require('../config.js');
|
const config = require('../config.js')
|
||||||
const fs = require('fs');
|
const fs = require('fs')
|
||||||
const gm = require('gm');
|
const gm = require('gm')
|
||||||
const ffmpeg = require('fluent-ffmpeg');
|
const ffmpeg = require('fluent-ffmpeg')
|
||||||
const db = require('knex')(config.database);
|
const db = require('knex')(config.database)
|
||||||
|
|
||||||
const utilsController = {};
|
const utilsController = {}
|
||||||
utilsController.imageExtensions = ['.jpg', '.jpeg', '.bmp', '.gif', '.png'];
|
utilsController.imageExtensions = ['.jpg', '.jpeg', '.bmp', '.gif', '.png']
|
||||||
utilsController.videoExtensions = ['.webm', '.mp4', '.wmv', '.avi', '.mov'];
|
utilsController.videoExtensions = ['.webm', '.mp4', '.wmv', '.avi', '.mov']
|
||||||
|
|
||||||
utilsController.getPrettyDate = function(date) {
|
utilsController.getPrettyDate = function (date) {
|
||||||
return date.getFullYear() + '-'
|
return date.getFullYear() + '-' +
|
||||||
+ (date.getMonth() + 1) + '-'
|
(date.getMonth() + 1) + '-' +
|
||||||
+ date.getDate() + ' '
|
date.getDate() + ' ' +
|
||||||
+ (date.getHours() < 10 ? '0' : '')
|
(date.getHours() < 10 ? '0' : '') +
|
||||||
+ date.getHours() + ':'
|
date.getHours() + ':' +
|
||||||
+ (date.getMinutes() < 10 ? '0' : '')
|
(date.getMinutes() < 10 ? '0' : '') +
|
||||||
+ date.getMinutes() + ':'
|
date.getMinutes() + ':' +
|
||||||
+ (date.getSeconds() < 10 ? '0' : '')
|
(date.getSeconds() < 10 ? '0' : '') +
|
||||||
+ date.getSeconds();
|
date.getSeconds()
|
||||||
}
|
}
|
||||||
|
|
||||||
utilsController.authorize = async (req, res) => {
|
utilsController.authorize = async (req, res) => {
|
||||||
const token = req.headers.token;
|
const token = req.headers.token
|
||||||
if (token === undefined) return res.status(401).json({ success: false, description: 'No token provided' });
|
if (token === undefined) return res.status(401).json({ success: false, description: 'No token provided' })
|
||||||
|
|
||||||
const user = await db.table('users').where('token', token).first();
|
const user = await db.table('users').where('token', token).first()
|
||||||
if (!user) return res.status(401).json({ success: false, description: 'Invalid token' });
|
if (!user) return res.status(401).json({ success: false, description: 'Invalid token' })
|
||||||
return user;
|
return user
|
||||||
};
|
}
|
||||||
|
|
||||||
utilsController.generateThumbs = function(file, basedomain) {
|
utilsController.generateThumbs = function (file, basedomain) {
|
||||||
if (config.uploads.generateThumbnails !== true) return;
|
if (config.uploads.generateThumbnails !== true) return
|
||||||
const ext = path.extname(file.name).toLowerCase();
|
const ext = path.extname(file.name).toLowerCase()
|
||||||
|
|
||||||
let thumbname = path.join(__dirname, '..', config.uploads.folder, 'thumbs', file.name.slice(0, -ext.length) + '.png');
|
let thumbname = path.join(__dirname, '..', config.uploads.folder, 'thumbs', file.name.slice(0, -ext.length) + '.png')
|
||||||
fs.access(thumbname, err => {
|
fs.access(thumbname, err => {
|
||||||
if (err && err.code === 'ENOENT') {
|
if (err && err.code === 'ENOENT') {
|
||||||
if (utilsController.videoExtensions.includes(ext)) {
|
if (utilsController.videoExtensions.includes(ext)) {
|
||||||
ffmpeg(path.join(__dirname, '..', config.uploads.folder, file.name))
|
ffmpeg(path.join(__dirname, '..', config.uploads.folder, file.name))
|
||||||
.thumbnail({
|
.thumbnail({
|
||||||
timestamps: [0],
|
timestamps: [0],
|
||||||
filename: '%b.png',
|
filename: '%b.png',
|
||||||
folder: path.join(__dirname, '..', config.uploads.folder, 'thumbs'),
|
folder: path.join(__dirname, '..', config.uploads.folder, 'thumbs'),
|
||||||
size: '200x?'
|
size: '200x?'
|
||||||
})
|
})
|
||||||
.on('error', error => console.log('Error - ', error.message));
|
.on('error', error => console.log('Error - ', error.message))
|
||||||
} else {
|
} else {
|
||||||
let size = {
|
let size = {
|
||||||
width: 200,
|
width: 200,
|
||||||
height: 200
|
height: 200
|
||||||
};
|
}
|
||||||
gm(path.join(__dirname, '..', config.uploads.folder, file.name))
|
gm(path.join(__dirname, '..', config.uploads.folder, file.name))
|
||||||
.resize(size.width, size.height + '>')
|
.resize(size.width, size.height + '>')
|
||||||
.gravity('Center')
|
.gravity('Center')
|
||||||
.extent(size.width, size.height)
|
.extent(size.width, size.height)
|
||||||
.background('transparent')
|
.background('transparent')
|
||||||
.write(thumbname, error => {
|
.write(thumbname, error => {
|
||||||
if (error) console.log('Error - ', error);
|
if (error) console.log('Error - ', error)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
module.exports = utilsController;
|
module.exports = utilsController
|
||||||
|
@ -1,50 +1,49 @@
|
|||||||
let init = function(db){
|
let init = function (db) {
|
||||||
|
// Create the tables we need to store galleries and files
|
||||||
|
db.schema.createTableIfNotExists('albums', function (table) {
|
||||||
|
table.increments()
|
||||||
|
table.integer('userid')
|
||||||
|
table.string('name')
|
||||||
|
table.string('identifier')
|
||||||
|
table.integer('enabled')
|
||||||
|
table.integer('timestamp')
|
||||||
|
}).then(() => {})
|
||||||
|
|
||||||
// Create the tables we need to store galleries and files
|
db.schema.createTableIfNotExists('files', function (table) {
|
||||||
db.schema.createTableIfNotExists('albums', function (table) {
|
table.increments()
|
||||||
table.increments()
|
table.integer('userid')
|
||||||
table.integer('userid')
|
table.string('name')
|
||||||
table.string('name')
|
table.string('original')
|
||||||
table.string('identifier')
|
table.string('type')
|
||||||
table.integer('enabled')
|
table.string('size')
|
||||||
table.integer('timestamp')
|
table.string('hash')
|
||||||
}).then(() => {})
|
table.string('ip')
|
||||||
|
table.integer('albumid')
|
||||||
|
table.integer('timestamp')
|
||||||
|
}).then(() => {})
|
||||||
|
|
||||||
db.schema.createTableIfNotExists('files', function (table) {
|
db.schema.createTableIfNotExists('users', function (table) {
|
||||||
table.increments()
|
table.increments()
|
||||||
table.integer('userid')
|
table.string('username')
|
||||||
table.string('name')
|
table.string('password')
|
||||||
table.string('original')
|
table.string('token')
|
||||||
table.string('type')
|
table.integer('timestamp')
|
||||||
table.string('size')
|
}).then(() => {
|
||||||
table.string('hash')
|
db.table('users').where({username: 'root'}).then((user) => {
|
||||||
table.string('ip')
|
if (user.length > 0) return
|
||||||
table.integer('albumid')
|
|
||||||
table.integer('timestamp')
|
|
||||||
}).then(() => {})
|
|
||||||
|
|
||||||
db.schema.createTableIfNotExists('users', function (table) {
|
require('bcrypt').hash('root', 10, function (err, hash) {
|
||||||
table.increments()
|
if (err) console.error('Error generating password hash for root')
|
||||||
table.string('username')
|
|
||||||
table.string('password')
|
|
||||||
table.string('token')
|
|
||||||
table.integer('timestamp')
|
|
||||||
}).then(() => {
|
|
||||||
db.table('users').where({username: 'root'}).then((user) => {
|
|
||||||
if(user.length > 0) return
|
|
||||||
|
|
||||||
require('bcrypt').hash('root', 10, function(err, hash) {
|
db.table('users').insert({
|
||||||
if(err) console.error('Error generating password hash for root')
|
username: 'root',
|
||||||
|
password: hash,
|
||||||
db.table('users').insert({
|
token: require('randomstring').generate(64),
|
||||||
username: 'root',
|
timestamp: Math.floor(Date.now() / 1000)
|
||||||
password: hash,
|
}).then(() => {})
|
||||||
token: require('randomstring').generate(64),
|
})
|
||||||
timestamp: Math.floor(Date.now() / 1000)
|
})
|
||||||
}).then(() => {})
|
})
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = init
|
module.exports = init
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
const config = require('../config.js');
|
const config = require('../config.js')
|
||||||
const db = require('knex')(config.database);
|
const db = require('knex')(config.database)
|
||||||
|
|
||||||
const migration = {};
|
const migration = {}
|
||||||
migration.start = async () => {
|
migration.start = async () => {
|
||||||
await db.schema.table('albums', table => {
|
await db.schema.table('albums', table => {
|
||||||
table.dateTime('editedAt');
|
table.dateTime('editedAt')
|
||||||
table.dateTime('zipGeneratedAt');
|
table.dateTime('zipGeneratedAt')
|
||||||
});
|
})
|
||||||
console.log('Migration finished! Now start lolisafe normally');
|
console.log('Migration finished! Now start lolisafe normally')
|
||||||
};
|
}
|
||||||
|
|
||||||
migration.start();
|
migration.start()
|
||||||
|
106
lolisafe.js
@ -1,58 +1,80 @@
|
|||||||
const config = require('./config.js');
|
const config = require('./config.js')
|
||||||
const api = require('./routes/api.js');
|
const api = require('./routes/api.js')
|
||||||
const album = require('./routes/album.js');
|
const album = require('./routes/album.js')
|
||||||
const express = require('express');
|
const express = require('express')
|
||||||
const helmet = require('helmet');
|
const helmet = require('helmet')
|
||||||
const bodyParser = require('body-parser');
|
const bodyParser = require('body-parser')
|
||||||
const RateLimit = require('express-rate-limit');
|
const RateLimit = require('express-rate-limit')
|
||||||
const db = require('knex')(config.database);
|
const db = require('knex')(config.database)
|
||||||
const fs = require('fs');
|
const fs = require('fs')
|
||||||
const exphbs = require('express-handlebars');
|
const exphbs = require('express-handlebars')
|
||||||
const safe = express();
|
const safe = express()
|
||||||
|
|
||||||
require('./database/db.js')(db);
|
require('./database/db.js')(db)
|
||||||
|
|
||||||
fs.existsSync('./pages/custom' ) || fs.mkdirSync('./pages/custom');
|
fs.existsSync('./pages/custom') || fs.mkdirSync('./pages/custom')
|
||||||
fs.existsSync('./' + config.logsFolder) || fs.mkdirSync('./' + config.logsFolder);
|
fs.existsSync('./' + config.logsFolder) || fs.mkdirSync('./' + config.logsFolder)
|
||||||
fs.existsSync('./' + config.uploads.folder) || fs.mkdirSync('./' + config.uploads.folder);
|
fs.existsSync('./' + config.uploads.folder) || fs.mkdirSync('./' + config.uploads.folder)
|
||||||
fs.existsSync('./' + config.uploads.folder + '/thumbs') || fs.mkdirSync('./' + config.uploads.folder + '/thumbs');
|
fs.existsSync('./' + config.uploads.folder + '/thumbs') || fs.mkdirSync('./' + config.uploads.folder + '/thumbs')
|
||||||
fs.existsSync('./' + config.uploads.folder + '/zips') || fs.mkdirSync('./' + config.uploads.folder + '/zips')
|
fs.existsSync('./' + config.uploads.folder + '/zips') || fs.mkdirSync('./' + config.uploads.folder + '/zips')
|
||||||
|
|
||||||
safe.use(helmet());
|
safe.use(helmet())
|
||||||
safe.set('trust proxy', 1);
|
safe.set('trust proxy', 1)
|
||||||
|
|
||||||
safe.engine('handlebars', exphbs({ defaultLayout: 'main' }));
|
safe.engine('handlebars', exphbs({ defaultLayout: 'main' }))
|
||||||
safe.set('view engine', 'handlebars');
|
safe.set('view engine', 'handlebars')
|
||||||
safe.enable('view cache');
|
safe.enable('view cache')
|
||||||
|
|
||||||
let limiter = new RateLimit({ windowMs: 5000, max: 2 });
|
let limiter = new RateLimit({ windowMs: 5000, max: 2 })
|
||||||
safe.use('/api/login/', limiter);
|
safe.use('/api/login/', limiter)
|
||||||
safe.use('/api/register/', limiter);
|
safe.use('/api/register/', limiter)
|
||||||
|
|
||||||
safe.use(bodyParser.urlencoded({ extended: true }));
|
safe.use(bodyParser.urlencoded({ extended: true }))
|
||||||
safe.use(bodyParser.json());
|
safe.use(bodyParser.json())
|
||||||
|
|
||||||
|
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) {
|
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('/', express.static('./public', { setHeaders }))
|
||||||
safe.use('/', album);
|
safe.use('/', album)
|
||||||
safe.use('/api', api);
|
safe.use('/api', api)
|
||||||
|
|
||||||
for (let page of config.pages) {
|
for (let page of config.pages) {
|
||||||
let root = './pages/';
|
let root = './pages/'
|
||||||
if (fs.existsSync(`./pages/custom/${page}.html`)) {
|
if (fs.existsSync(`./pages/custom/${page}.html`)) {
|
||||||
root = './pages/custom/';
|
root = './pages/custom/'
|
||||||
}
|
}
|
||||||
if (page === 'home') {
|
if (page === 'home') {
|
||||||
safe.get('/', (req, res, next) => res.sendFile(`${page}.html`, { root: root }));
|
safe.get('/', (req, res, next) => res.sendFile(`${page}.html`, { root: root }))
|
||||||
} else {
|
} else {
|
||||||
safe.get(`/${page}`, (req, res, next) => res.sendFile(`${page}.html`, { root: root }));
|
safe.get(`/${page}`, (req, res, next) => res.sendFile(`${page}.html`, { root: root }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
safe.use((req, res, next) => res.status(404).sendFile('404.html', { root: './pages/error/' }));
|
// NOTE: Uses fiery-me branch of https://github.com/BobbyWibowo/HttpErrorPages
|
||||||
safe.use((req, res, next) => res.status(500).sendFile('500.html', { root: './pages/error/' }));
|
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}`)
|
||||||
|
})
|
||||||
|
37
package.json
@ -14,35 +14,32 @@
|
|||||||
"node": ">=7.0.0"
|
"node": ">=7.0.0"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node ./lolisafe.js",
|
||||||
|
"pm2": "pm2 start --name lolisafe ./lolisafe.js "
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bcrypt": "^1.0.3",
|
"bcrypt": "^1.0.3",
|
||||||
"body-parser": "^1.16.0",
|
"body-parser": "^1.18.2",
|
||||||
"express": "^4.14.0",
|
"express": "^4.16.2",
|
||||||
"express-handlebars": "^3.0.0",
|
"express-handlebars": "^3.0.0",
|
||||||
"express-rate-limit": "^2.6.0",
|
"express-rate-limit": "^2.11.0",
|
||||||
"fluent-ffmpeg": "^2.1.0",
|
"fluent-ffmpeg": "^2.1.0",
|
||||||
"gm": "^1.23.0",
|
"gm": "^1.23.1",
|
||||||
"helmet": "^3.5.0",
|
"helmet": "^3.10.0",
|
||||||
"jszip": "^3.1.4",
|
"jszip": "^3.1.4",
|
||||||
"knex": "^0.12.6",
|
"knex": "^0.14.2",
|
||||||
"multer": "^1.2.1",
|
"multer": "^1.2.1",
|
||||||
"randomstring": "^1.1.5",
|
"randomstring": "^1.1.5",
|
||||||
"sqlite3": "^3.1.11"
|
"sqlite3": "^3.1.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^4.4.1",
|
"standard": "^10.0.3"
|
||||||
"eslint-config-aqua": "^1.4.1"
|
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"standard": {
|
||||||
"extends": [
|
"envs": [
|
||||||
"aqua"
|
"browser",
|
||||||
],
|
"node"
|
||||||
"env": {
|
]
|
||||||
"browser": true,
|
|
||||||
"node": true
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"func-names": 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
110
pages/album.html
@ -1,60 +1,72 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<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="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
|
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="https://lolisafe.moe/images/icons/apple-touch-icon.png?v=XBreOJMe24">
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" type="image/png" href="https://lolisafe.moe/images/icons/favicon-32x32.png?v=XBreOJMe24" sizes="32x32">
|
<meta name="description" content="A pomf-like file uploading service that doesn't suck.">
|
||||||
<link rel="icon" type="image/png" href="https://lolisafe.moe/images/icons/favicon-16x16.png?v=XBreOJMe24" sizes="16x16">
|
<meta name="keywords" content="upload,lolisafe,file,images,hosting,bobby,fiery">
|
||||||
<link rel="manifest" href="https://lolisafe.moe/images/icons/manifest.json?v=XBreOJMe24">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link rel="mask-icon" href="https://lolisafe.moe/images/icons/safari-pinned-tab.svg?v=XBreOJMe24" color="#5bbad5">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
<link rel="shortcut icon" href="https://lolisafe.moe/images/icons/favicon.ico?v=XBreOJMe24">
|
<title>safe.fiery.me – A small safe worth protecting.</title>
|
||||||
<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">
|
|
||||||
|
|
||||||
<meta property="og:url" content="https://lolisafe.moe" />
|
<!-- Stylesheets and scripts -->
|
||||||
<meta property="og:type" content="website" />
|
<link rel="stylesheet" type="text/css" href="libs/bulma/bulma.min.css">
|
||||||
<meta property="og:title" content="lolisafe.moe | A small safe worth protecting." />
|
<link rel="stylesheet" type="text/css" href="libs/sweetalert/sweetalert.min.css">
|
||||||
<meta property="og:description" content="A pomf-like file uploading service that doesn't suck." />
|
<link rel="stylesheet" type="text/css" href="css/style.css">
|
||||||
<meta property="og:image" content="http://lolisafe.moe/images/logo_square.png" />
|
<script type="text/javascript" src="libs/sweetalert/sweetalert.min.js"></script>
|
||||||
<meta property="og:image:secure_url" content="https://lolisafe.moe/images/logo_square.png" />
|
<script type="text/javascript" src="libs/axios/axios.min.js"></script>
|
||||||
|
<script type="text/javascript" src="js/album.js"></script>
|
||||||
|
|
||||||
<meta name="twitter:card" content="summary">
|
<!-- Open Graph tags -->
|
||||||
<meta name="twitter:title" content="lolisafe.moe | A small safe worth protecting.">
|
<meta property="og:type" content="website" />
|
||||||
<meta name="twitter:description" content="A pomf-like file uploading service that doesn't suck.">
|
<meta property="og:title" content="safe.fiery.me – A small safe worth protecting." />
|
||||||
<meta name="twitter:image" content="https://listen.moe/files/images/logo_square.png">
|
<meta property="og:url" content="https://safe.fiery.me/" />
|
||||||
<meta name="twitter:image:src" content="https://lolisafe.moe/images/logo_square.png">
|
<meta property="og:description" content="A pomf-like file uploading service that doesn't suck." />
|
||||||
|
<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" />
|
||||||
|
|
||||||
<title>lolisafe - A small safe worth protecting.</title>
|
<!-- Twitter Card tags -->
|
||||||
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.3.0/css/bulma.min.css">
|
<meta name="twitter:card" content="summary">
|
||||||
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.css">
|
<meta name="twitter:title" content="safe.fiery.me – A small safe worth protecting.">
|
||||||
<link rel="stylesheet" type="text/css" href="/css/style.css">
|
<meta name="twitter:description" content="A pomf-like file uploading service that doesn't suck.">
|
||||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.js"></script>
|
<meta name="twitter:image" content="https://safe.fiery.me/icons/600px.png">
|
||||||
<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>
|
<!-- 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">
|
||||||
|
|
||||||
<section class="hero is-fullheight">
|
</head>
|
||||||
<div class="hero-head">
|
|
||||||
<div class="container">
|
|
||||||
<h1 class="title" id='title' style='margin-top: 1.5rem;'></h1>
|
|
||||||
<h1 class="subtitle" id='count'></h1>
|
|
||||||
<hr>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="hero-body">
|
|
||||||
<div class="container" id='container'>
|
|
||||||
|
|
||||||
</div>
|
<body>
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
</body>
|
<section class="hero is-fullheight">
|
||||||
|
<div class="hero-head">
|
||||||
|
<div class="container">
|
||||||
|
<h1 class="title" id='title' style='margin-top: 1.5rem;'></h1>
|
||||||
|
<h1 class="subtitle" id='count'></h1>
|
||||||
|
<hr>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hero-body">
|
||||||
|
<div class="container" id='container'>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
161
pages/auth.html
@ -1,87 +1,100 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
|
||||||
<meta name="description" content="A pomf-like file uploading service that doesn't suck.">
|
<meta charset="utf-8" />
|
||||||
<meta name="keywords" content="upload,lolisafe,file,images,hosting">
|
<meta name="description" content="A pomf-like file uploading service that doesn't suck.">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="keywords" content="upload,lolisafe,file,images,hosting,bobby,fiery">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
|
<title>safe.fiery.me – 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">
|
<!-- Stylesheets and scripts -->
|
||||||
<link rel="icon" type="image/png" href="https://lolisafe.moe/images/icons/favicon-32x32.png?v=XBreOJMe24" sizes="32x32">
|
<link rel="stylesheet" type="text/css" href="libs/bulma/bulma.min.css">
|
||||||
<link rel="icon" type="image/png" href="https://lolisafe.moe/images/icons/favicon-16x16.png?v=XBreOJMe24" sizes="16x16">
|
<link rel="stylesheet" type="text/css" href="libs/sweetalert/sweetalert.min.css">
|
||||||
<link rel="manifest" href="https://lolisafe.moe/images/icons/manifest.json?v=XBreOJMe24">
|
<link rel="stylesheet" type="text/css" href="libs/fontello/fontello.css">
|
||||||
<link rel="mask-icon" href="https://lolisafe.moe/images/icons/safari-pinned-tab.svg?v=XBreOJMe24" color="#5bbad5">
|
<link rel="stylesheet" type="text/css" href="css/style.css">
|
||||||
<link rel="shortcut icon" href="https://lolisafe.moe/images/icons/favicon.ico?v=XBreOJMe24">
|
<script type="text/javascript" src="libs/sweetalert/sweetalert.min.js"></script>
|
||||||
<meta name="apple-mobile-web-app-title" content="lolisafe">
|
<script type="text/javascript" src="libs/axios/axios.min.js"></script>
|
||||||
<meta name="application-name" content="lolisafe">
|
<script type="text/javascript" src="js/auth.js"></script>
|
||||||
<meta name="msapplication-config" content="https://lolisafe.moe/images/icons/browserconfig.xml?v=XBreOJMe24">
|
|
||||||
<meta name="theme-color" content="#ffffff">
|
|
||||||
|
|
||||||
<meta property="og:url" content="https://lolisafe.moe" />
|
<!-- Open Graph tags -->
|
||||||
<meta property="og:type" content="website" />
|
<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 – A small safe worth protecting." />
|
||||||
<meta property="og:description" content="A pomf-like file uploading service that doesn't suck." />
|
<meta property="og:url" content="https://safe.fiery.me/" />
|
||||||
<meta property="og:image" content="http://lolisafe.moe/images/logo_square.png" />
|
<meta property="og:description" content="A pomf-like file uploading service that doesn't suck." />
|
||||||
<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" />
|
||||||
|
|
||||||
<meta name="twitter:card" content="summary">
|
<!-- Twitter Card tags -->
|
||||||
<meta name="twitter:title" content="lolisafe.moe | A small safe worth protecting.">
|
<meta name="twitter:card" content="summary">
|
||||||
<meta name="twitter:description" content="A pomf-like file uploading service that doesn't suck.">
|
<meta name="twitter:title" content="safe.fiery.me – A small safe worth protecting.">
|
||||||
<meta name="twitter:image" content="https://listen.moe/files/images/logo_square.png">
|
<meta name="twitter:description" content="A pomf-like file uploading service that doesn't suck.">
|
||||||
<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>
|
<!-- Icons and configs -->
|
||||||
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.3.0/css/bulma.min.css">
|
<link rel="icon" type="image/png" href="https://safe.fiery.me/icons/32pxr.png" sizes="32x32">
|
||||||
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.css">
|
<link rel="icon" type="image/png" href="https://safe.fiery.me/icons/96pxr.png" sizes="96x96">
|
||||||
<link rel="stylesheet" type="text/css" href="/css/style.css">
|
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/120px.png" sizes="120x120">
|
||||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.js"></script>
|
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/152px.png" sizes="152x152">
|
||||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.15.3/axios.min.js"></script>
|
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/167px.png" sizes="167x167">
|
||||||
<script type="text/javascript" src="https://use.fontawesome.com/cd26baa9bd.js"></script>
|
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/180px.png" sizes="180x180">
|
||||||
<script type="text/javascript" src="/js/auth.js"></script>
|
<link rel="manifest" href="https://safe.fiery.me/icons/manifest.json?v=hEjFGFcHY8">
|
||||||
</head>
|
<meta name="apple-mobile-web-app-title" content="safe.fiery.me">
|
||||||
<body>
|
<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>
|
||||||
section#login {
|
/** Colors based on KDE Breeze Dark **/
|
||||||
background-color: #f5f6f8;
|
section#login {
|
||||||
}
|
background-color: #232629;
|
||||||
</style>
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<section id='login' class="hero is-fullheight">
|
</head>
|
||||||
<div class="hero-body">
|
|
||||||
<div class="container">
|
|
||||||
<h1 class="title">
|
|
||||||
Dashboard Access
|
|
||||||
</h1>
|
|
||||||
<h2 class="subtitle">
|
|
||||||
Login or register
|
|
||||||
</h2>
|
|
||||||
<div class="columns">
|
|
||||||
<div class="column">
|
|
||||||
<p class="control">
|
|
||||||
<input id='user' class="input" type="text" placeholder="Your username">
|
|
||||||
</p>
|
|
||||||
<p class="control">
|
|
||||||
<input id='pass' class="input" type="password" placeholder="Your password">
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p class="control has-addons is-pulled-right">
|
<body>
|
||||||
<a class="button" id='registerBtn' onclick="page.do('register')">
|
|
||||||
<span>Register</span>
|
|
||||||
</a>
|
|
||||||
<a class="button" id='loginBtn' onclick="page.do('login')">
|
|
||||||
<span>Log in</span>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
</div>
|
<section id='login' class="hero is-fullheight">
|
||||||
<div class="column is-hidden-mobile"></div>
|
<div class="hero-body">
|
||||||
<div class="column is-hidden-mobile"></div>
|
<div class="container">
|
||||||
</div>
|
<h1 class="title">
|
||||||
</div>
|
Dashboard Access
|
||||||
</div>
|
</h1>
|
||||||
</section>
|
<h2 class="subtitle">
|
||||||
|
Login or register
|
||||||
|
</h2>
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column">
|
||||||
|
<p class="control">
|
||||||
|
<input id='user' class="input" type="text" placeholder="Your username">
|
||||||
|
</p>
|
||||||
|
<p class="control">
|
||||||
|
<input id='pass' class="input" type="password" placeholder="Your password">
|
||||||
|
</p>
|
||||||
|
|
||||||
</body>
|
<p class="control has-addons is-pulled-right">
|
||||||
|
<a class="button" id='registerBtn' onclick="page.do('register')">
|
||||||
|
<span>Register</span>
|
||||||
|
</a>
|
||||||
|
<a class="button" id='loginBtn' onclick="page.do('login')">
|
||||||
|
<span>Log in</span>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="column is-hidden-mobile"></div>
|
||||||
|
<div class="column is-hidden-mobile"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,100 +1,113 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
|
||||||
<meta name="description" content="A pomf-like file uploading service that doesn't suck.">
|
<meta charset="utf-8" />
|
||||||
<meta name="keywords" content="upload,lolisafe,file,images,hosting">
|
<meta name="description" content="A pomf-like file uploading service that doesn't suck.">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="keywords" content="upload,lolisafe,file,images,hosting,bobby,fiery">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
|
<title>safe.fiery.me – 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">
|
<!-- Stylesheets and scripts -->
|
||||||
<link rel="icon" type="image/png" href="https://lolisafe.moe/images/icons/favicon-32x32.png?v=XBreOJMe24" sizes="32x32">
|
<link rel="stylesheet" type="text/css" href="libs/bulma/bulma.min.css">
|
||||||
<link rel="icon" type="image/png" href="https://lolisafe.moe/images/icons/favicon-16x16.png?v=XBreOJMe24" sizes="16x16">
|
<link rel="stylesheet" type="text/css" href="libs/sweetalert/sweetalert.min.css">
|
||||||
<link rel="manifest" href="https://lolisafe.moe/images/icons/manifest.json?v=XBreOJMe24">
|
<link rel="stylesheet" type="text/css" href="libs/fontello/fontello.css">
|
||||||
<link rel="mask-icon" href="https://lolisafe.moe/images/icons/safari-pinned-tab.svg?v=XBreOJMe24" color="#5bbad5">
|
<link rel="stylesheet" type="text/css" href="css/style.css">
|
||||||
<link rel="shortcut icon" href="https://lolisafe.moe/images/icons/favicon.ico?v=XBreOJMe24">
|
<link rel="stylesheet" type="text/css" href="css/dashboard.css">
|
||||||
<meta name="apple-mobile-web-app-title" content="lolisafe">
|
<script type="text/javascript" src="libs/sweetalert/sweetalert.min.js"></script>
|
||||||
<meta name="application-name" content="lolisafe">
|
<script type="text/javascript" src="libs/axios/axios.min.js"></script>
|
||||||
<meta name="msapplication-config" content="https://lolisafe.moe/images/icons/browserconfig.xml?v=XBreOJMe24">
|
<script type="text/javascript" src="js/dashboard.js"></script>
|
||||||
<meta name="theme-color" content="#ffffff">
|
|
||||||
|
|
||||||
<meta property="og:url" content="https://lolisafe.moe" />
|
<!-- Open Graph tags -->
|
||||||
<meta property="og:type" content="website" />
|
<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 – A small safe worth protecting." />
|
||||||
<meta property="og:description" content="A pomf-like file uploading service that doesn't suck." />
|
<meta property="og:url" content="https://safe.fiery.me/" />
|
||||||
<meta property="og:image" content="http://lolisafe.moe/images/logo_square.png" />
|
<meta property="og:description" content="A pomf-like file uploading service that doesn't suck." />
|
||||||
<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" />
|
||||||
|
|
||||||
<meta name="twitter:card" content="summary">
|
<!-- Twitter Card tags -->
|
||||||
<meta name="twitter:title" content="lolisafe.moe | A small safe worth protecting.">
|
<meta name="twitter:card" content="summary">
|
||||||
<meta name="twitter:description" content="A pomf-like file uploading service that doesn't suck.">
|
<meta name="twitter:title" content="safe.fiery.me – A small safe worth protecting.">
|
||||||
<meta name="twitter:image" content="https://listen.moe/files/images/logo_square.png">
|
<meta name="twitter:description" content="A pomf-like file uploading service that doesn't suck.">
|
||||||
<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>
|
<!-- Icons and configs -->
|
||||||
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.3.0/css/bulma.min.css">
|
<link rel="icon" type="image/png" href="https://safe.fiery.me/icons/32pxr.png" sizes="32x32">
|
||||||
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.css">
|
<link rel="icon" type="image/png" href="https://safe.fiery.me/icons/96pxr.png" sizes="96x96">
|
||||||
<link rel="stylesheet" type="text/css" href="/css/style.css">
|
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/120px.png" sizes="120x120">
|
||||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.js"></script>
|
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/152px.png" sizes="152x152">
|
||||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.15.3/axios.min.js"></script>
|
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/167px.png" sizes="167x167">
|
||||||
<script type="text/javascript" src="https://use.fontawesome.com/cd26baa9bd.js"></script>
|
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/180px.png" sizes="180x180">
|
||||||
<script type="text/javascript" src="/js/dashboard.js"></script>
|
<link rel="manifest" href="https://safe.fiery.me/icons/manifest.json?v=hEjFGFcHY8">
|
||||||
</head>
|
<meta name="apple-mobile-web-app-title" content="safe.fiery.me">
|
||||||
<body>
|
<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">
|
||||||
|
|
||||||
<section id='auth' class="hero is-light is-fullheight">
|
</head>
|
||||||
|
|
||||||
<div class="hero-body">
|
<body>
|
||||||
<div class="container">
|
|
||||||
<h1 class="title">
|
|
||||||
Admin dashboard
|
|
||||||
</h1>
|
|
||||||
<h2 class="subtitle">
|
|
||||||
<p class="control has-addons">
|
|
||||||
<input id='token' class="input is-danger" type="text" placeholder="Your admin token">
|
|
||||||
<a id='tokenSubmit' class="button is-danger is-outlined">Check</a>
|
|
||||||
</p>
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</section>
|
<section id='auth' class="hero is-light is-fullheight">
|
||||||
|
|
||||||
<section id='dashboard' class="section">
|
<div class="hero-body">
|
||||||
|
<div class="container">
|
||||||
|
<h1 class="title">
|
||||||
|
Admin dashboard
|
||||||
|
</h1>
|
||||||
|
<h2 class="subtitle">
|
||||||
|
<p class="control has-addons">
|
||||||
|
<input id='token' class="input is-danger" type="text" placeholder="Your admin token">
|
||||||
|
<a id='tokenSubmit' class="button is-danger is-outlined">Check</a>
|
||||||
|
</p>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="panel" class="container">
|
</section>
|
||||||
<h1 class="title">Dashboard</h1>
|
|
||||||
<h2 class="subtitle">A simple <strong>dashboard</strong>, to sort your uploaded stuff</h2>
|
|
||||||
<hr>
|
|
||||||
<div class="columns">
|
|
||||||
<div class="column is-3">
|
|
||||||
<aside class="menu" id="menu">
|
|
||||||
<p class="menu-label">General</p>
|
|
||||||
<ul class="menu-list">
|
|
||||||
<li><a href="/">Frontpage</a></li>
|
|
||||||
<li><a id="itemUploads" onclick="panel.getUploads()">Uploads</a></li>
|
|
||||||
</ul>
|
|
||||||
<p class="menu-label">Albums</p>
|
|
||||||
<ul class="menu-list">
|
|
||||||
<li><a id="itemManageGallery" onclick="panel.getAlbums()">Manage your albums</a></li>
|
|
||||||
<li>
|
|
||||||
<ul id='albumsContainer'></ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<p class="menu-label">Administration</p>
|
|
||||||
<ul class="menu-list">
|
|
||||||
<li><a id="itemTokens" onclick="panel.changeToken()">Change your token</a></li>
|
|
||||||
<li><a id="itemPassword" onclick="panel.changePassword()">Change your password</a></li>
|
|
||||||
<li><a id="itemLogout"onclick="panel.logout()">Logout</a></li>
|
|
||||||
</ul>
|
|
||||||
</aside>
|
|
||||||
</div>
|
|
||||||
<div class="column has-text-centered" id='page'>
|
|
||||||
<img src="/images/logo.png">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</section>
|
<section id='dashboard' class="section">
|
||||||
</body>
|
|
||||||
|
<div id="panel" class="container">
|
||||||
|
<h1 class="title">Dashboard</h1>
|
||||||
|
<h2 class="subtitle">A simple <strong>dashboard</strong>, to sort your uploaded stuff</h2>
|
||||||
|
<hr>
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column is-3">
|
||||||
|
<aside class="menu" id="menu">
|
||||||
|
<p class="menu-label">General</p>
|
||||||
|
<ul class="menu-list">
|
||||||
|
<li><a href=".">Frontpage</a></li>
|
||||||
|
<li><a id="itemUploads" onclick="panel.getUploads()">Uploads</a></li>
|
||||||
|
</ul>
|
||||||
|
<p class="menu-label">Albums</p>
|
||||||
|
<ul class="menu-list">
|
||||||
|
<li><a id="itemManageGallery" onclick="panel.getAlbums()">Manage your albums</a></li>
|
||||||
|
<li>
|
||||||
|
<ul id='albumsContainer'></ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p class="menu-label">Administration</p>
|
||||||
|
<ul class="menu-list">
|
||||||
|
<li><a id="itemTokens" onclick="panel.changeToken()">Change your token</a></li>
|
||||||
|
<li><a id="itemPassword" onclick="panel.changePassword()">Change your password</a></li>
|
||||||
|
<li><a id="itemLogout"onclick="panel.logout()">Logout</a></li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
<div class="column has-text-centered" id='page'>
|
||||||
|
<img src="images/logo.png">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -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>
|
|
@ -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>
|
|
167
pages/faq.html
@ -1,83 +1,114 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<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="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
|
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="https://lolisafe.moe/images/icons/apple-touch-icon.png?v=XBreOJMe24">
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" type="image/png" href="https://lolisafe.moe/images/icons/favicon-32x32.png?v=XBreOJMe24" sizes="32x32">
|
<meta name="description" content="A pomf-like file uploading service that doesn't suck.">
|
||||||
<link rel="icon" type="image/png" href="https://lolisafe.moe/images/icons/favicon-16x16.png?v=XBreOJMe24" sizes="16x16">
|
<meta name="keywords" content="upload,lolisafe,file,images,hosting,bobby,fiery">
|
||||||
<link rel="manifest" href="https://lolisafe.moe/images/icons/manifest.json?v=XBreOJMe24">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link rel="mask-icon" href="https://lolisafe.moe/images/icons/safari-pinned-tab.svg?v=XBreOJMe24" color="#5bbad5">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
<link rel="shortcut icon" href="https://lolisafe.moe/images/icons/favicon.ico?v=XBreOJMe24">
|
<title>safe.fiery.me – A small safe worth protecting.</title>
|
||||||
<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">
|
|
||||||
|
|
||||||
<meta property="og:url" content="https://lolisafe.moe" />
|
<!-- Stylesheets and scripts -->
|
||||||
<meta property="og:type" content="website" />
|
<link rel="stylesheet" type="text/css" href="libs/bulma/bulma.min.css">
|
||||||
<meta property="og:title" content="lolisafe.moe | A small safe worth protecting." />
|
<link rel="stylesheet" type="text/css" href="css/style.css">
|
||||||
<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 name="twitter:card" content="summary">
|
<!-- Open Graph tags -->
|
||||||
<meta name="twitter:title" content="lolisafe.moe | A small safe worth protecting.">
|
<meta property="og:type" content="website" />
|
||||||
<meta name="twitter:description" content="A pomf-like file uploading service that doesn't suck.">
|
<meta property="og:title" content="safe.fiery.me – A small safe worth protecting." />
|
||||||
<meta name="twitter:image" content="https://listen.moe/files/images/logo_square.png">
|
<meta property="og:url" content="https://safe.fiery.me/" />
|
||||||
<meta name="twitter:image:src" content="https://lolisafe.moe/images/logo_square.png">
|
<meta property="og:description" content="A pomf-like file uploading service that doesn't suck." />
|
||||||
|
<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" />
|
||||||
|
|
||||||
<title>lolisafe - A small safe worth protecting.</title>
|
<!-- Twitter Card tags -->
|
||||||
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.3.0/css/bulma.min.css">
|
<meta name="twitter:card" content="summary">
|
||||||
<link rel="stylesheet" type="text/css" href="/css/style.css">
|
<meta name="twitter:title" content="safe.fiery.me – A small safe worth protecting.">
|
||||||
</head>
|
<meta name="twitter:description" content="A pomf-like file uploading service that doesn't suck.">
|
||||||
|
<meta name="twitter:image" content="https://safe.fiery.me/icons/600px.png">
|
||||||
|
|
||||||
<body>
|
<!-- Icons and configs -->
|
||||||
<section class="hero is-fullheight has-text-centered" id="home">
|
<link rel="icon" type="image/png" href="https://safe.fiery.me/icons/32pxr.png" sizes="32x32">
|
||||||
<div class="hero-body">
|
<link rel="icon" type="image/png" href="https://safe.fiery.me/icons/96pxr.png" sizes="96x96">
|
||||||
<div class="container has-text-left">
|
<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">
|
||||||
|
|
||||||
<h2 class='subtitle'>What is lolisafe?</h2>
|
<style>
|
||||||
<article class="message">
|
/** Colors based on KDE Breeze Dark **/
|
||||||
<div class="message-body">
|
.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.
|
color: #eff0f1;
|
||||||
</div>
|
background-color: #31363b;
|
||||||
</article>
|
border-color: #898b8d;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<h2 class='subtitle'>Will you keep my files forever?</h2>
|
</head>
|
||||||
<article class="message">
|
|
||||||
<div class="message-body">
|
|
||||||
Unless we receive a copyright complain or some other bullshit, we will.
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<h2 class='subtitle'>How can I keep track of my uploads?</h2>
|
<body>
|
||||||
<article class="message">
|
|
||||||
<div class="message-body">
|
|
||||||
Simply create a user on the site and every upload will be associated with your account, granting you access to your uploaded files through our dashboard.
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<h2 class='subtitle'>What are albums?</h2>
|
<section class="hero is-fullheight has-text-centered" id="home">
|
||||||
<article class="message">
|
<div class="hero-body">
|
||||||
<div class="message-body">
|
<div class="container has-text-left">
|
||||||
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.
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<h2 class='subtitle'>Why should I use this?</h2>
|
<h2 class='subtitle'>What is safe.fiery.me?</h2>
|
||||||
<article class="message">
|
<article class="message">
|
||||||
<div class="message-body">
|
<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.
|
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>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
</div>
|
<h2 class='subtitle'>Will you keep my files forever?</h2>
|
||||||
</div>
|
<article class="message">
|
||||||
</section>
|
<div class="message-body">
|
||||||
|
Unless we receive a copyright complain or some other bullshit, we will.
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
</body>
|
<h2 class='subtitle'>How can I keep track of my uploads?</h2>
|
||||||
|
<article class="message">
|
||||||
|
<div class="message-body">
|
||||||
|
Simply create a user on the site and every upload will be associated with your account, granting you access to your uploaded files through our dashboard.
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<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 <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">
|
||||||
|
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>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
173
pages/home.html
@ -1,93 +1,106 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<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="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
|
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="https://lolisafe.moe/images/icons/apple-touch-icon.png?v=XBreOJMe24">
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" type="image/png" href="https://lolisafe.moe/images/icons/favicon-32x32.png?v=XBreOJMe24" sizes="32x32">
|
<meta name="description" content="A pomf-like file uploading service that doesn't suck.">
|
||||||
<link rel="icon" type="image/png" href="https://lolisafe.moe/images/icons/favicon-16x16.png?v=XBreOJMe24" sizes="16x16">
|
<meta name="keywords" content="upload,lolisafe,file,images,hosting,bobby,fiery">
|
||||||
<link rel="manifest" href="https://lolisafe.moe/images/icons/manifest.json?v=XBreOJMe24">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link rel="mask-icon" href="https://lolisafe.moe/images/icons/safari-pinned-tab.svg?v=XBreOJMe24" color="#5bbad5">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
<link rel="shortcut icon" href="https://lolisafe.moe/images/icons/favicon.ico?v=XBreOJMe24">
|
<title>safe.fiery.me – A small safe worth protecting.</title>
|
||||||
<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">
|
|
||||||
|
|
||||||
<meta property="og:url" content="https://lolisafe.moe" />
|
<!-- Stylesheets and scripts -->
|
||||||
<meta property="og:type" content="website" />
|
<link rel="stylesheet" type="text/css" href="libs/bulma/bulma.min.css">
|
||||||
<meta property="og:title" content="lolisafe.moe | A small safe worth protecting." />
|
<link rel="stylesheet" type="text/css" href="libs/sweetalert/sweetalert.min.css">
|
||||||
<meta property="og:description" content="A pomf-like file uploading service that doesn't suck." />
|
<link rel="stylesheet" type="text/css" href="css/style.css">
|
||||||
<meta property="og:image" content="http://lolisafe.moe/images/logo_square.png" />
|
<script type="text/javascript" src="libs/sweetalert/sweetalert.min.js"></script>
|
||||||
<meta property="og:image:secure_url" content="https://lolisafe.moe/images/logo_square.png" />
|
<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 name="twitter:card" content="summary">
|
<!-- Open Graph tags -->
|
||||||
<meta name="twitter:title" content="lolisafe.moe | A small safe worth protecting.">
|
<meta property="og:type" content="website" />
|
||||||
<meta name="twitter:description" content="A pomf-like file uploading service that doesn't suck.">
|
<meta property="og:title" content="safe.fiery.me – A small safe worth protecting." />
|
||||||
<meta name="twitter:image" content="https://listen.moe/files/images/logo_square.png">
|
<meta property="og:url" content="https://safe.fiery.me/" />
|
||||||
<meta name="twitter:image:src" content="https://lolisafe.moe/images/logo_square.png">
|
<meta property="og:description" content="A pomf-like file uploading service that doesn't suck." />
|
||||||
|
<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" />
|
||||||
|
|
||||||
<title>lolisafe - A small safe worth protecting.</title>
|
<!-- Twitter Card tags -->
|
||||||
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.3.0/css/bulma.min.css">
|
<meta name="twitter:card" content="summary">
|
||||||
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.css">
|
<meta name="twitter:title" content="safe.fiery.me – A small safe worth protecting.">
|
||||||
<link rel="stylesheet" type="text/css" href="/css/style.css">
|
<meta name="twitter:description" content="A pomf-like file uploading service that doesn't suck.">
|
||||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.js"></script>
|
<meta name="twitter:image" content="https://safe.fiery.me/icons/600px.png">
|
||||||
<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>
|
<!-- Icons and configs -->
|
||||||
<section class="hero is-fullheight has-text-centered" id="home">
|
<link rel="icon" type="image/png" href="https://safe.fiery.me/icons/32pxr.png" sizes="32x32">
|
||||||
<div class="hero-body">
|
<link rel="icon" type="image/png" href="https://safe.fiery.me/icons/96pxr.png" sizes="96x96">
|
||||||
<div class="container">
|
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/120px.png" sizes="120x120">
|
||||||
<p id="b">
|
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/152px.png" sizes="152x152">
|
||||||
<img class="logo" src="/images/logo_smol.png">
|
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/167px.png" sizes="167x167">
|
||||||
</p>
|
<link rel="apple-touch-icon" href="https://safe.fiery.me/icons/180px.png" sizes="180x180">
|
||||||
<h1 class="title">lolisafe</h1>
|
<link rel="manifest" href="https://safe.fiery.me/icons/manifest.json?v=hEjFGFcHY8">
|
||||||
<h2 class="subtitle">A <strong>modern</strong> self-hosted file upload service</h2>
|
<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">
|
||||||
|
|
||||||
<h3 class="subtitle" id="maxFileSize"></h3>
|
</head>
|
||||||
<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>
|
|
||||||
<div class="field" id="albumDiv" style="display: none">
|
|
||||||
<p class="control select-wrapper">
|
|
||||||
<span class="select">
|
|
||||||
<select id="albumSelect">
|
|
||||||
<option value="">Upload to album</option>
|
|
||||||
</select>
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="column is-hidden-mobile"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="uploads">
|
<body>
|
||||||
<div id="template" class="columns">
|
|
||||||
<div class="column is-hidden-mobile"></div>
|
|
||||||
<div class="column">
|
|
||||||
<progress class="progress is-small is-danger" value="0" max="100" data-dz-uploadprogress></progress>
|
|
||||||
<p data-dz-errormessage></p>
|
|
||||||
<p class="link"></p>
|
|
||||||
</div>
|
|
||||||
<div class="column is-hidden-mobile"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h3 class="subtitle"><a href="/auth" id="loginLinkText"></a></h3>
|
<section class="hero is-fullheight has-text-centered" id="home">
|
||||||
<h3 id="links">
|
<div class="hero-body">
|
||||||
<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>
|
<div class="container">
|
||||||
</h3>
|
<p id="b">
|
||||||
|
<img class="logo" src="images/logo_smol.png">
|
||||||
|
</p>
|
||||||
|
<h1 class="title">safe.fiery.me</h1>
|
||||||
|
<h2 class="subtitle">A <strong>modern</strong> self-hosted file upload service</h2>
|
||||||
|
|
||||||
</div>
|
<h3 class="subtitle" id="maxFileSize"></h3>
|
||||||
</div>
|
<div class="columns">
|
||||||
</section>
|
<div class="column is-hidden-mobile"></div>
|
||||||
|
<div class="column" id="uploadContainer">
|
||||||
|
<a id="loginToUpload" href="auth" class="button is-danger">Loading…</a>
|
||||||
|
<div class="field" id="albumDiv" style="display: none">
|
||||||
|
<p class="control select-wrapper">
|
||||||
|
<span class="select">
|
||||||
|
<select id="albumSelect">
|
||||||
|
<option value="">Upload to album</option>
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column is-hidden-mobile"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</body>
|
<div id="uploads">
|
||||||
|
<div id="template" class="columns">
|
||||||
|
<div class="column is-hidden-mobile"></div>
|
||||||
|
<div class="column">
|
||||||
|
<progress class="progress is-small is-danger" value="0" max="100" data-dz-uploadprogress></progress>
|
||||||
|
<p data-dz-errormessage></p>
|
||||||
|
<p class="link"></p>
|
||||||
|
</div>
|
||||||
|
<div class="column is-hidden-mobile"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 class="subtitle"><a href="auth" id="loginLinkText"></a></h3>
|
||||||
|
<h3 id="links">
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
80
public/css/dashboard.css
Normal 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;
|
||||||
|
}
|
@ -1,31 +1,31 @@
|
|||||||
/* ------------------
|
/* ------------------
|
||||||
HOME
|
HOME
|
||||||
------------------ */
|
------------------ */
|
||||||
|
|
||||||
section#home #b {
|
section#home #b {
|
||||||
-webkit-animation-delay: 0.5s;
|
-webkit-animation-delay: 0.5s;
|
||||||
animation-delay: 0.5s;
|
animation-delay: 0.5s;
|
||||||
-webkit-animation-duration: 1.5s;
|
-webkit-animation-duration: 1.5s;
|
||||||
animation-duration: 1.5s;
|
animation-duration: 1.5s;
|
||||||
-webkit-animation-fill-mode: both;
|
-webkit-animation-fill-mode: both;
|
||||||
animation-fill-mode: both;
|
animation-fill-mode: both;
|
||||||
-webkit-animation-name: floatUp;
|
-webkit-animation-name: floatUp;
|
||||||
animation-name: floatUp;
|
animation-name: floatUp;
|
||||||
-webkit-animation-timing-function: cubic-bezier(0, 0.71, 0.29, 1);
|
-webkit-animation-timing-function: cubic-bezier(0, 0.71, 0.29, 1);
|
||||||
animation-timing-function: cubic-bezier(0, 0.71, 0.29, 1);
|
animation-timing-function: cubic-bezier(0, 0.71, 0.29, 1);
|
||||||
border-radius: 24px;
|
border-radius: 24px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: 240px;
|
height: 240px;
|
||||||
margin-bottom: 40px;
|
margin-bottom: 40px;
|
||||||
position: relative;
|
position: relative;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
width: 240px;
|
width: 240px;
|
||||||
box-shadow: 0 20px 60px rgba(10, 10, 10, 0.05), 0 5px 10px rgba(10, 10, 10, 0.1), 0 1px 1px rgba(10, 10, 10, 0.2);
|
box-shadow: 0 20px 60px rgba(10, 10, 10, 0.05), 0 5px 10px rgba(10, 10, 10, 0.1), 0 1px 1px rgba(10, 10, 10, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
section#home div#dropzone {
|
section#home div#dropzone {
|
||||||
border: 1px solid #dbdbdb;
|
border: 1px solid #dbdbdb;
|
||||||
background-color: rgba(0, 0, 0, 0);
|
background-color: rgba(0, 0, 0, 0);
|
||||||
border-color: #ff3860;
|
border-color: #ff3860;
|
||||||
color: #ff3860;
|
color: #ff3860;
|
||||||
display: none;
|
display: none;
|
||||||
@ -41,7 +41,7 @@ section#home div#dropzone {
|
|||||||
padding-left: .75em;
|
padding-left: .75em;
|
||||||
padding-right: .75em;
|
padding-right: .75em;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
section#home div#uploads, section#home p#tokenContainer, section#home a#panel { display: none; }
|
section#home div#uploads, section#home p#tokenContainer, section#home a#panel { display: none; }
|
||||||
@ -55,31 +55,31 @@ section#home .dz-preview img, section#home .dz-preview .dz-success-mark, section
|
|||||||
section#home div#uploads { margin-bottom: 25px; }
|
section#home div#uploads { margin-bottom: 25px; }
|
||||||
|
|
||||||
@keyframes floatUp {
|
@keyframes floatUp {
|
||||||
0% {
|
0% {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
box-shadow: 0 0 0 rgba(10, 10, 10, 0), 0 0 0 rgba(10, 10, 10, 0), 0 0 0 rgba(10, 10, 10, 0);
|
box-shadow: 0 0 0 rgba(10, 10, 10, 0), 0 0 0 rgba(10, 10, 10, 0), 0 0 0 rgba(10, 10, 10, 0);
|
||||||
-webkit-transform: scale(0.86);
|
-webkit-transform: scale(0.86);
|
||||||
transform: scale(0.86);
|
transform: scale(0.86);
|
||||||
}
|
}
|
||||||
25% { opacity: 100; }
|
25% { opacity: 100; }
|
||||||
67% {
|
67% {
|
||||||
box-shadow: 0 0 0 rgba(10, 10, 10, 0), 0 5px 10px rgba(10, 10, 10, 0.1), 0 1px 1px rgba(10, 10, 10, 0.2);
|
box-shadow: 0 0 0 rgba(10, 10, 10, 0), 0 5px 10px rgba(10, 10, 10, 0.1), 0 1px 1px rgba(10, 10, 10, 0.2);
|
||||||
-webkit-transform: scale(1);
|
-webkit-transform: scale(1);
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
box-shadow: 0 20px 60px rgba(10, 10, 10, 0.05), 0 5px 10px rgba(10, 10, 10, 0.1), 0 1px 1px rgba(10, 10, 10, 0.2);
|
box-shadow: 0 20px 60px rgba(10, 10, 10, 0.05), 0 5px 10px rgba(10, 10, 10, 0.1), 0 1px 1px rgba(10, 10, 10, 0.2);
|
||||||
-webkit-transform: scale(1);
|
-webkit-transform: scale(1);
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------
|
/* ------------------
|
||||||
PANEL
|
PANEL
|
||||||
------------------ */
|
------------------ */
|
||||||
|
|
||||||
section#login input, section#login p.control a.button {
|
section#login input, section#login p.control a.button {
|
||||||
border-left: 0px;
|
border-left: 0px;
|
||||||
border-top: 0px;
|
border-top: 0px;
|
||||||
border-right: 0px;
|
border-right: 0px;
|
||||||
border-radius: 0px;
|
border-radius: 0px;
|
||||||
@ -94,7 +94,7 @@ section#login p.control a#registerBtn { border-left: 0px; }
|
|||||||
section#auth, section#dashboard { display: none }
|
section#auth, section#dashboard { display: none }
|
||||||
section#auth input { background: rgba(0, 0, 0, 0); }
|
section#auth input { background: rgba(0, 0, 0, 0); }
|
||||||
section#auth input, section#auth a {
|
section#auth input, section#auth a {
|
||||||
border-left: 0px;
|
border-left: 0px;
|
||||||
border-top: 0px;
|
border-top: 0px;
|
||||||
border-right: 0px;
|
border-right: 0px;
|
||||||
border-radius: 0px;
|
border-radius: 0px;
|
||||||
@ -107,6 +107,51 @@ section#dashboard div#table div.column a { width: 100%; }
|
|||||||
section#dashboard div#table div.column a img { width:200px; }
|
section#dashboard div#table div.column a img { width:200px; }
|
||||||
|
|
||||||
.select-wrapper {
|
.select-wrapper {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 10px;
|
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
After Width: | Height: | Size: 26 KiB |
BIN
public/icons/152px.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
public/icons/167px.png
Normal file
After Width: | Height: | Size: 49 KiB |
BIN
public/icons/180px.png
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
public/icons/192px.png
Normal file
After Width: | Height: | Size: 63 KiB |
BIN
public/icons/270px.png
Normal file
After Width: | Height: | Size: 113 KiB |
BIN
public/icons/310px.png
Normal file
After Width: | Height: | Size: 154 KiB |
BIN
public/icons/310pxw.png
Normal file
After Width: | Height: | Size: 77 KiB |
BIN
public/icons/32pxr.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
public/icons/384px.png
Normal file
After Width: | Height: | Size: 225 KiB |
BIN
public/icons/600px.png
Normal file
After Width: | Height: | Size: 486 KiB |
BIN
public/icons/600pxr.png
Normal file
After Width: | Height: | Size: 554 KiB |
BIN
public/icons/70px.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
BIN
public/icons/96pxr.png
Normal file
After Width: | Height: | Size: 19 KiB |
12
public/icons/browserconfig.xml
Normal 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>
|
21
public/icons/manifest.json
Normal 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
BIN
public/icons/xcf/600pxr.xcf
Normal file
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 83 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 7.9 KiB |
@ -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>
|
|
Before Width: | Height: | Size: 920 B |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 15 KiB |
@ -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"
|
|
||||||
}
|
|
Before Width: | Height: | Size: 6.1 KiB |
@ -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 |
Before Width: | Height: | Size: 154 KiB After Width: | Height: | Size: 264 KiB |
Before Width: | Height: | Size: 4.0 MiB After Width: | Height: | Size: 466 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 269 KiB |
@ -1,56 +1,56 @@
|
|||||||
var page = {};
|
/* global swal, axios */
|
||||||
|
|
||||||
page.do = function(dest){
|
var page = {}
|
||||||
|
|
||||||
var user = document.getElementById('user').value;
|
page.do = function (dest) {
|
||||||
var pass = document.getElementById('pass').value;
|
var user = document.getElementById('user').value
|
||||||
|
var pass = document.getElementById('pass').value
|
||||||
|
|
||||||
if(user === undefined || user === null || user === '')
|
if (user === undefined || user === null || user === '') {
|
||||||
return swal('Error', 'You need to specify a username', 'error');
|
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 (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,
|
username: user,
|
||||||
password: pass
|
password: pass
|
||||||
})
|
})
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
|
if (response.data.success === false) {
|
||||||
|
return swal('Error', response.data.description, 'error')
|
||||||
|
}
|
||||||
|
|
||||||
if(response.data.success === false)
|
localStorage.token = response.data.token
|
||||||
return swal('Error', response.data.description, 'error');
|
window.location = 'dashboard'
|
||||||
|
})
|
||||||
localStorage.token = response.data.token;
|
.catch(function (error) {
|
||||||
window.location = '/dashboard';
|
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) {
|
|
||||||
return swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error');
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
page.verify = function(){
|
page.verify = function () {
|
||||||
page.token = localStorage.token;
|
page.token = localStorage.token
|
||||||
if(page.token === undefined) return;
|
if (page.token === undefined) return
|
||||||
|
|
||||||
axios.post('/api/tokens/verify', {
|
axios.post('api/tokens/verify', {
|
||||||
token: page.token
|
token: page.token
|
||||||
})
|
})
|
||||||
.then(function (response) {
|
.then(function (response) {
|
||||||
|
if (response.data.success === false) {
|
||||||
if(response.data.success === false)
|
return swal('Error', response.data.description, 'error')
|
||||||
return swal('Error', response.data.description, 'error');
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
window.location = 'dashboard'
|
||||||
|
})
|
||||||
|
.catch(function (error) {
|
||||||
|
console.log(error)
|
||||||
|
return swal('An error ocurred', 'There was an error with the request, please check the console for more information.', 'error')
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
window.onload = function () {
|
window.onload = function () {
|
||||||
page.verify();
|
page.verify()
|
||||||
}
|
}
|
||||||
|
@ -1,219 +1,216 @@
|
|||||||
var upload = {};
|
/* eslint-disable no-unused-expressions */
|
||||||
|
/* global swal, axios, Dropzone */
|
||||||
|
|
||||||
upload.isPrivate = true;
|
var upload = {}
|
||||||
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(){
|
upload.isPrivate = true
|
||||||
axios.get('/api/check')
|
upload.token = localStorage.token
|
||||||
.then(function (response) {
|
upload.maxFileSize
|
||||||
upload.isPrivate= response.data.private;
|
// Add the album var to the upload so we can store the album id in there
|
||||||
upload.maxFileSize = response.data.maxFileSize;
|
upload.album
|
||||||
upload.preparePage();
|
upload.myDropzone
|
||||||
})
|
|
||||||
.catch(function (error) {
|
upload.checkIfPublic = function () {
|
||||||
swal("An error ocurred", 'There was an error with the request, please check the console for more information.', "error");
|
axios.get('api/check')
|
||||||
return console.log(error);
|
.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')
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
upload.preparePage = function(){
|
upload.preparePage = function () {
|
||||||
if(!upload.isPrivate) return upload.prepareUpload();
|
if (upload.isPrivate) {
|
||||||
if(!upload.token) return document.getElementById('loginToUpload').style.display = 'inline-flex';
|
if (upload.token) {
|
||||||
upload.verifyToken(upload.token, true);
|
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){
|
upload.verifyToken = function (token, reloadOnError) {
|
||||||
if(reloadOnError === undefined)
|
if (reloadOnError === undefined) { reloadOnError = false }
|
||||||
reloadOnError = false;
|
|
||||||
|
|
||||||
axios.post('/api/tokens/verify', {
|
|
||||||
token: token
|
|
||||||
})
|
|
||||||
.then(function (response) {
|
|
||||||
|
|
||||||
if(response.data.success === false){
|
axios.post('api/tokens/verify', { token: token })
|
||||||
swal({
|
.then(response => {
|
||||||
title: "An error ocurred",
|
if (response.data.success === false) {
|
||||||
text: response.data.description,
|
swal({
|
||||||
type: "error"
|
title: 'An error ocurred',
|
||||||
}, function(){
|
text: response.data.description,
|
||||||
if(reloadOnError){
|
type: 'error'
|
||||||
localStorage.removeItem("token");
|
}, () => {
|
||||||
location.reload();
|
if (reloadOnError) {
|
||||||
}
|
localStorage.removeItem('token')
|
||||||
})
|
location.reload()
|
||||||
return;
|
}
|
||||||
}
|
})
|
||||||
|
return
|
||||||
localStorage.token = token;
|
}
|
||||||
upload.token = token;
|
|
||||||
return upload.prepareUpload();
|
|
||||||
|
|
||||||
})
|
|
||||||
.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);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
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')
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
upload.prepareUpload = function(){
|
upload.prepareUpload = function () {
|
||||||
// I think this fits best here because we need to check for a valid token before we can get the albums
|
// I think this fits best here because we need to check for a valid token before we can get the albums
|
||||||
if (upload.token) {
|
if (upload.token) {
|
||||||
var select = document.getElementById('albumSelect');
|
var select = document.getElementById('albumSelect')
|
||||||
|
|
||||||
select.addEventListener('change', function() {
|
|
||||||
upload.album = select.value;
|
|
||||||
});
|
|
||||||
|
|
||||||
axios.get('/api/albums', { headers: { token: upload.token }})
|
select.addEventListener('change', () => {
|
||||||
.then(function(res) {
|
upload.album = select.value
|
||||||
var albums = res.data.albums;
|
})
|
||||||
|
|
||||||
// if the user doesn't have any albums we don't really need to display
|
|
||||||
// an album selection
|
|
||||||
if (albums.length === 0) return;
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
// 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);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
div = document.createElement('div');
|
axios.get('api/albums', { headers: { token: upload.token } })
|
||||||
div.id = 'dropzone';
|
.then(res => {
|
||||||
div.innerHTML = 'Click here or drag and drop files';
|
var albums = res.data.albums
|
||||||
div.style.display = 'flex';
|
|
||||||
|
|
||||||
document.getElementById('maxFileSize').innerHTML = 'Maximum upload size per file is ' + upload.maxFileSize;
|
// If the user doesn't have any albums we don't really need to display
|
||||||
document.getElementById('loginToUpload').style.display = 'none';
|
// an album selection
|
||||||
|
if (albums.length === 0) return
|
||||||
if(upload.token === undefined)
|
|
||||||
document.getElementById('loginLinkText').innerHTML = 'Create an account and keep track of your uploads';
|
|
||||||
|
|
||||||
document.getElementById('uploadContainer').appendChild(div);
|
// Loop through the albums and create an option for each album
|
||||||
|
for (var i = 0; i < albums.length; i++) {
|
||||||
upload.prepareDropzone();
|
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'
|
||||||
|
})
|
||||||
|
.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')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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'
|
||||||
|
|
||||||
|
if (upload.token === undefined) { document.getElementById('loginLinkText').innerHTML = 'Create an account and keep track of your uploads' }
|
||||||
|
|
||||||
|
document.getElementById('uploadContainer').appendChild(div)
|
||||||
|
|
||||||
|
upload.prepareDropzone()
|
||||||
}
|
}
|
||||||
|
|
||||||
upload.prepareDropzone = function(){
|
upload.prepareDropzone = function () {
|
||||||
var previewNode = document.querySelector('#template');
|
var previewNode = document.querySelector('#template')
|
||||||
previewNode.id = '';
|
previewNode.id = ''
|
||||||
var previewTemplate = previewNode.parentNode.innerHTML;
|
var previewTemplate = previewNode.parentNode.innerHTML
|
||||||
previewNode.parentNode.removeChild(previewNode);
|
previewNode.parentNode.removeChild(previewNode)
|
||||||
|
|
||||||
var dropzone = new Dropzone('div#dropzone', {
|
var dropzone = new Dropzone('div#dropzone', {
|
||||||
url: '/api/upload',
|
url: 'api/upload',
|
||||||
paramName: 'files[]',
|
paramName: 'files[]',
|
||||||
maxFilesize: upload.maxFileSize.slice(0, -2),
|
maxFilesize: upload.maxFileSize.slice(0, -2),
|
||||||
parallelUploads: 2,
|
parallelUploads: 2,
|
||||||
uploadMultiple: false,
|
uploadMultiple: false,
|
||||||
previewsContainer: 'div#uploads',
|
previewsContainer: 'div#uploads',
|
||||||
previewTemplate: previewTemplate,
|
previewTemplate: previewTemplate,
|
||||||
createImageThumbnails: false,
|
createImageThumbnails: false,
|
||||||
maxFiles: 1000,
|
maxFiles: 1000,
|
||||||
autoProcessQueue: true,
|
autoProcessQueue: true,
|
||||||
headers: {
|
headers: { token: upload.token },
|
||||||
'token': upload.token
|
init: function () {
|
||||||
},
|
upload.myDropzone = this
|
||||||
init: function() {
|
this.on('addedfile', file => {
|
||||||
upload.myDropzone = this;
|
document.getElementById('uploads').style.display = 'block'
|
||||||
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', (file, xhr) => {
|
||||||
// add the selected albumid, if an album is selected, as a header
|
if (upload.album) {
|
||||||
this.on('sending', function(file, xhr) {
|
xhr.setRequestHeader('albumid', upload.album)
|
||||||
if (upload.album) {
|
}
|
||||||
xhr.setRequestHeader('albumid', upload.album)
|
})
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update the total progress bar
|
// Update the total progress bar
|
||||||
dropzone.on('uploadprogress', function(file, progress) {
|
dropzone.on('uploadprogress', (file, progress) => {
|
||||||
file.previewElement.querySelector('.progress').setAttribute('value', progress);
|
file.previewElement.querySelector('.progress').setAttribute('value', progress)
|
||||||
file.previewElement.querySelector('.progress').innerHTML = progress + '%';
|
file.previewElement.querySelector('.progress').innerHTML = `${progress}%`
|
||||||
});
|
})
|
||||||
|
|
||||||
dropzone.on('success', function(file, response) {
|
dropzone.on('success', (file, response) => {
|
||||||
|
// Handle the responseText here. For example, add the text to the preview element:
|
||||||
|
|
||||||
// Handle the responseText here. For example, add the text to the preview element:
|
if (response.success === false) {
|
||||||
|
var span = document.createElement('span')
|
||||||
|
span.innerHTML = response.description
|
||||||
|
file.previewTemplate.querySelector('.link').appendChild(span)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if(response.success === false){
|
var a = document.createElement('a')
|
||||||
var span = document.createElement('span');
|
a.href = response.files[0].url
|
||||||
span.innerHTML = response.description;
|
a.target = '_blank'
|
||||||
file.previewTemplate.querySelector('.link').appendChild(span);
|
a.innerHTML = response.files[0].url
|
||||||
return;
|
file.previewTemplate.querySelector('.link').appendChild(a)
|
||||||
}
|
|
||||||
|
|
||||||
a = document.createElement('a');
|
file.previewTemplate.querySelector('.progress').style.display = 'none'
|
||||||
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';
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
upload.prepareShareX();
|
upload.prepareShareX()
|
||||||
}
|
}
|
||||||
|
|
||||||
upload.prepareShareX = function(){
|
upload.prepareShareX = function () {
|
||||||
if (upload.token) {
|
if (upload.token) {
|
||||||
var sharex_element = document.getElementById("ShareX");
|
var sharexElement = document.getElementById('ShareX')
|
||||||
var sharex_file = "{\r\n\
|
var sharexFile = `{\r\n\
|
||||||
\"Name\": \"" + location.hostname + "\",\r\n\
|
"Name": "${location.hostname}",\r\n\
|
||||||
\"DestinationType\": \"ImageUploader, FileUploader\",\r\n\
|
"DestinationType": "ImageUploader, FileUploader",\r\n\
|
||||||
\"RequestType\": \"POST\",\r\n\
|
"RequestType": "POST",\r\n\
|
||||||
\"RequestURL\": \"" + location.origin + "/api/upload\",\r\n\
|
"RequestURL": "${location.origin}/api/upload",\r\n\
|
||||||
\"FileFormName\": \"files[]\",\r\n\
|
"FileFormName": "files[]",\r\n\
|
||||||
\"Headers\": {\r\n\
|
"Headers": {\r\n\
|
||||||
\"token\": \"" + upload.token + "\"\r\n\
|
"token": "${upload.token}"\r\n\
|
||||||
},\r\n\
|
},\r\n\
|
||||||
\"ResponseType\": \"Text\",\r\n\
|
"ResponseType": "Text",\r\n\
|
||||||
\"URL\": \"$json:files[0].url$\",\r\n\
|
"URL": "$json:files[0].url$",\r\n\
|
||||||
\"ThumbnailURL\": \"$json:files[0].url$\"\r\n\
|
"ThumbnailURL": "$json:files[0].url$"\r\n\
|
||||||
}";
|
}`
|
||||||
var sharex_blob = new Blob([sharex_file], {type: "application/octet-binary"});
|
var sharexBlob = new Blob([sharexFile], { type: 'application/octet-binary' })
|
||||||
sharex_element.setAttribute("href", URL.createObjectURL(sharex_blob))
|
sharexElement.setAttribute('href', URL.createObjectURL(sharexBlob))
|
||||||
sharex_element.setAttribute("download", location.hostname + ".sxcu");
|
sharexElement.setAttribute('download', `${location.hostname}.sxcu`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Handle image paste event
|
// Handle image paste event
|
||||||
window.addEventListener('paste', function(event) {
|
window.addEventListener('paste', event => {
|
||||||
var items = (event.clipboardData || event.originalEvent.clipboardData).items;
|
var items = (event.clipboardData || event.originalEvent.clipboardData).items
|
||||||
for (index in items) {
|
for (var index in items) {
|
||||||
var item = items[index];
|
var item = items[index]
|
||||||
if (item.kind === 'file') {
|
if (item.kind === 'file') {
|
||||||
var blob = item.getAsFile();
|
var blob = item.getAsFile()
|
||||||
console.log(blob.type);
|
console.log(blob.type)
|
||||||
var file = new File([blob], "pasted-image."+blob.type.match(/(?:[^\/]*\/)([^;]*)/)[1]);
|
var file = new File([blob], `pasted-image.${blob.type.match(/(?:[^/]*\/)([^;]*)/)[1]}`)
|
||||||
file.type = blob.type;
|
file.type = blob.type
|
||||||
console.log(file);
|
console.log(file)
|
||||||
upload.myDropzone.addFile(file);
|
upload.myDropzone.addFile(file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
window.onload = function () {
|
window.onload = function () {
|
||||||
upload.checkIfPublic();
|
upload.checkIfPublic()
|
||||||
};
|
}
|
||||||
|
|
||||||
|
19
public/libs/axios/LICENSE
Normal 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
21
public/libs/bulma/LICENSE
Normal 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
12
public/libs/dropzone/LICENSE
Normal 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
12
public/libs/fontello/LICENSE
Normal 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
@ -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'; } /* '' */
|
BIN
public/libs/fontello/fontello.eot
Normal file
18
public/libs/fontello/fontello.svg
Normal 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="" 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="" 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="" 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="" 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 |
BIN
public/libs/fontello/fontello.ttf
Normal file
BIN
public/libs/fontello/fontello.woff
Normal file
BIN
public/libs/fontello/fontello.woff2
Normal file
9
public/libs/sweetalert/LICENSE
Normal 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.
|
5
public/libs/sweetalert/sweetalert.min.css
vendored
Normal file
1
public/libs/sweetalert/sweetalert.min.js
vendored
Normal file
@ -1,56 +1,55 @@
|
|||||||
const config = require('../config.js');
|
const config = require('../config.js')
|
||||||
const routes = require('express').Router();
|
const routes = require('express').Router()
|
||||||
const db = require('knex')(config.database);
|
const db = require('knex')(config.database)
|
||||||
const path = require('path');
|
const path = require('path')
|
||||||
const utils = require('../controllers/utilsController.js');
|
const utils = require('../controllers/utilsController.js')
|
||||||
|
|
||||||
routes.get('/a/:identifier', async (req, res, next) => {
|
routes.get('/a/:identifier', async (req, res, next) => {
|
||||||
let identifier = req.params.identifier;
|
let identifier = req.params.identifier
|
||||||
if (identifier === undefined) return res.status(401).json({ success: false, description: 'No identifier provided' });
|
if (identifier === undefined) return res.status(401).json({ success: false, description: 'No identifier provided' })
|
||||||
|
|
||||||
const album = await db.table('albums').where({ identifier, enabled: 1 }).first();
|
const album = await db.table('albums').where({ identifier, enabled: 1 }).first()
|
||||||
if (!album) return res.status(404).sendFile('404.html', { root: './pages/error/' });
|
if (!album) return res.status(404).sendFile('404.html', { root: './pages/error/' })
|
||||||
|
|
||||||
const files = await db.table('files').select('name').where('albumid', album.id).orderBy('id', 'DESC');
|
const files = await db.table('files').select('name').where('albumid', album.id).orderBy('id', 'DESC')
|
||||||
let thumb = '';
|
let thumb = ''
|
||||||
const basedomain = config.domain;
|
const basedomain = config.domain
|
||||||
|
|
||||||
for (let file of files) {
|
for (let file of files) {
|
||||||
file.file = `${basedomain}/${file.name}`;
|
file.file = `${basedomain}/${file.name}`
|
||||||
|
|
||||||
let ext = path.extname(file.name).toLowerCase();
|
let ext = path.extname(file.name).toLowerCase()
|
||||||
if (utils.imageExtensions.includes(ext) || utils.videoExtensions.includes(ext)) {
|
if (utils.imageExtensions.includes(ext) || utils.videoExtensions.includes(ext)) {
|
||||||
file.thumb = `${basedomain}/thumbs/${file.name.slice(0, -ext.length)}.png`;
|
file.thumb = `${basedomain}/thumbs/${file.name.slice(0, -ext.length)}.png`
|
||||||
|
|
||||||
/*
|
/*
|
||||||
If thumbnail for album is still not set, do it.
|
If thumbnail for album is still not set, do it.
|
||||||
A potential improvement would be to let the user upload a specific image as an album cover
|
A potential improvement would be to let the user upload a specific image as an album cover
|
||||||
since embedding the first image could potentially result in nsfw content when pasting links.
|
since embedding the first image could potentially result in nsfw content when pasting links.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (thumb === '') {
|
if (thumb === '') {
|
||||||
thumb = file.thumb;
|
thumb = file.thumb
|
||||||
}
|
}
|
||||||
|
|
||||||
file.thumb = `<img src="${file.thumb}"/>`;
|
file.thumb = `<img src="${file.thumb}"/>`
|
||||||
} else {
|
} else {
|
||||||
file.thumb = `<h1 class="title">.${ext}</h1>`;
|
file.thumb = `<h1 class="title">.${ext}</h1>`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let enableDownload = false
|
||||||
|
if (config.uploads.generateZips) enableDownload = true
|
||||||
|
|
||||||
let enableDownload = false;
|
return res.render('album', {
|
||||||
if (config.uploads.generateZips) enableDownload = true;
|
layout: false,
|
||||||
|
title: album.name,
|
||||||
|
count: files.length,
|
||||||
|
thumb,
|
||||||
|
files,
|
||||||
|
identifier,
|
||||||
|
enableDownload
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
return res.render('album', {
|
module.exports = routes
|
||||||
layout: false,
|
|
||||||
title: album.name,
|
|
||||||
count: files.length,
|
|
||||||
thumb,
|
|
||||||
files,
|
|
||||||
identifier,
|
|
||||||
enableDownload
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = routes;
|
|
||||||
|
@ -1,37 +1,37 @@
|
|||||||
const config = require('../config.js');
|
const config = require('../config.js')
|
||||||
const routes = require('express').Router();
|
const routes = require('express').Router()
|
||||||
const uploadController = require('../controllers/uploadController');
|
const uploadController = require('../controllers/uploadController')
|
||||||
const albumsController = require('../controllers/albumsController');
|
const albumsController = require('../controllers/albumsController')
|
||||||
const tokenController = require('../controllers/tokenController');
|
const tokenController = require('../controllers/tokenController')
|
||||||
const authController = require('../controllers/authController');
|
const authController = require('../controllers/authController')
|
||||||
|
|
||||||
routes.get('/check', (req, res, next) => {
|
routes.get('/check', (req, res, next) => {
|
||||||
return res.json({
|
return res.json({
|
||||||
private: config.private,
|
private: config.private,
|
||||||
maxFileSize: config.uploads.maxSize
|
maxFileSize: config.uploads.maxSize
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
routes.post('/login', (req, res, next) => authController.verify(req, res, next));
|
routes.post('/login', (req, res, next) => authController.verify(req, res, next))
|
||||||
routes.post('/register', (req, res, next) => authController.register(req, res, next));
|
routes.post('/register', (req, res, next) => authController.register(req, res, next))
|
||||||
routes.post('/password/change', (req, res, next) => authController.changePassword(req, res, next));
|
routes.post('/password/change', (req, res, next) => authController.changePassword(req, res, next))
|
||||||
routes.get('/uploads', (req, res, next) => uploadController.list(req, res, next));
|
routes.get('/uploads', (req, res, next) => uploadController.list(req, res, next))
|
||||||
routes.get('/uploads/:page', (req, res, next) => uploadController.list(req, res, next));
|
routes.get('/uploads/:page', (req, res, next) => uploadController.list(req, res, next))
|
||||||
routes.post('/upload', (req, res, next) => uploadController.upload(req, res, next));
|
routes.post('/upload', (req, res, next) => uploadController.upload(req, res, next))
|
||||||
routes.post('/upload/delete', (req, res, next) => uploadController.delete(req, res, next));
|
routes.post('/upload/delete', (req, res, next) => uploadController.delete(req, res, next))
|
||||||
routes.post('/upload/:albumid', (req, res, next) => uploadController.upload(req, res, next));
|
routes.post('/upload/:albumid', (req, res, next) => uploadController.upload(req, res, next))
|
||||||
routes.get('/album/get/:identifier', (req, res, next) => albumsController.get(req, res, next));
|
routes.get('/album/get/:identifier', (req, res, next) => albumsController.get(req, res, next))
|
||||||
routes.get('/album/zip/:identifier', (req, res, next) => albumsController.generateZip(req, res, next));
|
routes.get('/album/zip/:identifier', (req, res, next) => albumsController.generateZip(req, res, next))
|
||||||
routes.get('/album/:id', (req, res, next) => uploadController.list(req, res, next));
|
routes.get('/album/:id', (req, res, next) => uploadController.list(req, res, next))
|
||||||
routes.get('/album/:id/:page', (req, res, next) => uploadController.list(req, res, next));
|
routes.get('/album/:id/:page', (req, res, next) => uploadController.list(req, res, next))
|
||||||
routes.get('/albums', (req, res, next) => albumsController.list(req, res, next));
|
routes.get('/albums', (req, res, next) => albumsController.list(req, res, next))
|
||||||
routes.get('/albums/:sidebar', (req, res, next) => albumsController.list(req, res, next));
|
routes.get('/albums/:sidebar', (req, res, next) => albumsController.list(req, res, next))
|
||||||
routes.post('/albums', (req, res, next) => albumsController.create(req, res, next));
|
routes.post('/albums', (req, res, next) => albumsController.create(req, res, next))
|
||||||
routes.post('/albums/delete', (req, res, next) => albumsController.delete(req, res, next));
|
routes.post('/albums/delete', (req, res, next) => albumsController.delete(req, res, next))
|
||||||
routes.post('/albums/rename', (req, res, next) => albumsController.rename(req, res, next));
|
routes.post('/albums/rename', (req, res, next) => albumsController.rename(req, res, next))
|
||||||
routes.get('/albums/test', (req, res, next) => albumsController.test(req, res, next));
|
routes.get('/albums/test', (req, res, next) => albumsController.test(req, res, next))
|
||||||
routes.get('/tokens', (req, res, next) => tokenController.list(req, res, next));
|
routes.get('/tokens', (req, res, next) => tokenController.list(req, res, next))
|
||||||
routes.post('/tokens/verify', (req, res, next) => tokenController.verify(req, res, next));
|
routes.post('/tokens/verify', (req, res, next) => tokenController.verify(req, res, next))
|
||||||
routes.post('/tokens/change', (req, res, next) => tokenController.change(req, res, next));
|
routes.post('/tokens/change', (req, res, next) => tokenController.change(req, res, next))
|
||||||
|
|
||||||
module.exports = routes;
|
module.exports = routes
|
||||||
|
@ -1,41 +1,50 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<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="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
|
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="https://lolisafe.moe/images/icons/apple-touch-icon.png?v=XBreOJMe24">
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" type="image/png" href="https://lolisafe.moe/images/icons/favicon-32x32.png?v=XBreOJMe24" sizes="32x32">
|
<meta name="description" content="A pomf-like file uploading service that doesn't suck.">
|
||||||
<link rel="icon" type="image/png" href="https://lolisafe.moe/images/icons/favicon-16x16.png?v=XBreOJMe24" sizes="16x16">
|
<meta name="keywords" content="upload,lolisafe,file,images,hosting,bobby,fiery">
|
||||||
<link rel="manifest" href="https://lolisafe.moe/images/icons/manifest.json?v=XBreOJMe24">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link rel="mask-icon" href="https://lolisafe.moe/images/icons/safari-pinned-tab.svg?v=XBreOJMe24" color="#5bbad5">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
<link rel="shortcut icon" href="https://lolisafe.moe/images/icons/favicon.ico?v=XBreOJMe24">
|
<title>{{ title }}</title>
|
||||||
<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">
|
|
||||||
|
|
||||||
<meta property="og:url" content="https://lolisafe.moe" />
|
<!-- Stylesheets and scripts -->
|
||||||
<meta property="og:type" content="website" />
|
<link rel="stylesheet" type="text/css" href="libs/bulma/bulma.min.css">
|
||||||
<meta property="og:title" content="{{ title }} | {{ count }} files" />
|
<link rel="stylesheet" type="text/css" href="libs/sweetalert/sweetalert.min.css">
|
||||||
<meta property="og:description" content="lolisafe.moe | A small safe worth protecting." />
|
<link rel="stylesheet" type="text/css" href="css/style.css">
|
||||||
<meta property="og:image" content="{{ thumb }}" />
|
<script type="text/javascript" src="libs/sweetalert/sweetalert.min.js"></script>
|
||||||
<meta property="og:image:secure_url" content="{{ thumb }}" />
|
<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 name="twitter:card" content="summary">
|
<!-- Open Graph tags -->
|
||||||
<meta name="twitter:title" content="{{ title }} | {{ count }} files">
|
<meta property="og:type" content="website" />
|
||||||
<meta name="twitter:description" content="lolisafe.moe | A small safe worth protecting.">
|
<meta property="og:title" content="{{ title }} | {{ count }} files" />
|
||||||
<meta name="twitter:image" content="{{ thumb }}">
|
<meta property="og:url" content="https://safe.fiery.me/" />
|
||||||
<meta name="twitter:image:src" content="{{ thumb }}">
|
<meta property="og:description" content="A pomf-like file uploading service that doesn't suck." />
|
||||||
|
<meta property="og:image" 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="A pomf-like file uploading service that doesn't suck.">
|
||||||
|
<meta name="twitter:image" content="{{ thumb }}">
|
||||||
|
|
||||||
|
<!-- 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>{{ 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>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@ -50,7 +59,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="column is-3" style="text-align: right; padding-top: 45px;">
|
<div class="column is-3" style="text-align: right; padding-top: 45px;">
|
||||||
{{#if enableDownload}}
|
{{#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}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|