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