add: API Token相关的前端页面

This commit is contained in:
Akkia 2022-05-18 20:52:18 +08:00
parent fcd0ccae56
commit 914d7cbbae
No known key found for this signature in database
GPG Key ID: DABE9A4AB2DD7EF3
11 changed files with 149 additions and 1 deletions

View File

@ -57,18 +57,21 @@ func (ma *memberAPI) serve() {
type apiResult struct { type apiResult struct {
Token string `json:"token"` Token string `json:"token"`
Note string `json:"note"`
} }
// getToken 获取 Token // getToken 获取 Token
func (ma *memberAPI) getToken(c *gin.Context) { func (ma *memberAPI) getToken(c *gin.Context) {
u := c.MustGet(model.CtxKeyAuthorizedUser).(*model.User) u := c.MustGet(model.CtxKeyAuthorizedUser).(*model.User)
singleton.ApiLock.RLock() singleton.ApiLock.RLock()
defer singleton.ApiLock.RUnlock()
tokenList := singleton.UserIDToApiTokenList[u.ID] tokenList := singleton.UserIDToApiTokenList[u.ID]
singleton.ApiLock.RUnlock()
res := make([]*apiResult, len(tokenList)) res := make([]*apiResult, len(tokenList))
for i, token := range tokenList { for i, token := range tokenList {
res[i] = &apiResult{ res[i] = &apiResult{
Token: token, Token: token,
Note: singleton.ApiTokenList[token].Note,
} }
} }
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
@ -78,12 +81,26 @@ func (ma *memberAPI) getToken(c *gin.Context) {
}) })
} }
type TokenForm struct {
Note string
}
// issueNewToken 生成新的 token // issueNewToken 生成新的 token
func (ma *memberAPI) issueNewToken(c *gin.Context) { func (ma *memberAPI) issueNewToken(c *gin.Context) {
u := c.MustGet(model.CtxKeyAuthorizedUser).(*model.User) u := c.MustGet(model.CtxKeyAuthorizedUser).(*model.User)
tf := &TokenForm{}
err := c.ShouldBindJSON(tf)
if err != nil {
c.JSON(http.StatusOK, model.Response{
Code: http.StatusBadRequest,
Message: fmt.Sprintf("请求错误:%s", err),
})
return
}
token := &model.ApiToken{ token := &model.ApiToken{
UserID: u.ID, UserID: u.ID,
Token: utils.MD5(fmt.Sprintf("%d%d%s", time.Now().UnixNano(), u.ID, u.Login)), Token: utils.MD5(fmt.Sprintf("%d%d%s", time.Now().UnixNano(), u.ID, u.Login)),
Note: tf.Note,
} }
singleton.DB.Create(token) singleton.DB.Create(token)
@ -97,6 +114,7 @@ func (ma *memberAPI) issueNewToken(c *gin.Context) {
Message: "success", Message: "success",
Result: map[string]string{ Result: map[string]string{
"token": token.Token, "token": token.Token,
"note": token.Note,
}, },
}) })
} }

View File

