From 541501d0ca04571ee2bec9cb0f18b6eccd5c4277 Mon Sep 17 00:00:00 2001 From: Chris Gianelloni Date: Fri, 4 Oct 2024 10:24:57 -0400 Subject: [PATCH] feat: static html support (#243) Signed-off-by: Chris Gianelloni --- CODEOWNERS | 1 + internal/api/api.go | 38 ++++++++++++++++++-------- internal/api/static/index.html | 14 ++++++++++ internal/api/static/txsubmit-logo.png | Bin 0 -> 25641 bytes 4 files changed, 42 insertions(+), 11 deletions(-) create mode 100644 internal/api/static/index.html create mode 100644 internal/api/static/txsubmit-logo.png diff --git a/CODEOWNERS b/CODEOWNERS index 7e28784..745ce55 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -2,4 +2,5 @@ # * @blinklabs-io/core *.md @blinklabs-io/core @blinklabs-io/pms +internal/api/static/* @blinklabs-io/core @blinklabs-io/frontend LICENSE @blinklabs-io/core @blinklabs-io/pms diff --git a/internal/api/api.go b/internal/api/api.go index 5a377c4..39da78d 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -15,8 +15,11 @@ package api import ( + "embed" "fmt" "io" + "io/fs" + "net/http" "time" ouroboros "github.com/blinklabs-io/gouroboros" @@ -34,16 +37,18 @@ import ( "github.com/blinklabs-io/tx-submit-api/submit" ) -// @title tx-submit-api -// @version v0 -// @description Cardano Transaction Submit API -// @Schemes http -// @BasePath / - -// @contact.name Blink Labs -// @contact.url https://blinklabs.io -// @contact.email support@blinklabs.io +//go:embed static +var staticFS embed.FS +// @title tx-submit-api +// @version v0 +// @description Cardano Transaction Submit API +// @Schemes http +// @BasePath / +// @contact.name Blink Labs +// @contact.url https://blinklabs.io +// @contact.email support@blinklabs.io +// // @license.name Apache 2.0 // @license.url http://www.apache.org/licenses/LICENSE-2.0.html func Start(cfg *config.Config) error { @@ -71,6 +76,18 @@ func Start(cfg *config.Config) error { })) router.Use(ginzap.RecoveryWithZap(accessLogger, true)) + // Configure static route + fsys, err := fs.Sub(staticFS, "static") + if err != nil { + return err + } + router.StaticFS("/ui", http.FS(fsys)) + // Redirect from root + router.GET("/", func(c *gin.Context) { + c.Request.URL.Path = "/ui" + router.HandleContext(c) + }) + // Create a healthcheck (before metrics so it's not instrumented) router.GET("/healthcheck", handleHealthcheck) // Create a swagger endpoint (not instrumented) @@ -121,10 +138,9 @@ func Start(cfg *config.Config) error { router.GET("/api/hastx/:tx_hash", handleHasTx) // Start API listener - err := router.Run(fmt.Sprintf("%s:%d", + return router.Run(fmt.Sprintf("%s:%d", cfg.Api.ListenAddress, cfg.Api.ListenPort)) - return err } func handleHealthcheck(c *gin.Context) { diff --git a/internal/api/static/index.html b/internal/api/static/index.html new file mode 100644 index 0000000..f1b0a85 --- /dev/null +++ b/internal/api/static/index.html @@ -0,0 +1,14 @@ + + + +Tx Submit API + + +

+ +

+

+GitHub: https://github.com/blinklabs-io/tx-submit-api +

+ + diff --git a/internal/api/static/txsubmit-logo.png b/internal/api/static/txsubmit-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..40b76d89071ccaf41dd80efa1e12e0aa25fad185 GIT binary patch literal 25641 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