Compare commits
No commits in common. "2b41bbdbb9f69ff950a50b01e2da73a98b808616" and "8d2f4110735967ad48df034dcf19c66f8209f723" have entirely different histories.
2b41bbdbb9
...
8d2f411073
|
@ -1,37 +0,0 @@
|
||||||
// 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")
|
|
||||||
}
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
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")
|
||||||
|
}
|
|
@ -1,137 +0,0 @@
|
||||||
// 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)
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
// 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,17 +1,3 @@
|
||||||
// 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 (
|
||||||
|
@ -22,15 +8,14 @@ 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(jwtKeyBytes)
|
tokenString, err := token.SignedString(config.GlobalConfig.JwtKey)
|
||||||
|
|
||||||
return tokenString, err
|
return tokenString, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,7 @@
|
||||||
// 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"
|
||||||
|
@ -73,7 +58,6 @@ 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,23 +1,10 @@
|
||||||
// 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"
|
||||||
|
@ -58,7 +45,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.DatabaseFile == "" {
|
if GlobalConfig.JwtKey == "" || 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")
|
||||||
}
|
}
|
||||||
|
@ -67,7 +54,7 @@ func LoadConfig() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadConfigFromFile(filePath string, config *AppConfig) error {
|
func loadConfigFromFile(filePath string, config *AppConfig) error {
|
||||||
fileData, err := os.ReadFile(filePath)
|
fileData, err := ioutil.ReadFile(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -94,7 +81,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 := os.ReadFile(secretFilePath); err == nil {
|
if secretValue, err := ioutil.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,17 +1,3 @@
|
||||||
// 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": "Apache 2.0",
|
"name": "MIT",
|
||||||
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
|
"url": "http://git.beisel.it/florian/hostname-service/"
|
||||||
},
|
},
|
||||||
"version": "{{.Version}}"
|
"version": "{{.Version}}"
|
||||||
},
|
},
|
||||||
|
@ -25,11 +25,6 @@ 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"
|
||||||
|
@ -38,7 +33,7 @@ const docTemplate = `{
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"Manipulate existing Hostnames"
|
"Generating Hostnames"
|
||||||
],
|
],
|
||||||
"summary": "Update hostname for category \"notebook\"",
|
"summary": "Update hostname for category \"notebook\"",
|
||||||
"operationId": "update-notebook-hostname",
|
"operationId": "update-notebook-hostname",
|
||||||
|
@ -63,11 +58,6 @@ 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"
|
||||||
|
@ -101,110 +91,6 @@ 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": [
|
||||||
|
@ -488,29 +374,6 @@ 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": "Apache 2.0",
|
"name": "MIT",
|
||||||
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
|
"url": "http://git.beisel.it/florian/hostname-service/"
|
||||||
},
|
},
|
||||||
"version": "1"
|
"version": "1"
|
||||||
},
|
},
|
||||||
|
@ -19,11 +19,6 @@
|
||||||
"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"
|
||||||
|
@ -32,7 +27,7 @@
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"Manipulate existing Hostnames"
|
"Generating Hostnames"
|
||||||
],
|
],
|
||||||
"summary": "Update hostname for category \"notebook\"",
|
"summary": "Update hostname for category \"notebook\"",
|
||||||
"operationId": "update-notebook-hostname",
|
"operationId": "update-notebook-hostname",
|
||||||
|
@ -57,11 +52,6 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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"
|
||||||
|
@ -95,110 +85,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/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": [
|
||||||
|
@ -482,29 +368,6 @@
|
||||||
"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,21 +58,6 @@ 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:
|
||||||
|
@ -81,8 +66,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: Apache 2.0
|
name: MIT
|
||||||
url: http://www.apache.org/licenses/LICENSE-2.0.html
|
url: http://git.beisel.it/florian/hostname-service/
|
||||||
title: Hostname Service API
|
title: Hostname Service API
|
||||||
version: "1"
|
version: "1"
|
||||||
paths:
|
paths:
|
||||||
|
@ -183,8 +168,6 @@ 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
|
||||||
|
@ -207,77 +190,9 @@ 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,17 +1,3 @@
|
||||||
# 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,9 +19,15 @@ 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/router"
|
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
// @title Hostname Service API
|
// @title Hostname Service API
|
||||||
|
@ -48,9 +54,48 @@ 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 := router.New()
|
router := gin.Default()
|
||||||
router.Run(":8080")
|
|
||||||
|
|
||||||
|
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")
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,7 @@ 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")
|
||||||
}
|
}
|
||||||
jwtKeyBytes := []byte(config.GlobalConfig.JwtKey)
|
return config.GlobalConfig.JwtKey, nil
|
||||||
return jwtKeyBytes, nil
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1,17 +1,3 @@
|
||||||
// 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,17 +1,3 @@
|
||||||
// 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,17 +1,3 @@
|
||||||
// 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,17 +1,3 @@
|
||||||
// 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
|
||||||
|
|
|
@ -1,73 +0,0 @@
|
||||||
// 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,53 +9,41 @@ 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, params map[string]interface{}) (string, error)
|
Insert(category string, hostname string, paramsJSON []byte) error
|
||||||
Update(category string, oldhostname string, params map[string]interface{}) (string, error)
|
Update(category string, oldhostname string, hostname string, paramsJSON []byte) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type BaseRule struct{}
|
type BaseRule struct{}
|
||||||
|
|
||||||
func (br *BaseRule) baseInsert(rule HostnameRule, category string, params map[string]interface{}) (string, error) {
|
func (br *BaseRule) baseInsert(category string, hostname string, paramsJSON []byte) 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 {
|
||||||
log.Printf("error checking existence of hostname: %v", err)
|
return fmt.Errorf("error checking existence of hostname: %v", err.Error())
|
||||||
return "", err
|
|
||||||
}
|
}
|
||||||
if exists {
|
if exists {
|
||||||
log.Printf("hostname %s does not exist in category %s", hostname, category)
|
return fmt.Errorf("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
|
||||||
return hostname, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// baseUpdate method for BaseRule
|
func (br *BaseRule) baseUpdate(category string, oldhostname string, hostname string, paramsJSON []byte) error {
|
||||||
func (br *BaseRule) baseUpdate(rule HostnameRule, category string, oldhostname string, params map[string]interface{}) (string, error) {
|
exists, err := db.HostnameExists(category, oldhostname)
|
||||||
// Generate the new hostname using the passed rule's Generate method
|
|
||||||
newHostname, paramsJSON, err := rule.Generate(params)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the hostname in the database
|
err = db.UpdateHostname(category, oldhostname, hostname, paramsJSON)
|
||||||
err = db.UpdateHostname(category, oldhostname, newHostname, paramsJSON)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error updating hostname in DB: %v", err)
|
log.Printf("Error inserting hostname into DB: %v", err)
|
||||||
return "", err
|
return err
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
return newHostname, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,11 +75,8 @@ 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]
|
||||||
// @Security Bearer
|
func (nr *NotebookRule) Insert(category string, hostname string, paramsJSON []byte) error {
|
||||||
func (nr *NotebookRule) Insert(category string, params map[string]interface{}) (string, error) {
|
return nr.baseInsert(category, hostname, paramsJSON)
|
||||||
// Generate the hostname
|
|
||||||
|
|
||||||
return nr.baseInsert(nr, category, params)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Summary Update hostname for category "notebook"
|
// @Summary Update hostname for category "notebook"
|
||||||
|
@ -87,11 +84,10 @@ func (nr *NotebookRule) Insert(category string, params map[string]interface{}) (
|
||||||
// @ID update-notebook-hostname
|
// @ID update-notebook-hostname
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Tags Manipulate existing Hostnames
|
// @Tags Generating 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]
|
||||||
// @Security Bearer
|
func (nr *NotebookRule) Update(category string, oldhostname string, hostname string, paramsJSON []byte) error {
|
||||||
func (nr *NotebookRule) Update(category string, oldhostname string, params map[string]interface{}) (string, error) {
|
return nr.baseUpdate(category, oldhostname, hostname, paramsJSON)
|
||||||
return nr.baseUpdate(nr, category, oldhostname, params)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
// 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 ...
|
|
||||||
}
|
|
139
rules/server.go
139
rules/server.go
|
@ -1,139 +0,0 @@
|
||||||
// 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