This repository has no description
1package notella
2
3import (
4 "context"
5 "encoding/json"
6 "fmt"
7 "log"
8 "net/http"
9
10 "firebase.google.com/go/v4/messaging"
11 ll "github.com/gwennlbh/label-logger-go"
12 "github.com/nats-io/nats.go"
13 "github.com/nats-io/nats.go/jetstream"
14)
15
16type HealthResponse struct {
17 Redis bool `json:"redis"`
18 NATS bool `json:"nats"`
19 ChurrosDatabase bool `json:"churros_db"`
20 Firebase bool `json:"firebase"`
21}
22
23func (r HealthResponse) AllGood() bool {
24 return r.Redis && r.NATS && r.ChurrosDatabase && r.Firebase
25}
26
27func healthHandler(w http.ResponseWriter, r *http.Request) {
28 ll.Debug("Checking health due to request from %s", r.RemoteAddr)
29 // Set the content type to JSON
30 w.Header().Set("Content-Type", "application/json")
31
32 // Example response (you can modify this with your own business logic)
33 response := CheckHealth()
34
35 // Marshal the response to JSON and write it to the response writer
36 if err := json.NewEncoder(w).Encode(response); err != nil {
37 http.Error(w, "Unable to encode JSON", http.StatusInternalServerError)
38 return
39 }
40}
41
42func CheckHealth() HealthResponse {
43 response := HealthResponse{}
44
45 if err := CheckRedisHealth(); err != nil {
46 ll.ErrorDisplay("while checking Redis health", err)
47 } else {
48 response.Redis = true
49 }
50
51 if err := CheckNATSHealth(); err != nil {
52 ll.ErrorDisplay("while checking NATS health", err)
53 } else {
54 response.NATS = true
55 }
56
57 if err := CheckChurrosDatabaseHealth(); err != nil {
58 ll.ErrorDisplay("while checking Churros database health", err)
59 } else {
60 response.ChurrosDatabase = true
61 }
62
63 if err := CheckFirebaseHealth(); err != nil {
64 ll.ErrorDisplay("while checking Firebase Cloud Messaging health", err)
65 } else {
66 response.Firebase = true
67 }
68 return response
69}
70
71func StartHealthCheckEndpoint(port int) {
72 // Set up route for the /health endpoint
73 http.HandleFunc("/health", healthHandler)
74
75 // Start the server and log any errors
76 ll.Log("Starting", "cyan", "health check endpoint on :%d/health", port)
77 if err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil); err != nil {
78 log.Fatalf("Server failed to start: %v", err)
79 }
80}
81
82func CheckRedisHealth() error {
83 return redisClient.Ping(context.Background()).Err()
84}
85
86func CheckNATSHealth() error {
87 nc, err := nats.Connect(config.NatsURL)
88 if err != nil {
89 return fmt.Errorf("could not connect to NATS at %s: %w", config.NatsURL, err)
90 }
91
92 defer nc.Close()
93
94 js, err := jetstream.New(nc)
95 if err != nil {
96 return fmt.Errorf("could not connect to Jetstream: %w", err)
97 }
98
99 stream, err := js.CreateStream(context.Background(), jetstream.StreamConfig{
100 Name: StreamName,
101 Subjects: []string{SubjectName},
102 })
103 if err != nil {
104 return fmt.Errorf("could not create stream: %w", err)
105 }
106
107 consumers := stream.ListConsumers(context.Background())
108 if consumers.Err() != nil {
109 return fmt.Errorf("could not list consumers: %w", consumers.Err())
110 }
111
112 for info := range consumers.Info() {
113 if consumers.Err() != nil {
114 return fmt.Errorf("could not get consumer info: %w", consumers.Err())
115 }
116 if info.Name == ConsumerName {
117 return nil
118 }
119 }
120
121 return fmt.Errorf("%s not connected to stream", ConsumerName)
122}
123
124func CheckChurrosDatabaseHealth() error {
125 return prisma.Prisma.QueryRaw("SELECT 1").Exec(context.Background(), nil)
126}
127
128func CheckFirebaseHealth() error {
129 if !config.HasValidFirebaseServiceAccount() {
130 return nil
131 }
132
133 fcm, err := firebaseClient.Messaging(firebaseCtx)
134 if err != nil {
135 return fmt.Errorf("while initializing messaging client: %w", err)
136 }
137
138 _, err = fcm.SendDryRun(firebaseCtx, &messaging.Message{
139 Notification: &messaging.Notification{
140 Title: "Health check attempt",
141 Body: "This is a health check attempt to ensure that the FCM service is working properly. The notification is not supposed to be displayed to the user.",
142 },
143 Token: "invalid",
144 })
145 if err != nil && err.Error() == "The registration token is not a valid FCM registration token" {
146 return nil
147 }
148 return err
149}