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,20 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package cacheutils
// KeyFailReason Key相关失败原因
func KeyFailReason(reasonCode string) string {
switch reasonCode {
case "requireKey":
return "空的Key"
case "requireDomain":
return "找不到Key对应的域名"
case "requireServer":
return "找不到Key对应的网站"
case "requireUser":
return "该域名不属于当前用户"
case "requireClusterId":
return "该网站没有部署到集群"
}
return "未知错误"
}

View File

@@ -0,0 +1,29 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package cache
import (
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
)
type DeleteTaskAction struct {
actionutils.ParentAction
}
func (this *DeleteTaskAction) RunPost(params struct {
TaskId int64
}) {
defer this.CreateLogInfo(codes.HTTPCacheTask_LogDeleteHTTPCacheTask, params.TaskId)
_, err := this.RPC().HTTPCacheTaskRPC().DeleteHTTPCacheTask(this.UserContext(), &pb.DeleteHTTPCacheTaskRequest{
HttpCacheTaskId: params.TaskId,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,162 @@
package cache
import (
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/TeaOSLab/EdgeUser/internal/configloaders"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/cache/cacheutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
"net"
"net/url"
"strings"
)
type FetchAction struct {
actionutils.ParentAction
}
func (this *FetchAction) Init() {
this.Nav("", "", "fetch")
}
func (this *FetchAction) RunGet(params struct{}) {
// 初始化菜单数据
err := InitMenu(this.Parent())
if err != nil {
this.ErrorPage(err)
return
}
// 后台配置
var maxKeysPerTask = userconfigs.MaxCacheKeysPerTask
var maxKeysPerDay = userconfigs.MaxCacheKeysPerDay
serverConfig, err := configloaders.LoadServerConfig()
if err != nil {
this.ErrorPage(err)
return
}
if serverConfig != nil && serverConfig.HTTPCacheTaskFetchConfig != nil {
if serverConfig.HTTPCacheTaskFetchConfig.MaxKeysPerTask > 0 {
maxKeysPerTask = serverConfig.HTTPCacheTaskFetchConfig.MaxKeysPerTask
}
if serverConfig.HTTPCacheTaskFetchConfig.MaxKeysPerDay > 0 {
maxKeysPerDay = serverConfig.HTTPCacheTaskFetchConfig.MaxKeysPerDay
}
}
this.Data["maxKeysPerTask"] = maxKeysPerTask
this.Data["maxKeysPerDay"] = maxKeysPerDay
// 剩余配额
this.Data["quotaKeysToday"] = 0
if maxKeysPerDay > 0 {
countKeysResp, err := this.RPC().HTTPCacheTaskKeyRPC().CountHTTPCacheTaskKeysWithDay(this.UserContext(), &pb.CountHTTPCacheTaskKeysWithDayRequest{
KeyType: "fetch",
Day: timeutil.Format("Ymd"),
})
if err != nil {
this.ErrorPage(err)
return
}
var quota = maxKeysPerDay - types.Int32(countKeysResp.Count)
if quota < 0 {
quota = 0
}
this.Data["quotaKeysToday"] = quota
}
this.Show()
}
func (this *FetchAction) RunPost(params struct {
UrlList string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.HTTPCacheTask_LogCreateHTTPCacheTaskFetch)
// 查找当前用户的所有域名
serverNamesResp, err := this.RPC().ServerRPC().FindAllEnabledServerNamesWithUserId(this.UserContext(), &pb.FindAllEnabledServerNamesWithUserIdRequest{UserId: this.UserId()})
if err != nil {
this.ErrorPage(err)
return
}
var serverNames = serverNamesResp.ServerNames
var keys = []string{}
for _, key := range strings.Split(params.UrlList, "\n") {
key = strings.TrimSpace(key)
if len(key) == 0 {
continue
}
if lists.ContainsString(keys, key) {
continue
}
// 检查域名
u, err := url.Parse(key)
if err != nil || len(u.Host) == 0 || (u.Scheme != "http" && u.Scheme != "https") {
this.Fail("'" + key + "'不是正确的URL格式")
}
var host = u.Host
// 去掉端口
hostString, _, err := net.SplitHostPort(host)
if err == nil {
host = hostString
}
if !configutils.MatchDomains(serverNames, host) {
this.Fail("'" + key + "'中域名'" + u.Host + "'没有绑定")
}
keys = append(keys, key)
}
if len(keys) == 0 {
this.Fail("请输入要刷新的URL列表")
}
// 校验Key
validateResp, err := this.RPC().HTTPCacheTaskKeyRPC().ValidateHTTPCacheTaskKeys(this.UserContext(), &pb.ValidateHTTPCacheTaskKeysRequest{Keys: keys})
if err != nil {
this.ErrorPage(err)
return
}
var failKeyMaps = []maps.Map{}
if len(validateResp.FailKeys) > 0 {
for _, key := range validateResp.FailKeys {
failKeyMaps = append(failKeyMaps, maps.Map{
"key": key.Key,
"reason": cacheutils.KeyFailReason(key.ReasonCode),
})
}
}
this.Data["failKeys"] = failKeyMaps
if len(failKeyMaps) > 0 {
this.Fail("有" + types.String(len(failKeyMaps)) + "个Key无法完成操作请删除后重试")
}
// 提交任务
_, err = this.RPC().HTTPCacheTaskRPC().CreateHTTPCacheTask(this.UserContext(), &pb.CreateHTTPCacheTaskRequest{
Type: "fetch",
KeyType: "key",
Keys: keys,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,177 @@
package cache
import (
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/TeaOSLab/EdgeCommon/pkg/langs/codes"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeCommon/pkg/userconfigs"
"github.com/TeaOSLab/EdgeUser/internal/configloaders"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/default/servers/cache/cacheutils"
"github.com/iwind/TeaGo/actions"
"github.com/iwind/TeaGo/lists"
"github.com/iwind/TeaGo/maps"
"github.com/iwind/TeaGo/types"
timeutil "github.com/iwind/TeaGo/utils/time"
"net"
"net/url"
"strings"
)
type IndexAction struct {
actionutils.ParentAction
}
func (this *IndexAction) Init() {
this.Nav("", "", "purge")
}
func (this *IndexAction) RunGet(params struct{}) {
// 初始化菜单数据
err := InitMenu(this.Parent())
if err != nil {
this.ErrorPage(err)
return
}
// 后台配置
var maxKeysPerTask = userconfigs.MaxCacheKeysPerTask
var maxKeysPerDay = userconfigs.MaxCacheKeysPerDay
serverConfig, err := configloaders.LoadServerConfig()
if err != nil {
this.ErrorPage(err)
return
}
if serverConfig != nil && serverConfig.HTTPCacheTaskPurgeConfig != nil {
if serverConfig.HTTPCacheTaskPurgeConfig.MaxKeysPerTask > 0 {
maxKeysPerTask = serverConfig.HTTPCacheTaskPurgeConfig.MaxKeysPerTask
}
if serverConfig.HTTPCacheTaskPurgeConfig.MaxKeysPerDay > 0 {
maxKeysPerDay = serverConfig.HTTPCacheTaskPurgeConfig.MaxKeysPerDay
}
}
this.Data["maxKeysPerTask"] = maxKeysPerTask
this.Data["maxKeysPerDay"] = maxKeysPerDay
// 剩余配额
this.Data["quotaKeysToday"] = 0
if maxKeysPerDay > 0 {
countKeysResp, err := this.RPC().HTTPCacheTaskKeyRPC().CountHTTPCacheTaskKeysWithDay(this.UserContext(), &pb.CountHTTPCacheTaskKeysWithDayRequest{
KeyType: "purge",
Day: timeutil.Format("Ymd"),
})
if err != nil {
this.ErrorPage(err)
return
}
var quota = maxKeysPerDay - types.Int32(countKeysResp.Count)
if quota < 0 {
quota = 0
}
this.Data["quotaKeysToday"] = quota
}
this.Show()
}
func (this *IndexAction) RunPost(params struct {
KeyType string
UrlList string
Must *actions.Must
CSRF *actionutils.CSRF
}) {
defer this.CreateLogInfo(codes.HTTPCacheTask_LogCreateHTTPCacheTaskPurge)
switch params.KeyType {
case "key", "prefix":
default:
this.Fail("请选择正确的刷新类型")
}
// 查找当前用户的所有域名
serverNamesResp, err := this.RPC().ServerRPC().FindAllEnabledServerNamesWithUserId(this.UserContext(), &pb.FindAllEnabledServerNamesWithUserIdRequest{UserId: this.UserId()})
if err != nil {
this.ErrorPage(err)
return
}
var serverNames = serverNamesResp.ServerNames
var keys = []string{}
for _, key := range strings.Split(params.UrlList, "\n") {
key = strings.TrimSpace(key)
if len(key) == 0 {
continue
}
if lists.ContainsString(keys, key) {
continue
}
// 检查域名
u, err := url.Parse(key)
if err != nil || len(u.Host) == 0 || (u.Scheme != "http" && u.Scheme != "https") {
this.Fail("'" + key + "'不是正确的URL格式")
}
var host = u.Host
// 去掉端口
hostString, _, err := net.SplitHostPort(host)
if err == nil {
host = hostString
}
if !configutils.MatchDomains(serverNames, host) {
this.Fail("'" + key + "'中域名'" + u.Host + "'没有绑定")
}
if params.KeyType == "prefix" && !strings.HasSuffix(key, "/") {
key += "/"
}
keys = append(keys, key)
}
if len(keys) == 0 {
if params.KeyType == "key" {
this.Fail("请输入要刷新的URL列表")
} else if params.KeyType == "prefix" {
this.Fail("请输入要刷新的目录列表")
}
}
// 校验Key
validateResp, err := this.RPC().HTTPCacheTaskKeyRPC().ValidateHTTPCacheTaskKeys(this.UserContext(), &pb.ValidateHTTPCacheTaskKeysRequest{Keys: keys})
if err != nil {
this.ErrorPage(err)
return
}
var failKeyMaps = []maps.Map{}
if len(validateResp.FailKeys) > 0 {
for _, key := range validateResp.FailKeys {
failKeyMaps = append(failKeyMaps, maps.Map{
"key": key.Key,
"reason": cacheutils.KeyFailReason(key.ReasonCode),
})
}
}
this.Data["failKeys"] = failKeyMaps
if len(failKeyMaps) > 0 {
this.Fail("有" + types.String(len(failKeyMaps)) + "个Key无法完成操作请删除后重试")
}
// 提交任务
_, err = this.RPC().HTTPCacheTaskRPC().CreateHTTPCacheTask(this.UserContext(), &pb.CreateHTTPCacheTaskRequest{
Type: "purge",
KeyType: params.KeyType,
Keys: keys,
})
if err != nil {
this.ErrorPage(err)
return
}
this.Success()
}

View File

@@ -0,0 +1,22 @@
package cache
import (
"github.com/TeaOSLab/EdgeUser/internal/web/helpers"
"github.com/iwind/TeaGo"
)
func init() {
TeaGo.BeforeStart(func(server *TeaGo.Server) {
server.
Helper(helpers.NewUserMustAuth("")).
Data("teaMenu", "servers").
Data("teaSubMenu", "cache").
Prefix("/servers/cache").
GetPost("", new(IndexAction)).
GetPost("/fetch", new(FetchAction)).
Get("/tasks", new(TasksAction)).
GetPost("/task", new(TaskAction)).
Post("/deleteTask", new(DeleteTaskAction)).
EndAll()
})
}

View File

@@ -0,0 +1,123 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package cache
import (
"encoding/json"
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
"sort"
)
type TaskAction struct {
actionutils.ParentAction
}
func (this *TaskAction) Init() {
this.Nav("", "", "task")
}
func (this *TaskAction) RunGet(params struct {
TaskId int64
}) {
// 初始化菜单数据
err := InitMenu(this.Parent())
if err != nil {
this.ErrorPage(err)
return
}
if !this.readTask(params.TaskId) {
return
}
this.Show()
}
func (this *TaskAction) RunPost(params struct {
TaskId int64
}) {
if !this.readTask(params.TaskId) {
return
}
this.Success()
}
func (this *TaskAction) readTask(taskId int64) (ok bool) {
taskResp, err := this.RPC().HTTPCacheTaskRPC().FindEnabledHTTPCacheTask(this.UserContext(), &pb.FindEnabledHTTPCacheTaskRequest{HttpCacheTaskId: taskId})
if err != nil {
this.ErrorPage(err)
return
}
var task = taskResp.HttpCacheTask
if task == nil {
this.NotFound("HTTPCacheTask", taskId)
return
}
// 用户
var userMap = maps.Map{"id": 0, "username": "", "fullname": ""}
if task.User != nil {
userMap = maps.Map{
"id": task.User.Id,
"username": task.User.Username,
"fullname": task.User.Fullname,
}
}
// keys
var keyMaps = []maps.Map{}
for _, key := range task.HttpCacheTaskKeys {
// 错误信息
var errorMaps = []maps.Map{}
if len(key.ErrorsJSON) > 0 {
var m = map[int64]string{}
err = json.Unmarshal(key.ErrorsJSON, &m)
if err != nil {
this.ErrorPage(err)
return
}
for nodeId, errString := range m {
errorMaps = append(errorMaps, maps.Map{
"nodeId": nodeId,
"error": errString,
})
}
}
// 错误信息排序
if len(errorMaps) > 0 {
sort.Slice(errorMaps, func(i, j int) bool {
var m1 = errorMaps[i]
var m2 = errorMaps[j]
return m1.GetInt64("nodeId") < m2.GetInt64("nodeId")
})
}
keyMaps = append(keyMaps, maps.Map{
"key": key.Key,
"isDone": key.IsDone,
"isDoing": key.IsDoing,
"errors": errorMaps,
})
}
this.Data["task"] = maps.Map{
"id": task.Id,
"type": task.Type,
"keyType": task.KeyType,
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", task.CreatedAt),
"doneTime": timeutil.FormatTime("Y-m-d H:i:s", task.DoneAt),
"isDone": task.IsDone,
"isOk": task.IsOk,
"keys": keyMaps,
"user": userMap,
}
ok = true
return
}

View File

@@ -0,0 +1,72 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package cache
import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
"github.com/iwind/TeaGo/maps"
timeutil "github.com/iwind/TeaGo/utils/time"
)
type TasksAction struct {
actionutils.ParentAction
}
func (this *TasksAction) Init() {
this.Nav("", "", "task")
}
func (this *TasksAction) RunGet(params struct{}) {
// 初始化菜单数据
err := InitMenu(this.Parent())
if err != nil {
this.ErrorPage(err)
return
}
// 任务数量
countResp, err := this.RPC().HTTPCacheTaskRPC().CountHTTPCacheTasks(this.UserContext(), &pb.CountHTTPCacheTasksRequest{})
if err != nil {
this.ErrorPage(err)
return
}
var count = countResp.Count
var page = this.NewPage(count)
this.Data["page"] = page.AsHTML()
// 任务列表
var taskMaps = []maps.Map{}
tasksResp, err := this.RPC().HTTPCacheTaskRPC().ListHTTPCacheTasks(this.UserContext(), &pb.ListHTTPCacheTasksRequest{
Offset: page.Offset,
Size: page.Size,
})
if err != nil {
this.ErrorPage(err)
return
}
for _, task := range tasksResp.HttpCacheTasks {
var userMap = maps.Map{"id": 0, "username": "", "fullname": ""}
if task.User != nil {
userMap = maps.Map{
"id": task.User.Id,
"username": task.User.Username,
"fullname": task.User.Fullname,
}
}
taskMaps = append(taskMaps, maps.Map{
"id": task.Id,
"type": task.Type,
"keyType": task.KeyType,
"isDone": task.IsDone,
"isOk": task.IsOk,
"createdTime": timeutil.FormatTime("Y-m-d H:i:s", task.CreatedAt),
"user": userMap,
})
}
this.Data["tasks"] = taskMaps
this.Show()
}

View File

@@ -0,0 +1,24 @@
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved. Official site: https://goedge.cn .
package cache
import (
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
"github.com/TeaOSLab/EdgeUser/internal/rpc"
"github.com/TeaOSLab/EdgeUser/internal/web/actions/actionutils"
)
func InitMenu(parent *actionutils.ParentAction) error {
rpcClient, err := rpc.SharedRPC()
if err != nil {
return err
}
countTasksResp, err := rpcClient.HTTPCacheTaskRPC().CountDoingHTTPCacheTasks(parent.UserContext(), &pb.CountDoingHTTPCacheTasksRequest{})
if err != nil {
return err
}
parent.Data["countDoingTasks"] = countTasksResp.Count
return nil
}