Add backend/cmd/moonrelay/main.go
This commit is contained in:
parent
a513a64b54
commit
2f97c8db4b
1 changed files with 114 additions and 0 deletions
114
backend/cmd/moonrelay/main.go
Normal file
114
backend/cmd/moonrelay/main.go
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
// moonrelay — self-hosted streaming manager backend.
|
||||
//
|
||||
// In Docker mode the React SPA is embedded at build time via go:embed.
|
||||
// Set MOONRELAY_NO_EMBED=1 to skip the SPA (API-only / dev mode).
|
||||
//
|
||||
// Environment variables:
|
||||
// MOONRELAY_PORT - HTTP listen port (default: 8080)
|
||||
// MOONRELAY_HOST - HTTP listen address (default: 0.0.0.0)
|
||||
// TS_AUTHKEY - Tailscale auth key (optional; prompts login if unset)
|
||||
// TS_HOSTNAME - Tailscale device hostname (default: moonrelay)
|
||||
// MOONLIGHT_PATH - Override path to moonlight binary
|
||||
// MOONRELAY_NO_TS - Set "1" to disable Tailscale (LAN-only)
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"moonrelay/internal/api"
|
||||
"moonrelay/internal/discovery"
|
||||
"moonrelay/internal/moonlight"
|
||||
tsnode "moonrelay/internal/tailscale"
|
||||
)
|
||||
|
||||
// ui holds the React production build, copied here by the Dockerfile
|
||||
// before `go build`. The path must match where the build output lands.
|
||||
//
|
||||
//go:embed ui
|
||||
var uiFiles embed.FS
|
||||
|
||||
func main() {
|
||||
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||
defer cancel()
|
||||
|
||||
port := env("MOONRELAY_PORT", "8080")
|
||||
host := env("MOONRELAY_HOST", "0.0.0.0")
|
||||
noTS := os.Getenv("MOONRELAY_NO_TS") == "1"
|
||||
|
||||
// --- Moonlight launcher ---
|
||||
launcher, err := moonlight.New(os.Getenv("MOONLIGHT_PATH"))
|
||||
if err != nil {
|
||||
log.Printf("Warning: %v", err)
|
||||
launcher, _ = moonlight.New("")
|
||||
}
|
||||
|
||||
// --- mDNS discovery ---
|
||||
disc := discovery.New()
|
||||
go disc.Run(ctx)
|
||||
|
||||
// --- Tailscale node (optional) ---
|
||||
var ts *tsnode.Node
|
||||
if !noTS {
|
||||
log.Println("Starting embedded Tailscale node…")
|
||||
ts, err = tsnode.New(ctx, tsnode.Config{
|
||||
Hostname: env("TS_HOSTNAME", "moonrelay"),
|
||||
AuthKey: os.Getenv("TS_AUTHKEY"),
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("Warning: Tailscale failed to start (%v) — running in LAN-only mode", err)
|
||||
ts = nil
|
||||
} else {
|
||||
if addr := ts.LocalAddr(); addr != "" {
|
||||
log.Printf("Tailscale connected: %s", addr)
|
||||
} else {
|
||||
log.Println("Tailscale: waiting for auth (check logs for login URL)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Embedded SPA ---
|
||||
// Strip the "ui/" prefix so the FS root contains index.html directly.
|
||||
uiRoot, err := fs.Sub(uiFiles, "ui")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to access embedded UI: %v", err)
|
||||
}
|
||||
|
||||
// --- HTTP server ---
|
||||
srv := api.New(disc, launcher, ts, uiRoot)
|
||||
addr := fmt.Sprintf("%s:%s", host, port)
|
||||
ln, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
log.Fatalf("Cannot listen on %s: %v", addr, err)
|
||||
}
|
||||
log.Printf("Moonlight Relay listening on http://%s", addr)
|
||||
|
||||
httpSrv := &http.Server{Handler: srv.Handler()}
|
||||
go func() {
|
||||
if err := httpSrv.Serve(ln); err != nil && err != http.ErrServerClosed {
|
||||
log.Printf("HTTP server error: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
<-ctx.Done()
|
||||
log.Println("Shutting down…")
|
||||
_ = httpSrv.Close()
|
||||
if ts != nil {
|
||||
_ = ts.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func env(key, fallback string) string {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
return v
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
Loading…
Reference in a new issue