stats.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. package results
  2. import (
  3. "html/template"
  4. "net/http"
  5. log "github.com/sirupsen/logrus"
  6. "backend/config"
  7. "backend/database"
  8. "backend/database/schema"
  9. )
  10. type StatsData struct {
  11. NoPassword bool
  12. LoggedIn bool
  13. Data []schema.TelemetryData
  14. }
  15. func Stats(w http.ResponseWriter, r *http.Request) {
  16. w.Header().Set("Content-Type", "text/html; charset=utf-8")
  17. t, err := template.New("template").Parse(htmlTemplate)
  18. if err != nil {
  19. log.Errorf("Failed to parse template: %s", err)
  20. w.WriteHeader(http.StatusInternalServerError)
  21. return
  22. }
  23. conf := config.LoadedConfig()
  24. var data StatsData
  25. if conf.StatsPassword == "PASSWORD" {
  26. data.NoPassword = true
  27. }
  28. if !data.NoPassword {
  29. op := r.FormValue("op")
  30. c, _ := r.Cookie("logged")
  31. if c != nil && c.Value == "true" {
  32. if op == "logout" {
  33. cookie := &http.Cookie{
  34. Name: "logged",
  35. Value: "false",
  36. }
  37. http.SetCookie(w, cookie)
  38. http.Redirect(w, r, "/stats", http.StatusTemporaryRedirect)
  39. } else {
  40. data.LoggedIn = true
  41. id := r.FormValue("id")
  42. switch id {
  43. case "L100":
  44. stats, err := database.DB.FetchLast100()
  45. if err != nil {
  46. log.Errorf("Error fetching data from database: %s", err)
  47. w.WriteHeader(http.StatusInternalServerError)
  48. return
  49. }
  50. data.Data = stats
  51. case "":
  52. default:
  53. stat, err := database.DB.FetchByUUID(id)
  54. if err != nil {
  55. log.Errorf("Error fetching data from database: %s", err)
  56. w.WriteHeader(http.StatusInternalServerError)
  57. return
  58. }
  59. data.Data = append(data.Data, *stat)
  60. }
  61. }
  62. } else {
  63. if op == "login" {
  64. password := r.FormValue("password")
  65. if password == conf.StatsPassword {
  66. cookie := &http.Cookie{
  67. Name: "logged",
  68. Value: "true",
  69. }
  70. http.SetCookie(w, cookie)
  71. http.Redirect(w, r, "/stats", http.StatusTemporaryRedirect)
  72. } else {
  73. w.WriteHeader(http.StatusForbidden)
  74. }
  75. }
  76. }
  77. }
  78. if err := t.Execute(w, data); err != nil {
  79. log.Errorf("Error executing template: %s", err)
  80. w.WriteHeader(http.StatusInternalServerError)
  81. }
  82. }
  83. const htmlTemplate = `<!DOCTYPE html>
  84. <html>
  85. <head>
  86. <title>LibreSpeed - Stats</title>
  87. <style type="text/css">
  88. html,body{
  89. margin:0;
  90. padding:0;
  91. border:none;
  92. width:100%; min-height:100%;
  93. }
  94. html{
  95. background-color: hsl(198,72%,35%);
  96. font-family: "Segoe UI","Roboto",sans-serif;
  97. }
  98. body{
  99. background-color:#FFFFFF;
  100. box-sizing:border-box;
  101. width:100%;
  102. max-width:70em;
  103. margin:4em auto;
  104. box-shadow:0 1em 6em #00000080;
  105. padding:1em 1em 4em 1em;
  106. border-radius:0.4em;
  107. }
  108. h1,h2,h3,h4,h5,h6{
  109. font-weight:300;
  110. margin-bottom: 0.1em;
  111. }
  112. h1{
  113. text-align:center;
  114. }
  115. table{
  116. margin:2em 0;
  117. width:100%;
  118. }
  119. table, tr, th, td {
  120. border: 1px solid #AAAAAA;
  121. }
  122. th {
  123. width: 6em;
  124. }
  125. td {
  126. word-break: break-all;
  127. }
  128. </style>
  129. </head>
  130. <body>
  131. <h1>LibreSpeed - Stats</h1>
  132. {{ if .NoPassword }}
  133. Please set statistics_password in settings.toml to enable access.
  134. {{ else if .LoggedIn }}
  135. <form action="stats" method="GET"><input type="hidden" name="op" value="logout" /><input type="submit" value="Logout" /></form>
  136. <form action="stats" method="GET">
  137. <h3>Search test results</h6>
  138. <input type="hidden" name="op" value="id" />
  139. <input type="text" name="id" id="id" placeholder="Test ID" value=""/>
  140. <input type="submit" value="Find" />
  141. <input type="submit" onclick="document.getElementById('id').value='L100'" value="Show last 100 tests" />
  142. </form>
  143. {{ range $i, $v := .Data }}
  144. <table>
  145. <tr><th>Test ID</th><td>{{ $v.UUID }}</td></tr>
  146. <tr><th>Date and time</th><td>{{ $v.Timestamp }}</td></tr>
  147. <tr><th>IP and ISP Info</th><td>{{ $v.IPAddress }}<br/>{{ $v.ISPInfo }}</td></tr>
  148. <tr><th>User agent and locale</th><td>{{ $v.UserAgent }}<br/>{{ $v.Language }}</td></tr>
  149. <tr><th>Download speed</th><td>{{ $v.Download }}</td></tr>
  150. <tr><th>Upload speed</th><td>{{ $v.Upload }}</td></tr>
  151. <tr><th>Ping</th><td>{{ $v.Ping }}</td></tr>
  152. <tr><th>Jitter</th><td>{{ $v.Jitter }}</td></tr>
  153. <tr><th>Log</th><td>{{ $v.Log }}</td></tr>
  154. <tr><th>Extra info</th><td>{{ $v.Extra }}</td></tr>
  155. </table>
  156. {{ end }}
  157. {{ else }}
  158. <form action="stats?op=login" method="POST">
  159. <h3>Login</h3>
  160. <input type="password" name="password" placeholder="Password" value=""/>
  161. <input type="submit" value="Login" />
  162. </form>
  163. {{ end }}
  164. </body>
  165. </html>`