mirror of
https://github.com/hrfee/jfa-go.git
synced 2025-01-03 15:00:12 +00:00
Added setup, self restarts
This commit is contained in:
parent
62621dabb9
commit
f508b65fa0
58
api.go
58
api.go
@ -6,6 +6,9 @@ import (
|
|||||||
"github.com/knz/strtime"
|
"github.com/knz/strtime"
|
||||||
"github.com/lithammer/shortuuid/v3"
|
"github.com/lithammer/shortuuid/v3"
|
||||||
"gopkg.in/ini.v1"
|
"gopkg.in/ini.v1"
|
||||||
|
"os"
|
||||||
|
//"os/exec"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -572,5 +575,60 @@ func (ctx *appContext) ModifyConfig(gc *gin.Context) {
|
|||||||
tempConfig.SaveTo(ctx.config_path)
|
tempConfig.SaveTo(ctx.config_path)
|
||||||
ctx.debug.Println("Config saved")
|
ctx.debug.Println("Config saved")
|
||||||
gc.JSON(200, map[string]bool{"success": true})
|
gc.JSON(200, map[string]bool{"success": true})
|
||||||
|
if req["restart-program"].(bool) {
|
||||||
|
ctx.info.Println("Restarting...")
|
||||||
|
err := Restart()
|
||||||
|
if err != nil {
|
||||||
|
ctx.err.Printf("Couldn't restart, try restarting manually. (%s)", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
ctx.loadConfig()
|
ctx.loadConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// func Restart() error {
|
||||||
|
// defer func() {
|
||||||
|
// if r := recover(); r != nil {
|
||||||
|
// os.Exit(0)
|
||||||
|
// }
|
||||||
|
// }()
|
||||||
|
// cwd, err := os.Getwd()
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// args := os.Args
|
||||||
|
// // for _, key := range args {
|
||||||
|
// // fmt.Println(key)
|
||||||
|
// // }
|
||||||
|
// cmd := exec.Command(args[0], args[1:]...)
|
||||||
|
// cmd.Stdout = os.Stdout
|
||||||
|
// cmd.Stderr = os.Stderr
|
||||||
|
// cmd.Dir = cwd
|
||||||
|
// err = cmd.Start()
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// // cmd.Process.Release()
|
||||||
|
// panic(fmt.Errorf("restarting"))
|
||||||
|
// }
|
||||||
|
|
||||||
|
func Restart() error {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
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()
|
||||||
|
fmt.Printf("EXECUTABLE: %s\n", os.Getenv("JFA_EXEC"))
|
||||||
|
err := syscall.Exec(os.Getenv("JFA_EXEC"), []string{""}, env)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
panic(fmt.Errorf("restarting"))
|
||||||
|
}
|
||||||
|
@ -21,16 +21,19 @@ function checkEmailRadio() {
|
|||||||
document.getElementById('emailCommonArea').style.display = '';
|
document.getElementById('emailCommonArea').style.display = '';
|
||||||
document.getElementById('emailSMTPArea').style.display = '';
|
document.getElementById('emailSMTPArea').style.display = '';
|
||||||
document.getElementById('emailMailgunArea').style.display = 'none';
|
document.getElementById('emailMailgunArea').style.display = 'none';
|
||||||
|
document.getElementById('notificationsEnabled').checked = true;
|
||||||
} else if (document.getElementById('emailMailgunRadio').checked) {
|
} else if (document.getElementById('emailMailgunRadio').checked) {
|
||||||
document.getElementById('emailCommonArea').style.display = '';
|
document.getElementById('emailCommonArea').style.display = '';
|
||||||
document.getElementById('emailSMTPArea').style.display = 'none';
|
document.getElementById('emailSMTPArea').style.display = 'none';
|
||||||
document.getElementById('emailMailgunArea').style.display = '';
|
document.getElementById('emailMailgunArea').style.display = '';
|
||||||
|
document.getElementById('notificationsEnabled').checked = true;
|
||||||
} else if (document.getElementById('emailDisabledRadio').checked) {
|
} else if (document.getElementById('emailDisabledRadio').checked) {
|
||||||
document.getElementById('emailCommonArea').style.display = 'none';
|
document.getElementById('emailCommonArea').style.display = 'none';
|
||||||
document.getElementById('emailSMTPArea').style.display = 'none';
|
document.getElementById('emailSMTPArea').style.display = 'none';
|
||||||
document.getElementById('emailMailgunArea').style.display = 'none';
|
document.getElementById('emailMailgunArea').style.display = 'none';
|
||||||
document.getElementById('emailNextButton').href = '#page-8';
|
document.getElementById('emailNextButton').href = '#page-8';
|
||||||
document.getElementById('valBackButton').href = '#page-4';
|
document.getElementById('valBackButton').href = '#page-4';
|
||||||
|
document.getElementById('notificationsEnabled').checked = false;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
var emailRadios = ['emailDisabledRadio', 'emailSMTPRadio', 'emailMailgunRadio'];
|
var emailRadios = ['emailDisabledRadio', 'emailSMTPRadio', 'emailMailgunRadio'];
|
||||||
@ -165,6 +168,7 @@ document.getElementById('submitButton').onclick = function() {
|
|||||||
if (document.getElementById('emailDisabledRadio').checked) {
|
if (document.getElementById('emailDisabledRadio').checked) {
|
||||||
config['password_resets']['enabled'] = 'false';
|
config['password_resets']['enabled'] = 'false';
|
||||||
config['invite_emails']['enabled'] = 'false';
|
config['invite_emails']['enabled'] = 'false';
|
||||||
|
config['notifications']['enabled'] = 'false';
|
||||||
} else {
|
} else {
|
||||||
if (document.getElementById('emailSMTPRadio').checked) {
|
if (document.getElementById('emailSMTPRadio').checked) {
|
||||||
if (document.getElementById('emailSSL_TLS').checked) {
|
if (document.getElementById('emailSSL_TLS').checked) {
|
||||||
@ -226,6 +230,7 @@ document.getElementById('submitButton').onclick = function() {
|
|||||||
config['ui']['help_message'] = document.getElementById('msgHelp').value;
|
config['ui']['help_message'] = document.getElementById('msgHelp').value;
|
||||||
config['ui']['success_message'] = document.getElementById('msgSuccess').value;
|
config['ui']['success_message'] = document.getElementById('msgSuccess').value;
|
||||||
// Send it
|
// Send it
|
||||||
|
config["restart-program"] = true;
|
||||||
var req = new XMLHttpRequest();
|
var req = new XMLHttpRequest();
|
||||||
req.open("POST", "/modifyConfig", true);
|
req.open("POST", "/modifyConfig", true);
|
||||||
req.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
|
req.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
|
||||||
|
14
jfapi.go
14
jfapi.go
@ -82,10 +82,18 @@ func (jf *Jellyfin) authenticate(username, password string) (map[string]interfac
|
|||||||
jf.loginParams = map[string]string{
|
jf.loginParams = map[string]string{
|
||||||
"Username": username,
|
"Username": username,
|
||||||
"Pw": password,
|
"Pw": password,
|
||||||
|
"Password": password,
|
||||||
}
|
}
|
||||||
loginParams, _ := json.Marshal(jf.loginParams)
|
buffer := &bytes.Buffer{}
|
||||||
url := fmt.Sprintf("%s/emby/Users/AuthenticateByName", jf.server)
|
encoder := json.NewEncoder(buffer)
|
||||||
req, err := http.NewRequest("POST", url, bytes.NewBuffer(loginParams))
|
encoder.SetEscapeHTML(false)
|
||||||
|
err := encoder.Encode(jf.loginParams)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
// loginParams, _ := json.Marshal(jf.loginParams)
|
||||||
|
url := fmt.Sprintf("%s/Users/authenticatebyname", jf.server)
|
||||||
|
req, err := http.NewRequest("POST", url, buffer)
|
||||||
defer jf.timeoutHandler()
|
defer jf.timeoutHandler()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
|
71
main.go
71
main.go
@ -46,6 +46,7 @@ type appContext struct {
|
|||||||
info, debug, err *log.Logger
|
info, debug, err *log.Logger
|
||||||
host string
|
host string
|
||||||
port int
|
port int
|
||||||
|
version string
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateSecret(length int) (string, error) {
|
func GenerateSecret(length int) (string, error) {
|
||||||
@ -103,7 +104,7 @@ func main() {
|
|||||||
port := flag.Int("port", 0, "alternate port to host web ui on.")
|
port := flag.Int("port", 0, "alternate port to host web ui on.")
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
fmt.Println(*dataPath, *configPath, *host, *port)
|
||||||
if ctx.config_path == *configPath && ctx.data_path != *dataPath {
|
if ctx.config_path == *configPath && ctx.data_path != *dataPath {
|
||||||
ctx.config_path = filepath.Join(*dataPath, "config.ini")
|
ctx.config_path = filepath.Join(*dataPath, "config.ini")
|
||||||
} else {
|
} else {
|
||||||
@ -111,12 +112,24 @@ func main() {
|
|||||||
ctx.data_path = *dataPath
|
ctx.data_path = *dataPath
|
||||||
}
|
}
|
||||||
|
|
||||||
//var firstRun bool
|
// Env variables are necessary because syscall.Exec for self-restarts doesn't doesn't work with arguments for some reason.
|
||||||
|
|
||||||
|
if v := os.Getenv("JFA_CONFIGPATH"); v != "" {
|
||||||
|
ctx.config_path = v
|
||||||
|
}
|
||||||
|
if v := os.Getenv("JFA_DATAPATH"); v != "" {
|
||||||
|
ctx.data_path = v
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Setenv("JFA_CONFIGPATH", ctx.config_path)
|
||||||
|
os.Setenv("JFA_DATAPATH", ctx.data_path)
|
||||||
|
|
||||||
|
var firstRun bool
|
||||||
if _, err := os.Stat(ctx.data_path); os.IsNotExist(err) {
|
if _, err := os.Stat(ctx.data_path); os.IsNotExist(err) {
|
||||||
os.Mkdir(ctx.data_path, 0700)
|
os.Mkdir(ctx.data_path, 0700)
|
||||||
}
|
}
|
||||||
if _, err := os.Stat(ctx.config_path); os.IsNotExist(err) {
|
if _, err := os.Stat(ctx.config_path); os.IsNotExist(err) {
|
||||||
//firstRun = true
|
firstRun = true
|
||||||
dConfigPath := filepath.Join(ctx.local_path, "config-default.ini")
|
dConfigPath := filepath.Join(ctx.local_path, "config-default.ini")
|
||||||
var dConfig *os.File
|
var dConfig *os.File
|
||||||
dConfig, err = os.Open(dConfigPath)
|
dConfig, err = os.Open(dConfigPath)
|
||||||
@ -136,11 +149,21 @@ func main() {
|
|||||||
}
|
}
|
||||||
ctx.info.Printf("Copied default configuration to \"%s\"", ctx.config_path)
|
ctx.info.Printf("Copied default configuration to \"%s\"", ctx.config_path)
|
||||||
}
|
}
|
||||||
|
var debugMode bool
|
||||||
|
var address string
|
||||||
if ctx.loadConfig() != nil {
|
if ctx.loadConfig() != nil {
|
||||||
ctx.err.Fatalf("Failed to load config file \"%s\"", ctx.config_path)
|
ctx.err.Fatalf("Failed to load config file \"%s\"", ctx.config_path)
|
||||||
}
|
}
|
||||||
|
ctx.version = ctx.config.Section("jellyfin").Key("version").String()
|
||||||
|
|
||||||
|
debugMode = ctx.config.Section("ui").Key("debug").MustBool(true)
|
||||||
|
if debugMode {
|
||||||
|
ctx.debug = log.New(os.Stdout, "[DEBUG] ", log.Ltime|log.Lshortfile)
|
||||||
|
} else {
|
||||||
|
ctx.debug = log.New(ioutil.Discard, "", 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !firstRun {
|
||||||
ctx.host = ctx.config.Section("ui").Key("host").String()
|
ctx.host = ctx.config.Section("ui").Key("host").String()
|
||||||
ctx.port = ctx.config.Section("ui").Key("port").MustInt(8056)
|
ctx.port = ctx.config.Section("ui").Key("port").MustInt(8056)
|
||||||
|
|
||||||
@ -151,14 +174,18 @@ func main() {
|
|||||||
ctx.port = *port
|
ctx.port = *port
|
||||||
}
|
}
|
||||||
|
|
||||||
address := fmt.Sprintf("%s:%d", ctx.host, ctx.port)
|
if h := os.Getenv("JFA_HOST"); h != "" {
|
||||||
|
ctx.host = h
|
||||||
debugMode := ctx.config.Section("ui").Key("debug").MustBool(true)
|
if p := os.Getenv("JFA_PORT"); p != "" {
|
||||||
if debugMode {
|
var port int
|
||||||
ctx.debug = log.New(os.Stdout, "[DEBUG] ", log.Ltime|log.Lshortfile)
|
_, err := fmt.Sscan(p, &port)
|
||||||
} else {
|
if err == nil {
|
||||||
ctx.debug = log.New(ioutil.Discard, "", 0)
|
ctx.port = port
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address = fmt.Sprintf("%s:%d", ctx.host, ctx.port)
|
||||||
|
|
||||||
ctx.debug.Printf("Loaded config file \"%s\"", ctx.config_path)
|
ctx.debug.Printf("Loaded config file \"%s\"", ctx.config_path)
|
||||||
|
|
||||||
@ -214,14 +241,14 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
server := ctx.config.Section("jellyfin").Key("server").String()
|
server := ctx.config.Section("jellyfin").Key("server").String()
|
||||||
ctx.jf.init(server, "jfa-go", "0.1", "hrfee-arch", "hrfee-arch")
|
ctx.jf.init(server, "jfa-go", ctx.version, "hrfee-arch", "hrfee-arch")
|
||||||
var status int
|
var status int
|
||||||
_, status, err = ctx.jf.authenticate(ctx.config.Section("jellyfin").Key("username").String(), ctx.config.Section("jellyfin").Key("password").String())
|
_, status, err = ctx.jf.authenticate(ctx.config.Section("jellyfin").Key("username").String(), ctx.config.Section("jellyfin").Key("password").String())
|
||||||
if status != 200 || err != nil {
|
if status != 200 || err != nil {
|
||||||
ctx.err.Fatalf("Failed to authenticate with Jellyfin @ %s: Code %d", server, status)
|
ctx.err.Fatalf("Failed to authenticate with Jellyfin @ %s: Code %d", server, status)
|
||||||
}
|
}
|
||||||
ctx.info.Printf("Authenticated with %s", server)
|
ctx.info.Printf("Authenticated with %s", server)
|
||||||
ctx.authJf.init(server, "jfa-go", "0.1", "auth", "auth")
|
ctx.authJf.init(server, "jfa-go", ctx.version, "auth", "auth")
|
||||||
|
|
||||||
ctx.loadStrftime()
|
ctx.loadStrftime()
|
||||||
|
|
||||||
@ -247,6 +274,11 @@ func main() {
|
|||||||
if ctx.config.Section("password_resets").Key("enabled").MustBool(false) {
|
if ctx.config.Section("password_resets").Key("enabled").MustBool(false) {
|
||||||
go ctx.StartPWR()
|
go ctx.StartPWR()
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
debugMode = false
|
||||||
|
gin.SetMode(gin.ReleaseMode)
|
||||||
|
address = "0.0.0.0:8056"
|
||||||
|
}
|
||||||
|
|
||||||
ctx.info.Println("Loading routes")
|
ctx.info.Println("Loading routes")
|
||||||
router := gin.New()
|
router := gin.New()
|
||||||
@ -255,13 +287,14 @@ func main() {
|
|||||||
|
|
||||||
router.Use(gin.Recovery())
|
router.Use(gin.Recovery())
|
||||||
router.Use(static.Serve("/", static.LocalFile("data/static", false)))
|
router.Use(static.Serve("/", static.LocalFile("data/static", false)))
|
||||||
router.Use(static.Serve("/invite/", static.LocalFile("data/static", false)))
|
|
||||||
router.LoadHTMLGlob("data/templates/*")
|
router.LoadHTMLGlob("data/templates/*")
|
||||||
|
router.NoRoute(ctx.NoRouteHandler)
|
||||||
|
if !firstRun {
|
||||||
router.GET("/", ctx.AdminPage)
|
router.GET("/", ctx.AdminPage)
|
||||||
router.GET("/getToken", ctx.GetToken)
|
router.GET("/getToken", ctx.GetToken)
|
||||||
router.POST("/newUser", ctx.NewUser)
|
router.POST("/newUser", ctx.NewUser)
|
||||||
router.GET("/invite/:invCode", ctx.InviteProxy)
|
router.GET("/invite/:invCode", ctx.InviteProxy)
|
||||||
router.NoRoute(ctx.NoRouteHandler)
|
router.Use(static.Serve("/invite/", static.LocalFile("data/static", false)))
|
||||||
api := router.Group("/", ctx.webAuth())
|
api := router.Group("/", ctx.webAuth())
|
||||||
api.POST("/generateInvite", ctx.GenerateInvite)
|
api.POST("/generateInvite", ctx.GenerateInvite)
|
||||||
api.GET("/getInvites", ctx.GetInvites)
|
api.GET("/getInvites", ctx.GetInvites)
|
||||||
@ -273,5 +306,13 @@ func main() {
|
|||||||
api.GET("/getConfig", ctx.GetConfig)
|
api.GET("/getConfig", ctx.GetConfig)
|
||||||
api.POST("/modifyConfig", ctx.ModifyConfig)
|
api.POST("/modifyConfig", ctx.ModifyConfig)
|
||||||
ctx.info.Printf("Starting router @ %s", address)
|
ctx.info.Printf("Starting router @ %s", address)
|
||||||
|
} else {
|
||||||
|
router.GET("/", func(gc *gin.Context) {
|
||||||
|
gc.HTML(200, "setup.html", gin.H{})
|
||||||
|
})
|
||||||
|
router.POST("/testJF", ctx.TestJF)
|
||||||
|
router.POST("/modifyConfig", ctx.ModifyConfig)
|
||||||
|
ctx.info.Printf("Loading setup @ %s", address)
|
||||||
|
}
|
||||||
router.Run(address)
|
router.Run(address)
|
||||||
}
|
}
|
||||||
|
25
setup.go
Normal file
25
setup.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testReq struct {
|
||||||
|
Host string `json:"jfHost"`
|
||||||
|
Username string `json:"jfUser"`
|
||||||
|
Password string `json:"jfPassword"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *appContext) TestJF(gc *gin.Context) {
|
||||||
|
var req testReq
|
||||||
|
gc.BindJSON(&req)
|
||||||
|
tempjf := Jellyfin{}
|
||||||
|
tempjf.init(req.Host, "jfa-go-setup", ctx.version, "auth", "auth")
|
||||||
|
_, status, err := tempjf.authenticate(req.Username, req.Password)
|
||||||
|
if !(status == 200 || status == 204) || err != nil {
|
||||||
|
ctx.info.Printf("Auth failed with code %d (%s)", status, err)
|
||||||
|
gc.JSON(401, map[string]bool{"success": false})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gc.JSON(200, map[string]bool{"success": true})
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user