node package manager
We need your input. Help make JavaScript better: Take the 2017 JavaScript Ecosystem survey »



Blogh is a github based blog engine

All the blog workflow is based on Github

  • to publish a new post, you commit a new markdown file in a GH repo
  • once commited, you post a new "Announce issue" on same repo, formatted like this one
  • As soon as the engine retrieve the new issue, it retrieve the markdown file, parse it with marked , and serve it to the front-end koa.js app
  • Any comment on the issue will become a new comment on the blog post.

Announce issue formatting

  • the title of the issue will become the title of the blog post
  • the date of the issue will become the publish date of the blog post
  • the issue must be labeled with new-post label
  • any other label on the issue will become a tag of the post
  • issue body formatting:
## New post submitted!    //this will be discarded, is just to give the issue a meaning
    This is the universally present hello world, in blog version    //indented lines will become the abstract of the post  
[post content](/parroit/ //a link with `post content` text must link to the markdown file
any other content present in the issue body will be discarded
### Project structure
The system is built by three main modules:
* Back-end: responsible to receive issue notification via [GH webhooks](, to retrieve md content from GH and finally build a posts and comments model in memory.
* The model could be based on
* To manage webhook, use [@rvagg github-webhook-handler](
Specifically, it will listen for this GH events via web-hook:
    - issue_comment: received any time an announce issue comment is posted. This will
        lend to a new comment to the post be pushed in the model
    - issues: received any time a new announcement issue is created. This will
        lend to a new post be pushed in the model.
    **investigate best way to notify post content has changed. Maybe via push? or via new issue? or issue comment? Or change to original issue (this way could be used to change also title, abstract)**
  The backend will read all repo content on start, and update it on GH hook notification
An install page could be useful here to automatically create web-hooks in choosed repository.
* Front-end responsible to pre-cache a HTML rendered version of all posts, and to serve them to client.
    - model has to emit events whenever a new post is published, it's content is edited, or a new comment appended to a post.
    - templates and static config will be retrieved from a GH repo or NPM repo, to allow for
        templating. It will be beautiful to allow any kind of template engine to be used,
        maybe using [tj consolidate.js](
    - Initially, we will define a view for landing page and one for single post page. The landing page will
        receive as model the last N posts published (N configurable via options?). The single post page will
        receive as model the single post data.
    - A base template module will be specified that export two function for rendering these two views
        (it will render them using consolidate.js). The using template should re-export this base module
        function, and include in package.json the prop specifying which template engine to use via
        consolidate, plus dependencies for it.
    - Static content needed by a template will be put in a "static" folder.
* Comment engine: Responsible to serve new comments to client anytime they are added to memory model. It will listen to model changes and then push new comments to clients via substack shoe.
The client will have a little React app that add comments to proper section when are received via shoe.
Post new comments will be published from the browser directly to github repo, as issue comment (user have to be
logged in via GH).
Every time a user post a comment with :+1:, we should count a rating up on the post.
Every time a user post a comment with :-1:, we should count a rating up on the post.
### Data model
Data model is based on [** BlogPosting](
[and Blog]( schemas.
Specifically, a post is formatted this way:
blog = {
    about: {
        description: ''             //The subject matter of the blog.
    image: '',                      //image for the front page of the blog, (in data uri format)
    author: {                       //blog owner
        email: '',                  //email if GH public
        alternateName: '',          //this will be GH account name
        image: '',                  //url for the post image, (in data uri format)
    blogPost: []                    //array of posts, with format below
post = {
    articleBody: '',
    wordCount: 0,
    keywords: '',                   //should be a comma separated list of tags
    about: {
        description: ''             //The subject matter of the content.
    image: '',                      //url for the post image, (in data uri format)
    url: ''                         //url for the post
    //aggregateRating: {}           //maybe it's not usable with :+1:
    author: {
        email: '',                  //email if GH public
        alternateName: '',          //this will be GH account name
        image: '',                  //url for the post image, (in data uri format)
    commentCount: 0,
    datePublished: '',              //an ISO date string
    discussionUrl: '',              //link to comments page
    headline: '',                   //post title  
    isBasedOnUrl: '',               //link to original md file on GH
    text: '',                       //post body in markdown format
    comment: [{                     //post comments
        commentText: ''             //comment body in markdown format
        commentTime: ''             //an ISO date string
        creator: {
            email: '',                  //email if GH public
            alternateName: '',          //this will be GH account name
            image: '',                  //url for the post image, (in data uri format)


This is a work in progress don't use it