feat: restart to apply update, privileged helper, rollback
This commit is contained in:
1 parent
70460e4170
commit
54e8fd5fed
63 files changed
+8635
-164
No files matched your search
@@ -3,26 +3,37 @@ package update
|
||||
import (
|
||||
"RenewCMS/api/pkg/apperr"
|
||||
"RenewCMS/api/pkg/claims"
|
||||
validator "RenewCMS/api/pkg/validator"
|
||||
domain "RenewCMS/internal/domain/update"
|
||||
"RenewCMS/internal/domain/user"
|
||||
"RenewCMS/internal/infrastructure/useCases"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const restartBodyLimitBytes = 1024
|
||||
|
||||
type Handler struct {
|
||||
GetUpdateStatusUseCase *useCases.GetUpdateStatusUseCase
|
||||
CheckForUpdateUseCase *useCases.CheckForUpdateUseCase
|
||||
StartUpdateDownloadUseCase *useCases.StartUpdateDownloadUseCase
|
||||
RequestRestartUseCase *useCases.RequestRestartUseCase
|
||||
GetUserUseCase *useCases.GetUserUseCase
|
||||
}
|
||||
|
||||
func NewHandler(getStatus *useCases.GetUpdateStatusUseCase, checkForUpdate *useCases.CheckForUpdateUseCase, startDownload *useCases.StartUpdateDownloadUseCase, getUser *useCases.GetUserUseCase) *Handler {
|
||||
type restartRequest struct {
|
||||
Kind *string `json:"kind" validate:"omitempty,oneof=update restart"`
|
||||
}
|
||||
|
||||
func NewHandler(getStatus *useCases.GetUpdateStatusUseCase, checkForUpdate *useCases.CheckForUpdateUseCase, startDownload *useCases.StartUpdateDownloadUseCase, requestRestart *useCases.RequestRestartUseCase, getUser *useCases.GetUserUseCase) *Handler {
|
||||
return &Handler{
|
||||
GetUpdateStatusUseCase: getStatus,
|
||||
CheckForUpdateUseCase: checkForUpdate,
|
||||
StartUpdateDownloadUseCase: startDownload,
|
||||
RequestRestartUseCase: requestRestart,
|
||||
GetUserUseCase: getUser,
|
||||
}
|
||||
}
|
||||
@@ -56,6 +67,44 @@ func (h *Handler) Download(w http.ResponseWriter, _ *http.Request) {
|
||||
writeStatus(w, http.StatusAccepted, status)
|
||||
}
|
||||
|
||||
func (h *Handler) Restart(w http.ResponseWriter, r *http.Request) {
|
||||
kind, ok := decodeRestartKind(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
userId, ok := claims.UserId(r)
|
||||
if !ok {
|
||||
http.Error(w, apperr.UnauthorizedErrorMessage, http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
status, err := h.RequestRestartUseCase.Request(userId, kind)
|
||||
if err != nil {
|
||||
writeRestartError(w, err)
|
||||
return
|
||||
}
|
||||
writeStatus(w, http.StatusAccepted, status)
|
||||
}
|
||||
|
||||
func decodeRestartKind(w http.ResponseWriter, r *http.Request) (string, bool) {
|
||||
body, err := io.ReadAll(http.MaxBytesReader(w, r.Body, restartBodyLimitBytes))
|
||||
if err != nil {
|
||||
http.Error(w, apperr.BodyErrorMessage, http.StatusBadRequest)
|
||||
return "", false
|
||||
}
|
||||
if strings.TrimSpace(string(body)) == "" {
|
||||
return "", true
|
||||
}
|
||||
var request restartRequest
|
||||
if err := json.Unmarshal(body, &request); err != nil || validator.Validate.Struct(request) != nil {
|
||||
http.Error(w, apperr.BodyErrorMessage, http.StatusBadRequest)
|
||||
return "", false
|
||||
}
|
||||
if request.Kind == nil {
|
||||
return "", true
|
||||
}
|
||||
return *request.Kind, true
|
||||
}
|
||||
|
||||
func (h *Handler) isOwner(w http.ResponseWriter, r *http.Request) (bool, bool) {
|
||||
userId, ok := claims.UserId(r)
|
||||
if !ok {
|
||||
@@ -78,7 +127,7 @@ func writeDownloadError(w http.ResponseWriter, err error) {
|
||||
switch {
|
||||
case errors.Is(err, domain.ErrNotPrepared):
|
||||
http.Error(w, apperr.UpdateNotPreparedErrorMessage, http.StatusConflict)
|
||||
case errors.Is(err, domain.ErrJobInProgress):
|
||||
case errors.Is(err, domain.ErrJobInProgress), errors.Is(err, domain.ErrRestartPending):
|
||||
http.Error(w, apperr.UpdateAlreadyRunningErrorMessage, http.StatusConflict)
|
||||
case errors.Is(err, domain.ErrNothingToUpdate):
|
||||
http.Error(w, apperr.NothingToUpdateErrorMessage, http.StatusBadRequest)
|
||||
@@ -89,6 +138,19 @@ func writeDownloadError(w http.ResponseWriter, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
func writeRestartError(w http.ResponseWriter, err error) {
|
||||
switch {
|
||||
case errors.Is(err, domain.ErrNotPrepared):
|
||||
http.Error(w, apperr.UpdateNotPreparedErrorMessage, http.StatusConflict)
|
||||
case errors.Is(err, domain.ErrJobInProgress), errors.Is(err, domain.ErrRestartPending):
|
||||
http.Error(w, apperr.UpdateAlreadyRunningErrorMessage, http.StatusConflict)
|
||||
case errors.Is(err, domain.ErrRestartKindMismatch):
|
||||
http.Error(w, apperr.UpdateRestartMismatchErrorMessage, http.StatusConflict)
|
||||
default:
|
||||
http.Error(w, "Internal server error", http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func writeStatus(w http.ResponseWriter, code int, status domain.UpdateStatus) {
|
||||
body, err := json.Marshal(status)
|
||||
if err != nil {
|
||||
|
||||
Reference in new issue
Block a user