Triangle CMS (Delta) is the Drexel Triangle's solution for a minimal WordPress replacement. Its core is a headless CMS (Content Management System) that serves and edits content from a local database. WordPress content can be migrated into Delta via the wordpress-etl tool. Delta also offers a minimal frontend similar to WordPress, and because the core CMS is headless, users are welcome to build their own. Delta also includes built-in logging, accessible through a Grafana dashboard, to enable easy monitoring of API calls and internal errors.
Triangle CMS is split into:
server/: Go backendfrontend/: React frontendobservability/: Loki + Promtail + Grafana config for logging purposesscripts/: setup scripts
API specification and response/data model documentation live in the project wiki:
- https://github.com/DrexelTriangle/triangle-cms/wiki
- Endpoints: https://github.com/DrexelTriangle/triangle-cms/wiki/Endpoints
- Response shapes/models: https://github.com/DrexelTriangle/triangle-cms/wiki/Response-Shapes
- Docker and Docker Compose
- Go 1.24+
- Node.js 20+ and npm
- Local clone of
wordpress-etl: https://github.com/DrexelTriangle/wordpress-etl
- Run the WordPress ETL pipeline locally in
wordpress-etlto generate source SQL files. - From
triangle-cmsrepo root, generate CMS SQL files:
./scripts/generate_wordpress_sql.shBy default, the script auto-detects ETL SQL if the wordpress-etl repository is in the same directory or the parent directory of triangle-cms:
../wordpress-etl/logs/sql./wordpress-etl/logs/sql
- Start only MariaDB in Docker (recommended for local backend development):
docker compose up -d mariadbThis starts:
- MariaDB (
mariadb) onlocalhost:${MARIADB_PORT_FORWARD:-3306}(container port3306), populated from the ETL pipeline output
If you want the full Docker stack (CMS + observability), run:
./scripts/setup-containers.shThis starts:
- MariaDB (
mariadb) onlocalhost:${MARIADB_PORT_FORWARD:-3306}(container port3306), populated from the ETL pipeline output - CMS backend (
cms) onhttps://localhost:8080which exposes the API - Promtail (
promtail) which collects logs from Docker containers - Loki (
loki) on port3100, which indexes logs from Promtail - Grafana (
http://localhost:3000, defaultUser:admin, Password:admin) which allows you to explore and query logs from Loki
Important:
- Logging/observability (Promtail, Loki, Grafana) is only available when running the full Docker stack.
- If you run the backend locally via
go run(instead of thecmsDocker container), those Docker logging pipelines do not apply to your local process.
Reset all compose volumes and rebuild:
Warning
Running this command will erase all logs and database entries, equivalent to a fresh install
./scripts/setup-containers.sh --reset-data- Ensure MariaDB is running (via Docker or local instance).
- If using Docker MariaDB and running the backend via
go run, configureserver/.envwith the same values used by your composeMARIADB_DATABASE,MARIADB_USER, andMARIADB_PASSWORD:
DB_NAME=triangle
DB_USER=triangle_user
DB_PASSWORD=triangle_password
DB_HOST=127.0.0.1
DB_PORT=3306If using a separate local MariaDB instance, configure server/.env for that instance instead.
- If the Docker
cmsservice is running, stop it first to avoid port conflict on:8080:
docker compose stop cms- Run backend:
cd server
go run ./main.goThe backend serves HTTPS on https://localhost:8080 using (the certs are just there to keep Postman happy):
server/certs/localhost.crtserver/certs/localhost.key
When you change Go backend code under server/ and want those changes reflected in the Docker cms container, rebuild and restart that service image:
docker compose up -d --build cmsIf your change is to MariaDB bootstrap SQL files in server/internal/database/wordpress_etl/, those are only applied on fresh DB initialization. Recreate volumes for those to take effect:
Warning
Running this command will erase all logs and database entries, equivalent to a fresh install
./scripts/setup-containers.sh --reset-datacd frontend
npm install
npm run devVite dev server starts on http://localhost:5173.
SQL files for bootstrap imports live in:
server/internal/database/wordpress_etl/
Before running this step, you must have already run the ETL pipeline in:
Generate CMS SQL files from the ETL pipeline output:
./scripts/generate_wordpress_sql.sh [source_sql_dir] [output_dir]Defaults:
- source: auto-detected (
../wordpress-etl,../wordpress-etl/logs/sql,./wordpress-etl,./wordpress-etl/logs/sql) - output: default (
server/internal/database/wordpress_etl)
Overrides:
- Pass explicit args:
./scripts/generate_wordpress_sql.sh ../wordpress-etl/logs/sql server/internal/database/wordpress_etl - Or use env vars:
WP_ETL_SQL_DIR=../wordpress-etl ./scripts/generate_wordpress_sql.shWP_ETL_OUT_DIR=server/internal/database/wordpress_etl ./scripts/generate_wordpress_sql.shWP_ETL_SQL_DIR=../wordpress-etl WP_ETL_OUT_DIR=server/internal/database/wordpress_etl ./scripts/generate_wordpress_sql.sh
Generated files:
01-authors.sql02-articles.sql03-articles-authors.sql04-seo.sql
These are mounted into MariaDB init at container startup through docker-compose.yml.
Backend tests:
cd server
go test ./...Test coverage:
Run to see the percentage of code each test covers
cd server
go test -coverprofile=cover.out ./...This also generates the cover.out file, showing exactly which lines are run during tests.