From 69233cd7697d61015bd58cc4722533ff2b32c278 Mon Sep 17 00:00:00 2001 From: Dan Sosedoff Date: Mon, 19 Dec 2022 12:33:13 -0600 Subject: [PATCH] Establish connections using bookmark ID only (#619) * Establish connections using bookmark ID only * Refactor specs * Extra tests * Fix homedir assertion for bookmarks path * Fix newline in the warning message * Check for bookmark file existence before reading * Connect code restructure --- pkg/api/api.go | 65 +++++++++----- pkg/bookmarks/bookmarks.go | 122 ++----------------------- pkg/bookmarks/bookmarks_test.go | 132 ++++++--------------------- pkg/bookmarks/manager.go | 152 ++++++++++++++++++++++++++++++++ pkg/bookmarks/manager_test.go | 98 ++++++++++++++++++++ pkg/cli/cli.go | 27 ++---- pkg/client/client.go | 25 ++++++ pkg/client/tunnel.go | 11 ++- pkg/command/options.go | 8 ++ pkg/command/options_test.go | 8 ++ static/js/app.js | 88 +++++++----------- 11 files changed, 411 insertions(+), 325 deletions(-) create mode 100644 pkg/bookmarks/manager.go create mode 100644 pkg/bookmarks/manager_test.go diff --git a/pkg/api/api.go b/pkg/api/api.go index 0849bed..ecda2ee 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -142,27 +142,16 @@ func Connect(c *gin.Context) { return } - url := c.Request.FormValue("url") - if url == "" { - badRequest(c, errURLRequired) - return - } + var ( + cl *client.Client + err error + ) - url, err := connection.FormatURL(command.Options{ - URL: url, - Passfile: command.Opts.Passfile, - }) - if err != nil { - badRequest(c, err) - return + if bookmarkID := c.Request.FormValue("bookmark_id"); bookmarkID != "" { + cl, err = ConnectWithBookmark(bookmarkID) + } else { + cl, err = ConnectWithURL(c) } - - var sshInfo *shared.SSHInfo - if c.Request.FormValue("ssh") != "" { - sshInfo = parseSshInfo(c) - } - - cl, err := client.NewFromUrl(url, sshInfo) if err != nil { badRequest(c, err) return @@ -187,6 +176,39 @@ func Connect(c *gin.Context) { successResponse(c, info.Format()[0]) } +func ConnectWithURL(c *gin.Context) (*client.Client, error) { + url := c.Request.FormValue("url") + if url == "" { + return nil, errURLRequired + } + + url, err := connection.FormatURL(command.Options{ + URL: url, + Passfile: command.Opts.Passfile, + }) + if err != nil { + return nil, err + } + + var sshInfo *shared.SSHInfo + if c.Request.FormValue("ssh") != "" { + sshInfo = parseSshInfo(c) + } + + return client.NewFromUrl(url, sshInfo) +} + +func ConnectWithBookmark(id string) (*client.Client, error) { + manager := bookmarks.NewManager(command.Opts.BookmarksDir) + + bookmark, err := manager.Get(id) + if err != nil { + return nil, err + } + + return client.NewFromBookmark(bookmark) +} + // SwitchDb perform database switch for the client connection func SwitchDb(c *gin.Context) { if command.Opts.LockSession { @@ -500,8 +522,9 @@ func HandleQuery(query string, c *gin.Context) { // GetBookmarks renders the list of available bookmarks func GetBookmarks(c *gin.Context) { - bookmarks, err := bookmarks.ReadAll(bookmarks.Path(command.Opts.BookmarksDir)) - serveResult(c, bookmarks, err) + manager := bookmarks.NewManager(command.Opts.BookmarksDir) + ids, err := manager.ListIDs() + serveResult(c, ids, err) } // GetInfo renders the pgweb system information diff --git a/pkg/bookmarks/bookmarks.go b/pkg/bookmarks/bookmarks.go index f89efb8..d4d45fb 100644 --- a/pkg/bookmarks/bookmarks.go +++ b/pkg/bookmarks/bookmarks.go @@ -1,28 +1,21 @@ package bookmarks import ( - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/BurntSushi/toml" - "github.com/mitchellh/go-homedir" - "github.com/sosedoff/pgweb/pkg/command" "github.com/sosedoff/pgweb/pkg/shared" ) // Bookmark contains information about bookmarked database connection type Bookmark struct { - URL string `json:"url"` // Postgres connection URL - Host string `json:"host"` // Server hostname - Port int `json:"port"` // Server port - User string `json:"user"` // Database user - Password string `json:"password"` // User password - Database string `json:"database"` // Database name - SSLMode string `json:"ssl"` // Connection SSL mode - SSH *shared.SSHInfo `json:"ssh"` // SSH tunnel config + ID string // ID generated from the filename + URL string // Postgres connection URL + Host string // Server hostname + Port int // Server port + User string // Database user + Password string // User password + Database string // Database name + SSLMode string // Connection SSL mode + SSH *shared.SSHInfo // SSH tunnel config } // SSHInfoIsEmpty returns true if ssh configuration is not provided @@ -42,100 +35,3 @@ func (b Bookmark) ConvertToOptions() command.Options { SSLMode: b.SSLMode, } } - -func readServerConfig(path string) (Bookmark, error) { - bookmark := Bookmark{} - - buff, err := os.ReadFile(path) - if err != nil { - return bookmark, err - } - - _, err = toml.Decode(string(buff), &bookmark) - - if bookmark.Port == 0 { - bookmark.Port = 5432 - } - - // List of all supported postgres modes - modes := []string{"disable", "allow", "prefer", "require", "verify-ca", "verify-full"} - valid := false - - for _, mode := range modes { - if bookmark.SSLMode == mode { - valid = true - break - } - } - - // Fall back to a default mode if mode is not set or invalid - // Typical typo: ssl mode set to "disabled" - if bookmark.SSLMode == "" || !valid { - bookmark.SSLMode = "disable" - } - - // Set default SSH port if it's not provided by user - if bookmark.SSH != nil && bookmark.SSH.Port == "" { - bookmark.SSH.Port = "22" - } - - return bookmark, err -} - -func fileBasename(path string) string { - filename := filepath.Base(path) - return strings.Replace(filename, filepath.Ext(path), "", 1) -} - -// Path returns bookmarks storage path -func Path(overrideDir string) string { - if overrideDir == "" { - path, _ := homedir.Dir() - return fmt.Sprintf("%s/.pgweb/bookmarks", path) - } - return overrideDir -} - -// ReadAll returns all available bookmarks -func ReadAll(path string) (map[string]Bookmark, error) { - results := map[string]Bookmark{} - - files, err := os.ReadDir(path) - if err != nil { - return results, err - } - - for _, file := range files { - if filepath.Ext(file.Name()) != ".toml" { - continue - } - - fullPath := filepath.Join(path, file.Name()) - key := fileBasename(file.Name()) - config, err := readServerConfig(fullPath) - - if err != nil { - fmt.Printf("%s parse error: %s\n", fullPath, err) - continue - } - - results[key] = config - } - - return results, nil -} - -// GetBookmark reads an existing bookmark -func GetBookmark(bookmarkPath string, bookmarkName string) (Bookmark, error) { - bookmarks, err := ReadAll(bookmarkPath) - if err != nil { - return Bookmark{}, err - } - - bookmark, ok := bookmarks[bookmarkName] - if !ok { - return Bookmark{}, fmt.Errorf("couldn't find a bookmark with name %s", bookmarkName) - } - - return bookmark, nil -} diff --git a/pkg/bookmarks/bookmarks_test.go b/pkg/bookmarks/bookmarks_test.go index d3d59db..66c730d 100644 --- a/pkg/bookmarks/bookmarks_test.go +++ b/pkg/bookmarks/bookmarks_test.go @@ -8,115 +8,34 @@ import ( "github.com/stretchr/testify/assert" ) -func Test_Invalid_Bookmark_Files(t *testing.T) { - _, err := readServerConfig("foobar") - assert.Error(t, err) +func TestBookmarkSSHInfoIsEmpty(t *testing.T) { + t.Run("empty", func(t *testing.T) { + info := &shared.SSHInfo{ + Host: "", + Port: "", + User: "", + } - _, err = readServerConfig("../../data/invalid.toml") - assert.Error(t, err) - assert.Equal(t, "toml: line 1: expected '.' or '=', but got 'e' instead", err.Error()) + b := Bookmark{SSH: nil} + assert.True(t, b.SSHInfoIsEmpty()) + + b = Bookmark{SSH: info} + assert.True(t, b.SSHInfoIsEmpty()) + }) + + t.Run("populated", func(t *testing.T) { + info := &shared.SSHInfo{ + Host: "localhost", + Port: "8080", + User: "postgres", + } + + b := Bookmark{SSH: info} + assert.False(t, b.SSHInfoIsEmpty()) + }) } -func Test_Bookmark(t *testing.T) { - bookmark, err := readServerConfig("../../data/bookmark.toml") - assert.Equal(t, nil, err) - assert.Equal(t, "localhost", bookmark.Host) - assert.Equal(t, 5432, bookmark.Port) - assert.Equal(t, "postgres", bookmark.User) - assert.Equal(t, "mydatabase", bookmark.Database) - assert.Equal(t, "disable", bookmark.SSLMode) - assert.Equal(t, "", bookmark.Password) - assert.Equal(t, "", bookmark.URL) - - bookmark, err = readServerConfig("../../data/bookmark_invalid_ssl.toml") - assert.Equal(t, nil, err) - assert.Equal(t, "disable", bookmark.SSLMode) -} - -func Test_Bookmark_URL(t *testing.T) { - bookmark, err := readServerConfig("../../data/bookmark_url.toml") - - assert.Equal(t, nil, err) - assert.Equal(t, "postgres://username:password@host:port/database?sslmode=disable", bookmark.URL) - assert.Equal(t, "", bookmark.Host) - assert.Equal(t, 5432, bookmark.Port) - assert.Equal(t, "", bookmark.User) - assert.Equal(t, "", bookmark.Database) - assert.Equal(t, "disable", bookmark.SSLMode) - assert.Equal(t, "", bookmark.Password) -} - -func Test_Bookmarks_Path(t *testing.T) { - assert.NotEqual(t, "/.pgweb/bookmarks", Path("")) -} - -func Test_Basename(t *testing.T) { - assert.Equal(t, "filename", fileBasename("filename.toml")) - assert.Equal(t, "filename", fileBasename("path/filename.toml")) - assert.Equal(t, "filename", fileBasename("~/long/path/filename.toml")) - assert.Equal(t, "filename", fileBasename("filename")) -} - -func Test_ReadBookmarks_Invalid(t *testing.T) { - bookmarks, err := ReadAll("foobar") - - assert.Error(t, err) - assert.Equal(t, 0, len(bookmarks)) -} - -func Test_ReadBookmarks(t *testing.T) { - bookmarks, err := ReadAll("../../data") - - assert.Equal(t, nil, err) - assert.Equal(t, 3, len(bookmarks)) -} - -func Test_GetBookmark(t *testing.T) { - expBookmark := Bookmark{ - - Host: "localhost", - Port: 5432, - User: "postgres", - Password: "", - Database: "mydatabase", - SSLMode: "disable", - } - b, err := GetBookmark("../../data", "bookmark") - if assert.NoError(t, err) { - assert.Equal(t, expBookmark, b) - } - - _, err = GetBookmark("../../data", "bar") - expErrStr := "couldn't find a bookmark with name bar" - assert.Equal(t, expErrStr, err.Error()) - - _, err = GetBookmark("foo", "bookmark") - assert.Error(t, err) -} - -func Test_Bookmark_SSHInfoIsEmpty(t *testing.T) { - emptySSH := &shared.SSHInfo{ - Host: "", - Port: "", - User: "", - } - populatedSSH := &shared.SSHInfo{ - Host: "localhost", - Port: "8080", - User: "postgres", - } - - b := Bookmark{SSH: nil} - assert.True(t, b.SSHInfoIsEmpty()) - - b = Bookmark{SSH: emptySSH} - assert.True(t, b.SSHInfoIsEmpty()) - - b.SSH = populatedSSH - assert.False(t, b.SSHInfoIsEmpty()) -} - -func Test_ConvertToOptions(t *testing.T) { +func TestBookmarkConvertToOptions(t *testing.T) { b := Bookmark{ URL: "postgres://username:password@host:port/database?sslmode=disable", Host: "localhost", @@ -136,6 +55,7 @@ func Test_ConvertToOptions(t *testing.T) { DbName: "mydatabase", SSLMode: "disable", } + opt := b.ConvertToOptions() assert.Equal(t, expOpt, opt) } diff --git a/pkg/bookmarks/manager.go b/pkg/bookmarks/manager.go new file mode 100644 index 0000000..6b9af2d --- /dev/null +++ b/pkg/bookmarks/manager.go @@ -0,0 +1,152 @@ +package bookmarks + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/BurntSushi/toml" +) + +type Manager struct { + dir string +} + +func NewManager(dir string) Manager { + return Manager{ + dir: dir, + } +} + +func (m Manager) Get(id string) (*Bookmark, error) { + bookmarks, err := m.list() + if err != nil { + return nil, err + } + + for _, b := range bookmarks { + if b.ID == id { + return &b, nil + } + } + + return nil, fmt.Errorf("bookmark %v not found", id) +} + +func (m Manager) List() ([]Bookmark, error) { + return m.list() +} + +func (m Manager) ListIDs() ([]string, error) { + bookmarks, err := m.list() + if err != nil { + return nil, err + } + + ids := make([]string, len(bookmarks)) + for i, bookmark := range bookmarks { + ids[i] = bookmark.ID + } + + return ids, nil +} + +func (m Manager) list() ([]Bookmark, error) { + result := []Bookmark{} + + if m.dir == "" { + return result, nil + } + + info, err := os.Stat(m.dir) + if err != nil { + // Do not fail if base dir does not exists: it's not created by default + if errors.Is(err, os.ErrNotExist) { + fmt.Fprintf(os.Stderr, "[WARN] bookmarks dir %s does not exist\n", m.dir) + return result, nil + } + return nil, err + } + if !info.IsDir() { + return nil, fmt.Errorf("path %s is not a directory", m.dir) + } + + dirEntries, err := os.ReadDir(m.dir) + if err != nil { + return nil, err + } + + for _, entry := range dirEntries { + name := entry.Name() + if filepath.Ext(name) != ".toml" { + continue + } + + bookmark, err := readBookmark(filepath.Join(m.dir, name)) + if err != nil { + // Do not fail if one of the bookmarks is invalid + fmt.Fprintf(os.Stderr, "[WARN] bookmark file %s is invalid: %s\n", name, err) + continue + } + + result = append(result, bookmark) + } + + return result, nil +} + +func readBookmark(path string) (Bookmark, error) { + bookmark := Bookmark{ + ID: fileBasename(path), + } + + _, err := os.Stat(path) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + err = fmt.Errorf("bookmark file %s does not exist", path) + } + return bookmark, err + } + + buff, err := os.ReadFile(path) + if err != nil { + return bookmark, err + } + + _, err = toml.Decode(string(buff), &bookmark) + + if bookmark.Port == 0 { + bookmark.Port = 5432 + } + + // List of all supported postgres modes + modes := []string{"disable", "allow", "prefer", "require", "verify-ca", "verify-full"} + valid := false + + for _, mode := range modes { + if bookmark.SSLMode == mode { + valid = true + break + } + } + + // Fall back to a default mode if mode is not set or invalid + // Typical typo: ssl mode set to "disabled" + if bookmark.SSLMode == "" || !valid { + bookmark.SSLMode = "disable" + } + + // Set default SSH port if it's not provided by user + if bookmark.SSH != nil && bookmark.SSH.Port == "" { + bookmark.SSH.Port = "22" + } + + return bookmark, err +} + +func fileBasename(path string) string { + filename := filepath.Base(path) + return strings.Replace(filename, filepath.Ext(path), "", 1) +} diff --git a/pkg/bookmarks/manager_test.go b/pkg/bookmarks/manager_test.go new file mode 100644 index 0000000..9b72b98 --- /dev/null +++ b/pkg/bookmarks/manager_test.go @@ -0,0 +1,98 @@ +package bookmarks + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestManagerList(t *testing.T) { + examples := []struct { + dir string + num int + err string + }{ + {"../../data", 3, ""}, + {"../../data/bookmark.toml", 0, "is not a directory"}, + {"../../data2", 0, ""}, + {"", 0, ""}, + } + + for _, ex := range examples { + t.Run(ex.dir, func(t *testing.T) { + bookmarks, err := NewManager(ex.dir).List() + if ex.err != "" { + assert.Contains(t, err.Error(), ex.err) + } + assert.Len(t, bookmarks, ex.num) + }) + } +} + +func TestManagerListIDs(t *testing.T) { + ids, err := NewManager("../../data").ListIDs() + assert.NoError(t, err) + assert.Equal(t, []string{"bookmark", "bookmark_invalid_ssl", "bookmark_url"}, ids) +} + +func TestManagerGet(t *testing.T) { + manager := NewManager("../../data") + + b, err := manager.Get("bookmark") + assert.NoError(t, err) + assert.Equal(t, "bookmark", b.ID) + + b, err = manager.Get("foo") + assert.Equal(t, "bookmark foo not found", err.Error()) + assert.Nil(t, b) +} + +func Test_fileBasename(t *testing.T) { + assert.Equal(t, "filename", fileBasename("filename.toml")) + assert.Equal(t, "filename", fileBasename("path/filename.toml")) + assert.Equal(t, "filename", fileBasename("~/long/path/filename.toml")) + assert.Equal(t, "filename", fileBasename("filename")) +} + +func Test_readBookmark(t *testing.T) { + t.Run("good", func(t *testing.T) { + b, err := readBookmark("../../data/bookmark.toml") + assert.NoError(t, err) + assert.Equal(t, "bookmark", b.ID) + assert.Equal(t, "localhost", b.Host) + assert.Equal(t, 5432, b.Port) + assert.Equal(t, "postgres", b.User) + assert.Equal(t, "mydatabase", b.Database) + assert.Equal(t, "disable", b.SSLMode) + assert.Equal(t, "", b.Password) + assert.Equal(t, "", b.URL) + }) + + t.Run("with url", func(t *testing.T) { + b, err := readBookmark("../../data/bookmark_url.toml") + assert.NoError(t, err) + assert.Equal(t, "postgres://username:password@host:port/database?sslmode=disable", b.URL) + assert.Equal(t, "", b.Host) + assert.Equal(t, 5432, b.Port) + assert.Equal(t, "", b.User) + assert.Equal(t, "", b.Database) + assert.Equal(t, "disable", b.SSLMode) + assert.Equal(t, "", b.Password) + }) + + t.Run("invalid ssl", func(t *testing.T) { + b, err := readBookmark("../../data/bookmark_invalid_ssl.toml") + assert.NoError(t, err) + assert.Equal(t, "disable", b.SSLMode) + }) + + t.Run("invalid file", func(t *testing.T) { + _, err := readBookmark("foobar") + assert.Equal(t, "bookmark file foobar does not exist", err.Error()) + }) + + t.Run("invalid syntax", func(t *testing.T) { + _, err := readBookmark("../../data/invalid.toml") + assert.Equal(t, "toml: line 1: expected '.' or '=', but got 'e' instead", err.Error()) + }) +} diff --git a/pkg/cli/cli.go b/pkg/cli/cli.go index 2a939e9..7b92bbc 100644 --- a/pkg/cli/cli.go +++ b/pkg/cli/cli.go @@ -19,7 +19,6 @@ import ( "github.com/sosedoff/pgweb/pkg/client" "github.com/sosedoff/pgweb/pkg/command" "github.com/sosedoff/pgweb/pkg/connection" - "github.com/sosedoff/pgweb/pkg/shared" "github.com/sosedoff/pgweb/pkg/util" ) @@ -47,30 +46,14 @@ func exitWithMessage(message string) { os.Exit(1) } -func initClientUsingBookmark(bookmarkPath, bookmarkName string) (*client.Client, error) { - bookmark, err := bookmarks.GetBookmark(bookmarkPath, bookmarkName) +func initClientUsingBookmark(baseDir, bookmarkName string) (*client.Client, error) { + manager := bookmarks.NewManager(baseDir) + bookmark, err := manager.Get(bookmarkName) if err != nil { return nil, err } - opt := bookmark.ConvertToOptions() - var connStr string - - if opt.URL != "" { // if the bookmark has url set, use it - connStr = opt.URL - } else { - connStr, err = connection.BuildStringFromOptions(opt) - if err != nil { - return nil, fmt.Errorf("error building connection string: %v", err) - } - } - - var ssh *shared.SSHInfo - if !bookmark.SSHInfoIsEmpty() { - ssh = bookmark.SSH - } - - return client.NewFromUrl(connStr, ssh) + return client.NewFromBookmark(bookmark) } func initClient() { @@ -82,7 +65,7 @@ func initClient() { var err error if options.Bookmark != "" { - cl, err = initClientUsingBookmark(bookmarks.Path(options.BookmarksDir), options.Bookmark) + cl, err = initClientUsingBookmark(options.BookmarksDir, options.Bookmark) } else { cl, err = client.New() } diff --git a/pkg/client/client.go b/pkg/client/client.go index 22216ca..4fce389 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -13,6 +13,7 @@ import ( "github.com/jmoiron/sqlx" _ "github.com/lib/pq" + "github.com/sosedoff/pgweb/pkg/bookmarks" "github.com/sosedoff/pgweb/pkg/command" "github.com/sosedoff/pgweb/pkg/connection" "github.com/sosedoff/pgweb/pkg/history" @@ -137,6 +138,30 @@ func NewFromUrl(url string, sshInfo *shared.SSHInfo) (*Client, error) { return &client, nil } +func NewFromBookmark(bookmark *bookmarks.Bookmark) (*Client, error) { + var ( + connStr string + err error + ) + + options := bookmark.ConvertToOptions() + if options.URL != "" { + connStr = options.URL + } else { + connStr, err = connection.BuildStringFromOptions(options) + if err != nil { + return nil, err + } + } + + var sshInfo *shared.SSHInfo + if !bookmark.SSHInfoIsEmpty() { + sshInfo = bookmark.SSH + } + + return NewFromUrl(connStr, sshInfo) +} + func (client *Client) init() { if command.Opts.QueryTimeout > 0 { client.queryTimeout = time.Second * time.Duration(command.Opts.QueryTimeout) diff --git a/pkg/client/tunnel.go b/pkg/client/tunnel.go index b64e515..b539b5c 100644 --- a/pkg/client/tunnel.go +++ b/pkg/client/tunnel.go @@ -59,14 +59,13 @@ func parsePrivateKey(keyPath string, keyPass string) (ssh.Signer, error) { } signer, err := ssh.ParsePrivateKey(buff) - if err != nil { - if strings.Contains(err.Error(), "cannot decode encrypted private keys") { - if keyPass == "" { - return nil, errors.New("SSH key password is not provided") - } - return sshkeys.ParseEncryptedPrivateKey(buff, []byte(keyPass)) + if _, ok := err.(*ssh.PassphraseMissingError); ok { + if keyPass == "" { + return nil, errors.New("SSH key password is not provided") } + return sshkeys.ParseEncryptedPrivateKey(buff, []byte(keyPass)) } + return signer, err } diff --git a/pkg/command/options.go b/pkg/command/options.go index 0bdf19c..0269e1e 100644 --- a/pkg/command/options.go +++ b/pkg/command/options.go @@ -10,6 +10,7 @@ import ( "github.com/jackc/pgpassfile" "github.com/jessevdk/go-flags" + "github.com/mitchellh/go-homedir" "github.com/sirupsen/logrus" ) @@ -154,6 +155,13 @@ func ParseOptions(args []string) (Options, error) { } } + if opts.BookmarksDir == "" { + path, err := homedir.Dir() + if err == nil { + opts.BookmarksDir = filepath.Join(path, ".pgweb/bookmarks") + } + } + return opts, nil } diff --git a/pkg/command/options_test.go b/pkg/command/options_test.go index 75eed5f..565002d 100644 --- a/pkg/command/options_test.go +++ b/pkg/command/options_test.go @@ -2,12 +2,19 @@ package command import ( "os" + "path/filepath" "testing" + "github.com/mitchellh/go-homedir" "github.com/stretchr/testify/assert" ) func TestParseOptions(t *testing.T) { + var hdir string + if d, err := homedir.Dir(); err == nil { + hdir = d + } + t.Run("defaults", func(t *testing.T) { opts, err := ParseOptions([]string{}) assert.NoError(t, err) @@ -22,6 +29,7 @@ func TestParseOptions(t *testing.T) { assert.Equal(t, false, opts.Cors) assert.Equal(t, "*", opts.CorsOrigin) assert.Equal(t, "", opts.Passfile) + assert.Equal(t, filepath.Join(hdir, ".pgweb/bookmarks"), opts.BookmarksDir) }) t.Run("sessions", func(t *testing.T) { diff --git a/static/js/app.js b/static/js/app.js index 2323b4c..1b853b8 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -1000,18 +1000,18 @@ function getLatestReleaseInfo(current) { function showConnectionSettings() { // Show the current postgres version $(".connection-settings .version").text("v" + appInfo.version).show(); + $("#connection_window").show(); // Check github release page for updates getLatestReleaseInfo(appInfo); getBookmarks(function(data) { - // Do not add any bookmarks if we've got an error if (data.error) { - console.log("Cant get bookmarks:", data.error); + console.log("Error while fetching bookmarks:", data.error); return; } - if (Object.keys(data).length > 0) { + if (data.length > 0) { // Set bookmarks in global var bookmarks = data; @@ -1019,10 +1019,10 @@ function showConnectionSettings() { $("#connection_bookmarks").html(""); // Add blank option - $("").appendTo("#connection_bookmarks"); + $("").appendTo("#connection_bookmarks"); // Add all available bookmarks - for (key in data) { + for (key of data) { $("").appendTo("#connection_bookmarks"); } @@ -1032,8 +1032,6 @@ function showConnectionSettings() { $(".bookmarks").hide(); } }); - - $("#connection_window").show(); } function getConnectionString() { @@ -1624,67 +1622,43 @@ $(document).ready(function() { }); $("#connection_bookmarks").on("change", function(e) { - var name = $.trim($(this).val()); - if (name == "") return; + var selection = $(this).val(); - var item = bookmarks[name]; + var inputs = [ + $("#connection_form input[type='text']"), + $("#connection_form input[type='password']"), + $("#connection_ssl") + ]; - // Check if bookmark only has url set - if (item.url && item.url != "") { - $("#connection_url").val(item.url); - $("#connection_scheme").click(); - return; - } - - // Fill in bookmarked connection settings - $("#pg_host").val(item.host); - $("#pg_port").val(item.port); - $("#pg_user").val(item.user); - $("#pg_password").val(item.password); - $("#pg_db").val(item.database); - $("#connection_ssl").val(item.ssl); - - if (item.ssh && Object.keys(item.ssh).length > 0) { - $("#ssh_host").val(item.ssh.host); - $("#ssh_port").val(item.ssh.port); - $("#ssh_user").val(item.ssh.user); - $("#ssh_password").val(item.ssh.password); - $("#ssh_key").val(item.ssh.key); - $("#ssh_key_password").val(item.ssh.keypassword); - $("#connection_ssh").click(); - } - else { - $("#ssh_host").val(""); - $("#ssh_port").val(""); - $("#ssh_user").val(""); - $("#ssh_password").val(""); - $("#ssh_key").val(""); - $("#ssh_key_password").val(""); - $(".connection-ssh-group").hide(); - $("#connection_standard").click(); - } + inputs.forEach(function(selector) { + selector.val("").prop("disabled", selection == "" ? "" : "disabled"); + }); }); $("#connection_form").on("submit", function(e) { e.preventDefault(); var button = $(this).find("button.open-connection"); - var params = { - url: getConnectionString() - }; + var params = {}; - if (params.url.length == 0) { - return; + if ($("#connection_bookmarks").val() != "") { + params["bookmark_id"] = $("#connection_bookmarks").val(); } + else { + params.url = getConnectionString(); + if (params.url.length == 0) { + return; + } - if ($(".connection-group-switch button.active").attr("data") == "ssh") { - params["ssh"] = 1 - params["ssh_host"] = $("#ssh_host").val(); - params["ssh_port"] = $("#ssh_port").val(); - params["ssh_user"] = $("#ssh_user").val(); - params["ssh_password"] = $("#ssh_password").val(); - params["ssh_key"] = $("#ssh_key").val(); - params["ssh_key_password"] = $("#ssh_key_password").val() + if ($(".connection-group-switch button.active").attr("data") == "ssh") { + params["ssh"] = 1 + params["ssh_host"] = $("#ssh_host").val(); + params["ssh_port"] = $("#ssh_port").val(); + params["ssh_user"] = $("#ssh_user").val(); + params["ssh_password"] = $("#ssh_password").val(); + params["ssh_key"] = $("#ssh_key").val(); + params["ssh_key_password"] = $("#ssh_key_password").val() + } } $("#connection_error").hide();