From 471ef4fdd897723f1d48dcb000ed9475a5fee72e Mon Sep 17 00:00:00 2001 From: James Tombleson Date: Tue, 7 May 2024 19:14:37 -0700 Subject: [PATCH] adding userId to the jwt and updated routes to use scope requirements --- internal/domain/requests.go | 5 + internal/domain/scopes.go | 12 +- internal/handler/v1/articles.go | 9 +- internal/handler/v1/auth.go | 10 +- internal/handler/v1/discordwebhooks.go | 37 +++- internal/handler/v1/handler.go | 26 ++- internal/handler/v1/jwt.go | 12 +- internal/handler/v1/sources.go | 190 ++++++++++---------- internal/respositoryServices/userService.go | 2 +- 9 files changed, 176 insertions(+), 127 deletions(-) diff --git a/internal/domain/requests.go b/internal/domain/requests.go index 4be9e81..557e3b5 100644 --- a/internal/domain/requests.go +++ b/internal/domain/requests.go @@ -1,5 +1,10 @@ package domain +type LoginFormRequest struct { + Username string `form:"username"` + Password string `form:"password"` +} + type GetSourceBySourceAndNameParamRequest struct { Name string `query:"name"` Source string `query:"source"` diff --git a/internal/domain/scopes.go b/internal/domain/scopes.go index 23f4bbf..15e9dc8 100644 --- a/internal/domain/scopes.go +++ b/internal/domain/scopes.go @@ -1,8 +1,14 @@ package domain const ( - ScopeAll = "newsbot:all" - ScopeArticleRead = "newsbot:article:read" - ScopeSourceCreate = "newsbot:source:create" + ScopeAll = "newsbot:all" + + ScopeArticleRead = "newsbot:article:read" + ScopeArticleDisable = "newsbot:article:disable" + + ScopeSourceRead = "newsbot:source:read" + ScopeSourceCreate = "newsbot:source:create" + ScopeDiscordWebHookCreate = "newsbot:discordwebhook:create" + ScopeDiscordWebhookRead = "newsbot:discordwebhook:read" ) diff --git a/internal/handler/v1/articles.go b/internal/handler/v1/articles.go index 21ff796..870180a 100644 --- a/internal/handler/v1/articles.go +++ b/internal/handler/v1/articles.go @@ -20,6 +20,8 @@ import ( // @Failure 500 {object} domain.BaseResponse // @Security Bearer func (s *Handler) listArticles(c echo.Context) error { + s.ValidateJwtToken(c, domain.ScopeArticleRead) + resp := domain.ArticleResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, @@ -51,6 +53,7 @@ func (s *Handler) listArticles(c echo.Context) error { // @Failure 500 {object} domain.BaseResponse // @Security Bearer func (s *Handler) getArticle(c echo.Context) error { + s.ValidateJwtToken(c, domain.ScopeArticleRead) p := domain.ArticleResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, @@ -86,13 +89,12 @@ func (s *Handler) getArticle(c echo.Context) error { // @Failure 500 {object} domain.BaseResponse // @Security Bearer func (s *Handler) getArticleDetails(c echo.Context) error { + s.ValidateJwtToken(c, domain.ScopeArticleRead) p := domain.ArticleDetailedResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, }, - Payload: domain.ArticleAndSourceModel{ - - }, + Payload: domain.ArticleAndSourceModel{}, } id, err := strconv.Atoi(c.Param("ID")) @@ -128,6 +130,7 @@ func (s *Handler) getArticleDetails(c echo.Context) error { // @Failure 500 {object} domain.BaseResponse // @Security Bearer func (s *Handler) ListArticlesBySourceId(c echo.Context) error { + s.ValidateJwtToken(c, domain.ScopeArticleRead) p := domain.ArticleResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, diff --git a/internal/handler/v1/auth.go b/internal/handler/v1/auth.go index 458c320..bfd8496 100644 --- a/internal/handler/v1/auth.go +++ b/internal/handler/v1/auth.go @@ -90,7 +90,7 @@ func (h *Handler) AuthLogin(c echo.Context) error { // TODO think about moving this down some? expiresAt := time.Now().Add(time.Hour * 48) - jwt, err := h.generateJwtWithExp(username, user.Scopes, h.config.ServerAddress, expiresAt) + jwt, err := h.generateJwtWithExp(username, user.Scopes, h.config.ServerAddress, user.ID, expiresAt) if err != nil { return h.InternalServerErrorResponse(c, err.Error()) } @@ -121,7 +121,7 @@ func (h *Handler) createAdminToken(c echo.Context, password string) error { return h.UnauthorizedResponse(c, ErrUserNotFound) } - token, err := h.generateJwt("admin", domain.ScopeAll, h.config.ServerAddress) + token, err := h.generateJwt("admin", domain.ScopeAll, h.config.ServerAddress, -1) if err != nil { return h.InternalServerErrorResponse(c, err.Error()) } @@ -130,8 +130,8 @@ func (h *Handler) createAdminToken(c echo.Context, password string) error { BaseResponse: domain.BaseResponse{ Message: "OK", }, - Token: token, - Type: "Bearer", + Token: token, + Type: "Bearer", }) } @@ -163,7 +163,7 @@ func (h *Handler) RefreshJwtToken(c echo.Context) error { return h.InternalServerErrorResponse(c, err.Error()) } - jwt, err := h.generateJwtWithExp(request.Username, user.Scopes, h.config.ServerAddress, time.Now().Add(time.Hour*48)) + jwt, err := h.generateJwtWithExp(request.Username, user.Scopes, h.config.ServerAddress, user.ID, time.Now().Add(time.Hour*48)) if err != nil { return h.InternalServerErrorResponse(c, err.Error()) } diff --git a/internal/handler/v1/discordwebhooks.go b/internal/handler/v1/discordwebhooks.go index 65fd3ee..7e2e6f6 100644 --- a/internal/handler/v1/discordwebhooks.go +++ b/internal/handler/v1/discordwebhooks.go @@ -18,7 +18,9 @@ import ( // @Success 200 {object} domain.DiscordWebhookResponse // @Failure 400 {object} domain.BaseResponse // @Failure 500 {object} domain.BaseResponse +// @Security Bearer func (s *Handler) ListDiscordWebHooks(c echo.Context) error { + s.ValidateJwtToken(c, domain.ScopeDiscordWebhookRead) p := domain.DiscordWebhookResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, @@ -42,7 +44,9 @@ func (s *Handler) ListDiscordWebHooks(c echo.Context) error { // @Success 200 {object} domain.DiscordWebhookResponse "OK" // @Failure 400 {object} domain.BaseResponse // @Failure 500 {object} domain.BaseResponse +// @Security Bearer func (s *Handler) GetDiscordWebHooksById(c echo.Context) error { + s.ValidateJwtToken(c, domain.ScopeDiscordWebhookRead) p := domain.DiscordWebhookResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, @@ -74,7 +78,9 @@ func (s *Handler) GetDiscordWebHooksById(c echo.Context) error { // @Success 200 {object} domain.DiscordWebhookResponse "OK" // @Failure 400 {object} domain.BaseResponse // @Failure 500 {object} domain.BaseResponse +// @Security Bearer func (s *Handler) GetDiscordWebHooksByServerAndChannel(c echo.Context) error { + s.ValidateJwtToken(c, domain.ScopeDiscordWebhookRead) p := domain.DiscordWebhookResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, @@ -110,13 +116,9 @@ func (s *Handler) GetDiscordWebHooksByServerAndChannel(c echo.Context) error { // @Success 200 {object} domain.DiscordWebhookResponse "OK" // @Failure 400 {object} domain.BaseResponse // @Failure 500 {object} domain.BaseResponse +// @Security Bearer func (s *Handler) NewDiscordWebHook(c echo.Context) error { - token, err := s.getJwtToken(c) - if err != nil { - return c.JSON(http.StatusUnauthorized, domain.BaseResponse{ - Message: ErrJwtMissing, - }) - } + token := s.ValidateJwtToken(c, domain.ScopeDiscordWebHookCreate) _url := c.QueryParam("url") _server := c.QueryParam("server") @@ -181,7 +183,9 @@ func (s *Handler) NewDiscordWebHook(c echo.Context) error { // @Success 200 {object} domain.DiscordWebhookResponse "OK" // @Failure 400 {object} domain.BaseResponse // @Failure 500 {object} domain.BaseResponse +// @Security Bearer func (s *Handler) disableDiscordWebHook(c echo.Context) error { + s.ValidateJwtToken(c, domain.ScopeDiscordWebHookCreate) id, err := strconv.Atoi(c.Param("ID")) if err != nil { return c.JSON(http.StatusBadRequest, domain.BaseResponse{ @@ -190,11 +194,15 @@ func (s *Handler) disableDiscordWebHook(c echo.Context) error { } // Check to make sure we can find the record - _, err = s.repo.DiscordWebHooks.GetById(c.Request().Context(), int64(id)) + record, err := s.repo.DiscordWebHooks.GetById(c.Request().Context(), int64(id)) if err != nil { s.WriteError(c, err, http.StatusInternalServerError) } + if record.UserID != s.GetUserIdFromJwtToken(c) { + s.WriteMessage(c, ErrYouDontOwnTheRecord, http.StatusBadRequest) + } + // flip the it updated, err := s.repo.DiscordWebHooks.Disable(c.Request().Context(), int64(id)) if err != nil { @@ -226,18 +234,24 @@ func (s *Handler) disableDiscordWebHook(c echo.Context) error { // @Param id path int true "id" // @Tags DiscordWebhook // @Router /v1/discord/webhooks/{ID}/enable [post] +// @Security Bearer func (s *Handler) enableDiscordWebHook(c echo.Context) error { + s.ValidateJwtToken(c, domain.ScopeDiscordWebHookCreate) id, err := strconv.Atoi(c.Param("ID")) if err != nil { s.WriteError(c, err, http.StatusBadRequest) } // Check to make sure we can find the record - _, err = s.repo.DiscordWebHooks.GetById(c.Request().Context(), int64(id)) + record, err := s.repo.DiscordWebHooks.GetById(c.Request().Context(), int64(id)) if err != nil { s.WriteError(c, err, http.StatusBadRequest) } + if record.UserID != s.GetUserIdFromJwtToken(c) { + s.WriteMessage(c, ErrYouDontOwnTheRecord, http.StatusBadRequest) + } + updated, err := s.repo.DiscordWebHooks.Enable(c.Request().Context(), int64(id)) if err != nil { s.WriteError(c, err, http.StatusInternalServerError) @@ -271,17 +285,22 @@ func (s *Handler) enableDiscordWebHook(c echo.Context) error { // @Failure 400 {object} domain.BaseResponse // @Failure 500 {object} domain.BaseResponse func (s *Handler) deleteDiscordWebHook(c echo.Context) error { + s.ValidateJwtToken(c, domain.ScopeDiscordWebHookCreate) id, err := strconv.Atoi(c.Param("ID")) if err != nil { return c.JSON(http.StatusBadRequest, err.Error()) } // Check to make sure we can find the record - _, err = s.repo.DiscordWebHooks.GetById(c.Request().Context(), int64(id)) + record, err := s.repo.DiscordWebHooks.GetById(c.Request().Context(), int64(id)) if err != nil { return c.JSON(http.StatusInternalServerError, err.Error()) } + if record.UserID != s.GetUserIdFromJwtToken(c) { + s.WriteMessage(c, ErrYouDontOwnTheRecord, http.StatusBadRequest) + } + // Soft delete the record updated, err := s.repo.DiscordWebHooks.SoftDelete(c.Request().Context(), int64(id)) if err != nil { diff --git a/internal/handler/v1/handler.go b/internal/handler/v1/handler.go index 56e0da7..d8369f0 100644 --- a/internal/handler/v1/handler.go +++ b/internal/handler/v1/handler.go @@ -12,25 +12,28 @@ import ( swagger "github.com/swaggo/echo-swagger" _ "git.jamestombleson.com/jtom38/newsbot-api/docs" - "git.jamestombleson.com/jtom38/newsbot-api/internal/database" "git.jamestombleson.com/jtom38/newsbot-api/internal/domain" "git.jamestombleson.com/jtom38/newsbot-api/internal/services" ) type Handler struct { Router *echo.Echo - Db *database.Queries + //Db *database.Queries config services.Configs repo services.RepositoryService } const ( - ErrParameterIdMissing = "The requested parameter ID was not found." - ErrParameterMissing = "The requested parameter was not found found:" - ErrUnableToParseId = "Unable to parse the requested ID" + ErrParameterIdMissing = "The requested parameter ID was not found." + ErrParameterMissing = "The requested parameter was not found found:" + ErrUnableToParseId = "Unable to parse the requested ID" + ErrRecordMissing = "The requested record was not found" ErrFailedToCreateRecord = "The record was not created due to a database problem" - ErrUserUnknown = "User is unknown" + ErrFailedToUpdateRecord = "The requested record was not updated due to a database problem" + + ErrUserUnknown = "User is unknown" + ErrYouDontOwnTheRecord = "The record requested does not belong to you" ResponseMessageSuccess = "Success" ) @@ -169,4 +172,13 @@ func (s *Handler) ValidateJwtToken(c echo.Context, requiredScope string) JwtToke } return token -} \ No newline at end of file +} + +func (s *Handler) GetUserIdFromJwtToken(c echo.Context) int64 { + token, err := s.getJwtTokenFromContext(c) + if err != nil { + s.WriteMessage(c, ErrJwtMissing, http.StatusUnauthorized) + } + + return token.GetUserId() +} diff --git a/internal/handler/v1/jwt.go b/internal/handler/v1/jwt.go index 5ded140..22aaa74 100644 --- a/internal/handler/v1/jwt.go +++ b/internal/handler/v1/jwt.go @@ -23,6 +23,7 @@ type JwtToken struct { Iss string `json:"iss"` Authorized bool `json:"authorized"` UserName string `json:"username"` + UserId int64 `json:"userId"` Scopes []string `json:"scopes"` jwt.RegisteredClaims } @@ -52,6 +53,10 @@ func (j JwtToken) GetUsername() string { return j.UserName } +func (j JwtToken) GetUserId() int64 { + return j.UserId +} + func (j JwtToken) hasExpired() error { // Check to see if the token has expired hasExpired := j.Exp.Compare(time.Now()) @@ -77,11 +82,11 @@ func (j JwtToken) hasScope(scope string) error { return errors.New(ErrJwtScopeMissing) } -func (h *Handler) generateJwt(username, scopes, issuer string) (string, error) { - return h.generateJwtWithExp(username, scopes, issuer, time.Now().Add(10*time.Minute)) +func (h *Handler) generateJwt(username, scopes, issuer string, userId int64) (string, error) { + return h.generateJwtWithExp(username, scopes, issuer, userId, time.Now().Add(10*time.Minute)) } -func (h *Handler) generateJwtWithExp(username, userScopes, issuer string, expiresAt time.Time) (string, error) { +func (h *Handler) generateJwtWithExp(username, userScopes, issuer string, userId int64, expiresAt time.Time) (string, error) { secret := []byte(h.config.JwtSecret) // Anyone who wants to decrypt the key needs to use the same method @@ -91,6 +96,7 @@ func (h *Handler) generateJwtWithExp(username, userScopes, issuer string, expire claims["authorized"] = true claims["username"] = username claims["iss"] = issuer + claims["userId"] = userId var scopes []string scopes = append(scopes, domain.ScopeAll) diff --git a/internal/handler/v1/sources.go b/internal/handler/v1/sources.go index 7379f2c..f5f1543 100644 --- a/internal/handler/v1/sources.go +++ b/internal/handler/v1/sources.go @@ -1,17 +1,13 @@ package v1 import ( - "context" - "encoding/json" "fmt" "net/http" "strconv" "strings" - "git.jamestombleson.com/jtom38/newsbot-api/internal/database" "git.jamestombleson.com/jtom38/newsbot-api/internal/domain" "git.jamestombleson.com/jtom38/newsbot-api/internal/services" - "github.com/google/uuid" "github.com/labstack/echo/v4" ) @@ -25,6 +21,7 @@ import ( // @Failure 400 {object} domain.BaseResponse "Unable to reach SQL or Data problems" // @Security Bearer func (s *Handler) listSources(c echo.Context) error { + s.ValidateJwtToken(c, domain.ScopeSourceRead) resp := domain.SourcesResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, @@ -58,6 +55,7 @@ func (s *Handler) listSources(c echo.Context) error { // @Failure 500 {object} domain.BaseResponse // @Security Bearer func (s *Handler) listSourcesBySource(c echo.Context) error { + s.ValidateJwtToken(c, domain.ScopeSourceRead) resp := domain.SourcesResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, @@ -97,6 +95,7 @@ func (s *Handler) listSourcesBySource(c echo.Context) error { // @Failure 500 {object} domain.BaseResponse // @Security Bearer func (s *Handler) getSource(c echo.Context) error { + s.ValidateJwtToken(c, domain.ScopeSourceRead) resp := domain.SourcesResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, @@ -133,6 +132,7 @@ func (s *Handler) getSource(c echo.Context) error { // @Failure 500 {object} domain.BaseResponse // @Security Bearer func (s *Handler) GetSourceBySourceAndName(c echo.Context) error { + s.ValidateJwtToken(c, domain.ScopeSourceRead) resp := domain.SourcesResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, @@ -169,6 +169,8 @@ func (s *Handler) GetSourceBySourceAndName(c echo.Context) error { // @Failure 500 {object} domain.BaseResponse // @Security Bearer func (s *Handler) newRedditSource(c echo.Context) error { + s.ValidateJwtToken(c, domain.ScopeSourceCreate) + resp := domain.SourcesResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, @@ -178,20 +180,13 @@ func (s *Handler) newRedditSource(c echo.Context) error { var param domain.NewSourceParamRequest err := c.Bind(¶m) if err != nil { - return c.JSON(http.StatusBadRequest, domain.BaseResponse{ - Message: err.Error(), - }) + s.WriteError(c, err, http.StatusBadRequest) } - if param.Url == "" { - return c.JSON(http.StatusBadRequest, domain.BaseResponse{ - Message: "Url is missing a value", - }) + s.WriteMessage(c, "url is missing", http.StatusBadRequest) } if !strings.Contains(param.Url, "reddit.com") { - return c.JSON(http.StatusBadRequest, domain.BaseResponse{ - Message: "Invalid URL given", - }) + s.WriteMessage(c, "invalid url", http.StatusBadRequest) } tags := fmt.Sprintf("twitch, %v, %s", param.Name, param.Tags) @@ -223,61 +218,54 @@ func (s *Handler) newRedditSource(c echo.Context) error { // @Router /v1/sources/new/youtube [post] // @Security Bearer func (s *Handler) newYoutubeSource(c echo.Context) error { + // Validate the jwt + s.ValidateJwtToken(c, domain.ScopeSourceCreate) + var param domain.NewSourceParamRequest err := c.Bind(¶m) if err != nil { - return c.JSON(http.StatusBadRequest, domain.BaseResponse{ - Message: err.Error(), - }) + s.WriteError(c, err, http.StatusBadRequest) } - - //query := r.URL.Query() - //_name := query["name"][0] - //_url := query["url"][0] - ////_tags := query["tags"][0] - if param.Url == "" { - return c.JSON(http.StatusBadRequest, domain.BaseResponse{ - Message: "url is missing a value", - }) + s.WriteMessage(c, "url is missing a value", http.StatusBadRequest) } if !strings.Contains(param.Url, "youtube.com") { - return c.JSON(http.StatusBadRequest, domain.BaseResponse{ - Message: "Invalid URL", - }) + s.WriteMessage(c, "invalid url", http.StatusBadRequest) + } + + resp := domain.SourcesResponse{ + BaseResponse: domain.BaseResponse{ + Message: ResponseMessageSuccess, + }, + } + + item, err := s.repo.Sources.GetBySourceAndName(c.Request().Context(), domain.SourceCollectorYoutube, param.Name) + if err == nil { + var dto []domain.SourceDto + dto = append(dto, services.SourceToDto(item)) + resp.Payload = dto + return c.JSON(http.StatusOK, resp) } - /* - if _tags == "" { - tags = fmt.Sprintf("twitch, %v", _name) - } else { - } - */ tags := fmt.Sprintf("twitch, %v", param.Name) - - params := database.CreateSourceParams{ - ID: uuid.New(), - Site: "youtube", - Name: param.Name, - Source: "youtube", - Type: "feed", - Enabled: true, - Url: param.Url, - Tags: tags, - } - err = s.Db.CreateSource(context.Background(), params) + rows, err := s.repo.Sources.Create(c.Request().Context(), domain.SourceCollectorYoutube, param.Name, param.Url, tags, true) if err != nil { return c.JSON(http.StatusInternalServerError, err.Error()) } - bJson, err := json.Marshal(¶ms) - if err != nil { - return c.JSON(http.StatusInternalServerError, domain.BaseResponse{ - Message: err.Error(), - }) + if rows != 1 { + s.WriteMessage(c, ErrFailedToCreateRecord, http.StatusInternalServerError) } - return c.JSON(http.StatusOK, bJson) + item, err = s.repo.Sources.GetBySourceAndName(c.Request().Context(), domain.SourceCollectorYoutube, param.Name) + if err == nil { + var dto []domain.SourceDto + dto = append(dto, services.SourceToDto(item)) + resp.Payload = dto + return c.JSON(http.StatusOK, resp) + } + + return c.JSON(http.StatusOK, resp) } // NewTwitchSource @@ -287,6 +275,8 @@ func (s *Handler) newYoutubeSource(c echo.Context) error { // @Router /v1/sources/new/twitch [post] // @Security Bearer func (s *Handler) newTwitchSource(c echo.Context) error { + s.ValidateJwtToken(c, domain.ScopeSourceCreate) + var param domain.NewSourceParamRequest err := c.Bind(¶m) if err != nil { @@ -295,37 +285,41 @@ func (s *Handler) newTwitchSource(c echo.Context) error { }) } - //query := r.URL.Query() - //_name := query["name"][0] + resp := domain.SourcesResponse{ + BaseResponse: domain.BaseResponse{ + Message: ResponseMessageSuccess, + }, + } tags := fmt.Sprintf("twitch, %v", param.Name) - _url := fmt.Sprintf("https://twitch.tv/%v", param.Name) + url := fmt.Sprintf("https://twitch.tv/%v", param.Name) - params := database.CreateSourceParams{ - ID: uuid.New(), - Site: "twitch", - Name: param.Name, - Source: "twitch", - Type: "api", - Enabled: true, - Url: _url, - Tags: tags, + // Check if the record already exists + item, err := s.repo.Sources.GetBySourceAndName(c.Request().Context(), domain.SourceCollectorTwitch, param.Name) + if err == nil { + var dto []domain.SourceDto + dto = append(dto, services.SourceToDto(item)) + resp.Payload = dto + return c.JSON(http.StatusOK, resp) } - err = s.Db.CreateSource(c.Request().Context(), params) + + rows, err := s.repo.Sources.Create(c.Request().Context(), domain.SourceCollectorTwitch, param.Name, url, tags, true) if err != nil { return c.JSON(http.StatusInternalServerError, domain.BaseResponse{ Message: err.Error(), }) } - bJson, err := json.Marshal(¶ms) - if err != nil { - return c.JSON(http.StatusInternalServerError, domain.BaseResponse{ - Message: err.Error(), - }) + if rows != 1 { + s.WriteMessage(c, ErrFailedToCreateRecord, http.StatusInternalServerError) } - return c.JSON(http.StatusOK, bJson) + item, err = s.repo.Sources.GetBySourceAndName(c.Request().Context(), domain.SourceCollectorTwitch, param.Name) + var dto []domain.SourceDto + dto = append(dto, services.SourceToDto(item)) + resp.Payload = dto + + return c.JSON(http.StatusOK, resp) } // NewRssSource @@ -339,6 +333,8 @@ func (s *Handler) newTwitchSource(c echo.Context) error { // @Failure 500 {object} domain.BaseResponse // @Security Bearer func (s *Handler) newRssSource(c echo.Context) error { + s.ValidateJwtToken(c, domain.ScopeSourceCreate) + resp := domain.SourcesResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, @@ -382,48 +378,50 @@ func (s *Handler) newRssSource(c echo.Context) error { // DeleteSource // @Summary Marks a source as deleted based on its ID value. -// @Param id path string true "id" +// @Param id path int true "id" // @Tags Source // @Router /v1/sources/{id} [POST] +// @Success 200 {object} domain.SourcesResponse "ok" +// @Failure 400 {object} domain.BaseResponse +// @Failure 500 {object} domain.BaseResponse // @Security Bearer func (s *Handler) deleteSources(c echo.Context) error { - id := c.Param("ID") - uuid, err := uuid.Parse(id) + s.ValidateJwtToken(c, domain.ScopeAll) + id, err := strconv.Atoi(c.Param("ID")) if err != nil { - return c.JSON(http.StatusBadRequest, domain.BaseResponse{ - Message: err.Error(), - }) + s.WriteError(c, err, http.StatusBadRequest) } // Check to make sure we can find the record - _, err = s.Db.GetSourceByID(c.Request().Context(), uuid) + _, err = s.repo.Sources.GetById(c.Request().Context(), int64(id)) if err != nil { - return c.JSON(http.StatusInternalServerError, domain.BaseResponse{ - Message: err.Error(), - }) + s.WriteError(c, err, http.StatusInternalServerError) } // Delete the record - err = s.Db.DeleteSource(c.Request().Context(), uuid) + rows, err := s.repo.Sources.SoftDelete(c.Request().Context(), int64(id)) if err != nil { - return c.JSON(http.StatusInternalServerError, domain.BaseResponse{ - Message: err.Error(), - }) + s.WriteError(c, err, http.StatusInternalServerError) + } + if rows != 1 { + s.WriteMessage(c, ErrFailedToUpdateRecord, http.StatusInternalServerError) } - p := ApiStatusModel{ - Message: "OK", - StatusCode: http.StatusOK, - } - - b, err := json.Marshal(p) + // pull the record with its updated value + item, err := s.repo.Sources.GetById(c.Request().Context(), int64(id)) if err != nil { - return c.JSON(http.StatusInternalServerError, domain.BaseResponse{ - Message: err.Error(), - }) + s.WriteError(c, err, http.StatusInternalServerError) } - return c.JSON(http.StatusOK, b) + var items []domain.SourceDto + items = append(items, services.SourceToDto(item)) + + return c.JSON(http.StatusOK, domain.SourcesResponse{ + BaseResponse: domain.BaseResponse{ + Message: "OK", + }, + Payload: items, + }) } // DisableSource diff --git a/internal/respositoryServices/userService.go b/internal/respositoryServices/userService.go index e187ca0..ea02f51 100644 --- a/internal/respositoryServices/userService.go +++ b/internal/respositoryServices/userService.go @@ -130,7 +130,7 @@ func (us UserService) Create(ctx context.Context, name, password, scope string) return domain.UserEntity{}, err } - us.repo.Create(ctx, name, password, domain.ScopeRead) + us.repo.Create(ctx, name, password, domain.ScopeArticleRead) return domain.UserEntity{}, nil }