Initial commit (code only without large binaries)

This commit is contained in:
robin
2026-02-15 18:58:44 +08:00
commit 35df75498f
9442 changed files with 1495866 additions and 0 deletions

View File

@@ -0,0 +1,261 @@
//go:build plus
package userui
import (
"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/nodeconfigs"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs"
"github.com/iwind/TeaGo/actions"
"io"
"strings"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "index")
}
func (this *IndexAction) RunGet(params struct{}) {
config, err := configloaders.LoadUserUIConfig()
if err != nil {
this.ErrorPage(err)
return
}
this.Data["config"] = config
// 时区
this.Data["timeZoneGroups"] = nodeconfigs.FindAllTimeZoneGroups()
this.Data["timeZoneLocations"] = nodeconfigs.FindAllTimeZoneLocations()
if len(config.TimeZone) == 0 {
config.TimeZone = nodeconfigs.DefaultTimeZoneLocation
}
this.Data["timeZoneLocation"] = nodeconfigs.FindTimeZoneLocation(config.TimeZone)
// 风格样式
this.Data["themeBackgroundColors"] = systemconfigs.DefaultThemeBackgroundColors()
this.Show()
}
func (this *IndexAction) RunPost(params struct {
ProductName string
UserSystemName string
ShowPageFooter bool
PageFooterHTML string
ThemeBackgroundColor string
ShowVersion bool
Version string
ShowFinance bool
FaviconFile *actions.File
LogoFile *actions.File
TimeZone string
DnsResolverType string
ClientIPHeaderNames string
ServerCheckCNAME bool
ShowTrafficCharts bool
ShowCacheInfoInTrafficCharts bool
ShowBandwidthCharts bool
BandwidthUnit string
PortalIsOn bool
PortalLogoFile *actions.File
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.AdminUserUI_LogUpdateUISettings)
params.Must.
Field("productName", params.ProductName).
Require("请输入产品名称").
Field("userSystemName", params.UserSystemName).
Require("请输入管理员系统名称")
config, err := configloaders.LoadUserUIConfig()
if err != nil {
this.ErrorPage(err)
return
}
config.ProductName = params.ProductName
config.UserSystemName = params.UserSystemName
config.ShowPageFooter = params.ShowPageFooter
config.PageFooterHTML = params.PageFooterHTML
config.Theme.BackgroundColor = strings.TrimLeft(params.ThemeBackgroundColor, "#")
config.ShowVersion = params.ShowVersion
config.Version = params.Version
config.ShowFinance = params.ShowFinance
config.Server.CheckCNAME = params.ServerCheckCNAME
config.ShowTrafficCharts = params.ShowTrafficCharts
config.ShowCacheInfoInTrafficCharts = params.ShowCacheInfoInTrafficCharts
config.ShowBandwidthCharts = params.ShowBandwidthCharts
config.BandwidthUnit = params.BandwidthUnit
config.TimeZone = params.TimeZone
config.DNSResolver.Type = params.DnsResolverType
config.ClientIPHeaderNames = params.ClientIPHeaderNames
// 上传Favicon文件
if params.FaviconFile != nil {
createResp, err := this.RPC().FileRPC().CreateFile(this.AdminContext(), &pb.CreateFileRequest{
Filename: params.FaviconFile.Filename,
Size: params.FaviconFile.Size,
IsPublic: true,
})
if err != nil {
this.ErrorPage(err)
return
}
var fileId = createResp.FileId
// 上传内容
var buf = make([]byte, 512*1024)
reader, err := params.FaviconFile.OriginFile.Open()
if err != nil {
this.ErrorPage(err)
return
}
for {
n, err := reader.Read(buf)
if n > 0 {
_, err = this.RPC().FileChunkRPC().CreateFileChunk(this.AdminContext(), &pb.CreateFileChunkRequest{
FileId: fileId,
Data: buf[:n],
})
if err != nil {
this.Fail("上传失败:" + err.Error())
}
}
if err != nil {
if err == io.EOF {
break
}
this.Fail("上传失败:" + err.Error())
}
}
// 置为已完成
_, err = this.RPC().FileRPC().UpdateFileFinished(this.AdminContext(), &pb.UpdateFileFinishedRequest{FileId: fileId})
if err != nil {
this.ErrorPage(err)
}
config.FaviconFileId = fileId
}
// 上传Logo文件
if params.LogoFile != nil {
createResp, err := this.RPC().FileRPC().CreateFile(this.AdminContext(), &pb.CreateFileRequest{
Filename: params.LogoFile.Filename,
Size: params.LogoFile.Size,
IsPublic: true,
})
if err != nil {
this.ErrorPage(err)
return
}
var fileId = createResp.FileId
// 上传内容
var buf = make([]byte, 512*1024)
reader, err := params.LogoFile.OriginFile.Open()
if err != nil {
this.ErrorPage(err)
return
}
for {
n, err := reader.Read(buf)
if n > 0 {
_, err = this.RPC().FileChunkRPC().CreateFileChunk(this.AdminContext(), &pb.CreateFileChunkRequest{
FileId: fileId,
Data: buf[:n],
})
if err != nil {
this.Fail("上传失败:" + err.Error())
}
}
if err != nil {
if err == io.EOF {
break
}
this.Fail("上传失败:" + err.Error())
}
}
// 置为已完成
_, err = this.RPC().FileRPC().UpdateFileFinished(this.AdminContext(), &pb.UpdateFileFinishedRequest{FileId: fileId})
if err != nil {
this.ErrorPage(err)
}
config.LogoFileId = fileId
}
// portal
config.Portal.IsOn = params.PortalIsOn
// 上传Portal Logo文件
if params.PortalLogoFile != nil {
createResp, err := this.RPC().FileRPC().CreateFile(this.AdminContext(), &pb.CreateFileRequest{
Filename: params.PortalLogoFile.Filename,
Size: params.PortalLogoFile.Size,
IsPublic: true,
})
if err != nil {
this.ErrorPage(err)
return
}
var fileId = createResp.FileId
// 上传内容
var buf = make([]byte, 512*1024)
reader, err := params.PortalLogoFile.OriginFile.Open()
if err != nil {
this.ErrorPage(err)
return
}
for {
n, err := reader.Read(buf)
if n > 0 {
_, err = this.RPC().FileChunkRPC().CreateFileChunk(this.AdminContext(), &pb.CreateFileChunkRequest{
FileId: fileId,
Data: buf[:n],
})
if err != nil {
this.Fail("上传失败:" + err.Error())
}
}
if err != nil {
if err == io.EOF {
break
}
this.Fail("上传失败:" + err.Error())
}
}
// 置为已完成
_, err = this.RPC().FileRPC().UpdateFileFinished(this.AdminContext(), &pb.UpdateFileFinishedRequest{FileId: fileId})
if err != nil {
this.ErrorPage(err)
}
config.Portal.LogoFileId = fileId
}
err = configloaders.UpdateUserUIConfig(config)
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,34 @@
//go:build plus
package userui
import (
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
"github.com/TeaOSLab/EdgeAdmin/internal/plus"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/settingutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/user-ui/posts"
"github.com/TeaOSLab/EdgeAdmin/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth(configloaders.AdminModuleCodeCommon)).
Helper(plus.NewBasicHelper()).
Helper(settingutils.NewHelper("userUI")).
Prefix("/settings/user-ui").
// settings
GetPost("", new(IndexAction)).
// posts
Get("/posts", new(posts.IndexAction)).
Post("/posts/delete", new(posts.DeleteAction)).
Post("/posts/publish", new(posts.PublishAction)).
GetPost("/posts/create", new(posts.CreateAction)).
GetPost("/posts/update", new(posts.UpdateAction)).
Get("/posts/post", new(posts.PostAction)).
EndAll()
})
}

View File

@@ -0,0 +1,102 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package posts
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/user-ui/posts/postutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type CreateAction struct {
actionutils.ParentAction
}
func (this *CreateAction) Init() {
this.Nav("", "", "post")
this.SecondMenu("create")
}
func (this *CreateAction) RunGet(params struct{}) {
// products
this.Data["products"] = postutils.FindAllProductOptions(this.LangCode())
// categories
categoriesResp, err := this.RPC().PostCategoryRPC().FindAllPostCategories(this.AdminContext(), &pb.FindAllPostCategoriesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var categoryMaps = []maps.Map{}
for _, category := range categoriesResp.PostCategories {
categoryMaps = append(categoryMaps, maps.Map{
"id": category.Id,
"name": category.Name,
})
}
this.Data["categories"] = categoryMaps
this.Show()
}
func (this *CreateAction) RunPost(params struct {
ProductCode string
CategoryId int64
Subject string
Type string
Body string
Url string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
var postId int64
defer func() {
this.CreateLogInfo(codes.Post_LogCreatePost, postId)
}()
params.Must.
Field("productCode", params.ProductCode).
Require("请选择所属产品").
Field("categoryId", params.CategoryId).
Gt(0, "请选择文章分类").
Field("subject", params.Subject).
Require("文章标题不能为空")
switch params.Type {
case "normal":
if len(params.Body) == 0 {
this.FailField("body", "请输入文章内容")
return
}
params.Url = ""
case "url":
if len(params.Url) == 0 {
this.FailField("url", "请输入文章URL")
return
}
params.Body = ""
default:
this.FailField("type", "请选择正确的类型")
return
}
createResp, err := this.RPC().PostRPC().CreatePost(this.AdminContext(), &pb.CreatePostRequest{
ProductCode: params.ProductCode,
PostCategoryId: params.CategoryId,
Type: params.Type,
Subject: params.Subject,
Url: params.Url,
Body: params.Body,
})
if err != nil {
this.ErrorPage(err)
return
}
postId = createResp.PostId
this.Success()
}

View File

@@ -0,0 +1,28 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package posts
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 {
PostId int64
}) {
defer this.CreateLogInfo(codes.Post_LogDeletePost, params.PostId)
_, err := this.RPC().PostRPC().DeletePost(this.AdminContext(), &pb.DeletePostRequest{PostId: params.PostId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,91 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package posts
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/user-ui/posts/postutils"
"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("", "", "post")
this.SecondMenu("index")
}
func (this *IndexAction) RunGet(params struct {
CategoryId int64
}) {
// categories
categoriesResp, err := this.RPC().PostCategoryRPC().FindAllPostCategories(this.AdminContext(), &pb.FindAllPostCategoriesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var categoryMaps = []maps.Map{}
for _, category := range categoriesResp.PostCategories {
categoryMaps = append(categoryMaps, maps.Map{
"id": category.Id,
"isOn": category.IsOn,
"name": category.Name,
})
}
this.Data["categories"] = categoryMaps
// posts count
countResp, err := this.RPC().PostRPC().CountPosts(this.AdminContext(), &pb.CountPostsRequest{
PostCategoryId: params.CategoryId,
})
if err != nil {
this.ErrorPage(err)
return
}
var page = this.NewPage(countResp.Count)
this.Data["page"] = page.AsHTML()
// posts
postsResp, err := this.RPC().PostRPC().ListPosts(this.AdminContext(), &pb.ListPostsRequest{
Offset: page.Offset,
Size: page.Size,
PostCategoryId: params.CategoryId,
PostCategoryCode: "",
PublishedOnly: false,
ContainsBody: false,
})
if err != nil {
this.ErrorPage(err)
return
}
var postMaps = []maps.Map{}
for _, post := range postsResp.Posts {
var categoryMap = maps.Map{
"id": 0,
}
if post.PostCategory != nil {
categoryMap = maps.Map{
"id": post.PostCategory.Id,
"name": post.PostCategory.Name,
}
}
postMaps = append(postMaps, maps.Map{
"id": post.Id,
"productName": postutils.FindProductName(this.LangCode(), post.ProductCode),
"subject": post.Subject,
"type": post.Type,
"isPublished": post.IsPublished,
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", post.CreatedAt),
"category": categoryMap,
})
}
this.Data["posts"] = postMaps
this.Show()
}

View File

@@ -0,0 +1,60 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package posts
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/user-ui/posts/postutils"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
)
type PostAction struct {
actionutils.ParentAction
}
func (this *PostAction) Init() {
this.Nav("", "", "")
this.SecondMenu("index")
}
func (this *PostAction) RunGet(params struct {
PostId int64
}) {
postResp, err := this.RPC().PostRPC().FindPost(this.AdminContext(), &pb.FindPostRequest{PostId: params.PostId})
if err != nil {
this.ErrorPage(err)
return
}
var post = postResp.Post
if post == nil {
this.NotFound("post", params.PostId)
return
}
var categoryMap = maps.Map{
"id": 0,
}
if post.PostCategory != nil {
categoryMap = maps.Map{
"id": post.PostCategory.Id,
"name": post.PostCategory.Name,
}
}
this.Data["post"] = maps.Map{
"id": post.Id,
"productName": postutils.FindProductName(this.LangCode(), post.ProductCode),
"subject": post.Subject,
"type": post.Type,
"isPublished": post.IsPublished,
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", post.CreatedAt),
"url": post.Url,
"body": post.Body,
"category": categoryMap,
}
this.Show()
}

View File

@@ -0,0 +1,37 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
package postutils
import (
"github.com/TeaOSLab/EdgeCommon/pkg/langs"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/iwind/TeaGo/maps"
)
// FindAllProductOptions product code options
func FindAllProductOptions(langCode langs.LangCode) []maps.Map {
return []maps.Map{
{
"name": "CDN",
"code": "cdn",
},
{
"name": "DNS",
"code": "dns",
},
{
"name": langs.Message(langCode, codes.Post_ProductGlobal),
"code": "global",
},
}
}
// FindProductName find product name with product code
func FindProductName(langCode langs.LangCode, code string) string {
for _, m := range FindAllProductOptions(langCode) {
if m.GetString("code") == code {
return m.GetString("name")
}
}
return ""
}

View File

@@ -0,0 +1,28 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package posts
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
)
type PublishAction struct {
actionutils.ParentAction
}
func (this *PublishAction) RunPost(params struct {
PostId int64
}) {
defer this.CreateLogInfo(codes.Post_LogPublishPost, params.PostId)
_, err := this.RPC().PostRPC().PublishPost(this.AdminContext(), &pb.PublishPostRequest{PostId: params.PostId})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,129 @@
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
//go:build plus
package posts
import (
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/default/settings/user-ui/posts/postutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/maps"
)
type UpdateAction struct {
actionutils.ParentAction
}
func (this *UpdateAction) Init() {
this.Nav("", "", "post")
this.SecondMenu("index")
}
func (this *UpdateAction) RunGet(params struct {
PostId int64
}) {
// products
this.Data["products"] = postutils.FindAllProductOptions(this.LangCode())
// categories
categoriesResp, err := this.RPC().PostCategoryRPC().FindAllPostCategories(this.AdminContext(), &pb.FindAllPostCategoriesRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var categoryMaps = []maps.Map{}
for _, category := range categoriesResp.PostCategories {
categoryMaps = append(categoryMaps, maps.Map{
"id": category.Id,
"name": category.Name,
})
}
this.Data["categories"] = categoryMaps
// post information
postResp, err := this.RPC().PostRPC().FindPost(this.AdminContext(), &pb.FindPostRequest{PostId: params.PostId})
if err != nil {
this.ErrorPage(err)
return
}
var post = postResp.Post
if post == nil {
this.NotFound("post", params.PostId)
return
}
this.Data["post"] = maps.Map{
"id": post.Id,
"productCode": post.ProductCode,
"subject": post.Subject,
"type": post.Type,
"isPublished": post.IsPublished,
"url": post.Url,
"body": post.Body,
"categoryId": post.PostCategoryId,
}
this.Show()
}
func (this *UpdateAction) RunPost(params struct {
PostId int64
ProductCode string
CategoryId int64
Subject string
Type string
Body string
Url string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.Post_LogUpdatePost, params.PostId)
params.Must.
Field("postId", params.PostId).
Gt(0, "请选择要修改的文章").
Field("productCode", params.ProductCode).
Require("请选择所属产品").
Field("categoryId", params.CategoryId).
Gt(0, "请选择文章分类").
Field("subject", params.Subject).
Require("文章标题不能为空")
switch params.Type {
case "normal":
if len(params.Body) == 0 {
this.FailField("body", "请输入文章内容")
return
}
params.Url = ""
case "url":
if len(params.Url) == 0 {
this.FailField("url", "请输入文章URL")
return
}
params.Body = ""
default:
this.FailField("type", "请选择正确的类型")
return
}
_, err := this.RPC().PostRPC().UpdatePost(this.AdminContext(), &pb.UpdatePostRequest{
PostId: params.PostId,
ProductCode: params.ProductCode,
PostCategoryId: params.CategoryId,
Type: params.Type,
Subject: params.Subject,
Url: params.Url,
Body: params.Body,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}