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"
crashPath = "/telemetry/crash"
registrationPath = "/license/registration"
verificationPath = "/license/verification"
jsonContentType = "application/json"
)
@ -44,8 +45,18 @@ func (c *BackendClient) ReportInstallation(statistics *metadata.Statistics) {
}
}
func (c *BackendClient) ReportUsageStatistics(error string, statistics *metadata.Statistics) {
// TODO
func (c *BackendClient) ReportStatistics(statistics *metadata.Statistics) {
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) {
@ -100,3 +111,35 @@ func (c *BackendClient) RegisterLicenseKey(licenseKey string, statistics *metada
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) {
logo := []string{
" __ ",
" _________ ____ ___ ____ / /__ _____",
" / ___/ __ `/ __ `__ \\/ __ \\/ / _ \\/ ___/",
" (__ ) /_/ / / / / / / /_/ / / __/ / ",
"/____/\\__,_/_/ /_/ /_/ .___/_/\\___/_/ ",
" /_/ ",
}
introText := append(logo, []string{
introText := append(util.AsciiLogo, []string{
"", "", "",
"Welcome.",
"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?",
}...)
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",
}...)
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:",
"www.sampler.dev",

View File

@ -10,6 +10,7 @@ import (
"github.com/sqshq/sampler/data"
"image"
"math"
"time"
)
type Layout struct {
@ -18,10 +19,12 @@ type Layout struct {
statusbar *component.StatusBar
menu *component.Menu
intro *component.Intro
nag *component.NagWindow
ChangeModeEvents chan Mode
mode Mode
selection int
positionsChanged bool
startupTime time.Time
}
type Mode rune
@ -29,25 +32,28 @@ type Mode rune
const (
ModeDefault Mode = 0
ModeIntro Mode = 1
ModePause Mode = 2
ModeComponentSelect Mode = 3
ModeMenuOptionSelect Mode = 4
ModeComponentMove Mode = 5
ModeComponentResize Mode = 6
ModeChartPinpoint Mode = 7
ModeNag Mode = 2
ModePause Mode = 3
ModeComponentSelect Mode = 4
ModeMenuOptionSelect Mode = 5
ModeComponentMove Mode = 6
ModeComponentResize Mode = 7
ModeChartPinpoint Mode = 8
)
const (
minDimension = 3
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()
block := *ui.NewBlock()
block.SetRect(0, 0, width, height)
intro.SetRect(0, 0, width, height)
nag.SetRect(0, 0, width, height)
statusline.SetRect(0, height-statusbarHeight, width, height)
return &Layout{
@ -56,9 +62,11 @@ func NewLayout(statusline *component.StatusBar, menu *component.Menu, intro *com
statusbar: statusline,
menu: menu,
intro: intro,
nag: nag,
mode: ModeDefault,
selection: 0,
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)
}
func (l *Layout) RunIntro() {
func (l *Layout) StartWithIntro() {
l.mode = ModeIntro
}
func (l *Layout) StartWithNagWindow() {
l.mode = ModeNag
}
func (l *Layout) changeMode(m Mode) {
if m == ModeComponentResize || m == ModeComponentMove {
l.positionsChanged = true
@ -79,7 +91,7 @@ func (l *Layout) changeMode(m Mode) {
}
func (l *Layout) HandleMouseClick(x int, y int) {
if l.mode == ModeIntro {
if l.mode == ModeIntro || l.mode == ModeNag {
return
}
l.menu.Idle()
@ -145,6 +157,8 @@ func (l *Layout) HandleKeyboardEvent(e string) {
} else {
l.changeMode(ModeDefault)
}
case ModeNag:
l.nag.Accept()
}
case console.KeyEsc:
l.resetAlerts()
@ -310,15 +324,28 @@ func (l *Layout) Draw(buffer *ui.Buffer) {
columnWidth := float64(l.GetRect().Dx()) / float64(console.ColumnsCount)
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 {
l.intro.SetRect(l.Min.X, l.Min.Y, l.Max.X, l.Max.Y)
l.intro.Draw(buffer)
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 {
rectangle := calculateComponentCoordinates(c, columnWidth, rowHeight)
c.SetRect(rectangle.Min.X, rectangle.Min.Y, rectangle.Max.X, rectangle.Max.Y)
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"
)
var AsciiLogo = []string{
" __ ",
" _________ ____ ___ ____ / /__ _____",
" / ___/ __ `/ __ `__ \\/ __ \\/ / _ \\/ ___/",
" (__ ) /_/ / / / / / / /_/ / / __/ / ",
"/____/\\__,_/_/ /_/ /_/ .___/_/\\___/_/ ",
" /_/ ",
}
func FormatValue(value float64, scale int) string {
if math.Abs(value) == math.MaxFloat64 {
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()
palette := console.GetPalette(*cfg.Theme)
lout := layout.NewLayout(
component.NewStatusLine(*opt.ConfigFile, palette, license), component.NewMenu(palette), component.NewIntro(palette))
lout := layout.NewLayout(component.NewStatusLine(*opt.ConfigFile, palette, license),
component.NewMenu(palette), component.NewIntro(palette), component.NewNagWindow(palette))
if statistics.LaunchCount == 0 {
if !opt.DisableTelemetry {
go bc.ReportInstallation(statistics)
}
lout.RunIntro()
} else /* with random */ {
// TODO if license == nil lout.showNagWindow() with timeout and OK button
// TODO if license != nil, verify license
// TODO report statistics
lout.StartWithIntro()
} else if statistics.LaunchCount%20 == 0 { // once in a while
if license == nil || !license.Valid {
lout.StartWithNagWindow()
} else {
go verifyLicense(license, bc)
}
if !opt.DisableTelemetry {
go bc.ReportStatistics(statistics)
}
}
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.")
}
}
func verifyLicense(license *metadata.License, bc *client.BackendClient) {
verifiedLicense, _ := bc.VerifyLicenseKey(*license.Key)
if verifiedLicense != nil {
metadata.SaveLicense(*verifiedLicense)
}
}