Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ dist/
*.db
*.env
*.flaskenv
frontend/node_modules/
frontend/dist/
137 changes: 137 additions & 0 deletions .plans/fastapi-railway-ts-mvp.md
Original file line number Diff line number Diff line change
@@ -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
43 changes: 43 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -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
```
50 changes: 41 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,59 @@ 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:

```config
SECRET_KEY="<some random 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="<put something random here>"
export SECRET_KEY="<put something random here>"
```

### Running the development web server
### Running the legacy development web server

In the root of the project, run:

Expand All @@ -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.

Expand Down
2 changes: 2 additions & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/zorkdemo
CORS_ALLOW_ORIGINS=http://localhost:5173,https://bcorfman.github.io
18 changes: 18 additions & 0 deletions backend/README.md
Original file line number Diff line number Diff line change
@@ -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
```
26 changes: 26 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -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`
1 change: 1 addition & 0 deletions frontend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VITE_API_BASE_URL=http://localhost:8000
18 changes: 18 additions & 0 deletions frontend/README.md
Original file line number Diff line number Diff line change
@@ -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
```
32 changes: 32 additions & 0 deletions frontend/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>ZorkDemo Frontend Scaffold</title>
<style>
body {
font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
max-width: 720px;
margin: 3rem auto;
padding: 0 1rem;
line-height: 1.5;
}
code {
background: #f2f2f2;
padding: 0.1rem 0.3rem;
border-radius: 0.25rem;
}
</style>
</head>
<body>
<h1>ZorkDemo Frontend Scaffold</h1>
<p>
This is the Phase 0 placeholder for the future React + TypeScript frontend.
</p>
<p>
Configure backend URL with <code>VITE_API_BASE_URL</code> in
<code>frontend/.env.example</code> when the app is initialized in Phase 3.
</p>
</body>
</html>