Skip to content

A document-based headless CMS with API + back office

Notifications You must be signed in to change notification settings

JulianCataldo/paper-cms

Repository files navigation

📑  Paper CMS  📝

A document-based headless CMS with API + back office.

Made primarly for editorial content management.
Stock definitions are modelled after Schema.org vocabulary.



🏁  Usage

Live demo

👉  Check here for a Paper CMS online tryout.
🔁  Default dataset is reseted periodically.
🔑  Use good ol' admin / password.

or… local quick start 📦

# Clone this repo
git clone git@github.com:JulianCataldo/paper-cms.git
cd paper-cms
code ./

# Install all dependencies
npm run deps:i

# Run and watch API + back office
npm run dev

# Open back office in browser
open http://localhost:7777

# Default credentials
# > User : `admin`
# > Password : `password`

# Edit API definition
code -r ./models/api-v1.yaml

# Created entries and uploaded medias + variations
# are stored in root project by default in `.data`
tree ./.data

or… with Docker Compose 🐳

cd ./docker
make rdc-std

-
# Data persistence folder
tree ./docker/std/.volume/.data

See the Makefile for all containers variants and shorthands.

Some views  👀

❄️  Features

Auto-generated forms from OpenAPI definitions

auto-formgen.mp4

JSON Schema validation on run-time for both client and server sides

client-validation.mp4

Browser and everything in between cannot be trusted for data transmission.
In Paper CMS, both sides are taken care of, thanks to AJV and JSON Schema specs.

For example, you might need to do manual data transformation, directly on entry files, skipping the back-office.
Use cases : search and replace, refactoring fields names, automatic external data ingestion…
Everytime a file is read OR written, it will pass through schema validation, so no silent models shifting while refactoring them. additionalProperties: false can be leveraged to be warn about orphaned fields.

UI Schema augmentation (non-standard)

ui-schema.mp4

Pure JSON Schema isn't designed (yet?) for declarative UI parameters.
Special dialects to cater for this are experimented here and here.
In Paper CMS, we're using react-jsonschema-form so see
General uiSchema Reference - react-jsonschema-form documentation

Settings are splitted in two different schemas at runtime.
Standard compliant JSON Schema alongside non standard UI Schema.

JSON files database

All your textual data is hosted unded ./data/docs (root can be overriden with PAPER_DATA_DIR).
Run multiple instances of Paper CMS easily by setting different PAPER_PORT.
Quickly swip datasets : demo / tests / staging / production…

Image(s) upload + dynamic resize while fetching + caching

JWT authentication for both human editors and API consumers

auth.mp4

Full live reload for models / client / server

When editing model definitions, server or client code, everything is re-runned/builded/bundled. Also, browser is automatically refreshed thanks to a long polling web socket.

You can also trigger browser refresh by hitting curl localhost:${PAPER_PORT:-7777}/reload.

Document revisions history

Soft deletion, restore from trash

"Has many" relationship via references

has-many-diverse-refs.mp4

When using an array of items for a property, and thanks to a special:
'ui:field': 'hyperlink'
combined with a:
pattern: '/(Person|Organization|Whatever)/(.*).json'
you will get an "has-many" heterogeneous references dynamic widget.
See the api-v1.yaml for full examples.

Nested referenced documents editing

A "sub" document is just another document reference.
There is no hierarchy or inheritance, only cross-references betweens entities.
While you can create, edit or associate an existing document from the main document form, only $refs URI for child references actually gets recorded, not the entire subtree.

Everything can be a document collection, as soon as it make sense to reference the entity from elsewhere. Self-sufficient data should live embedded in the document itself.

There is no limit for nesting, though stacked modal editors force us to save only one (sub)document at a time: no full tree update propagation. This design choice is meant to prevent data-loss / infinite recursive nesting.

Schema.org inspired default definitions

While Schema.org states that:

The type hierarchy presented on this site is not intended to be a 'global ontology' of the world.

