FEAT: Multi query support!
Queries will be split by the ';' and ran at the same time! They will each have their own table display and info.
This commit is contained in:
parent
da3ad813be
commit
3382a1537b
BIN
bin/web_server
BIN
bin/web_server
Binary file not shown.
@ -4,6 +4,7 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/Azpect3120/Web-Database-Viewer/internal/templates"
|
"github.com/Azpect3120/Web-Database-Viewer/internal/templates"
|
||||||
"github.com/gin-contrib/sessions"
|
"github.com/gin-contrib/sessions"
|
||||||
@ -16,13 +17,19 @@ func QueryCurrent(c *gin.Context) {
|
|||||||
query := c.PostForm("sql")
|
query := c.PostForm("sql")
|
||||||
conn := getConnection(c)
|
conn := getConnection(c)
|
||||||
|
|
||||||
|
queries := strings.Split(query, ";")
|
||||||
|
var results []string
|
||||||
|
for _, query := range queries {
|
||||||
cols, data, err := queryConnection(query, conn)
|
cols, data, err := queryConnection(query, conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.String(200, templates.ErrorQueryResults(err))
|
c.String(200, templates.ErrorQueryResults(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.String(200, templates.QueryResults(cols, data))
|
results = append(results, templates.QueryResult(cols, data))
|
||||||
|
}
|
||||||
|
|
||||||
|
c.String(200, templates.ConcatResults(results))
|
||||||
}
|
}
|
||||||
|
|
||||||
func queryConnection(query, url string) ([]string, []map[string]interface{}, error) {
|
func queryConnection(query, url string) ([]string, []map[string]interface{}, error) {
|
||||||
|
|||||||
@ -13,7 +13,7 @@ const MANUAL_QUERY string = `
|
|||||||
<label for="auto-toggle" class="text-sm font-medium text-gray-700">Auto-Run</label>
|
<label for="auto-toggle" class="text-sm font-medium text-gray-700">Auto-Run</label>
|
||||||
<input type="checkbox" name="toggle" class="toggle-checkbox">
|
<input type="checkbox" name="toggle" class="toggle-checkbox">
|
||||||
</form>
|
</form>
|
||||||
<button hx-post="/v1/api/query" hx-trigger="click" hx-swap="outerHTML" hx-target="#query-result" hx-indicator="#spinner" hx-include="#sql" class="bg-blue-500 text-white py-2 px-3 rounded-md text-xs md:text-sm">Run Query</button>
|
<button hx-post="/v1/api/query" hx-trigger="click" hx-swap="outerHTML" hx-target="#query-results" hx-indicator="#spinner" hx-include="#sql" class="bg-blue-500 text-white py-2 px-3 rounded-md text-xs md:text-sm">Run Query</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<textarea id="sql" name="sql" rows="4" class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"></textarea>
|
<textarea id="sql" name="sql" rows="4" class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"></textarea>
|
||||||
@ -34,7 +34,7 @@ const AUTO_QUERY string = `
|
|||||||
<button class="bg-blue-500 text-white py-2 px-3 rounded-md text-xs md:text-sm opacity-60 cursor-default" title="Disable Auto-Run to use manual queries." disabled>Run Query</button>
|
<button class="bg-blue-500 text-white py-2 px-3 rounded-md text-xs md:text-sm opacity-60 cursor-default" title="Disable Auto-Run to use manual queries." disabled>Run Query</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<textarea id="sql" name="sql" rows="4" class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm" hx-post="/v1/api/query" hx-trigger="input delay:500ms" hx-swap="outerHTML" hx-target="#query-result" hx-indicator="#spinner"></textarea>
|
<textarea id="sql" name="sql" rows="4" class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm" hx-post="/v1/api/query" hx-trigger="input delay:500ms" hx-swap="outerHTML" hx-target="#query-results" hx-indicator="#spinner"></textarea>
|
||||||
<p id="spinner" class="text-gray-700 text-sm px-1 py-2 htmx-indicator hidden"> Query running... </p>
|
<p id="spinner" class="text-gray-700 text-sm px-1 py-2 htmx-indicator hidden"> Query running... </p>
|
||||||
<p id="query-error" hx-swap-oob="outerHTML" class="text-red-500 py-2 text-sm hidden"></p>
|
<p id="query-error" hx-swap-oob="outerHTML" class="text-red-500 py-2 text-sm hidden"></p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -4,8 +4,19 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Result list definition
|
||||||
|
const result_list_open string = `<ul id="query-results">`
|
||||||
|
const result_list_close string = `</ul>`
|
||||||
|
|
||||||
|
// Result item definition
|
||||||
|
const result_item string = `
|
||||||
|
<li class="overflow-x-auto overflow-y-hidden bg-white rounded-lg shadow-md mb-8">
|
||||||
|
%s
|
||||||
|
</li>
|
||||||
|
`
|
||||||
|
|
||||||
// Table wrapper definitions
|
// Table wrapper definitions
|
||||||
const table_open string = `<table id="query-result" class="min-w-full divide-y divide-gray-200">`
|
const table_open string = `<table class="min-w-full divide-y divide-gray-200">`
|
||||||
const table_close string = `</table>`
|
const table_close string = `</table>`
|
||||||
|
|
||||||
// Header definitions
|
// Header definitions
|
||||||
@ -23,10 +34,20 @@ const query_error_message string = `<p id="query-error" hx-swap-oob="outerHTML"
|
|||||||
const query_error_message_blank string = `<p id="query-error" hx-swap-oob="outerHTML" class="text-red-500 py-2 text-sm hidden"></p>`
|
const query_error_message_blank string = `<p id="query-error" hx-swap-oob="outerHTML" class="text-red-500 py-2 text-sm hidden"></p>`
|
||||||
|
|
||||||
func ErrorQueryResults(e error) string {
|
func ErrorQueryResults(e error) string {
|
||||||
return table_open + table_close + fmt.Sprintf(query_error_message, e.Error())
|
return result_list_open + result_list_close + fmt.Sprintf(query_error_message, e.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
func QueryResults(cols []string, rows []map[string]interface{}) string {
|
func ConcatResults(items []string) string {
|
||||||
|
var html string = result_list_open
|
||||||
|
|
||||||
|
for _, h := range items {
|
||||||
|
html += h
|
||||||
|
}
|
||||||
|
|
||||||
|
return html + result_list_close
|
||||||
|
}
|
||||||
|
|
||||||
|
func QueryResult(cols []string, rows []map[string]interface{}) string {
|
||||||
head := generateHead(cols)
|
head := generateHead(cols)
|
||||||
|
|
||||||
body := table_body_open
|
body := table_body_open
|
||||||
@ -36,7 +57,7 @@ func QueryResults(cols []string, rows []map[string]interface{}) string {
|
|||||||
|
|
||||||
body += table_body_close
|
body += table_body_close
|
||||||
|
|
||||||
return table_open + head + body + table_close + query_error_message_blank
|
return fmt.Sprintf(result_item, table_open+head+body+table_close+query_error_message_blank)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate the tables head row
|
// Generate the tables head row
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -1,16 +1,16 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Database Query Tool</title>
|
<title>Database Query Tool</title>
|
||||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
|
||||||
<script src="https://unpkg.com/htmx.org@2.0.1"></script>
|
<script src="https://unpkg.com/htmx.org@2.0.1"></script>
|
||||||
<link rel="icon" type="image/png" href="/v1/web/assets/favicon.ico">
|
<link rel="icon" type="image/png" href="/v1/web/assets/favicon.ico">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="bg-gray-100">
|
<body class="bg-gray-100">
|
||||||
|
|
||||||
<div class="flex flex-col h-screen">
|
<div class="flex flex-col h-screen">
|
||||||
<!-- Top Bar -->
|
<!-- Top Bar -->
|
||||||
@ -80,17 +80,13 @@
|
|||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<label for="sql" class="block text-sm font-medium text-gray-700">SQL Query</label>
|
<label for="sql" class="block text-sm font-medium text-gray-700">SQL Query</label>
|
||||||
<div class="flex items-center space-x-6">
|
<div class="flex items-center space-x-6">
|
||||||
<form class="flex items-center space-x-2" hx-get="/v1/web/query/auto" hx-swap="outerHTML"
|
<form class="flex items-center space-x-2" hx-get="/v1/web/query/auto" hx-swap="outerHTML" hx-target="#query-main" hx-trigger="input">
|
||||||
hx-target="#query-main" hx-trigger="input">
|
|
||||||
<label for="auto-toggle" class="text-sm font-medium text-gray-700">Auto-Run</label>
|
<label for="auto-toggle" class="text-sm font-medium text-gray-700">Auto-Run</label>
|
||||||
<input type="checkbox" name="toggle" class="toggle-checkbox"
|
<input type="checkbox" name="toggle" class="toggle-checkbox" title="Toggle auto-query functionality. Note: This will send whatever query is in the input and clear the box.">
|
||||||
title="Toggle auto-query functionality. Note: This will send whatever query is in the input and clear the box.">
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<!-- Manual Query Button -->
|
<!-- Manual Query Button -->
|
||||||
<button hx-post="/v1/api/query" hx-trigger="click" hx-swap="outerHTML" hx-target="#query-result"
|
<button hx-post="/v1/api/query" hx-trigger="click" hx-swap="outerHTML" hx-target="#query-results" hx-indicator="#spinner" hx-include="#sql" class="bg-blue-500 text-white py-2 px-3 rounded-md text-xs md:text-sm">Run Query</button>
|
||||||
hx-indicator="#spinner" hx-include="#sql"
|
|
||||||
class="bg-blue-500 text-white py-2 px-3 rounded-md text-xs md:text-sm">Run Query</button>
|
|
||||||
|
|
||||||
<!-- Auto Query Button -->
|
<!-- Auto Query Button -->
|
||||||
<!-- <button class="bg-blue-500 text-white py-2 px-3 rounded-md text-xs md:text-sm opacity-60 cursor-default" title="Disable Auto-Run to use manual queries." disabled>Run Query</button> -->
|
<!-- <button class="bg-blue-500 text-white py-2 px-3 rounded-md text-xs md:text-sm opacity-60 cursor-default" title="Disable Auto-Run to use manual queries." disabled>Run Query</button> -->
|
||||||
@ -98,20 +94,56 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Manual Query Input -->
|
<!-- Manual Query Input -->
|
||||||
<textarea id="sql" name="sql" rows="4"
|
<textarea id="sql" name="sql" rows="4" class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"></textarea>
|
||||||
class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"></textarea>
|
|
||||||
|
|
||||||
<!-- Auto Query Input -->
|
<!-- Auto Query Input -->
|
||||||
<!-- <textarea id="sql" name="sql" rows="4" class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm" hx-post="/v1/api/query" hx-trigger="input delay:500ms" hx-swap="outerHTML" hx-target="#query-result" hx-indicator="#spinner"></textarea> -->
|
<!-- <textarea id="sql" name="sql" rows="4" class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm" hx-post="/v1/api/query" hx-trigger="input delay:500ms" hx-swap="outerHTML" hx-target="#query-results" hx-indicator="#spinner"></textarea> -->
|
||||||
|
|
||||||
<p id="spinner" class="text-gray-700 text-sm px-1 py-2 htmx-indicator hidden"> Query running... </p>
|
<p id="spinner" class="text-gray-700 text-sm px-1 py-2 htmx-indicator hidden"> Query running... </p>
|
||||||
<p id="query-error" hx-swap-oob="outerHTML" class="text-red-500 py-2 text-sm hidden"></p>
|
<p id="query-error" hx-swap-oob="outerHTML" class="text-red-500 py-2 text-sm hidden"></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Query Results -->
|
<!-- Query Results -->
|
||||||
<div class="overflow-x-auto overflow-y-hidden bg-white shadow-md rounded-lg">
|
<ul id="query-results">
|
||||||
<table id="query-result" class="min-w-full divide-y divide-gray-200"></table>
|
<!-- <li class="overflow-x-auto overflow-y-hidden bg-white rounded-lg shadow-md"> -->
|
||||||
</div>
|
<!-- <table class="min-w-full divide-y divide-gray-200"> -->
|
||||||
|
<!-- <thead class="bg-gray-50"> -->
|
||||||
|
<!-- <tr> -->
|
||||||
|
<!-- <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">id</th> -->
|
||||||
|
<!-- <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">username</th> -->
|
||||||
|
<!-- <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">password</th> -->
|
||||||
|
<!-- <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">secret</th> -->
|
||||||
|
<!-- <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">permission</th> -->
|
||||||
|
<!-- </tr> -->
|
||||||
|
<!-- </thead> -->
|
||||||
|
<!-- <tbody class="bg-white divide-y divide-gray-200"> -->
|
||||||
|
<!-- <tr> -->
|
||||||
|
<!-- <td class="px-6 py-4 whitespace-nowrap">513fbe25-ff69-4386-87c2-90ff6b691169</td> -->
|
||||||
|
<!-- <td class="px-6 py-4 whitespace-nowrap">ConnieH</td> -->
|
||||||
|
<!-- <td class="px-6 py-4 whitespace-nowrap">d1d15c321c523cbcfe2c18f92168cb5b:</td> -->
|
||||||
|
<!-- <td class="px-6 py-4 whitespace-nowrap"><nil></td> -->
|
||||||
|
<!-- <td class="px-6 py-4 whitespace-nowrap">Admin</td> -->
|
||||||
|
<!-- </tr> -->
|
||||||
|
<!-- <tr> -->
|
||||||
|
<!-- <td class="px-6 py-4 whitespace-nowrap">5e02c767-f035-40da-867a-1c067c1678e2</td> -->
|
||||||
|
<!-- <td class="px-6 py-4 whitespace-nowrap">AustinH</td> -->
|
||||||
|
<!-- <td class="px-6 py-4 whitespace-nowrap">588b2205579a4ee48f9014f2742fd1b0:</td> -->
|
||||||
|
<!-- <td class="px-6 py-4 whitespace-nowrap"><nil></td> -->
|
||||||
|
<!-- <td class="px-6 py-4 whitespace-nowrap">User</td> -->
|
||||||
|
<!-- </tr> -->
|
||||||
|
<!-- <tr> -->
|
||||||
|
<!-- <td class="px-6 py-4 whitespace-nowrap">1d007e19-d84b-4cc6-9e6b-1c51122aa3e5</td> -->
|
||||||
|
<!-- <td class="px-6 py-4 whitespace-nowrap">Azpect</td> -->
|
||||||
|
<!-- <td class="px-6 py-4 whitespace-nowrap">a8778398e9afed7f00a88b13ffc14a95:</td> -->
|
||||||
|
<!-- <td class="px-6 py-4 whitespace-nowrap"><nil></td> -->
|
||||||
|
<!-- <td class="px-6 py-4 whitespace-nowrap">Dev</td> -->
|
||||||
|
<!-- </tr> -->
|
||||||
|
<!-- </tbody> -->
|
||||||
|
<!-- </table> -->
|
||||||
|
<!-- <p id="query-error" hx-swap-oob="outerHTML" class="text-red-500 py-2 text-sm hidden"></p> -->
|
||||||
|
<!-- </li> -->
|
||||||
|
</ul>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -230,7 +262,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- Manage Connections Modal -->
|
<!-- Manage Connections Modal -->
|
||||||
<div id="manager-modal" class="fixed inset-0 bg-gray-600 bg-opacity-50 hidden opacity-0"></div>
|
<div id="manager-modal" class="fixed inset-0 bg-gray-600 bg-opacity-50 hidden opacity-0"></div>
|
||||||
|
|
||||||
@ -238,6 +269,6 @@
|
|||||||
<script src="/v1/web/static/scripts/password.js"></script>
|
<script src="/v1/web/static/scripts/password.js"></script>
|
||||||
<script src="/v1/web/static/scripts/modal.js"></script>
|
<script src="/v1/web/static/scripts/modal.js"></script>
|
||||||
<script src="/v1/web/static/scripts/tree.js"></script>
|
<script src="/v1/web/static/scripts/tree.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user