Commit 1df829121974bb54eb7ab678d9317eee8160736a

Authored by Marko Tikvić
Exists in master

Merge branch 'master' of ssh://git.to-net.rs:6322/marko.tikvic/webutility

... ... @@ -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) {
... ...
... ... @@ -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 +}
... ...
... ... @@ -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, " ")
... ...
... ... @@ -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 +}
... ...