2026-04-07 12:30:27 +08:00

417 lines
12 KiB
Go

package transport
import (
"net/http"
"strconv"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
"app-deploy-platform/backend/internal/config"
"app-deploy-platform/backend/internal/model"
"app-deploy-platform/backend/internal/service"
)
type routerHandler struct {
manager *service.Manager
}
type hostPayload struct {
ID uint `json:"id"`
Name string `json:"name"`
InstanceID string `json:"instance_id"`
PrivateIP string `json:"private_ip"`
Role string `json:"role"`
Environment string `json:"environment"`
Description string `json:"description"`
TATOnline bool `json:"tat_online"`
}
type instancePayload struct {
ID uint `json:"id"`
ServiceName string `json:"service_name"`
HostID uint `json:"host_id"`
Port int `json:"port"`
UnitName string `json:"unit_name"`
HealthPath string `json:"health_path"`
ReadyPath string `json:"ready_path"`
CurrentReleaseID string `json:"current_release_id"`
}
type releasePayload struct {
ID uint `json:"id"`
ServiceName string `json:"service_name"`
ReleaseID string `json:"release_id"`
GitSHA string `json:"git_sha"`
COSKey string `json:"cos_key"`
SHA256 string `json:"sha256"`
ArtifactURL string `json:"artifact_url"`
BuildHost string `json:"build_host"`
}
type deploymentPayload struct {
ServiceName string `json:"service_name"`
ReleaseID string `json:"release_id"`
Operation string `json:"operation"`
Operator string `json:"operator"`
HostIDs []uint `json:"host_ids"`
}
type buildPayload struct {
ServiceName string `json:"service_name"`
ReleaseID string `json:"release_id"`
Branch string `json:"branch"`
Operator string `json:"operator"`
}
type releaseRunPayload struct {
ReleaseID string `json:"release_id"`
Operator string `json:"operator"`
}
func NewRouter(cfg config.Config, manager *service.Manager) *gin.Engine {
gin.SetMode(gin.ReleaseMode)
router := gin.New()
router.Use(gin.Logger(), gin.Recovery())
corsConfig := cors.Config{
AllowOrigins: []string{cfg.Platform.CORSOrigin},
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowHeaders: []string{"Content-Type", "Authorization"},
AllowCredentials: cfg.Platform.CORSOrigin != "*",
}
if cfg.Platform.CORSOrigin == "*" {
corsConfig.AllowAllOrigins = true
}
router.Use(cors.New(corsConfig))
handler := &routerHandler{manager: manager}
router.GET("/api/healthz", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"status": "ok"})
})
api := router.Group("/api")
{
api.GET("/overview", handler.overview)
api.GET("/hosts", handler.listHosts)
api.POST("/hosts", handler.saveHost)
api.PUT("/hosts/:id", handler.saveHost)
api.DELETE("/hosts/:id", handler.deleteHost)
api.GET("/instances", handler.listInstances)
api.POST("/instances", handler.saveInstance)
api.PUT("/instances/:id", handler.saveInstance)
api.DELETE("/instances/:id", handler.deleteInstance)
api.POST("/instances/:id/restart", handler.restartInstance)
api.GET("/services", handler.listServices)
api.GET("/releases", handler.listReleases)
api.POST("/releases", handler.saveRelease)
api.GET("/builds", handler.listBuilds)
api.GET("/builds/:id", handler.getBuild)
api.POST("/builds", handler.createBuild)
api.GET("/release-runs", handler.listReleaseRuns)
api.GET("/release-runs/:id", handler.getReleaseRun)
api.POST("/release-runs", handler.createReleaseRun)
api.GET("/deployments", handler.listDeployments)
api.GET("/deployments/:id", handler.getDeployment)
api.POST("/deployments", handler.createDeployment)
api.POST("/deployments/:id/rollback", handler.rollbackDeployment)
}
return router
}
func (h *routerHandler) overview(c *gin.Context) {
data, err := h.manager.GetOverview(c.Request.Context())
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, data)
}
func (h *routerHandler) listHosts(c *gin.Context) {
data, err := h.manager.ListHosts(c.Request.Context())
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, data)
}
func (h *routerHandler) saveHost(c *gin.Context) {
var payload hostPayload
if err := c.ShouldBindJSON(&payload); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
id := payload.ID
if id == 0 {
id = uintFromParam(c.Param("id"))
}
host := &model.Host{
Model: model.Host{}.Model,
Name: payload.Name,
InstanceID: payload.InstanceID,
PrivateIP: payload.PrivateIP,
Role: payload.Role,
Environment: payload.Environment,
Description: payload.Description,
TATOnline: payload.TATOnline,
}
host.ID = id
if err := h.manager.SaveHost(c.Request.Context(), host); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, host)
}
func (h *routerHandler) deleteHost(c *gin.Context) {
if err := h.manager.DeleteHost(c.Request.Context(), uintFromParam(c.Param("id"))); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.Status(http.StatusNoContent)
}
func (h *routerHandler) listInstances(c *gin.Context) {
data, err := h.manager.ListInstances(c.Request.Context())
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, data)
}
func (h *routerHandler) saveInstance(c *gin.Context) {
var payload instancePayload
if err := c.ShouldBindJSON(&payload); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
id := payload.ID
if id == 0 {
id = uintFromParam(c.Param("id"))
}
instance := &model.ServiceInstance{
ServiceName: payload.ServiceName,
HostID: payload.HostID,
Port: payload.Port,
UnitName: payload.UnitName,
HealthPath: payload.HealthPath,
ReadyPath: payload.ReadyPath,
CurrentReleaseID: payload.CurrentReleaseID,
}
instance.ID = id
if err := h.manager.SaveInstance(c.Request.Context(), instance); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, instance)
}
func (h *routerHandler) deleteInstance(c *gin.Context) {
if err := h.manager.DeleteInstance(c.Request.Context(), uintFromParam(c.Param("id"))); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.Status(http.StatusNoContent)
}
func (h *routerHandler) restartInstance(c *gin.Context) {
operator := c.Query("operator")
data, err := h.manager.CreateRestartForInstance(c.Request.Context(), uintFromParam(c.Param("id")), operator)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusAccepted, data)
}
func (h *routerHandler) listServices(c *gin.Context) {
c.JSON(http.StatusOK, h.manager.ListServices(c.Request.Context()))
}
func (h *routerHandler) listReleases(c *gin.Context) {
data, err := h.manager.ListReleases(c.Request.Context(), c.Query("service_name"))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, data)
}
func (h *routerHandler) saveRelease(c *gin.Context) {
var payload releasePayload
if err := c.ShouldBindJSON(&payload); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
release := &model.Release{
ServiceName: payload.ServiceName,
ReleaseID: payload.ReleaseID,
GitSHA: payload.GitSHA,
COSKey: payload.COSKey,
SHA256: payload.SHA256,
ArtifactURL: payload.ArtifactURL,
BuildHost: payload.BuildHost,
}
release.ID = payload.ID
if err := h.manager.SaveRelease(c.Request.Context(), release); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, release)
}
func (h *routerHandler) listBuilds(c *gin.Context) {
data, err := h.manager.ListBuilds(c.Request.Context())
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, data)
}
func (h *routerHandler) getBuild(c *gin.Context) {
data, err := h.manager.GetBuild(c.Request.Context(), uintFromParam(c.Param("id")))
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, data)
}
func (h *routerHandler) createBuild(c *gin.Context) {
var payload buildPayload
if err := c.ShouldBindJSON(&payload); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
data, err := h.manager.CreateBuild(c.Request.Context(), service.CreateBuildRequest{
ServiceName: payload.ServiceName,
ReleaseID: payload.ReleaseID,
Branch: payload.Branch,
Operator: payload.Operator,
})
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusAccepted, data)
}
func (h *routerHandler) listReleaseRuns(c *gin.Context) {
data, err := h.manager.ListReleaseRuns(c.Request.Context())
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, data)
}
func (h *routerHandler) getReleaseRun(c *gin.Context) {
data, err := h.manager.GetReleaseRun(c.Request.Context(), uintFromParam(c.Param("id")))
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, data)
}
func (h *routerHandler) createReleaseRun(c *gin.Context) {
var payload releaseRunPayload
if err := c.ShouldBindJSON(&payload); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
data, err := h.manager.CreateReleaseRun(c.Request.Context(), service.CreateReleaseRunRequest{
ReleaseID: payload.ReleaseID,
Operator: payload.Operator,
})
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusAccepted, data)
}
func (h *routerHandler) listDeployments(c *gin.Context) {
data, err := h.manager.ListDeployments(c.Request.Context())
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, data)
}
func (h *routerHandler) getDeployment(c *gin.Context) {
data, err := h.manager.GetDeployment(c.Request.Context(), uintFromParam(c.Param("id")))
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, data)
}
func (h *routerHandler) createDeployment(c *gin.Context) {
var payload deploymentPayload
if err := c.ShouldBindJSON(&payload); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
data, err := h.manager.CreateDeployment(c.Request.Context(), service.CreateDeploymentRequest{
ServiceName: payload.ServiceName,
ReleaseID: payload.ReleaseID,
Operation: payload.Operation,
Operator: payload.Operator,
HostIDs: payload.HostIDs,
})
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusAccepted, data)
}
func (h *routerHandler) rollbackDeployment(c *gin.Context) {
var payload struct {
ReleaseID string `json:"release_id"`
Operator string `json:"operator"`
}
if err := c.ShouldBindJSON(&payload); err != nil && err.Error() != "EOF" {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
data, err := h.manager.CreateRollback(c.Request.Context(), uintFromParam(c.Param("id")), payload.ReleaseID, payload.Operator)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusAccepted, data)
}
func uintFromParam(raw string) uint {
value, _ := strconv.ParseUint(raw, 10, 64)
return uint(value)
}