Merge pull request #77 from sqshq/remove_backend_interaction
Remove all backend interaction
This commit is contained in:
commit
e3d5b9dc0a
|
@ -1,153 +0,0 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/sqshq/sampler/metadata"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
backendUrl = "http://localhost/api/v1"
|
||||
installationPath = "/telemetry/installation"
|
||||
statisticsPath = "/telemetry/statistics"
|
||||
crashPath = "/telemetry/crash"
|
||||
registrationPath = "/license/registration"
|
||||
verificationPath = "/license/verification"
|
||||
)
|
||||
|
||||
// BackendClient is used to verify license and to send telemetry
|
||||
// for analyses (anonymous usage data statistics and crash reports)
|
||||
type BackendClient struct {
|
||||
client http.Client
|
||||
}
|
||||
|
||||
func NewBackendClient() *BackendClient {
|
||||
return &BackendClient{
|
||||
client: http.Client{},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *BackendClient) ReportInstallation(statistics *metadata.Statistics) {
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
err := json.NewEncoder(buf).Encode(statistics)
|
||||
if err != nil {
|
||||
c.ReportCrash(err.Error(), statistics)
|
||||
}
|
||||
|
||||
_, err = sendRequest(backendUrl+installationPath, buf)
|
||||
|
||||
if err != nil {
|
||||
c.ReportCrash(err.Error(), statistics)
|
||||
}
|
||||
}
|
||||
|
||||
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 = sendRequest(backendUrl+statisticsPath, buf)
|
||||
if err != nil {
|
||||
c.ReportCrash(err.Error(), statistics)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *BackendClient) ReportCrash(error string, statistics *metadata.Statistics) {
|
||||
|
||||
req := struct {
|
||||
Error string
|
||||
Statistics *metadata.Statistics
|
||||
}{
|
||||
error,
|
||||
statistics,
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
err := json.NewEncoder(buf).Encode(req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = sendRequest(backendUrl+crashPath, buf)
|
||||
}
|
||||
|
||||
func (c *BackendClient) RegisterLicenseKey(licenseKey string, statistics *metadata.Statistics) (*metadata.License, error) {
|
||||
|
||||
req := struct {
|
||||
LicenseKey string
|
||||
Statistics *metadata.Statistics
|
||||
}{
|
||||
licenseKey,
|
||||
statistics,
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
err := json.NewEncoder(buf).Encode(req)
|
||||
if err != nil {
|
||||
c.ReportCrash(err.Error(), statistics)
|
||||
}
|
||||
|
||||
response, err := sendRequest(backendUrl+registrationPath, 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
|
||||
}
|
||||
|
||||
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 := sendRequest(backendUrl+verificationPath, 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
|
||||
}
|
||||
|
||||
func sendRequest(url string, body *bytes.Buffer) (resp *http.Response, err error) {
|
||||
c := http.DefaultClient
|
||||
req, err := http.NewRequest("POST", url, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
return c.Do(req)
|
||||
}
|
|
@ -1,142 +0,0 @@
|
|||
package component
|
||||
|
||||
import (
|
||||
ui "github.com/gizak/termui/v3"
|
||||
"github.com/sqshq/sampler/component/util"
|
||||
"github.com/sqshq/sampler/console"
|
||||
)
|
||||
|
||||
type Intro struct {
|
||||
*ui.Block
|
||||
page IntroPage
|
||||
option introOption
|
||||
palette console.Palette
|
||||
}
|
||||
|
||||
type IntroPage rune
|
||||
|
||||
const (
|
||||
IntroPageWelcome IntroPage = 0
|
||||
IntroPageCommercial IntroPage = 1
|
||||
IntroPagePersonal IntroPage = 2
|
||||
)
|
||||
|
||||
type introOption rune
|
||||
|
||||
const (
|
||||
introOptionCommercial introOption = 0
|
||||
introOptionPersonal introOption = 1
|
||||
)
|
||||
|
||||
const (
|
||||
buttonCommercial string = " COMMERCIAL USE "
|
||||
buttonPersonal string = " PERSONAL USE "
|
||||
buttonOk string = " OK "
|
||||
)
|
||||
|
||||
func (intro *Intro) Up() {
|
||||
intro.option = introOptionCommercial
|
||||
}
|
||||
|
||||
func (intro *Intro) Down() {
|
||||
intro.option = introOptionPersonal
|
||||
}
|
||||
|
||||
func (intro *Intro) NextPage() {
|
||||
if intro.option == introOptionCommercial {
|
||||
intro.page = IntroPageCommercial
|
||||
} else {
|
||||
intro.page = IntroPagePersonal
|
||||
}
|
||||
}
|
||||
|
||||
func (intro *Intro) GetSelectedPage() IntroPage {
|
||||
return intro.page
|
||||
}
|
||||
|
||||
func NewIntro(palette console.Palette) *Intro {
|
||||
return &Intro{
|
||||
Block: NewBlock("", false, palette),
|
||||
palette: palette,
|
||||
}
|
||||
}
|
||||
|
||||
func (intro *Intro) Draw(buffer *ui.Buffer) {
|
||||
|
||||
introText := append(util.AsciiLogo, []string{
|
||||
"", "", "",
|
||||
"Welcome.",
|
||||
"",
|
||||
"Sampler is an OSS project, and it needs funding to be alive and keep developing",
|
||||
"Before the first start, please explore our licensing options below. For more details, visit WWW.SAMPLER.DEV",
|
||||
"", "", "",
|
||||
"How do you plan to use Sampler?",
|
||||
}...)
|
||||
|
||||
commericalText := append(util.AsciiLogo, []string{
|
||||
"", "", "", "",
|
||||
"With Sampler, you can easily save time and solve some of your business problems.",
|
||||
"That's why support of the project is in the interest of your organization.",
|
||||
"",
|
||||
"",
|
||||
"We are offering commercial licenses which provide priority support and technical assistance.",
|
||||
"After entering the licence key, your company name will appear in the status bar.",
|
||||
"",
|
||||
"",
|
||||
"To make a purchase, please visit WWW.SAMPLER.DEV",
|
||||
}...)
|
||||
|
||||
personalText := append(util.AsciiLogo, []string{
|
||||
"", "", "", "",
|
||||
"Sampler is always free to use, but you can support the project and donate any amount to get a personal license.",
|
||||
"Once it is activated, your name will appear in the status bar.",
|
||||
"",
|
||||
"",
|
||||
"To become a sponsor, please visit WWW.SAMPLER.DEV",
|
||||
}...)
|
||||
|
||||
text := introText
|
||||
|
||||
switch intro.page {
|
||||
case IntroPageWelcome:
|
||||
text = introText
|
||||
case IntroPageCommercial:
|
||||
text = commericalText
|
||||
case IntroPagePersonal:
|
||||
text = personalText
|
||||
}
|
||||
|
||||
for i, a := range text {
|
||||
util.PrintString(
|
||||
a,
|
||||
ui.NewStyle(intro.palette.BaseColor),
|
||||
util.GetMiddlePoint(intro.Block.Rectangle, a, i-15),
|
||||
buffer)
|
||||
}
|
||||
|
||||
highlightedStyle := ui.NewStyle(intro.palette.ReverseColor, intro.palette.BaseColor)
|
||||
regularStyle := ui.NewStyle(intro.palette.BaseColor, intro.palette.ReverseColor)
|
||||
|
||||
if intro.page == IntroPageWelcome {
|
||||
|
||||
commercialButtonStyle := highlightedStyle
|
||||
if intro.option == introOptionPersonal {
|
||||
commercialButtonStyle = regularStyle
|
||||
}
|
||||
|
||||
personalButtonStyle := highlightedStyle
|
||||
if intro.option == introOptionCommercial {
|
||||
personalButtonStyle = regularStyle
|
||||
}
|
||||
|
||||
buffer.SetString(buttonCommercial, commercialButtonStyle,
|
||||
util.GetMiddlePoint(intro.Block.Rectangle, buttonCommercial, 5))
|
||||
buffer.SetString(buttonPersonal, personalButtonStyle,
|
||||
util.GetMiddlePoint(intro.Block.Rectangle, buttonPersonal, 7))
|
||||
} else {
|
||||
buffer.SetString(buttonOk, highlightedStyle,
|
||||
util.GetMiddlePoint(intro.Block.Rectangle, buttonOk, 7))
|
||||
}
|
||||
|
||||
intro.Block.Draw(buffer)
|
||||
}
|
|
@ -19,8 +19,6 @@ type Layout struct {
|
|||
Components []*component.Component
|
||||
statusbar *component.StatusBar
|
||||
menu *component.Menu
|
||||
intro *component.Intro
|
||||
nag *component.NagWindow
|
||||
ChangeModeEvents chan Mode
|
||||
mode Mode
|
||||
selection int
|
||||
|
@ -33,28 +31,24 @@ type Mode rune
|
|||
const (
|
||||
ModeDefault Mode = 0
|
||||
ModeIntro Mode = 1
|
||||
ModeNag Mode = 2
|
||||
ModePause Mode = 3
|
||||
ModeComponentSelect Mode = 4
|
||||
ModeMenuOptionSelect Mode = 5
|
||||
ModeComponentMove Mode = 6
|
||||
ModeComponentResize Mode = 7
|
||||
ModeChartPinpoint Mode = 8
|
||||
ModePause Mode = 2
|
||||
ModeComponentSelect Mode = 3
|
||||
ModeMenuOptionSelect Mode = 4
|
||||
ModeComponentMove Mode = 5
|
||||
ModeComponentResize Mode = 6
|
||||
ModeChartPinpoint Mode = 7
|
||||
)
|
||||
|
||||
const (
|
||||
minDimension = 3
|
||||
statusbarHeight = 1
|
||||
nagWindowDurationSec = 5
|
||||
)
|
||||
|
||||
func NewLayout(statusline *component.StatusBar, menu *component.Menu, intro *component.Intro, nag *component.NagWindow) *Layout {
|
||||
func NewLayout(statusline *component.StatusBar, menu *component.Menu) *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{
|
||||
|
@ -62,8 +56,6 @@ func NewLayout(statusline *component.StatusBar, menu *component.Menu, intro *com
|
|||
Components: make([]*component.Component, 0),
|
||||
statusbar: statusline,
|
||||
menu: menu,
|
||||
intro: intro,
|
||||
nag: nag,
|
||||
mode: ModeDefault,
|
||||
selection: 0,
|
||||
ChangeModeEvents: make(chan Mode, 10),
|
||||
|
@ -79,10 +71,6 @@ 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
|
||||
|
@ -92,7 +80,7 @@ func (l *Layout) changeMode(m Mode) {
|
|||
}
|
||||
|
||||
func (l *Layout) HandleMouseClick(x int, y int) {
|
||||
if l.mode == ModeIntro || l.mode == ModeNag {
|
||||
if l.mode == ModeIntro {
|
||||
return
|
||||
}
|
||||
l.menu.Idle()
|
||||
|
@ -151,15 +139,6 @@ func (l *Layout) HandleKeyboardEvent(e string) {
|
|||
l.menu.Idle()
|
||||
l.changeMode(ModeDefault)
|
||||
break
|
||||
case ModeIntro:
|
||||
page := l.intro.GetSelectedPage()
|
||||
if page == component.IntroPageWelcome {
|
||||
l.intro.NextPage()
|
||||
} else {
|
||||
l.changeMode(ModeDefault)
|
||||
}
|
||||
case ModeNag:
|
||||
l.nag.Accept()
|
||||
}
|
||||
case console.KeyEsc:
|
||||
l.resetAlerts()
|
||||
|
@ -222,8 +201,6 @@ func (l *Layout) HandleKeyboardEvent(e string) {
|
|||
selected.Move(0, -1)
|
||||
case ModeComponentResize:
|
||||
selected.Resize(0, -1)
|
||||
case ModeIntro:
|
||||
l.intro.Up()
|
||||
}
|
||||
case console.KeyDown:
|
||||
switch l.mode {
|
||||
|
@ -239,8 +216,6 @@ func (l *Layout) HandleKeyboardEvent(e string) {
|
|||
selected.Move(0, 1)
|
||||
case ModeComponentResize:
|
||||
selected.Resize(0, 1)
|
||||
case ModeIntro:
|
||||
l.intro.Down()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -330,22 +305,6 @@ func (l *Layout) Draw(buffer *ui.Buffer) {
|
|||
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 {
|
||||
c.Draw(buffer)
|
||||
}
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
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, but you can sponsor the project and buy the personal or commercial license",
|
||||
"for priority support and technical assistance.",
|
||||
"",
|
||||
"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(buttonOk, ui.NewStyle(n.palette.ReverseColor, n.palette.BaseColor),
|
||||
util.GetMiddlePoint(n.Block.Rectangle, buttonOk, 4))
|
||||
|
||||
n.Block.Draw(buffer)
|
||||
}
|
|
@ -4,7 +4,6 @@ import (
|
|||
"fmt"
|
||||
ui "github.com/gizak/termui/v3"
|
||||
"github.com/sqshq/sampler/console"
|
||||
"github.com/sqshq/sampler/metadata"
|
||||
"image"
|
||||
)
|
||||
|
||||
|
@ -20,24 +19,11 @@ type StatusBar struct {
|
|||
pause bool
|
||||
}
|
||||
|
||||
func NewStatusBar(configFileName string, palette console.Palette, license *metadata.License) *StatusBar {
|
||||
func NewStatusBar(configFileName string, palette console.Palette) *StatusBar {
|
||||
|
||||
block := *ui.NewBlock()
|
||||
block.Border = false
|
||||
text := fmt.Sprintf(" %s %s | ", console.AppTitle, console.AppVersion)
|
||||
|
||||
if license == nil || !license.Valid || license.Type == nil {
|
||||
text += console.AppLicenseWarning
|
||||
} else if *license.Type == metadata.TypePersonal {
|
||||
text += fmt.Sprintf("%s | personal license: %s", configFileName, *license.Username)
|
||||
} else if license.Username != nil {
|
||||
text += fmt.Sprintf("%s | licensed to %s", configFileName, *license.Username)
|
||||
if license.Company != nil {
|
||||
text += fmt.Sprintf(", %s", *license.Company)
|
||||
}
|
||||
} else {
|
||||
text += fmt.Sprintf("%s | licensed to %s", configFileName, *license.Company)
|
||||
}
|
||||
text := fmt.Sprintf(" %s %s | %s", console.AppTitle, console.AppVersion, configFileName)
|
||||
|
||||
return &StatusBar{
|
||||
Block: NewBlock("", false, palette),
|
||||
|
|
|
@ -34,14 +34,10 @@ func LoadConfig() (*Config, Options) {
|
|||
console.Exit(console.AppVersion)
|
||||
}
|
||||
|
||||
if opt.ConfigFile == nil && opt.LicenseKey == nil {
|
||||
if opt.ConfigFile == nil {
|
||||
console.Exit("Please specify config file using --config flag. Example: sampler --config example.yml")
|
||||
}
|
||||
|
||||
if opt.LicenseKey != nil {
|
||||
return nil, opt
|
||||
}
|
||||
|
||||
cfg := readFile(opt.ConfigFile)
|
||||
cfg.validate()
|
||||
cfg.setDefaults()
|
||||
|
|
|
@ -3,8 +3,6 @@ package config
|
|||
// Options with cli flags
|
||||
type Options struct {
|
||||
ConfigFile *string `short:"c" long:"config" description:"Path to YAML config file"`
|
||||
LicenseKey *string `short:"l" long:"license" description:"License key. Visit www.sampler.dev for details"`
|
||||
Environment []string `short:"e" long:"env" description:"Specify name=value variable to use in script placeholder as $name. This flag takes precedence over the same name variables, specified in config yml"`
|
||||
Version bool `short:"v" long:"version" description:"Print version"`
|
||||
DisableTelemetry bool `long:"disable-telemetry" description:"Disable anonymous usage statistics and errors to be sent to Sampler online service for analyses"`
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ const (
|
|||
RowsCount = 40
|
||||
AppTitle = "sampler"
|
||||
AppVersion = "1.0.3"
|
||||
AppLicenseWarning = "NOT ACTIVATED. PLEASE CONSIDER TO PURCHASE PERSONAL OR COMMERCIAL LICENSE. WWW.SAMPLER.DEV "
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
65
main.go
65
main.go
|
@ -1,10 +1,8 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
ui "github.com/gizak/termui/v3"
|
||||
"github.com/sqshq/sampler/asset"
|
||||
"github.com/sqshq/sampler/client"
|
||||
"github.com/sqshq/sampler/component"
|
||||
"github.com/sqshq/sampler/component/asciibox"
|
||||
"github.com/sqshq/sampler/component/barchart"
|
||||
|
@ -17,8 +15,6 @@ import (
|
|||
"github.com/sqshq/sampler/console"
|
||||
"github.com/sqshq/sampler/data"
|
||||
"github.com/sqshq/sampler/event"
|
||||
"github.com/sqshq/sampler/metadata"
|
||||
"runtime/debug"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -71,14 +67,6 @@ func (s *Starter) start(drawable ui.Drawable, consumer *data.Consumer, component
|
|||
func main() {
|
||||
|
||||
cfg, opt := config.LoadConfig()
|
||||
bc := client.NewBackendClient()
|
||||
|
||||
statistics := metadata.GetStatistics(cfg)
|
||||
license := metadata.GetLicense()
|
||||
|
||||
if opt.LicenseKey != nil {
|
||||
registerLicense(statistics, opt, bc)
|
||||
}
|
||||
|
||||
console.Init()
|
||||
defer console.Close()
|
||||
|
@ -88,28 +76,8 @@ func main() {
|
|||
defer player.Close()
|
||||
}
|
||||
|
||||
defer handleCrash(statistics, opt, bc)
|
||||
defer updateStatistics(cfg, time.Now())
|
||||
|
||||
palette := console.GetPalette(*cfg.Theme)
|
||||
lout := layout.NewLayout(component.NewStatusBar(*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.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)
|
||||
}
|
||||
}
|
||||
lout := layout.NewLayout(component.NewStatusBar(*opt.ConfigFile, palette), component.NewMenu(palette))
|
||||
|
||||
starter := &Starter{player, lout, palette, opt, *cfg}
|
||||
samplers := starter.startAll()
|
||||
|
@ -117,34 +85,3 @@ func main() {
|
|||
handler := event.NewHandler(samplers, opt, lout)
|
||||
handler.HandleEvents()
|
||||
}
|
||||
|
||||
func handleCrash(statistics *metadata.Statistics, opt config.Options, bc *client.BackendClient) {
|
||||
if rec := recover(); rec != nil {
|
||||
err := rec.(error)
|
||||
if !opt.DisableTelemetry {
|
||||
bc.ReportCrash(fmt.Sprintf("%s\n%s", err.Error(), string(debug.Stack())), statistics)
|
||||
}
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func updateStatistics(cfg *config.Config, startTime time.Time) {
|
||||
metadata.PersistStatistics(cfg, time.Since(startTime))
|
||||
}
|
||||
|
||||
func registerLicense(statistics *metadata.Statistics, opt config.Options, bc *client.BackendClient) {
|
||||
lc, err := bc.RegisterLicenseKey(*opt.LicenseKey, statistics)
|
||||
if err != nil {
|
||||
console.Exit("License registration failed: " + err.Error())
|
||||
} else {
|
||||
metadata.SaveLicense(*lc)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
package metadata
|
||||
|
||||
import (
|
||||
"gopkg.in/yaml.v3"
|
||||
"log"
|
||||
)
|
||||
|
||||
type License struct {
|
||||
Key *string `yaml:"k"`
|
||||
Username *string `yaml:"u"`
|
||||
Company *string `yaml:"c"`
|
||||
Type *LicenseType `yaml:"t"`
|
||||
Valid bool `yaml:"v"`
|
||||
}
|
||||
|
||||
type LicenseType rune
|
||||
|
||||
const (
|
||||
TypePersonal LicenseType = 0
|
||||
TypeCommercial LicenseType = 1
|
||||
)
|
||||
|
||||
const licenseFileName = "license.yml"
|
||||
|
||||
func GetLicense() *License {
|
||||
|
||||
if !fileExists(licenseFileName) {
|
||||
return nil
|
||||
}
|
||||
|
||||
file := readStorageFile(getPlatformStoragePath(licenseFileName))
|
||||
|
||||
license := new(License)
|
||||
err := yaml.Unmarshal(file, license)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to read license file: %v", err)
|
||||
}
|
||||
|
||||
return license
|
||||
}
|
||||
|
||||
func SaveLicense(license License) {
|
||||
|
||||
initStorage()
|
||||
|
||||
file, err := yaml.Marshal(license)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to marshal license file: %v", err)
|
||||
}
|
||||
|
||||
saveStorageFile(file, licenseFileName)
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
package metadata
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_getEmptyLicense(t *testing.T) {
|
||||
|
||||
cleanupPlatformStorage()
|
||||
license := GetLicense()
|
||||
|
||||
if license != nil {
|
||||
t.Errorf("expected to be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_saveAndGetExistingLicense(t *testing.T) {
|
||||
|
||||
cleanupPlatformStorage()
|
||||
|
||||
original := License{
|
||||
Valid: true,
|
||||
}
|
||||
|
||||
SaveLicense(original)
|
||||
|
||||
retrieved := *GetLicense()
|
||||
|
||||
if original != retrieved {
|
||||
t.Errorf("read file != saved file")
|
||||
}
|
||||
}
|
||||
|
||||
func cleanupPlatformStorage() {
|
||||
_ = os.RemoveAll(getPlatformStoragePath(""))
|
||||
_ = os.Remove(getPlatformStoragePath(""))
|
||||
}
|
|
@ -1,113 +0,0 @@
|
|||
package metadata
|
||||
|
||||
import (
|
||||
ui "github.com/gizak/termui/v3"
|
||||
"github.com/sqshq/sampler/config"
|
||||
"github.com/sqshq/sampler/console"
|
||||
"gopkg.in/yaml.v3"
|
||||
"log"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Statistics represents anonymous usage data, which we collect for analyses and improvements
|
||||
// User can disable it, along with crash reports, using --disable-telemetry flag
|
||||
type Statistics struct {
|
||||
Version string
|
||||
OS string
|
||||
WindowWidth int `yaml:"ww"`
|
||||
WindowHeight int `yaml:"wh"`
|
||||
LaunchCount int `yaml:"lc"`
|
||||
UsageTime int `yaml:"ut"`
|
||||
ComponentsCount map[string]int `yaml:"cc"`
|
||||
}
|
||||
|
||||
const statisticsFileName = "statistics.yml"
|
||||
|
||||
func PersistStatistics(config *config.Config, uptime time.Duration) *Statistics {
|
||||
|
||||
statistics := new(Statistics)
|
||||
w, h := ui.TerminalDimensions()
|
||||
|
||||
if fileExists(statisticsFileName) {
|
||||
file := readStorageFile(getPlatformStoragePath(statisticsFileName))
|
||||
err := yaml.Unmarshal(file, statistics)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to read statistics file: %v", err)
|
||||
}
|
||||
|
||||
if config != nil {
|
||||
statistics.ComponentsCount = countComponentsPerType(config)
|
||||
}
|
||||
|
||||
statistics.Version = console.AppVersion
|
||||
statistics.WindowWidth = w
|
||||
statistics.WindowHeight = h
|
||||
statistics.LaunchCount += 1
|
||||
statistics.UsageTime += int(uptime.Seconds())
|
||||
|
||||
} else {
|
||||
statistics = &Statistics{
|
||||
Version: console.AppVersion,
|
||||
OS: runtime.GOOS,
|
||||
WindowWidth: w,
|
||||
WindowHeight: h,
|
||||
LaunchCount: 1,
|
||||
UsageTime: 0,
|
||||
ComponentsCount: countComponentsPerType(config),
|
||||
}
|
||||
initStorage()
|
||||
}
|
||||
|
||||
file, err := yaml.Marshal(statistics)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to marshal statistics file: %v", err)
|
||||
}
|
||||
|
||||
saveStorageFile(file, statisticsFileName)
|
||||
|
||||
return statistics
|
||||
}
|
||||
|
||||
func GetStatistics(cfg *config.Config) *Statistics {
|
||||
|
||||
if !fileExists(statisticsFileName) {
|
||||
return &Statistics{
|
||||
Version: console.AppVersion,
|
||||
OS: runtime.GOOS,
|
||||
LaunchCount: 0,
|
||||
WindowWidth: 0,
|
||||
WindowHeight: 0,
|
||||
ComponentsCount: countComponentsPerType(cfg),
|
||||
}
|
||||
}
|
||||
|
||||
file := readStorageFile(getPlatformStoragePath(statisticsFileName))
|
||||
license := new(Statistics)
|
||||
|
||||
err := yaml.Unmarshal(file, license)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to read statistics file: %v", err)
|
||||
}
|
||||
|
||||
return license
|
||||
}
|
||||
|
||||
func countComponentsPerType(config *config.Config) map[string]int {
|
||||
|
||||
m := make(map[string]int)
|
||||
|
||||
if config == nil {
|
||||
return m
|
||||
}
|
||||
|
||||
m["runcharts"] = len(config.RunCharts)
|
||||
m["sparkLines"] = len(config.SparkLines)
|
||||
m["barcharts"] = len(config.BarCharts)
|
||||
m["gauges"] = len(config.Gauges)
|
||||
m["asciiboxes"] = len(config.AsciiBoxes)
|
||||
m["textboxes"] = len(config.TextBoxes)
|
||||
|
||||
return m
|
||||
}
|
Loading…
Reference in New Issue