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