247 lines
4.8 KiB
Go
247 lines
4.8 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"flag"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
|
|
"git.abbiamoundominio.org/blallo/broadcast"
|
|
"git.sr.ht/~blallo/logz/interface"
|
|
"git.sr.ht/~blallo/logz/zlog"
|
|
)
|
|
|
|
var (
|
|
debug = flag.Bool("debug", false, "Enable debug logging")
|
|
addr = flag.String("addr", ":8080", "Address to bind to, in the 'ipaddress:port' format")
|
|
tlsCert = flag.String("tls-cert", "", "Path to certificate file for TLS connection")
|
|
tlsKey = flag.String("tls-key", "", "Path to key file for TLS connection")
|
|
)
|
|
|
|
func main() {
|
|
logger := zlog.NewConsoleLogger()
|
|
|
|
flag.Parse()
|
|
|
|
if flag.NArg() < 1 {
|
|
logger.Err(map[string]any{
|
|
"msg": "Wrong number of arguments",
|
|
})
|
|
os.Exit(1)
|
|
}
|
|
|
|
if *debug {
|
|
logger.SetLevel(logz.LogDebug)
|
|
}
|
|
|
|
prog := flag.Arg(0)
|
|
cmdLine := flag.Args()[1:]
|
|
|
|
radio, err := broadcast.NewRadio(logger, prog, cmdLine...)
|
|
if err != nil {
|
|
logger.Err(map[string]any{
|
|
"msg": "Failed to start",
|
|
"err": err,
|
|
})
|
|
os.Exit(2)
|
|
}
|
|
|
|
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
|
|
defer cancel()
|
|
|
|
start := make(chan os.Signal)
|
|
stop := make(chan os.Signal)
|
|
status := make(chan os.Signal)
|
|
|
|
signal.Notify(start, syscall.SIGUSR1)
|
|
signal.Notify(stop, syscall.SIGTSTP)
|
|
signal.Notify(status, syscall.SIGUSR2)
|
|
|
|
go func() {
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-start:
|
|
resp := <-radio.Start()
|
|
if resp != nil {
|
|
logger.Warn(map[string]any{
|
|
"msg": "Failed to start",
|
|
"context": "os",
|
|
"err": resp.(error).Error(),
|
|
})
|
|
} else {
|
|
logger.Info(map[string]any{
|
|
"msg": "Started",
|
|
"context": "os",
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
|
|
go func() {
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-stop:
|
|
resp := <-radio.Stop()
|
|
if resp != nil {
|
|
logger.Warn(map[string]any{
|
|
"msg": "Failed to stop",
|
|
"context": "os",
|
|
"err": resp.(error).Error(),
|
|
})
|
|
} else {
|
|
logger.Info(map[string]any{
|
|
"msg": "Stopped",
|
|
"context": "os",
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
|
|
go func() {
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-status:
|
|
resp := <-radio.Status()
|
|
for i, line := range resp.([]string) {
|
|
logger.Info(map[string]any{
|
|
"msg": line,
|
|
"context": "os",
|
|
"lineNum": i,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
|
|
go func() {
|
|
resp, err := withTimeout(ctx, radio.Start())
|
|
if err != nil {
|
|
logger.Err(map[string]any{
|
|
"msg": "Cannot start",
|
|
"context": "os",
|
|
"err": err.Error(),
|
|
})
|
|
} else {
|
|
if resp != nil {
|
|
logger.Info(map[string]any{
|
|
"msg": "Started",
|
|
"context": "os",
|
|
"resp": resp.(error).Error(),
|
|
})
|
|
}
|
|
}
|
|
}()
|
|
|
|
handler, err := setupHandler(radio, logger, *addr)
|
|
if err != nil {
|
|
logger.Err(map[string]any{
|
|
"msg": "Cannot create handler",
|
|
"err": err.Error(),
|
|
})
|
|
os.Exit(2)
|
|
}
|
|
|
|
if isValidTLSConf(logger) {
|
|
go http.ListenAndServeTLS(*addr, *tlsCert, *tlsKey, handler)
|
|
} else {
|
|
go http.ListenAndServe(*addr, handler)
|
|
}
|
|
|
|
if err := radio.Run(ctx); err != nil {
|
|
logger.Err(map[string]any{
|
|
"msg": "Execution failed",
|
|
"err": err.Error(),
|
|
})
|
|
os.Exit(2)
|
|
}
|
|
}
|
|
|
|
func withTimeout[T any](ctx context.Context, respCh <-chan T) (zero T, err error) {
|
|
shortCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
|
defer cancel()
|
|
|
|
select {
|
|
case <-shortCtx.Done():
|
|
return zero, shortCtx.Err()
|
|
case resp := <-respCh:
|
|
return resp, nil
|
|
}
|
|
}
|
|
|
|
func isValidTLSConf(logger logz.Logger) bool {
|
|
if *tlsCert == "" && *tlsKey == "" {
|
|
return false
|
|
}
|
|
if (*tlsCert != "" && *tlsKey == "") || (*tlsCert == "" && *tlsKey != "") {
|
|
logger.Err(map[string]any{
|
|
"msg": "You must specify both the path to the certificate and to the key to use TLS",
|
|
})
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func setupHandler(radio *broadcast.Radio, logger logz.Logger, addr string) (http.Handler, error) {
|
|
handler := &radioHandler{
|
|
radio: radio,
|
|
logger: logger,
|
|
}
|
|
|
|
wsHandler := &livenessHandler{
|
|
radio: radio,
|
|
logger: logger,
|
|
}
|
|
|
|
testUI := &testUIHandler{
|
|
baseAddr: getBaseAddr(addr),
|
|
logger: logger,
|
|
}
|
|
|
|
assetsHandler, err := newAssetsHandler(logger)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
router := chi.NewRouter()
|
|
router.Handle("/", assetsHandler)
|
|
router.Handle("/{elem}", assetsHandler)
|
|
router.Handle("/static/js/{elem}", assetsHandler)
|
|
router.Handle("/static/css/{elem}", assetsHandler)
|
|
router.Post("/start", handler.Start)
|
|
router.Post("/stop", handler.Stop)
|
|
router.Get("/status", handler.Status)
|
|
router.Handle("/liveness", wsHandler)
|
|
router.Get("/test_ui", testUI.TestUI)
|
|
|
|
return router, nil
|
|
}
|
|
|
|
func getBaseAddr(addr string) string {
|
|
parts := strings.Split(addr, ":")
|
|
if parts[0] == "" {
|
|
parts[0] = "localhost"
|
|
}
|
|
|
|
if len(parts) == 2 {
|
|
return fmt.Sprintf("%s:%s", parts[0], parts[1])
|
|
}
|
|
|
|
return parts[0]
|
|
}
|