Add all wiki pages: Architecture, Deployment, Admin Guide, Study System, API Reference

nmemmert committed 2026-09-03 17:05:26 -04:00
1 parent a0203988ab
commit 7d48c0bc4e
6 files changed
+389 -1

No files matched your search

+96
@@ -0,0 +1,96 @@
# API Reference
All endpoints are at port **4173**. Study endpoints require a study session cookie. Admin endpoints require an admin session cookie.
## Authentication
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | `/api/study-auth/signup` | — | Create study account |
| POST | `/api/study-auth/login` | — | Sign in, sets session cookie |
| POST | `/api/study-auth/logout` | Study | Sign out |
| GET | `/api/study-auth/status` | — | Auth state + enrolled slugs |
## Study Data
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/api/study-community?studySlug=` | Study | List community posts |
| POST | `/api/study-community/posts` | Study | Create a post |
| POST | `/api/study-community/posts/:id/replies` | Study | Reply to a post |
| POST | `/api/study-community/posts/:id/like` | Study | Toggle like |
| GET | `/api/study-notes/:noteId` | Study | Get a note |
| PUT | `/api/study-notes/:noteId` | Study | Save a note |
| GET | `/api/study-quiz/:studySlug/:sectionId` | Study | Get quiz answers |
| POST | `/api/study-quiz/:studySlug/:sectionId` | Study | Save quiz answers |
| GET | `/api/study-progress/:studySlug` | Study | Get progress |
| POST | `/api/study-progress/:studySlug/:sectionId/complete` | Study | Mark section complete |
## Study Account
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/api/study-account` | Study | Get profile |
| PATCH | `/api/study-account` | Study | Update display name, avatar, preferences |
| POST | `/api/study-account/enroll` | Study | Enroll in a study |
| DELETE | `/api/study-account` | Study | Delete own account |
## Comments (per-lesson, separate from community feed)
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/api/study-comments/:studySlug/:sectionId` | — | Public approved comments |
| POST | `/api/study-comments/:studySlug/:sectionId` | Study | Post a comment |
| DELETE | `/api/study-comments/:studySlug/:sectionId/:id` | Study | Delete own comment |
## Certificates
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/api/study-certificate/:studySlug` | Study | Get own certificate |
## Public / Content
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/api/admin-content` | — | Public site content |
| GET | `/api/episodes` | — | Episode list |
| GET | `/api/bible-passage` | — | Fetch a Bible passage (BSB) |
| GET | `/api/episode-scripts/search?q=` | — | Full-text script search |
| GET | `/sitemap.xml` | — | Dynamic sitemap |
| GET | `/feed.xml` | — | Podcast RSS feed |
## Contact / Questions
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | `/api/contact` | — | Submit a contact form |
| GET | `/api/questions` | — | Public approved Q&A |
| POST | `/api/questions` | — | Submit a question |
## Admin
All admin routes require an admin session cookie.
| Method | Path | Description |
|--------|------|-------------|
| POST | `/api/admin-auth/login` | Admin sign in |
| POST | `/api/admin-auth/logout` | Admin sign out |
| GET/POST/PATCH | `/api/admin-content` | Read/write site content |
| GET/POST/DELETE | `/api/admin-assets` | Upload/list/delete assets |
| GET | `/api/admin/study-users` | List all study users |
| PATCH | `/api/admin/study-users/:id` | Edit a user |
| DELETE | `/api/admin/study-users/:id` | Delete a user |
| POST | `/api/admin/study-users/:id/send-reengagement` | Trigger re-engagement email |
| GET | `/api/admin-backup` | List backups |
| POST | `/api/admin-backup/restore` | Restore a backup |
| GET/POST/DELETE | `/api/admin-episode-scripts/:n` | Manage episode scripts |
| GET/POST/PATCH/DELETE | `/api/webhooks` | Manage outgoing webhooks |
## Analytics
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/api/analytics/hits` | Admin | Page hit stats |
| GET | `/api/analytics/visitors` | Admin | Visitor stats |
| POST | `/api/analytics/event` | — | Track a custom event |
+59
@@ -0,0 +1,59 @@
# Admin Guide
The admin panel lives at `/admin`. Sign in with the `ADMIN_PASSWORD` environment variable.
> ⚠️ Change the default password (`changeme`) before going to production.
## Sections
### Site Content
Edit global site settings: SEO metadata, canonical URL, Ko-fi URL, legal pages (Privacy, Terms), and the Studies dropdown in the nav.
### Episodes
Add, edit, and reorder podcast episodes. Each episode has:
- **Number, title, description, URL** (podcast player link)
- **Show notes** — legacy free-text OR structured fields:
- Summary, Key Themes, Scripture References, Notable Quotes, Resources
- **Scriptures** — `Book Chapter:Verse` strings powering the Scripture Index
- **Tags** for filtering
Episode data lives in `data/admin-content.json` under `siteContent.episodes`.
### Studies
Create and manage Bible study courses. Each study has:
- Slug (URL identifier, e.g. `colossians`)
- Title, description, hero image
- **Sections (lessons)** — each with: id, title, release date, scripture text, commentary, reflection questions, Greek notes
Sections with a future `releaseDate` are locked for students until that date.
### Assets
Upload images, PDFs, and documents. Stored in `data/uploads/`. Tag them for easy lookup.
### Study Users
View all enrolled study users with progress, note count, streak, and reminder settings.
Admin actions per user:
- Edit display name or reset password
- Add / remove study enrollments
- Manually trigger a re-engagement email (T1 / T2 / T3)
- Delete account
### Backups
Snapshots of `data/` are taken on startup and on a schedule. The Backups tab lists available snapshots and lets you restore any of them.
### Episode Scripts
Upload `.docx` script files for episodes. Text is extracted and made full-text-searchable via `/api/episode-scripts/search`.
### Webhooks
Configure outgoing webhooks triggered by app events (e.g. new contact submission).
+66
@@ -0,0 +1,66 @@
# Architecture
## Tech Stack
| Layer | Technology |
|-------|-----------|
| Frontend | React 18, TypeScript, Vite |
| Backend | Node.js 22, Express |
| PWA | Workbox (service worker, precache) |
| Styling | Plain CSS (`src/App.css`) with CSS custom properties |
| Container | Docker / Podman, multi-arch (amd64 + arm64) |
| Registry | Gitea Container Registry (`git.necloud.us`) |
## Folder Layout
```
Siteforge/
├── src/ # React frontend
│ ├── App.tsx # Root component + all page routes
│ ├── App.css # Global styles
│ ├── colossiansStudy.tsx # Entire study platform UI (~3700 lines)
│ ├── AdminPage.tsx # Admin panel UI
│ └── content.ts # SiteContent type definitions
├── server/ # Express backend
│ ├── routes/ # One file per feature area (see API Reference)
│ ├── data.js # Data load/save helpers + sanitizers
│ ├── state.js # In-memory state (loaded from JSON on start)
│ ├── study-helpers.js # Study auth, slug normalise, email scheduling
│ ├── email.js # Resend email senders
│ ├── auth.js # Admin session validation
│ ├── config.js # Constants (limits, file paths)
│ └── helpers.js # Shared utilities
├── server.js # Express app entry point
├── data/ # Persisted JSON (bind-mounted in Docker)
│ ├── admin-content.json # All site content (episodes, studies, SEO…)
│ ├── study-users.json # Study platform accounts
│ ├── study-community.json
│ ├── study-notes/ # Per-user note files (<userId>.json)
│ ├── study-progress/ # Per-user progress files
│ └── ...
├── dist/ # Built frontend (served by Express)
├── public/ # Static assets copied to dist
└── .github/workflows/ # Gitea Actions CI
```
## Request Flow
1. Browser hits port **4173**
2. Express checks `/api/*` routes first
3. Hit-counting middleware records page views
4. `public.js` serves `dist/` static files; unknown paths fall back to `dist/index.html` (SPA)
5. React Router handles client-side routing
## Data Storage
All state is held **in memory** (`server/state.js`) and written to JSON files in `data/` via debounced write queues. On startup every file is loaded into memory.
There is no database — JSON files are the source of truth. The `data/` directory is bind-mounted from the host so data survives container restarts and image upgrades.
## Key Patterns
- **Route registration**: each `server/routes/*.js` exports `register(app)` and is called in `server.js`
- **Sanitizers**: every field written to disk passes through a sanitizer in `data.js` (e.g. `sanitizeStudyCommunityPosts`). New persistent fields **must** be added to the sanitizer or they are silently stripped on the next write.
- **Study auth**: cookie-based sessions in `state.studySessions` (Map); expire after 30 days
- **Admin auth**: single password from `ADMIN_PASSWORD` env var; session token in `state.adminSessions`
- **Optimistic UI**: community posts and likes update the frontend immediately before the API call confirms
+73
@@ -0,0 +1,73 @@
# Deployment
## CI Pipeline (Gitea Actions)
Pushing to `main` triggers `.github/workflows/publish.yml`:
1. Checks out code
2. Installs dependencies (`npm ci`)
3. Runs `npm run build` (Vite)
4. Logs into Gitea Container Registry
5. Builds and pushes a **multi-arch image** (amd64 + arm64) tagged `latest` + short SHA
Image: `git.necloud.us/nate/siteforge:latest`
## Servers
### Rocky (Beta)
Podman on ZimaOS NAS, reachable over Tailscale.
```bash
podman run -d --name siteforge-app \
--restart unless-stopped \
-p 4173:4173 \
--env-file /mnt/raid/siteforge/.env \
--mount type=bind,source=/mnt/raid/siteforge/data,target=/app/data,Z \
git.necloud.us/nate/siteforge:latest
```
### Oracle Arm (Production)
Docker on Oracle Cloud free-tier ARM instance.
```bash
docker pull git.necloud.us/nate/siteforge:latest
docker rm -f siteforge-app
docker run -d --name siteforge-app \
--restart unless-stopped \
-p 4173:4173 \
--env-file /home/opc/siteforge/.env \
--mount type=bind,source=/home/opc/siteforge/data,target=/app/data \
git.necloud.us/nate/siteforge:latest
docker ps
```
## Environment Variables (`.env`)
| Variable | Description |
|----------|-------------|
| `ADMIN_PASSWORD` | Password for `/admin` — **change from default `changeme`** |
| `PORT` | Server port (default `4173`) |
| `TRUST_PROXY_HOPS` | Number of reverse-proxy hops to trust (default `1`) |
| `RESEND_API_KEY` | Resend API key for transactional email |
| `FROM_EMAIL` | Sender address for study emails |
## Data Directory
The `data/` bind mount is the only stateful piece. Upgrading is safe — pull the new image and restart. The data directory is never modified by the container build.
**To back up**: copy the entire `data/` directory from the host.
## Service Worker Cache Note
After a deploy, users may see the old version until the Workbox service worker refreshes. Force-clear it in the browser console:
```js
const regs = await navigator.serviceWorker.getRegistrations()
for (const r of regs) await r.unregister()
location.reload()
```
+22 -1
@@ -1 +1,22 @@
Welcome to the Wiki. # Siteforge Wiki
Documentation for the **Verse by Verse with Nate** web app (Siteforge).
## Pages
| Page | What it covers |
|------|---------------|
| [Architecture](Architecture) | Tech stack, folder layout, data storage, key files |
| [Deployment](Deployment) | CI pipeline, container registry, server deploy commands |
| [Admin Guide](Admin-Guide) | Using the admin panel: content, episodes, users, backups |
| [Study System](Study-System) | Lessons, sections, community, notes, reminders, certificates |
| [API Reference](API-Reference) | All `/api/` endpoints grouped by feature |
## Quick facts
- **Stack**: Node.js + Express backend, React + TypeScript frontend, Vite build, PWA (Workbox)
- **Data**: JSON files in `/app/data/` (persisted via Docker bind mount)
- **Port**: `4173` in production
- **Admin**: `/admin` — password set via `ADMIN_PASSWORD` env var
- **Study platform**: `/study` — separate user accounts from admin
- **Version**: v2.0.0
+73
@@ -0,0 +1,73 @@
# Study System
The study platform lives at `/study`. It uses its own user accounts, separate from the admin.
## User Accounts
- Sign up at `/study/signup` with email + password
- Sessions last 30 days (cookie-based)
- Users enroll in one or more studies
- Profile fields: display name, avatar URL, newsletter opt-in, reminder preferences
User data: `data/study-users.json`
## Studies & Sections
Each study (e.g. "Colossians: Rooted in Christ", slug `colossians`) has a list of **sections** (lessons):
| Field | Description |
|-------|-------------|
| `id` | URL slug, e.g. `1-1-2` |
| `title` | Lesson title |
| `releaseDate` | Locks the lesson until this date |
| `scriptureText` | Passage text |
| `commentary` | Instructor commentary |
| `reflectionQuestions` | Array of questions |
| `greekNotes` | Array of word study entries |
URL pattern: `/study/:studySlug/:sectionId`
## Progress Tracking
Completing all reflection questions marks a section complete. Progress is stored in `data/study-progress/<userId>.json`.
Completion unlocks the Next button to the following section.
## Notes
Students take notes anchored to parts of each lesson (Scripture, Commentary, Reflection). Notes are stored in `data/study-notes/<userId>.json` as a map of anchor keys → HTML strings (TipTap rich text). Auto-saved with a 1.5-second debounce.
## Community
Each study has a feed at `/study/:studySlug/community`.
- Posts are scoped to a study, optionally to a specific lesson (`sectionId`)
- Enrolled users can post, reply, and like posts
- Likes stored as `likedBy` array of user IDs on each post
- **"Discuss this lesson"** shortcut in the lesson sidebar links to the community pre-filtered to the current lesson
Data: `data/study-community.json` (up to 500 posts, 50 replies each)
## Reminders & Re-engagement Emails
Users opt into study reminder emails via their account page.
| Type | Trigger |
|------|---------|
| Lesson reminder | New lesson released → email to enrolled users with reminders on |
| Re-engagement | User inactive 7 / 14 / 30 days → tiered emails (T1/T2/T3) |
| Weekly digest | Weekly lesson summary (for users who chose digest frequency) |
User fields: `studyRemindersEnabled`, `reminderFrequency` (`immediate` or `weekly`), `remindersPausedUntil`.
Emails sent via **Resend**. Templates live in the Resend dashboard.
## Certificates
Completing all sections of a study generates a certificate, emailed to the student. Publicly viewable at `/certificate/:token`.
Data: `data/study-certificates.json`
## Streaks
Each section completion updates `currentStreak` and `longestStreak` on the user. Displayed on the account page.