dashboard.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. package dashboard
  2. import (
  3. "html/template"
  4. "math/rand"
  5. "net/http"
  6. "time"
  7. "github.com/gorilla/mux"
  8. "github.com/gorilla/websocket"
  9. )
  10. const (
  11. dashboard = "index.html"
  12. login = "login.html"
  13. dashboardPath = "html/index.html"
  14. loginPath = "html/login.html"
  15. sessionTimeout = time.Hour * 24 // TODO replace with config
  16. )
  17. var (
  18. // Cache of HTML templates
  19. templates = template.Must(template.ParseFiles(dashboardPath, loginPath))
  20. config *Config
  21. sessions sessionStore
  22. store *dataStore
  23. )
  24. var upgrader = websocket.Upgrader{
  25. ReadBufferSize: 1024,
  26. WriteBufferSize: 1024,
  27. }
  28. type Config struct {
  29. Username string
  30. Password string
  31. ListenInterface string
  32. }
  33. func Run(c *Config) {
  34. config = c
  35. r := mux.NewRouter()
  36. r.HandleFunc("/", indexHandler)
  37. r.HandleFunc("/login", loginHandler)
  38. r.HandleFunc("/ws", webSocketHandler)
  39. rand.Seed(time.Now().UnixNano())
  40. sessions = make(sessionStore)
  41. go sessions.cleaner(sessionTimeout)
  42. store = newDataStore()
  43. go ramListener(tickInterval, store)
  44. http.ListenAndServe(c.ListenInterface, r)
  45. }
  46. func indexHandler(w http.ResponseWriter, r *http.Request) {
  47. if isLoggedIn(r) {
  48. w.WriteHeader(http.StatusOK)
  49. templates.ExecuteTemplate(w, dashboard, nil)
  50. } else {
  51. http.Redirect(w, r, "/login", http.StatusTemporaryRedirect)
  52. }
  53. }
  54. func loginHandler(w http.ResponseWriter, r *http.Request) {
  55. switch r.Method {
  56. case "GET":
  57. if isLoggedIn(r) {
  58. http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
  59. } else {
  60. templates.ExecuteTemplate(w, login, nil)
  61. }
  62. case "POST":
  63. user := r.FormValue("username")
  64. pass := r.FormValue("password")
  65. if user == config.Username && pass == config.Password {
  66. err := startSession(w, r)
  67. if err != nil {
  68. w.WriteHeader(http.StatusInternalServerError)
  69. // TODO Internal error
  70. return
  71. }
  72. http.Redirect(w, r, "/", http.StatusSeeOther)
  73. } else {
  74. templates.ExecuteTemplate(w, login, nil) // TODO info about failed login
  75. }
  76. default:
  77. w.WriteHeader(http.StatusMethodNotAllowed)
  78. }
  79. }
  80. func webSocketHandler(w http.ResponseWriter, r *http.Request) {
  81. if !isLoggedIn(r) {
  82. w.WriteHeader(http.StatusForbidden)
  83. return
  84. }
  85. sess := getSession(r)
  86. conn, err := upgrader.Upgrade(w, r, nil)
  87. if err != nil {
  88. w.WriteHeader(http.StatusInternalServerError)
  89. // TODO Internal error
  90. return
  91. }
  92. sess.ws = conn
  93. c := make(chan *point)
  94. sess.send = c
  95. store.subscribe(c)
  96. go sess.receive()
  97. go sess.transmit()
  98. }
  99. func startSession(w http.ResponseWriter, r *http.Request) error {
  100. sessionID := newSessionID()
  101. cookie := &http.Cookie{
  102. Name: "SID",
  103. Value: sessionID,
  104. Path: "/",
  105. // Secure: true,
  106. }
  107. sess := &session{
  108. start: time.Now(),
  109. expires: time.Now().Add(sessionTimeout), // TODO config for this
  110. id: sessionID,
  111. }
  112. http.SetCookie(w, cookie)
  113. sessions[sessionID] = sess
  114. return nil
  115. }
  116. func getSession(r *http.Request) *session {
  117. c, err := r.Cookie("SID")
  118. if err != nil {
  119. return nil
  120. }
  121. sid := c.Value
  122. sess, ok := sessions[sid]
  123. if !ok {
  124. return nil
  125. }
  126. return sess
  127. }
  128. func isLoggedIn(r *http.Request) bool {
  129. sess := getSession(r)
  130. if sess == nil {
  131. return false
  132. }
  133. if !sess.valid() {
  134. return false
  135. }
  136. return true
  137. }