Restructure application

This commit is contained in:
Dan Sosedoff 2015-04-30 11:47:07 -05:00
parent 7a75447364
commit c513930e27
18 changed files with 686 additions and 710 deletions

View File

@ -22,7 +22,7 @@ test:
godep go test -cover
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:
@$(MAKE) --no-print-directory assets BINDATA_OPTS="-debug"

File diff suppressed because one or more lines are too long

View File

@ -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
View File

@ -7,31 +7,18 @@ import (
"os/signal"
"github.com/gin-gonic/gin"
"github.com/jessevdk/go-flags"
_ "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"
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 dbClient *Client
var options Options
var options command.Options
func exitWithMessage(message string) {
fmt.Println("Error:", message)
@ -39,49 +26,48 @@ func exitWithMessage(message string) {
}
func initClient() {
if connectionSettingsBlank(options) {
if connection.IsBlank(command.Opts) {
return
}
client, err := NewClient()
cl, err := client.New()
if err != nil {
exitWithMessage(err.Error())
}
if options.Debug {
fmt.Println("Server connection string:", client.connectionString)
if command.Opts.Debug {
fmt.Println("Server connection string:", cl.ConnectionString)
}
fmt.Println("Connecting to server...")
err = client.Test()
err = cl.Test()
if err != nil {
exitWithMessage(err.Error())
}
fmt.Println("Checking tables...")
_, err = client.Tables()
_, err = cl.Tables()
if err != nil {
exitWithMessage(err.Error())
}
dbClient = client
api.DbClient = cl
}
func initOptions() {
_, err := flags.ParseArgs(&options, os.Args)
err := command.ParseOptions()
if err != nil {
os.Exit(1)
}
if options.Url == "" {
options.Url = os.Getenv("DATABASE_URL")
}
options = command.Opts
if options.Version {
fmt.Printf("pgweb v%s\n", VERSION)
os.Exit(0)
}
fmt.Println("Pgweb version", VERSION)
}
func startServer() {
@ -93,7 +79,7 @@ func startServer() {
router.Use(gin.BasicAuth(auth))
}
setupRoutes(router)
api.SetupRoutes(router)
fmt.Println("Starting server...")
go func() {
@ -129,20 +115,19 @@ func openPage() {
func main() {
initOptions()
fmt.Println("Pgweb version", VERSION)
initClient()
if dbClient != nil {
defer dbClient.db.Close()
if api.DbClient != nil {
defer api.DbClient.Close()
}
if !options.Debug {
gin.SetMode("release")
}
// Print memory usage every 30 seconds with debug flag
if options.Debug {
startRuntimeProfiler()
util.StartProfiler()
}
startServer()

View File

@ -1,4 +1,4 @@
package main
package api
import (
"errors"
@ -10,6 +10,11 @@ import (
"time"
"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{
@ -20,6 +25,8 @@ var extraMimeTypes = map[string]string{
".svg": "image/svg+xml",
}
var DbClient *client.Client
type Error struct {
Message string `json:"error"`
}
@ -43,7 +50,7 @@ func assetContentType(name string) string {
return result
}
func setupRoutes(router *gin.Engine) {
func SetupRoutes(router *gin.Engine) {
router.GET("/", API_Home)
router.GET("/static/*path", API_ServeAsset)
@ -79,7 +86,7 @@ func ApiMiddleware() gin.HandlerFunc {
}
return func(c *gin.Context) {
if dbClient != nil {
if DbClient != nil {
c.Next()
return
}
@ -106,7 +113,7 @@ func ApiMiddleware() gin.HandlerFunc {
}
func API_Home(c *gin.Context) {
data, err := Asset("static/index.html")
data, err := data.Asset("static/index.html")
if err != nil {
c.String(400, err.Error())
@ -124,41 +131,41 @@ func API_Connect(c *gin.Context) {
return
}
opts := Options{Url: url}
url, err := formatConnectionUrl(opts)
opts := command.Options{Url: url}
url, err := connection.FormatUrl(opts)
if err != nil {
c.JSON(400, Error{err.Error()})
return
}
client, err := NewClientFromUrl(url)
cl, err := client.NewFromUrl(url)
if err != nil {
c.JSON(400, Error{err.Error()})
return
}
err = client.Test()
err = cl.Test()
if err != nil {
c.JSON(400, Error{err.Error()})
return
}
info, err := client.Info()
info, err := cl.Info()
if err == nil {
if dbClient != nil {
dbClient.db.Close()
if DbClient != nil {
DbClient.Close()
}
dbClient = client
DbClient = cl
}
c.JSON(200, info.Format()[0])
}
func API_GetDatabases(c *gin.Context) {
names, err := dbClient.Databases()
names, err := DbClient.Databases()
if err != nil {
c.JSON(400, NewError(err))
@ -191,7 +198,7 @@ func API_ExplainQuery(c *gin.Context) {
}
func API_GetSchemas(c *gin.Context) {
names, err := dbClient.Schemas()
names, err := DbClient.Schemas()
if err != nil {
c.JSON(400, NewError(err))
@ -202,7 +209,7 @@ func API_GetSchemas(c *gin.Context) {
}
func API_GetTables(c *gin.Context) {
names, err := dbClient.Tables()
names, err := DbClient.Tables()
if err != nil {
c.JSON(400, NewError(err))
@ -213,7 +220,7 @@ func API_GetTables(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 {
c.JSON(400, NewError(err))
@ -243,13 +250,13 @@ func API_GetTableRows(c *gin.Context) {
limit = num
}
opts := RowsOptions{
opts := client.RowsOptions{
Limit: limit,
SortColumn: c.Request.FormValue("sort_column"),
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 {
c.JSON(400, NewError(err))
@ -260,7 +267,7 @@ func API_GetTableRows(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 {
c.JSON(400, NewError(err))
@ -271,11 +278,11 @@ func API_GetTableInfo(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) {
res, err := dbClient.Info()
res, err := DbClient.Info()
if err != nil {
c.JSON(400, NewError(err))
@ -286,7 +293,7 @@ func API_ConnectionInfo(c *gin.Context) {
}
func API_Activity(c *gin.Context) {
res, err := dbClient.Activity()
res, err := DbClient.Activity()
if err != nil {
c.JSON(400, NewError(err))
return
@ -296,7 +303,7 @@ func API_Activity(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 {
c.JSON(400, NewError(err))
@ -307,7 +314,7 @@ func API_TableIndexes(c *gin.Context) {
}
func API_HandleQuery(query string, c *gin.Context) {
result, err := dbClient.Query(query)
result, err := DbClient.Query(query)
if err != nil {
c.JSON(400, NewError(err))
@ -327,7 +334,7 @@ func API_HandleQuery(query string, c *gin.Context) {
}
func API_Bookmarks(c *gin.Context) {
bookmarks, err := readAllBookmarks(bookmarksPath())
bookmarks, err := bookmarks.ReadAll(bookmarks.Path())
if err != nil {
c.JSON(400, NewError(err))
@ -339,7 +346,7 @@ func API_Bookmarks(c *gin.Context) {
func API_ServeAsset(c *gin.Context) {
path := "static" + c.Params.ByName("path")
data, err := Asset(path)
data, err := data.Asset(path)
if err != nil {
c.String(400, err.Error())

View File

@ -1,4 +1,4 @@
package main
package api
import (
"testing"

View File

@ -1,4 +1,4 @@
package main
package bookmarks
import (
"fmt"
@ -37,12 +37,12 @@ func fileBasename(path string) string {
return strings.Replace(filename, filepath.Ext(path), "", 1)
}
func bookmarksPath() string {
func Path() string {
path, _ := homedir.Dir()
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{}
files, err := ioutil.ReadDir(path)

View File

@ -1,4 +1,4 @@
package main
package bookmarks
import (
"testing"

View File

@ -1,4 +1,4 @@
package main
package client
import (
"bytes"
@ -7,12 +7,16 @@ import (
"reflect"
"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 {
db *sqlx.DB
history []HistoryRecord
connectionString string
History []history.Record
ConnectionString string
}
type Row []interface{}
@ -29,10 +33,10 @@ type RowsOptions struct {
SortOrder string // Sort direction (ASC, DESC)
}
func NewClient() (*Client, error) {
str, err := buildConnectionString(options)
func New() (*Client, error) {
str, err := connection.BuildString(command.Opts)
if options.Debug && str != "" {
if command.Opts.Debug && str != "" {
fmt.Println("Creating a new client for:", str)
}
@ -48,15 +52,15 @@ func NewClient() (*Client, error) {
client := Client{
db: db,
connectionString: str,
history: NewHistory(),
ConnectionString: str,
History: history.New(),
}
return &client, nil
}
func NewClientFromUrl(url string) (*Client, error) {
if options.Debug {
func NewFromUrl(url string) (*Client, error) {
if command.Opts.Debug {
fmt.Println("Creating a new client for:", url)
}
@ -68,8 +72,8 @@ func NewClientFromUrl(url string) (*Client, error) {
client := Client{
db: db,
connectionString: url,
history: NewHistory(),
ConnectionString: url,
History: history.New(),
}
return &client, nil
@ -80,23 +84,23 @@ func (client *Client) Test() error {
}
func (client *Client) Info() (*Result, error) {
return client.query(PG_INFO)
return client.query(statements.PG_INFO)
}
func (client *Client) Databases() ([]string, error) {
return client.fetchRows(PG_DATABASES)
return client.fetchRows(statements.PG_DATABASES)
}
func (client *Client) Schemas() ([]string, error) {
return client.fetchRows(PG_SCHEMAS)
return client.fetchRows(statements.PG_SCHEMAS)
}
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) {
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) {
@ -118,11 +122,11 @@ func (client *Client) TableRows(table string, opts RowsOptions) (*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) {
res, err := client.query(PG_TABLE_INDEXES, table)
res, err := client.query(statements.PG_TABLE_INDEXES, table)
if err != nil {
return nil, err
@ -133,7 +137,7 @@ func (client *Client) TableIndexes(table string) (*Result, error) {
// Returns all active queriers on the server
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) {
@ -141,7 +145,7 @@ func (client *Client) Query(query string) (*Result, error) {
// Save history records only if query did not fail
if err == nil {
client.history = append(client.history, NewHistoryRecord(query))
client.History = append(client.History, history.NewRecord(query))
}
return res, err
@ -231,6 +235,11 @@ func (res *Result) CSV() []byte {
return buff.Bytes()
}
// Close database connection
func (client *Client) Close() error {
return client.db.Close()
}
// Fetch all rows as strings for a single column
func (client *Client) fetchRows(q string) ([]string, error) {
res, err := client.query(q)

View File

@ -1,4 +1,4 @@
package main
package client
import (
"fmt"

39
pkg/command/options.go Normal file
View 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
}

View File

@ -1,4 +1,4 @@
package main
package connection
import (
"errors"
@ -6,6 +6,8 @@ import (
"os"
"os/user"
"strings"
"github.com/sosedoff/pgweb/pkg/command"
)
func currentUser() (string, error) {
@ -22,7 +24,7 @@ func currentUser() (string, error) {
return "", errors.New("Unable to detect OS user")
}
func formatConnectionUrl(opts Options) (string, error) {
func FormatUrl(opts command.Options) (string, error) {
url := opts.Url
// Make sure to only accept urls in a standard format
@ -50,13 +52,13 @@ func formatConnectionUrl(opts Options) (string, error) {
return url, nil
}
func connectionSettingsBlank(opts Options) bool {
func IsBlank(opts command.Options) bool {
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 != "" {
return formatConnectionUrl(opts)
return FormatUrl(opts)
}
// Try to detect user from current OS user

View File

@ -1,4 +1,4 @@
package main
package connection
import (
"fmt"

492
pkg/data/bindata.go Normal file
View 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
View 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(),
}
}

View File

@ -1,4 +1,4 @@
package main
package statements
const (
PG_DATABASES = `SELECT datname FROM pg_database WHERE NOT datistemplate ORDER BY datname ASC`

32
pkg/util/profiler.go Normal file
View 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()
}

View File

@ -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)
}
}()
}