Compare commits
11 Commits
8d2f411073
...
2b41bbdbb9
Author | SHA1 | Date |
---|---|---|
Florian Beisel | 2b41bbdbb9 | |
Florian Beisel | 1669a65512 | |
Florian Beisel | 600fb0e997 | |
Florian Beisel | b681395287 | |
Florian Beisel | 8563c80ffe | |
Florian Beisel | 7df43f2cf3 | |
Florian Beisel | f4b6197728 | |
Florian Beisel | df6a785ac4 | |
Florian Beisel | a871f165b7 | |
Florian Beisel | c51a17993e | |
Florian Beisel | a4748d7619 |
|
@ -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")
|
|
||||||
}
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -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")
|
||||||
|
}
|
19
auth/auth.go
19
auth/auth.go
|
@ -1,3 +1,17 @@
|
||||||
|
// 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 auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -8,14 +22,15 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func GenerateToken(username string) (string, error) {
|
func GenerateToken(username string) (string, error) {
|
||||||
|
jwtKeyBytes := []byte(config.GlobalConfig.JwtKey)
|
||||||
|
|
||||||
expirationTime := time.Now().Add(1 * time.Hour)
|
expirationTime := time.Now().Add(1 * time.Hour)
|
||||||
claims := &jwt.StandardClaims{
|
claims := &jwt.StandardClaims{
|
||||||
Subject: username,
|
Subject: username,
|
||||||
ExpiresAt: expirationTime.Unix(),
|
ExpiresAt: expirationTime.Unix(),
|
||||||
}
|
}
|
||||||
|
|
||||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||||
tokenString, err := token.SignedString(config.GlobalConfig.JwtKey)
|
tokenString, err := token.SignedString(jwtKeyBytes)
|
||||||
|
|
||||||
return tokenString, err
|
return tokenString, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,22 @@
|
||||||
|
// 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 auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"git.beisel.it/florian/hostname-service/db"
|
"git.beisel.it/florian/hostname-service/db"
|
||||||
|
@ -11,19 +26,19 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LoginHandler godoc
|
// LoginHandler godoc
|
||||||
//
|
//
|
||||||
// @Summary User login
|
// @Summary User login
|
||||||
// @Description Authenticate user and return JWT token
|
// @Description Authenticate user and return JWT token
|
||||||
// @Tags Authentication
|
// @Tags Authentication
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param loginCredentials body models.LoginCredentials true "Login Credentials"
|
// @Param loginCredentials body models.LoginCredentials true "Login Credentials"
|
||||||
// @Success 200 {object} models.TokenResponse "Successfully authenticated, JWT token returned"
|
// @Success 200 {object} models.TokenResponse "Successfully authenticated, JWT token returned"
|
||||||
// @Failure 400 {object} models.ErrorResponse "Invalid request body"
|
// @Failure 400 {object} models.ErrorResponse "Invalid request body"
|
||||||
// @Failure 401 {object} models.ErrorResponse "Invalid login credentials"
|
// @Failure 401 {object} models.ErrorResponse "Invalid login credentials"
|
||||||
// @Failure 500 {object} models.ErrorResponse "Internal server error"
|
// @Failure 500 {object} models.ErrorResponse "Internal server error"
|
||||||
// @Router /login [post]
|
// @Router /login [post]
|
||||||
func LoginHandler(c *gin.Context) {
|
func LoginHandler(c *gin.Context) {
|
||||||
var creds models.LoginCredentials
|
var creds models.LoginCredentials
|
||||||
|
|
||||||
|
@ -58,6 +73,7 @@ func LoginHandler(c *gin.Context) {
|
||||||
token, err := GenerateToken(storedCreds.Username)
|
token, err := GenerateToken(storedCreds.Username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate token"})
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate token"})
|
||||||
|
log.Printf("Error generating token %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,23 @@
|
||||||
|
// 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 config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -45,7 +58,7 @@ func LoadConfig() error {
|
||||||
loadConfigFromEnvOrSecrets(GlobalConfig)
|
loadConfigFromEnvOrSecrets(GlobalConfig)
|
||||||
|
|
||||||
// Check if the configuration has been loaded successfully
|
// Check if the configuration has been loaded successfully
|
||||||
if GlobalConfig.JwtKey == "" || GlobalConfig.DatabaseFile == "" {
|
if GlobalConfig.DatabaseFile == "" {
|
||||||
// Add more checks as necessary for other required config fields
|
// Add more checks as necessary for other required config fields
|
||||||
return errors.New("failed to load configuration from any source")
|
return errors.New("failed to load configuration from any source")
|
||||||
}
|
}
|
||||||
|
@ -54,7 +67,7 @@ func LoadConfig() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadConfigFromFile(filePath string, config *AppConfig) error {
|
func loadConfigFromFile(filePath string, config *AppConfig) error {
|
||||||
fileData, err := ioutil.ReadFile(filePath)
|
fileData, err := os.ReadFile(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -81,7 +94,7 @@ func loadConfigFromEnvOrSecrets(config *AppConfig) {
|
||||||
// Handling Docker secrets (file-based secrets)
|
// Handling Docker secrets (file-based secrets)
|
||||||
secretFileEnvVar := envVar + "_FILE"
|
secretFileEnvVar := envVar + "_FILE"
|
||||||
if secretFilePath, exists := os.LookupEnv(secretFileEnvVar); exists {
|
if secretFilePath, exists := os.LookupEnv(secretFileEnvVar); exists {
|
||||||
if secretValue, err := ioutil.ReadFile(secretFilePath); err == nil {
|
if secretValue, err := os.ReadFile(secretFilePath); err == nil {
|
||||||
val.Field(i).SetString(string(secretValue))
|
val.Field(i).SetString(string(secretValue))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
14
db/db.go
14
db/db.go
|
@ -1,3 +1,17 @@
|
||||||
|
// 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 db
|
package db
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
143
docs/docs.go
143
docs/docs.go
|
@ -15,8 +15,8 @@ const docTemplate = `{
|
||||||
"email": "florian@beisel.it"
|
"email": "florian@beisel.it"
|
||||||
},
|
},
|
||||||
"license": {
|
"license": {
|
||||||
"name": "MIT",
|
"name": "Apache 2.0",
|
||||||
"url": "http://git.beisel.it/florian/hostname-service/"
|
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
|
||||||
},
|
},
|
||||||
"version": "{{.Version}}"
|
"version": "{{.Version}}"
|
||||||
},
|
},
|
||||||
|
@ -25,6 +25,11 @@ const docTemplate = `{
|
||||||
"paths": {
|
"paths": {
|
||||||
"/api/notebook": {
|
"/api/notebook": {
|
||||||
"put": {
|
"put": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"Bearer": []
|
||||||
|
}
|
||||||
|
],
|
||||||
"description": "Generates a new hostname for a notebook based on dynamic rules.",
|
"description": "Generates a new hostname for a notebook based on dynamic rules.",
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"application/json"
|
"application/json"
|
||||||
|
@ -33,7 +38,7 @@ const docTemplate = `{
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"Generating Hostnames"
|
"Manipulate existing Hostnames"
|
||||||
],
|
],
|
||||||
"summary": "Update hostname for category \"notebook\"",
|
"summary": "Update hostname for category \"notebook\"",
|
||||||
"operationId": "update-notebook-hostname",
|
"operationId": "update-notebook-hostname",
|
||||||
|
@ -58,6 +63,11 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"post": {
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"Bearer": []
|
||||||
|
}
|
||||||
|
],
|
||||||
"description": "Generates a hostname for a notebook based on dynamic rules.",
|
"description": "Generates a hostname for a notebook based on dynamic rules.",
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"application/json"
|
"application/json"
|
||||||
|
@ -91,6 +101,110 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/rules": {
|
||||||
|
"get": {
|
||||||
|
"description": "Get a list of all available hostname generation rules.",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Rules"
|
||||||
|
],
|
||||||
|
"summary": "List Available Rules",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "List of available rules with descriptions",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/server": {
|
||||||
|
"put": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"Bearer": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Generates a new hostname for a notebook based on dynamic rules.",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Manipulate existing Hostnames"
|
||||||
|
],
|
||||||
|
"summary": "Update hostname for category \"notebook\"",
|
||||||
|
"operationId": "update-server-hostname",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "Input data to generate hostname",
|
||||||
|
"name": "body",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/rules.ServerRuleInput"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Hostname",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.SimpleHostnameResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"Bearer": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Generates a hostname for a notebook based on dynamic rules.",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Generating Hostnames"
|
||||||
|
],
|
||||||
|
"summary": "Generate hostname for category \"notebook\"",
|
||||||
|
"operationId": "insert-server-hostname",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "Input data to generate hostname",
|
||||||
|
"name": "body",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/rules.ServerRuleInput"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Hostname",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.SimpleHostnameResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/hello": {
|
"/hello": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
|
@ -374,6 +488,29 @@ const docTemplate = `{
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"rules.ServerRuleInput": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"Description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"ILO": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"IP": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"Location": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"OrgUnit": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"Responsible": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"securityDefinitions": {
|
"securityDefinitions": {
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
"email": "florian@beisel.it"
|
"email": "florian@beisel.it"
|
||||||
},
|
},
|
||||||
"license": {
|
"license": {
|
||||||
"name": "MIT",
|
"name": "Apache 2.0",
|
||||||
"url": "http://git.beisel.it/florian/hostname-service/"
|
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
|
||||||
},
|
},
|
||||||
"version": "1"
|
"version": "1"
|
||||||
},
|
},
|
||||||
|
@ -19,6 +19,11 @@
|
||||||
"paths": {
|
"paths": {
|
||||||
"/api/notebook": {
|
"/api/notebook": {
|
||||||
"put": {
|
"put": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"Bearer": []
|
||||||
|
}
|
||||||
|
],
|
||||||
"description": "Generates a new hostname for a notebook based on dynamic rules.",
|
"description": "Generates a new hostname for a notebook based on dynamic rules.",
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"application/json"
|
"application/json"
|
||||||
|
@ -27,7 +32,7 @@
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"Generating Hostnames"
|
"Manipulate existing Hostnames"
|
||||||
],
|
],
|
||||||
"summary": "Update hostname for category \"notebook\"",
|
"summary": "Update hostname for category \"notebook\"",
|
||||||
"operationId": "update-notebook-hostname",
|
"operationId": "update-notebook-hostname",
|
||||||
|
@ -52,6 +57,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"post": {
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"Bearer": []
|
||||||
|
}
|
||||||
|
],
|
||||||
"description": "Generates a hostname for a notebook based on dynamic rules.",
|
"description": "Generates a hostname for a notebook based on dynamic rules.",
|
||||||
"consumes": [
|
"consumes": [
|
||||||
"application/json"
|
"application/json"
|
||||||
|
@ -85,6 +95,110 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/rules": {
|
||||||
|
"get": {
|
||||||
|
"description": "Get a list of all available hostname generation rules.",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Rules"
|
||||||
|
],
|
||||||
|
"summary": "List Available Rules",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "List of available rules with descriptions",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/server": {
|
||||||
|
"put": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"Bearer": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Generates a new hostname for a notebook based on dynamic rules.",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Manipulate existing Hostnames"
|
||||||
|
],
|
||||||
|
"summary": "Update hostname for category \"notebook\"",
|
||||||
|
"operationId": "update-server-hostname",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "Input data to generate hostname",
|
||||||
|
"name": "body",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/rules.ServerRuleInput"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Hostname",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.SimpleHostnameResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"Bearer": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Generates a hostname for a notebook based on dynamic rules.",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Generating Hostnames"
|
||||||
|
],
|
||||||
|
"summary": "Generate hostname for category \"notebook\"",
|
||||||
|
"operationId": "insert-server-hostname",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "Input data to generate hostname",
|
||||||
|
"name": "body",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/rules.ServerRuleInput"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Hostname",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/models.SimpleHostnameResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/hello": {
|
"/hello": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
|
@ -368,6 +482,29 @@
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"rules.ServerRuleInput": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"Description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"ILO": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"IP": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"Location": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"OrgUnit": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"Responsible": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"securityDefinitions": {
|
"securityDefinitions": {
|
||||||
|
|
|
@ -58,6 +58,21 @@ definitions:
|
||||||
OrgUnit:
|
OrgUnit:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
rules.ServerRuleInput:
|
||||||
|
properties:
|
||||||
|
Description:
|
||||||
|
type: string
|
||||||
|
ILO:
|
||||||
|
type: string
|
||||||
|
IP:
|
||||||
|
type: string
|
||||||
|
Location:
|
||||||
|
type: string
|
||||||
|
OrgUnit:
|
||||||
|
type: string
|
||||||
|
Responsible:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
host: localhost:8080
|
host: localhost:8080
|
||||||
info:
|
info:
|
||||||
contact:
|
contact:
|
||||||
|
@ -66,8 +81,8 @@ info:
|
||||||
url: http://git.beisel.it/florian
|
url: http://git.beisel.it/florian
|
||||||
description: This is a sample server for a hostname service.
|
description: This is a sample server for a hostname service.
|
||||||
license:
|
license:
|
||||||
name: MIT
|
name: Apache 2.0
|
||||||
url: http://git.beisel.it/florian/hostname-service/
|
url: http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
title: Hostname Service API
|
title: Hostname Service API
|
||||||
version: "1"
|
version: "1"
|
||||||
paths:
|
paths:
|
||||||
|
@ -168,6 +183,8 @@ paths:
|
||||||
description: Hostname
|
description: Hostname
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/models.SimpleHostnameResponse'
|
$ref: '#/definitions/models.SimpleHostnameResponse'
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
summary: Generate hostname for category "notebook"
|
summary: Generate hostname for category "notebook"
|
||||||
tags:
|
tags:
|
||||||
- Generating Hostnames
|
- Generating Hostnames
|
||||||
|
@ -190,9 +207,77 @@ paths:
|
||||||
description: Hostname
|
description: Hostname
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/models.SimpleHostnameResponse'
|
$ref: '#/definitions/models.SimpleHostnameResponse'
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
summary: Update hostname for category "notebook"
|
summary: Update hostname for category "notebook"
|
||||||
tags:
|
tags:
|
||||||
|
- Manipulate existing Hostnames
|
||||||
|
/api/rules:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Get a list of all available hostname generation rules.
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: List of available rules with descriptions
|
||||||
|
schema:
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
summary: List Available Rules
|
||||||
|
tags:
|
||||||
|
- Rules
|
||||||
|
/api/server:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Generates a hostname for a notebook based on dynamic rules.
|
||||||
|
operationId: insert-server-hostname
|
||||||
|
parameters:
|
||||||
|
- description: Input data to generate hostname
|
||||||
|
in: body
|
||||||
|
name: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/rules.ServerRuleInput'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Hostname
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.SimpleHostnameResponse'
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
summary: Generate hostname for category "notebook"
|
||||||
|
tags:
|
||||||
- Generating Hostnames
|
- Generating Hostnames
|
||||||
|
put:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Generates a new hostname for a notebook based on dynamic rules.
|
||||||
|
operationId: update-server-hostname
|
||||||
|
parameters:
|
||||||
|
- description: Input data to generate hostname
|
||||||
|
in: body
|
||||||
|
name: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/rules.ServerRuleInput'
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Hostname
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/models.SimpleHostnameResponse'
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
summary: Update hostname for category "notebook"
|
||||||
|
tags:
|
||||||
|
- Manipulate existing Hostnames
|
||||||
/hello:
|
/hello:
|
||||||
get:
|
get:
|
||||||
consumes:
|
consumes:
|
||||||
|
|
|
@ -1,3 +1,17 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
# Define API URLs
|
# Define API URLs
|
||||||
$loginUrl = "http://localhost:8080/api/v1/login"
|
$loginUrl = "http://localhost:8080/api/v1/login"
|
||||||
$generateNotebookUrl = "http://localhost:8080/api/v1/notebook"
|
$generateNotebookUrl = "http://localhost:8080/api/v1/notebook"
|
||||||
|
|
51
main.go
51
main.go
|
@ -19,15 +19,9 @@ package main
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"git.beisel.it/florian/hostname-service/api"
|
|
||||||
"git.beisel.it/florian/hostname-service/auth"
|
|
||||||
"git.beisel.it/florian/hostname-service/config"
|
"git.beisel.it/florian/hostname-service/config"
|
||||||
"git.beisel.it/florian/hostname-service/db"
|
"git.beisel.it/florian/hostname-service/db"
|
||||||
"git.beisel.it/florian/hostname-service/docs"
|
"git.beisel.it/florian/hostname-service/router"
|
||||||
"git.beisel.it/florian/hostname-service/middleware"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
swaggerFiles "github.com/swaggo/files"
|
|
||||||
ginSwagger "github.com/swaggo/gin-swagger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// @title Hostname Service API
|
// @title Hostname Service API
|
||||||
|
@ -54,48 +48,9 @@ func main() {
|
||||||
log.Fatalf("Failed to load configuration: %v", err)
|
log.Fatalf("Failed to load configuration: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
gin.SetMode(gin.DebugMode)
|
|
||||||
|
|
||||||
db.Init(config.GlobalConfig.DatabaseFile)
|
db.Init(config.GlobalConfig.DatabaseFile)
|
||||||
|
|
||||||
router := gin.Default()
|
router := router.New()
|
||||||
|
|
||||||
docs.SwaggerInfo.Host = "localhost:8080"
|
|
||||||
docs.SwaggerInfo.BasePath = "/api/v1"
|
|
||||||
|
|
||||||
v1 := router.Group("/api/v1")
|
|
||||||
{
|
|
||||||
// public routes
|
|
||||||
v1.POST("/login", auth.LoginHandler)
|
|
||||||
|
|
||||||
// Protected Routes
|
|
||||||
authenticated := v1.Group("/").Use(middleware.Authenticate())
|
|
||||||
{
|
|
||||||
authenticated.GET("/hello", api.Helloworld)
|
|
||||||
|
|
||||||
// Create Host
|
|
||||||
authenticated.POST("/:category", func(c *gin.Context) {
|
|
||||||
api.CreateOrUpdateHostname(c, false)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Get Host Details
|
|
||||||
authenticated.GET("/:category/:hostname", api.GetHostnameByCategoryAndName)
|
|
||||||
|
|
||||||
// Update Host
|
|
||||||
authenticated.PUT("/:category/:oldhostname", func(c *gin.Context) {
|
|
||||||
api.CreateOrUpdateHostname(c, true)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Delete Host
|
|
||||||
authenticated.DELETE("/:category/:hostname", api.DeleteHostname)
|
|
||||||
|
|
||||||
// List Hostnames
|
|
||||||
authenticated.GET("/:category", api.ListHostnamesByCategory)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Swagger endpoint
|
|
||||||
router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
|
||||||
|
|
||||||
router.Run(":8080")
|
router.Run(":8080")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,8 @@ func Authenticate() gin.HandlerFunc {
|
||||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||||
return nil, fmt.Errorf("unexpected signing method")
|
return nil, fmt.Errorf("unexpected signing method")
|
||||||
}
|
}
|
||||||
return config.GlobalConfig.JwtKey, nil
|
jwtKeyBytes := []byte(config.GlobalConfig.JwtKey)
|
||||||
|
return jwtKeyBytes, nil
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1,3 +1,17 @@
|
||||||
|
// 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 models
|
package models
|
||||||
|
|
||||||
type ErrorResponse struct {
|
type ErrorResponse struct {
|
||||||
|
|
|
@ -1,3 +1,17 @@
|
||||||
|
// 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 models
|
package models
|
||||||
|
|
||||||
import "time"
|
import "time"
|
||||||
|
|
|
@ -1,3 +1,17 @@
|
||||||
|
// 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 models
|
package models
|
||||||
|
|
||||||
// User Credentials Model
|
// User Credentials Model
|
||||||
|
|
|
@ -1,3 +1,17 @@
|
||||||
|
// 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 models
|
package models
|
||||||
|
|
||||||
// User Model
|
// User Model
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
// 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 router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.beisel.it/florian/hostname-service/api"
|
||||||
|
"git.beisel.it/florian/hostname-service/auth"
|
||||||
|
"git.beisel.it/florian/hostname-service/docs"
|
||||||
|
"git.beisel.it/florian/hostname-service/middleware"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
swaggerFiles "github.com/swaggo/files"
|
||||||
|
ginSwagger "github.com/swaggo/gin-swagger"
|
||||||
|
// ... other necessary imports ...
|
||||||
|
)
|
||||||
|
|
||||||
|
func New() *gin.Engine {
|
||||||
|
gin.SetMode(gin.DebugMode)
|
||||||
|
router := gin.Default()
|
||||||
|
|
||||||
|
docs.SwaggerInfo.Host = "localhost:8080"
|
||||||
|
docs.SwaggerInfo.BasePath = "/api/v1"
|
||||||
|
|
||||||
|
v1 := router.Group("/api/v1")
|
||||||
|
{
|
||||||
|
// public routes
|
||||||
|
v1.POST("/login", auth.LoginHandler)
|
||||||
|
|
||||||
|
// Protected Routes
|
||||||
|
authenticated := v1.Group("/").Use(middleware.Authenticate())
|
||||||
|
{
|
||||||
|
authenticated.GET("/hello", api.Helloworld)
|
||||||
|
|
||||||
|
// Create Host
|
||||||
|
authenticated.POST("/:category", func(c *gin.Context) {
|
||||||
|
api.CreateOrUpdateHostname(c, false)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Get Host Details
|
||||||
|
authenticated.GET("/:category/:hostname", api.GetHostnameByCategoryAndName)
|
||||||
|
|
||||||
|
// Update Host
|
||||||
|
authenticated.PUT("/:category/:oldhostname", func(c *gin.Context) {
|
||||||
|
api.CreateOrUpdateHostname(c, true)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Delete Host
|
||||||
|
authenticated.DELETE("/:category/:hostname", api.DeleteHostname)
|
||||||
|
|
||||||
|
// List Hostnames
|
||||||
|
authenticated.GET("/:category", api.ListHostnamesByCategory)
|
||||||
|
|
||||||
|
// List available Rules
|
||||||
|
authenticated.GET("/api/rules", api.ListAvailableRules)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swagger endpoint
|
||||||
|
router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
||||||
|
|
||||||
|
return router
|
||||||
|
}
|
|
@ -9,41 +9,53 @@ import (
|
||||||
|
|
||||||
type HostnameRule interface {
|
type HostnameRule interface {
|
||||||
Generate(params map[string]interface{}) (string, []byte, error)
|
Generate(params map[string]interface{}) (string, []byte, error)
|
||||||
Insert(category string, hostname string, paramsJSON []byte) error
|
Insert(category string, params map[string]interface{}) (string, error)
|
||||||
Update(category string, oldhostname string, hostname string, paramsJSON []byte) error
|
Update(category string, oldhostname string, params map[string]interface{}) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type BaseRule struct{}
|
type BaseRule struct{}
|
||||||
|
|
||||||
func (br *BaseRule) baseInsert(category string, hostname string, paramsJSON []byte) error {
|
func (br *BaseRule) baseInsert(rule HostnameRule, category string, params map[string]interface{}) (string, error) {
|
||||||
|
// Generate the hostname using the passed rule's Generate method
|
||||||
|
hostname, paramsJSON, err := rule.Generate(params)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
exists, err := db.HostnameExists(category, hostname)
|
exists, err := db.HostnameExists(category, hostname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error checking existence of hostname: %v", err.Error())
|
log.Printf("error checking existence of hostname: %v", err)
|
||||||
|
return "", err
|
||||||
}
|
}
|
||||||
if exists {
|
if exists {
|
||||||
return fmt.Errorf("hostname %s does not exist in category %s", hostname, category)
|
log.Printf("hostname %s does not exist in category %s", hostname, category)
|
||||||
|
return "", fmt.Errorf("hostname-exists")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Insert the hostname into the database
|
||||||
err = db.InsertHostname(category, hostname, paramsJSON)
|
err = db.InsertHostname(category, hostname, paramsJSON)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error inserting hostname into DB: %v", err)
|
log.Printf("Error inserting hostname into DB: %v", err)
|
||||||
return err
|
return "", err
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (br *BaseRule) baseUpdate(category string, oldhostname string, hostname string, paramsJSON []byte) error {
|
|
||||||
exists, err := db.HostnameExists(category, oldhostname)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error checking existence of hostname: %v", err.Error())
|
|
||||||
}
|
|
||||||
if !exists {
|
|
||||||
return fmt.Errorf("hostname %s does not exist in category %s", oldhostname, category)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = db.UpdateHostname(category, oldhostname, hostname, paramsJSON)
|
return hostname, nil
|
||||||
if err != nil {
|
}
|
||||||
log.Printf("Error inserting hostname into DB: %v", err)
|
|
||||||
return err
|
// baseUpdate method for BaseRule
|
||||||
}
|
func (br *BaseRule) baseUpdate(rule HostnameRule, category string, oldhostname string, params map[string]interface{}) (string, error) {
|
||||||
return nil
|
// Generate the new hostname using the passed rule's Generate method
|
||||||
|
newHostname, paramsJSON, err := rule.Generate(params)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the hostname in the database
|
||||||
|
err = db.UpdateHostname(category, oldhostname, newHostname, paramsJSON)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error updating hostname in DB: %v", err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return newHostname, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,8 +75,11 @@ func (nr *NotebookRule) Generate(params map[string]interface{}) (string, []byte,
|
||||||
// @Param body body NotebookRuleInput true "Input data to generate hostname"
|
// @Param body body NotebookRuleInput true "Input data to generate hostname"
|
||||||
// @Success 200 {object} models.SimpleHostnameResponse "Hostname"
|
// @Success 200 {object} models.SimpleHostnameResponse "Hostname"
|
||||||
// @Router /api/notebook [post]
|
// @Router /api/notebook [post]
|
||||||
func (nr *NotebookRule) Insert(category string, hostname string, paramsJSON []byte) error {
|
// @Security Bearer
|
||||||
return nr.baseInsert(category, hostname, paramsJSON)
|
func (nr *NotebookRule) Insert(category string, params map[string]interface{}) (string, error) {
|
||||||
|
// Generate the hostname
|
||||||
|
|
||||||
|
return nr.baseInsert(nr, category, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Summary Update hostname for category "notebook"
|
// @Summary Update hostname for category "notebook"
|
||||||
|
@ -84,10 +87,11 @@ func (nr *NotebookRule) Insert(category string, hostname string, paramsJSON []by
|
||||||
// @ID update-notebook-hostname
|
// @ID update-notebook-hostname
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Tags Generating Hostnames
|
// @Tags Manipulate existing Hostnames
|
||||||
// @Param body body NotebookRuleInput true "Input data to generate hostname"
|
// @Param body body NotebookRuleInput true "Input data to generate hostname"
|
||||||
// @Success 200 {object} models.SimpleHostnameResponse "Hostname"
|
// @Success 200 {object} models.SimpleHostnameResponse "Hostname"
|
||||||
// @Router /api/notebook [put]
|
// @Router /api/notebook [put]
|
||||||
func (nr *NotebookRule) Update(category string, oldhostname string, hostname string, paramsJSON []byte) error {
|
// @Security Bearer
|
||||||
return nr.baseUpdate(category, oldhostname, hostname, paramsJSON)
|
func (nr *NotebookRule) Update(category string, oldhostname string, params map[string]interface{}) (string, error) {
|
||||||
|
return nr.baseUpdate(nr, category, oldhostname, params)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
// 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 rules
|
||||||
|
|
||||||
|
type RuleFactoryFunc func() HostnameRule
|
||||||
|
|
||||||
|
type RuleDescriptor struct {
|
||||||
|
Description string
|
||||||
|
Factory RuleFactoryFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
var RulesRegistry = map[string]RuleDescriptor{
|
||||||
|
"notebook": {
|
||||||
|
Description: "Generates hostnames for notebooks.",
|
||||||
|
Factory: func() HostnameRule { return &NotebookRule{} },
|
||||||
|
},
|
||||||
|
"server": {
|
||||||
|
Description: "Generates hostnames for servers.",
|
||||||
|
Factory: func() HostnameRule { return &ServerRule{} },
|
||||||
|
},
|
||||||
|
// ... other rules ...
|
||||||
|
}
|
|
@ -0,0 +1,139 @@
|
||||||
|
// 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 rules
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"git.beisel.it/florian/hostname-service/db"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServerRule struct {
|
||||||
|
BaseRule
|
||||||
|
OrgUnit string `json:"OrgUnit"`
|
||||||
|
Location string `json:"Location"`
|
||||||
|
Number int `json:"Number"`
|
||||||
|
Description string `json:"Description"`
|
||||||
|
IP string `json:"IP"`
|
||||||
|
ILO string `json:"ILO"`
|
||||||
|
Responsible string `json:"Responsible"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerRuleInput struct {
|
||||||
|
OrgUnit string `json:"OrgUnit"`
|
||||||
|
Location string `json:"Location"`
|
||||||
|
Description string `json:"Description"`
|
||||||
|
IP string `json:"IP"`
|
||||||
|
ILO string `json:"ILO"`
|
||||||
|
Responsible string `json:"Responsible"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that ServerRule implements HostnameRule interface
|
||||||
|
var _ HostnameRule = &ServerRule{}
|
||||||
|
|
||||||
|
func (sr *ServerRule) Generate(params map[string]interface{}) (string, []byte, error) {
|
||||||
|
var ok bool
|
||||||
|
|
||||||
|
// Get the OrgUnit
|
||||||
|
if sr.OrgUnit, ok = params["OrgUnit"].(string); !ok {
|
||||||
|
return "", nil, errors.New("OrgUnit parameter is required and must be a string")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the Location
|
||||||
|
if sr.Location, ok = params["Location"].(string); !ok {
|
||||||
|
return "", nil, errors.New("location parameter is required and must be a string")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the Description
|
||||||
|
if sr.Description, ok = params["Description"].(string); !ok {
|
||||||
|
return "", nil, errors.New("parameter Description is required and must be a string")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the IP
|
||||||
|
if sr.IP, ok = params["IP"].(string); !ok {
|
||||||
|
return "", nil, errors.New("IP parameter is required and must be a string")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the ILO IP
|
||||||
|
if sr.ILO, ok = params["ILO"].(string); !ok {
|
||||||
|
return "", nil, errors.New("ILO parameter is required and must be a string")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the Responsible
|
||||||
|
if sr.Responsible, ok = params["Responsible"].(string); !ok {
|
||||||
|
return "", nil, errors.New("parameter Responsible parameter is required and must be a string")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get last used number from the database and increment it
|
||||||
|
maxNumber, err := db.GetMaxNumberForCategory("server")
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
newNumber := 1
|
||||||
|
if maxNumber >= 1 {
|
||||||
|
newNumber = int(maxNumber) + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
sr.Number = newNumber
|
||||||
|
|
||||||
|
// Generate the hostname (orgUnit + Location + "NB" + Number)
|
||||||
|
hostname := fmt.Sprintf("%s%sSV%04d", sr.OrgUnit, sr.Location, sr.Number)
|
||||||
|
|
||||||
|
// Store the generated hostname in the database
|
||||||
|
// JSON parameters can be stored by marshalling the params map
|
||||||
|
paramsJSON, err := json.Marshal(sr)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error converting Category Struct to JSON: %v", err)
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the generated hostname and marshaled parameters
|
||||||
|
return hostname, paramsJSON, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Summary Generate hostname for category "notebook"
|
||||||
|
// @Description Generates a hostname for a notebook based on dynamic rules.
|
||||||
|
// @ID insert-server-hostname
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Tags Generating Hostnames
|
||||||
|
// @Param body body ServerRuleInput true "Input data to generate hostname"
|
||||||
|
// @Success 200 {object} models.SimpleHostnameResponse "Hostname"
|
||||||
|
// @Router /api/server [post]
|
||||||
|
// @Security Bearer
|
||||||
|
func (nr *ServerRule) Insert(category string, params map[string]interface{}) (string, error) {
|
||||||
|
// Generate the hostname
|
||||||
|
|
||||||
|
return nr.baseInsert(nr, category, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Summary Update hostname for category "notebook"
|
||||||
|
// @Description Generates a new hostname for a notebook based on dynamic rules.
|
||||||
|
// @ID update-server-hostname
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Tags Manipulate existing Hostnames
|
||||||
|
// @Param body body ServerRuleInput true "Input data to generate hostname"
|
||||||
|
// @Success 200 {object} models.SimpleHostnameResponse "Hostname"
|
||||||
|
// @Router /api/server [put]
|
||||||
|
// @Security Bearer
|
||||||
|
func (nr *ServerRule) Update(category string, oldhostname string, params map[string]interface{}) (string, error) {
|
||||||
|
return nr.baseUpdate(nr, category, oldhostname, params)
|
||||||
|
}
|
Loading…
Reference in New Issue