147 lines
4.0 KiB
Go
147 lines
4.0 KiB
Go
package httpapi
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net"
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"git.pcux.cn/xuans/Scert/backend/internal/buildinfo"
|
|
"git.pcux.cn/xuans/Scert/backend/internal/config"
|
|
"git.pcux.cn/xuans/Scert/backend/internal/domain"
|
|
"git.pcux.cn/xuans/Scert/backend/internal/logging"
|
|
"git.pcux.cn/xuans/Scert/backend/internal/repository"
|
|
)
|
|
|
|
type Dependencies struct {
|
|
Config config.Config
|
|
Logger *logging.Logger
|
|
Repository repository.DomainRepository
|
|
}
|
|
|
|
type Server struct {
|
|
config config.Config
|
|
logger *logging.Logger
|
|
repository repository.DomainRepository
|
|
httpServer *http.Server
|
|
router *gin.Engine
|
|
}
|
|
|
|
func New(deps Dependencies) *Server {
|
|
gin.SetMode(gin.ReleaseMode)
|
|
router := gin.New()
|
|
router.Use(gin.Recovery())
|
|
server := &Server{config: deps.Config, logger: deps.Logger, repository: deps.Repository, router: router}
|
|
server.registerRoutes()
|
|
return server
|
|
}
|
|
|
|
func (s *Server) registerRoutes() {
|
|
s.router.GET("/healthz", s.health)
|
|
s.router.GET("/api/v1/status", s.status)
|
|
s.router.GET("/api/v1/domains", s.listDomains)
|
|
s.router.POST("/api/v1/domains", s.createDomain)
|
|
s.router.DELETE("/api/v1/domains/:id", s.deleteDomain)
|
|
}
|
|
|
|
func (s *Server) Handler() http.Handler {
|
|
return s.router
|
|
}
|
|
|
|
func (s *Server) ListenAndServe() error {
|
|
s.httpServer = &http.Server{
|
|
Addr: net.JoinHostPort(s.config.Server.Host, strconv.Itoa(s.config.Server.Port)),
|
|
Handler: s.router,
|
|
ReadHeaderTimeout: 10 * time.Second,
|
|
ReadTimeout: 30 * time.Second,
|
|
WriteTimeout: 30 * time.Second,
|
|
IdleTimeout: 60 * time.Second,
|
|
}
|
|
if s.logger != nil {
|
|
s.logger.Info("HTTP 服务启动", "address", s.httpServer.Addr, "https_mode", s.config.Server.HTTPSMode)
|
|
}
|
|
return s.httpServer.ListenAndServe()
|
|
}
|
|
|
|
func (s *Server) Shutdown(ctx context.Context) error {
|
|
if s.httpServer == nil {
|
|
return nil
|
|
}
|
|
err := s.httpServer.Shutdown(ctx)
|
|
if errors.Is(err, http.ErrServerClosed) {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
type createDomainRequest struct {
|
|
Name string `json:"name" binding:"required"`
|
|
CertificateType string `json:"certificate_type" binding:"required"`
|
|
SANs []string `json:"sans"`
|
|
}
|
|
|
|
func (s *Server) health(c *gin.Context) {
|
|
c.JSON(http.StatusOK, gin.H{"status": "ok", "service": "scert"})
|
|
}
|
|
|
|
func (s *Server) status(c *gin.Context) {
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"service": "scert",
|
|
"version": buildinfo.Version,
|
|
"config_version": s.config.Version,
|
|
"setup_ready": s.config.SetupReady,
|
|
"https_mode": s.config.Server.HTTPSMode,
|
|
"database_driver": s.config.Database.Driver,
|
|
})
|
|
}
|
|
|
|
func (s *Server) listDomains(c *gin.Context) {
|
|
items, err := s.repository.List(c.Request.Context())
|
|
if err != nil {
|
|
s.writeError(c, http.StatusInternalServerError, "查询域名失败", err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"items": items})
|
|
}
|
|
|
|
func (s *Server) createDomain(c *gin.Context) {
|
|
var request createDomainRequest
|
|
if err := c.ShouldBindJSON(&request); err != nil {
|
|
s.writeError(c, http.StatusBadRequest, "请求参数无效", err)
|
|
return
|
|
}
|
|
item, err := domain.NewDomain(request.Name, domain.CertificateType(request.CertificateType), request.SANs)
|
|
if err != nil {
|
|
s.writeError(c, http.StatusBadRequest, "域名配置无效", err)
|
|
return
|
|
}
|
|
if err := s.repository.Create(c.Request.Context(), item); err != nil {
|
|
s.writeError(c, http.StatusInternalServerError, "保存域名失败", err)
|
|
return
|
|
}
|
|
c.JSON(http.StatusCreated, item)
|
|
}
|
|
|
|
func (s *Server) deleteDomain(c *gin.Context) {
|
|
if err := s.repository.Delete(c.Request.Context(), c.Param("id")); err != nil {
|
|
if repository.IsNotFound(err) {
|
|
s.writeError(c, http.StatusNotFound, "域名不存在", err)
|
|
return
|
|
}
|
|
s.writeError(c, http.StatusInternalServerError, "删除域名失败", err)
|
|
return
|
|
}
|
|
c.Status(http.StatusNoContent)
|
|
}
|
|
|
|
func (s *Server) writeError(c *gin.Context, status int, message string, err error) {
|
|
if s.logger != nil {
|
|
s.logger.Error(message, "error", err)
|
|
}
|
|
c.JSON(status, gin.H{"error": message})
|
|
}
|