Skip to content

Commit

Permalink
Add API to register server (#472)
Browse files Browse the repository at this point in the history
  • Loading branch information
IamTaoChen authored Nov 6, 2024
1 parent 3d6edd6 commit f4b7483
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.git
.gitignore
docker-compose.yml
Dockerfile
Dockerfile.dev


data/*
63 changes: 63 additions & 0 deletions Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -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"]
40 changes: 40 additions & 0 deletions cmd/dashboard/controller/api_v1.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down Expand Up @@ -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)
Expand Down
17 changes: 17 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -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
62 changes: 62 additions & 0 deletions service/singleton/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down Expand Up @@ -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 (
Expand Down

0 comments on commit f4b7483

Please sign in to comment.