Skip to content

Commit b6fcf11

Browse files
docs(api): publish MyFestival OpenAPI spec via Redoc at /api-docs/ (#428)
* docs(api): publish MyFestival OpenAPI spec via Redoc at /api-docs/ Adds a Redoc-rendered API docs page at /api-docs/ in the Cloudflare Pages deployment. The OpenAPI spec is generated from proto at build time (not committed), consistent with the proto-in-CI pattern from #426. - web/api-docs/index.html: Redoc page loading openapi.yaml from jsDelivr CDN - web/api-docs/.gitignore: openapi.yaml is generated, not committed - web/_headers: /api-docs/* CSP override allowing cdn.jsdelivr.net for Redoc - ci.yml build-web: buf generate + copy openapi.yaml before flutter build - mise.dev.toml proto:generate: also copies to web/api-docs/ for local dev https://claude.ai/code/session_015uTnGiC56cEELZMH2cQQU4 * docs: add API docs link to README https://claude.ai/code/session_015uTnGiC56cEELZMH2cQQU4 * fix(api-docs): exempt /api-docs/ from Flutter SPA catch-all rewrite Cloudflare Pages does not resolve implicit directory indexes before evaluating _redirects rewrite rules, so /api-docs/ was being caught by /* /index.html 200 before the Redoc page could be served. Adding explicit /api-docs and /api-docs/ rules before the catch-all routes those two paths to /api-docs/index.html directly. https://claude.ai/code/session_015uTnGiC56cEELZMH2cQQU4 * fix(api-docs): serve Redoc from self rather than CDN to avoid CSP issues Downloading redoc.standalone.js at build time (CI) and local dev (proto:generate) so it is served from 'self', which the existing Flutter CSP already allows. Removes the /api-docs/* CSP override that was added to permit cdn.jsdelivr.net. https://claude.ai/code/session_015uTnGiC56cEELZMH2cQQU4 --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 9dfc334 commit b6fcf11

6 files changed

Lines changed: 47 additions & 2 deletions

File tree

.github/workflows/ci.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,14 @@ jobs:
194194
with:
195195
google-services-json: ${{ secrets.GOOGLE_SERVICES_JSON }}
196196

197+
- name: Generate OpenAPI spec from proto
198+
uses: bufbuild/buf-action@v1
199+
with:
200+
setup_only: true
201+
- run: cd proto && buf generate
202+
- run: cp docs/code/api/openapi/openapi.yaml web/api-docs/openapi.yaml
203+
- run: curl -sLo web/api-docs/redoc.standalone.js https://cdn.jsdelivr.net/npm/redoc@2.4.0/bundles/redoc.standalone.js
204+
197205
- name: Get git version info
198206
id: git_version
199207
run: scripts/get_version_info.sh github >> $GITHUB_OUTPUT

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ A Flutter app for browsing beers, ciders, meads, and more at the Cambridge Beer
1414
**Production**: [cambeerfestival.app](https://cambeerfestival.app)
1515
**Staging**: [staging.cambeerfestival.app](https://staging.cambeerfestival.app)
1616
**Android**: [Google Play](https://play.google.com/store/apps/details?id=ralcock.cbf)
17+
**API docs**: [cambeerfestival.app/api-docs](https://cambeerfestival.app/api-docs/)
1718

1819
## Features
1920

mise.dev.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,13 @@ dir = "proto"
4141
run = "buf dep update"
4242

4343
[tasks."proto:generate"]
44-
description = "Generate OpenAPI from the proto contract (BSR remote plugin)"
44+
description = "Generate OpenAPI from the proto contract (BSR remote plugin), then sync to web/api-docs/"
4545
dir = "proto"
46-
run = "buf generate"
46+
run = """
47+
buf generate
48+
cp ../docs/code/api/openapi/openapi.yaml ../web/api-docs/openapi.yaml
49+
curl -sLo ../web/api-docs/redoc.standalone.js https://cdn.jsdelivr.net/npm/redoc@2.4.0/bundles/redoc.standalone.js
50+
"""
4751

4852
[tasks."proto:clients"]
4953
description = "Generate Worker TS types and Flutter Dart client from OpenAPI spec"

web/_redirects

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,10 @@
22
# Redirect to festival drinks list — these URLs were never shared publicly
33
/:festivalId/drink/:id /:festivalId 302
44

5+
# API docs — serve the Redoc page before the Flutter SPA catch-all intercepts it.
6+
# Cloudflare Pages doesn't resolve implicit directory indexes before checking
7+
# rewrite rules, so /api-docs/ would otherwise match /* below.
8+
/api-docs /api-docs/index.html 200
9+
/api-docs/ /api-docs/index.html 200
10+
511
/* /index.html 200

web/api-docs/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Generated in CI (build-web job) and locally via: MISE_ENV=dev ./bin/mise run proto:generate
2+
openapi.yaml
3+
redoc.standalone.js

web/api-docs/index.html

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Cambridge Beer Festival — MyFestival API</title>
7+
<link rel="icon" type="image/png" href="../favicon.png" />
8+
<style>
9+
body {
10+
margin: 0;
11+
padding: 0;
12+
}
13+
</style>
14+
</head>
15+
<body>
16+
<redoc
17+
spec-url="openapi.yaml"
18+
expand-responses="200"
19+
required-props-first="true"
20+
></redoc>
21+
<script src="redoc.standalone.js"></script>
22+
</body>
23+
</html>

0 commit comments

Comments
 (0)