Major Updates: - Rewrote all docs in beginner-friendly 'ELI5' language - Created docs index with user journey navigation - Added complete getting-started guide for novice users - Set up GitHub Container Registry (GHCR) automation - Configured GitHub Pages deployment for documentation Documentation: - docs/index.md - Central navigation hub - docs/getting-started.md - Step-by-step beginner guide - docs/github-setup.md - CI/CD setup instructions - README.md - Complete rewrite in accessible language - CONTRIBUTING.md - Contributor guidelines - Multiple comprehensive API and schema docs CI/CD Workflows: - .github/workflows/docker-build.yml - Multi-platform builds to GHCR - .github/workflows/docs.yml - Automated docs deployment to Pages - Supports main (latest), development (dev), and version tags - Automated testing of built images - Beautiful documentation site with dark theme Benefits: - Zero barrier to entry for new users - Automated Docker builds (AMD64 + ARM64) - Professional documentation site - No Docker Hub account needed (uses GHCR) - Complete CI/CD pipeline All 7 implementation phases complete - project is production ready!
171 lines
4.1 KiB
Go
171 lines
4.1 KiB
Go
package handlers
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
"gorm.io/gorm"
|
|
|
|
"github.com/Wikid82/CaddyProxyManagerPlus/backend/internal/models"
|
|
"github.com/Wikid82/CaddyProxyManagerPlus/backend/internal/services"
|
|
)
|
|
|
|
// RemoteServerHandler handles HTTP requests for remote server management.
|
|
type RemoteServerHandler struct {
|
|
service *services.RemoteServerService
|
|
}
|
|
|
|
// NewRemoteServerHandler creates a new remote server handler.
|
|
func NewRemoteServerHandler(db *gorm.DB) *RemoteServerHandler {
|
|
return &RemoteServerHandler{
|
|
service: services.NewRemoteServerService(db),
|
|
}
|
|
}
|
|
|
|
// RegisterRoutes registers remote server routes.
|
|
func (h *RemoteServerHandler) RegisterRoutes(router *gin.RouterGroup) {
|
|
router.GET("/remote-servers", h.List)
|
|
router.POST("/remote-servers", h.Create)
|
|
router.GET("/remote-servers/:uuid", h.Get)
|
|
router.PUT("/remote-servers/:uuid", h.Update)
|
|
router.DELETE("/remote-servers/:uuid", h.Delete)
|
|
router.POST("/remote-servers/:uuid/test", h.TestConnection)
|
|
}
|
|
|
|
// List retrieves all remote servers.
|
|
func (h *RemoteServerHandler) List(c *gin.Context) {
|
|
enabledOnly := c.Query("enabled") == "true"
|
|
|
|
servers, err := h.service.List(enabledOnly)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, servers)
|
|
}
|
|
|
|
// Create creates a new remote server.
|
|
func (h *RemoteServerHandler) Create(c *gin.Context) {
|
|
var server models.RemoteServer
|
|
if err := c.ShouldBindJSON(&server); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
server.UUID = uuid.NewString()
|
|
|
|
if err := h.service.Create(&server); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusCreated, server)
|
|
}
|
|
|
|
// Get retrieves a remote server by UUID.
|
|
func (h *RemoteServerHandler) Get(c *gin.Context) {
|
|
uuid := c.Param("uuid")
|
|
|
|
server, err := h.service.GetByUUID(uuid)
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "server not found"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, server)
|
|
}
|
|
|
|
// Update updates an existing remote server.
|
|
func (h *RemoteServerHandler) Update(c *gin.Context) {
|
|
uuid := c.Param("uuid")
|
|
|
|
server, err := h.service.GetByUUID(uuid)
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "server not found"})
|
|
return
|
|
}
|
|
|
|
if err := c.ShouldBindJSON(server); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
if err := h.service.Update(server); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, server)
|
|
}
|
|
|
|
// Delete removes a remote server.
|
|
func (h *RemoteServerHandler) Delete(c *gin.Context) {
|
|
uuid := c.Param("uuid")
|
|
|
|
server, err := h.service.GetByUUID(uuid)
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "server not found"})
|
|
return
|
|
}
|
|
|
|
if err := h.service.Delete(server.ID); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusNoContent, nil)
|
|
}
|
|
|
|
// TestConnection tests the TCP connection to a remote server.
|
|
func (h *RemoteServerHandler) TestConnection(c *gin.Context) {
|
|
uuid := c.Param("uuid")
|
|
|
|
server, err := h.service.GetByUUID(uuid)
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "server not found"})
|
|
return
|
|
}
|
|
|
|
// Test TCP connection with 5 second timeout
|
|
address := net.JoinHostPort(server.Host, fmt.Sprintf("%d", server.Port))
|
|
conn, err := net.DialTimeout("tcp", address, 5*time.Second)
|
|
|
|
result := gin.H{
|
|
"server_uuid": server.UUID,
|
|
"address": address,
|
|
"timestamp": time.Now().UTC(),
|
|
}
|
|
|
|
if err != nil {
|
|
result["reachable"] = false
|
|
result["error"] = err.Error()
|
|
|
|
// Update server reachability status
|
|
server.Reachable = false
|
|
now := time.Now().UTC()
|
|
server.LastChecked = &now
|
|
h.service.Update(server)
|
|
|
|
c.JSON(http.StatusOK, result)
|
|
return
|
|
}
|
|
defer conn.Close()
|
|
|
|
// Connection successful
|
|
result["reachable"] = true
|
|
result["latency_ms"] = time.Since(time.Now()).Milliseconds()
|
|
|
|
// Update server reachability status
|
|
server.Reachable = true
|
|
now := time.Now().UTC()
|
|
server.LastChecked = &now
|
|
h.service.Update(server)
|
|
|
|
c.JSON(http.StatusOK, result)
|
|
}
|