Basic functionality working
Some checks failed
Deploy and Push Docker Image / BuildAndPush (push) Failing after 14s
Some checks failed
Deploy and Push Docker Image / BuildAndPush (push) Failing after 14s
This commit is contained in:
28
.gitea/workflows/docker-build.yml
Normal file
28
.gitea/workflows/docker-build.yml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
name: Deploy and Push Docker Image
|
||||||
|
run-name: ${{ gitea.actor }} deploying to production 🚀
|
||||||
|
on: [push]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
BuildAndPush:
|
||||||
|
runs-on: ubuntu
|
||||||
|
steps:
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
- name: Check out repository code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Login to Docker Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: gitea.henriburau.de
|
||||||
|
username: ${{ gitea.actor }}
|
||||||
|
password: ${{ secrets.REGISTRY_TOKEN}}
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: build/Dockerfile
|
||||||
|
push: true
|
||||||
|
platforms: linux/arm64,linux/amd64
|
||||||
|
tags: |
|
||||||
|
gitea.henriburau.de/ace966/WeeWooWebhook:latest
|
||||||
|
gitea.henriburau.de/ace966/WeeWooWebhook:${{ gitea.run_id }}
|
||||||
18
build/Dockerfile
Normal file
18
build/Dockerfile
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
FROM golang:1.24-alpine AS builder
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy source code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Build the Go binary statically
|
||||||
|
RUN CGO_ENABLED=0 GOOS=linux go build -o weewoowebhook /app/cmd/wee_woo_webhook/main.go
|
||||||
|
|
||||||
|
FROM scratch
|
||||||
|
|
||||||
|
# Copy statically built binary from the builder
|
||||||
|
COPY --from=builder /app/weewoowebhook /weewoowebhook
|
||||||
|
|
||||||
|
# Set the binary as the container entrypoint
|
||||||
|
ENTRYPOINT ["/weewoowebhook"]
|
||||||
19
cmd/wee_woo_webhook/main.go
Normal file
19
cmd/wee_woo_webhook/main.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"gitea.henriburau.de/ace966/WeeWooWebhook/internal/olc"
|
||||||
|
"gitea.henriburau.de/ace966/WeeWooWebhook/internal/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
WeeWooAddr = "http://192.168.1.167"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
officeLightClient := olc.NewClient(WeeWooAddr)
|
||||||
|
server := server.NewServer(officeLightClient)
|
||||||
|
|
||||||
|
log.Fatal(server.ListenAndServe())
|
||||||
|
}
|
||||||
3
go.mod
Normal file
3
go.mod
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
module gitea.henriburau.de/ace966/WeeWooWebhook
|
||||||
|
|
||||||
|
go 1.23.5
|
||||||
84
internal/model/uptime_kuma_webhook_request.go
Normal file
84
internal/model/uptime_kuma_webhook_request.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
const (
|
||||||
|
MonitorStatusDown = 0
|
||||||
|
MonitorStatusUp = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
type UptimeKumaWebhookRequest struct {
|
||||||
|
Heartbeat struct {
|
||||||
|
MonitorID int `json:"monitorID"`
|
||||||
|
Status int `json:"status"`
|
||||||
|
Time string `json:"time"`
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
Important bool `json:"important"`
|
||||||
|
Duration int `json:"duration"`
|
||||||
|
Timezone string `json:"timezone"`
|
||||||
|
TimezoneOffset string `json:"timezoneOffset"`
|
||||||
|
LocalDateTime string `json:"localDateTime"`
|
||||||
|
} `json:"heartbeat"`
|
||||||
|
Monitor struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description any `json:"description"`
|
||||||
|
PathName string `json:"pathName"`
|
||||||
|
Parent any `json:"parent"`
|
||||||
|
ChildrenIDs []any `json:"childrenIDs"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
Method string `json:"method"`
|
||||||
|
Hostname any `json:"hostname"`
|
||||||
|
Port any `json:"port"`
|
||||||
|
Maxretries int `json:"maxretries"`
|
||||||
|
Weight int `json:"weight"`
|
||||||
|
Active bool `json:"active"`
|
||||||
|
ForceInactive bool `json:"forceInactive"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Timeout int `json:"timeout"`
|
||||||
|
Interval int `json:"interval"`
|
||||||
|
RetryInterval int `json:"retryInterval"`
|
||||||
|
ResendInterval int `json:"resendInterval"`
|
||||||
|
Keyword any `json:"keyword"`
|
||||||
|
InvertKeyword bool `json:"invertKeyword"`
|
||||||
|
ExpiryNotification bool `json:"expiryNotification"`
|
||||||
|
IgnoreTLS bool `json:"ignoreTls"`
|
||||||
|
UpsideDown bool `json:"upsideDown"`
|
||||||
|
PacketSize int `json:"packetSize"`
|
||||||
|
Maxredirects int `json:"maxredirects"`
|
||||||
|
AcceptedStatuscodes []string `json:"accepted_statuscodes"`
|
||||||
|
DNSResolveType string `json:"dns_resolve_type"`
|
||||||
|
DNSResolveServer string `json:"dns_resolve_server"`
|
||||||
|
DNSLastResult any `json:"dns_last_result"`
|
||||||
|
DockerContainer string `json:"docker_container"`
|
||||||
|
DockerHost any `json:"docker_host"`
|
||||||
|
ProxyID any `json:"proxyId"`
|
||||||
|
NotificationIDList struct {
|
||||||
|
Num5 bool `json:"5"`
|
||||||
|
} `json:"notificationIDList"`
|
||||||
|
Tags []any `json:"tags"`
|
||||||
|
Maintenance bool `json:"maintenance"`
|
||||||
|
MqttTopic string `json:"mqttTopic"`
|
||||||
|
MqttSuccessMessage string `json:"mqttSuccessMessage"`
|
||||||
|
DatabaseQuery any `json:"databaseQuery"`
|
||||||
|
AuthMethod any `json:"authMethod"`
|
||||||
|
GrpcURL any `json:"grpcUrl"`
|
||||||
|
GrpcProtobuf any `json:"grpcProtobuf"`
|
||||||
|
GrpcMethod any `json:"grpcMethod"`
|
||||||
|
GrpcServiceName any `json:"grpcServiceName"`
|
||||||
|
GrpcEnableTLS bool `json:"grpcEnableTls"`
|
||||||
|
RadiusCalledStationID any `json:"radiusCalledStationId"`
|
||||||
|
RadiusCallingStationID any `json:"radiusCallingStationId"`
|
||||||
|
Game any `json:"game"`
|
||||||
|
GamedigGivenPortOnly bool `json:"gamedigGivenPortOnly"`
|
||||||
|
HTTPBodyEncoding string `json:"httpBodyEncoding"`
|
||||||
|
JSONPath any `json:"jsonPath"`
|
||||||
|
ExpectedValue any `json:"expectedValue"`
|
||||||
|
KafkaProducerTopic any `json:"kafkaProducerTopic"`
|
||||||
|
KafkaProducerBrokers []any `json:"kafkaProducerBrokers"`
|
||||||
|
KafkaProducerSsl bool `json:"kafkaProducerSsl"`
|
||||||
|
KafkaProducerAllowAutoTopicCreation bool `json:"kafkaProducerAllowAutoTopicCreation"`
|
||||||
|
KafkaProducerMessage any `json:"kafkaProducerMessage"`
|
||||||
|
Screenshot any `json:"screenshot"`
|
||||||
|
IncludeSensitiveData bool `json:"includeSensitiveData"`
|
||||||
|
} `json:"monitor"`
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
}
|
||||||
57
internal/olc/office_light_client.go
Normal file
57
internal/olc/office_light_client.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package olc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OfficeLightClient struct {
|
||||||
|
BaseURL string
|
||||||
|
Client *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(baseURL string) *OfficeLightClient {
|
||||||
|
return &OfficeLightClient{
|
||||||
|
BaseURL: baseURL,
|
||||||
|
Client: &http.Client{
|
||||||
|
Timeout: 10 * time.Second,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (olc *OfficeLightClient) SetLightState(state bool) error {
|
||||||
|
endpoint := fmt.Sprintf("%s/state", olc.BaseURL)
|
||||||
|
stateString := "off"
|
||||||
|
if state {
|
||||||
|
stateString = "on"
|
||||||
|
}
|
||||||
|
|
||||||
|
params := url.Values{}
|
||||||
|
params.Set("state", stateString)
|
||||||
|
|
||||||
|
reqURL := fmt.Sprintf("%s?%s", endpoint, params.Encode())
|
||||||
|
resp, err := olc.Client.Post(reqURL, "application/x-www-form-urlencoded", nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (olc *OfficeLightClient) SetColor(r, g, b uint8) error {
|
||||||
|
endpoint := fmt.Sprintf("%s/color", olc.BaseURL)
|
||||||
|
params := url.Values{}
|
||||||
|
params.Set("r", fmt.Sprintf("%d", r))
|
||||||
|
params.Set("g", fmt.Sprintf("%d", g))
|
||||||
|
params.Set("b", fmt.Sprintf("%d", b))
|
||||||
|
|
||||||
|
reqURL := fmt.Sprintf("%s?%s", endpoint, params.Encode())
|
||||||
|
resp, err := olc.Client.Post(reqURL, "application/x-www-form-urlencoded", nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
28
internal/server/server.go
Normal file
28
internal/server/server.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"gitea.henriburau.de/ace966/WeeWooWebhook/internal/olc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
OfficeLightClient *olc.OfficeLightClient
|
||||||
|
downServices map[int]bool
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServer(olc *olc.OfficeLightClient) *Server {
|
||||||
|
return &Server{
|
||||||
|
OfficeLightClient: olc,
|
||||||
|
downServices: make(map[int]bool),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) ListenAndServe() error {
|
||||||
|
fmt.Println("Server listening on :8085/wehook...")
|
||||||
|
http.HandleFunc("/webhook", s.webhookHandler())
|
||||||
|
return http.ListenAndServe(":8085", nil)
|
||||||
|
}
|
||||||
56
internal/server/webhook.go
Normal file
56
internal/server/webhook.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"gitea.henriburau.de/ace966/WeeWooWebhook/internal/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Server) webhookHandler() http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var data model.UptimeKumaWebhookRequest
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
|
||||||
|
http.Error(w, "Invalid JSON", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Received webhook msg: %s\n", data.Msg)
|
||||||
|
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
|
wasDown := s.downServices[data.Monitor.ID]
|
||||||
|
|
||||||
|
switch data.Heartbeat.Status {
|
||||||
|
case model.MonitorStatusDown:
|
||||||
|
if !wasDown {
|
||||||
|
s.downServices[data.Heartbeat.MonitorID] = true
|
||||||
|
log.Printf("Service DOWN: %s", data.Monitor.Name)
|
||||||
|
go s.activateEmergencyLight()
|
||||||
|
}
|
||||||
|
case model.MonitorStatusUp:
|
||||||
|
if wasDown {
|
||||||
|
delete(s.downServices, data.Heartbeat.MonitorID)
|
||||||
|
log.Printf("Service UP: %s", data.Monitor.Name)
|
||||||
|
if len(s.downServices) == 0 {
|
||||||
|
go s.deactivateEmergencyLight()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
fmt.Fprintln(w, "Received")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) activateEmergencyLight() {
|
||||||
|
s.OfficeLightClient.SetColor(255, 0, 0) // Set to red
|
||||||
|
s.OfficeLightClient.SetLightState(true) // Turn on
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) deactivateEmergencyLight() {
|
||||||
|
s.OfficeLightClient.SetLightState(false) // Turn off
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user