Commit b80ee4b2bc37bdc1ab97b7a2eb302badaa906cd2
1 parent
832243a5df
Exists in
master
new stuff
Showing
9 changed files
with
276 additions
and
140 deletions
Show diff stats
date_util.go
... | ... | @@ -0,0 +1,41 @@ |
1 | +package webutility | |
2 | + | |
3 | +import ( | |
4 | + "fmt" | |
5 | + "time" | |
6 | +) | |
7 | + | |
8 | +const ( | |
9 | + YYYYMMDD_sl = "2006/01/02" | |
10 | + YYYYMMDD_ds = "2006-01-02" | |
11 | + YYYYMMDD_dt = "2006.01.02." | |
12 | + | |
13 | + DDMMYYYY_sl = "02/01/2006" | |
14 | + DDMMYYYY_ds = "02-01-2006" | |
15 | + DDMMYYYY_dt = "02.01.2006." | |
16 | + | |
17 | + YYYYMMDD_HHMMSS_sl = "2006/01/02 15:04:05" | |
18 | + YYYYMMDD_HHMMSS_ds = "2006-01-02 15:04:05" | |
19 | + YYYYMMDD_HHMMSS_dt = "2006.01.02. 15:04:05" | |
20 | + | |
21 | + DDMMYYYY_HHMMSS_sl = "02/01/2006 15:04:05" | |
22 | + DDMMYYYY_HHMMSS_ds = "02-01-2006 15:04:05" | |
23 | + DDMMYYYY_HHMMSS_dt = "02.01.2006. 15:04:05" | |
24 | +) | |
25 | + | |
26 | +func Systime() int64 { | |
27 | + return time.Now().Unix() | |
28 | +} | |
29 | + | |
30 | +func DateToEpoch(date, format string) int64 { | |
31 | + t, err := time.Parse(format, date) | |
32 | + if err != nil { | |
33 | + fmt.Println(err.Error()) | |
34 | + return 0 | |
35 | + } | |
36 | + return t.Unix() | |
37 | +} | |
38 | + | |
39 | +func EpochToDate(e int64, format string) string { | |
40 | + return time.Unix(e, 0).Format(format) | |
41 | +} | ... | ... |
filtering.go
1 | 1 | package webutility |
2 | 2 | |
3 | 3 | import ( |
4 | - "fmt" | |
5 | 4 | "net/http" |
6 | 5 | "net/url" |
7 | 6 | "strings" |
... | ... | @@ -37,7 +36,7 @@ func (f Filter) ValueAt(val string, index int) string { |
37 | 36 | return "" |
38 | 37 | } |
39 | 38 | |
40 | -func (f Filter) validate(validFilters []string) (Filter, bool) { | |
39 | +func (f Filter) Validate(validFilters []string) (Filter, bool) { | |
41 | 40 | goodFilters := make(Filter) |
42 | 41 | cnt, len := 0, 0 |
43 | 42 | for fi := range f { |
... | ... | @@ -82,53 +81,3 @@ func ParseFilters(req *http.Request, header string) (filters Filter) { |
82 | 81 | |
83 | 82 | return filters |
84 | 83 | } |
85 | - | |
86 | -// MakeFilterString is very dodgy, needs more robustness. | |
87 | -// TODO(marko) | |
88 | -func MakeFilterString(prefix string, filters Filter, validFilters []string) (res string, ok bool) { | |
89 | - if prefix != "" { | |
90 | - prefix += "." | |
91 | - } | |
92 | - | |
93 | - if filters.Count() == 0 { | |
94 | - return "", true | |
95 | - } | |
96 | - | |
97 | - filters, ok = filters.validate(validFilters) | |
98 | - if !ok { | |
99 | - return "", false | |
100 | - } | |
101 | - | |
102 | - first := true | |
103 | - for k, filter := range filters { | |
104 | - symbol := "=" | |
105 | - | |
106 | - if first { | |
107 | - res += " where " | |
108 | - first = false | |
109 | - } else { | |
110 | - res += " and " | |
111 | - } | |
112 | - | |
113 | - res += "(" | |
114 | - for i, f := range filter { | |
115 | - if strings.HasPrefix(f, "<") || strings.HasPrefix(f, ">") || strings.HasPrefix(f, "!") { | |
116 | - symbol = string(f[0]) | |
117 | - f = strings.TrimLeft(f, "<>!") | |
118 | - if strings.HasPrefix(f, "=") { | |
119 | - f = strings.TrimLeft(f, "=") | |
120 | - symbol += "=" | |
121 | - } | |
122 | - } | |
123 | - | |
124 | - res += fmt.Sprintf("%s%s %s '%s'", prefix, k, symbol, f) | |
125 | - | |
126 | - if i < len(filter)-1 { | |
127 | - res += " or " | |
128 | - } | |
129 | - } | |
130 | - res += ")" | |
131 | - } | |
132 | - | |
133 | - return res, ok | |
134 | -} | ... | ... |
float_util.go
... | ... | @@ -0,0 +1,59 @@ |
1 | +package webutility | |
2 | + | |
3 | +import ( | |
4 | + "fmt" | |
5 | + "math" | |
6 | + "math/big" | |
7 | +) | |
8 | + | |
9 | +func RoundFloat64(f float64, dec int) float64 { | |
10 | + p := math.Pow(10, float64(dec)) | |
11 | + return math.Round(f*p) / p | |
12 | +} | |
13 | + | |
14 | +func NewBF(f float64, prec uint) *big.Float { | |
15 | + x := big.NewFloat(f) | |
16 | + x.SetPrec(prec) | |
17 | + return x | |
18 | +} | |
19 | + | |
20 | +func AddBF(x, y *big.Float) *big.Float { | |
21 | + z := big.NewFloat(0.0) | |
22 | + z.SetPrec(x.Prec()) | |
23 | + z.Add(x, y) | |
24 | + return z | |
25 | +} | |
26 | + | |
27 | +func SubBF(x, y *big.Float) *big.Float { | |
28 | + z := big.NewFloat(0.0) | |
29 | + z.SetPrec(x.Prec()) | |
30 | + | |
31 | + yneg := big.NewFloat(0.0) | |
32 | + yneg.Neg(y) | |
33 | + | |
34 | + z.Add(x, yneg) | |
35 | + return z | |
36 | +} | |
37 | + | |
38 | +func MulBF(x, y *big.Float) *big.Float { | |
39 | + z := big.NewFloat(0.0) | |
40 | + z.SetPrec(x.Prec()) | |
41 | + z.Mul(x, y) | |
42 | + return z | |
43 | +} | |
44 | + | |
45 | +func DivBF(x, y *big.Float) *big.Float { | |
46 | + z := big.NewFloat(0.0) | |
47 | + z.SetPrec(x.Prec()) | |
48 | + z.Quo(x, y) | |
49 | + return z | |
50 | +} | |
51 | + | |
52 | +func BFtoFloat(f *big.Float) float64 { | |
53 | + v, _ := f.Float64() | |
54 | + return v | |
55 | +} | |
56 | + | |
57 | +func Float64ToString(f float64) string { | |
58 | + return fmt.Sprintf("%.2f", f) | |
59 | +} | ... | ... |
int_util.go
... | ... | @@ -2,7 +2,6 @@ package webutility |
2 | 2 | |
3 | 3 | import ( |
4 | 4 | "fmt" |
5 | - "strconv" | |
6 | 5 | ) |
7 | 6 | |
8 | 7 | // ClampInt64 ... |
... | ... | @@ -21,18 +20,6 @@ func InRangeInt64(v, min, max int64) bool { |
21 | 20 | return (v >= min && v <= max) |
22 | 21 | } |
23 | 22 | |
24 | -// StringToInt64 ... | |
25 | -func StringToInt64(s string) int64 { | |
26 | - i, _ := strconv.ParseInt(s, 10, 64) | |
27 | - return i | |
28 | -} | |
29 | - | |
30 | -// StringToFloat64 ... | |
31 | -func StringToFloat64(s string) float64 { | |
32 | - f, _ := strconv.ParseFloat(s, 64) | |
33 | - return f | |
34 | -} | |
35 | - | |
36 | 23 | // Int64ToString ... |
37 | 24 | func Int64ToString(i int64) string { |
38 | 25 | return fmt.Sprintf("%d", i) |
... | ... | @@ -51,10 +38,11 @@ func Int64ToBool(i int64) bool { |
51 | 38 | return i != 0 |
52 | 39 | } |
53 | 40 | |
54 | -func StringToValidInt64(s string) (int64, bool) { | |
55 | - i, err := strconv.ParseInt(s, 10, 64) | |
56 | - if err != nil { | |
57 | - return i, false | |
41 | +func MaxInt(vars ...int) (max int) { | |
42 | + for _, v := range vars { | |
43 | + if v > max { | |
44 | + max = v | |
45 | + } | |
58 | 46 | } |
59 | - return i, true | |
47 | + return max | |
60 | 48 | } | ... | ... |
middleware/main.go
... | ... | @@ -4,6 +4,8 @@ import ( |
4 | 4 | "fmt" |
5 | 5 | "io/ioutil" |
6 | 6 | "net/http" |
7 | + "os" | |
8 | + "strings" | |
7 | 9 | |
8 | 10 | web "git.to-net.rs/marko.tikvic/webutility" |
9 | 11 | ) |
... | ... | @@ -27,54 +29,103 @@ func LogTraffic(h http.HandlerFunc) http.HandlerFunc { |
27 | 29 | func TrafficLogsHandler(w http.ResponseWriter, req *http.Request) { |
28 | 30 | switch req.Method { |
29 | 31 | case "GET": |
30 | - files, err := ioutil.ReadDir(httpLogger.GetOutDir() + "/") | |
31 | - if err != nil { | |
32 | - web.InternalServerError(w, req, err.Error()) | |
33 | - return | |
34 | - } | |
32 | + logfile := req.FormValue("logfile") | |
33 | + if logfile == "" { | |
34 | + files, err := ioutil.ReadDir(httpLogger.GetOutDir() + "/") | |
35 | + if err != nil { | |
36 | + web.InternalServerError(w, req, err.Error()) | |
37 | + return | |
38 | + } | |
35 | 39 | |
36 | - web.SetContentType(w, "text/html; charset=utf-8") | |
37 | - web.SetResponseStatus(w, http.StatusOK) | |
38 | - | |
39 | - web.WriteResponse(w, []byte("<body style='background-color: black; color: white'>")) | |
40 | - inputForm := ` | |
41 | - <div> | |
42 | - <form action="/api/v1/logs" method="POST" target="_blank"> | |
43 | - Username:<br> | |
44 | - <input type="text" name="username"><br> | |
45 | - Password:<br> | |
46 | - <input type="password" name="password"><br> | |
47 | - Log:<br> | |
48 | - <input type="text" name="logfile"><br> | |
49 | - <input type="submit" value="View"> | |
50 | - </form> | |
51 | - </div>` | |
52 | - web.WriteResponse(w, []byte(inputForm)) | |
53 | - | |
54 | - web.WriteResponse(w, []byte("<table>")) | |
55 | - web.WriteResponse(w, []byte("<tr><th>Name</th><th>Size</th></tr>")) | |
56 | - for i := range files { | |
57 | - name := files[i].Name() | |
58 | - size := files[i].Size() | |
59 | - div := fmt.Sprintf(`<tr><td>%s</td><td style="text-align:right">%dB</td></tr>`, name, size) | |
60 | - web.WriteResponse(w, []byte(div)) | |
61 | - } | |
62 | - web.WriteResponse(w, []byte("</table></body>")) | |
40 | + errorLogs := make([]os.FileInfo, 0) | |
41 | + httpLogs := make([]os.FileInfo, 0) | |
63 | 42 | |
64 | - case "POST": | |
65 | - web.SetContentType(w, "text/html; charset=utf-8") | |
43 | + var errorLogsCount, httpLogsCount int | |
44 | + for _, f := range files { | |
45 | + if strings.HasPrefix(f.Name(), "err") { | |
46 | + errorLogs = append(errorLogs, f) | |
47 | + errorLogsCount++ | |
48 | + } else if strings.HasPrefix(f.Name(), "http") { | |
49 | + httpLogs = append(httpLogs, f) | |
50 | + httpLogsCount++ | |
51 | + } | |
52 | + } | |
66 | 53 | |
67 | - logfile := req.FormValue("logfile") | |
68 | - content, err := web.ReadFileContent(httpLogger.GetOutDir() + "/" + logfile) | |
69 | - if err != nil { | |
70 | - web.InternalServerError(w, req, err.Error()) | |
71 | - return | |
54 | + web.SetContentType(w, "text/html; charset=utf-8") | |
55 | + web.SetResponseStatus(w, http.StatusOK) | |
56 | + | |
57 | + web.WriteResponse(w, []byte(` | |
58 | + <body style='background-color: black; color: white'> | |
59 | + <table> | |
60 | + <tr> | |
61 | + <th>Error logs</th><th></th> <th style="width: 25px"></th> | |
62 | + <th>Traffic logs</th><th></th> | |
63 | + </tr> | |
64 | + `)) | |
65 | + | |
66 | + var ( | |
67 | + div, name string | |
68 | + size int64 | |
69 | + ) | |
70 | + | |
71 | + max := errorLogsCount | |
72 | + if httpLogsCount > errorLogsCount { | |
73 | + max = httpLogsCount | |
74 | + } | |
75 | + | |
76 | + for i := 0; i < max; i++ { | |
77 | + div = "<tr>" | |
78 | + | |
79 | + if i < errorLogsCount { | |
80 | + name = errorLogs[i].Name() | |
81 | + size = errorLogs[i].Size() | |
82 | + div += fmt.Sprintf(` | |
83 | + <td> | |
84 | + <a style="color: white" | |
85 | + href="/api/v1/logs?logfile=%s" | |
86 | + target="_blank">%s | |
87 | + </a> | |
88 | + </td> | |
89 | + <td style="color: white; text-align:right">%dB</td>`, | |
90 | + name, name, size, | |
91 | + ) | |
92 | + } else { | |
93 | + div += fmt.Sprintf(`<td></td><td></td>`) | |
94 | + } | |
95 | + | |
96 | + div += "<td></td>" | |
97 | + | |
98 | + if i < httpLogsCount { | |
99 | + name := httpLogs[i].Name() | |
100 | + size := httpLogs[i].Size() | |
101 | + div += fmt.Sprintf(` | |
102 | + <td> | |
103 | + <a style="color: white" | |
104 | + href="/api/v1/logs?logfile=%s" | |
105 | + target="_blank">%s | |
106 | + </a></td> | |
107 | + <td style="color: white; text-align:right">%dB</td>`, | |
108 | + name, name, size, | |
109 | + ) | |
110 | + } else { | |
111 | + div += fmt.Sprintf(`<td></td><td></td>`) | |
112 | + } | |
113 | + | |
114 | + div += "</tr>" | |
115 | + web.WriteResponse(w, []byte(div)) | |
116 | + } | |
117 | + web.WriteResponse(w, []byte("</table></body>")) | |
118 | + } else { | |
119 | + content, err := web.ReadFileContent(httpLogger.GetOutDir() + "/" + logfile) | |
120 | + if err != nil { | |
121 | + web.InternalServerError(w, req, err.Error()) | |
122 | + return | |
123 | + } | |
124 | + web.SetResponseStatus(w, http.StatusOK) | |
125 | + web.WriteResponse(w, []byte("<body style='background-color: black; color: white'>")) | |
126 | + web.WriteResponse(w, []byte("<pre>")) | |
127 | + web.WriteResponse(w, content) | |
128 | + web.WriteResponse(w, []byte("</pre></body>")) | |
72 | 129 | } |
73 | - web.SetResponseStatus(w, http.StatusOK) | |
74 | - web.WriteResponse(w, []byte("<body style='background-color: black; color: white'>")) | |
75 | - web.WriteResponse(w, []byte("<pre>")) | |
76 | - web.WriteResponse(w, content) | |
77 | - web.WriteResponse(w, []byte("</pre></body>")) | |
78 | - return | |
79 | 130 | } |
80 | 131 | } | ... | ... |
nullables.go
... | ... | @@ -123,6 +123,14 @@ func (ni *NullInt64) Scan(value interface{}) error { |
123 | 123 | return nil |
124 | 124 | } |
125 | 125 | |
126 | +// ScanPtr ... | |
127 | +func (ni *NullInt64) ScanPtr(v interface{}) error { | |
128 | + if ip, ok := v.(*int64); ok && ip != nil { | |
129 | + return ni.Scan(*ip) | |
130 | + } | |
131 | + return nil | |
132 | +} | |
133 | + | |
126 | 134 | // Value ... |
127 | 135 | func (ni *NullInt64) Value() (driver.Value, error) { |
128 | 136 | if !ni.Valid { |
... | ... | @@ -173,6 +181,14 @@ func (nf *NullFloat64) Scan(value interface{}) error { |
173 | 181 | return nil |
174 | 182 | } |
175 | 183 | |
184 | +// ScanPtr ... | |
185 | +func (nf *NullFloat64) ScanPtr(v interface{}) error { | |
186 | + if fp, ok := v.(*float64); ok && fp != nil { | |
187 | + return nf.Scan(*fp) | |
188 | + } | |
189 | + return nil | |
190 | +} | |
191 | + | |
176 | 192 | // Value ... |
177 | 193 | func (nf *NullFloat64) Value() (driver.Value, error) { |
178 | 194 | if !nf.Valid { | ... | ... |
payload.go
... | ... | @@ -51,13 +51,13 @@ type Translation struct { |
51 | 51 | // Payload ... |
52 | 52 | type Payload struct { |
53 | 53 | Method string `json:"method"` |
54 | - Params map[string]string `json:"params"` | |
55 | - Lang []Translation `json:"lang"` | |
56 | - Fields []Field `json:"fields"` | |
57 | - Correlations []CorrelationField `json:"correlationFields"` | |
58 | - IDField string `json:"idField"` | |
54 | + Params map[string]string `json:"params,omitempty"` | |
55 | + Lang []Translation `json:"lang,omitempty"` | |
56 | + Fields []Field `json:"fields,omitempty"` | |
57 | + Correlations []CorrelationField `json:"correlationFields,omitempty"` | |
58 | + IDField string `json:"idField,omitempty"` | |
59 | 59 | |
60 | - Links PaginationLinks `json:"_links"` | |
60 | + Links PaginationLinks `json:"_links,omitempty"` | |
61 | 61 | |
62 | 62 | // Data holds JSON payload. It can't be used for itteration. |
63 | 63 | Data interface{} `json:"data"` | ... | ... |
quicksort.go
... | ... | @@ -3,8 +3,8 @@ package webutility |
3 | 3 | type QSortDirection int |
4 | 4 | |
5 | 5 | const ( |
6 | - QSortAscending = 0 | |
7 | - QSortDescending = 1 | |
6 | + QSortAscending QSortDirection = iota | |
7 | + QSortDescending | |
8 | 8 | ) |
9 | 9 | |
10 | 10 | // QuickSortable is an interface for quicksorting slices. |
... | ... | @@ -30,19 +30,22 @@ func Quicksort(que QuickSortable, low, high int, dir QSortDirection) { |
30 | 30 | } |
31 | 31 | |
32 | 32 | func partition(que QuickSortable, low, high int, dir QSortDirection) int { |
33 | + swap := 0 | |
34 | + // -1 -> i > j | |
35 | + // 1 -> i < j | |
36 | + if dir == QSortDescending { | |
37 | + swap = -1 | |
38 | + } else { | |
39 | + swap = 1 | |
40 | + } | |
41 | + | |
33 | 42 | i := low - 1 |
34 | 43 | for j := low; j <= high-1; j++ { |
35 | - if dir == QSortAscending { | |
36 | - if que.Compare(j, high) == -1 { | |
37 | - i++ | |
38 | - que.Swap(i, j) | |
39 | - } | |
40 | - } else if dir == QSortDescending { | |
41 | - if que.Compare(j, high) == 1 { | |
42 | - i++ | |
43 | - que.Swap(i, j) | |
44 | - } | |
44 | + if que.Compare(j, high) == swap { | |
45 | + i++ | |
46 | + que.Swap(i, j) | |
45 | 47 | } |
48 | + continue | |
46 | 49 | } |
47 | 50 | que.Swap(i+1, high) |
48 | 51 | return i + 1 | ... | ... |
string_util.go
... | ... | @@ -22,13 +22,14 @@ func IsWrappedWith(src, begin, end string) bool { |
22 | 22 | // ParseInt64Arr ... |
23 | 23 | func ParseInt64Arr(s, sep string) (arr []int64) { |
24 | 24 | s = strings.TrimSpace(s) |
25 | - if s != "" { | |
26 | - parts := strings.Split(s, sep) | |
27 | - arr = make([]int64, len(parts)) | |
28 | - for i, p := range parts { | |
29 | - num := StringToInt64(p) | |
30 | - arr[i] = num | |
31 | - } | |
25 | + if s == "" { | |
26 | + return | |
27 | + } | |
28 | + parts := strings.Split(s, sep) | |
29 | + arr = make([]int64, len(parts)) | |
30 | + for i, p := range parts { | |
31 | + num := StringToInt64(p) | |
32 | + arr[i] = num | |
32 | 33 | } |
33 | 34 | |
34 | 35 | return arr |
... | ... | @@ -174,6 +175,14 @@ func LimitMSWordTextWithThreeDots(txt string, maxLen int) string { |
174 | 175 | return txt[:maxLen] + "..." |
175 | 176 | } |
176 | 177 | |
178 | +func ThreeDots(txt string, maxLen int) string { | |
179 | + if len(txt) <= maxLen { | |
180 | + return txt | |
181 | + } | |
182 | + | |
183 | + return txt[:maxLen] + "..." | |
184 | +} | |
185 | + | |
177 | 186 | // SplitStringAtWholeWords ... |
178 | 187 | func SplitStringAtWholeWords(s string, maxLen int) (res []string) { |
179 | 188 | parts := strings.Split(s, " ") |
... | ... | @@ -197,3 +206,23 @@ func SplitStringAtWholeWords(s string, maxLen int) (res []string) { |
197 | 206 | |
198 | 207 | return res |
199 | 208 | } |
209 | + | |
210 | +// StringToInt64 ... | |
211 | +func StringToInt64(s string) int64 { | |
212 | + i, _ := strconv.ParseInt(s, 10, 64) | |
213 | + return i | |
214 | +} | |
215 | + | |
216 | +// StringToFloat64 ... | |
217 | +func StringToFloat64(s string) float64 { | |
218 | + f, _ := strconv.ParseFloat(s, 64) | |
219 | + return f | |
220 | +} | |
221 | + | |
222 | +func StringToValidInt64(s string) (int64, bool) { | |
223 | + i, err := strconv.ParseInt(s, 10, 64) | |
224 | + if err != nil { | |
225 | + return i, false | |
226 | + } | |
227 | + return i, true | |
228 | +} | ... | ... |