1
0
mirror of https://github.com/hrfee/jfa-go.git synced 2024-12-24 01:50:11 +00:00

Compare commits

..

No commits in common. "831296a3e88e0772f6cc4c68788a15f037b7694d" and "f5f2a0f1904c0077b60dd2969fa57b18539cd0db" have entirely different histories.

5 changed files with 72 additions and 169 deletions

47
api.go
View File

@ -2,7 +2,10 @@ package main
import ( import (
"fmt" "fmt"
"os"
"os/signal"
"strings" "strings"
"syscall"
"time" "time"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@ -696,30 +699,24 @@ func (app *appContext) Logout(gc *gin.Context) {
// panic(fmt.Errorf("restarting")) // panic(fmt.Errorf("restarting"))
// } // }
// func (app *appContext) Restart() error {
// defer func() {
// if r := recover(); r != nil {
// signal.Notify(app.quit, os.Interrupt)
// <-app.quit
// }
// }()
// args := os.Args
// // After a single restart, args[0] gets messed up and isnt the real executable.
// // JFA_DEEP tells the new process its a child, and JFA_EXEC is the real executable
// if os.Getenv("JFA_DEEP") == "" {
// os.Setenv("JFA_DEEP", "1")
// os.Setenv("JFA_EXEC", args[0])
// }
// env := os.Environ()
// err := syscall.Exec(os.Getenv("JFA_EXEC"), []string{""}, env)
// if err != nil {
// return err
// }
// panic(fmt.Errorf("restarting"))
// }
// no need to syscall.exec anymore!
func (app *appContext) Restart() error { func (app *appContext) Restart() error {
RESTART <- true defer func() {
return nil if r := recover(); r != nil {
signal.Notify(app.quit, os.Interrupt)
<-app.quit
}
}()
args := os.Args
// After a single restart, args[0] gets messed up and isnt the real executable.
// JFA_DEEP tells the new process its a child, and JFA_EXEC is the real executable
if os.Getenv("JFA_DEEP") == "" {
os.Setenv("JFA_DEEP", "1")
os.Setenv("JFA_EXEC", args[0])
}
env := os.Environ()
err := syscall.Exec(os.Getenv("JFA_EXEC"), []string{""}, env)
if err != nil {
return err
}
panic(fmt.Errorf("restarting"))
} }

View File

@ -269,6 +269,15 @@
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title">Warning</h5> <h5 class="modal-title">Warning</h5>
</div> </div>
{{ if .windows }}
<div class="modal-body">
<p>A restart is needed to apply some settings. Self-restarts aren't possible on Windows, so you must restart manually.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-light" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-secondary" id="applyRestarts" data-dismiss="modal">Apply, Restart manually</button>
</div>
{{ else }}
<div class="modal-body"> <div class="modal-body">
<p>A restart is needed to apply some settings. Restart now, later, or cancel?</p> <p>A restart is needed to apply some settings. Restart now, later, or cancel?</p>
</div> </div>
@ -277,6 +286,7 @@
<button type="button" class="btn btn-secondary" id="applyRestarts" data-dismiss="modal">Apply, Restart later</button> <button type="button" class="btn btn-secondary" id="applyRestarts" data-dismiss="modal">Apply, Restart later</button>
<button type="button" class="btn btn-primary" id="applyAndRestart" data-dismiss="modal">Apply &amp; Restart</button> <button type="button" class="btn btn-primary" id="applyAndRestart" data-dismiss="modal">Apply &amp; Restart</button>
</div> </div>
{{ end }}
</div> </div>
</div> </div>
</div> </div>

View File

@ -359,7 +359,11 @@
<div class="card-body text-center"> <div class="card-body text-center">
<h5 class="card-title">Finished!</h5> <h5 class="card-title">Finished!</h5>
<p class="card-text"> <p class="card-text">
{{ if .windows }}
Press the button below to submit your settings. Unfortunately you're running on Windows, so jfa-go <a href="https://github.com/golang/go/issues/30662">cannot restart itself.</a> You will manually have to restart.
{{ else }}
Press the button below to submit your settings. The program will restart. Once it's done, refresh this page. Press the button below to submit your settings. The program will restart. Once it's done, refresh this page.
{{ end }}
</p> </p>
<button id="submitButton" class="btn btn-primary">Submit</button> <button id="submitButton" class="btn btn-primary">Submit</button>
</div> </div>

175
main.go
View File

@ -10,10 +10,8 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"log" "log"
"net"
"net/http" "net/http"
"os" "os"
"os/exec"
"os/signal" "os/signal"
"path/filepath" "path/filepath"
"runtime" "runtime"
@ -98,17 +96,10 @@ func setGinLogger(router *gin.Engine, debugMode bool) {
} }
} }
var ( var PLATFORM string = runtime.GOOS
PLATFORM string = runtime.GOOS
SOCK string = "jfa-go.sock"
SRV *http.Server
RESTART chan bool
DATA, CONFIG, HOST *string
PORT *int
DEBUG *bool
)
func start(asDaemon, firstCall bool) { func main() {
fmt.Printf("jfa-go version: %s (%s)\n", VERSION, COMMIT)
// app encompasses essentially all useful functions. // app encompasses essentially all useful functions.
app := new(appContext) app := new(appContext)
@ -126,25 +117,23 @@ func start(asDaemon, firstCall bool) {
app.info = log.New(os.Stdout, "[INFO] ", log.Ltime) app.info = log.New(os.Stdout, "[INFO] ", log.Ltime)
app.err = log.New(os.Stdout, "[ERROR] ", log.Ltime|log.Lshortfile) app.err = log.New(os.Stdout, "[ERROR] ", log.Ltime|log.Lshortfile)
if firstCall { dataPath := flag.String("data", app.data_path, "alternate path to data directory.")
DATA = flag.String("data", app.data_path, "alternate path to data directory.") configPath := flag.String("config", app.config_path, "alternate path to config file.")
CONFIG = flag.String("config", app.config_path, "alternate path to config file.") host := flag.String("host", "", "alternate address to host web ui on.")
HOST = flag.String("host", "", "alternate address to host web ui on.") port := flag.Int("port", 0, "alternate port to host web ui on.")
PORT = flag.Int("port", 0, "alternate port to host web ui on.") debug := flag.Bool("debug", false, "Enables debug logging and exposes pprof.")
DEBUG = flag.Bool("debug", false, "Enables debug logging and exposes pprof.")
flag.Parse() flag.Parse()
}
// attempt to apply command line flags correctly // attempt to apply command line flags correctly
if app.config_path == *CONFIG && app.data_path != *DATA { if app.config_path == *configPath && app.data_path != *dataPath {
app.data_path = *DATA app.data_path = *dataPath
app.config_path = filepath.Join(app.data_path, "config.ini") app.config_path = filepath.Join(app.data_path, "config.ini")
} else if app.config_path != *CONFIG && app.data_path == *DATA { } else if app.config_path != *configPath && app.data_path == *dataPath {
app.config_path = *CONFIG app.config_path = *configPath
} else { } else {
app.config_path = *CONFIG app.config_path = *configPath
app.data_path = *DATA app.data_path = *dataPath
} }
// env variables are necessary because syscall.Exec for self-restarts doesn't doesn't work with arguments for some reason. // env variables are necessary because syscall.Exec for self-restarts doesn't doesn't work with arguments for some reason.
@ -194,7 +183,7 @@ func start(asDaemon, firstCall bool) {
// read from config... // read from config...
debugMode = app.config.Section("ui").Key("debug").MustBool(false) debugMode = app.config.Section("ui").Key("debug").MustBool(false)
// then from flag // then from flag
if *DEBUG { if *debug {
debugMode = true debugMode = true
} }
if debugMode { if debugMode {
@ -204,54 +193,15 @@ func start(asDaemon, firstCall bool) {
app.debug = log.New(ioutil.Discard, "", 0) app.debug = log.New(ioutil.Discard, "", 0)
} }
if asDaemon {
go func() {
socket := SOCK
os.Remove(socket)
listener, err := net.Listen("unix", socket)
if err != nil {
app.err.Fatalf("Couldn't establish socket connection at %s\n", SOCK)
}
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
<-c
os.Remove(socket)
os.Exit(1)
}()
defer func() {
listener.Close()
os.Remove(SOCK)
}()
for {
con, err := listener.Accept()
if err != nil {
app.err.Printf("Couldn't read message on %s: %s", socket, err)
continue
}
buf := make([]byte, 512)
nr, err := con.Read(buf)
if err != nil {
app.err.Printf("Couldn't read message on %s: %s", socket, err)
continue
}
command := string(buf[0:nr])
if command == "stop" {
app.shutdown()
}
}
}()
}
if !firstRun { if !firstRun {
app.host = app.config.Section("ui").Key("host").String() app.host = app.config.Section("ui").Key("host").String()
app.port = app.config.Section("ui").Key("port").MustInt(8056) app.port = app.config.Section("ui").Key("port").MustInt(8056)
if *HOST != app.host && *HOST != "" { if *host != app.host && *host != "" {
app.host = *HOST app.host = *host
} }
if *PORT != app.port && *PORT > 0 { if *port != app.port && *port > 0 {
app.port = *PORT app.port = *port
} }
if h := os.Getenv("JFA_HOST"); h != "" { if h := os.Getenv("JFA_HOST"); h != "" {
@ -415,100 +365,37 @@ func start(asDaemon, firstCall bool) {
} }
app.info.Printf("Starting router @ %s", address) app.info.Printf("Starting router @ %s", address)
} else { } else {
windows := false
if PLATFORM == "windows" {
windows = true
}
router.GET("/", func(gc *gin.Context) { router.GET("/", func(gc *gin.Context) {
gc.HTML(200, "setup.html", gin.H{}) gc.HTML(200, "setup.html", gin.H{
"windows": windows,
})
}) })
router.POST("/testJF", app.TestJF) router.POST("/testJF", app.TestJF)
router.POST("/modifyConfig", app.ModifyConfig) router.POST("/modifyConfig", app.ModifyConfig)
app.info.Printf("Loading setup @ %s", address) app.info.Printf("Loading setup @ %s", address)
} }
SRV = &http.Server{ srv := &http.Server{
Addr: address, Addr: address,
Handler: router, Handler: router,
} }
go func() { go func() {
if err := SRV.ListenAndServe(); err != nil { if err := srv.ListenAndServe(); err != nil {
app.err.Printf("Failure serving: %s", err) app.err.Printf("Failure serving: %s", err)
} }
}() }()
app.quit = make(chan os.Signal) app.quit = make(chan os.Signal)
signal.Notify(app.quit, os.Interrupt) signal.Notify(app.quit, os.Interrupt)
go func() { <-app.quit
for range app.quit {
app.shutdown()
}
}()
for range RESTART {
cntx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
if err := SRV.Shutdown(cntx); err != nil {
app.err.Fatalf("Server shutdown error: %s", err)
}
return
}
}
func (app *appContext) shutdown() {
app.info.Println("Shutting down...") app.info.Println("Shutting down...")
cntx, cancel := context.WithTimeout(context.Background(), time.Second*5) cntx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel() defer cancel()
if err := SRV.Shutdown(cntx); err != nil { if err := srv.Shutdown(cntx); err != nil {
app.err.Fatalf("Server shutdown error: %s", err) app.err.Fatalf("Server shutdown error: %s", err)
} }
os.Exit(1)
}
func flagPassed(name string) (found bool) {
for _, f := range os.Args {
if f == name {
found = true
}
}
return
}
func main() {
fmt.Printf("jfa-go version: %s (%s)\n", VERSION, COMMIT)
folder := "/tmp"
if PLATFORM == "windows" {
folder = os.Getenv("TEMP")
}
SOCK = filepath.Join(folder, SOCK)
fmt.Println(SOCK)
if flagPassed("start") {
args := []string{}
for i, f := range os.Args {
if f == "start" {
args = append(args, "daemon")
} else if i != 0 {
args = append(args, f)
}
}
cmd := exec.Command(os.Args[0], args...)
cmd.Start()
os.Exit(1)
} else if flagPassed("stop") {
con, err := net.Dial("unix", SOCK)
if err != nil {
fmt.Printf("Couldn't dial socket %s, are you sure jfa-go is running?\n", SOCK)
os.Exit(1)
}
_, err = con.Write([]byte("stop"))
if err != nil {
fmt.Printf("Couldn't send command to socket %s, are you sure jfa-go is running?\n", SOCK)
os.Exit(1)
}
fmt.Println("Sent.")
} else if flagPassed("daemon") {
start(true, true)
} else {
RESTART = make(chan bool, 1)
start(false, true)
for {
fmt.Printf("jfa-go version: %s (%s)\n", VERSION, COMMIT)
start(false, false)
}
}
} }

View File

@ -11,6 +11,10 @@ func (app *appContext) AdminPage(gc *gin.Context) {
emailEnabled, _ := app.config.Section("invite_emails").Key("enabled").Bool() emailEnabled, _ := app.config.Section("invite_emails").Key("enabled").Bool()
notificationsEnabled, _ := app.config.Section("notifications").Key("enabled").Bool() notificationsEnabled, _ := app.config.Section("notifications").Key("enabled").Bool()
ombiEnabled := app.config.Section("ombi").Key("enabled").MustBool(false) ombiEnabled := app.config.Section("ombi").Key("enabled").MustBool(false)
windows := false
if PLATFORM == "windows" {
windows = true
}
gc.HTML(http.StatusOK, "admin.html", gin.H{ gc.HTML(http.StatusOK, "admin.html", gin.H{
"bs5": bs5, "bs5": bs5,
"cssFile": app.cssFile, "cssFile": app.cssFile,
@ -20,6 +24,7 @@ func (app *appContext) AdminPage(gc *gin.Context) {
"version": VERSION, "version": VERSION,
"commit": COMMIT, "commit": COMMIT,
"ombiEnabled": ombiEnabled, "ombiEnabled": ombiEnabled,
"windows": windows,
}) })
} }