| @@ -56,7 +56,7 @@ func RunEventLoop(ctx context.Context, a *ServerApp) { | |||||
| continue | continue | ||||
| } | } | ||||
| if msg.Type == "Eddystone" && msg.Battery < 3000 { | if msg.Type == "Eddystone" && msg.Battery < 3000 { | ||||
| service.SendAlert(id, "BatteryLow", a.KafkaManager.GetWriter("alert"), ctx, a.DB) | |||||
| service.SendAlert(id, "BatteryLow", "", a.KafkaManager.GetWriter("alert"), ctx, a.DB) | |||||
| } | } | ||||
| case <-beaconTicker.C: | case <-beaconTicker.C: | ||||
| var list []model.Tracker | var list []model.Tracker | ||||
| @@ -36,7 +36,6 @@ func GetGateways(token string, client *http.Client, cfg *config.Config) ([]model | |||||
| if err != nil { | if err != nil { | ||||
| return []model.Gateway{}, err | return []model.Gateway{}, err | ||||
| } | } | ||||
| fmt.Printf("Gateways raw response: %s\n", body) | |||||
| var i []model.Gateway | var i []model.Gateway | ||||
| err = json.NewDecoder(bytes.NewReader(body)).Decode(&i) | err = json.NewDecoder(bytes.NewReader(body)).Decode(&i) | ||||
| @@ -108,7 +108,7 @@ func LocationToBeaconService(msg model.HTTPLocation, db *gorm.DB, writer *kafka. | |||||
| return | return | ||||
| } | } | ||||
| sendRestrictedZoneAlert(gw.ID, msg.ID, writer, ctx, allowedZones, db) | |||||
| sendRestrictedZoneAlert(gw.Zone, msg.ID, writer, ctx, allowedZones, db) | |||||
| } | } | ||||
| func LocationToBeaconServiceAI(msg model.HTTPLocation, db *gorm.DB, writer *kafka.Writer, ctx context.Context) { | func LocationToBeaconServiceAI(msg model.HTTPLocation, db *gorm.DB, writer *kafka.Writer, ctx context.Context) { | ||||
| @@ -165,10 +165,10 @@ func LocationToBeaconServiceAI(msg model.HTTPLocation, db *gorm.DB, writer *kafk | |||||
| return | return | ||||
| } | } | ||||
| sendRestrictedZoneAlert(gw.ID, tracker.ID, writer, ctx, allowedZones, db) | |||||
| sendRestrictedZoneAlert(gw.Zone, tracker.ID, writer, ctx, allowedZones, db) | |||||
| } | } | ||||
| func SendAlert(trackerId, alertType string, writer *kafka.Writer, ctx context.Context, db *gorm.DB) { | |||||
| func SendAlert(trackerId, alertType, zone string, writer *kafka.Writer, ctx context.Context, db *gorm.DB) { | |||||
| var existingAlert model.Alert | var existingAlert model.Alert | ||||
| result := db.Select("status").Where("tracker_id = ? AND type = ?", trackerId, alertType).Order("timestamp DESC").Limit(1).Find(&existingAlert) | result := db.Select("status").Where("tracker_id = ? AND type = ?", trackerId, alertType).Order("timestamp DESC").Limit(1).Find(&existingAlert) | ||||
| @@ -179,6 +179,7 @@ func SendAlert(trackerId, alertType string, writer *kafka.Writer, ctx context.Co | |||||
| Type: alertType, | Type: alertType, | ||||
| Status: "new", | Status: "new", | ||||
| Timestamp: time.Now(), | Timestamp: time.Now(), | ||||
| Zone: zone, | |||||
| } | } | ||||
| if err := InsertAlert(alert, db, ctx); err != nil { | if err := InsertAlert(alert, db, ctx); err != nil { | ||||
| @@ -208,9 +209,9 @@ func SendAlert(trackerId, alertType string, writer *kafka.Writer, ctx context.Co | |||||
| } | } | ||||
| } | } | ||||
| func sendRestrictedZoneAlert(gwId, trackerId string, writer *kafka.Writer, ctx context.Context, allowedZones []string, db *gorm.DB) { | |||||
| if len(allowedZones) != 0 && !slices.Contains(allowedZones, gwId) { | |||||
| SendAlert(trackerId, "Restricted zone", writer, ctx, db) | |||||
| func sendRestrictedZoneAlert(gwZone, trackerId string, writer *kafka.Writer, ctx context.Context, allowedZones []string, db *gorm.DB) { | |||||
| if len(allowedZones) != 0 && !slices.Contains(allowedZones, gwZone) { | |||||
| SendAlert(trackerId, "Restricted zone", gwZone, writer, ctx, db) | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,17 +1,35 @@ | |||||
| #!/bin/bash | #!/bin/bash | ||||
| # PATCH alert status by alert id. | |||||
| # Usage: ./alert_status.sh <alert_id> [status] | |||||
| # alert_id - required, the alert id (e.g. from GET /reslevis/alerts) | |||||
| # status - optional, default: resolved | |||||
| # Example: ./alert_status.sh abc-123 resolved | |||||
| # BASE_URL=http://host:port ./alert_status.sh abc-123 acknowledged | |||||
| # Find the first "Restricted zone" alert, update its status and operator, | |||||
| # then list alerts before and after. | |||||
| # Usage: ./alert_status.sh [status] [operator] | |||||
| # status - optional, default: resolved | |||||
| # operator - optional, default: admin | |||||
| # Example: ./alert_status.sh acknowledged "John Doe" | |||||
| # BASE_URL=http://host:port ./alert_status.sh resolved admin | |||||
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | ||||
| . "${SCRIPT_DIR}/../_common.sh" | . "${SCRIPT_DIR}/../_common.sh" | ||||
| ALERT_ID="${1:?Usage: $0 <alert_id> [status]}" | |||||
| STATUS="${2:-resolved}" | |||||
| STATUS="${1:-resolved}" | |||||
| OPERATOR="${2:-admin}" | |||||
| echo "=== Alerts before update ===" | |||||
| ALERTS=$(curl -s -X GET "${BASE_URL}/reslevis/alerts?limit=100&offset=0") | |||||
| echo "$ALERTS" | jq '.' | |||||
| ALERT_ID=$(echo "$ALERTS" | jq -r '[.[] | select(.type == "Restricted zone")] | first | .id') | |||||
| if [ -z "$ALERT_ID" ] || [ "$ALERT_ID" = "null" ]; then | |||||
| echo "No alert with type 'Restricted zone' found." | |||||
| exit 1 | |||||
| fi | |||||
| echo "" | |||||
| echo "=== Updating alert ${ALERT_ID} (status=${STATUS}, operator=${OPERATOR}) ===" | |||||
| curl -s -X PATCH "${BASE_URL}/reslevis/alerts/${ALERT_ID}" \ | curl -s -X PATCH "${BASE_URL}/reslevis/alerts/${ALERT_ID}" \ | ||||
| -H "Content-Type: application/json" \ | -H "Content-Type: application/json" \ | ||||
| -d "{\"status\": \"${STATUS}\"}" | |||||
| -d "{\"status\": \"${STATUS}\", \"operator\": \"${OPERATOR}\"}" | |||||
| echo "" | |||||
| echo "" | echo "" | ||||
| echo "=== Alerts after update ===" | |||||
| curl -s -X GET "${BASE_URL}/reslevis/alerts?limit=100&offset=0" | jq '.' | |||||