use app identifier instead of ctx

changing this because ctx is commonly used with the context package.
This commit is contained in:
Harvey Tindall
2020-08-16 13:36:54 +01:00
parent fffb3471d6
commit fd766e7b1a
9 changed files with 452 additions and 445 deletions
+127 -126
View File
@@ -7,11 +7,6 @@ import (
"encoding/json"
"flag"
"fmt"
"github.com/gin-contrib/pprof"
"github.com/gin-contrib/static"
"github.com/gin-gonic/gin"
"github.com/lithammer/shortuuid/v3"
"gopkg.in/ini.v1"
"io"
"io/ioutil"
"log"
@@ -20,6 +15,12 @@ import (
"os/signal"
"path/filepath"
"time"
"github.com/gin-contrib/pprof"
"github.com/gin-contrib/static"
"github.com/gin-gonic/gin"
"github.com/lithammer/shortuuid/v3"
"gopkg.in/ini.v1"
)
// Username is JWT!
@@ -95,190 +96,190 @@ func setGinLogger(router *gin.Engine, debugMode bool) {
}
func main() {
ctx := new(appContext)
app := new(appContext)
userConfigDir, _ := os.UserConfigDir()
ctx.data_path = filepath.Join(userConfigDir, "jfa-go")
ctx.config_path = filepath.Join(ctx.data_path, "config.ini")
app.data_path = filepath.Join(userConfigDir, "jfa-go")
app.config_path = filepath.Join(app.data_path, "config.ini")
executable, _ := os.Executable()
ctx.local_path = filepath.Join(filepath.Dir(executable), "data")
app.local_path = filepath.Join(filepath.Dir(executable), "data")
ctx.info = log.New(os.Stdout, "[INFO] ", log.Ltime)
ctx.err = log.New(os.Stdout, "[ERROR] ", log.Ltime|log.Lshortfile)
app.info = log.New(os.Stdout, "[INFO] ", log.Ltime)
app.err = log.New(os.Stdout, "[ERROR] ", log.Ltime|log.Lshortfile)
dataPath := flag.String("data", ctx.data_path, "alternate path to data directory.")
configPath := flag.String("config", ctx.config_path, "alternate path to config file.")
dataPath := flag.String("data", app.data_path, "alternate path to data directory.")
configPath := flag.String("config", app.config_path, "alternate path to config file.")
host := flag.String("host", "", "alternate address to host web ui on.")
port := flag.Int("port", 0, "alternate port to host web ui on.")
flag.Parse()
if ctx.config_path == *configPath && ctx.data_path != *dataPath {
ctx.config_path = filepath.Join(*dataPath, "config.ini")
if app.config_path == *configPath && app.data_path != *dataPath {
app.config_path = filepath.Join(*dataPath, "config.ini")
} else {
ctx.config_path = *configPath
ctx.data_path = *dataPath
app.config_path = *configPath
app.data_path = *dataPath
}
// 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
app.config_path = v
}
if v := os.Getenv("JFA_DATAPATH"); v != "" {
ctx.data_path = v
app.data_path = v
}
os.Setenv("JFA_CONFIGPATH", ctx.config_path)
os.Setenv("JFA_DATAPATH", ctx.data_path)
os.Setenv("JFA_CONFIGPATH", app.config_path)
os.Setenv("JFA_DATAPATH", app.data_path)
var firstRun bool
if _, err := os.Stat(ctx.data_path); os.IsNotExist(err) {
os.Mkdir(ctx.data_path, 0700)
if _, err := os.Stat(app.data_path); os.IsNotExist(err) {
os.Mkdir(app.data_path, 0700)
}
if _, err := os.Stat(ctx.config_path); os.IsNotExist(err) {
if _, err := os.Stat(app.config_path); os.IsNotExist(err) {
firstRun = true
dConfigPath := filepath.Join(ctx.local_path, "config-default.ini")
dConfigPath := filepath.Join(app.local_path, "config-default.ini")
var dConfig *os.File
dConfig, err = os.Open(dConfigPath)
if err != nil {
ctx.err.Fatalf("Couldn't find default config file \"%s\"", dConfigPath)
app.err.Fatalf("Couldn't find default config file \"%s\"", dConfigPath)
}
defer dConfig.Close()
var nConfig *os.File
nConfig, err := os.Create(ctx.config_path)
nConfig, err := os.Create(app.config_path)
if err != nil {
ctx.err.Fatalf("Couldn't open config file for writing: \"%s\"", dConfigPath)
app.err.Fatalf("Couldn't open config file for writing: \"%s\"", dConfigPath)
}
defer nConfig.Close()
_, err = io.Copy(nConfig, dConfig)
if err != nil {
ctx.err.Fatalf("Couldn't copy default config. To do this manually, copy\n%s\nto\n%s", dConfigPath, ctx.config_path)
app.err.Fatalf("Couldn't copy default config. To do this manually, copy\n%s\nto\n%s", dConfigPath, app.config_path)
}
ctx.info.Printf("Copied default configuration to \"%s\"", ctx.config_path)
app.info.Printf("Copied default configuration to \"%s\"", app.config_path)
}
var debugMode bool
var address string
if ctx.loadConfig() != nil {
ctx.err.Fatalf("Failed to load config file \"%s\"", ctx.config_path)
if app.loadConfig() != nil {
app.err.Fatalf("Failed to load config file \"%s\"", app.config_path)
}
ctx.version = ctx.config.Section("jellyfin").Key("version").String()
app.version = app.config.Section("jellyfin").Key("version").String()
debugMode = ctx.config.Section("ui").Key("debug").MustBool(true)
debugMode = app.config.Section("ui").Key("debug").MustBool(true)
if debugMode {
ctx.debug = log.New(os.Stdout, "[DEBUG] ", log.Ltime|log.Lshortfile)
app.debug = log.New(os.Stdout, "[DEBUG] ", log.Ltime|log.Lshortfile)
} else {
ctx.debug = log.New(ioutil.Discard, "", 0)
app.debug = log.New(ioutil.Discard, "", 0)
}
if !firstRun {
ctx.host = ctx.config.Section("ui").Key("host").String()
ctx.port = ctx.config.Section("ui").Key("port").MustInt(8056)
app.host = app.config.Section("ui").Key("host").String()
app.port = app.config.Section("ui").Key("port").MustInt(8056)
if *host != ctx.host && *host != "" {
ctx.host = *host
if *host != app.host && *host != "" {
app.host = *host
}
if *port != ctx.port && *port > 0 {
ctx.port = *port
if *port != app.port && *port > 0 {
app.port = *port
}
if h := os.Getenv("JFA_HOST"); h != "" {
ctx.host = h
app.host = h
if p := os.Getenv("JFA_PORT"); p != "" {
var port int
_, err := fmt.Sscan(p, &port)
if err == nil {
ctx.port = port
app.port = port
}
}
}
address = fmt.Sprintf("%s:%d", ctx.host, ctx.port)
address = fmt.Sprintf("%s:%d", app.host, app.port)
ctx.debug.Printf("Loaded config file \"%s\"", ctx.config_path)
app.debug.Printf("Loaded config file \"%s\"", app.config_path)
if ctx.config.Section("ui").Key("bs5").MustBool(false) {
ctx.cssFile = "bs5-jf.css"
ctx.bsVersion = 5
if app.config.Section("ui").Key("bs5").MustBool(false) {
app.cssFile = "bs5-jf.css"
app.bsVersion = 5
} else {
ctx.cssFile = "bs4-jf.css"
ctx.bsVersion = 4
app.cssFile = "bs4-jf.css"
app.bsVersion = 4
}
ctx.debug.Println("Loading storage")
app.debug.Println("Loading storage")
ctx.storage.invite_path = filepath.Join(ctx.data_path, "invites.json")
ctx.storage.loadInvites()
ctx.storage.emails_path = filepath.Join(ctx.data_path, "emails.json")
ctx.storage.loadEmails()
ctx.storage.policy_path = filepath.Join(ctx.data_path, "user_template.json")
ctx.storage.loadPolicy()
ctx.storage.configuration_path = filepath.Join(ctx.data_path, "user_configuration.json")
ctx.storage.loadConfiguration()
ctx.storage.displayprefs_path = filepath.Join(ctx.data_path, "user_displayprefs.json")
ctx.storage.loadDisplayprefs()
app.storage.invite_path = filepath.Join(app.data_path, "invites.json")
app.storage.loadInvites()
app.storage.emails_path = filepath.Join(app.data_path, "emails.json")
app.storage.loadEmails()
app.storage.policy_path = filepath.Join(app.data_path, "user_template.json")
app.storage.loadPolicy()
app.storage.configuration_path = filepath.Join(app.data_path, "user_configuration.json")
app.storage.loadConfiguration()
app.storage.displayprefs_path = filepath.Join(app.data_path, "user_displayprefs.json")
app.storage.loadDisplayprefs()
ctx.configBase_path = filepath.Join(ctx.local_path, "config-base.json")
config_base, _ := ioutil.ReadFile(ctx.configBase_path)
json.Unmarshal(config_base, &ctx.configBase)
app.configBase_path = filepath.Join(app.local_path, "config-base.json")
config_base, _ := ioutil.ReadFile(app.configBase_path)
json.Unmarshal(config_base, &app.configBase)
themes := map[string]string{
"Jellyfin (Dark)": fmt.Sprintf("bs%d-jf.css", ctx.bsVersion),
"Bootstrap (Light)": fmt.Sprintf("bs%d.css", ctx.bsVersion),
"Jellyfin (Dark)": fmt.Sprintf("bs%d-jf.css", app.bsVersion),
"Bootstrap (Light)": fmt.Sprintf("bs%d.css", app.bsVersion),
"Custom CSS": "",
}
if val, ok := themes[ctx.config.Section("ui").Key("theme").String()]; ok {
ctx.cssFile = val
if val, ok := themes[app.config.Section("ui").Key("theme").String()]; ok {
app.cssFile = val
}
ctx.debug.Printf("Using css file \"%s\"", ctx.cssFile)
app.debug.Printf("Using css file \"%s\"", app.cssFile)
secret, err := GenerateSecret(16)
if err != nil {
ctx.err.Fatal(err)
app.err.Fatal(err)
}
os.Setenv("JFA_SECRET", secret)
ctx.jellyfinLogin = true
if val, _ := ctx.config.Section("ui").Key("jellyfin_login").Bool(); !val {
ctx.jellyfinLogin = false
app.jellyfinLogin = true
if val, _ := app.config.Section("ui").Key("jellyfin_login").Bool(); !val {
app.jellyfinLogin = false
user := User{}
user.UserID = shortuuid.New()
user.Username = ctx.config.Section("ui").Key("username").String()
user.Password = ctx.config.Section("ui").Key("password").String()
ctx.users = append(ctx.users, user)
user.Username = app.config.Section("ui").Key("username").String()
user.Password = app.config.Section("ui").Key("password").String()
app.users = append(app.users, user)
} else {
ctx.debug.Println("Using Jellyfin for authentication")
app.debug.Println("Using Jellyfin for authentication")
}
server := ctx.config.Section("jellyfin").Key("server").String()
ctx.jf.init(server, "jfa-go", ctx.version, "hrfee-arch", "hrfee-arch")
server := app.config.Section("jellyfin").Key("server").String()
app.jf.init(server, "jfa-go", app.version, "hrfee-arch", "hrfee-arch")
var status int
_, status, err = ctx.jf.authenticate(ctx.config.Section("jellyfin").Key("username").String(), ctx.config.Section("jellyfin").Key("password").String())
_, status, err = app.jf.authenticate(app.config.Section("jellyfin").Key("username").String(), app.config.Section("jellyfin").Key("password").String())
if status != 200 || err != nil {
ctx.err.Fatalf("Failed to authenticate with Jellyfin @ %s: Code %d", server, status)
app.err.Fatalf("Failed to authenticate with Jellyfin @ %s: Code %d", server, status)
}
ctx.info.Printf("Authenticated with %s", server)
ctx.authJf.init(server, "jfa-go", ctx.version, "auth", "auth")
app.info.Printf("Authenticated with %s", server)
app.authJf.init(server, "jfa-go", app.version, "auth", "auth")
ctx.loadStrftime()
app.loadStrftime()
validatorConf := ValidatorConf{
"characters": ctx.config.Section("password_validation").Key("min_length").MustInt(0),
"uppercase characters": ctx.config.Section("password_validation").Key("upper").MustInt(0),
"lowercase characters": ctx.config.Section("password_validation").Key("lower").MustInt(0),
"numbers": ctx.config.Section("password_validation").Key("number").MustInt(0),
"special characters": ctx.config.Section("password_validation").Key("special").MustInt(0),
"characters": app.config.Section("password_validation").Key("min_length").MustInt(0),
"uppercase characters": app.config.Section("password_validation").Key("upper").MustInt(0),
"lowercase characters": app.config.Section("password_validation").Key("lower").MustInt(0),
"numbers": app.config.Section("password_validation").Key("number").MustInt(0),
"special characters": app.config.Section("password_validation").Key("special").MustInt(0),
}
if !ctx.config.Section("password_validation").Key("enabled").MustBool(false) {
if !app.config.Section("password_validation").Key("enabled").MustBool(false) {
for key := range validatorConf {
validatorConf[key] = 0
}
}
ctx.validator.init(validatorConf)
app.validator.init(validatorConf)
ctx.email.init(ctx)
app.email.init(app)
inviteDaemon := NewRepeater(time.Duration(60*time.Second), ctx)
inviteDaemon := NewRepeater(time.Duration(60*time.Second), app)
go inviteDaemon.Run()
if ctx.config.Section("password_resets").Key("enabled").MustBool(false) {
go ctx.StartPWR()
if app.config.Section("password_resets").Key("enabled").MustBool(false) {
go app.StartPWR()
}
} else {
debugMode = false
@@ -286,43 +287,43 @@ func main() {
address = "0.0.0.0:8056"
}
ctx.info.Println("Loading routes")
app.info.Println("Loading routes")
router := gin.New()
setGinLogger(router, debugMode)
router.Use(gin.Recovery())
router.Use(static.Serve("/", static.LocalFile(filepath.Join(ctx.local_path, "static"), false)))
router.LoadHTMLGlob(filepath.Join(ctx.local_path, "templates", "*"))
router.NoRoute(ctx.NoRouteHandler)
router.Use(static.Serve("/", static.LocalFile(filepath.Join(app.local_path, "static"), false)))
router.LoadHTMLGlob(filepath.Join(app.local_path, "templates", "*"))
router.NoRoute(app.NoRouteHandler)
if debugMode {
ctx.debug.Println("Loading pprof")
app.debug.Println("Loading pprof")
pprof.Register(router)
}
if !firstRun {
router.GET("/", ctx.AdminPage)
router.GET("/getToken", ctx.GetToken)
router.POST("/newUser", ctx.NewUser)
router.Use(static.Serve("/invite/", static.LocalFile(filepath.Join(ctx.local_path, "static"), false)))
router.GET("/invite/:invCode", ctx.InviteProxy)
api := router.Group("/", ctx.webAuth())
api.POST("/generateInvite", ctx.GenerateInvite)
api.GET("/getInvites", ctx.GetInvites)
api.POST("/setNotify", ctx.SetNotify)
api.POST("/deleteInvite", ctx.DeleteInvite)
api.GET("/getUsers", ctx.GetUsers)
api.POST("/modifyUsers", ctx.ModifyEmails)
api.POST("/setDefaults", ctx.SetDefaults)
api.GET("/getConfig", ctx.GetConfig)
api.POST("/modifyConfig", ctx.ModifyConfig)
ctx.info.Printf("Starting router @ %s", address)
router.GET("/", app.AdminPage)
router.GET("/getToken", app.GetToken)
router.POST("/newUser", app.NewUser)
router.Use(static.Serve("/invite/", static.LocalFile(filepath.Join(app.local_path, "static"), false)))
router.GET("/invite/:invCode", app.InviteProxy)
api := router.Group("/", app.webAuth())
api.POST("/generateInvite", app.GenerateInvite)
api.GET("/getInvites", app.GetInvites)
api.POST("/setNotify", app.SetNotify)
api.POST("/deleteInvite", app.DeleteInvite)
api.GET("/getUsers", app.GetUsers)
api.POST("/modifyUsers", app.ModifyEmails)
api.POST("/setDefaults", app.SetDefaults)
api.GET("/getConfig", app.GetConfig)
api.POST("/modifyConfig", app.ModifyConfig)
app.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.POST("/testJF", app.TestJF)
router.POST("/modifyConfig", app.ModifyConfig)
app.info.Printf("Loading setup @ %s", address)
}
srv := &http.Server{
@@ -331,17 +332,17 @@ func main() {
}
go func() {
if err := srv.ListenAndServe(); err != nil {
ctx.err.Printf("Failure serving: %s", err)
app.err.Printf("Failure serving: %s", err)
}
}()
ctx.quit = make(chan os.Signal)
signal.Notify(ctx.quit, os.Interrupt)
<-ctx.quit
ctx.info.Println("Shutting down...")
app.quit = make(chan os.Signal)
signal.Notify(app.quit, os.Interrupt)
<-app.quit
app.info.Println("Shutting down...")
cntx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
if err := srv.Shutdown(cntx); err != nil {
ctx.err.Fatalf("Server shutdown error: %s", err)
app.err.Fatalf("Server shutdown error: %s", err)
}
}