FEAT: Connection manager changes!
Can now edit the connection names when creating a connection and the connection manager can now edit those names! Much better ease of use.
This commit is contained in:
parent
9c8a25609e
commit
36f7b9fe39
@ -13,8 +13,8 @@ import (
|
|||||||
// in the session.
|
// in the session.
|
||||||
func CreateConnection(c *gin.Context) {
|
func CreateConnection(c *gin.Context) {
|
||||||
var (
|
var (
|
||||||
url string = c.PostForm("db-url")
|
url string = c.PostForm("db-url")
|
||||||
database string = c.PostForm("db-database")
|
name string = c.PostForm("db-conn-name")
|
||||||
)
|
)
|
||||||
|
|
||||||
session := sessions.Default(c)
|
session := sessions.Default(c)
|
||||||
@ -30,7 +30,7 @@ func CreateConnection(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
connections[database] = url
|
connections[name] = url
|
||||||
|
|
||||||
conn_bytes, err := json.Marshal(connections)
|
conn_bytes, err := json.Marshal(connections)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -38,9 +38,9 @@ func CreateConnection(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
session.Set("connections", []byte(conn_bytes))
|
session.Set("connections", []byte(conn_bytes))
|
||||||
session.Set("current", database)
|
session.Set("current", name)
|
||||||
session.Save()
|
session.Save()
|
||||||
|
|
||||||
html := templates.ConnectionsList(connections, database)
|
html := templates.ConnectionsList(connections, name)
|
||||||
c.String(200, html)
|
c.String(200, html)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
func DeleteConnections(c *gin.Context) {
|
func DeleteConnections(c *gin.Context) {
|
||||||
session := sessions.Default(c)
|
session := sessions.Default(c)
|
||||||
|
current, ok := session.Get("current").(string)
|
||||||
connections_bytes, ok := session.Get("connections").([]byte)
|
connections_bytes, ok := session.Get("connections").([]byte)
|
||||||
if !ok {
|
if !ok {
|
||||||
fmt.Println("No connections found")
|
fmt.Println("No connections found")
|
||||||
@ -29,6 +30,21 @@ func DeleteConnections(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for name, url := range connections {
|
||||||
|
newName := c.PostForm(url)
|
||||||
|
|
||||||
|
if name == newName {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(connections, name)
|
||||||
|
connections[newName] = url
|
||||||
|
|
||||||
|
if name == current {
|
||||||
|
session.Set("current", newName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
connections_bytes, err := json.Marshal(connections)
|
connections_bytes, err := json.Marshal(connections)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
|
|||||||
@ -16,7 +16,7 @@ const MANAGER string = `
|
|||||||
<h2 class="text-xl font-bold">
|
<h2 class="text-xl font-bold">
|
||||||
Manage Stored Connections
|
Manage Stored Connections
|
||||||
<br>
|
<br>
|
||||||
<span class="text-xs font-light">Connection data is stored in the browsers session and can be deleted here.</span>
|
<span class="text-xs font-light">Connection data is stored in the browsers session and can be renamed or deleted here.</span>
|
||||||
</h2>
|
</h2>
|
||||||
<button hx-get="/v1/web/manager/hide" hx-trigger="click" hx-swap="outerHTML" hx-target="#manager-modal" class="text-gray-500 hover:text-gray-700">
|
<button hx-get="/v1/web/manager/hide" hx-trigger="click" hx-swap="outerHTML" hx-target="#manager-modal" class="text-gray-500 hover:text-gray-700">
|
||||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
||||||
@ -51,16 +51,18 @@ const MANAGER string = `
|
|||||||
const MANAGER_ENTRY string = `
|
const MANAGER_ENTRY string = `
|
||||||
<tr class="overflow-x-auto">
|
<tr class="overflow-x-auto">
|
||||||
<td class="px-6 py-4 whitespace-nowrap flex items-center justify-center">
|
<td class="px-6 py-4 whitespace-nowrap flex items-center justify-center">
|
||||||
<input type="checkbox" name="connections" value="%s" class="w-4 h-4">
|
<input type="checkbox" name="connections" value="%s" class="w-4 h-4 m-2">
|
||||||
|
</td>
|
||||||
|
<td class="px-6 py-4 whitespace-nowrap">
|
||||||
|
<input type="text" name="%s" value="%s" class="p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm">
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap">%s</td>
|
|
||||||
<td class="px-6 py-4 whitespace-nowrap">%s</td>
|
<td class="px-6 py-4 whitespace-nowrap">%s</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap">%s</td>
|
<td class="px-6 py-4 whitespace-nowrap">%s</td>
|
||||||
</tr>
|
</tr>
|
||||||
`
|
`
|
||||||
|
|
||||||
const MANAGER_CLOSED string = `
|
const MANAGER_CLOSED string = `
|
||||||
<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" hx-get="/v1/web/connections" hx-trigger="load" hx-swap="outerHTML" hx-target="#connected-database" hx-params="none"></div>
|
||||||
`
|
`
|
||||||
|
|
||||||
func OpenManager(c *gin.Context) {
|
func OpenManager(c *gin.Context) {
|
||||||
@ -77,7 +79,7 @@ func OpenManager(c *gin.Context) {
|
|||||||
|
|
||||||
var entries string
|
var entries string
|
||||||
for name, url := range connections {
|
for name, url := range connections {
|
||||||
entries += fmt.Sprintf(MANAGER_ENTRY, url, name, "PostgreSQL", url)
|
entries += fmt.Sprintf(MANAGER_ENTRY, url, url, name, "PostgreSQL", url)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.String(200, fmt.Sprintf(MANAGER, entries))
|
c.String(200, fmt.Sprintf(MANAGER, entries))
|
||||||
|
|||||||
@ -16,6 +16,11 @@ function ShowModal() {
|
|||||||
function HideModal() {
|
function HideModal() {
|
||||||
modal.classList.add("hidden");
|
modal.classList.add("hidden");
|
||||||
modal.classList.add("opacity-0");
|
modal.classList.add("opacity-0");
|
||||||
|
|
||||||
|
// Clear the inputs
|
||||||
|
for (const key in input) {
|
||||||
|
input[key].value = "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const input = {
|
const input = {
|
||||||
@ -25,6 +30,7 @@ const input = {
|
|||||||
username: document.getElementById("db-username"),
|
username: document.getElementById("db-username"),
|
||||||
password: document.getElementById("db-password"),
|
password: document.getElementById("db-password"),
|
||||||
database: document.getElementById("db-database"),
|
database: document.getElementById("db-database"),
|
||||||
|
name: document.getElementById("db-conn-name"),
|
||||||
connectionURL: document.getElementById("db-url")
|
connectionURL: document.getElementById("db-url")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,10 +76,17 @@ for (const key in input) {
|
|||||||
ParseURL(input);
|
ParseURL(input);
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
input[key].addEventListener("input", () => {
|
// If the input changed is the database name, update the connection name as well.
|
||||||
GenerateURL(input);
|
// This will create a default connection name based on the database name.
|
||||||
})
|
if (key == "database") {
|
||||||
|
input[key].addEventListener("input", () => {
|
||||||
|
GenerateURL(input);
|
||||||
|
input.name.value = input[key].value;
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
input[key].addEventListener("input", () => {
|
||||||
|
GenerateURL(input);
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -583,6 +583,10 @@ video {
|
|||||||
grid-column: span 2 / span 2;
|
grid-column: span 2 / span 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.m-2 {
|
||||||
|
margin: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
.mx-1 {
|
.mx-1 {
|
||||||
margin-left: 0.25rem;
|
margin-left: 0.25rem;
|
||||||
margin-right: 0.25rem;
|
margin-right: 0.25rem;
|
||||||
@ -670,6 +674,11 @@ video {
|
|||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.h-fit {
|
||||||
|
height: -moz-fit-content;
|
||||||
|
height: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
.max-h-full {
|
.max-h-full {
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
}
|
}
|
||||||
@ -710,23 +719,10 @@ video {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.w-fit {
|
|
||||||
width: -moz-fit-content;
|
|
||||||
width: fit-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
.min-w-full {
|
.min-w-full {
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.max-w-\[50\%\] {
|
|
||||||
max-width: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.max-w-\[50\%\%\] {
|
|
||||||
max-width: 50%%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.max-w-full {
|
.max-w-full {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
@ -824,10 +820,6 @@ video {
|
|||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overflow-x-hidden {
|
|
||||||
overflow-x: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.overflow-y-hidden {
|
.overflow-y-hidden {
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
}
|
}
|
||||||
@ -919,16 +911,6 @@ video {
|
|||||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-red-600 {
|
|
||||||
--tw-bg-opacity: 1;
|
|
||||||
background-color: rgb(220 38 38 / var(--tw-bg-opacity));
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-red-300 {
|
|
||||||
--tw-bg-opacity: 1;
|
|
||||||
background-color: rgb(252 165 165 / var(--tw-bg-opacity));
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-opacity-50 {
|
.bg-opacity-50 {
|
||||||
--tw-bg-opacity: 0.5;
|
--tw-bg-opacity: 0.5;
|
||||||
}
|
}
|
||||||
@ -945,6 +927,10 @@ video {
|
|||||||
padding: 1.5rem;
|
padding: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.p-1 {
|
||||||
|
padding: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
.px-1 {
|
.px-1 {
|
||||||
padding-left: 0.25rem;
|
padding-left: 0.25rem;
|
||||||
padding-right: 0.25rem;
|
padding-right: 0.25rem;
|
||||||
|
|||||||
@ -1,214 +1,230 @@
|
|||||||
<!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">
|
||||||
<style>
|
<style>
|
||||||
.htmx-request {
|
.htmx-request {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</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 -->
|
||||||
<div class="bg-white shadow-md p-4 flex items-center justify-between border-b">
|
<div class="bg-white shadow-md p-4 flex items-center justify-between border-b">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-2xl font-bold">Database Query Tool</h1>
|
<h1 class="text-2xl font-bold">Database Query Tool</h1>
|
||||||
<p class="text-sm text-gray-600">Connect and query your databases effortlessly.</p>
|
<p class="text-sm text-gray-600">Connect and query your databases effortlessly.</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-end space-x-4 flex-wrap">
|
||||||
|
<form hx-post="/v1/api/connections/connect" hx-trigger="change" hx-swap="outerHTML"
|
||||||
|
hx-target="#connected-database" hx-indicator="#table-loading" hx-encoding="multipart/form-data"
|
||||||
|
class="flex items-center justify-end space-x-2 flex-wrap">
|
||||||
|
<label for="connected-database" class="block text-sm font-medium text-gray-700">Connected Database:</label>
|
||||||
|
<select hx-get="/v1/web/connections" hx-trigger="load, change" hx-swap="outerHTML" id="connected-database"
|
||||||
|
name="connected-database" hx-params="none"
|
||||||
|
class="mt-1 block p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 text-sm md:text-base"></select>
|
||||||
|
</form>
|
||||||
|
<button onclick="ShowModal();" class="bg-blue-500 text-white px-4 py-2 my-2 rounded-md text-sm md:text-base">
|
||||||
|
Add Connection
|
||||||
|
</button>
|
||||||
|
<button hx-get="/v1/web/manager/open" hx-trigger="click" hx-target="#manager-modal" hx-swap="outerHTML"
|
||||||
|
class="bg-blue-500 text-white px-4 py-2 my-2 rounded-md text-sm md:text-base">
|
||||||
|
Manage Connections
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col md:flex-row flex-grow">
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<div class="w-full md:w-1/4 bg-white shadow-md">
|
||||||
|
<div class="p-4 border-b flex justify-between items-center">
|
||||||
|
<h2 class="text-lg font-bold">
|
||||||
|
<span id="database-name-tree">database</span>
|
||||||
|
<span id="table-loading" class="text-sm font-normal mx-1 htmx-indicator duration-300">loading
|
||||||
|
tables...</span>
|
||||||
|
</h2>
|
||||||
|
<button hx-get="/v1/web/connections/tree" hx-trigger="click" hx-target="#database-table-tree"
|
||||||
|
class="hover:bg-gray-100 p-2 rounded-md" hx-indicator="#table-loading">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="ionicon h-4 w-4" viewBox="0 0 512 512">
|
||||||
|
<path
|
||||||
|
d="M400 148l-21.12-24.57A191.43 191.43 0 00240 64C134 64 48 150 48 256s86 192 192 192a192.09 192.09 0 00181.07-128"
|
||||||
|
fill="none" stroke="currentColor" stroke-linecap="round" stroke-miterlimit="10" stroke-width="32" />
|
||||||
|
<path
|
||||||
|
d="M464 97.42V208a16 16 0 01-16 16H337.42c-14.26 0-21.4-17.23-11.32-27.31L436.69 86.1C446.77 76 464 83.16 464 97.42z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center justify-end space-x-4 flex-wrap">
|
<div class="p-4 max-h-full" hx-get="/v1/web/connections/tree" hx-trigger="load" hx-params="none"
|
||||||
<form hx-post="/v1/api/connections/connect" hx-trigger="change" hx-swap="outerHTML" hx-target="#connected-database" hx-indicator="#table-loading" hx-encoding="multipart/form-data" class="flex items-center justify-end space-x-2 flex-wrap">
|
hx-indicator="#table-loading" hx-target="#database-table-tree">
|
||||||
<label for="connected-database" class="block text-sm font-medium text-gray-700">Connected Database:</label>
|
<ul hx-swap-oob="outerHTML" id="database-table-tree" class="space-y-2"></ul>
|
||||||
<select hx-get="/v1/web/connections" hx-trigger="load, change" hx-swap="outerHTML" id="connected-database" name="connected-database" hx-params="none" class="mt-1 block p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 text-sm md:text-base"></select>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<div class="w-full md:w-3/4 p-4">
|
||||||
|
<main>
|
||||||
|
<!-- Query Input -->
|
||||||
|
<div id="query-main" class="mb-4">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<label for="sql" class="block text-sm font-medium text-gray-700">SQL Query</label>
|
||||||
|
<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"
|
||||||
|
hx-target="#query-main" hx-trigger="input">
|
||||||
|
<label for="auto-toggle" class="text-sm font-medium text-gray-700">Auto-Run</label>
|
||||||
|
<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.">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<!-- Manual Query Button -->
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<!-- 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> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Manual 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"></textarea>
|
||||||
|
|
||||||
|
<!-- 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> -->
|
||||||
|
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Query Results -->
|
||||||
|
<div class="overflow-x-auto overflow-y-hidden bg-white shadow-md rounded-lg">
|
||||||
|
<table id="query-result" class="min-w-full divide-y divide-gray-200"></table>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Create Connection Modal -->
|
||||||
|
<div id="connection-modal" class="fixed inset-0 bg-gray-600 bg-opacity-50 hidden opacity-0">
|
||||||
|
<div class="flex items-center justify-center min-h-screen">
|
||||||
|
<div class="bg-white p-6 rounded-lg shadow-lg w-2/3">
|
||||||
|
<div class="flex justify-between items-center border-b pb-2">
|
||||||
|
<h2 class="text-xl font-bold">Add New Connection</h2>
|
||||||
|
<button onclick="HideModal();" class="text-gray-500 hover:text-gray-700">
|
||||||
|
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
||||||
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="mt-4">
|
||||||
|
<form id="connection-form" class="grid grid-cols-2 gap-4">
|
||||||
|
<div>
|
||||||
|
<label for="db-host" class="block text-sm font-medium text-gray-700">Host</label>
|
||||||
|
<input id="db-host" name="db-host" type="text" placeholder="127.0.0.1" 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">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="db-port" class="block text-sm font-medium text-gray-700">Port</label>
|
||||||
|
<input id="db-port" name="db-port" type="text" placeholder="5432" 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">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="db-username" class="block text-sm font-medium text-gray-700">Username</label>
|
||||||
|
<input id="db-username" name="db-username" type="text" placeholder="admin" 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">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="db-password" class="block text-sm font-medium text-gray-700">Password</label>
|
||||||
|
<div class="relative mt-1">
|
||||||
|
<input id="db-password" name="db-password" type="password" placeholder="●●●●●●●●●" class="block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm">
|
||||||
|
<button type="button" id="togglePassword" class="absolute inset-y-0 right-0 px-3 py-2 text-gray-500 bg-gray-200 rounded-r-md border border-gray-300" title="Display secret details">
|
||||||
|
<svg id="eyeIcon" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="0 0 24 24" fill="none"
|
||||||
|
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M1 12s3.5-7 11-7 11 7 11 7-3.5 7-11 7S1 12 1 12z"></path>
|
||||||
|
<circle cx="12" cy="12" r="3"></circle>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="db-driver" class="block text-sm font-medium text-gray-700">Driver/Type of Database</label>
|
||||||
|
<select id="db-driver" name="db-driver"
|
||||||
|
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">
|
||||||
|
<option value="postgresql">PostgreSQL</option>
|
||||||
|
<option value="mysql">MySQL</option>
|
||||||
|
<option value="sqlite">SQLite</option>
|
||||||
|
<option value="sqlserver">SQL Server</option>
|
||||||
|
<option value="oracle">Oracle</option>
|
||||||
|
<option value="mariadb">MariaDB</option>
|
||||||
|
<option value="db2">DB2</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="db-database" class="block text-sm font-medium text-gray-700">Database Name</label>
|
||||||
|
<input id="db-database" name="db-database" type="text" placeholder="master_database" 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">
|
||||||
|
</div>
|
||||||
|
<div class="col-span-2">
|
||||||
|
<label for="db-url" class="block text-sm font-medium text-gray-700">
|
||||||
|
Connection Name
|
||||||
|
<br>
|
||||||
|
<span class="text-xs font-light">
|
||||||
|
This is the name of the connection that will be displayed in the list of connections. By default it
|
||||||
|
will match the database name.
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<input name="db-conn-name" id="db-conn-name" placeholder="master_database" 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">
|
||||||
|
</div>
|
||||||
|
<div class="col-span-2">
|
||||||
|
<label for="db-url" class="block text-sm font-medium text-gray-700">
|
||||||
|
Connection URL
|
||||||
|
<br>
|
||||||
|
<span class="text-xs font-light">
|
||||||
|
The connection URL will be automatically generated based on the above fields. To view the URL
|
||||||
|
generated,
|
||||||
|
push the "display secret details" button in the password section.
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<input id="db-url" name="db-url" type="password"
|
||||||
|
placeholder="postgresql://admin:password@127.0.0.1:5432/master_database"
|
||||||
|
value="postgresql://azpect:Panther4487!!!!@127.0.0.1:5432/azpect?sslmode=disable"
|
||||||
|
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">
|
||||||
|
<span id="db-url-invalid" class="text-xs text-red-500 hidden">Connection URL is incomplete or
|
||||||
|
invalid.</span>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<button onclick="ShowModal();" class="bg-blue-500 text-white px-4 py-2 my-2 rounded-md text-sm md:text-base">
|
<div class="flex items-center space-x-4 mt-4">
|
||||||
Add Connection
|
<button hx-post="/v1/api/connections" hx-trigger="click" hx-target="#connected-database" hx-swap="outerHTML"
|
||||||
</button>
|
hx-include="#connection-form" onclick="HideModal();" class="bg-blue-500 text-white px-4 py-2 rounded-md">
|
||||||
<button hx-get="/v1/web/manager/open" hx-trigger="click" hx-target="#manager-modal" hx-swap="outerHTML" class="bg-blue-500 text-white px-4 py-2 my-2 rounded-md text-sm md:text-base">
|
Create Connection
|
||||||
Manage Connections
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-col md:flex-row flex-grow">
|
|
||||||
<!-- Sidebar -->
|
|
||||||
<div class="w-full md:w-1/4 bg-white shadow-md">
|
|
||||||
<div class="p-4 border-b flex justify-between items-center">
|
|
||||||
<h2 class="text-lg font-bold">
|
|
||||||
<span id="database-name-tree">database</span>
|
|
||||||
<span id="table-loading" class="text-sm font-normal mx-1 htmx-indicator duration-300">loading tables...</span>
|
|
||||||
</h2>
|
|
||||||
<button
|
|
||||||
hx-get="/v1/web/connections/tree"
|
|
||||||
hx-trigger="click"
|
|
||||||
hx-target="#database-table-tree"
|
|
||||||
class="hover:bg-gray-100 p-2 rounded-md"
|
|
||||||
hx-indicator="#table-loading"
|
|
||||||
>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="ionicon h-4 w-4" viewBox="0 0 512 512">
|
|
||||||
<path d="M400 148l-21.12-24.57A191.43 191.43 0 00240 64C134 64 48 150 48 256s86 192 192 192a192.09 192.09 0 00181.07-128" fill="none" stroke="currentColor" stroke-linecap="round" stroke-miterlimit="10" stroke-width="32"/>
|
|
||||||
<path d="M464 97.42V208a16 16 0 01-16 16H337.42c-14.26 0-21.4-17.23-11.32-27.31L436.69 86.1C446.77 76 464 83.16 464 97.42z"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
<button hx-post="/v1/api/connections/test" hx-trigger="click" hx-swap="outerHTML"
|
||||||
<div class="p-4 max-h-full" hx-get="/v1/web/connections/tree" hx-trigger="load" hx-params="none" hx-indicator="#table-loading" hx-target="#database-table-tree">
|
hx-target="#connection-status" hx-include="#connection-form" hx-encoding="multipart/form-data"
|
||||||
<ul hx-swap-oob="outerHTML" id="database-table-tree" class="space-y-2"></ul>
|
class="bg-gray-200 text-gray-700 px-4 py-2 rounded-md flex items-center space-x-2">
|
||||||
</div>
|
<span>Test Connection</span>
|
||||||
</div>
|
<span id="connection-status" class="w-3 h-3 rounded-full bg-gray-400"></span>
|
||||||
|
|
||||||
<!-- Main Content -->
|
|
||||||
<div class="w-full md:w-3/4 p-4">
|
|
||||||
<main>
|
|
||||||
<!-- Query Input -->
|
|
||||||
<div id="query-main" class="mb-4">
|
|
||||||
<div class="flex items-center justify-between">
|
|
||||||
<label for="sql" class="block text-sm font-medium text-gray-700">SQL Query</label>
|
|
||||||
<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" hx-target="#query-main" hx-trigger="input">
|
|
||||||
<label for="auto-toggle" class="text-sm font-medium text-gray-700">Auto-Run</label>
|
|
||||||
<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.">
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<!-- Manual Query Button -->
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<!-- 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> -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Manual 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"></textarea>
|
|
||||||
|
|
||||||
<!-- 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> -->
|
|
||||||
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Query Results -->
|
|
||||||
<div class="overflow-x-auto overflow-y-hidden bg-white shadow-md rounded-lg">
|
|
||||||
<table id="query-result" class="min-w-full divide-y divide-gray-200"></table>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Create Connection Modal -->
|
|
||||||
<div id="connection-modal" class="fixed inset-0 bg-gray-600 bg-opacity-50 hidden opacity-0">
|
|
||||||
<div class="flex items-center justify-center min-h-screen">
|
|
||||||
<div class="bg-white p-6 rounded-lg shadow-lg w-2/3">
|
|
||||||
<div class="flex justify-between items-center border-b pb-2">
|
|
||||||
<h2 class="text-xl font-bold">Add New Connection</h2>
|
|
||||||
<button onclick="HideModal();" class="text-gray-500 hover:text-gray-700">
|
|
||||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
<span id="connection-message"></span>
|
||||||
<div class="mt-4">
|
|
||||||
<form id="connection-form" class="grid grid-cols-2 gap-4">
|
|
||||||
<div>
|
|
||||||
<label for="db-host" class="block text-sm font-medium text-gray-700">Host</label>
|
|
||||||
<input id="db-host" name="db-host" type="text" placeholder="127.0.0.1" value="127.0.0.1"
|
|
||||||
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">
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label for="db-port" class="block text-sm font-medium text-gray-700">Port</label>
|
|
||||||
<input id="db-port" name="db-port" type="text" placeholder="5432" value="5432"
|
|
||||||
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">
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label for="db-username" class="block text-sm font-medium text-gray-700">Username</label>
|
|
||||||
<input id="db-username" name="db-username" type="text" placeholder="admin" value="azpect"
|
|
||||||
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">
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label for="db-password" class="block text-sm font-medium text-gray-700">Password</label>
|
|
||||||
<div class="relative mt-1">
|
|
||||||
<input id="db-password" name="db-password" type="password" placeholder="●●●●●●●●●"
|
|
||||||
value="Panther4487!!!!"
|
|
||||||
class="block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm">
|
|
||||||
<button type="button" id="togglePassword"
|
|
||||||
class="absolute inset-y-0 right-0 px-3 py-2 text-gray-500 bg-gray-200 rounded-r-md border border-gray-300"
|
|
||||||
title="Display secret details">
|
|
||||||
<svg id="eyeIcon" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="0 0 24 24" fill="none"
|
|
||||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
||||||
<path d="M1 12s3.5-7 11-7 11 7 11 7-3.5 7-11 7S1 12 1 12z"></path>
|
|
||||||
<circle cx="12" cy="12" r="3"></circle>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label for="db-driver" class="block text-sm font-medium text-gray-700">Driver/Type of Database</label>
|
|
||||||
<select id="db-driver" name="db-driver"
|
|
||||||
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">
|
|
||||||
<option value="postgresql">PostgreSQL</option>
|
|
||||||
<option value="mysql">MySQL</option>
|
|
||||||
<option value="sqlite">SQLite</option>
|
|
||||||
<option value="sqlserver">SQL Server</option>
|
|
||||||
<option value="oracle">Oracle</option>
|
|
||||||
<option value="mariadb">MariaDB</option>
|
|
||||||
<option value="db2">DB2</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label for="db-database" class="block text-sm font-medium text-gray-700">Database Name</label>
|
|
||||||
<input id="db-database" name="db-database" type="text" placeholder="master_database" value="azpect"
|
|
||||||
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">
|
|
||||||
</div>
|
|
||||||
<div class="col-span-2">
|
|
||||||
<label for="db-url" class="block text-sm font-medium text-gray-700">
|
|
||||||
Connection URL
|
|
||||||
<br>
|
|
||||||
<span class="text-xs font-light">
|
|
||||||
The connection URL will be automatically generated based on the above fields. To view the URL
|
|
||||||
generated,
|
|
||||||
push the "display secret details" button in the password section.
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
<input id="db-url" name="db-url" type="password"
|
|
||||||
placeholder="postgresql://admin:password@127.0.0.1:5432/master_database"
|
|
||||||
value="postgresql://azpect:Panther4487!!!!@127.0.0.1:5432/azpect?sslmode=disable"
|
|
||||||
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">
|
|
||||||
<span id="db-url-invalid" class="text-xs text-red-500 hidden">Connection URL is incomplete or
|
|
||||||
invalid.</span>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<div class="flex items-center space-x-4 mt-4">
|
|
||||||
<button hx-post="/v1/api/connections" hx-trigger="click" hx-target="#connected-database" hx-swap="outerHTML" hx-include="#connection-form" onclick="HideModal();" class="bg-blue-500 text-white px-4 py-2 rounded-md">
|
|
||||||
Create Connection
|
|
||||||
</button>
|
|
||||||
<button hx-post="/v1/api/connections/test" hx-trigger="click" hx-swap="outerHTML" hx-target="#connection-status" hx-include="#connection-form" hx-encoding="multipart/form-data" class="bg-gray-200 text-gray-700 px-4 py-2 rounded-md flex items-center space-x-2">
|
|
||||||
<span>Test Connection</span>
|
|
||||||
<span id="connection-status" class="w-3 h-3 rounded-full bg-gray-400"></span>
|
|
||||||
</button>
|
|
||||||
<span id="connection-message"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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>
|
||||||
|
|
||||||
<!-- Scripts -->
|
<!-- Scripts -->
|
||||||
<script defer src="/v1/web/static/scripts/password.js"></script>
|
<script src="/v1/web/static/scripts/password.js"></script>
|
||||||
<script defer src="/v1/web/static/scripts/modal.js"></script>
|
<script src="/v1/web/static/scripts/modal.js"></script>
|
||||||
<script defer 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