From 7557ac854e2d7b54875efeebd7a846aa1e54cdaa Mon Sep 17 00:00:00 2001 From: Dan Sosedoff Date: Thu, 8 Dec 2022 15:07:40 -0600 Subject: [PATCH] Configure client side query timeout (#610) * Configure client side query timeout * Update test command on windows * Make query timeout cli opt an uint * Fix windows test command * Check for updates when rendering connection page * Fix typo --- pkg/api/api.go | 8 +++++- pkg/command/options.go | 2 +- static/js/app.js | 62 +++++++++++++++++++++++------------------- 3 files changed, 42 insertions(+), 30 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index 17a89a4..152da8f 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -507,7 +507,13 @@ func GetBookmarks(c *gin.Context) { // GetInfo renders the pgweb system information func GetInfo(c *gin.Context) { - successResponse(c, command.Info) + successResponse(c, gin.H{ + "app": command.Info, + "features": gin.H{ + "session_lock": command.Opts.LockSession, + "query_timeout": command.Opts.QueryTimeout, + }, + }) } // DataExport performs database table export diff --git a/pkg/command/options.go b/pkg/command/options.go index ca76aff..348deb4 100644 --- a/pkg/command/options.go +++ b/pkg/command/options.go @@ -49,7 +49,7 @@ type Options struct { ConnectHeaders string `long:"connect-headers" description:"List of headers to pass to the connect backend"` DisableConnectionIdleTimeout bool `long:"no-idle-timeout" description:"Disable connection idle timeout"` ConnectionIdleTimeout int `long:"idle-timeout" description:"Set connection idle timeout in minutes" default:"180"` - QueryTimeout int `long:"query-timeout" description:"Set global query execution timeout in seconds" default:"0"` + QueryTimeout uint `long:"query-timeout" description:"Set global query execution timeout in seconds" default:"300"` Cors bool `long:"cors" description:"Enable Cross-Origin Resource Sharing (CORS)"` CorsOrigin string `long:"cors-origin" description:"Allowed CORS origins" default:"*"` BinaryCodec string `long:"binary-codec" description:"Codec for binary data serialization, one of 'none', 'hex', 'base58', 'base64'" default:"none"` diff --git a/static/js/app.js b/static/js/app.js index 468b22b..2323b4c 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -1,3 +1,5 @@ +var appInfo = {}; +var appFeatures = {}; var editor = null; var connected = false; var bookmarks = {}; @@ -57,10 +59,13 @@ function getPagesCount(rowsCount) { } function apiCall(method, path, params, cb) { - var timeout = 300000; // 5 mins is enough + var timeout = appFeatures.query_timeout; + if (timeout == null) { + timeout = 300; // in seconds + } $.ajax({ - timeout: timeout, + timeout: timeout * 1000, // in milliseconds url: "api" + path, method: method, cache: false, @@ -68,12 +73,10 @@ function apiCall(method, path, params, cb) { headers: { "x-session-id": getSessionId() }, - success: function(data) { - cb(data); - }, + success: cb, error: function(xhr, status, data) { if (status == "timeout") { - return cb({ error: "Query timeout after " + (timeout / 1000) + "s" }); + return cb({ error: "Query timeout after " + timeout + "s" }); } cb(jQuery.parseJSON(xhr.responseText)); @@ -82,6 +85,7 @@ function apiCall(method, path, params, cb) { } function getInfo(cb) { apiCall("get", "/info", {}, cb); } +function getConnection(cb) { apiCall("get", "/connection", {}, cb); } function getObjects(cb) { apiCall("get", "/objects", {}, cb); } function getTables(cb) { apiCall("get", "/tables", {}, cb); } function getTableRows(table, opts, cb) { apiCall("get", "/tables/" + table + "/rows", opts, cb); } @@ -644,7 +648,7 @@ function showConnectionPanel() { $("#input").hide(); $("#body").addClass("full"); - apiCall("get", "/connection", {}, function(data) { + getConnection(function(data) { var rows = []; for(key in data) { @@ -994,23 +998,16 @@ function getLatestReleaseInfo(current) { } function showConnectionSettings() { - // Fetch server info - getInfo(function(data) { - if (data.error) return; - if (!data.version) return; + // Show the current postgres version + $(".connection-settings .version").text("v" + appInfo.version).show(); - // Show the current postgres version - $(".connection-settings .version").text("v" + data.version).show(); - - // Check for updates if running the actual release from Github - if (data.git_sha == "") { - getLatestReleaseInfo(data); - } - }); + // 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); return; } @@ -1713,6 +1710,7 @@ $(document).ready(function() { initEditor(); addShortcutTooltips(); + bindDatabaseObjectsFilter(); // Set session from the url var reqUrl = new URL(window.location); @@ -1723,25 +1721,33 @@ $(document).ready(function() { window.history.pushState({}, document.title, window.location.pathname); } - apiCall("get", "/connection", {}, function(resp) { + getInfo(function(resp) { if (resp.error) { - connected = false; - showConnectionSettings(); - $(".connection-actions").show(); + alert("Unable to fetch app info: " + resp.error + ". Please reload the browser page."); + return; } - else { + + appInfo = resp.app; + appFeatures = resp.features; + + getConnection(function(resp) { + if (resp.error) { + connected = false; + showConnectionSettings(); + $(".connection-actions").show(); + return; + } + connected = true; loadSchemas(); $("#current_database").text(resp.current_database); $("#main").show(); - if (!resp.session_lock) { + if (!appFeatures.session_lock) { $(".connection-actions").show(); } - } + }); }); - - bindDatabaseObjectsFilter(); });