149 lines
3.4 KiB
Go
149 lines
3.4 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
"time"
|
|
|
|
"gitea.dwysokinski.me/Kichiyaki/notificationarr/internal/rest"
|
|
"gitea.dwysokinski.me/Kichiyaki/notificationarr/internal/service"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/go-chi/chi/v5/middleware"
|
|
)
|
|
|
|
const (
|
|
defaultPort = "9234"
|
|
readTimeout = 2 * time.Second
|
|
readHeaderTimeout = 2 * time.Second
|
|
writeTimeout = 2 * time.Second
|
|
idleTimeout = 2 * time.Second
|
|
serverShutdownTimeout = 10 * time.Second
|
|
ntfyDefaultUrl = "https://ntfy.sh"
|
|
publisherDefaultTimeout = 5 * time.Second
|
|
)
|
|
|
|
func main() {
|
|
srv, err := newServer()
|
|
if err != nil {
|
|
log.Fatalln("newServer:", err)
|
|
}
|
|
|
|
go startServer(srv)
|
|
|
|
log.Println("Server is listening on the port", defaultPort)
|
|
|
|
waitForSignal(context.Background())
|
|
|
|
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), serverShutdownTimeout)
|
|
defer cancelShutdown()
|
|
err = srv.Shutdown(ctxShutdown)
|
|
if err != nil {
|
|
log.Println("srv.Shutdown:", err)
|
|
}
|
|
}
|
|
|
|
func newServer() (*http.Server, error) {
|
|
router, err := newRouter()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("newRouter: %w", err)
|
|
}
|
|
|
|
return &http.Server{
|
|
Addr: ":" + defaultPort,
|
|
Handler: router,
|
|
ReadTimeout: readTimeout,
|
|
ReadHeaderTimeout: readHeaderTimeout,
|
|
WriteTimeout: writeTimeout,
|
|
IdleTimeout: idleTimeout,
|
|
}, nil
|
|
}
|
|
|
|
func newRouter() (*chi.Mux, error) {
|
|
sonarrSvc, err := newSonarrService()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("newSonarrService: %w", err)
|
|
}
|
|
|
|
radarrSvc, err := newRadarrService()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("newRadarrService: %w", err)
|
|
}
|
|
|
|
r := chi.NewRouter()
|
|
r.Use(
|
|
middleware.RealIP,
|
|
middleware.RequestLogger(&middleware.DefaultLogFormatter{
|
|
NoColor: true,
|
|
Logger: log.Default(),
|
|
}),
|
|
middleware.Recoverer,
|
|
middleware.Heartbeat("/health"),
|
|
)
|
|
rest.NewWebhookHandler(sonarrSvc, radarrSvc).Register(r)
|
|
return r, nil
|
|
}
|
|
|
|
func newRadarrService() (*service.Radarr, error) {
|
|
ntfy, err := newNtfyService("RADARR")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("newNtfyService: %w", err)
|
|
}
|
|
return service.NewRadarr(ntfy), nil
|
|
}
|
|
|
|
func newSonarrService() (*service.Sonarr, error) {
|
|
ntfy, err := newNtfyService("SONARR")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("newNtfyService: %w", err)
|
|
}
|
|
return service.NewSonarr(ntfy), nil
|
|
}
|
|
|
|
func newNtfyService(envPrefix string) (*service.Ntfy, error) {
|
|
url := os.Getenv(envPrefix + "_NTFY_URL")
|
|
if url == "" {
|
|
url = ntfyDefaultUrl
|
|
}
|
|
|
|
topic := os.Getenv(envPrefix + "_NTFY_TOPIC")
|
|
if topic == "" {
|
|
return nil, fmt.Errorf(`env "%s_NTFY_TOPIC" is required`, envPrefix)
|
|
}
|
|
|
|
timeout := os.Getenv(envPrefix + "_NTFY_REQ_TIMEOUT")
|
|
parsedTimeout := publisherDefaultTimeout
|
|
var err error
|
|
if timeout != "" {
|
|
parsedTimeout, err = time.ParseDuration(timeout)
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf(`os.Getenv("%s_NTFY_REQ_TIMEOUT"): %w`, envPrefix, err)
|
|
}
|
|
|
|
return service.NewNtfy(
|
|
&http.Client{Timeout: parsedTimeout},
|
|
url,
|
|
topic,
|
|
os.Getenv(envPrefix+"_NTFY_USERNAME"),
|
|
os.Getenv(envPrefix+"_NTFY_PASSWORD"),
|
|
), nil
|
|
}
|
|
|
|
func startServer(srv *http.Server) {
|
|
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
log.Fatalln("srv.ListenAndServe:", err)
|
|
}
|
|
}
|
|
|
|
func waitForSignal(ctx context.Context) {
|
|
ctx, stop := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM)
|
|
defer stop()
|
|
<-ctx.Done()
|
|
}
|