2014-01-05 23:54:57 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"log"
|
|
|
|
"net/http"
|
2014-01-10 00:09:27 +01:00
|
|
|
"runtime"
|
|
|
|
"sync"
|
2014-01-05 23:54:57 +01:00
|
|
|
|
2014-01-06 11:11:18 +01:00
|
|
|
"github.com/calmh/syncthing/model"
|
2014-01-05 23:54:57 +01:00
|
|
|
"github.com/codegangsta/martini"
|
|
|
|
)
|
|
|
|
|
2014-02-12 12:10:44 +01:00
|
|
|
var configInSync = true
|
|
|
|
|
2014-01-06 11:11:18 +01:00
|
|
|
func startGUI(addr string, m *model.Model) {
|
2014-01-05 23:54:57 +01:00
|
|
|
router := martini.NewRouter()
|
2014-01-06 00:05:07 +01:00
|
|
|
router.Get("/", getRoot)
|
2014-01-05 23:54:57 +01:00
|
|
|
router.Get("/rest/version", restGetVersion)
|
|
|
|
router.Get("/rest/model", restGetModel)
|
|
|
|
router.Get("/rest/connections", restGetConnections)
|
|
|
|
router.Get("/rest/config", restGetConfig)
|
2014-02-12 12:10:44 +01:00
|
|
|
router.Get("/rest/config/sync", restGetConfigInSync)
|
2014-01-05 23:54:57 +01:00
|
|
|
router.Get("/rest/need", restGetNeed)
|
2014-01-10 00:09:27 +01:00
|
|
|
router.Get("/rest/system", restGetSystem)
|
2014-01-05 23:54:57 +01:00
|
|
|
|
2014-02-01 20:23:19 +01:00
|
|
|
router.Post("/rest/config", restPostConfig)
|
2014-02-12 12:10:44 +01:00
|
|
|
router.Post("/rest/restart", restPostRestart)
|
2014-01-20 23:01:38 +01:00
|
|
|
|
2014-01-05 23:54:57 +01:00
|
|
|
go func() {
|
|
|
|
mr := martini.New()
|
2014-02-01 20:23:19 +01:00
|
|
|
mr.Use(embeddedStatic())
|
2014-01-05 23:54:57 +01:00
|
|
|
mr.Use(martini.Recovery())
|
|
|
|
mr.Action(router.Handle)
|
|
|
|
mr.Map(m)
|
2014-01-08 13:56:29 +01:00
|
|
|
err := http.ListenAndServe(addr, mr)
|
|
|
|
if err != nil {
|
|
|
|
warnln("GUI not possible:", err)
|
|
|
|
}
|
2014-01-05 23:54:57 +01:00
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2014-01-06 00:05:07 +01:00
|
|
|
func getRoot(w http.ResponseWriter, r *http.Request) {
|
|
|
|
http.Redirect(w, r, "/index.html", 302)
|
|
|
|
}
|
|
|
|
|
2014-01-05 23:54:57 +01:00
|
|
|
func restGetVersion() string {
|
|
|
|
return Version
|
|
|
|
}
|
|
|
|
|
2014-01-06 11:11:18 +01:00
|
|
|
func restGetModel(m *model.Model, w http.ResponseWriter) {
|
2014-01-05 23:54:57 +01:00
|
|
|
var res = make(map[string]interface{})
|
|
|
|
|
2014-01-06 05:57:41 +01:00
|
|
|
globalFiles, globalDeleted, globalBytes := m.GlobalSize()
|
|
|
|
res["globalFiles"], res["globalDeleted"], res["globalBytes"] = globalFiles, globalDeleted, globalBytes
|
|
|
|
|
|
|
|
localFiles, localDeleted, localBytes := m.LocalSize()
|
|
|
|
res["localFiles"], res["localDeleted"], res["localBytes"] = localFiles, localDeleted, localBytes
|
|
|
|
|
2014-01-06 06:38:01 +01:00
|
|
|
inSyncFiles, inSyncBytes := m.InSyncSize()
|
|
|
|
res["inSyncFiles"], res["inSyncBytes"] = inSyncFiles, inSyncBytes
|
|
|
|
|
2014-01-05 23:54:57 +01:00
|
|
|
files, total := m.NeedFiles()
|
|
|
|
res["needFiles"], res["needBytes"] = len(files), total
|
|
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
json.NewEncoder(w).Encode(res)
|
|
|
|
}
|
|
|
|
|
2014-01-06 11:11:18 +01:00
|
|
|
func restGetConnections(m *model.Model, w http.ResponseWriter) {
|
2014-01-05 23:54:57 +01:00
|
|
|
var res = m.ConnectionStats()
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
json.NewEncoder(w).Encode(res)
|
|
|
|
}
|
|
|
|
|
|
|
|
func restGetConfig(w http.ResponseWriter) {
|
2014-02-01 20:23:19 +01:00
|
|
|
json.NewEncoder(w).Encode(cfg)
|
|
|
|
}
|
|
|
|
|
|
|
|
func restPostConfig(req *http.Request) {
|
|
|
|
err := json.NewDecoder(req.Body).Decode(&cfg)
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
} else {
|
|
|
|
saveConfig()
|
2014-02-12 12:10:44 +01:00
|
|
|
configInSync = false
|
2014-02-01 20:23:19 +01:00
|
|
|
}
|
2014-01-05 23:54:57 +01:00
|
|
|
}
|
|
|
|
|
2014-02-12 12:10:44 +01:00
|
|
|
func restGetConfigInSync(w http.ResponseWriter) {
|
|
|
|
json.NewEncoder(w).Encode(map[string]bool{"configInSync": configInSync})
|
|
|
|
}
|
|
|
|
|
|
|
|
func restPostRestart(req *http.Request) {
|
|
|
|
restart()
|
|
|
|
}
|
|
|
|
|
2014-01-06 11:11:18 +01:00
|
|
|
type guiFile model.File
|
|
|
|
|
|
|
|
func (f guiFile) MarshalJSON() ([]byte, error) {
|
|
|
|
type t struct {
|
|
|
|
Name string
|
|
|
|
Size int
|
|
|
|
}
|
|
|
|
return json.Marshal(t{
|
|
|
|
Name: f.Name,
|
|
|
|
Size: model.File(f).Size(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func restGetNeed(m *model.Model, w http.ResponseWriter) {
|
2014-01-05 23:54:57 +01:00
|
|
|
files, _ := m.NeedFiles()
|
2014-01-06 11:11:18 +01:00
|
|
|
gfs := make([]guiFile, len(files))
|
|
|
|
for i, f := range files {
|
|
|
|
gfs[i] = guiFile(f)
|
2014-01-05 23:54:57 +01:00
|
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
2014-01-06 11:11:18 +01:00
|
|
|
json.NewEncoder(w).Encode(gfs)
|
2014-01-05 23:54:57 +01:00
|
|
|
}
|
|
|
|
|
2014-01-10 00:09:27 +01:00
|
|
|
var cpuUsagePercent float64
|
|
|
|
var cpuUsageLock sync.RWMutex
|
|
|
|
|
|
|
|
func restGetSystem(w http.ResponseWriter) {
|
|
|
|
var m runtime.MemStats
|
|
|
|
runtime.ReadMemStats(&m)
|
|
|
|
|
|
|
|
res := make(map[string]interface{})
|
2014-02-01 20:23:19 +01:00
|
|
|
res["myID"] = myID
|
2014-01-10 00:09:27 +01:00
|
|
|
res["goroutines"] = runtime.NumGoroutine()
|
|
|
|
res["alloc"] = m.Alloc
|
|
|
|
res["sys"] = m.Sys
|
|
|
|
cpuUsageLock.RLock()
|
|
|
|
res["cpuPercent"] = cpuUsagePercent
|
|
|
|
cpuUsageLock.RUnlock()
|
|
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
json.NewEncoder(w).Encode(res)
|
|
|
|
}
|