added periodic actions (licence verification, statistics report, start with nag window)
This commit is contained in:
parent
72665f55a4
commit
988ef7de8e
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -9,6 +9,15 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
var AsciiLogo = []string{
|
||||
" __ ",
|
||||
" _________ ____ ___ ____ / /__ _____",
|
||||
" / ___/ __ `/ __ `__ \\/ __ \\/ / _ \\/ ___/",
|
||||
" (__ ) /_/ / / / / / / /_/ / / __/ / ",
|
||||
"/____/\\__,_/_/ /_/ /_/ .___/_/\\___/_/ ",
|
||||
" /_/ ",
|
||||
}
|
||||
|
||||
func FormatValue(value float64, scale int) string {
|
||||
if math.Abs(value) == math.MaxFloat64 {
|
||||
return "Inf"
|
||||
|
|
|
@ -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
26
main.go
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue