Passed
Push — master ( 1ea234...f6784e )
by Stanislav
01:09
created

app/main.go   A

Size/Duplication

Total Lines 264
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
cc 42
eloc 170
dl 0
loc 264
rs 9.0399
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A main.main 0 3 1
A main.getLogger 0 19 4
A main.onExit 0 2 1
A main.onReady 0 17 1
F main.initApp 0 94 17
B main.checkNewVersion 0 35 7
A main.parseFlags 0 10 3
B main.connectServices 0 41 8
1
package main
2
3
import (
4
	"encoding/json"
5
	"fmt"
6
	"io"
7
	"io/ioutil"
8
	"log"
9
	"net/http"
10
	"os"
11
	"time"
12
13
	"flag"
14
	"github.com/getlantern/systray"
15
	"github.com/popstas/go-toggl"
16
	"github.com/viasite/planfix-toggl-server/app/client"
17
	"github.com/viasite/planfix-toggl-server/app/config"
18
	"github.com/viasite/planfix-toggl-server/app/icon"
19
	"github.com/viasite/planfix-toggl-server/app/rest"
20
	"github.com/viasite/planfix-toggl-server/app/util"
21
	"runtime"
22
)
23
24
var version string
25
var trayMenu map[string] *systray.MenuItem
26
27
func getLogger(cfg config.Config) *log.Logger {
28
	// logging
29
	logger := log.New(os.Stderr, "[planfix-toggl] ", log.LstdFlags)
30
	if cfg.Debug {
31
		logger.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds | log.Lshortfile)
32
	} else {
33
		toggl.DisableLog()
34
	}
