Add all wiki pages: Architecture, Deployment, Admin Guide, Study System, API Reference
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.
|
||||
Reference in new issue
Block a user