From ec578eeef5e5d64bd673faa580f52e920ab6cace Mon Sep 17 00:00:00 2001 From: robert Date: Wed, 1 Jul 2026 19:59:46 -0400 Subject: [PATCH] Bump to v0.4, document self-hosted backend in README The rework-backend merge added an optional self-hosted Express/ws/SQLite backend (npm workspaces, single-container Docker deployment) alongside the existing Firebase default. Bump APP_VERSION and refresh README to cover both storage modes and the new repo layout. Co-Authored-By: Claude Sonnet 5 --- README.md | 76 +++++++++++++++++++++++++++++++++++++++++++++--------- src/App.js | 2 +- 2 files changed, 65 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 528025c..aa859f3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# TTRPG Initiative Tracker (v0.2.5) +# TTRPG Initiative Tracker (v0.4) ![Here it is in use](images/in_use.png) @@ -12,6 +12,8 @@ A web-based application designed to help Dungeon Masters (DMs) manage and displa Have you tried it? Got feedback or questions? Discuss here: [https://discourse.draft13.com/c/ttrpg-initiative-tracker/16](https://discourse.draft13.com/c/ttrpg-initiative-tracker/16) +As of v0.4, the app can optionally run against a self-hosted backend (Node/Express/ws/SQLite, shipped as a single Docker container) instead of Firebase — useful if you'd rather not depend on a Google account. Firebase remains the default; see [Self-Hosted Backend (Optional)](#self-hosted-backend-optional) below. + ## Features ![DM View.](images/dm_view.png) @@ -59,7 +61,7 @@ Have you tried it? Got feedback or questions? Discuss here: [https://discourse.d * A **fullscreen button** (top-right corner) toggles the browser into fullscreen mode — ideal for a dedicated second monitor. * A **prevent sleep toggle** (moon/coffee icon, top-right corner) uses the browser Wake Lock API to keep the screen on while active. * **Combat Action Log:** A running log of combat events (HP changes, condition changes, turn advances, participant additions/removals, encounter starts/ends, etc.) is available at `/logs`. Entries are timestamped and tagged with the encounter name. Most entries include an **↩ Undo** button that rolls back the action in Firestore (restoring HP, conditions, turn order, etc.). Rolled-back entries are greyed out with a strikethrough. The log can be cleared in bulk from that page. -* **Real-time Updates:** Uses Firebase Firestore for real-time synchronization between DM actions and the player display. +* **Real-time Updates:** Uses Firebase Firestore for real-time synchronization between DM actions and the player display (or a self-hosted WebSocket backend, see below). * **Initiative Tie-Breaking:** DMs can drag-and-drop participants with tied initiative scores (before an encounter starts or while paused) to set a manual order. * **Responsive Design:** Styled with Tailwind CSS. * **Confirmation Modals:** Used for destructive actions like deleting campaigns, characters, encounters, or ending combat. @@ -68,8 +70,9 @@ Have you tried it? Got feedback or questions? Discuss here: [https://discourse.d * **Frontend:** React * **Styling:** Tailwind CSS -* **Backend/Database:** Firebase Firestore (for real-time data) -* **Authentication:** Firebase Anonymous Authentication +* **Backend/Database:** Firebase Firestore (default), or a self-hosted Node/Express + `ws` + SQLite (`better-sqlite3`) backend behind Caddy (optional, see [Self-Hosted Backend](#self-hosted-backend-optional)) +* **Authentication:** Firebase Anonymous Authentication (Firebase mode only; the self-hosted backend has no auth layer — intended for trusted/local networks) +* **Shared logic:** Turn-order state machine (`shared/`) is framework-agnostic and used by both the frontend and the self-hosted backend, covered by a Jest test suite ## App Usage Overview @@ -128,11 +131,13 @@ This flow allows the DM to prepare and run encounters efficiently while providin ### Prerequisites * **Node.js and npm:** Ensure you have Node.js (which includes npm) installed. You can download it from [nodejs.org](https://nodejs.org/). -* **Firebase Project:** You'll need a Firebase project with: +* **Firebase Project** (only if using the default Firebase storage mode): You'll need a Firebase project with: * Firestore Database created and initialized. * Anonymous Authentication enabled in the "Authentication" > "Sign-in method" tab. * **Git:** For cloning the repository. +The project is an npm workspaces monorepo (`server/`, `shared/`, plus the CRA frontend at the root) — a single `npm install` at the repo root installs everything. + ### Local Development Setup (using npm) 1. **Clone the Repository:** @@ -190,7 +195,14 @@ This flow allows the DM to prepare and run encounters efficiently while providin ### Deployment with Docker -This project includes a `Dockerfile` to containerize the application for deployment. It uses a multi-stage build: +There are two Docker paths, depending on which storage mode you want: + +* **Firebase mode (default):** the root `Dockerfile` builds a static frontend-only image, described below. +* **Self-hosted mode:** the `docker/` directory builds a single container running the Node backend + SQLite + the frontend behind Caddy, with no Firebase dependency. See [Self-Hosted Backend (Optional)](#self-hosted-backend-optional). + +#### Firebase-mode image (root `Dockerfile`) + +This project includes a `Dockerfile` to containerize the Firebase-backed application for deployment. It uses a multi-stage build: * **Stage 1 (build):** Installs dependencies, copies your `.env.local` (for local testing builds), and builds the static React application using `npm run build`. * **Stage 2 (nginx):** Uses an Nginx server to serve the static files produced in the build stage. @@ -225,6 +237,30 @@ This project includes a `Dockerfile` to containerize the application for deploym * If your CI/CD pipeline builds the Docker image, ensure these environment variables are securely provided to the build environment. * **Implement strict Firebase Security Rules** appropriate for a production application to protect your data. +### Self-Hosted Backend (Optional) + +If you'd rather not depend on Firebase/Google, the app can run entirely self-hosted: a Node/Express + `ws` backend backed by SQLite, with the frontend talking to it instead of Firestore. Intended for trusted/local networks (no auth layer yet). + +**Quickest path — Docker Compose (single container: Caddy + Node + SQLite):** +```bash +docker compose -f docker/docker-compose.yml up --build +``` +This serves the app at `http://localhost:8080` (override with `PORT`), proxies `/api` and `/ws` to the backend, and persists the SQLite database in a named Docker volume. Override the Firestore-style app-id namespace with `TRACKER_APP_ID` if needed. + +**Local dev (backend + frontend separately):** +```bash +npm install # installs root, server/, and shared/ workspaces +npm run server:dev # starts backend on :4001 (SQLite at server/data/tracker.sqlite) + +# in another terminal: +REACT_APP_STORAGE=ws \ +REACT_APP_BACKEND_URL=http://127.0.0.1:4001 \ +REACT_APP_BACKEND_WS=ws://127.0.0.1:4001/ws \ +npm start +``` + +See `docs/DEVELOPMENT.md` for the full architecture (generic KV doc store, storage adapter interface, test layers) and `docs/REWORK_PLAN.md` for the design rationale. + ## Project Structure
@@ -233,21 +269,37 @@ ttrpg-initiative-tracker/
 ├── .env.example         # Example environment variables
 ├── .env.local           # Local environment variables (ignored by Git)
 ├── .gitignore           # Specifies intentionally untracked files that Git should ignore
