package main import ( "crypto/rand" "fmt" "strings" "time" "github.com/gin-gonic/gin" "github.com/studio-b12/gowebdav" ) func (a *App) getSettings(c *gin.Context) { c.JSON(200, gin.H{ "amApi": a.cfg.AMAPI, "downloadDir": a.cfg.DownloadDir, "webdavUrl": a.cfg.WebDAVURL, "webdavUser": a.cfg.WebDAVUser, "webdavPass": "", "hasWebdavPassword": a.cfg.WebDAVPass != "", "webdavPath": a.cfg.WebDAVPath, "quality": a.cfg.DefaultQuality, "webdavVersionPolicy": a.cfg.WebDAVVersionPolicy, "localFilePolicy": a.cfg.LocalFilePolicy, }) } func (a *App) putSettings(c *gin.Context) { var v map[string]string if c.BindJSON(&v) != nil { c.Status(400) return } if x := v["amApi"]; x != "" { a.cfg.AMAPI = x a.saveSetting("am_api", x) a.am = &AMAPIClient{BaseURL: x, HTTP: a.httpClient} } if x := v["downloadDir"]; x != "" { a.cfg.DownloadDir = x a.saveSetting("download_dir", x) } if p := v["webdavVersionPolicy"]; p == "keep" || p == "replace_lower" { a.cfg.WebDAVVersionPolicy = p } if p := v["localFilePolicy"]; p == "keep" || p == "delete_after_upload" { a.cfg.LocalFilePolicy = p } if x, ok := v["webdavUrl"]; ok { a.cfg.WebDAVURL = x } if x, ok := v["webdavUser"]; ok { a.cfg.WebDAVUser = x } // The GET endpoint intentionally never returns the WebDAV password. // Therefore an empty webdavPass means "keep the existing password", not clear it. if x, ok := v["webdavPass"]; ok && x != "" { a.cfg.WebDAVPass = x } if x, ok := v["webdavPath"]; ok { a.cfg.WebDAVPath = strings.Trim(x, "/") } if x, ok := v["quality"]; ok && x != "" { a.cfg.DefaultQuality = x } for k, x := range map[string]string{ "webdav_url": a.cfg.WebDAVURL, "webdav_user": a.cfg.WebDAVUser, "webdav_pass": a.cfg.WebDAVPass, "webdav_path": a.cfg.WebDAVPath, "quality": a.cfg.DefaultQuality, "webdav_version_policy": a.cfg.WebDAVVersionPolicy, "local_file_policy": a.cfg.LocalFilePolicy, } { a.saveSetting(k, x) } if a.cfg.WebDAVURL != "" { a.storage = NewWebDAVStorage(a.cfg) } else { a.storage = nil } // Credentials/config changes should repair already-downloaded local files // immediately instead of waiting for the periodic retry interval. go a.resyncPendingUploads() c.JSON(200, gin.H{"ok": true}) } func (a *App) testWebDAV(c *gin.Context) { var v struct{ URL, User, Pass, Path string } if c.BindJSON(&v) != nil { c.Status(400) return } // An empty password means "use the saved password". The password is never // returned to the browser, so this also makes a fresh settings page test // the credential that is actually stored by the server. if v.URL == "" { v.URL = a.cfg.WebDAVURL } if v.User == "" { v.User = a.cfg.WebDAVUser } if v.Pass == "" { v.Pass = a.cfg.WebDAVPass } v.Path = strings.Trim(v.Path, "/") if v.Path == "" { v.Path = a.cfg.WebDAVPath } if v.URL == "" || v.User == "" || v.Pass == "" { c.JSON(400, gin.H{"ok": false, "error": "WebDAV URL、用户名或密码未配置"}) return } client := gowebdav.NewClient(v.URL, v.User, v.Pass) client.SetTimeout(20 * time.Second) if err := client.Connect(); err != nil { c.JSON(502, gin.H{"ok": false, "error": "WebDAV authentication failed: " + err.Error()}) return } if v.Path != "" { if err := client.MkdirAll(v.Path, 0755); err != nil { c.JSON(502, gin.H{"ok": false, "error": "WebDAV target path is not writable: " + err.Error()}) return } } var token [12]byte if _, err := rand.Read(token[:]); err != nil { c.JSON(500, gin.H{"ok": false, "error": "failed to create test token"}) return } testPath := strings.Trim(v.Path, "/") + "/.am-downloader-test-" + fmt.Sprintf("%x", token[:]) if err := client.Write(testPath, []byte("ok"), 0644); err != nil { c.JSON(502, gin.H{"ok": false, "error": "WebDAV write test failed: " + err.Error()}) return } _ = client.Remove(testPath) c.JSON(200, gin.H{"ok": true, "path": v.Path}) }