feat(logs): show which AmneziaWG client an access-log line belongs to

The dokodemo-door TPROXY bridge every AmneziaWG peer's traffic is routed
through has no per-user identity, so Xray's own access log never carries an
"email:" token for these lines -- the Access Logs modal showed a blank
Email column for every in-*-udp row, even though every other protocol's
rows show the client normally.

The peer's decapsulated tunnel IP does survive as the log's "from" address,
and that IP deterministically maps to exactly one configured peer. Builds a
"<inbound tag>|<ip>" -> email index from the same AmneziaWG inbounds already
parsed elsewhere (amneziawg.InstanceFromInbound), and fills in Email from it
whenever the raw log line didn't have one.
This commit is contained in:
Kuzz007
2026-07-27 23:36:00 +03:00
parent 89f17f6fef
commit 5b58369af2
2 changed files with 84 additions and 1 deletions
+42 -1
View File
@@ -1,6 +1,12 @@
package service
import "testing"
import (
"encoding/json"
"testing"
"github.com/mhsanaei/3x-ui/v3/internal/amneziawg"
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
)
func TestParseAccessLogFields(t *testing.T) {
malformed := []string{
@@ -43,3 +49,38 @@ func TestParseAccessLogFields(t *testing.T) {
t.Error("DateTime was not parsed from a well-formed line")
}
}
func TestAmneziawgEmailIndex(t *testing.T) {
settings, err := json.Marshal(amneziawg.InboundSettings{
Server: &amneziawg.ServerSettings{
PrivateKey: "priv",
PublicKey: "pub",
SubnetIP: "10.8.1.0",
SubnetCIDR: 24,
},
Clients: []model.Client{
{Email: "router@x", Enable: true, PublicKey: "pubA", AllowedIPs: []string{"10.8.1.3/32"}},
{Email: "disabled@x", Enable: false, PublicKey: "pubB", AllowedIPs: []string{"10.8.1.4/32"}},
},
})
if err != nil {
t.Fatalf("marshal settings: %v", err)
}
inbounds := []*model.Inbound{
{Id: 10, Tag: "in-443-udp", Protocol: model.AmneziaWG, Port: 443, Settings: string(settings)},
{Id: 11, Tag: "in-443-tcp", Protocol: model.VLESS, Port: 443, Settings: "{}"},
}
index := amneziawgEmailIndex(inbounds)
if got := index["in-443-udp|10.8.1.3"]; got != "router@x" {
t.Errorf(`index["in-443-udp|10.8.1.3"] = %q, want "router@x"`, got)
}
if _, ok := index["in-443-udp|10.8.1.4"]; ok {
t.Error("a disabled peer must not appear in the index")
}
if len(index) != 1 {
t.Errorf("expected exactly 1 entry (non-amneziawg inbound and disabled peer excluded), got %d: %+v", len(index), index)
}
}