From d8523bbdac4a65e3e33bca6d1a50a58f33804f52 Mon Sep 17 00:00:00 2001 From: mhsanaei Date: Tue, 14 Oct 2025 22:03:17 +0200 Subject: [PATCH 1/5] fix(import): prevent sqlite disk I/O error by validating temp DB then swapping --- database/db.go | 27 +++++++++++++++++++++++++++ web/service/server.go | 25 +++++++++++++++++++------ 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/database/db.go b/database/db.go index 6de81d79..6b579dd9 100644 --- a/database/db.go +++ b/database/db.go @@ -4,6 +4,7 @@ package database import ( "bytes" + "errors" "io" "io/fs" "log" @@ -199,3 +200,29 @@ func Checkpoint() error { } return nil } + +// ValidateSQLiteDB opens the provided sqlite DB path with a throw-away connection +// and runs a PRAGMA integrity_check to ensure the file is structurally sound. +// It does not mutate global state or run migrations. +func ValidateSQLiteDB(dbPath string) error { + if _, err := os.Stat(dbPath); err != nil { // file must exist + return err + } + gdb, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{Logger: logger.Discard}) + if err != nil { + return err + } + sqlDB, err := gdb.DB() + if err != nil { + return err + } + defer sqlDB.Close() + var res string + if err := gdb.Raw("PRAGMA integrity_check;").Scan(&res).Error; err != nil { + return err + } + if res != "ok" { + return errors.New("sqlite integrity check failed: " + res) + } + return nil +} diff --git a/web/service/server.go b/web/service/server.go index eb261c88..b7cfc3a7 100644 --- a/web/service/server.go +++ b/web/service/server.go @@ -942,13 +942,26 @@ func (s *ServerService) ImportDB(file multipart.File) error { return common.NewErrorf("Error saving db: %v", err) } - // Check if we can init the db or not - if err = database.InitDB(tempPath); err != nil { - return common.NewErrorf("Error checking db: %v", err) + // Close temp file before opening via sqlite + if err = tempFile.Close(); err != nil { + return common.NewErrorf("Error closing temporary db file: %v", err) + } + tempFile = nil + + // Validate integrity (no migrations / side effects) + if err = database.ValidateSQLiteDB(tempPath); err != nil { + return common.NewErrorf("Invalid or corrupt db file: %v", err) } - // Stop Xray - s.StopXrayService() + // Stop Xray (ignore error but log) + if errStop := s.StopXrayService(); errStop != nil { + logger.Warningf("Failed to stop Xray before DB import: %v", errStop) + } + + // Close existing DB to release file locks (especially on Windows) + if errClose := database.CloseDB(); errClose != nil { + logger.Warningf("Failed to close existing DB before replacement: %v", errClose) + } // Backup the current database for fallback fallbackPath := fmt.Sprintf("%s.backup", config.GetDBPath()) @@ -983,7 +996,7 @@ func (s *ServerService) ImportDB(file multipart.File) error { return common.NewErrorf("Error moving db file: %v", err) } - // Migrate DB + // Open & migrate new DB if err = database.InitDB(config.GetDBPath()); err != nil { if errRename := os.Rename(fallbackPath, config.GetDBPath()); errRename != nil { return common.NewErrorf("Error migrating db and restoring fallback: %v", errRename) From 2b2ed3349a081ee0c50041fcf7e01db4760c268e Mon Sep 17 00:00:00 2001 From: mhsanaei Date: Wed, 15 Oct 2025 11:40:04 +0200 Subject: [PATCH 2/5] Xray-core v25.10.15 --- .github/workflows/release.yml | 4 +-- DockerInit.sh | 2 +- go.mod | 32 +++++++++---------- go.sum | 60 +++++++++++++++++------------------ 4 files changed, 49 insertions(+), 49 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7d56cfdf..08112c7b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -85,7 +85,7 @@ jobs: cd x-ui/bin # Download dependencies - Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v25.9.11/" + Xray_URL="https://github.com/XTLS/Xray-core/releases/download/v25.10.15/" if [ "${{ matrix.platform }}" == "amd64" ]; then wget -q ${Xray_URL}Xray-linux-64.zip unzip Xray-linux-64.zip @@ -183,7 +183,7 @@ jobs: cd x-ui\bin # Download Xray for Windows - $Xray_URL = "https://github.com/XTLS/Xray-core/releases/download/v25.9.11/" + $Xray_URL = "https://github.com/XTLS/Xray-core/releases/download/v25.10.15/" Invoke-WebRequest -Uri "${Xray_URL}Xray-windows-64.zip" -OutFile "Xray-windows-64.zip" Expand-Archive -Path "Xray-windows-64.zip" -DestinationPath . Remove-Item "Xray-windows-64.zip" diff --git a/DockerInit.sh b/DockerInit.sh index db391359..fb603fb8 100755 --- a/DockerInit.sh +++ b/DockerInit.sh @@ -27,7 +27,7 @@ case $1 in esac mkdir -p build/bin cd build/bin -wget -q "https://github.com/XTLS/Xray-core/releases/download/v25.9.11/Xray-linux-${ARCH}.zip" +wget -q "https://github.com/XTLS/Xray-core/releases/download/v25.10.15/Xray-linux-${ARCH}.zip" unzip "Xray-linux-${ARCH}.zip" rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat mv xray "xray-linux-${FNAME}" diff --git a/go.mod b/go.mod index 4040c87a..816d05a2 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,9 @@ module github.com/mhsanaei/3x-ui/v2 -go 1.25.1 +go 1.25.2 require ( - github.com/gin-contrib/gzip v1.2.3 + github.com/gin-contrib/gzip v1.2.4 github.com/gin-contrib/sessions v1.0.4 github.com/gin-gonic/gin v1.11.0 github.com/go-ldap/ldap/v3 v3.4.12 @@ -17,13 +17,13 @@ require ( github.com/robfig/cron/v3 v3.0.1 github.com/shirou/gopsutil/v4 v4.25.9 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e - github.com/valyala/fasthttp v1.66.0 + github.com/valyala/fasthttp v1.67.0 github.com/xlzd/gotp v0.1.0 - github.com/xtls/xray-core v1.250911.0 + github.com/xtls/xray-core v1.250911.1-0.20251015080723-b69a376aa1b6 go.uber.org/atomic v1.11.0 - golang.org/x/crypto v0.42.0 - golang.org/x/sys v0.36.0 - golang.org/x/text v0.29.0 + golang.org/x/crypto v0.43.0 + golang.org/x/sys v0.37.0 + golang.org/x/text v0.30.0 google.golang.org/grpc v1.76.0 gorm.io/driver/sqlite v1.6.0 gorm.io/gorm v1.31.0 @@ -61,7 +61,7 @@ require ( github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/kr/text v0.2.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect - github.com/lufia/plan9stats v0.0.0-20250827001030-24949be3fa54 // indirect + github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-sqlite3 v1.14.32 // indirect github.com/miekg/dns v1.1.68 // indirect @@ -71,7 +71,7 @@ require ( github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/quic-go/qpack v0.5.1 // indirect github.com/quic-go/quic-go v0.55.0 // indirect - github.com/refraction-networking/utls v1.8.0 // indirect + github.com/refraction-networking/utls v1.8.1 // indirect github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/sagernet/sing v0.7.12 // indirect @@ -86,18 +86,18 @@ require ( github.com/valyala/fastjson v1.6.4 // indirect github.com/vishvananda/netlink v1.3.1 // indirect github.com/vishvananda/netns v0.0.5 // indirect - github.com/xtls/reality v0.0.0-20251005124704-8f4f0a188196 // indirect + github.com/xtls/reality v0.0.0-20251014195629-e4eec4520535 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect - golang.org/x/arch v0.21.0 // indirect - golang.org/x/mod v0.28.0 // indirect - golang.org/x/net v0.44.0 // indirect + golang.org/x/arch v0.22.0 // indirect + golang.org/x/mod v0.29.0 // indirect + golang.org/x/net v0.46.0 // indirect golang.org/x/sync v0.17.0 // indirect - golang.org/x/time v0.13.0 // indirect - golang.org/x/tools v0.37.0 // indirect + golang.org/x/time v0.14.0 // indirect + golang.org/x/tools v0.38.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251006185510-65f7160b3a87 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251014184007-4626949a642f // indirect google.golang.org/protobuf v1.36.10 // indirect gvisor.dev/gvisor v0.0.0-20250503011706-39ed1f5ac29c // indirect lukechampine.com/blake3 v1.4.1 // indirect diff --git a/go.sum b/go.sum index 05f5cc41..d3e85890 100644 --- a/go.sum +++ b/go.sum @@ -29,8 +29,8 @@ github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIp github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4= github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I= -github.com/gin-contrib/gzip v1.2.3 h1:dAhT722RuEG330ce2agAs75z7yB+NKvX/ZM1r8w0u2U= -github.com/gin-contrib/gzip v1.2.3/go.mod h1:ad72i4Bzmaypk8M762gNXa2wkxxjbz0icRNnuLJ9a/c= +github.com/gin-contrib/gzip v1.2.4 h1:yNz4EhPC2kHSZJD1oc1zwp7MLEhEZ3goQeGM3a1b6jU= +github.com/gin-contrib/gzip v1.2.4/go.mod h1:aomRgR7ftdZV3uWY0gW/m8rChfxau0n8YVvwlOHONzw= github.com/gin-contrib/sessions v1.0.4 h1:ha6CNdpYiTOK/hTp05miJLbpTSNfOnFg5Jm2kbcqy8U= github.com/gin-contrib/sessions v1.0.4/go.mod h1:ccmkrb2z6iU2osiAHZG3x3J4suJK+OU27oqzlWOqQgs= github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= @@ -117,8 +117,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/lufia/plan9stats v0.0.0-20250827001030-24949be3fa54 h1:mFWunSatvkQQDhpdyuFAYwyAan3hzCuma+Pz8sqvOfg= -github.com/lufia/plan9stats v0.0.0-20250827001030-24949be3fa54/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= +github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k= +github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= @@ -150,8 +150,8 @@ github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk= github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U= -github.com/refraction-networking/utls v1.8.0 h1:L38krhiTAyj9EeiQQa2sg+hYb4qwLCqdMcpZrRfbONE= -github.com/refraction-networking/utls v1.8.0/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM= +github.com/refraction-networking/utls v1.8.1 h1:yNY1kapmQU8JeM1sSw2H2asfTIwWxIkrMJI0pRUOCAo= +github.com/refraction-networking/utls v1.8.1/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= @@ -190,8 +190,8 @@ github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.66.0 h1:M87A0Z7EayeyNaV6pfO3tUTUiYO0dZfEJnRGXTVNuyU= -github.com/valyala/fasthttp v1.66.0/go.mod h1:Y4eC+zwoocmXSVCB1JmhNbYtS7tZPRI2ztPB72EVObs= +github.com/valyala/fasthttp v1.67.0 h1:tqKlJMUP6iuNG8hGjK/s9J4kadH7HLV4ijEcPGsezac= +github.com/valyala/fasthttp v1.67.0/go.mod h1:qYSIpqt/0XNmShgo/8Aq8E3UYWVVwNS2QYmzd8WIEPM= github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0= @@ -200,10 +200,10 @@ github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zd github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/xlzd/gotp v0.1.0 h1:37blvlKCh38s+fkem+fFh7sMnceltoIEBYTVXyoa5Po= github.com/xlzd/gotp v0.1.0/go.mod h1:ndLJ3JKzi3xLmUProq4LLxCuECL93dG9WASNLpHz8qg= -github.com/xtls/reality v0.0.0-20251005124704-8f4f0a188196 h1:jb1y+Rm6UBW/CEV0FehsKlQ/2dnLsQjyUjn3UfWwbic= -github.com/xtls/reality v0.0.0-20251005124704-8f4f0a188196/go.mod h1:XxvnCCgBee4WWE0bc4E+a7wbk8gkJ/rS0vNVNtC5qp0= -github.com/xtls/xray-core v1.250911.0 h1:KMN8zVurAjHFixiUoFV/jwmzYohf27dQRntjV+8LQno= -github.com/xtls/xray-core v1.250911.0/go.mod h1:LkqA/BFVtPS2e5fRzg/bkYas9nQu4Uztlx+/fjlLM9k= +github.com/xtls/reality v0.0.0-20251014195629-e4eec4520535 h1:nwobseOLLRtdbP6z7Z2aVI97u8ZptTgD1ofovhAKmeU= +github.com/xtls/reality v0.0.0-20251014195629-e4eec4520535/go.mod h1:vbHCV/3VWUvy1oKvTxxWJRPEWSeR1sYgQHIh6u/JiZQ= +github.com/xtls/xray-core v1.250911.1-0.20251015080723-b69a376aa1b6 h1:gwgJxWb9OABUJAYxiS33nQzk3MRVjidzBnHBrzKnxOw= +github.com/xtls/xray-core v1.250911.1-0.20251015080723-b69a376aa1b6/go.mod h1:72ZU/srfutsNPmw9y8SCGRy0iccvshIRk8BNGR8D2Ik= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= @@ -226,14 +226,14 @@ go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= -golang.org/x/arch v0.21.0 h1:iTC9o7+wP6cPWpDWkivCvQFGAHDQ59SrSxsLPcnkArw= -golang.org/x/arch v0.21.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= -golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= -golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= -golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= -golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= -golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI= +golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= +golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= +golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= +golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= +golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= +golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -242,22 +242,22 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= -golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= -golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= -golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI= -golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= -golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= -golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= +golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= +golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb h1:whnFRlWMcXI9d+ZbWg+4sHnLp52d5yiIPUxMBSt4X9A= golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb/go.mod h1:rpwXGsirqLqN2L0JDJQlwOboGHmptD5ZD6T2VmcqhTw= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251006185510-65f7160b3a87 h1:WgGZrMngVRRve7T3P5gbXdmedSmUpkf8uIUu1fg+biY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251006185510-65f7160b3a87/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251014184007-4626949a642f h1:1FTH6cpXFsENbPR5Bu8NQddPSaUUE6NA2XdZdDSAJK4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251014184007-4626949a642f/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A= google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c= google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= From 01d4a7488dac0bb81dc8f31ec29d5c83f254b29e Mon Sep 17 00:00:00 2001 From: mhsanaei Date: Wed, 15 Oct 2025 11:40:40 +0200 Subject: [PATCH 3/5] v2.8.5 --- config/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/version b/config/version index 0409c163..7f04bb11 100644 --- a/config/version +++ b/config/version @@ -1 +1 @@ -2.8.4 \ No newline at end of file +2.8.5 \ No newline at end of file From 713a7328f60eec94c56b7ad6d17a942dffcfd944 Mon Sep 17 00:00:00 2001 From: mhsanaei Date: Tue, 21 Oct 2025 13:02:55 +0200 Subject: [PATCH 4/5] gofmt --- util/ldap/ldap.go | 238 ++++++++++++++++++++--------------------- web/entity/entity.go | 44 ++++---- web/service/inbound.go | 25 +++-- web/service/setting.go | 80 +++++++------- web/service/user.go | 62 +++++------ 5 files changed, 223 insertions(+), 226 deletions(-) diff --git a/util/ldap/ldap.go b/util/ldap/ldap.go index 1c7a20e7..795d0e23 100644 --- a/util/ldap/ldap.go +++ b/util/ldap/ldap.go @@ -1,144 +1,142 @@ package ldaputil import ( - "crypto/tls" - "fmt" + "crypto/tls" + "fmt" - "github.com/go-ldap/ldap/v3" + "github.com/go-ldap/ldap/v3" ) type Config struct { - Host string - Port int - UseTLS bool - BindDN string - Password string - BaseDN string - UserFilter string - UserAttr string - FlagField string - TruthyVals []string - Invert bool + Host string + Port int + UseTLS bool + BindDN string + Password string + BaseDN string + UserFilter string + UserAttr string + FlagField string + TruthyVals []string + Invert bool } // FetchVlessFlags returns map[email]enabled func FetchVlessFlags(cfg Config) (map[string]bool, error) { - addr := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port) - var conn *ldap.Conn - var err error - if cfg.UseTLS { - conn, err = ldap.DialTLS("tcp", addr, &tls.Config{InsecureSkipVerify: false}) - } else { - conn, err = ldap.Dial("tcp", addr) - } - if err != nil { - return nil, err - } - defer conn.Close() + addr := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port) + var conn *ldap.Conn + var err error + if cfg.UseTLS { + conn, err = ldap.DialTLS("tcp", addr, &tls.Config{InsecureSkipVerify: false}) + } else { + conn, err = ldap.Dial("tcp", addr) + } + if err != nil { + return nil, err + } + defer conn.Close() - if cfg.BindDN != "" { - if err := conn.Bind(cfg.BindDN, cfg.Password); err != nil { - return nil, err - } - } + if cfg.BindDN != "" { + if err := conn.Bind(cfg.BindDN, cfg.Password); err != nil { + return nil, err + } + } - if cfg.UserFilter == "" { - cfg.UserFilter = "(objectClass=person)" - } - if cfg.UserAttr == "" { - cfg.UserAttr = "mail" - } - // if field not set we fallback to legacy vless_enabled - if cfg.FlagField == "" { - cfg.FlagField = "vless_enabled" - } + if cfg.UserFilter == "" { + cfg.UserFilter = "(objectClass=person)" + } + if cfg.UserAttr == "" { + cfg.UserAttr = "mail" + } + // if field not set we fallback to legacy vless_enabled + if cfg.FlagField == "" { + cfg.FlagField = "vless_enabled" + } - req := ldap.NewSearchRequest( - cfg.BaseDN, - ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, - cfg.UserFilter, - []string{cfg.UserAttr, cfg.FlagField}, - nil, - ) + req := ldap.NewSearchRequest( + cfg.BaseDN, + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, + cfg.UserFilter, + []string{cfg.UserAttr, cfg.FlagField}, + nil, + ) - res, err := conn.Search(req) - if err != nil { - return nil, err - } + res, err := conn.Search(req) + if err != nil { + return nil, err + } - result := make(map[string]bool, len(res.Entries)) - for _, e := range res.Entries { - user := e.GetAttributeValue(cfg.UserAttr) - if user == "" { - continue - } - val := e.GetAttributeValue(cfg.FlagField) - enabled := false - for _, t := range cfg.TruthyVals { - if val == t { - enabled = true - break - } - } - if cfg.Invert { - enabled = !enabled - } - result[user] = enabled - } - return result, nil + result := make(map[string]bool, len(res.Entries)) + for _, e := range res.Entries { + user := e.GetAttributeValue(cfg.UserAttr) + if user == "" { + continue + } + val := e.GetAttributeValue(cfg.FlagField) + enabled := false + for _, t := range cfg.TruthyVals { + if val == t { + enabled = true + break + } + } + if cfg.Invert { + enabled = !enabled + } + result[user] = enabled + } + return result, nil } // AuthenticateUser searches user by cfg.UserAttr and attempts to bind with provided password. func AuthenticateUser(cfg Config, username, password string) (bool, error) { - addr := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port) - var conn *ldap.Conn - var err error - if cfg.UseTLS { - conn, err = ldap.DialTLS("tcp", addr, &tls.Config{InsecureSkipVerify: false}) - } else { - conn, err = ldap.Dial("tcp", addr) - } - if err != nil { - return false, err - } - defer conn.Close() + addr := fmt.Sprintf("%s:%d", cfg.Host, cfg.Port) + var conn *ldap.Conn + var err error + if cfg.UseTLS { + conn, err = ldap.DialTLS("tcp", addr, &tls.Config{InsecureSkipVerify: false}) + } else { + conn, err = ldap.Dial("tcp", addr) + } + if err != nil { + return false, err + } + defer conn.Close() - // Optional initial bind for search - if cfg.BindDN != "" { - if err := conn.Bind(cfg.BindDN, cfg.Password); err != nil { - return false, err - } - } + // Optional initial bind for search + if cfg.BindDN != "" { + if err := conn.Bind(cfg.BindDN, cfg.Password); err != nil { + return false, err + } + } - if cfg.UserFilter == "" { - cfg.UserFilter = "(objectClass=person)" - } - if cfg.UserAttr == "" { - cfg.UserAttr = "uid" - } + if cfg.UserFilter == "" { + cfg.UserFilter = "(objectClass=person)" + } + if cfg.UserAttr == "" { + cfg.UserAttr = "uid" + } - // Build filter to find specific user - filter := fmt.Sprintf("(&%s(%s=%s))", cfg.UserFilter, cfg.UserAttr, ldap.EscapeFilter(username)) - req := ldap.NewSearchRequest( - cfg.BaseDN, - ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 1, 0, false, - filter, - []string{"dn"}, - nil, - ) - res, err := conn.Search(req) - if err != nil { - return false, err - } - if len(res.Entries) == 0 { - return false, nil - } - userDN := res.Entries[0].DN - // Try to bind as the user - if err := conn.Bind(userDN, password); err != nil { - return false, nil - } - return true, nil + // Build filter to find specific user + filter := fmt.Sprintf("(&%s(%s=%s))", cfg.UserFilter, cfg.UserAttr, ldap.EscapeFilter(username)) + req := ldap.NewSearchRequest( + cfg.BaseDN, + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 1, 0, false, + filter, + []string{"dn"}, + nil, + ) + res, err := conn.Search(req) + if err != nil { + return false, err + } + if len(res.Entries) == 0 { + return false, nil + } + userDN := res.Entries[0].DN + // Try to bind as the user + if err := conn.Bind(userDN, password); err != nil { + return false, nil + } + return true, nil } - - diff --git a/web/entity/entity.go b/web/entity/entity.go index de054e2b..42e2df85 100644 --- a/web/entity/entity.go +++ b/web/entity/entity.go @@ -74,30 +74,30 @@ type AllSetting struct { SubJsonFragment string `json:"subJsonFragment" form:"subJsonFragment"` // JSON subscription fragment configuration SubJsonNoises string `json:"subJsonNoises" form:"subJsonNoises"` // JSON subscription noise configuration SubJsonMux string `json:"subJsonMux" form:"subJsonMux"` // JSON subscription mux configuration - SubJsonRules string `json:"subJsonRules" form:"subJsonRules"` - + SubJsonRules string `json:"subJsonRules" form:"subJsonRules"` + // LDAP settings - LdapEnable bool `json:"ldapEnable" form:"ldapEnable"` - LdapHost string `json:"ldapHost" form:"ldapHost"` - LdapPort int `json:"ldapPort" form:"ldapPort"` - LdapUseTLS bool `json:"ldapUseTLS" form:"ldapUseTLS"` - LdapBindDN string `json:"ldapBindDN" form:"ldapBindDN"` - LdapPassword string `json:"ldapPassword" form:"ldapPassword"` - LdapBaseDN string `json:"ldapBaseDN" form:"ldapBaseDN"` - LdapUserFilter string `json:"ldapUserFilter" form:"ldapUserFilter"` - LdapUserAttr string `json:"ldapUserAttr" form:"ldapUserAttr"` // e.g., mail or uid - LdapVlessField string `json:"ldapVlessField" form:"ldapVlessField"` - LdapSyncCron string `json:"ldapSyncCron" form:"ldapSyncCron"` + LdapEnable bool `json:"ldapEnable" form:"ldapEnable"` + LdapHost string `json:"ldapHost" form:"ldapHost"` + LdapPort int `json:"ldapPort" form:"ldapPort"` + LdapUseTLS bool `json:"ldapUseTLS" form:"ldapUseTLS"` + LdapBindDN string `json:"ldapBindDN" form:"ldapBindDN"` + LdapPassword string `json:"ldapPassword" form:"ldapPassword"` + LdapBaseDN string `json:"ldapBaseDN" form:"ldapBaseDN"` + LdapUserFilter string `json:"ldapUserFilter" form:"ldapUserFilter"` + LdapUserAttr string `json:"ldapUserAttr" form:"ldapUserAttr"` // e.g., mail or uid + LdapVlessField string `json:"ldapVlessField" form:"ldapVlessField"` + LdapSyncCron string `json:"ldapSyncCron" form:"ldapSyncCron"` // Generic flag configuration - LdapFlagField string `json:"ldapFlagField" form:"ldapFlagField"` - LdapTruthyValues string `json:"ldapTruthyValues" form:"ldapTruthyValues"` - LdapInvertFlag bool `json:"ldapInvertFlag" form:"ldapInvertFlag"` - LdapInboundTags string `json:"ldapInboundTags" form:"ldapInboundTags"` - LdapAutoCreate bool `json:"ldapAutoCreate" form:"ldapAutoCreate"` - LdapAutoDelete bool `json:"ldapAutoDelete" form:"ldapAutoDelete"` - LdapDefaultTotalGB int `json:"ldapDefaultTotalGB" form:"ldapDefaultTotalGB"` - LdapDefaultExpiryDays int `json:"ldapDefaultExpiryDays" form:"ldapDefaultExpiryDays"` - LdapDefaultLimitIP int `json:"ldapDefaultLimitIP" form:"ldapDefaultLimitIP"` + LdapFlagField string `json:"ldapFlagField" form:"ldapFlagField"` + LdapTruthyValues string `json:"ldapTruthyValues" form:"ldapTruthyValues"` + LdapInvertFlag bool `json:"ldapInvertFlag" form:"ldapInvertFlag"` + LdapInboundTags string `json:"ldapInboundTags" form:"ldapInboundTags"` + LdapAutoCreate bool `json:"ldapAutoCreate" form:"ldapAutoCreate"` + LdapAutoDelete bool `json:"ldapAutoDelete" form:"ldapAutoDelete"` + LdapDefaultTotalGB int `json:"ldapDefaultTotalGB" form:"ldapDefaultTotalGB"` + LdapDefaultExpiryDays int `json:"ldapDefaultExpiryDays" form:"ldapDefaultExpiryDays"` + LdapDefaultLimitIP int `json:"ldapDefaultLimitIP" form:"ldapDefaultLimitIP"` // JSON subscription routing rules } diff --git a/web/service/inbound.go b/web/service/inbound.go index 93414801..66e87a4f 100644 --- a/web/service/inbound.go +++ b/web/service/inbound.go @@ -1569,21 +1569,20 @@ func (s *InboundService) ToggleClientEnableByEmail(clientEmail string) (bool, bo return !clientOldEnabled, needRestart, nil } - // SetClientEnableByEmail sets client enable state to desired value; returns (changed, needRestart, error) func (s *InboundService) SetClientEnableByEmail(clientEmail string, enable bool) (bool, bool, error) { - current, err := s.checkIsEnabledByEmail(clientEmail) - if err != nil { - return false, false, err - } - if current == enable { - return false, false, nil - } - newEnabled, needRestart, err := s.ToggleClientEnableByEmail(clientEmail) - if err != nil { - return false, needRestart, err - } - return newEnabled == enable, needRestart, nil + current, err := s.checkIsEnabledByEmail(clientEmail) + if err != nil { + return false, false, err + } + if current == enable { + return false, false, nil + } + newEnabled, needRestart, err := s.ToggleClientEnableByEmail(clientEmail) + if err != nil { + return false, needRestart, err + } + return newEnabled == enable, needRestart, nil } func (s *InboundService) ResetClientIpLimitByEmail(clientEmail string, count int) (bool, error) { diff --git a/web/service/setting.go b/web/service/setting.go index fa85d58c..c8ce7896 100644 --- a/web/service/setting.go +++ b/web/service/setting.go @@ -74,26 +74,26 @@ var defaultValueMap = map[string]string{ "externalTrafficInformEnable": "false", "externalTrafficInformURI": "", // LDAP defaults - "ldapEnable": "false", - "ldapHost": "", - "ldapPort": "389", - "ldapUseTLS": "false", - "ldapBindDN": "", - "ldapPassword": "", - "ldapBaseDN": "", - "ldapUserFilter": "(objectClass=person)", - "ldapUserAttr": "mail", - "ldapVlessField": "vless_enabled", - "ldapSyncCron": "@every 1m", - "ldapFlagField": "", - "ldapTruthyValues": "true,1,yes,on", - "ldapInvertFlag": "false", - "ldapInboundTags": "", - "ldapAutoCreate": "false", - "ldapAutoDelete": "false", - "ldapDefaultTotalGB": "0", - "ldapDefaultExpiryDays": "0", - "ldapDefaultLimitIP": "0", + "ldapEnable": "false", + "ldapHost": "", + "ldapPort": "389", + "ldapUseTLS": "false", + "ldapBindDN": "", + "ldapPassword": "", + "ldapBaseDN": "", + "ldapUserFilter": "(objectClass=person)", + "ldapUserAttr": "mail", + "ldapVlessField": "vless_enabled", + "ldapSyncCron": "@every 1m", + "ldapFlagField": "", + "ldapTruthyValues": "true,1,yes,on", + "ldapInvertFlag": "false", + "ldapInboundTags": "", + "ldapAutoCreate": "false", + "ldapAutoDelete": "false", + "ldapDefaultTotalGB": "0", + "ldapDefaultExpiryDays": "0", + "ldapDefaultLimitIP": "0", } // SettingService provides business logic for application settings management. @@ -565,83 +565,83 @@ func (s *SettingService) GetIpLimitEnable() (bool, error) { // LDAP exported getters func (s *SettingService) GetLdapEnable() (bool, error) { - return s.getBool("ldapEnable") + return s.getBool("ldapEnable") } func (s *SettingService) GetLdapHost() (string, error) { - return s.getString("ldapHost") + return s.getString("ldapHost") } func (s *SettingService) GetLdapPort() (int, error) { - return s.getInt("ldapPort") + return s.getInt("ldapPort") } func (s *SettingService) GetLdapUseTLS() (bool, error) { - return s.getBool("ldapUseTLS") + return s.getBool("ldapUseTLS") } func (s *SettingService) GetLdapBindDN() (string, error) { - return s.getString("ldapBindDN") + return s.getString("ldapBindDN") } func (s *SettingService) GetLdapPassword() (string, error) { - return s.getString("ldapPassword") + return s.getString("ldapPassword") } func (s *SettingService) GetLdapBaseDN() (string, error) { - return s.getString("ldapBaseDN") + return s.getString("ldapBaseDN") } func (s *SettingService) GetLdapUserFilter() (string, error) { - return s.getString("ldapUserFilter") + return s.getString("ldapUserFilter") } func (s *SettingService) GetLdapUserAttr() (string, error) { - return s.getString("ldapUserAttr") + return s.getString("ldapUserAttr") } func (s *SettingService) GetLdapVlessField() (string, error) { - return s.getString("ldapVlessField") + return s.getString("ldapVlessField") } func (s *SettingService) GetLdapSyncCron() (string, error) { - return s.getString("ldapSyncCron") + return s.getString("ldapSyncCron") } func (s *SettingService) GetLdapFlagField() (string, error) { - return s.getString("ldapFlagField") + return s.getString("ldapFlagField") } func (s *SettingService) GetLdapTruthyValues() (string, error) { - return s.getString("ldapTruthyValues") + return s.getString("ldapTruthyValues") } func (s *SettingService) GetLdapInvertFlag() (bool, error) { - return s.getBool("ldapInvertFlag") + return s.getBool("ldapInvertFlag") } func (s *SettingService) GetLdapInboundTags() (string, error) { - return s.getString("ldapInboundTags") + return s.getString("ldapInboundTags") } func (s *SettingService) GetLdapAutoCreate() (bool, error) { - return s.getBool("ldapAutoCreate") + return s.getBool("ldapAutoCreate") } func (s *SettingService) GetLdapAutoDelete() (bool, error) { - return s.getBool("ldapAutoDelete") + return s.getBool("ldapAutoDelete") } func (s *SettingService) GetLdapDefaultTotalGB() (int, error) { - return s.getInt("ldapDefaultTotalGB") + return s.getInt("ldapDefaultTotalGB") } func (s *SettingService) GetLdapDefaultExpiryDays() (int, error) { - return s.getInt("ldapDefaultExpiryDays") + return s.getInt("ldapDefaultExpiryDays") } func (s *SettingService) GetLdapDefaultLimitIP() (int, error) { - return s.getInt("ldapDefaultLimitIP") + return s.getInt("ldapDefaultLimitIP") } func (s *SettingService) UpdateAllSetting(allSetting *entity.AllSetting) error { diff --git a/web/service/user.go b/web/service/user.go index 87c46bf2..1bde69f6 100644 --- a/web/service/user.go +++ b/web/service/user.go @@ -7,7 +7,7 @@ import ( "github.com/mhsanaei/3x-ui/v2/database/model" "github.com/mhsanaei/3x-ui/v2/logger" "github.com/mhsanaei/3x-ui/v2/util/crypto" - ldaputil "github.com/mhsanaei/3x-ui/v2/util/ldap" + ldaputil "github.com/mhsanaei/3x-ui/v2/util/ldap" "github.com/xlzd/gotp" "gorm.io/gorm" ) @@ -49,38 +49,38 @@ func (s *UserService) CheckUser(username string, password string, twoFactorCode return nil } - // If LDAP enabled and local password check fails, attempt LDAP auth - if !crypto.CheckPasswordHash(user.Password, password) { - ldapEnabled, _ := s.settingService.GetLdapEnable() - if !ldapEnabled { - return nil - } + // If LDAP enabled and local password check fails, attempt LDAP auth + if !crypto.CheckPasswordHash(user.Password, password) { + ldapEnabled, _ := s.settingService.GetLdapEnable() + if !ldapEnabled { + return nil + } - host, _ := s.settingService.GetLdapHost() - port, _ := s.settingService.GetLdapPort() - useTLS, _ := s.settingService.GetLdapUseTLS() - bindDN, _ := s.settingService.GetLdapBindDN() - ldapPass, _ := s.settingService.GetLdapPassword() - baseDN, _ := s.settingService.GetLdapBaseDN() - userFilter, _ := s.settingService.GetLdapUserFilter() - userAttr, _ := s.settingService.GetLdapUserAttr() + host, _ := s.settingService.GetLdapHost() + port, _ := s.settingService.GetLdapPort() + useTLS, _ := s.settingService.GetLdapUseTLS() + bindDN, _ := s.settingService.GetLdapBindDN() + ldapPass, _ := s.settingService.GetLdapPassword() + baseDN, _ := s.settingService.GetLdapBaseDN() + userFilter, _ := s.settingService.GetLdapUserFilter() + userAttr, _ := s.settingService.GetLdapUserAttr() - cfg := ldaputil.Config{ - Host: host, - Port: port, - UseTLS: useTLS, - BindDN: bindDN, - Password: ldapPass, - BaseDN: baseDN, - UserFilter: userFilter, - UserAttr: userAttr, - } - ok, err := ldaputil.AuthenticateUser(cfg, username, password) - if err != nil || !ok { - return nil - } - // On successful LDAP auth, continue 2FA checks below - } + cfg := ldaputil.Config{ + Host: host, + Port: port, + UseTLS: useTLS, + BindDN: bindDN, + Password: ldapPass, + BaseDN: baseDN, + UserFilter: userFilter, + UserAttr: userAttr, + } + ok, err := ldaputil.AuthenticateUser(cfg, username, password) + if err != nil || !ok { + return nil + } + // On successful LDAP auth, continue 2FA checks below + } twoFactorEnable, err := s.settingService.GetTwoFactorEnable() if err != nil { From 0649a15c892ba677bdfa33f2ba8bd4599f3d24c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9=20=D0=A1=D0=B0?= =?UTF-8?q?=D0=B5=D0=BD=D0=BA=D0=BE?= Date: Sun, 26 Oct 2025 15:43:34 +0300 Subject: [PATCH 5/5] feature: setting certs for subscription whule generating --- main.go | 14 ++++++++++++++ web/service/setting.go | 8 ++++++++ 2 files changed, 22 insertions(+) diff --git a/main.go b/main.go index 8ab8b13f..94b23bc8 100644 --- a/main.go +++ b/main.go @@ -321,6 +321,20 @@ func updateCert(publicKey string, privateKey string) { } else { fmt.Println("set certificate private key success") } + + err = settingService.SetSubCertFile(publicKey) + if err != nil { + fmt.Println("set certificate for subscription public key failed:", err) + } else { + fmt.Println("set certificate for subscription public key success") + } + + err = settingService.SetSubKeyFile(privateKey) + if err != nil { + fmt.Println("set certificate for subscription private key failed:", err) + } else { + fmt.Println("set certificate for subscription private key success") + } } else { fmt.Println("both public and private key should be entered.") } diff --git a/web/service/setting.go b/web/service/setting.go index c8ce7896..56db346d 100644 --- a/web/service/setting.go +++ b/web/service/setting.go @@ -479,10 +479,18 @@ func (s *SettingService) GetSubDomain() (string, error) { return s.getString("subDomain") } +func (s *SettingService) SetSubCertFile(subCertFile string) error { + return s.setString("subCertFile", subCertFile) +} + func (s *SettingService) GetSubCertFile() (string, error) { return s.getString("subCertFile") } +func (s *SettingService) SetSubKeyFile(subKeyFile string) error { + return s.setString("subKeyFile", subKeyFile) +} + func (s *SettingService) GetSubKeyFile() (string, error) { return s.getString("subKeyFile") }