diff --git a/README.md b/README.md index b36d91a..0d18322 100644 --- a/README.md +++ b/README.md @@ -26,33 +26,7 @@ npm install cd gitblog.md cp config.example.json config.json ``` -then edit the config.json file with your values : -> default values for config.json -````json -{ - "nodePort": 3000, - "dataDir": "data", - "modules" : { - "plantuml" : false, - "rss": true, - "webhook": true - }, - "home" : { - "index" : "index.ejs" - }, - "article" : { - "index" : "index.md" - }, - "rss" : { - "endpoint" : "/rss", - "length" : 10 - }, - "webhook" : { - "endpoint": "/webhook", - "secretFile": "git_secret" - } -} -```` +then edit the config.json file with your custom values. **3. Start your server** diff --git a/src/config.default.json b/src/config.default.json index f62a4ed..2ee6f02 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -10,7 +10,10 @@ "index" : "index.ejs" }, "article" : { - "index" : "index.md" + "index" : "index.md", + "thumbnail_tag" : "thumbnail", + "default_title": "Untitled", + "default_thumbnail" : null }, "rss" : { "endpoint" : "/rss", diff --git a/src/config.js b/src/config.js index f698325..ac75ed5 100644 --- a/src/config.js +++ b/src/config.js @@ -15,7 +15,7 @@ const merge = (ref, src) => { module.exports = () => { try { - let configData = fs.readFileSync('./config.json'); + let configData = fs.readFileSync('./config.json', {encoding:'UTF-8'}); let config = JSON.parse(configData); return merge(refConfig, config); } catch (error) { diff --git a/src/file_walker.js b/src/file_walker.js index 2d2c9ad..1b1766b 100644 --- a/src/file_walker.js +++ b/src/file_walker.js @@ -1,6 +1,6 @@ const fs = require('fs'); -const fileTree = (path, cb) => { +const getFileTree = (path, cb) => { let list = []; let remaining = 0; fs.readdir(path, {withFileTypes: true}, (err, items) => { @@ -9,7 +9,7 @@ const fileTree = (path, cb) => { items.forEach((item) => { if (item.isDirectory()) { remaining++; - fileTree(`${path}/${item.name}`, (err, out) => { + getFileTree(`${path}/${item.name}`, (err, out) => { if (err) return cb(err); list.push(...out); @@ -26,16 +26,30 @@ const fileTree = (path, cb) => { }); }; -const readIndexFile = (path,cb) => { - //TODO reading page title and possibly ![thumbnail](url) +const readIndexFile = (path, thumbnailTag, cb) => { + fs.readFile(path, {encoding: 'UTF-8'}, (err, data) => { + if (err) + return cb(err); + + let info = {}; + + const regRes1 = data.match(/(^|[^#])#([^#\r\n]*)\r?\n?$/m); + info.title = regRes1 ? regRes1[2].trim() : undefined; + + const thumbnailRegEx = new RegExp(`!\\[${thumbnailTag}]\\(([^)]*)\\)`, 'i'); + const regRes2 = data.match(thumbnailRegEx); + info.thumbnail = regRes2 ? regRes2[1].trim() : undefined; + + cb(null, info); + }); }; module.exports = (config) => { return { - fileTree: config['test'] ? fileTree : undefined, + fileTree: config['test'] ? getFileTree : undefined, readIndexFile: config['test'] ? readIndexFile : undefined, fetchArticles: (cb) => { - fileTree(config['data_dir'], (err, fileList) => { + getFileTree(config['data_dir'], (err, fileList) => { if (err) return cb(err); const paths = fileList @@ -44,16 +58,30 @@ module.exports = (config) => { .map(path => path.substr(0, path.length - config['article']['index'].length)) .map(path => path.match(/^\/(\d{4})\/(\d{2})\/(\d{2})\/$/)) .filter(path => path && path.length > 1); + if (paths.length === 0) + cb(null, []); const list = []; + let remaining = 0; paths.forEach(path => { - list.push({ + const article = { path: config['data_dir'] + path[0] + config['article']['index'], year: parseInt(path[1]), month: parseInt(path[2]), day: parseInt(path[3]) + }; + remaining++; + readIndexFile(article.path, config['article']['thumbnail_tag'], (err, info) => { + if (err) + return cb(err); + article.title = info.title || config['article']['default_title']; + article.thumbnail = info.thumbnail ? (config['data_dir'] + path[0] + info.thumbnail) : config['article']['default_thumbnail']; + list.push(article); + remaining--; + if (remaining === 0) + cb(null, list); }); }); - cb(null, list); + }); } }; diff --git a/test/file_walker.test.js b/test/file_walker.test.js index 10da942..e1a1c1a 100644 --- a/test/file_walker.test.js +++ b/test/file_walker.test.js @@ -8,14 +8,17 @@ const config = { 'test': true, 'data_dir': dataDir, 'article': { - 'index': testIndex + 'index': testIndex, + 'default_title': 'Untitled', + 'default_thumbnail': 'default.png', + 'thumbnail_tag': 'thumbnail' } }; const fw = require('../src/file_walker')(config); const deleteFolderSync = (path) => { - if(!fs.existsSync(path)) + if (!fs.existsSync(path)) return; fs.readdirSync(path, {withFileTypes: true}).forEach((item) => { if (item.isDirectory()) @@ -105,10 +108,95 @@ describe('Test function fileTree', () => { }); }); +describe('Test index article reading', () => { + const file = `${dataDir}/${testIndex}`; + + test('invalid file', (done) => { + fw.readIndexFile('invalid file', 'thumbnail', (err, info) => { + expect(err).not.toBeNull(); + expect(info).not.toBeDefined(); + done(); + }); + + }); + + test('correct file', (done) => { + fs.writeFileSync(file, ` + # This is an awesome title !?¤ + ![custom_thumbnail](./thumbnail.jpg) + this is some text + `); + fw.readIndexFile(file, 'custom_thumbnail', (err, info) => { + expect(err).toBeNull(); + expect(info).toBeDefined(); + expect(info.title).toBe('This is an awesome title !?¤'); + expect(info.thumbnail).toBe('./thumbnail.jpg'); + done(); + }); + }); + + test('no title', (done) => { + fs.writeFileSync(file, ` + ## This is an awesome title !?¤ + ![custom_thumbnail](./thumbnail.jpg) + ### this is some text + `); + fw.readIndexFile(file, 'custom_thumbnail', (err, info) => { + expect(err).toBeNull(); + expect(info).toBeDefined(); + expect(info.title).not.toBeDefined(); + expect(info.thumbnail).toBe('./thumbnail.jpg'); + done(); + }); + }); + + test('title at beginning', (done) => { + fs.writeFileSync(file, '#title'); + fw.readIndexFile(file, 'custom_thumbnail', (err, info) => { + expect(err).toBeNull(); + expect(info).toBeDefined(); + expect(info.title).toBe('title'); + expect(info.thumbnail).not.toBeDefined(); + done(); + }); + }); + + test('no thumbnail', (done) => { + fs.writeFileSync(file, ` + # This is an awesome title !?¤ + ![custom_thumbnail](./thumbnail.jpg) + this is some text + `); + fw.readIndexFile(file, 'thumbnail', (err, info) => { + expect(err).toBeNull(); + expect(info).toBeDefined(); + expect(info.title).toBe('This is an awesome title !?¤'); + expect(info.thumbnail).not.toBeDefined(); + done(); + }); + }); + + test('multiple thumbnails', (done) => { + fs.writeFileSync(file, ` + # This is an awesome title !?¤ + ![custom_thumbnail](./thumbnail.jpg) + this is some text + ![custom_thumbnail](./thumbnail2.jpg) + `); + fw.readIndexFile(file, 'custom_thumbnail', (err, info) => { + expect(err).toBeNull(); + expect(info).toBeDefined(); + expect(info.title).toBe('This is an awesome title !?¤'); + expect(info.thumbnail).toBe('./thumbnail.jpg'); + done(); + }); + }); +}); + describe('Test article fetching', () => { test('invalid data dir', (done) => { config['data_dir'] = 'invalid root'; - fw.fetchArticles((err,list) => { + fw.fetchArticles((err, list) => { expect(err).not.toBeNull(); expect(list).not.toBeDefined(); config['data_dir'] = dataDir; @@ -116,7 +204,7 @@ describe('Test article fetching', () => { }); }); test('empty data dir', (done) => { - fw.fetchArticles((err,list) => { + fw.fetchArticles((err, list) => { expect(err).toBeNull(); expect(list).toBeDefined(); expect(list.length).toBe(0); @@ -133,30 +221,55 @@ describe('Test article fetching', () => { `${dataDir}/test/test/${testIndex}`, `${dataDir}/2019/05/${testIndex}`, ]); - fw.fetchArticles((err,list) => { + fw.fetchArticles((err, list) => { expect(err).toBeNull(); expect(list).toBeDefined(); expect(list.length).toBe(0); done(); }); }); + test('empty index file', (done) => { + createEmptyDirs([ + `${dataDir}/2019/05/05`, + ]); + const file = `${dataDir}/2019/05/05/${testIndex}`; + createEmptyFiles([file]); + fw.fetchArticles((err, list) => { + expect(err).toBeNull(); + expect(list).toBeDefined(); + expect(list.length).toBe(1); + expect(list[0]).toEqual({ + path: file, + year: 2019, + month: 5, + day: 5, + title:'Untitled', + thumbnail:'default.png' + }); + done(); + }); + }); test('correct index file', (done) => { createEmptyDirs([ `${dataDir}/2019/05/05`, ]); - const fileList = [ - `${dataDir}/2019/05/05/${testIndex}`, - ]; - createEmptyFiles(fileList); - fw.fetchArticles((err,list) => { + const file = `${dataDir}/2019/05/05/${testIndex}`; + fs.writeFileSync(file, ` + # Title + ![thumbnail](./thumbnail.jpg) + this is some text + `); + fw.fetchArticles((err, list) => { expect(err).toBeNull(); expect(list).toBeDefined(); expect(list.length).toBe(1); expect(list[0]).toEqual({ - path: fileList[0], - year:2019, - month:5, - day:5 + path: file, + year: 2019, + month: 5, + day: 5, + title:'Title', + thumbnail:`${dataDir}/2019/05/05/./thumbnail.jpg` }); done(); });