arjion
Este commit está contenido en:
5
production/arjion/arjion/Dockerfile
Archivo normal
5
production/arjion/arjion/Dockerfile
Archivo normal
@@ -0,0 +1,5 @@
|
|||||||
|
FROM node:8-slim
|
||||||
|
RUN mkdir -p /usr/share/man/man1 && mkdir -p /usr/share/man/man5 && mkdir -p /usr/share/man/man8
|
||||||
|
RUN apt update && apt -y upgrade && apt -y install python default-jdk build-essential libimage-exiftool-perl && apt clean
|
||||||
|
USER node
|
||||||
|
WORKDIR /arjion
|
||||||
17
production/arjion/arjion/config.js
Archivo normal
17
production/arjion/arjion/config.js
Archivo normal
@@ -0,0 +1,17 @@
|
|||||||
|
module.exports = {
|
||||||
|
mailserver: 'smtp.hatthieves.es',
|
||||||
|
mailport: 587,
|
||||||
|
mailsecure: true,
|
||||||
|
mailfrom: 'webmaster@hatthieves.es',
|
||||||
|
mailuser: 'webmaster@hatthieves.es',
|
||||||
|
mailpass: 'w3bm4st3r.',
|
||||||
|
mailtext: verify_link => 'Verify your account visiting next link: https://meta.hatthieves.es/verify?link=' + verify_link,
|
||||||
|
mailhtml: verify_link => 'Verify your account visiting next <a href="https://meta.hatthieves.es/verify?link=' + verify_link + '">Verify Link</a>',
|
||||||
|
indexhost: 'https://elastic.hatthieves.es',
|
||||||
|
indexuser: 'docker',
|
||||||
|
indexpass: 'docker',
|
||||||
|
index: 'arjion',
|
||||||
|
type: 'user',
|
||||||
|
port: 3000,
|
||||||
|
anonymous: 'anonymous'
|
||||||
|
}
|
||||||
2
production/arjion/arjion/entrypoint.sh
Archivo normal
2
production/arjion/arjion/entrypoint.sh
Archivo normal
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
yarn && node server
|
||||||
108
production/arjion/arjion/lib/elastic.js
Archivo normal
108
production/arjion/arjion/lib/elastic.js
Archivo normal
@@ -0,0 +1,108 @@
|
|||||||
|
module.exports = (config, client6) => {
|
||||||
|
(async () => {
|
||||||
|
const exist = await client6.indices.exists({
|
||||||
|
index: config.index
|
||||||
|
})
|
||||||
|
if (!exist.body) {
|
||||||
|
await client6.indices.create({
|
||||||
|
index: config.index,
|
||||||
|
body: {
|
||||||
|
settings: {
|
||||||
|
number_of_shards: 1,
|
||||||
|
number_of_replicas: 0
|
||||||
|
},
|
||||||
|
mappings: {
|
||||||
|
user: {
|
||||||
|
dynamic_templates: [
|
||||||
|
{
|
||||||
|
metadata_as_keywords: {
|
||||||
|
path_match: 'documents.metadata.*',
|
||||||
|
mapping: {
|
||||||
|
type: 'keyword'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
properties: {
|
||||||
|
date: {
|
||||||
|
type: 'date',
|
||||||
|
format: 'dd-MM-yyyy HH:mm:ss||dd-MM-yyyy||epoch_millis'
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
type: 'text',
|
||||||
|
fielddata: true
|
||||||
|
},
|
||||||
|
password: {
|
||||||
|
type: 'keyword'
|
||||||
|
},
|
||||||
|
email: {
|
||||||
|
type: 'text'
|
||||||
|
},
|
||||||
|
verified: {
|
||||||
|
type: 'boolean'
|
||||||
|
},
|
||||||
|
verify_link: {
|
||||||
|
type: 'text'
|
||||||
|
},
|
||||||
|
locked: {
|
||||||
|
type: 'boolean'
|
||||||
|
},
|
||||||
|
documents: {
|
||||||
|
type: 'nested',
|
||||||
|
properties: {
|
||||||
|
date: {
|
||||||
|
type: 'date',
|
||||||
|
format: 'dd-MM-yyyy HH:mm:ss||dd-MM-yyyy||epoch_millis'
|
||||||
|
},
|
||||||
|
originalname: {
|
||||||
|
type: 'text'
|
||||||
|
},
|
||||||
|
filename: {
|
||||||
|
type: 'text'
|
||||||
|
},
|
||||||
|
path: {
|
||||||
|
type: 'text'
|
||||||
|
},
|
||||||
|
mimetype: {
|
||||||
|
type: 'text'
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: 'long'
|
||||||
|
},
|
||||||
|
metadata: {
|
||||||
|
type: 'object'
|
||||||
|
},
|
||||||
|
language: {
|
||||||
|
type: 'text'
|
||||||
|
},
|
||||||
|
sha256: {
|
||||||
|
type: 'text'
|
||||||
|
},
|
||||||
|
deleted: {
|
||||||
|
type: 'boolean'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch(err => console.error)
|
||||||
|
await client6.index({
|
||||||
|
index: config.index,
|
||||||
|
type: config.type,
|
||||||
|
body: {
|
||||||
|
date: Date.now(),
|
||||||
|
user: config.anonymous,
|
||||||
|
password: null,
|
||||||
|
email: null,
|
||||||
|
verified: true,
|
||||||
|
verify_link: null,
|
||||||
|
documents: [],
|
||||||
|
locked: false
|
||||||
|
},
|
||||||
|
refresh: true
|
||||||
|
}).catch(err => console.error)
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
}
|
||||||
210
production/arjion/arjion/lib/index.js
Archivo normal
210
production/arjion/arjion/lib/index.js
Archivo normal
@@ -0,0 +1,210 @@
|
|||||||
|
module.exports = (router, passport, config, client6, upload, fs, tika, promisify, crypto, transporter) => {
|
||||||
|
router.get('/', (req, res) => {
|
||||||
|
return res.render('home', { user: req.user })
|
||||||
|
})
|
||||||
|
|
||||||
|
router.get('/login', (req, res) => {
|
||||||
|
return res.render('login', { error: req.flash('error') })
|
||||||
|
})
|
||||||
|
|
||||||
|
router.post('/login', passport.authenticate('local', { failWithError: true, successRedirect: '/' }), (err, req, res, next) => {
|
||||||
|
if (err) {
|
||||||
|
req.flash('error', err.message)
|
||||||
|
return res.redirect('/login')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
router.get('/logout', (req, res) => {
|
||||||
|
req.logout()
|
||||||
|
return res.redirect('/')
|
||||||
|
})
|
||||||
|
|
||||||
|
router.get('/register', (req, res) => {
|
||||||
|
return res.render('register', { error: req.flash('error') })
|
||||||
|
})
|
||||||
|
|
||||||
|
router.post('/register', (req, res, next) => {
|
||||||
|
if (req.body.email === req.body.email2) {
|
||||||
|
if (req.body.username.length >= 4) {
|
||||||
|
client6.search({
|
||||||
|
index: config.index,
|
||||||
|
body: {
|
||||||
|
query: {
|
||||||
|
match: {
|
||||||
|
user: req.body.username
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, (err, user) => {
|
||||||
|
if (err) {
|
||||||
|
return next(err)
|
||||||
|
}
|
||||||
|
else if (user.body.hits.total === 0) {
|
||||||
|
if (req.body.password.length > 6) {
|
||||||
|
if (req.body.email.match(/\S+@\S+\.\S+/)) {
|
||||||
|
const verify_link = crypto.createHash('sha256').update(Date.now().toString()).digest('hex')
|
||||||
|
transporter.sendMail({
|
||||||
|
from: config.mailfrom,
|
||||||
|
to: req.body.email,
|
||||||
|
subject: 'Verify link',
|
||||||
|
text: config.mailtext(verify_link),
|
||||||
|
html: config.mailhtml(verify_link)
|
||||||
|
}, async (error, info) => {
|
||||||
|
if (error) {
|
||||||
|
return next(err)
|
||||||
|
} else {
|
||||||
|
await client6.index({
|
||||||
|
index: config.index,
|
||||||
|
type: config.type,
|
||||||
|
body: {
|
||||||
|
date: Date.now(),
|
||||||
|
user: req.body.username,
|
||||||
|
password: crypto.createHash('sha256').update(req.body.password).digest('base64'),
|
||||||
|
email: req.body.email,
|
||||||
|
verified: false,
|
||||||
|
verify_link: verify_link,
|
||||||
|
documents: [],
|
||||||
|
locked: false
|
||||||
|
},
|
||||||
|
refresh: true
|
||||||
|
}).catch(err => {
|
||||||
|
return next(err)
|
||||||
|
})
|
||||||
|
return res.render('verify', { message: info })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
req.flash('error', 'Email not valid')
|
||||||
|
return res.redirect('/register')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
req.flash('error', 'Password must be more than 6 chars')
|
||||||
|
return res.redirect('/register')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
req.flash('error', 'User exists')
|
||||||
|
return res.redirect('/register')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
req.flash('error', 'Username must be 4 or more chars')
|
||||||
|
return res.redirect('/register')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
req.flash('error', 'Email must be the same and real')
|
||||||
|
return res.redirect('/register')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
router.get('/verify', (req, res, next) => {
|
||||||
|
if (req.query.link && req.query.link.length === 64 && req.query.link.match(/^[0-9a-f]{64}$/g)) {
|
||||||
|
client6.search({
|
||||||
|
index: config.index,
|
||||||
|
body: {
|
||||||
|
query: {
|
||||||
|
match: {
|
||||||
|
verify_link: req.query.link
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, async (err, user) => {
|
||||||
|
if (err) {
|
||||||
|
return next(err)
|
||||||
|
} else if (user.body.hits.total === 1 && !user.body.hits.hits[0]._source.verified) {
|
||||||
|
await client6.update({
|
||||||
|
index: config.index,
|
||||||
|
type: config.type,
|
||||||
|
id: user.body.hits.hits[0]._id,
|
||||||
|
body: {
|
||||||
|
doc: {
|
||||||
|
verified: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch(err => next(err))
|
||||||
|
return res.redirect('/login')
|
||||||
|
} else {
|
||||||
|
req.flash('error', 'Invalid user')
|
||||||
|
return res.redirect('/login')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
req.flash('error', 'Invalid link')
|
||||||
|
return res.redirect('/login')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
router.get('/upload', (req, res) => {
|
||||||
|
return res.render('upload')
|
||||||
|
})
|
||||||
|
|
||||||
|
router.post('/upload', upload.array('documents', 6), (req, res, next) => {
|
||||||
|
client6.search({
|
||||||
|
index: config.index,
|
||||||
|
body: {
|
||||||
|
query: {
|
||||||
|
match: {
|
||||||
|
user: config.anonymous
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, async (err, user) => {
|
||||||
|
if (err) {
|
||||||
|
return next(err)
|
||||||
|
} else if (user.body.hits.total === 1 && !user.body.hits.hits[0]._source.locked) {
|
||||||
|
const files = req.files.map(file => {
|
||||||
|
return promisify(tika.language)(fs.readFileSync(__dirname + '/../' + file.path).toString()).then((language, reasonablyCertain) => {
|
||||||
|
return promisify(tika.meta)(__dirname + '/../' + file.path, { contentType: file.mimetype }).then(async meta => {
|
||||||
|
delete meta['X-Parsed-By']
|
||||||
|
delete meta.resourceName
|
||||||
|
await client6.update({
|
||||||
|
index: config.index,
|
||||||
|
type: config.type,
|
||||||
|
id: user.body.hits.hits[0]._id,
|
||||||
|
body: {
|
||||||
|
script: {
|
||||||
|
source: 'ctx._source.documents.add(params.document)',
|
||||||
|
params: {
|
||||||
|
document: {
|
||||||
|
date: Date.now(),
|
||||||
|
originalname: file.originalname,
|
||||||
|
filename: file.filename,
|
||||||
|
path: file.path,
|
||||||
|
mimetype: file.mimetype,
|
||||||
|
size: file.size,
|
||||||
|
metadata: meta,
|
||||||
|
language: !file.mimetype.match(/^image/) ? language : null,
|
||||||
|
sha256: crypto.createHash('sha256').update(fs.readFileSync(__dirname + '/../' + file.path)).digest('hex'),
|
||||||
|
deleted: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
refresh: 'wait_for',
|
||||||
|
retry_on_conflict: 6
|
||||||
|
}).catch(err => next(err))
|
||||||
|
return {
|
||||||
|
name: file.originalname,
|
||||||
|
size: file.size,
|
||||||
|
mime: file.mimetype,
|
||||||
|
language: !file.mimetype.match(/^image/) ? language : null,
|
||||||
|
filename: file.filename,
|
||||||
|
metadata: meta
|
||||||
|
}
|
||||||
|
}).catch(err => next(err))
|
||||||
|
}).catch(err => next(err))
|
||||||
|
})
|
||||||
|
Promise.all(files).then(data => {
|
||||||
|
return res.render('file', { documents: data })
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return next(new Error('File trouble'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
router.get('/error', (req, res) => {
|
||||||
|
return res.render('error')
|
||||||
|
})
|
||||||
|
|
||||||
|
return router
|
||||||
|
}
|
||||||
62
production/arjion/arjion/lib/passport.js
Archivo normal
62
production/arjion/arjion/lib/passport.js
Archivo normal
@@ -0,0 +1,62 @@
|
|||||||
|
module.exports = (passport, Strategy, crypto, config, client6) => {
|
||||||
|
passport.use(new Strategy((username, password, cb) => {
|
||||||
|
if (username && password) {
|
||||||
|
client6.search({
|
||||||
|
index: config.index,
|
||||||
|
body: {
|
||||||
|
query: {
|
||||||
|
match: {
|
||||||
|
user: username
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, (err, user) => {
|
||||||
|
if (err) {
|
||||||
|
return cb(err)
|
||||||
|
}
|
||||||
|
if (!user || !user.body) {
|
||||||
|
return cb(null, false)
|
||||||
|
}
|
||||||
|
if (user.body.hits.total === 1 && user.body.hits.hits[0]._source.verified && !user.body.hits.hits[0]._source.locked
|
||||||
|
&& user.body.hits.hits[0]._source.password === crypto.createHash('sha256').update(password).digest('base64')) {
|
||||||
|
return cb(null, {
|
||||||
|
id: user.body.hits.hits[0]._id,
|
||||||
|
user: user.body.hits.hits[0]._source.user,
|
||||||
|
email: user.body.hits.hits[0]._source.email
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return cb(null, false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return cb(null, false)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
passport.serializeUser((user, cb) => {
|
||||||
|
cb(null, user.user)
|
||||||
|
})
|
||||||
|
|
||||||
|
passport.deserializeUser(async (username, cb) => {
|
||||||
|
client6.search({
|
||||||
|
index: config.index,
|
||||||
|
body: {
|
||||||
|
query: {
|
||||||
|
match: {
|
||||||
|
user: username
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, (err, user) => {
|
||||||
|
if (err) {
|
||||||
|
return cb(err)
|
||||||
|
}
|
||||||
|
return cb(null, {
|
||||||
|
id: user.body.hits.hits[0]._id,
|
||||||
|
user: user.body.hits.hits[0]._source.user,
|
||||||
|
email: user.body.hits.hits[0]._source.email
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
236
production/arjion/arjion/lib/user.js
Archivo normal
236
production/arjion/arjion/lib/user.js
Archivo normal
@@ -0,0 +1,236 @@
|
|||||||
|
module.exports = (router, config, client6, upload, fs, tika, promisify, crypto, exiftool) => {
|
||||||
|
router.get('/profile', (req, res) => {
|
||||||
|
return res.render('profile', { user: req.user })
|
||||||
|
})
|
||||||
|
|
||||||
|
router.get('/upload', (req, res) => {
|
||||||
|
return res.render('upload', { user: req.user })
|
||||||
|
})
|
||||||
|
|
||||||
|
router.post('/upload', upload.array('documents', 6), (req, res, next) => {
|
||||||
|
const files = req.files.map(file => {
|
||||||
|
return promisify(tika.language)(fs.readFileSync(__dirname + '/../' + file.path).toString()).then((language, reasonablyCertain) => {
|
||||||
|
return promisify(tika.meta)(__dirname + '/../' + file.path, { contentType: file.mimetype }).then(async meta => {
|
||||||
|
delete meta['X-Parsed-By']
|
||||||
|
delete meta.resourceName
|
||||||
|
await client6.update({
|
||||||
|
index: config.index,
|
||||||
|
type: config.type,
|
||||||
|
id: req.user.id,
|
||||||
|
body: {
|
||||||
|
script: {
|
||||||
|
source: 'ctx._source.documents.add(params.document)',
|
||||||
|
params: {
|
||||||
|
document: {
|
||||||
|
date: Date.now(),
|
||||||
|
originalname: file.originalname,
|
||||||
|
filename: file.filename,
|
||||||
|
path: file.path,
|
||||||
|
mimetype: file.mimetype,
|
||||||
|
size: file.size,
|
||||||
|
metadata: meta,
|
||||||
|
language: !file.mimetype.match(/^image/) ? language : null,
|
||||||
|
sha256: crypto.createHash('sha256').update(fs.readFileSync(__dirname + '/../' + file.path)).digest('hex'),
|
||||||
|
deleted: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
refresh: 'wait_for',
|
||||||
|
retry_on_conflict: 6
|
||||||
|
}).catch(err => next(err))
|
||||||
|
return {
|
||||||
|
name: file.originalname,
|
||||||
|
size: file.size,
|
||||||
|
mime: file.mimetype,
|
||||||
|
language: !file.mimetype.match(/^image/) ? language : null,
|
||||||
|
filename: file.filename,
|
||||||
|
metadata: meta
|
||||||
|
}
|
||||||
|
}).catch(err => next(err))
|
||||||
|
}).catch(err => next(err))
|
||||||
|
})
|
||||||
|
Promise.all(files).then(data => {
|
||||||
|
return res.render('file', { documents: data, user: req.user })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
router.get('/download-meta', (req, res, next) => {
|
||||||
|
if (req.query.file && req.query.file.length === 32 && req.query.file.match(/^[0-9a-f]{32}$/g)) {
|
||||||
|
client6.search({
|
||||||
|
index: config.index,
|
||||||
|
body: {
|
||||||
|
query: {
|
||||||
|
nested: {
|
||||||
|
path: 'documents',
|
||||||
|
query: {
|
||||||
|
match: {
|
||||||
|
'documents.filename': req.query.file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, async (err, user) => {
|
||||||
|
if (err) {
|
||||||
|
return next(err)
|
||||||
|
} else if (user.body.hits.total === 1 && !user.body.hits.hits[0]._source.locked) {
|
||||||
|
const document = user.body.hits.hits[0]._source.documents.filter(document => document.filename === req.query.file)[0]
|
||||||
|
fs.exists(__dirname + '/../' + document.path, exists => {
|
||||||
|
if (exists) {
|
||||||
|
const ep = new exiftool.ExiftoolProcess('/usr/bin/exiftool'),
|
||||||
|
read = fs.createReadStream(__dirname + '/../' + document.path),
|
||||||
|
write = fs.createWriteStream(`/tmp/${document.filename}`)
|
||||||
|
res.set('Content-Disposition', `attachment; filename="${document.originalname}"`)
|
||||||
|
res.set('Content-Type', 'application/octet-stream')
|
||||||
|
read.on('error', err => next(err))
|
||||||
|
write.on('error', err => next(err))
|
||||||
|
write.on('finish', () => {
|
||||||
|
ep
|
||||||
|
.open()
|
||||||
|
.then(() => {
|
||||||
|
ep.writeMetadata(`/tmp/${document.filename}`, { all: '' }, ['overwrite_original'])
|
||||||
|
})
|
||||||
|
.then(console.log, err => next(err))
|
||||||
|
.then(() => {
|
||||||
|
ep.close()
|
||||||
|
const nometa = fs.createReadStream(`/tmp/${document.filename}`)
|
||||||
|
nometa.on('error', err => next(err))
|
||||||
|
nometa.pipe(res)
|
||||||
|
})
|
||||||
|
.catch(err => next(err))
|
||||||
|
})
|
||||||
|
read.pipe(write)
|
||||||
|
} else {
|
||||||
|
return next(new Error('File not exists'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return next(new Error('File trouble'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return next(new Error('Is not a file'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
router.get('/download', (req, res, next) => {
|
||||||
|
if (req.query.file && req.query.file.length === 32 && req.query.file.match(/^[0-9a-f]{32}$/g)) {
|
||||||
|
client6.search({
|
||||||
|
index: config.index,
|
||||||
|
body: {
|
||||||
|
query: {
|
||||||
|
nested: {
|
||||||
|
path: 'documents',
|
||||||
|
query: {
|
||||||
|
match: {
|
||||||
|
'documents.filename': req.query.file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, async (err, user) => {
|
||||||
|
if (err) {
|
||||||
|
return next(err)
|
||||||
|
} else if (user.body.hits.total === 1 && !user.body.hits.hits[0]._source.locked) {
|
||||||
|
const document = user.body.hits.hits[0]._source.documents.filter(document => document.filename === req.query.file)[0]
|
||||||
|
fs.exists(__dirname + '/../' + document.path, exists => {
|
||||||
|
if (exists) {
|
||||||
|
res.set('Content-Disposition', `attachment; filename="${document.originalname}"`)
|
||||||
|
res.set('Content-Type', 'application/octet-stream')
|
||||||
|
fs.createReadStream(__dirname + '/../' + document.path).pipe(res)
|
||||||
|
} else {
|
||||||
|
return next(new Error('File not exists'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return next(new Error('File trouble'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return next(new Error('Is not a file'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
router.get('/files', (req, res, next) => {
|
||||||
|
if (req.user && req.user.user) {
|
||||||
|
client6.search({
|
||||||
|
index: config.index,
|
||||||
|
body: {
|
||||||
|
query: {
|
||||||
|
match: {
|
||||||
|
user: req.user.user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, async (err, user) => {
|
||||||
|
if (err) {
|
||||||
|
return next(err)
|
||||||
|
} else if (user.body.hits.total === 1 && !user.body.hits.hits[0]._source.locked) {
|
||||||
|
const documents = user.body.hits.hits[0]._source.documents
|
||||||
|
res.render('files', {
|
||||||
|
documents: documents.filter(document => !document.deleted).map(document => {
|
||||||
|
return {
|
||||||
|
name: document.originalname,
|
||||||
|
size: document.size,
|
||||||
|
mime: document.mimetype,
|
||||||
|
language: document.language,
|
||||||
|
filename: document.filename,
|
||||||
|
metadata: document.metadata
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
user: req.user
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return next(new Error('User required'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
router.get('/deletefile', (req, res, next) => {
|
||||||
|
if (req.query.file && req.query.file.length === 32 && req.query.file.match(/^[0-9a-f]{32}$/g)) {
|
||||||
|
client6.search({
|
||||||
|
index: config.index,
|
||||||
|
body: {
|
||||||
|
query: {
|
||||||
|
nested: {
|
||||||
|
path: 'documents',
|
||||||
|
query: {
|
||||||
|
match: {
|
||||||
|
'documents.filename': req.query.file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, async (err, user) => {
|
||||||
|
if (err) {
|
||||||
|
return next(err)
|
||||||
|
} else if (user.body.hits.total === 1 && !user.body.hits.hits[0]._source.locked) {
|
||||||
|
await client6.update({
|
||||||
|
index: config.index,
|
||||||
|
type: config.type,
|
||||||
|
id: user.body.hits.hits[0]._id,
|
||||||
|
body: {
|
||||||
|
script: {
|
||||||
|
lang: 'painless',
|
||||||
|
source: 'for (d in ctx._source.documents) { if (d.filename == "' + req.query.file + '") { d.deleted = true }}'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
refresh: 'wait_for',
|
||||||
|
retry_on_conflict: 6
|
||||||
|
}).catch(err => next(err))
|
||||||
|
return res.redirect('/user/files')
|
||||||
|
} else {
|
||||||
|
return next(new Error('File trouble'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return next(new Error('File required'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return router
|
||||||
|
}
|
||||||
35
production/arjion/arjion/package.json
Archivo normal
35
production/arjion/arjion/package.json
Archivo normal
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"name": "arjion",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Arjion HatMeta",
|
||||||
|
"main": "server.js",
|
||||||
|
"scripts": {
|
||||||
|
"install": "cp -r node_modules/material-components-web/dist/material-components-web.min.css* node_modules/material-icons/iconfont public/css/ && cp node_modules/material-components-web/dist/material-components-web.min.js public/js/"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"arjion",
|
||||||
|
"metadata"
|
||||||
|
],
|
||||||
|
"author": "ale & gus",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"body-parser": "*",
|
||||||
|
"connect-ensure-login": "*",
|
||||||
|
"connect-flash": "*",
|
||||||
|
"ejs": "*",
|
||||||
|
"es6": "npm:@elastic/elasticsearch@^6.8.0",
|
||||||
|
"es6-promisify": "*",
|
||||||
|
"express": "*",
|
||||||
|
"express-session": "*",
|
||||||
|
"material-components-web": "*",
|
||||||
|
"material-icons": "*",
|
||||||
|
"morgan": "*",
|
||||||
|
"multer": "*",
|
||||||
|
"node-exiftool": "*",
|
||||||
|
"nodemailer": "*",
|
||||||
|
"passport": "*",
|
||||||
|
"passport-local": "*",
|
||||||
|
"rotating-file-stream": "^1.4.6",
|
||||||
|
"tika": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
43
production/arjion/arjion/public/css/main.css
Archivo normal
43
production/arjion/arjion/public/css/main.css
Archivo normal
@@ -0,0 +1,43 @@
|
|||||||
|
body {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.index {
|
||||||
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mdc-card {
|
||||||
|
margin: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mdc-layout-grid__cell {
|
||||||
|
text-align: center;
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 650px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsible:after {
|
||||||
|
content: "\0002B"; /* Unicode character for "plus" sign (+) */
|
||||||
|
font-size: 13px;
|
||||||
|
color: white;
|
||||||
|
float: right;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active:after {
|
||||||
|
content: "\2212"; /* Unicode character for "minus" sign (-) */
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
padding: 0 18px;
|
||||||
|
background-color: white;
|
||||||
|
max-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: max-height 0.2s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--mdc-theme-primary: black;
|
||||||
|
--mdc-theme-secondary: black;
|
||||||
|
}
|
||||||
46
production/arjion/arjion/public/js/main.js
Archivo normal
46
production/arjion/arjion/public/js/main.js
Archivo normal
@@ -0,0 +1,46 @@
|
|||||||
|
window.onload = function () {
|
||||||
|
new mdc.topAppBar.MDCTopAppBar(document.querySelector('.mdc-top-app-bar'))
|
||||||
|
var menu = new mdc.menu.MDCMenu(document.querySelector('.mdc-menu'))
|
||||||
|
menu.setAnchorCorner(mdc.menuSurface.Corner.BOTTOM_LEFT)
|
||||||
|
document.getElementById('menu-button').addEventListener('click', function (event) {
|
||||||
|
menu.open = !menu.open
|
||||||
|
})
|
||||||
|
document.querySelectorAll('.mdc-text-field').forEach(function (field) {
|
||||||
|
mdc.textField.MDCTextField.attachTo(field)
|
||||||
|
})
|
||||||
|
document.querySelectorAll('.mdc-icon-button').forEach(function (field) {
|
||||||
|
new mdc.iconButton.MDCIconButtonToggle(field)
|
||||||
|
})
|
||||||
|
if (document.querySelector('.collapsible')) {
|
||||||
|
document.querySelectorAll('.collapsible').forEach(function (col) {
|
||||||
|
col.addEventListener('click', function () {
|
||||||
|
this.classList.toggle('active')
|
||||||
|
var content = this.parentNode.parentNode.nextElementSibling.nextElementSibling
|
||||||
|
if (content.style.maxHeight) {
|
||||||
|
content.style.maxHeight = null
|
||||||
|
} else {
|
||||||
|
content.style.maxHeight = content.scrollHeight + "px"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (document.querySelector('.mdc-dialog')) {
|
||||||
|
var dialog = new mdc.dialog.MDCDialog(document.querySelector('.mdc-dialog'))
|
||||||
|
if (document.querySelector('.mdc-dialog #delete')) {
|
||||||
|
document.querySelectorAll('.delete').forEach(function (d) {
|
||||||
|
d.addEventListener('click', function (event) {
|
||||||
|
document.body.setAttribute('data-selected', this.getAttribute('data-filename'))
|
||||||
|
dialog.open()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
document.querySelector('.opened').addEventListener('click', function (event) {
|
||||||
|
document.body.getAttribute('data-selected') ? window.location.href = '/user/deletefile?file=' + document.body.getAttribute('data-selected') : ''
|
||||||
|
})
|
||||||
|
document.querySelector('.closed').addEventListener('click', function (event) {
|
||||||
|
dialog.close()
|
||||||
|
})
|
||||||
|
} else if (document.querySelector('.mdc-dialog #error')) {
|
||||||
|
dialog.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
79
production/arjion/arjion/server.js
Archivo normal
79
production/arjion/arjion/server.js
Archivo normal
@@ -0,0 +1,79 @@
|
|||||||
|
const express = require('express'),
|
||||||
|
passport = require('passport'),
|
||||||
|
Strategy = require('passport-local').Strategy,
|
||||||
|
crypto = require('crypto'),
|
||||||
|
fs = require('fs'),
|
||||||
|
config = require('./config'),
|
||||||
|
rfs = require('rotating-file-stream'),
|
||||||
|
accessLogStream = rfs('access.log', {
|
||||||
|
interval: '1d', // rotate daily
|
||||||
|
path: __dirname + '/log'
|
||||||
|
}),
|
||||||
|
upload = require('multer')({
|
||||||
|
dest: 'uploads/',
|
||||||
|
limits: {
|
||||||
|
fileSize: 5 * 1024 * 1024 // 5Mb
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
tika = require('tika'),
|
||||||
|
flash = require('connect-flash'),
|
||||||
|
promisify = require("es6-promisify").promisify,
|
||||||
|
nodemailer = require('nodemailer'),
|
||||||
|
transporter = nodemailer.createTransport({
|
||||||
|
host: config.mailserver,
|
||||||
|
port: config.mailport,
|
||||||
|
secure: config.mailsecure,
|
||||||
|
auth: {
|
||||||
|
user: config.mailuser,
|
||||||
|
pass: config.mailpass
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
{ Client: Client6 } = require('es6'),
|
||||||
|
client6 = new Client6({
|
||||||
|
node: config.indexhost,
|
||||||
|
auth: {
|
||||||
|
username: config.indexuser,
|
||||||
|
password: config.indexpass
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
exiftool = require('node-exiftool');
|
||||||
|
|
||||||
|
// Initialize elastic
|
||||||
|
require('./lib/elastic')(config, client6)
|
||||||
|
|
||||||
|
// Server
|
||||||
|
const app = express(),
|
||||||
|
server = app.disable('x-powered-by').listen(config.port, () => {
|
||||||
|
console.log(`Listening on ${server.address().address}:${server.address().port}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
app.set('trust proxy', 1)
|
||||||
|
|
||||||
|
app.set('views', __dirname + '/views')
|
||||||
|
app.set('view engine', 'ejs')
|
||||||
|
|
||||||
|
// Middleware
|
||||||
|
app.use(require('morgan')('combined', { stream: accessLogStream }))
|
||||||
|
app.use(require('body-parser').json())
|
||||||
|
app.use(require('body-parser').urlencoded({ extended: false }))
|
||||||
|
app.use(require('express-session')({ secret: 'keyboard hat', resave: false, saveUninitialized: true }))
|
||||||
|
app.use(flash())
|
||||||
|
|
||||||
|
// Auth
|
||||||
|
require('./lib/passport')(passport, Strategy, crypto, config, client6)
|
||||||
|
app.use(passport.initialize())
|
||||||
|
app.use(passport.session({ cookie: { maxAge: 43200 } })) // 12H
|
||||||
|
|
||||||
|
// Routes
|
||||||
|
app.use('/', require('./lib/index')(express.Router(), passport, config, client6, upload, fs, tika, promisify, crypto, transporter))
|
||||||
|
app.use('/user', require('connect-ensure-login').ensureLoggedIn('/login'), require('./lib/user')(express.Router(), config, client6, upload, fs, tika, promisify, crypto, exiftool))
|
||||||
|
|
||||||
|
// Resources
|
||||||
|
app.use('/css', express.static(__dirname + '/public/css'))
|
||||||
|
app.use('/js', express.static(__dirname + '/public/js'))
|
||||||
|
|
||||||
|
// Errors
|
||||||
|
app.use((err, req, res, next) => {
|
||||||
|
res.status(500)
|
||||||
|
return res.render('error', { error: err.message })
|
||||||
|
})
|
||||||
5
production/arjion/arjion/views/error.ejs
Archivo normal
5
production/arjion/arjion/views/error.ejs
Archivo normal
@@ -0,0 +1,5 @@
|
|||||||
|
<%- include('header') %>
|
||||||
|
<h3>Error: <%= locals.error %>, go <button class="mdc-button mdc-button--raised"
|
||||||
|
onclick="window.location.href=document.referrer"><span class="mdc-button__ripple"></span><span
|
||||||
|
class="mdc-button__text">Back</span></button></h3>
|
||||||
|
<%- include('footer') %>
|
||||||
78
production/arjion/arjion/views/file.ejs
Archivo normal
78
production/arjion/arjion/views/file.ejs
Archivo normal
@@ -0,0 +1,78 @@
|
|||||||
|
<%- include('header') %>
|
||||||
|
<% if (locals.documents && locals.documents.length > 0) { %>
|
||||||
|
<% documents.map(document => {
|
||||||
|
var chips = [] %>
|
||||||
|
<div class="mdc-card">
|
||||||
|
<div class="mdc-card__primary-action" tabindex="0">
|
||||||
|
<ul class="mdc-list">
|
||||||
|
<li class="mdc-list-item" tabindex="0">
|
||||||
|
<h3 class="mdc-list-item__text"><%= document.name %></h3>
|
||||||
|
</li>
|
||||||
|
<li role="separator" class="mdc-list-divider"></li>
|
||||||
|
<li class="mdc-list-item">
|
||||||
|
<span class="mdc-list-item__text">Size: <%= document.size %></span>
|
||||||
|
</li>
|
||||||
|
<li role="separator" class="mdc-list-divider"></li>
|
||||||
|
<li class="mdc-list-item">
|
||||||
|
<span class="mdc-list-item__text">Mime: <%= document.mime %></span>
|
||||||
|
</li>
|
||||||
|
<li role="separator" class="mdc-list-divider"></li>
|
||||||
|
<% if (document.language) { %>
|
||||||
|
<li class="mdc-list-item">
|
||||||
|
<span class="mdc-list-item__text">Language: <%= document.language %></span>
|
||||||
|
</li>
|
||||||
|
<li role="separator" class="mdc-list-divider"></li>
|
||||||
|
<% } if (document.metadata) { %>
|
||||||
|
<li class="mdc-list-item">
|
||||||
|
<span class="mdc-list-item__text">Metadata: <button type="button"
|
||||||
|
class="collapsible mdc-button mdc-button--raised">
|
||||||
|
<div class="mdc-button__ripple"></div>
|
||||||
|
</button></span>
|
||||||
|
</li>
|
||||||
|
<li role="separator" class="mdc-list-divider"></li>
|
||||||
|
<ul class="content">
|
||||||
|
<% for(var i=0; i < Object.keys(document.metadata).length; i++) { %>
|
||||||
|
<li class="mdc-list-item" <% if (i === 0) { %> tabindex="0" <% } %>>
|
||||||
|
<span class="mdc-list-item__text"><%= Object.keys(document.metadata)[i] %>:
|
||||||
|
<%= Object.keys(document.metadata).length > 0 ? document.metadata[Object.keys(document.metadata)[i]].join(', ') : '' %></span>
|
||||||
|
</li>
|
||||||
|
<% chips.push(Object.keys(document.metadata)[i]) %>
|
||||||
|
<% } %>
|
||||||
|
</ul>
|
||||||
|
<li role="separator" class="mdc-list-divider"></li>
|
||||||
|
<div class="mdc-chip-set" role="grid">
|
||||||
|
<% chips.map(chip => { %>
|
||||||
|
<div class="mdc-chip" role="row">
|
||||||
|
<div class="mdc-chip__ripple"></div>
|
||||||
|
<span role="gridcell">
|
||||||
|
<span role="button" <% if (i === 0) { %> tabindex="0" <% } %> class="mdc-chip__primary-action">
|
||||||
|
<span class="mdc-chip__text"><%=chip %></span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<% }) %>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% })
|
||||||
|
} else { %>
|
||||||
|
<div class="mdc-dialog">
|
||||||
|
<div class="mdc-dialog__container">
|
||||||
|
<div class="mdc-dialog__surface" role="alertdialog" aria-modal="true" aria-labelledby="error"
|
||||||
|
aria-describedby="error-content">
|
||||||
|
<h2 class="mdc-dialog__title" id="error">Error</h2>
|
||||||
|
<div class="mdc-dialog__content" id="error-content">
|
||||||
|
<ul class="mdc-list mdc-list--avatar-list">
|
||||||
|
<li class="mdc-list-item" tabindex="0" data-mdc-dialog-action="none">
|
||||||
|
<span class="mdc-list-item__text">NO Documents</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mdc-dialog__scrim"></div>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
<%- include('footer') %>
|
||||||
117
production/arjion/arjion/views/files.ejs
Archivo normal
117
production/arjion/arjion/views/files.ejs
Archivo normal
@@ -0,0 +1,117 @@
|
|||||||
|
<%- include('header') %>
|
||||||
|
<% if (locals.documents && locals.documents.length > 0) { %>
|
||||||
|
<% documents.map(document => {
|
||||||
|
var chips = [] %>
|
||||||
|
<div class="mdc-card">
|
||||||
|
<div class="mdc-card__primary-action" tabindex="0">
|
||||||
|
<ul class="mdc-list">
|
||||||
|
<li class="mdc-list-item" tabindex="0">
|
||||||
|
<h3 class="mdc-list-item__text"><%= document.name %></h3>
|
||||||
|
</li>
|
||||||
|
<li role="separator" class="mdc-list-divider"></li>
|
||||||
|
<li class="mdc-list-item">
|
||||||
|
<span class="mdc-list-item__text">Size: <%= document.size %></span>
|
||||||
|
</li>
|
||||||
|
<li role="separator" class="mdc-list-divider"></li>
|
||||||
|
<li class="mdc-list-item">
|
||||||
|
<span class="mdc-list-item__text">Mime: <%= document.mime %></span>
|
||||||
|
</li>
|
||||||
|
<li role="separator" class="mdc-list-divider"></li>
|
||||||
|
<% if (document.language) { %>
|
||||||
|
<li class="mdc-list-item">
|
||||||
|
<span class="mdc-list-item__text">Language: <%= document.language %></span>
|
||||||
|
</li>
|
||||||
|
<li role="separator" class="mdc-list-divider"></li>
|
||||||
|
<% } if (document.metadata) { %>
|
||||||
|
<li class="mdc-list-item">
|
||||||
|
<span class="mdc-list-item__text">Metadata:
|
||||||
|
<button type="button" class="collapsible mdc-button mdc-button--raised">
|
||||||
|
<div class="mdc-button__ripple"></div>
|
||||||
|
</button></span>
|
||||||
|
</li>
|
||||||
|
<li role="separator" class="mdc-list-divider"></li>
|
||||||
|
<ul class="content">
|
||||||
|
<% for(var i=0; i < Object.keys(document.metadata).length; i++) { %>
|
||||||
|
<li class="mdc-list-item" <% if (i === 0) { %> tabindex="0" <% } %>>
|
||||||
|
<span class="mdc-list-item__text"><%= Object.keys(document.metadata)[i] %>:
|
||||||
|
<%= Object.keys(document.metadata).length > 0 ? document.metadata[Object.keys(document.metadata)[i]].join(', ') : '' %></span>
|
||||||
|
</li>
|
||||||
|
<% chips.push(Object.keys(document.metadata)[i]) %>
|
||||||
|
<% } %>
|
||||||
|
</ul>
|
||||||
|
<li role="separator" class="mdc-list-divider"></li>
|
||||||
|
<div class="mdc-chip-set" role="grid">
|
||||||
|
<% chips.map(chip => { %>
|
||||||
|
<div class="mdc-chip" role="row">
|
||||||
|
<div class="mdc-chip__ripple"></div>
|
||||||
|
<span role="gridcell">
|
||||||
|
<span role="button" <% if (i === 0) { %> tabindex="0" <% } %> class="mdc-chip__primary-action">
|
||||||
|
<span class="mdc-chip__text"><%=chip %></span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<% }) %>
|
||||||
|
</div>
|
||||||
|
<li role="separator" class="mdc-list-divider"></li>
|
||||||
|
<% } %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="mdc-card__actions">
|
||||||
|
<div class="mdc-card__action-buttons">
|
||||||
|
<button class="mdc-button mdc-button--raised mdc-card__action mdc-card__action--button"
|
||||||
|
onclick="window.location.href='/user/download?file=<%= document.filename %>'"><span
|
||||||
|
class="mdc-button__ripple"></span><span class="mdc-button__text">Download</span></button>
|
||||||
|
<button class="mdc-button mdc-button--raised mdc-card__action mdc-card__action--button"
|
||||||
|
onclick="window.location.href='/user/download-meta?file=<%= document.filename %>'"><span
|
||||||
|
class="mdc-button__ripple"></span><span class="mdc-button__text">Download with NO
|
||||||
|
Metadata</span></button>
|
||||||
|
</div>
|
||||||
|
<div class="mdc-card__action-icons">
|
||||||
|
<button class="mdc-button material-icons mdc-icon-button mdc-card__action mdc-card__action--icon delete"
|
||||||
|
title="Delete" data-filename="<%= document.filename %>">delete</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% }) %>
|
||||||
|
<div class="mdc-dialog">
|
||||||
|
<div class="mdc-dialog__container">
|
||||||
|
<div class="mdc-dialog__surface" role="alertdialog" aria-modal="true" aria-labelledby="delete"
|
||||||
|
aria-describedby="delete-content">
|
||||||
|
<h2 class="mdc-dialog__title" id="delete">Delete file?</h2>
|
||||||
|
<div class="mdc-dialog__content" id="delete-content">
|
||||||
|
You will can NOT recover it...
|
||||||
|
</div>
|
||||||
|
<footer class="mdc-dialog__actions">
|
||||||
|
<button type="button" class="mdc-button mdc-dialog__button closed" data-mdc-dialog-action="No"
|
||||||
|
data-mdc-dialog-button-default>
|
||||||
|
<div class="mdc-button__ripple"></div>
|
||||||
|
<span class="mdc-button__label">No</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="mdc-button mdc-dialog__button opened" data-mdc-dialog-action="Yes">
|
||||||
|
<div class="mdc-button__ripple"></div>
|
||||||
|
<span class="mdc-button__label">Yes</span>
|
||||||
|
</button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mdc-dialog__scrim"></div>
|
||||||
|
</div>
|
||||||
|
<% } else { %>
|
||||||
|
<div class="mdc-dialog">
|
||||||
|
<div class="mdc-dialog__container">
|
||||||
|
<div class="mdc-dialog__surface" role="alertdialog" aria-modal="true" aria-labelledby="error"
|
||||||
|
aria-describedby="error-content">
|
||||||
|
<h2 class="mdc-dialog__title" id="error">Error</h2>
|
||||||
|
<div class="mdc-dialog__content" id="error-content">
|
||||||
|
<ul class="mdc-list mdc-list--avatar-list">
|
||||||
|
<li class="mdc-list-item" tabindex="0" data-mdc-dialog-action="none">
|
||||||
|
<span class="mdc-list-item__text">NO Documents</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mdc-dialog__scrim"></div>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
<%- include('footer') %>
|
||||||
6
production/arjion/arjion/views/footer.ejs
Archivo normal
6
production/arjion/arjion/views/footer.ejs
Archivo normal
@@ -0,0 +1,6 @@
|
|||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
75
production/arjion/arjion/views/header.ejs
Archivo normal
75
production/arjion/arjion/views/header.ejs
Archivo normal
@@ -0,0 +1,75 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="stylesheet" type="text/css" href="/css/material-components-web.min.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="/css/iconfont/material-icons.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="/css/main.css">
|
||||||
|
<script type="text/javascript" src="/js/material-components-web.min.js"></script>
|
||||||
|
<script type="text/javascript" src="/js/main.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<header class="mdc-top-app-bar mdc-top-app-bar--fixed">
|
||||||
|
<div class="mdc-top-app-bar__row">
|
||||||
|
<section class="mdc-top-app-bar__section mdc-top-app-bar__section--align-start">
|
||||||
|
<a href="/" class="index">
|
||||||
|
<h2 class="mdc-top-app-bar__title"><span class="material-icons">school</span> HatMeta</h2>
|
||||||
|
</a>
|
||||||
|
</section>
|
||||||
|
<section class="mdc-top-app-bar__section mdc-top-app-bar__section--align-end">
|
||||||
|
<div id="toolbar" class="toolbar mdc-menu-surface--anchor mdc-top-app-bar__action-item">
|
||||||
|
<button id="menu-button"
|
||||||
|
class="material-icons mdc-top-app-bar__navigation-icon mdc-icon-button">more_vert</button>
|
||||||
|
<div class="mdc-menu mdc-menu-surface">
|
||||||
|
<ul class="mdc-list" role="menu" aria-hidden="true" aria-orientation="vertical" tabindex="-1">
|
||||||
|
<% if (!locals.user) { %>
|
||||||
|
<li class="mdc-list-item" role="menuitem">
|
||||||
|
<button class="mdc-button" aria-label="login" onclick="window.location.href='/login'">
|
||||||
|
<span class="material-icons mdc-button__icon">perm_identity</span><span
|
||||||
|
class="mdc-button__label">LOGIN</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li class="mdc-list-item" role="menuitem">
|
||||||
|
<button class="mdc-button" aria-label="upload"
|
||||||
|
onclick="window.location.href='/upload'"><span
|
||||||
|
class="material-icons mdc-button__icon">cloud_upload</span><span
|
||||||
|
class="mdc-button__label">UPLOAD</span></button>
|
||||||
|
</li>
|
||||||
|
<% } else { %>
|
||||||
|
<li class="mdc-list-item" role="menuitem">
|
||||||
|
<button class="mdc-button" aria-label="profile"
|
||||||
|
onclick="window.location.href='/user/profile'"><span
|
||||||
|
class="material-icons mdc-button__icon">perm_identity</span><span
|
||||||
|
class="mdc-button__label">PROFILE</span></button>
|
||||||
|
</li>
|
||||||
|
<li class="mdc-list-item" role="menuitem">
|
||||||
|
<button class="mdc-button" aria-label="folder"
|
||||||
|
onclick="window.location.href='/user/files'"><span
|
||||||
|
class="material-icons mdc-button__icon">folder</span><span
|
||||||
|
class="mdc-button__label">FOLDER</span></button>
|
||||||
|
</li>
|
||||||
|
<li class="mdc-list-item" role="menuitem">
|
||||||
|
<button class="mdc-button" aria-label="upload"
|
||||||
|
onclick="window.location.href='/user/upload'"><span
|
||||||
|
class="material-icons mdc-button__icon">cloud_upload</span><span
|
||||||
|
class="mdc-button__label">UPLOAD</span></button>
|
||||||
|
</li>
|
||||||
|
<li class="mdc-list-item" role="menuitem">
|
||||||
|
<button class="mdc-button" aria-label="logout"
|
||||||
|
onclick="window.location.href='/logout'"><span
|
||||||
|
class="material-icons mdc-button__icon">exit_to_app</span><span
|
||||||
|
class="mdc-button__label">LOGOUT</span></button>
|
||||||
|
</li>
|
||||||
|
<% } %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<br><br><br><br>
|
||||||
|
<div class="mdc-layout-grid">
|
||||||
|
<div class="mdc-layout-grid__inner">
|
||||||
|
<div class="mdc-layout-grid__cell mdc-layout-grid__cell--span-12">
|
||||||
14
production/arjion/arjion/views/home.ejs
Archivo normal
14
production/arjion/arjion/views/home.ejs
Archivo normal
@@ -0,0 +1,14 @@
|
|||||||
|
<%- include('header') %>
|
||||||
|
<% if (!locals.user) { %>
|
||||||
|
<h3>Welcome! Please <button class="mdc-button mdc-button--raised" onclick="window.location.href='/login'"><span
|
||||||
|
class="mdc-button__ripple"></span><span class="mdc-button__text">Login</span></button> or
|
||||||
|
<button class="mdc-button mdc-button--raised" onclick="window.location.href='/register'"><span
|
||||||
|
class="mdc-button__ripple"></span><span class="mdc-button__text">Register</span></button></h3>
|
||||||
|
<% } else { %>
|
||||||
|
<h3>Hello, <%= locals.user.user %>. View your <button class="mdc-button mdc-button--raised"
|
||||||
|
onclick="window.location.href='/user/profile'"><span class="mdc-button__ripple"></span><span
|
||||||
|
class="mdc-button__text">Profile</span></button> or view your <button class="mdc-button mdc-button--raised"
|
||||||
|
onclick="window.location.href='/user/files'"><span class="mdc-button__ripple"></span><span
|
||||||
|
class="mdc-button__text">Folder</span></button></h3>
|
||||||
|
<% } %>
|
||||||
|
<%- include('footer') %>
|
||||||
36
production/arjion/arjion/views/login.ejs
Archivo normal
36
production/arjion/arjion/views/login.ejs
Archivo normal
@@ -0,0 +1,36 @@
|
|||||||
|
<%- include('header') %>
|
||||||
|
<form action="/login" method="post">
|
||||||
|
<div class="mdc-text-field">
|
||||||
|
<input class="mdc-text-field__input" id="username" name="username" type="text" required>
|
||||||
|
<div class="mdc-line-ripple"></div>
|
||||||
|
<label for="username" class="mdc-floating-label">Username</label>
|
||||||
|
</div>
|
||||||
|
<div class="mdc-text-field">
|
||||||
|
<input class="mdc-text-field__input" id="password" name="password" type="password" required>
|
||||||
|
<div class="mdc-line-ripple"></div>
|
||||||
|
<label for="password" class="mdc-floating-label">Password</label>
|
||||||
|
</div>
|
||||||
|
<br><br>
|
||||||
|
<div>
|
||||||
|
<button class="mdc-button mdc-button--raised" type="submit"><span class="mdc-button__ripple"></span>Login</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<% if (locals.error && locals.error.length > 0) { %>
|
||||||
|
<div class="mdc-dialog">
|
||||||
|
<div class="mdc-dialog__container">
|
||||||
|
<div class="mdc-dialog__surface" role="alertdialog" aria-modal="true" aria-labelledby="error"
|
||||||
|
aria-describedby="error-content">
|
||||||
|
<h2 class="mdc-dialog__title" id="error">Error</h2>
|
||||||
|
<div class="mdc-dialog__content" id="error-content">
|
||||||
|
<ul class="mdc-list mdc-list--avatar-list">
|
||||||
|
<li class="mdc-list-item" tabindex="0" data-mdc-dialog-action="none">
|
||||||
|
<span class="mdc-list-item__text"><%= locals.error %></span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mdc-dialog__scrim"></div>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
<%- include('footer') %>
|
||||||
15
production/arjion/arjion/views/profile.ejs
Archivo normal
15
production/arjion/arjion/views/profile.ejs
Archivo normal
@@ -0,0 +1,15 @@
|
|||||||
|
<%- include('header') %>
|
||||||
|
<ul class="mdc-list">
|
||||||
|
<li class="mdc-list-item" tabindex="0">
|
||||||
|
<span class="mdc-list-item__text">User: <%= locals.user.user %></span>
|
||||||
|
</li>
|
||||||
|
<li role="separator" class="mdc-list-divider"></li>
|
||||||
|
<li class="mdc-list-item">
|
||||||
|
<span class="mdc-list-item__text">Email: <%= locals.user.email %></span>
|
||||||
|
</li>
|
||||||
|
<li role="separator" class="mdc-list-divider"></li>
|
||||||
|
</ul>
|
||||||
|
<br><br>
|
||||||
|
<button class="mdc-button mdc-button--raised" onclick="window.location.href='/logout'"><span
|
||||||
|
class="mdc-button__ripple"></span><span class="mdc-button__text">Logout</span></button>
|
||||||
|
<%- include('footer') %>
|
||||||
32
production/arjion/arjion/views/register.ejs
Archivo normal
32
production/arjion/arjion/views/register.ejs
Archivo normal
@@ -0,0 +1,32 @@
|
|||||||
|
<%- include('header') %>
|
||||||
|
<form action="/register" method="post" onsubmit="return checkEmail(this)">
|
||||||
|
<div class="mdc-text-field">
|
||||||
|
<input class="mdc-text-field__input" id="username" name="username" type="text" required>
|
||||||
|
<div class="mdc-line-ripple"></div>
|
||||||
|
<label for="username" class="mdc-floating-label">Username</label>
|
||||||
|
</div>
|
||||||
|
<div class="mdc-text-field">
|
||||||
|
<input class="mdc-text-field__input" id="password" name="password" type="password" required>
|
||||||
|
<div class="mdc-line-ripple"></div>
|
||||||
|
<label for="password" class="mdc-floating-label">Password</label>
|
||||||
|
</div>
|
||||||
|
<div class="mdc-text-field">
|
||||||
|
<input class="mdc-text-field__input" id="email" name="email" type="email" required>
|
||||||
|
<div class="mdc-line-ripple"></div>
|
||||||
|
<label for="email" class="mdc-floating-label">Email</label>
|
||||||
|
</div>
|
||||||
|
<div class="mdc-text-field">
|
||||||
|
<input class="mdc-text-field__input" id="email2" name="email2" type="email" required>
|
||||||
|
<div class="mdc-line-ripple"></div>
|
||||||
|
<label for="email2" class="mdc-floating-label">Confirm Email</label>
|
||||||
|
</div>
|
||||||
|
<br><br>
|
||||||
|
<div>
|
||||||
|
<button class="mdc-button mdc-button--raised" type="submit"><span
|
||||||
|
class="mdc-button__ripple"></span>Submit</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<% if (locals.error && locals.error.length > 0) { %>
|
||||||
|
<p>Error: <%= locals.error %></p>
|
||||||
|
<% } %>
|
||||||
|
<%- include('footer') %>
|
||||||
13
production/arjion/arjion/views/upload.ejs
Archivo normal
13
production/arjion/arjion/views/upload.ejs
Archivo normal
@@ -0,0 +1,13 @@
|
|||||||
|
<%- include('header') %>
|
||||||
|
<form action="<%= !locals.user ? '/upload' : '/user/upload' %>" method="post" enctype="multipart/form-data">
|
||||||
|
<div class="mdc-text-field">
|
||||||
|
<input class="mdc-text-field__input" id="documents" name="documents" type="file" multiple required>
|
||||||
|
<div class="mdc-line-ripple"></div>
|
||||||
|
</div>
|
||||||
|
<br><br>
|
||||||
|
<div>
|
||||||
|
<button class="mdc-button mdc-button--raised" type="submit"><span
|
||||||
|
class="mdc-button__ripple"></span>Upload</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<%- include('footer') %>
|
||||||
14
production/arjion/arjion/views/verify.ejs
Archivo normal
14
production/arjion/arjion/views/verify.ejs
Archivo normal
@@ -0,0 +1,14 @@
|
|||||||
|
<%- include('header') %>
|
||||||
|
<% if (locals.message) {
|
||||||
|
if(message.accepted.length > 0) {
|
||||||
|
%>
|
||||||
|
<h3>Visit your email to activate your account <button class="mdc-button mdc-button--raised"
|
||||||
|
onclick="window.location.href='/login'"><span class="mdc-button__ripple"></span><span
|
||||||
|
class="mdc-button__text">Login</span></button></h3>
|
||||||
|
<% } else { %>
|
||||||
|
<h3>Error sending email, go <button class="mdc-button mdc-button--raised"
|
||||||
|
onclick="window.location.href=document.referrer"><span class="mdc-button__ripple"></span><span
|
||||||
|
class="mdc-button__text">Back</span></button></h3>
|
||||||
|
<% }
|
||||||
|
} %>
|
||||||
|
<%- include('footer') %>
|
||||||
25
production/arjion/docker-compose.yml
Archivo normal
25
production/arjion/docker-compose.yml
Archivo normal
@@ -0,0 +1,25 @@
|
|||||||
|
version: '2'
|
||||||
|
|
||||||
|
services:
|
||||||
|
arjion:
|
||||||
|
build: ./arjion
|
||||||
|
container_name: arjion
|
||||||
|
hostname: arjion
|
||||||
|
restart: always
|
||||||
|
entrypoint:
|
||||||
|
- /bin/bash
|
||||||
|
- /arjion/entrypoint.sh
|
||||||
|
volumes:
|
||||||
|
- ./arjion:/arjion
|
||||||
|
expose:
|
||||||
|
- 3000
|
||||||
|
networks:
|
||||||
|
mynet:
|
||||||
|
ipv4_address: 172.134.0.101
|
||||||
|
|
||||||
|
networks:
|
||||||
|
mynet:
|
||||||
|
driver: bridge
|
||||||
|
ipam:
|
||||||
|
config:
|
||||||
|
- subnet: 172.134.0.0/24
|
||||||
Referencia en una nueva incidencia
Block a user