🎨 Refactor basic server structure
These changes refactor the router handling into its own package to keep main.go clean. Also API handlers are here refactored to their corresponding files.
This commit is contained in:
		
							
								
								
									
										37
									
								
								api/auth_handlers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								api/auth_handlers.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
// Copyright 2024 Florian Beisel
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
package api
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Helloworld godoc
 | 
			
		||||
//
 | 
			
		||||
//	@Summary	Check your authentication
 | 
			
		||||
//	@Schemes
 | 
			
		||||
//	@Description	Checks whether the user is successfully authenticated
 | 
			
		||||
//	@ID				hello
 | 
			
		||||
//	@Tags			Authentication
 | 
			
		||||
//	@Accept			json
 | 
			
		||||
//	@Produce		json
 | 
			
		||||
//	@Security		Bearer
 | 
			
		||||
//	@Success		200	{string}	Helloworld
 | 
			
		||||
//	@Router			/hello [get]
 | 
			
		||||
func Helloworld(g *gin.Context) {
 | 
			
		||||
	g.JSON(http.StatusOK, "helloworld")
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										161
									
								
								api/handlers.go
									
									
									
									
									
								
							
							
						
						
									
										161
									
								
								api/handlers.go
									
									
									
									
									
								
							@@ -1,161 +0,0 @@
 | 
			
		||||
package api
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"git.beisel.it/florian/hostname-service/db"
 | 
			
		||||
	"git.beisel.it/florian/hostname-service/models"
 | 
			
		||||
	"git.beisel.it/florian/hostname-service/rules"
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func getHostnameRuleByCategory(category string) (rules.HostnameRule, error) {
 | 
			
		||||
	switch category {
 | 
			
		||||
	case "notebook":
 | 
			
		||||
		return &rules.NotebookRule{}, nil
 | 
			
		||||
	// ... other categories
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, errors.New("unknown category")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateOrUpdateHostname handles the hostname generation
 | 
			
		||||
func CreateOrUpdateHostname(c *gin.Context, isUpdate bool) {
 | 
			
		||||
	category := c.Param("category")
 | 
			
		||||
	var oldHostname string
 | 
			
		||||
	if isUpdate {
 | 
			
		||||
		oldHostname = c.Param("oldhostname")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var params map[string]interface{}
 | 
			
		||||
	if err := c.BindJSON(¶ms); err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid parameters"})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rule, err := getHostnameRuleByCategory(category)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hostname, paramsJSON, err := rule.Generate(params)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if isUpdate {
 | 
			
		||||
		err = rule.Update(category, oldHostname, hostname, paramsJSON)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if strings.Contains(err.Error(), "does not exist") {
 | 
			
		||||
				c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
 | 
			
		||||
				return
 | 
			
		||||
			} else {
 | 
			
		||||
				c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		err = rule.Insert(category, hostname, paramsJSON)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Error processing hostname"})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	response := models.SimpleHostnameResponse{Hostname: hostname}
 | 
			
		||||
	c.JSON(http.StatusOK, response)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//	@Summary		Delete a hostname from the database
 | 
			
		||||
//	@Description	List all details for a given category
 | 
			
		||||
//	@ID				delete-hostnames-by-category-and-name
 | 
			
		||||
//	@Produce		json
 | 
			
		||||
//	@Param			category	path		string							true	"Category of the hostname"
 | 
			
		||||
//	@Param			hostname	path		string							true	"Hostname to delete"
 | 
			
		||||
//	@Success		200			{object}	models.SimpleHostnameResponse	"Hostname"
 | 
			
		||||
//	@Security		Bearer
 | 
			
		||||
//	@Tags			Manipulate existing Hostnames
 | 
			
		||||
//	@Router			/{category}/{hostname} [delete]
 | 
			
		||||
func DeleteHostname(c *gin.Context) {
 | 
			
		||||
	category := c.Param("category")
 | 
			
		||||
	hostname := c.Param("hostname")
 | 
			
		||||
 | 
			
		||||
	err := db.DeleteHostname(category, hostname)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	response := models.SimpleHostnameResponse{Hostname: hostname}
 | 
			
		||||
	c.JSON(http.StatusOK, response)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//	@Summary		Return a list of hosts and their details filtered by category
 | 
			
		||||
//	@Description	List all details for a given category
 | 
			
		||||
//	@ID				list-hostnames-by-category
 | 
			
		||||
//	@Produce		json
 | 
			
		||||
//	@Param			category	path	string			true	"Category of the hostname"
 | 
			
		||||
//	@Success		200			{array}	models.Hostname	"An array of responses"
 | 
			
		||||
//	@Security		Bearer
 | 
			
		||||
//	@Tags			Querying Hostnames
 | 
			
		||||
//	@Router			/{category} [get]
 | 
			
		||||
func ListHostnamesByCategory(c *gin.Context) {
 | 
			
		||||
	category := c.Param("category")
 | 
			
		||||
 | 
			
		||||
	hostnames, err := db.GetHostnamesByCategory(category)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if strings.Contains(err.Error(), "no rows found") {
 | 
			
		||||
			c.JSON(http.StatusNotFound, gin.H{"error": "hostname not found"})
 | 
			
		||||
		}
 | 
			
		||||
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Error retrieving hostnames"})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.JSON(http.StatusOK, hostnames)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//	@Summary		Return a single hostname by Category and Name
 | 
			
		||||
//	@Description	Return details for a single hostname identified by its category
 | 
			
		||||
//	@ID				get-hostname-by-category-and-name
 | 
			
		||||
//	@Produce		json
 | 
			
		||||
//	@Param			category	path		string			true	"Category of the hostname"
 | 
			
		||||
//	@Param			hostname	path		string			true	"Category of the hostname"
 | 
			
		||||
//	@Success		200			{object}	models.Hostname	"A single response object"
 | 
			
		||||
//	@Security		Bearer
 | 
			
		||||
//	@Tags			Querying Hostnames
 | 
			
		||||
//	@Router			/{category}/{hostname} [get]
 | 
			
		||||
func GetHostnameByCategoryAndName(c *gin.Context) {
 | 
			
		||||
	category := c.Param("category")
 | 
			
		||||
	hostname := c.Param("hostname")
 | 
			
		||||
 | 
			
		||||
	hostinfo, err := db.GetHostnameByCategoryAndName(category, hostname)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if strings.Contains(err.Error(), "no rows found") {
 | 
			
		||||
			c.JSON(http.StatusNotFound, gin.H{"error": "hostname not found"})
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Error retrieving hostname"})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.JSON(http.StatusOK, hostinfo)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Helloworld godoc
 | 
			
		||||
//
 | 
			
		||||
//	@Summary	Check your authentication
 | 
			
		||||
//	@Schemes
 | 
			
		||||
//	@Description	Checks whether the user is successfully authenticated
 | 
			
		||||
//	@ID				hello
 | 
			
		||||
//	@Tags			Authentication
 | 
			
		||||
//	@Accept			json
 | 
			
		||||
//	@Produce		json
 | 
			
		||||
//	@Security		Bearer
 | 
			
		||||
//	@Success		200	{string}	Helloworld
 | 
			
		||||
//	@Router			/hello [get]
 | 
			
		||||
func Helloworld(g *gin.Context) {
 | 
			
		||||
	g.JSON(http.StatusOK, "helloworld")
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										137
									
								
								api/hostname_handlers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								api/hostname_handlers.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,137 @@
 | 
			
		||||
// Copyright 2024 Florian Beisel
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
package api
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"git.beisel.it/florian/hostname-service/db"
 | 
			
		||||
	"git.beisel.it/florian/hostname-service/models"
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// @Summary		Return a single hostname by Category and Name
 | 
			
		||||
// @Description	Return details for a single hostname identified by its category
 | 
			
		||||
// @ID				get-hostname-by-category-and-name
 | 
			
		||||
// @Produce		json
 | 
			
		||||
// @Param			category	path		string			true	"Category of the hostname"
 | 
			
		||||
// @Param			hostname	path		string			true	"Category of the hostname"
 | 
			
		||||
// @Success		200			{object}	models.Hostname	"A single response object"
 | 
			
		||||
// @Security		Bearer
 | 
			
		||||
// @Tags			Querying Hostnames
 | 
			
		||||
// @Router			/{category}/{hostname} [get]
 | 
			
		||||
func GetHostnameByCategoryAndName(c *gin.Context) {
 | 
			
		||||
	category := c.Param("category")
 | 
			
		||||
	hostname := c.Param("hostname")
 | 
			
		||||
 | 
			
		||||
	hostinfo, err := db.GetHostnameByCategoryAndName(category, hostname)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if strings.Contains(err.Error(), "no rows found") {
 | 
			
		||||
			c.JSON(http.StatusNotFound, gin.H{"error": "hostname not found"})
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Error retrieving hostname"})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.JSON(http.StatusOK, hostinfo)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// @Summary		Return a list of hosts and their details filtered by category
 | 
			
		||||
// @Description	List all details for a given category
 | 
			
		||||
// @ID				list-hostnames-by-category
 | 
			
		||||
// @Produce		json
 | 
			
		||||
// @Param			category	path	string			true	"Category of the hostname"
 | 
			
		||||
// @Success		200			{array}	models.Hostname	"An array of responses"
 | 
			
		||||
// @Security		Bearer
 | 
			
		||||
// @Tags			Querying Hostnames
 | 
			
		||||
// @Router			/{category} [get]
 | 
			
		||||
func ListHostnamesByCategory(c *gin.Context) {
 | 
			
		||||
	category := c.Param("category")
 | 
			
		||||
 | 
			
		||||
	hostnames, err := db.GetHostnamesByCategory(category)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if strings.Contains(err.Error(), "no rows found") {
 | 
			
		||||
			c.JSON(http.StatusNotFound, gin.H{"error": "hostname not found"})
 | 
			
		||||
		}
 | 
			
		||||
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Error retrieving hostnames"})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.JSON(http.StatusOK, hostnames)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// @Summary		Delete a hostname from the database
 | 
			
		||||
// @Description	List all details for a given category
 | 
			
		||||
// @ID				delete-hostnames-by-category-and-name
 | 
			
		||||
// @Produce		json
 | 
			
		||||
// @Param			category	path		string							true	"Category of the hostname"
 | 
			
		||||
// @Param			hostname	path		string							true	"Hostname to delete"
 | 
			
		||||
// @Success		200			{object}	models.SimpleHostnameResponse	"Hostname"
 | 
			
		||||
// @Security		Bearer
 | 
			
		||||
// @Tags			Manipulate existing Hostnames
 | 
			
		||||
// @Router			/{category}/{hostname} [delete]
 | 
			
		||||
func DeleteHostname(c *gin.Context) {
 | 
			
		||||
	category := c.Param("category")
 | 
			
		||||
	hostname := c.Param("hostname")
 | 
			
		||||
 | 
			
		||||
	err := db.DeleteHostname(category, hostname)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	response := models.SimpleHostnameResponse{Hostname: hostname}
 | 
			
		||||
	c.JSON(http.StatusOK, response)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func CreateOrUpdateHostname(c *gin.Context, isUpdate bool) {
 | 
			
		||||
	category := c.Param("category")
 | 
			
		||||
	var oldHostname string
 | 
			
		||||
	if isUpdate {
 | 
			
		||||
		oldHostname = c.Param("oldhostname")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var params map[string]interface{}
 | 
			
		||||
	if err := c.BindJSON(¶ms); err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid parameters"})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rule, err := getHostnameRuleByCategory(category)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var hostname string
 | 
			
		||||
	if isUpdate {
 | 
			
		||||
		hostname, err = rule.Update(category, oldHostname, params)
 | 
			
		||||
	} else {
 | 
			
		||||
		hostname, err = rule.Insert(category, params)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if isUpdate && strings.Contains(err.Error(), "does not exist") {
 | 
			
		||||
			c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
 | 
			
		||||
		} else {
 | 
			
		||||
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	response := models.SimpleHostnameResponse{Hostname: hostname}
 | 
			
		||||
	c.JSON(http.StatusOK, response)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										38
									
								
								api/rule_handlers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								api/rule_handlers.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
// Copyright 2024 Florian Beisel
 | 
			
		||||
//
 | 
			
		||||
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
// you may not use this file except in compliance with the License.
 | 
			
		||||
// You may obtain a copy of the License at
 | 
			
		||||
//
 | 
			
		||||
//     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
//
 | 
			
		||||
// Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
// See the License for the specific language governing permissions and
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
package api
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/http"
 | 
			
		||||
 | 
			
		||||
	"git.beisel.it/florian/hostname-service/rules"
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func ListAvailableRules(c *gin.Context) {
 | 
			
		||||
	descriptions := make(map[string]string)
 | 
			
		||||
	for category, descriptor := range rules.RulesRegistry {
 | 
			
		||||
		descriptions[category] = descriptor.Description
 | 
			
		||||
	}
 | 
			
		||||
	c.JSON(http.StatusOK, descriptions)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getHostnameRuleByCategory(category string) (rules.HostnameRule, error) {
 | 
			
		||||
	if descriptor, exists := rules.RulesRegistry[category]; exists {
 | 
			
		||||
		return descriptor.Factory(), nil
 | 
			
		||||
	}
 | 
			
		||||
	return nil, errors.New("unknown category")
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user