Basic functionality working
Some checks failed
Deploy and Push Docker Image / BuildAndPush (push) Failing after 14s

This commit is contained in:
Henri Burau
2025-06-19 13:57:36 +02:00
parent fac655242f
commit 64f955c4a1
8 changed files with 293 additions and 0 deletions

View 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
View 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"]

View 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
View File

@ -0,0 +1,3 @@
module gitea.henriburau.de/ace966/WeeWooWebhook
go 1.23.5

View 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"`
}

View 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
View 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)
}

View 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
}