Initial commit
This commit is contained in:
commit
b58430af9a
|
@ -0,0 +1,158 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"git.beisel.it/florian/hostname-service/db"
|
||||
"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")
|
||||
}
|
||||
}
|
||||
|
||||
// generateHostname 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
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"hostname": hostname})
|
||||
}
|
||||
|
||||
// @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 {json} json "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
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"hostname": hostname})
|
||||
}
|
||||
|
||||
// @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 {json} json "Hostname"
|
||||
// @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"
|
||||
// @Security Bearer
|
||||
// @Success 200 {json} json "Hostname"
|
||||
// @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,21 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.beisel.it/florian/hostname-service/config"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
)
|
||||
|
||||
func GenerateToken(username string) (string, error) {
|
||||
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.JwtKey)
|
||||
|
||||
return tokenString, err
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"net/http"
|
||||
|
||||
"git.beisel.it/florian/hostname-service/db"
|
||||
"git.beisel.it/florian/hostname-service/models"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// 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} map[string]string "Successfully authenticated, JWT token returned"
|
||||
// @Failure 400 {object} map[string]string "Invalid request body"
|
||||
// @Failure 401 {object} map[string]string "Invalid login credentials"
|
||||
// @Failure 500 {object} map[string]string "Internal server error"
|
||||
// @Router /login [post]
|
||||
func LoginHandler(c *gin.Context) {
|
||||
var creds models.LoginCredentials
|
||||
|
||||
// Bind JSON to creds
|
||||
if err := c.BindJSON(&creds); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
|
||||
return
|
||||
}
|
||||
|
||||
// Fetch user from the database
|
||||
var storedCreds models.User
|
||||
err := db.DB.QueryRow("SELECT username, password FROM users WHERE username = ?", creds.Username).Scan(&storedCreds.Username, &storedCreds.Password)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
// User not found
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid login credentials"})
|
||||
return
|
||||
}
|
||||
// Other errors
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal server error"})
|
||||
return
|
||||
}
|
||||
|
||||
// Compare provided password with stored hashed password
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(storedCreds.Password), []byte(creds.Password)); err != nil {
|
||||
// Password does not match
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid login credentials"})
|
||||
return
|
||||
}
|
||||
|
||||
// If password matches, generate a JWT token
|
||||
token, err := GenerateToken(storedCreds.Username)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate token"})
|
||||
return
|
||||
}
|
||||
|
||||
// Send the token in the response
|
||||
c.JSON(http.StatusOK, gin.H{"token": token})
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
package config
|
||||
|
||||
var JwtKey = []byte("your_secret_key")
|
|
@ -0,0 +1,192 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
|
||||
"git.beisel.it/florian/hostname-service/models"
|
||||
"github.com/golang-migrate/migrate/v4"
|
||||
_ "github.com/golang-migrate/migrate/v4/database/sqlite3"
|
||||
_ "github.com/golang-migrate/migrate/v4/source/file"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
var DB *sql.DB
|
||||
|
||||
// Initialize the database and create tables if they don't exist
|
||||
func Init() {
|
||||
var err error
|
||||
DB, err = sql.Open("sqlite3", "hostname-service.db")
|
||||
if err != nil {
|
||||
log.Fatalf("Error opening database: %v", err)
|
||||
}
|
||||
|
||||
m, err := migrate.New(
|
||||
"file://db/migrations/",
|
||||
"sqlite3://hostname-service.db",
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Migration initialization failed: %v", err)
|
||||
}
|
||||
|
||||
// Apply all up migrations
|
||||
if err := m.Up(); err != nil && err != migrate.ErrNoChange {
|
||||
log.Fatalf("Migration up failed: %v", err)
|
||||
}
|
||||
|
||||
// Check if users table is empty
|
||||
var userCount int
|
||||
err = DB.QueryRow("SELECT COUNT(*) FROM users").Scan(&userCount)
|
||||
if err != nil {
|
||||
log.Fatalf("Error checking users table: %v", err)
|
||||
}
|
||||
|
||||
// If there are no users, create a default admin user
|
||||
if userCount == 0 {
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte("defaultPassword"), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
log.Fatalf("Error hashing password: %v", err)
|
||||
}
|
||||
|
||||
_, err = DB.Exec("INSERT INTO users (username, password) VALUES (?, ?)", "admin", string(hashedPassword))
|
||||
if err != nil {
|
||||
log.Fatalf("Error creating default admin user: %v", err)
|
||||
}
|
||||
|
||||
log.Println("Default admin user created")
|
||||
}
|
||||
|
||||
log.Println("Database migrations applied successfully")
|
||||
}
|
||||
|
||||
func CreateUser(user *models.User) error {
|
||||
statement, err := DB.Prepare("INSERT INTO users(username, password) VALUES (?, ?)")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = statement.Exec(user.Username, user.Password)
|
||||
return err
|
||||
}
|
||||
|
||||
func InsertHostname(category string, hostname string, paramsJSON []byte) error {
|
||||
_, err := DB.Exec("INSERT INTO hostnames (category, hostname, parameters) VALUES (?, ?, ?)", category, hostname, paramsJSON)
|
||||
if err != nil {
|
||||
log.Printf("Error inserting hostname into DB: %v", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateHostname(category string, oldhostname string, hostname string, paramsJSON []byte) error {
|
||||
_, err := DB.Exec("UPDATE hostnames set category = ?, hostname =?, parameters =? where category = ? and hostname = ?", category, hostname, paramsJSON, category, oldhostname)
|
||||
if err != nil {
|
||||
log.Printf("Error inserting hostname into DB: %v", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeleteHostname(category string, hostname string) error {
|
||||
log.Printf("Soft-Deleting hostname: %v in category: %v", category, hostname)
|
||||
_, err := DB.Exec("UPDATE hostnames set deleted = true where category = ? and hostname = ?", category, hostname)
|
||||
if err != nil {
|
||||
log.Printf("Error deleting hostname from DB: %v", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HostnameExists checks if a hostname exists within a given category
|
||||
func HostnameExists(category string, hostname string) (bool, error) {
|
||||
var exists bool
|
||||
|
||||
query := "SELECT EXISTS(SELECT 1 FROM hostnames WHERE category = ? AND hostname = ?)"
|
||||
err := DB.QueryRow(query, category, hostname).Scan(&exists)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
// No rows found, meaning the hostname does not exist
|
||||
return false, nil
|
||||
}
|
||||
// An actual error occurred
|
||||
return false, err
|
||||
}
|
||||
|
||||
return exists, nil
|
||||
}
|
||||
|
||||
func GetMaxNumberForCategory(category string) (int, error) {
|
||||
var maxResult sql.NullInt64
|
||||
err := DB.QueryRow("SELECT MAX(CAST(json_extract(parameters, '$.Number') AS INTEGER)) FROM hostnames WHERE category = ?", category).Scan(&maxResult)
|
||||
if err != nil {
|
||||
log.Printf("Error querying max number for category %s: %v", category, err)
|
||||
return 0, err
|
||||
}
|
||||
if !maxResult.Valid {
|
||||
return 0, nil // No rows found, start with 0
|
||||
}
|
||||
return int(maxResult.Int64), nil
|
||||
}
|
||||
|
||||
func GetHostnamesByCategory(category string) ([]models.Hostname, error) {
|
||||
var hostnames []models.Hostname
|
||||
|
||||
rows, err := DB.Query("SELECT id, category, hostname, parameters, created_at FROM hostnames WHERE category = ? and deleted = false", category)
|
||||
if err != nil {
|
||||
log.Printf("Error querying hostnames: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var h models.Hostname
|
||||
var paramsJSON string
|
||||
err := rows.Scan(&h.ID, &h.Category, &h.Hostname, ¶msJSON, &h.CreatedAt)
|
||||
if err != nil {
|
||||
log.Printf("Error scanning hostname: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
// Unmarshal parameters JSON
|
||||
err = json.Unmarshal([]byte(paramsJSON), &h.Parameters)
|
||||
if err != nil {
|
||||
log.Printf("Error unmarshaling parameters: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
hostnames = append(hostnames, h)
|
||||
}
|
||||
|
||||
return hostnames, nil
|
||||
}
|
||||
|
||||
func GetHostnameByCategoryAndName(category string, hostname string) (models.Hostname, error) {
|
||||
var host models.Hostname
|
||||
var paramsJSON string
|
||||
|
||||
row, err := DB.Query("SELECT id, category, hostname, parameters, created_at FROM hostnames where category = ? and hostname = ? and deleted = false", category, hostname)
|
||||
if err != nil {
|
||||
log.Printf("Error querying hostname: %v", err)
|
||||
return models.Hostname{}, err
|
||||
}
|
||||
defer row.Close()
|
||||
|
||||
if !row.Next() {
|
||||
log.Printf("no rows found for category %s and hostname %s", category, hostname)
|
||||
return models.Hostname{}, errors.New("no rows found")
|
||||
}
|
||||
|
||||
err = row.Scan(&host.ID, &host.Category, &host.Hostname, ¶msJSON, &host.CreatedAt)
|
||||
if err != nil {
|
||||
log.Printf("Error scanning hostname: %v", err)
|
||||
return models.Hostname{}, err
|
||||
}
|
||||
err = json.Unmarshal([]byte(paramsJSON), &host.Parameters)
|
||||
if err != nil {
|
||||
log.Printf("Error unmarshaling parameters: %v", err)
|
||||
return models.Hostname{}, nil
|
||||
}
|
||||
|
||||
return host, nil
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
DROP TABLE IF EXISTS users;
|
|
@ -0,0 +1,5 @@
|
|||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT NOT NULL UNIQUE,
|
||||
password TEXT NOT NULL
|
||||
);
|
|
@ -0,0 +1 @@
|
|||
DROP TABLE IF EXISTS hostnames;
|
|
@ -0,0 +1,7 @@
|
|||
CREATE TABLE hostnames (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
category TEXT NOT NULL,
|
||||
hostname TEXT NOT NULL,
|
||||
parameters JSON,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE hostnames
|
||||
DROP COLUMN deleted;
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE hostnames
|
||||
ADD COLUMN deleted BOOLEAN DEFAULT FALSE;
|
|
@ -0,0 +1,356 @@
|
|||
// Package docs Code generated by swaggo/swag. DO NOT EDIT
|
||||
package docs
|
||||
|
||||
import "github.com/swaggo/swag"
|
||||
|
||||
const docTemplate = `{
|
||||
"schemes": {{ marshal .Schemes }},
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "{{escape .Description}}",
|
||||
"title": "{{.Title}}",
|
||||
"contact": {
|
||||
"name": "Florian Beisel",
|
||||
"url": "http://git.beisel.it/florian",
|
||||
"email": "florian@beisel.it"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "http://git.beisel.it/florian/hostname-service/"
|
||||
},
|
||||
"version": "{{.Version}}"
|
||||
},
|
||||
"host": "{{.Host}}",
|
||||
"basePath": "{{.BasePath}}",
|
||||
"paths": {
|
||||
"/api/notebook": {
|
||||
"put": {
|
||||
"description": "Generates a new hostname for a notebook based on dynamic rules.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Generating Hostnames"
|
||||
],
|
||||
"summary": "Update hostname for category \"notebook\"",
|
||||
"operationId": "update-notebook-hostname",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Input data to generate hostname",
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rules.NotebookRuleInput"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Hostname",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"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-notebook-hostname",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Input data to generate hostname",
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rules.NotebookRuleInput"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Hostname",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/hello": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Checks whether the user is successfully authenticated",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Authentication"
|
||||
],
|
||||
"summary": "Check your authentication",
|
||||
"operationId": "hello",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/login": {
|
||||
"post": {
|
||||
"description": "Authenticate user and return JWT token",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Authentication"
|
||||
],
|
||||
"summary": "User login",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Login Credentials",
|
||||
"name": "loginCredentials",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/models.LoginCredentials"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successfully authenticated, JWT token returned",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid request body",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Invalid login credentials",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/{category}": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "List all details for a given category",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Querying Hostnames"
|
||||
],
|
||||
"summary": "Return a list of hosts and their details filtered by category",
|
||||
"operationId": "list-hostnames-by-category",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Category of the hostname",
|
||||
"name": "category",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Hostname",
|
||||
"schema": {
|
||||
"type": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/{category}/{hostname}": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Return details for a single hostname identified by its category",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Querying Hostnames"
|
||||
],
|
||||
"summary": "Return a single hostname by Category and Name",
|
||||
"operationId": "get-hostname-by-category-and-name",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Category of the hostname",
|
||||
"name": "category",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Category of the hostname",
|
||||
"name": "hostname",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Hostname",
|
||||
"schema": {
|
||||
"type": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "List all details for a given category",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Manipulate existing Hostnames"
|
||||
],
|
||||
"summary": "Delete a hostname from the database",
|
||||
"operationId": "delete-hostnames-by-category-and-name",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Category of the hostname",
|
||||
"name": "category",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Hostname to delete",
|
||||
"name": "hostname",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Hostname",
|
||||
"schema": {
|
||||
"type": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"models.LoginCredentials": {
|
||||
"description": "User account information used in the login process with Username and password",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"password": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"rules.NotebookRuleInput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Location": {
|
||||
"type": "string"
|
||||
},
|
||||
"OrgUnit": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
"Bearer": {
|
||||
"description": "Type \"Bearer\" followed by a space and JWT token.",
|
||||
"type": "apiKey",
|
||||
"name": "Authorization",
|
||||
"in": "header"
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
// SwaggerInfo holds exported Swagger Info so clients can modify it
|
||||
var SwaggerInfo = &swag.Spec{
|
||||
Version: "1",
|
||||
Host: "localhost:8080",
|
||||
BasePath: "/api/v1",
|
||||
Schemes: []string{},
|
||||
Title: "Hostname Service API",
|
||||
Description: "This is a sample server for a hostname service.",
|
||||
InfoInstanceName: "swagger",
|
||||
SwaggerTemplate: docTemplate,
|
||||
LeftDelim: "{{",
|
||||
RightDelim: "}}",
|
||||
}
|
||||
|
||||
func init() {
|
||||
swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo)
|
||||
}
|
|
@ -0,0 +1,332 @@
|
|||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"description": "This is a sample server for a hostname service.",
|
||||
"title": "Hostname Service API",
|
||||
"contact": {
|
||||
"name": "Florian Beisel",
|
||||
"url": "http://git.beisel.it/florian",
|
||||
"email": "florian@beisel.it"
|
||||
},
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "http://git.beisel.it/florian/hostname-service/"
|
||||
},
|
||||
"version": "1"
|
||||
},
|
||||
"host": "localhost:8080",
|
||||
"basePath": "/api/v1",
|
||||
"paths": {
|
||||
"/api/notebook": {
|
||||
"put": {
|
||||
"description": "Generates a new hostname for a notebook based on dynamic rules.",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Generating Hostnames"
|
||||
],
|
||||
"summary": "Update hostname for category \"notebook\"",
|
||||
"operationId": "update-notebook-hostname",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Input data to generate hostname",
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rules.NotebookRuleInput"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Hostname",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"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-notebook-hostname",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Input data to generate hostname",
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/rules.NotebookRuleInput"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Hostname",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/hello": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Checks whether the user is successfully authenticated",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Authentication"
|
||||
],
|
||||
"summary": "Check your authentication",
|
||||
"operationId": "hello",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/login": {
|
||||
"post": {
|
||||
"description": "Authenticate user and return JWT token",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Authentication"
|
||||
],
|
||||
"summary": "User login",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Login Credentials",
|
||||
"name": "loginCredentials",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/models.LoginCredentials"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successfully authenticated, JWT token returned",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid request body",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Invalid login credentials",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/{category}": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "List all details for a given category",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Querying Hostnames"
|
||||
],
|
||||
"summary": "Return a list of hosts and their details filtered by category",
|
||||
"operationId": "list-hostnames-by-category",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Category of the hostname",
|
||||
"name": "category",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Hostname",
|
||||
"schema": {
|
||||
"type": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/{category}/{hostname}": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Return details for a single hostname identified by its category",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Querying Hostnames"
|
||||
],
|
||||
"summary": "Return a single hostname by Category and Name",
|
||||
"operationId": "get-hostname-by-category-and-name",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Category of the hostname",
|
||||
"name": "category",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Category of the hostname",
|
||||
"name": "hostname",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Hostname",
|
||||
"schema": {
|
||||
"type": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "List all details for a given category",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Manipulate existing Hostnames"
|
||||
],
|
||||
"summary": "Delete a hostname from the database",
|
||||
"operationId": "delete-hostnames-by-category-and-name",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Category of the hostname",
|
||||
"name": "category",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Hostname to delete",
|
||||
"name": "hostname",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Hostname",
|
||||
"schema": {
|
||||
"type": "json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"models.LoginCredentials": {
|
||||
"description": "User account information used in the login process with Username and password",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"password": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"rules.NotebookRuleInput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Location": {
|
||||
"type": "string"
|
||||
},
|
||||
"OrgUnit": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"securityDefinitions": {
|
||||
"Bearer": {
|
||||
"description": "Type \"Bearer\" followed by a space and JWT token.",
|
||||
"type": "apiKey",
|
||||
"name": "Authorization",
|
||||
"in": "header"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,218 @@
|
|||
basePath: /api/v1
|
||||
definitions:
|
||||
models.LoginCredentials:
|
||||
description: User account information used in the login process with Username
|
||||
and password
|
||||
properties:
|
||||
password:
|
||||
type: string
|
||||
username:
|
||||
type: string
|
||||
type: object
|
||||
rules.NotebookRuleInput:
|
||||
properties:
|
||||
Location:
|
||||
type: string
|
||||
OrgUnit:
|
||||
type: string
|
||||
type: object
|
||||
host: localhost:8080
|
||||
info:
|
||||
contact:
|
||||
email: florian@beisel.it
|
||||
name: Florian Beisel
|
||||
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/
|
||||
title: Hostname Service API
|
||||
version: "1"
|
||||
paths:
|
||||
/{category}:
|
||||
get:
|
||||
description: List all details for a given category
|
||||
operationId: list-hostnames-by-category
|
||||
parameters:
|
||||
- description: Category of the hostname
|
||||
in: path
|
||||
name: category
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Hostname
|
||||
schema:
|
||||
type: json
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Return a list of hosts and their details filtered by category
|
||||
tags:
|
||||
- Querying Hostnames
|
||||
/{category}/{hostname}:
|
||||
delete:
|
||||
description: List all details for a given category
|
||||
operationId: delete-hostnames-by-category-and-name
|
||||
parameters:
|
||||
- description: Category of the hostname
|
||||
in: path
|
||||
name: category
|
||||
required: true
|
||||
type: string
|
||||
- description: Hostname to delete
|
||||
in: path
|
||||
name: hostname
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Hostname
|
||||
schema:
|
||||
type: json
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Delete a hostname from the database
|
||||
tags:
|
||||
- Manipulate existing Hostnames
|
||||
get:
|
||||
description: Return details for a single hostname identified by its category
|
||||
operationId: get-hostname-by-category-and-name
|
||||
parameters:
|
||||
- description: Category of the hostname
|
||||
in: path
|
||||
name: category
|
||||
required: true
|
||||
type: string
|
||||
- description: Category of the hostname
|
||||
in: path
|
||||
name: hostname
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Hostname
|
||||
schema:
|
||||
type: json
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Return a single hostname by Category and Name
|
||||
tags:
|
||||
- Querying Hostnames
|
||||
/api/notebook:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Generates a hostname for a notebook based on dynamic rules.
|
||||
operationId: insert-notebook-hostname
|
||||
parameters:
|
||||
- description: Input data to generate hostname
|
||||
in: body
|
||||
name: body
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/rules.NotebookRuleInput'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Hostname
|
||||
schema:
|
||||
type: string
|
||||
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-notebook-hostname
|
||||
parameters:
|
||||
- description: Input data to generate hostname
|
||||
in: body
|
||||
name: body
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/rules.NotebookRuleInput'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Hostname
|
||||
schema:
|
||||
type: string
|
||||
summary: Update hostname for category "notebook"
|
||||
tags:
|
||||
- Generating Hostnames
|
||||
/hello:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Checks whether the user is successfully authenticated
|
||||
operationId: hello
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
type: string
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Check your authentication
|
||||
tags:
|
||||
- Authentication
|
||||
/login:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Authenticate user and return JWT token
|
||||
parameters:
|
||||
- description: Login Credentials
|
||||
in: body
|
||||
name: loginCredentials
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/models.LoginCredentials'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: Successfully authenticated, JWT token returned
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
"400":
|
||||
description: Invalid request body
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
"401":
|
||||
description: Invalid login credentials
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
"500":
|
||||
description: Internal server error
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
summary: User login
|
||||
tags:
|
||||
- Authentication
|
||||
securityDefinitions:
|
||||
Bearer:
|
||||
description: Type "Bearer" followed by a space and JWT token.
|
||||
in: header
|
||||
name: Authorization
|
||||
type: apiKey
|
||||
swagger: "2.0"
|
|
@ -0,0 +1,54 @@
|
|||
# Define API URLs
|
||||
$loginUrl = "http://localhost:8080/api/v1/login"
|
||||
$generateNotebookUrl = "http://localhost:8080/api/v1/notebook"
|
||||
$docsUrl = "http://localhost:8080/swagger/doc.json" # URL to fetch the docs.json
|
||||
|
||||
# User credentials
|
||||
$username = "admin"
|
||||
$password = "defaultPassword" # Replace with the actual password
|
||||
|
||||
# Prepare login request body
|
||||
$loginBody = @{
|
||||
username = $username
|
||||
password = $password
|
||||
} | ConvertTo-Json
|
||||
|
||||
# Send login request
|
||||
$response = Invoke-RestMethod -Method Post -Uri $loginUrl -Body $loginBody -ContentType "application/json"
|
||||
|
||||
# Extract token from login response
|
||||
$token = $response.token
|
||||
|
||||
# Check if we got a token
|
||||
if (-not $token) {
|
||||
Write-Error "Authentication failed"
|
||||
exit
|
||||
}
|
||||
|
||||
# Prepare the request header with the received token
|
||||
$headers = @{
|
||||
Authorization = "Bearer $token"
|
||||
}
|
||||
|
||||
# Fetch the docs.json
|
||||
$docs = Invoke-RestMethod -Uri $docsUrl
|
||||
|
||||
# Extract NotebookRuleInput model
|
||||
$notebookRuleInputModel = $docs.definitions."rules.NotebookRuleInput".properties
|
||||
|
||||
# Create newNotebookParams based on the NotebookRuleInput model
|
||||
$newNotebookParams = @{}
|
||||
foreach ($prop in $notebookRuleInputModel.PSObject.Properties) {
|
||||
$userInput = Read-Host -Prompt "Enter value for $($prop.Name)"
|
||||
$newNotebookParams[$prop.Name] = $userInput
|
||||
}
|
||||
|
||||
# Convert newNotebookParams to JSON
|
||||
$newNotebookParamsJson = $newNotebookParams | ConvertTo-Json
|
||||
|
||||
# Send request to generate a new notebook
|
||||
$newNotebookResponse = Invoke-RestMethod -Method Post -Uri $generateNotebookUrl -Headers $headers -Body $newNotebookParamsJson -ContentType "application/json"
|
||||
|
||||
# Output the response
|
||||
Write-Output "New Notebook Response:"
|
||||
$newNotebookResponse
|
|
@ -0,0 +1,54 @@
|
|||
module git.beisel.it/florian/hostname-service
|
||||
|
||||
go 1.21.1
|
||||
|
||||
require (
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
github.com/swaggo/swag v1.16.2
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.20.2 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.4 // indirect
|
||||
github.com/go-openapi/spec v0.20.14 // indirect
|
||||
github.com/go-openapi/swag v0.22.7 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
golang.org/x/tools v0.17.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.9.1 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.14.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/golang-migrate/migrate/v4 v4.17.0
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||
github.com/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.19
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
||||
github.com/swaggo/files v1.0.1
|
||||
github.com/swaggo/gin-swagger v1.6.0
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/crypto v0.18.0 // indirect
|
||||
golang.org/x/net v0.20.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
|
@ -0,0 +1,162 @@
|
|||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
||||
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
|
||||
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||
github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q=
|
||||
github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs=
|
||||
github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU=
|
||||
github.com/go-openapi/jsonreference v0.20.4/go.mod h1:5pZJyJP2MnYCpoeoMAql78cCHauHj0V9Lhc506VOpw4=
|
||||
github.com/go-openapi/spec v0.20.14 h1:7CBlRnw+mtjFGlPDRZmAMnq35cRzI91xj03HVyUi/Do=
|
||||
github.com/go-openapi/spec v0.20.14/go.mod h1:8EOhTpBoFiask8rrgwbLC3zmJfz4zsCUueRuPM6GNkw=
|
||||
github.com/go-openapi/swag v0.22.7 h1:JWrc1uc/P9cSomxfnsFSVWoE1FW6bNbrVPmpQYpCcR8=
|
||||
github.com/go-openapi/swag v0.22.7/go.mod h1:Gl91UqO+btAM0plGGxHqJcQZ1ZTy6jbmridBTsDy8A0=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
|
||||
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/golang-migrate/migrate/v4 v4.17.0 h1:rd40H3QXU0AA4IoLllFcEAEo9dYKRHYND2gB4p7xcaU=
|
||||
github.com/golang-migrate/migrate/v4 v4.17.0/go.mod h1:+Cp2mtLP4/aXDTKb9wmXYitdrNx2HGs45rbWAo6OsKM=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI=
|
||||
github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
|
||||
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
|
||||
github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
|
||||
github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M=
|
||||
github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo=
|
||||
github.com/swaggo/swag v1.16.2 h1:28Pp+8DkQoV+HLzLx8RGJZXNGKbFqnuvSbAAtoxiY04=
|
||||
github.com/swaggo/swag v1.16.2/go.mod h1:6YzXnDcpr0767iOejs318CwYkCQqyGer6BizOg03f+E=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
|
||||
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
Binary file not shown.
|
@ -0,0 +1,73 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"git.beisel.it/florian/hostname-service/api"
|
||||
"git.beisel.it/florian/hostname-service/auth"
|
||||
"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"
|
||||
)
|
||||
|
||||
// @title Hostname Service API
|
||||
// @description This is a sample server for a hostname service.
|
||||
// @version 1
|
||||
// @contact.name Florian Beisel
|
||||
// @contact.url http://git.beisel.it/florian
|
||||
// @contact.email florian@beisel.it
|
||||
// @license.name MIT
|
||||
// @license.url http://git.beisel.it/florian/hostname-service/
|
||||
|
||||
// @host localhost:8080
|
||||
// @BasePath /api/v1
|
||||
|
||||
// @securityDefinitions.apikey Bearer
|
||||
// @in header
|
||||
// @name Authorization
|
||||
// @description Type "Bearer" followed by a space and JWT token.
|
||||
|
||||
func main() {
|
||||
db.Init()
|
||||
|
||||
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.Run(":8080")
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"git.beisel.it/florian/hostname-service/config"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func Authenticate() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
const Bearer_schema = "Bearer "
|
||||
header := c.GetHeader("Authorization")
|
||||
if header == "" {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "No token provided"})
|
||||
return
|
||||
}
|
||||
|
||||
tokenString := header[len(Bearer_schema):]
|
||||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method")
|
||||
}
|
||||
return config.JwtKey, nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid token: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
|
||||
// Extract the username from the MapClaims
|
||||
if username, ok := claims["sub"].(string); ok {
|
||||
c.Set("username", username)
|
||||
} else {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid token claims"})
|
||||
return
|
||||
}
|
||||
} else {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
// Hostname Model
|
||||
// @Description Model of the Hostname as it
|
||||
// @Description is represented in the database
|
||||
type Hostname struct {
|
||||
ID int `json:"id"` // Internal ID of the Hostname within the database
|
||||
Category string `json:"category"` // Category / Rule that was used when generating the hostname
|
||||
Hostname string `json:"hostname"` // Generated hostname
|
||||
Parameters map[string]interface{} `json:"parameters"` // Parameter object of rule specific attributes
|
||||
CreatedAt time.Time `json:"created_at"` // Creation Time of the entry
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package models
|
||||
|
||||
// User Credentials Model
|
||||
// @Description User account information used in the login process
|
||||
// @Description with Username and password
|
||||
type LoginCredentials struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package models
|
||||
|
||||
// User Model
|
||||
// @Description User account information
|
||||
// @Description with user id and username
|
||||
type User struct {
|
||||
ID int // Database ID for the User
|
||||
Username string // Username
|
||||
Password string // hashed password
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package rules
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"git.beisel.it/florian/hostname-service/db"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
type BaseRule struct{}
|
||||
|
||||
func (br *BaseRule) baseInsert(category string, hostname string, paramsJSON []byte) error {
|
||||
exists, err := db.HostnameExists(category, hostname)
|
||||
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", hostname, category)
|
||||
}
|
||||
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, hostname)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error checking existence of hostname: %v", err.Error())
|
||||
}
|
||||
if !exists {
|
||||
return fmt.Errorf("hostname %s does not exist in category %s", oldhostname, category)
|
||||
}
|
||||
|
||||
err = db.UpdateHostname(category, oldhostname, hostname, paramsJSON)
|
||||
if err != nil {
|
||||
log.Printf("Error inserting hostname into DB: %v", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
package rules
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"git.beisel.it/florian/hostname-service/db"
|
||||
// other imports if necessary
|
||||
)
|
||||
|
||||
type NotebookRule struct {
|
||||
BaseRule
|
||||
OrgUnit string `json:"OrgUnit"`
|
||||
Location string `json:"Location"`
|
||||
Number int `json:"Number"`
|
||||
}
|
||||
|
||||
type NotebookRuleInput struct {
|
||||
OrgUnit string `json:"OrgUnit"`
|
||||
Location string `json:"Location"`
|
||||
}
|
||||
|
||||
// Ensure that NotebookRule implements HostnameRule interface
|
||||
var _ HostnameRule = &NotebookRule{}
|
||||
|
||||
func (nr *NotebookRule) Generate(params map[string]interface{}) (string, []byte, error) {
|
||||
var ok bool
|
||||
|
||||
// Get the orgUnit (IDE)
|
||||
if nr.OrgUnit, ok = params["OrgUnit"].(string); !ok {
|
||||
return "", nil, errors.New("OrgUnit parameter is required and must be a string")
|
||||
}
|
||||
|
||||
// Get the location (HEN)
|
||||
if nr.Location, ok = params["Location"].(string); !ok {
|
||||
return "", nil, errors.New("location parameter is required and must be a string")
|
||||
}
|
||||
|
||||
// Get last used number from the database and increment it
|
||||
maxNumber, err := db.GetMaxNumberForCategory("notebook")
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
newNumber := 1
|
||||
if maxNumber >= 1 {
|
||||
newNumber = int(maxNumber) + 1
|
||||
}
|
||||
|
||||
nr.Number = newNumber
|
||||
|
||||
// Generate the hostname (orgUnit + Location + "NB" + Number)
|
||||
hostname := fmt.Sprintf("%s%sNB%04d", nr.OrgUnit, nr.Location, nr.Number)
|
||||
|
||||
// Store the generated hostname in the database
|
||||
// JSON parameters can be stored by marshalling the params map
|
||||
paramsJSON, err := json.Marshal(nr)
|
||||
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-notebook-hostname
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags Generating Hostnames
|
||||
// @Param body body NotebookRuleInput true "Input data to generate hostname"
|
||||
// @Success 200 {string} string "Hostname"
|
||||
// @Router /api/notebook [post]
|
||||
func (nr *NotebookRule) Insert(category string, hostname string, paramsJSON []byte) error {
|
||||
return nr.baseInsert(category, hostname, paramsJSON)
|
||||
}
|
||||
|
||||
// @Summary Update hostname for category "notebook"
|
||||
// @Description Generates a new hostname for a notebook based on dynamic rules.
|
||||
// @ID update-notebook-hostname
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags Generating Hostnames
|
||||
// @Param body body NotebookRuleInput true "Input data to generate hostname"
|
||||
// @Success 200 {string} string "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)
|
||||
}
|
Loading…
Reference in New Issue