gui.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "log"
  6. "mime"
  7. "net/http"
  8. "path/filepath"
  9. "runtime"
  10. "sync"
  11. "time"
  12. "github.com/calmh/syncthing/auto"
  13. "github.com/calmh/syncthing/model"
  14. "github.com/codegangsta/martini"
  15. "github.com/cratonica/embed"
  16. )
  17. func startGUI(addr string, m *model.Model) {
  18. router := martini.NewRouter()
  19. router.Get("/", getRoot)
  20. router.Get("/rest/version", restGetVersion)
  21. router.Get("/rest/model", restGetModel)
  22. router.Get("/rest/connections", restGetConnections)
  23. router.Get("/rest/config", restGetConfig)
  24. router.Get("/rest/need", restGetNeed)
  25. router.Get("/rest/system", restGetSystem)
  26. fs, err := embed.Unpack(auto.Resources)
  27. if err != nil {
  28. panic(err)
  29. }
  30. go func() {
  31. mr := martini.New()
  32. mr.Use(embeddedStatic(fs))
  33. mr.Use(martini.Recovery())
  34. mr.Action(router.Handle)
  35. mr.Map(m)
  36. err := http.ListenAndServe(addr, mr)
  37. if err != nil {
  38. warnln("GUI not possible:", err)
  39. }
  40. }()
  41. }
  42. func getRoot(w http.ResponseWriter, r *http.Request) {
  43. http.Redirect(w, r, "/index.html", 302)
  44. }
  45. func restGetVersion() string {
  46. return Version
  47. }
  48. func restGetModel(m *model.Model, w http.ResponseWriter) {
  49. var res = make(map[string]interface{})
  50. globalFiles, globalDeleted, globalBytes := m.GlobalSize()
  51. res["globalFiles"], res["globalDeleted"], res["globalBytes"] = globalFiles, globalDeleted, globalBytes
  52. localFiles, localDeleted, localBytes := m.LocalSize()
  53. res["localFiles"], res["localDeleted"], res["localBytes"] = localFiles, localDeleted, localBytes
  54. inSyncFiles, inSyncBytes := m.InSyncSize()
  55. res["inSyncFiles"], res["inSyncBytes"] = inSyncFiles, inSyncBytes
  56. files, total := m.NeedFiles()
  57. res["needFiles"], res["needBytes"] = len(files), total
  58. w.Header().Set("Content-Type", "application/json")
  59. json.NewEncoder(w).Encode(res)
  60. }
  61. func restGetConnections(m *model.Model, w http.ResponseWriter) {
  62. var res = m.ConnectionStats()
  63. w.Header().Set("Content-Type", "application/json")
  64. json.NewEncoder(w).Encode(res)
  65. }
  66. func restGetConfig(w http.ResponseWriter) {
  67. var res = make(map[string]interface{})
  68. res["myID"] = myID
  69. res["repository"] = config.OptionMap("repository")
  70. res["nodes"] = config.OptionMap("nodes")
  71. w.Header().Set("Content-Type", "application/json")
  72. json.NewEncoder(w).Encode(res)
  73. }
  74. type guiFile model.File
  75. func (f guiFile) MarshalJSON() ([]byte, error) {
  76. type t struct {
  77. Name string
  78. Size int
  79. }
  80. return json.Marshal(t{
  81. Name: f.Name,
  82. Size: model.File(f).Size(),
  83. })
  84. }
  85. func restGetNeed(m *model.Model, w http.ResponseWriter) {
  86. files, _ := m.NeedFiles()
  87. gfs := make([]guiFile, len(files))
  88. for i, f := range files {
  89. gfs[i] = guiFile(f)
  90. }
  91. w.Header().Set("Content-Type", "application/json")
  92. json.NewEncoder(w).Encode(gfs)
  93. }
  94. var cpuUsagePercent float64
  95. var cpuUsageLock sync.RWMutex
  96. func restGetSystem(w http.ResponseWriter) {
  97. var m runtime.MemStats
  98. runtime.ReadMemStats(&m)
  99. res := make(map[string]interface{})
  100. res["goroutines"] = runtime.NumGoroutine()
  101. res["alloc"] = m.Alloc
  102. res["sys"] = m.Sys
  103. cpuUsageLock.RLock()
  104. res["cpuPercent"] = cpuUsagePercent
  105. cpuUsageLock.RUnlock()
  106. w.Header().Set("Content-Type", "application/json")
  107. json.NewEncoder(w).Encode(res)
  108. }
  109. func embeddedStatic(fs map[string][]byte) interface{} {
  110. var modt = time.Now().UTC().Format(http.TimeFormat)
  111. return func(res http.ResponseWriter, req *http.Request, log *log.Logger) {
  112. file := req.URL.Path
  113. if file[0] == '/' {
  114. file = file[1:]
  115. }
  116. bs, ok := fs[file]
  117. if !ok {
  118. return
  119. }
  120. mtype := mime.TypeByExtension(filepath.Ext(req.URL.Path))
  121. if len(mtype) != 0 {
  122. res.Header().Set("Content-Type", mtype)
  123. }
  124. res.Header().Set("Content-Size", fmt.Sprintf("%d", len(bs)))
  125. res.Header().Set("Last-Modified", modt)
  126. res.Write(bs)
  127. }
  128. }