Files
teletypegames/README.md
T
mr.zeroandClaude Opus 5 5517133e25 A store registry, so a client is not told which store to install
The desktop graphical client had our store's config URL compiled into it. That is
backwards: which stores exist for a catalog is something the site knows, and a
client should be able to ask. `GET /api/stores` answers, and the client picks from
what comes back.

A `Store` row is three fields — name, catalog_url, store_repository_url — with an
ActiveAdmin panel, a Blueprinter serializer in the catalog's camelCase, and a
service+controller pair following the events and members shape. `db/seeds.rb`
creates our own record, so a fresh database serves a working registry.

The endpoint is **public**, which is the point: the client runs on someone's
laptop before any store exists and has nobody to log in as. It exposes three URLs
that are public anyway.

This deliberately does not live in WarpEngine. The engine serves one catalog and
has no business knowing who ships stores for it; a registry of stores is a
property of this site, not of the catalog software. Anyone mounting WarpEngine can
keep their own list, or none.

16 model and controller examples pass. Worth noting for the next person who runs
them: the specs need RAILS_ENV=test, as the README says — without it rspec runs in
the development environment, host authorization rejects Rack::Test's hostname, and
every request spec fails with a 403 and an HTML body that looks nothing like a
routing problem.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 13:12:01 +02:00

114 lines
3.8 KiB
Markdown

# Teletype Games
Monorepo for the Teletype Games portal: a Vue 3 frontend, a Rails 8 API host app,
and the reusable **WarpEngine** software-catalog engine.
## Project structure
```
apps/
frontend/ # Vue 3 + Vite + TypeScript + Tailwind SPA
api/ # Rails 8 host app: TTG-specific API + ActiveAdmin shell
libs/
ruby/warp_engine/ # WarpEngine: mountable Rails engine (catalog, updater, admin resources)
```
The API is split in two layers:
- **WarpEngine** (`libs/ruby/warp_engine`) owns the software catalog: models
(softwares, releases, release assets, images, platform links, download stats),
the CI-callable `/build/*` publishing endpoints, the public read-only JSON API
(`/api/software*`, `/api/builds*`, `/api/image`, `/api/download`, `/file/*`)
and the catalog ActiveAdmin resources. See its [README](libs/ruby/warp_engine/README.md).
- **The host app** (`apps/api`) owns everything TTG-specific: members, events,
the store registry, wiki proxy, RSS feeds, Devise/ActiveAdmin authentication,
theming and assets. It consumes WarpEngine as a path gem and mounts it at `/`.
## The store registry
`GET /api/stores` lists the stores a client can install from. The desktop
graphical client reads it on first run, which is why it is **public**: a client has
nobody to log in as.
```json
[
{
"name": "Teletype Games",
"catalogUrl": "https://teletypegames.org",
"storeRepositoryUrl": "https://git.teletypegames.org/stores/ttg-desktop-store"
}
]
```
A `Store` row is three fields — `name`, `catalog_url`, `store_repository_url`
maintained from **ActiveAdmin ▸ 🛒 Stores**, and `db/seeds.rb` creates our own.
The client derives everything else: it reads `config.json` from the store
repository, points it at `catalog_url`, and falls back to the engine's defaults if
that repository has no config file.
This lives in the host app **on purpose, not in WarpEngine**. The engine serves
one catalog and has no business knowing which stores exist for it; who ships a
store for a catalog is a property of the site.
| | |
|---|---|
| Model | `apps/api/app/models/store.rb` |
| Endpoint | `apps/api/app/controllers/api/stores_controller.rb` |
| Admin | `apps/api/app/admin/stores.rb` |
| Client | [`warp-engine-desktop-gui`](https://git.teletypegames.org/stores/warp-engine-desktop-gui) |
## Development environment
```bash
docker compose up -d
```
| Service | URL |
|---|---|
| Frontend | `http://${WEBAPP_DOMAIN}` |
| API | `http://${WEBAPP_DOMAIN}/api` |
| Admin | `http://${WEBAPP_DOMAIN}/admin` |
| API docs | `http://${WEBAPP_DOMAIN}/api/swagger` |
The api image is built from the repo root (so the `libs/` path gems are visible
during `bundle install`) and mounts `./libs` at runtime. After changing the
compose file or the Gemfile, rebuild with `docker compose up -d --build api`.
## Testing
```bash
make api-test
```
Runs the host suite (`apps/api`, `softwares_test` DB), the WarpEngine suite
(dummy app, `warp_engine_test` DB) and a production-mode `zeitwerk:check`.
Individually:
```bash
docker exec -e RAILS_ENV=test api bundle exec rspec # host
docker exec -w /libs/ruby/warp_engine -e RAILS_ENV=test api bundle exec rspec # engine
```
JSON contract baselines for `/api/software` and `/api/builds` live in
`apps/api/spec/snapshots/` — diff against them after refactors.
## Linting
### Backend (RuboCop)
```bash
docker compose run --rm --no-deps api bundle exec rubocop # check
docker compose run --rm --no-deps api bundle exec rubocop -A # autofix
```
Config: `apps/api/.rubocop.yml` (rubocop-rails-omakase preset)
### Frontend (ESLint)
```bash
docker compose run --rm --no-deps frontend npm run lint # check
docker compose run --rm --no-deps frontend npm run lint:fix # autofix
```
Config: `apps/frontend/eslint.config.js` (ESLint 9 flat config, Vue + TypeScript)