# yorknouse/website architecture

> Architecture diagram of the GitHub repository yorknouse/website, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-06-24). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/yorknouse/website
- Repository: https://github.com/yorknouse/website

## Overview

1. **Overall shape**
   This repo is a mixed legacy-and-migration codebase for the Nouse website. It contains:
   - a legacy PHP CMS/runtime under `html/`
   - a newer Next.js admin/backend app in `edit-backend/`
   - a newer Astro public site in `readers-frontend/`
   - Docker and ops assets for running the full stack locally and in production.

2. **Request routing and edge**
   Caddy sits in front of Apache to terminate SSL and proxy traffic. Apache still owns most URL routing for the legacy PHP site, which is why the project explicitly avoids a conventional application router. The Docker setup and `docker-compose.yml` are the main runtime boundary for the whole system.

3. **Legacy PHP application**
   The old CMS is organized around `html/admin`, `html/default`, and `html/common`.  
   - `html/admin` is the editorial/admin dashboard
   - `html/default` is the public site entrypoint
   - `html/common` holds shared PHP classes, templates, and helpers  
   The admin side uses page-rendered Twig templates plus jQuery-triggered API calls for mutations, rather than a single SPA-style UI.

4. **New admin/backend application**
   `edit-backend/` is the modern TypeScript replacement path. It uses Next.js App Router with:
   - server-rendered admin pages under `src/app/(app)`
   - auth flows under `src/app/(auth)`
   - JSON APIs under `src/app/api/*`  
   This app exposes both admin-facing endpoints and “frontend” content endpoints, so it acts as the central API layer for newer clients.

5. **Public reader site**
   `readers-frontend/` is the new public-facing site, built with Astro and some embedded SolidJS components. It renders pages such as articles, authors, editions, categories, search, and static informational pages. It is structured as a content consumer, pulling data from backend APIs rather than owning content state itself.

6. **Primary data store**
   MySQL is the core system of record. The newer backend uses Prisma (`edit-backend/prisma/schema.prisma`, migrations, seed data) to model and access the database. The legacy PHP code also interacts with the same underlying database directly, so both generations of the app share the same content/data boundary.

7. **Cache and supporting state**
   The new backend has explicit cache and Redis modules (`src/lib/cache.ts`, `src/lib/redis.ts`), indicating some read-heavy endpoints or session/state helpers are cached outside MySQL. This matters for architecture because many public-content reads are served from API layers that likely avoid hitting the database every time.

8. **Media and file upload flow**
   Uploaded assets are handled through signed uploads to object storage rather than being stored in the app containers. The backend exposes endpoints for:
   - generating upload signatures
   - completing uploads
   - resolving file URLs  
   The repo’s Backblaze B2 / S3 setup and `cors.json` show that object storage is an external dependency for user avatars, article media, and other files.

9. **Authentication and permissions**
   Authentication is split across generations:
   - the new backend uses NextAuth-style routes plus OAuth/authorise/token endpoints
   - the legacy PHP side has its own auth helpers in `html/common` and admin auth pages  
   The admin area also includes user management, suspension, permissions, and audit-log functionality, so authorization is a meaningful subsystem rather than a thin login gate.

10. **Scheduled and background work**
   There are cron-style jobs in both stacks:
   - PHP cron scripts under `html/cron`
   - TypeScript cron scripts under `edit-backend/scripts`
   - Docker-side cron helpers in `docker/cron-minute.sh` and `docker/job-cron`  
   These handle maintenance tasks such as updating article read summaries and other periodic backend work.

11. **Operational and observability stack**
   The repo includes production support for Grafana and Netdata, both provisioned via Docker configs. Grafana dashboards are predeclared as JSON and wired to MySQL, while Netdata provides host/service monitoring. The readme also mentions phpMyAdmin access for database administration.

12. **Deployment and rebuild path**
   Deployments are container-based, with separate Dockerfiles for the PHP, Astro, and React/Next-style apps. There is also a webhook-driven rebuild flow used by the admin side to trigger image rebuilds and restart services when static or generated pages need refreshing. That makes “build/redeploy” part of the content publishing workflow, not just an ops concern.

