Merge remote-tracking branch 'upstream'
# Conflicts: # apis/public/subscribe.api # initialize/migrate/database/02115_ads.up.sql # internal/logic/auth/deviceLoginLogic.go # internal/logic/public/subscribe/queryUserSubscribeNodeListLogic.go # internal/logic/public/user/unbindDeviceLogic.go # internal/types/types.go # pkg/tool/encryption_test.go
This commit is contained in:
commit
cd3b9d4fc8
6
go.mod
6
go.mod
@ -44,7 +44,7 @@ require (
|
||||
go.opentelemetry.io/otel/sdk v1.29.0
|
||||
go.opentelemetry.io/otel/trace v1.29.0
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/crypto v0.32.0
|
||||
golang.org/x/crypto v0.35.0
|
||||
golang.org/x/oauth2 v0.25.0
|
||||
golang.org/x/time v0.6.0
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
@ -138,8 +138,8 @@ require (
|
||||
golang.org/x/arch v0.13.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240525044651-4c93da0ed11d // indirect
|
||||
golang.org/x/net v0.34.0 // indirect
|
||||
golang.org/x/sys v0.29.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
|
||||
12
go.sum
12
go.sum
@ -402,8 +402,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
|
||||
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20240525044651-4c93da0ed11d h1:N0hmiNbwsSNwHBAvR3QB5w25pUwH4tK0Y/RltD1j1h4=
|
||||
golang.org/x/exp v0.0.0-20240525044651-4c93da0ed11d/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
||||
@ -463,8 +463,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
@ -478,8 +478,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
|
||||
@ -1,2 +1,20 @@
|
||||
ALTER TABLE `ads`
|
||||
ADD COLUMN `description` VARCHAR(255) DEFAULT '' COMMENT 'Description';
|
||||
-- 只有当 ads 表中不存在 description 字段时才添加
|
||||
SET
|
||||
@col_exists := (
|
||||
SELECT COUNT(*)
|
||||
FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'ads'
|
||||
AND COLUMN_NAME = 'description'
|
||||
);
|
||||
|
||||
SET
|
||||
@query := IF(
|
||||
@col_exists = 0,
|
||||
'ALTER TABLE `ads` ADD COLUMN `description` VARCHAR(255) DEFAULT '''' COMMENT ''Description'';',
|
||||
'SELECT "Column `description` already exists"'
|
||||
);
|
||||
|
||||
PREPARE stmt FROM @query;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
3
initialize/migrate/database/02116_user_algo.down.sql
Normal file
3
initialize/migrate/database/02116_user_algo.down.sql
Normal file
@ -0,0 +1,3 @@
|
||||
ALTER TABLE `user`
|
||||
DROP COLUMN `algo`,
|
||||
DROP COLUMN `salt`;
|
||||
35
initialize/migrate/database/02116_user_algo.up.sql
Normal file
35
initialize/migrate/database/02116_user_algo.up.sql
Normal file
@ -0,0 +1,35 @@
|
||||
-- 添加 algo 列(如果不存在)
|
||||
SET @dbname = DATABASE();
|
||||
SET @tablename = 'user';
|
||||
SET @colname = 'algo';
|
||||
SET @sql = (
|
||||
SELECT IF(
|
||||
COUNT(*) = 0,
|
||||
'ALTER TABLE `user` ADD COLUMN `algo` VARCHAR(20) NOT NULL DEFAULT ''default'' COMMENT ''Encryption Algorithm'' AFTER `password`;',
|
||||
'SELECT "Column `algo` already exists";'
|
||||
)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @dbname
|
||||
AND TABLE_NAME = @tablename
|
||||
AND COLUMN_NAME = @colname
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- 添加 salt 列(如果不存在)
|
||||
SET @colname = 'salt';
|
||||
SET @sql = (
|
||||
SELECT IF(
|
||||
COUNT(*) = 0,
|
||||
'ALTER TABLE `user` ADD COLUMN `salt` VARCHAR(20) NOT NULL DEFAULT ''default'' COMMENT ''Password Salt'' AFTER `algo`;',
|
||||
'SELECT "Column `salt` already exists";'
|
||||
)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @dbname
|
||||
AND TABLE_NAME = @tablename
|
||||
AND COLUMN_NAME = @colname
|
||||
);
|
||||
PREPARE stmt FROM @sql;
|
||||
EXECUTE stmt;
|
||||
DEALLOCATE PREPARE stmt;
|
||||
@ -0,0 +1,7 @@
|
||||
INSERT INTO `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 `system` WHERE `category` = 'site' AND `key` = 'CustomData'
|
||||
);
|
||||
@ -0,0 +1,7 @@
|
||||
INSERT INTO `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 `system` WHERE `category` = 'site' AND `key` = 'CustomData'
|
||||
);
|
||||
@ -0,0 +1 @@
|
||||
ALTER TABLE traffic_log DROP INDEX idx_timestamp;
|
||||
1
initialize/migrate/database/02118_traffic_log_idx.up.sql
Normal file
1
initialize/migrate/database/02118_traffic_log_idx.up.sql
Normal file
@ -0,0 +1 @@
|
||||
ALTER TABLE traffic_log ADD INDEX idx_timestamp (timestamp);
|
||||
@ -40,6 +40,7 @@ func (l *CreateUserLogic) CreateUser(req *types.CreateUserRequest) error {
|
||||
pwd := tool.EncodePassWord(req.Password)
|
||||
newUser := &user.User{
|
||||
Password: pwd,
|
||||
Algo: "default",
|
||||
ReferralPercentage: req.ReferralPercentage,
|
||||
OnlyFirstPurchase: &req.OnlyFirstPurchase,
|
||||
ReferCode: req.ReferCode,
|
||||
|
||||
@ -129,6 +129,7 @@ func (l *UpdateUserBasicInfoLogic) UpdateUserBasicInfo(req *types.UpdateUserBasi
|
||||
return errors.Wrapf(xerr.NewErrCodeMsg(503, "Demo mode does not allow modification of the admin user password"), "UpdateUserBasicInfo failed: cannot update admin user password in demo mode")
|
||||
}
|
||||
userInfo.Password = tool.EncodePassWord(req.Password)
|
||||
userInfo.Algo = "default"
|
||||
}
|
||||
|
||||
err = l.svcCtx.UserModel.Update(l.ctx, userInfo)
|
||||
|
||||
@ -104,7 +104,8 @@ func (l *ResetPasswordLogic) ResetPassword(req *types.ResetPasswordRequest) (res
|
||||
|
||||
// Update password
|
||||
userInfo.Password = tool.EncodePassWord(req.Password)
|
||||
if err := l.svcCtx.UserModel.Update(l.ctx, userInfo); err != nil {
|
||||
userInfo.Algo = "default"
|
||||
if err = l.svcCtx.UserModel.Update(l.ctx, userInfo); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseUpdateError), "update user info failed: %v", err.Error())
|
||||
}
|
||||
|
||||
|
||||
@ -98,7 +98,7 @@ func (l *TelephoneLoginLogic) TelephoneLogin(req *types.TelephoneLoginRequest, r
|
||||
|
||||
if req.TelephoneCode == "" {
|
||||
// Verify password
|
||||
if !tool.VerifyPassWord(req.Password, userInfo.Password) {
|
||||
if !tool.MultiPasswordVerify(userInfo.Algo, userInfo.Salt, req.Password, userInfo.Password) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.UserPasswordError), "user password")
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -78,6 +78,7 @@ func (l *TelephoneResetPasswordLogic) TelephoneResetPassword(req *types.Telephon
|
||||
// Generate password
|
||||
pwd := tool.EncodePassWord(req.Password)
|
||||
userInfo.Password = pwd
|
||||
userInfo.Algo = "default"
|
||||
err = l.svcCtx.UserModel.Update(l.ctx, userInfo)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "update user password failed: %v", err.Error())
|
||||
|
||||
@ -107,6 +107,7 @@ func (l *TelephoneUserRegisterLogic) TelephoneUserRegister(req *types.TelephoneR
|
||||
pwd := tool.EncodePassWord(req.Password)
|
||||
userInfo := &user.User{
|
||||
Password: pwd,
|
||||
Algo: "default",
|
||||
OnlyFirstPurchase: &l.svcCtx.Config.Invite.OnlyFirstPurchase,
|
||||
AuthMethods: []user.AuthMethods{
|
||||
{
|
||||
|
||||
@ -77,7 +77,7 @@ func (l *UserLoginLogic) UserLogin(req *types.UserLoginRequest) (resp *types.Log
|
||||
}
|
||||
|
||||
// Verify password
|
||||
if !tool.VerifyPassWord(req.Password, userInfo.Password) {
|
||||
if !tool.MultiPasswordVerify(userInfo.Algo, userInfo.Salt, req.Password, userInfo.Password) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.UserPasswordError), "user password")
|
||||
}
|
||||
|
||||
|
||||
@ -90,6 +90,7 @@ func (l *UserRegisterLogic) UserRegister(req *types.UserRegisterRequest) (resp *
|
||||
pwd := tool.EncodePassWord(req.Password)
|
||||
userInfo := &user.User{
|
||||
Password: pwd,
|
||||
Algo: "default",
|
||||
OnlyFirstPurchase: &l.svcCtx.Config.Invite.OnlyFirstPurchase,
|
||||
}
|
||||
if referer != nil {
|
||||
|
||||
@ -57,7 +57,7 @@ func (l *EPayNotifyLogic) EPayNotify(req *types.EPayNotifyRequest) error {
|
||||
return err
|
||||
}
|
||||
// Verify sign
|
||||
client := epay.NewClient(config.Pid, config.Url, config.Key)
|
||||
client := epay.NewClient(config.Pid, config.Url, config.Key, config.Type)
|
||||
if !client.VerifySign(urlParamsToMap(l.ctx.Request.URL.RawQuery)) && !l.svcCtx.Config.Debug {
|
||||
l.Logger.Error("[EPayNotify] Verify sign failed")
|
||||
return nil
|
||||
|
||||
@ -267,7 +267,7 @@ func (l *PurchaseCheckoutLogic) epayPayment(config *payment.Payment, info *order
|
||||
return "", errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "Unmarshal error: %s", err.Error())
|
||||
}
|
||||
// Initialize EPay client with merchant credentials
|
||||
client := epay.NewClient(epayConfig.Pid, epayConfig.Url, epayConfig.Key)
|
||||
client := epay.NewClient(epayConfig.Pid, epayConfig.Url, epayConfig.Key, epayConfig.Type)
|
||||
|
||||
// Convert order amount to CNY using current exchange rate
|
||||
amount, err := l.queryExchangeRate("CNY", info.Amount)
|
||||
@ -309,7 +309,7 @@ func (l *PurchaseCheckoutLogic) CryptoSaaSPayment(config *payment.Payment, info
|
||||
return "", errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "Unmarshal error: %s", err.Error())
|
||||
}
|
||||
// Initialize EPay client with merchant credentials
|
||||
client := epay.NewClient(epayConfig.AccountID, epayConfig.Endpoint, epayConfig.SecretKey)
|
||||
client := epay.NewClient(epayConfig.AccountID, epayConfig.Endpoint, epayConfig.SecretKey, epayConfig.Type)
|
||||
|
||||
// Convert order amount to CNY using current exchange rate
|
||||
amount, err := l.queryExchangeRate("CNY", info.Amount)
|
||||
@ -347,6 +347,11 @@ func (l *PurchaseCheckoutLogic) queryExchangeRate(to string, src int64) (amount
|
||||
// Convert cents to decimal amount
|
||||
amount = float64(src) / float64(100)
|
||||
|
||||
if l.svcCtx.ExchangeRate != 0 && to == "CNY" {
|
||||
amount = amount * l.svcCtx.ExchangeRate
|
||||
return amount, nil
|
||||
}
|
||||
|
||||
// Retrieve system currency configuration
|
||||
currency, err := l.svcCtx.SystemModel.GetCurrencyConfig(l.ctx)
|
||||
if err != nil {
|
||||
|
||||
@ -83,6 +83,12 @@ func (l *PurchaseLogic) Purchase(req *types.PortalPurchaseRequest) (resp *types.
|
||||
if couponInfo.Count != 0 && couponInfo.Count <= couponInfo.UsedCount {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.CouponInsufficientUsage), "coupon used")
|
||||
}
|
||||
// Check expiration time
|
||||
expireTime := time.Unix(couponInfo.ExpireTime, 0)
|
||||
if time.Now().After(expireTime) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.CouponExpired), "coupon expired")
|
||||
}
|
||||
|
||||
couponSub := tool.StringToInt64Slice(couponInfo.Subscribe)
|
||||
if len(couponSub) > 0 && !tool.Contains(couponSub, req.SubscribeId) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.CouponNotApplicable), "coupon not match")
|
||||
|
||||
@ -85,9 +85,10 @@ func (l *AlipayF2FConfig) Unmarshal(data []byte) error {
|
||||
}
|
||||
|
||||
type EPayConfig struct {
|
||||
Pid string `json:"pid"`
|
||||
Url string `json:"url"`
|
||||
Key string `json:"key"`
|
||||
Pid string `json:"pid"`
|
||||
Url string `json:"url"`
|
||||
Key string `json:"key"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
func (l *EPayConfig) Marshal() ([]byte, error) {
|
||||
@ -109,6 +110,7 @@ type CryptoSaaSConfig struct {
|
||||
Endpoint string `json:"endpoint"`
|
||||
AccountID string `json:"account_id"`
|
||||
SecretKey string `json:"secret_key"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
func (l *CryptoSaaSConfig) Marshal() ([]byte, error) {
|
||||
|
||||
@ -7,6 +7,8 @@ import (
|
||||
type User struct {
|
||||
Id int64 `gorm:"primaryKey"`
|
||||
Password string `gorm:"type:varchar(100);not null;comment:User Password"`
|
||||
Algo string `gorm:"type:varchar(20);default:'default';comment:Encryption Algorithm"`
|
||||
Salt string `gorm:"type:varchar(20);default:null;comment:Password Salt"`
|
||||
Avatar string `gorm:"type:MEDIUMTEXT;comment:User Avatar"`
|
||||
Balance int64 `gorm:"default:0;comment:User Balance"` // User Balance Amount
|
||||
ReferCode string `gorm:"type:varchar(20);default:'';comment:Referral Code"`
|
||||
|
||||
@ -32,10 +32,12 @@ import (
|
||||
)
|
||||
|
||||
type ServiceContext struct {
|
||||
DB *gorm.DB
|
||||
Redis *redis.Client
|
||||
Config config.Config
|
||||
Queue *asynq.Client
|
||||
DB *gorm.DB
|
||||
Redis *redis.Client
|
||||
Config config.Config
|
||||
Queue *asynq.Client
|
||||
ExchangeRate float64
|
||||
|
||||
//NodeCache *cache.NodeCacheClient
|
||||
AuthModel auth.Model
|
||||
AdsModel ads.Model
|
||||
@ -82,10 +84,11 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
||||
}
|
||||
authLimiter := limit.NewPeriodLimit(86400, 15, rds, config.SendCountLimitKeyPrefix, limit.Align())
|
||||
srv := &ServiceContext{
|
||||
DB: db,
|
||||
Redis: rds,
|
||||
Config: c,
|
||||
Queue: NewAsynqClient(c),
|
||||
DB: db,
|
||||
Redis: rds,
|
||||
Config: c,
|
||||
Queue: NewAsynqClient(c),
|
||||
ExchangeRate: 1.0,
|
||||
//NodeCache: cache.NewNodeCacheClient(rds),
|
||||
AuthLimiter: authLimiter,
|
||||
AdsModel: ads.NewModel(db, rds),
|
||||
|
||||
@ -14,9 +14,10 @@ import (
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
Pid string
|
||||
Url string
|
||||
Key string
|
||||
Pid string
|
||||
Url string
|
||||
Key string
|
||||
Type string
|
||||
}
|
||||
|
||||
type Order struct {
|
||||
@ -37,11 +38,12 @@ type queryOrderStatusResponse struct {
|
||||
Status int `json:"status"`
|
||||
}
|
||||
|
||||
func NewClient(pid, url, key string) *Client {
|
||||
func NewClient(pid, url, key string, Type string) *Client {
|
||||
return &Client{
|
||||
Pid: pid,
|
||||
Url: url,
|
||||
Key: key,
|
||||
Pid: pid,
|
||||
Url: url,
|
||||
Key: key,
|
||||
Type: Type,
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,6 +55,7 @@ func (c *Client) CreatePayUrl(order Order) string {
|
||||
params.Set("notify_url", order.NotifyUrl)
|
||||
params.Set("out_trade_no", order.OrderNo)
|
||||
params.Set("pid", c.Pid)
|
||||
params.Set("type", c.Type)
|
||||
params.Set("return_url", order.ReturnUrl)
|
||||
|
||||
// Generate the sign using the CreateSign function
|
||||
@ -117,6 +120,7 @@ func (c *Client) structToMap(order Order) map[string]string {
|
||||
result["notify_url"] = order.NotifyUrl
|
||||
result["out_trade_no"] = order.OrderNo
|
||||
result["pid"] = c.Pid
|
||||
result["type"] = c.Type
|
||||
result["return_url"] = order.ReturnUrl
|
||||
return result
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ package epay
|
||||
import "testing"
|
||||
|
||||
func TestEpay(t *testing.T) {
|
||||
client := NewClient("", "http://127.0.0.1", "")
|
||||
client := NewClient("", "http://127.0.0.1", "", "")
|
||||
order := Order{
|
||||
Name: "测试",
|
||||
OrderNo: "123456789",
|
||||
@ -19,7 +19,7 @@ func TestEpay(t *testing.T) {
|
||||
|
||||
func TestQueryOrderStatus(t *testing.T) {
|
||||
t.Skipf("Skip TestQueryOrderStatus test")
|
||||
client := NewClient("Pid", "Url", "Key")
|
||||
client := NewClient("Pid", "Url", "Key", "Type")
|
||||
orderNo := "123456789"
|
||||
status := client.QueryOrderStatus(orderNo)
|
||||
t.Logf("OrderNo: %s, Status: %v\n", orderNo, status)
|
||||
@ -40,7 +40,7 @@ func TestVerifySign(t *testing.T) {
|
||||
}
|
||||
|
||||
key := "LbTabbB580zWyhXhyyww7wwvy5u8k0wl"
|
||||
c := NewClient("Pid", "Url", key)
|
||||
c := NewClient("Pid", "Url", key, "Type")
|
||||
if c.VerifySign(params) {
|
||||
t.Logf("Sign verification success!")
|
||||
} else {
|
||||
|
||||
@ -65,9 +65,10 @@ func GetSupportedPlatforms() []types.PlatformInfo {
|
||||
Platform: EPay.String(),
|
||||
PlatformUrl: "",
|
||||
PlatformFieldDescription: map[string]string{
|
||||
"pid": "PID",
|
||||
"url": "URL",
|
||||
"key": "Key",
|
||||
"pid": "PID",
|
||||
"url": "URL",
|
||||
"key": "Key",
|
||||
"type": "Type",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@ -2,12 +2,14 @@ package tool
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/anaskhan96/go-password-encoder"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
var options = &password.Options{SaltLen: 16, Iterations: 100, KeyLen: 32, HashFunction: sha512.New}
|
||||
@ -32,3 +34,24 @@ func Md5Encode(str string, isUpper bool) string {
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func MultiPasswordVerify(algo, salt, password, hash string) bool {
|
||||
switch algo {
|
||||
case "md5":
|
||||
sum := md5.Sum([]byte(password))
|
||||
return hex.EncodeToString(sum[:]) == hash
|
||||
case "sha256":
|
||||
sum := sha256.Sum256([]byte(password))
|
||||
return hex.EncodeToString(sum[:]) == hash
|
||||
case "md5salt":
|
||||
sum := md5.Sum([]byte(password + salt))
|
||||
return hex.EncodeToString(sum[:]) == hash
|
||||
case "default": // PPanel's default algorithm
|
||||
return VerifyPassWord(password, hash)
|
||||
case "bcrypt":
|
||||
// Bcrypt (corresponding to PHP's password_hash/password_verify)
|
||||
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
||||
return err == nil
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@ -1,7 +1,15 @@
|
||||
package tool
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEncodePassWord(t *testing.T) {
|
||||
t.Logf("EncodePassWord: %v", EncodePassWord("password"))
|
||||
}
|
||||
|
||||
func TestMultiPasswordVerify(t *testing.T) {
|
||||
pwd := "$2y$10$WFO17pdtohfeBILjEChoGeVxpDG.u9kVCKhjDAeEeNmCjIlj3tDRy"
|
||||
status := MultiPasswordVerify("bcrypt", "", "admin1", pwd)
|
||||
t.Logf("MultiPasswordVerify: %v", status)
|
||||
}
|
||||
|
||||
@ -57,6 +57,7 @@ const (
|
||||
CouponAlreadyUsed uint32 = 50002 // Coupon has already been used
|
||||
CouponNotApplicable uint32 = 50003 // Coupon does not match the order or conditions
|
||||
CouponInsufficientUsage uint32 = 50004 // Coupon has insufficient remaining uses
|
||||
CouponExpired uint32 = 50005 // Coupon is expired
|
||||
)
|
||||
|
||||
// Subscribe
|
||||
|
||||
@ -46,6 +46,7 @@ func init() {
|
||||
CouponAlreadyUsed: "Coupon has already been used",
|
||||
CouponNotApplicable: "Coupon does not match the order or conditions",
|
||||
CouponInsufficientUsage: "Coupon has insufficient remaining uses",
|
||||
CouponExpired: "Coupon is expired",
|
||||
|
||||
// Subscribe
|
||||
SubscribeExpired: "Subscribe is expired",
|
||||
|
||||
@ -3,7 +3,6 @@ package handler
|
||||
import (
|
||||
"github.com/hibiken/asynq"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
countrylogic "github.com/perfect-panel/server/queue/logic/country"
|
||||
orderLogic "github.com/perfect-panel/server/queue/logic/order"
|
||||
smslogic "github.com/perfect-panel/server/queue/logic/sms"
|
||||
"github.com/perfect-panel/server/queue/logic/subscription"
|
||||
@ -15,8 +14,6 @@ import (
|
||||
)
|
||||
|
||||
func RegisterHandlers(mux *asynq.ServeMux, serverCtx *svc.ServiceContext) {
|
||||
// get country task
|
||||
mux.Handle(types.ForthwithGetCountry, countrylogic.NewGetNodeCountryLogic(serverCtx))
|
||||
// Send email task
|
||||
mux.Handle(types.ForthwithSendEmail, emailLogic.NewSendEmailLogic(serverCtx))
|
||||
// Send sms task
|
||||
|
||||
@ -1,22 +0,0 @@
|
||||
package countrylogic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hibiken/asynq"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
)
|
||||
|
||||
type GetNodeCountryLogic struct {
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetNodeCountryLogic(svcCtx *svc.ServiceContext) *GetNodeCountryLogic {
|
||||
return &GetNodeCountryLogic{
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
func (l *GetNodeCountryLogic) ProcessTask(ctx context.Context, task *asynq.Task) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -223,6 +223,7 @@ func (l *ActivateOrderLogic) createGuestUser(ctx context.Context, orderInfo *ord
|
||||
|
||||
userInfo := &user.User{
|
||||
Password: tool.EncodePassWord(tempOrder.Password),
|
||||
Algo: "default",
|
||||
AuthMethods: []user.AuthMethods{
|
||||
{
|
||||
AuthType: tempOrder.AuthType,
|
||||
|
||||
52
queue/logic/task/rateLogic.go
Normal file
52
queue/logic/task/rateLogic.go
Normal file
@ -0,0 +1,52 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hibiken/asynq"
|
||||
"github.com/perfect-panel/server/internal/svc"
|
||||
"github.com/perfect-panel/server/pkg/exchangeRate"
|
||||
"github.com/perfect-panel/server/pkg/logger"
|
||||
"github.com/perfect-panel/server/pkg/tool"
|
||||
)
|
||||
|
||||
type RateLogic struct {
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewRateLogic(svcCtx *svc.ServiceContext) *RateLogic {
|
||||
return &RateLogic{
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *RateLogic) ProcessTask(ctx context.Context, _ *asynq.Task) error {
|
||||
// Retrieve system currency configuration
|
||||
currency, err := l.svcCtx.SystemModel.GetCurrencyConfig(ctx)
|
||||
if err != nil {
|
||||
logger.Errorw("[PurchaseCheckout] GetCurrencyConfig error", logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
// Parse currency configuration
|
||||
configs := struct {
|
||||
CurrencyUnit string
|
||||
CurrencySymbol string
|
||||
AccessKey string
|
||||
}{}
|
||||
tool.SystemConfigSliceReflectToStruct(currency, &configs)
|
||||
|
||||
// Skip conversion if no exchange rate API key configured
|
||||
if configs.AccessKey == "" {
|
||||
logger.Debugf("[RateLogic] skip exchange rate, no access key configured")
|
||||
return nil
|
||||
}
|
||||
// Update exchange rates
|
||||
result, err := exchangeRate.GetExchangeRete(configs.CurrencyUnit, "CNY", configs.AccessKey, 1)
|
||||
if err != nil {
|
||||
logger.Errorw("[RateLogic] GetExchangeRete error", logger.Field("error", err.Error()))
|
||||
return err
|
||||
}
|
||||
l.svcCtx.ExchangeRate = result
|
||||
logger.WithContext(ctx).Infof("[RateLogic] GetExchangeRete success, result: %+v", result)
|
||||
return nil
|
||||
}
|
||||
@ -167,7 +167,7 @@ func (l *StatLogic) ProcessTask(ctx context.Context, _ *asynq.Task) error {
|
||||
|
||||
// Delete old traffic logs
|
||||
if l.svc.Config.Log.AutoClear {
|
||||
err = tx.WithContext(ctx).Model(&traffic.TrafficLog{}).Where("created_at <= ?", end.AddDate(0, 0, int(-l.svc.Config.Log.ClearDays))).Delete(&traffic.TrafficLog{}).Error
|
||||
err = tx.WithContext(ctx).Model(&traffic.TrafficLog{}).Where("timestamp <= ?", end.AddDate(0, 0, int(-l.svc.Config.Log.ClearDays))).Delete(&traffic.TrafficLog{}).Error
|
||||
if err != nil {
|
||||
logger.Errorf("[Traffic Stat Queue] Delete server traffic log failed: %v", err.Error())
|
||||
}
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
package types
|
||||
|
||||
const (
|
||||
// ForthwithGetCountry forthwith country get
|
||||
ForthwithGetCountry = "forthwith:country:get"
|
||||
)
|
||||
|
||||
type GetNodeCountry struct {
|
||||
Protocol string `json:"protocol"`
|
||||
ServerAddr string `json:"server_addr"`
|
||||
}
|
||||
@ -6,4 +6,7 @@ const (
|
||||
|
||||
// ForthwithQuotaTask create quota task immediately
|
||||
ForthwithQuotaTask = "forthwith:quota:task"
|
||||
|
||||
// SchedulerExchangeRate fetch exchange rate task
|
||||
SchedulerExchangeRate = "scheduler:exchange:rate"
|
||||
)
|
||||
|
||||
@ -46,6 +46,12 @@ func (m *Service) Start() {
|
||||
logger.Errorf("register traffic stat task failed: %s", err.Error())
|
||||
}
|
||||
|
||||
// schedule update exchange rate task: every day at 01:00
|
||||
rateTask := asynq.NewTask(types.ForthwithQuotaTask, nil)
|
||||
if _, err := m.server.Register("0 1 * * *", rateTask, asynq.MaxRetry(3)); err != nil {
|
||||
logger.Errorf("register update exchange rate task failed: %s", err.Error())
|
||||
}
|
||||
|
||||
if err := m.server.Run(); err != nil {
|
||||
logger.Errorf("run scheduler failed: %s", err.Error())
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user