1
// Copyright 2016 - 2020 The aurora Authors. All rights reserved. Use of this
2
// source code is governed by a MIT license that can be found in the LICENSE
3
// file.
4
//
5
// The aurora is a web-based beanstalkd queue server console written in Go
6
// and works on macOS, Linux and Windows machines. Main idea behind using Go
7
// for backend development is to utilize ability of the compiler to produce
8
// zero-dependency binaries for multiple platforms. aurora was created as an
9
// attempt to build very simple and portable application to work with local or
10
// remote beanstalkd server.
11

12
package main
13

14
import (
15
	"bytes"
16
	"encoding/base64"
17
	"encoding/json"
18
	"flag"
19
	"fmt"
20
	"html"
21
	"io"
22
	"io/ioutil"
23
	"math/rand"
24
	"net/http"
25
	"os"
26
	"os/exec"
27
	"path/filepath"
28
	"strconv"
29
	"strings"
30
	"time"
31

32
	"github.com/BurntSushi/toml"
33
)
34

35
// readConf read external config file when program startup.
36
func readConf() error {
37 10
	buf := new(strings.Builder)
38 10
	if _, err := os.Stat(ConfigFile); os.IsNotExist(err) {
39 10
		err := ioutil.WriteFile(ConfigFile, []byte(ConfigFileTemplate), 0644)
40 10
		if err != nil {
41 0
			return err
42
		}
43
	}
44 10
	buf.Reset()
45 10
	tomlData, err := os.Open(ConfigFile)
46 10
	if err != nil {
47 0
		return err
48
	}
49 10
	_, err = io.Copy(buf, tomlData)
50 10
	if err != nil {
51 0
		return err
52
	}
53 10
	tomlData.Close()
54 10
	if _, err := toml.Decode(buf.String(), &pubConf); err != nil {
55 0
		return err
56
	}
57 10
	if err := json.Unmarshal([]byte(pubConf.Sample.Storage), &sampleJobs); err != nil {
58 0
		return err
59
	}
60 10
	parseConf()
61 10
	return nil
62
}
63

64
// parseConf parse server config in external config file.
65
func parseConf() {
66 10
	selfConf.Servers = append(selfConf.Servers, pubConf.Servers...)
67
}
68

69
// removeArrayDuplicates provide a function remove duplicates value elements in
70
// a slice.
71
func removeArrayDuplicates(elements []string) []string {
72
	// Use map to record duplicates as we find them.
73 10
	encountered := map[string]bool{}
74 10
	result := []string{}
75

76
	for v := range elements {
77 10
		if encountered[elements[v]] {
78
			// Do not add duplicate.
79 10
		} else {
80
			// Record this element as an encountered element.
81 10
			encountered[elements[v]] = true
82
			// Append to result slice.
83 10
			result = append(result, elements[v])
84
		}
85
	}
86
	// Return the new slice.
87 10
	return result
88
}
89

90
// removeArrayEmpty provide a function remove empty value elements in a slice.
91
func removeArrayEmpty(s []string) []string {
92 10
	var r []string
93
	for _, str := range s {
94 10
		if str != "" {
95 10
			r = append(r, str)
96
		}
97
	}
98 10
	return r
99
}
100

101
// removeServerInConfig provide a method to remove property in config by given
102
// field.
103
func removeServerInConfig(server string) {
104
	for k, v := range selfConf.Servers {
105 10
		if v == server {
106 10
			selfConf.Servers = selfConf.Servers[:k+copy(selfConf.Servers[k:], selfConf.Servers[k+1:])]
107
		}
108
	}
109
}
110

111
// runCmd run command opens a new browser window pointing to url.
112
func runCmd(prog string, args ...string) error {
113 10
	cmd := exec.Command(prog, args...)
114 10
	cmd.Stdout = Stdout
115 10
	cmd.Stderr = Stderr
116 10
	return cmd.Run()
117
}
118

119
// checkInSlice return bool type value to check if exits in slice by given
120
// string.
121
func checkInSlice(list []string, value string) bool {
122 10
	set := make(map[string]bool)
123
	for _, v := range list {
124 10
		set[v] = true
125
	}
126 10
	return set[value]
127
}
128

129
// prettyJSON provide method get JSON string with indent.
130
func prettyJSON(b []byte) []byte {
131 10
	var out bytes.Buffer
132 10
	err := json.Indent(&out, b, "", "\t")
133 10
	if err != nil {
134 9
		return b
135
	}
136 10
	return out.Bytes()
137
}
138

139
// base64Decode provide method get Base64 decode string.
140
func base64Decode(b string) string {
141 10
	data, err := base64.StdEncoding.DecodeString(b)
142 10
	if err != nil {
143 10
		return string(b)
144
	}
145 10
	return string(data)
146
}
147

148
// preformat provide method get job body after format with config.
149
func preformat(jobBody []byte) string {
150 9
	var job = string(jobBody)
151 9
	if selfConf.IsDisabledJSONDecode != 1 {
152 9
		job = string(prettyJSON(jobBody))
153
	}
154 9
	if selfConf.IsEnabledBase64Decode != 0 {
155 0
		job = base64Decode(job)
156
	}
157 9
	job = html.EscapeString(job)
158 9
	return job
159
}
160

