Skip to content

Commit 730d3b2

Browse files
committed
Update.
1 parent db96840 commit 730d3b2

30 files changed

+632
-108
lines changed

.gitignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,6 @@
1818
/main
1919
/cmd/agent/main
2020
/cmd/dashboard/main
21-
/config.yml
21+
/config.yml
22+
/resource/template/theme-custom
23+
/resource/static/theme-custom

Dockerfile

+5-3
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,17 @@ RUN cd cmd/dashboard && go build -o app -ldflags="-s -w"
77

88
FROM alpine:latest
99
ENV TZ="Asia/Shanghai"
10+
COPY ./script/entrypoint.sh /entrypoint.sh
1011
RUN apk --no-cache --no-progress add \
1112
ca-certificates \
1213
tzdata && \
1314
cp "/usr/share/zoneinfo/$TZ" /etc/localtime && \
14-
echo "$TZ" > /etc/timezone
15+
echo "$TZ" > /etc/timezone && \
16+
chmod +x /entrypoint.sh
1517
WORKDIR /dashboard
1618
COPY ./resource ./resource
1719
COPY --from=binarybuilder /dashboard/cmd/dashboard/app ./app
1820

1921
VOLUME ["/dashboard/data"]
20-
EXPOSE 8008 2222
21-
CMD ["/dashboard/app"]
22+
EXPOSE 80 2222
23+
ENTRYPOINT ["/entrypoint.sh"]

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# 探针轻量版
22
> 本项目为原项目[哪吒探针](https://github.com/naiba/nezha)的精简修改自用版
33
4-
![GitHub Workflow Status](https://img.shields.io/github/workflow/status/xOS/ServerStatus/Dashboard%20image?label=管理面板%20v0.1.15&logo=github&style=for-the-badge) ![Agent release](https://img.shields.io/github/v/release/xOS/ServerStatus?color=brightgreen&label=Agent&style=for-the-badge&logo=github) ![GitHub Workflow Status](https://img.shields.io/github/workflow/status/xOS/ServerStatus/Agent%20release?label=Agent%20CI&logo=github&style=for-the-badge) ![shell](https://img.shields.io/badge/安装脚本-v0.1.7-brightgreen?style=for-the-badge&logo=linux)
4+
![GitHub Workflow Status](https://img.shields.io/github/workflow/status/xOS/ServerStatus/Dashboard%20image?label=管理面板%20v0.1.16&logo=github&style=for-the-badge) ![Agent release](https://img.shields.io/github/v/release/xOS/ServerStatus?color=brightgreen&label=Agent&style=for-the-badge&logo=github) ![GitHub Workflow Status](https://img.shields.io/github/workflow/status/xOS/ServerStatus/Agent%20release?label=Agent%20CI&logo=github&style=for-the-badge) ![shell](https://img.shields.io/badge/安装脚本-v0.1.8-brightgreen?style=for-the-badge&logo=linux)
55

66
## 注意:
77

cmd/agent/processgroup/process_group.go

-54
This file was deleted.

cmd/agent/processgroup/process_group_windows.go

-30
This file was deleted.

cmd/dashboard/controller/api_v1.go

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package controller
2+
3+
import (
4+
"github.com/gin-gonic/gin"
5+
"github.com/xos/serverstatus/pkg/mygin"
6+
"github.com/xos/serverstatus/service/singleton"
7+
"strconv"
8+
"strings"
9+
)
10+
11+
type apiV1 struct {
12+
r gin.IRouter
13+
}
14+
15+
func (v *apiV1) serve() {
16+
r := v.r.Group("")
17+
// API
18+
r.Use(mygin.Authorize(mygin.AuthorizeOption{
19+
Member: true,
20+
IsPage: false,
21+
AllowAPI: true,
22+
Msg: "访问此接口需要认证",
23+
Btn: "点此登录",
24+
Redirect: "/login",
25+
}))
26+
r.GET("/server/list", v.serverList)
27+
r.GET("/server/details", v.serverDetails)
28+
29+
}
30+
31+
// serverList 获取服务器列表 不传入Query参数则获取全部
32+
// header: Authorization: Token
33+
// query: tag (服务器分组)
34+
func (v *apiV1) serverList(c *gin.Context) {
35+
tag := c.Query("tag")
36+
if tag != "" {
37+
c.JSON(200, singleton.ServerAPI.GetListByTag(tag))
38+
return
39+
}
40+
c.JSON(200, singleton.ServerAPI.GetAllList())
41+
}
42+
43+
// serverDetails 获取服务器信息 不传入Query参数则获取全部
44+
// header: Authorization: Token
45+
// query: id (服务器ID,逗号分隔,优先级高于tag查询)
46+
// query: tag (服务器分组)
47+
func (v *apiV1) serverDetails(c *gin.Context) {
48+
var idList []uint64
49+
idListStr := strings.Split(c.Query("id"), ",")
50+
if c.Query("id") != "" {
51+
idList = make([]uint64, len(idListStr))
52+
for i, v := range idListStr {
53+
id, _ := strconv.ParseUint(v, 10, 64)
54+
idList[i] = id
55+
}
56+
}
57+
tag := c.Query("tag")
58+
if tag != "" {
59+
c.JSON(200, singleton.ServerAPI.GetStatusByTag(tag))
60+
return
61+
}
62+
if len(idList) != 0 {
63+
c.JSON(200, singleton.ServerAPI.GetStatusByIDList(idList))
64+
return
65+
}
66+
c.JSON(200, singleton.ServerAPI.GetAllStatus())
67+
}

cmd/dashboard/controller/member_api.go

+145
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,120 @@ func (ma *memberAPI) serve() {
3939
mr.POST("/setting", ma.updateSetting)
4040
mr.DELETE("/:model/:id", ma.delete)
4141
mr.POST("/logout", ma.logout)
42+
mr.GET("/token", ma.getToken)
43+
mr.POST("/token", ma.issueNewToken)
44+
mr.DELETE("/token/:token", ma.deleteToken)
45+
46+
// API
47+
v1 := ma.r.Group("v1")
48+
{
49+
apiv1 := &apiV1{v1}
50+
apiv1.serve()
51+
}
52+
}
53+
54+
type apiResult struct {
55+
Token string `json:"token"`
56+
Note string `json:"note"`
57+
}
58+
59+
// getToken 获取 Token
60+
func (ma *memberAPI) getToken(c *gin.Context) {
61+
u := c.MustGet(model.CtxKeyAuthorizedUser).(*model.User)
62+
singleton.ApiLock.RLock()
63+
defer singleton.ApiLock.RUnlock()
64+
65+
tokenList := singleton.UserIDToApiTokenList[u.ID]
66+
res := make([]*apiResult, len(tokenList))
67+
for i, token := range tokenList {
68+
res[i] = &apiResult{
69+
Token: token,
70+
Note: singleton.ApiTokenList[token].Note,
71+
}
72+
}
73+
c.JSON(http.StatusOK, gin.H{
74+
"code": 0,
75+
"message": "success",
76+
"result": res,
77+
})
78+
}
79+
80+
type TokenForm struct {
81+
Note string
82+
}
83+
84+
// issueNewToken 生成新的 token
85+
func (ma *memberAPI) issueNewToken(c *gin.Context) {
86+
u := c.MustGet(model.CtxKeyAuthorizedUser).(*model.User)
87+
tf := &TokenForm{}
88+
err := c.ShouldBindJSON(tf)
89+
if err != nil {
90+
c.JSON(http.StatusOK, model.Response{
91+
Code: http.StatusBadRequest,
92+
Message: fmt.Sprintf("请求错误:%s", err),
93+
})
94+
return
95+
}
96+
token := &model.ApiToken{
97+
UserID: u.ID,
98+
Token: utils.MD5(fmt.Sprintf("%d%d%s", time.Now().UnixNano(), u.ID, u.Login)),
99+
Note: tf.Note,
100+
}
101+
singleton.DB.Create(token)
102+
103+
singleton.ApiLock.Lock()
104+
singleton.ApiTokenList[token.Token] = token
105+
singleton.UserIDToApiTokenList[u.ID] = append(singleton.UserIDToApiTokenList[u.ID], token.Token)
106+
singleton.ApiLock.Unlock()
107+
108+
c.JSON(http.StatusOK, model.Response{
109+
Code: http.StatusOK,
110+
Message: "success",
111+
Result: map[string]string{
112+
"token": token.Token,
113+
"note": token.Note,
114+
},
115+
})
116+
}
117+
118+
// deleteToken 删除 token
119+
func (ma *memberAPI) deleteToken(c *gin.Context) {
120+
token := c.Param("token")
121+
if token == "" {
122+
c.JSON(http.StatusOK, model.Response{
123+
Code: http.StatusBadRequest,
124+
Message: "token 不能为空",
125+
})
126+
return
127+
}
128+
singleton.ApiLock.Lock()
129+
defer singleton.ApiLock.Unlock()
130+
if _, ok := singleton.ApiTokenList[token]; !ok {
131+
c.JSON(http.StatusOK, model.Response{
132+
Code: http.StatusBadRequest,
133+
Message: "token 不存在",
134+
})
135+
return
136+
}
137+
// 在数据库中删除该Token
138+
singleton.DB.Unscoped().Delete(&model.ApiToken{}, "token = ?", token)
139+
140+
// 在UserIDToApiTokenList中删除该Token
141+
for i, t := range singleton.UserIDToApiTokenList[singleton.ApiTokenList[token].UserID] {
142+
if t == token {
143+
singleton.UserIDToApiTokenList[singleton.ApiTokenList[token].UserID] = append(singleton.UserIDToApiTokenList[singleton.ApiTokenList[token].UserID][:i], singleton.UserIDToApiTokenList[singleton.ApiTokenList[token].UserID][i+1:]...)
144+
break
145+
}
146+
}
147+
if len(singleton.UserIDToApiTokenList[singleton.ApiTokenList[token].UserID]) == 0 {
148+
delete(singleton.UserIDToApiTokenList, singleton.ApiTokenList[token].UserID)
149+
}
150+
// 在ApiTokenList中删除该Token
151+
delete(singleton.ApiTokenList, token)
152+
c.JSON(http.StatusOK, model.Response{
153+
Code: http.StatusOK,
154+
Message: "success",
155+
})
42156
}
43157

44158
func (ma *memberAPI) delete(c *gin.Context) {
@@ -58,8 +172,21 @@ func (ma *memberAPI) delete(c *gin.Context) {
58172
if err == nil {
59173
// 删除服务器
60174
singleton.ServerLock.Lock()
175+
tag := singleton.ServerList[id].Tag
61176
delete(singleton.SecretToID, singleton.ServerList[id].Secret)
62177
delete(singleton.ServerList, id)
178+
index := 0
179+
for index < len(singleton.ServerTagToIDList[tag]) {
180+
if singleton.ServerTagToIDList[tag][index] == id {
181+
break
182+
}
183+
index++
184+
}
185+
// 删除旧 Tag-ID 绑定关系
186+
singleton.ServerTagToIDList[tag] = append(singleton.ServerTagToIDList[tag][:index], singleton.ServerTagToIDList[tag][index+1:]...)
187+
if len(singleton.ServerTagToIDList[tag]) == 0 {
188+
delete(singleton.ServerTagToIDList, tag)
189+
}
63190
singleton.ServerLock.Unlock()
64191
singleton.ReSortServer()
65192
}
@@ -161,6 +288,23 @@ func (ma *memberAPI) addOrEditServer(c *gin.Context) {
161288
// 设置新的 Secret-ID 绑定关系
162289
delete(singleton.SecretToID, singleton.ServerList[s.ID].Secret)
163290
}
291+
// 如果修改了Tag
292+
if s.Tag != singleton.ServerList[s.ID].Tag {
293+
index := 0
294+
for index < len(singleton.ServerTagToIDList[s.Tag]) {
295+
if singleton.ServerTagToIDList[s.Tag][index] == s.ID {
296+
break
297+
}
298+
index++
299+
}
300+
// 删除旧 Tag-ID 绑定关系
301+
singleton.ServerTagToIDList[singleton.ServerList[s.ID].Tag] = append(singleton.ServerTagToIDList[singleton.ServerList[s.ID].Tag][:index], singleton.ServerTagToIDList[singleton.ServerList[s.ID].Tag][index+1:]...)
302+
// 设置新的 Tag-ID 绑定关系
303+
singleton.ServerTagToIDList[s.Tag] = append(singleton.ServerTagToIDList[s.Tag], s.ID)
304+
if len(singleton.ServerTagToIDList[s.Tag]) == 0 {
305+
delete(singleton.ServerTagToIDList, s.Tag)
306+
}
307+
}
164308
singleton.ServerList[s.ID] = &s
165309
singleton.ServerLock.Unlock()
166310
} else {
@@ -169,6 +313,7 @@ func (ma *memberAPI) addOrEditServer(c *gin.Context) {
169313
singleton.ServerLock.Lock()
170314
singleton.SecretToID[s.Secret] = s.ID
171315
singleton.ServerList[s.ID] = &s
316+
singleton.ServerTagToIDList[s.Tag] = append(singleton.ServerTagToIDList[s.Tag], s.ID)
172317
singleton.ServerLock.Unlock()
173318
}
174319
singleton.ReSortServer()

cmd/dashboard/controller/member_page.go

+10
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,16 @@ func (mp *memberPage) serve() {
2626
mr.GET("/server", mp.server)
2727
mr.GET("/notification", mp.notification)
2828
mr.GET("/setting", mp.setting)
29+
mr.GET("/api", mp.api)
30+
}
31+
32+
func (mp *memberPage) api(c *gin.Context) {
33+
singleton.ApiLock.RLock()
34+
defer singleton.ApiLock.RUnlock()
35+
c.HTML(http.StatusOK, "dashboard/api", mygin.CommonEnvironment(c, gin.H{
36+
"title": singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "ApiManagement"}),
37+
"Tokens": singleton.ApiTokenList,
38+
}))
2939
}
3040

3141
func (mp *memberPage) server(c *gin.Context) {

model/api_token.go

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package model
2+
3+
type ApiToken struct {
4+
Common
5+
UserID uint64 `json:"user_id"`
6+
Token string `json:"token"`
7+
Note string `json:"note"`
8+
}

0 commit comments

Comments
 (0)