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
|
||||
|
||||
import (
|
||||
|
@ -8,14 +22,15 @@ import (
|
|||
)
|
||||
|
||||
func GenerateToken(username string) (string, error) {
|
||||
jwtKeyBytes := []byte(config.GlobalConfig.JwtKey)
|
||||
|
||||
expirationTime := time.Now().Add(1 * time.Hour)
|
||||
claims := &jwt.StandardClaims{
|
||||
Subject: username,
|
||||
ExpiresAt: expirationTime.Unix(),
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
tokenString, err := token.SignedString(config.GlobalConfig.JwtKey)
|
||||
tokenString, err := token.SignedString(jwtKeyBytes)
|
||||
|
||||
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
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"git.beisel.it/florian/hostname-service/db"
|
||||
|
@ -11,19 +26,19 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// LoginHandler godoc
|
||||
// LoginHandler godoc
|
||||
//
|
||||
// @Summary User login
|
||||
// @Description Authenticate user and return JWT token
|
||||
// @Tags Authentication
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param loginCredentials body models.LoginCredentials true "Login Credentials"
|
||||
// @Success 200 {object} models.TokenResponse "Successfully authenticated, JWT token returned"
|
||||
// @Failure 400 {object} models.ErrorResponse "Invalid request body"
|
||||
// @Failure 401 {object} models.ErrorResponse "Invalid login credentials"
|
||||
// @Failure 500 {object} models.ErrorResponse "Internal server error"
|
||||
// @Router /login [post]
|
||||
// @Summary User login
|
||||
// @Description Authenticate user and return JWT token
|
||||
// @Tags Authentication
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param loginCredentials body models.LoginCredentials true "Login Credentials"
|
||||
// @Success 200 {object} models.TokenResponse "Successfully authenticated, JWT token returned"
|
||||
// @Failure 400 {object} models.ErrorResponse "Invalid request body"
|
||||
// @Failure 401 {object} models.ErrorResponse "Invalid login credentials"
|
||||
// @Failure 500 {object} models.ErrorResponse "Internal server error"
|
||||
// @Router /login [post]
|
||||
func LoginHandler(c *gin.Context) {
|
||||
var creds models.LoginCredentials
|
||||
|
||||
|
@ -58,6 +73,7 @@ func LoginHandler(c *gin.Context) {
|
|||
token, err := GenerateToken(storedCreds.Username)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate token"})
|
||||
log.Printf("Error generating token %v", err)
|
||||
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
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
@ -45,7 +58,7 @@ func LoadConfig() error {
|
|||
loadConfigFromEnvOrSecrets(GlobalConfig)
|
||||
|
||||
// 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
|
||||
return errors.New("failed to load configuration from any source")
|
||||
}
|
||||
|
@ -54,7 +67,7 @@ func LoadConfig() error {
|
|||
}
|
||||
|
||||
func loadConfigFromFile(filePath string, config *AppConfig) error {
|
||||
fileData, err := ioutil.ReadFile(filePath)
|
||||
fileData, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -81,7 +94,7 @@ func loadConfigFromEnvOrSecrets(config *AppConfig) {
|
|||
// Handling Docker secrets (file-based secrets)
|
||||
secretFileEnvVar := envVar + "_FILE"
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
|
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
|
||||
|
||||
import (
|
||||
|
|
143
docs/docs.go
143
docs/docs.go
|
@ -15,8 +15,8 @@ const docTemplate = `{
|
|||
"email": "florian@beisel.it"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "http://git.beisel.it/florian/hostname-service/"
|
||||
"name": "Apache 2.0",
|
||||
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
|
||||
},
|
||||
"version": "{{.Version}}"
|
||||
},
|
||||
|
@ -25,6 +25,11 @@ const docTemplate = `{
|
|||
"paths": {
|
||||
"/api/notebook": {
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Generates a new hostname for a notebook based on dynamic rules.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
|
@ -33,7 +38,7 @@ const docTemplate = `{
|
|||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Generating Hostnames"
|
||||
"Manipulate existing Hostnames"
|
||||
],
|
||||
"summary": "Update hostname for category \"notebook\"",
|
||||
"operationId": "update-notebook-hostname",
|
||||
|
@ -58,6 +63,11 @@ const docTemplate = `{
|
|||
}
|
||||
},
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Generates a hostname for a notebook based on dynamic rules.",
|
||||
"consumes": [
|
||||
"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": {
|
||||
"get": {
|
||||
"security": [
|
||||
|
@ -374,6 +488,29 @@ const docTemplate = `{
|
|||
"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": {
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
"email": "florian@beisel.it"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "http://git.beisel.it/florian/hostname-service/"
|
||||
"name": "Apache 2.0",
|
||||
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
|
||||
},
|
||||
"version": "1"
|
||||
},
|
||||
|
@ -19,6 +19,11 @@
|
|||
"paths": {
|
||||
"/api/notebook": {
|
||||
"put": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Generates a new hostname for a notebook based on dynamic rules.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
|
@ -27,7 +32,7 @@
|
|||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Generating Hostnames"
|
||||
"Manipulate existing Hostnames"
|
||||
],
|
||||
"summary": "Update hostname for category \"notebook\"",
|
||||
"operationId": "update-notebook-hostname",
|
||||
|
@ -52,6 +57,11 @@
|
|||
}
|
||||
},
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Generates a hostname for a notebook based on dynamic rules.",
|
||||
"consumes": [
|
||||
"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": {
|
||||
"get": {
|
||||
"security": [
|
||||
|
@ -368,6 +482,29 @@
|
|||
"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": {
|
||||
|
|
|
@ -58,6 +58,21 @@ definitions:
|
|||
OrgUnit:
|
||||
type: string
|
||||
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
|
||||
info:
|
||||
contact:
|
||||
|
@ -66,8 +81,8 @@ info:
|
|||
url: http://git.beisel.it/florian
|
||||
description: This is a sample server for a hostname service.
|
||||
license:
|
||||
name: MIT
|
||||
url: http://git.beisel.it/florian/hostname-service/
|
||||
name: Apache 2.0
|
||||
url: http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
title: Hostname Service API
|
||||
version: "1"
|
||||
paths:
|
||||
|
@ -168,6 +183,8 @@ paths:
|
|||
description: Hostname
|
||||
schema:
|
||||
$ref: '#/definitions/models.SimpleHostnameResponse'
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Generate hostname for category "notebook"
|
||||
tags:
|
||||
- Generating Hostnames
|
||||
|
@ -190,9 +207,77 @@ paths:
|
|||
description: Hostname
|
||||
schema:
|
||||
$ref: '#/definitions/models.SimpleHostnameResponse'
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Update hostname for category "notebook"
|
||||
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
|
||||
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:
|
||||
get:
|
||||
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
|
||||
$loginUrl = "http://localhost:8080/api/v1/login"
|
||||
$generateNotebookUrl = "http://localhost:8080/api/v1/notebook"
|
||||
|
|
51
main.go
51
main.go
|
@ -19,15 +19,9 @@ package main
|
|||
import (
|
||||
"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/db"
|
||||
"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"
|
||||
"git.beisel.it/florian/hostname-service/router"
|
||||
)
|
||||
|
||||
// @title Hostname Service API
|
||||
|
@ -54,48 +48,9 @@ func main() {
|
|||
log.Fatalf("Failed to load configuration: %v", err)
|
||||
}
|
||||
|
||||
gin.SetMode(gin.DebugMode)
|
||||
|
||||
db.Init(config.GlobalConfig.DatabaseFile)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// Swagger endpoint
|
||||
router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
||||
|
||||
router := router.New()
|
||||
router.Run(":8080")
|
||||
|
||||
}
|
||||
|
|
|
@ -23,7 +23,8 @@ func Authenticate() gin.HandlerFunc {
|
|||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method")
|
||||
}
|
||||
return config.GlobalConfig.JwtKey, nil
|
||||
jwtKeyBytes := []byte(config.GlobalConfig.JwtKey)
|
||||
return jwtKeyBytes, 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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
// 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
|
||||
|
||||
// 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 {
|
||||
Generate(params map[string]interface{}) (string, []byte, error)
|
||||
Insert(category string, hostname string, paramsJSON []byte) error
|
||||
Update(category string, oldhostname string, hostname string, paramsJSON []byte) error
|
||||
Insert(category string, params map[string]interface{}) (string, error)
|
||||
Update(category string, oldhostname string, params map[string]interface{}) (string, error)
|
||||
}
|
||||
|
||||
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)
|
||||
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 {
|
||||
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)
|
||||
if err != nil {
|
||||
log.Printf("Error inserting hostname into DB: %v", 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)
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = db.UpdateHostname(category, oldhostname, hostname, paramsJSON)
|
||||
if err != nil {
|
||||
log.Printf("Error inserting hostname into DB: %v", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return hostname, nil
|
||||
}
|
||||
|
||||
// baseUpdate method for BaseRule
|
||||
func (br *BaseRule) baseUpdate(rule HostnameRule, category string, oldhostname string, params map[string]interface{}) (string, error) {
|
||||
// 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"
|
||||
// @Success 200 {object} models.SimpleHostnameResponse "Hostname"
|
||||
// @Router /api/notebook [post]
|
||||
func (nr *NotebookRule) Insert(category string, hostname string, paramsJSON []byte) error {
|
||||
return nr.baseInsert(category, hostname, paramsJSON)
|
||||
// @Security Bearer
|
||||
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"
|
||||
|
@ -84,10 +87,11 @@ func (nr *NotebookRule) Insert(category string, hostname string, paramsJSON []by
|
|||
// @ID update-notebook-hostname
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags Generating Hostnames
|
||||
// @Tags Manipulate existing Hostnames
|
||||
// @Param body body NotebookRuleInput true "Input data to generate hostname"
|
||||
// @Success 200 {object} models.SimpleHostnameResponse "Hostname"
|
||||
// @Router /api/notebook [put]
|
||||
func (nr *NotebookRule) Update(category string, oldhostname string, hostname string, paramsJSON []byte) error {
|
||||
return nr.baseUpdate(category, oldhostname, hostname, paramsJSON)
|
||||
// @Security Bearer
|
||||
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