Skip to content

Commit 1d20cbc

Browse files
committed
feat: 备注折叠与导入自动测速
1 parent e5fe29e commit 1d20cbc

8 files changed

Lines changed: 459 additions & 189 deletions

File tree

backend/api/handlers.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,40 @@ func (h *Handler) UpdateNode(c *gin.Context) {
466466
c.JSON(http.StatusOK, req)
467467
}
468468

469+
func (h *Handler) UpdateNodeRemark(c *gin.Context) {
470+
id, err := strconv.Atoi(c.Param("id"))
471+
if err != nil {
472+
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"})
473+
return
474+
}
475+
476+
var req struct {
477+
Remark string `json:"remark"`
478+
}
479+
if err := c.BindJSON(&req); err != nil {
480+
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
481+
return
482+
}
483+
484+
res, err := h.db.Exec(`
485+
UPDATE proxy_nodes
486+
SET remark = ?, updated_at = CURRENT_TIMESTAMP
487+
WHERE id = ?
488+
`, req.Remark, id)
489+
if err != nil {
490+
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update remark"})
491+
return
492+
}
493+
494+
affected, _ := res.RowsAffected()
495+
if affected == 0 {
496+
c.JSON(http.StatusNotFound, gin.H{"error": "node not found"})
497+
return
498+
}
499+
500+
c.JSON(http.StatusOK, gin.H{"id": id, "remark": req.Remark})
501+
}
502+
469503
// DeleteNode deletes a proxy node
470504
func (h *Handler) DeleteNode(c *gin.Context) {
471505
id, err := strconv.Atoi(c.Param("id"))

backend/api/handlers_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net/http"
88
"net/http/httptest"
99
"strconv"
10+
"strings"
1011
"testing"
1112

1213
"sb-proxy/backend/models"
@@ -171,3 +172,39 @@ func TestCheckNodeIPRejectsSocksFallback(t *testing.T) {
171172
t.Fatalf("expected cleared status after fallback, got ip=%s location=%s country=%s latency=%d", ip, location, countryCode, latency)
172173
}
173174
}
175+
176+
func TestUpdateNodeRemarkUpdatesRemark(t *testing.T) {
177+
gin.SetMode(gin.TestMode)
178+
179+
handler := newTestHandler(t, func(proxyAddr, username, password string) (*services.IPInfo, error) {
180+
return nil, fmt.Errorf("not used")
181+
})
182+
183+
nodeID := insertTestNode(t, handler.db)
184+
185+
rec := httptest.NewRecorder()
186+
ctx, _ := gin.CreateTestContext(rec)
187+
ctx.Params = gin.Params{gin.Param{Key: "id", Value: strconv.Itoa(nodeID)}}
188+
189+
req, _ := http.NewRequest(
190+
http.MethodPut,
191+
"/api/nodes/"+strconv.Itoa(nodeID)+"/remark",
192+
strings.NewReader(`{"remark":"hello"}`),
193+
)
194+
req.Header.Set("Content-Type", "application/json")
195+
ctx.Request = req
196+
197+
handler.UpdateNodeRemark(ctx)
198+
199+
if rec.Code != http.StatusOK {
200+
t.Fatalf("unexpected status %d", rec.Code)
201+
}
202+
203+
var remark string
204+
if err := handler.db.QueryRow("SELECT remark FROM proxy_nodes WHERE id = ?", nodeID).Scan(&remark); err != nil {
205+
t.Fatalf("query node: %v", err)
206+
}
207+
if remark != "hello" {
208+
t.Fatalf("expected remark to be updated, got %q", remark)
209+
}
210+
}

backend/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ func main() {
174174
authorized.POST("/nodes/batch-delete", handler.BatchDeleteNodes)
175175
authorized.POST("/nodes/batch-export", handler.BatchExportNodes)
176176
authorized.PUT("/nodes/:id", handler.UpdateNode)
177+
authorized.PUT("/nodes/:id/remark", handler.UpdateNodeRemark)
177178
authorized.PUT("/nodes/:id/replace", handler.ReplaceNode)
178179
authorized.DELETE("/nodes/:id", handler.DeleteNode)
179180
authorized.GET("/nodes/:id/export", handler.ExportNode)

0 commit comments

Comments
 (0)