From 0e771bfbb57976d8514e3409d612618b445cbe68 Mon Sep 17 00:00:00 2001 From: nmemmert Date: Thu, 16 Jul 2026 08:12:45 -0400 Subject: [PATCH] Rewrite README to accurately describe the app; bump to v1.0.2 Replaces outdated portfolio/project-cards description with accurate documentation of the podcast/ministry site: features, all env vars, routes, API endpoints, persistent data paths, and admin capabilities. Co-Authored-By: Claude Sonnet 4.6 --- README.md | 333 +++++++++++++++++++++++++++++++++++---------------- package.json | 2 +- 2 files changed, 229 insertions(+), 106 deletions(-) diff --git a/README.md b/README.md index c19177c..a90ab04 100644 --- a/README.md +++ b/README.md @@ -1,136 +1,259 @@ # Siteforge -This project is a React + TypeScript portfolio app built with Vite. +A podcast and ministry website for **Verse by Verse with Nate** — a verse-by-verse Bible teaching podcast. Built with React + TypeScript (Vite) on the frontend and Express.js on the backend, deployed as a Docker container via GitHub Actions. -It is designed to showcase two categories of work: +## Features -- Websites -- Apps +- **Podcast episodes** — episode listing, detail pages, downloadable resources, finished series archive +- **Study system** — per-user enrollment in structured Bible studies with sections, notes, progress tracking, section quizzes, community posts/comments, and completion certificates +- **Study accounts** — signup/login, email verification, avatar upload, email change with re-verification +- **Two-factor authentication** — TOTP authenticator app + email OTP backup, recovery codes +- **Contact form** — submissions stored server-side, admin reply with templates, inbound email webhook (Resend) +- **Q&A** — public question submission, admin moderation, pinned answers, shareable question links +- **Email integration** — welcome emails, admin notifications, admin replies, study reminder emails (via Resend) +- **Admin panel** — content management with draft/publish workflow, episode scripts, podcast checklist, QR code management, subscriber management, asset uploads +- **Analytics** — page views, audio plays, download tracking, geo-based visitor stats (country/state/city), consent-based cookie opt-in +- **Backup system** — automatic JSON snapshots with restore, full `tar.gz` data export/import for server migration +- **Version display** — app version and git commit shown in admin sidebar; `/api/version` endpoint -The app now supports: +--- -- Dynamic cards generated from a project data source -- Built vs Hosted filters -- A dedicated About page route for each project -- An admin editor route to update cards and About pages -- A per-project README summary block on each project page -- Embedded Spotify podcast player on the home section +## Tech Stack -## Run locally +| Layer | Technology | +|---|---| +| Frontend | React 19, TypeScript, Vite, React Router v7 | +| Backend | Express 5, Node.js 22 | +| Email | Resend | +| TOTP | otplib | +| QR Codes | qrcode | +| Rich text | TipTap | +| Charts | Chart.js + react-chartjs-2, react-simple-maps | +| Documents | docx, mammoth | +| Container | Docker (GHCR), GitHub Actions CI | + +--- + +## Project Structure + +``` +siteforge/ +├── src/ # React frontend +│ ├── App.tsx # Root + all client routes +│ ├── App.css +│ └── ... # Page and component files +├── server/ # Express backend +│ ├── config.js # Constants, env vars, APP_VERSION, GIT_COMMIT +│ ├── data.js # In-memory state + JSON persistence +│ ├── helpers.js # Shared utilities (IP, rate limiting, etc.) +│ ├── study-helpers.js # Study email scheduling, cert generation +│ └── routes/ # Route handlers (one file per domain) +├── data/ # Seed data (JSON); live data at /app/data in container +├── server.js # Entry point — mounts all routers +├── Dockerfile +├── entrypoint.sh # Seeds /app/data on first run, never overwrites live data +├── docker-compose.yml +└── .github/workflows/docker-publish.yml +``` + +--- + +## Environment Variables + +| Variable | Required | Description | +|---|---|---| +| `SESSION_SECRET` | Yes | Secret for Express session signing | +| `ADMIN_PASSWORD_HASH` | Yes | bcrypt hash of the admin password | +| `RESEND_API_KEY` | Yes | Resend API key for sending email | +| `RESEND_FROM` | Yes | Sender address (e.g. `Nate `) | +| `RESEND_TO` | Yes | Address that receives contact form notifications | +| `RESEND_REPLY_TO` | No | Reply-to address on outbound emails | +| `RESEND_WEBHOOK_TOKEN` | No | Validates inbound Resend webhook requests | +| `INBOUND_EMAIL_SECRET` | No | Validates inbound email route requests | +| `RESEND_CONTACTS_API_KEY` | No | Resend Contacts API key for subscriber sync | +| `RESEND_SEGMENT_ID` | No | Resend segment ID for subscriber list | +| `COMMIT_SHA` | No | Git SHA baked in at build time by CI (shown in admin UI) | +| `TRUST_PROXY_HOPS` | No | Number of proxy hops to trust (default `1`) — set correctly for accurate client IPs behind a reverse proxy | +| `ALLOW_INSECURE_COOKIES` | No | Set to `true` to allow session cookies over HTTP (for local/non-HTTPS access) | +| `TITUS_STUDY_FILE` | No | Path to the Titus study PDF (defaults to bundled file) | +| `TITUS_STUDY_DOWNLOAD_NAME` | No | Filename shown to users downloading the Titus PDF | +| `PORT` | No | HTTP port (default `4173`) | +| `CACHE_PURGE_WEBHOOK_URL` | No | Webhook URL for one-click cache purge from Admin Operations | +| `DEPLOY_WEBHOOK_URL` | No | Webhook URL for one-click deploy trigger from Admin Operations | + +--- + +## Frontend Routes + +| Path | Page | +|---|---| +| `/` | Landing page | +| `/start-here` | Start here / intro page | +| `/episodes` | Episode listing | +| `/episodes/:id` | Episode detail | +| `/finished` | Finished series listing | +| `/finished/:id` | Finished series detail | +| `/resources` | Resources listing | +| `/downloads/:id` | Download detail | +| `/about` | About page | +| `/contact` | Contact form | +| `/questions` | Q&A listing | +| `/subscribe` | Email subscribe | +| `/subscribe/thanks` | Subscribe confirmation | +| `/study` | Study landing page | +| `/study/signup` | Study account signup | +| `/study/account` | Study account dashboard | +| `/study/:studySlug` | Study index | +| `/study/:studySlug/community` | Study community posts | +| `/study/:studySlug/notes` | Study notes | +| `/study/:studySlug/:sectionId` | Study section | +| `/study/:studySlug/:sectionId/quiz` | Section quiz | +| `/certificate/:token` | Public completion certificate | +| `/privacy` | Privacy policy | +| `/thanks` | Contact thank-you | +| `/admin` | Admin panel (requires password + TOTP) | +| `/preview` | Content draft preview | + +--- + +## Key API Endpoints + +| Endpoint | Description | +|---|---| +| `GET /api/version` | Returns `{ version, commit }` | +| `GET /api/content` | Public site content (CMS data) | +| `POST /api/contact` | Submit contact form | +| `POST /api/analytics/pageview` | Record page view | +| `POST /api/analytics/play` | Record audio play | +| `GET /api/episode-audio` | Episode audio proxy | +| `GET /api/admin-auth/status` | Admin session status + version info | +| `POST /api/admin-auth/login` | Admin login | +| `POST /api/admin-auth/totp-verify` | Complete TOTP 2FA | +| `POST /api/admin-auth/totp-setup-init` | Begin TOTP enrollment | +| `POST /api/admin-auth/totp-setup-confirm` | Confirm TOTP enrollment | +| `GET /api/admin-stats` | Hit/visitor analytics | +| `GET /api/admin-stats/backup` | Trigger stats snapshot | +| `GET /api/admin-backup/export` | Download full `tar.gz` data backup | +| `POST /api/admin-backup/import` | Restore from `tar.gz` backup | +| `GET /api/admin/study-users` | List study accounts | +| `GET /api/admin-study-certificates` | Study completion certificates | +| `GET /api/admin-contact-submissions` | Contact form submissions | +| `POST /api/admin-contact-submissions/:id/reply` | Reply to a submission | +| `GET /api/admin/qr-codes` | QR code management | +| `GET /api/admin-episode-scripts/:episodeNumber` | Episode script | +| `GET /api/admin-podcast-checklist` | Podcast checklist state | +| `GET /api/admin-subscribers` | Subscriber list | + +--- + +## Development ```bash +# Install dependencies npm install + +# Start frontend dev server only (Vite, http://localhost:5173) +npm run dev + +# Start backend API server only (reads .env, http://localhost:4173) +npm run api + +# Start both concurrently npm run dev:full -``` -`dev:full` starts both: - -- Vite frontend (`http://localhost:5173`) -- Admin content API (`http://localhost:4173`) - -## Build for production - -```bash +# Production build npm run build + +# Start production server (reads .env) +npm start ``` -## Run in container +Create a `.env` file at the project root with the required environment variables before running locally. The server uses `--env-file=.env`. -Pull image directly: +--- -```bash -docker pull ghcr.io/nmemmert/siteforge:latest -docker run -d --name siteforge-app -p 4173:4173 -v ./data:/app/data ghcr.io/nmemmert/siteforge:latest +## Docker / Deployment + +Images are built and pushed to `ghcr.io/nmemmert/siteforge` automatically on every push to `main` or when a `v*` tag is pushed. The git commit SHA is baked into the image as `COMMIT_SHA` at build time and displayed in the admin sidebar. + +### First run + +On first boot, `entrypoint.sh` copies seed data from the bundled `data-seed/` into `/app/data/` if that directory is empty. Subsequent restarts skip this step, so live data is never overwritten by an upgrade. + +### docker-compose.yml + +A `docker-compose.yml` is included at the project root. Mount `/app/data` to a persistent volume or host path and supply all required environment variables. + +```yaml +volumes: + - /path/to/your/data:/app/data ``` -Run with Docker Compose: +### Useful commands ```bash +# Pull latest image and restart docker compose pull docker compose up -d -``` -Admin auth: - -- Set `ADMIN_PASSWORD` on the server/container to protect `/admin` and admin stats/maintenance endpoints. -- Without `ADMIN_PASSWORD`, admin login is disabled until configured. -- Optional: set `CACHE_PURGE_WEBHOOK_URL` to enable one-click cache purge from Admin Operations. -- Optional: set `DEPLOY_WEBHOOK_URL` to enable one-click deploy trigger from Admin Operations. -- Optional: set `BUILD_COMMIT`, `BUILD_NUMBER`, and `DEPLOYED_AT` to display deployment status in Admin. - -The app will be available at `http://localhost:4173`. - -Persistent admin saves: - -- Admin updates are written to `data/admin-content.json`. -- Admin drafts are written to `data/admin-content-draft.json`. -- Built-in page hit stats are written to `data/hit-stats.json`. -- Detailed visitor analytics are written to `data/visitor-stats.json`. -- Backup snapshots are written to `data/backups/`. -- Uploaded admin assets are stored in `data/uploads/` and served at `/uploads/*`. -- `docker-compose.yml` mounts `./data` into the container at `/app/data`. -- This keeps all admin-managed data after container restarts/rebuilds/updates. - -## Where to edit content - -- Project domain data and ownership: `src/data/projects.ts` -- Portfolio rendering logic: `src/App.tsx` -- Portfolio UI styles: `src/App.css` -- Global theme variables: `src/index.css` - -## Routes - -- Site home: `/` -- Start Here: `/start-here` -- Questions: `/questions` -- Privacy: `/privacy` -- Terms: `/terms` -- Admin editor: `/admin` -- Admin draft preview: `/preview` - -## Admin editing - -- Open `/admin` to edit homepage content, links, archived studies, Q&A moderation, and analytics. -- Use **Operations** to manage SEO metadata, sitemap paths, legal page text, redirect rules, and podcast highlights. -- Use **Operations** to upload/delete hosted image assets for use in content and social cards. -- Use **Save Draft**, **Preview Draft**, and **Publish Draft** for a safer publish workflow. -- Saved edits are written to `data/admin-content.json` through the API server. -- Built-in stats in `/admin` include page hits plus visitor details (IP, country/state/county/city, returning visitors, and recent visitor log). -- Site Stats in `/admin` includes a Bible Questions inbox sourced from contact form submissions marked as Bible Question. -- Analytics cookies are consent-based. Visitors can accept or decline tracking from the site banner. -- Admin now includes maintenance actions: **Export JSON**, **Backup Now**, **Prune Old Data**, and **Clear Analytics**. -- Admin also supports restoring from a backup snapshot from `/admin`. -- The server creates startup + daily backup snapshots and retains recent backups automatically. -- Operations can trigger optional **Purge Cache** and **Trigger Deploy** webhooks. - -Deployment note: - -- To keep Admin saves working on the internet, deploy with the Node API (`server.js`) and writable server storage for `data/`. - -Titus study download gate: - -- The site now gates Titus study downloads behind a name/email form. -- Default source file is `A_Study_of_Titus.pdf` in the project root. -- Override source path with `TITUS_STUDY_FILE` (relative to project root or absolute). -- Override downloaded filename with `TITUS_STUDY_DOWNLOAD_NAME`. -- When the form checkbox is left enabled (default), contacts are synced to Resend using the same contact sync flow as the contact form. - -Useful container commands: - -```bash +# View logs docker compose logs -f + +# Stop docker compose down ``` -Update to latest image: +--- -```bash -docker compose pull -docker compose up -d -``` +## Persistent Data -## Notes +All server-managed data lives under `/app/data` (or `./data` locally): -- The current project cards are sample placeholders. -- Most metadata was scanned from live pages. -- Skywatch currently uses a manual fallback description due to a `401` response during automated scan. +| Path | Contents | +|---|---| +| `admin-content.json` | Published CMS content | +| `admin-content-draft.json` | Draft content (unpublished) | +| `hit-stats.json` | Page hit analytics | +| `visitor-stats.json` | Detailed visitor analytics | +| `contact-submissions.json` | Contact form submissions | +| `study-users.json` | Study account records | +| `study-reminders.json` | Scheduled study reminder state | +| `qr-codes.json` | QR code registry | +| `episode-scripts/` | Per-episode script files | +| `backups/` | Automatic JSON snapshot backups | +| `uploads/` | Admin-uploaded assets (served at `/uploads/*`) | + +--- + +## Admin Access + +1. Navigate to `/admin` +2. Enter the admin password (hashed value in `ADMIN_PASSWORD_HASH`) +3. Complete TOTP 2FA, or use an email OTP if no TOTP app is enrolled +4. On first use, enroll a TOTP authenticator app from Admin → Settings + +Recovery codes are generated during TOTP setup and can be regenerated from Admin → Settings. + +### Admin capabilities + +- **Content** — edit and publish site content with draft/preview workflow +- **Episode scripts** — write and manage per-episode scripts; export to `.docx` +- **Podcast checklist** — track per-episode production steps +- **QR codes** — create, manage, and track scan counts for QR codes +- **Questions** — moderate submitted Q&A, pin answers, reply +- **Contact** — view submissions, reply with templates, check email health +- **Study users** — view enrollments, progress, certificates, manually enroll/unenroll +- **Study comments** — moderate community post comments +- **Subscribers** — view and export subscriber list +- **Assets** — upload and delete hosted images +- **Stats** — page hits, visitor geo analytics, download stats; export CSV +- **Backup** — snapshot now, restore from snapshot, export/import full `tar.gz` +- **Operations** — purge cache, trigger deploy, manage SEO/sitemap/redirects + +--- + +## Versioning + +The app version is read from `package.json` at startup (`APP_VERSION`). The git commit is baked in via `COMMIT_SHA` at Docker build time (set automatically by CI). Both are visible in the admin sidebar and at `GET /api/version`. + +Bump `package.json` before every commit pushed to `main`: patch for fixes, minor for features, major for breaking changes. diff --git a/package.json b/package.json index 8f0287b..4644b7a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "siteforge", "private": true, - "version": "1.0.1", + "version": "1.0.2", "type": "module", "scripts": { "dev": "vite",