package main import ( "fmt" "github.com/aarnaud/ipxeblue/controllers" _ "github.com/aarnaud/ipxeblue/docs" "github.com/aarnaud/ipxeblue/midlewares" "github.com/aarnaud/ipxeblue/utils" "github.com/gin-contrib/cors" "github.com/gin-contrib/logger" "github.com/gin-gonic/gin" "github.com/pin/tftp/v3" "github.com/rs/zerolog" "github.com/rs/zerolog/log" swaggerFiles "github.com/swaggo/files" ginSwagger "github.com/swaggo/gin-swagger" "net/http" "os" "time" ) // @title ipxeblue API // @version 0.1 // @description Manage PXE boot // @termsOfService http://swagger.io/terms/ // @license.name Apache 2.0 // @license.url http://www.apache.org/licenses/LICENSE-2.0.html // @host localhost:8080 // @BasePath /api/v1 func main() { zerolog.SetGlobalLevel(zerolog.InfoLevel) appconf := utils.GetConfig() router := gin.New() router.Use(logger.SetLogger(logger.Config{ SkipPath: []string{"/healthz"}, })) router.Use(gin.Recovery()) // CORS for https://foo.com and https://github.com origins, allowing: // - PUT and PATCH methods // - Origin header // - Credentials share // - Preflight requests cached for 12 hours router.Use(cors.New(cors.Config{ AllowOrigins: []string{"http://localhost:3000", "http://localhost:8080"}, AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE"}, AllowHeaders: []string{"Origin", "Content-Type"}, ExposeHeaders: []string{"x-total-count", "content-length"}, AllowCredentials: true, MaxAge: 12 * time.Hour, })) router.LoadHTMLGlob("templates/*") db := utils.Database() filestore := utils.NewFileStore(appconf) log.Info().Msg("starting TokenCleaner in goroutine") go utils.TokenCleaner(db) // Provide db variable to controllers router.Use(func(c *gin.Context) { c.Set("db", db) c.Set("filestore", filestore) c.Set("config", appconf) c.Header("Cache-Control", "no-store, max-age=0") c.Next() }) if gin.Mode() == gin.DebugMode { zerolog.SetGlobalLevel(zerolog.DebugLevel) // Configure SwaggerUI url := ginSwagger.URL("http://localhost:8080/swagger/doc.json") // The url pointing to API definition router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, url)) // proxies UI call to nodejs react server router.Use(midlewares.MidlewareDevWebUI()) } else { // Serve react-admin webui router.Static("/admin", "./admin") } router.GET("/healthz", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{}) }) if appconf.GrubSupportEnabled { // Grub request without auth log.Info().Msg("enabling grub http endpoint") router.GET("/grub/", controllers.GrubScript) } // grub don't support long http queries // using TFTP to pass positional metadata in tftp path if appconf.TFTPEnabled { s := tftp.NewServer(controllers.GetTFTPReader(appconf, db), controllers.GetTFTPWriter(appconf)) s.SetTimeout(30 * time.Second) go func() { log.Info().Msg("starting tftp server") err := s.ListenAndServe(":69") if err != nil { fmt.Fprintf(os.Stdout, "server: %v\n", err) } }() } // iPXE request with auth ipxeroute := router.Group("/", midlewares.BasicAuthIpxeAccount(false)) ipxeroute.GET("/", controllers.IpxeScript) router.HEAD("/files/public/:uuid/*filepath", controllers.DownloadPublicFile) router.GET("/files/public/:uuid/*filepath", controllers.DownloadPublicFile) router.HEAD("/files/token/:token/:uuid/*filepath", controllers.DownloadProtectedFile) router.GET("/files/token/:token/:uuid/*filepath", controllers.DownloadProtectedFile) var v1 *gin.RouterGroup if appconf.EnableAPIAuth { // API v1 = router.Group("/api/v1", midlewares.BasicAuthIpxeAccount(true)) } else { // API v1 = router.Group("/api/v1") } // Computer v1.GET("/computers", controllers.ListComputers) v1.GET("/computers/:id", controllers.GetComputer) v1.PUT("/computers/:id", controllers.UpdateComputer) v1.DELETE("/computers/:id", controllers.DeleteComputer) // IPXE account v1.GET("/ipxeaccounts", controllers.ListIpxeaccount) v1.GET("/ipxeaccounts/:username", controllers.GetIpxeaccount) v1.POST("/ipxeaccounts", controllers.CreateIpxeaccount) v1.PUT("/ipxeaccounts/:username", controllers.UpdateIpxeaccount) v1.DELETE("/ipxeaccounts/:username", controllers.DeleteIpxeaccount) // Bootentry v1.GET("/bootentries", controllers.ListBootentries) v1.GET("/bootentries/:uuid", controllers.GetBootentry) v1.POST("/bootentries", controllers.CreateBootentry) v1.PUT("/bootentries/:uuid", controllers.UpdateBootentry) v1.DELETE("/bootentries/:uuid", controllers.DeleteBootentry) v1.POST("/bootentries/:uuid/files/:name", controllers.UploadBootentryFile) v1.GET("/bootentries/:uuid/files/:name", controllers.DownloadBootentryFile) log.Info().Msg("starting http server") router.Run(fmt.Sprintf(":%d", appconf.Port)) }