Skip to content

Commit 534f620

Browse files
authored
feat: add badge endpoint for shields.io (#25)
* feat: add badge endpoint for shields.io * docs: update readme
1 parent ee91986 commit 534f620

6 files changed

Lines changed: 95 additions & 18 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ The server is configured using environment variables, the following options are
2727
| `CORS_ALLOWED_ORIGINS` | string/array | Comma-separated list of allowed CORS origins. | `*` |
2828
| `TRUSTED_PROXIES` | string/array | Comma-separated list of trusted proxy IPs. | `` |
2929
| `DASHBOARD_ENABLED` | boolean | Whether to enable the dashboard. | `true` |
30+
| `BADGE_ENABLED` | boolean | Whether to enable the badge endpoint. | `true` |
3031

3132
## Contributing
3233

badge_handler.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
7+
_ "embed"
8+
9+
"github.com/go-chi/render"
10+
"github.com/tinyauthapp/analytics/queries"
11+
)
12+
13+
type BadgeHandler struct {
14+
queries *queries.Queries
15+
}
16+
17+
func NewBadgeHandler(queries *queries.Queries) *BadgeHandler {
18+
return &BadgeHandler{
19+
queries: queries,
20+
}
21+
}
22+
23+
type shieldsioData struct {
24+
SchemaVersion int `json:"schemaVersion"`
25+
Label string `json:"label"`
26+
Message string `json:"message"`
27+
Color string `json:"color,omitempty"`
28+
LabelColor string `json:"labelColor,omitempty"`
29+
IsError bool `json:"isError,omitempty"`
30+
NamedLogo string `json:"namedLogo,omitempty"`
31+
LogoSvg string `json:"logoSvg,omitempty"`
32+
LogoColor string `json:"logoColor,omitempty"`
33+
LogoSize string `json:"logoSize,omitempty"`
34+
Style string `json:"style,omitempty"`
35+
}
36+
37+
func (h *BadgeHandler) Badge(w http.ResponseWriter, r *http.Request) {
38+
instanceCount, err := h.queries.GetInstanceCount(r.Context())
39+
40+
if err != nil {
41+
w.WriteHeader(http.StatusInternalServerError)
42+
return
43+
}
44+
45+
badgeData := shieldsioData{
46+
SchemaVersion: 1,
47+
Label: "Active Instances",
48+
Message: fmt.Sprintf("%d", instanceCount),
49+
Color: "brightgreen",
50+
LabelColor: "grey",
51+
}
52+
53+
w.Header().Set("Content-Type", "application/json")
54+
w.WriteHeader(http.StatusOK)
55+
render.JSON(w, r, badgeData)
56+
}

dashboard_handler.go

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@ import (
1212
//go:embed dashboard.html
1313
var dashboardTemplate string
1414

15-
//go:embed favicon.ico
16-
var faviconData []byte
17-
1815
type DashboardHandler struct {
1916
queries *queries.Queries
2017
}
@@ -88,15 +85,3 @@ func (h *DashboardHandler) Dashboard(w http.ResponseWriter, r *http.Request) {
8885
return
8986
}
9087
}
91-
92-
func (h *DashboardHandler) Favicon(w http.ResponseWriter, r *http.Request) {
93-
w.Header().Set("Content-Type", "image/x-icon")
94-
w.WriteHeader(http.StatusOK)
95-
w.Write(faviconData)
96-
}
97-
98-
func (h *DashboardHandler) Robots(w http.ResponseWriter, r *http.Request) {
99-
w.Header().Set("Content-Type", "text/plain")
100-
w.WriteHeader(http.StatusOK)
101-
w.Write([]byte("User-agent: *\nDisallow: /"))
102-
}

main.go

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,13 @@ import (
1616
"github.com/go-chi/cors"
1717
"github.com/spf13/viper"
1818
_ "modernc.org/sqlite"
19+
20+
_ "embed"
1921
)
2022

23+
//go:embed favicon.ico
24+
var faviconData []byte
25+
2126
var version = "development"
2227

2328
type Config struct {
@@ -28,6 +33,7 @@ type Config struct {
2833
TrustedProxies []string `mapstructure:"trusted_proxies"`
2934
CORSAllowedOrigins []string `mapstructure:"cors_allowed_origins"`
3035
DashboardEnabled bool `mapstructure:"dashboard_enabled"`
36+
BadgeEnabled bool `mapstructure:"badge_enabled"`
3137
}
3238

3339
func main() {
@@ -40,6 +46,7 @@ func main() {
4046
v.SetDefault("trusted_proxies", []string{""})
4147
v.SetDefault("cors_allowed_origins", []string{"*"})
4248
v.SetDefault("dashboard_enabled", true)
49+
v.SetDefault("badge_enabled", true)
4350

4451
v.AutomaticEnv()
4552

@@ -84,7 +91,6 @@ func main() {
8491

8592
instancesHandler := NewInstancesHandler(queries)
8693
healthHandler := NewHealthHandler()
87-
dashboardHandler := NewDashboardHandler(queries)
8894

8995
router.Get("/v1/healthz", healthHandler.Health)
9096

@@ -100,12 +106,27 @@ func main() {
100106
r.Post("/v1/instances/heartbeat", instancesHandler.Heartbeat)
101107
})
102108

109+
router.Get("/robots.txt", func(w http.ResponseWriter, r *http.Request) {
110+
w.Header().Set("Content-Type", "text/plain")
111+
w.WriteHeader(http.StatusOK)
112+
w.Write([]byte("User-agent: *\nDisallow: /"))
113+
})
114+
103115
if config.DashboardEnabled {
116+
dashboardHandler := NewDashboardHandler(queries)
104117
router.Get("/dashboard", dashboardHandler.Dashboard)
118+
router.Get("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {
119+
w.Header().Set("Content-Type", "image/x-icon")
120+
w.WriteHeader(http.StatusOK)
121+
w.Write(faviconData)
122+
})
123+
105124
}
106125

107-
router.Get("/favicon.ico", dashboardHandler.Favicon)
108-
router.Get("/robots.txt", dashboardHandler.Robots)
126+
if config.BadgeEnabled {
127+
badgeHandler := NewBadgeHandler(queries)
128+
router.Get("/v1/badge", badgeHandler.Badge)
129+
}
109130

110131
srv := &http.Server{
111132
Addr: fmt.Sprintf("%s:%d", config.Address, config.Port),

queries.sql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,6 @@ WHERE uuid = ?;
2525
DELETE FROM instances
2626
WHERE last_seen < ? OR version = '' OR uuid = ''
2727
RETURNING *;
28+
29+
-- name: GetInstanceCount :one
30+
SELECT COUNT(*) AS count FROM instances;

queries/queries.sql.go

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)