mirror of
https://github.com/nezhahq/nezha.git
synced 2025-01-22 12:48:14 -05:00
Add API to register server (#472)
This commit is contained in:
parent
3d6edd602c
commit
f4b7483807
8
.dockerignore
Normal file
8
.dockerignore
Normal file
@ -0,0 +1,8 @@
|
||||
.git
|
||||
.gitignore
|
||||
docker-compose.yml
|
||||
Dockerfile
|
||||
Dockerfile.dev
|
||||
|
||||
|
||||
data/*
|
63
Dockerfile.dev
Normal file
63
Dockerfile.dev
Normal file
@ -0,0 +1,63 @@
|
||||
# Use build arguments for Go version and architecture
|
||||
ARG GO_VERSION=1.22
|
||||
ARG BUILDARCH=amd64
|
||||
|
||||
# Stage 1: Builder Stage
|
||||
# FROM golang:${GO_VERSION}-alpine AS builder
|
||||
FROM crazymax/xgo:${GO_VERSION} AS builder
|
||||
|
||||
# Set up working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Step 1: Copy the source code
|
||||
COPY . .
|
||||
|
||||
# use --mount=type=cache,target=/go/pkg/mod to cache the go mod
|
||||
# Step 2: Download dependencies
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
go mod tidy && go mod download
|
||||
|
||||
# Step 3: Build the Go application with CGO enabled and specified ldflags
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
CGO_ENABLED=1 GOOS=linux go build -a \
|
||||
-ldflags "-s -w --extldflags '-static -fpic'" \
|
||||
-installsuffix cgo -o dashboard cmd/dashboard/main.go
|
||||
|
||||
|
||||
# Stage 2: Create the final image
|
||||
FROM alpine:latest
|
||||
|
||||
ARG COUNTRY
|
||||
# Install required tools without caching index to minimize image size
|
||||
RUN if [ "$COUNTRY" = "CN" ] ; then \
|
||||
echo "It is in China, updating the repositories"; \
|
||||
sed -i 's#https\?://dl-cdn.alpinelinux.org/alpine#https://mirrors.tuna.tsinghua.edu.cn/alpine#g' /etc/apk/repositories; \
|
||||
fi && \
|
||||
apk update && apk add --no-cache tzdata && \
|
||||
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
|
||||
echo 'Asia/Shanghai' >/etc/timezone && \
|
||||
rm -rf /var/cache/apk/* && \
|
||||
mkdir -p /dashboard/data
|
||||
|
||||
|
||||
# Copy the entrypoint script and ensure it is executable
|
||||
COPY ./script/entrypoint.sh /entrypoint.sh
|
||||
|
||||
# Set up the entrypoint script
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
WORKDIR /dashboard
|
||||
|
||||
# Copy the statically linked binary from the builder stage
|
||||
COPY --from=builder /app/dashboard ./app
|
||||
# Copy the configuration file and the resource directory
|
||||
COPY ./script/config.yaml ./data/config.yaml
|
||||
COPY ./resource ./resource
|
||||
|
||||
|
||||
# Set up volume and expose ports
|
||||
VOLUME ["/dashboard/data"]
|
||||
EXPOSE 80 5555 443
|
||||
|
||||
# Define the entrypoint
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
@ -28,6 +28,7 @@ func (v *apiV1) serve() {
|
||||
}))
|
||||
r.GET("/server/list", v.serverList)
|
||||
r.GET("/server/details", v.serverDetails)
|
||||
r.POST("/server/register", v.RegisterServer)
|
||||
// 不强制认证的 API
|
||||
mr := v.r.Group("monitor")
|
||||
mr.Use(mygin.Authorize(mygin.AuthorizeOption{
|
||||
@ -83,6 +84,45 @@ func (v *apiV1) serverDetails(c *gin.Context) {
|
||||
c.JSON(200, singleton.ServerAPI.GetAllStatus())
|
||||
}
|
||||
|
||||
// RegisterServer adds a server and responds with the full ServerRegisterResponse
|
||||
// header: Authorization: Token
|
||||
// body: RegisterServer
|
||||
// response: ServerRegisterResponse or Secret string
|
||||
func (v *apiV1) RegisterServer(c *gin.Context) {
|
||||
var rs singleton.RegisterServer
|
||||
// Attempt to bind JSON to RegisterServer struct
|
||||
if err := c.ShouldBindJSON(&rs); err != nil {
|
||||
c.JSON(400, singleton.ServerRegisterResponse{
|
||||
CommonResponse: singleton.CommonResponse{
|
||||
Code: 400,
|
||||
Message: "Parse JSON failed",
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
// Check if simple mode is requested
|
||||
simple := c.Query("simple") == "true" || c.Query("simple") == "1"
|
||||
// Set defaults if fields are empty
|
||||
if rs.Name == "" {
|
||||
rs.Name = c.ClientIP()
|
||||
}
|
||||
if rs.Tag == "" {
|
||||
rs.Tag = "AutoRegister"
|
||||
}
|
||||
if rs.HideForGuest == "" {
|
||||
rs.HideForGuest = "on"
|
||||
}
|
||||
// Call the Register function and get the response
|
||||
response := singleton.ServerAPI.Register(&rs)
|
||||
// Respond with Secret only if in simple mode, otherwise full response
|
||||
if simple {
|
||||
c.JSON(response.Code, response.Secret)
|
||||
} else {
|
||||
c.JSON(response.Code, response)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func (v *apiV1) monitorHistoriesById(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
|
17
docker-compose.yml
Normal file
17
docker-compose.yml
Normal file
@ -0,0 +1,17 @@
|
||||
services:
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.dev
|
||||
args:
|
||||
COUNTRY: CN
|
||||
image: nezha:dev
|
||||
container_name: nezha-dev
|
||||
ports:
|
||||
- ${NEZHA_PORT:-80}:18080
|
||||
- 5555:5555
|
||||
volumes:
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
- ./data:/dashboard/data
|
||||
# - ./resource:/dashboard/resource
|
@ -25,6 +25,19 @@ type CommonResponse struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type RegisterServer struct {
|
||||
Name string
|
||||
Tag string
|
||||
Note string
|
||||
HideForGuest string
|
||||
}
|
||||
|
||||
type ServerRegisterResponse struct {
|
||||
CommonResponse
|
||||
Secret string `json:"secret"`
|
||||
}
|
||||
|
||||
|
||||
type CommonServerInfo struct {
|
||||
ID uint64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
@ -227,6 +240,55 @@ func (s *ServerAPIService) GetAllList() *ServerInfoResponse {
|
||||
}
|
||||
return res
|
||||
}
|
||||
func (s *ServerAPIService) Register(rs *RegisterServer) *ServerRegisterResponse {
|
||||
var serverInfo model.Server
|
||||
var err error
|
||||
// Populate serverInfo fields
|
||||
serverInfo.Name = rs.Name
|
||||
serverInfo.Tag = rs.Tag
|
||||
serverInfo.Note = rs.Note
|
||||
serverInfo.HideForGuest = rs.HideForGuest == "on"
|
||||
// Generate a random secret
|
||||
serverInfo.Secret, err = utils.GenerateRandomString(18)
|
||||
if err != nil {
|
||||
return &ServerRegisterResponse{
|
||||
CommonResponse: CommonResponse{
|
||||
Code: 500,
|
||||
Message: "Generate secret failed: " + err.Error(),
|
||||
},
|
||||
Secret: "",
|
||||
}
|
||||
}
|
||||
// Attempt to save serverInfo in the database
|
||||
err = DB.Create(&serverInfo).Error
|
||||
if err != nil {
|
||||
return &ServerRegisterResponse{
|
||||
CommonResponse: CommonResponse{
|
||||
Code: 500,
|
||||
Message: "Database error: " + err.Error(),
|
||||
},
|
||||
Secret: "",
|
||||
}
|
||||
}
|
||||
|
||||
serverInfo.Host = &model.Host{}
|
||||
serverInfo.State = &model.HostState{}
|
||||
serverInfo.TaskCloseLock = new(sync.Mutex)
|
||||
ServerLock.Lock()
|
||||
SecretToID[serverInfo.Secret] = serverInfo.ID
|
||||
ServerList[serverInfo.ID] = &serverInfo
|
||||
ServerTagToIDList[serverInfo.Tag] = append(ServerTagToIDList[serverInfo.Tag], serverInfo.ID)
|
||||
ServerLock.Unlock()
|
||||
ReSortServer()
|
||||
// Successful response
|
||||
return &ServerRegisterResponse{
|
||||
CommonResponse: CommonResponse{
|
||||
Code: 200,
|
||||
Message: "Server created successfully",
|
||||
},
|
||||
Secret: serverInfo.Secret,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MonitorAPIService) GetMonitorHistories(query map[string]any) *MonitorInfoResponse {
|
||||
var (
|
||||
|
Loading…
Reference in New Issue
Block a user