Compare commits
No commits in common. "6dcd75a39b601082d91e7a0f5624703d830894be" and "3fb23e3106538641fe6d81c9c2017a345ed9bfe5" have entirely different histories.
6dcd75a39b
...
3fb23e3106
@ -114,8 +114,8 @@ proxy services. Built with Go, it emphasizes performance, security, and scalabil
|
|||||||
|
|
||||||
4. **Pull from Docker Hub** (after CI/CD publishes):
|
4. **Pull from Docker Hub** (after CI/CD publishes):
|
||||||
```bash
|
```bash
|
||||||
docker pull ppanel/ppanel-server:latest
|
docker pull yourusername/ppanel-server:latest
|
||||||
docker run --rm -p 8080:8080 ppanel/ppanel-server:latest
|
docker run --rm -p 8080:8080 yourusername/ppanel-server:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
## 📖 API Documentation
|
## 📖 API Documentation
|
||||||
|
|||||||
@ -29,12 +29,10 @@ func NewPreviewSubscribeTemplateLogic(ctx context.Context, svcCtx *svc.ServiceCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *PreviewSubscribeTemplateLogic) PreviewSubscribeTemplate(req *types.PreviewSubscribeTemplateRequest) (resp *types.PreviewSubscribeTemplateResponse, err error) {
|
func (l *PreviewSubscribeTemplateLogic) PreviewSubscribeTemplate(req *types.PreviewSubscribeTemplateRequest) (resp *types.PreviewSubscribeTemplateResponse, err error) {
|
||||||
enable := true
|
|
||||||
_, servers, err := l.svcCtx.NodeModel.FilterNodeList(l.ctx, &node.FilterNodeParams{
|
_, servers, err := l.svcCtx.NodeModel.FilterNodeList(l.ctx, &node.FilterNodeParams{
|
||||||
Page: 1,
|
Page: 1,
|
||||||
Size: 1000,
|
Size: 1000,
|
||||||
Preload: true,
|
Preload: true,
|
||||||
Enabled: &enable,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Errorf("[PreviewSubscribeTemplateLogic] FindAllServer error: %v", err.Error())
|
l.Errorf("[PreviewSubscribeTemplateLogic] FindAllServer error: %v", err.Error())
|
||||||
|
|||||||
@ -230,7 +230,7 @@ func (l *MigrateServerNodeLogic) adapterServer(info *server.Server) (*node.Serve
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
protocol := node.Protocol{
|
protocol := node.Protocol{
|
||||||
Type: "hysteria",
|
Type: "hysteria2",
|
||||||
Port: uint16(src.Port),
|
Port: uint16(src.Port),
|
||||||
HopPorts: src.HopPorts,
|
HopPorts: src.HopPorts,
|
||||||
HopInterval: src.HopInterval,
|
HopInterval: src.HopInterval,
|
||||||
|
|||||||
@ -8,10 +8,7 @@ const (
|
|||||||
Trojan = "trojan"
|
Trojan = "trojan"
|
||||||
AnyTLS = "anytls"
|
AnyTLS = "anytls"
|
||||||
Tuic = "tuic"
|
Tuic = "tuic"
|
||||||
Hysteria = "hysteria"
|
Hysteria2 = "hysteria2"
|
||||||
// Deprecated: Hysteria2 is deprecated, use Hysteria instead
|
|
||||||
// TODO: remove in future versions
|
|
||||||
Hysteria2 = "hysteria2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SecurityConfig struct {
|
type SecurityConfig struct {
|
||||||
|
|||||||
@ -57,11 +57,6 @@ func (l *GetServerConfigLogic) GetServerConfig(req *types.GetServerConfigRequest
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// compatible hysteria2, remove in future versions
|
|
||||||
if req.Protocol == Hysteria2 {
|
|
||||||
req.Protocol = Hysteria
|
|
||||||
}
|
|
||||||
|
|
||||||
protocols, err := data.UnmarshalProtocols()
|
protocols, err := data.UnmarshalProtocols()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -214,7 +209,7 @@ func (l *GetServerConfigLogic) compatible(config node.Protocol) map[string]inter
|
|||||||
RealityShortId: config.RealityShortId,
|
RealityShortId: config.RealityShortId,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
case Hysteria:
|
case Hysteria2:
|
||||||
result = Hysteria2Node{
|
result = Hysteria2Node{
|
||||||
Port: config.Port,
|
Port: config.Port,
|
||||||
HopPorts: config.HopPorts,
|
HopPorts: config.HopPorts,
|
||||||
|
|||||||
@ -249,9 +249,8 @@ func (l *SubscribeLogic) createExpiredServers() []*node.Node {
|
|||||||
Port: 18080,
|
Port: 18080,
|
||||||
Address: "127.0.0.1",
|
Address: "127.0.0.1",
|
||||||
Server: &node.Server{
|
Server: &node.Server{
|
||||||
Id: 1,
|
|
||||||
Name: "Subscribe Expired",
|
Name: "Subscribe Expired",
|
||||||
Protocols: "[{\"type\":\"shadowsocks\",\"cipher\":\"aes-256-gcm\",\"port\":1}]",
|
Protocols: "[{\"type:\"\"shadowsocks\",\"cipher\":\"aes-256-gcm\",\"port\":1}]",
|
||||||
},
|
},
|
||||||
Protocol: "shadowsocks",
|
Protocol: "shadowsocks",
|
||||||
Enabled: &enable,
|
Enabled: &enable,
|
||||||
@ -262,9 +261,8 @@ func (l *SubscribeLogic) createExpiredServers() []*node.Node {
|
|||||||
Port: 18080,
|
Port: 18080,
|
||||||
Address: "127.0.0.1",
|
Address: "127.0.0.1",
|
||||||
Server: &node.Server{
|
Server: &node.Server{
|
||||||
Id: 1,
|
|
||||||
Name: "Subscribe Expired",
|
Name: "Subscribe Expired",
|
||||||
Protocols: "[{\"type\":\"shadowsocks\",\"cipher\":\"aes-256-gcm\",\"port\":1}]",
|
Protocols: "[{\"type:\"\"shadowsocks\",\"cipher\":\"aes-256-gcm\",\"port\":1}]",
|
||||||
},
|
},
|
||||||
Protocol: "shadowsocks",
|
Protocol: "shadowsocks",
|
||||||
Enabled: &enable,
|
Enabled: &enable,
|
||||||
|
|||||||
@ -3,10 +3,8 @@ package node
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/perfect-panel/server/pkg/tool"
|
"github.com/perfect-panel/server/pkg/tool"
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type customServerLogicModel interface {
|
type customServerLogicModel interface {
|
||||||
@ -87,7 +85,10 @@ func (m *customServerModel) FilterNodeList(ctx context.Context, params *FilterNo
|
|||||||
query = query.Where("server_id IN ?", params.ServerId)
|
query = query.Where("server_id IN ?", params.ServerId)
|
||||||
}
|
}
|
||||||
if len(params.Tag) > 0 {
|
if len(params.Tag) > 0 {
|
||||||
query = query.Scopes(InSet("tags", params.Tag))
|
query = query.Where("1 = 0")
|
||||||
|
for _, tag := range params.Tag {
|
||||||
|
query = query.Or("FIND_IN_SET(?,tags)", tag)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if params.Protocol != "" {
|
if params.Protocol != "" {
|
||||||
query = query.Where("protocol = ?", params.Protocol)
|
query = query.Where("protocol = ?", params.Protocol)
|
||||||
@ -164,22 +165,3 @@ func (m *customServerModel) ClearServerCache(ctx context.Context, serverId int64
|
|||||||
}
|
}
|
||||||
return nil
|
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,9 +7,11 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/perfect-panel/server/internal/model/log"
|
"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/constant"
|
||||||
"github.com/perfect-panel/server/pkg/logger"
|
"github.com/perfect-panel/server/pkg/logger"
|
||||||
|
|
||||||
@ -464,8 +466,17 @@ func (l *ActivateOrderLogic) calculateCommission(price int64, percentage uint8)
|
|||||||
|
|
||||||
// clearServerCache clears user list cache for all servers associated with the subscription
|
// clearServerCache clears user list cache for all servers associated with the subscription
|
||||||
func (l *ActivateOrderLogic) clearServerCache(ctx context.Context, sub *subscribe.Subscribe) {
|
func (l *ActivateOrderLogic) clearServerCache(ctx context.Context, sub *subscribe.Subscribe) {
|
||||||
if err := l.svc.SubscribeModel.ClearCache(ctx, sub.Id); err != nil {
|
nodeIds := tool.StringToInt64Slice(sub.Nodes)
|
||||||
logger.WithContext(ctx).Error("[Order Queue] Clear subscribe cache failed", logger.Field("error", err.Error()))
|
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()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,8 +3,11 @@ package subscription
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/perfect-panel/server/internal/model/node"
|
||||||
|
"github.com/perfect-panel/server/pkg/tool"
|
||||||
queue "github.com/perfect-panel/server/queue/types"
|
queue "github.com/perfect-panel/server/queue/types"
|
||||||
|
|
||||||
"github.com/perfect-panel/server/pkg/logger"
|
"github.com/perfect-panel/server/pkg/logger"
|
||||||
@ -30,7 +33,7 @@ func (l *CheckSubscriptionLogic) ProcessTask(ctx context.Context, _ *asynq.Task)
|
|||||||
// Check subscription traffic
|
// Check subscription traffic
|
||||||
err := l.svc.UserModel.Transaction(ctx, func(db *gorm.DB) error {
|
err := l.svc.UserModel.Transaction(ctx, func(db *gorm.DB) error {
|
||||||
var list []*user.Subscribe
|
var list []*user.Subscribe
|
||||||
err := db.Model(&user.Subscribe{}).Where("upload + download >= traffic AND status IN (0, 1) AND traffic > 0 ").Find(&list).Error
|
err := db.Model(&user.Subscribe{}).Where("upload + download >= traffic AND status = 1 AND traffic > 0 ").Find(&list).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorw("[Check Subscription Traffic] Query subscribe failed", logger.Field("error", err.Error()))
|
logger.Errorw("[Check Subscription Traffic] Query subscribe failed", logger.Field("error", err.Error()))
|
||||||
return err
|
return err
|
||||||
@ -75,7 +78,7 @@ func (l *CheckSubscriptionLogic) ProcessTask(ctx context.Context, _ *asynq.Task)
|
|||||||
// Check subscription expire
|
// Check subscription expire
|
||||||
err = l.svc.UserModel.Transaction(ctx, func(db *gorm.DB) error {
|
err = l.svc.UserModel.Transaction(ctx, func(db *gorm.DB) error {
|
||||||
var list []*user.Subscribe
|
var list []*user.Subscribe
|
||||||
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
|
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
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("[Check Subscription] Find subscribe failed", logger.Field("error", err.Error()))
|
logger.Error("[Check Subscription] Find subscribe failed", logger.Field("error", err.Error()))
|
||||||
return err
|
return err
|
||||||
@ -204,8 +207,31 @@ func (l *CheckSubscriptionLogic) clearServerCache(ctx context.Context, userSubs
|
|||||||
}
|
}
|
||||||
|
|
||||||
for sub, _ := range subs {
|
for sub, _ := range subs {
|
||||||
if err := l.svc.SubscribeModel.ClearCache(ctx, sub); err != nil {
|
info, err := l.svc.SubscribeModel.FindOne(ctx, sub)
|
||||||
logger.Errorw("[CheckSubscription] ClearCache failed", logger.Field("error", err.Error()), logger.Field("subscribe_id", 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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,10 +9,12 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/perfect-panel/server/internal/model/log"
|
"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/subscribe"
|
||||||
"github.com/perfect-panel/server/internal/model/user"
|
"github.com/perfect-panel/server/internal/model/user"
|
||||||
"github.com/perfect-panel/server/internal/svc"
|
"github.com/perfect-panel/server/internal/svc"
|
||||||
"github.com/perfect-panel/server/pkg/logger"
|
"github.com/perfect-panel/server/pkg/logger"
|
||||||
|
"github.com/perfect-panel/server/pkg/tool"
|
||||||
"github.com/perfect-panel/server/queue/types"
|
"github.com/perfect-panel/server/queue/types"
|
||||||
|
|
||||||
"github.com/hibiken/asynq"
|
"github.com/hibiken/asynq"
|
||||||
@ -596,11 +598,31 @@ func (l *ResetTrafficLogic) clearCache(ctx context.Context, list []*user.Subscri
|
|||||||
}
|
}
|
||||||
|
|
||||||
for sub, _ := range subs {
|
for sub, _ := range subs {
|
||||||
if err := l.svc.SubscribeModel.ClearCache(ctx, sub); err != nil {
|
info, err := l.svc.SubscribeModel.FindOne(ctx, sub)
|
||||||
logger.Errorw("[ResetTraffic] Failed to clear subscription cache",
|
if err != nil {
|
||||||
logger.Field("subscribeId", sub),
|
logger.Errorw("[CheckSubscription] FindOne subscribe failed", logger.Field("error", err.Error()), logger.Field("subscribe_id", sub))
|
||||||
logger.Field("error", err.Error()),
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -113,8 +113,8 @@ PPanel 服务端是 PPanel 项目的后端组件,为代理服务提供强大
|
|||||||
|
|
||||||
4. **从 Docker Hub 拉取**(CI/CD 发布后):
|
4. **从 Docker Hub 拉取**(CI/CD 发布后):
|
||||||
```bash
|
```bash
|
||||||
docker pull ppanel/ppanel-server:latest
|
docker pull yourusername/ppanel-server:latest
|
||||||
docker run --rm -p 8080:8080 ppanel/ppanel-server:latest
|
docker run --rm -p 8080:8080 yourusername/ppanel-server:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
## 📖 API 文档
|
## 📖 API 文档
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user