2014-10-09 06:26:57 +04:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"os"
|
2014-10-17 01:38:11 +04:00
|
|
|
"os/exec"
|
|
|
|
"os/signal"
|
2014-11-16 20:58:05 +03:00
|
|
|
"os/user"
|
2014-10-31 03:30:34 +03:00
|
|
|
"strings"
|
2014-11-11 08:10:05 +03:00
|
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/jessevdk/go-flags"
|
|
|
|
_ "github.com/lib/pq"
|
2014-10-09 06:26:57 +04:00
|
|
|
)
|
|
|
|
|
2014-12-02 06:04:39 +03:00
|
|
|
const VERSION = "0.4.1"
|
2014-10-14 06:12:19 +04:00
|
|
|
|
2014-10-09 06:26:57 +04:00
|
|
|
var options struct {
|
2014-10-27 23:50:57 +03:00
|
|
|
Version bool `short:"v" long:"version" description:"Print version"`
|
|
|
|
Debug bool `short:"d" long:"debug" description:"Enable debugging mode" default:"false"`
|
2014-10-26 19:47:15 +03:00
|
|
|
Url string `long:"url" description:"Database connection string"`
|
2014-11-01 23:44:24 +03:00
|
|
|
Host string `long:"host" description:"Server hostname or IP"`
|
2014-10-26 19:47:15 +03:00
|
|
|
Port int `long:"port" description:"Server port" default:"5432"`
|
2014-11-01 23:44:24 +03:00
|
|
|
User string `long:"user" description:"Database user"`
|
2014-10-29 06:22:17 +03:00
|
|
|
Pass string `long:"pass" description:"Password for user"`
|
2014-11-01 23:44:24 +03:00
|
|
|
DbName string `long:"db" description:"Database name"`
|
2014-11-16 20:58:05 +03:00
|
|
|
Ssl string `long:"ssl" description:"SSL option"`
|
2014-10-31 02:51:49 +03:00
|
|
|
HttpHost string `long:"bind" description:"HTTP server host" default:"localhost"`
|
2014-10-27 02:43:33 +03:00
|
|
|
HttpPort uint `long:"listen" description:"HTTP server listen port" default:"8080"`
|
2014-10-30 03:45:12 +03:00
|
|
|
AuthUser string `long:"auth-user" description:"HTTP basic auth user"`
|
|
|
|
AuthPass string `long:"auth-pass" description:"HTTP basic auth password"`
|
2014-10-31 02:27:37 +03:00
|
|
|
SkipOpen bool `short:"s" long:"skip-open" description:"Skip browser open on start"`
|
2014-10-09 06:26:57 +04:00
|
|
|
}
|
|
|
|
|
2014-10-11 02:14:17 +04:00
|
|
|
var dbClient *Client
|
2014-10-10 09:03:03 +04:00
|
|
|
|
2014-10-12 07:33:59 +04:00
|
|
|
func exitWithMessage(message string) {
|
|
|
|
fmt.Println("Error:", message)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
2014-10-09 06:26:57 +04:00
|
|
|
func getConnectionString() string {
|
2014-10-10 07:42:52 +04:00
|
|
|
if options.Url != "" {
|
2014-10-31 03:30:34 +03:00
|
|
|
url := options.Url
|
|
|
|
|
2014-11-12 03:18:35 +03:00
|
|
|
if strings.Contains(url, "postgresql://") {
|
|
|
|
fmt.Println("Invalid URL format. It should match: postgres://user:password@host:port/db?sslmode=mode")
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
2014-11-16 20:58:05 +03:00
|
|
|
// Append sslmode parameter only if its defined as a flag and not present
|
|
|
|
// in the connection string.
|
2014-10-31 03:30:34 +03:00
|
|
|
if options.Ssl != "" && !strings.Contains(url, "sslmode") {
|
|
|
|
url += fmt.Sprintf("?sslmode=%s", options.Ssl)
|
|
|
|
}
|
|
|
|
|
|
|
|
return url
|
2014-10-10 07:42:52 +04:00
|
|
|
}
|
|
|
|
|
2014-11-16 20:58:05 +03:00
|
|
|
// Try to detect user from current OS user
|
|
|
|
if options.User == "" {
|
|
|
|
user, err := user.Current()
|
|
|
|
|
|
|
|
if err == nil {
|
|
|
|
options.User = user.Username
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-29 06:22:17 +03:00
|
|
|
str := fmt.Sprintf(
|
2014-11-16 20:58:05 +03:00
|
|
|
"host=%s port=%d user=%s dbname=%s",
|
2014-10-09 06:26:57 +04:00
|
|
|
options.Host, options.Port,
|
|
|
|
options.User, options.DbName,
|
|
|
|
)
|
2014-10-29 06:22:17 +03:00
|
|
|
|
2014-11-16 20:58:05 +03:00
|
|
|
if options.Ssl == "" {
|
|
|
|
// Disable ssl for localhost connections, most users have it disabled
|
|
|
|
if options.Host == "localhost" || options.Host == "127.0.0.1" {
|
|
|
|
options.Ssl = "disable"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if options.Ssl != "" {
|
|
|
|
str += fmt.Sprintf(" sslmode=%s", options.Ssl)
|
|
|
|
}
|
|
|
|
|
2014-10-29 06:22:17 +03:00
|
|
|
if options.Pass != "" {
|
|
|
|
str += fmt.Sprintf(" password=%s", options.Pass)
|
|
|
|
}
|
|
|
|
|
|
|
|
return str
|
2014-10-09 06:26:57 +04:00
|
|
|
}
|
|
|
|
|
2014-11-01 23:44:24 +03:00
|
|
|
func connectionSettingsBlank() bool {
|
|
|
|
return options.Host == "" &&
|
|
|
|
options.User == "" &&
|
|
|
|
options.DbName == "" &&
|
|
|
|
options.Url == ""
|
|
|
|
}
|
|
|
|
|
2014-10-09 06:26:57 +04:00
|
|
|
func initClient() {
|
2014-11-01 23:44:24 +03:00
|
|
|
if connectionSettingsBlank() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-10-09 06:26:57 +04:00
|
|
|
client, err := NewClient()
|
|
|
|
if err != nil {
|
2014-10-12 07:33:59 +04:00
|
|
|
exitWithMessage(err.Error())
|
2014-10-09 06:26:57 +04:00
|
|
|
}
|
|
|
|
|
2014-10-12 07:38:32 +04:00
|
|
|
fmt.Println("Connecting to server...")
|
|
|
|
err = client.Test()
|
2014-10-10 09:25:06 +04:00
|
|
|
if err != nil {
|
2014-10-12 07:33:59 +04:00
|
|
|
exitWithMessage(err.Error())
|
2014-10-10 09:25:06 +04:00
|
|
|
}
|
|
|
|
|
2014-10-12 07:38:32 +04:00
|
|
|
fmt.Println("Checking tables...")
|
2014-10-29 22:11:52 +03:00
|
|
|
_, err = client.Tables()
|
2014-10-12 07:32:10 +04:00
|
|
|
if err != nil {
|
2014-10-12 07:33:59 +04:00
|
|
|
exitWithMessage(err.Error())
|
2014-10-12 07:32:10 +04:00
|
|
|
}
|
|
|
|
|
2014-10-09 06:26:57 +04:00
|
|
|
dbClient = client
|
|
|
|
}
|
|
|
|
|
|
|
|
func initOptions() {
|
|
|
|
_, err := flags.ParseArgs(&options, os.Args)
|
|
|
|
|
|
|
|
if err != nil {
|
2014-10-11 02:20:14 +04:00
|
|
|
os.Exit(1)
|
2014-10-09 06:26:57 +04:00
|
|
|
}
|
2014-10-22 17:54:47 +04:00
|
|
|
|
2014-10-28 16:52:04 +03:00
|
|
|
if options.Url == "" {
|
|
|
|
options.Url = os.Getenv("DATABASE_URL")
|
|
|
|
}
|
|
|
|
|
2014-10-22 17:54:47 +04:00
|
|
|
if options.Version {
|
|
|
|
fmt.Printf("pgweb v%s\n", VERSION)
|
2014-10-27 23:49:43 +03:00
|
|
|
os.Exit(0)
|
2014-10-22 17:54:47 +04:00
|
|
|
}
|
2014-10-09 06:26:57 +04:00
|
|
|
}
|
|
|
|
|
2014-10-17 01:38:11 +04:00
|
|
|
func startServer() {
|
2014-10-09 06:26:57 +04:00
|
|
|
router := gin.Default()
|
2014-10-10 04:05:51 +04:00
|
|
|
|
2014-10-30 03:45:12 +03:00
|
|
|
// Enable HTTP basic authentication only if both user and password are set
|
|
|
|
if options.AuthUser != "" && options.AuthPass != "" {
|
|
|
|
auth := map[string]string{options.AuthUser: options.AuthPass}
|
|
|
|
router.Use(gin.BasicAuth(auth))
|
|
|
|
}
|
|
|
|
|
2014-10-13 22:55:19 +04:00
|
|
|
router.GET("/", API_Home)
|
2014-11-01 06:37:58 +03:00
|
|
|
router.POST("/connect", API_Connect)
|
2014-10-16 01:05:23 +04:00
|
|
|
router.GET("/databases", API_GetDatabases)
|
2014-12-03 06:20:04 +03:00
|
|
|
router.GET("/connection", API_ConnectionInfo)
|
2014-10-09 06:26:57 +04:00
|
|
|
router.GET("/tables", API_GetTables)
|
2014-10-11 22:20:16 +04:00
|
|
|
router.GET("/tables/:table", API_GetTable)
|
2014-10-18 07:30:08 +04:00
|
|
|
router.GET("/tables/:table/info", API_GetTableInfo)
|
2014-10-11 22:20:16 +04:00
|
|
|
router.GET("/tables/:table/indexes", API_TableIndexes)
|
2014-10-11 02:14:17 +04:00
|
|
|
router.GET("/query", API_RunQuery)
|
|
|
|
router.POST("/query", API_RunQuery)
|
2014-10-11 22:24:12 +04:00
|
|
|
router.GET("/explain", API_ExplainQuery)
|
|
|
|
router.POST("/explain", API_ExplainQuery)
|
2014-10-10 04:59:18 +04:00
|
|
|
router.GET("/history", API_History)
|
2014-10-13 23:40:56 +04:00
|
|
|
router.GET("/static/:type/:name", API_ServeAsset)
|
2014-10-10 04:05:51 +04:00
|
|
|
|
2014-10-14 03:40:17 +04:00
|
|
|
fmt.Println("Starting server...")
|
2014-10-31 02:51:49 +03:00
|
|
|
go router.Run(fmt.Sprintf("%v:%v", options.HttpHost, options.HttpPort))
|
2014-10-17 01:38:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
func handleSignals() {
|
|
|
|
c := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(c, os.Interrupt, os.Kill)
|
|
|
|
<-c
|
|
|
|
}
|
|
|
|
|
|
|
|
func openPage() {
|
2014-10-31 02:51:49 +03:00
|
|
|
url := fmt.Sprintf("http://%v:%v", options.HttpHost, options.HttpPort)
|
2014-10-26 19:47:15 +03:00
|
|
|
fmt.Println("To view database open", url, "in browser")
|
2014-10-17 01:38:11 +04:00
|
|
|
|
2014-10-31 02:27:37 +03:00
|
|
|
if options.SkipOpen {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-10-17 01:38:11 +04:00
|
|
|
_, err := exec.Command("which", "open").Output()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-10-26 19:47:15 +03:00
|
|
|
exec.Command("open", url).Output()
|
2014-10-17 01:38:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
initOptions()
|
2014-11-16 21:01:13 +03:00
|
|
|
|
|
|
|
fmt.Println("Pgweb version", VERSION)
|
2014-10-17 01:38:11 +04:00
|
|
|
initClient()
|
|
|
|
|
2014-11-01 23:44:24 +03:00
|
|
|
if dbClient != nil {
|
|
|
|
defer dbClient.db.Close()
|
|
|
|
}
|
2014-10-17 01:38:11 +04:00
|
|
|
|
|
|
|
if !options.Debug {
|
|
|
|
gin.SetMode("release")
|
|
|
|
}
|
|
|
|
|
2014-11-21 05:00:45 +03:00
|
|
|
if options.Debug {
|
2014-11-23 23:44:42 +03:00
|
|
|
startRuntimeProfiler()
|
2014-11-21 05:00:45 +03:00
|
|
|
}
|
|
|
|
|
2014-10-17 01:38:11 +04:00
|
|
|
startServer()
|
|
|
|
openPage()
|
|
|
|
handleSignals()
|
2014-10-09 06:26:57 +04:00
|
|
|
}
|