Git[Hub/Lab/ea] as a CMS

01 Jan 2022 - Genevieve Clifford

I’ve recently started work on cy, a replacement for cyhoeddwn (my pervasive display software) allowing for drop-in user management, i18n, and to overhaul a lot of code smell from a clean slate.

cy, like its predecessor is based around Ruby on Rails. I’ve been thinking about this closely coupled front-/back-end architecture a lot recently. I’ve been having a tinker with Vue, using it with Django (to provide a RESTful API) for ardudwy (which I’ve put on the back-burner for a while) where I’ve decoupled the front- and back-ends.

What I’ve been thinking about is the back-end specifically. The project I designed cyhoeddwn for was supervised by Stuart Nicholson at Swansea University who has previously worked on rural pervasive display networks, most notably on “Showboater”, deployed in rural Northumberland, North-East England.

I mainly focussed my project around HCI considerations, using software to reduce burden and to allow for better democratic inclusion (Clifford, 2021). To this end, on the software front, I designed a CMS with social media styling, to add familiarity to the use of existing social media use in the community. In parallel, I designed a static site using Jekyll (a static site generator, the same as this website uses), to allow hosting of community guidelines and community-written guides (i.e. for deployment), in the style of the Community Technology field guide used in the Equitable Internet Initiative championed by the Detroit Community Technology Project.

The static site is maintained through the community interacting with the project’s GitHub repository and is built using GitHub Actions — a CI/CD platform using Docker containers — and is then hosted using GitHub Pages. The application itself is hosted using Heroku, I am not sure exactly what their architecture entails. Rails apps typically are hosted behind a web server (like Apache or NGINX) which proxy their non-static requests to the Rails app running in a Rack compatible server. The app also uses a PostgreSQL database (for Active Record), a Redis database (for queue management using Resque, an adapter for Active Job) and AWS S3 (for hosting media referenced through Active Storage).

While a community could feasibly run cyhoeddwn though a cloud provider, in a VPS, or self-hosted, there is a minimal but real monthly cost to hosting. In the design of ardudwy, I am thrilled that my Vue frontend can be deployed through GitLab Pages at zero cost to me, but the need for a VPS or cloud platform to run the Django backend irks me.

As I say, I have been thinking about this for a while, but it was this morning that I had the lightbulb moment. I had read the following quote from Nicholson et al. (“Showboater,” 2019) while researching for my project, I hadn’t considered implementing it, but the thought has continued to circulate in my brain since early-April:

To minimize barriers, services can be decoupled as much as possible from the requirements of bespoke platforms: a shift towards unplatformed design, making use of existing, common public services. One possible direction for this would be to replace the database and bespoke API with a user interface layer over an existing version control repository (e.g. git version control platforms such as GitHub and GitLab). Such systems are typically free for public/open use and would provide the heavy lifting for user access control, resource storage, serving of web resources, and provide the necessary APIs such that the front-end CMS and display clients could be adapted.

(Nicholson et al., 2019, p. 6)

Instead of using a backend utilising an object-relational mapper as implemented, the ability to host content with a user-accessible CMS could be leveraged by a third-party like GitHub or GitLab, as suggested by Nicholson et al. The way I interpret Nicholson’s recommendation is that the API of the remote repository service should be leveraged by some JavaScript front-end (like Vue or React), therefore I propose the following:

Using a remote Git repository as a CMS

The proposed architecture for the system consists of two front-end applications (for managing content upload and moderation and for displaying content on connected displays) and a Git repository for hosting content, as shown in Figure 1.

Figure 1: Overall application architecture

In effect, the frontend CMS acts as a mask, simplifying remote Git repository features into much more lay-user friendly concepts. This translates, for example, a post on the CMS into a pull request with commit on the repository. CI/CD builds are run on the repository to generate a static API which can be consumed by the display app on connected displays. Each of these features will be explained in subsequent subsections.

Content management system

Assume a user Bob would like to publish a new post containing information about a car boot sale he’s organising in his community. Bob makes a post using the CMS app as shown in Figure 2 and shown in sequence form in Figure 3. The community like his post, an administrator approves it for display, where it appears on displays the next day. More detailed information flows are given in Figures 3a and 3b.

Wireframe mock-up of frontend CMS

Figure 2: Frontend CMS example for scenario

Figure 3: Simplified sequence diagram of typical user interaction flow

Figure 3a: Detail view of red box in Figure 2

Figure 3b: Detail view of green box in Figure 2

Repository-level data structures

When Bob submits a post, the CMS application generates a pull request with an associate commit. This commit creates a directory in the root of the repository with a UUID as directory name as shown in Snippet 1, the media that Bob submitted with his original post is moved into this directory and renamed with UUIDs. The metadata collected from Bob in Figure 2 is bundled into a JSON file (manifest.json).

