Skip to content
/ py-blog Public

Simple, lightweight dockerized python blog service using falcon and mongoengine.

License

Notifications You must be signed in to change notification settings

neetjn/py-blog

Repository files navigation

py-blog

license

build codecov docker-automated docker-build

py-blog is a backend blog service that can easily and comfortably sit behind any frontend.

Why

I used this project as an opportunity to learn Falcon, a minimal RESTful framework for Python.

About

This project provides a seamless REST api for your blog. py-blog has the capability to integrate with popular webservices such as aws right out of the box. This project only supplies you with a REST api and doesn't try to assume your use case or architecture. So why use py-blog as opposed to Wordpress, Ghost, or the various free, mature, and established platforms available? Size and flexibility.

Project Size

py-blog is composed of a simple RESTful api built on top of Falcon and Gunicorn WSGI designed with HATEOAS (resource expansion).

Flexbility

This project was crafted with consumability in mind. The root endpoint serves a service description that can be used as a rel/link mapping for dynamically hydrating front end interfaces. The service description helps simplify resource expansion, endpoint migration, and will allow front end developers to design their blog without having to manually construct any URLs.

Service Description Example

{
  "links": [
    {
      "rel": "post-collection",
      "href": "localhost:8000/v1/posts/",
      "acceptedMethods": [
        "GET",
        "POST"
      ]
    },
    {
      "rel": "post-search",
      "href": "localhost:8000/v1/posts/search/",
      "acceptedMethods": [
        "POST"
      ]
    },
    {
      "rel": "user-authentication",
      "href": "localhost:8000/v1/user/authenticate/",
      "acceptedMethods": [
        "POST"
      ]
    },
    {
      "rel": "user",
      "href": "localhost:8000/v1/user/",
      "acceptedMethods": [
        "GET",
        "PUT"
      ]
    },
    {
      "rel": "user-registration",
      "href": "localhost:8000/v1/user/register/",
      "acceptedMethods": [
        "POST"
      ]
    }
  ]
}

Post Collection Example

{
  "posts": [
    {
      "href": "localhost:8000/v1/post/5b776070df72ea7bdeb80a19/",
      "author": "John Nolette",
      "title": "My First Post",
      "description": "This is my first post ever!",
      "content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
      "tags": [
        "hello",
        "world"
      ],
      "private": false,
      "featured": false,
      "created": "2018-08-17 23:55:28.206000",
      "edited": null,
      "comments": 0,
      "likes": 0,
      "views": 0,
      "links": [
        {
          "rel": "self",
          "href": "localhost:8000/v1/post/5b776070df72ea7bdeb80a19/",
          "acceptedMethods": [
            "GET",
            "PUT",
            "DELETE"
          ]
        },
        {
          "rel": "post-comment",
          "href": "localhost:8000/v1/post/5b776070df72ea7bdeb80a19/comment",
          "acceptedMethods": [
            "POST"
          ]
        },
        {
          "rel": "post-like",
          "href": "localhost:8000/v1/post/5b776070df72ea7bdeb80a19/like",
          "acceptedMethods": [
            "PUT"
          ]
        },
        {
          "rel": "post-view",
          "href": "localhost:8000/v1/post/5b776070df72ea7bdeb80a19/view",
          "acceptedMethods": [
            "PUT"
          ]
        }
      ]
    }
  ]
}

Security

By design this blog encrypts any and all post or comment content within the given database. Blog content secret keys can be defined by the administrator, and will be hashed into a 32 bit key for AES encryption/decryption.

Features

The py-blog project ships with (but is not limited to) the following:

  • Service Description (resource expansion)
  • Logging
  • User Registration/Authentication (includes login failure handling)
  • Email verification
  • User Roles (blogger, moderator, admin)
  • User news letters
  • User avatars and avatar storage
  • AWS S3 support for media management
  • Live blog settings management
  • Creating, deleting, liking, "viewing" posts
  • Fetching all public posts (with pagination)
  • Post privacy (editing/publishing)
  • Featured posts
  • Post content encryption (database level)
  • Searching for posts by title, description, content, and author (with cooldown and pagination)
  • Creating, deleting, liking post comments
  • Comment content encryption (database level)
  • Resource caching.

Configuration

This project is highly configurable, all blog constants can be found in blog/constants.py and all settings can be found in blog/settings.yml.

