added periodic actions (licence verification, statistics report, start with nag window)

This commit is contained in:
sqshq 2019-06-09 00:16:56 -04:00
parent 72665f55a4
commit 988ef7de8e
7 changed files with 170 additions and 68 deletions

View File

@ -15,6 +15,7 @@ const (
statisticsPath = "/telemetry/statistics" statisticsPath = "/telemetry/statistics"
crashPath = "/telemetry/crash" crashPath = "/telemetry/crash"
registrationPath = "/license/registration" registrationPath = "/license/registration"
verificationPath = "/license/verification"
jsonContentType = "application/json" jsonContentType = "application/json"
) )
@ -44,8 +45,18 @@ func (c *BackendClient) ReportInstallation(statistics *metadata.Statistics) {
} }
} }
func (c *BackendClient) ReportUsageStatistics(error string, statistics *metadata.Statistics) { func (c *BackendClient) ReportStatistics(statistics *metadata.Statistics) {
// TODO
buf := new(bytes.Buffer)
err := json.NewEncoder(buf).Encode(statistics)
if err != nil {
c.ReportCrash(err.Error(), statistics)
}
_, err = http.Post(backendUrl+statisticsPath, jsonContentType, buf)
if err != nil {
c.ReportCrash(err.Error(), statistics)
}
} }
func (c *BackendClient) ReportCrash(error string, statistics *metadata.Statistics) { func (c *BackendClient) ReportCrash(error string, statistics *metadata.Statistics) {
@ -100,3 +111,35 @@ func (c *BackendClient) RegisterLicenseKey(licenseKey string, statistics *metada
return &license, nil return &license, nil
} }
func (c *BackendClient) VerifyLicenseKey(licenseKey string) (*metadata.License, error) {
req := struct {
LicenseKey string
}{
licenseKey,
}
buf := new(bytes.Buffer)
err := json.NewEncoder(buf).Encode(req)
if err != nil {
c.ReportCrash(err.Error(), nil)
}
response, err := http.Post(
backendUrl+verificationPath, jsonContentType, buf)
if err != nil {
return nil, err
}
if response.StatusCode != 200 {
body, _ := ioutil.ReadAll(response.Body)
return nil, errors.New(string(body))
}
var license metadata.License
json.NewDecoder(response.Body).Decode(&license)
return &license, nil
}

View File

@ -63,30 +63,21 @@ func NewIntro(palette console.Palette) *Intro {
func (intro *Intro) Draw(buffer *ui.Buffer) { func (intro *Intro) Draw(buffer *ui.Buffer) {
logo := []string{ introText := append(util.AsciiLogo, []string{
" __ ",
" _________ ____ ___ ____ / /__ _____",
" / ___/ __ `/ __ `__ \\/ __ \\/ / _ \\/ ___/",
" (__ ) /_/ / / / / / / /_/ / / __/ / ",
"/____/\\__,_/_/ /_/ /_/ .___/_/\\___/_/ ",
" /_/ ",
}
introText := append(logo, []string{
"", "", "", "", "", "",
"Welcome.", "Welcome.",
"Sampler is free of charge for personal use, but license must be purchased to use it for business purposes.", "Sampler is free of charge for personal use, but license must be purchased to use it for business purposes.",
"Clicking below indicates you agree to the terms of the license agreement and privacy policy: www.sampler.dev/license", "By proceeding, you agree to the terms of the license agreement and privacy policy: www.sampler.dev/license",
"", "", "", "", "", "",
"How do you plan to use Sampler?", "How do you plan to use Sampler?",
}...) }...)
commericalText := append(logo, []string{ commericalText := append(util.AsciiLogo, []string{
"", "", "", "", "", "", "", "",
"Please visit www.sampler.dev to purchase a license and then start Sampler with --license flag", "Please visit www.sampler.dev to purchase a license and then start Sampler with --license flag",
}...) }...)
personalText := append(logo, []string{ personalText := append(util.AsciiLogo, []string{
"", "", "", "", "", "", "", "",
"Sampler is always free for non-commercial use, but you can support the project and buy a personal license:", "Sampler is always free for non-commercial use, but you can support the project and buy a personal license:",
"www.sampler.dev", "www.sampler.dev",

View File

@ -10,6 +10,7 @@ import (
"github.com/sqshq/sampler/data" "github.com/sqshq/sampler/data"
"image" "image"
"math" "math"
"time"
) )
type Layout struct { type Layout struct {
@ -18,10 +19,12 @@ type Layout struct {
statusbar *component.StatusBar statusbar *component.StatusBar
menu *component.Menu menu *component.Menu
intro *component.Intro intro *component.Intro
nag *component.NagWindow
ChangeModeEvents chan Mode ChangeModeEvents chan Mode
mode Mode mode Mode
selection int selection int
positionsChanged bool positionsChanged bool
startupTime time.Time
} }
type Mode rune type Mode rune
@ -29,25 +32,28 @@ type Mode rune
const ( const (
ModeDefault Mode = 0 ModeDefault Mode = 0
ModeIntro Mode = 1 ModeIntro Mode = 1
ModePause Mode = 2 ModeNag Mode = 2
ModeComponentSelect Mode = 3 ModePause Mode = 3
ModeMenuOptionSelect Mode = 4 ModeComponentSelect Mode = 4
ModeComponentMove Mode = 5 ModeMenuOptionSelect Mode = 5
ModeComponentResize Mode = 6 ModeComponentMove Mode = 6
ModeChartPinpoint Mode = 7 ModeComponentResize Mode = 7
ModeChartPinpoint Mode = 8
) )
const ( const (
minDimension = 3 minDimension = 3
statusbarHeight = 1 statusbarHeight = 1
nagWindowDurationSec = 5
) )
func NewLayout(statusline *component.StatusBar, menu *component.Menu, intro *component.Intro) *Layout { func NewLayout(statusline *component.StatusBar, menu *component.Menu, intro *component.Intro, nag *component.NagWindow) *Layout {
width, height := ui.TerminalDimensions() width, height := ui.TerminalDimensions()
block := *ui.NewBlock() block := *ui.NewBlock()
block.SetRect(0, 0, width, height) block.SetRect(0, 0, width, height)
intro.SetRect(0, 0, width, height) intro.SetRect(0, 0, width, height)
nag.SetRect(0, 0, width, height)
statusline.SetRect(0, height-statusbarHeight, width, height) statusline.SetRect(0, height-statusbarHeight, width, height)
return &Layout{ return &Layout{
@ -56,9 +62,11 @@ func NewLayout(statusline *component.StatusBar, menu *component.Menu, intro *com
statusbar: statusline, statusbar: statusline,
menu: menu, menu: menu,
intro: intro, intro: intro,
nag: nag,
mode: ModeDefault, mode: ModeDefault,
selection: 0, selection: 0,
ChangeModeEvents: make(chan Mode, 10), ChangeModeEvents: make(chan Mode, 10),
startupTime: time.Now(),
} }
} }
@ -66,10 +74,14 @@ func (l *Layout) AddComponent(cpt *component.Component) {
l.Components = append(l.Components, cpt) l.Components = append(l.Components, cpt)
} }
func (l *Layout) RunIntro() { func (l *Layout) StartWithIntro() {
l.mode = ModeIntro l.mode = ModeIntro
} }
func (l *Layout) StartWithNagWindow() {
l.mode = ModeNag
}
func (l *Layout) changeMode(m Mode) { func (l *Layout) changeMode(m Mode) {
if m == ModeComponentResize || m == ModeComponentMove { if m == ModeComponentResize || m == ModeComponentMove {
l.positionsChanged = true l.positionsChanged = true
@ -79,7 +91,7 @@ func (l *Layout) changeMode(m Mode) {
} }
func (l *Layout) HandleMouseClick(x int, y int) { func (l *Layout) HandleMouseClick(x int, y int) {
if l.mode == ModeIntro { if l.mode == ModeIntro || l.mode == ModeNag {
return return
} }
l.menu.Idle() l.menu.Idle()
@ -145,6 +157,8 @@ func (l *Layout) HandleKeyboardEvent(e string) {
} else { } else {
l.changeMode(ModeDefault) l.changeMode(ModeDefault)
} }
case ModeNag:
l.nag.Accept()
} }
case console.KeyEsc: case console.KeyEsc:
l.resetAlerts() l.resetAlerts()
@ -310,15 +324,28 @@ func (l *Layout) Draw(buffer *ui.Buffer) {
columnWidth := float64(l.GetRect().Dx()) / float64(console.ColumnsCount) columnWidth := float64(l.GetRect().Dx()) / float64(console.ColumnsCount)
rowHeight := float64(l.GetRect().Dy()-statusbarHeight) / float64(console.RowsCount) rowHeight := float64(l.GetRect().Dy()-statusbarHeight) / float64(console.RowsCount)
for _, c := range l.Components {
rectangle := calculateComponentCoordinates(c, columnWidth, rowHeight)
c.SetRect(rectangle.Min.X, rectangle.Min.Y, rectangle.Max.X, rectangle.Max.Y)
}
if l.mode == ModeIntro { if l.mode == ModeIntro {
l.intro.SetRect(l.Min.X, l.Min.Y, l.Max.X, l.Max.Y) l.intro.SetRect(l.Min.X, l.Min.Y, l.Max.X, l.Max.Y)
l.intro.Draw(buffer) l.intro.Draw(buffer)
return return
} }
if l.mode == ModeNag {
if l.nag.IsAccepted() && time.Since(l.startupTime).Seconds() > nagWindowDurationSec {
l.mode = ModeDefault
} else {
l.nag.SetRect(l.Min.X, l.Min.Y, l.Max.X, l.Max.Y)
l.nag.Draw(buffer)
return
}
}
for _, c := range l.Components { for _, c := range l.Components {
rectangle := calculateComponentCoordinates(c, columnWidth, rowHeight)
c.SetRect(rectangle.Min.X, rectangle.Min.Y, rectangle.Max.X, rectangle.Max.Y)
c.Draw(buffer) c.Draw(buffer)
} }

53
component/nag.go Normal file
View File

@ -0,0 +1,53 @@
package component
import (
ui "github.com/gizak/termui/v3"
"github.com/sqshq/sampler/component/util"
"github.com/sqshq/sampler/console"
)
type NagWindow struct {
*ui.Block
palette console.Palette
accepted bool
}
func NewNagWindow(palette console.Palette) *NagWindow {
return &NagWindow{
Block: NewBlock("", false, palette),
palette: palette,
accepted: false,
}
}
func (n *NagWindow) Accept() {
n.accepted = true
}
func (n *NagWindow) IsAccepted() bool {
return n.accepted
}
func (n *NagWindow) Draw(buffer *ui.Buffer) {
text := append(util.AsciiLogo, []string{
"", "", "",
"Thank you for using Sampler.",
"It is always free for non-commercial use, but you can support the project and buy a personal license.",
"",
"Please visit www.sampler.dev",
}...)
for i, a := range text {
util.PrintString(
a,
ui.NewStyle(n.palette.BaseColor),
util.GetMiddlePoint(n.Block.Rectangle, a, i-15),
buffer)
}
buffer.SetString(string(buttonOk), ui.NewStyle(n.palette.ReverseColor, n.palette.BaseColor),
util.GetMiddlePoint(n.Block.Rectangle, string(buttonOk), 4))
n.Block.Draw(buffer)
}

View File

@ -9,6 +9,15 @@ import (
"strings" "strings"
) )
var AsciiLogo = []string{
" __ ",
" _________ ____ ___ ____ / /__ _____",
" / ___/ __ `/ __ `__ \\/ __ \\/ / _ \\/ ___/",
" (__ ) /_/ / / / / / / /_/ / / __/ / ",
"/____/\\__,_/_/ /_/ /_/ .___/_/\\___/_/ ",
" /_/ ",
}
func FormatValue(value float64, scale int) string { func FormatValue(value float64, scale int) string {
if math.Abs(value) == math.MaxFloat64 { if math.Abs(value) == math.MaxFloat64 {
return "Inf" return "Inf"

View File

@ -1,33 +0,0 @@
variables:
PGPASSWORD: fred
mongoconnection: mongo --quiet --host=localhost blog
mysqlconnection: mysql -u root -s --database mysql --skip-column-names
neo4jconnection: cypher-shell -u neo4j -p 121314 --format plain
postgresconnection: psql -h localhost -U postgres --no-align --tuples-only
sshconnection: ssh -i ~/sqshq.pem ec2-user@3.215.108.82
textboxes:
- title: Neo4j
position: [[0, 0], [10, 40]]
pty: true
init: $neo4jconnection
sample: RETURN rand();
transform: echo "$sample" | tail -n 1
- title: Postgres
position: [[10, 0], [9, 40]]
init: $postgresconnection
sample: select random();
- title: MySQL
position: [[19, 0], [10, 40]]
pty: true
init: $mysqlconnection
sample: select rand();
- title: MongoDB
position: [[29, 0], [10, 40]]
rate-ms: 500
init: $mongoconnection
sample: sleep(3000);Date.now();
- title: SSH
position: [[39, 0], [41, 40]]
pty: true
init: $sshconnection
sample: top

26
main.go
View File

@ -89,18 +89,23 @@ func main() {
defer player.Close() defer player.Close()
palette := console.GetPalette(*cfg.Theme) palette := console.GetPalette(*cfg.Theme)
lout := layout.NewLayout( lout := layout.NewLayout(component.NewStatusLine(*opt.ConfigFile, palette, license),
component.NewStatusLine(*opt.ConfigFile, palette, license), component.NewMenu(palette), component.NewIntro(palette)) component.NewMenu(palette), component.NewIntro(palette), component.NewNagWindow(palette))
if statistics.LaunchCount == 0 { if statistics.LaunchCount == 0 {
if !opt.DisableTelemetry { if !opt.DisableTelemetry {
go bc.ReportInstallation(statistics) go bc.ReportInstallation(statistics)
} }
lout.RunIntro() lout.StartWithIntro()
} else /* with random */ { } else if statistics.LaunchCount%20 == 0 { // once in a while
// TODO if license == nil lout.showNagWindow() with timeout and OK button if license == nil || !license.Valid {
// TODO if license != nil, verify license lout.StartWithNagWindow()
// TODO report statistics } else {
go verifyLicense(license, bc)
}
if !opt.DisableTelemetry {
go bc.ReportStatistics(statistics)
}
} }
metadata.PersistStatistics(cfg) metadata.PersistStatistics(cfg)
@ -130,3 +135,10 @@ func registerLicense(statistics *metadata.Statistics, opt config.Options, bc *cl
console.Exit("License successfully verified, Sampler can be restarted without --license flag now. Thank you.") console.Exit("License successfully verified, Sampler can be restarted without --license flag now. Thank you.")
} }
} }
func verifyLicense(license *metadata.License, bc *client.BackendClient) {
verifiedLicense, _ := bc.VerifyLicenseKey(*license.Key)
if verifiedLicense != nil {
metadata.SaveLicense(*verifiedLicense)
}
}