Files
waf-platform/EdgeUser/internal/web/actions/actionutils/parent_action.go

281 lines
7.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package actionutils
import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/dao"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/TeaOSLab/EdgeUser/internal/configloaders"
"github.com/TeaOSLab/EdgeUser/internal/nodes/serverutils"
"github.com/TeaOSLab/EdgeUser/internal/oplogs"
"github.com/TeaOSLab/EdgeUser/internal/remotelogs"
"github.com/TeaOSLab/EdgeUser/internal/rpc"
"github.com/TeaOSLab/EdgeUser/internal/utils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/login/loginutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/logs"
"github.com/iwind/TeaGo/types"
"net"
"net/http"
"strconv"
)
type ParentAction struct {
actions.ActionObject
rpcClient *rpc.RPCClient
}
// Parent 可以调用自身的一个简便方法
func (this *ParentAction) Parent() *ParentAction {
return this
}
func (this *ParentAction) ErrorPage(err error) {
if err == nil {
return
}
if !rpc.IsConnError(err) {
remotelogs.Error("ERROR_PAGE", this.Request.URL.String()+": "+err.Error())
}
// 日志
this.CreateLog(oplogs.LevelError, codes.UserCommon_LogSystemError, err.Error())
if this.Request.Method == http.MethodGet {
FailPage(this, err)
} else {
Fail(this, err)
}
}
func (this *ParentAction) ErrorText(err string) {
this.ErrorPage(errors.New(err))
}
func (this *ParentAction) NotFound(name string, itemId int64) {
this.ErrorPage(errors.New(name + " id: '" + strconv.FormatInt(itemId, 10) + "' is not found"))
}
func (this *ParentAction) ForbidPage() {
this.ResponseWriter.WriteHeader(http.StatusForbidden)
}
func (this *ParentAction) NewPage(total int64, size ...int64) *Page {
if len(size) > 0 {
return NewActionPage(this, total, size[0])
}
return NewActionPage(this, total, 10)
}
func (this *ParentAction) Nav(mainMenu string, tab string, firstMenu string) {
this.Data["mainMenu"] = mainMenu
this.Data["mainTab"] = tab
this.Data["firstMenuItem"] = firstMenu
}
func (this *ParentAction) FirstMenu(menuItem string) {
this.Data["firstMenuItem"] = menuItem
}
func (this *ParentAction) SecondMenu(menuItem string) {
this.Data["secondMenuItem"] = menuItem
}
func (this *ParentAction) TinyMenu(menuItem string) {
this.Data["tinyMenuItem"] = menuItem
}
func (this *ParentAction) UserId() int64 {
return this.Context.GetInt64("userId")
}
func (this *ParentAction) CreateLog(level string, messageCode langs.MessageCode, args ...any) {
var description = messageCode.For(this.LangCode())
var desc = fmt.Sprintf(description, args...)
if level == oplogs.LevelInfo {
if this.Code != 200 {
level = oplogs.LevelWarn
if len(this.Message) > 0 {
desc += " 失败:" + this.Message
}
}
}
err := dao.SharedLogDAO.CreateUserLog(this.UserContext(), level, this.Request.URL.Path, desc, loginutils.RemoteIP(&this.ActionObject))
if err != nil {
utils.PrintError(err)
}
}
func (this *ParentAction) CreateLogInfo(messageCode langs.MessageCode, args ...any) {
this.CreateLog(oplogs.LevelInfo, messageCode, args...)
}
func (this *ParentAction) LangCode() langs.LangCode {
var langCode = this.Data.GetString("teaLang")
if len(langCode) == 0 {
langCode = langs.DefaultManager().DefaultLang()
}
return langCode
}
// RPC 获取RPC
func (this *ParentAction) RPC() *rpc.RPCClient {
if this.rpcClient != nil {
return this.rpcClient
}
// 所有集群
rpcClient, err := rpc.SharedRPC()
if err != nil {
logs.Fatal(err)
return nil
}
this.rpcClient = rpcClient
return rpcClient
}
// UserContext 获取Context
// 每个请求的context都必须是一个新的实例
func (this *ParentAction) UserContext() context.Context {
if this.rpcClient == nil {
rpcClient, err := rpc.SharedRPC()
if err != nil {
logs.Fatal(err)
return nil
}
this.rpcClient = rpcClient
}
return this.rpcClient.Context(this.UserId())
}
// 一个不包含用户ID的上下文
func (this *ParentAction) NullUserContext() context.Context {
if this.rpcClient == nil {
rpcClient, err := rpc.SharedRPC()
if err != nil {
logs.Fatal(err)
return nil
}
this.rpcClient = rpcClient
}
return this.rpcClient.Context(0)
}
// ValidateFeature 校验Feature
func (this *ParentAction) ValidateFeature(featureCode string, serverId int64) bool {
b, err := this.validateFeature(featureCode, serverId)
if err != nil {
remotelogs.Error("FEATURE", "validate feature: "+err.Error())
}
return b
}
func (this *ParentAction) validateFeature(featureCode string, serverId int64) (bool, error) {
// 检查审核和认证
if configloaders.RequireVerification() && !this.Context.GetBool("isVerified") {
return false, nil
}
if configloaders.RequireIdentity() && !this.Context.GetBool("isIdentified") {
return false, nil
}
var featureDef = userconfigs.FindUserFeature(featureCode)
if featureDef == nil {
return false, errors.New("invalid feature code '" + featureCode + "'")
}
// 检查网站绑定的套餐
if serverId > 0 && featureDef.SupportPlan {
userPlanResp, err := this.RPC().ServerRPC().FindServerUserPlan(this.UserContext(), &pb.FindServerUserPlanRequest{ServerId: serverId})
if err != nil {
return false, err
}
if userPlanResp.UserPlan != nil && userPlanResp.UserPlan.IsOn && userPlanResp.UserPlan.Plan != nil /** 不需要判断套餐是否启用即不需要判断是否为UserPlan.Plan.isOn以便于兼容套餐被删除的情况 **/ {
var plan = userPlanResp.UserPlan.Plan
if plan.HasFullFeatures {
return true, nil
}
var supportedFeatureCodes []string
if len(plan.FeaturesJSON) > 0 {
err = json.Unmarshal(plan.FeaturesJSON, &supportedFeatureCodes)
if err != nil {
return false, err
}
}
return lists.ContainsString(supportedFeatureCodes, featureCode), nil
}
}
// 用户功能
userFeatureResp, err := this.RPC().UserRPC().FindUserFeatures(this.UserContext(), &pb.FindUserFeaturesRequest{UserId: this.UserId()})
if err != nil {
return false, err
}
var userFeatureCodes = []string{}
for _, feature := range userFeatureResp.Features {
userFeatureCodes = append(userFeatureCodes, feature.Code)
}
return lists.ContainsString(userFeatureCodes, featureCode), nil
}
func (this *ParentAction) CheckUserStatus() bool {
// 审核和认证
if configloaders.RequireVerification() && !this.Context.GetBool("isVerified") {
this.RedirectURL("/")
return false
}
if configloaders.RequireIdentity() && !this.Context.GetBool("isIdentified") {
this.RedirectURL("/")
return false
}
return true
}
func (this *ParentAction) FindServerIdWithWebId(webId int64) (serverId int64, err error) {
serverIdResp, err := this.RPC().HTTPWebRPC().FindServerIdWithHTTPWebId(this.UserContext(), &pb.FindServerIdWithHTTPWebIdRequest{
HttpWebId: webId,
})
if err != nil {
this.ErrorPage(err)
return
}
serverId = serverIdResp.ServerId
return
}
func (this *ParentAction) CheckHTTPSRedirecting() {
if this.Request.TLS == nil {
httpsPort, _ := serverutils.ReadServerHTTPS()
if httpsPort > 0 {
currentHost, _, hostErr := net.SplitHostPort(this.Request.Host)
if hostErr != nil {
currentHost = this.Request.Host
}
var newHost = configutils.QuoteIP(currentHost)
if httpsPort != 443 /** default https port **/ {
newHost += ":" + types.String(httpsPort)
}
// 如果没有前端反向代理,则跳转
if len(this.Request.Header.Get("X-Forwarded-For")) == 0 && len(this.Request.Header.Get("X-Real-Ip")) == 0 {
this.RedirectURL("https://" + newHost + this.Request.RequestURI)
return
}
}
}
}