feature: Change state management from global variables to object passing
This restructures dbcore (now the db package) and jwtcore (now the jwt package) to use a single struct. There is now a state package, which contains a struct with the full application state. After this, instead of initializing the API routes directly in the main function, the state object gets passed, and the API routes get initialized with their accompanying code. One fix done to reduce memory usage and increase speed is that the validator object is now persistent across requests, instead of recreating it each time. This should speed things up slightly, and improve memory usage. One additional chore done is that the database models have been moved to be a seperate file from the DB initialization itself.
This commit is contained in:
parent
71d53990de
commit
d56a8eb7bf
23 changed files with 1901 additions and 2161 deletions
|
@ -7,13 +7,12 @@ import (
|
|||
"net/http"
|
||||
|
||||
"git.terah.dev/imterah/hermes/backend/api/backendruntime"
|
||||
"git.terah.dev/imterah/hermes/backend/api/dbcore"
|
||||
"git.terah.dev/imterah/hermes/backend/api/jwtcore"
|
||||
"git.terah.dev/imterah/hermes/backend/api/db"
|
||||
"git.terah.dev/imterah/hermes/backend/api/permissions"
|
||||
"git.terah.dev/imterah/hermes/backend/api/state"
|
||||
"git.terah.dev/imterah/hermes/backend/commonbackend"
|
||||
"github.com/charmbracelet/log"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
type BackendCreationRequest struct {
|
||||
|
@ -24,131 +23,114 @@ type BackendCreationRequest struct {
|
|||
BackendParameters interface{} `json:"connectionDetails" validate:"required"`
|
||||
}
|
||||
|
||||
func CreateBackend(c *gin.Context) {
|
||||
var req BackendCreationRequest
|
||||
func SetupCreateBackend(state *state.State) {
|
||||
state.Engine.POST("/api/v1/backends/create", func(c *gin.Context) {
|
||||
var req BackendCreationRequest
|
||||
|
||||
if err := c.BindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": fmt.Sprintf("Failed to parse body: %s", err.Error()),
|
||||
})
|
||||
if err := c.BindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": fmt.Sprintf("Failed to parse body: %s", err.Error()),
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err := validator.New().Struct(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": fmt.Sprintf("Failed to validate body: %s", err.Error()),
|
||||
})
|
||||
if err := state.Validator.Struct(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": fmt.Sprintf("Failed to validate body: %s", err.Error()),
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
user, err := jwtcore.GetUserFromJWT(req.Token)
|
||||
user, err := state.JWT.GetUserFromJWT(req.Token)
|
||||
|
||||
if err != nil {
|
||||
if err.Error() == "token is expired" || err.Error() == "user does not exist" {
|
||||
if err != nil {
|
||||
if err.Error() == "token is expired" || err.Error() == "user does not exist" {
|
||||
c.JSON(http.StatusForbidden, gin.H{
|
||||
"error": err.Error(),
|
||||
})
|
||||
|
||||
return
|
||||
} else {
|
||||
log.Warnf("Failed to get user from the provided JWT token: %s", err.Error())
|
||||
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to parse token",
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if !permissions.UserHasPermission(user, "backends.add") {
|
||||
c.JSON(http.StatusForbidden, gin.H{
|
||||
"error": err.Error(),
|
||||
})
|
||||
|
||||
return
|
||||
} else {
|
||||
log.Warnf("Failed to get user from the provided JWT token: %s", err.Error())
|
||||
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to parse token",
|
||||
"error": "Missing permissions",
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if !permissions.UserHasPermission(user, "backends.add") {
|
||||
c.JSON(http.StatusForbidden, gin.H{
|
||||
"error": "Missing permissions",
|
||||
})
|
||||
var backendParameters []byte
|
||||
|
||||
return
|
||||
}
|
||||
switch parameters := req.BackendParameters.(type) {
|
||||
case string:
|
||||
backendParameters = []byte(parameters)
|
||||
case map[string]interface{}:
|
||||
backendParameters, err = json.Marshal(parameters)
|
||||
|
||||
var backendParameters []byte
|
||||
if err != nil {
|
||||
log.Warnf("Failed to marshal JSON recieved as BackendParameters: %s", err.Error())
|
||||
|
||||
switch parameters := req.BackendParameters.(type) {
|
||||
case string:
|
||||
backendParameters = []byte(parameters)
|
||||
case map[string]interface{}:
|
||||
backendParameters, err = json.Marshal(parameters)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to prepare parameters",
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
default:
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": fmt.Sprintf("Invalid type for connectionDetails (recieved %T)", parameters),
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var backendRuntimeFilePath string
|
||||
|
||||
for _, runtime := range backendruntime.AvailableBackends {
|
||||
if runtime.Name == req.Backend {
|
||||
backendRuntimeFilePath = runtime.Path
|
||||
}
|
||||
}
|
||||
|
||||
if backendRuntimeFilePath == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "Unsupported backend recieved",
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
backend := backendruntime.NewBackend(backendRuntimeFilePath)
|
||||
err = backend.Start()
|
||||
|
||||
if err != nil {
|
||||
log.Warnf("Failed to marshal JSON recieved as BackendParameters: %s", err.Error())
|
||||
log.Warnf("Failed to start backend: %s", err.Error())
|
||||
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to prepare parameters",
|
||||
"error": "Failed to start backend",
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
default:
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": fmt.Sprintf("Invalid type for connectionDetails (recieved %T)", parameters),
|
||||
|
||||
backendParamCheckResponse, err := backend.ProcessCommand(&commonbackend.CheckServerParameters{
|
||||
Arguments: backendParameters,
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var backendRuntimeFilePath string
|
||||
|
||||
for _, runtime := range backendruntime.AvailableBackends {
|
||||
if runtime.Name == req.Backend {
|
||||
backendRuntimeFilePath = runtime.Path
|
||||
}
|
||||
}
|
||||
|
||||
if backendRuntimeFilePath == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "Unsupported backend recieved",
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
backend := backendruntime.NewBackend(backendRuntimeFilePath)
|
||||
err = backend.Start()
|
||||
|
||||
if err != nil {
|
||||
log.Warnf("Failed to start backend: %s", err.Error())
|
||||
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to start backend",
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
backendParamCheckResponse, err := backend.ProcessCommand(&commonbackend.CheckServerParameters{
|
||||
Arguments: backendParameters,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Warnf("Failed to get response for backend: %s", err.Error())
|
||||
|
||||
err = backend.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Warnf("Failed to stop backend: %s", err.Error())
|
||||
}
|
||||
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to get status response from backend",
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
switch responseMessage := backendParamCheckResponse.(type) {
|
||||
case *commonbackend.CheckParametersResponse:
|
||||
if responseMessage.InResponseTo != "checkServerParameters" {
|
||||
log.Errorf("Got illegal response to CheckServerParameters: %s", responseMessage.InResponseTo)
|
||||
log.Warnf("Failed to get response for backend: %s", err.Error())
|
||||
|
||||
err = backend.Stop()
|
||||
|
||||
|
@ -163,107 +145,126 @@ func CreateBackend(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if !responseMessage.IsValid {
|
||||
switch responseMessage := backendParamCheckResponse.(type) {
|
||||
case *commonbackend.CheckParametersResponse:
|
||||
if responseMessage.InResponseTo != "checkServerParameters" {
|
||||
log.Errorf("Got illegal response to CheckServerParameters: %s", responseMessage.InResponseTo)
|
||||
|
||||
err = backend.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Warnf("Failed to stop backend: %s", err.Error())
|
||||
}
|
||||
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to get status response from backend",
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if !responseMessage.IsValid {
|
||||
err = backend.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Warnf("Failed to stop backend: %s", err.Error())
|
||||
}
|
||||
|
||||
var errorMessage string
|
||||
|
||||
if responseMessage.Message == "" {
|
||||
errorMessage = "Unkown error while trying to parse connectionDetails"
|
||||
} else {
|
||||
errorMessage = fmt.Sprintf("Invalid backend parameters: %s", responseMessage.Message)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errorMessage,
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
default:
|
||||
log.Warnf("Got illegal response type for backend: %T", responseMessage)
|
||||
}
|
||||
|
||||
log.Info("Passed backend checks successfully")
|
||||
|
||||
backendInDatabase := &db.Backend{
|
||||
UserID: user.ID,
|
||||
Name: req.Name,
|
||||
Description: req.Description,
|
||||
Backend: req.Backend,
|
||||
BackendParameters: base64.StdEncoding.EncodeToString(backendParameters),
|
||||
}
|
||||
|
||||
if result := state.DB.DB.Create(&backendInDatabase); result.Error != nil {
|
||||
log.Warnf("Failed to create backend: %s", result.Error.Error())
|
||||
|
||||
err = backend.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Warnf("Failed to stop backend: %s", err.Error())
|
||||
}
|
||||
|
||||
var errorMessage string
|
||||
|
||||
if responseMessage.Message == "" {
|
||||
errorMessage = "Unkown error while trying to parse connectionDetails"
|
||||
} else {
|
||||
errorMessage = fmt.Sprintf("Invalid backend parameters: %s", responseMessage.Message)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errorMessage,
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to add backend into database",
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
default:
|
||||
log.Warnf("Got illegal response type for backend: %T", responseMessage)
|
||||
}
|
||||
|
||||
log.Info("Passed backend checks successfully")
|
||||
|
||||
backendInDatabase := &dbcore.Backend{
|
||||
UserID: user.ID,
|
||||
Name: req.Name,
|
||||
Description: req.Description,
|
||||
Backend: req.Backend,
|
||||
BackendParameters: base64.StdEncoding.EncodeToString(backendParameters),
|
||||
}
|
||||
|
||||
if result := dbcore.DB.Create(&backendInDatabase); result.Error != nil {
|
||||
log.Warnf("Failed to create backend: %s", result.Error.Error())
|
||||
|
||||
err = backend.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Warnf("Failed to stop backend: %s", err.Error())
|
||||
}
|
||||
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to add backend into database",
|
||||
backendStartResponse, err := backend.ProcessCommand(&commonbackend.Start{
|
||||
Arguments: backendParameters,
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
backendStartResponse, err := backend.ProcessCommand(&commonbackend.Start{
|
||||
Arguments: backendParameters,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Warnf("Failed to get response for backend: %s", err.Error())
|
||||
|
||||
err = backend.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Warnf("Failed to stop backend: %s", err.Error())
|
||||
}
|
||||
log.Warnf("Failed to get response for backend: %s", err.Error())
|
||||
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to get status response from backend",
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
switch responseMessage := backendStartResponse.(type) {
|
||||
case *commonbackend.BackendStatusResponse:
|
||||
if !responseMessage.IsRunning {
|
||||
err = backend.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Warnf("Failed to start backend: %s", err.Error())
|
||||
log.Warnf("Failed to stop backend: %s", err.Error())
|
||||
}
|
||||
|
||||
var errorMessage string
|
||||
|
||||
if responseMessage.Message == "" {
|
||||
errorMessage = "Unkown error while trying to start the backend"
|
||||
} else {
|
||||
errorMessage = fmt.Sprintf("Failed to start backend: %s", responseMessage.Message)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errorMessage,
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to get status response from backend",
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
default:
|
||||
log.Warnf("Got illegal response type for backend: %T", responseMessage)
|
||||
}
|
||||
|
||||
backendruntime.RunningBackends[backendInDatabase.ID] = backend
|
||||
switch responseMessage := backendStartResponse.(type) {
|
||||
case *commonbackend.BackendStatusResponse:
|
||||
if !responseMessage.IsRunning {
|
||||
err = backend.Stop()
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
if err != nil {
|
||||
log.Warnf("Failed to start backend: %s", err.Error())
|
||||
}
|
||||
|
||||
var errorMessage string
|
||||
|
||||
if responseMessage.Message == "" {
|
||||
errorMessage = "Unkown error while trying to start the backend"
|
||||
} else {
|
||||
errorMessage = fmt.Sprintf("Failed to start backend: %s", responseMessage.Message)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": errorMessage,
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
default:
|
||||
log.Warnf("Got illegal response type for backend: %T", responseMessage)
|
||||
}
|
||||
|
||||
backendruntime.RunningBackends[backendInDatabase.ID] = backend
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -7,12 +7,11 @@ import (
|
|||
"strings"
|
||||
|
||||
"git.terah.dev/imterah/hermes/backend/api/backendruntime"
|
||||
"git.terah.dev/imterah/hermes/backend/api/dbcore"
|
||||
"git.terah.dev/imterah/hermes/backend/api/jwtcore"
|
||||
"git.terah.dev/imterah/hermes/backend/api/db"
|
||||
"git.terah.dev/imterah/hermes/backend/api/permissions"
|
||||
"git.terah.dev/imterah/hermes/backend/api/state"
|
||||
"github.com/charmbracelet/log"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
type BackendLookupRequest struct {
|
||||
|
@ -38,95 +37,80 @@ type LookupResponse struct {
|
|||
Data []*SanitizedBackend `json:"data"`
|
||||
}
|
||||
|
||||
func LookupBackend(c *gin.Context) {
|
||||
var req BackendLookupRequest
|
||||
func SetupLookupBackend(state *state.State) {
|
||||
state.Engine.POST("/api/v1/backends/lookup", func(c *gin.Context) {
|
||||
var req BackendLookupRequest
|
||||
|
||||
if err := c.BindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": fmt.Sprintf("Failed to parse body: %s", err.Error()),
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if err := validator.New().Struct(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": fmt.Sprintf("Failed to validate body: %s", err.Error()),
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
user, err := jwtcore.GetUserFromJWT(req.Token)
|
||||
|
||||
if err != nil {
|
||||
if err.Error() == "token is expired" || err.Error() == "user does not exist" {
|
||||
c.JSON(http.StatusForbidden, gin.H{
|
||||
"error": err.Error(),
|
||||
})
|
||||
|
||||
return
|
||||
} else {
|
||||
log.Warnf("Failed to get user from the provided JWT token: %s", err.Error())
|
||||
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to parse token",
|
||||
if err := c.BindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": fmt.Sprintf("Failed to parse body: %s", err.Error()),
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if !permissions.UserHasPermission(user, "backends.visible") {
|
||||
c.JSON(http.StatusForbidden, gin.H{
|
||||
"error": "Missing permissions",
|
||||
})
|
||||
if err := state.Validator.Struct(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": fmt.Sprintf("Failed to validate body: %s", err.Error()),
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
backends := []dbcore.Backend{}
|
||||
queryString := []string{}
|
||||
queryParameters := []interface{}{}
|
||||
user, err := state.JWT.GetUserFromJWT(req.Token)
|
||||
|
||||
if req.BackendID != nil {
|
||||
queryString = append(queryString, "id = ?")
|
||||
queryParameters = append(queryParameters, req.BackendID)
|
||||
}
|
||||
if err != nil {
|
||||
if err.Error() == "token is expired" || err.Error() == "user does not exist" {
|
||||
c.JSON(http.StatusForbidden, gin.H{
|
||||
"error": err.Error(),
|
||||
})
|
||||
|
||||
if req.Name != nil {
|
||||
queryString = append(queryString, "name = ?")
|
||||
queryParameters = append(queryParameters, req.Name)
|
||||
}
|
||||
return
|
||||
} else {
|
||||
log.Warnf("Failed to get user from the provided JWT token: %s", err.Error())
|
||||
|
||||
if req.Description != nil {
|
||||
queryString = append(queryString, "description = ?")
|
||||
queryParameters = append(queryParameters, req.Description)
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to parse token",
|
||||
})
|
||||
|
||||
if req.Backend != nil {
|
||||
queryString = append(queryString, "is_bot = ?")
|
||||
queryParameters = append(queryParameters, req.Backend)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := dbcore.DB.Where(strings.Join(queryString, " AND "), queryParameters...).Find(&backends).Error; err != nil {
|
||||
log.Warnf("Failed to get backends: %s", err.Error())
|
||||
if !permissions.UserHasPermission(user, "backends.visible") {
|
||||
c.JSON(http.StatusForbidden, gin.H{
|
||||
"error": "Missing permissions",
|
||||
})
|
||||
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to get backends",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
backends := []db.Backend{}
|
||||
queryString := []string{}
|
||||
queryParameters := []interface{}{}
|
||||
|
||||
sanitizedBackends := make([]*SanitizedBackend, len(backends))
|
||||
hasSecretVisibility := permissions.UserHasPermission(user, "backends.secretVis")
|
||||
if req.BackendID != nil {
|
||||
queryString = append(queryString, "id = ?")
|
||||
queryParameters = append(queryParameters, req.BackendID)
|
||||
}
|
||||
|
||||
for backendIndex, backend := range backends {
|
||||
foundBackend, ok := backendruntime.RunningBackends[backend.ID]
|
||||
if req.Name != nil {
|
||||
queryString = append(queryString, "name = ?")
|
||||
queryParameters = append(queryParameters, req.Name)
|
||||
}
|
||||
|
||||
if !ok {
|
||||
log.Warnf("Failed to get backend #%d controller", backend.ID)
|
||||
if req.Description != nil {
|
||||
queryString = append(queryString, "description = ?")
|
||||
queryParameters = append(queryParameters, req.Description)
|
||||
}
|
||||
|
||||
if req.Backend != nil {
|
||||
queryString = append(queryString, "is_bot = ?")
|
||||
queryParameters = append(queryParameters, req.Backend)
|
||||
}
|
||||
|
||||
if err := state.DB.DB.Where(strings.Join(queryString, " AND "), queryParameters...).Find(&backends).Error; err != nil {
|
||||
log.Warnf("Failed to get backends: %s", err.Error())
|
||||
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to get backends",
|
||||
|
@ -135,29 +119,46 @@ func LookupBackend(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
sanitizedBackends[backendIndex] = &SanitizedBackend{
|
||||
BackendID: backend.ID,
|
||||
OwnerID: backend.UserID,
|
||||
Name: backend.Name,
|
||||
Description: backend.Description,
|
||||
Backend: backend.Backend,
|
||||
Logs: foundBackend.Logs,
|
||||
}
|
||||
sanitizedBackends := make([]*SanitizedBackend, len(backends))
|
||||
hasSecretVisibility := permissions.UserHasPermission(user, "backends.secretVis")
|
||||
|
||||
if backend.UserID == user.ID || hasSecretVisibility {
|
||||
backendParametersBytes, err := base64.StdEncoding.DecodeString(backend.BackendParameters)
|
||||
for backendIndex, backend := range backends {
|
||||
foundBackend, ok := backendruntime.RunningBackends[backend.ID]
|
||||
|
||||
if err != nil {
|
||||
log.Warnf("Failed to decode base64 backend parameters: %s", err.Error())
|
||||
if !ok {
|
||||
log.Warnf("Failed to get backend #%d controller", backend.ID)
|
||||
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to get backends",
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
backendParameters := string(backendParametersBytes)
|
||||
sanitizedBackends[backendIndex].BackendParameters = &backendParameters
|
||||
}
|
||||
}
|
||||
sanitizedBackends[backendIndex] = &SanitizedBackend{
|
||||
BackendID: backend.ID,
|
||||
OwnerID: backend.UserID,
|
||||
Name: backend.Name,
|
||||
Description: backend.Description,
|
||||
Backend: backend.Backend,
|
||||
Logs: foundBackend.Logs,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, &LookupResponse{
|
||||
Success: true,
|
||||
Data: sanitizedBackends,
|
||||
if backend.UserID == user.ID || hasSecretVisibility {
|
||||
backendParametersBytes, err := base64.StdEncoding.DecodeString(backend.BackendParameters)
|
||||
|
||||
if err != nil {
|
||||
log.Warnf("Failed to decode base64 backend parameters: %s", err.Error())
|
||||
}
|
||||
|
||||
backendParameters := string(backendParametersBytes)
|
||||
sanitizedBackends[backendIndex].BackendParameters = &backendParameters
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, &LookupResponse{
|
||||
Success: true,
|
||||
Data: sanitizedBackends,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -5,12 +5,11 @@ import (
|
|||
"net/http"
|
||||
|
||||
"git.terah.dev/imterah/hermes/backend/api/backendruntime"
|
||||
"git.terah.dev/imterah/hermes/backend/api/dbcore"
|
||||
"git.terah.dev/imterah/hermes/backend/api/jwtcore"
|
||||
"git.terah.dev/imterah/hermes/backend/api/db"
|
||||
"git.terah.dev/imterah/hermes/backend/api/permissions"
|
||||
"git.terah.dev/imterah/hermes/backend/api/state"
|
||||
"github.com/charmbracelet/log"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
type BackendRemovalRequest struct {
|
||||
|
@ -18,106 +17,108 @@ type BackendRemovalRequest struct {
|
|||
BackendID uint `json:"id" validate:"required"`
|
||||
}
|
||||
|
||||
func RemoveBackend(c *gin.Context) {
|
||||
var req BackendRemovalRequest
|
||||
func SetupRemoveBackend(state *state.State) {
|
||||
state.Engine.POST("/api/v1/backends/remove", func(c *gin.Context) {
|
||||
var req BackendRemovalRequest
|
||||
|
||||
if err := c.BindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": fmt.Sprintf("Failed to parse body: %s", err.Error()),
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if err := validator.New().Struct(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": fmt.Sprintf("Failed to validate body: %s", err.Error()),
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
user, err := jwtcore.GetUserFromJWT(req.Token)
|
||||
|
||||
if err != nil {
|
||||
if err.Error() == "token is expired" || err.Error() == "user does not exist" {
|
||||
c.JSON(http.StatusForbidden, gin.H{
|
||||
"error": err.Error(),
|
||||
})
|
||||
|
||||
return
|
||||
} else {
|
||||
log.Warnf("Failed to get user from the provided JWT token: %s", err.Error())
|
||||
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to parse token",
|
||||
if err := c.BindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": fmt.Sprintf("Failed to parse body: %s", err.Error()),
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if !permissions.UserHasPermission(user, "backends.remove") {
|
||||
c.JSON(http.StatusForbidden, gin.H{
|
||||
"error": "Missing permissions",
|
||||
})
|
||||
if err := state.Validator.Struct(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": fmt.Sprintf("Failed to validate body: %s", err.Error()),
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var backend *dbcore.Backend
|
||||
backendRequest := dbcore.DB.Where("id = ?", req.BackendID).Find(&backend)
|
||||
|
||||
if backendRequest.Error != nil {
|
||||
log.Warnf("failed to find if backend exists or not: %s", backendRequest.Error.Error())
|
||||
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to find if backend exists",
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
backendExists := backendRequest.RowsAffected > 0
|
||||
|
||||
if !backendExists {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Backend doesn't exist",
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if err := dbcore.DB.Delete(backend).Error; err != nil {
|
||||
log.Warnf("failed to delete backend: %s", err.Error())
|
||||
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to delete backend",
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
backendInstance, ok := backendruntime.RunningBackends[req.BackendID]
|
||||
|
||||
if ok {
|
||||
err = backendInstance.Stop()
|
||||
user, err := state.JWT.GetUserFromJWT(req.Token)
|
||||
|
||||
if err != nil {
|
||||
log.Warnf("Failed to stop backend: %s", err.Error())
|
||||
if err.Error() == "token is expired" || err.Error() == "user does not exist" {
|
||||
c.JSON(http.StatusForbidden, gin.H{
|
||||
"error": err.Error(),
|
||||
})
|
||||
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Backend deleted, but failed to stop",
|
||||
return
|
||||
} else {
|
||||
log.Warnf("Failed to get user from the provided JWT token: %s", err.Error())
|
||||
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to parse token",
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if !permissions.UserHasPermission(user, "backends.remove") {
|
||||
c.JSON(http.StatusForbidden, gin.H{
|
||||
"error": "Missing permissions",
|
||||
})
|
||||
|
||||
delete(backendruntime.RunningBackends, req.BackendID)
|
||||
return
|
||||
}
|
||||
|
||||
delete(backendruntime.RunningBackends, req.BackendID)
|
||||
}
|
||||
var backend *db.Backend
|
||||
backendRequest := state.DB.DB.Where("id = ?", req.BackendID).Find(&backend)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
if backendRequest.Error != nil {
|
||||
log.Warnf("failed to find if backend exists or not: %s", backendRequest.Error.Error())
|
||||
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to find if backend exists",
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
backendExists := backendRequest.RowsAffected > 0
|
||||
|
||||
if !backendExists {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Backend doesn't exist",
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if err := state.DB.DB.Delete(backend).Error; err != nil {
|
||||
log.Warnf("failed to delete backend: %s", err.Error())
|
||||
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Failed to delete backend",
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
backendInstance, ok := backendruntime.RunningBackends[req.BackendID]
|
||||
|
||||
if ok {
|
||||
err = backendInstance.Stop()
|
||||
|
||||
if err != nil {
|
||||
log.Warnf("Failed to stop backend: %s", err.Error())
|
||||
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "Backend deleted, but failed to stop",
|
||||
})
|
||||
|
||||
delete(backendruntime.RunningBackends, req.BackendID)
|
||||
return
|
||||
}
|
||||
|
||||
delete(backendruntime.RunningBackends, req.BackendID)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue