re-write all the things

This commit is contained in:
jms 2020-11-07 20:24:12 -06:00
parent 313ea55f57
commit 1f05d1579c
18 changed files with 724 additions and 446 deletions

View File

@ -25,7 +25,7 @@ import (
"github.com/spf13/viper"
"io/ioutil"
"log"
"shiny-pancake/database"
"shiny-pancake/internal/model"
"os"
"shiny-pancake/logger"
@ -90,7 +90,7 @@ func configWriter(f *tview.Form){
var fp string
var hostFile string
var cfg []byte
var c database.HostDetails
var c model.HostDetails
var hd string
hd = viper.GetString("hostsDirectory")
@ -120,7 +120,7 @@ func configWriter(f *tview.Form){
}
file, err := os.Create(fp)
c = database.HostDetails{ip[3], hostFile, ip[1], ip[2]}
c = model.HostDetails{ip[3], hostFile, ip[1], ip[2]}
if err != nil {
log.Fatal("unable to create file: " + fp)
}

View File

@ -26,8 +26,9 @@ import (
"io/ioutil"
"log"
"os"
"shiny-pancake/database"
"shiny-pancake/internal/database"
"shiny-pancake/internal/gui"
"shiny-pancake/internal/model"
"shiny-pancake/logger"
"strconv"
"strings"
@ -83,9 +84,10 @@ to quickly create a Cobra application.`,
// Print out the final choice.
if fn = <-result; len(fn) !=0 {
var hostDir string
var db *database.Database
hostDir = viper.GetString("hostsDirectory")
file, _ := ioutil.ReadFile(hostDir + "/"+ fn)
data := database.HostDetails{}
data := model.HostDetails{}
err := json.Unmarshal(file, &data)
if err != nil {
var l logger.Log
@ -97,7 +99,13 @@ to quickly create a Cobra application.`,
var l logger.Log
l = logger.Log{"info", logrus.Fields{"loaded": fn }, "config loading to ui"}
logger.Lgr(&l)
gui.Gui(data)
db = database.New(&data)
// Initialise the Gui / Tui
gui := gui.New(db)
if err := gui.Start(); err != nil {
log.Fatalf("main: Cannot start tui: %s", err)
}
}
},

View File

@ -28,6 +28,7 @@ var (
hostsDirectory = "hosts"
logsDirectory = "logs"
configDirectory = "config"
queryDirectory = "query"
)
var cfgFile string
@ -91,6 +92,7 @@ func initConfig() {
viper.SetDefault("hostsDirectory", home + "/" + configHome + "/" + hostsDirectory)
viper.SetDefault("logsDirectory", home + "/" + configHome + "/" + logsDirectory)
viper.SetDefault("configDirectory", home + "/" +configHome + "/" + configDirectory)
viper.SetDefault("queryDirectory", home + "/" +configHome + "/" + queryDirectory)
viper.SetDefault("logLevel", "debug")
viper.SetDefault("logfile", "log")
viper.AutomaticEnv() // read in environment variables that match
@ -118,6 +120,7 @@ func directoryInit() {
logsDirectory,
hostsDirectory,
configDirectory,
queryDirectory,
}
for _, dr := range s {
if _, err := os.Stat(cfgDir + "/" + dr); os.IsNotExist(err) {

View File

@ -1,68 +0,0 @@
package database
import (
_ "github.com/lib/pq"
)
//func DbConn(c HostDetails) (*sql.DB, error) {
// var h string
// var u string
// var pw string
// var dbn string
// var psqlInfo string
// h = c.Hostname
// u = c.Username
// pw = c.Secret
// dbn = c.DatabaseName
//
// logger.Logger("[LOG] connection details: host = " + h + " username = " + u)
//
// found, testUserName := data.ViperPgmConfig("test_user", "r")
// if found == true {
// logger.Logger("[LOG] found test user in .pgm.yaml")
// }
// psqlInfo = fmt.Sprintf("host=%s port=%d user=%s "+
// "password=%s dbname=%s sslmode=disable",
// h, 5432, u, pw, dbn)
// db, err := sql.Open("postgres", psqlInfo)
// if err != nil {
// var db *sql.DB
// switch u {
// case testUserName:
// logger.Logger("[ERROR] go tests expects to connect with the docker compose database." +
// "[ERROR] run \"docker-compose up\" and re-run tests")
// err := errors.New("unable to ping db" + dbn + "local db is closed, please run docker-compose up")
// return db, err
// default:
// logger.Logger("[ERROR] db error: " + err.Error())
// return db, err
// }
//
// }
// logger.Logger("[LOG] pinging db " + dbn)
// ctx := context.Background()
// err = db.PingContext(ctx)
//
// if err != nil {
// var db *sql.DB
// switch u {
// case testUserName:
// logger.Logger("[ERROR] db error, unable to ping: " + err.Error()+
// "\n[ERROR] ensure docker database is running and re-run tests")
// fmt.Println("local db is closed, please run docker-compose up")
// err1 := errors.New("unable to ping db" + dbn)
// return db, err1
// default:
// logger.Logger("[ERROR] db error: " + err.Error())
// return db, err
// }
// }
// return db, nil
//}
type HostDetails struct {
Secret string `json:"secret"`
Hostname string `json:"hostname"`
DatabaseName string `json:"databaseName"`
Username string `json:"username"`
}

View File

@ -0,0 +1,18 @@
version: '3.7'
services:
pg-db:
image: postgres:11.5
container_name: pg-db
expose:
- "5432"
ports:
- "5432:5432"
environment:
- POSTGRES_DB=localdb
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
volumes:
- ${PWD}/internal/database/container/init.sql:/docker-entrypoint-initdb.d/init.sql

5
go.mod
View File

@ -4,7 +4,9 @@ go 1.15
require (
github.com/charmbracelet/bubbletea v0.12.2
github.com/gdamore/tcell/v2 v2.0.1-0.20201017141208-acf90d56d591
github.com/gdamore/tcell v1.2.0
github.com/gdamore/tcell/v2 v2.0.1-0.20201019142633-1057d5591ed1
github.com/idlephysicist/cave-logger v1.2.2
github.com/lib/pq v1.8.0
github.com/mitchellh/go-homedir v1.1.0
github.com/muesli/termenv v0.7.4
@ -12,4 +14,5 @@ require (
github.com/sirupsen/logrus v1.7.0
github.com/spf13/cobra v1.1.1
github.com/spf13/viper v1.7.1
gitlab.com/tslocum/cview v1.5.1
)

31
go.sum
View File

@ -13,6 +13,7 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiy
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@ -23,6 +24,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/bvinc/go-sqlite-lite v0.6.1 h1:JU8Rz5YAOZQiU3WEulKF084wfXpytRiqD2IaW2QjPz4=
github.com/bvinc/go-sqlite-lite v0.6.1/go.mod h1:2GiE60NUdb0aNhDdY+LXgrqAVDpi2Ijc6dB6ZMp9x6s=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/charmbracelet/bubbletea v0.12.2 h1:y9Yo2Pv8tcm3mAJsWONGsmHhzrbNxJVxpVtemikxE9A=
github.com/charmbracelet/bubbletea v0.12.2/go.mod h1:3gZkYELUOiEUOp0bTInkxguucy/xRbGSOcbMs1geLxg=
@ -43,8 +46,14 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell v1.1.2/go.mod h1:h3kq4HO9l2On+V9ed8w8ewqQEmGCSSHOgQ+2h8uzurE=
github.com/gdamore/tcell v1.2.0 h1:ikixzsxc8K8o3V2/CEmyoEW8mJZaNYQQ3NP3VIQdUe4=
github.com/gdamore/tcell v1.2.0/go.mod h1:Hjvr+Ofd+gLglo7RYKxxnzCBmev3BzsS67MebKS4zMM=
github.com/gdamore/tcell/v2 v2.0.0-dev/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA=
github.com/gdamore/tcell/v2 v2.0.1-0.20201017141208-acf90d56d591 h1:0WWUDZ1oxq7NxVyGo8M3KI5jbkiwNAdZFFzAdC68up4=
github.com/gdamore/tcell/v2 v2.0.1-0.20201017141208-acf90d56d591/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA=
github.com/gdamore/tcell/v2 v2.0.1-0.20201019142633-1057d5591ed1 h1:gp9ujdOQmQf1gMvqOYYgxdMS5tRpRGE3HAgRH4Hgzd4=
github.com/gdamore/tcell/v2 v2.0.1-0.20201019142633-1057d5591ed1/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
@ -97,6 +106,8 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/idlephysicist/cave-logger v1.2.2 h1:PoGzbYx9rJ1l/hZDA3bDJUW9M++fTMAlAbC7AW8thbI=
github.com/idlephysicist/cave-logger v1.2.2/go.mod h1:BZG2qItPt8BnN/+T837nzY57qRihQXcb2YXUZoRYcrU=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
@ -106,11 +117,13 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
@ -118,6 +131,7 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
@ -154,8 +168,10 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rivo/tview v0.0.0-20190829161255-f8bc69b90341/go.mod h1:+rKjP5+h9HMwWRpAfhIkkQ9KE3m3Nz5rwn7YtUpwgqk=
github.com/rivo/tview v0.0.0-20201018122409-d551c850a743 h1:9BBjVJTRxuYBeCAv9DFH2hSzY0ujLx5sxMg5D3K/Xeg=
github.com/rivo/tview v0.0.0-20201018122409-d551c850a743/go.mod h1:t7mcA3nlK9dxD1DMoz/DQRMWFMkGBUj6rJBM5VNfLFA=
github.com/rivo/uniseg v0.0.0-20190513083848-b9f5b9457d44/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
@ -165,6 +181,7 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
@ -184,9 +201,14 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
gitlab.com/tslocum/cbind v0.1.3 h1:FT/fTQ4Yj3eo5021lB3IbkIt8eVtYGhrw/xur+cjvUU=
gitlab.com/tslocum/cbind v0.1.3/go.mod h1:RvwYE3auSjBNlCmWeGspzn+jdLUVQ8C2QGC+0nP9ChI=
gitlab.com/tslocum/cview v1.5.1 h1:1zHvNJvyhHxAVBf3FsSzzqgB15mrojNEm7VbvLXWcpQ=
gitlab.com/tslocum/cview v1.5.1/go.mod h1:BRtUi0zXzVXufhqFm/1GD7GL+iznKh5m9pEGN19SnKA=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
@ -249,24 +271,31 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190913121621-c3b328c6e5a7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201013132646-2da7054afaeb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201017003518-b09fb700fbb7 h1:XtNJkfEjb4zR3q20BBBcYUykVOEMgZeIUOpBPfNYgxg=
golang.org/x/sys v0.0.0-20201017003518-b09fb700fbb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1 h1:a/mKvvZr9Jcc8oKfcmgzyp7OwF73JPWsQLvH1z2Kxck=
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -311,11 +340,13 @@ google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -0,0 +1,21 @@
create schema test;
create table test.employees (
Id SERIAL NOT NULL PRIMARY KEY,
Name VARCHAR(50),
Location VARCHAR(50)
);
create table test.company (
cid SERIAL NOT NULL PRIMARY KEY,
Name VARCHAR(50)
);
create table test.employer (
eid SERIAL NOT NULL PRIMARY KEY,
Id INT,
cid INT
);
ALTER TABLE test.employer add constraint emp_fk foreign key(Id) REFERENCES test.employees(id) ON UPDATE CASCADE ON DELETE CASCADE;
insert into test.employees (name,location) VALUES ('bob','kalamazoo');
create role test with login password 'test';
insert into test.employees (name,location) VALUES ('cheeks','magoo');
update test.employees set location = 'buttsville' where name = 'cheeks';
grant all privileges on schema test to test;

View File

@ -0,0 +1,77 @@
package database
import (
"context"
"database/sql"
"fmt"
_ "github.com/lib/pq"
"github.com/sirupsen/logrus"
"shiny-pancake/internal/model"
)
const datetime = `2006-01-02T15:04:05Z`
const date = `2006-01-02`
type Database struct {
conn *sql.DB
log *logrus.Logger
ctx context.Context
}
func New(c *model.HostDetails) *Database {
var db Database
var h string
var u string
var pw string
var dbn string
var psqlInfo string
var log *logrus.Logger
h = c.Hostname
u = c.Username
pw = c.Secret
dbn = c.DatabaseName
db.ctx = context.Background()
db.log = log
psqlInfo = fmt.Sprintf("host=%s port=%d user=%s "+
"password=%s dbname=%s sslmode=disable",
h, 5432, u, pw, dbn)
dbC, err := sql.Open("postgres", psqlInfo)
if err != nil {
log.Fatalf("database.new: Cannot establish connection to %s: %v", h, err)
}
db.conn = dbC
return &db
}
func (db *Database) UserSessions() ([]*model.Session, error) {
query := `select usename, count(usename) from pg_stat_activity where usename is not null group by usename`
rows, err := db.conn.Query(query)
if err != nil {
// handle this error better than this
panic(err)
}
sessions := make([]*model.Session, 0)
defer rows.Close()
for rows.Next() {
var sessCount model.Session
err = rows.Scan(&sessCount.Username, &sessCount.Count)
if err != nil {
// handle this error
panic(err)
}
sessions = append(sessions, &sessCount)
}
// get any error encountered during iteration
err = rows.Err()
if err != nil {
panic(err)
}
return sessions, err
}

View File

@ -0,0 +1,4 @@
[sessions.Current]
columns = 2
headers = ["users", "count users"]
query = "select usename, count(usename) from pg_stat_activity where usename is not null"

View File

@ -1,367 +1,367 @@
package gui
import (
"database/sql"
"fmt"
"github.com/sirupsen/logrus"
"reflect"
"shiny-pancake/database"
"shiny-pancake/logger"
"strconv"
"sync"
"time"
"github.com/gdamore/tcell/v2"
_ "github.com/lib/pq"
"github.com/rivo/tview"
)
const (
batchSize = 80 // The number of rows loaded per batch.
finderPage = "*finder*" // The name of the Finder page.
)
var (
app *tview.Application // The tview application.
pages *tview.Pages // The application pages.
finderFocus tview.Primitive // The primitive in the Finder that last had focus.
)
// Main entry point.
func data(host database.HostDetails) {
var h string
var u string
var pw string
var dbn string
var psqlInfo string
h = host.Hostname
u = host.Username
pw = host.Secret
dbn = host.DatabaseName
// Start the application.
psqlInfo = fmt.Sprintf("host=%s port=%d user=%s "+
"password=%s dbname=%s sslmode=disable",
h, 5432, u, pw, dbn)
app = tview.NewApplication()
finder(psqlInfo)
if err := app.Run(); err != nil {
fmt.Printf("Error running application: %s\n", err)
}
}
// Sets up a "Finder" used to navigate the databases, tables, and columns.
func finder(connString string) {
// Create the basic objects.
databases := tview.NewList().ShowSecondaryText(false)
databases.SetBorder(true).SetTitle("Databases")
columns := tview.NewTable().SetBorders(true)
columns.SetBorder(true).SetTitle("Columns")
tables := tview.NewList()
tables.ShowSecondaryText(false).
SetDoneFunc(func() {
tables.Clear()
columns.Clear()
app.SetFocus(databases)
})
tables.SetBorder(true).SetTitle("Tables")
// Create the layout.
flex := tview.NewFlex().
AddItem(databases, 0, 1, true).
AddItem(tables, 0, 1, false).
AddItem(columns, 0, 3, false)
// We keep one connection pool per database.
dbMutex := sync.Mutex{}
dbs := make(map[string]*sql.DB)
getDB := func(database string) *sql.DB {
// Connect to a new database.
dbMutex.Lock()
defer dbMutex.Unlock()
if db, ok := dbs[database]; ok {
return db
}
db, err := sql.Open("postgres", connString)
if err != nil {
panic(err)
}
dbs[database] = db
return db
}
// Get a list of all databases.
generalDB, err := sql.Open("postgres", connString)
if err != nil {
panic(err)
}
defer generalDB.Close() // We really close the DB because we only use it for this one query.
rows, err := generalDB.Query("select datname from pg_database where datistemplate = false")
if err != nil {
panic(err)
}
defer rows.Close()
for rows.Next() {
var dbName string
if err := rows.Scan(&dbName); err != nil {
panic(err)
}
databases.AddItem(dbName, "", 0, func() {
// A database was selected. Show all of its tables.
columns.Clear()
tables.Clear()
db := getDB(dbName)
t, err := db.Query("select table_name from information_schema.tables where table_schema not in ('information_schema','pg_catalog')")
if err != nil {
panic(err)
}
defer t.Close()
for t.Next() {
var tableName string
if err := t.Scan(&tableName); err != nil {
panic(err)
}
tables.AddItem(tableName, "", 0, nil)
}
if err := t.Err(); err != nil {
panic(err)
}
app.SetFocus(tables)
// When the user navigates to a table, show its columns.
tables.SetChangedFunc(func(i int, tableName string, t string, s rune) {
// A table was selected. Show its columns.
columns.Clear()
c, err := db.Query(`
select
c.column_name,
c.is_nullable,
c.data_type,
c.character_maximum_length,
c.numeric_precision,
c.numeric_scale,
c.ordinal_position,
tc.constraint_type pkey
from
information_schema.columns c
left join
information_schema.constraint_column_usage as ccu
on
c.table_schema = ccu.table_schema
and c.table_name = ccu.table_name
and c.column_name = ccu.column_name
left join
information_schema.table_constraints as tc
on
ccu.constraint_schema = tc.constraint_schema
and ccu.constraint_name = tc.constraint_name
where
c.table_schema not in ('information_schema','pg_catalog')
and c.table_name = $1
`, tableName)
if err != nil {
var l logger.Log
l = logger.Log{"error", logrus.Fields{"database": err.Error() }, "an error occured in view."}
logger.Lgr(&l)
}
defer c.Close()
columns.SetCell(0, 0, &tview.TableCell{Text: "Name", Align: tview.AlignCenter, Color: tcell.ColorYellow}).
SetCell(0, 1, &tview.TableCell{Text: "Type", Align: tview.AlignCenter, Color: tcell.ColorYellow}).
SetCell(0, 2, &tview.TableCell{Text: "Size", Align: tview.AlignCenter, Color: tcell.ColorYellow}).
SetCell(0, 3, &tview.TableCell{Text: "Null", Align: tview.AlignCenter, Color: tcell.ColorYellow}).
SetCell(0, 4, &tview.TableCell{Text: "Constraint", Align: tview.AlignCenter, Color: tcell.ColorYellow})
for c.Next() {
var (
columnName, isNullable, dataType string
constraintType sql.NullString
size, numericPrecision, numericScale sql.NullInt64
ordinalPosition int
)
if err := c.Scan(&columnName,
&isNullable,
&dataType,
&size,
&numericPrecision,
&numericScale,
&ordinalPosition,
&constraintType,
); err != nil {
panic(err)
}
sizeText := ""
if size.Valid {
sizeText = strconv.Itoa(int(size.Int64))
} else if numericPrecision.Valid {
sizeText = strconv.Itoa(int(numericPrecision.Int64))
if numericScale.Valid {
sizeText += "," + strconv.Itoa(int(numericScale.Int64))
}
}
color := tcell.ColorWhite
if constraintType.Valid {
color = map[string]tcell.Color{
"CHECK": tcell.ColorGreen,
"FOREIGN KEY": tcell.ColorDarkMagenta,
"PRIMARY KEY": tcell.ColorRed,
"UNIQUE": tcell.ColorDarkCyan,
}[constraintType.String]
}
columns.SetCell(ordinalPosition, 0, &tview.TableCell{Text: columnName, Color: color}).
SetCell(ordinalPosition, 1, &tview.TableCell{Text: dataType, Color: color}).
SetCell(ordinalPosition, 2, &tview.TableCell{Text: sizeText, Align: tview.AlignRight, Color: color}).
SetCell(ordinalPosition, 3, &tview.TableCell{Text: isNullable, Align: tview.AlignRight, Color: color}).
SetCell(ordinalPosition, 4, &tview.TableCell{Text: constraintType.String, Align: tview.AlignLeft, Color: color})
}
if err := c.Err(); err != nil {
panic(err)
}
})
tables.SetCurrentItem(0) // Trigger the initial selection.
// When the user selects a table, show its content.
tables.SetSelectedFunc(func(i int, tableName string, t string, s rune) {
content(db, dbName, tableName)
})
})
}
if err := rows.Err(); err != nil {
panic(err)
}
// Set up the pages and show the Finder.
pages = tview.NewPages().
AddPage(finderPage, flex, true, true)
app.SetRoot(pages, true)
}
// Shows the contents of the given table.
func content(db *sql.DB, dbName, tableName string) {
finderFocus = app.GetFocus()
// If this page already exists, just show it.
if pages.HasPage(dbName + "." + tableName) {
pages.SwitchToPage(dbName + "." + tableName)
return
}
var l logger.Log
l = logger.Log{"info", logrus.Fields{"database": tableName }, "selected table to view"}
logger.Lgr(&l)
var schemaName string
err := db.QueryRow(fmt.Sprintf("select table_schema from information_schema.tables where table_schema not in ('information_schema','pg_catalog') and table_name = '%s'", tableName)).Scan(&schemaName)
if err != nil {
panic(err)
}
// We display the data in a table embedded in a frame.
table := tview.NewTable().
SetFixed(1, 0).
SetBordersColor(tcell.ColorYellow)
frame := tview.NewFrame(table).
SetBorders(0, 0, 0, 0, 0, 0)
frame.SetBorder(true).
SetTitle(fmt.Sprintf(`Contents of table "%s.%s"`,schemaName, tableName))
// How many rows does this table have?
var rowCount int
err = db.QueryRow(fmt.Sprintf("select count(*) from %s.%s", schemaName, tableName)).Scan(&rowCount)
if err != nil {
panic(err)
}
// Load a batch of rows.
loadRows := func(offset int) {
rows, err := db.Query(fmt.Sprintf("select * from %s.%s limit $1 offset $2", schemaName, tableName), batchSize, offset)
if err != nil {
panic(err)
}
defer rows.Close()
// The first row in the table is the list of column names.
columnNames, err := rows.Columns()
if err != nil {
panic(err)
}
for index, name := range columnNames {
table.SetCell(0, index, &tview.TableCell{Text: name, Align: tview.AlignCenter, Color: tcell.ColorYellow})
}
// Read the rows.
columns := make([]interface{}, len(columnNames))
columnPointers := make([]interface{}, len(columns))
for index := range columnPointers {
columnPointers[index] = &columns[index]
}
for rows.Next() {
// Read the columns.
err := rows.Scan(columnPointers...)
if err != nil {
panic(err)
}
// Transfer them to the table.
row := table.GetRowCount()
for index, column := range columns {
switch value := column.(type) {
case int64:
table.SetCell(row, index, &tview.TableCell{Text: strconv.Itoa(int(value)), Align: tview.AlignRight, Color: tcell.ColorDarkCyan})
case float64:
table.SetCell(row, index, &tview.TableCell{Text: strconv.FormatFloat(value, 'f', 2, 64), Align: tview.AlignRight, Color: tcell.ColorDarkCyan})
case string:
table.SetCellSimple(row, index, value)
case time.Time:
t := value.Format("2006-01-02")
table.SetCell(row, index, &tview.TableCell{Text: t, Align: tview.AlignRight, Color: tcell.ColorDarkMagenta})
case []uint8:
str := make([]byte, len(value))
for index, num := range value {
str[index] = byte(num)
}
table.SetCell(row, index, &tview.TableCell{Text: string(str), Align: tview.AlignRight, Color: tcell.ColorGreen})
case nil:
table.SetCell(row, index, &tview.TableCell{Text: "NULL", Align: tview.AlignCenter, Color: tcell.ColorRed})
default:
// We've encountered a type that we don't know yet.
t := reflect.TypeOf(value)
str := "?nil?"
if t != nil {
str = "?" + t.String() + "?"
}
table.SetCellSimple(row, index, str)
}
}
}
if err := rows.Err(); err != nil {
panic(err)
}
// Show how much we've loaded.
frame.Clear()
loadMore := ""
if table.GetRowCount()-1 < rowCount {
loadMore = " - press Enter to load more"
}
loadMore = fmt.Sprintf("Loaded %d of %d rows%s. esc to return to previous menu", table.GetRowCount()-1, rowCount, loadMore)
frame.AddText(loadMore, false, tview.AlignCenter, tcell.ColorYellow)
}
// Load the first batch of rows.
loadRows(0)
// Handle key presses.
table.SetDoneFunc(func(key tcell.Key) {
switch key {
case tcell.KeyEscape:
// Go back to Finder.
pages.SwitchToPage(finderPage)
if finderFocus != nil {
app.SetFocus(finderFocus)
}
case tcell.KeyEnter:
// Load the next batch of rows.
loadRows(table.GetRowCount() - 1)
table.ScrollToEnd()
}
})
// Add a new page and show it.
pages.AddPage(dbName+"."+tableName, frame, true, true)
}
//
//import (
// "database/sql"
// "fmt"
// "github.com/sirupsen/logrus"
// "reflect"
// "shiny-pancake/internal/database"
// "shiny-pancake/logger"
// "strconv"
// "sync"
// "time"
//
// "github.com/gdamore/tcell/v2"
// _ "github.com/lib/pq"
// "github.com/rivo/tview"
//)
//
//const (
// batchSize = 80 // The number of rows loaded per batch.
// finderPage = "*finder*" // The name of the Finder page.
//)
//
//var (
// app *tview.Application // The tview application.
// panels *tview.Pages // The application panels.
// finderFocus tview.Primitive // The primitive in the Finder that last had focus.
//)
//
//// Main entry point.
//func data(host database.HostDetails) {
// var h string
// var u string
// var pw string
// var dbn string
// var psqlInfo string
// h = host.Hostname
// u = host.Username
// pw = host.Secret
// dbn = host.DatabaseName
// // Start the application.
// psqlInfo = fmt.Sprintf("host=%s port=%d user=%s "+
// "password=%s dbname=%s sslmode=disable",
// h, 5432, u, pw, dbn)
// app = tview.NewApplication()
// finder(psqlInfo)
// if err := app.Run(); err != nil {
// fmt.Printf("Error running application: %s\n", err)
// }
//}
//
//// Sets up a "Finder" used to navigate the databases, tables, and columns.
//func finder(connString string) {
// // Create the basic objects.
// databases := tview.NewList().ShowSecondaryText(false)
// databases.SetBorder(true).SetTitle("Databases")
// columns := tview.NewTable().SetBorders(true)
// columns.SetBorder(true).SetTitle("Columns")
// tables := tview.NewList()
// tables.ShowSecondaryText(false).
// SetDoneFunc(func() {
// tables.Clear()
// columns.Clear()
// app.SetFocus(databases)
// })
// tables.SetBorder(true).SetTitle("Tables")
//
// // Create the layout.
// flex := tview.NewFlex().
// AddItem(databases, 0, 1, true).
// AddItem(tables, 0, 1, false).
// AddItem(columns, 0, 3, false)
//
// // We keep one connection pool per database.
// dbMutex := sync.Mutex{}
// dbs := make(map[string]*sql.DB)
// getDB := func(database string) *sql.DB {
// // Connect to a new database.
// dbMutex.Lock()
// defer dbMutex.Unlock()
// if db, ok := dbs[database]; ok {
// return db
// }
// db, err := sql.Open("postgres", connString)
// if err != nil {
// panic(err)
// }
// dbs[database] = db
// return db
// }
//
// // Get a list of all databases.
// generalDB, err := sql.Open("postgres", connString)
// if err != nil {
// panic(err)
// }
// defer generalDB.Close() // We really close the DB because we only use it for this one query.
// rows, err := generalDB.Query("select datname from pg_database where datistemplate = false")
// if err != nil {
// panic(err)
// }
// defer rows.Close()
// for rows.Next() {
// var dbName string
// if err := rows.Scan(&dbName); err != nil {
// panic(err)
// }
// databases.AddItem(dbName, "", 0, func() {
// // A database was selected. Show all of its tables.
// columns.Clear()
// tables.Clear()
// db := getDB(dbName)
// t, err := db.Query("select table_name from information_schema.tables where table_schema not in ('information_schema','pg_catalog')")
// if err != nil {
// panic(err)
// }
// defer t.Close()
// for t.Next() {
// var tableName string
// if err := t.Scan(&tableName); err != nil {
// panic(err)
// }
// tables.AddItem(tableName, "", 0, nil)
// }
// if err := t.Err(); err != nil {
// panic(err)
// }
// app.SetFocus(tables)
//
// // When the user navigates to a table, show its columns.
// tables.SetChangedFunc(func(i int, tableName string, t string, s rune) {
// // A table was selected. Show its columns.
// columns.Clear()
// c, err := db.Query(`
// select
// c.column_name,
// c.is_nullable,
// c.data_type,
// c.character_maximum_length,
// c.numeric_precision,
// c.numeric_scale,
// c.ordinal_position,
// tc.constraint_type pkey
// from
// information_schema.columns c
// left join
// information_schema.constraint_column_usage as ccu
// on
// c.table_schema = ccu.table_schema
// and c.table_name = ccu.table_name
// and c.column_name = ccu.column_name
// left join
// information_schema.table_constraints as tc
// on
// ccu.constraint_schema = tc.constraint_schema
// and ccu.constraint_name = tc.constraint_name
// where
// c.table_schema not in ('information_schema','pg_catalog')
// and c.table_name = $1
// `, tableName)
// if err != nil {
// var l logger.Log
// l = logger.Log{"error", logrus.Fields{"database": err.Error()}, "an error occured in view."}
// logger.Lgr(&l)
// }
// defer c.Close()
// columns.SetCell(0, 0, &tview.TableCell{Text: "Name", Align: tview.AlignCenter, Color: tcell.ColorYellow}).
// SetCell(0, 1, &tview.TableCell{Text: "Type", Align: tview.AlignCenter, Color: tcell.ColorYellow}).
// SetCell(0, 2, &tview.TableCell{Text: "Size", Align: tview.AlignCenter, Color: tcell.ColorYellow}).
// SetCell(0, 3, &tview.TableCell{Text: "Null", Align: tview.AlignCenter, Color: tcell.ColorYellow}).
// SetCell(0, 4, &tview.TableCell{Text: "Constraint", Align: tview.AlignCenter, Color: tcell.ColorYellow})
// for c.Next() {
// var (
// columnName, isNullable, dataType string
// constraintType sql.NullString
// size, numericPrecision, numericScale sql.NullInt64
// ordinalPosition int
// )
// if err := c.Scan(&columnName,
// &isNullable,
// &dataType,
// &size,
// &numericPrecision,
// &numericScale,
// &ordinalPosition,
// &constraintType,
// ); err != nil {
// panic(err)
// }
// sizeText := ""
// if size.Valid {
// sizeText = strconv.Itoa(int(size.Int64))
// } else if numericPrecision.Valid {
// sizeText = strconv.Itoa(int(numericPrecision.Int64))
// if numericScale.Valid {
// sizeText += "," + strconv.Itoa(int(numericScale.Int64))
// }
// }
// color := tcell.ColorWhite
// if constraintType.Valid {
// color = map[string]tcell.Color{
// "CHECK": tcell.ColorGreen,
// "FOREIGN KEY": tcell.ColorDarkMagenta,
// "PRIMARY KEY": tcell.ColorRed,
// "UNIQUE": tcell.ColorDarkCyan,
// }[constraintType.String]
// }
// columns.SetCell(ordinalPosition, 0, &tview.TableCell{Text: columnName, Color: color}).
// SetCell(ordinalPosition, 1, &tview.TableCell{Text: dataType, Color: color}).
// SetCell(ordinalPosition, 2, &tview.TableCell{Text: sizeText, Align: tview.AlignRight, Color: color}).
// SetCell(ordinalPosition, 3, &tview.TableCell{Text: isNullable, Align: tview.AlignRight, Color: color}).
// SetCell(ordinalPosition, 4, &tview.TableCell{Text: constraintType.String, Align: tview.AlignLeft, Color: color})
// }
// if err := c.Err(); err != nil {
// panic(err)
// }
// })
// tables.SetCurrentItem(0) // Trigger the initial selection.
//
// // When the user selects a table, show its content.
// tables.SetSelectedFunc(func(i int, tableName string, t string, s rune) {
// content(db, dbName, tableName)
// })
// })
// }
// if err := rows.Err(); err != nil {
// panic(err)
// }
//
// // Set up the panels and show the Finder.
// panels = tview.NewPages().
// AddPage(finderPage, flex, true, true)
// app.SetRoot(panels, true)
//}
//
//// Shows the contents of the given table.
//func content(db *sql.DB, dbName, tableName string) {
// finderFocus = app.GetFocus()
//
// // If this page already exists, just show it.
// if panels.HasPage(dbName + "." + tableName) {
// panels.SwitchToPage(dbName + "." + tableName)
// return
// }
// var l logger.Log
// l = logger.Log{"info", logrus.Fields{"database": tableName}, "selected table to view"}
// logger.Lgr(&l)
// var schemaName string
// err := db.QueryRow(fmt.Sprintf("select table_schema from information_schema.tables where table_schema not in ('information_schema','pg_catalog') and table_name = '%s'", tableName)).Scan(&schemaName)
// if err != nil {
// panic(err)
// }
//
// // We display the data in a table embedded in a frame.
// table := tview.NewTable().
// SetFixed(1, 0).
// SetBordersColor(tcell.ColorYellow)
// frame := tview.NewFrame(table).
// SetBorders(0, 0, 0, 0, 0, 0)
// frame.SetBorder(true).
// SetTitle(fmt.Sprintf(`Contents of table "%s.%s"`, schemaName, tableName))
//
// // How many rows does this table have?
// var rowCount int
// err = db.QueryRow(fmt.Sprintf("select count(*) from %s.%s", schemaName, tableName)).Scan(&rowCount)
// if err != nil {
// panic(err)
// }
//
// // Load a batch of rows.
// loadRows := func(offset int) {
// rows, err := db.Query(fmt.Sprintf("select * from %s.%s limit $1 offset $2", schemaName, tableName), batchSize, offset)
// if err != nil {
// panic(err)
// }
// defer rows.Close()
//
// // The first row in the table is the list of column names.
// columnNames, err := rows.Columns()
// if err != nil {
// panic(err)
// }
// for index, name := range columnNames {
// table.SetCell(0, index, &tview.TableCell{Text: name, Align: tview.AlignCenter, Color: tcell.ColorYellow})
// }
//
// // Read the rows.
// columns := make([]interface{}, len(columnNames))
// columnPointers := make([]interface{}, len(columns))
// for index := range columnPointers {
// columnPointers[index] = &columns[index]
// }
// for rows.Next() {
// // Read the columns.
// err := rows.Scan(columnPointers...)
// if err != nil {
// panic(err)
// }
//
// // Transfer them to the table.
// row := table.GetRowCount()
// for index, column := range columns {
// switch value := column.(type) {
// case int64:
// table.SetCell(row, index, &tview.TableCell{Text: strconv.Itoa(int(value)), Align: tview.AlignRight, Color: tcell.ColorDarkCyan})
// case float64:
// table.SetCell(row, index, &tview.TableCell{Text: strconv.FormatFloat(value, 'f', 2, 64), Align: tview.AlignRight, Color: tcell.ColorDarkCyan})
// case string:
// table.SetCellSimple(row, index, value)
// case time.Time:
// t := value.Format("2006-01-02")
// table.SetCell(row, index, &tview.TableCell{Text: t, Align: tview.AlignRight, Color: tcell.ColorDarkMagenta})
// case []uint8:
// str := make([]byte, len(value))
// for index, num := range value {
// str[index] = byte(num)
// }
// table.SetCell(row, index, &tview.TableCell{Text: string(str), Align: tview.AlignRight, Color: tcell.ColorGreen})
// case nil:
// table.SetCell(row, index, &tview.TableCell{Text: "NULL", Align: tview.AlignCenter, Color: tcell.ColorRed})
// default:
// // We've encountered a type that we don't know yet.
// t := reflect.TypeOf(value)
// str := "?nil?"
// if t != nil {
// str = "?" + t.String() + "?"
// }
// table.SetCellSimple(row, index, str)
// }
// }
// }
// if err := rows.Err(); err != nil {
// panic(err)
// }
//
// // Show how much we've loaded.
// frame.Clear()
// loadMore := ""
// if table.GetRowCount()-1 < rowCount {
// loadMore = " - press Enter to load more"
// }
// loadMore = fmt.Sprintf("Loaded %d of %d rows%s. esc to return to previous menu", table.GetRowCount()-1, rowCount, loadMore)
// frame.AddText(loadMore, false, tview.AlignCenter, tcell.ColorYellow)
// }
//
// // Load the first batch of rows.
// loadRows(0)
//
// // Handle key presses.
// table.SetDoneFunc(func(key tcell.Key) {
// switch key {
// case tcell.KeyEscape:
// // Go back to Finder.
// panels.SwitchToPage(finderPage)
// if finderFocus != nil {
// app.SetFocus(finderFocus)
// }
// case tcell.KeyEnter:
// // Load the next batch of rows.
// loadRows(table.GetRowCount() - 1)
// table.ScrollToEnd()
// }
// })
//
// // Add a new page and show it.
// panels.AddPage(dbName+"."+tableName, frame, true, true)
//}

View File

@ -1,6 +1,15 @@
package gui
import "github.com/rivo/tview"
import (
"fmt"
"github.com/sirupsen/logrus"
"shiny-pancake/logger"
"shiny-pancake/internal/database"
"strings"
tview "gitlab.com/tslocum/cview"
)
type panels struct {
currentPanel int
@ -23,8 +32,84 @@ func newState() *state {
type Gui struct {
app *tview.Application
pages *tview.Pages
panels *tview.Panels
state *state
db *db.Database
//statsLocations *statsLocations
}
db *database.Database
}
func New(db *database.Database) *Gui {
return &Gui{
app: tview.NewApplication(),
panels: tview.NewPanels(),
state: newState(),
db: db,
}
}
// Start start application
func (g *Gui) Start() error {
g.initPanels()
var l logger.Log
l = logger.Log{"info", logrus.Fields{"init" : "ran"}, "got this far"}
logger.Lgr(&l)
if err := g.app.Run(); err != nil {
g.app.Stop()
return err
}
return nil
}
func (g *Gui) Stop() {
g.app.Stop()
}
// Page "definitions"
func (g *Gui) sessionPanel() *activeSessions {
for _, panel := range g.state.panels.panel {
if panel.name() == `activeSessions` {
return panel.(*activeSessions)
}
}
return nil
}
func (g *Gui) initPanels() {
g.state.tabBar = newTabBar()
// Page definitions
s := newSessions(g)
var l logger.Log
l = logger.Log{"info", logrus.Fields{"initPanels" : "ran"}, "got this far"}
logger.Lgr(&l)
/*
// NOTE: I would really like to get this working as it would be far neater.
// The issue is with the three panels being of different types.
// cannot use pg (type panel) as type tview.Primitive in argument to g.panels.AddPage:
// panel does not implement tview.Primitive (missing Blur method)
for idx, pg := range []panel{trips, cavers, caves} {
name := pg.name()
g.panels.AddPage(name, pg, true, idx == 0)
fmt.Fprintf(g.state.tabBar, ` %d ["%d"][darkcyan]%s[white][""] `, idx+1, idx, strings.Title(name))
}
g.state.tabBar.Highlight("0")
*/
// Add panels to the "book"
g.panels.AddPanel(`activeSessions`, s, true, true)
fmt.Fprintf(g.state.tabBar, ` ["%d"]%d %s[""] `, 0, 1, strings.Title(s.name()))
g.state.tabBar.Highlight("0")
// Panels
g.state.panels.panel = append(g.state.panels.panel, s)
g.app.SetRoot(g.panels, true)
}

View File

@ -0,0 +1,18 @@
package gui
//func (g *Gui) startMonitoring() {
// stop := make(chan int, 1)
// g.state.stopChans["activeSessions"] = stop
// go g.sessionPanel().monitoringSessions(g)
//}
//
//func (g *Gui) stopMonitoring() {
// g.state.stopChans["activeSessions"] <- 1
//
//}
/*func (g *Gui) updateTask() {
g.app.QueueUpdateDraw(func() {
g.caversPanel().setEntries(g) // REVIEW: Why is this just people ?
})
}*/

9
internal/gui/panel.go Normal file
View File

@ -0,0 +1,9 @@
package gui
type panel interface {
name() string
entries(*Gui)
//setEntries(*Gui)
//focus(*Gui)
//unfocus()
}

46
internal/gui/sessions.go Normal file
View File

@ -0,0 +1,46 @@
package gui
import (
tview "gitlab.com/tslocum/cview"
"shiny-pancake/internal/model"
)
type resources struct {
ActSess []*model.Session
//statsLocations []*model.Statistic
}
type activeSessions struct {
*tview.Table
sn chan *model.Session
filterCol, filterTerm string
}
func newSessions(g *Gui) *activeSessions {
sess := &activeSessions{
Table: tview.NewTable(),
sn: make(chan *model.Session),
}
sess.SetBorder(true)
sess.entries(g)
return sess
}
func (t *activeSessions) name() string {
return `activeSessions`
}
func (t *activeSessions) entries(g *Gui) {
sss, err := g.db.UserSessions()
if err != nil {
return
}
g.state.resources.ActSess = sss
}

10
internal/gui/tabbar.go Normal file
View File

@ -0,0 +1,10 @@
package gui
import (
tview "gitlab.com/tslocum/cview"
)
func newTabBar() *tview.TextView {
return tview.NewTextView()
}

13
internal/model/forms.go Normal file
View File

@ -0,0 +1,13 @@
package model
type HostDetails struct {
Secret string `json:"secret"`
Hostname string `json:"hostname"`
DatabaseName string `json:"databaseName"`
Username string `json:"username"`
}
type Session struct {
Username string
Count int
}

BIN
main

Binary file not shown.