feat(api/v2): serve Scalar docs UI at /api/v2/docs
This commit is contained in:
parent
21194e61b0
commit
00b42234e9
40
magefile.go
40
magefile.go
|
|
@ -1510,6 +1510,46 @@ func (Generate) ConfigYAML(commented bool) {
|
|||
generateConfigYAMLFromJSON(DefaultConfigYAMLSamplePath, commented)
|
||||
}
|
||||
|
||||
// ScalarBundle downloads the Scalar API reference standalone JS bundle into
|
||||
// pkg/routes/api/v2/scalar/. Version is pinned to match the Scalar version
|
||||
// used in Huma's internal docs at the time of last update.
|
||||
func (Generate) ScalarBundle() error {
|
||||
const (
|
||||
version = "1.44.20"
|
||||
dest = "pkg/routes/api/v2/scalar/scalar.standalone.js"
|
||||
)
|
||||
url := fmt.Sprintf("https://unpkg.com/@scalar/api-reference@%s/dist/browser/standalone.js", version)
|
||||
|
||||
fmt.Printf("Downloading Scalar bundle %s from %s\n", version, url)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
||||
defer cancel()
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) //nolint:gosec // This is a dev-only mage task and the URL is hard-coded above.
|
||||
if err != nil {
|
||||
return fmt.Errorf("build scalar bundle request: %w", err)
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("download scalar bundle: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("download scalar bundle: unexpected status %s", resp.Status)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if _, err := io.Copy(&buf, resp.Body); err != nil {
|
||||
return fmt.Errorf("read scalar bundle body: %w", err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(dest, buf.Bytes(), 0o600); err != nil {
|
||||
return fmt.Errorf("write %s: %w", dest, err)
|
||||
}
|
||||
|
||||
fmt.Printf("Wrote %d bytes to %s\n", buf.Len(), dest)
|
||||
return nil
|
||||
}
|
||||
|
||||
func localBranchExists(ctx context.Context, name string) bool {
|
||||
return exec.CommandContext(ctx, "git", "show-ref", "--verify", "--quiet", "refs/heads/"+name).Run() == nil //nolint:gosec // This is a dev-only mage task and the branch name is supplied by the developer running it.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-present Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package apiv2
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v5"
|
||||
)
|
||||
|
||||
//go:embed scalar/scalar.html
|
||||
var scalarHTML string
|
||||
|
||||
//go:embed scalar/scalar.standalone.js
|
||||
var scalarJS []byte
|
||||
|
||||
// ScalarUI serves the Scalar API reference HTML; assets ship locally to avoid a CDN dependency.
|
||||
func ScalarUI(c *echo.Context) error {
|
||||
return c.HTML(http.StatusOK, scalarHTML)
|
||||
}
|
||||
|
||||
// ScalarJS serves the embedded Scalar standalone JS bundle.
|
||||
func ScalarJS(c *echo.Context) error {
|
||||
return c.Blob(http.StatusOK, echo.MIMEApplicationJavaScript, scalarJS)
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="referrer" content="no-referrer">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Vikunja API</title>
|
||||
</head>
|
||||
<body>
|
||||
<script id="api-reference" data-url="/api/v2/openapi.json"></script>
|
||||
<script src="/api/v2/docs/scalar.standalone.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// Placeholder — the actual Scalar standalone bundle is vendored by
|
||||
// `mage generate:scalarBundle` (see magefile.go) and lands in a follow-up
|
||||
// PR to keep this one reviewable. Until then, /api/v2/docs renders the
|
||||
// HTML shell but the Scalar reference itself will not initialise.
|
||||
(function () {
|
||||
var el = document.getElementById('api-reference');
|
||||
if (el && el.parentNode) {
|
||||
var note = document.createElement('p');
|
||||
note.style.cssText = 'font-family:system-ui;padding:2rem;color:#555';
|
||||
note.textContent = 'Scalar bundle not vendored yet — run `mage generate:scalarBundle` to fetch it.';
|
||||
el.parentNode.insertBefore(note, el);
|
||||
}
|
||||
})();
|
||||
|
|
@ -384,6 +384,10 @@ func registerAPIRoutesV2(e *echo.Echo, a *echo.Group) {
|
|||
|
||||
api := apiv2.NewAPI(e, a)
|
||||
|
||||
// Scalar docs UI — embedded, no CDN. See pkg/routes/api/v2/docs.go.
|
||||
a.GET("/docs", apiv2.ScalarUI)
|
||||
a.GET("/docs/scalar.standalone.js", apiv2.ScalarJS)
|
||||
|
||||
// Resource registrations.
|
||||
apiv2.RegisterLabelRoutes(api)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue