Add MTProxy, MASQUE, VPN, Link parser. Update AmneziaWG. Remove Tunneling

This commit is contained in:
Sergei Maklagin
2026-04-29 22:11:30 +03:00
parent 09f9f114aa
commit 04908a6a67
158 changed files with 7994 additions and 2277 deletions

View File

@@ -77,6 +77,7 @@ func UserTableFactory(manager CM.Manager, logger log.Logger) func(ctx *context.C
Options: types.FieldOptions{
{Text: "Hysteria", Value: "hysteria"},
{Text: "Hysteria2", Value: "hysteria2"},
{Text: "MTProxy", Value: "mtproxy"},
{Text: "Trojan", Value: "trojan"},
{Text: "TUIC", Value: "tuic"},
{Text: "VLESS", Value: "vless"},
@@ -183,16 +184,18 @@ func UserTableFactory(manager CM.Manager, logger log.Logger) func(ctx *context.C
FieldOptions(types.FieldOptions{
{Text: "Hysteria", Value: "hysteria"},
{Text: "Hysteria2", Value: "hysteria2"},
{Text: "MTProxy", Value: "mtproxy"},
{Text: "Trojan", Value: "trojan"},
{Text: "TUIC", Value: "tuic"},
{Text: "VLESS", Value: "vless"},
{Text: "VMess", Value: "vmess"},
}).
FieldOnChooseOptionsHide([]string{""}, "inbound").
FieldOnChooseOptionsHide([]string{"", "hysteria", "hysteria2", "shadowsocks", "trojan", "tuic"}, "uuid").
FieldOnChooseOptionsHide([]string{"", "vless", "vmess"}, "password").
FieldOnChooseOptionsHide([]string{"", "hysteria", "hysteria2", "shadowsocks", "trojan", "tuic", "vmess"}, "flow").
FieldOnChooseOptionsHide([]string{"", "hysteria", "hysteria2", "shadowsocks", "trojan", "tuic", "vless"}, "alter_id")
FieldOnChooseOptionsHide([]string{"", "hysteria", "hysteria2", "mtproxy", "shadowsocks", "trojan", "tuic"}, "uuid").
FieldOnChooseOptionsHide([]string{"", "mtproxy", "vless", "vmess"}, "password").
FieldOnChooseOptionsHide([]string{"", "hysteria", "hysteria2", "shadowsocks", "trojan", "tuic", "vless", "vmess"}, "secret").
FieldOnChooseOptionsHide([]string{"", "hysteria", "hysteria2", "mtproxy", "shadowsocks", "trojan", "tuic", "vmess"}, "flow").
FieldOnChooseOptionsHide([]string{"", "hysteria", "hysteria2", "mtproxy", "shadowsocks", "trojan", "tuic", "vless"}, "alter_id")
formList.AddField("Inbound", "inbound", db.Varchar, form.Text).
FieldMust().
FieldDisplayButCanNotEditWhenUpdate().
@@ -203,6 +206,7 @@ func UserTableFactory(manager CM.Manager, logger log.Logger) func(ctx *context.C
})
formList.AddField("UUID", "uuid", db.Varchar, form.Text)
formList.AddField("Password", "password", db.Varchar, form.Text)
formList.AddField("Secret", "secret", db.Varchar, form.Text)
formList.AddField("Flow", "flow", db.Varchar, form.SelectSingle).
FieldOptions(types.FieldOptions{
{Text: "xtls-rprx-vision", Value: "xtls-rprx-vision"},
@@ -233,6 +237,7 @@ func UserTableFactory(manager CM.Manager, logger log.Logger) func(ctx *context.C
Inbound: values.Get("inbound"),
UUID: values.Get("uuid"),
Password: values.Get("password"),
Secret: values.Get("secret"),
Flow: values.Get("flow"),
AlterID: alterId,
})
@@ -269,6 +274,7 @@ func UserTableFactory(manager CM.Manager, logger log.Logger) func(ctx *context.C
_, err = manager.UpdateUser(id, CM.UserUpdate{
UUID: values.Get("uuid"),
Password: values.Get("password"),
Secret: values.Get("secret"),
Flow: values.Get("flow"),
AlterID: alterId,
})

View File

@@ -48,6 +48,7 @@ type User struct {
Inbound string `json:"inbound" validate:"required"`
UUID string `json:"uuid" validate:"required"`
Password string `json:"password" validate:"required"`
Secret string `json:"secret" validate:"required"`
Flow string `json:"flow" validate:"required"`
AlterID int `json:"alter_id" validate:"required"`
CreatedAt time.Time `json:"created_at" validate:"required"`
@@ -57,10 +58,11 @@ type User struct {
type UserCreate struct {
SquadIDs []int `json:"squad_ids" validate:"required"`
Username string `json:"username" validate:"required"`
Type string `json:"type" validate:"required,oneof=hysteria hysteria2 trojan tuic vless vmess"`
Type string `json:"type" validate:"required,oneof=hysteria hysteria2 mtproxy trojan tuic vless vmess"`
Inbound string `json:"inbound" validate:"required"`
UUID string `json:"uuid" validate:"omitempty,uuid4"`
Password string `json:"password" validate:"omitempty"`
Secret string `json:"secret" validate:"omitempty"`
Flow string `json:"flow" validate:"omitempty"`
AlterID int `json:"alter_id" validate:"omitempty"`
}
@@ -68,6 +70,7 @@ type UserCreate struct {
type UserUpdate struct {
UUID string `json:"uuid" validate:"omitempty,uuid4"`
Password string `json:"password" validate:"omitempty"`
Secret string `json:"secret" validate:"omitempty"`
Flow string `json:"flow" validate:"omitempty"`
AlterID int `json:"alter_id" validate:"omitempty"`
}
@@ -75,6 +78,7 @@ type UserUpdate struct {
type BaseUser struct {
UUID string `json:"uuid" validate:"omitempty,uuid4"`
Password string `json:"password" validate:"omitempty"`
Secret string `json:"secret" validate:"omitempty"`
Flow string `json:"flow" validate:"omitempty"`
AlterID int `json:"alter_id" validate:"omitempty"`
}

View File

@@ -40,6 +40,7 @@ var migrations = map[string]string{
inbound TEXT NOT NULL,
uuid TEXT NOT NULL,
password TEXT NOT NULL,
secret TEXT NOT NULL,
flow TEXT NOT NULL,
alter_id INTEGER NOT NULL,
created_at TIMESTAMP NOT NULL,

View File

@@ -391,12 +391,13 @@ func (r *PostgreSQLRepository) CreateUser(user constant.UserCreate) (constant.Us
inbound,
uuid,
password,
secret,
flow,
alter_id,
created_at,
updated_at
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
RETURNING
id,
username,
@@ -404,6 +405,7 @@ func (r *PostgreSQLRepository) CreateUser(user constant.UserCreate) (constant.Us
inbound,
uuid,
password,
secret,
flow,
alter_id,
created_at,
@@ -414,6 +416,7 @@ func (r *PostgreSQLRepository) CreateUser(user constant.UserCreate) (constant.Us
user.Inbound,
user.UUID,
user.Password,
user.Secret,
user.Flow,
user.AlterID,
now,
@@ -425,11 +428,15 @@ func (r *PostgreSQLRepository) CreateUser(user constant.UserCreate) (constant.Us
&u.Inbound,
&u.UUID,
&u.Password,
&u.Secret,
&u.Flow,
&u.AlterID,
&u.CreatedAt,
&u.UpdatedAt,
)
if err != nil {
return u, err
}
rows := make([][]any, len(user.SquadIDs))
for i, squadID := range user.SquadIDs {
rows[i] = []any{u.ID, squadID}
@@ -465,6 +472,7 @@ func (r *PostgreSQLRepository) GetUsers(filters map[string][]string) ([]constant
"inbound",
"uuid",
"password",
"secret",
"flow",
"alter_id",
"created_at",
@@ -495,6 +503,7 @@ func (r *PostgreSQLRepository) GetUsers(filters map[string][]string) ([]constant
&u.Inbound,
&u.UUID,
&u.Password,
&u.Secret,
&u.Flow,
&u.AlterID,
&u.CreatedAt,
@@ -539,6 +548,7 @@ func (r *PostgreSQLRepository) GetUser(id int) (constant.User, error) {
inbound,
uuid,
password,
secret,
flow,
alter_id,
created_at,
@@ -553,6 +563,7 @@ func (r *PostgreSQLRepository) GetUser(id int) (constant.User, error) {
&u.Inbound,
&u.UUID,
&u.Password,
&u.Secret,
&u.Flow,
&u.AlterID,
&u.CreatedAt,
@@ -568,10 +579,11 @@ func (r *PostgreSQLRepository) UpdateUser(id int, user constant.UserUpdate) (con
SET
uuid = $1,
password = $2,
flow = $3,
alter_id = $4,
updated_at = $5
WHERE id = $6
secret = $3,
flow = $4,
alter_id = $5,
updated_at = $6
WHERE id = $7
RETURNING
id,
ARRAY(
@@ -584,6 +596,7 @@ func (r *PostgreSQLRepository) UpdateUser(id int, user constant.UserUpdate) (con
inbound,
uuid,
password,
secret,
flow,
alter_id,
created_at,
@@ -591,6 +604,7 @@ func (r *PostgreSQLRepository) UpdateUser(id int, user constant.UserUpdate) (con
`,
user.UUID,
user.Password,
user.Secret,
user.Flow,
user.AlterID,
time.Now(),
@@ -603,6 +617,7 @@ func (r *PostgreSQLRepository) UpdateUser(id int, user constant.UserUpdate) (con
&u.Inbound,
&u.UUID,
&u.Password,
&u.Secret,
&u.Flow,
&u.AlterID,
&u.CreatedAt,
@@ -628,6 +643,7 @@ func (r *PostgreSQLRepository) DeleteUser(id int) (constant.User, error) {
inbound,
uuid,
password,
secret,
flow,
alter_id,
created_at,
@@ -640,6 +656,7 @@ func (r *PostgreSQLRepository) DeleteUser(id int) (constant.User, error) {
&u.Inbound,
&u.UUID,
&u.Password,
&u.Secret,
&u.Flow,
&u.AlterID,
&u.CreatedAt,

View File

@@ -10,7 +10,7 @@ import (
"github.com/go-playground/validator/v10"
"github.com/gofrs/uuid/v5"
"github.com/patrickmn/go-cache"
"github.com/patrickmn/go-cache/v2"
"github.com/sagernet/sing-box/adapter"
boxService "github.com/sagernet/sing-box/adapter/service"
C "github.com/sagernet/sing-box/constant"
@@ -32,7 +32,7 @@ type Service struct {
repository constant.Repository
nodes map[string]constant.ConnectedNode
limiterLocks map[int]map[string]*cache.Cache
limiterLocks map[int]map[string]*cache.Cache[string, struct{}]
userValidator *validator.Validate
defaultValidator *validator.Validate
@@ -79,6 +79,10 @@ func NewService(ctx context.Context, logger log.ContextLogger, tag string, optio
if user.Password == "" {
sl.ReportError(user.Password, "password", "Password", "required", "")
}
case "mtproxy":
if user.Secret == "" {
sl.ReportError(user.Secret, "secret", "Secret", "required", "")
}
}
}, constant.UserCreate{})
return &Service{
@@ -87,7 +91,7 @@ func NewService(ctx context.Context, logger log.ContextLogger, tag string, optio
logger: logger,
repository: repository,
nodes: make(map[string]constant.ConnectedNode, 0),
limiterLocks: make(map[int]map[string]*cache.Cache),
limiterLocks: make(map[int]map[string]*cache.Cache[string, struct{}]),
userValidator: userValidator,
defaultValidator: validator.New(),
}, nil
@@ -519,7 +523,7 @@ func (s *Service) AcquireLock(limiterId int, id string) (string, error) {
}
locks, ok := s.limiterLocks[limiterId]
if !ok {
locks = make(map[string]*cache.Cache)
locks = make(map[string]*cache.Cache[string, struct{}])
s.limiterLocks[limiter.ID] = locks
}
lock, ok := locks[id]
@@ -527,8 +531,8 @@ func (s *Service) AcquireLock(limiterId int, id string) (string, error) {
if len(locks) == int(limiter.Count) {
return "", E.New("not enough free locks")
}
lock = cache.New(time.Second*30, time.Second)
lock.OnEvicted(func(_ string, _ interface{}) {
lock = cache.New[string, struct{}](time.Second*30, time.Second)
lock.OnEvicted(func(_ string, _ struct{}) {
s.connLockMtx.Lock()
defer s.connLockMtx.Unlock()
if lock.ItemCount() == 0 {
@@ -541,7 +545,7 @@ func (s *Service) AcquireLock(limiterId int, id string) (string, error) {
if err != nil {
return "", err
}
lock.SetDefault(handleID.String(), new(struct{}))
lock.SetDefault(handleID.String(), struct{}{})
return handleID.String(), nil
}
@@ -556,7 +560,7 @@ func (s *Service) RefreshLock(limiterId int, id string, handleId string) error {
if !ok {
return E.New("lock not found")
}
err := lock.Replace(handleId, new(struct{}), time.Second*30)
err := lock.Replace(handleId, struct{}{}, time.Second*30)
return err
}

View File

@@ -0,0 +1,88 @@
package inbound
import (
"sync"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/protocol/mtproxy"
CM "github.com/sagernet/sing-box/service/manager/constant"
"github.com/sagernet/sing-box/service/node/constant"
)
type MTProxyManager struct {
access sync.Mutex
inbounds map[string]*MTProxyUserManager
}
func NewMTProxyManager() *MTProxyManager {
return &MTProxyManager{
inbounds: make(map[string]*MTProxyUserManager),
}
}
func (m *MTProxyManager) AddUserManager(inbound adapter.Inbound) error {
m.access.Lock()
defer m.access.Unlock()
m.inbounds[inbound.Tag()] = &MTProxyUserManager{
inbound: inbound.(*mtproxy.Inbound),
usersMap: make(map[string]option.MTProxyUser),
}
return nil
}
func (m *MTProxyManager) GetUserManager(tag string) (constant.UserManager, bool) {
m.access.Lock()
defer m.access.Unlock()
inbound, ok := m.inbounds[tag]
return inbound, ok
}
func (m *MTProxyManager) GetUserManagerTags() []string {
m.access.Lock()
defer m.access.Unlock()
tags := make([]string, 0, len(m.inbounds))
for tag, _ := range m.inbounds {
tags = append(tags, tag)
}
return tags
}
type MTProxyUserManager struct {
inbound *mtproxy.Inbound
usersMap map[string]option.MTProxyUser
mtx sync.Mutex
}
func (i *MTProxyUserManager) postUpdate() {
users := make([]option.MTProxyUser, 0, len(i.usersMap))
for _, user := range i.usersMap {
users = append(users, user)
}
i.inbound.UpdateUsers(users)
}
func (i *MTProxyUserManager) UpdateUser(user CM.User) {
i.mtx.Lock()
defer i.mtx.Unlock()
i.usersMap[user.Username] = option.MTProxyUser{Name: user.Username, Secret: user.Secret}
i.postUpdate()
}
func (i *MTProxyUserManager) UpdateUsers(users []CM.User) {
i.mtx.Lock()
defer i.mtx.Unlock()
clear(i.usersMap)
for _, user := range users {
i.usersMap[user.Username] = option.MTProxyUser{Name: user.Username, Secret: user.Secret}
}
i.postUpdate()
}
func (i *MTProxyUserManager) DeleteUser(username string) {
i.mtx.Lock()
defer i.mtx.Unlock()
delete(i.usersMap, username)
i.postUpdate()
}