-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #25 from Nuklai/up-dev
Sync dev branch with main
- Loading branch information
Showing
22 changed files
with
1,671 additions
and
125 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// Copyright (C) 2025, Nuklai. All rights reserved. | ||
// See the file LICENSE for licensing terms. | ||
|
||
package api | ||
|
||
import ( | ||
"database/sql" | ||
"log" | ||
"net/http" | ||
|
||
"github.com/gin-gonic/gin" | ||
"github.com/nuklai/nuklaivm-external-subscriber/models" | ||
) | ||
|
||
// GetAccountStats retrieves all account stats | ||
func GetAccountStats(db *sql.DB) gin.HandlerFunc { | ||
return func(c *gin.Context) { | ||
stats, err := models.FetchAccountStats(db) | ||
if err != nil { | ||
log.Printf("Error fetching account stats: %v", err) | ||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Unable to retrieve account stats"}) | ||
return | ||
} | ||
|
||
c.JSON(http.StatusOK, stats) | ||
} | ||
} | ||
|
||
// GetAccountDetails retrieves details for a specific account/address | ||
func GetAccountDetails(db *sql.DB) gin.HandlerFunc { | ||
return func(c *gin.Context) { | ||
address := c.Param("address") | ||
|
||
details, err := models.FetchAccountByAddress(db, address) | ||
if err != nil { | ||
log.Printf("Error fetching account details: %v", err) | ||
c.JSON(http.StatusNotFound, gin.H{"error": "Account not found"}) | ||
return | ||
} | ||
|
||
c.JSON(http.StatusOK, details) | ||
} | ||
} | ||
|
||
// GetAllAccounts retrieves accounts address, balance, transaction count | ||
func GetAllAccounts(db *sql.DB) gin.HandlerFunc { | ||
return func(c *gin.Context) { | ||
limit := c.DefaultQuery("limit", "20") | ||
offset := c.DefaultQuery("offset", "0") | ||
|
||
// Get the total count of accounts | ||
totalCount, err := models.CountAccounts(db) | ||
if err != nil { | ||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Unable to count accounts"}) | ||
return | ||
} | ||
|
||
accounts, err := models.FetchAllAccounts(db, limit, offset) | ||
if err != nil { | ||
log.Printf("Error fetching accounts: %v", err) | ||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Unable to retrieve accounts"}) | ||
return | ||
} | ||
|
||
c.JSON(http.StatusOK, gin.H{ | ||
"counter": totalCount, | ||
"items": accounts, | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,62 +1,93 @@ | ||
// Copyright (C) 2024, Nuklai. All rights reserved. | ||
// Copyright (C) 2025, Nuklai. All rights reserved. | ||
// See the file LICENSE for licensing terms. | ||
|
||
package api | ||
|
||
import ( | ||
"database/sql" | ||
"log" | ||
"net" | ||
"net/http" | ||
"time" | ||
|
||
"github.com/lib/pq" | ||
|
||
"github.com/gin-gonic/gin" | ||
"github.com/nuklai/nuklaivm-external-subscriber/models" | ||
) | ||
|
||
// checkGRPC checks the reachability of the gRPC server. | ||
func checkGRPC(grpcPort string) string { | ||
conn, err := net.DialTimeout("tcp", grpcPort, 2*time.Second) | ||
if err != nil { | ||
log.Printf("gRPC connection failed: %v", err) | ||
return "unreachable" | ||
} | ||
defer conn.Close() | ||
return "reachable" | ||
} | ||
// GetHealth retrieves the current health status of the system | ||
func GetHealth(monitor *HealthMonitor) gin.HandlerFunc { | ||
return func(c *gin.Context) { | ||
status := monitor.GetHealthStatus() | ||
|
||
// checkDatabase checks the reachability of the database. | ||
func checkDatabase(db *sql.DB) string { | ||
if err := db.Ping(); err != nil { | ||
log.Printf("Database connection failed: %v", err) | ||
return "unreachable" | ||
httpStatus := http.StatusOK | ||
if status.State == models.HealthStateRed { | ||
httpStatus = http.StatusServiceUnavailable | ||
} | ||
|
||
c.JSON(httpStatus, status) | ||
} | ||
return "reachable" | ||
} | ||
|
||
// HealthCheck performs a comprehensive health check of the subscriber. | ||
func HealthCheck(grpcPort string, db *sql.DB) gin.HandlerFunc { | ||
// GetHealthHistory retrieves historical health events for insidents | ||
func GetHealthHistory(db *sql.DB) gin.HandlerFunc { | ||
return func(c *gin.Context) { | ||
grpcStatus := checkGRPC(grpcPort) | ||
databaseStatus := checkDatabase(db) | ||
|
||
// Determine the overall status | ||
status := "ok" | ||
if grpcStatus == "unreachable" || databaseStatus == "unreachable" { | ||
status = "service unavailable" | ||
rows, err := db.Query(` | ||
SELECT id, state, description, service_names, | ||
start_time, end_time, COALESCE(duration, 0), timestamp | ||
FROM health_events | ||
ORDER BY timestamp DESC`) | ||
if err != nil { | ||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Unable to fetch health history"}) | ||
return | ||
} | ||
defer rows.Close() | ||
|
||
// Return the health status | ||
httpStatusCode := http.StatusOK | ||
if status == "service unavailable" { | ||
httpStatusCode = http.StatusServiceUnavailable | ||
var events []models.HealthEvent | ||
for rows.Next() { | ||
var event models.HealthEvent | ||
var endTime sql.NullTime | ||
var serviceNamesArray pq.StringArray | ||
var duration sql.NullInt64 | ||
|
||
err := rows.Scan( | ||
&event.ID, | ||
&event.State, | ||
&event.Description, | ||
&serviceNamesArray, | ||
&event.StartTime, | ||
&endTime, | ||
&duration, | ||
&event.Timestamp, | ||
) | ||
if err != nil { | ||
log.Printf("Error scanning health event: %v", err) | ||
continue | ||
} | ||
|
||
if endTime.Valid { | ||
event.EndTime = &endTime.Time | ||
} | ||
if duration.Valid { | ||
event.Duration = duration.Int64 | ||
} | ||
event.ServiceNames = []string(serviceNamesArray) | ||
events = append(events, event) | ||
} | ||
|
||
c.JSON(httpStatusCode, gin.H{ | ||
"status": status, | ||
"details": gin.H{ | ||
"database": databaseStatus, | ||
"grpc": grpcStatus, | ||
}, | ||
}) | ||
c.JSON(http.StatusOK, events) | ||
} | ||
} | ||
|
||
// Get90DayHealth retrives a 90day health summary | ||
func Get90DayHealth(db *sql.DB) gin.HandlerFunc { | ||
return func(c *gin.Context) { | ||
summaries, err := models.Fetch90DayHealth(db) | ||
if err != nil { | ||
log.Printf("Error fetching 90-day health history: %v", err) | ||
c.JSON(http.StatusInternalServerError, | ||
gin.H{"error": "Unable to retrieve health history"}) | ||
return | ||
} | ||
c.JSON(http.StatusOK, summaries) | ||
} | ||
} |
Oops, something went wrong.