Files

148 lines
3.7 KiB
Go

package main
import (
"crypto/rand"
"embed"
"encoding/base64"
"flag"
"fmt"
"io/fs"
"log"
"net"
"net/http"
"os"
"os/exec"
"runtime"
"time"
)
//go:embed web/*
var embeddedWeb embed.FS
var version = "dev"
func main() {
noOpen := flag.Bool("no-open", false, "do not open the browser automatically")
publicMode := flag.Bool("public", false, "run the public web service")
listenAddress := flag.String("listen", "0.0.0.0:8080", "public web service listen address")
publicOrigin := flag.String("origin", "https://lrc.flechazo.xin", "allowed public browser origin")
downloadDirectory := flag.String("downloads", "", "directory containing optional local-app downloads")
healthcheckURL := flag.String("healthcheck", "", "check a running service and exit")
showVersion := flag.Bool("version", false, "print version and exit")
flag.Parse()
if *showVersion {
fmt.Println(version)
return
}
if *healthcheckURL != "" {
client := &http.Client{Timeout: 3 * time.Second}
response, err := client.Get(*healthcheckURL)
if err != nil {
log.Fatal(err)
}
_ = response.Body.Close()
if response.StatusCode != http.StatusOK {
log.Fatalf("healthcheck returned HTTP %d", response.StatusCode)
}
return
}
if *publicMode {
runPublic(*listenAddress, *publicOrigin, *downloadDirectory)
return
}
runLocal(*noOpen)
}
func runLocal(noOpen bool) {
listener, err := net.Listen("tcp4", "127.0.0.1:0")
if err != nil {
log.Fatal(err)
}
token, err := randomToken()
if err != nil {
log.Fatal(err)
}
webFS, err := fs.Sub(embeddedWeb, "web")
if err != nil {
log.Fatal(err)
}
baseURL := "http://" + listener.Addr().String()
app := newApp(baseURL, token, webFS, newQQClient())
server := &http.Server{
Handler: app.routes(),
ReadHeaderTimeout: 5 * time.Second,
ReadTimeout: 15 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 60 * time.Second,
MaxHeaderBytes: 16 * 1024,
}
launchURL := baseURL + "/start?t=" + token
fmt.Printf("LRC Local %s\n仅监听本机:%s\n关闭此窗口即可停止。\n", version, baseURL)
if !noOpen {
go func() {
time.Sleep(150 * time.Millisecond)
if err := openBrowser(launchURL); err != nil {
log.Printf("无法自动打开浏览器,请手动访问:%s", launchURL)
}
}()
} else {
fmt.Printf("请访问:%s\n", launchURL)
}
if err := server.Serve(listener); err != nil && err != http.ErrServerClosed {
log.Fatal(err)
}
}
func runPublic(listenAddress, origin, downloadDirectory string) {
listener, err := net.Listen("tcp4", listenAddress)
if err != nil {
log.Fatal(err)
}
webFS, err := fs.Sub(embeddedWeb, "web")
if err != nil {
log.Fatal(err)
}
app, err := newPublicApp(origin, downloadDirectory, webFS, newQQClient())
if err != nil {
log.Fatal(err)
}
server := &http.Server{
Handler: app.routes(),
ReadHeaderTimeout: 5 * time.Second,
ReadTimeout: 15 * time.Second,
WriteTimeout: 5 * time.Minute,
IdleTimeout: 60 * time.Second,
MaxHeaderBytes: 16 * 1024,
}
log.Printf("LRC Local %s public service listening on %s", version, listener.Addr())
if err := server.Serve(listener); err != nil && err != http.ErrServerClosed {
log.Fatal(err)
}
}
func randomToken() (string, error) {
buf := make([]byte, 32)
if _, err := rand.Read(buf); err != nil {
return "", err
}
return base64.RawURLEncoding.EncodeToString(buf), nil
}
func openBrowser(url string) error {
var command *exec.Cmd
switch runtime.GOOS {
case "windows":
command = exec.Command("rundll32", "url.dll,FileProtocolHandler", url)
case "darwin":
command = exec.Command("open", url)
default:
command = exec.Command("xdg-open", url)
}
command.Stdout = os.Stdout
command.Stderr = os.Stderr
return command.Start()
}