Main entry points: [`docker/mysql/mysql.conf`](https://github.com/yorknouse/website/blob/main/docker/mysql/mysql.conf) (MySQL), [`docker-compose.yml`](https://github.com/yorknouse/website/blob/main/docker-compose.yml) (Compose stack), [`edit-backend/src/app/api/frontend/articles/route.ts`](https://github.com/yorknouse/website/blob/main/edit-backend/src/app/api/frontend/articles/route.ts) (Backend APIs), [`Dockerfile.php-df`](https://github.com/yorknouse/website/blob/main/Dockerfile.php-df) (Apache/PHP)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_ops["Ops & runtime"]
  node_caddy["Caddy<br/>edge proxy"]
  node_apache["Apache/PHP<br/>web runtime<br/>[Dockerfile.php-df]"]
  node_compose["Compose stack<br/>deployment<br/>[docker-compose.yml]"]
  node_grafana["Grafana<br/>monitoring<br/>[home.json]"]
  node_netdata["Netdata<br/>monitoring<br/>[netdata.conf]"]
end

subgraph group_legacy["Legacy PHP site"]
  node_legacy_admin["Legacy admin<br/>PHP CMS<br/>[index.php]"]
  node_legacy_public["Legacy site<br/>PHP public<br/>[index.php]"]
  node_legacy_common["Shared PHP<br/>helpers/templates<br/>[config.php]"]
  node_legacy_admin_api["Legacy API<br/>PHP endpoints<br/>[apiHead.php]"]
  node_legacy_cron["PHP cron<br/>scheduled jobs"]
end

subgraph group_backend["Next backend"]
  node_backend_app["Next app<br/>admin web app<br/>[page.tsx]"]
  node_backend_api["Backend APIs<br/>JSON APIs<br/>[route.ts]"]
  node_backend_lib["Backend lib<br/>service layer<br/>[prisma.ts]"]
  node_backend_prisma["Prisma model<br/>schema/migrations<br/>[schema.prisma]"]
  node_backend_cron["TS cron<br/>scheduled jobs<br/>[cron.ts]"]
end

subgraph group_reader["Reader site"]
  node_reader_site["Astro site<br/>public frontend<br/>[index.astro]"]
  node_reader_components["Reader UI<br/>UI components<br/>[Navbar.astro]"]
  node_reader_pages["Reader pages<br/>routes<br/>[[slug].astro]"]
end

subgraph group_data["Shared data"]
  node_mysql[("MySQL<br/>system of record<br/>[mysql.conf]")]
  node_redis[("Redis<br/>cache/state<br/>[redis.ts]")]
  node_object_store[("Object storage<br/>uploads/media<br/>[s3.ts]")]
end

node_compose -->|"runs"| node_caddy
node_compose -->|"runs"| node_apache
node_compose -->|"runs"| node_grafana
node_compose -->|"runs"| node_netdata
node_caddy -->|"proxies"| node_apache
node_apache -->|"serves"| node_legacy_admin
node_apache -->|"serves"| node_legacy_public
node_legacy_admin -->|"uses"| node_legacy_common
node_legacy_public -->|"uses"| node_legacy_common
node_legacy_admin -->|"calls"| node_legacy_admin_api
node_legacy_admin_api -->|"reads/writes"| node_mysql
node_legacy_common -->|"queries"| node_mysql
node_legacy_cron -->|"updates"| node_mysql
node_backend_app -->|"uses"| node_backend_api
node_backend_api -->|"calls"| node_backend_lib
node_backend_lib -->|"models"| node_backend_prisma
node_backend_prisma -->|"maps to"| node_mysql
node_backend_lib -->|"caches"| node_redis
node_backend_api -->|"uploads"| node_object_store
node_backend_cron -->|"maintains"| node_mysql
node_reader_site -->|"composes"| node_reader_components
node_reader_site -->|"routes"| node_reader_pages
node_reader_site -->|"fetches"| node_backend_api
node_reader_components -->|"fetches"| node_backend_api
node_grafana -->|"observes"| node_mysql
node_netdata -->|"monitors"| node_compose

click node_caddy "https://github.com/yorknouse/website/tree/main/docker/caddy/Caddyfile"
click node_apache "https://github.com/yorknouse/website/blob/main/Dockerfile.php-df"
click node_compose "https://github.com/yorknouse/website/blob/main/docker-compose.yml"
click node_grafana "https://github.com/yorknouse/website/blob/main/docker/grafana/dashboards/home.json"
click node_netdata "https://github.com/yorknouse/website/blob/main/docker/netdata/config/netdata.conf"
click node_legacy_admin "https://github.com/yorknouse/website/blob/main/html/admin/index.php"
click node_legacy_public "https://github.com/yorknouse/website/blob/main/html/default/index.php"
click node_legacy_common "https://github.com/yorknouse/website/blob/main/html/common/config.php"
click node_legacy_admin_api "https://github.com/yorknouse/website/blob/main/html/admin/api/apiHead.php"
click node_legacy_cron "https://github.com/yorknouse/website/blob/main/html/cron/updateArticlesReadsSummary.php"
click node_backend_app "https://github.com/yorknouse/website/blob/main/edit-backend/src/app/(app)/page.tsx"
click node_backend_api "https://github.com/yorknouse/website/blob/main/edit-backend/src/app/api/frontend/articles/route.ts"
click node_backend_lib "https://github.com/yorknouse/website/blob/main/edit-backend/src/lib/prisma.ts"
click node_backend_prisma "https://github.com/yorknouse/website/blob/main/edit-backend/prisma/schema.prisma"
click node_backend_cron "https://github.com/yorknouse/website/blob/main/edit-backend/scripts/cron.ts"
click node_reader_site "https://github.com/yorknouse/website/blob/main/readers-frontend/src/pages/index.astro"
click node_reader_components "https://github.com/yorknouse/website/blob/main/readers-frontend/src/components/Navbar.astro"
click node_reader_pages "https://github.com/yorknouse/website/blob/main/readers-frontend/src/pages/articles/[year]/[month]/[day]/[slug].astro"
click node_mysql "https://github.com/yorknouse/website/blob/main/docker/mysql/mysql.conf"
click node_redis "https://github.com/yorknouse/website/blob/main/edit-backend/src/lib/redis.ts"
click node_object_store "https://github.com/yorknouse/website/blob/main/edit-backend/src/lib/s3.ts"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_caddy,node_apache,node_compose,node_grafana,node_netdata toneBlue
class node_legacy_admin,node_legacy_public,node_legacy_common,node_legacy_admin_api,node_legacy_cron toneAmber
class node_backend_app,node_backend_api,node_backend_lib,node_backend_prisma,node_backend_cron toneMint
class node_reader_site,node_reader_components,node_reader_pages toneRose
class node_mysql,node_redis,node_object_store toneIndigo
```

## Components

### Ops & runtime

Container, proxy, and observability boundary for the whole stack.

- [Caddy](https://github.com/yorknouse/website/tree/main/docker/caddy/Caddyfile): edge proxy · Terminates SSL and proxies traffic into the stack. · `docker/caddy/Caddyfile`
- [Apache/PHP](https://github.com/yorknouse/website/blob/main/Dockerfile.php-df): web runtime · Legacy HTTP and PHP serving layer behind the edge proxy. · `Dockerfile.php-df`
- [Compose stack](https://github.com/yorknouse/website/blob/main/docker-compose.yml): deployment · Main container boundary coordinating the full local/production stack. · `docker-compose.yml`
- [Grafana](https://github.com/yorknouse/website/blob/main/docker/grafana/dashboards/home.json): monitoring · Preconfigured dashboards and datasource provisioning for metrics and DB views. · `docker/grafana/dashboards/home.json`
- [Netdata](https://github.com/yorknouse/website/blob/main/docker/netdata/config/netdata.conf): monitoring · Host/service monitoring configuration for runtime health. · `docker/netdata/config/netdata.conf`

### Legacy PHP site

Older CMS/runtime that still serves the main site and admin flows.

- [Legacy admin](https://github.com/yorknouse/website/blob/main/html/admin/index.php): PHP CMS · Editorial/admin interface for the older site generation. · `html/admin/index.php`
- [Legacy site](https://github.com/yorknouse/website/blob/main/html/default/index.php): PHP public · Public site entrypoint and legacy URL handling. · `html/default/index.php`
- [Shared PHP](https://github.com/yorknouse/website/blob/main/html/common/config.php): helpers/templates · Common PHP config, auth, classes, and shared templates. · `html/common/config.php`
- [Legacy API](https://github.com/yorknouse/website/blob/main/html/admin/api/apiHead.php): PHP endpoints · Mutation endpoints for articles, comments, permissions, files, and rebuilds. · `html/admin/api/apiHead.php`
- [PHP cron](https://github.com/yorknouse/website/blob/main/html/cron/updateArticlesReadsSummary.php): scheduled jobs · Legacy scheduled maintenance and article summary updates. · `html/cron/updateArticlesReadsSummary.php`

### Next backend

Modern TypeScript admin and API layer.

- [Next app](https://github.com/yorknouse/website/blob/main/edit-backend/src/app/(app)/page.tsx): admin web app · Modern Next.js App Router application for admin and auth flows. · `edit-backend/src/app/(app)/page.tsx`
- [Backend APIs](https://github.com/yorknouse/website/blob/main/edit-backend/src/app/api/frontend/articles/route.ts): JSON APIs · Central API layer for admin, auth, upload, and frontend-content endpoints. · `edit-backend/src/app/api/frontend/articles/route.ts`
- [Backend lib](https://github.com/yorknouse/website/blob/main/edit-backend/src/lib/prisma.ts): service layer · Shared backend services for auth, cache, Prisma, Redis, S3, and content logic. · `edit-backend/src/lib/prisma.ts`
- [Prisma model](https://github.com/yorknouse/website/blob/main/edit-backend/prisma/schema.prisma): schema/migrations · Database schema, migrations, and seed data for the newer backend. · `edit-backend/prisma/schema.prisma`
- [TS cron](https://github.com/yorknouse/website/blob/main/edit-backend/scripts/cron.ts): scheduled jobs · TypeScript maintenance and periodic backend jobs. · `edit-backend/scripts/cron.ts`

### Reader site

New public-facing Astro frontend.

- [Astro site](https://github.com/yorknouse/website/blob/main/readers-frontend/src/pages/index.astro): public frontend · Public reader experience built with Astro and embedded SolidJS components. · `readers-frontend/src/pages/index.astro`
- [Reader UI](https://github.com/yorknouse/website/blob/main/readers-frontend/src/components/Navbar.astro): UI components · Shared Astro and SolidJS building blocks for article, navbar, search, and listing pages. · `readers-frontend/src/components/Navbar.astro`
- [Reader pages](https://github.com/yorknouse/website/blob/main/readers-frontend/src/pages/articles/[year]/[month]/[day]/[slug].astro): routes · Page routes for articles, authors, editions, categories, search, and static pages. · `readers-frontend/src/pages/articles/[year]/[month]/[day]/[slug].astro`

### Shared data

Database, cache, and object storage dependencies used across generations.

- [MySQL](https://github.com/yorknouse/website/blob/main/docker/mysql/mysql.conf): system of record · Shared relational database used by both legacy PHP and the new backend. · `docker/mysql/mysql.conf`
- [Redis](https://github.com/yorknouse/website/blob/main/edit-backend/src/lib/redis.ts): cache/state · Backend cache and lightweight state store. · `edit-backend/src/lib/redis.ts`
- [Object storage](https://github.com/yorknouse/website/blob/main/edit-backend/src/lib/s3.ts): uploads/media · External file storage for signed uploads, avatars, and article media. · `edit-backend/src/lib/s3.ts`

## Connections

- Compose stack → Caddy: runs · Compose provisions the edge proxy.
- Compose stack → Apache/PHP: runs · Compose provisions the legacy PHP runtime.
- Compose stack → Grafana: runs · Compose provisions observability services.
- Compose stack → Netdata: runs · Compose provisions host monitoring.
- Caddy → Apache/PHP: proxies · SSL edge routes traffic to Apache and the legacy app.
- Apache/PHP → Legacy admin: serves · Apache hosts the admin CMS.
- Apache/PHP → Legacy site: serves · Apache hosts the public PHP site.
- Legacy admin → Shared PHP: uses · Admin pages share common PHP auth/config/helpers.
- Legacy site → Shared PHP: uses · Public site reuses shared PHP runtime pieces.
- Legacy admin → Legacy API: calls · Admin UI triggers legacy mutation endpoints.
- Legacy API → MySQL: reads/writes · Legacy admin endpoints persist directly to MySQL.
- Shared PHP → MySQL: queries · Shared PHP code reads the same database boundary.
- PHP cron → MySQL: updates · Legacy cron jobs maintain content-derived summaries.
- Next app → Backend APIs: uses · Server-rendered pages depend on backend endpoints and shared services.
- Backend APIs → Backend lib: calls · API routes are implemented through backend service modules.
- Backend lib → Prisma model: models · Prisma defines the backend database schema layer.
- Prisma model → MySQL: maps to · Prisma persists the new backend model into MySQL.
- Backend lib → Redis: caches · Backend services use Redis for cache/state.
- Backend APIs → Object storage: uploads · Signed upload and URL endpoints integrate with object storage.
- TS cron → MySQL: maintains · Backend cron jobs perform periodic database work.
- Astro site → Reader UI: composes · Astro pages are built from shared UI components.
- Astro site → Reader pages: routes · Reader site page structure covers content routes.
- Astro site → Backend APIs: fetches · Public pages consume backend frontend APIs.
- Reader UI → Backend APIs: fetches · Interactive components query the backend for content and search data.
- Grafana → MySQL: observes · Dashboards are wired to database metrics and views.
- Netdata → Compose stack: monitors · Netdata monitors the containerized runtime.
