begin transition to php environment for rebuild
parent
125256838c
commit
05ff48a51c
@ -1,235 +0,0 @@
|
|||||||
import * as DataEvent from '../../../src/com/events/DataEvent';
|
|
||||||
import mdparser from 'markdown-yaml-metadata-parser';
|
|
||||||
const uuidv4 = require('uuid/v4');
|
|
||||||
const express = require('express');
|
|
||||||
const router = express.Router();
|
|
||||||
const bcrypt = require('bcrypt');
|
|
||||||
const jwt = require('jsonwebtoken');
|
|
||||||
const fs = require('fs-extra');
|
|
||||||
const _ = require('lodash');
|
|
||||||
const crypto = require('crypto'); // for setting up new accounts
|
|
||||||
const secret_key = '58d5aeec3c604e2837aef70bc1606f35131ab8fea9731925558f5acfaa00da60';
|
|
||||||
const moment = require('moment');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get Auth Status
|
|
||||||
*/
|
|
||||||
router.get('/', function (req, res) {
|
|
||||||
var token = req.headers['x-access-token'];
|
|
||||||
if (!token) return res.status(401).send({ auth: false, message: 'No token provided.' });
|
|
||||||
|
|
||||||
jwt.verify(token, 'super-secret-string', function (err, decoded) {
|
|
||||||
if (err)
|
|
||||||
return res.status(500).send({ auth: false, message: 'Failed to authenticate token.' });
|
|
||||||
res.status(200).send(decoded);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get Auth Status
|
|
||||||
*/
|
|
||||||
router.get('/status', function (req, res) {
|
|
||||||
if (req.session.user) {
|
|
||||||
let session = req.session;
|
|
||||||
res.json({
|
|
||||||
type: DataEvent.API_REQUEST_GOOD,
|
|
||||||
message: 'Auth is Good',
|
|
||||||
token: session.hashToken
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
res.json({
|
|
||||||
type: DataEvent.API_REQUEST_LAME,
|
|
||||||
message: 'NOT AUTHORIZED'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
/**
|
|
||||||
* Login Member and return token
|
|
||||||
*/
|
|
||||||
router.post('/login', function (req, res) {
|
|
||||||
fs.readJson('site/folks.json').then(folks => {
|
|
||||||
let found = _.find(folks, { handle: req.body.handle });
|
|
||||||
if (found) {
|
|
||||||
if (!isValidPassword(found, req.body.password)) {
|
|
||||||
res.json({
|
|
||||||
type: DataEvent.REQUEST_LAME,
|
|
||||||
message: 'CHECK YOUR PASSWORD'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let token = jwt.sign({ id: found.id }, found.key, {
|
|
||||||
expiresIn: 86400 // expires in 24 hours
|
|
||||||
});
|
|
||||||
|
|
||||||
let session = req.session;
|
|
||||||
session.user = found;
|
|
||||||
session.token = token;
|
|
||||||
session.hashToken = hashToken(token);
|
|
||||||
res.json({
|
|
||||||
type: DataEvent.REQUEST_GOOD,
|
|
||||||
message: 'Welcome Back',
|
|
||||||
token: session.hashToken
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
res.json({
|
|
||||||
type: DataEvent.REQUEST_LAME,
|
|
||||||
message: 'Need to see some id, champ.'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initial Site Setup
|
|
||||||
*/
|
|
||||||
router.post('/init', function (req, res) {
|
|
||||||
let body = req.body;
|
|
||||||
let re = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
|
|
||||||
// check email
|
|
||||||
if (!re.test(body.new_member_email)) {
|
|
||||||
res.json({
|
|
||||||
type: DataEvent.API_INIT_LAME,
|
|
||||||
message: 'Need a valid email address'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
//check handle is being passed
|
|
||||||
if (body.new_member_handle === null || body.new_member_handle === '') {
|
|
||||||
res.json({
|
|
||||||
type: DataEvent.API_INIT_LAME,
|
|
||||||
message: 'No handle. Kinda need that.'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// check password match
|
|
||||||
if (
|
|
||||||
body.new_member_pass !== body.new_member_pass2 ||
|
|
||||||
body.new_member_pass === '' ||
|
|
||||||
body.new_member_pass2 === ''
|
|
||||||
) {
|
|
||||||
res.json({
|
|
||||||
type: DataEvent.API_INIT_LAME,
|
|
||||||
message: 'Passwords do not match.'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (body.new_member_title === null || body.new_member_title === '') {
|
|
||||||
res.json({
|
|
||||||
type: DataEvent.API_INIT_LAME,
|
|
||||||
message: 'No title. Gotta call it something.'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let key = crypto
|
|
||||||
.createHash('sha256')
|
|
||||||
.update(body.new_member_pass + secret_key)
|
|
||||||
.digest('hex');
|
|
||||||
|
|
||||||
// set up config files
|
|
||||||
fs.readJson('site/init/settings-template.json').then(fresh => {
|
|
||||||
fresh.global.title = body.new_member_title;
|
|
||||||
fs.writeJSON('site/settings.json', fresh);
|
|
||||||
});
|
|
||||||
|
|
||||||
fs.readJson('site/init/folks-template.json').then(folks => {
|
|
||||||
folks[0].id = 1;
|
|
||||||
folks[0].handle = body.new_member_handle;
|
|
||||||
folks[0].email = body.new_member_email;
|
|
||||||
folks[0].password = bcrypt.hashSync(body.new_member_pass, bcrypt.genSaltSync(10), null);
|
|
||||||
folks[0].key = key;
|
|
||||||
folks[0].role = 'hnic';
|
|
||||||
folks[0].created = moment(Date.now()).format();
|
|
||||||
folks[0].updated = moment(Date.now()).format();
|
|
||||||
fs.writeJSON('site/folks.json', folks);
|
|
||||||
});
|
|
||||||
|
|
||||||
fs.writeJson('site/tags.json', { tags: [] });
|
|
||||||
|
|
||||||
//set up index file as first page
|
|
||||||
|
|
||||||
fs.readFile('site/init/index-template.md', { encoding: 'utf8' }).then(file => {
|
|
||||||
let index = mdparser(file);
|
|
||||||
let data = index.metadata;
|
|
||||||
data.uuid = uuidv4();
|
|
||||||
data.path = moment().format('YYYY') + '/' + moment().format('MM');
|
|
||||||
data.author = body.new_member_handle;
|
|
||||||
data.created = moment(Date.now()).format();
|
|
||||||
data.updated = moment(Date.now()).format();
|
|
||||||
|
|
||||||
var init =
|
|
||||||
'---\n' +
|
|
||||||
'id: ' +
|
|
||||||
data.id +
|
|
||||||
'\n' +
|
|
||||||
'uuid: ' +
|
|
||||||
data.uuid +
|
|
||||||
'\n' +
|
|
||||||
'title: ' +
|
|
||||||
data.title +
|
|
||||||
'\n' +
|
|
||||||
'feature: ' +
|
|
||||||
data.feature +
|
|
||||||
'\n' +
|
|
||||||
'path: ' +
|
|
||||||
moment(Date.now()).format('YYYY') +
|
|
||||||
'/' +
|
|
||||||
moment(Date.now()).format('MM') +
|
|
||||||
'\n' +
|
|
||||||
'layout: ' +
|
|
||||||
'index' +
|
|
||||||
'\n' +
|
|
||||||
'tags: ' +
|
|
||||||
data.tags +
|
|
||||||
'\n' +
|
|
||||||
'author: ' +
|
|
||||||
body.new_member_handle +
|
|
||||||
'\n' +
|
|
||||||
'created: ' +
|
|
||||||
moment(Date.now()).format() +
|
|
||||||
'\n' +
|
|
||||||
'updated: ' +
|
|
||||||
moment(Date.now()).format() +
|
|
||||||
'\n' +
|
|
||||||
'deleted: ' +
|
|
||||||
'false' +
|
|
||||||
'\n' +
|
|
||||||
'menu: ' +
|
|
||||||
data.menu +
|
|
||||||
'\n' +
|
|
||||||
'featured: ' +
|
|
||||||
data.featured +
|
|
||||||
'\n' +
|
|
||||||
'published: ' +
|
|
||||||
data.published +
|
|
||||||
'\n' +
|
|
||||||
'slug: ' +
|
|
||||||
data.slug +
|
|
||||||
'\n' +
|
|
||||||
'---\n' +
|
|
||||||
index.content;
|
|
||||||
|
|
||||||
fs.ensureDir('content/pages/').then(() => {
|
|
||||||
fs.writeFile('content/pages/index.md', init)
|
|
||||||
.then(() => {
|
|
||||||
//console.log('index file created');
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
//console.log('ERROR', err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
type: DataEvent.API_INIT_GOOD,
|
|
||||||
message: 'All Set Up'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//router.post('/logout', function(req, res) {});
|
|
||||||
module.exports = router;
|
|
||||||
|
|
||||||
function isValidPassword(user, password) {
|
|
||||||
return bcrypt.compareSync(password, user.password);
|
|
||||||
}
|
|
||||||
|
|
||||||
function hashToken(token) {
|
|
||||||
return bcrypt.hashSync(token, bcrypt.genSaltSync(10), null);
|
|
||||||
}
|
|
@ -1,107 +0,0 @@
|
|||||||
import * as DataEvent from '../../../src/com/events/DataEvent';
|
|
||||||
import Auth from '../../data/Auth';
|
|
||||||
import Utils from '../../data/Utils';
|
|
||||||
const express = require('express');
|
|
||||||
const router = express.Router();
|
|
||||||
const multer = require('multer');
|
|
||||||
const auth = new Auth();
|
|
||||||
const utils = new Utils();
|
|
||||||
|
|
||||||
var backup_upload = multer().array('backup_upload');
|
|
||||||
var backup_restore = multer().any();
|
|
||||||
|
|
||||||
/***
|
|
||||||
CREATE BACK UP
|
|
||||||
*/
|
|
||||||
router.post('/create', (req, res) => {
|
|
||||||
auth.authCheck(req)
|
|
||||||
.then(() => {
|
|
||||||
utils
|
|
||||||
.createBackup()
|
|
||||||
.then(() => {
|
|
||||||
res.json({
|
|
||||||
type: DataEvent.API_BACKUP_CREATE,
|
|
||||||
message: "You're backed up. Hi fives"
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.json({
|
|
||||||
type: err.type,
|
|
||||||
message: err.message
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.json({
|
|
||||||
type: err.type,
|
|
||||||
message: err.message
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
/***
|
|
||||||
RETRIEVE BACKUP
|
|
||||||
*/
|
|
||||||
router.get('/download', (req, res) => {
|
|
||||||
if (req.session.user) {
|
|
||||||
var filePath = 'content/backup.zip'; // Or format the path using the `id` rest param
|
|
||||||
var fileName = 'backup.zip'; // The default name the browser will use
|
|
||||||
|
|
||||||
res.download(filePath, fileName);
|
|
||||||
} else {
|
|
||||||
res.json({
|
|
||||||
type: DataEvent.REQUEST_LAME,
|
|
||||||
message: "You're not logged in, champ"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//Move to route?
|
|
||||||
});
|
|
||||||
|
|
||||||
/***
|
|
||||||
RESTORE BACKUP
|
|
||||||
*/
|
|
||||||
|
|
||||||
router.post('/restore', backup_upload, (req, res) => {
|
|
||||||
auth.authCheck(req)
|
|
||||||
.then(() => {
|
|
||||||
utils
|
|
||||||
.restoreBackup(req.files[0])
|
|
||||||
.then(() => {
|
|
||||||
res.json({
|
|
||||||
type: DataEvent.API_BACKUP_RESTORE,
|
|
||||||
message: 'Settings, files and pages restored. Nice work.'
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.json({
|
|
||||||
type: err.type,
|
|
||||||
message: 'Backup not restored. Uh oh.'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.json({
|
|
||||||
type: err.type,
|
|
||||||
message: err.message
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
router.post('/init-restore', backup_restore, (req, res) => {
|
|
||||||
utils
|
|
||||||
.verifyBackup(req.files[0], req.body)
|
|
||||||
.then(response => {
|
|
||||||
res.json({
|
|
||||||
type: response.type,
|
|
||||||
message: response.message
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.json({
|
|
||||||
type: err.type,
|
|
||||||
message: err.message
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
module.exports = router;
|
|
@ -1,83 +0,0 @@
|
|||||||
import Settings, { SETTINGS_FILE } from '../../data/Settings';
|
|
||||||
import Auth from '../../data/Auth';
|
|
||||||
var express = require('express');
|
|
||||||
var router = express.Router();
|
|
||||||
var nodemailer = require('nodemailer');
|
|
||||||
var mg = require('nodemailer-mailgun-transport');
|
|
||||||
const pug = require('pug');
|
|
||||||
const settings = new Settings();
|
|
||||||
const auth = new Auth();
|
|
||||||
router.post('/', function (req, res) {
|
|
||||||
auth.authCheck(req)
|
|
||||||
.then(() => {
|
|
||||||
settings
|
|
||||||
.load(SETTINGS_FILE)
|
|
||||||
.then(settings => {
|
|
||||||
let transport = '';
|
|
||||||
var auth = '';
|
|
||||||
switch (settings.email.active) {
|
|
||||||
case 'option-smtp':
|
|
||||||
auth = {
|
|
||||||
host: settings.email.smtp.domain,
|
|
||||||
port: 587,
|
|
||||||
secure: false,
|
|
||||||
auth: {
|
|
||||||
type: 'login',
|
|
||||||
user: settings.email.smtp,
|
|
||||||
pass: settings.email.smtp.password
|
|
||||||
}
|
|
||||||
};
|
|
||||||
transport = nodemailer.createTransport(auth);
|
|
||||||
break;
|
|
||||||
case 'option-mg':
|
|
||||||
auth = {
|
|
||||||
auth: {
|
|
||||||
api_key: settings.email.mailgun.key,
|
|
||||||
domain: settings.email.mailgun.domain
|
|
||||||
}
|
|
||||||
};
|
|
||||||
transport = nodemailer.createTransport(mg(auth));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let render = pug.compileFile('brain/views/email/base.pug');
|
|
||||||
let html = render({
|
|
||||||
title: settings.global.title,
|
|
||||||
header: 'a note from ' + settings.global.title,
|
|
||||||
content: req.body.content,
|
|
||||||
footer: 'powered by fipamo'
|
|
||||||
});
|
|
||||||
transport.sendMail(
|
|
||||||
{
|
|
||||||
from: 'control@playvico.us',
|
|
||||||
to: req.session.user.email, // An array if you have multiple recipients.
|
|
||||||
subject: 'Hey beautiful',
|
|
||||||
//You can use "html:" to send HTML email content. It's magic!
|
|
||||||
html: html
|
|
||||||
//You can use "text:" to send plain-text content. It's oldschool!
|
|
||||||
//text: 'Mailgun rocks, pow pow!'
|
|
||||||
},
|
|
||||||
function (err, info) {
|
|
||||||
if (err) {
|
|
||||||
res.json({
|
|
||||||
message: 'MAIL ERROR',
|
|
||||||
desc: err
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
//console.log(info);
|
|
||||||
res.json({
|
|
||||||
message: 'MAIL SENT',
|
|
||||||
desc: info
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
//console.error(err);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.json(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
module.exports = router;
|
|
@ -1,268 +0,0 @@
|
|||||||
import Book from '../../data/Book';
|
|
||||||
import Auth from '../../data/Auth';
|
|
||||||
import Settings, { SETTINGS_FILE } from '../../data/Settings';
|
|
||||||
import * as DataEvent from '../../../src/com/events/DataEvent';
|
|
||||||
import Render from '../../data/Render';
|
|
||||||
const express = require('express');
|
|
||||||
const router = express.Router();
|
|
||||||
const multer = require('multer');
|
|
||||||
const fs = require('fs-extra');
|
|
||||||
const moment = require('moment');
|
|
||||||
const book = new Book();
|
|
||||||
const auth = new Auth();
|
|
||||||
const settings = new Settings();
|
|
||||||
const render = new Render();
|
|
||||||
const _ = require('lodash');
|
|
||||||
const uploadPath =
|
|
||||||
'./public/assets/images/blog/' + moment().format('YYYY') + '/' + moment().format('MM');
|
|
||||||
|
|
||||||
var storage = multer.diskStorage({
|
|
||||||
destination: function (req, file, cb) {
|
|
||||||
fs.ensureDir(uploadPath, () => {
|
|
||||||
// dir has now been created, including the directory it is to be placed in
|
|
||||||
cb(null, uploadPath);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
filename: function (req, file, cb) {
|
|
||||||
var splice = file.originalname.split(':');
|
|
||||||
cb(null, splice[0]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var feature_upload = multer({
|
|
||||||
storage: storage
|
|
||||||
}).array('feature_image');
|
|
||||||
var post_upload = multer({
|
|
||||||
storage: storage
|
|
||||||
}).array('post_image');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves a page of a published entries
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
router.get('/published/:pageNum?', (req, res) => {
|
|
||||||
//console.log('PAGE NUM', req.params.pageNum);
|
|
||||||
let pageNum = req.params.pageNum;
|
|
||||||
if (pageNum === null || pageNum === '' || !pageNum) pageNum = 1;
|
|
||||||
let pages = [];
|
|
||||||
book.getPage().then(result => {
|
|
||||||
result.sort((a, b) => parseFloat(b.metadata.id) - parseFloat(a.metadata.id));
|
|
||||||
let displayed = _.filter(result, page => {
|
|
||||||
return (
|
|
||||||
page.metadata.deleted === false &&
|
|
||||||
page.metadata.published === true &&
|
|
||||||
page.metadata.layout != 'index'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
var pageLimit = 6;
|
|
||||||
var count = Math.ceil(displayed.length / pageLimit);
|
|
||||||
if (pageNum > count || isNaN(pageNum))
|
|
||||||
res.json({ type: DataEvent.REQUEST_LAME, message: "That page doesn't exist, champ." });
|
|
||||||
var rangeIndex = pageNum * pageLimit - pageLimit;
|
|
||||||
|
|
||||||
let meta = [];
|
|
||||||
|
|
||||||
for (let index = 0; index < pageLimit; index++) {
|
|
||||||
const page = displayed[index + rangeIndex];
|
|
||||||
try {
|
|
||||||
if (
|
|
||||||
page.metadata.id != null &&
|
|
||||||
page.metadata.deleted === false &&
|
|
||||||
page.metadata.published === true
|
|
||||||
) {
|
|
||||||
let entry = page.metadata;
|
|
||||||
entry.content = page.content;
|
|
||||||
//console.log('ENTRY', entry);
|
|
||||||
pages.push({
|
|
||||||
page: entry,
|
|
||||||
displayDate: moment(page.metadata.created).fromNow()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
//console.log("NO POST", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
meta.push({ currentPage: pageNum, totalPages: count });
|
|
||||||
let data = { pages: pages, meta: meta };
|
|
||||||
res.json({
|
|
||||||
type: DataEvent.REQUEST_GOOD,
|
|
||||||
message: 'This is Page ' + pageNum + ' of ' + count,
|
|
||||||
data: data
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves single entry
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
router.get('/single/:id', (req, res) => {
|
|
||||||
let id = req.params.id;
|
|
||||||
if (id === null || id === '')
|
|
||||||
res.json({ type: DataEvent.REQUEST_LAME, message: " Nah, this isn't here." });
|
|
||||||
book.getPage(id)
|
|
||||||
.then(page => {
|
|
||||||
let entry = page.metadata;
|
|
||||||
entry.content = page.content;
|
|
||||||
res.json({
|
|
||||||
type: DataEvent.REQUEST_GOOD,
|
|
||||||
message: 'Found it. Here you go.',
|
|
||||||
data: entry
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.json({
|
|
||||||
type: DataEvent.REQUEST_LAME,
|
|
||||||
message: "This doesn't seem to be here, homie.",
|
|
||||||
err: err.message
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add/Update Page
|
|
||||||
*/
|
|
||||||
router.post('/write/:task?', feature_upload, (req, res) => {
|
|
||||||
auth.authCheck(req)
|
|
||||||
.then(() => {
|
|
||||||
let body = _.mapValues(req.body);
|
|
||||||
let feature = '';
|
|
||||||
let task = '';
|
|
||||||
req.params.task === 'new'
|
|
||||||
? (task = DataEvent.API_PAGE_CREATE)
|
|
||||||
: (task = DataEvent.API_PAGE_WRITE);
|
|
||||||
if (req.files.length > 0) {
|
|
||||||
var path = req.files[0].path;
|
|
||||||
//console.log('NEW FEATURE URL', path);
|
|
||||||
feature = '/' + path.substring(7, path.length);
|
|
||||||
} else {
|
|
||||||
var url = body.feature_image;
|
|
||||||
//switch this to the new feature path edit
|
|
||||||
if (url != null || url != undefined || url != '') {
|
|
||||||
let chunks = url.split('/');
|
|
||||||
let strip = chunks[0] + '/' + chunks[1] + chunks[2];
|
|
||||||
feature = url.substr(strip.length + 1, url.length);
|
|
||||||
} else {
|
|
||||||
feature = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
body.feature = feature;
|
|
||||||
body.deleted = false;
|
|
||||||
//if title changes, get rid of a pages with old title
|
|
||||||
if (body.current_title !== body.slug) {
|
|
||||||
let path =
|
|
||||||
moment(body.created).format('YYYY') + '/' + moment(body.created).format('MM');
|
|
||||||
|
|
||||||
//remove html page
|
|
||||||
fs.unlink('public/' + path + '/' + body.current_title + '.html')
|
|
||||||
.then()
|
|
||||||
.catch(() => {
|
|
||||||
//console.log('HTML ERROR', err);
|
|
||||||
});
|
|
||||||
|
|
||||||
//remove markdown
|
|
||||||
fs.unlink('content/pages/' + path + '/' + body.current_title + '.md')
|
|
||||||
.then()
|
|
||||||
.catch(() => {
|
|
||||||
//console.log('MD ERROR', err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
book.editPage(body, body.page_uuid, task, req.session.user)
|
|
||||||
.then(result => {
|
|
||||||
if (result.type === DataEvent.PAGE_ADDED) {
|
|
||||||
settings.updatePageIndex();
|
|
||||||
}
|
|
||||||
//load all page data and render if render on save flag is set in settings file
|
|
||||||
getBookData()
|
|
||||||
.then(result => {
|
|
||||||
if (result.settings.global.renderOnSave === 'true') {
|
|
||||||
render
|
|
||||||
.publishAll(
|
|
||||||
result.pages,
|
|
||||||
result.settings.global.theme,
|
|
||||||
req.session.user.handle
|
|
||||||
)
|
|
||||||
.then(response => {
|
|
||||||
res.json({
|
|
||||||
type: response.type,
|
|
||||||
message: response.message
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.json({
|
|
||||||
type: DataEvent.PAGES_NOT_RENDERED,
|
|
||||||
message: 'Uh oh. Pages not rendered, sport',
|
|
||||||
error: err
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
//console.log('DONT RENDER PAGES');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
//console.log();
|
|
||||||
});
|
|
||||||
res.json(result);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.json(err);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.json(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Soft deletes Page
|
|
||||||
*/
|
|
||||||
|
|
||||||
router.post('/delete', (req, res) => {
|
|
||||||
auth.authCheck(req)
|
|
||||||
.then(() => {
|
|
||||||
book.editPage([], req.body.id, DataEvent.API_PAGE_DELETE, req.session.user)
|
|
||||||
.then(result => {
|
|
||||||
//remove item from menu in settings
|
|
||||||
res.json(result);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.json(err);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.json(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uploads image from a Page content
|
|
||||||
*/
|
|
||||||
|
|
||||||
router.post('/add-post-image', post_upload, function (req, res) {
|
|
||||||
//console.log(req.body);
|
|
||||||
var image = req.files[0].path;
|
|
||||||
return res.json({
|
|
||||||
type: DataEvent.POST_IMAGE_ADDED,
|
|
||||||
message: 'Added Image',
|
|
||||||
url: '/' + image.substr(7, image.length)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = router;
|
|
||||||
|
|
||||||
function getBookData() {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
let getSettings = settings.load(SETTINGS_FILE);
|
|
||||||
let getBook = book.getPage();
|
|
||||||
Promise.all([getSettings, getBook])
|
|
||||||
.then(result => {
|
|
||||||
const [settings, pages] = result;
|
|
||||||
let data = { settings: settings, pages: pages };
|
|
||||||
resolve(data);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,250 +0,0 @@
|
|||||||
import * as DataEvent from '../../../src/com/events/DataEvent';
|
|
||||||
import Auth from '../../data/Auth';
|
|
||||||
import Render from '../../data/Render';
|
|
||||||
import Settings, { SETTINGS_FILE, SETTINGS_FOLKS } from '../../data/Settings';
|
|
||||||
import Navigation from '../../data/Navigation';
|
|
||||||
import Book from '../../data/Book';
|
|
||||||
const express = require('express');
|
|
||||||
const router = express.Router();
|
|
||||||
const multer = require('multer');
|
|
||||||
const fs = require('fs-extra');
|
|
||||||
const moment = require('moment');
|
|
||||||
const _ = require('lodash');
|
|
||||||
const auth = new Auth();
|
|
||||||
const render = new Render();
|
|
||||||
const book = new Book();
|
|
||||||
const settings = new Settings();
|
|
||||||
const nav = new Navigation();
|
|
||||||
const uploadPath =
|
|
||||||
'./public/assets/images/user/' + moment().format('YYYY') + '/' + moment().format('MM');
|
|
||||||
|
|
||||||
var storage = multer.diskStorage({
|
|
||||||
destination: function (req, file, cb) {
|
|
||||||
fs.ensureDir(uploadPath, () => {
|
|
||||||
// dir has now been created, including the directory it is to be placed in
|
|
||||||
cb(null, uploadPath);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
filename: function (req, file, cb) {
|
|
||||||
var splice = file.originalname.split(':');
|
|
||||||
cb(null, splice[0]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
var avatar_upload = multer({
|
|
||||||
storage: storage
|
|
||||||
}).array('avatar_upload');
|
|
||||||
var background_upload = multer({
|
|
||||||
storage: storage
|
|
||||||
}).array('background_upload');
|
|
||||||
//** SYNC POSTS */
|
|
||||||
router.post('/sync', (req, res) => {
|
|
||||||
auth.authCheck(req)
|
|
||||||
.then(() => {
|
|
||||||
settings
|
|
||||||
.sync(req, res)
|
|
||||||
.then(() => {
|
|
||||||
res.json({
|
|
||||||
type: DataEvent.SETTINGS_UPDATED,
|
|
||||||
message: 'Settings Saved'
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.json({
|
|
||||||
type: DataEvent.REQUEST_LAME,
|
|
||||||
error: err.message,
|
|
||||||
message: "Uh oh. Settings didn't take, sport"
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.json({
|
|
||||||
type: err.type,
|
|
||||||
message: err.message
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
router.post('/nav-sync', (req, res) => {
|
|
||||||
auth.authCheck(req)
|
|
||||||
.then(() => {
|
|
||||||
// find removed menu item page and set menu to false
|
|
||||||
book.getPage(req.body.remove).then(page => {
|
|
||||||
let body = page.metadata;
|
|
||||||
body.content = page.content;
|
|
||||||
body.menu = false;
|
|
||||||
book.editPage(body, body.uuid, DataEvent.API_PAGE_WRITE, req.session.user);
|
|
||||||
});
|
|
||||||
nav.sync(req.body)
|
|
||||||
.then(response => {
|
|
||||||
res.json({
|
|
||||||
type: response.type,
|
|
||||||
message: response.message
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.json({
|
|
||||||
type: DataEvent.REQUEST_LAME,
|
|
||||||
message: err
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.json({
|
|
||||||
type: err.type,
|
|
||||||
message: err.message
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
router.post('/publish-pages', (req, res) => {
|
|
||||||
auth.authCheck(req)
|
|
||||||
.then(() => {
|
|
||||||
getBookData()
|
|
||||||
.then(result => {
|
|
||||||
render
|
|
||||||
.publishAll(
|
|
||||||
result.pages,
|
|
||||||
result.settings.global.theme,
|
|
||||||
req.session.user.handle
|
|
||||||
)
|
|
||||||
.then(response => {
|
|
||||||
res.json({
|
|
||||||
type: response.type,
|
|
||||||
message: response.message
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.json({
|
|
||||||
type: DataEvent.PAGES_NOT_RENDERED,
|
|
||||||
message: 'Uh oh. Pages not rendered, sport',
|
|
||||||
error: err
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.json({
|
|
||||||
type: DataEvent.PAGES_NOT_RENDERED,
|
|
||||||
message: 'Uh oh. Pages not rendered, sport',
|
|
||||||
error: err
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.json({
|
|
||||||
type: err.type,
|
|
||||||
message: err.message
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
/***
|
|
||||||
UPLOAD AVATAR
|
|
||||||
*/
|
|
||||||
|
|
||||||
router.post('/add-avatar', avatar_upload, (req, res) => {
|
|
||||||
if (req.session.user) {
|
|
||||||
let user = req.session.user;
|
|
||||||
settings
|
|
||||||
.load(SETTINGS_FOLKS)
|
|
||||||
.then(folks => {
|
|
||||||
let found = _.find(folks, { handle: user.handle });
|
|
||||||
if (found) {
|
|
||||||
var index = found.id - 1;
|
|
||||||
var path = req.files[0].path;
|
|
||||||
var image = path.substr(7, path.length);
|
|
||||||
folks[index].avi = '/' + image;
|
|
||||||
fs.writeJson('site/folks.json', folks);
|
|
||||||
user.avi = '/' + image;
|
|
||||||
res.json({
|
|
||||||
type: DataEvent.AVATAR_UPLOADED,
|
|
||||||
message: 'Changed avi. You look great.',
|
|
||||||
url: '/' + image
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
res.json({
|
|
||||||
type: DataEvent.REQUEST_LAME,
|
|
||||||
message: 'Members Not found'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
res.json({
|
|
||||||
type: DataEvent.REQUEST_LAME,
|
|
||||||
message: "You're not logged in, champ"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/***
|
|
||||||
UPLOAD FEATURE BACKGROUND
|
|
||||||
*/
|
|
||||||
|
|
||||||
router.post('/add-feature-background', background_upload, (req, res) => {
|
|
||||||
if (req.session.user) {
|
|
||||||
settings
|
|
||||||
.load(SETTINGS_FILE)
|
|
||||||
.then(settings => {
|
|
||||||
var path = req.files[0].path;
|
|
||||||
var image = path.substr(7, path.length);
|
|
||||||
settings.global.background = '/' + image;
|
|
||||||
fs.writeJson('site/settings.json', settings);
|
|
||||||
res.json({
|
|
||||||
type: DataEvent.SITE_BACKGROUND_UPLOADED,
|
|
||||||
message: 'Background Uploaded',
|
|
||||||
url: '/' + image
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
//console.log('ERROR', err);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
res.json({
|
|
||||||
type: DataEvent.REQUEST_LAME,
|
|
||||||
message: "You're not logged in, champ"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
router.post('/reindex', (req, res) => {
|
|
||||||
auth.authCheck(req)
|
|
||||||
.then(() => {
|
|
||||||
book.reindexPages(req)
|
|
||||||
.then(response => {
|
|
||||||
//reset settings index
|
|
||||||
settings.resetLibraryIndex(response.count + 1);
|
|
||||||
//return success to front end
|
|
||||||
res.json(response);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.json({
|
|
||||||
type: err.type,
|
|
||||||
message: err.message
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.json({
|
|
||||||
type: err.type,
|
|
||||||
message: err.message
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = router;
|
|
||||||
|
|
||||||
function getBookData() {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
let getSettings = settings.load(SETTINGS_FILE);
|
|
||||||
let getBook = book.getPage();
|
|
||||||
Promise.all([getSettings, getBook])
|
|
||||||
.then(result => {
|
|
||||||
const [settings, pages] = result;
|
|
||||||
let data = { settings: settings, pages: pages };
|
|
||||||
resolve(data);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,94 +0,0 @@
|
|||||||
var express = require('express');
|
|
||||||
var path = require('path');
|
|
||||||
//var favicon = require('serve-favicon');
|
|
||||||
var logger = require('morgan');
|
|
||||||
var cookieParser = require('cookie-parser');
|
|
||||||
var bodyParser = require('body-parser');
|
|
||||||
var session = require('express-session');
|
|
||||||
var MemoryStore = require('memorystore')(session);
|
|
||||||
var flash = require('connect-flash');
|
|
||||||
var app = express();
|
|
||||||
// favicon stuff
|
|
||||||
//app.use(favicon(path.join(__dirname, 'favicons', 'favicon.ico')));
|
|
||||||
|
|
||||||
// view engine setup
|
|
||||||
app.set('views', path.join(__dirname, './views'));
|
|
||||||
app.set('view engine', 'pug');
|
|
||||||
app.use(logger('dev'));
|
|
||||||
|
|
||||||
app.use(bodyParser.json({ limit: '50mb' }));
|
|
||||||
app.use(
|
|
||||||
bodyParser.urlencoded({
|
|
||||||
extended: false,
|
|
||||||
limit: '50mb'
|
|
||||||
})
|
|
||||||
);
|
|
||||||
app.use(cookieParser());
|
|
||||||
app.use(express.static(path.join(__dirname, '../public'), { extensions: ['html'] }));
|
|
||||||
|
|
||||||
app.use(
|
|
||||||
session({
|
|
||||||
store: new MemoryStore({
|
|
||||||
checkPeriod: 86400000 // prune expired entries every 24h
|
|
||||||
}),
|
|
||||||
secret: '1KqZ18W8KskE1iSw',
|
|
||||||
saveUninitialized: false,
|
|
||||||
resave: false,
|
|
||||||
cookie: {
|
|
||||||
maxAge: 608800000
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
app.use(flash());
|
|
||||||
//sections
|
|
||||||
//var front = require('./routes/front/index')(session);
|
|
||||||
var dash = require('./routes/dash/index');
|
|
||||||
var page = require('./routes/dash/pages');
|
|
||||||
var settings = require('./routes/dash/settings');
|
|
||||||
var nav = require('./routes/dash/nav');
|
|
||||||
//api
|
|
||||||
var pages = require('./api/v1/pages');
|
|
||||||
var setting = require('./api/v1/settings');
|
|
||||||
var mailer = require('./api/v1/mailer');
|
|
||||||
var auth = require('./api/v1/auth');
|
|
||||||
var backup = require('./api/v1/backup');
|
|
||||||
// API PATHS
|
|
||||||
|
|
||||||
app.use('/api/v1/page', pages);
|
|
||||||
app.use('/api/v1/settings', setting);
|
|
||||||
app.use('/api/v1/auth', auth);
|
|
||||||
app.use('/api/v1/mailer', mailer);
|
|
||||||
app.use('/api/v1/backup', backup);
|
|
||||||
// PAGES
|
|
||||||
app.use('/@/dashboard', dash);
|
|
||||||
app.use('/@/dashboard/page', page);
|
|
||||||
app.use('/@/dashboard/settings', settings);
|
|
||||||
app.use('/@/dashboard/navigation', nav);
|
|
||||||
// catch 404 and forward to error handler
|
|
||||||
app.use(function (req, res, next) {
|
|
||||||
var err = new Error('Not Found');
|
|
||||||
err.status = 404;
|
|
||||||
next(err);
|
|
||||||
});
|
|
||||||
// error handlers
|
|
||||||
// development error handler
|
|
||||||
// will print stacktrace
|
|
||||||
if (app.get('env') === 'development') {
|
|
||||||
app.use(function (err, req, res) {
|
|
||||||
res.status(err.status || 500);
|
|
||||||
res.render('error', {
|
|
||||||
message: err.message,
|
|
||||||
error: err
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// production error handler
|
|
||||||
// no stacktraces leaked to user
|
|
||||||
app.use(function (err, req, res) {
|
|
||||||
res.status(err.status || 500);
|
|
||||||
res.render('error', {
|
|
||||||
message: err.message,
|
|
||||||
error: {}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
module.exports = app;
|
|
@ -1,105 +0,0 @@
|
|||||||
import * as DataEvent from '../../src/com/events/DataEvent';
|
|
||||||
const bCrypt = require('bcrypt');
|
|
||||||
const jwt = require('jsonwebtoken');
|
|
||||||
const _ = require('lodash');
|
|
||||||
|
|
||||||
export default class Auth {
|
|
||||||
//--------------------------
|
|
||||||
// constructor
|
|
||||||
//--------------------------
|
|
||||||
constructor() {}
|
|
||||||
//--------------------------
|
|
||||||
// methods
|
|
||||||
//--------------------------
|
|
||||||
start() {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Makes sure access token is legit
|
|
||||||
* @parameter req
|
|
||||||
*/
|
|
||||||
|
|
||||||
authCheck(req) {
|
|
||||||
let self = this;
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
let hash = req.headers['x-access-token'];
|
|
||||||
let response = [];
|
|
||||||
//check to see if user is logged in
|
|
||||||
if (!req.session.user) {
|
|
||||||
response = {
|
|
||||||
status: false,
|
|
||||||
type: DataEvent.API_REQUEST_LAME,
|
|
||||||
message: "You're not logged in, champ."
|
|
||||||
};
|
|
||||||
reject(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Checks if token is a proper hash, if not reject
|
|
||||||
if (!self.isTokenValid(req.session.token, hash)) {
|
|
||||||
response = {
|
|
||||||
status: false,
|
|
||||||
type: DataEvent.API_REQUEST_LAME,
|
|
||||||
message: 'No Token Present. Auth Blocked'
|
|
||||||
};
|
|
||||||
reject(response);
|
|
||||||
//res.json();
|
|
||||||
} else {
|
|
||||||
var member = req.session.user;
|
|
||||||
jwt.verify(req.session.token, member.key, function (err, decoded) {
|
|
||||||
if (err) {
|
|
||||||
response = {
|
|
||||||
status: false,
|
|
||||||
type: DataEvent.API_REQUEST_LAME,
|
|
||||||
message: 'Invalid Token. Auth Blocked'
|
|
||||||
};
|
|
||||||
reject(response);
|
|
||||||
}
|
|
||||||
response = {
|
|
||||||
status: true,
|
|
||||||
type: DataEvent.API_REQUEST_GOOD,
|
|
||||||
message: 'Token Verified',
|
|
||||||
token: decoded
|
|
||||||
};
|
|
||||||
resolve(response);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
verifyCredentials(config, credentials) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
var found = _.find(config, { handle: credentials.handle });
|
|
||||||
var response;
|
|
||||||
if (found) {
|
|
||||||
if (!this.isValidPassword(found, credentials.pass)) {
|
|
||||||
response = {
|
|
||||||
type: DataEvent.REQUEST_LAME,
|
|
||||||
message: 'CHECK YOUR PASSWORD'
|
|
||||||
};
|
|
||||||
reject(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
response = { type: DataEvent.REQUEST_GOOD, message: 'Backup Verified. Restoring' };
|
|
||||||
resolve(response);
|
|
||||||
} else {
|
|
||||||
response = { type: DataEvent.REQUEST_LAME, message: 'Handle not found, boss' };
|
|
||||||
reject(response);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
isValidPassword(user, password) {
|
|
||||||
return bCrypt.compareSync(password, user.password);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks to make sure received token matches
|
|
||||||
* @parameter token: created token
|
|
||||||
* @parameter hashedToken: encrypted token
|
|
||||||
*/
|
|
||||||
isTokenValid(token, hashedToken) {
|
|
||||||
return bCrypt.compareSync(token, hashedToken);
|
|
||||||
}
|
|
||||||
//--------------------------
|
|
||||||
// event handlers
|
|
||||||
//--------------------------
|
|
||||||
}
|
|
@ -1,273 +0,0 @@
|
|||||||
import fh from 'filehound';
|
|
||||||
import fs from 'fs-extra';
|
|
||||||
import metadataParser from 'markdown-yaml-metadata-parser';
|
|
||||||
import _ from 'lodash';
|
|
||||||
import * as DataEvent from '../../src/com/events/DataEvent';
|
|
||||||
import Navigation from './Navigation';
|
|
||||||
import Utils from './Utils';
|
|
||||||
const moment = require('moment');
|
|
||||||
const nav = new Navigation();
|
|
||||||
const utils = new Utils();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class for handling blog content pages
|
|
||||||
*/
|
|
||||||
|
|
||||||
export default class Book {
|
|
||||||
//--------------------------
|
|
||||||
// constructor
|
|
||||||
//--------------------------
|
|
||||||
constructor() {}
|
|
||||||
//--------------------------
|
|
||||||
// methods
|
|
||||||
//--------------------------
|
|
||||||
start() {}
|
|
||||||
/**
|
|
||||||
* Retrieves single page or pages
|
|
||||||
* @parameter id: optional id if requesting a single Page
|
|
||||||
*/
|
|
||||||
getPage(id) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
fh.create()
|
|
||||||
.paths('content/pages')
|
|
||||||
.ext('md')
|
|
||||||
.find()
|
|
||||||
.then(files => {
|
|
||||||
let pages = [];
|
|
||||||
for (let index = 0; index < files.length; index++) {
|
|
||||||
fs.readFile(files[index], { encoding: 'utf8' }, (err, file) => {
|
|
||||||
pages.push(metadataParser(file));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (id === null || id === null || id === undefined) {
|
|
||||||
setTimeout(() => {
|
|
||||||
//TODO: Duct tape solution until something better created
|
|
||||||
utils.organizeTags(pages);
|
|
||||||
utils.organizeArchive(pages);
|
|
||||||
resolve(pages);
|
|
||||||
}, 100);
|
|
||||||
} else {
|
|
||||||
setTimeout(() => {
|
|
||||||
//TODO: Duct tape solution until something better created
|
|
||||||
|
|
||||||
//make check against menu to see if page should be marked as menu item
|
|
||||||
//if it doesn't exist in menu change, edit page to
|
|
||||||
let page = _.find(pages, list => {
|
|
||||||
return list.metadata.uuid === id;
|
|
||||||
});
|
|
||||||
resolve(page);
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Edits single page based on id and task
|
|
||||||
* @parameter body: object that contains all page information
|
|
||||||
* @parameter id: identifier for page being edited
|
|
||||||
* @parameter task: type of task being performed - listed in DataEvents Class /src/com/events
|
|
||||||
* @parameter user: object contain user information
|
|
||||||
*/
|
|
||||||
editPage(body, id, task, user) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
let self = this;
|
|
||||||
let response = [];
|
|
||||||
switch (task) {
|
|
||||||
case DataEvent.API_PAGE_CREATE:
|
|
||||||
case DataEvent.API_PAGE_WRITE:
|
|
||||||
var layout = 'page';
|
|
||||||
var path = '';
|
|
||||||
fs.ensureDir(
|
|
||||||
'content/pages/' +
|
|
||||||
moment(body.created).format('YYYY') +
|
|
||||||
'/' +
|
|
||||||
moment(body.created).format('MM') +
|
|
||||||
'/'
|
|
||||||
).then(() => {
|
|
||||||
if (body.menu === 'true') {
|
|
||||||
body.path =
|
|
||||||
moment(body.created).format('YYYY') +
|
|
||||||
'/' +
|
|
||||||
moment(body.created).format('MM');
|
|
||||||
nav.editMenu(DataEvent.MENU_ADD_ITEM, body, user);
|
|
||||||
} else {
|
|
||||||
nav.editMenu(DataEvent.MENU_DELETE_ITEM, body, user);
|
|
||||||
}
|
|
||||||
if (body.layout !== 'page') layout = body.layout;
|
|
||||||
if (body.layout === null || body.layout === 'null') layout = 'page';
|
|
||||||
var pageWrite =
|
|
||||||
'---\n' +
|
|
||||||
'id: ' +
|
|
||||||
body.id +
|
|
||||||
'\n' +
|
|
||||||
'uuid: ' +
|
|
||||||
body.uuid +
|
|
||||||
'\n' +
|
|
||||||
'title: ' +
|
|
||||||
body.title +
|
|
||||||
'\n' +
|
|
||||||
'feature: ' +
|
|
||||||
body.feature +
|
|
||||||
'\n' +
|
|
||||||
'path: ' +
|
|
||||||
moment(body.created).format('YYYY') +
|
|
||||||
'/' +
|
|
||||||
moment(body.created).format('MM') +
|
|
||||||
'\n' +
|
|
||||||
'layout: ' +
|
|
||||||
layout +
|
|
||||||
'\n' +
|
|
||||||
'tags: ' +
|
|
||||||
body.tags +
|
|
||||||
'\n' +
|
|
||||||
'author: ' +
|
|
||||||
user.handle +
|
|
||||||
'\n' +
|
|
||||||
'created: ' +
|
|
||||||
moment(body.created).format() +
|
|
||||||
'\n' +
|
|
||||||
'updated: ' +
|
|
||||||
moment(Date.now()).format() +
|
|
||||||
'\n' +
|
|
||||||
'deleted: ' +
|
|
||||||
body.deleted +
|
|
||||||
'\n' +
|
|
||||||
'menu: ' +
|
|
||||||
body.menu +
|
|
||||||
'\n' +
|
|
||||||
'featured: ' +
|
|
||||||
body.featured +
|
|
||||||
'\n' +
|
|
||||||
'published: ' +
|
|
||||||
body.published +
|
|
||||||
'\n' +
|
|
||||||
'slug: ' +
|
|
||||||
body.slug +
|
|
||||||
'\n' +
|
|
||||||
'---\n' +
|
|
||||||
body.content;
|
|
||||||
layout === 'index'
|
|
||||||
? (path = 'content/pages/index.md')
|
|
||||||
: (path =
|
|
||||||
'content/pages/' +
|
|
||||||
moment(body.created).format('YYYY') +
|
|
||||||
'/' +
|
|
||||||
moment(body.created).format('MM') +
|
|
||||||
'/' +
|
|
||||||
body.slug +
|
|
||||||
'.md');
|
|
||||||
fs.writeFile(path, pageWrite, err => {
|
|
||||||
// throws an error, you could also catch it here
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
response = { type: DataEvent.PAGE_ERROR, message: err };
|
|
||||||
reject(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
// success case, the file was saved
|
|
||||||
if (task === DataEvent.API_PAGE_CREATE) {
|
|
||||||
// if new file, update settings index and page count
|
|
||||||
response = {
|
|
||||||
type: DataEvent.PAGE_ADDED,
|
|
||||||
message: 'New Page Created',
|
|
||||||
id: body.uuid
|
|
||||||
};
|
|
||||||
resolve(response);
|
|
||||||
} else {
|
|
||||||
response = {
|
|
||||||
type: DataEvent.PAGE_UPDATED,
|
|
||||||
message: 'Page saved. Nice Work'
|
|
||||||
};
|
|
||||||
resolve(response);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
|
||||||
case DataEvent.API_PAGE_DELETE:
|
|
||||||
this.getPage(id)
|
|
||||||
.then(page => {
|
|
||||||
let body = _.mapValues(page.metadata);
|
|
||||||
|
|
||||||
body.content = page.content;
|
|
||||||
body.deleted = moment(Date.now()).format();
|
|
||||||
body.menu = false;
|
|
||||||
|
|
||||||
self.editPage(body, body.uuid, DataEvent.API_PAGE_WRITE, user)
|
|
||||||
.then(() => {
|
|
||||||
let item = {
|
|
||||||
title: body.title,
|
|
||||||
id: body.id,
|
|
||||||
slug: body.slug,
|
|
||||||
uuid: body.uuid
|
|
||||||
};
|
|
||||||
nav.editMenu(DataEvent.MENU_DELETE_ITEM, item);
|
|
||||||
|
|
||||||
response = {
|
|
||||||
type: DataEvent.PAGE_DELETED,
|
|
||||||
message: 'Page deleted, sport',
|
|
||||||
data: { uuid: body.uuid }
|
|
||||||
};
|
|
||||||
resolve(response);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
response = { type: DataEvent.PAGE_ERROR, message: err };
|
|
||||||
reject(response);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
response = { type: DataEvent.PAGE_ERROR, message: err };
|
|
||||||
reject(response);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
reindexPages(req) {
|
|
||||||
var response = '';
|
|
||||||
var self = this;
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
self.getPage()
|
|
||||||
.then(pages => {
|
|
||||||
let sorted = [];
|
|
||||||
for (let i = 0; i < pages.length; i++) {
|
|
||||||
let body = pages[i].metadata;
|
|
||||||
body.content = pages[i].content;
|
|
||||||
sorted.push(body);
|
|
||||||
}
|
|
||||||
//resorts pages by date created
|
|
||||||
let byDate = _.sortBy(sorted, page => {
|
|
||||||
return page.created;
|
|
||||||
});
|
|
||||||
//reassigns id sequentially based on sorted pages
|
|
||||||
for (let index = 0; index < byDate.length; index++) {
|
|
||||||
byDate[index].id = index;
|
|
||||||
self.editPage(
|
|
||||||
byDate[index],
|
|
||||||
index,
|
|
||||||
DataEvent.API_PAGE_WRITE,
|
|
||||||
req.session.user
|
|
||||||
);
|
|
||||||
}
|
|
||||||
response = {
|
|
||||||
type: DataEvent.API_REINDEX_PAGES,
|
|
||||||
message: 'Pages re-sorted. Easy peasy.',
|
|
||||||
count: byDate.length
|
|
||||||
};
|
|
||||||
resolve(response);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
response = { type: DataEvent.PAGE_ERROR, message: err };
|
|
||||||
reject(response);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------
|
|
||||||
// event handlers
|
|
||||||
//--------------------------
|
|
||||||
}
|
|
@ -1,75 +0,0 @@
|
|||||||
import fs from 'fs-extra';
|
|
||||||
import _ from 'lodash';
|
|
||||||
import * as DataEvent from '../../src/com/events/DataEvent';
|
|
||||||
import Settings, { SETTINGS_FILE } from './Settings';
|
|
||||||
const settings = new Settings();
|
|
||||||
|
|
||||||
export default class Navigation {
|
|
||||||
//--------------------------
|
|
||||||
// constructor
|
|
||||||
//--------------------------
|
|
||||||
constructor() {}
|
|
||||||
//--------------------------
|
|
||||||
// methods
|
|
||||||
//--------------------------
|
|
||||||
sync(body) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
let response = [];
|
|
||||||
settings
|
|
||||||
.load(SETTINGS_FILE)
|
|
||||||
.then(settings => {
|
|
||||||
let payload = body;
|
|
||||||
settings.menu = payload.nav;
|
|
||||||
fs.writeJson('site/settings.json', settings)
|
|
||||||
.then(() => {
|
|
||||||
response = {
|
|
||||||
type: DataEvent.SETTINGS_UPDATED,
|
|
||||||
message: 'Menu order saved, champ'
|
|
||||||
};
|
|
||||||
resolve(response);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
response = {
|
|
||||||
type: DataEvent.REQUEST_LAME,
|
|
||||||
message: err
|
|
||||||
};
|
|
||||||
reject(response);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
response = {
|
|
||||||
type: DataEvent.REQUEST_LAME,
|
|
||||||
message: err
|
|
||||||
};
|
|
||||||
reject(response);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
editMenu(task, item) {
|
|
||||||
settings.load(SETTINGS_FILE).then(settings => {
|
|
||||||
switch (task) {
|
|
||||||
case DataEvent.MENU_ADD_ITEM:
|
|
||||||
settings.menu.push({
|
|
||||||
title: item.title,
|
|
||||||
id: item.id,
|
|
||||||
slug: item.slug,
|
|
||||||
uuid: item.uuid,
|
|
||||||
path: item.path
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case DataEvent.MENU_DELETE_ITEM:
|
|
||||||
settings.menu = _.remove(settings.menu, m => {
|
|
||||||
return m.uuid != item.uuid;
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
fs.writeJSON(SETTINGS_FILE, settings);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------
|
|
||||||
// event handlers
|
|
||||||
//--------------------------
|
|
||||||
}
|
|
@ -1,304 +0,0 @@
|
|||||||
import * as DataEvent from '../../src/com/events/DataEvent';
|
|
||||||
import StringUtils from '../../src/com/utils/StringUtils';
|
|
||||||
import Settings, { SETTINGS_FILE, SETTINGS_TAG } from './Settings';
|
|
||||||
import fs from 'fs-extra';
|
|
||||||
import sanitize from 'sanitize-html';
|
|
||||||
import Utils from './Utils';
|
|
||||||
const pug = require('pug');
|
|
||||||
const md = require('markdown-it')('commonmark');
|
|
||||||
const _ = require('lodash');
|
|
||||||
const moment = require('moment');
|
|
||||||
const settings = new Settings();
|
|
||||||
|
|
||||||
export default class Render {
|
|
||||||
//--------------------------
|
|
||||||
// constructor
|
|
||||||
//--------------------------
|
|
||||||
constructor() {}
|
|
||||||
//--------------------------
|
|
||||||
// methods
|
|
||||||
//--------------------------
|
|
||||||
start() {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders all pages from markdown to html
|
|
||||||
* @parameter pages: payload for site pages
|
|
||||||
* @parameter theme: current theme being used as defined in settings
|
|
||||||
*/
|
|
||||||
publishAll(pages, theme, author) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
settings
|
|
||||||
.load(SETTINGS_FILE)
|
|
||||||
.then(config => {
|
|
||||||
let response = [];
|
|
||||||
let count = _.filter(pages, page => {
|
|
||||||
return page.metadata.deleted === false && page.metadata.published === true;
|
|
||||||
}).length;
|
|
||||||
let rendered = 0;
|
|
||||||
let display_count = 0;
|
|
||||||
let recent = [];
|
|
||||||
let featured = _.filter(pages, page => {
|
|
||||||
return (
|
|
||||||
page.metadata.deleted === false &&
|
|
||||||
page.metadata.published === true &&
|
|
||||||
page.metadata.featured === true &&
|
|
||||||
page.metadata.layout !== 'index'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
for (let index = 0; index < pages.length; index++) {
|
|
||||||
pages.sort((a, b) => parseFloat(b.metadata.id) - parseFloat(a.metadata.id));
|
|
||||||
const page = pages[index];
|
|
||||||
if (page.metadata.deleted === false && page.metadata.published === true) {
|
|
||||||
if (page.metadata.layout != 'index') {
|
|
||||||
if (recent.length < config.global.display_limit) {
|
|
||||||
recent.push({
|
|
||||||
title: page.metadata.title,
|
|
||||||
slug: page.metadata.slug,
|
|
||||||
feature: page.metadata.feature,
|
|
||||||
created: moment(page.metadata.created).fromNow(),
|
|
||||||
path: page.metadata.path
|
|
||||||
});
|
|
||||||
display_count = ++display_count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let writeFile, template;
|
|
||||||
|
|
||||||
let path =
|
|
||||||
'public/' +
|
|
||||||
moment(page.metadata.created).format('YYYY') +
|
|
||||||
'/' +
|
|
||||||
moment(page.metadata.created).format('MM') +
|
|
||||||
'/';
|
|
||||||
if (page.metadata.layout === 'index') {
|
|
||||||
template = 'content/themes/' + theme + '/index.pug';
|
|
||||||
writeFile = 'public/index.html';
|
|
||||||
} else {
|
|
||||||
writeFile = path + page.metadata.slug + '.html';
|
|
||||||
template = 'content/themes/' + theme + '/page.pug';
|
|
||||||
}
|
|
||||||
|
|
||||||
let buffed = sanitize(page.content, {
|
|
||||||
allowedTags: ['del', 'a', 'iframe', 'img'],
|
|
||||||
allowedAttributes: {
|
|
||||||
a: ['href', 'name', 'target'],
|
|
||||||
img: ['src'],
|
|
||||||
iframe: [
|
|
||||||
'height',
|
|
||||||
'width',
|
|
||||||
'src',
|
|
||||||
'frameborder',
|
|
||||||
'allow',
|
|
||||||
'allowfullscreen'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let bag = page.metadata.tags.split(',');
|
|
||||||
let tags = [];
|
|
||||||
for (let index = 0; index < bag.length; index++) {
|
|
||||||
let tag = bag[index].trim();
|
|
||||||
tags.push({
|
|
||||||
label: bag[index],
|
|
||||||
slug: new StringUtils().cleanString(tag)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
buffed = new StringUtils().decodeHTML(buffed);
|
|
||||||
let html = md.render(buffed, { html: true, xhtmlOut: true });
|
|
||||||
//add open graph meta variables
|
|
||||||
let file = pug.renderFile(template, {
|
|
||||||
title: page.metadata.title,
|
|
||||||
default_bg: page.metadata.feature,
|
|
||||||
image: page.metadata.feature,
|
|
||||||
keywords: page.metadata.tags,
|
|
||||||
content: html,
|
|
||||||
tags: tags,
|
|
||||||
menu: config.menu,
|
|
||||||
recent_posts: recent,
|
|
||||||
featured_posts: featured,
|
|
||||||
meta: {
|
|
||||||
who: author,
|
|
||||||
when: moment(page.metadata.created).fromNow(),
|
|
||||||
tags: tags
|
|
||||||
},
|
|
||||||
welcome_message: page.metadata.title
|
|
||||||
});
|
|
||||||
|
|
||||||
fs.ensureDir(path).then(() => {
|
|
||||||
fs.writeFile(writeFile, file, err => {
|
|
||||||
// throws an error, you could also catch it here
|
|
||||||
if (err) {
|
|
||||||
response = {
|
|
||||||
type: DataEvent.PAGES_NOT_RENDERED,
|
|
||||||
message: err
|
|
||||||
};
|
|
||||||
reject(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
// success case, the file was saved
|
|
||||||
});
|
|
||||||
});
|
|
||||||
rendered = ++rendered;
|
|
||||||
if (rendered === count) {
|
|
||||||
response = {
|
|
||||||
type: DataEvent.PAGES_RENDERED,
|
|
||||||
message: 'All Pages Rendered. Sweet.'
|
|
||||||
};
|
|
||||||
//move theme assets to public when pages are rendered
|
|
||||||
new Utils().moveAssets();
|
|
||||||
resolve(response);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (count === 0) {
|
|
||||||
response = {
|
|
||||||
type: DataEvent.PAGES_RENDERED,
|
|
||||||
message: 'No page rendering needed'
|
|
||||||
};
|
|
||||||
resolve(response);
|
|
||||||
}
|
|
||||||
//check to see if deleted pages have been renderered and delete them
|
|
||||||
if (page.metadata.layout !== 'index') {
|
|
||||||
fs.unlink(
|
|
||||||
'public/' +
|
|
||||||
page.metadata.path +
|
|
||||||
'/' +
|
|
||||||
page.metadata.slug +
|
|
||||||
'.html'
|
|
||||||
)
|
|
||||||
.then()
|
|
||||||
.catch(() => {
|
|
||||||
//console.log('ERROR', err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
//console.log('ERROR', err);
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Method to extract, group and render tags in page
|
|
||||||
* @parameter pages: payload for site pages
|
|
||||||
*/
|
|
||||||
publishTags(pages) {
|
|
||||||
let self = this;
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
self.loadRenderData()
|
|
||||||
.then(result => {
|
|
||||||
let tags = result.tags.tags;
|
|
||||||
let renderList = [];
|
|
||||||
for (let index = 0; index < tags.length; index++) {
|
|
||||||
let tag = tags[index];
|
|
||||||
//console.log('**TAG**', tag.tag_name);
|
|
||||||
var pageList = [];
|
|
||||||
for (let i = 0; i < pages.length; i++) {
|
|
||||||
let page = pages[i];
|
|
||||||
|
|
||||||
//TODO: filter for deleted and unpublished pages
|
|
||||||
if (
|
|
||||||
page.metadata.deleted === false &&
|
|
||||||
page.metadata.published === true
|
|
||||||
) {
|
|
||||||
if (_.includes(page.metadata.tags, tag.tag_name)) {
|
|
||||||
pageList.push({
|
|
||||||
title: page.metadata.title,
|
|
||||||
slug: page.metadata.slug,
|
|
||||||
path: page.metadata.path
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
renderList.push({ tag: tag.tag_name, tag_list: pageList, slug: tag.slug });
|
|
||||||
}
|
|
||||||
let response = [];
|
|
||||||
for (let index = 0; index < renderList.length; index++) {
|
|
||||||
let item = renderList[index];
|
|
||||||
let file = pug.renderFile(
|
|
||||||
'content/themes/' + result.settings.global.theme + '/tags.pug',
|
|
||||||
{
|
|
||||||
title: item.tag,
|
|
||||||
default_bg: result.settings.global.background,
|
|
||||||
content_tags: 'THESE ARE TAGS',
|
|
||||||
tag_list: item.tag_list,
|
|
||||||
menu: result.settings.menu
|
|
||||||
}
|
|
||||||
);
|
|
||||||
fs.ensureDir('public/tags', () => {
|
|
||||||
fs.writeFile('public/tags/' + item.slug + '.html', file, err => {
|
|
||||||
// throws an error, you could also catch it here
|
|
||||||
if (err) {
|
|
||||||
response = {
|
|
||||||
type: DataEvent.TAG_PAGES_NOT_RENDERED,
|
|
||||||
message: err
|
|
||||||
};
|
|
||||||
reject(response);
|
|
||||||
}
|
|
||||||
// success case, the file was saved
|
|
||||||
response = {
|
|
||||||
type: DataEvent.TAG_PAGES_RENDERED,
|
|
||||||
message: 'Tag Pages ready to go. Good job.'
|
|
||||||
};
|
|
||||||
resolve(response);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Method to build page that lists all active pages, organized by year and month
|
|
||||||
* @parameter pages: payload for site pages
|
|
||||||
*/
|
|
||||||
publishArchive(archive) {
|
|
||||||
settings
|
|
||||||
.load(SETTINGS_FILE)
|
|
||||||
.then(settings => {
|
|
||||||
let file = pug.renderFile(
|
|
||||||
'content/themes/' + settings.global.theme + '/archive.pug',
|
|
||||||
{
|
|
||||||
title: 'ARCHIVES',
|
|
||||||
default_bg: settings.global.background,
|
|
||||||
content_tags: 'COLD STORAGE',
|
|
||||||
archives: archive,
|
|
||||||
menu: settings.menu
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
fs.writeFile('public/archives.html', file, err => {
|
|
||||||
// throws an error, you could also catch it here
|
|
||||||
if (err) {
|
|
||||||
//console.log('ERROR', err);
|
|
||||||
//response = { type: DataEvent.TAG_PAGES_NOT_RENDERED, message: err };
|
|
||||||
}
|
|
||||||
// success case, the file was saved
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
//console.log(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
loadRenderData() {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
let getSettings = settings.load(SETTINGS_FILE);
|
|
||||||
let getTags = settings.load(SETTINGS_TAG);
|
|
||||||
Promise.all([getSettings, getTags])
|
|
||||||
.then(result => {
|
|
||||||
const [settings, tags] = result;
|
|
||||||
let data = { settings: settings, tags: tags };
|
|
||||||
resolve(data);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------
|
|
||||||
// event handlers
|
|
||||||
//--------------------------
|
|
||||||
}
|
|
@ -1,184 +0,0 @@
|
|||||||
import * as DataEvent from '../../src/com/events/DataEvent';
|
|
||||||
import fs from 'fs-extra';
|
|
||||||
const _ = require('lodash');
|
|
||||||
export const SETTINGS_FILE = 'site/settings.json';
|
|
||||||
export const SETTINGS_FOLKS = 'site/folks.json';
|
|
||||||
export const SETTINGS_TAG = 'site/tags.json';
|
|
||||||
|
|
||||||
export default class Settings {
|
|
||||||
//--------------------------
|
|
||||||
// constructor
|
|
||||||
//--------------------------
|
|
||||||
constructor() {}
|
|
||||||
//--------------------------
|
|
||||||
// methods
|
|
||||||
//--------------------------
|
|
||||||
sync(req) {
|
|
||||||
let self = this;
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
self.loadConfigData()
|
|
||||||
.then(result => {
|
|
||||||
let payload = req.body;
|
|
||||||
//so payload matches loaded config
|
|
||||||
payload.global.display_limit = result.settings.global.display_limit;
|
|
||||||
payload.global.port = result.settings.global.port;
|
|
||||||
payload.global.last_backup = result.settings.global.last_backup;
|
|
||||||
let user = req.session.user;
|
|
||||||
let found = _.find(result.folks, { id: user.id });
|
|
||||||
let needToUpdate = false;
|
|
||||||
let response = [];
|
|
||||||
if (found) {
|
|
||||||
let index = found.id - 1;
|
|
||||||
if (
|
|
||||||
result.folks[index].handle != payload.member.handle ||
|
|
||||||
result.folks[index].email != payload.member.email
|
|
||||||
) {
|
|
||||||
user.handle = payload.member.handle;
|
|
||||||
user.email = payload.member.email;
|
|
||||||
result.folks[index].handle = payload.member.handle;
|
|
||||||
result.folks[index].email = payload.member.email;
|
|
||||||
fs.writeJson('site/folks.json', result.folks);
|
|
||||||
} else {
|
|
||||||
//no need to save
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let response = {
|
|
||||||
type: DataEvent.REQUEST_LAME,
|
|
||||||
message: "You're not logged in, champ"
|
|
||||||
};
|
|
||||||
reject(response);
|
|
||||||
}
|
|
||||||
if (!_.isEqual(result.settings.global, payload.global)) {
|
|
||||||
let bg = payload.global.background;
|
|
||||||
let chunks = bg.split('/');
|
|
||||||
let strip = chunks[0] + '/' + chunks[1] + chunks[2];
|
|
||||||
payload.global.background = bg.substr(strip.length + 1, bg.length);
|
|
||||||
result.settings.global = payload.global;
|
|
||||||
needToUpdate = true;
|
|
||||||
} else {
|
|
||||||
//no need to save
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_.isEqual(result.settings.email, payload.email)) {
|
|
||||||
result.settings.email = payload.email;
|
|
||||||
needToUpdate = true;
|
|
||||||
} else {
|
|
||||||
//no need to save
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needToUpdate) {
|
|
||||||
fs.writeJson('site/settings.json', result.settings)
|
|
||||||
.then(() => {
|
|
||||||
response = {
|
|
||||||
type: DataEvent.SETTINGS_UPDATED,
|
|
||||||
message: 'Settings Saved'
|
|
||||||
};
|
|
||||||
resolve(response);
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
//console.error(err);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
//no need to update
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
saveTags(tags) {
|
|
||||||
let self = this;
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
self.load(SETTINGS_TAG)
|
|
||||||
.then(config => {
|
|
||||||
if (!_.isEqual(config.tags, tags)) {
|
|
||||||
config.tags = tags;
|
|
||||||
fs.writeJson('site/tags.json', config)
|
|
||||||
.then(() => {
|
|
||||||
let response = {
|
|
||||||
type: DataEvent.SETTINGS_UPDATED,
|
|
||||||
message: 'Settings Saved'
|
|
||||||
};
|
|
||||||
resolve(response);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
let response = {
|
|
||||||
type: DataEvent.SETTINGS_NOT_UPDATED,
|
|
||||||
message: 'Settings Already Saved'
|
|
||||||
};
|
|
||||||
resolve(response);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updatePageIndex() {
|
|
||||||
fs.readJSON('site/settings.json').then(settings => {
|
|
||||||
settings.library_stats.current_index = ++settings.library_stats.current_index;
|
|
||||||
setTimeout(() => {
|
|
||||||
//TODO: Duct tape solution until something better created
|
|
||||||
fs.writeJSON('site/settings.json', settings)
|
|
||||||
.then(() => {
|
|
||||||
//console.log('ALL TO THE GOOD');
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
//.log('ERR', err);
|
|
||||||
});
|
|
||||||
}, 100);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
resetLibraryIndex(index) {
|
|
||||||
fs.readJSON('site/settings.json').then(settings => {
|
|
||||||
settings.library_stats.current_index = index;
|
|
||||||
setTimeout(() => {
|
|
||||||
//TODO: Duct tape solution until something better created
|
|
||||||
fs.writeJSON('site/settings.json', settings)
|
|
||||||
.then(() => {
|
|
||||||
//console.log('ALL TO THE GOOD');
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
//.log('ERR', err);
|
|
||||||
});
|
|
||||||
}, 100);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
load(fileToLoad) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
fs.readJSON(fileToLoad)
|
|
||||||
.then(file => {
|
|
||||||
resolve(file);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
loadConfigData() {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
let getSettings = this.load(SETTINGS_FILE);
|
|
||||||
let getFolks = this.load(SETTINGS_FOLKS);
|
|
||||||
Promise.all([getSettings, getFolks])
|
|
||||||
.then(result => {
|
|
||||||
const [settings, folks] = result;
|
|
||||||
let data = { settings: settings, folks: folks };
|
|
||||||
resolve(data);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------
|
|
||||||
// event handlers
|
|
||||||
//--------------------------
|
|
||||||
}
|
|
@ -1,243 +0,0 @@
|
|||||||
import Settings, { SETTINGS_FILE } from './Settings';
|
|
||||||
import Render from './Render';
|
|
||||||
import StringUtils from '../../src/com/utils/StringUtils';
|
|
||||||
import _ from 'lodash';
|
|
||||||
import Auth from '../data/Auth';
|
|
||||||
const settings = new Settings();
|
|
||||||
const render = new Render();
|
|
||||||
const stringUtils = new StringUtils();
|
|
||||||
const moment = require('moment');
|
|
||||||
const fs = require('fs-extra');
|
|
||||||
const AdmZip = require('adm-zip');
|
|
||||||
const auth = new Auth();
|
|
||||||
|
|
||||||
export default class Utils {
|
|
||||||
constructor() {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves single page or pages
|
|
||||||
* @parameter pages: payload of pages
|
|
||||||
*/
|
|
||||||
organizeTags(pages) {
|
|
||||||
let tags = [];
|
|
||||||
for (let index = 0; index < pages.length; index++) {
|
|
||||||
const page = pages[index];
|
|
||||||
let temp = [];
|
|
||||||
temp = page.metadata.tags.split(',');
|
|
||||||
for (let i = 0; i < temp.length; i++) {
|
|
||||||
let label = temp[i].trim();
|
|
||||||
if (!_.find(tags, { tag_name: label })) {
|
|
||||||
tags.push({
|
|
||||||
tag_name: label,
|
|
||||||
slug: stringUtils.cleanString(label),
|
|
||||||
count: 1
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
_.find(tags, { tag_name: label }).count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tags = _.orderBy(tags, ['tag_name'], ['asc']);
|
|
||||||
|
|
||||||
settings.saveTags(tags).then(() => {
|
|
||||||
render
|
|
||||||
.publishTags(pages)
|
|
||||||
.then(() => {
|
|
||||||
//console.log(response);
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
//console.log(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
organizeArchive(pages) {
|
|
||||||
let years = [];
|
|
||||||
let archive = [];
|
|
||||||
for (let index = 0; index < pages.length; index++) {
|
|
||||||
let page = pages[index].metadata;
|
|
||||||
if (page.layout !== 'index') {
|
|
||||||
let year = moment(page.created).format('YYYY');
|
|
||||||
if (!_.find(years, { year: year })) {
|
|
||||||
years.push({ year: year, count: 1 });
|
|
||||||
} else {
|
|
||||||
_.find(years, { year: year }).count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
years.sort((a, b) => parseFloat(b.year) - parseFloat(a.year));
|
|
||||||
for (let index = 0; index < years.length; index++) {
|
|
||||||
let item = years[index];
|
|
||||||
let sorted = [];
|
|
||||||
let filtered = _.filter(pages, page => {
|
|
||||||
return moment(page.metadata.created).format('YYYY') === item.year;
|
|
||||||
});
|
|
||||||
for (let index = 0; index < filtered.length; index++) {
|
|
||||||
let obj = filtered[index].metadata;
|
|
||||||
let month = moment(obj.created).format('MM');
|
|
||||||
if (!_.find(sorted, { month: month })) {
|
|
||||||
sorted.push({
|
|
||||||
month: month,
|
|
||||||
full_month: moment(obj.created).format('MMMM'),
|
|
||||||
count: 1,
|
|
||||||
pages: _.filter(pages, page => {
|
|
||||||
return (
|
|
||||||
moment(page.metadata.created).format('YYYY') === item.year &&
|
|
||||||
moment(page.metadata.created).format('MM') === month &&
|
|
||||||
page.metadata.deleted === false &&
|
|
||||||
page.metadata.published === true &&
|
|
||||||
page.metadata.layout !== 'index'
|
|
||||||
);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
_.find(sorted, { month: month }).count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
archive.push({ year: item.year, year_data: sorted });
|
|
||||||
}
|
|
||||||
render.publishArchive(archive);
|
|
||||||
}
|
|
||||||
moveAssets() {
|
|
||||||
settings
|
|
||||||
.load(SETTINGS_FILE)
|
|
||||||
.then(settings => {
|
|
||||||
//move css assets to public directory
|
|
||||||
fs.copy(
|
|
||||||
'content/themes/' + settings.global.theme + '/assets/css',
|
|
||||||
'public/assets/css',
|
|
||||||
function (err) {
|
|
||||||
if (err) {
|
|
||||||
//console.log('An error occured while copying the folder.', err);
|
|
||||||
//return console.error(err);
|
|
||||||
}
|
|
||||||
//console.log('Copy completed!');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
//move js assets to public directory
|
|
||||||
fs.copy(
|
|
||||||
'content/themes/' + settings.global.theme + '/assets/scripts',
|
|
||||||
'public/assets/scripts',
|
|
||||||
function (err) {
|
|
||||||
if (err) {
|
|
||||||
//console.log('An error occured while copying the folder.', err);
|
|
||||||
//return console.error(err);
|
|
||||||
}
|
|
||||||
//console.log('Copy completed!');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
//TODO: Add method to move new logo to public from theme upload
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
//console.log('ERROR', err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
createBackup() {
|
|
||||||
//let self = this;
|
|
||||||
var response;
|
|
||||||
return new Promise(resolve => {
|
|
||||||
var zip = new AdmZip();
|
|
||||||
zip.addLocalFolder('public/assets/images/blog', 'public/assets/images/blog');
|
|
||||||
zip.addLocalFolder('content/pages', 'content/pages/');
|
|
||||||
zip.addLocalFile('site/folks.json', 'settings/');
|
|
||||||
zip.addLocalFile('site/settings.json', 'settings/');
|
|
||||||
zip.addLocalFile('site/tags.json', 'settings/');
|
|
||||||
zip.writeZip('content/backup.zip');
|
|
||||||
fs.readJSON('site/settings.json').then(settings => {
|
|
||||||
settings.global.last_backup = moment(Date.now()).format();
|
|
||||||
fs.writeJSON('site/settings.json', settings);
|
|
||||||
});
|
|
||||||
|
|
||||||
response = {
|
|
||||||
type: '',
|
|
||||||
message: 'BACKUP CREATED'
|
|
||||||
};
|
|
||||||
|
|
||||||
resolve(response);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
restoreBackup(file) {
|
|
||||||
var response;
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
var zip = new AdmZip(file.buffer);
|
|
||||||
try {
|
|
||||||
zip.extractEntryTo('settings/settings.json', 'site', false, true);
|
|
||||||
zip.extractEntryTo('settings/folks.json', 'site', false, true);
|
|
||||||
zip.extractEntryTo('settings/tags.json', 'site', false, true);
|
|
||||||
zip.getEntries().forEach(function (entry) {
|
|
||||||
var entryName = entry.entryName;
|
|
||||||
var list = entryName.split('/');
|
|
||||||
if (list[0] === 'public') {
|
|
||||||
if (list[6]) {
|
|
||||||
zip.extractEntryTo(
|
|
||||||
entryName,
|
|
||||||
'public/assets/images/blog/' + list[4] + '/' + list[5],
|
|
||||||
false,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (list[0] === 'content') {
|
|
||||||
if (list[4]) {
|
|
||||||
zip.extractEntryTo(
|
|
||||||
entryName,
|
|
||||||
'content/pages/' + list[2] + '/' + list[3],
|
|
||||||
false,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
zip.extractEntryTo('content/pages/index.md', 'content/pages', false, true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
resolve();
|
|
||||||
} catch (error) {
|
|
||||||
response = {
|
|
||||||
type: error,
|
|
||||||
message: 'ERROR READING BACKUP'
|
|
||||||
};
|
|
||||||
reject(response);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
verifyBackup(file, body) {
|
|
||||||
var response;
|
|
||||||
var zip = new AdmZip(file.buffer);
|
|
||||||
var credentials = { handle: body.restore_member_handle, pass: body.restore_member_pass };
|
|
||||||
var self = this;
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
try {
|
|
||||||
let folks = JSON.parse(zip.readAsText('settings/folks.json'));
|
|
||||||
auth.verifyCredentials(folks, credentials)
|
|
||||||
.then(() => {
|
|
||||||
//resolve(r);
|
|
||||||
self.restoreBackup(file)
|
|
||||||
.then(() => {
|
|
||||||
response = {
|
|
||||||
type: '',
|
|
||||||
message: 'RESTORE COMPLETE'
|
|
||||||
};
|
|
||||||
resolve(response);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
response = {
|
|
||||||
type: err,
|
|
||||||
message: 'ERROR RESTORING BACKUP'
|
|
||||||
};
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
response = {
|
|
||||||
type: 'error',
|
|
||||||
message: 'ERROR READING BACKUP FILE'
|
|
||||||
};
|
|
||||||
|
|
||||||
reject(response);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
import Book from '../../data/Book';
|
|
||||||
import Settings, { SETTINGS_FILE } from '../../data/Settings';
|
|
||||||
const express = require('express');
|
|
||||||
const moment = require('moment');
|
|
||||||
const router = express.Router();
|
|
||||||
const book = new Book();
|
|
||||||
const settings = new Settings();
|
|
||||||
const indexLimit = 5;
|
|
||||||
|
|
||||||
//--------------------------
|
|
||||||
// Index
|
|
||||||
//--------------------------
|
|
||||||
router.get('/', function (req, res) {
|
|
||||||
settings
|
|
||||||
.load(SETTINGS_FILE)
|
|
||||||
.then(config => {
|
|
||||||
book.getPage().then(result => {
|
|
||||||
result.sort((a, b) => parseFloat(b.metadata.id) - parseFloat(a.metadata.id));
|
|
||||||
let indexPages = [];
|
|
||||||
let indexCount = 0;
|
|
||||||
result.forEach(page => {
|
|
||||||
if (
|
|
||||||
typeof page.metadata.deleted === 'undefined' ||
|
|
||||||
page.metadata.deleted === false
|
|
||||||
) {
|
|
||||||
if (indexCount === indexLimit) return;
|
|
||||||
indexPages.push({
|
|
||||||
page: page,
|
|
||||||
date: moment(page.metadata.created).fromNow()
|
|
||||||
});
|
|
||||||
++indexCount;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let pageData = [];
|
|
||||||
if (req.session.user) {
|
|
||||||
pageData = { title: config.global.title, status: true, pages: indexPages };
|
|
||||||
} else {
|
|
||||||
pageData = { title: config.global.title, status: false, pages: indexPages };
|
|
||||||
}
|
|
||||||
|
|
||||||
res.render('index', pageData);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
if (err.code === 'ENOENT') {
|
|
||||||
let setupData = { title: 'Fipamo Set up' };
|
|
||||||
res.render('init', setupData);
|
|
||||||
} else {
|
|
||||||
res.render('error', { error: err });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//--------------------------
|
|
||||||
// Logout
|
|
||||||
//--------------------------
|
|
||||||
router.get('/logout', (req, res) => {
|
|
||||||
//req.logout();
|
|
||||||
req.session.destroy();
|
|
||||||
res.redirect('/@/dashboard');
|
|
||||||
});
|
|
||||||
module.exports = router;
|
|
@ -1,79 +0,0 @@
|
|||||||
import Book from '../../../brain/data/Book';
|
|
||||||
import Settings, { SETTINGS_FILE } from '../../data/Settings';
|
|
||||||
const express = require('express');
|
|
||||||
const router = express.Router();
|
|
||||||
const _ = require('lodash');
|
|
||||||
//const settings = require('../../../site/settings.json');
|
|
||||||
const book = new Book();
|
|
||||||
const settings = new Settings();
|
|
||||||
//--------------------------
|
|
||||||
// SETTINGS
|
|
||||||
//--------------------------
|
|
||||||
router.get('/', function (req, res) {
|
|
||||||
if (req.session.user) {
|
|
||||||
settings
|
|
||||||
.load(SETTINGS_FILE)
|
|
||||||
.then(settings => {
|
|
||||||
var nav = [];
|
|
||||||
book.getPage()
|
|
||||||
.then(pages => {
|
|
||||||
if (settings.menu.length === 0) {
|
|
||||||
for (let index = 0; index < pages.length; index++) {
|
|
||||||
let item = pages[index].metadata;
|
|
||||||
if (item.menu) {
|
|
||||||
nav.push({
|
|
||||||
id: item.id,
|
|
||||||
uuid: item.uuid,
|
|
||||||
title: item.title,
|
|
||||||
slug: item.slug,
|
|
||||||
path: item.path
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let newpages = [];
|
|
||||||
nav = settings.menu;
|
|
||||||
for (let index = 0; index < pages.length; index++) {
|
|
||||||
let item = pages[index].metadata;
|
|
||||||
if (item.menu)
|
|
||||||
newpages.push({
|
|
||||||
id: item.id,
|
|
||||||
uuid: item.uuid,
|
|
||||||
title: item.title,
|
|
||||||
slug: item.slug,
|
|
||||||
path: item.path
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for (let i = 0; i < newpages.length; i++) {
|
|
||||||
if (_.find(nav, { uuid: newpages[i].uuid })) {
|
|
||||||
//menu item already exists
|
|
||||||
} else {
|
|
||||||
nav.push({
|
|
||||||
id: newpages[i].id,
|
|
||||||
uuid: newpages[i].uuid,
|
|
||||||
title: newpages[i].title,
|
|
||||||
slug: newpages[i].slug,
|
|
||||||
path: newpages[i].path
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res.render('navigation', {
|
|
||||||
menu: nav,
|
|
||||||
welcome: 'Edit Navigation',
|
|
||||||
status: true,
|
|
||||||
title: 'Dashboard | Navigation'
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.render('error', { error: err });
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.render('error', { error: err });
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
res.redirect('/@/dashboard');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
module.exports = router;
|
|
@ -1,172 +0,0 @@
|
|||||||
import Book from '../../data/Book';
|
|
||||||
const express = require('express');
|
|
||||||
const router = express.Router();
|
|
||||||
const moment = require('moment');
|
|
||||||
const book = new Book();
|
|
||||||
const uuidv4 = require('uuid/v4');
|
|
||||||
const fs = require('fs-extra');
|
|
||||||
//--------------------------
|
|
||||||
// POSTS
|
|
||||||
//--------------------------
|
|
||||||
router.get('/list/:filter?/:page?', function (req, res) {
|
|
||||||
var pageNum = req.params.page;
|
|
||||||
var filter = req.params.filter;
|
|
||||||
if (pageNum == '' || pageNum == null) pageNum = 1;
|
|
||||||
if (filter == '' || filter == null) filter = 'all';
|
|
||||||
if (req.session.user) {
|
|
||||||
book.getPage()
|
|
||||||
.then(pages => {
|
|
||||||
pages.sort((a, b) => parseFloat(b.metadata.id) - parseFloat(a.metadata.id));
|
|
||||||
let all = [];
|
|
||||||
let deleted = [];
|
|
||||||
let published = [];
|
|
||||||
let menu = [];
|
|
||||||
let featured = [];
|
|
||||||
for (let index = 0; index < pages.length; index++) {
|
|
||||||
let item = pages[index].metadata;
|
|
||||||
if (typeof item.deleted === 'undefined' || item.deleted === false) {
|
|
||||||
all.push({
|
|
||||||
page: pages[index].metadata,
|
|
||||||
date: moment(pages[index].metadata.created).fromNow()
|
|
||||||
});
|
|
||||||
if (item.published == true)
|
|
||||||
published.push({
|
|
||||||
page: pages[index].metadata,
|
|
||||||
date: moment(pages[index].metadata.created).fromNow()
|
|
||||||
});
|
|
||||||
if (item.menu == true)
|
|
||||||
menu.push({
|
|
||||||
page: pages[index].metadata,
|
|
||||||
date: moment(pages[index].metadata.created).fromNow()
|
|
||||||
});
|
|
||||||
if (item.featured == true)
|
|
||||||
featured.push({
|
|
||||||
page: pages[index].metadata,
|
|
||||||
date: moment(pages[index].metadata.created).fromNow()
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
deleted.push({
|
|
||||||
page: pages[index].metadata,
|
|
||||||
date: moment(pages[index].metadata.created).fromNow()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var filtered;
|
|
||||||
switch (filter) {
|
|
||||||
case 'published':
|
|
||||||
filtered = published;
|
|
||||||
break;
|
|
||||||
case 'deleted':
|
|
||||||
filtered = deleted;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
filtered = all;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
var count = Math.ceil(filtered.length / 6);
|
|
||||||
var pageItems = [];
|
|
||||||
var itemLimit = 6;
|
|
||||||
var rangeStart = pageNum * itemLimit - itemLimit;
|
|
||||||
for (var i = 0; i < itemLimit; i++) {
|
|
||||||
try {
|
|
||||||
if (filtered[i + rangeStart].page.id != null) {
|
|
||||||
pageItems.push({
|
|
||||||
page: filtered[i + rangeStart].page,
|
|
||||||
date: moment(filtered[i + rangeStart].page.created).fromNow()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
//console.log("NO POST", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res.render('book-index', {
|
|
||||||
title: 'Dashbord | Book',
|
|
||||||
welcome: 'Your pages',
|
|
||||||
items: pageItems,
|
|
||||||
page_info: {
|
|
||||||
all: all.length,
|
|
||||||
deleted: deleted.length,
|
|
||||||
published: published.length,
|
|
||||||
pages: pages.length,
|
|
||||||
featured: featured.length
|
|
||||||
},
|
|
||||||
page_index: pageNum,
|
|
||||||
page_count: count,
|
|
||||||
postFilter: filter,
|
|
||||||
status: true
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
//console.log(value);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.render('error', { error: err });
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
res.redirect('/@/dashboard');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//--------------------------
|
|
||||||
// BLOG POST ADD DISPLAY
|
|
||||||
//--------------------------
|
|
||||||
router.get('/add/new', function (req, res) {
|
|
||||||
if (req.session.user) {
|
|
||||||
//need to grab a few copy of settings for the lastest index
|
|
||||||
fs.readJSON('site/settings.json')
|
|
||||||
.then(config => {
|
|
||||||
res.render('page-edit', {
|
|
||||||
id: config.library_stats.current_index,
|
|
||||||
uuid: uuidv4(),
|
|
||||||
title: 'Add New Page',
|
|
||||||
user_status: true,
|
|
||||||
welcome: 'Add New Page',
|
|
||||||
date: moment(Date.now()).format('YYYY MMM DD'),
|
|
||||||
page: [],
|
|
||||||
rawDate: moment(Date.now()).format(),
|
|
||||||
status: ['false', 'false', 'false'],
|
|
||||||
edit: false
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.render('error', { error: err });
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
res.redirect('/@/dashboard');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
//--------------------------
|
|
||||||
// BLOG POST EDIT DISPLAY
|
|
||||||
//--------------------------
|
|
||||||
router.get('/edit/:id', function (req, res) {
|
|
||||||
var id = req.params.id;
|
|
||||||
if (req.session.user) {
|
|
||||||
book.getPage(id)
|
|
||||||
.then(page => {
|
|
||||||
res.render('page-edit', {
|
|
||||||
id: page.metadata.id,
|
|
||||||
uuid: page.metadata.uuid,
|
|
||||||
title: 'Edit Page',
|
|
||||||
welcome: 'Edit Page',
|
|
||||||
page: page.metadata,
|
|
||||||
date: moment(page.metadata.created).format('YYYY MMM DD HH:mm'),
|
|
||||||
layout: page.metadata.layout,
|
|
||||||
rawDate: page.metadata.created,
|
|
||||||
content: page.content,
|
|
||||||
feature: page.metadata.feature,
|
|
||||||
status: [
|
|
||||||
String(page.metadata.menu),
|
|
||||||
String(page.metadata.featured),
|
|
||||||
String(page.metadata.published)
|
|
||||||
],
|
|
||||||
edit: true
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.render('error', { error: err });
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
res.redirect('/@/dashboard');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
module.exports = router;
|
|
@ -1,94 +0,0 @@
|
|||||||
import Settings, { SETTINGS_FILE } from '../../data/Settings';
|
|
||||||
const express = require('express');
|
|
||||||
const router = express.Router();
|
|
||||||
const FileHound = require('filehound');
|
|
||||||
const fs = require('fs-extra');
|
|
||||||
const settings = new Settings();
|
|
||||||
const moment = require('moment');
|
|
||||||
var config = [];
|
|
||||||
//--------------------------
|
|
||||||
// SETTINGS
|
|
||||||
//--------------------------
|
|
||||||
router.get('/', function (req, res) {
|
|
||||||
settings
|
|
||||||
.load(SETTINGS_FILE)
|
|
||||||
.then(obj => {
|
|
||||||
config = obj;
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.render('error', { error: err });
|
|
||||||
});
|
|
||||||
loadThemes()
|
|
||||||
.then(themes => {
|
|
||||||
if (req.session.user) {
|
|
||||||
let memberInfo = [];
|
|
||||||
let user = req.session.user;
|
|
||||||
memberInfo.push({
|
|
||||||
handle: user.handle,
|
|
||||||
email: user.email,
|
|
||||||
avi: user.avi
|
|
||||||
});
|
|
||||||
themes.sort(function (a, b) {
|
|
||||||
var textA = a.theme.name.toUpperCase();
|
|
||||||
var textB = b.theme.name.toUpperCase();
|
|
||||||
return textA < textB ? -1 : textA > textB ? 1 : 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
res.render('settings', {
|
|
||||||
title: 'Dashboard | Settings',
|
|
||||||
welcome: 'Your Settings',
|
|
||||||
status: true,
|
|
||||||
themes: themes,
|
|
||||||
settings: config,
|
|
||||||
last_backup: moment(config.global.last_backup).fromNow(),
|
|
||||||
member: memberInfo[0]
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
res.redirect('/@/dashboard');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
res.render('error', { error: err });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
module.exports = router;
|
|
||||||
|
|
||||||
function loadThemes() {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
settings
|
|
||||||
.load(SETTINGS_FILE)
|
|
||||||
.then(settings => {
|
|
||||||
FileHound.create()
|
|
||||||
.paths('content/themes')
|
|
||||||
.ext('json')
|
|
||||||
.find()
|
|
||||||
.then(files => {
|
|
||||||
let themes = [];
|
|
||||||
for (let index = 0; index < files.length; index++) {
|
|
||||||
fs.readJSON(files[index], (err, theme) => {
|
|
||||||
if (theme.name == settings.global.theme) {
|
|
||||||
themes.push({
|
|
||||||
theme: theme,
|
|
||||||
current: 'true'
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
themes.push({
|
|
||||||
theme: theme,
|
|
||||||
current: 'false'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setTimeout(() => {
|
|
||||||
resolve(themes);
|
|
||||||
}, 200);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
extends frame
|
|
||||||
block main-content
|
|
||||||
#post-index
|
|
||||||
#post-index-wrapper
|
|
||||||
#post-index-menu
|
|
||||||
- if(postFilter=='all')
|
|
||||||
a.current-filter(href="/@/dashboard/page/list/all")= "All Pages ("+page_info.all+")"
|
|
||||||
- else
|
|
||||||
a(href="/@/dashboard/page/list/all")= "All Pages ("+page_info.all+")"
|
|
||||||
| .
|
|
||||||
- if(postFilter=='published')
|
|
||||||
a.current-filter(href="/@/dashboard/page/list/published")= "Published ("+page_info.published+")"
|
|
||||||
- else
|
|
||||||
a(href="/@/dashboard/page/list/published")= "Published ("+page_info.published+")"
|
|
||||||
| .
|
|
||||||
- if(postFilter=='deleted')
|
|
||||||
a.current-filter(href="/@/dashboard/page/list/deleted")= "Deleted ("+page_info.deleted+")"
|
|
||||||
- else
|
|
||||||
a(href="/@/dashboard/page/list/deleted")= "Deleted ("+page_info.deleted+")"
|
|
||||||
|
|
||||||
a.add-new-post(href="/@/dashboard/page/add/new") +
|
|
||||||
label Create New Post
|
|
||||||
#posts-list
|
|
||||||
- var index = 0;
|
|
||||||
- for ( index; index < items.length; index++)
|
|
||||||
a.page-link(href="/@/dashboard/page/edit/"+items[index].page.uuid id=items[index].uuid)
|
|
||||||
div.page-bg(style="background: #fc6399 url("+items[index].page.feature+") no-repeat center center / cover")
|
|
||||||
#meta
|
|
||||||
- var menu = String(items[index].page.menu)
|
|
||||||
- var published = String(items[index].page.published)
|
|
||||||
- var featured = String(items[index].page.featured)
|
|
||||||
span= items[index].date
|
|
||||||
label= items[index].page.title
|
|
||||||
br
|
|
||||||
#options
|
|
||||||
span.meta-options(data-active=menu) MENU ITEM
|
|
||||||
span.meta-options(data-active=published) PUBLISHED
|
|
||||||
span.meta-options(data-active=featured) FEATURED
|
|
||||||
|
|
||||||
|
|
||||||
- var next = parseInt(page_index, 10) + 1
|
|
||||||
- var prev = parseInt(page_index, 10) - 1
|
|
||||||
- if(next > page_count) next = 1
|
|
||||||
- if(prev <= 0) prev = page_count
|
|
||||||
- if(page_count > 1)
|
|
||||||
br
|
|
||||||
.paginate
|
|
||||||
a.page-btns(href="/@/dashboard/page/list/"+postFilter+"/"+prev)
|
|
||||||
svg(viewBox="0 0 20 20" class="icons")
|
|
||||||
use(xlink:href='/assets/images/global/sprite.svg#entypo-chevron-left')
|
|
||||||
|
|
||||||
span.count= "PAGE "+page_index+" OF "+page_count
|
|
||||||
|
|
||||||
a.page-btns(href="/@/dashboard/page/list/"+postFilter+"/"+next)
|
|
||||||
svg(viewBox="0 0 20 20" class="icons")
|
|
||||||
use(xlink:href='/assets/images/global/sprite.svg#entypo-chevron-right')
|
|
||||||
|
|
@ -1,94 +0,0 @@
|
|||||||
doctype strict
|
|
||||||
head
|
|
||||||
meta(http-equiv='Content-Type' content='text/html; charset=utf-8')
|
|
||||||
meta(name='viewport' content='width=device-width, initial-scale=1.0')
|
|
||||||
title #{title}
|
|
||||||
style(type='text/css').
|
|
||||||
/* reset */
|
|
||||||
#outlook a {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
/* Force Outlook to provide a "view in browser" menu link. */
|
|
||||||
.ExternalClass {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
/* Force Hotmail to display emails at full width */
|
|
||||||
.ExternalClass,
|
|
||||||
.ExternalClass p,
|
|
||||||
.ExternalClass span,
|
|
||||||
.ExternalClass font,
|
|
||||||
.ExternalClass td,
|
|
||||||
.ExternalClass div {
|
|
||||||
line-height: 100%;
|
|
||||||
}
|
|
||||||
/* Forces Hotmail to display normal line spacing. More on that: http://www.emailonacid.com/forum/viewthread/43/ */
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
font-size: 0px;
|
|
||||||
line-height: 0px;
|
|
||||||
}
|
|
||||||
/* squash Exact Target injected paragraphs */
|
|
||||||
table td {
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
/* Outlook 07, 10 padding issue fix */
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
mso-table-lspace: 0pt;
|
|
||||||
mso-table-rspace: 0pt;
|
|
||||||
}
|
|
||||||
/* remove spacing around Outlook 07, 10 tables */
|
|
||||||
/* bring inline */
|
|
||||||
img {
|
|
||||||
display: block;
|
|
||||||
outline: none;
|
|
||||||
text-decoration: none;
|
|
||||||
-ms-interpolation-mode: bicubic;
|
|
||||||
}
|
|
||||||
a img {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: #000001;
|
|
||||||
}
|
|
||||||
/* text link */
|
|
||||||
a.phone {
|
|
||||||
text-decoration: none;
|
|
||||||
color: #000001 !important;
|
|
||||||
pointer-events: auto;
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
/* phone link, use as wrapper on phone numbers */
|
|
||||||
span {
|
|
||||||
font-size: 13px;
|
|
||||||
line-height: 17px;
|
|
||||||
font-family: monospace;
|
|
||||||
color: #000001;
|
|
||||||
}
|
|
||||||
//if gte mso 9
|
|
||||||
style.
|
|
||||||
/* Target Outlook 2007 and 2010 */
|
|
||||||
// body wrapper
|
|
||||||
table(cellpadding='0' cellspacing='0' border='0' style='margin:0; padding:0; width:100%; line-height: 100% !important;')
|
|
||||||
tr
|
|
||||||
td(valign='top')
|
|
||||||
// edge wrapper
|
|
||||||
table(cellpadding='0' cellspacing='0' border='0' align='center' width='600' style='background: #374857;')
|
|
||||||
tr
|
|
||||||
td(valign='top' style='vertical-align: top;')
|
|
||||||
// /////////////////////////////////////////////////////
|
|
||||||
table(cellpadding='0' cellspacing='0' border='0' align='center' style='width:100%')
|
|
||||||
tr
|
|
||||||
td(valign='top' style='vertical-align: top;text-align: center; padding: 10px')
|
|
||||||
span(style='font-family: Arial,Helvetica Neue,Helvetica,sans-serif; color:#f5ab35; font-size:20px; font-weight: bold;')
|
|
||||||
| #{header}
|
|
||||||
tr
|
|
||||||
td(valign='top' style='vertical-align: top; background: #161d23; padding:10px;')
|
|
||||||
span(style='font-family: Arial,Helvetica Neue,Helvetica,sans-serif; color:#cecece; font-size:16px;')
|
|
||||||
| #{content}
|
|
||||||
tr
|
|
||||||
td(valign='top' style='vertical-align: top; padding: 10px;')
|
|
||||||
span(style='font-family: Arial,Helvetica Neue,Helvetica,sans-serif; color:#b2cce5; font-size:12px;')
|
|
||||||
| #{footer}
|
|
@ -1,8 +0,0 @@
|
|||||||
extends frame
|
|
||||||
block main-content
|
|
||||||
#error-index
|
|
||||||
br
|
|
||||||
label#message Ok, so this is... awkward
|
|
||||||
br
|
|
||||||
label#error= error
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
|||||||
doctype html
|
|
||||||
html(xmlns='http://www.w3.org/1999/xhtml', lang='en', xml:lang="en")
|
|
||||||
head
|
|
||||||
title= title
|
|
||||||
meta(content="text/html;charset=utf-8", http-equiv="Content-Type")
|
|
||||||
meta(meta content="utf-8", http-equiv="encoding")
|
|
||||||
meta(name='viewport', content='width=device-width, initial-scale=1.0')
|
|
||||||
meta(name="keywords" content="")
|
|
||||||
meta(name="description" content="")
|
|
||||||
meta(http-equiv="content-type", content="text/html; charset=utf-8")
|
|
||||||
//meta(property="og:image" content="https://thetwelfth.house/base-assets/images/current.png")
|
|
||||||
//meta(name="twitter:image" content="https://thetwelfth.house/base-assets/images/current.png")
|
|
||||||
link(rel='stylesheet', href="/assets/css/dash.css", type='text/css')
|
|
||||||
body
|
|
||||||
#notifications.notifications
|
|
||||||
#notifyMessage.notifyMessage
|
|
||||||
.notify-icon#notify-good
|
|
||||||
svg(viewBox="0 0 20 20" class="icons")
|
|
||||||
use#submit-update(xlink:href='/assets/images/global/sprite.svg#entypo-emoji-flirt')
|
|
||||||
.notify-icon#notify-lame
|
|
||||||
svg(viewBox="0 0 20 20" class="icons")
|
|
||||||
use#submit-update(xlink:href='/assets/images/global/sprite.svg#entypo-emoji-sad')
|
|
||||||
.notify-icon#notify-working
|
|
||||||
#notify-working-box
|
|
||||||
svg(viewBox="0 0 20 20" class="icons")
|
|
||||||
use#submit-update(xlink:href='/assets/images/global/sprite.svg#entypo-cog')
|
|
||||||
p#message-text This is a message
|
|
||||||
.main-container#main-content
|
|
||||||
section#dash-index-content
|
|
||||||
header#header
|
|
||||||
#wrapper
|
|
||||||
#left
|
|
||||||
a(href="/@/dashboard")
|
|
||||||
img#the-logo(src="/assets/images/global/the-logo.svg")
|
|
||||||
#right
|
|
||||||
-if(status)
|
|
||||||
include partials/dash-nav
|
|
||||||
block main-content
|
|
||||||
//script(src='/assets/scripts/dashkit.min.js' type="text/javascript")
|
|
||||||
script(src='/assets/scripts/dash.min.js' type="text/javascript")
|
|
||||||
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
|||||||
extends frame
|
|
||||||
block main-content
|
|
||||||
#dash-index
|
|
||||||
#dash-index-wrapper
|
|
||||||
-if(!status)
|
|
||||||
include partials/login
|
|
||||||
-else
|
|
||||||
include partials/front
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
|||||||
extends frame
|
|
||||||
block main-content
|
|
||||||
#dash-index
|
|
||||||
#dash-index-wrapper
|
|
||||||
.dash-init#dash-init
|
|
||||||
br
|
|
||||||
form#init-form
|
|
||||||
h1 What up.
|
|
||||||
p Just fill these in and it'll get you started.
|
|
||||||
label What's your handle?
|
|
||||||
br
|
|
||||||
input.large(type='text', name='new_member_handle' id='new_member_handle', placeholder="What\'s your handle?")
|
|
||||||
br
|
|
||||||
label Let's get that email
|
|
||||||
br
|
|
||||||
input.large(type='text', name='new_member_email' id='new_member_email', placeholder="Email Please")
|
|
||||||
br
|
|
||||||
label Let's get a password
|
|
||||||
br
|
|
||||||
input.large(type='password', name='new_member_pass' id='new_member_pass', placeholder="Password Please")
|
|
||||||
br
|
|
||||||
label And let's confirm that password
|
|
||||||
br
|
|
||||||
input.large(type='password', name='new_member_pass2' id='new_member_pass2', placeholder="Password Confirm")
|
|
||||||
br
|
|
||||||
label And finally, a title
|
|
||||||
br
|
|
||||||
input.large(type='text', name='new_member_title' id='new_member_title', placeholder="Site Title Please")
|
|
||||||
br
|
|
||||||
button#init-blog(data-action='blog-init' type='submit') SET IT UP
|
|
||||||
.option
|
|
||||||
button.init-option#init-switch-restore OR RESTORE FROM BACKUP
|
|
||||||
|
|
||||||
.dash-restore#dash-restore
|
|
||||||
form#init-restore
|
|
||||||
h1 Restore backup.
|
|
||||||
p Let's verify your backup
|
|
||||||
label What's your handle?
|
|
||||||
br
|
|
||||||
input.large(type='text', name='restore_member_handle' id='restore_member_handle', placeholder="What\'s your handle?")
|
|
||||||
br
|
|
||||||
label Let's get a password
|
|
||||||
br
|
|
||||||
input.large(type='password', name='restore_member_pass' id='restore_member_pass', placeholder="Password Please")
|
|
||||||
br
|
|
||||||
label Backup File
|
|
||||||
br
|
|
||||||
input(id="backup-upload" type="file" name="backup-upload")
|
|
||||||
br
|
|
||||||
button#blog-restore(data-action='blog-restore' type='submit') RESTORE
|
|
||||||
.option
|
|
||||||
button.init-option#init-switch-fresh OR INSTALL FRESH SITE
|
|
@ -1,16 +0,0 @@
|
|||||||
extends frame
|
|
||||||
block main-content
|
|
||||||
#nav-index
|
|
||||||
#nav-index-wrapper
|
|
||||||
#nav-pages
|
|
||||||
- var index = 0;
|
|
||||||
- for ( index; index < menu.length; index++)
|
|
||||||
.nav-item(id=menu[index].id, data-slug=menu[index].slug, data-uuid=menu[index].uuid, data-path=menu[index].path)
|
|
||||||
svg#item-arrows(viewBox="0 0 20 20" class="icons")
|
|
||||||
use(xlink:href='/assets/images/global/sprite.svg#entypo-select-arrows')
|
|
||||||
label
|
|
||||||
= menu[index].title
|
|
||||||
#nav-btns
|
|
||||||
button.nav-btn#edit-item(data-id=menu[index].uuid) EDIT
|
|
||||||
button.nav-btn#remove-item(data-id=menu[index].id, data-uuid=menu[index].uuid) REMOVE
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
|||||||
extends frame
|
|
||||||
block main-content
|
|
||||||
#post-edit-index(data-index=id data-uuid=uuid data-layout=layout data-slug=page.slug)
|
|
||||||
#post-edit-index-wrapper
|
|
||||||
//h2 EDIT
|
|
||||||
=post_title
|
|
||||||
#post-feature
|
|
||||||
//label FEATURE IMAGE
|
|
||||||
if(post_feature == 'null')
|
|
||||||
#featured-image-drop
|
|
||||||
| DRAG AND DROP IMAGE OR
|
|
||||||
label(for="featured-image-upload") CLICK TO CHOOSE
|
|
||||||
|
|
||||||
else
|
|
||||||
#featured-new-image-btn
|
|
||||||
button#new-feature-upload
|
|
||||||
svg#new-feature-upload(viewBox="0 0 20 20" class="icons")
|
|
||||||
use(xlink:href='/assets/images/global/sprite.svg#entypo-image-inverted')
|
|
||||||
#featured-image-drop
|
|
||||||
img#featured-image(src=page.feature)
|
|
||||||
#post-header
|
|
||||||
#post-header-wrapper.columns
|
|
||||||
#post-title.column
|
|
||||||
label TITLE
|
|
||||||
textarea(id="post_title" type='text', name='post_title' class='post-edit', placeholder='title', required, autofocus)
|
|
||||||
=page.title
|
|
||||||
|
|
||||||
label CREATED
|
|
||||||
br
|
|
||||||
span(id="post-date" type="text" value=date data-raw=rawDate)= date
|
|
||||||
//#calendar-icon
|
|
||||||
svg(viewBox="0 0 20 20" class="icons")
|
|
||||||
use(xlink:href='/assets/images/global/sprite.svg#entypo-calendar')
|
|
||||||
//input(id="post-date" type="text" value=date data-raw=rawDate)
|
|
||||||
|
|
||||||
#post-meta.column
|
|
||||||
label TAGS
|
|
||||||
textarea(id='post_tags' type='text', name='post_tags' class='form-control', placeholder='tags [comma seperated]', autofocus)
|
|
||||||
=page.tags
|
|
||||||
// file inputs for image uploads
|
|
||||||
label OPTIONS
|
|
||||||
br
|
|
||||||
include partials/options
|
|
||||||
input(id="featured-image-upload" type="file" name="featured-image-upload")
|
|
||||||
input(id="post-image-upload" type="file" name="post-image-upload")
|
|
||||||
#edit-post
|
|
||||||
include partials/editor
|
|
||||||
#edit-post-wrapper
|
|
||||||
pre
|
|
||||||
code#edit-post-text(contenteditable="true") !{content}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
|||||||
#dash-menu
|
|
||||||
a#settings(href="/@/dashboard/settings") Settings
|
|
||||||
| .
|
|
||||||
a#navigation(href="/@/dashboard/navigation") Navigation
|
|
||||||
| .
|
|
||||||
|
|
||||||
a#navigation(href="/@/dashboard/logout") Log Out
|
|
@ -1,37 +0,0 @@
|
|||||||
#edit-control
|
|
||||||
button#edit-bold.content-editor-btn-text.editor-button(title="bold")
|
|
||||||
| B
|
|
||||||
button#edit-italic.content-editor-btn-text.editor-button(title="italics")
|
|
||||||
| I
|
|
||||||
button#edit-strikethrough.content-editor-btn-text.editor-button(title="strikethrough")
|
|
||||||
| S
|
|
||||||
button#edit-link.content-editor-btn-icon.editor-button(title="insert link")
|
|
||||||
svg#edit-link(viewBox="0 0 20 20" class="icons")
|
|
||||||
use#edit-link(xlink:href='/assets/images/global/sprite.svg#entypo-link')
|
|
||||||
button#edit-header1.content-editor-btn-text.editor-button(title="header 1")
|
|
||||||
| H1
|
|
||||||
button#edit-header2.content-editor-btn-text.editor-button(title="header 2")
|
|
||||||
| H2
|
|
||||||
button#edit-header3.content-editor-btn-text.editor-button(title="header 3")
|
|
||||||
| H3
|
|
||||||
button#edit-image.content-editor-btn-icon.editor-button(title='insert image')
|
|
||||||
svg#edit-image(viewBox="0 0 20 20" class="icons")
|
|
||||||
use#edit-image(xlink:href='/assets/images/global/sprite.svg#entypo-image')
|
|
||||||
|
|
||||||
if(edit)
|
|
||||||
button#edit-update.post-sumbit-btn.submit-start.editor-button(data-action='blog-update' data-id=page.id type='submit')
|
|
||||||
svg#submit-update(viewBox="0 0 20 20" class="icons")
|
|
||||||
use#submit-update(xlink:href='/assets/images/global/sprite.svg#entypo-save' data-action='blog-update' data-id=page.id)
|
|
||||||
svg#submit-good.icon-hide(viewBox="0 0 20 20" class="icons")
|
|
||||||
use(xlink:href='/assets/images/global/sprite.svg#entypo-thumbs-up')
|
|
||||||
svg#submit-error.icon-hide(viewBox="0 0 20 20" class="icons")
|
|
||||||
use(xlink:href='/assets/images/global/sprite.svg#entypo-thumbs-down')
|
|
||||||
button#edit-delete.content-editor-btn-icon.editor-button.submit-delete(for="post-delete" title='delete post')
|
|
||||||
svg#edit-delete(viewBox="0 0 20 20" class="icons")
|
|
||||||
use#edit-delete(xlink:href='/assets/images/global/sprite.svg#entypo-cross')
|
|
||||||
else
|
|
||||||
button#edit-save.post-sumbit-btn.submit-start.editor-button(data-action='blog-add' type='submit')
|
|
||||||
svg#submit-save(viewBox="0 0 20 20" class="icons")
|
|
||||||
use#submit-save(xlink:href='/assets/images/global/sprite.svg#entypo-plus' data-action='blog-add')
|
|
||||||
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
|||||||
#dash-recent
|
|
||||||
#recent-list
|
|
||||||
.recent-header
|
|
||||||
h3 Recent
|
|
||||||
.index-menu
|
|
||||||
a(href='/@/dashboard/page/list') View Pages
|
|
||||||
| .
|
|
||||||
a(href='/@/dashboard/page/add/new') Create Page
|
|
||||||
br
|
|
||||||
- var index = 0;
|
|
||||||
- var cap = 5; // number of posts to display, get rid of this and put it in the config
|
|
||||||
if(pages.length == 0)
|
|
||||||
label this is empty
|
|
||||||
else
|
|
||||||
- for ( index; index < pages.length; index++)
|
|
||||||
a.post-link(href="/@/dashboard/page/edit/"+pages[index].page.metadata.uuid id=pages[index].page.metadata.uuid style="background:url("+pages[index].page.metadata.feature+") no-repeat center center / cover")
|
|
||||||
div
|
|
||||||
|
|
||||||
label= pages[index].page.metadata.title
|
|
||||||
#options
|
|
||||||
- var menu = String(pages[index].page.metadata.menu)
|
|
||||||
- var published = String(pages[index].page.metadata.published)
|
|
||||||
- var featured = String(pages[index].page.metadata.featured)
|
|
||||||
span.item-options(data-active=menu) MENU ITEM
|
|
||||||
span.item-options(data-active=published) PUBLISHED
|
|
||||||
span.item-options(data-active=featured) FEATURED
|
|
||||||
span= pages[index].date
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
|||||||
#dash-login
|
|
||||||
.dash-form#dash-form
|
|
||||||
form(id="login" class='login', name="login" action="/@/dashboard/login" method="POST")
|
|
||||||
input(type='text', name='handle' class='form-control', placeholder='handle', required, autofocus)
|
|
||||||
input(type='password', name='password' class='form-control', placeholder='password', required)
|
|
||||||
button(id="login-btn", class='login-btn', type='submit') SUBMIT YOUR STUFF
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
|||||||
|
|
||||||
-if(settings.email.active == "option-smtp")
|
|
||||||
#mail-smtp(data-enabled='true')
|
|
||||||
input(type='text', name='smtp-domain' id='smtp-domain', placeholder='domain', value=settings.email.smtp.domain autofocus)
|
|
||||||
input(type='text', name='smtp-email' id='smtp-email', placeholder='email', value=settings.email.smtp.email , autofocus)
|
|
||||||
input(type='text', name='smtp-pass' id='smtp-pass', placeholder='password', value=settings.email.smtp.password , autofocus)
|
|
||||||
#mail-mg(data-enabled='false')
|
|
||||||
input(type='text', name='mg-domain' id='mg-domain', placeholder='domain', value=settings.email.mailgun.domain autofocus)
|
|
||||||
input(type='text', name='mg-key' id='mg-key', placeholder='api key', value=settings.email.mailgun.key , autofocus)
|
|
||||||
-else if(settings.email.active == "option-mg")
|
|
||||||
#mail-smtp(data-enabled='false')
|
|
||||||
input(type='text', name='smtp-domain' id='smtp-domain', placeholder='domain', value=settings.email.smtp.domain autofocus)
|
|
||||||
input(type='text', name='smtp-email' id='smtp-email', placeholder='email', value=settings.email.smtp.email , autofocus)
|
|
||||||
input(type='text', name='smtp-pass' id='smtp-pass', placeholder='password', value=settings.email.smtp.password , autofocus)
|
|
||||||
#mail-mg(data-enabled='mg')
|
|
||||||
input(type='text', name='mg-domain' id='mg-domain', placeholder='domain', value=settings.email.mailgun.domain autofocus)
|
|
||||||
input(type='text', name='mg-key' id='mg-key', placeholder='api key', value=settings.email.mailgun.key , autofocus)
|
|
||||||
-else
|
|
||||||
#mail-smtp(data-enabled='false')
|
|
||||||
input(type='text', name='smtp-domain' id='smtp-domain', placeholder='domain', value=settings.email.smtp.domain autofocus)
|
|
||||||
input(type='text', name='smtp-email' id='smtp-email', placeholder='email', value=settings.email.smtp.email , autofocus)
|
|
||||||
input(type='text', name='smtp-pass' id='smtp-pass', placeholder='password', value=settings.email.smtp.password , autofocus)
|
|
||||||
#mail-mg(data-enabled='false')
|
|
||||||
input(type='text', name='mg-domain' id='mg-domain', placeholder='domain', value=settings.email.mailgun.domain autofocus)
|
|
||||||
input(type='text', name='mg-key' id='mg-key', placeholder='api key', value=settings.email.mailgun.key , autofocus)
|
|
@ -1,13 +0,0 @@
|
|||||||
#post-options
|
|
||||||
button#option-menu-pin.option-inactive.post-option-btn(data-active= status[0], title='Pin to Menu')
|
|
||||||
svg#option-page-icon(viewBox="0 0 20 20" class="icons")
|
|
||||||
use#option-page-icon(xlink:href='/assets/images/global/sprite.svg#entypo-pin')
|
|
||||||
button#option-feature.option-inactive.post-option-btn(data-active= status[1], title='Feature')
|
|
||||||
svg#option-feature-icon(viewBox="0 0 20 20" class="icons")
|
|
||||||
use#option-feature-icon(xlink:href='/assets/images/global/sprite.svg#entypo-star')
|
|
||||||
button#option-published.option-inactive.post-option-btn(data-active= status[2], title='Publish')
|
|
||||||
svg#option-published-icon(viewBox="0 0 20 20" class="icons")
|
|
||||||
use#option-published-icon(xlink:href='/assets/images/global/sprite.svg#entypo-globe')
|
|
||||||
//button#option-preview.option-inactive(data-active="false")
|
|
||||||
svg#option-preview-icon(viewBox="0 0 20 20" class="icons")
|
|
||||||
use#option-preview-icon(xlink:href='/assets/images/global/sprite.svg#entypo-eye')
|
|
@ -1,94 +0,0 @@
|
|||||||
extends frame
|
|
||||||
block main-content
|
|
||||||
#settings-actions
|
|
||||||
#buttons
|
|
||||||
button#save-toggle
|
|
||||||
svg#submit-update(viewBox="0 0 20 20" class="icons")
|
|
||||||
use#submit-update(xlink:href='/assets/images/global/sprite.svg#entypo-save')
|
|
||||||
//button#privacy-toggle(data-private=settings.global.private)
|
|
||||||
-if (settings.global.private == 'false')
|
|
||||||
| SITE IS PRIVATE
|
|
||||||
-else
|
|
||||||
| SITE IS PUBLIC
|
|
||||||
button#publish-pages
|
|
||||||
svg#submit-update(viewBox="0 0 20 20" class="icons")
|
|
||||||
use#submit-update(xlink:href='/assets/images/global/sprite.svg#entypo-publish')
|
|
||||||
button#render-toggle(data-render=settings.global.renderOnSave)
|
|
||||||
svg#submit-update(viewBox="0 0 20 20" class="icons")
|
|
||||||
use#submit-update(xlink:href='/assets/images/global/sprite.svg#entypo-ccw')
|
|
||||||
|
|
||||||
#site-background
|
|
||||||
label FEATURE SITE IMAGE
|
|
||||||
img#background(src=settings.global.background, alt="image for site background", for="background-upload")
|
|
||||||
input(id="background-upload" type="file" name="backgrond-upload")
|
|
||||||
|
|
||||||
#settings-index
|
|
||||||
#settings-index-wrapper
|
|
||||||
#member-settings.columns
|
|
||||||
#member-settings-1.column
|
|
||||||
label AVATAR
|
|
||||||
#member-avatar-drop
|
|
||||||
img#avatar(src=member.avi, for="avatar-upload")
|
|
||||||
input(id="avatar-upload" type="file" name="avatar-upload")
|
|
||||||
#member-settings-2.column
|
|
||||||
label INFO
|
|
||||||
#member-info
|
|
||||||
input(type='text', name='handle' id='settings-handle', placeholder='handle', value=member.handle, autofocus)
|
|
||||||
input(type='text', name='email' id='settings-email', placeholder='email', value=member.email, autofocus)
|
|
||||||
input(type='text', name='base-url' id='settings-url', placeholder='url', value=settings.global.base_url, autofocus)
|
|
||||||
input(type='text', name='base-title' id='settings-title', placeholder='site title', value=settings.global.title, autofocus)
|
|
||||||
textarea(id="settings-desc" type='text', name='settings_desc' class='settings-dec', placeholder='description stuff', autofocus)
|
|
||||||
=settings.global.descriptions
|
|
||||||
#member-utils.columns
|
|
||||||
#util-1.column
|
|
||||||
label BACKUP TOOLS
|
|
||||||
br
|
|
||||||
button#create-backup CREATE BACKUP
|
|
||||||
br
|
|
||||||
-if(settings.global.last_backup != null)
|
|
||||||
.backup-meta
|
|
||||||
| The last back up was created
|
|
||||||
a(href='/api/v1/backup/download')= last_backup
|
|
||||||
br
|
|
||||||
-else
|
|
||||||
br
|
|
||||||
span No back ups. Frowny face.
|
|
||||||
button#restore-backup(for='backup-upload') RESTORE BACKUP
|
|
||||||
input(id="backup-upload" type="file" name="backup-upload")
|
|
||||||
#util-2.column
|
|
||||||
label MAINTENANCE
|
|
||||||
br
|
|
||||||
button#reindex-pages REINDEX PAGES
|
|
||||||
#option-settings.columns
|
|
||||||
#theme-settings.column
|
|
||||||
label THEMES
|
|
||||||
br
|
|
||||||
- var index = 0;
|
|
||||||
- for ( index; index < themes.length; index++)
|
|
||||||
-if(themes[index].current == "true")
|
|
||||||
a.theme-select(href="#" id=themes[index].theme.name, data-enabled="true")
|
|
||||||
= themes[index].theme["display-name"]
|
|
||||||
//svg(viewBox="0 0 20 20" class="icons")
|
|
||||||
use(xlink:href='/dash/assets/images/sprite.svg#entypo-check')
|
|
||||||
-else
|
|
||||||
a.theme-select(href="#" id=themes[index].theme.name, data-enabled="false")
|
|
||||||
= themes[index].theme["display-name"]
|
|
||||||
#mail-settings.column
|
|
||||||
label E-MAIL
|
|
||||||
-if(settings.email.active == "option-none")
|
|
||||||
a.mail-option#option-none(href="#", data-enabled='true') NONE
|
|
||||||
-else
|
|
||||||
a.mail-option#option-none(href="#", data-enabled='false') NONE
|
|
||||||
-if(settings.email.active == "option-mg")
|
|
||||||
a.mail-option#option-mg(href="#", data-enabled='true') MAILGUN
|
|
||||||
-else
|
|
||||||
a.mail-option#option-mg(href="#", data-enabled='false') MAILGUN
|
|
||||||
-if(settings.email.active == "option-smtp")
|
|
||||||
a.mail-option#option-smtp(href="#", data-enabled='true') SMTP
|
|
||||||
-else
|
|
||||||
a.mail-option#option-smtp(href="#", data-enabled='false') SMTP
|
|
||||||
include partials/mailforms
|
|
||||||
button#send-mail TEST MAIL
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1 @@
|
|||||||
|
[{"id":1,"handle":"ItsRo","avi":"/assets/images/user/2020/09/download20200802144459.png","email":"are0h@protonmail.com","password":"$2b$10$77PMC2W6aZ3gJP7TOA7OpeqQaz..SrRSO74WEa7cn61ehHI55.zKq","key":"fe79df250470815bf32dcea70221384c89163cad3a827a9c3da25d87159ed55a","role":"hnic","created":"2020-09-01T22:46:47+02:00","updated":"2020-09-01T22:46:47+02:00","deleted":null}]
|
@ -0,0 +1 @@
|
|||||||
|
{"global":{"base_url":"https://fipamo.blog","title":"It's Fipamo","descriptions":"Because it should be easy, boss.","background":"/assets/images/user/2020/09/default-bg.jpg","private":false,"renderOnSave":"false","theme":"fipamo-default","display_limit":5,"port":3314,"last_backup":"2020-09-15T22:14:42+02:00"},"library_stats":{"current_index":5},"email":{"active":"","smtp":{"domain":"","email":"","password":""},"mailgun":{"domain":"","key":""}},"menu":[]}
|
@ -0,0 +1 @@
|
|||||||
|
{"tags":[{"tag_name":"development","slug":"development","count":1},{"tag_name":"docs","slug":"docs","count":3},{"tag_name":"first","slug":"first","count":1},{"tag_name":"set up","slug":"set-up","count":3},{"tag_name":"start","slug":"start","count":2},{"tag_name":"themes","slug":"themes","count":1},{"tag_name":"updates","slug":"updates","count":2},{"tag_name":"welcome","slug":"welcome","count":1},{"tag_name":"wiki","slug":"wiki","count":1}]}
|
@ -1,92 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module dependencies.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var app = require('./brain/app');
|
|
||||||
var debug = require('debug')('fipamo:server');
|
|
||||||
var http = require('http');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get port from environment and store in Express.
|
|
||||||
*/
|
|
||||||
|
|
||||||
try {
|
|
||||||
var configPort = require('./site/settings.json').global.port;
|
|
||||||
} catch (err) {
|
|
||||||
console.log('settings.json not found, assuming this is a first run...');
|
|
||||||
}
|
|
||||||
|
|
||||||
var port = normalizePort(configPort || process.env.PORT || 2314);
|
|
||||||
app.set('port', port);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create HTTP server.
|
|
||||||
*/
|
|
||||||
var server = http.createServer(app);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listen on provided port, on all network interfaces.
|
|
||||||
*/
|
|
||||||
|
|
||||||
server.listen(port);
|
|
||||||
server.on('error', onError);
|
|
||||||
server.on('listening', onListening);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Normalize a port into a number, string, or false.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function normalizePort(val) {
|
|
||||||
var port = parseInt(val, 10);
|
|
||||||
|
|
||||||
if (isNaN(port)) {
|
|
||||||
// named pipe
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (port >= 0) {
|
|
||||||
// port number
|
|
||||||
return port;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event listener for HTTP server "error" event.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function onError(error) {
|
|
||||||
if (error.syscall !== 'listen') {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
//var bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port;
|
|
||||||
|
|
||||||
// handle specific listen errors with friendly messages
|
|
||||||
switch (error.code) {
|
|
||||||
case 'EACCES':
|
|
||||||
//console.error(bind + ' requires elevated privileges');
|
|
||||||
process.exit(1);
|
|
||||||
break;
|
|
||||||
case 'EADDRINUSE':
|
|
||||||
//console.error(bind + ' is already in use');
|
|
||||||
process.exit(1);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event listener for HTTP server "listening" event.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function onListening() {
|
|
||||||
var addr = server.address();
|
|
||||||
var bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port;
|
|
||||||
console.log('Up and runnin on port: ' + addr.port);
|
|
||||||
debug('Listening on ' + bind);
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,68 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "fipamo-development",
|
|
||||||
"version": "1.2.4",
|
|
||||||
"private": true,
|
|
||||||
"description": "The most chill blog framework ever.",
|
|
||||||
"repository": "https://code.playvicio.us/Are0h/Fipamo",
|
|
||||||
"scripts": {
|
|
||||||
"start": "pm2 --node-args='-r esm' start init.js",
|
|
||||||
"stop": "pm2 stop init.js",
|
|
||||||
"dev": "nodemon -r esm init.js --ignore node_modules/ -e js",
|
|
||||||
"debug": "nodemon inspect -r esm init.js --ignore node_modules/ -e js",
|
|
||||||
"watch": "sass --watch src/styles:public/assets/css & parcel watch src/com/Start.js --out-dir public/assets/scripts --out-file dash.min.js --public-url /assets/scripts"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10.16.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"adm-zip": "^0.4.16",
|
|
||||||
"bcrypt": "^5.0.0",
|
|
||||||
"bluebird": "^3.7.2",
|
|
||||||
"body-parser": "latest",
|
|
||||||
"caret-pos": "^1.2.2",
|
|
||||||
"connect-flash": "latest",
|
|
||||||
"cookie-parser": "~1.3.3",
|
|
||||||
"debug": "^4.1.1",
|
|
||||||
"esm": "^3.2.25",
|
|
||||||
"express": "^4.17.1",
|
|
||||||
"express-session": "^1.17.1",
|
|
||||||
"filehound": "^1.17.4",
|
|
||||||
"fs-extra": "^9.0.1",
|
|
||||||
"highlight.js": "^9.18.1",
|
|
||||||
"jsdom": "^12.2.0",
|
|
||||||
"jsonwebtoken": "^8.5.1",
|
|
||||||
"lodash": "^4.17.19",
|
|
||||||
"mailgun-js": "^0.18.0",
|
|
||||||
"markdown-it": "^8.4.1",
|
|
||||||
"markdown-yaml-metadata-parser": "^2.0.5",
|
|
||||||
"memorystore": "^1.6.2",
|
|
||||||
"moment": "^2.26.0",
|
|
||||||
"morgan": "^1.10.0",
|
|
||||||
"multer": "latest",
|
|
||||||
"nodemailer": "^6.4.8",
|
|
||||||
"nodemailer-mailgun-transport": "^1.4.0",
|
|
||||||
"pm2": "^4.4.0",
|
|
||||||
"pug": "^3.0.0",
|
|
||||||
"reframe.js": "^2.2.8",
|
|
||||||
"request": "^2.88.2",
|
|
||||||
"sanitize-html": "^1.26.0",
|
|
||||||
"serve-favicon": "latest",
|
|
||||||
"sortablejs": "^1.10.2",
|
|
||||||
"tiny-date-picker": "^3.2.8",
|
|
||||||
"uuid": "^3.4.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@babel/cli": "^7.10.1",
|
|
||||||
"@babel/core": "^7.10.2",
|
|
||||||
"@babel/preset-env": "^7.10.2",
|
|
||||||
"animejs": "^3.2.0",
|
|
||||||
"babel-preset-env": "^1.7.0",
|
|
||||||
"bulma": "^0.9.0",
|
|
||||||
"eslint": "^7.9.0",
|
|
||||||
"nodemon": "^2.0.4",
|
|
||||||
"parcel": "^1.12.4",
|
|
||||||
"prettier": "^2.0.5",
|
|
||||||
"sass": "^1.26.11",
|
|
||||||
"scramble-text": "0.0.8"
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue