Compare View

switch
from
...
to
 
Commits (2)
1 package webutility 1 package webutility
2 2
3 import ( 3 import (
4 "encoding/json" 4 "encoding/json"
5 "fmt" 5 "fmt"
6 "net/http" 6 "net/http"
7 ) 7 )
8 8
9 // StatusRecorder ... 9 // StatusRecorder ...
10 type StatusRecorder struct { 10 type StatusRecorder struct {
11 writer http.ResponseWriter 11 writer http.ResponseWriter
12 status int 12 status int
13 size int 13 size int
14 data []byte
14 data []byte 15 }
15 } 16
16 17 // NewStatusRecorder ...
17 // NewStatusRecorder ... 18 func NewStatusRecorder(w http.ResponseWriter) *StatusRecorder {
18 func NewStatusRecorder(w http.ResponseWriter) *StatusRecorder { 19 return &StatusRecorder{
19 return &StatusRecorder{ 20 writer: w,
20 writer: w, 21 status: 0,
21 status: 0, 22 size: 0,
23 data: nil,
22 size: 0, 24 }
23 data: nil, 25 }
24 } 26
25 } 27 // WriteHeader is a wrapper http.ResponseWriter interface
26 28 func (r *StatusRecorder) WriteHeader(code int) {
27 // WriteHeader is a wrapper http.ResponseWriter interface 29 r.status = code
28 func (r *StatusRecorder) WriteHeader(code int) { 30 r.writer.WriteHeader(code)
29 r.status = code 31 }
30 r.writer.WriteHeader(code) 32
31 } 33 // Write is a wrapper for http.ResponseWriter interface
32 34 func (r *StatusRecorder) Write(in []byte) (int, error) {
33 // Write is a wrapper for http.ResponseWriter interface 35 r.size = len(in)
36 if r.status >= 400 {
37 r.data = make([]byte, len(in))
38 copy(r.data, in)
39 }
34 func (r *StatusRecorder) Write(in []byte) (int, error) { 40 return r.writer.Write(in)
35 r.size = len(in) 41 }
36 if r.status >= 400 { 42
37 r.data = make([]byte, len(in)) 43 // Header is a wrapper for http.ResponseWriter interface
38 copy(r.data, in) 44 func (r *StatusRecorder) Header() http.Header {
39 } 45 return r.writer.Header()
40 return r.writer.Write(in) 46 }
41 } 47
42 48 // Status ...
43 // Header is a wrapper for http.ResponseWriter interface 49 func (r *StatusRecorder) Status() int {
44 func (r *StatusRecorder) Header() http.Header { 50 return r.status
45 return r.writer.Header() 51 }
46 } 52
47 53 // Size ...
48 // Status ... 54 func (r *StatusRecorder) Size() int {
49 func (r *StatusRecorder) Status() int { 55 return r.size
50 return r.status 56 }
51 } 57
58 // Size ...
59 func (r *StatusRecorder) Data() []byte {
60 return r.data
61 }
62
52 63 // NotFoundHandlerFunc writes HTTP error 404 to w.
53 // Size ... 64 func NotFoundHandlerFunc(w http.ResponseWriter, req *http.Request) {
54 func (r *StatusRecorder) Size() int { 65 SetAccessControlHeaders(w)
55 return r.size 66 SetContentType(w, "application/json")
56 } 67 NotFound(w, req, fmt.Sprintf("Resource you requested was not found: %s", req.URL.String()))
57 68 }
58 // Size ... 69
59 func (r *StatusRecorder) Data() []byte { 70 // SetContentType must be called before SetResponseStatus (w.WriteHeader) (?)
60 return r.data 71 func SetContentType(w http.ResponseWriter, ctype string) {
61 } 72 w.Header().Set("Content-Type", ctype)
62 73 }
63 // NotFoundHandlerFunc writes HTTP error 404 to w. 74
64 func NotFoundHandlerFunc(w http.ResponseWriter, req *http.Request) { 75 // SetResponseStatus ...
65 SetAccessControlHeaders(w) 76 func SetResponseStatus(w http.ResponseWriter, status int) {
66 SetContentType(w, "application/json") 77 w.WriteHeader(status)
67 NotFound(w, req, fmt.Sprintf("Resource you requested was not found: %s", req.URL.String())) 78 }
68 } 79
69 80 // WriteResponse ...
70 // SetContentType must be called before SetResponseStatus (w.WriteHeader) (?) 81 func WriteResponse(w http.ResponseWriter, content []byte) {
71 func SetContentType(w http.ResponseWriter, ctype string) { 82 w.Write(content)
72 w.Header().Set("Content-Type", ctype) 83 }
73 } 84
74 85 // SetAccessControlHeaders set's default headers for an HTTP response.
75 // SetResponseStatus ... 86 func SetAccessControlHeaders(w http.ResponseWriter) {
76 func SetResponseStatus(w http.ResponseWriter, status int) { 87 w.Header().Set("Access-Control-Allow-Origin", "*")
77 w.WriteHeader(status) 88 w.Header().Set("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS")
78 } 89 w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
79 90 }
80 // WriteResponse ... 91
81 func WriteResponse(w http.ResponseWriter, content []byte) { 92 // GetLocale ...
82 w.Write(content) 93 func GetLocale(req *http.Request, dflt string) string {
83 } 94 loc := req.FormValue("locale")
84 95 if loc == "" {
85 // SetAccessControlHeaders set's default headers for an HTTP response. 96 return dflt
86 func SetAccessControlHeaders(w http.ResponseWriter) { 97 }
87 w.Header().Set("Access-Control-Allow-Origin", "*") 98 return loc
88 w.Header().Set("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS") 99 }
89 w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization") 100
90 } 101 // Success ...
91 102 func Success(w http.ResponseWriter, payload interface{}, code int) {
92 // GetLocale ... 103 w.WriteHeader(code)
93 func GetLocale(req *http.Request, dflt string) string { 104 if payload != nil {
94 loc := req.FormValue("locale") 105 json.NewEncoder(w).Encode(payload)
95 if loc == "" { 106 }
96 return dflt 107 }
97 } 108
98 return loc 109 // OK ...
99 } 110 func OK(w http.ResponseWriter, payload interface{}) {
100 111 SetContentType(w, "application/json")
101 // Success ... 112 Success(w, payload, http.StatusOK)
102 func Success(w http.ResponseWriter, payload interface{}, code int) { 113 }
103 w.WriteHeader(code) 114
104 if payload != nil { 115 // Created ...
105 json.NewEncoder(w).Encode(payload) 116 func Created(w http.ResponseWriter, payload interface{}) {
106 } 117 SetContentType(w, "application/json")
107 } 118 Success(w, payload, http.StatusCreated)
108 119 }
109 // OK ... 120
110 func OK(w http.ResponseWriter, payload interface{}) { 121 type weberror struct {
111 SetContentType(w, "application/json") 122 Request string `json:"request"`
112 Success(w, payload, http.StatusOK) 123 Error string `json:"error"`
113 } 124 //Code int64 `json:"code"` TODO
114 125 }
115 // Created ... 126
116 func Created(w http.ResponseWriter, payload interface{}) { 127 // Error ...
117 SetContentType(w, "application/json") 128 func Error(w http.ResponseWriter, r *http.Request, code int, err string) {
118 Success(w, payload, http.StatusCreated) 129 werr := weberror{Error: err, Request: r.Method + " " + r.RequestURI}
119 } 130 w.WriteHeader(code)
120 131 json.NewEncoder(w).Encode(werr)
121 type weberror struct { 132 }
122 Request string `json:"request"` 133
123 Error string `json:"error"` 134 // BadRequest ...
124 //Code int64 `json:"code"` TODO 135 func BadRequest(w http.ResponseWriter, r *http.Request, err string) {
125 } 136 SetContentType(w, "application/json")
126 137 Error(w, r, http.StatusBadRequest, err)
127 // Error ... 138 }
128 func Error(w http.ResponseWriter, r *http.Request, code int, err string) { 139
129 werr := weberror{Error: err, Request: r.Method + " " + r.RequestURI} 140 // Unauthorized ...
130 w.WriteHeader(code) 141 func Unauthorized(w http.ResponseWriter, r *http.Request, err string) {
131 json.NewEncoder(w).Encode(werr) 142 SetContentType(w, "application/json")
132 } 143 Error(w, r, http.StatusUnauthorized, err)
133 144 }
134 // BadRequest ... 145
135 func BadRequest(w http.ResponseWriter, r *http.Request, err string) { 146 // Forbidden ...
136 SetContentType(w, "application/json") 147 func Forbidden(w http.ResponseWriter, r *http.Request, err string) {
137 Error(w, r, http.StatusBadRequest, err) 148 SetContentType(w, "application/json")
138 } 149 Error(w, r, http.StatusForbidden, err)
139 150 }
140 // Unauthorized ... 151
141 func Unauthorized(w http.ResponseWriter, r *http.Request, err string) { 152 // NotFound ...
142 SetContentType(w, "application/json") 153 func NotFound(w http.ResponseWriter, r *http.Request, err string) {
143 Error(w, r, http.StatusUnauthorized, err) 154 SetContentType(w, "application/json")
144 } 155 Error(w, r, http.StatusNotFound, err)
145 156 }
146 // Forbidden ... 157
147 func Forbidden(w http.ResponseWriter, r *http.Request, err string) { 158 // Conflict ...
148 SetContentType(w, "application/json") 159 func Conflict(w http.ResponseWriter, r *http.Request, err string) {
149 Error(w, r, http.StatusForbidden, err) 160 SetContentType(w, "application/json")
150 } 161 Error(w, r, http.StatusConflict, err)
151 162 }
152 // NotFound ... 163
153 func NotFound(w http.ResponseWriter, r *http.Request, err string) { 164 // InternalServerError ...
154 SetContentType(w, "application/json") 165 func InternalServerError(w http.ResponseWriter, r *http.Request, err string) {
155 Error(w, r, http.StatusNotFound, err) 166 SetContentType(w, "application/json")
156 } 167 Error(w, r, http.StatusInternalServerError, err)
157 168 }
158 // Conflict ... 169
159 func Conflict(w http.ResponseWriter, r *http.Request, err string) { 170 func SetHeader(r *http.Request, key, val string) {
160 SetContentType(w, "application/json") 171 r.Header.Set(key, val)
161 Error(w, r, http.StatusConflict, err) 172 }
162 } 173
163 174 func AddHeader(r *http.Request, key, val string) {
164 // InternalServerError ... 175 r.Header.Add(key, val)
165 func InternalServerError(w http.ResponseWriter, r *http.Request, err string) { 176 }
166 SetContentType(w, "application/json") 177
167 Error(w, r, http.StatusInternalServerError, err) 178 func GetHeader(r *http.Request, key string) string {
168 } 179 return r.Header.Get(key)
169 180 }
170 func SetHeader(r *http.Request, key, val string) { 181
171 r.Header.Set(key, val) 182 func ClientUTCOffset(req *http.Request) int64 {
172 } 183 return StringToInt64(GetHeader(req, "X-Timezone-Offset"))
173 184 }
174 func AddHeader(r *http.Request, key, val string) { 185
logger/http_logs.go
File was created 1 package logger
2
3 import (
4 "bytes"
5 "fmt"
6 "net/http"
7 "net/http/httputil"
8 "os"
9 "strings"
10 "time"
11 )
12
13 const splitLine = "=============================================================="
14
15 // LogHTTPRequest ...
16 func (l *Logger) LogHTTPRequest(req *http.Request, userID string) string {
17 if userID == "" {
18 userID = "-"
19 }
20
21 var b strings.Builder
22
23 b.WriteString("Request:\n")
24 // CLF-like header
25 fmt.Fprintf(&b, "%s %s %s\n", req.RemoteAddr, userID, time.Now().Format(dateTimeFormat))
26
27 body, err := httputil.DumpRequest(req, true)
28 if err != nil {
29 fmt.Fprintf(os.Stderr, "%v\n", err)
30 }
31
32 const sepStr = "\r\n\r\n"
33 sepIndex := bytes.Index(body, []byte(sepStr))
34 if sepIndex == -1 {
35 b.WriteString(string(body) + "\n\n")
36 } else {
37 sepIndex += len(sepStr)
38 payload, _ := printJSON(body[sepIndex:])
39 b.WriteString(string(body[:sepIndex]) + string(payload) + "\n\n")
40 }
41
42 return b.String()
43 }
44
45 // LogHTTPResponse ...
46 func (l *Logger) LogHTTPResponse(data []byte, status int, startTime time.Time) string {
47 duration := time.Now().Sub(startTime)
48 jsonData, _ := printJSON(data)
49 return fmt.Sprintf("Response:\n%d %v %dB\n%s\n%s\n\n", status, duration, len(data), jsonData, splitLine)
50 }
51
52 // CombineHTTPLogs ...
53 func (l *Logger) CombineHTTPLogs(in string, out string) {
54 if l.outputFile == nil {
55 return
56 }
57
58 l.mu.Lock()
59 defer l.mu.Unlock()
60
61 msg := in + out
62 if l.shouldSplit(len(msg)) {
63 l.split()
64 }
65 l.outputFile.WriteString(msg)
66 }
67
File was created 1 package logger
2
3 import (
4 "bytes"
5 "encoding/json"
6 "fmt"
7 "os"
8 "path/filepath"
9 "strings"
10 "sync"
11 "time"
12 )
13
14 const dateTimeFormat = "2006-01-02 15:04:05"
15
16 // Block ...
17 const (
18 MaxLogSize5MB int64 = 5 * 1024 * 1024
19 MaxLogSize1MB int64 = 1 * 1024 * 1024
20 MaxLogSize500KB int64 = 500 * 1024
21 MaxLogSize100KB int64 = 100 * 1024
22 MaxLogSize512B int64 = 512
23 )
24
25 // Logger ...
26 type Logger struct {
27 mu *sync.Mutex
28 outputFile *os.File
29
30 outputFileName string
31 fullName string
32 maxFileSize int64
33
34 splitCount int
35
36 directory string
37 }
38
39 // New ...
40 func New(name, dir string, maxFileSize int64) (logger *Logger, err error) {
41 logger = &Logger{}
42
43 logger.outputFileName = name
44 logger.mu = &sync.Mutex{}
45 logger.maxFileSize = maxFileSize
46 logger.directory = dir
47
48 err = os.Mkdir(dir, os.ModePerm)
49 if err != nil {
50 if !os.IsExist(err) {
51 return nil, err
52 }
53 }
54
55 date := strings.Replace(time.Now().Format(dateTimeFormat), ":", ".", -1)
56 logger.outputFileName += " " + date
57 logger.fullName = logger.outputFileName + ".txt"
58 path := filepath.Join(dir, logger.fullName)
59 if logger.outputFile, err = os.Create(path); err != nil {
60 return nil, err
61 }
62
63 return logger, nil
64 }
65
66 // Log ...
67 func (l *Logger) Log(format string, v ...interface{}) {
68 if l.outputFile == nil {
69 return
70 }
71
72 l.mu.Lock()
73 defer l.mu.Unlock()
74
75 msg := fmt.Sprintf(format, v...)
76 s := time.Now().Format(dateTimeFormat) + ": " + msg + "\n"
77 if l.shouldSplit(len(s)) {
78 l.split()
79 }
80 l.outputFile.WriteString(s)
81 }
82
83 // Print ...
84 func (l *Logger) Print(format string, v ...interface{}) {
85 msg := fmt.Sprintf(format, v...)
86 fmt.Printf("%s: %s\n", time.Now().Format(dateTimeFormat), msg)
87 }
88
89 // Trace ...
90 func (l *Logger) Trace(format string, v ...interface{}) string {
91 if l.outputFile == nil {
92 return ""
93 }
94
95 l.mu.Lock()
96 defer l.mu.Unlock()
97
98 s := getTrace(format, v...) + "\n"
99
100 if l.shouldSplit(len(s)) {
101 l.split()
102 }
103 l.outputFile.WriteString(s)
104
105 return s
106 }
107
108 // PrintTrace ...
109 func (l *Logger) PrintTrace(format string, v ...interface{}) {
110 s := getTrace(format, v...)
111 fmt.Printf("%s\n", s)
112 }
113
114 // PrintAndTrace ...
115 func (l *Logger) PrintAndTrace(format string, v ...interface{}) {
116 t := l.Trace(format, v...)
117 fmt.Println(t)
118 }
119
120 // Close ...
121 func (l *Logger) Close() error {
122 if l.outputFile == nil {
123 return nil
124 }
125
126 return l.outputFile.Close()
127 }
128
129 // GetOutDir ...
130 func (l *Logger) GetOutDir() string {
131 return l.directory
132 }
133
134 func (l *Logger) split() error {
135 if l.outputFile == nil {
136 return nil
137 }
138
139 // close old file
140 err := l.outputFile.Close()
141 if err != nil {
142 return err
143 }
144
145 // open new file
146 l.splitCount++
147 path := filepath.Join(l.directory, l.outputFileName+fmt.Sprintf("(%d)", l.splitCount)+".txt")
148 l.outputFile, err = os.Create(path)
149
150 return err
151 }
152
153 func (l *Logger) shouldSplit(nextEntrySize int) bool {
154 stats, _ := l.outputFile.Stat()
155 return int64(nextEntrySize) >= (l.maxFileSize - stats.Size())
156 }
157
158 func printJSON(in []byte) (out []byte, err error) {
159 var buf bytes.Buffer
160 err = json.Indent(&buf, in, "", " ")
161 return buf.Bytes(), err
162 }
163
logger/tracing.go
File was created 1 package logger
2
3 import (
4 "fmt"
5 "math"
6 "runtime"
7 "strings"
8 "time"
9 )
10
11 // getCallStack
12 func getCallStack() (stack []string) {
13 const (
14 maxStackDepth = 10000
15 skipRuntime = 2 // skip runtime and startup
16 skipCallers = 2
17 thisPackage = "logger"
18 )
19
20 callers := make([]uintptr, maxStackDepth) // min 1
21
22 stackDepth := runtime.Callers(skipCallers, callers)
23 frames := runtime.CallersFrames(callers)
24
25 for i := 0; i < skipRuntime; i++ {
26 frames.Next()
27 }
28
29 for i := 0; i < stackDepth-skipRuntime-skipCallers; i++ {
30 frame, next := frames.Next()
31 if !strings.Contains(frame.File, thisPackage) {
32 stack = append(stack, fmt.Sprintf("[%s %d]", frame.File, frame.Line))
33 }
34 if !next {
35 break
36 }
37 }
38
39 reverseStack(stack)
40
41 return stack
42 }
43
44 // in place
45 func reverseStack(stack []string) {
46 middle := int(math.Floor(float64(len(stack)) / 2.0))
47
48 lastIndex := len(stack) - 1
49 for i := 0; i < middle; i++ {
50 stack[i], stack[lastIndex] = stack[lastIndex], stack[i]
51 lastIndex--
52 }
53 }
54
55 func getTrace(format string, v ...interface{}) (t string) {
56 stack := getCallStack()
57
58 t = time.Now().Format(dateTimeFormat) + ":\n"
59 for _, s := range stack {
60 t += s + "\n"
61 }
62 t += fmt.Sprintf(format, v...) + "\n"
63
64 return t
65 }
66
middleware/middleware.go
1 package middleware 1 package middleware
2 2
3 import ( 3 import (
4 "net/http" 4 "net/http"
5 "time" 5 "time"
6 6
7 "git.to-net.rs/marko.tikvic/gologger" 7 "git.to-net.rs/marko.tikvic/gologger"
8 8
9 web "git.to-net.rs/marko.tikvic/webutility" 9 web "git.to-net.rs/marko.tikvic/webutility"
10 ) 10 )
11 11
12 var httpLogger *gologger.Logger 12 var httpLogger *gologger.Logger
13 13
14 func SetAccessControlHeaders(h http.HandlerFunc) http.HandlerFunc { 14 func SetAccessControlHeaders(h http.HandlerFunc) http.HandlerFunc {
15 return func(w http.ResponseWriter, req *http.Request) { 15 return func(w http.ResponseWriter, req *http.Request) {
16 web.SetAccessControlHeaders(w) 16 web.SetAccessControlHeaders(w)
17 17
18 h(w, req) 18 h(w, req)
19 } 19 }
20 } 20 }
21 21
22 // IgnoreOptionsRequests ... 22 // IgnoreOptionsRequests ...
23 func IgnoreOptionsRequests(h http.HandlerFunc) http.HandlerFunc { 23 func IgnoreOptionsRequests(h http.HandlerFunc) http.HandlerFunc {
24 return func(w http.ResponseWriter, req *http.Request) { 24 return func(w http.ResponseWriter, req *http.Request) {
25 if req.Method == http.MethodOptions { 25 if req.Method == http.MethodOptions {
26 return 26 return
27 } 27 }
28 28
29 h(w, req) 29 h(w, req)
30 } 30 }
31 } 31 }
32 32
33 // ParseForm ... 33 // ParseForm ...
34 func ParseForm(h http.HandlerFunc) http.HandlerFunc { 34 func ParseForm(h http.HandlerFunc) http.HandlerFunc {
35 return func(w http.ResponseWriter, req *http.Request) { 35 return func(w http.ResponseWriter, req *http.Request) {
36 err := req.ParseForm() 36 err := req.ParseForm()
37 if err != nil { 37 if err != nil {
38 web.BadRequest(w, req, err.Error()) 38 web.BadRequest(w, req, err.Error())
39 return 39 return
40 } 40 }
41 41
42 h(w, req) 42 h(w, req)
43 } 43 }
44 } 44 }
45 45
46 // ParseMultipartForm ... 46 // ParseMultipartForm ...
47 func ParseMultipartForm(h http.HandlerFunc) http.HandlerFunc { 47 func ParseMultipartForm(h http.HandlerFunc) http.HandlerFunc {
48 return func(w http.ResponseWriter, req *http.Request) { 48 return func(w http.ResponseWriter, req *http.Request) {
49 err := req.ParseMultipartForm(32 << 20) 49 err := req.ParseMultipartForm(32 << 20)
50 if err != nil { 50 if err != nil {
51 web.BadRequest(w, req, err.Error()) 51 web.BadRequest(w, req, err.Error())
52 return 52 return
53 } 53 }
54 54
55 h(w, req) 55 h(w, req)
56 } 56 }
57 } 57 }
58 58
59 // SetLogger ... 59 // SetLogger ...
60 func SetLogger(logger *gologger.Logger) { 60 func SetLogger(logger *gologger.Logger) {
61 httpLogger = logger 61 httpLogger = logger
62 } 62 }
63 63
64 func StartLogging(filename, dir string) (err error) { 64 func StartLogging(filename, dir string) (err error) {
65 if httpLogger, err = gologger.New(filename, dir, gologger.MaxLogSize1MB); err != nil { 65 if httpLogger, err = gologger.New(filename, dir, gologger.MaxLogSize1MB); err != nil {
66 return err 66 return err
67 } 67 }
68 return nil 68 return nil
69 } 69 }
70 70
71 func CloseLogger() { 71 func CloseLogger() {
72 httpLogger.Close() 72 httpLogger.Close()
73 } 73 }
74 74
75 // LogHTTP ... 75 // LogHTTP ...
76 func LogHTTP(hfunc http.HandlerFunc) http.HandlerFunc { 76 func LogHTTP(hfunc http.HandlerFunc) http.HandlerFunc {
77 return func(w http.ResponseWriter, req *http.Request) { 77 return func(w http.ResponseWriter, req *http.Request) {
78 if httpLogger == nil { 78 if httpLogger == nil {
79 hfunc(w, req) 79 hfunc(w, req)
80 return 80 return
81 } 81 }
82 82
83 t1 := time.Now() 83 t1 := time.Now()
84 84
85 claims, _ := web.GetTokenClaims(req) 85 claims, _ := web.GetTokenClaims(req)
86 in := httpLogger.LogHTTPRequest(req, claims.Username) 86 in := httpLogger.LogHTTPRequest(req, claims.Username)
87 87
88 rec := web.NewStatusRecorder(w) 88 rec := web.NewStatusRecorder(w)
89 89
90 hfunc(rec, req) 90 hfunc(rec, req)
91 91
92 out := httpLogger.LogHTTPResponse(rec.Data(), rec.Status(), t1) 92 out := httpLogger.LogHTTPResponse(rec.Data(), rec.Status(), t1)
93
94 httpLogger.CombineHTTPLogs(in, out) 93
95 } 94 httpLogger.CombineHTTPLogs(in, out)
96 } 95 }
97 96 }
98 // Auth ... 97
99 func Auth(roles string, h http.HandlerFunc) http.HandlerFunc { 98 // Auth ...
100 return func(w http.ResponseWriter, req *http.Request) { 99 func Auth(roles string, h http.HandlerFunc) http.HandlerFunc {
101 if _, err := web.AuthCheck(req, roles); err != nil { 100 return func(w http.ResponseWriter, req *http.Request) {
102 web.Unauthorized(w, req, err.Error()) 101 if _, err := web.AuthCheck(req, roles); err != nil {
103 return 102 web.Unauthorized(w, req, err.Error())
104 } 103 return
105 104 }
106 h(w, req) 105
107 } 106 h(w, req)
108 } 107 }
109 108 }
1 package webutility 1 package webutility
2 2
3 type QSortDirection int 3 import "sort"
4 4
5 const ( 5 // QuickSort quicksorts que.
6 QSortAscending QSortDirection = iota 6 func QuickSort(que sort.Interface, low, high int, reverse bool) {
7 QSortDescending
8 )
9
10 // QuickSortable is an interface for quicksorting slices.
11 type QuickSortable interface {
12 Swap(i, j int)
13 Compare(i, j int) int
14 Len() int
15 }
16
17 // Quicksort quicksorts que.
18 func Quicksort(que QuickSortable, low, high int, dir QSortDirection) {
19 if low >= high { 7 if low >= high {
20 return 8 return
21 } 9 }
22 10
23 if dir != QSortAscending && dir != QSortDescending { 11 index := quickSortPartition(que, low, high, reverse)
24 return 12 QuickSort(que, low, index-1, reverse)
25 } 13 QuickSort(que, index+1, high, reverse)
26
27 index := partition(que, low, high, dir)
28 Quicksort(que, low, index-1, dir)
29 Quicksort(que, index+1, high, dir)
30 } 14 }
31 15
32 func partition(que QuickSortable, low, high int, dir QSortDirection) int { 16 func quickSortPartition(que sort.Interface, low, high int, reverse bool) 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
42 i := low - 1 17 i := low - 1
43 for j := low; j <= high-1; j++ { 18 for j := low; j <= high-1; j++ {
44 if que.Compare(j, high) == swap { 19 if !reverse {
45 i++ 20 if que.Less(j, high) {
46 que.Swap(i, j) 21 i++
22 que.Swap(i, j)
23 }
24 } else {
25 if !que.Less(j, high) {
26 i++
27 que.Swap(i, j)
28 }
47 } 29 }
48 continue
49 } 30 }
50 que.Swap(i+1, high) 31 que.Swap(i+1, high)
51 return i + 1 32 return i + 1
52 } 33 }
53 34
54 func BubbleSort(arr []int64) { 35 func BubbleSort(arr []int64) {
55 for i := 0; i < len(arr)-1; i++ { 36 for i := 0; i < len(arr)-1; i++ {
56 for j := i; j < len(arr); j++ { 37 for j := i; j < len(arr); j++ {
1 package webutility 1 package webutility
2 2
3 import ( 3 import (
4 "database/sql" 4 "database/sql"
5 "fmt" 5 "fmt"
6 "net/http" 6 "net/http"
7 7
8 "git.to-net.rs/marko.tikvic/gologger" 8 "git.to-net.rs/marko.tikvic/webutility/logger"
9 "github.com/gorilla/mux" 9 "github.com/gorilla/mux"
10 ) 10 )
11 11
12 type Server struct { 12 type Server struct {
13 DB *sql.DB 13 DB *sql.DB
14 Router *mux.Router 14 Router *mux.Router
15 Logger *gologger.Logger 15 Logger *logger.Logger
16 Port string 16 Port string
17 DBs map[string]*sql.DB 17 DBs map[string]*sql.DB
18 dsn map[string]string 18 dsn map[string]string
19 } 19 }
20 20
21 func NewODBCServer(dsn, port, logDir string) (s *Server, err error) { 21 func NewODBCServer(dsn, port, logDir string) (s *Server, err error) {
22 s = new(Server) 22 s = new(Server)
23 23
24 s.Port = port 24 s.Port = port
25 25
26 if s.DB, err = sql.Open("odbc", fmt.Sprintf("DSN=%s;", dsn)); err != nil { 26 if s.DB, err = sql.Open("odbc", fmt.Sprintf("DSN=%s;", dsn)); err != nil {
27 return nil, err 27 return nil, err
28 } 28 }
29 29
30 s.Router = mux.NewRouter() 30 s.Router = mux.NewRouter()
31 31
32 if s.Logger, err = gologger.New("err", logDir, gologger.MaxLogSize1MB); err != nil { 32 if s.Logger, err = logger.New("err", logDir, logger.MaxLogSize1MB); err != nil {
33 return nil, fmt.Errorf("can't create logger: %s", err.Error()) 33 return nil, fmt.Errorf("can't create logger: %s", err.Error())
34 } 34 }
35 35
36 s.DBs = make(map[string]*sql.DB) 36 s.DBs = make(map[string]*sql.DB)
37 s.DBs["default"] = s.DB 37 s.DBs["default"] = s.DB
38 38
39 s.dsn = make(map[string]string) 39 s.dsn = make(map[string]string)
40 s.dsn["default"] = dsn 40 s.dsn["default"] = dsn
41 41
42 return s, nil 42 return s, nil
43 } 43 }
44 44
45 func (s *Server) Run() { 45 func (s *Server) Run() {
46 s.Logger.Print("Server listening on %s", s.Port) 46 s.Logger.Print("Server listening on %s", s.Port)
47 s.Logger.PrintAndTrace(http.ListenAndServe(s.Port, s.Router).Error()) 47 s.Logger.PrintAndTrace(http.ListenAndServe(s.Port, s.Router).Error())
48 } 48 }
49 49
50 func (s *Server) Cleanup() { 50 func (s *Server) Cleanup() {
51 if s.DB != nil { 51 if s.DB != nil {
52 s.DB.Close() 52 s.DB.Close()
53 } 53 }
54 54
55 if s.Logger != nil { 55 if s.Logger != nil {
56 s.Logger.Close() 56 s.Logger.Close()
57 } 57 }
58 } 58 }
59 59
60 func (s *Server) StartTransaction() (*sql.Tx, error) { 60 func (s *Server) StartTransaction() (*sql.Tx, error) {
61 return s.DB.Begin() 61 return s.DB.Begin()
62 } 62 }
63 63
64 func CommitChanges(tx *sql.Tx, err *error, opt ...error) { 64 func CommitChanges(tx *sql.Tx, err *error, opt ...error) {
65 if *err != nil { 65 if *err != nil {
66 tx.Rollback() 66 tx.Rollback()
67 return 67 return
68 } 68 }
69 69
70 for _, e := range opt { 70 for _, e := range opt {
71 if e != nil { 71 if e != nil {
72 tx.Rollback() 72 tx.Rollback()
73 return 73 return
74 } 74 }
75 } 75 }
76 76
77 if *err = tx.Commit(); *err != nil { 77 if *err = tx.Commit(); *err != nil {
78 tx.Rollback() 78 tx.Rollback()
79 } 79 }
80 } 80 }
81 81