[root]
     |
     `---[uuid]---,
                  |
                  `--- manifest.json
                  `--- 4eb17cc8-2bd5-4281-8849-169c1e001080.png
                  `--- 938ad446-b8ec-4722-b7d1-fe213a123729.png
Snippet 1: Generated commit directory structure
{
  "author": "bobworling32",
  "collection": "Upper Framlingham car boot sale January 2022",
  "tags": [
    "upper-framlingham",
    "business"
  ],
  "date-published": "2022-01-01T13:59:43Z",
  "items": [
    {
      "uuid": "4eb17cc8-2bd5-4281-8849-169c1e001080",
      "description": "Upper Framlingham boot sale flyer",
      "tags": "flyer",
    },
    {
      "uuid": "938ad446-b8ec-4722-b7d1-fe213a123729",
      "description": "Upper Framlingham boot sale poster",
      "tags": "poster",
    }
  ]
}
Snippet 2: Generated manifest (manifest.json)

Once per day, a CI/CD build is called on the main branch of the repository. Assuming there are changes to the repository, a static site builder generates a series of JSON files in a directory that mimics a dynamic API and moves photos to a common directory (Snippet 3)1. These assets are then served from the repository’s site feature (i.e. GitHub/GitLab pages) as a static API, which can be queried/consumed as if it were a dynamic API (Snippet 4).

[root]
     |
     `---assets---,
     |            |
     |            `4eb17cc8-2bd5-4281-8849-169c1e001080.png
     |            `938ad446-b8ec-4722-b7d1-fe213a123729.png
     |
     `---api/v1/posts/tags---,
                             |
                             `---upper-framlingham---,
                             |                       |
                             |                       `index.json
                             |
                             `---business---,
                             |              |
                             |              `index.json
                             |
                             `---poster---,
                             |            |
                             |            `index.json
                             |
                             `---flyer---,
                                         |
                                         `index.json
                             
Snippet 3: Generated commit directory structure

Request:

GET https://example.com/api/v1/posts/tags/upper-framlingham

Response:

Status: 200 OK
[
  {
    "author": "bobworling32",
    "collection": "Upper Framlingham car boot sale January 2022",
    "tags": [
      "upper-framlingham",
      "business",
      "flyer",
    ],
    "description": "Upper Framlingham boot sale flyer",
    "url": "https://example.com/assets/4eb17cc8-2bd5-4281-8849-169c1e001080.png",
    "date-published": "2022-01-01T13:59:43Z"
  },
  {
    "author": "bobworling32",
    "collection": "Upper Framlingham car boot sale January 2022",
    "tags": [
      "upper-framlingham",
      "business",
      "poster",
    ],
    "description": "Upper Framlingham boot sale poster",
    "url": "https://example.com/assets/938ad446-b8ec-4722-b7d1-fe213a123729.png",
    "date-published": "2022-01-01T13:59:43Z"
  }
]
Snippet 4: Static API request created from all manifests using static-site generator through CI/CD

Display application

There is relatively little to remark here, as this will be largely similar to the way cyhoeddwn handles displaying content. Each display will run the web application, and preferences (i.e. content with tags that should be displayed) will be stored in cookies. The site will consume the static API using either a web framework or a simple webpage with vanilla JavaScript.

Miscellanea

Implied within the architecture of the application is a remote authentication system, as there is no recourse to store credentials in a static web application. I propose the use of OAuth2: Figure 42 shows an OAuth2 grant for the system, the OAuth2 provider will be either GitHub or GitLab, as this will allow access to their respective APIs for repository, issues, discussions, or other such facilities, as shown in the diagram.

Figure 4: OAuth2 sequence diagram for application

Footnotes

  1. api/v1/posts/tags is shorthand for a nested set of directories (i.e. api contains v1, etc.) 

  2. Adapted from (Mosmans, 2017) 

References

  1. Mosmans, P. (2017). Use Emacs to create OAuth 2.0 UML sequence diagrams. Blog. https://www.onwebsecurity.com/configuration/use-emacs-to-create-oauth-2-0-uml-sequence-diagrams.html
  2. Nicholson, S., Jackson, D., Clear, A., & Olivier, P. (2019). Showboater. Proceedings of the 8th ACM International Symposium on Pervasive Displays. https://doi.org/10.1145/3321335.3324948
  3. Clifford, G. (2021). Designing Rural Pervasive Display Networks for Distributed and Democratic Governance [Master’s Thesis]. Computational Foundry, Swansea University.

The Information Exchange is the personal website of Genevieve Clifford, made with love between 2019 and 2022.
Creative Commons Licence
Settings.
Privacy Policy.