FEAT: Connection manager is working

All it can do is view and delete the connections but that was the key
functionality that was missing.
This commit is contained in:
Azpect3120 2024-08-14 12:30:16 -07:00
parent 7e233e53dd
commit 9c8a25609e
7 changed files with 196 additions and 4 deletions

View File

@ -0,0 +1,40 @@
package database
import (
"encoding/json"
"fmt"
"github.com/Azpect3120/Web-Database-Viewer/internal/templates"
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
)
func DeleteConnections(c *gin.Context) {
session := sessions.Default(c)
connections_bytes, ok := session.Get("connections").([]byte)
if !ok {
fmt.Println("No connections found")
}
var connections map[string]string
if err := json.Unmarshal(connections_bytes, &connections); err != nil {
fmt.Println(err)
}
for _, conn := range c.PostFormArray("connections") {
for name, url := range connections {
if conn == url {
delete(connections, name)
}
}
}
connections_bytes, err := json.Marshal(connections)
if err != nil {
fmt.Println(err)
}
session.Set("connections", connections_bytes)
session.Save()
c.String(200, templates.MANAGER_CLOSED)
}

View File

@ -50,6 +50,7 @@ func populate(web, api *gin.RouterGroup) {
"status": 200,
})
})
api.POST("/connections/delete", database.DeleteConnections)
web.GET("/connections", func(c *gin.Context) {
session := sessions.Default(c)
connections_bytes, conn_ok := session.Get("connections").([]byte)
@ -73,7 +74,9 @@ func populate(web, api *gin.RouterGroup) {
c.String(200, database.TableTree(c))
})
// This should return an HTML template which will be used to auto or not
// auto send the query to the server.
web.GET("/query/auto", templates.ToggleQueryType)
web.GET("/manager/open", templates.OpenManager)
web.GET("/manager/hide", templates.HideManager)
}

View File

@ -3,7 +3,7 @@ package templates
import "fmt"
// List item templates
const LIST_OPEN string = `<select id="connected-database" name="connected-database" 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">`
const LIST_OPEN string = `<select id="connected-database" name="connected-database" 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" title="Change the connection to query.">`
const LIST_ITEM string = `<option value="%s"%s>%s</option>`
const LIST_CLOSE string = `</select>`

View File

@ -0,0 +1,88 @@
package templates
import (
"encoding/json"
"fmt"
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
)
const MANAGER string = `
<div id="manager-modal" class="fixed inset-0 bg-gray-600 bg-opacity-50">
<div class="flex items-center justify-center min-h-screen">
<form hx-post="/v1/api/connections/delete" hx-swap="outerHTML" hx-target="#manager-modal" hx-trigger="submit" class="bg-white p-6 rounded-lg shadow-lg w-2/3">
<div class="flex justify-between items-start border-b pb-2">
<h2 class="text-xl font-bold">
Manage Stored Connections
<br>
<span class="text-xs font-light">Connection data is stored in the browsers session and can be deleted here.</span>
</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">
<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 w-full max-w-full overflow-x-auto">
<table>
<thead class="bg-gray-50">
<tr>
<th scope="col" class="px-6 py-3 text-center text-xs font-medium text-gray-500 uppercase tracking-wider">Delete</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase jracking-wider">Driver</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">URL</th>
</tr>
</thead>
<tbody class="w-full">
%s
</tbody>
</table>
</div>
<div class="flex items-center space-x-4 mt-4 border-t pt-4">
<button type="submit" class="bg-blue-500 text-white px-4 py-2 rounded-md"> Save Changes </button>
</div>
</form>
</div>
</div>
`
const MANAGER_ENTRY string = `
<tr class="overflow-x-auto">
<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">
</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>
`
const MANAGER_CLOSED string = `
<div id="manager-modal" class="fixed inset-0 bg-gray-600 bg-opacity-50 hidden opacity-0"></div>
`
func OpenManager(c *gin.Context) {
session := sessions.Default(c)
connections_bytes, ok := session.Get("connections").([]byte)
if !ok {
fmt.Println("No connections found")
}
var connections map[string]string
if err := json.Unmarshal(connections_bytes, &connections); err != nil {
fmt.Println(err)
}
var entries string
for name, url := range connections {
entries += fmt.Sprintf(MANAGER_ENTRY, url, name, "PostgreSQL", url)
}
c.String(200, fmt.Sprintf(MANAGER, entries))
}
func HideManager(c *gin.Context) {
c.String(200, MANAGER_CLOSED)
}

View File

@ -75,3 +75,5 @@ for (const key in input) {
})
}
}

View File

@ -710,10 +710,27 @@ video {
width: 100%;
}
.w-fit {
width: -moz-fit-content;
width: fit-content;
}
.min-w-full {
min-width: 100%;
}
.max-w-\[50\%\] {
max-width: 50%;
}
.max-w-\[50\%\%\] {
max-width: 50%%;
}
.max-w-full {
max-width: 100%;
}
.flex-grow {
flex-grow: 1;
}
@ -738,6 +755,10 @@ video {
flex-wrap: wrap;
}
.items-start {
align-items: flex-start;
}
.items-center {
align-items: center;
}
@ -803,6 +824,10 @@ video {
overflow-x: auto;
}
.overflow-x-hidden {
overflow-x: hidden;
}
.overflow-y-hidden {
overflow-y: hidden;
}
@ -840,6 +865,10 @@ video {
border-bottom-width: 1px;
}
.border-t {
border-top-width: 1px;
}
.border-gray-300 {
--tw-border-opacity: 1;
border-color: rgb(209 213 219 / var(--tw-border-opacity));
@ -885,6 +914,21 @@ video {
background-color: rgb(234 179 8 / var(--tw-bg-opacity));
}
.bg-red-500 {
--tw-bg-opacity: 1;
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 {
--tw-bg-opacity: 0.5;
}
@ -940,10 +984,18 @@ video {
padding-bottom: 0.5rem;
}
.pt-4 {
padding-top: 1rem;
}
.text-left {
text-align: left;
}
.text-center {
text-align: center;
}
.text-2xl {
font-size: 1.5rem;
line-height: 2rem;

View File

@ -32,6 +32,9 @@
<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>
@ -71,7 +74,7 @@
<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" checked>
<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 -->
@ -199,6 +202,10 @@
</div>
</div>
<!-- Manage Connections Modal -->
<div id="manager-modal" class="fixed inset-0 bg-gray-600 bg-opacity-50 hidden opacity-0"></div>
<!-- Scripts -->
<script defer src="/v1/web/static/scripts/password.js"></script>
<script defer src="/v1/web/static/scripts/modal.js"></script>