Constants (Environmental)

  • BLOG_TEST: Provision platform for test environments.
  • BLOG_RUN_MIGRATIONS: Runs blog database migrations on application start.
  • BLOG_HOST: Host blog will be served on for Gunicorn.
  • BLOG_PORT: Port blog will be served on for Gunicorn.
  • BLOG_REDIS_HOST: Blog redis host for caching.
  • BLOG_DB_HOST: Blog mongodb database host.
  • BLOG_DB_PORT: Blog mongodb database port.
  • BLOG_DB_NAME: Blog mongodb database name.
  • BLOG_DB_URI: Optional connection string for mongo client.
  • BLOG_CONTENT_KEY: Key used to encrypt/decrypt blog content.
  • BLOG_JWT_SECRET_KEY: Key used to encrypt/decrypt session JWT.

Note: If AWS credentials are not provided, api will alternatively store avatars and other media as base64 encoded binaries in mongodb.

  • BLOG_FAKE_S3_HOST: fakes3 host for blog to use in test mode.
  • BLOG_AWS_ACCESS_KEY_ID: AWS access key id for s3.
  • BLOG_AWS_SECRET_ACCESS_KEY: AWS secret access key for s3.
  • BLOG_AWS_S3_BUCKET: AWS s3 bucket for storing avatars.

Settings

  • login
    • max_failed_login: Maximum number of consecutive failed logins.
    • failed_login_timeout: Time in seconds to timeout after consecutive failed logins.
    • max_session_time: Time in seconds a session is valid after a token is generated.
  • post
    • view_time_delay: Time in seconds wait before processing another post view.
    • search_time_delay: Time in seconds to wait in between each post search request.
  • user
    • allow_avatar_capability: Allow avatars to be uploaded and served.
    • allow_manual_registration: Allow manual registration for new users, enabled registration endpoint.
    • require_email_verification: Enforce email verification for new users.
    • upload_avatar_s3: Upload avatar to s3 instead of mongodb gridfs storage.
  • rules
    • user
      • avatar_size: Maximum size in kb for uploaded user avatars.
      • username_min_char: Minimum number of characters for user names.
      • username_max_char: Maximum number of characters for user names.
      • name_min_char: Minimum number of characters for post titles.
      • name_max_char: Maximum number of characters for post titles.
    • post
      • title_min_char: Minimum number of characters for post titles.
      • title_max_char: Maximum number of characters for post titles.
      • content_min_char: Minimum number of characters for post content.
      • content_max_char: Maximum number of characters for post content.
      • tag_min_char: Minimum number of characters for a single post tag.
      • tag_max_char: Maximum number of characters for a single post tag.
      • tag_max_count: Maximum number of tags a post can have.
    • comment
      • content_min_char: Minimum number of characters for comments.
      • content_max_char: Maximum number of characters for comments.

Setting Up

The following requirements are required for staging py-blog for either development or production:

  • python 3.6
  • mongodb 3.6
  • redis 4
  • pipenv (python package manager)
  • s3 bucket or fakes3 (optional)

To install the project's dependencies use:

pipenv install

Once the necessary configurations have been made to blog/constants.py, the blog can be spun up using:

pipenv run python -m blog

For provisioning refer to the Configuration section.

Docker

Build your docker image:

docker build . -t py-blog

Run your container:

docker run --name blog -p 8000:8000 py-blog

Alternatively use docker-compose for a quick start, will provide a mongodb container:

docker-compose up

Testing

Note: Testing will require an instance of mongodb. Note: CI tests s3 functionality using fakes3 project.

The test suite is composed of operational tests exercising the blog's core, as well as mocked requests against the provided API using the falcon testing tools.

To install test dependencies in your local development environment using pipenv:

pipenv install --dev

For running the test suite with pytest:

# without coverage
BLOG_TEST=TRUE pipenv run pytest tests
# with coverage
BLOG_TEST=TRUE pipenv run pytest --cov blog.core tests

Alternatively, you may run your tests within a docker container using:

make build test test-clean

Migrations

This project leverages alley, forked from flask-mongoengine-migrations and built on top of mongoengine, for database migration. Database migrations can be ran automatically by toggling the environmental variable BLOG_RUN_MIGRATIONS:

BLOG_RUN_MIGRATIONS=TRUE pipenv run python -m blog

To test database migrations:

pipenv run pytest migration_tests

Alternatively, you may run your migrations tests within a docker container using:

make build test-migrations test-migrations-clean

Contributors

Basic contributing guidelines are as follows,

  • Any new features must be tested properly, both from an operational level and via mocked interaction.
  • New database changes require both a migration as well as a test.
  • Branches for bugs and features should be structured like so, issue-x-username.
  • Include your name and email in the contributors list.

Copyright (c) 2019 John Nolette Licensed under the MIT License.