feat(kiosk): registry v1 wire types and version rules

- RegistryAPIPath, RegistryPlugin, RegistryPluginDetail, RegistryVersion, envelopes, errors, ingest types
- ValidVersion, CompareVersions, BaseVersion, Satisfies, NewestCompatible
- docs: Registry API v1 reference in plugins/marketplace.md
This commit is contained in:
Jakub Zych
2026-10-29 17:05:41 +01:00
parent 51ec2f9daa
commit 4916ee3d43
6 changed files with 447 additions and 7 deletions

View File

@@ -87,3 +87,36 @@ func TestPluginManifest(t *testing.T) {
```
`kiosk.CheckT` runs the same check as `summer plugin:manifest --check` and names the command to rerun. A nested plugin gets its own test in its own directory.
## Registry API v1
summercms.io publishes the plugins it lists through a read-only JSON API. The marketplace pages, `summer` and the admin's Plugins screen read it. Its base path is `/api/registry/v1` (`kiosk.RegistryAPIPath`), and the `kiosk` module holds its wire types, so every client and the server share one definition of the shape.
| Request | Answer |
|---------|--------|
| `GET /api/registry/v1/plugins?q=&category=` | `{"data": [plugin, ...], "meta": {"total": N}}` (`kiosk.ListResponse`). `q` searches ID, name and description; `category` filters. |
| `GET /api/registry/v1/plugins/{id}` | `{"data": detail}` (`kiosk.DetailResponse`). |
| `GET /api/registry/v1/plugins/{id}/versions` | `{"data": [version, ...]}`, newest first (`kiosk.VersionsResponse`). |
| `POST /api/registry/v1/ingest` | Authenticated with an API token that has the `registry` scope; body `{"repository": "<https URL>"}` (`kiosk.IngestRequest`). Answers 202 with `{"data": {"repository": "...", "queued": true}}` (`kiosk.IngestResponse`). |
A plugin in a list (`kiosk.RegistryPlugin`) has these fields:
| Field | Meaning |
|-------|---------|
| `id`, `name`, `description` | From the latest release's `plugin.yaml`. |
| `module`, `package` | What `go get` and `summer.yaml` take. |
| `repository` | The HTTPS clone URL. |
| `category`, `featured`, `icon_url` | Presentation fields edited on summercms.io. |
| `latest_version`, `latest_framework` | The newest tag and its framework minimum. |
| `updated_at` | When the listing last changed, RFC 3339. |
The detail (`kiosk.RegistryPluginDetail`) adds `readme_markdown`, `readme_html`, `screenshots` (a list of `url` and `caption`) and `requires` of the latest release. A version (`kiosk.RegistryVersion`) has `version`, `framework`, `requires`, `module`, `package`, `commit` and `tagged_at`.
Errors answer with `{"error": {"code": "...", "message": "..."}}` (`kiosk.ErrorResponse`): 404 with `not_found` for an unknown ID, 400 with `invalid_query` for a bad parameter.
Compatibility rules:
- Every answer is an envelope object (`data`, `meta` or `error`), never a bare array.
- Clients ignore fields they do not know, and v1 changes only by adding fields. A change that removes or renames a field needs a `/api/registry/v2` path.
- The GET endpoints need no credentials, allow any origin (CORS) and answer with `Cache-Control: public, max-age=300` and an ETag.
- Versions are strict `vX.Y.Z` tags (`kiosk.ValidVersion`) compared numerically (`kiosk.CompareVersions`). A client picks the release to install with `kiosk.NewestCompatible`: the newest one whose framework minimum the application's framework version satisfies (`kiosk.Satisfies`).