Compare commits
10 Commits
3fb23e3106
...
6dcd75a39b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6dcd75a39b | ||
|
|
29dd54546d | ||
|
|
41d8de42c0 | ||
|
|
b97e2d650e | ||
|
|
c5be0081ca | ||
|
|
1a6cced814 | ||
|
|
bf24e1756a | ||
|
|
4e7a6e9e41 | ||
|
|
6c7d45a640 | ||
|
|
c835e751ac |
@ -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
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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...)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 文档
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user