@ -28,6 +28,16 @@ func (mp *memberPage) serve() {
mr.GET("/cron", mp.cron) mr.GET("/cron", mp.cron)
mr.GET("/notification", mp.notification) mr.GET("/notification", mp.notification)
mr.GET("/setting", mp.setting) mr.GET("/setting", mp.setting)
mr.GET("/api", mp.api)
}
func (mp *memberPage) api(c *gin.Context) {
singleton.ApiLock.RLock()
defer singleton.ApiLock.RUnlock()
c.HTML(http.StatusOK, "dashboard/api", mygin.CommonEnvironment(c, gin.H{
"title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "ApiManagement"}),
"Tokens": singleton.ApiTokenList,
}))
} }
func (mp *memberPage) server(c *gin.Context) { func (mp *memberPage) server(c *gin.Context) {

View File

@ -4,4 +4,5 @@ type ApiToken struct {
Common Common
UserID uint64 `json:"user_id"` UserID uint64 `json:"user_id"`
Token string `json:"token"` Token string `json:"token"`
Note string `json:"note"`
} }

View File

@ -17,6 +17,7 @@ var adminPage = map[string]bool{
"/setting": true, "/setting": true,
"/notification": true, "/notification": true,
"/cron": true, "/cron": true,
"/api": true,
} }
func CommonEnvironment(c *gin.Context, data map[string]interface{}) gin.H { func CommonEnvironment(c *gin.Context, data map[string]interface{}) gin.H {

View File

@ -469,6 +469,21 @@ other = "Services"
[ScheduledTasks] [ScheduledTasks]
other = "Scheduled Tasks" other = "Scheduled Tasks"
[ApiManagement]
other="API"
[IssueNewApiToken]
other="Create Token"
[Token]
other="Token"
[DeleteToken]
other="Delete Token"
[ConfirmToDeleteThisToken]
other="Confirm Delete?"
[YouAreNotAuthorized] [YouAreNotAuthorized]
other = "You are not authorized" other = "You are not authorized"

View File

@ -469,6 +469,21 @@ other = "Monitorización del servicio"
[ScheduledTasks] [ScheduledTasks]
other = "Tareas programadas" other = "Tareas programadas"
[ApiManagement]
other="API"
[IssueNewApiToken]
other="Create Token"
[Token]
other="Token"
[DeleteToken]
other="Delete Token"
[ConfirmToDeleteThisToken]
other="Confirm Delete?"
[YouAreNotAuthorized] [YouAreNotAuthorized]
other = "Esta página requiere un acceso" other = "Esta página requiere un acceso"

View File

@ -469,6 +469,21 @@ other = "服务监控"
[ScheduledTasks] [ScheduledTasks]
other = "计划任务" other = "计划任务"
[ApiManagement]
other="API"
[IssueNewApiToken]
other="添加Token"
[Token]
other="Token"
[DeleteToken]
other="删除Token"
[ConfirmToDeleteThisToken]
other="确认删除Token"
[YouAreNotAuthorized] [YouAreNotAuthorized]
other = "此页面需要登录" other = "此页面需要登录"

View File

@ -202,6 +202,18 @@ function post(path, params, method = 'post') {
document.body.removeChild(form); document.body.removeChild(form);
} }
function issueNewApiToken(apiToken) {
const modal = $(".api.modal");
modal.children(".header").text((apiToken ? LANG.Edit : LANG.Add) + ' ' + "API Token");
modal
.find(".nezha-primary-btn.button")
.html(
apiToken ? LANG.Edit + '<i class="edit icon"></i>' : LANG.Add + '<i class="add icon"></i>'
);
modal.find("textarea[name=Note]").val(apiToken ? apiToken.Note : null);
showFormModal(".api.modal", "#apiForm", "/api/token");
}
function addOrEditServer(server, conf) { function addOrEditServer(server, conf) {
const modal = $(".server.modal"); const modal = $(".server.modal");
modal.children(".header").text((server ? LANG.Edit : LANG.Add) + ' ' + LANG.Server); modal.children(".header").text((server ? LANG.Edit : LANG.Add) + ' ' + LANG.Server);

View File

@ -9,6 +9,7 @@
<a class='item{{if eq .MatchedPath "/monitor"}} active{{end}}' href="/monitor"><i class="rss icon"></i>{{tr "Services"}}</a> <a class='item{{if eq .MatchedPath "/monitor"}} active{{end}}' href="/monitor"><i class="rss icon"></i>{{tr "Services"}}</a>
<a class='item{{if eq .MatchedPath "/cron"}} active{{end}}' href="/cron"><i class="clock icon"></i>{{tr "Task"}}</a> <a class='item{{if eq .MatchedPath "/cron"}} active{{end}}' href="/cron"><i class="clock icon"></i>{{tr "Task"}}</a>
<a class='item{{if eq .MatchedPath "/notification"}} active{{end}}' href="/notification"><i class="bell icon"></i>{{tr "Notification"}}</a> <a class='item{{if eq .MatchedPath "/notification"}} active{{end}}' href="/notification"><i class="bell icon"></i>{{tr "Notification"}}</a>
<a class='item{{if eq .MatchedPath "/api"}} active{{end}}' href="/api"><i class="key icon"></i>API</a>
<a class='item{{if eq .MatchedPath "/setting"}} active{{end}}' href="/setting"> <a class='item{{if eq .MatchedPath "/setting"}} active{{end}}' href="/setting">
<i class="settings icon"></i>{{tr "Settings"}} <i class="settings icon"></i>{{tr "Settings"}}
</a> </a>

19
resource/template/component/api.html vendored Normal file
View File

@ -0,0 +1,19 @@
{{define "component/api"}}
<div class="ui tiny api modal transition hidden">
<div class="header">{{tr "IssueNewApiToken"}}</div>
<div class="content">
<form id="apiForm" class="ui form">
<input type="hidden" name="id">
<div class="field">
<label>{{tr "Note"}}</label>
<textarea name="Note"></textarea>
</div>
</form>
</div>
<div class=" actions">
<div class="ui negative button">{{tr "Cancel"}}</div>
<button class="ui positive nezha-primary-btn right labeled icon button">{{tr "Confirm"}}<i class="checkmark icon"></i>
</button>
</div>
</div>
{{end}}

View File

@ -0,0 +1,41 @@
{{define "dashboard/api"}}
{{template "common/header" .}}
{{template "common/menu" .}}
<div class="nb-container">
<div class="ui container">
<div class="ui grid">
<div class="right floated right aligned twelve wide column">
<button class="ui right labeled nezha-primary-btn icon button" onclick="issueNewApiToken()"><i class="add icon"></i>
{{tr "IssueNewApiToken"}}
</button>
</div>
</div>
<table class="ui very basic table">
<thead>
<tr>
<th>{{tr "Token"}}</th>
<th>{{tr "Note"}}</th>
</tr>
</thead>
<tbody>
{{range $token := .Tokens}}
<tr>
<td>{{$token.Token}}</td>
<td>{{$token.Note}}</td>
<td>
<div class="ui mini icon buttons">
<button class="ui button"
onclick="showConfirm('{{tr "DeleteToken"}}','{{tr "ConfirmToDeleteThisToken"}}',deleteRequest,'/api/token/'+{{$token.Token}})">
<i class="trash alternate outline icon"></i>
</button>
</div>
</td>
</tr>
{{end}}
</tbody>
</table>
</div>
</div>
{{template "component/api"}}
{{template "common/footer" .}}
{{end}}