mirror of
https://gitcode.com/JianFeeeee/webui4frpc.git
synced 2026-09-19 16:38:31 +00:00
fix: 注册 PUT /api/manager/settings 使设置保存生效
server.go NewServeMux 按方法分发到 handleSettingsPut(原仅注册 GET,PUT 静默返回旧值) 新增 TestSettingsPutRoundTrip 回归测试;gofmt 清理历史格式问题
This commit is contained in:
@ -257,8 +257,6 @@ func (h *Handler) handleProfile(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// ---- helpers ----
|
||||
|
||||
|
||||
|
||||
// tailFile returns the last maxBytes bytes of a file.
|
||||
func tailFile(path string, maxBytes int64) (string, error) {
|
||||
info, err := os.Stat(path)
|
||||
@ -280,4 +278,3 @@ func tailFile(path string, maxBytes int64) (string, error) {
|
||||
}
|
||||
return string(buf), nil
|
||||
}
|
||||
|
||||
|
||||
@ -61,7 +61,13 @@ func NewServeMux(h *Handler) (http.Handler, error) {
|
||||
// API routes (basic auth).
|
||||
mux.HandleFunc(apiPrefix+"/status", auth(h.handleStatus))
|
||||
mux.HandleFunc(apiPrefix+"/canvas", auth(h.handleCanvasSave))
|
||||
mux.HandleFunc(apiPrefix+"/settings", auth(h.handleSettingsGet))
|
||||
mux.HandleFunc(apiPrefix+"/settings", auth(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodPut {
|
||||
h.handleSettingsPut(w, r)
|
||||
return
|
||||
}
|
||||
h.handleSettingsGet(w, r)
|
||||
}))
|
||||
mux.HandleFunc(apiPrefix+"/binary/status", auth(h.handleBinaryStatus))
|
||||
mux.HandleFunc(apiPrefix+"/binary/install", auth(h.handleBinaryInstall))
|
||||
|
||||
@ -135,10 +141,10 @@ func (h *Handler) handleStatus(w http.ResponseWriter, r *http.Request) {
|
||||
settings, _ := h.Store.Settings()
|
||||
|
||||
type profileStatus struct {
|
||||
Name string `json:"name"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Status process.Status `json:"process"`
|
||||
HasProc bool `json:"hasProcess"`
|
||||
Name string `json:"name"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Status process.Status `json:"process"`
|
||||
HasProc bool `json:"hasProcess"`
|
||||
Forwards []store.Forward `json:"forwards"`
|
||||
}
|
||||
|
||||
@ -155,7 +161,7 @@ func (h *Handler) handleStatus(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
}
|
||||
|
||||
// Local status: each local plus its forwarding targets and whether each
|
||||
// Local status: each local plus its forwarding targets and whether each
|
||||
// target's worker is healthy (running).
|
||||
type localTargetStatus struct {
|
||||
Remote string `json:"remote"`
|
||||
@ -163,8 +169,8 @@ func (h *Handler) handleStatus(w http.ResponseWriter, r *http.Request) {
|
||||
WorkerState string `json:"workerState"`
|
||||
}
|
||||
type localStatus struct {
|
||||
Local store.Local `json:"local"`
|
||||
Targets []localTargetStatus `json:"targets"`
|
||||
Local store.Local `json:"local"`
|
||||
Targets []localTargetStatus `json:"targets"`
|
||||
}
|
||||
|
||||
localStatuses := make([]localStatus, 0, len(locals))
|
||||
@ -186,13 +192,13 @@ func (h *Handler) handleStatus(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
resp := map[string]any{
|
||||
"version": "0.1.0",
|
||||
"workDir": h.WorkDir,
|
||||
"settings": settings,
|
||||
"services": locals,
|
||||
"remotes": remotes,
|
||||
"binaryPath": binary,
|
||||
"profiles": profiles,
|
||||
"version": "0.1.0",
|
||||
"workDir": h.WorkDir,
|
||||
"settings": settings,
|
||||
"services": locals,
|
||||
"remotes": remotes,
|
||||
"binaryPath": binary,
|
||||
"profiles": profiles,
|
||||
"localStatus": localStatuses,
|
||||
}
|
||||
writeJSON(w, http.StatusOK, resp)
|
||||
|
||||
@ -97,3 +97,44 @@ func TestCanvasRoundTrip(t *testing.T) {
|
||||
t.Fatalf("remotes after reload = %+v", got2.Remotes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSettingsPutRoundTrip(t *testing.T) {
|
||||
_, ts := newTestHandler(t)
|
||||
client := ts.Client()
|
||||
|
||||
// PUT updated settings
|
||||
body := `{"autoStartProfiles":false,"restartOnExit":false,"restartIntervalSeconds":12}`
|
||||
req, _ := http.NewRequest(http.MethodPut, ts.URL+"/api/manager/settings", bytes.NewBufferString(body))
|
||||
req.SetBasicAuth("admin", "pw")
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("PUT settings status = %d, want 200", resp.StatusCode)
|
||||
}
|
||||
|
||||
// Confirm response body reflects saved values
|
||||
var saved store.Settings
|
||||
if err := json.NewDecoder(resp.Body).Decode(&saved); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if saved.AutoStartProfiles || saved.RestartOnExit || saved.RestartIntervalSeconds != 12 {
|
||||
t.Fatalf("saved settings = %+v", saved)
|
||||
}
|
||||
|
||||
// GET again to confirm persistence
|
||||
req2, _ := http.NewRequest(http.MethodGet, ts.URL+"/api/manager/settings", nil)
|
||||
req2.SetBasicAuth("admin", "pw")
|
||||
resp2, err2 := client.Do(req2)
|
||||
if err2 != nil {
|
||||
t.Fatal(err2)
|
||||
}
|
||||
defer resp2.Body.Close()
|
||||
var got store.Settings
|
||||
_ = json.NewDecoder(resp2.Body).Decode(&got)
|
||||
if got.AutoStartProfiles || got.RestartOnExit || got.RestartIntervalSeconds != 12 {
|
||||
t.Fatalf("settings after reload = %+v", got)
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ type Local struct {
|
||||
type Remote struct {
|
||||
Name string `json:"name"`
|
||||
IP string `json:"ip"`
|
||||
Port int `json:"port"` // port to connect to frps
|
||||
Port int `json:"port"` // port to connect to frps
|
||||
Token string `json:"token,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
Enabled bool `json:"enabled"`
|
||||
@ -32,7 +32,7 @@ type Remote struct {
|
||||
|
||||
// Link connects one local to one remote.
|
||||
type Link struct {
|
||||
ID int64 `json:"id,omitempty"`
|
||||
ID int64 `json:"id,omitempty"`
|
||||
Local string `json:"local"`
|
||||
Remote string `json:"remote"`
|
||||
RemotePort int `json:"remotePort"`
|
||||
|
||||
4
plan.md
4
plan.md
@ -30,7 +30,7 @@
|
||||
|
||||
## 已知问题(审查发现,待修复)
|
||||
|
||||
1. **设置保存失效(P0)**:`internal/httpapi/handlers.go` 定义了 `handleSettingsPut`,但 `server.go` 的 `NewServeMux` 只注册了 GET `/api/manager/settings`,**未注册 PUT**。PUT 请求会落入 `handleSettingsGet` 静默返回旧值,SettingsView 保存不生效。
|
||||
1. ~~设置保存失效(P0)~~ **已修复**:`server.go` 的 `NewServeMux` 现在对 `PUT /api/manager/settings` 分发到 `handleSettingsPut`,新增 `TestSettingsPutRoundTrip` 回归测试,前端 SettingsView 保存生效。
|
||||
2. **dev proxy 端口不一致**:`web/vite.config.ts` 的 proxy 指向 `127.0.0.1:17650`,与后端默认监听 `127.0.0.1:7500` 不符,前端 dev 联调需对齐。
|
||||
3. **冗余依赖**:`vue-router` 在 package.json 依赖中但未被使用(App.vue 以视图 ref 手动切页,无路由)。
|
||||
4. **README 笔误**:架构图写 `cmds/webui4frpc`,实际目录为 `cmd/webui4frpc`。
|
||||
@ -76,7 +76,7 @@
|
||||
|
||||
## 建议推进顺序
|
||||
|
||||
1. 修复「设置保存失效」——给 `PUT /api/manager/settings` 补注册(约 10 分钟)
|
||||
1. [x] 修复「设置保存失效」——给 `PUT /api/manager/settings` 补注册(server.go 方法分发 + httpapi 单测)
|
||||
2. 对齐 dev proxy 端口(vite.config.ts → 7500)
|
||||
3. M1 高级传输参数(store 加列 → render 输出 → 前端折叠表单 → 单测回归)
|
||||
4. M5 补一键构建脚本 / CI
|
||||
|
||||
Reference in New Issue
Block a user