Commit 1df829121974bb54eb7ab678d9317eee8160736a
Exists in
master
Merge branch 'master' of ssh://git.to-net.rs:6322/marko.tikvic/webutility
Showing
6 changed files
Show diff stats
guid.go
... | ... | @@ -0,0 +1,17 @@ |
1 | +package webutility | |
2 | + | |
3 | +import ( | |
4 | + "crypto/rand" | |
5 | + "fmt" | |
6 | +) | |
7 | + | |
8 | +// GUID ... | |
9 | +func GUID() (string, error) { | |
10 | + b := make([]byte, 16) | |
11 | + _, err := rand.Read(b) | |
12 | + if err != nil { | |
13 | + return "", err | |
14 | + } | |
15 | + id := fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) | |
16 | + return id, nil | |
17 | +} | ... | ... |
middleware/main.go
1 | 1 | package middleware |
2 | 2 | |
3 | 3 | import ( |
4 | + "fmt" | |
5 | + "io/ioutil" | |
4 | 6 | "net/http" |
7 | + | |
8 | + web "git.to-net.rs/marko.tikvic/webutility" | |
5 | 9 | ) |
6 | 10 | |
7 | 11 | func Headers(h http.HandlerFunc) http.HandlerFunc { |
... | ... | @@ -19,3 +23,58 @@ func AuthUserAndLog(roles string, h http.HandlerFunc) http.HandlerFunc { |
19 | 23 | func LogTraffic(h http.HandlerFunc) http.HandlerFunc { |
20 | 24 | return SetAccessControlHeaders(IgnoreOptionsRequests(ParseForm(LogHTTP(h)))) |
21 | 25 | } |
26 | + | |
27 | +func TrafficLogsHandler(w http.ResponseWriter, req *http.Request) { | |
28 | + switch req.Method { | |
29 | + case "GET": | |
30 | + files, err := ioutil.ReadDir(httpLogger.GetOutDir() + "/") | |
31 | + if err != nil { | |
32 | + web.InternalServerError(w, req, err.Error()) | |
33 | + return | |
34 | + } | |
35 | + | |
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>")) | |
63 | + | |
64 | + case "POST": | |
65 | + web.SetContentType(w, "text/html; charset=utf-8") | |
66 | + | |
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 | |
72 | + } | |
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 | + } | |
80 | +} | ... | ... |
middleware/middleware.go
... | ... | @@ -61,6 +61,17 @@ func SetLogger(logger *gologger.Logger) { |
61 | 61 | httpLogger = logger |
62 | 62 | } |
63 | 63 | |
64 | +func StartLogging(filename, dir string) (err error) { | |
65 | + if httpLogger, err = gologger.New(filename, dir, gologger.MaxLogSize1MB); err != nil { | |
66 | + return err | |
67 | + } | |
68 | + return nil | |
69 | +} | |
70 | + | |
71 | +func CloseLogger() { | |
72 | + httpLogger.Close() | |
73 | +} | |
74 | + | |
64 | 75 | // LogHTTP ... |
65 | 76 | func LogHTTP(h http.HandlerFunc) http.HandlerFunc { |
66 | 77 | return func(w http.ResponseWriter, req *http.Request) { | ... | ... |
server.go
... | ... | @@ -0,0 +1,75 @@ |
1 | +package webutility | |
2 | + | |
3 | +import ( | |
4 | + "database/sql" | |
5 | + "fmt" | |
6 | + "net/http" | |
7 | + | |
8 | + "git.to-net.rs/marko.tikvic/gologger" | |
9 | + "github.com/gorilla/mux" | |
10 | +) | |
11 | + | |
12 | +type Server struct { | |
13 | + DB *sql.DB | |
14 | + Router *mux.Router | |
15 | + Logger *gologger.Logger | |
16 | + Port string | |
17 | + UTCOffset int64 | |
18 | +} | |
19 | + | |
20 | +func NewServer(dsn, port, logDir string, utcOffset int64) (s *Server, err error) { | |
21 | + s = new(Server) | |
22 | + | |
23 | + s.Port = port | |
24 | + | |
25 | + if s.DB, err = sql.Open("odbc", fmt.Sprintf("DSN=%s;", dsn)); err != nil { | |
26 | + return nil, err | |
27 | + } | |
28 | + | |
29 | + s.Router = mux.NewRouter() | |
30 | + | |
31 | + if s.Logger, err = gologger.New("err", logDir, gologger.MaxLogSize1MB); err != nil { | |
32 | + return nil, fmt.Errorf("can't create logger: %s", err.Error()) | |
33 | + } | |
34 | + | |
35 | + s.UTCOffset = utcOffset | |
36 | + | |
37 | + return s, nil | |
38 | +} | |
39 | + | |
40 | +func (s *Server) Run() { | |
41 | + s.Logger.Print("Server listening on %s", s.Port) | |
42 | + s.Logger.PrintAndTrace(http.ListenAndServe(s.Port, s.Router).Error()) | |
43 | +} | |
44 | + | |
45 | +func (s *Server) Cleanup() { | |
46 | + if s.DB != nil { | |
47 | + s.DB.Close() | |
48 | + } | |
49 | + | |
50 | + if s.Logger != nil { | |
51 | + s.Logger.Close() | |
52 | + } | |
53 | +} | |
54 | + | |
55 | +func (s *Server) StartTransaction() (*sql.Tx, error) { | |
56 | + return s.DB.Begin() | |
57 | +} | |
58 | + | |
59 | +func CommitChanges(tx *sql.Tx, err *error, opt ...error) { | |
60 | + if *err != nil { | |
61 | + tx.Rollback() | |
62 | + return | |
63 | + } | |
64 | + | |
65 | + for _, e := range opt { | |
66 | + if e != nil { | |
67 | + tx.Rollback() | |
68 | + return | |
69 | + } | |
70 | + } | |
71 | + | |
72 | + if *err = tx.Commit(); *err != nil { | |
73 | + tx.Rollback() | |
74 | + } | |
75 | +} | ... | ... |
string_util.go
... | ... | @@ -148,6 +148,14 @@ func SplitText(s string, maxLen int) (lines []string) { |
148 | 148 | return lines |
149 | 149 | } |
150 | 150 | |
151 | +func CutTextWithThreeDots(txt string, maxLen int) string { | |
152 | + if len(txt) < maxLen || len(txt) <= 3 { | |
153 | + return txt | |
154 | + } | |
155 | + | |
156 | + return txt[:maxLen-3] + "..." | |
157 | +} | |
158 | + | |
151 | 159 | const threeDots = "\u2056\u2056\u2056" |
152 | 160 | |
153 | 161 | func LimitTextWithThreeDots(txt string, maxLen int) string { |
... | ... | @@ -158,6 +166,14 @@ func LimitTextWithThreeDots(txt string, maxLen int) string { |
158 | 166 | return txt[:maxLen] + threeDots |
159 | 167 | } |
160 | 168 | |
169 | +func LimitMSWordTextWithThreeDots(txt string, maxLen int) string { | |
170 | + if len(txt) <= maxLen { | |
171 | + return txt | |
172 | + } | |
173 | + | |
174 | + return txt[:maxLen] + "..." | |
175 | +} | |
176 | + | |
161 | 177 | // SplitStringAtWholeWords ... |
162 | 178 | func SplitStringAtWholeWords(s string, maxLen int) (res []string) { |
163 | 179 | parts := strings.Split(s, " ") | ... | ... |
timer.go
... | ... | @@ -0,0 +1,78 @@ |
1 | +package webutility | |
2 | + | |
3 | +import ( | |
4 | + "fmt" | |
5 | + "time" | |
6 | +) | |
7 | + | |
8 | +// Timer ... | |
9 | +type Timer struct { | |
10 | + name string | |
11 | + running bool | |
12 | + started time.Time | |
13 | + stopped time.Time | |
14 | + lastDuration time.Duration | |
15 | +} | |
16 | + | |
17 | +// NewTimer ... | |
18 | +func NewTimer(name string) *Timer { | |
19 | + t := &Timer{name: name} | |
20 | + t.Reset() | |
21 | + return t | |
22 | +} | |
23 | + | |
24 | +// Start ... | |
25 | +func (t *Timer) Start() time.Time { | |
26 | + t.running = true | |
27 | + t.started = time.Now() | |
28 | + return t.started | |
29 | +} | |
30 | + | |
31 | +// Stop ... | |
32 | +func (t *Timer) Stop() time.Duration { | |
33 | + t.running = false | |
34 | + t.stopped = time.Now() | |
35 | + t.lastDuration = t.stopped.Sub(t.started) | |
36 | + return t.lastDuration | |
37 | +} | |
38 | + | |
39 | +// LastRunDuration ... | |
40 | +func (t *Timer) LastRunDuration() time.Duration { | |
41 | + return t.lastDuration | |
42 | +} | |
43 | + | |
44 | +// Clear ... | |
45 | +func (t *Timer) Clear() { | |
46 | + t.started = time.Now() | |
47 | + t.stopped = time.Now() | |
48 | + t.lastDuration = 0 | |
49 | +} | |
50 | + | |
51 | +// Reset ... | |
52 | +func (t *Timer) Reset() { | |
53 | + t.Stop() | |
54 | + t.Start() | |
55 | +} | |
56 | + | |
57 | +// Elapsed ... | |
58 | +func (t *Timer) Elapsed() time.Duration { | |
59 | + if t.running { | |
60 | + return time.Now().Sub(t.started) | |
61 | + } | |
62 | + return 0 | |
63 | +} | |
64 | + | |
65 | +// Print ... | |
66 | +func (t *Timer) Print(s string) { | |
67 | + status := "RUNNING" | |
68 | + if !t.running { | |
69 | + status = "STOPPED" | |
70 | + } | |
71 | + fmt.Printf("timer[%s][%s]: %v %s\n", t.name, status, t.Elapsed(), s) | |
72 | +} | |
73 | + | |
74 | +// Lap ... | |
75 | +func (t *Timer) Lap(s string) { | |
76 | + t.Print(s) | |
77 | + t.Reset() | |
78 | +} | ... | ... |