Compare commits

...

10 Commits

11 changed files with 54 additions and 83 deletions

View File

@ -114,8 +114,8 @@ proxy services. Built with Go, it emphasizes performance, security, and scalabil
4. **Pull from Docker Hub** (after CI/CD publishes):
```bash
docker pull yourusername/ppanel-server:latest
docker run --rm -p 8080:8080 yourusername/ppanel-server:latest
docker pull ppanel/ppanel-server:latest
docker run --rm -p 8080:8080 ppanel/ppanel-server:latest
```
## 📖 API Documentation

View File

@ -29,10 +29,12 @@ func NewPreviewSubscribeTemplateLogic(ctx context.Context, svcCtx *svc.ServiceCo
}
func (l *PreviewSubscribeTemplateLogic) PreviewSubscribeTemplate(req *types.PreviewSubscribeTemplateRequest) (resp *types.PreviewSubscribeTemplateResponse, err error) {
enable := true
_, servers, err := l.svcCtx.NodeModel.FilterNodeList(l.ctx, &node.FilterNodeParams{
Page: 1,
Size: 1000,
Preload: true,
Enabled: &enable,
})
if err != nil {
l.Errorf("[PreviewSubscribeTemplateLogic] FindAllServer error: %v", err.Error())

View File

@ -230,7 +230,7 @@ func (l *MigrateServerNodeLogic) adapterServer(info *server.Server) (*node.Serve
return nil, err
}
protocol := node.Protocol{
Type: "hysteria2",
Type: "hysteria",
Port: uint16(src.Port),
HopPorts: src.HopPorts,
HopInterval: src.HopInterval,

View File

@ -8,7 +8,10 @@ const (
Trojan = "trojan"
AnyTLS = "anytls"
Tuic = "tuic"
Hysteria2 = "hysteria2"
Hysteria = "hysteria"
// Deprecated: Hysteria2 is deprecated, use Hysteria instead
// TODO: remove in future versions
Hysteria2 = "hysteria2"
)
type SecurityConfig struct {

View File

@ -57,6 +57,11 @@ func (l *GetServerConfigLogic) GetServerConfig(req *types.GetServerConfigRequest
return nil, err
}
// compatible hysteria2, remove in future versions
if req.Protocol == Hysteria2 {
req.Protocol = Hysteria
}
protocols, err := data.UnmarshalProtocols()
if err != nil {
return nil, err
@ -209,7 +214,7 @@ func (l *GetServerConfigLogic) compatible(config node.Protocol) map[string]inter
RealityShortId: config.RealityShortId,
},
}
case Hysteria2:
case Hysteria:
result = Hysteria2Node{
Port: config.Port,
HopPorts: config.HopPorts,

View File

@ -249,8 +249,9 @@ func (l *SubscribeLogic) createExpiredServers() []*node.Node {
Port: 18080,
Address: "127.0.0.1",
Server: &node.Server{
Id: 1,
Name: "Subscribe Expired",
Protocols: "[{\"type:\"\"shadowsocks\",\"cipher\":\"aes-256-gcm\",\"port\":1}]",
Protocols: "[{\"type\":\"shadowsocks\",\"cipher\":\"aes-256-gcm\",\"port\":1}]",
},
Protocol: "shadowsocks",
Enabled: &enable,
@ -261,8 +262,9 @@ func (l *SubscribeLogic) createExpiredServers() []*node.Node {
Port: 18080,
Address: "127.0.0.1",
Server: &node.Server{
Id: 1,
Name: "Subscribe Expired",
Protocols: "[{\"type:\"\"shadowsocks\",\"cipher\":\"aes-256-gcm\",\"port\":1}]",
Protocols: "[{\"type\":\"shadowsocks\",\"cipher\":\"aes-256-gcm\",\"port\":1}]",
},
Protocol: "shadowsocks",
Enabled: &enable,

View File

@ -3,8 +3,10 @@ package node
import (
"context"
"fmt"
"strings"
"github.com/perfect-panel/server/pkg/tool"
"gorm.io/gorm"
)
type customServerLogicModel interface {
@ -85,10 +87,7 @@ func (m *customServerModel) FilterNodeList(ctx context.Context, params *FilterNo
query = query.Where("server_id IN ?", params.ServerId)
}
if len(params.Tag) > 0 {
query = query.Where("1 = 0")
for _, tag := range params.Tag {
query = query.Or("FIND_IN_SET(?,tags)", tag)
}
query = query.Scopes(InSet("tags", params.Tag))
}
if params.Protocol != "" {
query = query.Where("protocol = ?", params.Protocol)
@ -165,3 +164,22 @@ func (m *customServerModel) ClearServerCache(ctx context.Context, serverId int64
}
return nil
}
// InSet 支持多值 OR 查询
func InSet(field string, values []string) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
if len(values) == 0 {
return db
}
conds := make([]string, len(values))
args := make([]interface{}, len(values))
for i, v := range values {
conds[i] = "FIND_IN_SET(?, " + field + ")"
args[i] = v
}
// 用括号包裹 OR 条件,保证外层 AND 不受影响
return db.Where("("+strings.Join(conds, " OR ")+")", args...)
}
}

View File

@ -7,11 +7,9 @@ import (
"encoding/json"
"fmt"
"strconv"
"strings"
"time"
"github.com/perfect-panel/server/internal/model/log"
"github.com/perfect-panel/server/internal/model/node"
"github.com/perfect-panel/server/pkg/constant"
"github.com/perfect-panel/server/pkg/logger"
@ -466,17 +464,8 @@ func (l *ActivateOrderLogic) calculateCommission(price int64, percentage uint8)
// clearServerCache clears user list cache for all servers associated with the subscription
func (l *ActivateOrderLogic) clearServerCache(ctx context.Context, sub *subscribe.Subscribe) {
nodeIds := tool.StringToInt64Slice(sub.Nodes)
tags := strings.Split(sub.NodeTags, ",")
err := l.svc.NodeModel.ClearNodeCache(ctx, &node.FilterNodeParams{
Page: 1,
Size: 1000,
NodeId: nodeIds,
Tag: tags,
})
if err != nil {
logger.WithContext(ctx).Error("[Order Queue] Clear node cache failed", logger.Field("error", err.Error()))
if err := l.svc.SubscribeModel.ClearCache(ctx, sub.Id); err != nil {
logger.WithContext(ctx).Error("[Order Queue] Clear subscribe cache failed", logger.Field("error", err.Error()))
}
}

View File

@ -3,11 +3,8 @@ package subscription
import (
"context"
"encoding/json"
"strings"
"time"
"github.com/perfect-panel/server/internal/model/node"
"github.com/perfect-panel/server/pkg/tool"
queue "github.com/perfect-panel/server/queue/types"
"github.com/perfect-panel/server/pkg/logger"
@ -33,7 +30,7 @@ func (l *CheckSubscriptionLogic) ProcessTask(ctx context.Context, _ *asynq.Task)
// Check subscription traffic
err := l.svc.UserModel.Transaction(ctx, func(db *gorm.DB) error {
var list []*user.Subscribe
err := db.Model(&user.Subscribe{}).Where("upload + download >= traffic AND status = 1 AND traffic > 0 ").Find(&list).Error
err := db.Model(&user.Subscribe{}).Where("upload + download >= traffic AND status IN (0, 1) AND traffic > 0 ").Find(&list).Error
if err != nil {
logger.Errorw("[Check Subscription Traffic] Query subscribe failed", logger.Field("error", err.Error()))
return err
@ -78,7 +75,7 @@ func (l *CheckSubscriptionLogic) ProcessTask(ctx context.Context, _ *asynq.Task)
// Check subscription expire
err = l.svc.UserModel.Transaction(ctx, func(db *gorm.DB) error {
var list []*user.Subscribe
err = db.Model(&user.Subscribe{}).Where("`status` = 1 AND `expire_time` < ? AND `expire_time` != ? and `finished_at` IS NULL", time.Now(), time.UnixMilli(0)).Find(&list).Error
err = db.Model(&user.Subscribe{}).Where("`status` IN (0, 1) AND `expire_time` < ? AND `expire_time` != ? and `finished_at` IS NULL", time.Now(), time.UnixMilli(0)).Find(&list).Error
if err != nil {
logger.Error("[Check Subscription] Find subscribe failed", logger.Field("error", err.Error()))
return err
@ -207,31 +204,8 @@ func (l *CheckSubscriptionLogic) clearServerCache(ctx context.Context, userSubs
}
for sub, _ := range subs {
info, err := l.svc.SubscribeModel.FindOne(ctx, sub)
if err != nil {
logger.Errorw("[CheckSubscription] FindOne subscribe failed", logger.Field("error", err.Error()), logger.Field("subscribe_id", sub))
continue
}
if info != nil && info.Id == sub {
var nodes []int64
if info.Nodes != "" {
nodes = tool.StringToInt64Slice(info.Nodes)
}
var tag []string
if info.NodeTags != "" {
tag = strings.Split(info.NodeTags, ",")
}
err = l.svc.NodeModel.ClearNodeCache(ctx, &node.FilterNodeParams{
Page: 1,
Size: 1000,
Tag: tag,
ServerId: nodes,
})
if err != nil {
logger.Errorw("[CheckSubscription] ClearNodeCache failed", logger.Field("error", err.Error()), logger.Field("subscribe_id", sub))
continue
}
if err := l.svc.SubscribeModel.ClearCache(ctx, sub); err != nil {
logger.Errorw("[CheckSubscription] ClearCache failed", logger.Field("error", err.Error()), logger.Field("subscribe_id", sub))
}
}
}

View File

@ -9,12 +9,10 @@ import (
"time"
"github.com/perfect-panel/server/internal/model/log"
"github.com/perfect-panel/server/internal/model/node"
"github.com/perfect-panel/server/internal/model/subscribe"
"github.com/perfect-panel/server/internal/model/user"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/pkg/logger"
"github.com/perfect-panel/server/pkg/tool"
"github.com/perfect-panel/server/queue/types"
"github.com/hibiken/asynq"
@ -598,31 +596,11 @@ func (l *ResetTrafficLogic) clearCache(ctx context.Context, list []*user.Subscri
}
for sub, _ := range subs {
info, err := l.svc.SubscribeModel.FindOne(ctx, sub)
if err != nil {
logger.Errorw("[CheckSubscription] FindOne subscribe failed", logger.Field("error", err.Error()), logger.Field("subscribe_id", sub))
continue
}
if info != nil && info.Id == sub {
var nodes []int64
if info.Nodes != "" {
nodes = tool.StringToInt64Slice(info.Nodes)
}
var tag []string
if info.NodeTags != "" {
tag = strings.Split(info.NodeTags, ",")
}
err = l.svc.NodeModel.ClearNodeCache(ctx, &node.FilterNodeParams{
Page: 1,
Size: 1000,
Tag: tag,
ServerId: nodes,
})
if err != nil {
logger.Errorw("[CheckSubscription] ClearNodeCache failed", logger.Field("error", err.Error()), logger.Field("subscribe_id", sub))
continue
}
if err := l.svc.SubscribeModel.ClearCache(ctx, sub); err != nil {
logger.Errorw("[ResetTraffic] Failed to clear subscription cache",
logger.Field("subscribeId", sub),
logger.Field("error", err.Error()),
)
}
}
}

View File

@ -113,8 +113,8 @@ PPanel 服务端是 PPanel 项目的后端组件,为代理服务提供强大
4. **从 Docker Hub 拉取**CI/CD 发布后):
```bash
docker pull yourusername/ppanel-server:latest
docker run --rm -p 8080:8080 yourusername/ppanel-server:latest
docker pull ppanel/ppanel-server:latest
docker run --rm -p 8080:8080 ppanel/ppanel-server:latest
```
## 📖 API 文档