FEAT: Sessions are complete!
Queries are also complete... Needs some bug testing and such but the queries are running and displaying!
This commit is contained in:
parent
111a6aead3
commit
5cbe004318
2
go.mod
2
go.mod
@ -22,7 +22,7 @@ require (
|
|||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
github.com/gorilla/context v1.1.2 // indirect
|
github.com/gorilla/context v1.1.2 // indirect
|
||||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||||
github.com/gorilla/sessions v1.2.2 // indirect
|
github.com/gorilla/sessions v1.3.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/kr/text v0.2.0 // indirect
|
||||||
|
|||||||
2
go.sum
2
go.sum
@ -41,6 +41,8 @@ github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kX
|
|||||||
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
||||||
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY=
|
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY=
|
||||||
github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
|
github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
|
||||||
|
github.com/gorilla/sessions v1.3.0 h1:XYlkq7KcpOB2ZhHBPv5WpjMIxrQosiZanfoy1HLZFzg=
|
||||||
|
github.com/gorilla/sessions v1.3.0/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
|
|||||||
43
internal/database/connect.go
Normal file
43
internal/database/connect.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
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 ChangeConnection(c *gin.Context) {
|
||||||
|
conn := c.PostForm("connected-database")
|
||||||
|
|
||||||
|
// Do something to change the connection
|
||||||
|
|
||||||
|
session := sessions.Default(c)
|
||||||
|
conn_bytes, ok := session.Get("connections").([]byte)
|
||||||
|
if !ok {
|
||||||
|
c.String(200, templates.ConnectionsList(nil, ""))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var connections map[string]string
|
||||||
|
if err := json.Unmarshal(conn_bytes, &connections); err != nil {
|
||||||
|
c.String(200, templates.ConnectionsList(nil, ""))
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var name string
|
||||||
|
for n, c := range connections {
|
||||||
|
if c == conn {
|
||||||
|
name = n
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
session.Set("current", name)
|
||||||
|
session.Save()
|
||||||
|
|
||||||
|
c.String(200, templates.ConnectionsList(connections, name))
|
||||||
|
}
|
||||||
@ -1,31 +1,46 @@
|
|||||||
package database
|
package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Azpect3120/Web-Database-Viewer/internal/templates"
|
||||||
"github.com/gin-contrib/sessions"
|
"github.com/gin-contrib/sessions"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Create a new connection to a database and store the details
|
||||||
|
// in the session.
|
||||||
func CreateConnection(c *gin.Context) {
|
func CreateConnection(c *gin.Context) {
|
||||||
session := sessions.Default(c)
|
|
||||||
var (
|
var (
|
||||||
url string = c.PostForm("db-url")
|
url string = c.PostForm("db-url")
|
||||||
database string = c.PostForm("db-database")
|
database string = c.PostForm("db-database")
|
||||||
)
|
)
|
||||||
|
|
||||||
connections, ok := session.Get("connections").(map[string]string)
|
session := sessions.Default(c)
|
||||||
|
|
||||||
|
var connections map[string]string
|
||||||
|
|
||||||
|
session_bytes, ok := session.Get("connections").([]byte)
|
||||||
if !ok {
|
if !ok {
|
||||||
fmt.Println("Creating new connections map /internal/database/create.go:19")
|
|
||||||
connections = make(map[string]string)
|
connections = make(map[string]string)
|
||||||
|
} else {
|
||||||
|
if err := json.Unmarshal(session_bytes, &connections); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
connections[database] = url
|
connections[database] = url
|
||||||
|
|
||||||
session.Set("connections", connections)
|
conn_bytes, err := json.Marshal(connections)
|
||||||
err := session.Save()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Failed to save session /internal/database/create.go:29")
|
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
session.Set("connections", []byte(conn_bytes))
|
||||||
|
session.Set("current", database)
|
||||||
|
session.Save()
|
||||||
|
|
||||||
|
html := templates.ConnectionsList(connections, database)
|
||||||
|
c.String(200, html)
|
||||||
}
|
}
|
||||||
|
|||||||
112
internal/database/query.go
Normal file
112
internal/database/query.go
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Azpect3120/Web-Database-Viewer/internal/templates"
|
||||||
|
"github.com/gin-contrib/sessions"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
_ "github.com/lib/pq"
|
||||||
|
)
|
||||||
|
|
||||||
|
func QueryCurrent(c *gin.Context) {
|
||||||
|
query := c.PostForm("sql")
|
||||||
|
conn := getConnection(c)
|
||||||
|
|
||||||
|
cols, data, err := queryConnection(query, conn)
|
||||||
|
if err != nil {
|
||||||
|
c.String(200, templates.ErrorQueryResults(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.String(200, templates.QueryResults(cols, data))
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryConnection(query, url string) ([]string, []map[string]interface{}, error) {
|
||||||
|
db, err := sql.Open("postgres", url)
|
||||||
|
if err != nil {
|
||||||
|
return []string{}, []map[string]interface{}{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := db.Query(query)
|
||||||
|
if err != nil {
|
||||||
|
return []string{}, []map[string]interface{}{}, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
cols, err := rows.Columns()
|
||||||
|
if err != nil {
|
||||||
|
return []string{}, []map[string]interface{}{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create values pointer and value pointer slices
|
||||||
|
// We need to use pointers because the Scan function
|
||||||
|
// requires a pointer to the value. However, there
|
||||||
|
// is no simple way to create an array of pointers.
|
||||||
|
values := make([]interface{}, len(cols))
|
||||||
|
valuePtrs := make([]interface{}, len(cols))
|
||||||
|
|
||||||
|
for i := range cols {
|
||||||
|
valuePtrs[i] = &values[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Final data structure to store the results
|
||||||
|
// An array of maps, where each map is a row
|
||||||
|
// and the keys are the column names.
|
||||||
|
var result []map[string]interface{}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
// Scan the result into the value pointers
|
||||||
|
err := rows.Scan(valuePtrs...)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a map to store the column data
|
||||||
|
row := make(map[string]interface{})
|
||||||
|
for i, col := range cols {
|
||||||
|
var v interface{}
|
||||||
|
val := values[i]
|
||||||
|
|
||||||
|
// Convert the value to a string representation
|
||||||
|
// I can't see when this would fail and return
|
||||||
|
// something that isn't a string, but it's better
|
||||||
|
// to be safe than sorry. However, this might be
|
||||||
|
// hard to handle in the frontend.
|
||||||
|
b, ok := val.([]byte)
|
||||||
|
if ok {
|
||||||
|
v = string(b)
|
||||||
|
} else {
|
||||||
|
v = val
|
||||||
|
}
|
||||||
|
|
||||||
|
row[col] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append the row to the result set
|
||||||
|
result = append(result, row)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cols, result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getConnection(c *gin.Context) (url string) {
|
||||||
|
session := sessions.Default(c)
|
||||||
|
conn_bytes, ok := session.Get("connections").([]byte)
|
||||||
|
curr, ok := session.Get("current").(string)
|
||||||
|
if !ok {
|
||||||
|
fmt.Println("No current connection")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var connections map[string]string
|
||||||
|
if err := json.Unmarshal(conn_bytes, &connections); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return connections[curr]
|
||||||
|
}
|
||||||
@ -1,9 +1,12 @@
|
|||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Azpect3120/Web-Database-Viewer/internal/database"
|
"github.com/Azpect3120/Web-Database-Viewer/internal/database"
|
||||||
|
"github.com/Azpect3120/Web-Database-Viewer/internal/templates"
|
||||||
"github.com/gin-contrib/sessions"
|
"github.com/gin-contrib/sessions"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
@ -21,18 +24,48 @@ func populate(web, api *gin.RouterGroup) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
api.POST("/query", func(c *gin.Context) {
|
api.POST("/query", database.QueryCurrent)
|
||||||
sql := c.PostForm("sql")
|
|
||||||
c.JSON(200, gin.H{"sql": sql})
|
|
||||||
})
|
|
||||||
|
|
||||||
api.POST("/connections/test", database.TestConnectionURL)
|
api.POST("/connections/test", database.TestConnectionURL)
|
||||||
api.POST("/connections", database.CreateConnection)
|
api.POST("/connections", database.CreateConnection)
|
||||||
api.GET("/connections", func(c *gin.Context) {
|
api.GET("/connections", func(c *gin.Context) {
|
||||||
session := sessions.Default(c)
|
session := sessions.Default(c)
|
||||||
connections, ok := session.Get("connections").(map[string]string)
|
connections_bytes, ok := session.Get("connections").([]byte)
|
||||||
|
if !ok {
|
||||||
c.JSON(200, gin.H{"okay": ok, "connections": connections})
|
c.JSON(200, gin.H{"connections": make(map[string]string), "current": "", "count": 0, "time": time.Now().String(), "status": 200})
|
||||||
|
return
|
||||||
})
|
}
|
||||||
|
current := session.Get("current")
|
||||||
|
|
||||||
|
var connections map[string]string
|
||||||
|
if err := json.Unmarshal(connections_bytes, &connections); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(200, gin.H{
|
||||||
|
"connections": connections,
|
||||||
|
"current": current,
|
||||||
|
"count": len(connections),
|
||||||
|
"time": time.Now().String(),
|
||||||
|
"status": 200,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
web.GET("/connections", func(c *gin.Context) {
|
||||||
|
session := sessions.Default(c)
|
||||||
|
connections_bytes, conn_ok := session.Get("connections").([]byte)
|
||||||
|
current, curr_ok := session.Get("current").(string)
|
||||||
|
|
||||||
|
var connections map[string]string
|
||||||
|
if conn_ok || curr_ok {
|
||||||
|
if err := json.Unmarshal(connections_bytes, &connections); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
connections = make(map[string]string)
|
||||||
|
}
|
||||||
|
|
||||||
|
html := templates.ConnectionsList(connections, current)
|
||||||
|
c.String(200, html)
|
||||||
|
})
|
||||||
|
api.POST("/connections/connect", database.ChangeConnection)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,13 @@
|
|||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/gob"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-contrib/cors"
|
"github.com/gin-contrib/cors"
|
||||||
|
"github.com/gin-contrib/sessions"
|
||||||
|
"github.com/gin-contrib/sessions/cookie"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -22,18 +26,28 @@ func New(port string) *Server {
|
|||||||
Router: gin.Default(),
|
Router: gin.Default(),
|
||||||
config: cors.DefaultConfig(),
|
config: cors.DefaultConfig(),
|
||||||
}
|
}
|
||||||
|
|
||||||
server.config.AllowOrigins = []string{"*"}
|
server.config.AllowOrigins = []string{"*"}
|
||||||
server.Router.Use(cors.New(server.config))
|
server.Router.Use(cors.New(server.config))
|
||||||
|
|
||||||
|
// Session configuration
|
||||||
|
gob.Register([]byte{})
|
||||||
|
gob.Register(map[string]string{})
|
||||||
|
store := cookie.NewStore([]byte("secret"))
|
||||||
|
store.Options(sessions.Options{
|
||||||
|
Path: "/",
|
||||||
|
Domain: "",
|
||||||
|
MaxAge: 86400 * 7, // 7 days
|
||||||
|
Secure: true,
|
||||||
|
HttpOnly: true,
|
||||||
|
SameSite: http.SameSiteLaxMode,
|
||||||
|
})
|
||||||
|
server.Router.Use(sessions.Sessions("mysession", store))
|
||||||
|
|
||||||
return server
|
return server
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup the server with the necessary configurations
|
// Setup the server with the necessary configurations
|
||||||
func (s *Server) Setup() *Server {
|
func (s *Server) Setup() *Server {
|
||||||
// This has to be first ALWAYS for some stupid reason :|
|
|
||||||
s.initSession()
|
|
||||||
|
|
||||||
v1 := s.Router.Group("/v1")
|
v1 := s.Router.Group("/v1")
|
||||||
web_g := v1.Group("/web")
|
web_g := v1.Group("/web")
|
||||||
api_g := v1.Group("/api")
|
api_g := v1.Group("/api")
|
||||||
|
|||||||
@ -1,18 +0,0 @@
|
|||||||
package http
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/gob"
|
|
||||||
|
|
||||||
"github.com/gin-contrib/sessions"
|
|
||||||
"github.com/gin-contrib/sessions/cookie"
|
|
||||||
)
|
|
||||||
|
|
||||||
const SESSION_SECRET string = "lqMVy04h8KWll6lCU9XCDbfUsBIjD1eG"
|
|
||||||
|
|
||||||
// Initialize the session
|
|
||||||
// Session is a map of "db name": "connection url"
|
|
||||||
func (s *Server) initSession() {
|
|
||||||
gob.Register(&map[string]string{})
|
|
||||||
store := cookie.NewStore([]byte(SESSION_SECRET))
|
|
||||||
s.Router.Use(sessions.Sessions("mysession", store))
|
|
||||||
}
|
|
||||||
29
internal/templates/connections.go
Normal file
29
internal/templates/connections.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package templates
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
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_ITEM string = `<option value="%s"%s>%s</option>`
|
||||||
|
const LIST_CLOSE string = `</select>`
|
||||||
|
|
||||||
|
// Generate a list of connections to display in the drop-down.
|
||||||
|
// Current connection will be toggled as selected
|
||||||
|
func ConnectionsList(connections map[string]string, current string) string {
|
||||||
|
var html string = LIST_OPEN
|
||||||
|
|
||||||
|
if len(connections) == 0 || connections == nil {
|
||||||
|
html += fmt.Sprintf(LIST_ITEM, "", " selected", "No connections")
|
||||||
|
return html + LIST_CLOSE
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, url := range connections {
|
||||||
|
if name == current {
|
||||||
|
html += fmt.Sprintf(LIST_ITEM, url, " selected", name)
|
||||||
|
} else {
|
||||||
|
html += fmt.Sprintf(LIST_ITEM, url, "", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
html += LIST_CLOSE
|
||||||
|
return html
|
||||||
|
}
|
||||||
63
internal/templates/results.go
Normal file
63
internal/templates/results.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package templates
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// Table wrapper definitions
|
||||||
|
const table_open string = `<table id="query-result" class="min-w-full divide-y divide-gray-200">`
|
||||||
|
const table_close string = `</table>`
|
||||||
|
|
||||||
|
// Header definitions
|
||||||
|
const table_head_open string = `<thead class="bg-gray-50"><tr>`
|
||||||
|
const table_head_close string = `</tr></thead>`
|
||||||
|
const table_head_row string = `<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">%s</th>`
|
||||||
|
|
||||||
|
// Body definitions
|
||||||
|
const table_body_open string = `<tbody class="bg-white divide-y divide-gray-200">`
|
||||||
|
const table_body_close string = `</tbody>`
|
||||||
|
const table_body_row string = `<td class="px-6 py-4 whitespace-nowrap">%v</td>`
|
||||||
|
|
||||||
|
// Error message
|
||||||
|
const query_error_message string = `<p id="query-error" hx-swap-oob="outerHTML" class="text-red-500 py-2 text-sm">Query Error: %s</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 {
|
||||||
|
return table_open + table_close + fmt.Sprintf(query_error_message, e.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func QueryResults(cols []string, rows []map[string]interface{}) string {
|
||||||
|
head := generateHead(cols)
|
||||||
|
|
||||||
|
body := table_body_open
|
||||||
|
for _, row := range rows {
|
||||||
|
body += generateRow(cols, row)
|
||||||
|
}
|
||||||
|
|
||||||
|
body += table_body_close
|
||||||
|
|
||||||
|
return table_open + head + body + table_close + query_error_message_blank
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the tables head row
|
||||||
|
func generateHead(cols []string) string {
|
||||||
|
html := table_head_open
|
||||||
|
|
||||||
|
for _, col := range cols {
|
||||||
|
html += fmt.Sprintf(table_head_row, col)
|
||||||
|
}
|
||||||
|
|
||||||
|
return html + table_head_close
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateRow(cols []string, data map[string]interface{}) string {
|
||||||
|
row := "<tr>"
|
||||||
|
|
||||||
|
for _, col := range cols {
|
||||||
|
str, ok := data[col].(string)
|
||||||
|
if !ok {
|
||||||
|
str = "NULL"
|
||||||
|
}
|
||||||
|
row += fmt.Sprintf(table_body_row, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
return row + "</tr>"
|
||||||
|
}
|
||||||
@ -1,3 +1,3 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
./tools/tailwind -i ./public/assets/styles/config.css -o ./public/assets/styles/main.css --minify
|
./tools/tailwind -i ./web/static/styles/config.css -o ./web/static/styles/main.css --minify
|
||||||
|
|||||||
@ -829,6 +829,11 @@ video {
|
|||||||
background-color: rgb(229 231 235 / var(--tw-bg-opacity));
|
background-color: rgb(229 231 235 / var(--tw-bg-opacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bg-gray-400 {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: rgb(156 163 175 / var(--tw-bg-opacity));
|
||||||
|
}
|
||||||
|
|
||||||
.bg-gray-50 {
|
.bg-gray-50 {
|
||||||
--tw-bg-opacity: 1;
|
--tw-bg-opacity: 1;
|
||||||
background-color: rgb(249 250 251 / var(--tw-bg-opacity));
|
background-color: rgb(249 250 251 / var(--tw-bg-opacity));
|
||||||
@ -839,11 +844,6 @@ video {
|
|||||||
background-color: rgb(75 85 99 / var(--tw-bg-opacity));
|
background-color: rgb(75 85 99 / var(--tw-bg-opacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-green-500 {
|
|
||||||
--tw-bg-opacity: 1;
|
|
||||||
background-color: rgb(34 197 94 / var(--tw-bg-opacity));
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-white {
|
.bg-white {
|
||||||
--tw-bg-opacity: 1;
|
--tw-bg-opacity: 1;
|
||||||
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
|
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
|
||||||
@ -900,6 +900,11 @@ video {
|
|||||||
padding-bottom: 1rem;
|
padding-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.px-2 {
|
||||||
|
padding-left: 0.5rem;
|
||||||
|
padding-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
.pb-2 {
|
.pb-2 {
|
||||||
padding-bottom: 0.5rem;
|
padding-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
@ -937,6 +942,10 @@ video {
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.font-light {
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
.font-medium {
|
.font-medium {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
@ -964,9 +973,9 @@ video {
|
|||||||
color: rgb(55 65 81 / var(--tw-text-opacity));
|
color: rgb(55 65 81 / var(--tw-text-opacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-green-500 {
|
.text-red-500 {
|
||||||
--tw-text-opacity: 1;
|
--tw-text-opacity: 1;
|
||||||
color: rgb(34 197 94 / var(--tw-text-opacity));
|
color: rgb(239 68 68 / var(--tw-text-opacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-white {
|
.text-white {
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<!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">
|
||||||
@ -8,6 +9,7 @@
|
|||||||
<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">
|
||||||
@ -18,11 +20,10 @@
|
|||||||
<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>
|
||||||
<div class="flex items-center justify-end space-x-4 flex-wrap">
|
<div class="flex items-center justify-end space-x-4 flex-wrap">
|
||||||
<label for="database" class="block text-sm font-medium text-gray-700">Connected Database:</label>
|
<form hx-post="/v1/api/connections/connect" hx-trigger="change" hx-swap="outerHTML" hx-target="#connected-database" hx-encoding="multipart/form-data" class="flex items-center justify-end space-x-2 flex-wrap">
|
||||||
<select id="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">
|
<label for="connected-database" class="block text-sm font-medium text-gray-700">Connected Database:</label>
|
||||||
<option value="database1">Database 1</option>
|
<select hx-get="/v1/web/connections" hx-trigger="load" hx-swap="outerHTML" 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"></select>
|
||||||
<option value="database2">Database 2</option>
|
</form>
|
||||||
</select>
|
|
||||||
<button onclick="ShowModal();" class="bg-blue-500 text-white px-4 py-2 my-2 rounded-md text-sm md:text-base">
|
<button onclick="ShowModal();" class="bg-blue-500 text-white px-4 py-2 my-2 rounded-md text-sm md:text-base">
|
||||||
Add Connection
|
Add Connection
|
||||||
</button>
|
</button>
|
||||||
@ -38,8 +39,10 @@
|
|||||||
<div class="p-4 max-h-full">
|
<div class="p-4 max-h-full">
|
||||||
<ul class="space-y-2">
|
<ul class="space-y-2">
|
||||||
<li>
|
<li>
|
||||||
<button class="w-full text-left text-gray-700 font-medium hover:bg-gray-100 p-2 rounded flex items-center" title="Select this table">
|
<button class="w-full text-left text-gray-700 font-medium hover:bg-gray-100 p-2 rounded flex items-center"
|
||||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
title="Select this table">
|
||||||
|
<svg class="w-4 h-4 mr-2" 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 9l6 6 6-6"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 9l6 6 6-6"></path>
|
||||||
</svg>
|
</svg>
|
||||||
Table 1
|
Table 1
|
||||||
@ -47,8 +50,10 @@
|
|||||||
<ul class="ml-6 mt-1 space-y-1 text-gray-600">
|
<ul class="ml-6 mt-1 space-y-1 text-gray-600">
|
||||||
<li>
|
<li>
|
||||||
<button class="flex items-center" title="Select this field">
|
<button class="flex items-center" title="Select this field">
|
||||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16m-7 6h7"></path>
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16m-7 6h7">
|
||||||
|
</path>
|
||||||
</svg>
|
</svg>
|
||||||
<span>Column 1</span>
|
<span>Column 1</span>
|
||||||
<span class="h-1.5 w-1.5 bg-yellow-500 rounded-full mx-2" title="Primary Key"></span>
|
<span class="h-1.5 w-1.5 bg-yellow-500 rounded-full mx-2" title="Primary Key"></span>
|
||||||
@ -56,8 +61,10 @@
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button class="flex items-center" title="Select this field">
|
<button class="flex items-center" title="Select this field">
|
||||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16m-7 6h7"></path>
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16m-7 6h7">
|
||||||
|
</path>
|
||||||
</svg>
|
</svg>
|
||||||
<span>Column 2</span>
|
<span>Column 2</span>
|
||||||
</button>
|
</button>
|
||||||
@ -65,8 +72,10 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button class="w-full text-left text-gray-700 font-medium hover:bg-gray-100 p-2 rounded flex items-center" title="Select this table">
|
<button class="w-full text-left text-gray-700 font-medium hover:bg-gray-100 p-2 rounded flex items-center"
|
||||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
title="Select this table">
|
||||||
|
<svg class="w-4 h-4 mr-2" 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 9l6 6 6-6"></path>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 9l6 6 6-6"></path>
|
||||||
</svg>
|
</svg>
|
||||||
Table 2
|
Table 2
|
||||||
@ -74,8 +83,10 @@
|
|||||||
<ul class="ml-6 mt-1 space-y-1 text-gray-600">
|
<ul class="ml-6 mt-1 space-y-1 text-gray-600">
|
||||||
<li>
|
<li>
|
||||||
<button class="flex items-center" title="Select this field">
|
<button class="flex items-center" title="Select this field">
|
||||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16m-7 6h7"></path>
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16m-7 6h7">
|
||||||
|
</path>
|
||||||
</svg>
|
</svg>
|
||||||
<span>Column A</span>
|
<span>Column A</span>
|
||||||
<span class="h-1.5 w-1.5 bg-yellow-500 rounded-full mx-2" title="Primary Key"></span>
|
<span class="h-1.5 w-1.5 bg-yellow-500 rounded-full mx-2" title="Primary Key"></span>
|
||||||
@ -83,8 +94,10 @@
|
|||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button class="flex items-center" title="Select this field">
|
<button class="flex items-center" title="Select this field">
|
||||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16m-7 6h7"></path>
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16m-7 6h7">
|
||||||
|
</path>
|
||||||
</svg>
|
</svg>
|
||||||
<span>Column B</span>
|
<span>Column B</span>
|
||||||
</button>
|
</button>
|
||||||
@ -99,42 +112,15 @@
|
|||||||
<div class="w-full md:w-3/4 p-4">
|
<div class="w-full md:w-3/4 p-4">
|
||||||
<main>
|
<main>
|
||||||
<!-- Query Input -->
|
<!-- Query Input -->
|
||||||
<form
|
<form class="mb-4" hx-post="/v1/api/query" hx-trigger="input delay:1000ms" hx-swap="outerHTML" hx-target="#query-result">
|
||||||
class="mb-4"
|
|
||||||
hx-post="/v1/api/query"
|
|
||||||
hx-trigger="input delay:1000ms"
|
|
||||||
hx-swap="none"
|
|
||||||
hx-target=""
|
|
||||||
>
|
|
||||||
<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>
|
||||||
<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>
|
||||||
id="sql"
|
<p id="query-error" hx-swap-oob="outerHTML" class="text-red-500 py-2 text-sm hidden"></p>
|
||||||
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>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<!-- Query Results -->
|
<!-- Query Results -->
|
||||||
<div class="overflow-x-auto overflow-y-hidden bg-white shadow-md rounded-lg">
|
<div class="overflow-x-auto overflow-y-hidden bg-white shadow-md rounded-lg">
|
||||||
<table class="min-w-full divide-y divide-gray-200">
|
<table id="query-result" class="min-w-full divide-y divide-gray-200"></table>
|
||||||
<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">Column 1</th>
|
|
||||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Column 2</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody class="bg-white divide-y divide-gray-200">
|
|
||||||
<tr>
|
|
||||||
<td class="px-6 py-4 whitespace-nowrap">Data 1</td>
|
|
||||||
<td class="px-6 py-4 whitespace-nowrap">Data 2</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="px-6 py-4 whitespace-nowrap">Data A</td>
|
|
||||||
<td class="px-6 py-4 whitespace-nowrap">Data B</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
@ -148,7 +134,8 @@
|
|||||||
<div class="flex justify-between items-center border-b pb-2">
|
<div class="flex justify-between items-center border-b pb-2">
|
||||||
<h2 class="text-xl font-bold">Add New Connection</h2>
|
<h2 class="text-xl font-bold">Add New Connection</h2>
|
||||||
<button onclick="HideModal();" class="text-gray-500 hover:text-gray-700">
|
<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">
|
<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>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
@ -157,29 +144,30 @@
|
|||||||
<form id="connection-form" class="grid grid-cols-2 gap-4">
|
<form id="connection-form" class="grid grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="db-host" class="block text-sm font-medium text-gray-700">Host</label>
|
<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">
|
<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>
|
||||||
<div>
|
<div>
|
||||||
<label for="db-port" class="block text-sm font-medium text-gray-700">Port</label>
|
<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">
|
<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>
|
||||||
<div>
|
<div>
|
||||||
<label for="db-username" class="block text-sm font-medium text-gray-700">Username</label>
|
<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">
|
<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>
|
||||||
<div>
|
<div>
|
||||||
<label for="db-password" class="block text-sm font-medium text-gray-700">Password</label>
|
<label for="db-password" class="block text-sm font-medium text-gray-700">Password</label>
|
||||||
<div class="relative mt-1">
|
<div class="relative mt-1">
|
||||||
<input
|
<input id="db-password" name="db-password" type="password" placeholder="●●●●●●●●●"
|
||||||
id="db-password"
|
|
||||||
name="db-password"
|
|
||||||
type="password"
|
|
||||||
placeholder="●●●●●●●●●"
|
|
||||||
value="Panther4487!!!!"
|
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"
|
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"
|
||||||
<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">
|
class="absolute inset-y-0 right-0 px-3 py-2 text-gray-500 bg-gray-200 rounded-r-md border border-gray-300"
|
||||||
<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">
|
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>
|
<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>
|
<circle cx="12" cy="12" r="3"></circle>
|
||||||
</svg>
|
</svg>
|
||||||
@ -188,7 +176,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="db-driver" class="block text-sm font-medium text-gray-700">Driver/Type of Database</label>
|
<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">
|
<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="postgresql">PostgreSQL</option>
|
||||||
<option value="mysql">MySQL</option>
|
<option value="mysql">MySQL</option>
|
||||||
<option value="sqlite">SQLite</option>
|
<option value="sqlite">SQLite</option>
|
||||||
@ -200,33 +189,35 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="db-database" class="block text-sm font-medium text-gray-700">Database Name</label>
|
<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">
|
<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>
|
||||||
<div class="col-span-2">
|
<div class="col-span-2">
|
||||||
<label for="db-url" class="block text-sm font-medium text-gray-700">
|
<label for="db-url" class="block text-sm font-medium text-gray-700">
|
||||||
Connection URL
|
Connection URL
|
||||||
<br>
|
<br>
|
||||||
<span class="text-xs font-light">
|
<span class="text-xs font-light">
|
||||||
The connection URL will be automatically generated based on the above fields. To view the URL generated,
|
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.
|
push the "display secret details" button in the password section.
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</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">
|
<input id="db-url" name="db-url" type="password"
|
||||||
<span id="db-url-invalid" class="text-xs text-red-500 hidden">Connection URL is incomplete or invalid.</span>
|
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>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div class="flex items-center space-x-4 mt-4">
|
<div class="flex items-center space-x-4 mt-4">
|
||||||
<button
|
<button hx-post="/v1/api/connections" hx-trigger="click" hx-target="#connected-database" hx-swap="outerHTML"
|
||||||
hx-post="/v1/api/connections"
|
hx-include="#connection-form" onclick="HideModal();" class="bg-blue-500 text-white px-4 py-2 rounded-md">
|
||||||
hx-trigger="click"
|
|
||||||
hx-swap="none"
|
|
||||||
hx-include="#connection-form"
|
|
||||||
onclick="HideModal();"
|
|
||||||
class="bg-blue-500 text-white px-4 py-2 rounded-md"
|
|
||||||
>
|
|
||||||
Create Connection
|
Create Connection
|
||||||
</button>
|
</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">
|
<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>Test Connection</span>
|
||||||
<span id="connection-status" class="w-3 h-3 rounded-full bg-gray-400"></span>
|
<span id="connection-status" class="w-3 h-3 rounded-full bg-gray-400"></span>
|
||||||
</button>
|
</button>
|
||||||
@ -241,4 +232,5 @@
|
|||||||
<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>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user