mirror of
https://github.com/BobbyWibowo/lolisafe.git
synced 2025-01-19 01:31:34 +00:00
Merged dev into master
This commit is contained in:
commit
6b7fd3bcf4
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,4 +3,5 @@ uploads/
|
||||
logs/
|
||||
database/db
|
||||
config.js
|
||||
start.json
|
||||
start.json
|
||||
npm-debug.log
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Pitu
|
||||
|
||||
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.
|
30
README.md
30
README.md
@ -1,27 +1,32 @@
|
||||
![loli-safe](https://a.cuntflaps.me/jcutlz.png)
|
||||
# loli-safe
|
||||
Pomf-like file uploading service, but way better.
|
||||
A small safe worth protecting.
|
||||
---
|
||||
### Sites using loli-safe
|
||||
|
||||
- [lolisafe.moe](https://lolisafe.moe): A small safe with a smile worth protecting.
|
||||
- [cuntflaps.me](https://cuntflaps.me)
|
||||
- [fluntcaps.me](https://fluntcaps.me)
|
||||
- Feel free to add yours here.
|
||||
|
||||
---
|
||||
|
||||
1. Clone
|
||||
2. Rename `config.sample.js` to `config.js`
|
||||
4. Modify port, basedomain and privacy options if desired
|
||||
3. run `npm install` to install all dependencies
|
||||
5. run `pm2 start lolisafe.js` or `node lolisafe.js` to start the service
|
||||
|
||||
### Token
|
||||
This service supports running both as public and private. The only difference is that one needs a token to upload and the other one doesn't. If you want it to be public so anyone can upload files either from the website or API, just set the option `private: false` in the `config.js` file.
|
||||
### Getting started
|
||||
This service supports running both as public and private. The only difference is that one needs a token to upload and the other one doesn't. If you want it to be public so anyone can upload files either from the website or API, just set the option `private: false` in the `config.js` file. In case you want to run it privately, you should set `private: true`.
|
||||
|
||||
In case you want to run it privately, you should set `private: true` and 2 tokens will be generated when you first run the service. This tokens are a client and admin token respectively.
|
||||
Upon running the service for the first time, it's gonna create a user account with the username `root` and password `root`. This is your admin account and you should change the password immediately. This account will let you manage all uploaded files and remove any if necessary.
|
||||
|
||||
The client token should be sent with every upload request to the service to validate an upload, making it so that only people with access to said token can upload files to the service.
|
||||
The admin token is used to access the admin dashboard. This one is generated regardless of how the service is running (public or private) because it grants you access to the dashboard where you can see all uploaded files, create albums to sort out stuff and change your access tokens.
|
||||
|
||||
When you first run the service both tokens will be displayed on the console so you can copy and save them. Keep in mind that the tokens are nothing more than random generated strings so you can always change them for something easy to remember, acting more like a password than a token. But since the token is sent to the server with each request either to upload files or access the admin dashboard, I suggest keeping them random.
|
||||
If you set `enableUserAccounts: true`, people will be able to create accounts on the service to keep track of their uploaded files and create albums to upload stuff to, pretty much like imgur does, but only through the API. Every user account has a token that the user can use to upload stuff through the API. You can find this token on the section called `Change your token` on the administration dashboard, and if it gets leaked or compromised you can renew it by clicking the button titled `Request new token`.
|
||||
|
||||
---
|
||||
## Using loli-safe
|
||||
Once the service starts you can start hitting the upload endpoint at `/api/upload` with any file. If you're using the frontend to do so then you are pretty much set, but if using the API to upload make sure the form name is set to `files[]` and the form type to `multipart/form-data`. If the service is running in private mode, dont forget to send a header of type `auth: YOUR-CLIENT-TOKEN` to validate the request.
|
||||
Once the service starts you can start hitting the upload endpoint at `/api/upload` with any file. If you're using the frontend to do so then you are pretty much set, but if using the API to upload make sure the form name is set to `files[]` and the form type to `multipart/form-data`. If the service is running in private mode, dont forget to send a header of type `token: YOUR-CLIENT-TOKEN` to validate the request.
|
||||
|
||||
A sample of the returning json from the endpoint can be seen below:
|
||||
```json
|
||||
@ -38,3 +43,10 @@ Because of how nodejs apps work, if you want it attached to a domain name you wi
|
||||
|
||||
If you choose to use a domain name and thus nginx, you should add the following directive into your location block with the limit you want to set on uploaded file's size:
|
||||
`client_max_body_size 512M;`
|
||||
|
||||
## Author
|
||||
|
||||
**loli-safe** © [Pitu](https://github.com/Pitu), Released under the [MIT](https://github.com/WeebDev/loli-safe/blob/master/LICENSE) License.<br>
|
||||
Authored and maintained by Pitu.
|
||||
|
||||
> [lolisafe.moe](https://lolisafe.moe) · GitHub [@Pitu](https://github.com/Pitu) · Twitter [@kanaadeko](https://twitter.com/kanaadeko)
|
||||
|
@ -9,6 +9,9 @@ module.exports = {
|
||||
*/
|
||||
private: true,
|
||||
|
||||
// If true, users will be able to create accounts and access their uploaded files
|
||||
enableUserAccounts: true,
|
||||
|
||||
// The registered domain where you will be serving the app. Use IP if none.
|
||||
domains: [
|
||||
|
||||
|
@ -5,93 +5,116 @@ let albumsController = {}
|
||||
|
||||
albumsController.list = function(req, res, next){
|
||||
|
||||
if(req.headers.auth !== config.adminToken)
|
||||
return res.status(401).json({ success: false, description: 'not-authorized'})
|
||||
let token = req.headers.token
|
||||
if(token === undefined) return res.status(401).json({ success: false, description: 'No token provided' })
|
||||
|
||||
let fields = ['id', 'name']
|
||||
db.table('users').where('token', token).then((user) => {
|
||||
if(user.length === 0) return res.status(401).json({ success: false, description: 'Invalid token'})
|
||||
|
||||
if(req.params.sidebar === undefined)
|
||||
fields.push('timestamp')
|
||||
|
||||
db.table('albums').select(fields).where('enabled', 1).then((albums) => {
|
||||
let fields = ['id', 'name']
|
||||
|
||||
if(req.params.sidebar === undefined)
|
||||
fields.push('timestamp')
|
||||
|
||||
if(req.params.sidebar !== undefined)
|
||||
return res.json({ success: true, albums })
|
||||
|
||||
let ids = []
|
||||
for(let album of albums){
|
||||
album.date = new Date(album.timestamp * 1000)
|
||||
album.date = album.date.getFullYear() + '-' + (album.date.getMonth() + 1) + '-' + album.date.getDate() + ' ' + (album.date.getHours() < 10 ? '0' : '') + album.date.getHours() + ':' + (album.date.getMinutes() < 10 ? '0' : '') + album.date.getMinutes() + ':' + (album.date.getSeconds() < 10 ? '0' : '') + album.date.getSeconds()
|
||||
|
||||
ids.push(album.id)
|
||||
}
|
||||
|
||||
db.table('files').whereIn('albumid', ids).select('albumid').then((files) => {
|
||||
|
||||
let albumsCount = {}
|
||||
db.table('albums').select(fields).where({enabled: 1, userid: user[0].id}).then((albums) => {
|
||||
|
||||
for(let id of ids) albumsCount[id] = 0
|
||||
for(let file of files) albumsCount[file.albumid] += 1
|
||||
for(let album of albums) album.files = albumsCount[album.id]
|
||||
if(req.params.sidebar !== undefined)
|
||||
return res.json({ success: true, albums })
|
||||
|
||||
return res.json({ success: true, albums })
|
||||
let ids = []
|
||||
for(let album of albums){
|
||||
album.date = new Date(album.timestamp * 1000)
|
||||
album.date = album.date.getFullYear() + '-' + (album.date.getMonth() + 1) + '-' + album.date.getDate() + ' ' + (album.date.getHours() < 10 ? '0' : '') + album.date.getHours() + ':' + (album.date.getMinutes() < 10 ? '0' : '') + album.date.getMinutes() + ':' + (album.date.getSeconds() < 10 ? '0' : '') + album.date.getSeconds()
|
||||
|
||||
ids.push(album.id)
|
||||
}
|
||||
|
||||
db.table('files').whereIn('albumid', ids).select('albumid').then((files) => {
|
||||
|
||||
let albumsCount = {}
|
||||
|
||||
for(let id of ids) albumsCount[id] = 0
|
||||
for(let file of files) albumsCount[file.albumid] += 1
|
||||
for(let album of albums) album.files = albumsCount[album.id]
|
||||
|
||||
return res.json({ success: true, albums })
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
|
||||
}
|
||||
|
||||
albumsController.create = function(req, res, next){
|
||||
|
||||
if(req.headers.auth !== config.adminToken)
|
||||
return res.status(401).json({ success: false, description: 'not-authorized'})
|
||||
let token = req.headers.token
|
||||
if(token === undefined) return res.status(401).json({ success: false, description: 'No token provided' })
|
||||
|
||||
let name = req.body.name
|
||||
if(name === undefined || name === '')
|
||||
return res.json({ success: false, description: 'No album name specified' })
|
||||
db.table('users').where('token', token).then((user) => {
|
||||
if(user.length === 0) return res.status(401).json({ success: false, description: 'Invalid token'})
|
||||
|
||||
db.table('albums').where('name', name).where('enabled', 1).then((album) => {
|
||||
if(album.length !== 0) return res.json({ success: false, description: 'There\'s already an album with that name' })
|
||||
let name = req.body.name
|
||||
if(name === undefined || name === '')
|
||||
return res.json({ success: false, description: 'No album name specified' })
|
||||
|
||||
db.table('albums').insert({
|
||||
name: name,
|
||||
db.table('albums').where({
|
||||
name: name,
|
||||
enabled: 1,
|
||||
timestamp: Math.floor(Date.now() / 1000)
|
||||
}).then(() => {
|
||||
return res.json({ success: true })
|
||||
})
|
||||
userid: user[0].id
|
||||
}).then((album) => {
|
||||
if(album.length !== 0) return res.json({ success: false, description: 'There\'s already an album with that name' })
|
||||
|
||||
db.table('albums').insert({
|
||||
name: name,
|
||||
enabled: 1,
|
||||
userid: user[0].id,
|
||||
timestamp: Math.floor(Date.now() / 1000)
|
||||
}).then(() => {
|
||||
return res.json({ success: true })
|
||||
})
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
|
||||
|
||||
}
|
||||
|
||||
albumsController.delete = function(req, res, next){
|
||||
if(req.headers.auth !== config.adminToken)
|
||||
return res.status(401).json({ success: false, description: 'not-authorized'})
|
||||
let token = req.headers.token
|
||||
if(token === undefined) return res.status(401).json({ success: false, description: 'No token provided' })
|
||||
|
||||
let id = req.body.id
|
||||
if(id === undefined || id === '')
|
||||
return res.json({ success: false, description: 'No album specified' })
|
||||
db.table('users').where('token', token).then((user) => {
|
||||
if(user.length === 0) return res.status(401).json({ success: false, description: 'Invalid token'})
|
||||
|
||||
db.table('albums').where('id', id).update({ enabled: 0 }).then(() => {
|
||||
return res.json({ success: true })
|
||||
let id = req.body.id
|
||||
if(id === undefined || id === '')
|
||||
return res.json({ success: false, description: 'No album specified' })
|
||||
|
||||
db.table('albums').where({id: id, userid: user[0].id}).update({ enabled: 0 }).then(() => {
|
||||
return res.json({ success: true })
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
}
|
||||
|
||||
albumsController.rename = function(req, res, next){
|
||||
if(req.headers.auth !== config.adminToken)
|
||||
return res.status(401).json({ success: false, description: 'not-authorized'})
|
||||
let token = req.headers.token
|
||||
if(token === undefined) return res.status(401).json({ success: false, description: 'No token provided' })
|
||||
|
||||
let id = req.body.id
|
||||
if(id === undefined || id === '')
|
||||
return res.json({ success: false, description: 'No album specified' })
|
||||
db.table('users').where('token', token).then((user) => {
|
||||
if(user.length === 0) return res.status(401).json({ success: false, description: 'Invalid token'})
|
||||
|
||||
let name = req.body.name
|
||||
if(name === undefined || name === '')
|
||||
return res.json({ success: false, description: 'No name specified' })
|
||||
let id = req.body.id
|
||||
if(id === undefined || id === '')
|
||||
return res.json({ success: false, description: 'No album specified' })
|
||||
|
||||
db.table('albums').where('name', name).then((results) => {
|
||||
if(results.length !== 0)
|
||||
return res.json({ success: false, description: 'Name already in use' })
|
||||
let name = req.body.name
|
||||
if(name === undefined || name === '')
|
||||
return res.json({ success: false, description: 'No name specified' })
|
||||
|
||||
db.table('albums').where('id', id).update({ name: name }).then(() => {
|
||||
return res.json({ success: true })
|
||||
db.table('albums').where({name: name, userid: user[0].id}).then((results) => {
|
||||
if(results.length !== 0) return res.json({ success: false, description: 'Name already in use' })
|
||||
|
||||
db.table('albums').where({id: id, userid: user[0].id}).update({ name: name }).then(() => {
|
||||
return res.json({ success: true })
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
|
||||
|
89
controllers/authController.js
Normal file
89
controllers/authController.js
Normal file
@ -0,0 +1,89 @@
|
||||
const config = require('../config.js')
|
||||
const db = require('knex')(config.database)
|
||||
const bcrypt = require('bcrypt')
|
||||
const saltRounds = 10
|
||||
const randomstring = require('randomstring')
|
||||
|
||||
let authController = {}
|
||||
|
||||
authController.verify = function(req, res, next){
|
||||
|
||||
let username = req.body.username
|
||||
let password = req.body.password
|
||||
|
||||
if(username === undefined) return res.json({ success: false, description: 'No username provided' })
|
||||
if(password === undefined) return res.json({ success: false, description: 'No password provided' })
|
||||
|
||||
db.table('users').where('username', username).then((user) => {
|
||||
if(user.length === 0) return res.json({ success: false, description: 'Username doesn\'t exist' })
|
||||
|
||||
bcrypt.compare(password, user[0].password, function(err, result) {
|
||||
if(result === false) return res.json({ success: false, description: 'Wrong password' })
|
||||
return res.json({ success: true, token: user[0].token })
|
||||
})
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
|
||||
}
|
||||
|
||||
authController.register = function(req, res, next){
|
||||
|
||||
if(config.enableUserAccounts === false)
|
||||
return res.json({ success: false, description: 'Register is disabled at the moment' })
|
||||
|
||||
let username = req.body.username
|
||||
let password = req.body.password
|
||||
|
||||
if(username === undefined) return res.json({ success: false, description: 'No username provided' })
|
||||
if(password === undefined) return res.json({ success: false, description: 'No password provided' })
|
||||
|
||||
if(username.length < 4 || username.length > 32)
|
||||
return res.json({ success: false, description: 'Username must have 6-32 characters' })
|
||||
if(password.length < 6 || password.length > 64)
|
||||
return res.json({ success: false, description: 'Password must have 6-64 characters' })
|
||||
|
||||
db.table('users').where('username', username).then((user) => {
|
||||
if(user.length !== 0) return res.json({ success: false, description: 'Username already exists' })
|
||||
|
||||
bcrypt.hash(password, saltRounds, function(err, hash) {
|
||||
if(err) return res.json({ success: false, description: 'Error generating password hash (╯°□°)╯︵ ┻━┻' })
|
||||
|
||||
let token = randomstring.generate(64)
|
||||
|
||||
db.table('users').insert({
|
||||
username: username,
|
||||
password: hash,
|
||||
token: token
|
||||
}).then(() => {
|
||||
return res.json({ success: true, token: token})
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
})
|
||||
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
|
||||
}
|
||||
|
||||
authController.changePassword = function(req, res, next){
|
||||
|
||||
let token = req.headers.token
|
||||
if(token === undefined) return res.status(401).json({ success: false, description: 'No token provided' })
|
||||
|
||||
db.table('users').where('token', token).then((user) => {
|
||||
if(user.length === 0) return res.status(401).json({ success: false, description: 'Invalid token'})
|
||||
|
||||
let password = req.body.password
|
||||
if(password === undefined) return res.json({ success: false, description: 'No password provided' })
|
||||
if(password.length < 6 || password.length > 64)
|
||||
return res.json({ success: false, description: 'Password must have 6-64 characters' })
|
||||
|
||||
bcrypt.hash(password, saltRounds, function(err, hash) {
|
||||
if(err) return res.json({ success: false, description: 'Error generating password hash (╯°□°)╯︵ ┻━┻' })
|
||||
|
||||
db.table('users').where('id', user[0].id).update({password: hash}).then(() => {
|
||||
return res.json({ success: true})
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
})
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
|
||||
}
|
||||
|
||||
module.exports = authController
|
@ -1,60 +1,47 @@
|
||||
const config = require('../config.js')
|
||||
const db = require('knex')(config.database)
|
||||
const randomstring = require('randomstring')
|
||||
|
||||
let tokenController = {}
|
||||
|
||||
tokenController.verify = function(req, res, next){
|
||||
let type = req.body.type
|
||||
|
||||
if(req.body.token === undefined) return res.json({ success: false, description: 'No token provided' })
|
||||
let token = req.body.token
|
||||
|
||||
if(type === undefined) return res.json({ success: false, description: 'No type provided.' })
|
||||
if(token === undefined) return res.json({ success: false, description: 'No token provided.' })
|
||||
if(type !== 'client' && type !== 'admin') return res.json({ success: false, description: 'Wrong type provided.' })
|
||||
|
||||
if(type === 'client'){
|
||||
if(token !== config.clientToken) return res.json({ success: false, description: 'Token mismatch.' })
|
||||
db.table('users').where('token', token).then((user) => {
|
||||
if(user.length === 0) return res.json({ success: false, description: 'Token mismatch' })
|
||||
return res.json({ success: true })
|
||||
}
|
||||
|
||||
if(type === 'admin'){
|
||||
if(token !== config.adminToken) return res.json({ success: false, description: 'Token mismatch.' })
|
||||
return res.json({ success: true })
|
||||
}
|
||||
|
||||
return res.json({ success: false, description: '(╯°□°)╯︵ ┻━┻' })
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
|
||||
}
|
||||
|
||||
tokenController.list = function(req, res, next){
|
||||
if(req.headers.auth !== config.adminToken)
|
||||
return res.status(401).json({ success: false, description: 'not-authorized'})
|
||||
|
||||
return res.json({
|
||||
clientToken: config.clientToken,
|
||||
adminToken: config.adminToken
|
||||
})
|
||||
let token = req.headers.token
|
||||
if(token === undefined) return res.status(401).json({ success: false, description: 'No token provided' })
|
||||
|
||||
db.table('users').where('token', token).then((user) => {
|
||||
if(user.length === 0) return res.json({ success: false, description: 'Token mismatch' })
|
||||
return res.json({ success: true, token: token })
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
|
||||
}
|
||||
|
||||
tokenController.change = function(req, res, next){
|
||||
if(req.headers.auth !== config.adminToken)
|
||||
return res.status(401).json({ success: false, description: 'not-authorized'})
|
||||
|
||||
let type = req.body.type
|
||||
let token = req.body.token
|
||||
let token = req.headers.token
|
||||
if(token === undefined) return res.status(401).json({ success: false, description: 'No token provided' })
|
||||
|
||||
if(type === undefined) return res.json({ success: false, description: 'No type provided.' })
|
||||
if(token === undefined) return res.json({ success: false, description: 'No token provided.' })
|
||||
if(type !== 'client' && type !== 'admin') return res.json({ success: false, description: 'Wrong type provided.' })
|
||||
|
||||
db.table('tokens').where('name', type).update({ value: token, timestamp: Math.floor(Date.now() / 1000) })
|
||||
.then(() => {
|
||||
|
||||
if(type === 'client')
|
||||
config.clientToken = token
|
||||
else if(type === 'admin')
|
||||
config.adminToken = token
|
||||
|
||||
res.json({ success: true })
|
||||
let newtoken = randomstring.generate(64)
|
||||
|
||||
db.table('users').where('token', token).update({
|
||||
token: newtoken,
|
||||
timestamp: Math.floor(Date.now() / 1000)
|
||||
}).then(() => {
|
||||
res.json({ success: true, token: newtoken })
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
|
||||
}
|
||||
|
||||
module.exports = tokenController
|
@ -25,81 +25,92 @@ const upload = multer({
|
||||
|
||||
uploadsController.upload = function(req, res, next){
|
||||
|
||||
if(config.private === true)
|
||||
if(req.headers.auth !== config.clientToken)
|
||||
return res.status(401).json({ success: false, description: 'not-authorized'})
|
||||
// Get the token
|
||||
let token = req.headers.token
|
||||
|
||||
let album = req.params.albumid
|
||||
// If we're running in private and there's no token, error
|
||||
if(config.private === true)
|
||||
if(token === undefined) return res.status(401).json({ success: false, description: 'No token provided' })
|
||||
|
||||
// If there is no token then just leave it blank so the query fails
|
||||
if(token === undefined) token = ''
|
||||
|
||||
if(album !== undefined)
|
||||
if(req.headers.adminauth !== config.adminToken)
|
||||
return res.status(401).json({ success: false, description: 'not-authorized'})
|
||||
|
||||
upload(req, res, function (err) {
|
||||
if (err) {
|
||||
console.error(err)
|
||||
return res.json({
|
||||
success: false,
|
||||
description: err
|
||||
})
|
||||
db.table('users').where('token', token).then((user) => {
|
||||
let userid
|
||||
if(user.length > 0)
|
||||
userid = user[0].id
|
||||
|
||||
// Check if user is trying to upload to an album
|
||||
let album = undefined
|
||||
if(userid !== undefined){
|
||||
album = req.headers.albumid
|
||||
if(album === undefined)
|
||||
album = req.params.albumid
|
||||
}
|
||||
|
||||
if(req.files.length === 0) return res.json({ success: false, description: 'no-files' })
|
||||
upload(req, res, function (err) {
|
||||
if (err) {
|
||||
console.error(err)
|
||||
return res.json({
|
||||
success: false,
|
||||
description: err
|
||||
})
|
||||
}
|
||||
|
||||
let files = []
|
||||
let existingFiles = []
|
||||
let iteration = 1
|
||||
if(req.files.length === 0) return res.json({ success: false, description: 'no-files' })
|
||||
|
||||
req.files.forEach(function(file) {
|
||||
let files = []
|
||||
let existingFiles = []
|
||||
let iteration = 1
|
||||
|
||||
// Check if the file exists by checking hash and size
|
||||
let hash = crypto.createHash('md5')
|
||||
let stream = fs.createReadStream('./' + config.uploads.folder + '/' + file.filename)
|
||||
req.files.forEach(function(file) {
|
||||
|
||||
stream.on('data', function (data) {
|
||||
hash.update(data, 'utf8')
|
||||
})
|
||||
// Check if the file exists by checking hash and size
|
||||
let hash = crypto.createHash('md5')
|
||||
let stream = fs.createReadStream('./' + config.uploads.folder + '/' + file.filename)
|
||||
|
||||
stream.on('end', function () {
|
||||
let fileHash = hash.digest('hex') // 34f7a3113803f8ed3b8fd7ce5656ebec
|
||||
|
||||
db.table('files').where({
|
||||
hash: fileHash,
|
||||
size: file.size
|
||||
}).then((dbfile) => {
|
||||
|
||||
if(dbfile.length !== 0){
|
||||
uploadsController.deleteFile(file.filename).then(() => {}).catch((e) => console.error(e))
|
||||
existingFiles.push(dbfile[0])
|
||||
}else{
|
||||
files.push({
|
||||
name: file.filename,
|
||||
original: file.originalname,
|
||||
type: file.mimetype,
|
||||
size: file.size,
|
||||
hash: fileHash,
|
||||
ip: req.ip,
|
||||
albumid: album,
|
||||
timestamp: Math.floor(Date.now() / 1000)
|
||||
})
|
||||
}
|
||||
|
||||
if(iteration === req.files.length)
|
||||
return uploadsController.processFilesForDisplay(req, res, files, existingFiles)
|
||||
iteration++
|
||||
stream.on('data', function (data) {
|
||||
hash.update(data, 'utf8')
|
||||
})
|
||||
|
||||
stream.on('end', function () {
|
||||
let fileHash = hash.digest('hex') // 34f7a3113803f8ed3b8fd7ce5656ebec
|
||||
|
||||
db.table('files').where({
|
||||
hash: fileHash,
|
||||
size: file.size
|
||||
}).then((dbfile) => {
|
||||
|
||||
if(dbfile.length !== 0){
|
||||
uploadsController.deleteFile(file.filename).then(() => {}).catch((e) => console.error(e))
|
||||
existingFiles.push(dbfile[0])
|
||||
}else{
|
||||
files.push({
|
||||
name: file.filename,
|
||||
original: file.originalname,
|
||||
type: file.mimetype,
|
||||
size: file.size,
|
||||
hash: fileHash,
|
||||
ip: req.ip,
|
||||
albumid: album,
|
||||
userid: userid,
|
||||
timestamp: Math.floor(Date.now() / 1000)
|
||||
})
|
||||
}
|
||||
|
||||
if(iteration === req.files.length)
|
||||
return uploadsController.processFilesForDisplay(req, res, files, existingFiles)
|
||||
iteration++
|
||||
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
}
|
||||
|
||||
uploadsController.processFilesForDisplay = function(req, res, files, existingFiles){
|
||||
|
||||
|
||||
let basedomain = req.get('host')
|
||||
for(let domain of config.domains)
|
||||
if(domain.host === req.get('host'))
|
||||
@ -139,28 +150,38 @@ uploadsController.processFilesForDisplay = function(req, res, files, existingFil
|
||||
|
||||
uploadsController.delete = function(req, res){
|
||||
|
||||
if(req.headers.auth !== config.adminToken)
|
||||
return res.status(401).json({ success: false, description: 'not-authorized'})
|
||||
let token = req.headers.token
|
||||
if(token === undefined) return res.status(401).json({ success: false, description: 'No token provided' })
|
||||
|
||||
let id = req.body.id
|
||||
if(id === undefined || id === '')
|
||||
return res.json({ success: false, description: 'No file specified' })
|
||||
|
||||
db.table('files').where('id', id).then((file) => {
|
||||
db.table('users').where('token', token).then((user) => {
|
||||
if(user.length === 0) return res.status(401).json({ success: false, description: 'Invalid token'})
|
||||
|
||||
uploadsController.deleteFile(file[0].name).then(() => {
|
||||
db.table('files').where('id', id).del().then(() =>{
|
||||
return res.json({ success: true })
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
}).catch((e) => {
|
||||
console.log(e.toString())
|
||||
db.table('files').where('id', id).del().then(() =>{
|
||||
return res.json({ success: true })
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
db.table('files')
|
||||
.where('id', id)
|
||||
.where(function(){
|
||||
if(user[0].username !== 'root')
|
||||
this.where('userid', user[0].id)
|
||||
})
|
||||
.then((file) => {
|
||||
|
||||
uploadsController.deleteFile(file[0].name).then(() => {
|
||||
db.table('files').where('id', id).del().then(() =>{
|
||||
return res.json({ success: true })
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
}).catch((e) => {
|
||||
console.log(e.toString())
|
||||
db.table('files').where('id', id).del().then(() =>{
|
||||
return res.json({ success: true })
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
})
|
||||
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
|
||||
|
||||
}
|
||||
|
||||
uploadsController.deleteFile = function(file){
|
||||
@ -179,86 +200,92 @@ uploadsController.deleteFile = function(file){
|
||||
|
||||
uploadsController.list = function(req, res){
|
||||
|
||||
if(req.headers.auth !== config.adminToken)
|
||||
return res.status(401).json({ success: false, description: 'not-authorized'})
|
||||
let token = req.headers.token
|
||||
if(token === undefined) return res.status(401).json({ success: false, description: 'No token provided' })
|
||||
|
||||
let offset = req.params.page
|
||||
if(offset === undefined) offset = 0
|
||||
db.table('users').where('token', token).then((user) => {
|
||||
if(user.length === 0) return res.status(401).json({ success: false, description: 'Invalid token'})
|
||||
|
||||
db.table('files')
|
||||
.where(function(){
|
||||
if(req.params.id === undefined)
|
||||
this.where('id', '<>', '')
|
||||
else
|
||||
this.where('albumid', req.params.id)
|
||||
})
|
||||
.orderBy('id', 'DESC')
|
||||
.limit(25)
|
||||
.offset(25 * offset)
|
||||
.then((files) => {
|
||||
db.table('albums').then((albums) => {
|
||||
let offset = req.params.page
|
||||
if(offset === undefined) offset = 0
|
||||
|
||||
let basedomain = req.get('host')
|
||||
for(let domain of config.domains)
|
||||
if(domain.host === req.get('host'))
|
||||
if(domain.hasOwnProperty('resolve'))
|
||||
basedomain = domain.resolve
|
||||
db.table('files')
|
||||
.where(function(){
|
||||
if(req.params.id === undefined)
|
||||
this.where('id', '<>', '')
|
||||
else
|
||||
this.where('albumid', req.params.id)
|
||||
})
|
||||
.where(function(){
|
||||
if(user[0].username !== 'root')
|
||||
this.where('userid', user[0].id)
|
||||
})
|
||||
.orderBy('id', 'DESC')
|
||||
.limit(25)
|
||||
.offset(25 * offset)
|
||||
.then((files) => {
|
||||
db.table('albums').then((albums) => {
|
||||
|
||||
for(let file of files){
|
||||
file.file = basedomain + '/' + file.name
|
||||
file.date = new Date(file.timestamp * 1000)
|
||||
file.date = file.date.getFullYear() + '-' + (file.date.getMonth() + 1) + '-' + file.date.getDate() + ' ' + (file.date.getHours() < 10 ? '0' : '') + file.date.getHours() + ':' + (file.date.getMinutes() < 10 ? '0' : '') + file.date.getMinutes() + ':' + (file.date.getSeconds() < 10 ? '0' : '') + file.date.getSeconds()
|
||||
let basedomain = req.get('host')
|
||||
for(let domain of config.domains)
|
||||
if(domain.host === req.get('host'))
|
||||
if(domain.hasOwnProperty('resolve'))
|
||||
basedomain = domain.resolve
|
||||
|
||||
file.album = ''
|
||||
|
||||
if(file.albumid !== undefined)
|
||||
for(let album of albums)
|
||||
if(file.albumid === album.id)
|
||||
file.album = album.name
|
||||
for(let file of files){
|
||||
file.file = basedomain + '/' + file.name
|
||||
file.date = new Date(file.timestamp * 1000)
|
||||
file.date = file.date.getFullYear() + '-' + (file.date.getMonth() + 1) + '-' + file.date.getDate() + ' ' + (file.date.getHours() < 10 ? '0' : '') + file.date.getHours() + ':' + (file.date.getMinutes() < 10 ? '0' : '') + file.date.getMinutes() + ':' + (file.date.getSeconds() < 10 ? '0' : '') + file.date.getSeconds()
|
||||
|
||||
if(config.uploads.generateThumbnails === true){
|
||||
file.album = ''
|
||||
|
||||
if(file.albumid !== undefined)
|
||||
for(let album of albums)
|
||||
if(file.albumid === album.id)
|
||||
file.album = album.name
|
||||
|
||||
let extensions = ['.jpg', '.jpeg', '.bmp', '.gif', '.png']
|
||||
for(let ext of extensions){
|
||||
if(path.extname(file.name) === ext){
|
||||
if(config.uploads.generateThumbnails === true){
|
||||
|
||||
file.thumb = basedomain + '/thumbs/' + file.name.slice(0, -4) + '.png'
|
||||
let extensions = ['.jpg', '.jpeg', '.bmp', '.gif', '.png']
|
||||
for(let ext of extensions){
|
||||
if(path.extname(file.name) === ext){
|
||||
|
||||
let thumbname = path.join(__dirname, '..', 'uploads', 'thumbs') + '/' + file.name.slice(0, -4) + '.png'
|
||||
fs.access(thumbname, function(err) {
|
||||
if (err && err.code === 'ENOENT') {
|
||||
// File doesnt exist
|
||||
file.thumb = basedomain + '/thumbs/' + file.name.slice(0, -4) + '.png'
|
||||
|
||||
let size = {
|
||||
width: 200,
|
||||
height: 200
|
||||
let thumbname = path.join(__dirname, '..', 'uploads', 'thumbs') + '/' + file.name.slice(0, -4) + '.png'
|
||||
fs.access(thumbname, function(err) {
|
||||
if (err && err.code === 'ENOENT') {
|
||||
// File doesnt exist
|
||||
|
||||
let size = {
|
||||
width: 200,
|
||||
height: 200
|
||||
}
|
||||
|
||||
gm('./' + config.uploads.folder + '/' + file.name)
|
||||
.resize(size.width, size.height + '>')
|
||||
.gravity('Center')
|
||||
.extent(size.width, size.height)
|
||||
.background('transparent')
|
||||
.write(thumbname, function (error) {
|
||||
if (error) console.log('Error - ', error)
|
||||
})
|
||||
}
|
||||
|
||||
gm('./' + config.uploads.folder + '/' + file.name)
|
||||
.resize(size.width, size.height + '>')
|
||||
.gravity('Center')
|
||||
.extent(size.width, size.height)
|
||||
.background('transparent')
|
||||
.write(thumbname, function (error) {
|
||||
if (error) console.log('Error - ', error)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
files
|
||||
})
|
||||
return res.json({
|
||||
success: true,
|
||||
files
|
||||
})
|
||||
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
}).catch(function(error) { console.log(error); res.json({success: false, description: 'error'}) })
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = uploadsController
|
@ -1,9 +1,9 @@
|
||||
|
||||
let init = function(db, config){
|
||||
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.integer('enabled')
|
||||
table.integer('timestamp')
|
||||
@ -11,6 +11,7 @@ let init = function(db, config){
|
||||
|
||||
db.schema.createTableIfNotExists('files', function (table) {
|
||||
table.increments()
|
||||
table.integer('userid')
|
||||
table.string('name')
|
||||
table.string('original')
|
||||
table.string('type')
|
||||
@ -21,48 +22,28 @@ let init = function(db, config){
|
||||
table.integer('timestamp')
|
||||
}).then(() => {})
|
||||
|
||||
db.schema.createTableIfNotExists('tokens', function (table) {
|
||||
table.string('name')
|
||||
table.string('value')
|
||||
db.schema.createTableIfNotExists('users', function (table) {
|
||||
table.increments()
|
||||
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
|
||||
|
||||
// == Generate a 1 time token == //
|
||||
db.table('tokens').then((tokens) => {
|
||||
if(tokens.length !== 0) return printAndSave(config, tokens[0].value, tokens[1].value)
|
||||
|
||||
// This is the first launch of the app
|
||||
let clientToken = require('randomstring').generate()
|
||||
let adminToken = require('randomstring').generate()
|
||||
let now = Math.floor(Date.now() / 1000)
|
||||
|
||||
db.table('tokens').insert(
|
||||
[
|
||||
{
|
||||
name: 'client',
|
||||
value: clientToken,
|
||||
timestamp: now
|
||||
},
|
||||
{
|
||||
name: 'admin',
|
||||
value: adminToken,
|
||||
timestamp: now
|
||||
}
|
||||
]
|
||||
).then(() => {
|
||||
printAndSave(config, clientToken, adminToken)
|
||||
}).catch(function(error) { console.log(error) })
|
||||
}).catch(function(error) { console.log(error) })
|
||||
require('bcrypt').hash('root', 10, function(err, hash) {
|
||||
if(err) console.error('Error generating password hash for root')
|
||||
|
||||
db.table('users').insert({
|
||||
username: 'root',
|
||||
password: hash,
|
||||
token: require('randomstring').generate(64),
|
||||
timestamp: Math.floor(Date.now() / 1000)
|
||||
}).then(() => {})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
function printAndSave(config, clientToken, adminToken){
|
||||
console.log('Your client token is: ' + clientToken)
|
||||
console.log('Your admin token is: ' + adminToken)
|
||||
config.clientToken = clientToken
|
||||
config.adminToken = adminToken
|
||||
}
|
||||
|
||||
module.exports = init
|
@ -6,7 +6,7 @@ const db = require('knex')(config.database)
|
||||
const fs = require('fs')
|
||||
const safe = express()
|
||||
|
||||
require('./database/db.js')(db, config)
|
||||
require('./database/db.js')(db)
|
||||
|
||||
fs.existsSync('./' + config.logsFolder) || fs.mkdirSync('./' + config.logsFolder)
|
||||
fs.existsSync('./' + config.uploads.folder) || fs.mkdirSync('./' + config.uploads.folder)
|
||||
@ -22,6 +22,7 @@ safe.use('/', express.static('./public'))
|
||||
safe.use('/api', api)
|
||||
|
||||
safe.get('/', (req, res, next) => res.sendFile('home.html', { root: './pages/' }))
|
||||
safe.get('/auth', (req, res, next) => res.sendFile('auth.html', { root: './pages/' }))
|
||||
safe.get('/panel', (req, res, next) => res.sendFile('panel.html', { root: './pages/' }))
|
||||
safe.use((req, res, next) => res.status(404).sendFile('404.html', { root: './pages/error/' }))
|
||||
safe.use((req, res, next) => res.status(500).sendFile('500.html', { root: './pages/error/' }))
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "loli-safe",
|
||||
"version": "1.0.0",
|
||||
"version": "2.0.0",
|
||||
"description": "Pomf-like uploading service, written in node",
|
||||
"author": "kanadeko",
|
||||
"repository": {
|
||||
@ -15,6 +15,7 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bcrypt": "^1.0.2",
|
||||
"body-parser": "^1.16.0",
|
||||
"express": "^4.14.0",
|
||||
"gm": "^1.23.0",
|
||||
|
57
pages/auth.html
Normal file
57
pages/auth.html
Normal file
@ -0,0 +1,57 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>loli-safe - A self hosted upload service</title>
|
||||
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.3.0/css/bulma.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/css/style.css">
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.15.3/axios.min.js"></script>
|
||||
<script type="text/javascript" src="https://use.fontawesome.com/cd26baa9bd.js"></script>
|
||||
<script type="text/javascript" src="/js/auth.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<style type="text/css">
|
||||
section#login {
|
||||
background-color: #f5f6f8;
|
||||
}
|
||||
</style>
|
||||
|
||||
<section id='login' class="hero is-fullheight">
|
||||
<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">
|
||||
<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>
|
@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>loli-safe - A self hosted upload service</title>
|
||||
<title>loli-safe - A small safe worth protecting.</title>
|
||||
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.3.0/css/bulma.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/css/style.css">
|
||||
@ -27,7 +27,7 @@
|
||||
<div class="column" id='uploadContainer'>
|
||||
|
||||
<p id='tokenContainer' class="control has-addons has-addons-centered">
|
||||
<input id='token' class="input is-danger" type="text" placeholder="Your upload token">
|
||||
<input id='token' class="input is-danger" type="text" placeholder="Your token">
|
||||
<a id='tokenSubmit' class="button is-danger">Check</a>
|
||||
</p>
|
||||
|
||||
@ -48,7 +48,7 @@
|
||||
</div>
|
||||
|
||||
<h3 id="links">
|
||||
<a href="https://github.com/kanadeko/loli-safe" target="_blank" class="is-danger">View on Github</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="/panel" target="_blank" class="is-danger">Dashboard</a>
|
||||
<a href="https://github.com/kanadeko/loli-safe" target="_blank" class="is-danger">View on Github</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="/auth" target="_blank" class="is-danger">Manage your uploads</a>
|
||||
</h3>
|
||||
|
||||
</div>
|
||||
|
@ -53,7 +53,7 @@
|
||||
</ul>
|
||||
<p class="menu-label">Administration</p>
|
||||
<ul class="menu-list">
|
||||
<li><a id="itemTokens" onclick="panel.changeTokens()">Change your tokens</a></li>
|
||||
<li><a id="itemTokens" onclick="panel.changeTokens()">Change your token</a></li>
|
||||
<li><a onclick="panel.logout()">Logout</a></li>
|
||||
</ul>
|
||||
</aside>
|
||||
|
@ -78,6 +78,19 @@ section#home div#uploads { margin-bottom: 25px; }
|
||||
PANEL
|
||||
------------------ */
|
||||
|
||||
section#login input, section#login p.control a.button {
|
||||
border-left: 0px;
|
||||
border-top: 0px;
|
||||
border-right: 0px;
|
||||
border-radius: 0px;
|
||||
box-shadow: 0 0 0;
|
||||
}
|
||||
|
||||
section#login p.control a.button { margin-left: 10px; }
|
||||
section#login p.control a#loginBtn { border-right: 0px; }
|
||||
section#login p.control a#registerBtn { border-left: 0px; }
|
||||
|
||||
|
||||
section#auth, section#dashboard { display: none }
|
||||
section#auth input { background: rgba(0, 0, 0, 0); }
|
||||
section#auth input, section#auth a {
|
||||
|
56
public/js/auth.js
Normal file
56
public/js/auth.js
Normal file
@ -0,0 +1,56 @@
|
||||
var page = {};
|
||||
|
||||
page.do = function(dest){
|
||||
|
||||
var user = document.getElementById('user').value;
|
||||
var pass = document.getElementById('pass').value;
|
||||
|
||||
if(user === undefined || user === null || user === '')
|
||||
return swal('Error', 'You need to specify a username', 'error');
|
||||
if(pass === undefined || pass === null || pass === '')
|
||||
return swal('Error', 'You need to specify a username', 'error');
|
||||
|
||||
axios.post('/api/' + dest, {
|
||||
username: user,
|
||||
password: pass
|
||||
})
|
||||
.then(function (response) {
|
||||
|
||||
if(response.data.success === false)
|
||||
return swal('Error', response.data.description, 'error');
|
||||
|
||||
localStorage.token = response.data.token;
|
||||
window.location = '/panel';
|
||||
|
||||
})
|
||||
.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.token = localStorage.token;
|
||||
if(page.token === undefined) return;
|
||||
|
||||
axios.post('/api/tokens/verify', {
|
||||
token: page.token
|
||||
})
|
||||
.then(function (response) {
|
||||
|
||||
if(response.data.success === false)
|
||||
return swal('Error', response.data.description, 'error');
|
||||
|
||||
window.location = '/panel';
|
||||
|
||||
})
|
||||
.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.onload = function () {
|
||||
page.verify();
|
||||
}
|
@ -1,17 +1,11 @@
|
||||
let panel = {}
|
||||
|
||||
panel.page;
|
||||
panel.token = localStorage.admintoken;
|
||||
panel.token = localStorage.token;
|
||||
panel.filesView = localStorage.filesView;
|
||||
|
||||
panel.preparePage = function(){
|
||||
if(!panel.token){
|
||||
document.getElementById('auth').style.display = 'flex';
|
||||
document.getElementById('tokenSubmit').addEventListener('click', function(){
|
||||
panel.verifyToken(document.getElementById('token').value);
|
||||
});
|
||||
return;
|
||||
}
|
||||
if(!panel.token) return window.location = '/auth';
|
||||
panel.verifyToken(panel.token, true);
|
||||
}
|
||||
|
||||
@ -20,7 +14,6 @@ panel.verifyToken = function(token, reloadOnError){
|
||||
reloadOnError = false;
|
||||
|
||||
axios.post('/api/tokens/verify', {
|
||||
type: 'admin',
|
||||
token: token
|
||||
})
|
||||
.then(function (response) {
|
||||
@ -32,15 +25,15 @@ panel.verifyToken = function(token, reloadOnError){
|
||||
type: "error"
|
||||
}, function(){
|
||||
if(reloadOnError){
|
||||
localStorage.removeItem("admintoken");
|
||||
location.reload();
|
||||
localStorage.removeItem("token");
|
||||
location.location = '/auth';
|
||||
}
|
||||
})
|
||||
return;
|
||||
}
|
||||
|
||||
axios.defaults.headers.common['auth'] = token;
|
||||
localStorage.admintoken = token;
|
||||
axios.defaults.headers.common['token'] = token;
|
||||
localStorage.token = token;
|
||||
panel.token = token;
|
||||
return panel.prepareDashboard();
|
||||
|
||||
@ -73,7 +66,7 @@ panel.prepareDashboard = function(){
|
||||
}
|
||||
|
||||
panel.logout = function(){
|
||||
localStorage.removeItem("admintoken");
|
||||
localStorage.removeItem("token");
|
||||
location.reload('/');
|
||||
}
|
||||
|
||||
@ -85,14 +78,12 @@ panel.getUploads = function(album = undefined, page = undefined){
|
||||
if(album !== undefined)
|
||||
url = '/api/album/' + album + '/' + page
|
||||
|
||||
axios.get(url)
|
||||
.then(function (response) {
|
||||
axios.get(url).then(function (response) {
|
||||
if(response.data.success === false){
|
||||
if(response.data.description === 'not-authorized') return panel.verifyToken(panel.token);
|
||||
if(response.data.description === 'No token provided') return panel.verifyToken(panel.token);
|
||||
else return swal("An error ocurred", response.data.description, "error");
|
||||
}
|
||||
|
||||
|
||||
var prevPage = 0;
|
||||
var nextPage = page + 1;
|
||||
|
||||
@ -125,9 +116,7 @@ panel.getUploads = function(album = undefined, page = undefined){
|
||||
|
||||
if(panel.filesView === 'thumbs'){
|
||||
|
||||
|
||||
container.innerHTML = `
|
||||
|
||||
${pagination}
|
||||
<hr>
|
||||
${listType}
|
||||
@ -135,10 +124,8 @@ panel.getUploads = function(album = undefined, page = undefined){
|
||||
|
||||
</div>
|
||||
${pagination}
|
||||
|
||||
`;
|
||||
|
||||
|
||||
panel.page.appendChild(container);
|
||||
var table = document.getElementById('table');
|
||||
|
||||
@ -157,7 +144,6 @@ panel.getUploads = function(album = undefined, page = undefined){
|
||||
}else{
|
||||
|
||||
container.innerHTML = `
|
||||
|
||||
${pagination}
|
||||
<hr>
|
||||
${listType}
|
||||
@ -175,7 +161,6 @@ panel.getUploads = function(album = undefined, page = undefined){
|
||||
</table>
|
||||
<hr>
|
||||
${pagination}
|
||||
|
||||
`;
|
||||
|
||||
panel.page.appendChild(container);
|
||||
@ -201,11 +186,7 @@ panel.getUploads = function(album = undefined, page = undefined){
|
||||
|
||||
table.appendChild(tr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
})
|
||||
.catch(function (error) {
|
||||
return swal("An error ocurred", 'There was an error with the request, please check the console for more information.', "error");
|
||||
@ -238,7 +219,7 @@ panel.deleteFile = function(id){
|
||||
.then(function (response) {
|
||||
|
||||
if(response.data.success === false){
|
||||
if(response.data.description === 'not-authorized') return panel.verifyToken(panel.token);
|
||||
if(response.data.description === 'No token provided') return panel.verifyToken(panel.token);
|
||||
else return swal("An error ocurred", response.data.description, "error");
|
||||
}
|
||||
|
||||
@ -258,10 +239,9 @@ panel.deleteFile = function(id){
|
||||
|
||||
panel.getAlbums = function(){
|
||||
|
||||
axios.get('/api/albums')
|
||||
.then(function (response) {
|
||||
axios.get('/api/albums').then(function (response) {
|
||||
if(response.data.success === false){
|
||||
if(response.data.description === 'not-authorized') return panel.verifyToken(panel.token);
|
||||
if(response.data.description === 'No token provided') return panel.verifyToken(panel.token);
|
||||
else return swal("An error ocurred", response.data.description, "error");
|
||||
}
|
||||
|
||||
@ -324,7 +304,6 @@ panel.getAlbums = function(){
|
||||
panel.submitAlbum();
|
||||
});
|
||||
|
||||
|
||||
})
|
||||
.catch(function (error) {
|
||||
return swal("An error ocurred", 'There was an error with the request, please check the console for more information.', "error");
|
||||
@ -357,7 +336,7 @@ panel.renameAlbum = function(id){
|
||||
.then(function (response) {
|
||||
|
||||
if(response.data.success === false){
|
||||
if(response.data.description === 'not-authorized') return panel.verifyToken(panel.token);
|
||||
if(response.data.description === 'No token provided') return panel.verifyToken(panel.token);
|
||||
else if(response.data.description === 'Name already in use') swal.showInputError("That name is already in use!");
|
||||
else swal("An error ocurred", response.data.description, "error");
|
||||
return;
|
||||
@ -396,7 +375,7 @@ panel.deleteAlbum = function(id){
|
||||
.then(function (response) {
|
||||
|
||||
if(response.data.success === false){
|
||||
if(response.data.description === 'not-authorized') return panel.verifyToken(panel.token);
|
||||
if(response.data.description === 'No token provided') return panel.verifyToken(panel.token);
|
||||
else return swal("An error ocurred", response.data.description, "error");
|
||||
}
|
||||
|
||||
@ -424,7 +403,7 @@ panel.submitAlbum = function(){
|
||||
.then(function (response) {
|
||||
|
||||
if(response.data.success === false){
|
||||
if(response.data.description === 'not-authorized') return panel.verifyToken(panel.token);
|
||||
if(response.data.description === 'No token provided') return panel.verifyToken(panel.token);
|
||||
else return swal("An error ocurred", response.data.description, "error");
|
||||
}
|
||||
|
||||
@ -446,7 +425,7 @@ panel.getAlbumsSidebar = function(){
|
||||
axios.get('/api/albums/sidebar')
|
||||
.then(function (response) {
|
||||
if(response.data.success === false){
|
||||
if(response.data.description === 'not-authorized') return panel.verifyToken(panel.token);
|
||||
if(response.data.description === 'No token provided') return panel.verifyToken(panel.token);
|
||||
else return swal("An error ocurred", response.data.description, "error");
|
||||
}
|
||||
|
||||
@ -489,7 +468,7 @@ panel.changeTokens = function(){
|
||||
axios.get('/api/tokens')
|
||||
.then(function (response) {
|
||||
if(response.data.success === false){
|
||||
if(response.data.description === 'not-authorized') return panel.verifyToken(panel.token);
|
||||
if(response.data.description === 'No token provided') return panel.verifyToken(panel.token);
|
||||
else return swal("An error ocurred", response.data.description, "error");
|
||||
}
|
||||
|
||||
@ -497,35 +476,21 @@ panel.changeTokens = function(){
|
||||
var container = document.createElement('div');
|
||||
container.className = "container";
|
||||
container.innerHTML = `
|
||||
<h2 class="subtitle">Manage your tokens</h2>
|
||||
<h2 class="subtitle">Manage your token</h2>
|
||||
|
||||
<label class="label">Client token:</label>
|
||||
<label class="label">Your current token:</label>
|
||||
<p class="control has-addons">
|
||||
<input id="clientToken" class="input is-expanded" type="text" placeholder="Your client token">
|
||||
<a id="submitClientToken" class="button is-primary">Save</a>
|
||||
</p>
|
||||
|
||||
<label class="label">Admin token:</label>
|
||||
<p class="control has-addons">
|
||||
<input id="adminToken" class="input is-expanded" type="text" placeholder="Your admin token">
|
||||
<a id="submitAdminToken" class="button is-primary">Save</a>
|
||||
<input id="token" readonly class="input is-expanded" type="text" placeholder="Your token" value="${response.data.token}">
|
||||
<a id="getNewToken" class="button is-primary">Request new token</a>
|
||||
</p>
|
||||
`;
|
||||
|
||||
panel.page.appendChild(container);
|
||||
|
||||
document.getElementById('clientToken').value = response.data.clientToken;
|
||||
document.getElementById('adminToken').value = response.data.adminToken;
|
||||
|
||||
document.getElementById('submitClientToken').addEventListener('click', function(){
|
||||
panel.submitToken('client', document.getElementById('clientToken').value);
|
||||
document.getElementById('getNewToken').addEventListener('click', function(){
|
||||
panel.getNewToken();
|
||||
});
|
||||
|
||||
document.getElementById('submitAdminToken').addEventListener('click', function(){
|
||||
panel.submitToken('admin', document.getElementById('adminToken').value);
|
||||
});
|
||||
|
||||
|
||||
})
|
||||
.catch(function (error) {
|
||||
return swal("An error ocurred", 'There was an error with the request, please check the console for more information.', "error");
|
||||
@ -534,16 +499,13 @@ panel.changeTokens = function(){
|
||||
|
||||
}
|
||||
|
||||
panel.submitToken = function(type, token){
|
||||
panel.getNewToken = function(){
|
||||
|
||||
axios.post('/api/tokens/change', {
|
||||
type: type,
|
||||
token: token
|
||||
})
|
||||
axios.post('/api/tokens/change')
|
||||
.then(function (response) {
|
||||
|
||||
if(response.data.success === false){
|
||||
if(response.data.description === 'not-authorized') return panel.verifyToken(panel.token);
|
||||
if(response.data.description === 'No token provided') return panel.verifyToken(panel.token);
|
||||
else return swal("An error ocurred", response.data.description, "error");
|
||||
}
|
||||
|
||||
@ -552,14 +514,8 @@ panel.submitToken = function(type, token){
|
||||
text: 'Your token was changed successfully.',
|
||||
type: "success"
|
||||
}, function(){
|
||||
|
||||
if(type === 'client')
|
||||
localStorage.token = token;
|
||||
else if(type === 'admin')
|
||||
localStorage.admintoken = token
|
||||
|
||||
localStorage.token = response.data.token;
|
||||
location.reload();
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
@ -36,7 +36,6 @@ upload.verifyToken = function(token, reloadOnError){
|
||||
reloadOnError = false;
|
||||
|
||||
axios.post('/api/tokens/verify', {
|
||||
type: 'client',
|
||||
token: token
|
||||
})
|
||||
.then(function (response) {
|
||||
@ -101,10 +100,11 @@ upload.prepareDropzone = function(){
|
||||
maxFiles: 1000,
|
||||
autoProcessQueue: true,
|
||||
headers: {
|
||||
'auth': upload.token
|
||||
'token': upload.token
|
||||
},
|
||||
init: function() {
|
||||
this.on('addedfile', function(file) {
|
||||
myDropzone = this;
|
||||
document.getElementById('uploads').style.display = 'block';
|
||||
});
|
||||
}
|
||||
@ -139,6 +139,22 @@ upload.prepareDropzone = function(){
|
||||
|
||||
}
|
||||
|
||||
//Handle image paste event
|
||||
window.addEventListener('paste', function(event) {
|
||||
var items = (event.clipboardData || event.originalEvent.clipboardData).items;
|
||||
for (index in items) {
|
||||
var item = items[index];
|
||||
if (item.kind === 'file') {
|
||||
var blob = item.getAsFile();
|
||||
console.log(blob.type);
|
||||
var file = new File([blob], "pasted-image."+blob.type.match(/(?:[^\/]*\/)([^;]*)/)[1]);
|
||||
file.type = blob.type;
|
||||
console.log(file);
|
||||
myDropzone.addFile(file);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
window.onload = function () {
|
||||
upload.checkIfPublic();
|
||||
};
|
@ -3,6 +3,7 @@ const routes = require('express').Router()
|
||||
const uploadController = require('../controllers/uploadController')
|
||||
const albumsController = require('../controllers/albumsController')
|
||||
const tokenController = require('../controllers/tokenController')
|
||||
const authController = require('../controllers/authController')
|
||||
|
||||
routes.get ('/check', (req, res, next) => {
|
||||
return res.json({
|
||||
@ -11,6 +12,9 @@ routes.get ('/check', (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.get ('/uploads', (req, res, next) => uploadController.list(req, res))
|
||||
routes.get ('/uploads/:page', (req, res, next) => uploadController.list(req, res))
|
||||
routes.post ('/upload', (req, res, next) => uploadController.upload(req, res, next))
|
||||
|
Loading…
Reference in New Issue
Block a user