pgweb/main.go

228 lines
4.2 KiB
Go
Raw Normal View History

2014-10-08 21:26:57 -05:00
package main
import (
"errors"
"fmt"
"github.com/gin-gonic/gin"
"github.com/jessevdk/go-flags"
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq"
"log"
"os"
"reflect"
"strings"
)
const (
2014-10-09 18:48:22 -05:00
SQL_TABLES = "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' ORDER BY table_schema,table_name;"
SQL_TABLE_SCHEMA = "SELECT column_name, data_type, is_nullable, character_maximum_length, character_set_catalog, column_default FROM information_schema.columns where table_name = '%s';"
2014-10-08 21:26:57 -05:00
)
type Client struct {
db *sqlx.DB
}
type Result struct {
Columns []string `json:"columns"`
Rows [][]interface{} `json:"rows"`
}
type Error struct {
Message string `json:"error"`
}
var dbClient *Client
2014-10-09 19:59:18 -05:00
var history []string
2014-10-08 21:26:57 -05:00
var options struct {
Url string `long:"url" description:"Database connection string"`
2014-10-08 21:26:57 -05:00
Host string `short:"h" long:"host" description:"Server hostname or IP" default:"localhost"`
Port int `short:"p" long:"port" description:"Server port" default:"5432"`
User string `short:"u" long:"user" description:"Database user" default:"postgres"`
DbName string `short:"d" long:"db" description:"Database name" default:"postgres"`
Ssl string `long:"ssl" description:"SSL option" default:"disable"`
Static string `short:"s" description:"Path to static assets" default:"./static"`
2014-10-08 21:26:57 -05:00
}
func getConnectionString() string {
if options.Url != "" {
return options.Url
}
2014-10-08 21:26:57 -05:00
return fmt.Sprintf(
"host=%s port=%d user=%s dbname=%s sslmode=disable",
options.Host, options.Port,
options.User, options.DbName,
)
}
func NewClient() (*Client, error) {
db, err := sqlx.Open("postgres", getConnectionString())
if err != nil {
return nil, err
}
return &Client{db: db}, nil
}
func NewError(err error) Error {
return Error{err.Error()}
}
2014-10-09 22:14:38 -05:00
func (client *Client) Tables() ([]string, error) {
res, err := client.Query(SQL_TABLES)
if err != nil {
return nil, err
}
var tables []string
for _, row := range res.Rows {
tables = append(tables, row[0].(string))
}
return tables, nil
}
2014-10-08 21:26:57 -05:00
func (client *Client) Query(query string) (*Result, error) {
rows, err := client.db.Queryx(query)
if err != nil {
return nil, err
}
defer rows.Close()
cols, err := rows.Columns()
if err != nil {
return nil, err
}
result := Result{
Columns: cols,
}
for rows.Next() {
obj, err := rows.SliceScan()
for i, item := range obj {
if item == nil {
obj[i] = nil
} else {
t := reflect.TypeOf(item).Kind().String()
if t == "slice" {
obj[i] = string(item.([]byte))
}
}
}
if err == nil {
result.Rows = append(result.Rows, obj)
}
}
return &result, nil
}
func API_RunQuery(c *gin.Context) {
query := strings.TrimSpace(c.Request.FormValue("query"))
if query == "" {
c.JSON(400, errors.New("Query parameter is missing"))
2014-10-08 21:26:57 -05:00
return
}
2014-10-09 19:59:18 -05:00
history = append(history, query)
2014-10-08 21:26:57 -05:00
API_HandleQuery(query, c)
}
func API_GetTables(c *gin.Context) {
2014-10-09 22:14:38 -05:00
names, err := dbClient.Tables()
if err != nil {
c.JSON(400, NewError(err))
return
}
c.JSON(200, names)
2014-10-09 18:48:22 -05:00
}
func API_GetTable(c *gin.Context) {
var columns []map[string]interface{}
2014-10-09 23:48:07 -05:00
res, err := dbClient.Query(fmt.Sprintf(SQL_TABLE_SCHEMA, c.Params.ByName("name")))
if err != nil {
c.JSON(400, NewError(err))
return
}
for _, row := range res.Rows {
item := make(map[string]interface{})
for i, c := range res.Columns {
item[c] = row[i]
}
columns = append(columns, item)
}
c.JSON(200, columns)
2014-10-08 21:26:57 -05:00
}
2014-10-09 19:59:18 -05:00
func API_History(c *gin.Context) {
c.JSON(200, history)
}
2014-10-08 21:26:57 -05:00
func API_HandleQuery(query string, c *gin.Context) {
result, err := dbClient.Query(query)
if err != nil {
c.JSON(400, NewError(err))
2014-10-08 21:26:57 -05:00
return
}
2014-10-09 22:47:20 -05:00
c.JSON(200, result)
2014-10-08 21:26:57 -05:00
}
func initClient() {
client, err := NewClient()
if err != nil {
log.Fatal(err)
}
dbClient = client
}
func initOptions() {
_, err := flags.ParseArgs(&options, os.Args)
if err != nil {
log.Fatal(err)
}
}
func main() {
initOptions()
initClient()
defer dbClient.db.Close()
router := gin.Default()
2014-10-09 19:05:51 -05:00
2014-10-08 21:26:57 -05:00
router.GET("/tables", API_GetTables)
2014-10-09 18:48:22 -05:00
router.GET("/tables/:name", API_GetTable)
2014-10-08 21:26:57 -05:00
router.GET("/select", API_RunQuery)
router.POST("/select", API_RunQuery)
2014-10-09 19:59:18 -05:00
router.GET("/history", API_History)
2014-10-08 21:26:57 -05:00
router.Static("/app", options.Static)
2014-10-09 19:05:51 -05:00
2014-10-08 21:26:57 -05:00
fmt.Println("Starting server at 0.0.0.0:8080")
router.Run(":8080")
}