mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-02 19:25:05 +03:00
f1444bec3f
The refresh rewrote the whole row it read before a download whose length the provider controls, so an admin's delete, disable or edit made in the meantime was undone; Update had the mirror problem. Both now write only their own columns, and a refresh of a row that was deleted or re-pointed stores nothing. Around the same path: - the preview fetches without creating (and leaking) a scratch row, and Create keeps enabled=false instead of falling back to the column default; - fetch errors name the host but not the subscription URL, which is a credential and ends up in the log and in lastError; - each fetch closes its transport's idle connections; - one document loads at most 2000 links, and the cut is reported; - wireguard outbounds from a subscription always run on the userspace TUN; - editing a subscription schedules an Xray reload like delete and move do.
100 lines
3.5 KiB
Go
100 lines
3.5 KiB
Go
package controller
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"gorm.io/gorm"
|
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database/dbtest"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/web/locale"
|
|
)
|
|
|
|
func TestWarpIntervalReportsClockPersistenceFailure(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
dbDir := t.TempDir()
|
|
t.Setenv("XUI_DB_FOLDER", dbDir)
|
|
dbtest.InitDB(t, filepath.Join(dbDir, "x-ui.db"))
|
|
db := database.GetDB()
|
|
for _, setting := range []*model.Setting{
|
|
{Key: "warpUpdateInterval", Value: "0"},
|
|
{Key: "warpLastUpdate", Value: "0"},
|
|
} {
|
|
if err := db.Create(setting).Error; err != nil {
|
|
t.Fatalf("seed %s: %v", setting.Key, err)
|
|
}
|
|
}
|
|
|
|
const callback = "test:fail_warp_clock_update"
|
|
errInjected := errors.New("injected WARP clock persistence failure")
|
|
if err := db.Callback().Update().Before("gorm:update").Register(callback, func(tx *gorm.DB) {
|
|
setting, ok := tx.Statement.Model.(*model.Setting)
|
|
if ok && setting.Key == "warpLastUpdate" {
|
|
tx.AddError(errInjected)
|
|
}
|
|
}); err != nil {
|
|
t.Fatalf("register update callback: %v", err)
|
|
}
|
|
t.Cleanup(func() {
|
|
if err := db.Callback().Update().Remove(callback); err != nil {
|
|
t.Errorf("remove update callback: %v", err)
|
|
}
|
|
})
|
|
|
|
engine := gin.New()
|
|
engine.Use(func(c *gin.Context) {
|
|
c.Set("I18n", func(_ locale.I18nType, key string, _ ...string) string { return key })
|
|
c.Next()
|
|
})
|
|
NewXraySettingController(engine.Group("/panel/api"))
|
|
form := url.Values{"interval": {"7"}}
|
|
req := httptest.NewRequest(http.MethodPost, "/panel/api/xray/warp/interval", strings.NewReader(form.Encode()))
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
w := httptest.NewRecorder()
|
|
engine.ServeHTTP(w, req)
|
|
if !strings.Contains(w.Body.String(), `"success":false`) {
|
|
t.Fatalf("interval update reported success after clock persistence failure: %s", w.Body.String())
|
|
}
|
|
if !strings.Contains(w.Body.String(), errInjected.Error()) {
|
|
t.Fatalf("response omitted clock persistence error: %s", w.Body.String())
|
|
}
|
|
}
|
|
|
|
// enabled and prepend decide which subscription outbounds the core runs, so an edit
|
|
// must schedule a reload like the sibling handlers do.
|
|
func TestUpdateOutboundSubSchedulesXrayReload(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
dbtest.InitDB(t, filepath.Join(t.TempDir(), "x-ui.db"))
|
|
sub := &model.OutboundSubscription{Url: "https://provider.example/sub", TagPrefix: "p-", Enabled: true}
|
|
if err := database.GetDB().Create(sub).Error; err != nil {
|
|
t.Fatalf("seed subscription: %v", err)
|
|
}
|
|
engine := gin.New()
|
|
engine.Use(func(c *gin.Context) {
|
|
c.Set("I18n", func(_ locale.I18nType, key string, _ ...string) string { return key })
|
|
c.Next()
|
|
})
|
|
a := NewXraySettingController(engine.Group("/panel/api"))
|
|
a.XrayService.IsNeedRestartAndSetFalse()
|
|
form := url.Values{"url": {"https://1.1.1.1/sub"}, "enabled": {"false"}}
|
|
req := httptest.NewRequest(http.MethodPost, "/panel/api/xray/outbound-subs/"+strconv.Itoa(sub.Id), strings.NewReader(form.Encode()))
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
rec := httptest.NewRecorder()
|
|
engine.ServeHTTP(rec, req)
|
|
if rec.Code != http.StatusOK || !strings.Contains(rec.Body.String(), `"success":true`) {
|
|
t.Fatalf("update = %d %s", rec.Code, rec.Body.String())
|
|
}
|
|
if !a.XrayService.IsNeedRestartAndSetFalse() {
|
|
t.Fatalf("disabling an outbound subscription did not schedule an Xray reload")
|
|
}
|
|
}
|