diff --git a/sample_data/home/template.ejs b/sample_data/home/template.ejs
new file mode 100644
index 0000000..6cd69bf
--- /dev/null
+++ b/sample_data/home/template.ejs
@@ -0,0 +1,13 @@
+
+
+
+
+ GitBlog.md - Home
+
+
+
+
+ <%- article.content %>
+
+
+
\ No newline at end of file
diff --git a/src/app.js b/src/app.js
index 257490c..4b3d7bb 100644
--- a/src/app.js
+++ b/src/app.js
@@ -15,13 +15,14 @@ const cons = {
module.exports = (config) => {
const fw = require('./file_walker')(config);
+ const renderer = require('./renderer')(config);
// set view engine from configuration
app.set('view engine', config['view_engine']);
// reroute the views folder to the root folder
app.set('views', path.join(__dirname, '..'));
- const articles = [];
+ const articles = {};
/**
* Fetch articles from the data folder and send success as a response
@@ -34,8 +35,9 @@ module.exports = (config) => {
return console.error(cons.error, 'loading articles : ' + err);
}
articles.splice(0, articles.length, ...list);
- if (articles.length > 0)
- console.log(cons.ok, `loaded ${articles.length} article${articles.length > 1 ? 's' : ''}`);
+ const nb = Object.keys(articles).length;
+ if (nb > 0)
+ console.log(cons.ok, `loaded ${nb} article${nb > 1 ? 's' : ''}`);
else
console.log(cons.warn, `no articles loaded, check your configuration`);
callback(true);
@@ -79,16 +81,42 @@ module.exports = (config) => {
// home endpoint : send the correct index page or error if not existing
app.get('/', (req, res) => {
- const homePath = `${config['data_dir']}/${config['home']['index']}`;
+ const homePath = path.join(config['data_dir'], config['home']['index']);
fs.access(homePath, fs.constants.R_OK, (err) => {
if (err)
showError(req.path, 404, res);
else
- render(res, homePath, {articles: articles});
+ render(res, homePath, {articles: Object.values(articles)});
});
});
- // catch all gets and return 404 if it's an hidden file type
+ // catch all article urls and render them
+ app.get('*', (req, res, next) => {
+ if (req.path.test(/^\/\d{4}\/\d{2}\/\d{2}\/(\w*\/)?$/)) {
+ const articlePath = req.path.substr(0, 11);
+ const article = articles[articlePath];
+ if (!article)
+ showError(req.path, 404, res);
+ else {
+ renderer.render(article.realPath, (err, html) => {
+ if (err)
+ return showError(req.path, 500, res);
+ article.content = html;
+ const templatePath = path.join(config['data_dir'], config['home']['index']);
+ fs.access(templatePath, fs.constants.R_OK, (err) => {
+ if (err)
+ showError(req.path, 404, res);
+ else
+ render(res, templatePath, {article: article});
+ });
+ });
+ }
+ } else {
+ next();
+ }
+ });
+
+ // catch all hidden file type and return 404
app.get('*', (req, res, next) => {
if (config['home']['hidden'].includes(path.extname(req.path)))
showError(req.path, 404, res);
diff --git a/src/config.default.json b/src/config.default.json
index ece0152..065a961 100644
--- a/src/config.default.json
+++ b/src/config.default.json
@@ -14,6 +14,7 @@
},
"article": {
"index": "index.md",
+ "template": "template.ejs",
"thumbnail_tag": "thumbnail",
"default_title": "Untitled",
"default_thumbnail": null
@@ -25,5 +26,13 @@
"webhook": {
"endpoint": "/webhook",
"secretFile": "git_secret"
+ },
+ "showdown": {
+ "parseImgDimensions": true,
+ "strikethrough": true,
+ "tables": true,
+ "tasklists": true,
+ "openLinksInNewWindow": true,
+ "emoji": true
}
}
\ No newline at end of file
diff --git a/src/file_walker.js b/src/file_walker.js
index 98a2126..ac30e12 100644
--- a/src/file_walker.js
+++ b/src/file_walker.js
@@ -76,7 +76,7 @@ module.exports = (config) => {
.filter((matches) => matches && matches.length > 1);
if (paths.length === 0)
cb(null, []);
- const list = [];
+ const articles = {};
let remaining = 0;
paths.forEach((matches) => {
const article = {
@@ -94,11 +94,11 @@ module.exports = (config) => {
article.title = info.title || config['article']['default_title'];
article.thumbnail = info.thumbnail ? path.join(article.path, info.thumbnail) : config['article']['default_thumbnail'];
article.escapedTitle = article.title.toLowerCase().replace(/[^\w]/gm, ' ').trim().replace(/ /gm, '_');
- article.url = path.join(article.path, article.escapedTitle);
- list.push(article);
+ article.url = path.join(article.path, article.escapedTitle)+'/';
+ articles[article.path]=article;
remaining--;
if (remaining === 0)
- cb(null, list);
+ cb(null, articles);
});
});
diff --git a/src/renderer.js b/src/renderer.js
new file mode 100644
index 0000000..139956b
--- /dev/null
+++ b/src/renderer.js
@@ -0,0 +1,16 @@
+const fs = require('fs');
+const showdown = require('showdown');
+
+module.exports = (config) => {
+ const converter = new showdown.Converter(config['showdown']);
+ return {
+ render : (file, cb) => {
+ fs.readFile(file, {encoding:'UTF-8'}, (err, data) => {
+ if(err)
+ return cb(err);
+ const html = converter.makeHtml(data);
+ cb(null,html);
+ });
+ }
+ };
+};
\ No newline at end of file
diff --git a/test/file_walker.test.js b/test/file_walker.test.js
index c36a58e..70abf86 100644
--- a/test/file_walker.test.js
+++ b/test/file_walker.test.js
@@ -234,7 +234,7 @@ describe('Test article fetching', () => {
title: 'Untitled',
thumbnail: 'default.png',
escapedTitle: 'untitled',
- url: path.join('2019', '05', '05', 'untitled'),
+ url: path.join('2019', '05', '05', 'untitled')+'/',
});
done();
});
@@ -262,7 +262,7 @@ describe('Test article fetching', () => {
title: 'Title with : info !',
thumbnail: path.join('2019', '05', '05', './thumbnail.jpg'),
escapedTitle: 'title_with___info',
- url: path.join('2019', '05', '05', 'title_with___info'),
+ url: path.join('2019', '05', '05', 'title_with___info')+'/',
});
done();
});
diff --git a/test/renderer.test.js b/test/renderer.test.js
new file mode 100644
index 0000000..1a42d27
--- /dev/null
+++ b/test/renderer.test.js
@@ -0,0 +1,53 @@
+/* jshint -W117 */
+const fs = require('fs');
+const path = require('path');
+const utils = require('./test_utils');
+
+const dataDir = 'test_data';
+const file = path.join(dataDir, 'test.md');
+
+const config = {
+ 'showdown': {
+ 'simplifiedAutoLink': true,
+ 'smartIndentationFix': true
+ }
+};
+
+const renderer = require('../src/renderer')(config);
+
+beforeEach(() => {
+ utils.deleteFolderSync(dataDir);
+ fs.mkdirSync(dataDir);
+});
+
+afterAll(() => {
+ if (fs.existsSync(dataDir)) {
+ utils.deleteFolderSync(dataDir);
+ }
+});
+
+test('invalid file', (done) => {
+ renderer.render('invalid file', (err, html) => {
+ expect(err).not.toBeNull();
+ expect(html).not.toBeDefined();
+ done();
+ });
+});
+
+test('normal file', (done) => {
+ fs.writeFileSync(file, `# Hello`);
+ renderer.render(file, (err, html) => {
+ expect(err).toBeNull();
+ expect(html).toBe('Hello
');
+ done();
+ });
+});
+
+test('custom rules', (done) => {
+ fs.writeFileSync(file, `www.google.com`);
+ renderer.render(file, (err, html) => {
+ expect(err).toBeNull();
+ expect(html).toBe('www.google.com
');
+ done();
+ });
+});
\ No newline at end of file