Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d4c80eedb | ||
|
|
af4bd950b7 | ||
|
|
f85f9b0920 | ||
|
|
89ea96c28e | ||
|
|
6f2e3f5f13 | ||
|
|
ee91e8312f |
@@ -9,7 +9,8 @@
|
|||||||
* `高扩展性` - 可以自由扩展新的节点,支持亿级数据
|
* `高扩展性` - 可以自由扩展新的节点,支持亿级数据
|
||||||
|
|
||||||
## 文档
|
## 文档
|
||||||
[点这里查看文档](http://edge.teaos.cn/docs)
|
* [新手指南](https://edge.teaos.cn/docs/QuickStart/Index.md)
|
||||||
|
* [文档](http://edge.teaos.cn/docs)
|
||||||
|
|
||||||
## 架构
|
## 架构
|
||||||

|

|
||||||
@@ -24,3 +25,4 @@
|
|||||||
|
|
||||||
## 感谢
|
## 感谢
|
||||||
* 感谢[JetBrains公司](https://www.jetbrains.com/)提供免费的IDE开发Licence。
|
* 感谢[JetBrains公司](https://www.jetbrains.com/)提供免费的IDE开发Licence。
|
||||||
|
* 感谢[Gitee](https://gitee.com/)提供国内源代码托管平台
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package teaconst
|
package teaconst
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version = "0.2.0"
|
Version = "0.2.2"
|
||||||
|
|
||||||
ProductName = "Edge Admin"
|
ProductName = "Edge Admin"
|
||||||
ProcessName = "edge-admin"
|
ProcessName = "edge-admin"
|
||||||
|
|||||||
@@ -336,6 +336,10 @@ func (this *RPCClient) UserBillRPC() pb.UserBillServiceClient {
|
|||||||
return pb.NewUserBillServiceClient(this.pickConn())
|
return pb.NewUserBillServiceClient(this.pickConn())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (this *RPCClient) UserAccessKeyRPC() pb.UserAccessKeyServiceClient {
|
||||||
|
return pb.NewUserAccessKeyServiceClient(this.pickConn())
|
||||||
|
}
|
||||||
|
|
||||||
func (this *RPCClient) LoginRPC() pb.LoginServiceClient {
|
func (this *RPCClient) LoginRPC() pb.LoginServiceClient {
|
||||||
return pb.NewLoginServiceClient(this.pickConn())
|
return pb.NewLoginServiceClient(this.pickConn())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ func (this *LocationHelper) createMenus(serverIdString string, locationIdString
|
|||||||
"name": "HTTP Header",
|
"name": "HTTP Header",
|
||||||
"url": "/servers/server/settings/locations/headers?serverId=" + serverIdString + "&locationId=" + locationIdString,
|
"url": "/servers/server/settings/locations/headers?serverId=" + serverIdString + "&locationId=" + locationIdString,
|
||||||
"isActive": secondMenuItem == "header",
|
"isActive": secondMenuItem == "header",
|
||||||
"isOn": locationConfig != nil && locationConfig.Web != nil && ((locationConfig.Web.RequestHeaderPolicyRef != nil && locationConfig.Web.RequestHeaderPolicyRef.IsPrior) || (locationConfig.Web.ResponseHeaderPolicyRef != nil && locationConfig.Web.ResponseHeaderPolicyRef.IsPrior)),
|
"isOn": locationConfig != nil && this.hasHTTPHeaders(locationConfig.Web),
|
||||||
})
|
})
|
||||||
menuItems = append(menuItems, maps.Map{
|
menuItems = append(menuItems, maps.Map{
|
||||||
"name": "Websocket",
|
"name": "Websocket",
|
||||||
@@ -158,3 +158,21 @@ func (this *LocationHelper) createMenus(serverIdString string, locationIdString
|
|||||||
|
|
||||||
return menuItems
|
return menuItems
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查是否已设置Header
|
||||||
|
func (this *LocationHelper) hasHTTPHeaders(web *serverconfigs.HTTPWebConfig) bool {
|
||||||
|
if web == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if web.RequestHeaderPolicyRef != nil {
|
||||||
|
if web.RequestHeaderPolicyRef.IsOn && web.RequestHeaderPolicy != nil && !web.RequestHeaderPolicy.IsEmpty() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if web.ResponseHeaderPolicyRef != nil {
|
||||||
|
if web.ResponseHeaderPolicyRef.IsOn && web.ResponseHeaderPolicy != nil && !web.ResponseHeaderPolicy.IsEmpty() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
@@ -310,7 +310,7 @@ func (this *ServerHelper) createSettingsMenu(secondMenuItem string, serverIdStri
|
|||||||
"name": "HTTP Header",
|
"name": "HTTP Header",
|
||||||
"url": "/servers/server/settings/headers?serverId=" + serverIdString,
|
"url": "/servers/server/settings/headers?serverId=" + serverIdString,
|
||||||
"isActive": secondMenuItem == "header",
|
"isActive": secondMenuItem == "header",
|
||||||
"isOn": serverConfig.Web != nil && ((serverConfig.Web.RequestHeaderPolicyRef != nil && serverConfig.Web.RequestHeaderPolicyRef.IsOn) || (serverConfig.Web.ResponseHeaderPolicyRef != nil && serverConfig.Web.ResponseHeaderPolicyRef.IsOn)),
|
"isOn": this.hasHTTPHeaders(serverConfig.Web),
|
||||||
})
|
})
|
||||||
menuItems = append(menuItems, maps.Map{
|
menuItems = append(menuItems, maps.Map{
|
||||||
"name": "Websocket",
|
"name": "Websocket",
|
||||||
@@ -378,3 +378,21 @@ func (this *ServerHelper) createDeleteMenu(secondMenuItem string, serverIdString
|
|||||||
})
|
})
|
||||||
return menuItems
|
return menuItems
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查是否已设置Header
|
||||||
|
func (this *ServerHelper) hasHTTPHeaders(web *serverconfigs.HTTPWebConfig) bool {
|
||||||
|
if web == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if web.RequestHeaderPolicyRef != nil {
|
||||||
|
if web.RequestHeaderPolicyRef.IsOn && web.RequestHeaderPolicy != nil && !web.RequestHeaderPolicy.IsEmpty() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if web.ResponseHeaderPolicyRef != nil {
|
||||||
|
if web.ResponseHeaderPolicyRef.IsOn && web.ResponseHeaderPolicy != nil && !web.ResponseHeaderPolicy.IsEmpty() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
49
internal/web/actions/default/users/accesskeys/createPopup.go
Normal file
49
internal/web/actions/default/users/accesskeys/createPopup.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
// 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/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("创建AccessKey %d", accessKeyIdResp.UserAccessKeyId)
|
||||||
|
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
24
internal/web/actions/default/users/accesskeys/delete.go
Normal file
24
internal/web/actions/default/users/accesskeys/delete.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package accesskeys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DeleteAction struct {
|
||||||
|
actionutils.ParentAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (this *DeleteAction) RunPost(params struct {
|
||||||
|
AccessKeyId int64
|
||||||
|
}) {
|
||||||
|
defer this.CreateLogInfo("删除AccessKey %d", params.AccessKeyId)
|
||||||
|
|
||||||
|
_, err := this.RPC().UserAccessKeyRPC().DeleteUserAccessKey(this.AdminContext(), &pb.DeleteUserAccessKeyRequest{UserAccessKeyId: params.AccessKeyId})
|
||||||
|
if err != nil {
|
||||||
|
this.ErrorPage(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Success()
|
||||||
|
}
|
||||||
54
internal/web/actions/default/users/accesskeys/index.go
Normal file
54
internal/web/actions/default/users/accesskeys/index.go
Normal 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()
|
||||||
|
}
|
||||||
28
internal/web/actions/default/users/accesskeys/updateIsOn.go
Normal file
28
internal/web/actions/default/users/accesskeys/updateIsOn.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package accesskeys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAdmin/internal/web/actions/actionutils"
|
||||||
|
"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("设置AccessKey %d 启用状态", 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()
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package users
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/TeaOSLab/EdgeAdmin/internal/configloaders"
|
"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/TeaOSLab/EdgeAdmin/internal/web/helpers"
|
||||||
"github.com/iwind/TeaGo"
|
"github.com/iwind/TeaGo"
|
||||||
)
|
)
|
||||||
@@ -18,6 +19,14 @@ func init() {
|
|||||||
GetPost("/update", new(UpdateAction)).
|
GetPost("/update", new(UpdateAction)).
|
||||||
Post("/delete", new(DeleteAction)).
|
Post("/delete", new(DeleteAction)).
|
||||||
GetPost("/features", new(FeaturesAction)).
|
GetPost("/features", new(FeaturesAction)).
|
||||||
|
|
||||||
|
// AccessKeys
|
||||||
|
Prefix("/users/accessKeys").
|
||||||
|
Get("", new(accesskeys.IndexAction)).
|
||||||
|
GetPost("/createPopup", new(accesskeys.CreatePopupAction)).
|
||||||
|
Post("/delete", new(accesskeys.DeleteAction)).
|
||||||
|
Post("/updateIsOn", new(accesskeys.UpdateIsOnAction)).
|
||||||
|
|
||||||
EndAll()
|
EndAll()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,4 +4,5 @@
|
|||||||
<menu-item :href="'/users/user?userId=' + user.id" code="index">{{user.fullname}} <span class="small">({{user.username}})</span></menu-item>
|
<menu-item :href="'/users/user?userId=' + user.id" code="index">{{user.fullname}} <span class="small">({{user.username}})</span></menu-item>
|
||||||
<menu-item :href="'/users/update?userId=' + user.id" code="update">修改</menu-item>
|
<menu-item :href="'/users/update?userId=' + user.id" code="update">修改</menu-item>
|
||||||
<menu-item :href="'/users/features?userId=' + user.id" code="feature">功能</menu-item>
|
<menu-item :href="'/users/features?userId=' + user.id" code="feature">功能</menu-item>
|
||||||
|
<menu-item :href="'/users/accessKeys?userId=' + user.id" code="accessKey">API AccessKey</menu-item>
|
||||||
</first-menu>
|
</first-menu>
|
||||||
20
web/views/@default/users/accesskeys/createPopup.html
Normal file
20
web/views/@default/users/accesskeys/createPopup.html
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{$layout "layout_popup"}
|
||||||
|
|
||||||
|
<h3>创建新AccessKey</h3>
|
||||||
|
|
||||||
|
<form class="ui form" method="post" data-tea-action="$" data-tea-success="success">
|
||||||
|
<csrf-token></csrf-token>
|
||||||
|
<input type="hidden" name="userId" :value="userId"/>
|
||||||
|
|
||||||
|
<table class="ui table definition selectable">
|
||||||
|
<tr>
|
||||||
|
<td class="title">备注 *</td>
|
||||||
|
<td>
|
||||||
|
<textarea rows="2" name="description" maxlength="100" ref="focus"></textarea>
|
||||||
|
<p class="comment">描述AccessKey的用途等。</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<submit-btn></submit-btn>
|
||||||
|
</form>
|
||||||
39
web/views/@default/users/accesskeys/index.html
Normal file
39
web/views/@default/users/accesskeys/index.html
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
{$layout}
|
||||||
|
{$template "../user_menu"}
|
||||||
|
|
||||||
|
<second-menu>
|
||||||
|
<menu-item @click.prevent="createAccessKey()">[创建AccessKey]</menu-item>
|
||||||
|
</second-menu>
|
||||||
|
|
||||||
|
<p class="comment" v-if="accessKeys.length == 0">暂时还没有AccessKey。</p>
|
||||||
|
|
||||||
|
<table class="ui table selectable" v-if="accessKeys.length > 0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>AccessKey ID</th>
|
||||||
|
<th>AccessKey密钥</th>
|
||||||
|
<th>备注</th>
|
||||||
|
<th>最后访问</th>
|
||||||
|
<th>状态</th>
|
||||||
|
<th class="two op">操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tr v-for="accessKey in accessKeys">
|
||||||
|
<td :class="{disabled: !accessKey.isOn}">{{accessKey.uniqueId}}</td>
|
||||||
|
<td :class="{disabled: !accessKey.isOn}">{{accessKey.secret}}</td>
|
||||||
|
<td :class="{disabled: !accessKey.isOn}">{{accessKey.description}}</td>
|
||||||
|
<td :class="{disabled: !accessKey.isOn}">
|
||||||
|
<span v-if="accessKey.accessedTime.length > 0">{{accessKey.accessedTime}}</span>
|
||||||
|
<span v-else class="disabled">尚无访问</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span v-if="accessKey.isOn" class="green">已启用</span>
|
||||||
|
<span v-else class="disabled">已禁用</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="" v-if="accessKey.isOn" @click.prevent="updateAccessKeyIsOn(accessKey.id, false)">禁用</a>
|
||||||
|
<a href="" v-if="!accessKey.isOn" @click.prevent="updateAccessKeyIsOn(accessKey.id, true)">启用</a>
|
||||||
|
<a href="" @click.prevent="deleteAccessKey(accessKey.id)">删除</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
41
web/views/@default/users/accesskeys/index.js
Normal file
41
web/views/@default/users/accesskeys/index.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
Tea.context(function () {
|
||||||
|
this.createAccessKey = function () {
|
||||||
|
teaweb.popup("/users/accessKeys/createPopup?userId=" + this.user.id, {
|
||||||
|
callback: function () {
|
||||||
|
teaweb.success("保存成功", function () {
|
||||||
|
teaweb.reload()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateAccessKeyIsOn = function (accessKeyId, isOn) {
|
||||||
|
let that = this
|
||||||
|
|
||||||
|
let message = ""
|
||||||
|
if (isOn) {
|
||||||
|
message = "确定要启用此AccessKey吗?"
|
||||||
|
} else {
|
||||||
|
message = "确定要禁用此AccessKey吗?"
|
||||||
|
}
|
||||||
|
teaweb.confirm(message, function () {
|
||||||
|
that.$post(".updateIsOn")
|
||||||
|
.params({
|
||||||
|
accessKeyId: accessKeyId,
|
||||||
|
isOn: isOn ? 1 : 0
|
||||||
|
})
|
||||||
|
.refresh()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.deleteAccessKey = function (accessKeyId) {
|
||||||
|
let that = this
|
||||||
|
teaweb.confirm("确定要删除此AccessKey吗?", function () {
|
||||||
|
that.$post(".delete")
|
||||||
|
.params({
|
||||||
|
accessKeyId: accessKeyId
|
||||||
|
})
|
||||||
|
.refresh()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user