Files
teletypegames/README.md
T
mr.zeroandClaude Opus 5 466aeac6ca A store registry record needs no repository
The client only ever took identity from a store repository — a slug, a name, a catalog —
and the store engine's own defaults cover everything else: the host-to-asset mapping, the
install modes, the platforms, the behaviour. So `store_repository_url` is now optional:
nullable in the schema, no presence validation, the format check only when a value is
given, and the serializer answers null rather than an empty string, because the client
branches on its absence.

Adding a store is therefore a row with two fields filled in. Given a repository the
client still reads its config.json, and that file remains the authority on how the store
behaves — the admin form and the endpoint's documentation say so.

The frontend's /stores page gains a section of its own for the graphical client on the
desktop tab: what it does, that it sets the store up itself, that it is the way in on
Windows where `curl … | sh` does not exist, and links to the releases, the repository and
the documentation — now under stores/warp-engine-client, which is where that repository
lives after the rename.

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

117 lines
4.2 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": null }
]
```
A `Store` row has two required fields — `name` and `catalog_url` — plus an
**optional** `store_repository_url`, maintained from **ActiveAdmin ▸ 🛒 Stores**;
`db/seeds.rb` creates our own.
**A store does not need a repository.** The store engine's own defaults already
cover the host-to-asset mapping, the install modes, the platforms and the
behaviour; what they cannot know is identity — a slug, a name and a catalog — and
that is what this row carries. With no repository the client derives the slug from
the catalog host, writes a small config and installs. Given one, it reads that
repository's `config.json` and points it at `catalog_url`, and that file remains the
authority on how the store behaves; a repository without a config file is treated
as no repository at all.
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-client`](https://git.teletypegames.org/stores/warp-engine-client) |
## 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)