While true, it provides a great starting point with commonly used taxonomies and entities relationships.
Schema.org is shining for content-driven web sites where SEO is crucial (but that's just an excuse).
While still in its infancy, JSON/LD is already bringing HATEOS concepts into real-life applications, step-by-step, organically.
Regardless, feature-rich widgets are democratizing, and data crawling between third-parties is becoming more insightful.

OpenAPI 3 UI (Swagger)

The endgoal is to make Paper CMS a non-deterministic data ingestion / organization / retrieval system.
At the end, an hypermedia API should be auto-discoverable.
Still, it's useful to rely on Swagger niceties for on-boarding an API.
Moreover, the OpenAPI 3 specs is adding links, a notable concept for Hypermedia minded APIs.

Wysiwyg HTML Editor

richt-text-editor.mp4

For rich text editing, Paper CMS uses Quill under the hood.
Basic additional HTML sanitization is done server-side.

Custom fields and widgets

🤔  Why?

Inspirations

We are seeing the emergence of different patterns in the content management world like:

  • Shared entities vocabularies
  • Automatic web form generation
  • Isomorphic user input runtime validation,
  • Entity / Collection oriented information architecture
  • Headless non-opinionated CMS APIs for JAMStack consumption
  • Hypermedia for non-deterministic data fetching via refs. | links | URIs
  • Plain files data storage for operations convenience (with some trade-offs)
  • Conventions over configuration, with extendabilities (models, widgets…)

Goals

All these concepts are explored at different levels of implementations in Paper CMS.
While it's still an experiment, the end-goal is to provide a lightweight solution suitable for projects which:

  • Has ten-ish max. editors in charge
  • Needs moderate authoring concurrency with silo-ed document edits
  • Might needs frequent content updates
  • Low needs for user-land data input
  • Are in an eco-system of focused services where caching / CDNs / whatever are dedicated

To sum up: Paper CMS is good for editors-driven web sites, but is not a good fit for users-driven web apps.

ℹ️  Project insights

Warning: Work in progress: NOT FOR PRODUCTION

Structure

Mono-repo. glued with PNPM recursive modules installation.

Major dependencies

  • Node.js
  • ESBuild
  • React
  • MUI
  • React Quill
  • JSON Schema React
  • AJV
  • Express
  • Sharp

Work in progress

  • Single media management
  • Batch media management
  • JSON/LD API output conformance
  • OpenAPI conformance
  • Swagger integration
  • Wider Schema.org support for stock definitions
  • Basic users account management

To do

  • Media metadata with EXIF + IPTC support
  • API collections' pagination
  • Automatic bi-directional relationships ("Is part of many" <=> "Has many")
  • Custom data fetching and population widgets for APIs / social networks content retrieval

Features ideas

  • Might propose fully dynamic OpenAPI GUI builder right inside the back office, instead OR alongside YAML config.
  • Might propose MongoDB/pSQL/S3 alternative to bare file storage, for increased mass and/or concurrency needs, but decreased portability.
  • Might propose an option for storing media binaries in an S3 bucket.

    ,ggggggggggg,
    dP""88""""""Y8,
    Yb, 88      `8b
    `"  88      ,8P
        88aaaad8P"
        88""""",gggg,gg  gg,gggg,     ,ggg,    ,gggggg,
        88    dP"  "Y8I  I8P"  "Yb   i8" "8i   dP""""8I
        88   i8'    ,8I  I8'    ,8i  I8, ,8I  ,8'    8I
        88  ,d8,   ,d8b,,I8 _  ,d8'  `YbadP' ,dP     Y8,
        88  P"Y8888P"`Y8PI8 YY88888P888P"Y8888P      `Y8
                          I8
                          I8       ,gggg,   ,ggg, ,ggg,_,ggg,        ,gg,
                          I8     ,88"""Y8b,dP""Y8dP""Y88P""Y8b      i8""8i
                          I8    d8"     `Y8Yb, `88'  `88'  `88      `8,,8'
                          I8   d8'   8b  d8 `"  88    88    88       `88'
                          I8  ,8I    "Y88P'     88    88    88       dP"8,
                              I8'               88    88    88      dP' `8a
                              d8                88    88    88     dP'   `Yb
                              Y8,               88    88    88 _ ,dP'     I8
                              `Yba,,_____,      88    88    Y8,"888,,____,dP
                                `"Y8888888      88    88    `Y8a8P"Y88888P"

👉  www.JulianCataldo.com

About

A document-based headless CMS with API + back office

Resources

Stars

Watchers

Forks

Packages

No packages published