A single-user blog

As an exercise, I’ve been trying to make the simplest possible blog, using the best possible tools.

It’s a single-user blog, so there’s no need to worry about protecting against malicious users, and the permissions are quite straightforward.

Authentication

The app uses Google for authentication, via Firebase. There’s a button to sign in, the OAuth2 callback is handled by react-redux-firebase, and there’s a button to sign out.

Database and permissions

The app uses Firebase’s Realtime Database to store the posts, divided into two collections: public, which anyone can read but only the authenticated user can write, and private, which only the authenticated user can read or write.

Each collection contains two further collections: content, which contains the HTML of each post, and metadata, which contains information about each post.

The authenticated user object contains a verified email address, which we use for permissions:

{
  "rules": {
    "public": {
      ".read": true,
      ".write": "auth.token.email_verified == true && auth.token.email == 'foo@example.com'",
      "metadata": {
        ".indexOn": "published"
      }
    },
    "private": {
      ".read": "auth.token.email_verified == true && auth.token.email == 'foo@example.com'",
      ".write": "auth.token.email_verified == true && auth.token.email == 'foo@example.com'",
      "metadata": {
        ".indexOn": "created"
      }
    }
  }
}

A post is created in the private collection, then written to the public collection on publication.

User interface

The client interface uses react-redux-firebase for communication with the database, and Material UI for the app elements.

The following components are inside an App container, which handles the routing:

Auth

Displays the user’s profile picture, or a “Sign in” button if they haven’t yet signed in.

Protected

Displays a loading bar until the user is authenticated.

Items

Displays a list of posts, with 3 possible actions:

Create

Creates a new post.

Actions

A menu of actions (“Unpublish”, “Remove”) for each post.

Edit

Each item links to the editor: a rich-text WYSIWYG editor built using the browser’s ContentEditable API.

The content format is HTML, so if the features provided by this editor aren't sufficient it's easy to switch to a different one (e.g. something built on ProseMirror, Slate or Draft.js.

The editor contains a “Publish” button, which writes the content and metadata to the public collection, where anyone can read it via the public JSON API.

Source code

The source code for this app is hosted on GitHub.