161
// parseFlags parse flags of program.
162
func parseFlags() {
163 10
	configPtr := flag.String("c", "", "Use config file.")
164 10
	verPtr := flag.Bool("v", false, "Output version and exit.")
165 10
	helpPtr := flag.Bool("h", false, "Output this help and exit.")
166 10
	flag.Parse()
167 10
	if *configPtr == "" {
168 10
		selfDir, err := filepath.Abs(filepath.Dir(os.Args[0]))
169 10
		if err != nil {
170 0
			os.Exit(0)
171
		}
172 10
		ConfigFile = selfDir + string(os.PathSeparator) + `aurora.toml`
173 0
	} else {
174 0
		ConfigFile = *configPtr
175
	}
176 10
	if *verPtr {
177 0
		fmt.Printf("aurora version: %.1f\r\n", Version)
178 0
		os.Exit(0)
179
	}
180 10
	if *helpPtr {
181 0
		fmt.Printf("aurora version: %.1f\r\nCopyright (c) 2016 - 2020 Ri Xu https://xuri.me All rights reserved.\r\n\r\nUsage: aurora [OPTIONS] [cmd [arg ...]]\n  -c <filename>   Use config file. (default: aurora.toml)\r\n  -h \t\t  Output this help and exit.\r\n  -v \t\t  Output version and exit.\r\n", Version)
182 0
		os.Exit(0)
183
	}
184
}
185

186
// basicAuth provide a simple method to HTTP authenticate.
187
func basicAuth(f ViewFunc) ViewFunc {
188 10
	if !pubConf.Auth.Enabled {
189 10
		return func(w http.ResponseWriter, r *http.Request) {
190 10
			f(w, r)
191
		}
192
	}
193 10
	return func(w http.ResponseWriter, r *http.Request) {
194 10
		basicAuthPrefix := "Basic "
195
		// Parse request header
196 10
		auth := r.Header.Get("Authorization")
197 10
		if strings.HasPrefix(auth, basicAuthPrefix) {
198
			// Decoding authentication information.
199 10
			payload, err := base64.StdEncoding.DecodeString(
200 10
				auth[len(basicAuthPrefix):],
201 10
			)
202 10
			if err == nil {
203 10
				pair := bytes.SplitN(payload, []byte(":"), 2)
204 10
				if len(pair) == 2 && bytes.Equal(pair[0], []byte(pubConf.Auth.Username)) &&
205 10
					bytes.Equal(pair[1], []byte(pubConf.Auth.Password)) {
206 10
					f(w, r)
207 10
					return
208
				}
209
			}
210
		}
211
		// Authorization fail, return 401 Unauthorized.
212 10
		w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
213 10
		w.WriteHeader(http.StatusUnauthorized)
214
	}
215
}
216

217
// randToken generate a random token with MD5.
218
func randToken() string {
219 10
	rand.Seed(time.Now().UnixNano())
220 10
	b := make([]byte, 16)
221 10
	rand.Read(b)
222 10
	return fmt.Sprintf("%x", b)
223
}
224

225
// setHeader provide common method set HTTP header response.
226
func setHeader(w http.ResponseWriter, r *http.Request) {
227 10
	w.Header().Set("Server", "WebServer")
228 10
	w.Header().Set("Content-Type", "text/html")
229 10
	w.Header().Set("Cache-Control", "no-cache, private, max-age=0")
230 10
	w.Header().Set("Expires", time.Unix(0, 0).Format(http.TimeFormat))
231 10
	w.Header().Set("Pragma", "no-cache")
232 10
	w.Header().Set("X-Accel-Expires", "0")
233
}
234

235
// checkUpdate render update notice alert.
236
func checkUpdate() string {
237 10
	if updateInfo != "uncheck" {
238 10
		return updateInfo
239
	}
240 10
	updateInfo = ""
241 10
	r, err := http.Get(UpdateURL)
242 10
	if err != nil {
243 0
		return updateInfo
244
	}
245 10
	body, err := ioutil.ReadAll(r.Body)
246 10
	if err != nil {
247 0
		r.Body.Close()
248 0
		return updateInfo
249
	}
250 10
	r.Body.Close()
251 10
	u := UpdateTags{}
252 10
	err = json.Unmarshal(body, &u)
253 10
	if err != nil {
254 0
		return updateInfo
255
	}
256 10
	if len(u) < 1 {
257 0
		return updateInfo
258
	}
259 10
	v, err := strconv.ParseFloat(u[0].Name, 64)
260 10
	if err != nil {
261 0
		return updateInfo
262
	}
263 10
	if Version < v {
264 0
		updateInfo = fmt.Sprintf(`<br/><div class="alert alert-info" style="position: relative;top:50px;"><span>You are currently running version %.1f of aurora. A new version is available: <b>%.1f</b> Get it from <b><a href="https://github.com/xuri/aurora" target="_blank">GitHub</a></b></span></div>`, Version, v)
265
	}
266 10
	return updateInfo
267
}

Read our documentation on viewing source code .

Loading