This commit is contained in:
parent
42061e38c0
commit
dca32cd11c
67
1.conf
67
1.conf
@ -1,67 +0,0 @@
|
|||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name api.hifast.biz 4d3vsw8.88xgaen.hifast.biz;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
return 301 https://$host$request_uri;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 443 ssl http2;
|
|
||||||
server_name api.hifast.biz;
|
|
||||||
client_max_body_size 150M;
|
|
||||||
|
|
||||||
ssl_certificate /etc/letsencrypt/live/api.hifast.biz/fullchain.pem;
|
|
||||||
ssl_certificate_key /etc/letsencrypt/live/api.hifast.biz/privkey.pem;
|
|
||||||
# 安全头
|
|
||||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
|
||||||
add_header X-Frame-Options DENY;
|
|
||||||
add_header X-Content-Type-Options nosniff;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
proxy_pass http://127.0.0.1:8080;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# de99e242子域名指向3001 (管理界面)
|
|
||||||
server {
|
|
||||||
listen 443 ssl http2;
|
|
||||||
server_name 4d3vsw8.88xgaen.hifast.biz;
|
|
||||||
client_max_body_size 150M;
|
|
||||||
|
|
||||||
ssl_certificate /etc/letsencrypt/live/4d3vsw8.88xgaen.hifast.biz/fullchain.pem;
|
|
||||||
ssl_certificate_key /etc/letsencrypt/live/4d3vsw8.88xgaen.hifast.biz/privkey.pem;
|
|
||||||
|
|
||||||
# 安全头
|
|
||||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
|
||||||
add_header X-Frame-Options DENY;
|
|
||||||
add_header X-Content-Type-Options nosniff;
|
|
||||||
|
|
||||||
# Gzip压缩
|
|
||||||
gzip on;
|
|
||||||
gzip_vary on;
|
|
||||||
gzip_min_length 1024;
|
|
||||||
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json image/svg+xml;
|
|
||||||
|
|
||||||
location ^~ / {
|
|
||||||
proxy_pass http://127.0.0.1:3001;
|
|
||||||
proxy_set_header Host $host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header REMOTE-HOST $remote_addr;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header Connection $http_connection;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
proxy_set_header X-Forwarded-Port $server_port;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
add_header X-Cache $upstream_cache_status;
|
|
||||||
add_header Cache-Control no-cache;
|
|
||||||
proxy_ssl_server_name off;
|
|
||||||
proxy_ssl_name $proxy_host;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -24,7 +24,7 @@ RUN BUILD_TIME=$(date -u +"%Y-%m-%d %H:%M:%S") && \
|
|||||||
go build -ldflags="-s -w -X 'github.com/perfect-panel/server/pkg/constant.Version=${VERSION}' -X 'github.com/perfect-panel/server/pkg/constant.BuildTime=${BUILD_TIME}'" -o /app/ppanel ppanel.go
|
go build -ldflags="-s -w -X 'github.com/perfect-panel/server/pkg/constant.Version=${VERSION}' -X 'github.com/perfect-panel/server/pkg/constant.BuildTime=${BUILD_TIME}'" -o /app/ppanel ppanel.go
|
||||||
|
|
||||||
# Final minimal image
|
# Final minimal image
|
||||||
FROM scratch
|
FROM alpine:latest
|
||||||
|
|
||||||
# Copy CA certificates and timezone data
|
# Copy CA certificates and timezone data
|
||||||
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||||
|
|||||||
@ -14,6 +14,43 @@ type (
|
|||||||
QuerySubscribeListRequest {
|
QuerySubscribeListRequest {
|
||||||
Language string `form:"language"`
|
Language string `form:"language"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueryUserSubscribeNodeListResponse {
|
||||||
|
List []UserSubscribeInfo `json:"list"`
|
||||||
|
}
|
||||||
|
|
||||||
|
UserSubscribeInfo {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
UserId int64 `json:"user_id"`
|
||||||
|
OrderId int64 `json:"order_id"`
|
||||||
|
SubscribeId int64 `json:"subscribe_id"`
|
||||||
|
StartTime int64 `json:"start_time"`
|
||||||
|
ExpireTime int64 `json:"expire_time"`
|
||||||
|
FinishedAt int64 `json:"finished_at"`
|
||||||
|
ResetTime int64 `json:"reset_time"`
|
||||||
|
Traffic int64 `json:"traffic"`
|
||||||
|
Download int64 `json:"download"`
|
||||||
|
Upload int64 `json:"upload"`
|
||||||
|
Token string `json:"token"`
|
||||||
|
Status uint8 `json:"status"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
UpdatedAt int64 `json:"updated_at"`
|
||||||
|
IsTryOut bool `json:"is_try_out"`
|
||||||
|
Nodes []*UserSubscribeNodeInfo `json:"nodes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
UserSubscribeNodeInfo{
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Uuid string `json:"uuid"`
|
||||||
|
Protocol string `json:"protocol"`
|
||||||
|
Port uint16 `json:"port"`
|
||||||
|
Address string `json:"address"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
Country string `json:"country"`
|
||||||
|
City string `json:"city"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@server (
|
@server (
|
||||||
@ -25,5 +62,9 @@ service ppanel {
|
|||||||
@doc "Get subscribe list"
|
@doc "Get subscribe list"
|
||||||
@handler QuerySubscribeList
|
@handler QuerySubscribeList
|
||||||
get /list (QuerySubscribeListRequest) returns (QuerySubscribeListResponse)
|
get /list (QuerySubscribeListRequest) returns (QuerySubscribeListResponse)
|
||||||
|
|
||||||
|
@doc "Get user subscribe node info"
|
||||||
|
@handler QueryUserSubscribeNodeList
|
||||||
|
get /node/list returns (QueryUserSubscribeNodeListResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -656,7 +656,7 @@ type (
|
|||||||
// public announcement
|
// public announcement
|
||||||
QueryAnnouncementRequest {
|
QueryAnnouncementRequest {
|
||||||
Page int `form:"page"`
|
Page int `form:"page"`
|
||||||
Size int `form:"size"`
|
Size int `form:"size,default=15"`
|
||||||
Pinned *bool `form:"pinned"`
|
Pinned *bool `form:"pinned"`
|
||||||
Popup *bool `form:"popup"`
|
Popup *bool `form:"popup"`
|
||||||
}
|
}
|
||||||
@ -673,6 +673,7 @@ type (
|
|||||||
List []SubscribeGroup `json:"list"`
|
List []SubscribeGroup `json:"list"`
|
||||||
Total int64 `json:"total"`
|
Total int64 `json:"total"`
|
||||||
}
|
}
|
||||||
|
|
||||||
GetUserSubscribeTrafficLogsRequest {
|
GetUserSubscribeTrafficLogsRequest {
|
||||||
Page int `form:"page"`
|
Page int `form:"page"`
|
||||||
Size int `form:"size"`
|
Size int `form:"size"`
|
||||||
|
|||||||
0
initialize/migrate/database/02115_ads.down.sql
Normal file
0
initialize/migrate/database/02115_ads.down.sql
Normal file
2
initialize/migrate/database/02115_ads.up.sql
Normal file
2
initialize/migrate/database/02115_ads.up.sql
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE `ads`
|
||||||
|
ADD COLUMN `description` VARCHAR(255) DEFAULT '' COMMENT 'Description';
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
INSERT INTO `ppanel`.`system` (`category`, `key`, `value`, `type`, `desc`, `created_at`, `updated_at`)
|
||||||
|
SELECT 'site', 'CustomData', '{
|
||||||
|
"kr_website_id": ""
|
||||||
|
}', 'string', 'Custom Data', '2025-04-22 14:25:16.637', '2025-10-14 15:47:19.187'
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1 FROM `ppanel`.`system` WHERE `category` = 'site' AND `key` = 'CustomData'
|
||||||
|
);
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
INSERT INTO `ppanel`.`system` (`category`, `key`, `value`, `type`, `desc`, `created_at`, `updated_at`)
|
||||||
|
SELECT 'site', 'CustomData', '{
|
||||||
|
"kr_website_id": ""
|
||||||
|
}', 'string', 'Custom Data', '2025-04-22 14:25:16.637', '2025-10-14 15:47:19.187'
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1 FROM `ppanel`.`system` WHERE `category` = 'site' AND `key` = 'CustomData'
|
||||||
|
);
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
package subscribe
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/perfect-panel/server/internal/logic/public/subscribe"
|
||||||
|
"github.com/perfect-panel/server/internal/svc"
|
||||||
|
"github.com/perfect-panel/server/pkg/result"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Get user subscribe node info
|
||||||
|
func QueryUserSubscribeNodeListHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
|
||||||
|
l := subscribe.NewQueryUserSubscribeNodeListLogic(c.Request.Context(), svcCtx)
|
||||||
|
resp, err := l.QueryUserSubscribeNodeList()
|
||||||
|
result.HttpResult(c, resp, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -739,6 +739,9 @@ func RegisterHandlers(router *gin.Engine, serverCtx *svc.ServiceContext) {
|
|||||||
{
|
{
|
||||||
// Get subscribe list
|
// Get subscribe list
|
||||||
publicSubscribeGroupRouter.GET("/list", publicSubscribe.QuerySubscribeListHandler(serverCtx))
|
publicSubscribeGroupRouter.GET("/list", publicSubscribe.QuerySubscribeListHandler(serverCtx))
|
||||||
|
|
||||||
|
// Get user subscribe node info
|
||||||
|
publicSubscribeGroupRouter.GET("/node/list", publicSubscribe.QueryUserSubscribeNodeListHandler(serverCtx))
|
||||||
}
|
}
|
||||||
|
|
||||||
publicTicketGroupRouter := router.Group("/v1/public/ticket")
|
publicTicketGroupRouter := router.Group("/v1/public/ticket")
|
||||||
|
|||||||
@ -35,11 +35,9 @@ func NewDeviceLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Devic
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *DeviceLoginLogic) DeviceLogin(req *types.DeviceLoginRequest) (resp *types.LoginResponse, err error) {
|
func (l *DeviceLoginLogic) DeviceLogin(req *types.DeviceLoginRequest) (resp *types.LoginResponse, err error) {
|
||||||
//TODO : check device login rate limit
|
if !l.svcCtx.Config.Device.Enable {
|
||||||
// Check if device login is enabled
|
return nil, xerr.NewErrMsg("Device login is disabled")
|
||||||
//if !l.svcCtx.Config.Register.EnableDevice {
|
}
|
||||||
// return nil, xerr.NewErrMsg("Device login is disabled")
|
|
||||||
//}
|
|
||||||
|
|
||||||
loginStatus := false
|
loginStatus := false
|
||||||
var userInfo *user.User
|
var userInfo *user.User
|
||||||
|
|||||||
@ -0,0 +1,195 @@
|
|||||||
|
package subscribe
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/perfect-panel/server/internal/model/node"
|
||||||
|
"github.com/perfect-panel/server/internal/model/user"
|
||||||
|
"github.com/perfect-panel/server/internal/svc"
|
||||||
|
"github.com/perfect-panel/server/internal/types"
|
||||||
|
"github.com/perfect-panel/server/pkg/constant"
|
||||||
|
"github.com/perfect-panel/server/pkg/logger"
|
||||||
|
"github.com/perfect-panel/server/pkg/tool"
|
||||||
|
"github.com/perfect-panel/server/pkg/xerr"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type QueryUserSubscribeNodeListLogic struct {
|
||||||
|
logger.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get user subscribe node info
|
||||||
|
func NewQueryUserSubscribeNodeListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QueryUserSubscribeNodeListLogic {
|
||||||
|
return &QueryUserSubscribeNodeListLogic{
|
||||||
|
Logger: logger.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *QueryUserSubscribeNodeListLogic) QueryUserSubscribeNodeList() (resp *types.QueryUserSubscribeNodeListResponse, err error) {
|
||||||
|
u, ok := l.ctx.Value(constant.CtxKeyUser).(*user.User)
|
||||||
|
if !ok {
|
||||||
|
logger.Error("current user is not found in context")
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidAccess), "Invalid Access")
|
||||||
|
}
|
||||||
|
|
||||||
|
userSubscribes, err := l.svcCtx.UserModel.QueryUserSubscribe(l.ctx, u.Id, 1, 2)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorw("failed to query user subscribe", logger.Field("error", err.Error()), logger.Field("user_id", u.Id))
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "DB_ERROR")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp = &types.QueryUserSubscribeNodeListResponse{}
|
||||||
|
for _, us := range userSubscribes {
|
||||||
|
userSubscribe, err := l.getUserSubscribe(us.Token)
|
||||||
|
if err != nil {
|
||||||
|
l.Errorw("[SubscribeLogic] Get user subscribe failed", logger.Field("error", err.Error()), logger.Field("token", userSubscribe.Token))
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
nodes, err := l.getServers(userSubscribe)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
userSubscribeInfo := types.UserSubscribeInfo{
|
||||||
|
Id: userSubscribe.Id,
|
||||||
|
Nodes: nodes,
|
||||||
|
Traffic: userSubscribe.Traffic,
|
||||||
|
Upload: userSubscribe.Upload,
|
||||||
|
Download: userSubscribe.Download,
|
||||||
|
Token: userSubscribe.Token,
|
||||||
|
UserId: userSubscribe.UserId,
|
||||||
|
OrderId: userSubscribe.OrderId,
|
||||||
|
SubscribeId: userSubscribe.SubscribeId,
|
||||||
|
StartTime: userSubscribe.StartTime.Unix(),
|
||||||
|
ExpireTime: userSubscribe.ExpireTime.Unix(),
|
||||||
|
Status: userSubscribe.Status,
|
||||||
|
CreatedAt: userSubscribe.CreatedAt.Unix(),
|
||||||
|
UpdatedAt: userSubscribe.UpdatedAt.Unix(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if userSubscribe.FinishedAt != nil {
|
||||||
|
userSubscribeInfo.FinishedAt = userSubscribe.FinishedAt.Unix()
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.svcCtx.Config.Register.EnableTrial && l.svcCtx.Config.Register.TrialSubscribe == userSubscribe.SubscribeId {
|
||||||
|
userSubscribeInfo.IsTryOut = true
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.List = append(resp.List, userSubscribeInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *QueryUserSubscribeNodeListLogic) getServers(userSub *user.Subscribe) (userSubscribeNodes []*types.UserSubscribeNodeInfo, err error) {
|
||||||
|
userSubscribeNodes = make([]*types.UserSubscribeNodeInfo, 0)
|
||||||
|
if l.isSubscriptionExpired(userSub) {
|
||||||
|
return l.createExpiredServers(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
subDetails, err := l.svcCtx.SubscribeModel.FindOne(l.ctx, userSub.SubscribeId)
|
||||||
|
if err != nil {
|
||||||
|
l.Errorw("[Generate Subscribe]find subscribe details error: %v", logger.Field("error", err.Error()))
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find subscribe details error: %v", err.Error())
|
||||||
|
}
|
||||||
|
nodeIds := tool.StringToInt64Slice(subDetails.Nodes)
|
||||||
|
tags := strings.Split(subDetails.NodeTags, ",")
|
||||||
|
|
||||||
|
l.Debugf("[Generate Subscribe]nodes: %v, NodeTags: %v", nodeIds, tags)
|
||||||
|
|
||||||
|
enable := true
|
||||||
|
|
||||||
|
_, nodes, err := l.svcCtx.NodeModel.FilterNodeList(l.ctx, &node.FilterNodeParams{
|
||||||
|
Page: 0,
|
||||||
|
Size: 1000,
|
||||||
|
NodeId: nodeIds,
|
||||||
|
Enabled: &enable, // Only get enabled nodes
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(nodes) > 0 {
|
||||||
|
var serverMapIds = make(map[int64]*node.Server)
|
||||||
|
for _, n := range nodes {
|
||||||
|
serverMapIds[n.ServerId] = nil
|
||||||
|
}
|
||||||
|
var serverIds []int64
|
||||||
|
for k := range serverMapIds {
|
||||||
|
serverIds = append(serverIds, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
servers, err := l.svcCtx.NodeModel.QueryServerList(l.ctx, serverIds)
|
||||||
|
if err != nil {
|
||||||
|
l.Errorw("[Generate Subscribe]find server details error: %v", logger.Field("error", err.Error()))
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find server details error: %v", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range servers {
|
||||||
|
serverMapIds[s.Id] = s
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, n := range nodes {
|
||||||
|
server := serverMapIds[n.ServerId]
|
||||||
|
if server == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
userSubscribeNode := &types.UserSubscribeNodeInfo{
|
||||||
|
Id: n.Id,
|
||||||
|
Name: n.Name,
|
||||||
|
Uuid: userSub.UUID,
|
||||||
|
Protocol: n.Protocol,
|
||||||
|
Port: n.Port,
|
||||||
|
Address: n.Address,
|
||||||
|
Tags: strings.Split(n.Tags, ","),
|
||||||
|
Country: server.Country,
|
||||||
|
City: server.City,
|
||||||
|
CreatedAt: n.CreatedAt.Unix(),
|
||||||
|
}
|
||||||
|
userSubscribeNodes = append(userSubscribeNodes, userSubscribeNode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Debugf("[Query Subscribe]found servers: %v", len(nodes))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
l.Errorw("[Generate Subscribe]find server details error: %v", logger.Field("error", err.Error()))
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find server details error: %v", err.Error())
|
||||||
|
}
|
||||||
|
logger.Debugf("[Generate Subscribe]found servers: %v", len(nodes))
|
||||||
|
return userSubscribeNodes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *QueryUserSubscribeNodeListLogic) isSubscriptionExpired(userSub *user.Subscribe) bool {
|
||||||
|
return userSub.ExpireTime.Unix() < time.Now().Unix() && userSub.ExpireTime.Unix() != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *QueryUserSubscribeNodeListLogic) createExpiredServers() []*types.UserSubscribeNodeInfo {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *QueryUserSubscribeNodeListLogic) getFirstHostLine() string {
|
||||||
|
host := l.svcCtx.Config.Host
|
||||||
|
lines := strings.Split(host, "\n")
|
||||||
|
if len(lines) > 0 {
|
||||||
|
return lines[0]
|
||||||
|
}
|
||||||
|
return host
|
||||||
|
}
|
||||||
|
func (l *QueryUserSubscribeNodeListLogic) getUserSubscribe(token string) (*user.Subscribe, error) {
|
||||||
|
userSub, err := l.svcCtx.UserModel.FindOneSubscribeByToken(l.ctx, token)
|
||||||
|
if err != nil {
|
||||||
|
l.Infow("[Generate Subscribe]find subscribe error: %v", logger.Field("error", err.Error()), logger.Field("token", token))
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find subscribe error: %v", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore expiration check
|
||||||
|
//if userSub.Status > 1 {
|
||||||
|
// l.Infow("[Generate Subscribe]subscribe is not available", logger.Field("status", int(userSub.Status)), logger.Field("token", token))
|
||||||
|
// return nil, errors.Wrapf(xerr.NewErrCode(xerr.SubscribeNotAvailable), "subscribe is not available")
|
||||||
|
//}
|
||||||
|
|
||||||
|
return userSub, nil
|
||||||
|
}
|
||||||
@ -2,7 +2,9 @@ package user
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/perfect-panel/server/internal/config"
|
||||||
"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/internal/types"
|
"github.com/perfect-panel/server/internal/types"
|
||||||
@ -10,6 +12,7 @@ import (
|
|||||||
"github.com/perfect-panel/server/pkg/logger"
|
"github.com/perfect-panel/server/pkg/logger"
|
||||||
"github.com/perfect-panel/server/pkg/xerr"
|
"github.com/perfect-panel/server/pkg/xerr"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UnbindDeviceLogic struct {
|
type UnbindDeviceLogic struct {
|
||||||
@ -38,5 +41,32 @@ func (l *UnbindDeviceLogic) UnbindDevice(req *types.UnbindDeviceRequest) error {
|
|||||||
return errors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), "device not belong to user")
|
return errors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), "device not belong to user")
|
||||||
}
|
}
|
||||||
|
|
||||||
return l.svcCtx.UserModel.DeleteDevice(l.ctx, req.Id)
|
return l.svcCtx.DB.Transaction(func(tx *gorm.DB) error {
|
||||||
|
var deleteDevice user.Device
|
||||||
|
err = tx.Model(&deleteDevice).Where("id = ?", req.Id).First(&deleteDevice).Error
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(xerr.NewErrCode(xerr.QueueEnqueueError), "find device err: %v", err)
|
||||||
|
}
|
||||||
|
err = tx.Delete(deleteDevice).Error
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseDeletedError), "delete device err: %v", err)
|
||||||
|
}
|
||||||
|
var userAuth user.AuthMethods
|
||||||
|
err = tx.Model(&userAuth).Where("auth_identifier = ? and auth_type = ?", deleteDevice.Identifier, "device").First(&userAuth).Error
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find device online record err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Delete(&userAuth).Error
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseDeletedError), "delete device online record err: %v", err)
|
||||||
|
}
|
||||||
|
sessionId := l.ctx.Value(constant.CtxKeySessionID)
|
||||||
|
sessionIdCacheKey := fmt.Sprintf("%v:%v", config.SessionIdKey, sessionId)
|
||||||
|
l.svcCtx.Redis.Del(l.ctx, sessionIdCacheKey)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,22 +28,30 @@ const (
|
|||||||
|
|
||||||
func DeviceMiddleware(srvCtx *svc.ServiceContext) func(c *gin.Context) {
|
func DeviceMiddleware(srvCtx *svc.ServiceContext) func(c *gin.Context) {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
loginType := c.GetString(string(constant.LoginType))
|
|
||||||
if loginType == "" {
|
|
||||||
loginType = c.GetHeader("Login-Type")
|
|
||||||
}
|
|
||||||
|
|
||||||
if loginType != "device" {
|
if !srvCtx.Config.Device.Enable {
|
||||||
c.Next()
|
c.Next()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Request = c.Request.WithContext(context.WithValue(c.Request.Context(), constant.LoginType, loginType))
|
if srvCtx.Config.Device.SecuritySecret == "" {
|
||||||
|
result.HttpResult(c, nil, errors.Wrapf(xerr.NewErrCode(xerr.SecretIsEmpty), "Secret is empty"))
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if !srvCtx.Config.Device.Enable || srvCtx.Config.Device.SecuritySecret == "" {
|
ctx := c.Request.Context()
|
||||||
|
if ctx.Value(constant.CtxKeyUser) == nil && c.GetHeader("Login-Type") != "" {
|
||||||
|
ctx = context.WithValue(ctx, constant.LoginType, c.GetHeader("Login-Type"))
|
||||||
|
c.Request = c.Request.WithContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
loginType, ok := ctx.Value(constant.LoginType).(string)
|
||||||
|
if !ok || loginType != "device" {
|
||||||
c.Next()
|
c.Next()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := NewResponseWriter(c, srvCtx)
|
rw := NewResponseWriter(c, srvCtx)
|
||||||
if !rw.Decrypt() {
|
if !rw.Decrypt() {
|
||||||
result.HttpResult(c, nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidCiphertext), "Invalid ciphertext"))
|
result.HttpResult(c, nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidCiphertext), "Invalid ciphertext"))
|
||||||
|
|||||||
@ -43,7 +43,7 @@ func (m *customAnnouncementModel) GetAnnouncementListByPage(ctx context.Context,
|
|||||||
if filter.Search != "" {
|
if filter.Search != "" {
|
||||||
conn = conn.Where("`title` LIKE ? OR `content` LIKE ?", "%"+filter.Search+"%", "%"+filter.Search+"%")
|
conn = conn.Where("`title` LIKE ? OR `content` LIKE ?", "%"+filter.Search+"%", "%"+filter.Search+"%")
|
||||||
}
|
}
|
||||||
return conn.Count(&total).Offset((page - 1) * size).Limit(size).Find(v).Error
|
return conn.Count(&total).Offset((page - 1) * size).Limit(size).Find(&list).Error
|
||||||
})
|
})
|
||||||
return total, list, err
|
return total, list, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,7 @@ type (
|
|||||||
UpdateServer(ctx context.Context, data *Server, tx ...*gorm.DB) error
|
UpdateServer(ctx context.Context, data *Server, tx ...*gorm.DB) error
|
||||||
DeleteServer(ctx context.Context, id int64, tx ...*gorm.DB) error
|
DeleteServer(ctx context.Context, id int64, tx ...*gorm.DB) error
|
||||||
Transaction(ctx context.Context, fn func(db *gorm.DB) error) error
|
Transaction(ctx context.Context, fn func(db *gorm.DB) error) error
|
||||||
|
QueryServerList(ctx context.Context, ids []int64) (servers []*Server, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeModel interface {
|
NodeModel interface {
|
||||||
|
|||||||
@ -65,6 +65,12 @@ func (m *customServerModel) FilterServerList(ctx context.Context, params *Filter
|
|||||||
return total, servers, err
|
return total, servers, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *customServerModel) QueryServerList(ctx context.Context, ids []int64) (servers []*Server, err error) {
|
||||||
|
query := m.WithContext(ctx).Model(&Server{})
|
||||||
|
err = query.Where("id IN (?)", ids).Find(&servers).Error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// FilterNodeList Filter Node List
|
// FilterNodeList Filter Node List
|
||||||
func (m *customServerModel) FilterNodeList(ctx context.Context, params *FilterNodeParams) (int64, []*Node, error) {
|
func (m *customServerModel) FilterNodeList(ctx context.Context, params *FilterNodeParams) (int64, []*Node, error) {
|
||||||
var nodes []*Node
|
var nodes []*Node
|
||||||
|
|||||||
@ -51,7 +51,7 @@ func (m *customUserModel) QueryDeviceList(ctx context.Context, userId int64) ([]
|
|||||||
var list []*Device
|
var list []*Device
|
||||||
var total int64
|
var total int64
|
||||||
err := m.QueryNoCacheCtx(ctx, &list, func(conn *gorm.DB, v interface{}) error {
|
err := m.QueryNoCacheCtx(ctx, &list, func(conn *gorm.DB, v interface{}) error {
|
||||||
return conn.Model(&Device{}).Where("`user_id` = ? and `subscribe_id` = ?", userId).Count(&total).Find(&list).Error
|
return conn.Model(&Device{}).Where("`user_id` = ?", userId).Count(&total).Find(&list).Error
|
||||||
})
|
})
|
||||||
return list, total, err
|
return list, total, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1540,7 +1540,7 @@ type PurchaseOrderResponse struct {
|
|||||||
|
|
||||||
type QueryAnnouncementRequest struct {
|
type QueryAnnouncementRequest struct {
|
||||||
Page int `form:"page"`
|
Page int `form:"page"`
|
||||||
Size int `form:"size"`
|
Size int `form:"size,default=15"`
|
||||||
Pinned *bool `form:"pinned"`
|
Pinned *bool `form:"pinned"`
|
||||||
Popup *bool `form:"popup"`
|
Popup *bool `form:"popup"`
|
||||||
}
|
}
|
||||||
@ -1697,6 +1697,10 @@ type QueryUserSubscribeListResponse struct {
|
|||||||
Total int64 `json:"total"`
|
Total int64 `json:"total"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type QueryUserSubscribeNodeListResponse struct {
|
||||||
|
List []UserSubscribeInfo `json:"list"`
|
||||||
|
}
|
||||||
|
|
||||||
type QuotaTask struct {
|
type QuotaTask struct {
|
||||||
Id int64 `json:"id"`
|
Id int64 `json:"id"`
|
||||||
Subscribers []int64 `json:"subscribers"`
|
Subscribers []int64 `json:"subscribers"`
|
||||||
@ -2595,6 +2599,26 @@ type UserSubscribeDetail struct {
|
|||||||
UpdatedAt int64 `json:"updated_at"`
|
UpdatedAt int64 `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UserSubscribeInfo struct {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
UserId int64 `json:"user_id"`
|
||||||
|
OrderId int64 `json:"order_id"`
|
||||||
|
SubscribeId int64 `json:"subscribe_id"`
|
||||||
|
StartTime int64 `json:"start_time"`
|
||||||
|
ExpireTime int64 `json:"expire_time"`
|
||||||
|
FinishedAt int64 `json:"finished_at"`
|
||||||
|
ResetTime int64 `json:"reset_time"`
|
||||||
|
Traffic int64 `json:"traffic"`
|
||||||
|
Download int64 `json:"download"`
|
||||||
|
Upload int64 `json:"upload"`
|
||||||
|
Token string `json:"token"`
|
||||||
|
Status uint8 `json:"status"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
UpdatedAt int64 `json:"updated_at"`
|
||||||
|
IsTryOut bool `json:"is_try_out"`
|
||||||
|
Nodes []*UserSubscribeNodeInfo `json:"nodes"`
|
||||||
|
}
|
||||||
|
|
||||||
type UserSubscribeLog struct {
|
type UserSubscribeLog struct {
|
||||||
Id int64 `json:"id"`
|
Id int64 `json:"id"`
|
||||||
UserId int64 `json:"user_id"`
|
UserId int64 `json:"user_id"`
|
||||||
@ -2605,6 +2629,19 @@ type UserSubscribeLog struct {
|
|||||||
Timestamp int64 `json:"timestamp"`
|
Timestamp int64 `json:"timestamp"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UserSubscribeNodeInfo struct {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Uuid string `json:"uuid"`
|
||||||
|
Protocol string `json:"protocol"`
|
||||||
|
Port uint16 `json:"port"`
|
||||||
|
Address string `json:"address"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
Country string `json:"country"`
|
||||||
|
City string `json:"city"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
type UserSubscribeTrafficLog struct {
|
type UserSubscribeTrafficLog struct {
|
||||||
SubscribeId int64 `json:"subscribe_id"` // Subscribe ID
|
SubscribeId int64 `json:"subscribe_id"` // Subscribe ID
|
||||||
UserId int64 `json:"user_id"` // User ID
|
UserId int64 `json:"user_id"` // User ID
|
||||||
|
|||||||
@ -47,6 +47,7 @@ const (
|
|||||||
ErrorTokenExpire uint32 = 40004
|
ErrorTokenExpire uint32 = 40004
|
||||||
InvalidAccess uint32 = 40005
|
InvalidAccess uint32 = 40005
|
||||||
InvalidCiphertext uint32 = 40006
|
InvalidCiphertext uint32 = 40006
|
||||||
|
SecretIsEmpty uint32 = 40007
|
||||||
)
|
)
|
||||||
|
|
||||||
//coupon error
|
//coupon error
|
||||||
|
|||||||
@ -14,6 +14,7 @@ func init() {
|
|||||||
ErrorTokenEmpty: "User token is empty",
|
ErrorTokenEmpty: "User token is empty",
|
||||||
ErrorTokenInvalid: "User token is invalid",
|
ErrorTokenInvalid: "User token is invalid",
|
||||||
ErrorTokenExpire: "User token is expired",
|
ErrorTokenExpire: "User token is expired",
|
||||||
|
SecretIsEmpty: "Secret is empty",
|
||||||
InvalidAccess: "Invalid access",
|
InvalidAccess: "Invalid access",
|
||||||
InvalidCiphertext: "Invalid ciphertext",
|
InvalidCiphertext: "Invalid ciphertext",
|
||||||
// Database error
|
// Database error
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user