35
	if cfg.LogFile != "" {
36
		f, err := os.OpenFile(cfg.LogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
37
		if err != nil {
38
			logger.Fatalf("[ERROR] Failed to open log file: %s", cfg.LogFile)
39
		}
40
		//defer f.Close()
41
		// file should be first, when -ldflags -H=windowsgui build, Stdout absent and block log output
42
		mw := io.MultiWriter(f, os.Stdout)
43
		logger.SetOutput(mw)
44
	}
45
	return logger
46
}
47
48
func parseFlags(cfg *config.Config) {
49
	dryRun := flag.Bool("dry-run", false, "Don't actually change data")
50
	if runtime.GOOS == "windows" {
51
		// Allow user to hide the console window
52
		flag.BoolVar(&cfg.NoConsole, "no-console", false, "Hide console window")
53
	}
54
	flag.Parse()
55
56
	if *dryRun {
57
		cfg.DryRun = true
58
	}
59
}
60
61
func connectServices(cfg *config.Config, logger *log.Logger, togglClient *client.TogglClient) (err error) {
62
	// toggl
63
	logger.Println("[INFO] подключение к Toggl...")
64
	account, err := togglClient.GetTogglUser()
65
	cfg.TogglUserID = account.Data.ID
66
	if err != nil {
67
		return err
68
	}
69
70
	ok, err := togglClient.IsWorkspaceExists(cfg.TogglWorkspaceID)
71
	if err != nil {
72
		return err
73
	}
74
	if !ok {
75
		return fmt.Errorf("Toggl workspace ID %d не найден", cfg.TogglWorkspaceID)
76
	}
77
78
	// planfix
79
	if cfg.PlanfixUserName != "" && cfg.PlanfixUserPassword != "" {
80
		logger.Println("[INFO] подключение к Планфиксу...")
81
		user, err := togglClient.GetPlanfixUser()
82
		cfg.PlanfixUserID = user.ID
83
		if err != nil {
84
			return err
85
		}
86
87
		logger.Println("[INFO] получение данных аналитики Планфикса...")
88
		_, err = togglClient.GetAnaliticDataCached(
89
			cfg.PlanfixAnaliticName,
90
			cfg.PlanfixAnaliticTypeName,
91
			cfg.PlanfixAnaliticTypeValue,
92
			cfg.PlanfixAnaliticCountName,
93
			cfg.PlanfixAnaliticCommentName,
94
			cfg.PlanfixAnaliticDateName,
95
			cfg.PlanfixAnaliticUsersName,
96
		)
97
		if err != nil {
98
			return fmt.Errorf("Поля аналитики указаны неправильно: %s", err.Error())
99
		}
100
	}
101
	return nil
102
}
103
104
func initApp() {
105
	fmt.Printf("planfix-toggl %s\n", version)
106
	cfg := config.GetConfig()
107
108
	parseFlags(&cfg)
109
110
	logger := getLogger(cfg)
111
112
	errors, isValid := cfg.Validate()
113
	if !isValid {
114
		for _, e := range errors {
115
			log.Println(e)
116
		}
117
	}
118
119
	if cfg.NoConsole {
120
		util.HideConsole()
121
	}
122
123
	if cfg.CheckNewVersion {
124
		checkNewVersion()
125
	}
126
127
	togglClient := client.TogglClient{
128
		Config:  &cfg,
129
		Logger:  logger,
130
		SentLog: make(map[string]int),
131
		Opts:    map[string]string{"LastSent": ""},
132
	}
133
	togglClient.ReloadConfig()
134
135
	// get planfix and toggl user IDs, for early API check
136
	err := connectServices(&cfg, logger, &togglClient)
137
	if err != nil {
138
		isValid = false
139
		logger.Printf("[ERROR] %s", err.Error())
140
		util.Notify(err.Error())
141
	}
142
143
	trayMenu["web"].Enable()
144
	trayMenu["log"].Enable()
145
146
	if isValid {
147
		trayMenu["send"].Enable()
148
		togglClient.Run()
149
	} else {
150
		util.OpenBrowser(fmt.Sprintf("https://localhost:%d/#settings", cfg.PortSSL))
151
	}
152
153
	// update last sent on menuitem
154
	go func() {
155
		for {
156
			if togglClient.Opts["LastSent"] != "" {
157
				t := togglClient.Opts["LastSent"]
158
				trayMenu["send"].SetTitle(fmt.Sprintf("Sync (last at %s)", t))
159
			}
160
			time.Sleep(time.Duration(60 * time.Second))
161
		}
162
	}()
163
164
	go func() {
165
		// tray menu actions
166
		for {
167
			select {
168
			case <-trayMenu["send"].ClickedCh:
169
				err := togglClient.SendToPlanfix()
170
				t := togglClient.Opts["LastSent"]
171
				trayMenu["send"].SetTitle(fmt.Sprintf("Sync (last at %s)", t))
172
				if err != nil {
173
					logger.Println(err)
174
				}
175
176
			case <-trayMenu["web"].ClickedCh:
177
				cfg := config.GetConfig()
178
				util.OpenBrowser(fmt.Sprintf("https://localhost:%d", cfg.PortSSL))
179
180
			case <-trayMenu["log"].ClickedCh:
181
				cfg := config.GetConfig()
182
				systray.ShowAppWindow(fmt.Sprintf("https://localhost:%d/log", cfg.PortSSL))
183
184
			case <-trayMenu["quit"].ClickedCh:
185
				onExit()
186
			}
187
		}
188
	}()
189
190
	// start API server
191
	server := rest.Server{
192
		Version:     version,
193
		TogglClient: &togglClient,
194
		Config:      &cfg,
195
		Logger:      logger,
196
	}
197
	server.Run(cfg.PortSSL)
198
}
199
200
func checkNewVersion () {
201
	if version == "" {
202
		return
203
	}
204
	versionUrl := "https://raw.githubusercontent.com/viasite/planfix-toggl-server/master/package.json"
0 ignored issues
show
introduced by
var versionUrl should be versionURL
Loading history...
205
	c := http.Client{
206
		Timeout: time.Second * 2,
207
	}
208
	req, err := http.NewRequest(http.MethodGet, versionUrl, nil)
209
	if err != nil {
210
		return
211
	}
212
	res, getErr := c.Do(req)
213
	if getErr != nil {
214
		return
215
	}
216
217
	body, readErr := ioutil.ReadAll(res.Body)
218
	if readErr != nil {
219
		log.Fatal(readErr)
220
	}
221
	/*if body != nil {
222
		defer body.Close()
223
	}*/
224
225
	packageJson := struct {
0 ignored issues
show
introduced by
var packageJson should be packageJSON
Loading history...
226
		Version string `json:"version"`
227
	}{}
228
	jsonErr := json.Unmarshal(body, &packageJson)
229
	if jsonErr != nil {
230
		log.Fatal(jsonErr)
231
	}
232
233
	if version != packageJson.Version {
234
		util.Notify(fmt.Sprintf("Доступна новая версия: %s", packageJson.Version))
235
	}
236
}
237
238
func onReady() {
239
	go initApp()
240
241
	// systray.EnableAppWindow("Lantern", 1024, 768) // in next systray versions
242
	systray.SetIcon(icon.Data)
243
	systray.SetTitle("planfix-toggl")
244
	systray.SetTooltip(fmt.Sprintf("planfix-toggl %s", version))
245
246
	trayMenu = make(map[string]*systray.MenuItem)
247
	trayMenu["send"] = systray.AddMenuItem("Sync", "")
248
	trayMenu["web"] = systray.AddMenuItem("Open web interface", "")
249
	trayMenu["log"] = systray.AddMenuItem("Open log", "")
250
	trayMenu["quit"] = systray.AddMenuItem("Quit", "Quit the whole app")
251
252
	trayMenu["send"].Disable()
253
	trayMenu["web"].Disable()
254
	trayMenu["log"].Disable()
255
}
256
257
func onExit() {
258
	systray.Quit()
259
	//os.Exit(0)
260
}
261
262
func main() {
263
	//systray.Run(onReady, onExit)
264
	systray.RunWithAppWindow("planfix-toggl", 1024, 768, onReady, onExit)
265
}
266