This commit is contained in:
unknown
2026-02-04 20:27:13 +08:00
commit 3b042d1dad
9410 changed files with 1488147 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package accesskeys
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
)
type CreatePopupAction struct {
actionutils.ParentAction
}
func (this *CreatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreatePopupAction) RunGet(params struct {
UserId int64
}) {
this.Data["userId"] = params.UserId
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
UserId int64
Description string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
params.Must.
Field("description", params.Description).
Require("请输入备注")
accessKeyIdResp, err := this.RPC().UserAccessKeyRPC().CreateUserAccessKey(this.AdminContext(), &pb.CreateUserAccessKeyRequest{
UserId: params.UserId,
Description: params.Description,
})
if err != nil {
this.ErrorPage(err)
return
}
defer this.CreateLogInfo(codes.UserAccessKey_LogCreateUserAccessKey, accessKeyIdResp.UserAccessKeyId)
this.Success()
}

View File

@@ -0,0 +1,25 @@
package accesskeys
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
AccessKeyId int64
}) {
defer this.CreateLogInfo(codes.UserAccessKey_LogDeleteUserAccessKey, params.AccessKeyId)
_, err := this.RPC().UserAccessKeyRPC().DeleteUserAccessKey(this.AdminContext(), &pb.DeleteUserAccessKeyRequest{UserAccessKeyId: params.AccessKeyId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,54 @@
// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package accesskeys
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/users/userutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "accessKey")
}
func (this *IndexAction) RunGet(params struct {
UserId int64
}) {
err := userutils.InitUser(this.Parent(), params.UserId)
if err != nil {
this.ErrorPage(err)
return
}
accessKeysResp, err := this.RPC().UserAccessKeyRPC().FindAllEnabledUserAccessKeys(this.AdminContext(), &pb.FindAllEnabledUserAccessKeysRequest{UserId: params.UserId})
if err != nil {
this.ErrorPage(err)
return
}
accessKeyMaps := []maps.Map{}
for _, accessKey := range accessKeysResp.UserAccessKeys {
var accessedTime string
if accessKey.AccessedAt > 0 {
accessedTime = timeutil.FormatTime("Y-m-d H:i:s", accessKey.AccessedAt)
}
accessKeyMaps = append(accessKeyMaps, maps.Map{
"id": accessKey.Id,
"isOn": accessKey.IsOn,
"uniqueId": accessKey.UniqueId,
"secret": accessKey.Secret,
"description": accessKey.Description,
"accessedTime": accessedTime,
})
}
this.Data["accessKeys"] = accessKeyMaps
this.Show()
}

View File

@@ -0,0 +1,29 @@
package accesskeys
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type UpdateIsOnAction struct {
actionutils.ParentAction
}
func (this *UpdateIsOnAction) RunPost(params struct {
AccessKeyId int64
IsOn bool
}) {
defer this.CreateLogInfo(codes.UserAccessKey_LogUpdateUserAccessKeyIsOn, params.AccessKeyId)
_, err := this.RPC().UserAccessKeyRPC().UpdateUserAccessKeyIsOn(this.AdminContext(), &pb.UpdateUserAccessKeyIsOnRequest{
UserAccessKeyId: params.AccessKeyId,
IsOn: params.IsOn,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,179 @@
package users
import (
"encoding/json"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/utils/numberutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/xlzd/gotp"
)
type CreatePopupAction struct {
actionutils.ParentAction
}
func (this *CreatePopupAction) Init() {
this.Nav("", "", "")
}
func (this *CreatePopupAction) RunGet(params struct{}) {
this.Show()
}
func (this *CreatePopupAction) RunPost(params struct {
Username string
Pass1 string
Pass2 string
Fullname string
Mobile string
Tel string
Email string
Remark string
ClusterId int64
FeaturesType string
// OTP
OtpOn bool
Must *actions.Must
CSRF *actionutils.CSRF
}) {
var userId int64
defer func() {
this.CreateLogInfo(codes.User_LogCreateUser, userId)
}()
params.Must.
Field("username", params.Username).
Require("请输入用户名").
Match(`^[a-zA-Z0-9_]+$`, "用户名中只能含有英文、数字和下划线")
checkUsernameResp, err := this.RPC().UserRPC().CheckUserUsername(this.AdminContext(), &pb.CheckUserUsernameRequest{
UserId: 0,
Username: params.Username,
})
if err != nil {
this.ErrorPage(err)
return
}
if checkUsernameResp.Exists {
this.FailField("username", "此用户名已经被占用,请换一个")
}
params.Must.
Field("pass1", params.Pass1).
Require("请输入密码").
Field("pass2", params.Pass2).
Require("请再次输入确认密码").
Equal(params.Pass1, "两次输入的密码不一致")
params.Must.
Field("fullname", params.Fullname).
Require("请输入全名")
if params.ClusterId <= 0 {
this.Fail("请选择关联集群")
}
if len(params.Mobile) > 0 {
params.Must.
Field("mobile", params.Mobile).
Mobile("请输入正确的手机号")
}
if len(params.Email) > 0 {
params.Must.
Field("email", params.Email).
Email("请输入正确的电子邮箱")
}
createResp, err := this.RPC().UserRPC().CreateUser(this.AdminContext(), &pb.CreateUserRequest{
Username: params.Username,
Password: params.Pass1,
Fullname: params.Fullname,
Mobile: params.Mobile,
Tel: params.Tel,
Email: params.Email,
Remark: params.Remark,
Source: "admin:" + numberutils.FormatInt64(this.AdminId()),
NodeClusterId: params.ClusterId,
})
if err != nil {
this.ErrorPage(err)
return
}
userId = createResp.UserId
// 功能
if teaconst.IsPlus {
if params.FeaturesType == "default" {
resp, err := this.RPC().SysSettingRPC().ReadSysSetting(this.AdminContext(), &pb.ReadSysSettingRequest{Code: systemconfigs.SettingCodeUserRegisterConfig})
if err != nil {
this.ErrorPage(err)
return
}
var config = userconfigs.DefaultUserRegisterConfig()
if len(resp.ValueJSON) > 0 {
err = json.Unmarshal(resp.ValueJSON, config)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().UserRPC().UpdateUserFeatures(this.AdminContext(), &pb.UpdateUserFeaturesRequest{
UserId: userId,
FeatureCodes: config.Features,
})
if err != nil {
this.ErrorPage(err)
return
}
}
} else if params.FeaturesType == "all" {
featuresResp, err := this.RPC().UserRPC().FindAllUserFeatureDefinitions(this.AdminContext(), &pb.FindAllUserFeatureDefinitionsRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var featureCodes = []string{}
for _, def := range featuresResp.Features {
featureCodes = append(featureCodes, def.Code)
}
_, err = this.RPC().UserRPC().UpdateUserFeatures(this.AdminContext(), &pb.UpdateUserFeaturesRequest{
UserId: userId,
FeatureCodes: featureCodes,
})
if err != nil {
this.ErrorPage(err)
return
}
}
}
// OTP
if params.OtpOn {
_, err = this.RPC().LoginRPC().UpdateLogin(this.AdminContext(), &pb.UpdateLoginRequest{Login: &pb.Login{
Id: 0,
Type: "otp",
ParamsJSON: maps.Map{
"secret": gotp.RandomSecret(16), // TODO 改成可以设置secret长度
}.AsJSON(),
IsOn: true,
AdminId: 0,
UserId: userId,
}})
if err != nil {
this.ErrorPage(err)
return
}
}
this.Success()
}

View File

@@ -0,0 +1,27 @@
package users
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type DeleteAction struct {
actionutils.ParentAction
}
func (this *DeleteAction) RunPost(params struct {
UserId int64
}) {
defer this.CreateLogInfo(codes.User_LogDeleteUser, params.UserId)
// TODO 检查用户是否有未完成的业务
_, err := this.RPC().UserRPC().DeleteUser(this.AdminContext(), &pb.DeleteUserRequest{UserId: params.UserId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,79 @@
package users
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/users/userutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
)
type FeaturesAction struct {
actionutils.ParentAction
}
func (this *FeaturesAction) Init() {
this.Nav("", "", "feature")
}
func (this *FeaturesAction) RunGet(params struct {
UserId int64
}) {
err := userutils.InitUser(this.Parent(), params.UserId)
if err != nil {
this.ErrorPage(err)
return
}
featuresResp, err := this.RPC().UserRPC().FindAllUserFeatureDefinitions(this.AdminContext(), &pb.FindAllUserFeatureDefinitionsRequest{})
if err != nil {
this.ErrorPage(err)
return
}
allFeatures := featuresResp.Features
userFeaturesResp, err := this.RPC().UserRPC().FindUserFeatures(this.AdminContext(), &pb.FindUserFeaturesRequest{UserId: params.UserId})
if err != nil {
this.ErrorPage(err)
return
}
userFeatureCodes := []string{}
for _, userFeature := range userFeaturesResp.Features {
userFeatureCodes = append(userFeatureCodes, userFeature.Code)
}
featureMaps := []maps.Map{}
for _, feature := range allFeatures {
featureMaps = append(featureMaps, maps.Map{
"name": feature.Name,
"code": feature.Code,
"description": feature.Description,
"isChecked": lists.ContainsString(userFeatureCodes, feature.Code),
})
}
this.Data["features"] = featureMaps
this.Show()
}
func (this *FeaturesAction) RunPost(params struct {
UserId int64
Codes []string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.User_LogUpdateUserFeatures, params.UserId)
_, err := this.RPC().UserRPC().UpdateUserFeatures(this.AdminContext(), &pb.UpdateUserFeaturesRequest{
UserId: params.UserId,
FeatureCodes: params.Codes,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,67 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package identity
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/users/userutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/iwind/TeaGo/maps"
)
type EnterpriseAction struct {
actionutils.ParentAction
}
func (this *EnterpriseAction) Init() {
this.Nav("", "", "identity")
}
func (this *EnterpriseAction) RunGet(params struct {
UserId int64
}) {
err := userutils.InitUser(this.Parent(), params.UserId)
if err != nil {
this.ErrorPage(err)
return
}
userIdentityResp, err := this.RPC().UserIdentityRPC().FindEnabledUserIdentityWithOrgType(this.AdminContext(), &pb.FindEnabledUserIdentityWithOrgTypeRequest{
UserId: params.UserId,
OrgType: userconfigs.UserIdentityOrgTypeEnterprise,
})
if err != nil {
this.ErrorPage(err)
return
}
var identity = userIdentityResp.UserIdentity
if identity == nil {
this.Data["identity"] = nil
this.Show()
return
}
if identity.FileIds == nil {
identity.FileIds = []int64{0}
}
var frontFileId int64 = 0
if len(identity.FileIds) > 0 {
frontFileId = identity.FileIds[0]
}
this.Data["identity"] = maps.Map{
"id": identity.Id,
"realName": identity.RealName,
"number": identity.Number,
"fileIds": identity.FileIds,
"frontFileId": frontFileId,
"status": identity.Status,
"rejectReason": identity.RejectReason,
}
this.Show()
}

View File

@@ -0,0 +1,72 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package identity
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/users/userutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "identity")
}
func (this *IndexAction) RunGet(params struct {
UserId int64
}) {
err := userutils.InitUser(this.Parent(), params.UserId)
if err != nil {
this.ErrorPage(err)
return
}
userIdentityResp, err := this.RPC().UserIdentityRPC().FindEnabledUserIdentityWithOrgType(this.AdminContext(), &pb.FindEnabledUserIdentityWithOrgTypeRequest{
UserId: params.UserId,
OrgType: userconfigs.UserIdentityOrgTypeIndividual,
})
if err != nil {
this.ErrorPage(err)
return
}
var identity = userIdentityResp.UserIdentity
if identity == nil {
this.Data["identity"] = nil
this.Show()
return
}
if identity.FileIds == nil {
identity.FileIds = []int64{0, 0}
}
var backFileId int64 = 0
var frontFileId int64 = 0
if len(identity.FileIds) > 0 {
backFileId = identity.FileIds[0]
}
if len(identity.FileIds) > 1 {
frontFileId = identity.FileIds[1]
}
this.Data["identity"] = maps.Map{
"id": identity.Id,
"realName": identity.RealName,
"number": identity.Number,
"fileIds": identity.FileIds,
"backFileId": backFileId,
"frontFileId": frontFileId,
"status": identity.Status,
"rejectReason": identity.RejectReason,
}
this.Show()
}

View File

@@ -0,0 +1,33 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package identity
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type RejectAction struct {
actionutils.ParentAction
}
func (this *RejectAction) RunPost(params struct {
UserId int64
IdentityId int64
Reason string
}) {
defer this.CreateLogInfo(codes.UserIdentity_LogRejectUserIdentity, params.UserId)
_, err := this.RPC().UserIdentityRPC().RejectUserIdentity(this.AdminContext(), &pb.RejectUserIdentityRequest{
UserIdentityId: params.IdentityId,
Reason: params.Reason,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,31 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package identity
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type ResetAction struct {
actionutils.ParentAction
}
func (this *ResetAction) RunPost(params struct {
UserId int64
IdentityId int64
}) {
defer this.CreateLogInfo(codes.UserIdentity_LogResetUserIdentity, params.UserId)
_, err := this.RPC().UserIdentityRPC().ResetUserIdentity(this.AdminContext(), &pb.ResetUserIdentityRequest{
UserIdentityId: params.IdentityId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,31 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package identity
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type VerifyAction struct {
actionutils.ParentAction
}
func (this *VerifyAction) RunPost(params struct {
UserId int64
IdentityId int64
}) {
defer this.CreateLogInfo(codes.UserIdentity_LogVerifyUserIdentity, params.UserId)
_, err := this.RPC().UserIdentityRPC().VerifyUserIdentity(this.AdminContext(), &pb.VerifyUserIdentityRequest{
UserIdentityId: params.IdentityId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,99 @@
package users
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "")
}
func (this *IndexAction) RunGet(params struct {
Keyword string
Verifying bool
MobileIsVerified int32 `default:"-1"`
}) {
this.Data["keyword"] = params.Keyword
this.Data["isVerifying"] = params.Verifying
this.Data["mobileIsVerified"] = params.MobileIsVerified
// 未审核的总数量
countVerifyingUsersResp, err := this.RPC().UserRPC().CountAllEnabledUsers(this.AdminContext(), &pb.CountAllEnabledUsersRequest{
IsVerifying: true,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Data["countVerifyingUsers"] = countVerifyingUsersResp.Count
// 当前匹配的数量
countResp, err := this.RPC().UserRPC().CountAllEnabledUsers(this.AdminContext(), &pb.CountAllEnabledUsersRequest{
Keyword: params.Keyword,
IsVerifying: params.Verifying,
MobileIsVerified: params.MobileIsVerified,
})
if err != nil {
this.ErrorPage(err)
return
}
count := countResp.Count
page := this.NewPage(count)
this.Data["page"] = page.AsHTML()
usersResp, err := this.RPC().UserRPC().ListEnabledUsers(this.AdminContext(), &pb.ListEnabledUsersRequest{
Keyword: params.Keyword,
IsVerifying: params.Verifying,
MobileIsVerified: params.MobileIsVerified,
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
var userMaps = []maps.Map{}
for _, user := range usersResp.Users {
var clusterMap maps.Map = nil
if user.NodeCluster != nil {
clusterMap = maps.Map{
"id": user.NodeCluster.Id,
"name": user.NodeCluster.Name,
}
}
isSubmittedResp, err := this.RPC().UserIdentityRPC().CheckUserIdentityIsSubmitted(this.AdminContext(), &pb.CheckUserIdentityIsSubmittedRequest{UserId: user.Id})
if err != nil {
this.ErrorPage(err)
return
}
var identityIsSubmitted = isSubmittedResp.IsSubmitted
userMaps = append(userMaps, maps.Map{
"id": user.Id,
"username": user.Username,
"isOn": user.IsOn,
"fullname": user.Fullname,
"email": user.Email,
"mobile": user.Mobile,
"tel": user.Tel,
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", user.CreatedAt),
"cluster": clusterMap,
"registeredIP": user.RegisteredIP,
"isVerified": user.IsVerified,
"isRejected": user.IsRejected,
"identityIsSubmitted": identityIsSubmitted,
"verifiedMobile": user.VerifiedMobile,
})
}
this.Data["users"] = userMaps
this.Show()
}

View File

@@ -0,0 +1,38 @@
package users
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/users/accesskeys"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeUser)).
Data("teaMenu", "users").
Prefix("/users").
Data("teaSubMenu", "users").
Get("", new(IndexAction)).
GetPost("/createPopup", new(CreatePopupAction)).
// 单个用户信息
Get("/user", new(UserAction)).
GetPost("/update", new(UpdateAction)).
Post("/delete", new(DeleteAction)).
GetPost("/features", new(FeaturesAction)).
GetPost("/verifyPopup", new(VerifyPopupAction)).
Get("/otpQrcode", new(OtpQrcodeAction)).
// AccessKeys
Prefix("/users/accesskeys").
Get("", new(accesskeys.IndexAction)).
GetPost("/createPopup", new(accesskeys.CreatePopupAction)).
Post("/delete", new(accesskeys.DeleteAction)).
Post("/updateIsOn", new(accesskeys.UpdateIsOnAction)).
//
EndAll()
})
}

View File

@@ -0,0 +1,51 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package users
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/users/identity"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/users/setting"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/users/user"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeUser)).
Data("teaMenu", "users").
// 用户相关
Prefix("/users/user").
Data("teaSubMenu", "users").
Get("/servers", new(user.ServersAction)).
EndPrefix().
// 设置
Prefix("/users/setting").
Data("teaSubMenu", "setting").
GetPost("", new(setting.IndexAction)).
GetPost("/server", new(setting.ServerAction)).
GetPost("/email", new(setting.EmailAction)).
GetPost("/emailTest", new(setting.EmailTestAction)).
GetPost("/sms", new(setting.SmsAction)).
GetPost("/smsTest", new(setting.SmsTestAction)).
EndPrefix().
// 实名认证
Prefix("/users/identity").
Data("teaSubMenu", "users").
Get("", new(identity.IndexAction)).
Get("/enterprise", new(identity.EnterpriseAction)).
Post("/verify", new(identity.VerifyAction)).
Post("/reject", new(identity.RejectAction)).
Post("/reset", new(identity.ResetAction)).
EndPrefix().
//
EndAll()
})
}

View File

@@ -0,0 +1,78 @@
package users
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/utils/otputils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
"github.com/skip2/go-qrcode"
"github.com/xlzd/gotp"
)
type OtpQrcodeAction struct {
actionutils.ParentAction
}
func (this *OtpQrcodeAction) Init() {
this.Nav("", "", "")
}
func (this *OtpQrcodeAction) RunGet(params struct {
UserId int64
Download bool
}) {
loginResp, err := this.RPC().LoginRPC().FindEnabledLogin(this.AdminContext(), &pb.FindEnabledLoginRequest{
UserId: params.UserId,
Type: "otp",
})
if err != nil {
this.ErrorPage(err)
return
}
var login = loginResp.Login
if login == nil || !login.IsOn {
this.NotFound("userLogin", params.UserId)
return
}
var loginParams = maps.Map{}
err = json.Unmarshal(login.ParamsJSON, &loginParams)
if err != nil {
this.ErrorPage(err)
return
}
var secret = loginParams.GetString("secret")
// 当前用户信息
userResp, err := this.RPC().UserRPC().FindEnabledUser(this.AdminContext(), &pb.FindEnabledUserRequest{UserId: params.UserId})
if err != nil {
this.ErrorPage(err)
return
}
var user = userResp.User
if user == nil {
this.NotFound("user", params.UserId)
return
}
productName, err := this.findProductName()
if err != nil {
this.ErrorPage(err)
return
}
var url = gotp.NewDefaultTOTP(secret).ProvisioningUri(user.Username, productName)
data, err := qrcode.Encode(otputils.FixIssuer(url), qrcode.Medium, 256)
if err != nil {
this.ErrorPage(err)
return
}
if params.Download {
var filename = "OTP-USER-" + user.Username + ".png"
this.AddHeader("Content-Disposition", "attachment; filename=\""+filename+"\";")
}
this.AddHeader("Content-Type", "image/png")
this.AddHeader("Content-Length", types.String(len(data)))
_, _ = this.Write(data)
}

View File

@@ -0,0 +1,18 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build !plus
package users
import "github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
func (this *OtpQrcodeAction) findProductName() (string, error) {
uiConfig, err := configloaders.LoadAdminUIConfig()
if err != nil {
return "", err
}
var productName = uiConfig.ProductName
if len(productName) == 0 {
productName = "GoEdge用户"
}
return productName, nil
}

View File

@@ -0,0 +1,18 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package users
import "github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
func (this *OtpQrcodeAction) findProductName() (string, error) {
uiConfig, err := configloaders.LoadUserUIConfig()
if err != nil {
return "", err
}
var productName = uiConfig.ProductName
if len(productName) == 0 {
productName = "GoEdge用户"
}
return productName, nil
}

View File

@@ -0,0 +1,130 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package setting
import (
"encoding/json"
"errors"
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/iwind/TeaGo/actions"
)
type EmailAction struct {
actionutils.ParentAction
}
func (this *EmailAction) Init() {
this.Nav("", "", "email")
}
func (this *EmailAction) RunGet(params struct{}) {
config, err := configloaders.LoadUserSenderConfig()
if err != nil {
this.ErrorPage(err)
return
}
if config == nil {
this.ErrorPage(errors.New("'config' should not be nil"))
return
}
// 验证邮件
if config.VerifyEmail == nil {
config.VerifyEmail = userconfigs.NewEmailSenderConfig()
}
// 通知邮件
if config.NotifyEmail == nil {
config.NotifyEmail = userconfigs.NewEmailSenderConfig()
}
this.Data["config"] = config
this.Show()
}
func (this *EmailAction) RunPost(params struct {
VerifyEmailJSON []byte
NotifyEmailJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.User_LogUpdateUserEmailSettings)
config, err := configloaders.LoadUserSenderConfig()
if err != nil {
this.ErrorPage(err)
return
}
var verifyEmailConfig = userconfigs.NewEmailSenderConfig()
err = json.Unmarshal(params.VerifyEmailJSON, verifyEmailConfig)
if err != nil {
this.ErrorPage(err)
return
}
config.VerifyEmail = verifyEmailConfig
if verifyEmailConfig.IsOn {
params.Must.
Field("verifyEmailJSONSmtpHost", verifyEmailConfig.SMTPHost).
Require("请输入SMTP地址").
Field("verifyEmailJSONSmtpPort", verifyEmailConfig.SMTPPort).
Gt(0, "请输入正确的SMTP端口").
Field("verifyEmailJSONUsername", verifyEmailConfig.Username).
Require("请输入用户名").
Field("verifyEmailJSONPassword", verifyEmailConfig.Password).
Require("请输入密码").
Field("verifyEmailJSONFromEmail", verifyEmailConfig.FromEmail).
Require("请输入发件人Email").
Email("发件人Email格式不正确")
}
var notifyEmailConfig = userconfigs.NewEmailSenderConfig()
err = json.Unmarshal(params.NotifyEmailJSON, notifyEmailConfig)
if err != nil {
this.ErrorPage(err)
return
}
config.NotifyEmail = notifyEmailConfig
if notifyEmailConfig.IsOn {
params.Must.
Field("notifyEmailJSONSmtpHost", notifyEmailConfig.SMTPHost).
Require("请输入SMTP地址").
Field("notifyEmailJSONSmtpPort", notifyEmailConfig.SMTPPort).
Gt(0, "请输入正确的SMTP端口").
Field("notifyEmailJSONUsername", notifyEmailConfig.Username).
Require("请输入用户名").
Field("notifyEmailJSONPassword", notifyEmailConfig.Password).
Require("请输入密码").
Field("notifyEmailJSONFromEmail", notifyEmailConfig.FromEmail).
Require("请输入发件人Email").
Email("发件人Email格式不正确")
}
configJSON, err := json.Marshal(config)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().SysSettingRPC().UpdateSysSetting(this.AdminContext(), &pb.UpdateSysSettingRequest{
Code: systemconfigs.SettingCodeUserSenderConfig,
ValueJSON: configJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,101 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package setting
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
)
type EmailTestAction struct {
actionutils.ParentAction
}
func (this *EmailTestAction) Init() {
this.Nav("", "", "")
}
func (this *EmailTestAction) RunGet(params struct{}) {
this.Show()
}
func (this *EmailTestAction) RunPost(params struct {
ConfigJSON []byte
ToEmail string
Subject string
Body string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
if len(params.ConfigJSON) == 0 {
this.Fail("读取不到邮件配置")
return
}
var config = userconfigs.NewEmailSenderConfig()
err := json.Unmarshal(params.ConfigJSON, config)
if err != nil {
this.Fail("解析邮件配置出错:" + err.Error())
return
}
if len(config.SMTPHost) == 0 {
this.Fail("缺少SMTP地址")
return
}
if config.SMTPPort <= 0 {
this.Fail("缺少SMTP端口")
return
}
if len(config.Username) == 0 {
this.Fail("缺少用户名")
return
}
if len(config.Password) == 0 {
this.Fail("缺少密码")
return
}
if len(config.FromEmail) == 0 {
this.Fail("缺少发件人Email")
return
}
params.Must.Field("fromEmail", config.FromEmail).
Email("发件人Email格式不正确")
params.Must.
Field("toEmail", params.ToEmail).
Require("请输入收件人Email").
Email("请输入正确的收件人Email").
Field("subject", params.Subject).
Require("请输入测试标题").
Field("body", params.Body).
Require("请输入测试内容")
// 发送测试
_, err = this.RPC().MessageMediaRPC().SendMediaMessage(this.AdminContext(), &pb.SendMediaMessageRequest{
MediaType: "email",
OptionsJSON: maps.Map{
"smtp": config.SMTPHost + ":" + types.String(config.SMTPPort),
"username": config.Username,
"password": config.Password,
"from": config.FromEmail,
"fromName": config.FromName,
}.AsJSON(),
User: params.ToEmail,
Subject: params.Subject,
Body: params.Body,
})
if err != nil {
this.Fail("发送失败:" + err.Error())
return
}
this.Success()
}

View File

@@ -0,0 +1,172 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package setting
import (
"encoding/json"
teaconst "github.com/TeaOSLab/EdgeAdmin/internal/const"
"github.com/TeaOSLab/EdgeAdmin/internal/plus"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "index")
}
func (this *IndexAction) RunGet(params struct{}) {
if !teaconst.IsPlus {
this.RedirectURL("/users")
return
}
// 当前配置
resp, err := this.RPC().SysSettingRPC().ReadSysSetting(this.AdminContext(), &pb.ReadSysSettingRequest{Code: systemconfigs.SettingCodeUserRegisterConfig})
if err != nil {
this.ErrorPage(err)
return
}
var config = userconfigs.DefaultUserRegisterConfig()
if len(resp.ValueJSON) > 0 {
err = json.Unmarshal(resp.ValueJSON, config)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["config"] = config
// 功能列表
var featureMaps = []maps.Map{}
for _, feature := range userconfigs.FindAllUserFeatures() {
featureMaps = append(featureMaps, maps.Map{
"name": feature.Name,
"description": feature.Description,
"code": feature.Code,
"isChecked": lists.ContainsString(config.Features, feature.Code),
})
}
this.Data["features"] = featureMaps
// 当前高防
this.Data["adIsVisible"] = plus.AllowComponent(plus.ComponentCodeAntiDDoS)
// 当前默认的智能DNS设置
this.Data["nsIsVisible"] = plus.AllowComponent(plus.ComponentCodeNS)
this.Show()
}
func (this *IndexAction) RunPost(params struct {
IsOn bool
ComplexPassword bool
RequireVerification bool
RequireIdentity bool
CheckClientRegion bool
EmailVerificationIsOn bool
EmailVerificationShowNotice bool
EmailVerificationCanLogin bool
EmailVerificationSubject string
EmailVerificationBody string
EmailResetPasswordIsOn bool
EmailResetPasswordSubject string
EmailResetPasswordBody string
MobileVerificationIsOn bool
MobileVerificationShowNotice bool
MobileVerificationCanLogin bool
MobileVerificationForce bool
MobileVerificationBody string
CdnIsOn bool
ClusterId int64
Features []string
FeatureOp string
AdIsOn bool
NsIsOn bool
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.User_LogUpdateUserGlobalSettings)
params.Must.
Field("clusterId", params.ClusterId).
Gt(0, "请选择一个集群")
var config = userconfigs.DefaultUserRegisterConfig()
config.IsOn = params.IsOn
config.ComplexPassword = params.ComplexPassword
config.RequireVerification = params.RequireVerification
config.RequireIdentity = params.RequireIdentity
config.CheckClientRegion = params.CheckClientRegion
config.EmailVerification.IsOn = params.EmailVerificationIsOn
config.EmailVerification.CanLogin = params.EmailVerificationCanLogin
config.EmailVerification.Subject = params.EmailVerificationSubject
config.EmailVerification.Body = params.EmailVerificationBody
config.EmailVerification.ShowNotice = params.EmailVerificationShowNotice
config.MobileVerification.IsOn = params.MobileVerificationIsOn
config.MobileVerification.CanLogin = params.MobileVerificationCanLogin
config.MobileVerification.Force = params.MobileVerificationForce
config.MobileVerification.Body = params.MobileVerificationBody
config.MobileVerification.ShowNotice = params.MobileVerificationShowNotice
config.EmailResetPassword.IsOn = params.EmailResetPasswordIsOn
config.EmailResetPassword.Subject = params.EmailResetPasswordSubject
config.EmailResetPassword.Body = params.EmailResetPasswordBody
config.CDNIsOn = params.CdnIsOn
config.ClusterId = params.ClusterId
config.Features = params.Features
config.ADIsOn = params.AdIsOn
config.NSIsOn = params.NsIsOn
configJSON, err := json.Marshal(config)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().SysSettingRPC().UpdateSysSetting(this.AdminContext(), &pb.UpdateSysSettingRequest{
Code: systemconfigs.SettingCodeUserRegisterConfig,
ValueJSON: configJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
if params.FeatureOp != "keep" {
_, err = this.RPC().UserRPC().UpdateAllUsersFeatures(this.AdminContext(), &pb.UpdateAllUsersFeaturesRequest{
FeatureCodes: params.Features,
Overwrite: params.FeatureOp == "overwrite",
})
if err != nil {
this.ErrorPage(err)
return
}
}
this.Success()
}

View File

@@ -0,0 +1,110 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
//go:build plus
package setting
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type ServerAction struct {
actionutils.ParentAction
}
func (this *ServerAction) Init() {
this.Nav("", "", "server")
}
func (this *ServerAction) RunGet(params struct{}) {
// 当前配置
resp, err := this.RPC().SysSettingRPC().ReadSysSetting(this.AdminContext(), &pb.ReadSysSettingRequest{Code: systemconfigs.SettingCodeUserServerConfig})
if err != nil {
this.ErrorPage(err)
return
}
var config = userconfigs.DefaultUserServerConfig()
if len(resp.ValueJSON) > 0 {
err = json.Unmarshal(resp.ValueJSON, config)
if err != nil {
this.ErrorPage(err)
return
}
}
this.Data["config"] = config
// 分组
groupsResp, err := this.RPC().ServerGroupRPC().FindAllEnabledServerGroups(this.AdminContext(), &pb.FindAllEnabledServerGroupsRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var groupMaps = []maps.Map{}
for _, group := range groupsResp.ServerGroups {
groupMaps = append(groupMaps, maps.Map{
"id": group.Id,
"name": group.Name,
})
}
this.Data["groups"] = groupMaps
// 缓存
this.Data["defaultMaxCacheKeysPerTask"] = userconfigs.MaxCacheKeysPerTask
this.Data["defaultMaxCacheKeysPerDay"] = userconfigs.MaxCacheKeysPerDay
this.Show()
}
func (this *ServerAction) RunPost(params struct {
GroupId int64
RequirePlan bool
EnableStat bool
// 缓存相关
PurgeMaxKeysPerTask int32
PurgeMaxKeysPerDay int32
FetchMaxKeysPerTask int32
FetchMaxKeysPerDay int32
Must *actions.Must
CSRF *actionutils.CSRF
}) {
var config = userconfigs.DefaultUserServerConfig()
config.GroupId = params.GroupId
config.RequirePlan = params.RequirePlan
config.EnableStat = params.EnableStat
if config.HTTPCacheTaskPurgeConfig != nil {
config.HTTPCacheTaskPurgeConfig.MaxKeysPerTask = params.PurgeMaxKeysPerTask
config.HTTPCacheTaskPurgeConfig.MaxKeysPerDay = params.PurgeMaxKeysPerDay
}
if config.HTTPCacheTaskFetchConfig != nil {
config.HTTPCacheTaskFetchConfig.MaxKeysPerTask = params.FetchMaxKeysPerTask
config.HTTPCacheTaskFetchConfig.MaxKeysPerDay = params.FetchMaxKeysPerDay
}
configJSON, err := json.Marshal(config)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().SysSettingRPC().UpdateSysSetting(this.AdminContext(), &pb.UpdateSysSettingRequest{
Code: systemconfigs.SettingCodeUserServerConfig,
ValueJSON: configJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,259 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package setting
import (
"encoding/json"
"errors"
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/iwind/TeaGo/actions"
"regexp"
)
type SmsAction struct {
actionutils.ParentAction
}
func (this *SmsAction) Init() {
this.Nav("", "", "sms")
}
func (this *SmsAction) RunGet(params struct{}) {
config, err := configloaders.LoadUserSenderConfig()
if err != nil {
this.ErrorPage(err)
return
}
if config == nil {
this.ErrorPage(errors.New("'config' should not be nil"))
return
}
// 验证短信
if config.VerifySMS == nil {
config.VerifySMS = userconfigs.NewSMSSenderConfig()
}
// 通知短信
if config.NotifySMS == nil {
config.NotifySMS = userconfigs.NewSMSSenderConfig()
}
this.Data["config"] = config
this.Show()
}
func (this *SmsAction) RunPost(params struct {
VerifySMSJSON []byte
NotifySMSJSON []byte
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.User_LogUpdateUserSmsSettings)
config, err := configloaders.LoadUserSenderConfig()
if err != nil {
this.ErrorPage(err)
return
}
var verifySMSConfig = userconfigs.NewSMSSenderConfig()
err = json.Unmarshal(params.VerifySMSJSON, verifySMSConfig)
if err != nil {
this.ErrorPage(err)
return
}
config.VerifySMS = verifySMSConfig
if verifySMSConfig.IsOn {
switch verifySMSConfig.Type {
case userconfigs.SMSSenderWebHook:
if verifySMSConfig.WebHookParams == nil {
this.Fail("请配置HTTP接口相关参数")
return
}
if len(verifySMSConfig.WebHookParams.URL) == 0 {
this.Fail("请输入激活短信HTTP接口URL地址")
return
}
if !regexp.MustCompile(`^(http|https)://`).MatchString(verifySMSConfig.WebHookParams.URL) {
this.Fail("激活短信HTTP接口URL地址必须以http://或https://开头")
return
}
case userconfigs.SMSSenderAliyunSMS:
if verifySMSConfig.AliyunSMSParams == nil {
this.Fail("请配置阿里云短信相关参数")
return
}
if len(verifySMSConfig.AliyunSMSParams.Sign) == 0 {
this.Fail("请输入签名名称")
return
}
if len(verifySMSConfig.AliyunSMSParams.TemplateCode) == 0 {
this.Fail("请输入模板CODE")
return
}
if len(verifySMSConfig.AliyunSMSParams.CodeVarName) == 0 {
this.Fail("请输入模板中验证码变量名称")
return
}
if len(verifySMSConfig.AliyunSMSParams.AccessKeyId) == 0 {
this.Fail("请输入AccessKey ID")
return
}
if len(verifySMSConfig.AliyunSMSParams.AccessKeySecret) == 0 {
this.Fail("请输入AccessKey Secret")
return
}
case userconfigs.SMSSenderTencentSMS:
if verifySMSConfig.TencentSMSParams == nil {
this.Fail("请配置腾讯云短信相关参数")
return
}
var digitsReg = regexp.MustCompile(`^\d+$`)
if len(verifySMSConfig.TencentSMSParams.SDKAppId) == 0 {
this.Fail("请输入SDK应用ID")
return
}
if !digitsReg.MatchString(verifySMSConfig.TencentSMSParams.SDKAppId) {
this.Fail("SDK应用ID是一组数字请重新输入")
return
}
if len(verifySMSConfig.TencentSMSParams.Sign) == 0 {
this.Fail("请输入签名内容")
return
}
if len(verifySMSConfig.TencentSMSParams.TemplateId) == 0 {
this.Fail("请输入模板ID")
return
}
if !digitsReg.MatchString(verifySMSConfig.TencentSMSParams.TemplateId) {
this.Fail("正文模板ID是一组数字请重新输入")
return
}
if len(verifySMSConfig.TencentSMSParams.AccessKeyId) == 0 {
this.Fail("请输入密钥SecretId")
return
}
if len(verifySMSConfig.TencentSMSParams.AccessKeySecret) == 0 {
this.Fail("请输入密钥SecretKey")
return
}
}
}
var notifySMSConfig = userconfigs.NewSMSSenderConfig()
err = json.Unmarshal(params.NotifySMSJSON, notifySMSConfig)
if err != nil {
this.ErrorPage(err)
return
}
config.NotifySMS = notifySMSConfig
if notifySMSConfig.IsOn {
switch notifySMSConfig.Type {
case userconfigs.SMSSenderWebHook:
if notifySMSConfig.WebHookParams == nil {
this.Fail("请配置HTTP接口相关参数")
return
}
if len(notifySMSConfig.WebHookParams.URL) == 0 {
this.Fail("请输入通知短信HTTP接口URL地址")
return
}
if !regexp.MustCompile(`^(http|https)://`).MatchString(notifySMSConfig.WebHookParams.URL) {
this.Fail("通知短信HTTP接口URL地址必须以http://或https://开头")
return
}
case userconfigs.SMSSenderAliyunSMS:
if notifySMSConfig.AliyunSMSParams == nil {
this.Fail("请配置阿里云短信相关参数")
return
}
if len(notifySMSConfig.AliyunSMSParams.Sign) == 0 {
this.Fail("请输入签名名称")
return
}
if len(notifySMSConfig.AliyunSMSParams.TemplateCode) == 0 {
this.Fail("请输入模板CODE")
return
}
if len(notifySMSConfig.AliyunSMSParams.CodeVarName) == 0 {
this.Fail("请输入模板中验证码变量名称")
return
}
if len(notifySMSConfig.AliyunSMSParams.AccessKeyId) == 0 {
this.Fail("请输入AccessKey ID")
return
}
if len(notifySMSConfig.AliyunSMSParams.AccessKeySecret) == 0 {
this.Fail("请输入AccessKey Secret")
return
}
case userconfigs.SMSSenderTencentSMS:
if notifySMSConfig.TencentSMSParams == nil {
this.Fail("请配置腾讯云短信相关参数")
return
}
var digitsReg = regexp.MustCompile(`^\d+$`)
if len(notifySMSConfig.TencentSMSParams.SDKAppId) == 0 {
this.Fail("请输入SDK应用ID")
return
}
if !digitsReg.MatchString(notifySMSConfig.TencentSMSParams.SDKAppId) {
this.Fail("SDK应用ID是一组数字请重新输入")
return
}
if len(notifySMSConfig.TencentSMSParams.Sign) == 0 {
this.Fail("请输入签名内容")
return
}
if len(notifySMSConfig.TencentSMSParams.TemplateId) == 0 {
this.Fail("请输入模板ID")
return
}
if !digitsReg.MatchString(notifySMSConfig.TencentSMSParams.TemplateId) {
this.Fail("正文模板ID是一组数字请重新输入")
return
}
if len(notifySMSConfig.TencentSMSParams.AccessKeyId) == 0 {
this.Fail("请输入密钥SecretId")
return
}
if len(notifySMSConfig.TencentSMSParams.AccessKeySecret) == 0 {
this.Fail("请输入密钥SecretKey")
return
}
}
}
configJSON, err := json.Marshal(config)
if err != nil {
this.ErrorPage(err)
return
}
_, err = this.RPC().SysSettingRPC().UpdateSysSetting(this.AdminContext(), &pb.UpdateSysSettingRequest{
Code: systemconfigs.SettingCodeUserSenderConfig,
ValueJSON: configJSON,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,172 @@
// Copyright 2023 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package setting
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/iwind/TeaGo/actions"
"regexp"
)
type SmsTestAction struct {
actionutils.ParentAction
}
func (this *SmsTestAction) Init() {
this.Nav("", "", "")
}
func (this *SmsTestAction) RunGet(params struct{}) {
this.Show()
}
func (this *SmsTestAction) RunPost(params struct {
ConfigJSON []byte
ToMobile string
Body string
Code string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
if len(params.ConfigJSON) == 0 {
this.Fail("读取不到短信配置")
return
}
var config = userconfigs.NewSMSSenderConfig()
err := json.Unmarshal(params.ConfigJSON, config)
if err != nil {
this.Fail("解析短信配置出错:" + err.Error())
return
}
var paramsJSON []byte
switch config.Type {
case userconfigs.SMSSenderWebHook:
if config.WebHookParams == nil {
this.Fail("请配置HTTP接口相关参数")
return
}
if len(config.WebHookParams.URL) == 0 {
this.Fail("请输入短信HTTP接口URL地址")
return
}
if !regexp.MustCompile(`^(http|https)://`).MatchString(config.WebHookParams.URL) {
this.Fail("短信WebHook URL地址必须以http://或https://开头")
return
}
paramsJSON, err = json.Marshal(config.WebHookParams)
if err != nil {
this.ErrorPage(err)
return
}
case userconfigs.SMSSenderAliyunSMS:
if config.AliyunSMSParams == nil {
this.Fail("请配置阿里云短信相关参数")
return
}
if len(config.AliyunSMSParams.Sign) == 0 {
this.Fail("请输入签名名称")
return
}
if len(config.AliyunSMSParams.TemplateCode) == 0 {
this.Fail("请输入模板CODE")
return
}
if len(config.AliyunSMSParams.CodeVarName) == 0 {
this.Fail("请输入模板中验证码变量名称")
return
}
if len(config.AliyunSMSParams.AccessKeyId) == 0 {
this.Fail("请输入AccessKey ID")
return
}
if len(config.AliyunSMSParams.AccessKeySecret) == 0 {
this.Fail("请输入AccessKey Secret")
return
}
paramsJSON, err = json.Marshal(config.AliyunSMSParams)
if err != nil {
this.ErrorPage(err)
return
}
case userconfigs.SMSSenderTencentSMS:
if config.TencentSMSParams == nil {
this.Fail("请配置腾讯云短信相关参数")
return
}
var digitsReg = regexp.MustCompile(`^\d+$`)
if len(config.TencentSMSParams.SDKAppId) == 0 {
this.Fail("请输入SDK应用ID")
return
}
if !digitsReg.MatchString(config.TencentSMSParams.SDKAppId) {
this.Fail("SDK应用ID是一组数字请重新输入")
return
}
if len(config.TencentSMSParams.Sign) == 0 {
this.Fail("请输入签名内容")
return
}
if len(config.TencentSMSParams.TemplateId) == 0 {
this.Fail("请输入模板ID")
return
}
if !digitsReg.MatchString(config.TencentSMSParams.TemplateId) {
this.Fail("正文模板ID是一组数字请重新输入")
return
}
if len(config.TencentSMSParams.AccessKeyId) == 0 {
this.Fail("请输入密钥SecretId")
return
}
if len(config.TencentSMSParams.AccessKeySecret) == 0 {
this.Fail("请输入密钥SecretKey")
return
}
paramsJSON, err = json.Marshal(config.TencentSMSParams)
if err != nil {
this.ErrorPage(err)
return
}
default:
this.Fail("不支持渠道'" + config.Type + "'")
}
params.Must.
Field("toMobile", params.ToMobile).
Require("请输入收信人手机号").
Mobile("请输入正确的收信人手机号").
Field("body", params.Body).
Require("请输入测试内容")
// 发送测试
resp, err := this.RPC().SMSSenderRPC().SendSMS(this.AdminContext(), &pb.SendSMSRequest{
Mobile: params.ToMobile,
Body: params.Body,
Code: params.Code,
Type: config.Type,
ParamsJSON: paramsJSON,
})
if err != nil {
this.Fail("发送失败:" + err.Error())
return
}
if !resp.IsOk {
this.Fail("发送失败,结果:" + resp.Result)
return
}
this.Success()
}

View File

@@ -0,0 +1,218 @@
package users
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/users/userutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
"github.com/xlzd/gotp"
)
type UpdateAction struct {
actionutils.ParentAction
}
func (this *UpdateAction) Init() {
this.Nav("", "", "update")
}
func (this *UpdateAction) RunGet(params struct {
UserId int64
}) {
err := userutils.InitUser(this.Parent(), params.UserId)
if err != nil {
this.ErrorPage(err)
return
}
userResp, err := this.RPC().UserRPC().FindEnabledUser(this.AdminContext(), &pb.FindEnabledUserRequest{UserId: params.UserId})
if err != nil {
this.ErrorPage(err)
return
}
var user = userResp.User
if user == nil {
this.NotFound("user", params.UserId)
return
}
// AccessKey数量
countAccessKeyResp, err := this.RPC().UserAccessKeyRPC().CountAllEnabledUserAccessKeys(this.AdminContext(), &pb.CountAllEnabledUserAccessKeysRequest{UserId: params.UserId})
if err != nil {
this.ErrorPage(err)
return
}
var countAccessKeys = countAccessKeyResp.Count
// 是否有实名认证
hasNewIndividualIdentity, hasNewEnterpriseIdentity, identityTag, err := userutils.CheckUserIdentity(this.RPC(), this.AdminContext(), params.UserId)
if err != nil {
this.ErrorPage(err)
return
}
// OTP认证
var otpLoginIsOn = false
if user.OtpLogin != nil {
otpLoginIsOn = user.OtpLogin.IsOn
}
this.Data["user"] = maps.Map{
"id": user.Id,
"username": user.Username,
"fullname": user.Fullname,
"email": user.Email,
"tel": user.Tel,
"remark": user.Remark,
"mobile": user.Mobile,
"isOn": user.IsOn,
"countAccessKeys": countAccessKeys,
"bandwidthAlgo": user.BandwidthAlgo,
// 实名认证
"hasNewIndividualIdentity": hasNewIndividualIdentity,
"hasNewEnterpriseIdentity": hasNewEnterpriseIdentity,
"identityTag": identityTag,
// otp
"otpLoginIsOn": otpLoginIsOn,
}
this.Data["clusterId"] = 0
if user.NodeCluster != nil {
this.Data["clusterId"] = user.NodeCluster.Id
}
this.Show()
}
func (this *UpdateAction) RunPost(params struct {
UserId int64
Username string
Pass1 string
Pass2 string
Fullname string
Mobile string
Tel string
Email string
Remark string
IsOn bool
ClusterId int64
BandwidthAlgo string
// OTP
OtpOn bool
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.User_LogUpdateUser, params.UserId)
params.Must.
Field("username", params.Username).
Require("请输入用户名").
Match(`^[a-zA-Z0-9_]+$`, "用户名中只能含有英文、数字和下划线")
checkUsernameResp, err := this.RPC().UserRPC().CheckUserUsername(this.AdminContext(), &pb.CheckUserUsernameRequest{
UserId: params.UserId,
Username: params.Username,
})
if err != nil {
this.ErrorPage(err)
return
}
if checkUsernameResp.Exists {
this.FailField("username", "此用户名已经被占用,请换一个")
}
if len(params.Pass1) > 0 {
params.Must.
Field("pass1", params.Pass1).
Require("请输入密码").
Field("pass2", params.Pass2).
Require("请再次输入确认密码").
Equal(params.Pass1, "两次输入的密码不一致")
}
params.Must.
Field("fullname", params.Fullname).
Require("请输入全名")
if len(params.Mobile) > 0 {
params.Must.
Field("mobile", params.Mobile).
Mobile("请输入正确的手机号")
}
if len(params.Email) > 0 {
params.Must.
Field("email", params.Email).
Email("请输入正确的电子邮箱")
}
_, err = this.RPC().UserRPC().UpdateUser(this.AdminContext(), &pb.UpdateUserRequest{
UserId: params.UserId,
Username: params.Username,
Password: params.Pass1,
Fullname: params.Fullname,
Mobile: params.Mobile,
Tel: params.Tel,
Email: params.Email,
Remark: params.Remark,
IsOn: params.IsOn,
NodeClusterId: params.ClusterId,
BandwidthAlgo: params.BandwidthAlgo,
})
if err != nil {
this.ErrorPage(err)
return
}
// 修改OTP
otpLoginResp, err := this.RPC().LoginRPC().FindEnabledLogin(this.AdminContext(), &pb.FindEnabledLoginRequest{
UserId: params.UserId,
Type: "otp",
})
if err != nil {
this.ErrorPage(err)
return
}
{
var otpLogin = otpLoginResp.Login
if params.OtpOn {
if otpLogin == nil {
otpLogin = &pb.Login{
Id: 0,
Type: "otp",
ParamsJSON: maps.Map{
"secret": gotp.RandomSecret(16), // TODO 改成可以设置secret长度
}.AsJSON(),
IsOn: true,
UserId: params.UserId,
}
} else {
// 如果已经有了,就覆盖,这样可以保留既有的参数
otpLogin.IsOn = true
}
_, err = this.RPC().LoginRPC().UpdateLogin(this.AdminContext(), &pb.UpdateLoginRequest{Login: otpLogin})
if err != nil {
this.ErrorPage(err)
return
}
} else {
_, err = this.RPC().LoginRPC().UpdateLogin(this.AdminContext(), &pb.UpdateLoginRequest{Login: &pb.Login{
Type: "otp",
IsOn: false,
UserId: params.UserId,
}})
if err != nil {
this.ErrorPage(err)
return
}
}
}
this.Success()
}

View File

@@ -0,0 +1,118 @@
package users
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/users/userutils"
"github.com/TeaOSLab/EdgeCommon/pkg/iplibrary"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
)
type UserAction struct {
actionutils.ParentAction
}
func (this *UserAction) Init() {
this.Nav("", "", "index")
}
func (this *UserAction) RunGet(params struct {
UserId int64
}) {
err := userutils.InitUser(this.Parent(), params.UserId)
if err != nil {
if err == userutils.ErrUserNotFound {
this.RedirectURL("/users")
return
}
this.ErrorPage(err)
return
}
userResp, err := this.RPC().UserRPC().FindEnabledUser(this.AdminContext(), &pb.FindEnabledUserRequest{UserId: params.UserId})
if err != nil {
this.ErrorPage(err)
return
}
var user = userResp.User
if user == nil {
this.NotFound("user", params.UserId)
return
}
var clusterMap maps.Map = nil
if user.NodeCluster != nil {
clusterMap = maps.Map{
"id": user.NodeCluster.Id,
"name": user.NodeCluster.Name,
}
}
// AccessKey数量
countAccessKeyResp, err := this.RPC().UserAccessKeyRPC().CountAllEnabledUserAccessKeys(this.AdminContext(), &pb.CountAllEnabledUserAccessKeysRequest{UserId: params.UserId})
if err != nil {
this.ErrorPage(err)
return
}
countAccessKeys := countAccessKeyResp.Count
// IP地址
var registeredRegion = ""
if len(user.RegisteredIP) > 0 {
var ipRegion = iplibrary.LookupIP(user.RegisteredIP)
if ipRegion != nil && ipRegion.IsOk() {
registeredRegion = ipRegion.Summary()
}
}
// 是否有实名认证
hasNewIndividualIdentity, hasNewEnterpriseIdentity, identityTag, err := userutils.CheckUserIdentity(this.RPC(), this.AdminContext(), params.UserId)
if err != nil {
this.ErrorPage(err)
return
}
// OTP
this.Data["otp"] = nil
if user.OtpLogin != nil && user.OtpLogin.IsOn {
loginParams := maps.Map{}
err = json.Unmarshal(user.OtpLogin.ParamsJSON, &loginParams)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["otp"] = maps.Map{
"isOn": true,
"params": loginParams,
}
}
this.Data["user"] = maps.Map{
"id": user.Id,
"username": user.Username,
"fullname": user.Fullname,
"email": user.Email,
"verifiedEmail": user.VerifiedEmail,
"tel": user.Tel,
"remark": user.Remark,
"mobile": user.Mobile,
"isOn": user.IsOn,
"cluster": clusterMap,
"countAccessKeys": countAccessKeys,
"isRejected": user.IsRejected,
"rejectReason": user.RejectReason,
"isVerified": user.IsVerified,
"registeredIP": user.RegisteredIP,
"registeredRegion": registeredRegion,
"bandwidthAlgo": user.BandwidthAlgo,
// 实名认证
"hasNewIndividualIdentity": hasNewIndividualIdentity,
"hasNewEnterpriseIdentity": hasNewEnterpriseIdentity,
"identityTag": identityTag,
}
this.Show()
}

View File

@@ -0,0 +1,255 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package user
import (
"encoding/json"
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/users/userutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
)
type ServersAction struct {
actionutils.ParentAction
}
func (this *ServersAction) Init() {
this.Nav("", "", "server")
}
func (this *ServersAction) RunGet(params struct {
UserId int64
ClusterId int64
GroupId int64
Keyword string
AuditingFlag int32
CheckDNS bool
TrafficOutOrder string
}) {
err := userutils.InitUser(this.Parent(), params.UserId)
if err != nil {
this.ErrorPage(err)
return
}
this.Data["clusterId"] = params.ClusterId
this.Data["groupId"] = params.GroupId
this.Data["keyword"] = params.Keyword
this.Data["auditingFlag"] = params.AuditingFlag
this.Data["checkDNS"] = params.CheckDNS
this.Data["hasOrder"] = len(params.TrafficOutOrder) > 0
this.Data["userId"] = params.UserId
dashboardResp, err := this.RPC().UserRPC().ComposeUserDashboard(this.AdminContext(), &pb.ComposeUserDashboardRequest{UserId: params.UserId})
if err != nil {
this.ErrorPage(err)
return
}
// 单位
userUIConfig, err := configloaders.LoadUserUIConfig()
if err != nil {
this.ErrorPage(err)
return
}
this.Data["uiConfig"] = maps.Map{
"showTrafficCharts": true,
"showBandwidthCharts": true,
"bandwidthUnit": systemconfigs.BandwidthUnitBit,
}
if userUIConfig != nil {
this.Data["uiConfig"] = maps.Map{
"showTrafficCharts": userUIConfig.ShowTrafficCharts,
"showBandwidthCharts": userUIConfig.ShowBandwidthCharts,
"bandwidthUnit": systemconfigs.BandwidthUnitBit, // 强制使用比特单位
}
}
// 整体带宽统计
var monthlyPeekBandwidthBytes = dashboardResp.MonthlyPeekBandwidthBytes
var dailyPeekBandwidthBytes = dashboardResp.DailyPeekBandwidthBytes
monthlyPeekBandwidthBytes *= 8
dailyPeekBandwidthBytes *= 8
this.Data["dashboard"] = maps.Map{
"countServers": dashboardResp.CountServers,
"monthlyTrafficBytes": dashboardResp.MonthlyTrafficBytes,
"monthlyPeekBandwidthBits": monthlyPeekBandwidthBytes,
"dailyTrafficBytes": dashboardResp.DailyTrafficBytes,
"dailyPeekBandwidthBits": dailyPeekBandwidthBytes,
}
// 服务列表
countServersResp, err := this.RPC().ServerRPC().CountAllEnabledServersMatch(this.AdminContext(), &pb.CountAllEnabledServersMatchRequest{
UserId: params.UserId,
NodeClusterId: params.ClusterId,
ServerGroupId: params.GroupId,
Keyword: params.Keyword,
AuditingFlag: params.AuditingFlag,
})
if err != nil {
this.ErrorPage(err)
return
}
var count = countServersResp.Count
var page = this.NewPage(count)
this.Data["page"] = page.AsHTML()
serversResp, err := this.RPC().ServerRPC().ListEnabledServersMatch(this.AdminContext(), &pb.ListEnabledServersMatchRequest{
Offset: page.Offset,
Size: page.Size,
UserId: params.UserId,
NodeClusterId: params.ClusterId,
ServerGroupId: params.GroupId,
Keyword: params.Keyword,
AuditingFlag: params.AuditingFlag,
TrafficOutDesc: params.TrafficOutOrder == "desc",
TrafficOutAsc: params.TrafficOutOrder == "asc",
IgnoreServerNames: true,
IgnoreSSLCerts: true,
})
if err != nil {
this.ErrorPage(err)
return
}
var serverMaps = []maps.Map{}
for _, server := range serversResp.Servers {
var config = &serverconfigs.ServerConfig{}
err = json.Unmarshal(server.Config, config)
if err != nil {
this.ErrorPage(err)
return
}
// 端口列表
var portMaps = []maps.Map{}
if config.HTTP != nil && config.HTTP.IsOn {
for _, listen := range config.HTTP.Listen {
portMaps = append(portMaps, maps.Map{
"protocol": listen.Protocol,
"portRange": listen.PortRange,
})
}
}
if config.HTTPS != nil && config.HTTPS.IsOn {
for _, listen := range config.HTTPS.Listen {
portMaps = append(portMaps, maps.Map{
"protocol": listen.Protocol,
"portRange": listen.PortRange,
})
}
}
if config.TCP != nil && config.TCP.IsOn {
for _, listen := range config.TCP.Listen {
portMaps = append(portMaps, maps.Map{
"protocol": listen.Protocol,
"portRange": listen.PortRange,
})
}
}
if config.TLS != nil && config.TLS.IsOn {
for _, listen := range config.TLS.Listen {
portMaps = append(portMaps, maps.Map{
"protocol": listen.Protocol,
"portRange": listen.PortRange,
})
}
}
if config.UDP != nil && config.UDP.IsOn {
for _, listen := range config.UDP.Listen {
portMaps = append(portMaps, maps.Map{
"protocol": listen.Protocol,
"portRange": listen.PortRange,
})
}
}
// 分组
var groupMaps = []maps.Map{}
if len(server.ServerGroups) > 0 {
for _, group := range server.ServerGroups {
groupMaps = append(groupMaps, maps.Map{
"id": group.Id,
"name": group.Name,
})
}
}
// 域名列表
// 域名列表
if server.IsAuditing || (server.AuditingResult != nil && !server.AuditingResult.IsOk) {
server.ServerNamesJSON = server.AuditingServerNamesJSON
if len(config.ServerNames) == 0 {
// 审核中的域名
if len(server.ServerNamesJSON) > 0 {
var serverNames = []*serverconfigs.ServerNameConfig{}
err = json.Unmarshal(server.ServerNamesJSON, &serverNames)
if err != nil {
this.ErrorPage(err)
return
}
config.ServerNames = serverNames
}
}
}
var auditingIsOk = true
if !server.IsAuditing && server.AuditingResult != nil && !server.AuditingResult.IsOk {
auditingIsOk = false
}
var firstServerName = ""
for _, serverNameConfig := range config.ServerNames {
if len(serverNameConfig.Name) > 0 {
firstServerName = serverNameConfig.Name
break
}
if len(serverNameConfig.SubNames) > 0 {
firstServerName = serverNameConfig.SubNames[0]
break
}
}
// 提交审核时间
var auditingTime = ""
if server.AuditingAt > 0 {
auditingTime = timeutil.FormatTime("Y-m-d", server.AuditingAt)
}
// 统计数据
var bandwidthBits int64 = 0
if server.BandwidthBytes > 0 {
bandwidthBits = server.BandwidthBytes * 8
}
serverMaps = append(serverMaps, maps.Map{
"id": server.Id,
"isOn": server.IsOn,
"name": server.Name,
"cluster": maps.Map{
"id": server.NodeCluster.Id,
"name": server.NodeCluster.Name,
},
"ports": portMaps,
"serverTypeName": serverconfigs.FindServerType(server.Type).GetString("name"),
"groups": groupMaps,
"firstServerName": firstServerName,
"countServerNames": server.CountServerNames,
"isAuditing": server.IsAuditing,
"auditingIsOk": auditingIsOk,
"auditingTime": auditingTime,
"bandwidthBits": bandwidthBits,
})
}
this.Data["servers"] = serverMaps
this.Show()
}

View File

@@ -0,0 +1,90 @@
package userutils
import (
"context"
"errors"
"github.com/TeaOSLab/EdgeAdmin/internal/rpc"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/iwind/TeaGo/maps"
"strings"
)
var ErrUserNotFound = errors.New("not found user")
// InitUser 查找用户基本信息
func InitUser(p *actionutils.ParentAction, userId int64) error {
resp, err := p.RPC().UserRPC().FindEnabledUser(p.AdminContext(), &pb.FindEnabledUserRequest{UserId: userId})
if err != nil {
return err
}
if resp.User == nil {
return ErrUserNotFound
}
// AccessKey数量
countAccessKeysResp, err := p.RPC().UserAccessKeyRPC().CountAllEnabledUserAccessKeys(p.AdminContext(), &pb.CountAllEnabledUserAccessKeysRequest{
AdminId: 0,
UserId: userId,
})
if err != nil {
return err
}
// 是否有实名认证
hasNewIndividualIdentity, hasNewEnterpriseIdentity, identityTag, err := CheckUserIdentity(p.RPC(), p.AdminContext(), userId)
if err != nil {
return err
}
p.Data["user"] = maps.Map{
"id": userId,
"fullname": resp.User.Fullname,
"username": resp.User.Username,
"countAccessKeys": countAccessKeysResp.Count,
"hasNewIndividualIdentity": hasNewIndividualIdentity,
"hasNewEnterpriseIdentity": hasNewEnterpriseIdentity,
"identityTag": identityTag,
}
return nil
}
// CheckUserIdentity 实名认证信息
func CheckUserIdentity(rpcClient *rpc.RPCClient, ctx context.Context, userId int64) (hasNewIndividualIdentity bool, hasNewEnterpriseIdentity bool, identityTag string, err error) {
var tags = []string{}
// 个人
individualIdentityResp, err := rpcClient.UserIdentityRPC().FindEnabledUserIdentityWithOrgType(ctx, &pb.FindEnabledUserIdentityWithOrgTypeRequest{
UserId: userId,
OrgType: userconfigs.UserIdentityOrgTypeIndividual,
})
if err != nil {
return false, false, "", err
}
var individualIdentity = individualIdentityResp.UserIdentity
hasNewIndividualIdentity = individualIdentity != nil && individualIdentity.Status == userconfigs.UserIdentityStatusSubmitted
if individualIdentity != nil && individualIdentity.Status == userconfigs.UserIdentityStatusVerified {
tags = append(tags, "个人")
}
// 企业
enterpriseIdentityResp, err := rpcClient.UserIdentityRPC().FindEnabledUserIdentityWithOrgType(ctx, &pb.FindEnabledUserIdentityWithOrgTypeRequest{
UserId: userId,
OrgType: userconfigs.UserIdentityOrgTypeEnterprise,
})
if err != nil {
return false, false, "", err
}
var enterpriseIdentity = enterpriseIdentityResp.UserIdentity
hasNewEnterpriseIdentity = enterpriseIdentity != nil && enterpriseIdentity.Status == userconfigs.UserIdentityStatusSubmitted
if enterpriseIdentity != nil && enterpriseIdentity.Status == userconfigs.UserIdentityStatusVerified {
tags = append(tags, "企业")
}
identityTag = strings.Join(tags, "+")
return
}

View File

@@ -0,0 +1,57 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
package users
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
)
type VerifyPopupAction struct {
actionutils.ParentAction
}
func (this *VerifyPopupAction) RunGet(params struct {
UserId int64
}) {
this.Data["userId"] = params.UserId
this.Show()
}
func (this *VerifyPopupAction) RunPost(params struct {
UserId int64
Result string
RejectReason string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.User_LogVerifyUser, params.UserId, params.Result)
if params.Result == "pass" {
params.RejectReason = ""
}
_, err := this.RPC().UserRPC().VerifyUser(this.AdminContext(), &pb.VerifyUserRequest{
UserId: params.UserId,
IsRejected: params.Result == "reject" || params.Result == "delete",
RejectReason: params.RejectReason,
})
if err != nil {
this.ErrorPage(err)
return
}
if params.Result == "delete" {
_, err = this.RPC().UserRPC().DeleteUser(this.AdminContext(), &pb.DeleteUserRequest{UserId: params.UserId})
if err != nil {
this.ErrorPage(err)
return
}
}
this.Success()
}