-├── Dockerfile           # Instructions to build the Docker image
+├── Dockerfile           # Firebase-mode image (frontend-only, served by nginx)
+├── package.json         # Workspaces root: frontend + server + shared
 ├── package-lock.json    # Records exact versions of dependencies
-├── package.json         # Project metadata and dependencies
 ├── postcss.config.js    # PostCSS configuration (for Tailwind CSS)
 ├── tailwind.config.js   # Tailwind CSS configuration
+├── docker/              # Self-hosted deployment: Dockerfile, docker-compose.yml, Caddyfile
+├── docs/                # Rework plan, dev setup, testing, encounter-builder guide, glossary
+├── scripts/             # Manual demo/ops tooling (e.g. replay-combat.js)
+├── tests/               # Exploratory audit tooling (not part of the automated suite)
 ├── public/              # Static assets
 │   ├── favicon.ico
 │   ├── index.html       # Main HTML template
 │   └── manifest.json
-└── src/                 # React application source code
-├── App.js           # Main application component
-├── index.css        # Global styles (including Tailwind directives)
-└── index.js         # React entry point
+├── src/                 # React frontend (Create React App)
+│   ├── App.js           # Main application component
+│   ├── storage/         # Storage adapter layer: firebase / ws / memory
+│   ├── index.css        # Global styles (including Tailwind directives)
+│   └── index.js         # React entry point
+├── server/              # Self-hosted backend: Express + ws + SQLite (better-sqlite3)
+└── shared/              # Framework-agnostic turn-order logic, used by frontend + server
 
+## Further Reading + +* `docs/DEVELOPMENT.md` — setup, running the backend, test suites +* `docs/REWORK_PLAN.md` — why/how the self-hosted backend was added +* `docs/TESTING.md` — test layers and how to run them +* `docs/ENCOUNTER_BUILDER.md` — entity model and storage paths behind the DM interface +* `docs/GLOSSARY.md` — domain terms (turn vs. round, etc.) +* `TODO.md` — known bugs and backlog + ## Contributing If you want to contribute, send me a message here: [https://discourse.draft13.com/c/ttrpg-initiative-tracker/16](https://discourse.draft13.com/c/ttrpg-initiative-tracker/16), and I can add to this Gitea instance and you can feel free to fork the repository and submit pull requests. For major changes, please pose a topic to the Discourse instance above linked above first to discuss what you would like to change. diff --git a/src/App.js b/src/App.js index d451fa0..293feb6 100644 --- a/src/App.js +++ b/src/App.js @@ -46,7 +46,7 @@ if (typeof document !== 'undefined') { // CONSTANTS // ============================================================================ -const APP_VERSION = 'v0.3'; +const APP_VERSION = 'v0.4'; const { DEFAULT_MAX_HP, DEFAULT_INIT_MOD, MONSTER_DEFAULT_INIT_MOD, generateId, rollD20, formatInitMod, sortParticipantsByInitiative, syncTurnOrder, computeTurnOrderAfterRemoval } = shared; const ROLL_DISPLAY_DURATION = 5000;