From cc3c120244931cac4351eac591ba5a60a872b3e7 Mon Sep 17 00:00:00 2001 From: janni06 Date: Wed, 15 Apr 2026 14:57:28 +0200 Subject: [PATCH 01/14] Seperate config loading into individual package --- internal/config/main.go | 31 ++++++++++++++++++++++++ internal/config/models.go | 36 ++++++++++++++++++++++++++++ internal/config/serialize.go | 42 ++++++++++++++++++++++++++++++++ internal/config/validate.go | 46 ++++++++++++++++++++++++++++++++++++ 4 files changed, 155 insertions(+) create mode 100644 internal/config/main.go create mode 100644 internal/config/models.go create mode 100644 internal/config/serialize.go create mode 100644 internal/config/validate.go diff --git a/internal/config/main.go b/internal/config/main.go new file mode 100644 index 0000000..75e62dc --- /dev/null +++ b/internal/config/main.go @@ -0,0 +1,31 @@ +package config + +import ( + "os" + + "gopkg.in/yaml.v3" +) + +func (conf *Config) LoadFromFile(path string) (*Config, error) { + yamlFile, err := os.ReadFile(path) + + if err != nil { + return nil, err + } + + err = yaml.Unmarshal(yamlFile, &conf) + + if err != nil { + return nil, err + } + + conf.Serialize() + + validation_err := conf.Validate() + + if validation_err != nil { + return nil, validation_err + } + + return conf, nil +} diff --git a/internal/config/models.go b/internal/config/models.go new file mode 100644 index 0000000..2424ca3 --- /dev/null +++ b/internal/config/models.go @@ -0,0 +1,36 @@ +package config + +type OpencastConfig struct { + URL string `yaml:"url"` + Username string `yaml:"username"` + Password string `yaml:"password"` + Agent string `yaml:"agent"` +} + +type DisplayStateConfig struct { + Text string `yaml:"text"` + Color string `yaml:"color"` + Background string `yaml:"background"` + Image string `yaml:"image"` + Info string `yaml:"info"` + Empty string `yaml:"none"` +} + +type DisplayConfig struct { + Capturing DisplayStateConfig `yaml:"capturing"` + Idle DisplayStateConfig `yaml:"idle"` + Unknown DisplayStateConfig `yaml:"unknown"` +} + +type MetricsConfig struct { + Enable bool `yaml:"enable"` + Listen string `yaml:"listen"` +} + +type Config struct { + Opencast OpencastConfig `yaml:"opencast"` + Display DisplayConfig `yaml:"display"` + Listen string `yaml:"listen"` + Timeout int `yaml:"timeout"` + Metrics MetricsConfig `yaml:"metrics"` +} diff --git a/internal/config/serialize.go b/internal/config/serialize.go new file mode 100644 index 0000000..f5b367e --- /dev/null +++ b/internal/config/serialize.go @@ -0,0 +1,42 @@ +package config + +import ( + "log/slog" + "strings" +) + +func (oc_conf *OpencastConfig) serialize() { + oc_conf.URL = strings.Trim(oc_conf.URL, "/") +} + +func (met_conf *MetricsConfig) serialize() { + if met_conf.Listen == "" && met_conf.Enable { + met_conf.Listen = "0.0.0.0:9100" + slog.Info("set metrics enpoit to default 0.0.0.0:9100") + } +} + +func (display_conf *DisplayConfig) serialize() {} + +func (display_state_conf *DisplayStateConfig) serialize() {} + +func (conf *Config) Serialize() { + // Serialize opencast config + conf.Opencast.serialize() + + // Serilaize Display config + conf.Display.serialize() + + // Serialize metrics config + conf.Metrics.serialize() + + if conf.Listen == "" { + conf.Listen = "127.0.0.1:8080" + slog.Info("set backend listen to default 127.0.0.1:8080") + } + + if conf.Timeout <= 0 { + conf.Timeout = 500 + slog.Info("set request timeout to a reasonable level") + } +} diff --git a/internal/config/validate.go b/internal/config/validate.go new file mode 100644 index 0000000..5f1910f --- /dev/null +++ b/internal/config/validate.go @@ -0,0 +1,46 @@ +package config + +import ( + "errors" +) + +func (oc_conf *OpencastConfig) validate() error { + if oc_conf.URL == "" { + return errors.New("Opencast: No Opencast server URL in configuration") + } + return nil +} + +func (display_conf *DisplayConfig) validate() error { + return nil +} + +func (disp_state_conf *DisplayStateConfig) validate() error { + return nil +} + +func (metrics_conf *MetricsConfig) validate() error { + return nil +} + +func (conf *Config) Validate() error { + opencast_err := conf.Opencast.validate() + + display_err := conf.Display.validate() + + metrics_err := conf.Metrics.validate() + + var timeout_err error + if conf.Timeout <= 0 { + timeout_err = errors.New("the timeout canĀ“t be zero or negative") + } + + var listen_err error + if conf.Listen == "" { + listen_err = errors.New("it most be configured, where the main backend should listen on") + } + + err := errors.Join(opencast_err, display_err, metrics_err, timeout_err, listen_err) + + return err +} From 5f76c91032eedde3d7a71226def5efd9c382a398 Mon Sep 17 00:00:00 2001 From: janni06 Date: Wed, 15 Apr 2026 17:09:40 +0200 Subject: [PATCH 02/14] Add doc strings --- internal/config/main.go | 3 +++ internal/config/serialize.go | 9 +++++++++ internal/config/validate.go | 12 ++++++++++++ 3 files changed, 24 insertions(+) diff --git a/internal/config/main.go b/internal/config/main.go index 75e62dc..0fa2466 100644 --- a/internal/config/main.go +++ b/internal/config/main.go @@ -6,6 +6,9 @@ import ( "gopkg.in/yaml.v3" ) +// LoadFromFile loads the configuration from a YAML file at the given path. +// It validates and serializes the configuration after loading. +// Returns the loaded Config or an error if loading, parsing, validation, or serialization fails. func (conf *Config) LoadFromFile(path string) (*Config, error) { yamlFile, err := os.ReadFile(path) diff --git a/internal/config/serialize.go b/internal/config/serialize.go index f5b367e..dc61dc0 100644 --- a/internal/config/serialize.go +++ b/internal/config/serialize.go @@ -5,10 +5,13 @@ import ( "strings" ) +// serialize cleans up the Opencast configuration by trimming trailing slashes from the URL. func (oc_conf *OpencastConfig) serialize() { oc_conf.URL = strings.Trim(oc_conf.URL, "/") } +// serialize sets default values for Metrics configuration if not specified. +// If Metrics are enabled but Listen is empty, it defaults to "0.0.0.0:9100". func (met_conf *MetricsConfig) serialize() { if met_conf.Listen == "" && met_conf.Enable { met_conf.Listen = "0.0.0.0:9100" @@ -16,10 +19,16 @@ func (met_conf *MetricsConfig) serialize() { } } +// serialize performs serialization on Display configuration. +// Currently a no-op (empty implementation). func (display_conf *DisplayConfig) serialize() {} +// serialize performs serialization on DisplayState configuration. +// Currently a no-op (empty implementation). func (display_state_conf *DisplayStateConfig) serialize() {} +// Serialize performs serialization on all configuration sections. +// It sets default values for Listen (127.0.0.1:8080) and Timeout (500) if not specified. func (conf *Config) Serialize() { // Serialize opencast config conf.Opencast.serialize() diff --git a/internal/config/validate.go b/internal/config/validate.go index 5f1910f..7842d43 100644 --- a/internal/config/validate.go +++ b/internal/config/validate.go @@ -4,6 +4,8 @@ import ( "errors" ) +// validate checks if the Opencast configuration has a valid URL. +// Returns an error if the URL is empty. func (oc_conf *OpencastConfig) validate() error { if oc_conf.URL == "" { return errors.New("Opencast: No Opencast server URL in configuration") @@ -11,18 +13,28 @@ func (oc_conf *OpencastConfig) validate() error { return nil } +// validate checks if the Display configuration is valid. +// Currently always returns nil (no validation implemented). func (display_conf *DisplayConfig) validate() error { return nil } +// validate checks if the DisplayState configuration is valid. +// Currently always returns nil (no validation implemented). func (disp_state_conf *DisplayStateConfig) validate() error { return nil } +// validate checks if the Metrics configuration is valid. +// Currently always returns nil (no validation implemented). func (metrics_conf *MetricsConfig) validate() error { return nil } +// Validate performs validation on all configuration sections. +// It checks Opencast, Display, and Metrics configurations, as well as +// the main timeout and listen settings. +// Returns a combined error of all validation failures, or nil if all are valid. func (conf *Config) Validate() error { opencast_err := conf.Opencast.validate() From caad59a4caaaf8fb560e3145cc5675dbcb48a288 Mon Sep 17 00:00:00 2001 From: janni06 Date: Thu, 16 Apr 2026 11:17:32 +0200 Subject: [PATCH 03/14] Make use of new config package --- internal/config/main.go | 3 + internal/config/models.go | 2 +- main.go | 158 ++++++++++++++++++++------------------ 3 files changed, 87 insertions(+), 76 deletions(-) diff --git a/internal/config/main.go b/internal/config/main.go index 0fa2466..24075ba 100644 --- a/internal/config/main.go +++ b/internal/config/main.go @@ -1,6 +1,7 @@ package config import ( + "log/slog" "os" "gopkg.in/yaml.v3" @@ -30,5 +31,7 @@ func (conf *Config) LoadFromFile(path string) (*Config, error) { return nil, validation_err } + slog.Info("Finished loading configuration from file") + return conf, nil } diff --git a/internal/config/models.go b/internal/config/models.go index 2424ca3..f9c8102 100644 --- a/internal/config/models.go +++ b/internal/config/models.go @@ -23,7 +23,7 @@ type DisplayConfig struct { } type MetricsConfig struct { - Enable bool `yaml:"enable"` + Enable bool `yaml:"prometheus"` // is currently still controlled through prometheus, TODO: change in future Listen string `yaml:"listen"` } diff --git a/main.go b/main.go index 604a63f..bb4b474 100644 --- a/main.go +++ b/main.go @@ -19,19 +19,18 @@ package main import ( "embed" "encoding/json" - "errors" "fmt" "io" "io/fs" "log" + "log/slog" "net" "net/http" + "opencast-ca-display/internal/config" "os" - "strings" "time" "github.com/gin-gonic/gin" - "gopkg.in/yaml.v3" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" @@ -82,14 +81,14 @@ type CalendarEntry struct { EpisodeDublinCore string `json:"episode-dublincore"` } -type DisplayConfig struct { - Text string `json:"text"` - Color string `json:"color"` - Background string `json:"background"` - Image string `json:"image"` - Info string `json:"info"` - Empty string `json:"none"` -} +// type DisplayConfig struct { +// Text string `json:"text"` +// Color string `json:"color"` +// Background string `json:"background"` +// Image string `json:"image"` +// Info string `json:"info"` +// Empty string `json:"none"` +// } type NetworkStatus struct { Interfaces []NetInterface `json:"interfaces"` @@ -104,31 +103,31 @@ type NetInterface struct { Flags string `json:"flags"` } -type Config struct { - Opencast struct { - Url string - Username string - Password string - Agent string - } - - Display struct { - Capturing DisplayConfig `json:"capturing"` - Idle DisplayConfig `json:"idle"` - Unknown DisplayConfig `json:"unknown"` - } - - Listen string - Timeout int - - Metrics struct { - Prometheus bool - Listen string - } -} +// type Config struct { +// Opencast struct { +// Url string +// Username string +// Password string +// Agent string +// } + +// Display struct { +// Capturing DisplayConfig `json:"capturing"` +// Idle DisplayConfig `json:"idle"` +// Unknown DisplayConfig `json:"unknown"` +// } + +// Listen string +// Timeout int + +// Metrics struct { +// Prometheus bool +// Listen string +// } +// } var ( - config Config + cConfig config.Config //go:embed assets res embed.FS @@ -170,39 +169,39 @@ var ( }, []string{"state"}) ) -func loadConfig(configPath string) (*Config, error) { - // Open config file - yamlFile, err := os.ReadFile(configPath) - if err != nil { - return nil, err - } +// func loadConfig(configPath string) (*config.Config, error) { +// // Open config file +// yamlFile, err := os.ReadFile(configPath) +// if err != nil { +// return nil, err +// } - // Decode YAML file - if err := yaml.Unmarshal(yamlFile, &config); err != nil { - return nil, err - } +// // Decode YAML file +// if err := yaml.Unmarshal(yamlFile, &cConfig); err != nil { +// return nil, err +// } - // Ensure URL does not have trailing / - config.Opencast.Url = strings.Trim(config.Opencast.Url, "/") - if config.Opencast.Url == "" { - return nil, errors.New("no Opencast server URL in configuration") - } +// // Ensure URL does not have trailing / +// cConfig.Opencast.URL = strings.Trim(cConfig.Opencast.URL, "/") +// if cConfig.Opencast.URL == "" { +// return nil, errors.New("no Opencast server URL in configuration") +// } - if config.Listen == "" { - config.Listen = "127.0.0.1:8080" - } +// if cConfig.Listen == "" { +// cConfig.Listen = "127.0.0.1:8080" +// } - if config.Metrics.Listen == "" { - config.Metrics.Listen = "0.0.0.0:9100" - } +// if cConfig.Metrics.Listen == "" { +// cConfig.Metrics.Listen = "0.0.0.0:9100" +// } - if config.Timeout == 0 { - // Timeout in Milliseconds - config.Timeout = 500 - } +// if cConfig.Timeout == 0 { +// // Timeout in Milliseconds +// cConfig.Timeout = 500 +// } - return &config, nil -} +// return &cConfig, nil +// } func setupRouter() *gin.Engine { r := gin.Default() @@ -227,13 +226,13 @@ func setupRouter() *gin.Engine { // Display Config r.GET("/config", func(c *gin.Context) { - c.JSON(http.StatusOK, config.Display) + c.JSON(http.StatusOK, cConfig.Display) }) // Status r.GET("/status", func(c *gin.Context) { - client := &http.Client{Timeout: time.Duration(config.Timeout * int(time.Millisecond))} - url := config.Opencast.Url + "/capture-admin/agents/" + config.Opencast.Agent + ".json" + client := &http.Client{Timeout: time.Duration(cConfig.Timeout * int(time.Millisecond))} + url := cConfig.Opencast.URL + "/capture-admin/agents/" + cConfig.Opencast.Agent + ".json" req, err := http.NewRequest("GET", url, nil) if err != nil { log.Println(err) @@ -241,7 +240,7 @@ func setupRouter() *gin.Engine { stateCollector.WithLabelValues("internal_server_error").Set(1) return } - req.SetBasicAuth(config.Opencast.Username, config.Opencast.Password) + req.SetBasicAuth(cConfig.Opencast.Username, cConfig.Opencast.Password) resp, err := client.Do(req) lastUpdate = time.Now() if err != nil { @@ -289,17 +288,17 @@ func setupRouter() *gin.Engine { }) r.GET("/calendar", func(c *gin.Context) { - client := &http.Client{Timeout: time.Duration(config.Timeout * int(time.Millisecond))} + client := &http.Client{Timeout: time.Duration(cConfig.Timeout * int(time.Millisecond))} // Cutoff is set to 24 hours from now cutoff := time.Now().UnixMilli() + 86400000 - url := config.Opencast.Url + "/recordings/calendar.json?agentid=" + config.Opencast.Agent + "&cutoff=" + fmt.Sprint(cutoff) + "×tamp=true" + url := cConfig.Opencast.URL + "/recordings/calendar.json?agentid=" + cConfig.Opencast.Agent + "&cutoff=" + fmt.Sprint(cutoff) + "×tamp=true" req, err := http.NewRequest("GET", url, nil) if err != nil { log.Println(err) c.JSON(http.StatusBadGateway, nil) return } - req.SetBasicAuth(config.Opencast.Username, config.Opencast.Password) + req.SetBasicAuth(cConfig.Opencast.Username, cConfig.Opencast.Password) resp, err := client.Do(req) if err != nil { if os.IsTimeout(err) { @@ -368,15 +367,15 @@ func setupRouter() *gin.Engine { inter := NetInterface{Name: net_inter.Name, MAC: net_inter.HardwareAddr.String(), Adress: addrs_str, Flags: net_inter.Flags.String()} net_status.Interfaces = append(net_status.Interfaces, inter) } - client := &http.Client{Timeout: time.Duration(config.Timeout * int(time.Millisecond))} - url := config.Opencast.Url + client := &http.Client{Timeout: time.Duration(cConfig.Timeout * int(time.Millisecond))} + url := cConfig.Opencast.URL req, err := http.NewRequest("GET", url, nil) if err != nil { log.Println(err) c.JSON(http.StatusBadGateway, nil) return } - // req.SetBasicAuth(config.Opencast.Username, config.Opencast.Password) + // req.SetBasicAuth(cConfig.Opencast.Username, cConfig.Opencast.Password) _, err = client.Do(req) if err != nil { net_status.Connected = false @@ -410,20 +409,29 @@ func setupMetricsRouter() *gin.Engine { } func main() { - if _, err := loadConfig("opencast-ca-display.yml"); err != nil { + // if _, err := loadConfig("opencast-ca-display.yml"); err != nil { + // log.Fatalf("Failed to load configuration: %v", err) + // } + + logger := slog.New(slog.NewTextHandler(os.Stderr, nil)) + slog.SetDefault(logger) + + cConfig, err:= cConfig.LoadFromFile("opencast-ca-display.yml") + if err != nil { log.Fatalf("Failed to load configuration: %v", err) } - if config.Metrics.Prometheus { + + if cConfig.Metrics.Enable { go func() { metricsRouter := setupMetricsRouter() - if err := metricsRouter.Run(config.Metrics.Listen); err != nil { + if err := metricsRouter.Run(cConfig.Metrics.Listen); err != nil { log.Fatalf("Failed to run metrics server: %v", err) } }() } r := setupRouter() - if err := r.Run(config.Listen); err != nil { + if err := r.Run(cConfig.Listen); err != nil { log.Fatalf("Failed to run server: %v", err) } } From 71897a1ef0384e7c14b498739f834528f25bdc55 Mon Sep 17 00:00:00 2001 From: janni06 Date: Thu, 16 Apr 2026 11:49:30 +0200 Subject: [PATCH 04/14] Update demo config to working endpoint --- opencast-ca-display.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/opencast-ca-display.yml b/opencast-ca-display.yml index 62f1595..e4269a5 100644 --- a/opencast-ca-display.yml +++ b/opencast-ca-display.yml @@ -1,13 +1,13 @@ opencast: # URL of the Opencast server to connect to - url: https://develop.opencast.org + url: https://stable.opencast.org # Uername and password of user with sufficient access to read agent status username: admin password: opencast # Capture agent to show the status for - agent: pyca@pyca-test.novalocal + agent: The_NLD # Display configuration # Each state configuration contains four fields: @@ -122,7 +122,7 @@ display: # IP address and port to bind to listen: 127.0.0.1:8080 # Variable Timout (sets to 500 Miliseconds if left blank) -timeout: 500 +timeout: 1500 metrics: # Enable Prometheus metrics From b28be6dcb2d42b55002b66f41306624ead889f06 Mon Sep 17 00:00:00 2001 From: janni06 Date: Thu, 16 Apr 2026 12:29:26 +0200 Subject: [PATCH 05/14] Set labels for JSON config export --- internal/config/models.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/config/models.go b/internal/config/models.go index f9c8102..ba51d1f 100644 --- a/internal/config/models.go +++ b/internal/config/models.go @@ -8,18 +8,18 @@ type OpencastConfig struct { } type DisplayStateConfig struct { - Text string `yaml:"text"` - Color string `yaml:"color"` - Background string `yaml:"background"` - Image string `yaml:"image"` - Info string `yaml:"info"` - Empty string `yaml:"none"` + Text string `yaml:"text" json:"text"` + Color string `yaml:"color" json:"color"` + Background string `yaml:"background" json:"background"` + Image string `yaml:"image" json:"image"` + Info string `yaml:"info" json:"info"` + Empty string `yaml:"none" json:"empty"` } type DisplayConfig struct { - Capturing DisplayStateConfig `yaml:"capturing"` - Idle DisplayStateConfig `yaml:"idle"` - Unknown DisplayStateConfig `yaml:"unknown"` + Capturing DisplayStateConfig `yaml:"capturing" json:"capturing"` + Idle DisplayStateConfig `yaml:"idle" json:"idle"` + Unknown DisplayStateConfig `yaml:"unknown" json:"unknown"` } type MetricsConfig struct { From e895aa217cc152118a040a3ca8f621d81ce407d2 Mon Sep 17 00:00:00 2001 From: janni06 Date: Thu, 16 Apr 2026 12:40:43 +0200 Subject: [PATCH 06/14] Remove the replaced structs and functions --- main.go | 66 --------------------------------------------------------- 1 file changed, 66 deletions(-) diff --git a/main.go b/main.go index bb4b474..554c8a0 100644 --- a/main.go +++ b/main.go @@ -81,15 +81,6 @@ type CalendarEntry struct { EpisodeDublinCore string `json:"episode-dublincore"` } -// type DisplayConfig struct { -// Text string `json:"text"` -// Color string `json:"color"` -// Background string `json:"background"` -// Image string `json:"image"` -// Info string `json:"info"` -// Empty string `json:"none"` -// } - type NetworkStatus struct { Interfaces []NetInterface `json:"interfaces"` Connected bool `json:"connected"` @@ -103,29 +94,6 @@ type NetInterface struct { Flags string `json:"flags"` } -// type Config struct { -// Opencast struct { -// Url string -// Username string -// Password string -// Agent string -// } - -// Display struct { -// Capturing DisplayConfig `json:"capturing"` -// Idle DisplayConfig `json:"idle"` -// Unknown DisplayConfig `json:"unknown"` -// } - -// Listen string -// Timeout int - -// Metrics struct { -// Prometheus bool -// Listen string -// } -// } - var ( cConfig config.Config @@ -169,40 +137,6 @@ var ( }, []string{"state"}) ) -// func loadConfig(configPath string) (*config.Config, error) { -// // Open config file -// yamlFile, err := os.ReadFile(configPath) -// if err != nil { -// return nil, err -// } - -// // Decode YAML file -// if err := yaml.Unmarshal(yamlFile, &cConfig); err != nil { -// return nil, err -// } - -// // Ensure URL does not have trailing / -// cConfig.Opencast.URL = strings.Trim(cConfig.Opencast.URL, "/") -// if cConfig.Opencast.URL == "" { -// return nil, errors.New("no Opencast server URL in configuration") -// } - -// if cConfig.Listen == "" { -// cConfig.Listen = "127.0.0.1:8080" -// } - -// if cConfig.Metrics.Listen == "" { -// cConfig.Metrics.Listen = "0.0.0.0:9100" -// } - -// if cConfig.Timeout == 0 { -// // Timeout in Milliseconds -// cConfig.Timeout = 500 -// } - -// return &cConfig, nil -// } - func setupRouter() *gin.Engine { r := gin.Default() // disable all proxies From a3d9d8b15bcd4f0c9045db5dd015edd839af23f2 Mon Sep 17 00:00:00 2001 From: janni06 Date: Thu, 16 Apr 2026 12:45:14 +0200 Subject: [PATCH 07/14] go fmt: Formatting --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index 554c8a0..048f538 100644 --- a/main.go +++ b/main.go @@ -350,7 +350,7 @@ func main() { logger := slog.New(slog.NewTextHandler(os.Stderr, nil)) slog.SetDefault(logger) - cConfig, err:= cConfig.LoadFromFile("opencast-ca-display.yml") + cConfig, err := cConfig.LoadFromFile("opencast-ca-display.yml") if err != nil { log.Fatalf("Failed to load configuration: %v", err) } From 5b9a1be420f9377ed65835cf444dc9cc8fd60dd8 Mon Sep 17 00:00:00 2001 From: janni06 Date: Wed, 15 Apr 2026 17:46:16 +0200 Subject: [PATCH 08/14] Add base endpoint config --- internal/endpoints/calendar.go | 7 +++++++ internal/endpoints/network.go | 5 +++++ internal/endpoints/router.go | 14 ++++++++++++++ internal/endpoints/status.go | 5 +++++ 4 files changed, 31 insertions(+) create mode 100644 internal/endpoints/calendar.go create mode 100644 internal/endpoints/network.go create mode 100644 internal/endpoints/router.go create mode 100644 internal/endpoints/status.go diff --git a/internal/endpoints/calendar.go b/internal/endpoints/calendar.go new file mode 100644 index 0000000..2bdf669 --- /dev/null +++ b/internal/endpoints/calendar.go @@ -0,0 +1,7 @@ +package endpoints + +import "github.com/gin-gonic/gin" + +func calendarEndpoint(c *gin.Context) { + +} diff --git a/internal/endpoints/network.go b/internal/endpoints/network.go new file mode 100644 index 0000000..bbb33a6 --- /dev/null +++ b/internal/endpoints/network.go @@ -0,0 +1,5 @@ +package endpoints + +import "github.com/gin-gonic/gin" + +func networkEndpoint(c *gin.Context) {} diff --git a/internal/endpoints/router.go b/internal/endpoints/router.go new file mode 100644 index 0000000..65cb620 --- /dev/null +++ b/internal/endpoints/router.go @@ -0,0 +1,14 @@ +package endpoints + +import "github.com/gin-gonic/gin" + +func SetupRouter(group gin.RouterGroup) { + // status Endpoint + group.GET("/status", statusEndpoint) + + // calendar Endpoint + group.GET("/calendar", calendarEndpoint) + + // network Endpoint + group.GET("/network_info", networkEndpoint) +} diff --git a/internal/endpoints/status.go b/internal/endpoints/status.go new file mode 100644 index 0000000..598cdf1 --- /dev/null +++ b/internal/endpoints/status.go @@ -0,0 +1,5 @@ +package endpoints + +import "github.com/gin-gonic/gin" + +func statusEndpoint(c *gin.Context) {} From 33adb9dd24be1bb39ea354e19d18c31197a2d5ac Mon Sep 17 00:00:00 2001 From: janni06 Date: Thu, 16 Apr 2026 13:41:42 +0200 Subject: [PATCH 09/14] Move enpoint implementation from main.go into individual files --- internal/endpoints/calendar.go | 107 ++++++++++++++++++++++++++++++++- internal/endpoints/network.go | 63 ++++++++++++++++++- internal/endpoints/router.go | 7 ++- internal/endpoints/status.go | 76 ++++++++++++++++++++++- 4 files changed, 246 insertions(+), 7 deletions(-) diff --git a/internal/endpoints/calendar.go b/internal/endpoints/calendar.go index 2bdf669..8099767 100644 --- a/internal/endpoints/calendar.go +++ b/internal/endpoints/calendar.go @@ -1,7 +1,112 @@ package endpoints -import "github.com/gin-gonic/gin" +import ( + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "os" + "time" + + "github.com/gin-gonic/gin" +) + +type Event struct { + Title string `json:"title"` + Start int `json:"start"` + End int `json:"end"` +} + +type CalendarWorkflowProperties struct { + StraightToPublishing string `json:"straightToPublishing"` +} + +type CalenderAgentConfig struct { + CaptureDeviceNames string `json:"capture.device.names"` + WorkflowDefinition string `json:"org.opencastproject.workflow.definition"` + WorkflowConfigStraightToPublishing string `json:"org.opencastproject.workflow.config.straightToPublishing"` + EventLocation string `json:"event.location"` + EventTitle string `json:"event.title"` +} + +type CalendarRecording struct { +} + +type CalendarData struct { + EventID string `json:"eventId"` + AgentID string `json:"agentId"` + StartDate int // Verwenden Sie time.Time statt string + EndDate int // Verwenden Sie time.Time statt string + Presenters []string `json:"presenters"` + WorkflowProperties CalendarWorkflowProperties `json:"workflowProperties"` + AgentConfig CalenderAgentConfig `json:"agentConfig"` + Recording CalendarRecording `json:"recording"` +} + +type CalendarEntry struct { + Data CalendarData `json:"data"` + EpisodeDublinCore string `json:"episode-dublincore"` +} func calendarEndpoint(c *gin.Context) { + client := &http.Client{Timeout: time.Duration(config.Timeout * int(time.Millisecond))} + // Cutoff is set to 24 hours from now + cutoff := time.Now().UnixMilli() + 86400000 + url := config.Opencast.Url + "/recordings/calendar.json?agentid=" + config.Opencast.Agent + "&cutoff=" + fmt.Sprint(cutoff) + "×tamp=true" + req, err := http.NewRequest("GET", url, nil) + if err != nil { + log.Println(err) + c.JSON(http.StatusBadGateway, nil) + return + } + req.SetBasicAuth(config.Opencast.Username, config.Opencast.Password) + resp, err := client.Do(req) + if err != nil { + if os.IsTimeout(err) { + log.Println("Request timed out:", err) + c.JSON(http.StatusGatewayTimeout, gin.H{"error": "Request timed out"}) + // stateCollector.WithLabelValues("gateway_timeout").Set(1) + } else { + log.Println(err) + c.JSON(http.StatusBadGateway, gin.H{"error": "Internal server error"}) + // stateCollector.WithLabelValues("internal_server_error").Set(1) + } + return + } + if resp.StatusCode != 200 { + log.Println(resp) + c.JSON(resp.StatusCode, nil) + return + } + + bodyText, err := io.ReadAll(resp.Body) + if err != nil { + log.Println(err) + c.JSON(http.StatusBadGateway, nil) + return + } + s := string([]byte(bodyText)) + + var allEvents []CalendarEntry + json_err := json.Unmarshal([]byte(s), &allEvents) + if json_err != nil { + log.Fatal(json_err) + } + + var events []Event + for _, eventData := range allEvents { + start := eventData.Data.StartDate + end := eventData.Data.EndDate + title := eventData.Data.AgentConfig.EventTitle + e := Event{Title: title, Start: start, End: end} + events = append(events, e) + } + if len(allEvents) > 0 { + fmt.Println(events) + c.JSON(http.StatusOK, events) + } else { + c.JSON(http.StatusOK, "") + } } diff --git a/internal/endpoints/network.go b/internal/endpoints/network.go index bbb33a6..6f045c1 100644 --- a/internal/endpoints/network.go +++ b/internal/endpoints/network.go @@ -1,5 +1,64 @@ package endpoints -import "github.com/gin-gonic/gin" +import ( + "log" + "net" + "net/http" + "os" + "time" -func networkEndpoint(c *gin.Context) {} + "github.com/gin-gonic/gin" +) + +type NetworkStatus struct { + Interfaces []NetInterface `json:"interfaces"` + Connected bool `json:"connected"` + Hostname string `json:"hostname"` +} + +type NetInterface struct { + Name string `json:"name"` + Adress []string `json:"addr"` + MAC string `json:"mac_adress"` + Flags string `json:"flags"` +} + +func networkEndpoint(c *gin.Context) { + var net_status NetworkStatus + net_interfaces, err := net.Interfaces() + if err != nil { + log.Fatalln("Network devices could not be loaded.") + return + } + for _, net_inter := range net_interfaces { + addrs, err := net_inter.Addrs() + var addrs_str []string + if err == nil { + for _, a := range addrs { + addrs_str = append(addrs_str, a.String()) + } + } + inter := NetInterface{Name: net_inter.Name, MAC: net_inter.HardwareAddr.String(), Adress: addrs_str, Flags: net_inter.Flags.String()} + net_status.Interfaces = append(net_status.Interfaces, inter) + } + client := &http.Client{Timeout: time.Duration(config.Timeout * int(time.Millisecond))} + url := config.Opencast.Url + req, err := http.NewRequest("GET", url, nil) + if err != nil { + log.Println(err) + c.JSON(http.StatusBadGateway, nil) + return + } + // req.SetBasicAuth(config.Opencast.Username, config.Opencast.Password) + _, err = client.Do(req) + if err != nil { + net_status.Connected = false + } else { + net_status.Connected = true + } + net_status.Hostname, err = os.Hostname() + if err != nil { + c.JSON(http.StatusInternalServerError, nil) + } + c.JSON(http.StatusOK, net_status) +} diff --git a/internal/endpoints/router.go b/internal/endpoints/router.go index 65cb620..1145c9d 100644 --- a/internal/endpoints/router.go +++ b/internal/endpoints/router.go @@ -1,8 +1,11 @@ package endpoints -import "github.com/gin-gonic/gin" +import ( + "github.com/gin-gonic/gin" +) + +func ApiRouter(group *gin.RouterGroup) { // TODO -func SetupRouter(group gin.RouterGroup) { // status Endpoint group.GET("/status", statusEndpoint) diff --git a/internal/endpoints/status.go b/internal/endpoints/status.go index 598cdf1..bfd115b 100644 --- a/internal/endpoints/status.go +++ b/internal/endpoints/status.go @@ -1,5 +1,77 @@ package endpoints -import "github.com/gin-gonic/gin" +import ( + "encoding/json" + "io" + "log" + "net/http" + "os" + "time" -func statusEndpoint(c *gin.Context) {} + "github.com/gin-gonic/gin" +) + +type AgentStateResult struct { + Update struct { + Name string + State string + Url string + } `json:"agent-state-update"` +} + +func statusEndpoint(c *gin.Context) { + client := &http.Client{Timeout: time.Duration(config.Timeout * int(time.Millisecond))} + url := config.Opencast.Url + "/capture-admin/agents/" + config.Opencast.Agent + ".json" + req, err := http.NewRequest("GET", url, nil) + if err != nil { + log.Println(err) + c.JSON(http.StatusInternalServerError, nil) + // stateCollector.WithLabelValues("internal_server_error").Set(1) + return + } + req.SetBasicAuth(config.Opencast.Username, config.Opencast.Password) + resp, err := client.Do(req) + // lastUpdate = time.Now() + if err != nil { + if os.IsTimeout(err) { + log.Println("Request timed out:", err) + c.JSON(http.StatusGatewayTimeout, gin.H{"error": "Request timed out"}) + // stateCollector.WithLabelValues("gateway_timeout").Set(1) + } else { + log.Println(err) + c.JSON(http.StatusBadGateway, gin.H{"error": "Internal server error"}) + // stateCollector.WithLabelValues("internal_server_error").Set(1) + } + return + } + + if resp.StatusCode != 200 { + log.Println(resp) + c.JSON(resp.StatusCode, nil) + // stateCollector.WithLabelValues(fmt.Sprintf("%d", resp.StatusCode)).Set(1) + return + } + + bodyText, err := io.ReadAll(resp.Body) + if err != nil { + log.Println(err) + c.JSON(http.StatusInternalServerError, nil) + // stateCollector.WithLabelValues("internal_server_error").Set(1) + return + } + s := string(bodyText) + var result AgentStateResult + jsonErr := json.Unmarshal([]byte(s), &result) + + if jsonErr != nil { + log.Println(err) + c.JSON(http.StatusInternalServerError, nil) + // stateCollector.WithLabelValues("internal_server_error").Set(1) + return + } + + // stateCollector.Reset() + // stateCollector.WithLabelValues(result.Update.State).Set(1) + + c.JSON(http.StatusOK, result.Update.State == "capturing") +} From b968fd38931d31a6e6cc7df7f191c42a8500c726 Mon Sep 17 00:00:00 2001 From: janni06 Date: Thu, 16 Apr 2026 13:55:42 +0200 Subject: [PATCH 10/14] Add Atomic globally usable config --- internal/config/main.go | 19 +++++++++++++++++++ main.go | 3 +++ 2 files changed, 22 insertions(+) diff --git a/internal/config/main.go b/internal/config/main.go index 24075ba..dba9c66 100644 --- a/internal/config/main.go +++ b/internal/config/main.go @@ -3,10 +3,21 @@ package config import ( "log/slog" "os" + "sync/atomic" "gopkg.in/yaml.v3" ) +var defaultConfig atomic.Pointer[Config] + +func init() { + defaultConfig.Store(New()) +} + +func New() *Config { + return &Config{} +} + // LoadFromFile loads the configuration from a YAML file at the given path. // It validates and serializes the configuration after loading. // Returns the loaded Config or an error if loading, parsing, validation, or serialization fails. @@ -35,3 +46,11 @@ func (conf *Config) LoadFromFile(path string) (*Config, error) { return conf, nil } + +func SetConfig(c *Config){ + defaultConfig.Store(c) +} + +func Default() *Config { + return defaultConfig.Load() +} \ No newline at end of file diff --git a/main.go b/main.go index 048f538..30e1906 100644 --- a/main.go +++ b/main.go @@ -351,6 +351,9 @@ func main() { slog.SetDefault(logger) cConfig, err := cConfig.LoadFromFile("opencast-ca-display.yml") + + config.SetConfig(cConfig) + if err != nil { log.Fatalf("Failed to load configuration: %v", err) } From 828c75fd59d18e235c580f9939247fda01253013 Mon Sep 17 00:00:00 2001 From: janni06 Date: Thu, 16 Apr 2026 14:06:59 +0200 Subject: [PATCH 11/14] Make use of atomic global config --- internal/endpoints/calendar.go | 10 +++++----- internal/endpoints/network.go | 6 +++--- internal/endpoints/router.go | 11 +++++++++++ internal/endpoints/status.go | 6 +++--- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/internal/endpoints/calendar.go b/internal/endpoints/calendar.go index 8099767..e5232d4 100644 --- a/internal/endpoints/calendar.go +++ b/internal/endpoints/calendar.go @@ -50,17 +50,17 @@ type CalendarEntry struct { } func calendarEndpoint(c *gin.Context) { - client := &http.Client{Timeout: time.Duration(config.Timeout * int(time.Millisecond))} - // Cutoff is set to 24 hours from now - cutoff := time.Now().UnixMilli() + 86400000 - url := config.Opencast.Url + "/recordings/calendar.json?agentid=" + config.Opencast.Agent + "&cutoff=" + fmt.Sprint(cutoff) + "×tamp=true" + client := &http.Client{Timeout: time.Duration(localConfig.Timeout * int(time.Millisecond))} + // Cutoff is set to 3 day from now; TODO: set back to 24 Hours + cutoff := time.Now().Add(time.Hour*720).UnixMilli() + url := localConfig.Opencast.URL + "/recordings/calendar.json?agentid=" + localConfig.Opencast.Agent + "&cutoff=" + fmt.Sprint(cutoff) + "×tamp=true" req, err := http.NewRequest("GET", url, nil) if err != nil { log.Println(err) c.JSON(http.StatusBadGateway, nil) return } - req.SetBasicAuth(config.Opencast.Username, config.Opencast.Password) + req.SetBasicAuth(localConfig.Opencast.Username, localConfig.Opencast.Password) resp, err := client.Do(req) if err != nil { if os.IsTimeout(err) { diff --git a/internal/endpoints/network.go b/internal/endpoints/network.go index 6f045c1..d2054d5 100644 --- a/internal/endpoints/network.go +++ b/internal/endpoints/network.go @@ -41,15 +41,15 @@ func networkEndpoint(c *gin.Context) { inter := NetInterface{Name: net_inter.Name, MAC: net_inter.HardwareAddr.String(), Adress: addrs_str, Flags: net_inter.Flags.String()} net_status.Interfaces = append(net_status.Interfaces, inter) } - client := &http.Client{Timeout: time.Duration(config.Timeout * int(time.Millisecond))} - url := config.Opencast.Url + client := &http.Client{Timeout: time.Duration(localConfig.Timeout * int(time.Millisecond))} + url := localConfig.Opencast.URL req, err := http.NewRequest("GET", url, nil) if err != nil { log.Println(err) c.JSON(http.StatusBadGateway, nil) return } - // req.SetBasicAuth(config.Opencast.Username, config.Opencast.Password) + // req.SetBasicAuth(localConfig.Opencast.Username, localConfig.Opencast.Password) _, err = client.Do(req) if err != nil { net_status.Connected = false diff --git a/internal/endpoints/router.go b/internal/endpoints/router.go index 1145c9d..dca938d 100644 --- a/internal/endpoints/router.go +++ b/internal/endpoints/router.go @@ -1,11 +1,22 @@ package endpoints import ( + "net/http" + "opencast-ca-display/internal/config" + "github.com/gin-gonic/gin" ) +var localConfig *config.Config + func ApiRouter(group *gin.RouterGroup) { // TODO + localConfig = config.Default() + + group.GET("/config", func(c *gin.Context) { + c.JSON(http.StatusOK, localConfig.Display) + }) + // status Endpoint group.GET("/status", statusEndpoint) diff --git a/internal/endpoints/status.go b/internal/endpoints/status.go index bfd115b..e3e3816 100644 --- a/internal/endpoints/status.go +++ b/internal/endpoints/status.go @@ -20,8 +20,8 @@ type AgentStateResult struct { } func statusEndpoint(c *gin.Context) { - client := &http.Client{Timeout: time.Duration(config.Timeout * int(time.Millisecond))} - url := config.Opencast.Url + "/capture-admin/agents/" + config.Opencast.Agent + ".json" + client := &http.Client{Timeout: time.Duration(localConfig.Timeout * int(time.Millisecond))} + url := localConfig.Opencast.URL + "/capture-admin/agents/" + localConfig.Opencast.Agent + ".json" req, err := http.NewRequest("GET", url, nil) if err != nil { log.Println(err) @@ -29,7 +29,7 @@ func statusEndpoint(c *gin.Context) { // stateCollector.WithLabelValues("internal_server_error").Set(1) return } - req.SetBasicAuth(config.Opencast.Username, config.Opencast.Password) + req.SetBasicAuth(localConfig.Opencast.Username, localConfig.Opencast.Password) resp, err := client.Do(req) // lastUpdate = time.Now() if err != nil { From 5d6118f853aa0a2026886c543f036a5d3b667ebf Mon Sep 17 00:00:00 2001 From: janni06 Date: Thu, 16 Apr 2026 14:07:17 +0200 Subject: [PATCH 12/14] Use endpoints in enpoint package --- main.go | 335 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 167 insertions(+), 168 deletions(-) diff --git a/main.go b/main.go index 30e1906..ca10547 100644 --- a/main.go +++ b/main.go @@ -18,15 +18,12 @@ package main import ( "embed" - "encoding/json" - "fmt" - "io" "io/fs" "log" "log/slog" - "net" "net/http" "opencast-ca-display/internal/config" + "opencast-ca-display/internal/endpoints" "os" "time" @@ -158,170 +155,172 @@ func setupRouter() *gin.Engine { } r.StaticFS("/assets", http.FS(assets)) - // Display Config - r.GET("/config", func(c *gin.Context) { - c.JSON(http.StatusOK, cConfig.Display) - }) - - // Status - r.GET("/status", func(c *gin.Context) { - client := &http.Client{Timeout: time.Duration(cConfig.Timeout * int(time.Millisecond))} - url := cConfig.Opencast.URL + "/capture-admin/agents/" + cConfig.Opencast.Agent + ".json" - req, err := http.NewRequest("GET", url, nil) - if err != nil { - log.Println(err) - c.JSON(http.StatusInternalServerError, nil) - stateCollector.WithLabelValues("internal_server_error").Set(1) - return - } - req.SetBasicAuth(cConfig.Opencast.Username, cConfig.Opencast.Password) - resp, err := client.Do(req) - lastUpdate = time.Now() - if err != nil { - if os.IsTimeout(err) { - log.Println("Request timed out:", err) - c.JSON(http.StatusGatewayTimeout, gin.H{"error": "Request timed out"}) - stateCollector.WithLabelValues("gateway_timeout").Set(1) - } else { - log.Println(err) - c.JSON(http.StatusBadGateway, gin.H{"error": "Internal server error"}) - stateCollector.WithLabelValues("internal_server_error").Set(1) - } - return - } - - if resp.StatusCode != 200 { - log.Println(resp) - c.JSON(resp.StatusCode, nil) - stateCollector.WithLabelValues(fmt.Sprintf("%d", resp.StatusCode)).Set(1) - return - } - - bodyText, err := io.ReadAll(resp.Body) - if err != nil { - log.Println(err) - c.JSON(http.StatusInternalServerError, nil) - stateCollector.WithLabelValues("internal_server_error").Set(1) - return - } - s := string(bodyText) - var result AgentStateResult - jsonErr := json.Unmarshal([]byte(s), &result) - - if jsonErr != nil { - log.Println(err) - c.JSON(http.StatusInternalServerError, nil) - stateCollector.WithLabelValues("internal_server_error").Set(1) - return - } - - stateCollector.Reset() - stateCollector.WithLabelValues(result.Update.State).Set(1) - - c.JSON(http.StatusOK, result.Update.State == "capturing") - }) - - r.GET("/calendar", func(c *gin.Context) { - client := &http.Client{Timeout: time.Duration(cConfig.Timeout * int(time.Millisecond))} - // Cutoff is set to 24 hours from now - cutoff := time.Now().UnixMilli() + 86400000 - url := cConfig.Opencast.URL + "/recordings/calendar.json?agentid=" + cConfig.Opencast.Agent + "&cutoff=" + fmt.Sprint(cutoff) + "×tamp=true" - req, err := http.NewRequest("GET", url, nil) - if err != nil { - log.Println(err) - c.JSON(http.StatusBadGateway, nil) - return - } - req.SetBasicAuth(cConfig.Opencast.Username, cConfig.Opencast.Password) - resp, err := client.Do(req) - if err != nil { - if os.IsTimeout(err) { - log.Println("Request timed out:", err) - c.JSON(http.StatusGatewayTimeout, gin.H{"error": "Request timed out"}) - stateCollector.WithLabelValues("gateway_timeout").Set(1) - } else { - log.Println(err) - c.JSON(http.StatusBadGateway, gin.H{"error": "Internal server error"}) - stateCollector.WithLabelValues("internal_server_error").Set(1) - } - return - } - if resp.StatusCode != 200 { - log.Println(resp) - c.JSON(resp.StatusCode, nil) - return - } - - bodyText, err := io.ReadAll(resp.Body) - if err != nil { - log.Println(err) - c.JSON(http.StatusBadGateway, nil) - return - } - s := string([]byte(bodyText)) - - var allEvents []CalendarEntry - json_err := json.Unmarshal([]byte(s), &allEvents) - if json_err != nil { - log.Fatal(json_err) - } - - var events []Event - for _, eventData := range allEvents { - start := eventData.Data.StartDate - end := eventData.Data.EndDate - title := eventData.Data.AgentConfig.EventTitle - e := Event{Title: title, Start: start, End: end} - events = append(events, e) - } - - if len(allEvents) > 0 { - fmt.Println(events) - c.JSON(http.StatusOK, events) - } else { - c.JSON(http.StatusOK, "") - } - }) - - r.GET("/network_info", func(c *gin.Context) { - var net_status NetworkStatus - net_interfaces, err := net.Interfaces() - if err != nil { - log.Fatalln("Network devices could not be loaded.") - return - } - for _, net_inter := range net_interfaces { - addrs, err := net_inter.Addrs() - var addrs_str []string - if err == nil { - for _, a := range addrs { - addrs_str = append(addrs_str, a.String()) - } - } - inter := NetInterface{Name: net_inter.Name, MAC: net_inter.HardwareAddr.String(), Adress: addrs_str, Flags: net_inter.Flags.String()} - net_status.Interfaces = append(net_status.Interfaces, inter) - } - client := &http.Client{Timeout: time.Duration(cConfig.Timeout * int(time.Millisecond))} - url := cConfig.Opencast.URL - req, err := http.NewRequest("GET", url, nil) - if err != nil { - log.Println(err) - c.JSON(http.StatusBadGateway, nil) - return - } - // req.SetBasicAuth(cConfig.Opencast.Username, cConfig.Opencast.Password) - _, err = client.Do(req) - if err != nil { - net_status.Connected = false - } else { - net_status.Connected = true - } - net_status.Hostname, err = os.Hostname() - if err != nil { - c.JSON(http.StatusInternalServerError, nil) - } - c.JSON(http.StatusOK, net_status) - }) + endpoints.ApiRouter(r.Group("/")) + + // // Display Config + // r.GET("/config", func(c *gin.Context) { + // c.JSON(http.StatusOK, cConfig.Display) + // }) + + // // Status + // r.GET("/status", func(c *gin.Context) { + // client := &http.Client{Timeout: time.Duration(cConfig.Timeout * int(time.Millisecond))} + // url := cConfig.Opencast.URL + "/capture-admin/agents/" + cConfig.Opencast.Agent + ".json" + // req, err := http.NewRequest("GET", url, nil) + // if err != nil { + // log.Println(err) + // c.JSON(http.StatusInternalServerError, nil) + // stateCollector.WithLabelValues("internal_server_error").Set(1) + // return + // } + // req.SetBasicAuth(cConfig.Opencast.Username, cConfig.Opencast.Password) + // resp, err := client.Do(req) + // lastUpdate = time.Now() + // if err != nil { + // if os.IsTimeout(err) { + // log.Println("Request timed out:", err) + // c.JSON(http.StatusGatewayTimeout, gin.H{"error": "Request timed out"}) + // stateCollector.WithLabelValues("gateway_timeout").Set(1) + // } else { + // log.Println(err) + // c.JSON(http.StatusBadGateway, gin.H{"error": "Internal server error"}) + // stateCollector.WithLabelValues("internal_server_error").Set(1) + // } + // return + // } + + // if resp.StatusCode != 200 { + // log.Println(resp) + // c.JSON(resp.StatusCode, nil) + // stateCollector.WithLabelValues(fmt.Sprintf("%d", resp.StatusCode)).Set(1) + // return + // } + + // bodyText, err := io.ReadAll(resp.Body) + // if err != nil { + // log.Println(err) + // c.JSON(http.StatusInternalServerError, nil) + // stateCollector.WithLabelValues("internal_server_error").Set(1) + // return + // } + // s := string(bodyText) + // var result AgentStateResult + // jsonErr := json.Unmarshal([]byte(s), &result) + + // if jsonErr != nil { + // log.Println(err) + // c.JSON(http.StatusInternalServerError, nil) + // stateCollector.WithLabelValues("internal_server_error").Set(1) + // return + // } + + // stateCollector.Reset() + // stateCollector.WithLabelValues(result.Update.State).Set(1) + + // c.JSON(http.StatusOK, result.Update.State == "capturing") + // }) + + // r.GET("/calendar", func(c *gin.Context) { + // client := &http.Client{Timeout: time.Duration(cConfig.Timeout * int(time.Millisecond))} + // // Cutoff is set to 24 hours from now + // cutoff := time.Now().UnixMilli() + 86400000 + // url := cConfig.Opencast.URL + "/recordings/calendar.json?agentid=" + cConfig.Opencast.Agent + "&cutoff=" + fmt.Sprint(cutoff) + "×tamp=true" + // req, err := http.NewRequest("GET", url, nil) + // if err != nil { + // log.Println(err) + // c.JSON(http.StatusBadGateway, nil) + // return + // } + // req.SetBasicAuth(cConfig.Opencast.Username, cConfig.Opencast.Password) + // resp, err := client.Do(req) + // if err != nil { + // if os.IsTimeout(err) { + // log.Println("Request timed out:", err) + // c.JSON(http.StatusGatewayTimeout, gin.H{"error": "Request timed out"}) + // stateCollector.WithLabelValues("gateway_timeout").Set(1) + // } else { + // log.Println(err) + // c.JSON(http.StatusBadGateway, gin.H{"error": "Internal server error"}) + // stateCollector.WithLabelValues("internal_server_error").Set(1) + // } + // return + // } + // if resp.StatusCode != 200 { + // log.Println(resp) + // c.JSON(resp.StatusCode, nil) + // return + // } + + // bodyText, err := io.ReadAll(resp.Body) + // if err != nil { + // log.Println(err) + // c.JSON(http.StatusBadGateway, nil) + // return + // } + // s := string([]byte(bodyText)) + + // var allEvents []CalendarEntry + // json_err := json.Unmarshal([]byte(s), &allEvents) + // if json_err != nil { + // log.Fatal(json_err) + // } + + // var events []Event + // for _, eventData := range allEvents { + // start := eventData.Data.StartDate + // end := eventData.Data.EndDate + // title := eventData.Data.AgentConfig.EventTitle + // e := Event{Title: title, Start: start, End: end} + // events = append(events, e) + // } + + // if len(allEvents) > 0 { + // fmt.Println(events) + // c.JSON(http.StatusOK, events) + // } else { + // c.JSON(http.StatusOK, "") + // } + // }) + + // r.GET("/network_info", func(c *gin.Context) { + // var net_status NetworkStatus + // net_interfaces, err := net.Interfaces() + // if err != nil { + // log.Fatalln("Network devices could not be loaded.") + // return + // } + // for _, net_inter := range net_interfaces { + // addrs, err := net_inter.Addrs() + // var addrs_str []string + // if err == nil { + // for _, a := range addrs { + // addrs_str = append(addrs_str, a.String()) + // } + // } + // inter := NetInterface{Name: net_inter.Name, MAC: net_inter.HardwareAddr.String(), Adress: addrs_str, Flags: net_inter.Flags.String()} + // net_status.Interfaces = append(net_status.Interfaces, inter) + // } + // client := &http.Client{Timeout: time.Duration(cConfig.Timeout * int(time.Millisecond))} + // url := cConfig.Opencast.URL + // req, err := http.NewRequest("GET", url, nil) + // if err != nil { + // log.Println(err) + // c.JSON(http.StatusBadGateway, nil) + // return + // } + // // req.SetBasicAuth(cConfig.Opencast.Username, cConfig.Opencast.Password) + // _, err = client.Do(req) + // if err != nil { + // net_status.Connected = false + // } else { + // net_status.Connected = true + // } + // net_status.Hostname, err = os.Hostname() + // if err != nil { + // c.JSON(http.StatusInternalServerError, nil) + // } + // c.JSON(http.StatusOK, net_status) + // }) return r } From fc03c587c50aae41ef05936a8f81974ba1e5fbd4 Mon Sep 17 00:00:00 2001 From: janni06 Date: Thu, 16 Apr 2026 14:13:18 +0200 Subject: [PATCH 13/14] Remove obsolete endpoint definitions and structs from main.go --- main.go | 223 -------------------------------------------------------- 1 file changed, 223 deletions(-) diff --git a/main.go b/main.go index ca10547..04dbdf4 100644 --- a/main.go +++ b/main.go @@ -33,64 +33,6 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" ) -type AgentStateResult struct { - Update struct { - Name string - State string - Url string - } `json:"agent-state-update"` -} - -type Event struct { - Title string `json:"title"` - Start int `json:"start"` - End int `json:"end"` -} - -type CalendarWorkflowProperties struct { - StraightToPublishing string `json:"straightToPublishing"` -} - -type CalenderAgentConfig struct { - CaptureDeviceNames string `json:"capture.device.names"` - WorkflowDefinition string `json:"org.opencastproject.workflow.definition"` - WorkflowConfigStraightToPublishing string `json:"org.opencastproject.workflow.config.straightToPublishing"` - EventLocation string `json:"event.location"` - EventTitle string `json:"event.title"` -} - -type CalendarRecording struct { -} - -type CalendarData struct { - EventID string `json:"eventId"` - AgentID string `json:"agentId"` - StartDate int // Verwenden Sie time.Time statt string - EndDate int // Verwenden Sie time.Time statt string - Presenters []string `json:"presenters"` - WorkflowProperties CalendarWorkflowProperties `json:"workflowProperties"` - AgentConfig CalenderAgentConfig `json:"agentConfig"` - Recording CalendarRecording `json:"recording"` -} - -type CalendarEntry struct { - Data CalendarData `json:"data"` - EpisodeDublinCore string `json:"episode-dublincore"` -} - -type NetworkStatus struct { - Interfaces []NetInterface `json:"interfaces"` - Connected bool `json:"connected"` - Hostname string `json:"hostname"` -} - -type NetInterface struct { - Name string `json:"name"` - Adress []string `json:"addr"` - MAC string `json:"mac_adress"` - Flags string `json:"flags"` -} - var ( cConfig config.Config @@ -157,171 +99,6 @@ func setupRouter() *gin.Engine { endpoints.ApiRouter(r.Group("/")) - // // Display Config - // r.GET("/config", func(c *gin.Context) { - // c.JSON(http.StatusOK, cConfig.Display) - // }) - - // // Status - // r.GET("/status", func(c *gin.Context) { - // client := &http.Client{Timeout: time.Duration(cConfig.Timeout * int(time.Millisecond))} - // url := cConfig.Opencast.URL + "/capture-admin/agents/" + cConfig.Opencast.Agent + ".json" - // req, err := http.NewRequest("GET", url, nil) - // if err != nil { - // log.Println(err) - // c.JSON(http.StatusInternalServerError, nil) - // stateCollector.WithLabelValues("internal_server_error").Set(1) - // return - // } - // req.SetBasicAuth(cConfig.Opencast.Username, cConfig.Opencast.Password) - // resp, err := client.Do(req) - // lastUpdate = time.Now() - // if err != nil { - // if os.IsTimeout(err) { - // log.Println("Request timed out:", err) - // c.JSON(http.StatusGatewayTimeout, gin.H{"error": "Request timed out"}) - // stateCollector.WithLabelValues("gateway_timeout").Set(1) - // } else { - // log.Println(err) - // c.JSON(http.StatusBadGateway, gin.H{"error": "Internal server error"}) - // stateCollector.WithLabelValues("internal_server_error").Set(1) - // } - // return - // } - - // if resp.StatusCode != 200 { - // log.Println(resp) - // c.JSON(resp.StatusCode, nil) - // stateCollector.WithLabelValues(fmt.Sprintf("%d", resp.StatusCode)).Set(1) - // return - // } - - // bodyText, err := io.ReadAll(resp.Body) - // if err != nil { - // log.Println(err) - // c.JSON(http.StatusInternalServerError, nil) - // stateCollector.WithLabelValues("internal_server_error").Set(1) - // return - // } - // s := string(bodyText) - // var result AgentStateResult - // jsonErr := json.Unmarshal([]byte(s), &result) - - // if jsonErr != nil { - // log.Println(err) - // c.JSON(http.StatusInternalServerError, nil) - // stateCollector.WithLabelValues("internal_server_error").Set(1) - // return - // } - - // stateCollector.Reset() - // stateCollector.WithLabelValues(result.Update.State).Set(1) - - // c.JSON(http.StatusOK, result.Update.State == "capturing") - // }) - - // r.GET("/calendar", func(c *gin.Context) { - // client := &http.Client{Timeout: time.Duration(cConfig.Timeout * int(time.Millisecond))} - // // Cutoff is set to 24 hours from now - // cutoff := time.Now().UnixMilli() + 86400000 - // url := cConfig.Opencast.URL + "/recordings/calendar.json?agentid=" + cConfig.Opencast.Agent + "&cutoff=" + fmt.Sprint(cutoff) + "×tamp=true" - // req, err := http.NewRequest("GET", url, nil) - // if err != nil { - // log.Println(err) - // c.JSON(http.StatusBadGateway, nil) - // return - // } - // req.SetBasicAuth(cConfig.Opencast.Username, cConfig.Opencast.Password) - // resp, err := client.Do(req) - // if err != nil { - // if os.IsTimeout(err) { - // log.Println("Request timed out:", err) - // c.JSON(http.StatusGatewayTimeout, gin.H{"error": "Request timed out"}) - // stateCollector.WithLabelValues("gateway_timeout").Set(1) - // } else { - // log.Println(err) - // c.JSON(http.StatusBadGateway, gin.H{"error": "Internal server error"}) - // stateCollector.WithLabelValues("internal_server_error").Set(1) - // } - // return - // } - // if resp.StatusCode != 200 { - // log.Println(resp) - // c.JSON(resp.StatusCode, nil) - // return - // } - - // bodyText, err := io.ReadAll(resp.Body) - // if err != nil { - // log.Println(err) - // c.JSON(http.StatusBadGateway, nil) - // return - // } - // s := string([]byte(bodyText)) - - // var allEvents []CalendarEntry - // json_err := json.Unmarshal([]byte(s), &allEvents) - // if json_err != nil { - // log.Fatal(json_err) - // } - - // var events []Event - // for _, eventData := range allEvents { - // start := eventData.Data.StartDate - // end := eventData.Data.EndDate - // title := eventData.Data.AgentConfig.EventTitle - // e := Event{Title: title, Start: start, End: end} - // events = append(events, e) - // } - - // if len(allEvents) > 0 { - // fmt.Println(events) - // c.JSON(http.StatusOK, events) - // } else { - // c.JSON(http.StatusOK, "") - // } - // }) - - // r.GET("/network_info", func(c *gin.Context) { - // var net_status NetworkStatus - // net_interfaces, err := net.Interfaces() - // if err != nil { - // log.Fatalln("Network devices could not be loaded.") - // return - // } - // for _, net_inter := range net_interfaces { - // addrs, err := net_inter.Addrs() - // var addrs_str []string - // if err == nil { - // for _, a := range addrs { - // addrs_str = append(addrs_str, a.String()) - // } - // } - // inter := NetInterface{Name: net_inter.Name, MAC: net_inter.HardwareAddr.String(), Adress: addrs_str, Flags: net_inter.Flags.String()} - // net_status.Interfaces = append(net_status.Interfaces, inter) - // } - // client := &http.Client{Timeout: time.Duration(cConfig.Timeout * int(time.Millisecond))} - // url := cConfig.Opencast.URL - // req, err := http.NewRequest("GET", url, nil) - // if err != nil { - // log.Println(err) - // c.JSON(http.StatusBadGateway, nil) - // return - // } - // // req.SetBasicAuth(cConfig.Opencast.Username, cConfig.Opencast.Password) - // _, err = client.Do(req) - // if err != nil { - // net_status.Connected = false - // } else { - // net_status.Connected = true - // } - // net_status.Hostname, err = os.Hostname() - // if err != nil { - // c.JSON(http.StatusInternalServerError, nil) - // } - // c.JSON(http.StatusOK, net_status) - // }) - return r } From cec8a0ffc1333b66dcad3447010118278b280be1 Mon Sep 17 00:00:00 2001 From: janni06 Date: Thu, 16 Apr 2026 14:14:19 +0200 Subject: [PATCH 14/14] gofmt: Formatting --- internal/endpoints/calendar.go | 2 +- main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/endpoints/calendar.go b/internal/endpoints/calendar.go index e5232d4..6793456 100644 --- a/internal/endpoints/calendar.go +++ b/internal/endpoints/calendar.go @@ -52,7 +52,7 @@ type CalendarEntry struct { func calendarEndpoint(c *gin.Context) { client := &http.Client{Timeout: time.Duration(localConfig.Timeout * int(time.Millisecond))} // Cutoff is set to 3 day from now; TODO: set back to 24 Hours - cutoff := time.Now().Add(time.Hour*720).UnixMilli() + cutoff := time.Now().Add(time.Hour * 720).UnixMilli() url := localConfig.Opencast.URL + "/recordings/calendar.json?agentid=" + localConfig.Opencast.Agent + "&cutoff=" + fmt.Sprint(cutoff) + "×tamp=true" req, err := http.NewRequest("GET", url, nil) if err != nil { diff --git a/main.go b/main.go index 04dbdf4..d80a999 100644 --- a/main.go +++ b/main.go @@ -129,7 +129,7 @@ func main() { cConfig, err := cConfig.LoadFromFile("opencast-ca-display.yml") config.SetConfig(cConfig) - + if err != nil { log.Fatalf("Failed to load configuration: %v", err) }