diff --git a/.gitignore b/.gitignore index d90c2fc..9d7176a 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ dist/ *.db *.env *.flaskenv +frontend/node_modules/ +frontend/dist/ diff --git a/.plans/fastapi-railway-ts-mvp.md b/.plans/fastapi-railway-ts-mvp.md new file mode 100644 index 0000000..ad800c2 --- /dev/null +++ b/.plans/fastapi-railway-ts-mvp.md @@ -0,0 +1,137 @@ +# [ ] MVP Migration Plan: FastAPI on Railway + TypeScript Frontend on GitHub Pages + +## [ ] Summary +- [ ] Build FastAPI backend on Railway using existing Python game logic +- [ ] Build React + TypeScript frontend on GitHub Pages +- [ ] Persist anonymous sessions in Railway Postgres via browser-stored UUID + +## [ ] Architecture Decisions (Locked) +- [ ] Frontend stack: `Vite + React + TypeScript` +- [ ] Session model: anonymous UUID stored in browser `localStorage` +- [ ] Session persistence: Railway Postgres +- [ ] API transition: clean API now under `/api/v1/*` (no Hug compatibility paths) + +## [ ] Public API / Interface Changes + +### [ ] Backend HTTP API (new) +- [ ] `GET /api/v1/health` -> `{ "status": "ok" }` +- [ ] `POST /api/v1/session` -> create/validate session +- [ ] `POST /api/v1/command` -> execute command and persist state +- [ ] `POST /api/v1/session/reset` -> clear state for session + +### [ ] TypeScript Frontend Types (new) +- [ ] `SessionCreateRequest`, `SessionCreateResponse` +- [ ] `CommandRequest`, `CommandResponse` +- [ ] `ApiError` with stable `code` + `message` +- [ ] Runtime API response validation (e.g. Zod) + +### [ ] Persistence Contract +- [ ] Create `adventure_sessions` table +- [ ] Fields: `session_id`, `save_data`, `created_at`, `updated_at` + +## [x] Phase 0: Repo Restructure + Guardrails (Day 1) +- [x] Create `backend/` and `frontend/` directories +- [x] Keep `adventure/` reusable by backend +- [x] Add architecture + local dev quickstart docs +- [x] Standardize env vars (`DATABASE_URL`, `CORS_ALLOW_ORIGINS`, `VITE_API_BASE_URL`) +- [x] Add `CONTRIBUTING.md` for backend/frontend workflows + +### [x] Exit Criteria +- [x] Monorepo layout documented +- [x] README has exact run instructions for both apps + +## [ ] Phase 1: FastAPI Adapter over Existing Engine (Day 1-2) +- [ ] Create FastAPI app with startup DB init + CORS + Pydantic models +- [ ] Implement service flow: load session -> optional `admin_load` -> execute -> `admin_save` +- [ ] Implement `/health`, `/session`, `/command`, `/session/reset` +- [ ] Preserve current markdown-to-HTML response behavior + +### [ ] Exit Criteria +- [ ] Manual API flow works end-to-end +- [ ] Command response parity with current Hug behavior + +## [ ] Phase 2: Postgres Persistence (Day 2) +- [ ] Add SQLAlchemy 2.0 + Alembic in backend +- [ ] Create initial migration for `adventure_sessions` +- [ ] Add `SessionRepository` abstraction +- [ ] Implement `PostgresSessionRepository` +- [ ] Ensure idempotent session create/get semantics + +### [ ] Exit Criteria +- [ ] Fresh DB migration succeeds +- [ ] Save/load survives app restart +- [ ] Reset endpoint clears state correctly + +## [ ] Phase 3: TS Frontend MVP (Day 2-3) +- [ ] Create React UI: transcript, input, status/error, reset/new game +- [ ] Implement session bootstrap from `localStorage` +- [ ] Implement command send/receive flow with loading states +- [ ] Configure `VITE_API_BASE_URL` for environment-specific backend URL +- [ ] Ensure production build works for GitHub Pages + +### [ ] Exit Criteria +- [ ] App playable from GitHub Pages +- [ ] Refresh resumes session +- [ ] Reset starts a fresh session + +## [ ] Phase 4: Deployments + CI/CD (Day 3-4) +- [ ] Deploy backend service to Railway +- [ ] Attach Railway Postgres plugin +- [ ] Configure backend env vars and health check +- [ ] Configure GitHub Action to build/deploy frontend to Pages +- [ ] Configure production CORS for GitHub Pages origin(s) + +### [ ] Exit Criteria +- [ ] Railway API healthy +- [ ] GitHub Pages deployed successfully +- [ ] Frontend can call production API without CORS errors + +## [ ] Phase 5: Quality, Tests, Contributor UX (Day 4-5) +- [ ] Backend route tests (`health/session/command/reset`) +- [ ] Backend service + persistence tests +- [ ] Frontend API + UI behavior tests +- [ ] End-to-end smoke test for play/resume/reset +- [ ] Add contributor docs for extending content and commands + +### [ ] Exit Criteria +- [ ] CI green for backend + frontend +- [ ] New contributor can run project in <10 minutes from docs + +## [ ] Data-Driven Expandability (MVP-Compatible Staging) +- [ ] Keep existing Python engine for MVP +- [ ] Define `content/` contract for locations/items/commands metadata +- [ ] Keep parser/engine in code for now +- [ ] Plan post-MVP migration of content into validated data files +- [ ] Add command registry pattern for future plugin-style expansion + +## [ ] Testing Scenarios and Acceptance Criteria + +### [ ] Core gameplay/session +- [ ] New browser session starts correctly +- [ ] Command execution returns expected HTML output +- [ ] Refresh resumes session state +- [ ] Reset clears state +- [ ] Invalid/empty input returns stable error + +### [ ] Reliability +- [ ] Session survives backend restart +- [ ] Unknown `session_id` handled by create-on-demand policy +- [ ] DB outage returns structured 5xx response + +### [ ] Deployment +- [ ] GitHub Pages deploy on main succeeds +- [ ] Railway deploy succeeds and health check passes +- [ ] Production supports multi-command gameplay flow + +## [ ] Risks and Mitigations +- [ ] Session schema drift -> add session versioning + migrations +- [ ] Markdown rendering drift -> snapshot parity tests +- [ ] CORS issues -> explicit allowed-origins env + integration check + +## [ ] Assumptions and Defaults +- [ ] No auth/login in MVP +- [ ] One local session token per browser +- [ ] Backend is API-only (no server-rendered pages) +- [ ] Python engine retained for MVP timeline +- [ ] Postgres is source of truth for saves +- [ ] Full data-driven engine evolution happens post-MVP diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..8bcb932 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,43 @@ +# Contributing + +## Development Workflow (Phase 0) + +This repository is in a staged migration from Hug to FastAPI + TypeScript frontend. + +### Prerequisites + +- Python 3.10+ +- `uv` +- Node.js 20+ (for frontend phase) + +### Install Python dependencies + +```sh +uv sync --all-groups +``` + +### Current runnable app (legacy Hug) + +```sh +uv run hug -m web.app +``` + +### New app directories + +- `backend/`: FastAPI service scaffold (implementation begins in Phase 1). +- `frontend/`: TypeScript frontend scaffold (implementation begins in Phase 3). + +## Environment Setup + +Copy and adjust env templates as needed: + +- `backend/.env.example` +- `frontend/.env.example` + +## Testing + +Current tests (legacy app): + +```sh +uv run pytest +``` diff --git a/README.md b/README.md index 3d64522..0e5b118 100644 --- a/README.md +++ b/README.md @@ -26,13 +26,45 @@ Two easy ways to launch the console project: NOTE: the MacOS version does not have code signing built into it yet (that's next on my list!). To run it, you will need to set the binary as executable with `chmod 755` or similar, and after trying to run it once, go through System Preferences: Security and Privacy: General and "Allow the program to run anyway". -## Web (Hug) version +## Monorepo Layout (Phase 0) -* Install [Python](https://www.python.org) 3.10 or higher -* Install [uv](https://docs.astral.sh/uv/) -* At a command prompt in the project directory, type `uv sync --all-groups` to set up dependencies +- `adventure/`: shared game engine logic +- `backend/`: FastAPI service scaffold (target Railway deploy) +- `frontend/`: TypeScript frontend scaffold (target GitHub Pages deploy) +- `web/`: current legacy Hug web app +- `docs/architecture.md`: migration architecture notes -Next. set the flask application environment variables: +## Local Development Quickstart + +Install tooling and Python dependencies: + +- Install [Python](https://www.python.org) 3.10+ +- Install [uv](https://docs.astral.sh/uv/) +- Run `uv sync --all-groups` from repo root + +Standardized environment variables: + +- Backend: `DATABASE_URL`, `CORS_ALLOW_ORIGINS` (see `backend/.env.example`) +- Frontend: `VITE_API_BASE_URL` (see `frontend/.env.example`) + +### Run Backend (Current Legacy App) + +```sh +uv run hug -m web.app +``` + +Navigate to [http://localhost:8000/](http://localhost:8000/) + +### Run Frontend (Phase 0 Placeholder) + +```sh +cd frontend +python -m http.server 5173 +``` + +Navigate to [http://localhost:5173/](http://localhost:5173/) + +## Legacy Hug Notes The easiest option is to create a `.env` file in the root of the project with the contents: @@ -40,13 +72,13 @@ The easiest option is to create a `.env` file in the root of the project with th SECRET_KEY="" ``` -alternatively, you can manually set your environment variables for your terminal session but you'll have to remember to do that for every new session. +Alternatively, you can manually set your environment variables for your terminal session, but you'll have to remember to do that for every new session. ```sh -EXPORT SECRET_KEY="" +export SECRET_KEY="" ``` -### Running the development web server +### Running the legacy development web server In the root of the project, run: @@ -58,7 +90,7 @@ Navigate in your browser to: [http://localhost:8000/](http://localhost:8000/) -Have Fun! +Have fun! If you want to restart delete your `sid` cookie from your browser and refresh the page. diff --git a/backend/.env.example b/backend/.env.example new file mode 100644 index 0000000..940d016 --- /dev/null +++ b/backend/.env.example @@ -0,0 +1,2 @@ +DATABASE_URL=postgresql://postgres:postgres@localhost:5432/zorkdemo +CORS_ALLOW_ORIGINS=http://localhost:5173,https://bcorfman.github.io diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 0000000..12bae0b --- /dev/null +++ b/backend/README.md @@ -0,0 +1,18 @@ +# Backend (Phase 0 Scaffold) + +This directory is reserved for the FastAPI backend service that will run on Railway. + +## Status + +Phase 0 scaffold only. The production backend logic is still in the legacy `web/` package until Phase 1 migration is complete. + +## Environment Variables (Standardized) + +- `DATABASE_URL`: Postgres connection string for session persistence. +- `CORS_ALLOW_ORIGINS`: Comma-separated allowed origins (for example, GitHub Pages URL + localhost). + +## Planned Run Command (Phase 1+) + +```sh +uv run uvicorn backend.app.main:app --host 0.0.0.0 --port 8000 --reload +``` diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..f736c17 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,26 @@ +# Architecture (MVP Target) + +## Monorepo Layout + +- `adventure/`: existing game engine logic (shared by backend during MVP). +- `backend/`: FastAPI service for API and session persistence. +- `frontend/`: TypeScript browser client (GitHub Pages). +- `web/`: legacy Hug implementation (to be retired after migration). +- `.plans/`: migration plans and checklists. + +## Runtime Topology + +- Frontend is static and served by GitHub Pages. +- Frontend calls backend HTTP API on Railway. +- Backend persists session save blobs in Postgres. + +## Environment Variables + +### Backend + +- `DATABASE_URL` +- `CORS_ALLOW_ORIGINS` + +### Frontend + +- `VITE_API_BASE_URL` diff --git a/frontend/.env.example b/frontend/.env.example new file mode 100644 index 0000000..c2058b5 --- /dev/null +++ b/frontend/.env.example @@ -0,0 +1 @@ +VITE_API_BASE_URL=http://localhost:8000 diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..f589ab1 --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,18 @@ +# Frontend (Phase 0 Scaffold) + +This directory is reserved for the browser frontend that will be deployed to GitHub Pages. + +## Status + +Phase 0 scaffold only. The TypeScript app is created during Phase 3. + +## Environment Variables (Standardized) + +- `VITE_API_BASE_URL`: FastAPI backend base URL. + +## Planned Run Commands (Phase 3+) + +```sh +npm install +npm run dev +``` diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..1678d20 --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,32 @@ + + + + + + ZorkDemo Frontend Scaffold + + + +

ZorkDemo Frontend Scaffold

+

+ This is the Phase 0 placeholder for the future React + TypeScript frontend. +

+

+ Configure backend URL with VITE_API_BASE_URL in + frontend/.env.example when the app is initialized in Phase 3. +

+ +