Compare commits

...

6 Commits

Author SHA1 Message Date
刘祥超
7d4c80eedb 调整版本为0.2.2 2021-06-17 18:08:14 +08:00
刘祥超
af4bd950b7 用户增加AccessKey管理 2021-06-16 10:48:45 +08:00
刘祥超
f85f9b0920 调整版本为0.2.1 2021-06-16 08:30:44 +08:00
刘祥超
89ea96c28e Update README.md 2021-06-15 14:32:04 +08:00
刘祥超
6f2e3f5f13 ReadMe增加对Gitee的感谢 2021-06-15 11:15:03 +08:00
刘祥超
ee91e8312f 优化界面 2021-06-15 10:32:40 +08:00
14 changed files with 312 additions and 5 deletions

View File

@@ -9,7 +9,8 @@
* `高扩展性` - 可以自由扩展新的节点,支持亿级数据
## 文档
[点这里查看文档](http://edge.teaos.cn/docs)
* [新手指南](https://edge.teaos.cn/docs/QuickStart/Index.md)
* [文档](http://edge.teaos.cn/docs)
## 架构
![架构](doc/architect-zh.jpg)
@@ -23,4 +24,5 @@
有什么问题和建议都可以加入QQ群 `659832182`
## 感谢
* 感谢[JetBrains公司](https://www.jetbrains.com/)提供免费的IDE开发Licence。
* 感谢[JetBrains公司](https://www.jetbrains.com/)提供免费的IDE开发Licence。
* 感谢[Gitee](https://gitee.com/)提供国内源代码托管平台

View File

@@ -1,7 +1,7 @@
package teaconst
const (
Version = "0.2.0"
Version = "0.2.2"
ProductName = "Edge Admin"
ProcessName = "edge-admin"

View File

@@ -336,6 +336,10 @@ func (this *RPCClient) UserBillRPC() pb.UserBillServiceClient {
return pb.NewUserBillServiceClient(this.pickConn())
}
func (this *RPCClient) UserAccessKeyRPC() pb.UserAccessKeyServiceClient {
return pb.NewUserAccessKeyServiceClient(this.pickConn())
}
func (this *RPCClient) LoginRPC() pb.LoginServiceClient {
return pb.NewLoginServiceClient(this.pickConn())
}

View File

@@ -141,7 +141,7 @@ func (this *LocationHelper) createMenus(serverIdString string, locationIdString
"name": "HTTP Header",
"url": "/servers/server/settings/locations/headers?serverId=" + serverIdString + "&locationId=" + locationIdString,
"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{
"name": "Websocket",
@@ -158,3 +158,21 @@ func (this *LocationHelper) createMenus(serverIdString string, locationIdString
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
}

View File

@@ -310,7 +310,7 @@ func (this *ServerHelper) createSettingsMenu(secondMenuItem string, serverIdStri
"name": "HTTP Header",
"url": "/servers/server/settings/headers?serverId=" + serverIdString,
"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{
"name": "Websocket",
@@ -378,3 +378,21 @@ func (this *ServerHelper) createDeleteMenu(secondMenuItem string, serverIdString
})
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
}

View 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()
}

View 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()
}

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,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()
}

View File

@@ -2,6 +2,7 @@ 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"
)
@@ -18,6 +19,14 @@ func init() {
GetPost("/update", new(UpdateAction)).
Post("/delete", new(DeleteAction)).
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()
})
}

View File

@@ -4,4 +4,5 @@
<menu-item :href="'/users/user?userId=' + user.id" code="index">{{user.fullname}}&nbsp; <span class="small">({{user.username}})</span></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/accessKeys?userId=' + user.id" code="accessKey">API AccessKey</menu-item>
</first-menu>

View 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>

View 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>
&nbsp; <a href="" @click.prevent="deleteAccessKey(accessKey.id)">删除</a>
</td>
</tr>
</table>

View 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()
})
}
})