func newStaticsServer(theme, assetDir string) *staticsServer { s := &staticsServer{ assetDir: assetDir, assets: auto.Assets(), mut: sync.NewRWMutex(), theme: theme, } seen := make(map[string]struct{}) // Load themes from compiled in assets. for file := range auto.Assets() { theme := strings.Split(file, "/")[0] if _, ok := seen[theme]; !ok { seen[theme] = struct{}{} s.availableThemes = append(s.availableThemes, theme) } } if assetDir != "" { // Load any extra themes from the asset override dir. for _, dir := range dirNames(assetDir) { if _, ok := seen[dir]; !ok { seen[dir] = struct{}{} s.availableThemes = append(s.availableThemes, dir) } } } return s }
func newAPIService(id protocol.DeviceID, cfg *config.Wrapper, assetDir string, m *model.Model, eventSub *events.BufferedSubscription, discoverer *discover.CachingMux, relayService *relay.Service, errors, systemLog *logger.Recorder) (*apiService, error) { service := &apiService{ id: id, cfg: cfg, assetDir: assetDir, model: m, eventSub: eventSub, discoverer: discoverer, relayService: relayService, systemConfigMut: sync.NewMutex(), guiErrors: errors, systemLog: systemLog, } seen := make(map[string]struct{}) for file := range auto.Assets() { theme := strings.Split(file, "/")[0] if _, ok := seen[theme]; !ok { seen[theme] = struct{}{} service.themes = append(service.themes, theme) } } var err error service.listener, err = service.getListener(cfg.GUI()) return service, err }
func TestAssets(t *testing.T) { assets := auto.Assets() idx, ok := assets["index.html"] if !ok { t.Fatal("No index.html in compiled in assets") } var gr *gzip.Reader gr, _ = gzip.NewReader(bytes.NewReader(idx)) idx, _ = ioutil.ReadAll(gr) if !bytes.Contains(idx, []byte("<html")) { t.Fatal("No html in index.html") } }
func newAPIService(id protocol.DeviceID, cfg configIntf, httpsCertFile, httpsKeyFile, assetDir string, m modelIntf, eventSub events.BufferedSubscription, discoverer discover.CachingMux, relayService relay.Service, errors, systemLog logger.Recorder) (*apiService, error) { service := &apiService{ id: id, cfg: cfg, httpsCertFile: httpsCertFile, httpsKeyFile: httpsKeyFile, assetDir: assetDir, model: m, eventSub: eventSub, discoverer: discoverer, relayService: relayService, systemConfigMut: sync.NewMutex(), stop: make(chan struct{}), configChanged: make(chan struct{}), listenerMut: sync.NewMutex(), guiErrors: errors, systemLog: systemLog, } seen := make(map[string]struct{}) // Load themes from compiled in assets. for file := range auto.Assets() { theme := strings.Split(file, "/")[0] if _, ok := seen[theme]; !ok { seen[theme] = struct{}{} service.themes = append(service.themes, theme) } } if assetDir != "" { // Load any extra themes from the asset override dir. for _, dir := range dirNames(assetDir) { if _, ok := seen[dir]; !ok { seen[dir] = struct{}{} service.themes = append(service.themes, dir) } } } var err error service.listener, err = service.getListener(cfg.GUI()) return service, err }
func (s *apiService) Serve() { s.stop = make(chan struct{}) // The GET handlers getRestMux := http.NewServeMux() getRestMux.HandleFunc("/rest/db/completion", s.getDBCompletion) // device folder getRestMux.HandleFunc("/rest/db/file", s.getDBFile) // folder file getRestMux.HandleFunc("/rest/db/ignores", s.getDBIgnores) // folder getRestMux.HandleFunc("/rest/db/need", s.getDBNeed) // folder [perpage] [page] getRestMux.HandleFunc("/rest/db/status", s.getDBStatus) // folder getRestMux.HandleFunc("/rest/db/browse", s.getDBBrowse) // folder [prefix] [dirsonly] [levels] getRestMux.HandleFunc("/rest/events", s.getEvents) // since [limit] getRestMux.HandleFunc("/rest/stats/device", s.getDeviceStats) // - getRestMux.HandleFunc("/rest/stats/folder", s.getFolderStats) // - getRestMux.HandleFunc("/rest/svc/deviceid", s.getDeviceID) // id getRestMux.HandleFunc("/rest/svc/lang", s.getLang) // - getRestMux.HandleFunc("/rest/svc/report", s.getReport) // - getRestMux.HandleFunc("/rest/system/browse", s.getSystemBrowse) // current getRestMux.HandleFunc("/rest/system/config", s.getSystemConfig) // - getRestMux.HandleFunc("/rest/system/config/insync", s.getSystemConfigInsync) // - getRestMux.HandleFunc("/rest/system/connections", s.getSystemConnections) // - getRestMux.HandleFunc("/rest/system/discovery", s.getSystemDiscovery) // - getRestMux.HandleFunc("/rest/system/error", s.getSystemError) // - getRestMux.HandleFunc("/rest/system/ping", s.restPing) // - getRestMux.HandleFunc("/rest/system/status", s.getSystemStatus) // - getRestMux.HandleFunc("/rest/system/upgrade", s.getSystemUpgrade) // - getRestMux.HandleFunc("/rest/system/version", s.getSystemVersion) // - getRestMux.HandleFunc("/rest/system/debug", s.getSystemDebug) // - getRestMux.HandleFunc("/rest/system/log", s.getSystemLog) // [since] getRestMux.HandleFunc("/rest/system/log.txt", s.getSystemLogTxt) // [since] // The POST handlers postRestMux := http.NewServeMux() postRestMux.HandleFunc("/rest/db/prio", s.postDBPrio) // folder file [perpage] [page] postRestMux.HandleFunc("/rest/db/ignores", s.postDBIgnores) // folder postRestMux.HandleFunc("/rest/db/override", s.postDBOverride) // folder postRestMux.HandleFunc("/rest/db/scan", s.postDBScan) // folder [sub...] [delay] postRestMux.HandleFunc("/rest/system/config", s.postSystemConfig) // <body> postRestMux.HandleFunc("/rest/system/error", s.postSystemError) // <body> postRestMux.HandleFunc("/rest/system/error/clear", s.postSystemErrorClear) // - postRestMux.HandleFunc("/rest/system/ping", s.restPing) // - postRestMux.HandleFunc("/rest/system/reset", s.postSystemReset) // [folder] postRestMux.HandleFunc("/rest/system/restart", s.postSystemRestart) // - postRestMux.HandleFunc("/rest/system/shutdown", s.postSystemShutdown) // - postRestMux.HandleFunc("/rest/system/upgrade", s.postSystemUpgrade) // - postRestMux.HandleFunc("/rest/system/pause", s.postSystemPause) // device postRestMux.HandleFunc("/rest/system/resume", s.postSystemResume) // device postRestMux.HandleFunc("/rest/system/debug", s.postSystemDebug) // [enable] [disable] // Debug endpoints, not for general use getRestMux.HandleFunc("/rest/debug/peerCompletion", s.getPeerCompletion) getRestMux.HandleFunc("/rest/debug/httpmetrics", s.getSystemHTTPMetrics) // A handler that splits requests between the two above and disables // caching restMux := noCacheMiddleware(metricsMiddleware(getPostHandler(getRestMux, postRestMux))) // The main routing handler mux := http.NewServeMux() mux.Handle("/rest/", restMux) mux.HandleFunc("/qr/", s.getQR) // Serve compiled in assets unless an asset directory was set (for development) mux.Handle("/", embeddedStatic{ assetDir: s.assetDir, assets: auto.Assets(), }) guiCfg := s.cfg.GUI() // Wrap everything in CSRF protection. The /rest prefix should be // protected, other requests will grant cookies. handler := csrfMiddleware(s.id.String()[:5], "/rest", guiCfg.APIKey(), mux) // Add our version and ID as a header to responses handler = withDetailsMiddleware(s.id, handler) // Wrap everything in basic auth, if user/password is set. if len(guiCfg.User) > 0 && len(guiCfg.Password) > 0 { handler = basicAuthAndSessionMiddleware("sessionid-"+s.id.String()[:5], guiCfg, handler) } // Redirect to HTTPS if we are supposed to if guiCfg.UseTLS() { handler = redirectToHTTPSMiddleware(handler) } handler = debugMiddleware(handler) srv := http.Server{ Handler: handler, ReadTimeout: 10 * time.Second, } s.fss = newFolderSummaryService(s.cfg, s.model) defer s.fss.Stop() s.fss.ServeBackground() l.Infoln("API listening on", s.listener.Addr()) l.Infoln("GUI URL is", guiCfg.URL()) err := srv.Serve(s.listener) // The return could be due to an intentional close. Wait for the stop // signal before returning. IF there is no stop signal within a second, we // assume it was unintentional and log the error before retrying. select { case <-s.stop: case <-time.After(time.Second): l.Warnln("API:", err) } }