Restructure application
This commit is contained in:
parent
7a75447364
commit
c513930e27
2
Makefile
2
Makefile
@ -22,7 +22,7 @@ test:
|
|||||||
godep go test -cover
|
godep go test -cover
|
||||||
|
|
||||||
assets: static/
|
assets: static/
|
||||||
go-bindata $(BINDATA_OPTS) $(BINDATA_IGNORE) -ignore=[.]gitignore -ignore=[.]gitkeep $<...
|
go-bindata -o pkg/data/bindata.go -pkg data $(BINDATA_OPTS) $(BINDATA_IGNORE) -ignore=[.]gitignore -ignore=[.]gitkeep $<...
|
||||||
|
|
||||||
dev-assets:
|
dev-assets:
|
||||||
@$(MAKE) --no-print-directory assets BINDATA_OPTS="-debug"
|
@$(MAKE) --no-print-directory assets BINDATA_OPTS="-debug"
|
||||||
|
560
bindata.go
560
bindata.go
File diff suppressed because one or more lines are too long
21
history.go
21
history.go
@ -1,21 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type HistoryRecord struct {
|
|
||||||
Query string `json:"query"`
|
|
||||||
Timestamp string `json:"timestamp"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewHistory() []HistoryRecord {
|
|
||||||
return make([]HistoryRecord, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewHistoryRecord(query string) HistoryRecord {
|
|
||||||
return HistoryRecord{
|
|
||||||
Query: query,
|
|
||||||
Timestamp: time.Now().String(),
|
|
||||||
}
|
|
||||||
}
|
|
61
main.go
61
main.go
@ -7,31 +7,18 @@ import (
|
|||||||
"os/signal"
|
"os/signal"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/jessevdk/go-flags"
|
|
||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
|
|
||||||
|
"github.com/sosedoff/pgweb/pkg/api"
|
||||||
|
"github.com/sosedoff/pgweb/pkg/client"
|
||||||
|
"github.com/sosedoff/pgweb/pkg/command"
|
||||||
|
"github.com/sosedoff/pgweb/pkg/connection"
|
||||||
|
"github.com/sosedoff/pgweb/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const VERSION = "0.5.2"
|
const VERSION = "0.5.2"
|
||||||
|
|
||||||
type Options struct {
|
var options command.Options
|
||||||
Version bool `short:"v" long:"version" description:"Print version"`
|
|
||||||
Debug bool `short:"d" long:"debug" description:"Enable debugging mode" default:"false"`
|
|
||||||
Url string `long:"url" description:"Database connection string"`
|
|
||||||
Host string `long:"host" description:"Server hostname or IP"`
|
|
||||||
Port int `long:"port" description:"Server port" default:"5432"`
|
|
||||||
User string `long:"user" description:"Database user"`
|
|
||||||
Pass string `long:"pass" description:"Password for user"`
|
|
||||||
DbName string `long:"db" description:"Database name"`
|
|
||||||
Ssl string `long:"ssl" description:"SSL option"`
|
|
||||||
HttpHost string `long:"bind" description:"HTTP server host" default:"localhost"`
|
|
||||||
HttpPort uint `long:"listen" description:"HTTP server listen port" default:"8081"`
|
|
||||||
AuthUser string `long:"auth-user" description:"HTTP basic auth user"`
|
|
||||||
AuthPass string `long:"auth-pass" description:"HTTP basic auth password"`
|
|
||||||
SkipOpen bool `short:"s" long:"skip-open" description:"Skip browser open on start"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var dbClient *Client
|
|
||||||
var options Options
|
|
||||||
|
|
||||||
func exitWithMessage(message string) {
|
func exitWithMessage(message string) {
|
||||||
fmt.Println("Error:", message)
|
fmt.Println("Error:", message)
|
||||||
@ -39,49 +26,48 @@ func exitWithMessage(message string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func initClient() {
|
func initClient() {
|
||||||
if connectionSettingsBlank(options) {
|
if connection.IsBlank(command.Opts) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := NewClient()
|
cl, err := client.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
exitWithMessage(err.Error())
|
exitWithMessage(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.Debug {
|
if command.Opts.Debug {
|
||||||
fmt.Println("Server connection string:", client.connectionString)
|
fmt.Println("Server connection string:", cl.ConnectionString)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Connecting to server...")
|
fmt.Println("Connecting to server...")
|
||||||
err = client.Test()
|
err = cl.Test()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
exitWithMessage(err.Error())
|
exitWithMessage(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Checking tables...")
|
fmt.Println("Checking tables...")
|
||||||
_, err = client.Tables()
|
_, err = cl.Tables()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
exitWithMessage(err.Error())
|
exitWithMessage(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
dbClient = client
|
api.DbClient = cl
|
||||||
}
|
}
|
||||||
|
|
||||||
func initOptions() {
|
func initOptions() {
|
||||||
_, err := flags.ParseArgs(&options, os.Args)
|
err := command.ParseOptions()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.Url == "" {
|
options = command.Opts
|
||||||
options.Url = os.Getenv("DATABASE_URL")
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.Version {
|
if options.Version {
|
||||||
fmt.Printf("pgweb v%s\n", VERSION)
|
fmt.Printf("pgweb v%s\n", VERSION)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Println("Pgweb version", VERSION)
|
||||||
}
|
}
|
||||||
|
|
||||||
func startServer() {
|
func startServer() {
|
||||||
@ -93,7 +79,7 @@ func startServer() {
|
|||||||
router.Use(gin.BasicAuth(auth))
|
router.Use(gin.BasicAuth(auth))
|
||||||
}
|
}
|
||||||
|
|
||||||
setupRoutes(router)
|
api.SetupRoutes(router)
|
||||||
|
|
||||||
fmt.Println("Starting server...")
|
fmt.Println("Starting server...")
|
||||||
go func() {
|
go func() {
|
||||||
@ -129,20 +115,19 @@ func openPage() {
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
initOptions()
|
initOptions()
|
||||||
|
|
||||||
fmt.Println("Pgweb version", VERSION)
|
|
||||||
initClient()
|
initClient()
|
||||||
|
|
||||||
if dbClient != nil {
|
if api.DbClient != nil {
|
||||||
defer dbClient.db.Close()
|
defer api.DbClient.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !options.Debug {
|
if !options.Debug {
|
||||||
gin.SetMode("release")
|
gin.SetMode("release")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Print memory usage every 30 seconds with debug flag
|
||||||
if options.Debug {
|
if options.Debug {
|
||||||
startRuntimeProfiler()
|
util.StartProfiler()
|
||||||
}
|
}
|
||||||
|
|
||||||
startServer()
|
startServer()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package main
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@ -10,6 +10,11 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/sosedoff/pgweb/pkg/bookmarks"
|
||||||
|
"github.com/sosedoff/pgweb/pkg/client"
|
||||||
|
"github.com/sosedoff/pgweb/pkg/command"
|
||||||
|
"github.com/sosedoff/pgweb/pkg/connection"
|
||||||
|
"github.com/sosedoff/pgweb/pkg/data"
|
||||||
)
|
)
|
||||||
|
|
||||||
var extraMimeTypes = map[string]string{
|
var extraMimeTypes = map[string]string{
|
||||||
@ -20,6 +25,8 @@ var extraMimeTypes = map[string]string{
|
|||||||
".svg": "image/svg+xml",
|
".svg": "image/svg+xml",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var DbClient *client.Client
|
||||||
|
|
||||||
type Error struct {
|
type Error struct {
|
||||||
Message string `json:"error"`
|
Message string `json:"error"`
|
||||||
}
|
}
|
||||||
@ -43,7 +50,7 @@ func assetContentType(name string) string {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupRoutes(router *gin.Engine) {
|
func SetupRoutes(router *gin.Engine) {
|
||||||
router.GET("/", API_Home)
|
router.GET("/", API_Home)
|
||||||
router.GET("/static/*path", API_ServeAsset)
|
router.GET("/static/*path", API_ServeAsset)
|
||||||
|
|
||||||
@ -79,7 +86,7 @@ func ApiMiddleware() gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
if dbClient != nil {
|
if DbClient != nil {
|
||||||
c.Next()
|
c.Next()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -106,7 +113,7 @@ func ApiMiddleware() gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func API_Home(c *gin.Context) {
|
func API_Home(c *gin.Context) {
|
||||||
data, err := Asset("static/index.html")
|
data, err := data.Asset("static/index.html")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.String(400, err.Error())
|
c.String(400, err.Error())
|
||||||
@ -124,41 +131,41 @@ func API_Connect(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := Options{Url: url}
|
opts := command.Options{Url: url}
|
||||||
url, err := formatConnectionUrl(opts)
|
url, err := connection.FormatUrl(opts)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(400, Error{err.Error()})
|
c.JSON(400, Error{err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := NewClientFromUrl(url)
|
cl, err := client.NewFromUrl(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(400, Error{err.Error()})
|
c.JSON(400, Error{err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = client.Test()
|
err = cl.Test()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(400, Error{err.Error()})
|
c.JSON(400, Error{err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
info, err := client.Info()
|
info, err := cl.Info()
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if dbClient != nil {
|
if DbClient != nil {
|
||||||
dbClient.db.Close()
|
DbClient.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
dbClient = client
|
DbClient = cl
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(200, info.Format()[0])
|
c.JSON(200, info.Format()[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
func API_GetDatabases(c *gin.Context) {
|
func API_GetDatabases(c *gin.Context) {
|
||||||
names, err := dbClient.Databases()
|
names, err := DbClient.Databases()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(400, NewError(err))
|
c.JSON(400, NewError(err))
|
||||||
@ -191,7 +198,7 @@ func API_ExplainQuery(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func API_GetSchemas(c *gin.Context) {
|
func API_GetSchemas(c *gin.Context) {
|
||||||
names, err := dbClient.Schemas()
|
names, err := DbClient.Schemas()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(400, NewError(err))
|
c.JSON(400, NewError(err))
|
||||||
@ -202,7 +209,7 @@ func API_GetSchemas(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func API_GetTables(c *gin.Context) {
|
func API_GetTables(c *gin.Context) {
|
||||||
names, err := dbClient.Tables()
|
names, err := DbClient.Tables()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(400, NewError(err))
|
c.JSON(400, NewError(err))
|
||||||
@ -213,7 +220,7 @@ func API_GetTables(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func API_GetTable(c *gin.Context) {
|
func API_GetTable(c *gin.Context) {
|
||||||
res, err := dbClient.Table(c.Params.ByName("table"))
|
res, err := DbClient.Table(c.Params.ByName("table"))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(400, NewError(err))
|
c.JSON(400, NewError(err))
|
||||||
@ -243,13 +250,13 @@ func API_GetTableRows(c *gin.Context) {
|
|||||||
limit = num
|
limit = num
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := RowsOptions{
|
opts := client.RowsOptions{
|
||||||
Limit: limit,
|
Limit: limit,
|
||||||
SortColumn: c.Request.FormValue("sort_column"),
|
SortColumn: c.Request.FormValue("sort_column"),
|
||||||
SortOrder: c.Request.FormValue("sort_order"),
|
SortOrder: c.Request.FormValue("sort_order"),
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := dbClient.TableRows(c.Params.ByName("table"), opts)
|
res, err := DbClient.TableRows(c.Params.ByName("table"), opts)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(400, NewError(err))
|
c.JSON(400, NewError(err))
|
||||||
@ -260,7 +267,7 @@ func API_GetTableRows(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func API_GetTableInfo(c *gin.Context) {
|
func API_GetTableInfo(c *gin.Context) {
|
||||||
res, err := dbClient.TableInfo(c.Params.ByName("table"))
|
res, err := DbClient.TableInfo(c.Params.ByName("table"))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(400, NewError(err))
|
c.JSON(400, NewError(err))
|
||||||
@ -271,11 +278,11 @@ func API_GetTableInfo(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func API_History(c *gin.Context) {
|
func API_History(c *gin.Context) {
|
||||||
c.JSON(200, dbClient.history)
|
c.JSON(200, DbClient.History)
|
||||||
}
|
}
|
||||||
|
|
||||||
func API_ConnectionInfo(c *gin.Context) {
|
func API_ConnectionInfo(c *gin.Context) {
|
||||||
res, err := dbClient.Info()
|
res, err := DbClient.Info()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(400, NewError(err))
|
c.JSON(400, NewError(err))
|
||||||
@ -286,7 +293,7 @@ func API_ConnectionInfo(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func API_Activity(c *gin.Context) {
|
func API_Activity(c *gin.Context) {
|
||||||
res, err := dbClient.Activity()
|
res, err := DbClient.Activity()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(400, NewError(err))
|
c.JSON(400, NewError(err))
|
||||||
return
|
return
|
||||||
@ -296,7 +303,7 @@ func API_Activity(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func API_TableIndexes(c *gin.Context) {
|
func API_TableIndexes(c *gin.Context) {
|
||||||
res, err := dbClient.TableIndexes(c.Params.ByName("table"))
|
res, err := DbClient.TableIndexes(c.Params.ByName("table"))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(400, NewError(err))
|
c.JSON(400, NewError(err))
|
||||||
@ -307,7 +314,7 @@ func API_TableIndexes(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func API_HandleQuery(query string, c *gin.Context) {
|
func API_HandleQuery(query string, c *gin.Context) {
|
||||||
result, err := dbClient.Query(query)
|
result, err := DbClient.Query(query)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(400, NewError(err))
|
c.JSON(400, NewError(err))
|
||||||
@ -327,7 +334,7 @@ func API_HandleQuery(query string, c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func API_Bookmarks(c *gin.Context) {
|
func API_Bookmarks(c *gin.Context) {
|
||||||
bookmarks, err := readAllBookmarks(bookmarksPath())
|
bookmarks, err := bookmarks.ReadAll(bookmarks.Path())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(400, NewError(err))
|
c.JSON(400, NewError(err))
|
||||||
@ -339,7 +346,7 @@ func API_Bookmarks(c *gin.Context) {
|
|||||||
|
|
||||||
func API_ServeAsset(c *gin.Context) {
|
func API_ServeAsset(c *gin.Context) {
|
||||||
path := "static" + c.Params.ByName("path")
|
path := "static" + c.Params.ByName("path")
|
||||||
data, err := Asset(path)
|
data, err := data.Asset(path)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.String(400, err.Error())
|
c.String(400, err.Error())
|
@ -1,4 +1,4 @@
|
|||||||
package main
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
@ -1,4 +1,4 @@
|
|||||||
package main
|
package bookmarks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -37,12 +37,12 @@ func fileBasename(path string) string {
|
|||||||
return strings.Replace(filename, filepath.Ext(path), "", 1)
|
return strings.Replace(filename, filepath.Ext(path), "", 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func bookmarksPath() string {
|
func Path() string {
|
||||||
path, _ := homedir.Dir()
|
path, _ := homedir.Dir()
|
||||||
return fmt.Sprintf("%s/.pgweb/bookmarks", path)
|
return fmt.Sprintf("%s/.pgweb/bookmarks", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func readAllBookmarks(path string) (map[string]Bookmark, error) {
|
func ReadAll(path string) (map[string]Bookmark, error) {
|
||||||
results := map[string]Bookmark{}
|
results := map[string]Bookmark{}
|
||||||
|
|
||||||
files, err := ioutil.ReadDir(path)
|
files, err := ioutil.ReadDir(path)
|
@ -1,4 +1,4 @@
|
|||||||
package main
|
package bookmarks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
@ -1,4 +1,4 @@
|
|||||||
package main
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@ -7,12 +7,16 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
|
"github.com/sosedoff/pgweb/pkg/command"
|
||||||
|
"github.com/sosedoff/pgweb/pkg/connection"
|
||||||
|
"github.com/sosedoff/pgweb/pkg/history"
|
||||||
|
"github.com/sosedoff/pgweb/pkg/statements"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
db *sqlx.DB
|
db *sqlx.DB
|
||||||
history []HistoryRecord
|
History []history.Record
|
||||||
connectionString string
|
ConnectionString string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Row []interface{}
|
type Row []interface{}
|
||||||
@ -29,10 +33,10 @@ type RowsOptions struct {
|
|||||||
SortOrder string // Sort direction (ASC, DESC)
|
SortOrder string // Sort direction (ASC, DESC)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient() (*Client, error) {
|
func New() (*Client, error) {
|
||||||
str, err := buildConnectionString(options)
|
str, err := connection.BuildString(command.Opts)
|
||||||
|
|
||||||
if options.Debug && str != "" {
|
if command.Opts.Debug && str != "" {
|
||||||
fmt.Println("Creating a new client for:", str)
|
fmt.Println("Creating a new client for:", str)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,15 +52,15 @@ func NewClient() (*Client, error) {
|
|||||||
|
|
||||||
client := Client{
|
client := Client{
|
||||||
db: db,
|
db: db,
|
||||||
connectionString: str,
|
ConnectionString: str,
|
||||||
history: NewHistory(),
|
History: history.New(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return &client, nil
|
return &client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClientFromUrl(url string) (*Client, error) {
|
func NewFromUrl(url string) (*Client, error) {
|
||||||
if options.Debug {
|
if command.Opts.Debug {
|
||||||
fmt.Println("Creating a new client for:", url)
|
fmt.Println("Creating a new client for:", url)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,8 +72,8 @@ func NewClientFromUrl(url string) (*Client, error) {
|
|||||||
|
|
||||||
client := Client{
|
client := Client{
|
||||||
db: db,
|
db: db,
|
||||||
connectionString: url,
|
ConnectionString: url,
|
||||||
history: NewHistory(),
|
History: history.New(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return &client, nil
|
return &client, nil
|
||||||
@ -80,23 +84,23 @@ func (client *Client) Test() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) Info() (*Result, error) {
|
func (client *Client) Info() (*Result, error) {
|
||||||
return client.query(PG_INFO)
|
return client.query(statements.PG_INFO)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) Databases() ([]string, error) {
|
func (client *Client) Databases() ([]string, error) {
|
||||||
return client.fetchRows(PG_DATABASES)
|
return client.fetchRows(statements.PG_DATABASES)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) Schemas() ([]string, error) {
|
func (client *Client) Schemas() ([]string, error) {
|
||||||
return client.fetchRows(PG_SCHEMAS)
|
return client.fetchRows(statements.PG_SCHEMAS)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) Tables() ([]string, error) {
|
func (client *Client) Tables() ([]string, error) {
|
||||||
return client.fetchRows(PG_TABLES)
|
return client.fetchRows(statements.PG_TABLES)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) Table(table string) (*Result, error) {
|
func (client *Client) Table(table string) (*Result, error) {
|
||||||
return client.query(PG_TABLE_SCHEMA, table)
|
return client.query(statements.PG_TABLE_SCHEMA, table)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) TableRows(table string, opts RowsOptions) (*Result, error) {
|
func (client *Client) TableRows(table string, opts RowsOptions) (*Result, error) {
|
||||||
@ -118,11 +122,11 @@ func (client *Client) TableRows(table string, opts RowsOptions) (*Result, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) TableInfo(table string) (*Result, error) {
|
func (client *Client) TableInfo(table string) (*Result, error) {
|
||||||
return client.query(PG_TABLE_INFO, table)
|
return client.query(statements.PG_TABLE_INFO, table)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) TableIndexes(table string) (*Result, error) {
|
func (client *Client) TableIndexes(table string) (*Result, error) {
|
||||||
res, err := client.query(PG_TABLE_INDEXES, table)
|
res, err := client.query(statements.PG_TABLE_INDEXES, table)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -133,7 +137,7 @@ func (client *Client) TableIndexes(table string) (*Result, error) {
|
|||||||
|
|
||||||
// Returns all active queriers on the server
|
// Returns all active queriers on the server
|
||||||
func (client *Client) Activity() (*Result, error) {
|
func (client *Client) Activity() (*Result, error) {
|
||||||
return client.query(PG_ACTIVITY)
|
return client.query(statements.PG_ACTIVITY)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) Query(query string) (*Result, error) {
|
func (client *Client) Query(query string) (*Result, error) {
|
||||||
@ -141,7 +145,7 @@ func (client *Client) Query(query string) (*Result, error) {
|
|||||||
|
|
||||||
// Save history records only if query did not fail
|
// Save history records only if query did not fail
|
||||||
if err == nil {
|
if err == nil {
|
||||||
client.history = append(client.history, NewHistoryRecord(query))
|
client.History = append(client.History, history.NewRecord(query))
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, err
|
return res, err
|
||||||
@ -231,6 +235,11 @@ func (res *Result) CSV() []byte {
|
|||||||
return buff.Bytes()
|
return buff.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close database connection
|
||||||
|
func (client *Client) Close() error {
|
||||||
|
return client.db.Close()
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch all rows as strings for a single column
|
// Fetch all rows as strings for a single column
|
||||||
func (client *Client) fetchRows(q string) ([]string, error) {
|
func (client *Client) fetchRows(q string) ([]string, error) {
|
||||||
res, err := client.query(q)
|
res, err := client.query(q)
|
@ -1,4 +1,4 @@
|
|||||||
package main
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
39
pkg/command/options.go
Normal file
39
pkg/command/options.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package command
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/jessevdk/go-flags"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Options struct {
|
||||||
|
Version bool `short:"v" long:"version" description:"Print version"`
|
||||||
|
Debug bool `short:"d" long:"debug" description:"Enable debugging mode" default:"false"`
|
||||||
|
Url string `long:"url" description:"Database connection string"`
|
||||||
|
Host string `long:"host" description:"Server hostname or IP"`
|
||||||
|
Port int `long:"port" description:"Server port" default:"5432"`
|
||||||
|
User string `long:"user" description:"Database user"`
|
||||||
|
Pass string `long:"pass" description:"Password for user"`
|
||||||
|
DbName string `long:"db" description:"Database name"`
|
||||||
|
Ssl string `long:"ssl" description:"SSL option"`
|
||||||
|
HttpHost string `long:"bind" description:"HTTP server host" default:"localhost"`
|
||||||
|
HttpPort uint `long:"listen" description:"HTTP server listen port" default:"8081"`
|
||||||
|
AuthUser string `long:"auth-user" description:"HTTP basic auth user"`
|
||||||
|
AuthPass string `long:"auth-pass" description:"HTTP basic auth password"`
|
||||||
|
SkipOpen bool `short:"s" long:"skip-open" description:"Skip browser open on start"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var Opts Options
|
||||||
|
|
||||||
|
func ParseOptions() error {
|
||||||
|
_, err := flags.ParseArgs(&Opts, os.Args)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if Opts.Url == "" {
|
||||||
|
Opts.Url = os.Getenv("DATABASE_URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package main
|
package connection
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@ -6,6 +6,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sosedoff/pgweb/pkg/command"
|
||||||
)
|
)
|
||||||
|
|
||||||
func currentUser() (string, error) {
|
func currentUser() (string, error) {
|
||||||
@ -22,7 +24,7 @@ func currentUser() (string, error) {
|
|||||||
return "", errors.New("Unable to detect OS user")
|
return "", errors.New("Unable to detect OS user")
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatConnectionUrl(opts Options) (string, error) {
|
func FormatUrl(opts command.Options) (string, error) {
|
||||||
url := opts.Url
|
url := opts.Url
|
||||||
|
|
||||||
// Make sure to only accept urls in a standard format
|
// Make sure to only accept urls in a standard format
|
||||||
@ -50,13 +52,13 @@ func formatConnectionUrl(opts Options) (string, error) {
|
|||||||
return url, nil
|
return url, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func connectionSettingsBlank(opts Options) bool {
|
func IsBlank(opts command.Options) bool {
|
||||||
return opts.Host == "" && opts.User == "" && opts.DbName == "" && opts.Url == ""
|
return opts.Host == "" && opts.User == "" && opts.DbName == "" && opts.Url == ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildConnectionString(opts Options) (string, error) {
|
func BuildString(opts command.Options) (string, error) {
|
||||||
if opts.Url != "" {
|
if opts.Url != "" {
|
||||||
return formatConnectionUrl(opts)
|
return FormatUrl(opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to detect user from current OS user
|
// Try to detect user from current OS user
|
@ -1,4 +1,4 @@
|
|||||||
package main
|
package connection
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
492
pkg/data/bindata.go
Normal file
492
pkg/data/bindata.go
Normal file
@ -0,0 +1,492 @@
|
|||||||
|
package data
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// bindata_read reads the given file from disk. It returns an error on failure.
|
||||||
|
func bindata_read(path, name string) ([]byte, error) {
|
||||||
|
buf, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error reading asset %s at %s: %v", name, path, err)
|
||||||
|
}
|
||||||
|
return buf, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type asset struct {
|
||||||
|
bytes []byte
|
||||||
|
info os.FileInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// static_css_app_css reads file data from disk. It returns an error on failure.
|
||||||
|
func static_css_app_css() (*asset, error) {
|
||||||
|
path := "/Users/sosedoff/go/src/github.com/sosedoff/pgweb/static/css/app.css"
|
||||||
|
name := "static/css/app.css"
|
||||||
|
bytes, err := bindata_read(path, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error reading asset info %s at %s: %v", name, path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
a := &asset{bytes: bytes, info: fi}
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// static_css_bootstrap_css reads file data from disk. It returns an error on failure.
|
||||||
|
func static_css_bootstrap_css() (*asset, error) {
|
||||||
|
path := "/Users/sosedoff/go/src/github.com/sosedoff/pgweb/static/css/bootstrap.css"
|
||||||
|
name := "static/css/bootstrap.css"
|
||||||
|
bytes, err := bindata_read(path, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error reading asset info %s at %s: %v", name, path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
a := &asset{bytes: bytes, info: fi}
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// static_css_font_awesome_css reads file data from disk. It returns an error on failure.
|
||||||
|
func static_css_font_awesome_css() (*asset, error) {
|
||||||
|
path := "/Users/sosedoff/go/src/github.com/sosedoff/pgweb/static/css/font-awesome.css"
|
||||||
|
name := "static/css/font-awesome.css"
|
||||||
|
bytes, err := bindata_read(path, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error reading asset info %s at %s: %v", name, path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
a := &asset{bytes: bytes, info: fi}
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// static_fonts_fontawesome_otf reads file data from disk. It returns an error on failure.
|
||||||
|
func static_fonts_fontawesome_otf() (*asset, error) {
|
||||||
|
path := "/Users/sosedoff/go/src/github.com/sosedoff/pgweb/static/fonts/FontAwesome.otf"
|
||||||
|
name := "static/fonts/FontAwesome.otf"
|
||||||
|
bytes, err := bindata_read(path, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error reading asset info %s at %s: %v", name, path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
a := &asset{bytes: bytes, info: fi}
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// static_fonts_fontawesome_webfont_eot reads file data from disk. It returns an error on failure.
|
||||||
|
func static_fonts_fontawesome_webfont_eot() (*asset, error) {
|
||||||
|
path := "/Users/sosedoff/go/src/github.com/sosedoff/pgweb/static/fonts/fontawesome-webfont.eot"
|
||||||
|
name := "static/fonts/fontawesome-webfont.eot"
|
||||||
|
bytes, err := bindata_read(path, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error reading asset info %s at %s: %v", name, path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
a := &asset{bytes: bytes, info: fi}
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// static_fonts_fontawesome_webfont_svg reads file data from disk. It returns an error on failure.
|
||||||
|
func static_fonts_fontawesome_webfont_svg() (*asset, error) {
|
||||||
|
path := "/Users/sosedoff/go/src/github.com/sosedoff/pgweb/static/fonts/fontawesome-webfont.svg"
|
||||||
|
name := "static/fonts/fontawesome-webfont.svg"
|
||||||
|
bytes, err := bindata_read(path, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error reading asset info %s at %s: %v", name, path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
a := &asset{bytes: bytes, info: fi}
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// static_fonts_fontawesome_webfont_ttf reads file data from disk. It returns an error on failure.
|
||||||
|
func static_fonts_fontawesome_webfont_ttf() (*asset, error) {
|
||||||
|
path := "/Users/sosedoff/go/src/github.com/sosedoff/pgweb/static/fonts/fontawesome-webfont.ttf"
|
||||||
|
name := "static/fonts/fontawesome-webfont.ttf"
|
||||||
|
bytes, err := bindata_read(path, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error reading asset info %s at %s: %v", name, path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
a := &asset{bytes: bytes, info: fi}
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// static_fonts_fontawesome_webfont_woff reads file data from disk. It returns an error on failure.
|
||||||
|
func static_fonts_fontawesome_webfont_woff() (*asset, error) {
|
||||||
|
path := "/Users/sosedoff/go/src/github.com/sosedoff/pgweb/static/fonts/fontawesome-webfont.woff"
|
||||||
|
name := "static/fonts/fontawesome-webfont.woff"
|
||||||
|
bytes, err := bindata_read(path, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error reading asset info %s at %s: %v", name, path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
a := &asset{bytes: bytes, info: fi}
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// static_img_icon_ico reads file data from disk. It returns an error on failure.
|
||||||
|
func static_img_icon_ico() (*asset, error) {
|
||||||
|
path := "/Users/sosedoff/go/src/github.com/sosedoff/pgweb/static/img/icon.ico"
|
||||||
|
name := "static/img/icon.ico"
|
||||||
|
bytes, err := bindata_read(path, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error reading asset info %s at %s: %v", name, path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
a := &asset{bytes: bytes, info: fi}
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// static_img_icon_png reads file data from disk. It returns an error on failure.
|
||||||
|
func static_img_icon_png() (*asset, error) {
|
||||||
|
path := "/Users/sosedoff/go/src/github.com/sosedoff/pgweb/static/img/icon.png"
|
||||||
|
name := "static/img/icon.png"
|
||||||
|
bytes, err := bindata_read(path, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error reading asset info %s at %s: %v", name, path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
a := &asset{bytes: bytes, info: fi}
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// static_index_html reads file data from disk. It returns an error on failure.
|
||||||
|
func static_index_html() (*asset, error) {
|
||||||
|
path := "/Users/sosedoff/go/src/github.com/sosedoff/pgweb/static/index.html"
|
||||||
|
name := "static/index.html"
|
||||||
|
bytes, err := bindata_read(path, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error reading asset info %s at %s: %v", name, path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
a := &asset{bytes: bytes, info: fi}
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// static_js_ace_pgsql_js reads file data from disk. It returns an error on failure.
|
||||||
|
func static_js_ace_pgsql_js() (*asset, error) {
|
||||||
|
path := "/Users/sosedoff/go/src/github.com/sosedoff/pgweb/static/js/ace-pgsql.js"
|
||||||
|
name := "static/js/ace-pgsql.js"
|
||||||
|
bytes, err := bindata_read(path, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error reading asset info %s at %s: %v", name, path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
a := &asset{bytes: bytes, info: fi}
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// static_js_ace_js reads file data from disk. It returns an error on failure.
|
||||||
|
func static_js_ace_js() (*asset, error) {
|
||||||
|
path := "/Users/sosedoff/go/src/github.com/sosedoff/pgweb/static/js/ace.js"
|
||||||
|
name := "static/js/ace.js"
|
||||||
|
bytes, err := bindata_read(path, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error reading asset info %s at %s: %v", name, path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
a := &asset{bytes: bytes, info: fi}
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// static_js_app_js reads file data from disk. It returns an error on failure.
|
||||||
|
func static_js_app_js() (*asset, error) {
|
||||||
|
path := "/Users/sosedoff/go/src/github.com/sosedoff/pgweb/static/js/app.js"
|
||||||
|
name := "static/js/app.js"
|
||||||
|
bytes, err := bindata_read(path, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error reading asset info %s at %s: %v", name, path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
a := &asset{bytes: bytes, info: fi}
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// static_js_jquery_js reads file data from disk. It returns an error on failure.
|
||||||
|
func static_js_jquery_js() (*asset, error) {
|
||||||
|
path := "/Users/sosedoff/go/src/github.com/sosedoff/pgweb/static/js/jquery.js"
|
||||||
|
name := "static/js/jquery.js"
|
||||||
|
bytes, err := bindata_read(path, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Error reading asset info %s at %s: %v", name, path, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
a := &asset{bytes: bytes, info: fi}
|
||||||
|
return a, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Asset loads and returns the asset for the given name.
|
||||||
|
// It returns an error if the asset could not be found or
|
||||||
|
// could not be loaded.
|
||||||
|
func Asset(name string) ([]byte, error) {
|
||||||
|
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
|
if f, ok := _bindata[cannonicalName]; ok {
|
||||||
|
a, err := f()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
|
||||||
|
}
|
||||||
|
return a.bytes, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("Asset %s not found", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustAsset is like Asset but panics when Asset would return an error.
|
||||||
|
// It simplifies safe initialization of global variables.
|
||||||
|
func MustAsset(name string) []byte {
|
||||||
|
a, err := Asset(name)
|
||||||
|
if (err != nil) {
|
||||||
|
panic("asset: Asset(" + name + "): " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetInfo loads and returns the asset info for the given name.
|
||||||
|
// It returns an error if the asset could not be found or
|
||||||
|
// could not be loaded.
|
||||||
|
func AssetInfo(name string) (os.FileInfo, error) {
|
||||||
|
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
|
if f, ok := _bindata[cannonicalName]; ok {
|
||||||
|
a, err := f()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
|
||||||
|
}
|
||||||
|
return a.info, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("AssetInfo %s not found", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetNames returns the names of the assets.
|
||||||
|
func AssetNames() []string {
|
||||||
|
names := make([]string, 0, len(_bindata))
|
||||||
|
for name := range _bindata {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
// _bindata is a table, holding each asset generator, mapped to its name.
|
||||||
|
var _bindata = map[string]func() (*asset, error){
|
||||||
|
"static/css/app.css": static_css_app_css,
|
||||||
|
"static/css/bootstrap.css": static_css_bootstrap_css,
|
||||||
|
"static/css/font-awesome.css": static_css_font_awesome_css,
|
||||||
|
"static/fonts/FontAwesome.otf": static_fonts_fontawesome_otf,
|
||||||
|
"static/fonts/fontawesome-webfont.eot": static_fonts_fontawesome_webfont_eot,
|
||||||
|
"static/fonts/fontawesome-webfont.svg": static_fonts_fontawesome_webfont_svg,
|
||||||
|
"static/fonts/fontawesome-webfont.ttf": static_fonts_fontawesome_webfont_ttf,
|
||||||
|
"static/fonts/fontawesome-webfont.woff": static_fonts_fontawesome_webfont_woff,
|
||||||
|
"static/img/icon.ico": static_img_icon_ico,
|
||||||
|
"static/img/icon.png": static_img_icon_png,
|
||||||
|
"static/index.html": static_index_html,
|
||||||
|
"static/js/ace-pgsql.js": static_js_ace_pgsql_js,
|
||||||
|
"static/js/ace.js": static_js_ace_js,
|
||||||
|
"static/js/app.js": static_js_app_js,
|
||||||
|
"static/js/jquery.js": static_js_jquery_js,
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetDir returns the file names below a certain
|
||||||
|
// directory embedded in the file by go-bindata.
|
||||||
|
// For example if you run go-bindata on data/... and data contains the
|
||||||
|
// following hierarchy:
|
||||||
|
// data/
|
||||||
|
// foo.txt
|
||||||
|
// img/
|
||||||
|
// a.png
|
||||||
|
// b.png
|
||||||
|
// then AssetDir("data") would return []string{"foo.txt", "img"}
|
||||||
|
// AssetDir("data/img") would return []string{"a.png", "b.png"}
|
||||||
|
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
|
||||||
|
// AssetDir("") will return []string{"data"}.
|
||||||
|
func AssetDir(name string) ([]string, error) {
|
||||||
|
node := _bintree
|
||||||
|
if len(name) != 0 {
|
||||||
|
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
|
pathList := strings.Split(cannonicalName, "/")
|
||||||
|
for _, p := range pathList {
|
||||||
|
node = node.Children[p]
|
||||||
|
if node == nil {
|
||||||
|
return nil, fmt.Errorf("Asset %s not found", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if node.Func != nil {
|
||||||
|
return nil, fmt.Errorf("Asset %s not found", name)
|
||||||
|
}
|
||||||
|
rv := make([]string, 0, len(node.Children))
|
||||||
|
for name := range node.Children {
|
||||||
|
rv = append(rv, name)
|
||||||
|
}
|
||||||
|
return rv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type _bintree_t struct {
|
||||||
|
Func func() (*asset, error)
|
||||||
|
Children map[string]*_bintree_t
|
||||||
|
}
|
||||||
|
var _bintree = &_bintree_t{nil, map[string]*_bintree_t{
|
||||||
|
"static": &_bintree_t{nil, map[string]*_bintree_t{
|
||||||
|
"css": &_bintree_t{nil, map[string]*_bintree_t{
|
||||||
|
"app.css": &_bintree_t{static_css_app_css, map[string]*_bintree_t{
|
||||||
|
}},
|
||||||
|
"bootstrap.css": &_bintree_t{static_css_bootstrap_css, map[string]*_bintree_t{
|
||||||
|
}},
|
||||||
|
"font-awesome.css": &_bintree_t{static_css_font_awesome_css, map[string]*_bintree_t{
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
"fonts": &_bintree_t{nil, map[string]*_bintree_t{
|
||||||
|
"FontAwesome.otf": &_bintree_t{static_fonts_fontawesome_otf, map[string]*_bintree_t{
|
||||||
|
}},
|
||||||
|
"fontawesome-webfont.eot": &_bintree_t{static_fonts_fontawesome_webfont_eot, map[string]*_bintree_t{
|
||||||
|
}},
|
||||||
|
"fontawesome-webfont.svg": &_bintree_t{static_fonts_fontawesome_webfont_svg, map[string]*_bintree_t{
|
||||||
|
}},
|
||||||
|
"fontawesome-webfont.ttf": &_bintree_t{static_fonts_fontawesome_webfont_ttf, map[string]*_bintree_t{
|
||||||
|
}},
|
||||||
|
"fontawesome-webfont.woff": &_bintree_t{static_fonts_fontawesome_webfont_woff, map[string]*_bintree_t{
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
"img": &_bintree_t{nil, map[string]*_bintree_t{
|
||||||
|
"icon.ico": &_bintree_t{static_img_icon_ico, map[string]*_bintree_t{
|
||||||
|
}},
|
||||||
|
"icon.png": &_bintree_t{static_img_icon_png, map[string]*_bintree_t{
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
"index.html": &_bintree_t{static_index_html, map[string]*_bintree_t{
|
||||||
|
}},
|
||||||
|
"js": &_bintree_t{nil, map[string]*_bintree_t{
|
||||||
|
"ace-pgsql.js": &_bintree_t{static_js_ace_pgsql_js, map[string]*_bintree_t{
|
||||||
|
}},
|
||||||
|
"ace.js": &_bintree_t{static_js_ace_js, map[string]*_bintree_t{
|
||||||
|
}},
|
||||||
|
"app.js": &_bintree_t{static_js_app_js, map[string]*_bintree_t{
|
||||||
|
}},
|
||||||
|
"jquery.js": &_bintree_t{static_js_jquery_js, map[string]*_bintree_t{
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
}}
|
||||||
|
|
||||||
|
// Restore an asset under the given directory
|
||||||
|
func RestoreAsset(dir, name string) error {
|
||||||
|
data, err := Asset(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
info, err := AssetInfo(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = os.MkdirAll(_filePath(dir, path.Dir(name)), os.FileMode(0755))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore assets under the given directory recursively
|
||||||
|
func RestoreAssets(dir, name string) error {
|
||||||
|
children, err := AssetDir(name)
|
||||||
|
if err != nil { // File
|
||||||
|
return RestoreAsset(dir, name)
|
||||||
|
} else { // Dir
|
||||||
|
for _, child := range children {
|
||||||
|
err = RestoreAssets(dir, path.Join(name, child))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func _filePath(dir, name string) string {
|
||||||
|
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
|
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
|
||||||
|
}
|
||||||
|
|
21
pkg/history/history.go
Normal file
21
pkg/history/history.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package history
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Record struct {
|
||||||
|
Query string `json:"query"`
|
||||||
|
Timestamp string `json:"timestamp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func New() []Record {
|
||||||
|
return make([]Record, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRecord(query string) Record {
|
||||||
|
return Record{
|
||||||
|
Query: query,
|
||||||
|
Timestamp: time.Now().String(),
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package main
|
package statements
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PG_DATABASES = `SELECT datname FROM pg_database WHERE NOT datistemplate ORDER BY datname ASC`
|
PG_DATABASES = `SELECT datname FROM pg_database WHERE NOT datistemplate ORDER BY datname ASC`
|
32
pkg/util/profiler.go
Normal file
32
pkg/util/profiler.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const MEGABYTE = 1024 * 1024
|
||||||
|
|
||||||
|
func runProfiler() {
|
||||||
|
logger := log.New(os.Stdout, "", 0)
|
||||||
|
m := &runtime.MemStats{}
|
||||||
|
|
||||||
|
for {
|
||||||
|
runtime.ReadMemStats(m)
|
||||||
|
|
||||||
|
logger.Printf(
|
||||||
|
"[DEBUG] Goroutines: %v, Mem used: %v (%v mb), Mem acquired: %v (%v mb)\n",
|
||||||
|
runtime.NumGoroutine(),
|
||||||
|
m.Alloc, m.Alloc/MEGABYTE,
|
||||||
|
m.Sys, m.Sys/MEGABYTE,
|
||||||
|
)
|
||||||
|
|
||||||
|
time.Sleep(time.Second * 30)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func StartProfiler() {
|
||||||
|
go runProfiler()
|
||||||
|
}
|
30
profiler.go
30
profiler.go
@ -1,30 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const MEGABYTE = 1024 * 1024
|
|
||||||
|
|
||||||
func startRuntimeProfiler() {
|
|
||||||
go func() {
|
|
||||||
logger := log.New(os.Stdout, "", 0)
|
|
||||||
m := &runtime.MemStats{}
|
|
||||||
|
|
||||||
for {
|
|
||||||
runtime.ReadMemStats(m)
|
|
||||||
|
|
||||||
logger.Printf(
|
|
||||||
"[DEBUG] Goroutines: %v, Mem used: %v (%v mb), Mem acquired: %v (%v mb)\n",
|
|
||||||
runtime.NumGoroutine(),
|
|
||||||
m.Alloc, m.Alloc/MEGABYTE,
|
|
||||||
m.Sys, m.Sys/MEGABYTE,
|
|
||||||
)
|
|
||||||
|
|
||||||
time.Sleep(time.Second * 30)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user