Skip to content

Commit

Permalink
Save config input for later use (#798)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidnewhall authored Aug 3, 2024
2 parents 93a0cd0 + 9bf8096 commit 8e3cd73
Show file tree
Hide file tree
Showing 19 changed files with 1,270 additions and 1,211 deletions.
1,912 changes: 956 additions & 956 deletions pkg/bindata/templates/config.html

Large diffs are not rendered by default.

330 changes: 165 additions & 165 deletions pkg/bindata/templates/triggers.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pkg/checkapp/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func testCommand(ctx context.Context, input *Input) (string, int) {
input.Real.Commands[input.Index].Run(&common.ActionInput{Type: website.EventGUI})
return "Command Triggered: " + input.Real.Commands[input.Index].Name, http.StatusOK
} else if len(input.Post.Commands) > input.Index { // check POST input for "new" command.
input.Post.Commands[input.Index].Setup(input.Real.Logger, input.Real.Services.Website)
input.Post.Commands[input.Index].Setup(input.Real.Logger, input.Real.Server)

if err := input.Post.Commands[input.Index].SetupRegexpArgs(); err != nil {
return err.Error(), http.StatusInternalServerError
Expand Down
2 changes: 1 addition & 1 deletion pkg/client/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ func (c *Client) handleAptHook(ctx context.Context) error { //nolint:cyclop
} //nolint:wsl
}

resp, _, err := c.website.RawGetData(ctx, &website.Request{
resp, _, err := c.Config.RawGetData(ctx, &website.Request{
Route: website.PkgRoute,
Event: "apt",
Payload: output,
Expand Down
6 changes: 3 additions & 3 deletions pkg/client/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,9 @@ func (c *Client) httpGuiHandlers(base string, compress func(handler http.Handler

// httpAPIHandlers initializes API routes.
func (c *Client) httpAPIHandlers() {
c.Config.HandleAPIpath("", "info", c.clientinfo.InfoHandler, "GET", "HEAD")
c.Config.HandleAPIpath("", "version", c.clientinfo.VersionHandler, "GET", "HEAD")
c.Config.HandleAPIpath("", "version/{app}/{instance:[0-9]+}", c.clientinfo.VersionHandlerInstance, "GET", "HEAD")
c.Config.HandleAPIpath("", "info", c.triggers.CI.InfoHandler, "GET", "HEAD")
c.Config.HandleAPIpath("", "version", c.triggers.CI.VersionHandler, "GET", "HEAD")
c.Config.HandleAPIpath("", "version/{app}/{instance:[0-9]+}", c.triggers.CI.VersionHandlerInstance, "GET", "HEAD")
c.Config.HandleAPIpath("", "trigger/{trigger:[0-9a-z-]+}", c.triggers.APIHandler, "GET", "POST")
c.Config.HandleAPIpath("", "trigger/{trigger:[0-9a-z-]+}/{content}", c.triggers.APIHandler, "GET", "POST")
c.Config.HandleAPIpath("", "services/{action}", c.Config.Services.APIHandler, "GET")
Expand Down
27 changes: 21 additions & 6 deletions pkg/client/handlers_gui.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"github.com/shirou/gopsutil/v4/disk"
"github.com/swaggo/swag"
"github.com/vearutop/statigz"
"golift.io/cnfgfile"
"golift.io/version"
)

Expand Down Expand Up @@ -716,20 +717,34 @@ func (c *Client) mergeAndValidateNewConfig(config *configfile.Config, request *h
return fmt.Errorf("decoding POST data into Go data structure failed: %w", err)
}

if err := c.validateNewCommandConfig(config); err != nil {
return err
}

return c.validateNewServiceConfig(config)
return c.validateNewConfig(config)
}

func (c *Client) validateNewCommandConfig(config *configfile.Config) error {
func (c *Client) validateNewConfig(config *configfile.Config) error {
for idx, cmd := range config.Commands {
if err := cmd.SetupRegexpArgs(); err != nil {
return fmt.Errorf("command %d '%s' failed setup: %w", idx+1, cmd.Name, err)
}
}

if err := c.validateNewServiceConfig(config); err != nil {
return err
}

copied, err := config.CopyConfig()
if err != nil {
return fmt.Errorf("copying config: %w", err)
}

_, err = cnfgfile.Parse(copied, &cnfgfile.Opts{
Name: mnd.Title,
TransformPath: configfile.ExpandHomedir,
Prefix: "filepath:",
})
if err != nil {
return fmt.Errorf("filepath: %w", err)
}

return nil
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/client/handlers_plex.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func (c *Client) PlexHandler(w http.ResponseWriter, r *http.Request) { //nolint:
case strings.EqualFold(hook.Event, "admin.database.corrupt"):
c.Printf("Plex Incoming Webhook: %s, %s '%s' ~> %s (relaying to Notifiarr)",
hook.Server.Title, hook.Account.Title, hook.Event, hook.Metadata.Title)
c.website.SendData(&website.Request{
c.Config.SendData(&website.Request{
Route: website.PlexRoute,
Event: website.EventHook,
LogPayload: true,
Expand Down
4 changes: 3 additions & 1 deletion pkg/client/html_templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@ func (c *Client) parseCustomTemplates() error {
}

type templateData struct {
Input *configfile.Config `json:"input"`
Config *configfile.Config `json:"config"`
Flags *configfile.Flags `json:"flags"`
Actions *triggers.Actions `json:"actions"`
Expand Down Expand Up @@ -485,7 +486,7 @@ func (c *Client) renderTemplate( //nolint:funlen

binary, _ := os.Executable()
userName, dynamic := c.getUserName(req)
hostInfo, _ := c.website.GetHostInfo(ctx)
hostInfo, _ := c.Config.GetHostInfo(ctx)
backupPath := filepath.Join(filepath.Dir(c.Flags.ConfigFile), "backups", filepath.Base(c.Flags.ConfigFile))
outboundIP := clientinfo.GetOutboundIP()
ifName, netmask := getIfNameAndNetmask(outboundIP)
Expand All @@ -495,6 +496,7 @@ func (c *Client) renderTemplate( //nolint:funlen
UpstreamIP: strings.Trim(req.RemoteAddr[:strings.LastIndex(req.RemoteAddr, ":")], "[]"),
Actions: c.triggers,
Config: c.Config,
Input: c.Input,
Flags: c.Flags,
Username: userName,
Dynamic: dynamic,
Expand Down
6 changes: 3 additions & 3 deletions pkg/client/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func (c *Client) PrintStartupInfo(ctx context.Context, clientInfo *clientinfo.Cl
clientInfo = &clientinfo.ClientInfo{}
}

switch host, err := c.website.GetHostInfo(ctx); {
switch host, err := c.Config.GetHostInfo(ctx); {
case err != nil:
c.Errorf("=> Unknown Host Info (this is bad): %v", err)
case c.Config.HostID == "":
Expand Down Expand Up @@ -81,7 +81,7 @@ func (c *Client) PrintStartupInfo(ctx context.Context, clientInfo *clientinfo.Cl
func (c *Client) printVersionChangeInfo(ctx context.Context) {
const clientVersion = "clientVersion"

values, err := c.website.GetState(ctx, clientVersion)
values, err := c.Config.GetState(ctx, clientVersion)
if err != nil {
c.Errorf("XX> Getting version from database: %v", err)
}
Expand All @@ -100,7 +100,7 @@ func (c *Client) printVersionChangeInfo(ctx context.Context) {
c.Printf("==> Detected application version change! %s => %s", previousVersion, currentVersion)
}

err = c.website.SetState(ctx, clientVersion, []byte(currentVersion))
err = c.Config.SetState(ctx, clientVersion, []byte(currentVersion))
if err != nil {
c.Errorf("Updating version in database: %v", err)
}
Expand Down
74 changes: 46 additions & 28 deletions pkg/client/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,11 @@ type Client struct {
plexTimer *cooldown.Timer
Flags *configfile.Flags
Config *configfile.Config
Input *configfile.Config
server *http.Server
sigkil chan os.Signal
sighup chan os.Signal
reload chan customReload
website *website.Server
clientinfo *clientinfo.Config
triggers *triggers.Actions
cookies *securecookie.SecureCookie
template *template.Template
Expand Down Expand Up @@ -121,7 +120,7 @@ func Start() error {
}

func (c *Client) checkFlags(ctx context.Context) error { //nolint:cyclop
msg, newPassword, err := c.loadConfiguration(ctx)
msgs, newPassword, err := c.loadConfiguration(ctx)

ctx, cancel := context.WithCancel(ctx)
defer cancel()
Expand All @@ -139,30 +138,35 @@ func (c *Client) checkFlags(ctx context.Context) error { //nolint:cyclop

return c.resetAdminPassword(ctx)
case c.Flags.Write != "" && (err == nil || strings.Contains(err.Error(), "ip:port")):
c.Printf("==> %s", msg)
for _, msg := range msgs {
c.Printf("==> %s", msg)
}

ctx, cancel := context.WithTimeout(ctx, mnd.DefaultTimeout)
defer cancel()

return c.forceWriteWithExit(ctx, c.Flags.Write)
case err != nil:
return fmt.Errorf("%s: %w", msg, err)
return fmt.Errorf("messages: %q, error: %w", msgs, err)
case c.Flags.Restart:
return nil
case c.Config.APIKey == "":
return fmt.Errorf("%s: %w %s_API_KEY", msg, ErrNilAPIKey, c.Flags.EnvPrefix)
return fmt.Errorf("messages: %q, %w %s_API_KEY", msgs, ErrNilAPIKey, c.Flags.EnvPrefix)
default:
return c.start(ctx, msg, newPassword)
return c.start(ctx, msgs, newPassword)
}
}

func (c *Client) start(ctx context.Context, msg, newPassword string) error {
func (c *Client) start(ctx context.Context, msgs []string, newPassword string) error {
c.Logger.SetupLogging(c.Config.LogConfig)
c.Printf(" %s %s v%s-%s Starting! [PID: %v, UID: %d, GID: %d] %s",
mnd.TodaysEmoji(), mnd.Title, version.Version, version.Revision,
os.Getpid(), os.Getuid(), os.Getgid(),
version.Started.Format("Mon, Jan 2, 2006 @ 3:04:05 PM MST -0700"))
c.Printf("==> %s", msg)

for _, msg := range msgs {
c.Printf("==> %s", msg)
}

if c.Flags.Updated {
go ui.Toast("%s updated to v%s-%s", mnd.Title, version.Version, version.Revision) //nolint:errcheck
Expand Down Expand Up @@ -202,39 +206,45 @@ func (c *Client) makeNewConfigFile(ctx context.Context, newPassword string) {
}

// loadConfiguration brings in, and sometimes creates, the initial running configuration.
func (c *Client) loadConfiguration(ctx context.Context) (string, string, error) {
func (c *Client) loadConfiguration(ctx context.Context) ([]string, string, error) {
var (
msg string
newPassword string
err error
msg, newPassword string
err error
moreMsgs map[string]string
)
// Find or write a config file. This does not parse it.
// A config file is only written when none is found on Windows, macOS (GUI App only), or Docker.
// And in the case of Docker, only if `/config` is a mounted volume.
write := (!c.Flags.Restart && ui.HasGUI()) || mnd.IsDocker
c.Flags.ConfigFile, newPassword, msg = c.Config.FindAndReturn(ctx, c.Flags.ConfigFile, write)
output := []string{msg}

if c.Flags.Restart {
return msg, newPassword, update.Restart(&update.Command{ //nolint:wrapcheck
return output, newPassword, update.Restart(&update.Command{ //nolint:wrapcheck
Path: os.Args[0],
Args: []string{"--updated", "--delay", "5s", "--config", c.Flags.ConfigFile},
})
}

// Parse the config file and environment variables.
c.website, c.triggers, err = c.Config.Get(c.Flags, c.Logger)
if err != nil {
return msg, newPassword, fmt.Errorf("getting config: %w", err)
if c.Input, err = c.Config.Get(c.Flags); err != nil {
return output, newPassword, fmt.Errorf("getting config: %w", err)
}

c.clientinfo = c.triggers.Timers.CIC
if c.triggers, moreMsgs, err = c.Config.Setup(c.Flags, c.Logger); err != nil {
return output, newPassword, fmt.Errorf("setting config: %w", err)
}

return msg, newPassword, nil
for file, path := range moreMsgs {
output = append(output, fmt.Sprintf("Extra Config File: %s => %s", file, path))
}

return output, newPassword, nil
}

// Load configuration from the website.
func (c *Client) loadSiteConfig(ctx context.Context) *clientinfo.ClientInfo {
clientInfo, err := c.clientinfo.SaveClientInfo(ctx, true)
clientInfo, err := c.triggers.CI.SaveClientInfo(ctx, true)
if err != nil || clientInfo == nil {
if errors.Is(err, website.ErrInvalidAPIKey) {
c.ErrorfNoShare("==> Problem validating API key: %v", err)
Expand All @@ -249,19 +259,19 @@ func (c *Client) loadSiteConfig(ctx context.Context) *clientinfo.ClientInfo {
// Snapshot is a bit complicated because config-file data (plugins) merges with site-data (snapshot config).
clientInfo.Actions.Snapshot.Plugins = c.Config.Snapshot.Plugins
c.Config.Snapshot = &clientInfo.Actions.Snapshot
c.triggers.Timers.Snapshot = c.Config.Snapshot
c.triggers.Snapshot = c.Config.Snapshot
c.Config.Services.Plugins = &c.Config.Snapshot.Plugins

return clientInfo
}

// configureServices is called on startup and on reload, so be careful what goes in here.
func (c *Client) configureServices(ctx context.Context) *clientinfo.ClientInfo {
c.website.Start(ctx)
c.Config.Start(ctx)

clientInfo := c.loadSiteConfig(ctx)
if clientInfo != nil && !clientInfo.User.StopLogs {
share.Setup(c.website)
share.Setup(c.Config)
}

c.configureServicesPlex(ctx)
Expand Down Expand Up @@ -337,14 +347,18 @@ func (c *Client) reloadConfiguration(ctx context.Context, event website.EventTyp

// start over.
c.Config = configfile.NewConfig(c.Logger)
if c.website, c.triggers, err = c.Config.Get(c.Flags, c.Logger); err != nil {
return fmt.Errorf("getting configuration: %w", err)
if c.Input, err = c.Config.Get(c.Flags); err != nil {
return fmt.Errorf("getting config: %w", err)
}

c.clientinfo = c.triggers.Timers.CIC
var output map[string]string

if c.triggers, output, err = c.Config.Setup(c.Flags, c.Logger); err != nil {
return fmt.Errorf("setting config: %w", err)
}

if errs := c.Logger.Close(); len(errs) > 0 {
return fmt.Errorf("closing logger(s): %w", errs[0])
return fmt.Errorf("closing logger: %w", errs[0])
}

defer c.StartWebServer(ctx)
Expand All @@ -369,6 +383,10 @@ func (c *Client) reloadConfiguration(ctx context.Context, event website.EventTyp
}
}

for path, file := range output {
c.Printf(" => Extra Config File: %s => %s", file, path)
}

// This doesn't need to lock because web server is not running.
c.reloading = false // We're done.

Expand All @@ -381,7 +399,7 @@ func (c *Client) stop(ctx context.Context, event website.EventType) error {
defer c.CapturePanic()
c.triggers.Stop(event)
c.Config.Services.Stop()
c.website.Stop()
c.Config.Stop()
c.Print("==> All systems powered down!")
}()

Expand Down
10 changes: 5 additions & 5 deletions pkg/client/tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func (c *Client) startTunnel(ctx context.Context) {

func (c *Client) makeTunnel(ctx context.Context, info *clientinfo.ClientInfo) {
hostname, _ := os.Hostname()
if hostInfo, err := c.clientinfo.GetHostInfo(ctx); err != nil {
if hostInfo, err := c.triggers.CI.GetHostInfo(ctx); err != nil {
hostname = hostInfo.Hostname
}

Expand All @@ -90,8 +90,8 @@ func (c *Client) makeTunnel(ctx context.Context, info *clientinfo.ClientInfo) {
Targets: getTunnels(info),
PoolIdleSize: 1,
PoolMaxSize: c.poolMax(info),
CleanInterval: time.Second + time.Duration(c.triggers.Timers.Rand().Intn(1000))*time.Millisecond,
Backoff: 600*time.Millisecond + time.Duration(c.triggers.Timers.Rand().Intn(600))*time.Millisecond,
CleanInterval: time.Second + time.Duration(c.triggers.Rand().Intn(1000))*time.Millisecond,
Backoff: 600*time.Millisecond + time.Duration(c.triggers.Rand().Intn(600))*time.Millisecond,
SecretKey: c.Config.APIKey,
Handler: remWs.Wrap(c.prefixURLbase(c.Config.Router), c.Logger.HTTPLog.Writer()).ServeHTTP,
RoundRobinConfig: c.roundRobinConfig(info),
Expand All @@ -117,7 +117,7 @@ func (c *Client) roundRobinConfig(ci *clientinfo.ClientInfo) *mulery.RoundRobinC
Callback: func(_ context.Context, socket string) {
defer data.Save("activeTunnel", socket)
// Tell the website we connected to a new tunnel, so it knows how to reach us.
c.website.SendData(&website.Request{
c.Config.SendData(&website.Request{
Route: website.TunnelRoute,
Event: website.EventSignal,
Payload: map[string]interface{}{"socket": socket, "previous": data.Get("activeTunnel")},
Expand Down Expand Up @@ -311,7 +311,7 @@ func (c *Client) saveTunnels(response http.ResponseWriter, request *http.Request
}
}

c.website.SendData(&website.Request{
c.Config.SendData(&website.Request{
Route: website.TunnelRoute,
Event: website.EventGUI,
Payload: map[string]any{"sockets": sockets},
Expand Down
Loading

0 comments on commit 8e3cd73

Please sign in to comment.