package v1 import ( "fmt" "net/http" "strconv" "strings" "git.jamestombleson.com/jtom38/newsbot-api/domain" "git.jamestombleson.com/jtom38/newsbot-api/internal/dtoconv" "github.com/labstack/echo/v4" ) // ListSources // @Summary Lists the top 50 records // @Param page query string false "page number" // @Produce application/json // @Tags Source // @Router /v1/sources [get] // @Success 200 {object} domain.SourcesResponse "ok" // @Failure 400 {object} domain.SourcesResponse "Unable to reach SQL or Data problems" // @Security Bearer func (s *Handler) listSources(c echo.Context) error { p := domain.SourcesResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, IsError: true, }, } _, err := s.ValidateJwtToken(c, domain.ScopeSourceRead) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusUnauthorized, p) } page, err := strconv.Atoi(c.QueryParam("page")) if err != nil { page = 0 } // Default way of showing all sources items, err := s.repo.Sources.List(c.Request().Context(), page, 25) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusInternalServerError, p) } p.Payload = dtoconv.SourcesToDto(items) p.BaseResponse.IsError = false return c.JSON(http.StatusOK, p) } // ListSourcesBySource // @Summary Lists the top 50 records based on the name given. Example: reddit // @Param source query string true "Source Name" // @Param page query string false "page number" // @Produce application/json // @Tags Source // @Router /v1/sources/by/source [get] // @Success 200 {object} domain.SourcesResponse "ok" // @Failure 400 {object} domain.SourcesResponse // @Failure 500 {object} domain.SourcesResponse // @Security Bearer func (s *Handler) listSourcesBySource(c echo.Context) error { p := domain.SourcesResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, IsError: true, }, } _, err := s.ValidateJwtToken(c, domain.ScopeSourceRead) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusUnauthorized, p) } source := c.QueryParam("source") if source == "" { p.BaseResponse.Message = fmt.Sprintf("%s source", ErrParameterMissing) return c.JSON(http.StatusBadRequest, p) } page, err := strconv.Atoi(c.QueryParam("page")) if err != nil { page = 0 } // Shows the list by Sources.source items, err := s.repo.Sources.ListBySource(c.Request().Context(), page, 25, source) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusInternalServerError, p) } p.Payload = dtoconv.SourcesToDto(items) p.BaseResponse.IsError = false return c.JSON(http.StatusOK, p) } // GetSource // @Summary Returns a single entity by ID // @Param id path int true "uuid" // @Produce application/json // @Tags Source // @Router /v1/sources/{id} [get] // @Success 200 {object} domain.SourcesResponse "ok" // @Failure 400 {object} domain.SourcesResponse // @Failure 500 {object} domain.SourcesResponse // @Security Bearer func (s *Handler) getSource(c echo.Context) error { p := domain.SourcesResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, }, } _, err := s.ValidateJwtToken(c, domain.ScopeSourceRead) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusUnauthorized, p) } id, err := strconv.Atoi(c.Param("ID")) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusBadRequest, p) } item, err := s.repo.Sources.GetById(c.Request().Context(), int64(id)) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusInternalServerError, p) } var dto []domain.SourceDto dto = append(dto, dtoconv.SourceToDto(item)) p.Payload = dto p.BaseResponse.IsError = false return c.JSON(http.StatusOK, p) } // GetSourceByNameAndSource // @Summary Returns a single entity by ID // @Param name query string true "dadjokes" // @Param source query string true "reddit" // @Produce application/json // @Tags Source // @Router /v1/sources/by/sourceAndName [get] // @Success 200 {object} domain.SourcesResponse "ok" // @Failure 400 {object} domain.BaseResponse // @Failure 500 {object} domain.BaseResponse // @Security Bearer func (s *Handler) GetSourceBySourceAndName(c echo.Context) error { p := domain.SourcesResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, IsError: true, }, } _, err := s.ValidateJwtToken(c, domain.ScopeSourceRead) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusUnauthorized, p) } var param domain.GetSourceBySourceAndNameParamRequest err = c.Bind(¶m) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusBadRequest, p) } item, err := s.repo.Sources.GetBySourceAndName(c.Request().Context(), param.Source, param.Name) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusInternalServerError, p) } var dto []domain.SourceDto dto = append(dto, dtoconv.SourceToDto(item)) p.Payload = dto p.BaseResponse.IsError = false return c.JSON(http.StatusOK, p) } // NewRedditSource // @Summary Creates a new reddit source to monitor. // @Param name query string true "name" // @Param url query string true "url" // @Tags Source // @Router /v1/sources/new/reddit [post] // @Success 200 {object} domain.SourcesResponse "ok" // @Failure 400 {object} domain.SourcesResponse // @Failure 500 {object} domain.SourcesResponse // @Security Bearer func (s *Handler) newRedditSource(c echo.Context) error { p := domain.SourcesResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, IsError: true, }, } _, err := s.ValidateJwtToken(c, domain.ScopeSourceCreate) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusUnauthorized, p) } var param domain.NewSourceParamRequest err = c.Bind(¶m) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusBadRequest, p) } if param.Url == "" { p.BaseResponse.Message = "url is missing" return c.JSON(http.StatusBadRequest, p) } if !strings.Contains(param.Url, "reddit.com") { p.BaseResponse.Message = "invalid url" return c.JSON(http.StatusBadRequest, p) } // Check to see if we already have this record, if we do, return it. item, err := s.repo.Sources.GetBySourceAndName(c.Request().Context(), domain.SourceCollectorReddit, param.Name) if err == nil { var dto []domain.SourceDto dto = append(dto, dtoconv.SourceToDto(item)) p.Payload = dto p.BaseResponse.IsError = false return c.JSON(http.StatusOK, p) } tags := fmt.Sprintf("twitch, %v, %s", param.Name, param.Tags) rows, err := s.repo.Sources.Create(c.Request().Context(), domain.SourceCollectorReddit, param.Name, param.Url, tags, true) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusInternalServerError, p) } if rows != 1 { p.BaseResponse.Message = ErrFailedToCreateRecord return c.JSON(http.StatusInternalServerError, p) } item, err = s.repo.Sources.GetBySourceAndName(c.Request().Context(), domain.SourceCollectorReddit, param.Name) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusInternalServerError, p) } var dto []domain.SourceDto dto = append(dto, dtoconv.SourceToDto(item)) p.Payload = dto p.BaseResponse.IsError = false return c.JSON(http.StatusOK, p) } // NewYoutubeSource // @Summary Creates a new youtube source to monitor. // @Param name query string true "name" // @Param url query string true "url" // @Tags Source // @Router /v1/sources/new/youtube [post] // @Success 200 {object} domain.SourcesResponse "ok" // @Failure 400 {object} domain.SourcesResponse // @Failure 500 {object} domain.SourcesResponse // @Security Bearer func (s *Handler) newYoutubeSource(c echo.Context) error { p := domain.SourcesResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, }, } // Validate the jwt _, err := s.ValidateJwtToken(c, domain.ScopeSourceCreate) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusUnauthorized, p) } var param domain.NewSourceParamRequest err = c.Bind(¶m) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusBadRequest, p) } if param.Url == "" { p.BaseResponse.Message = "url is missing a value" return c.JSON(http.StatusBadRequest, p) } if !strings.Contains(param.Url, "youtube.com") { p.BaseResponse.Message = "invalid url" return c.JSON(http.StatusBadRequest, p) } item, err := s.repo.Sources.GetBySourceAndName(c.Request().Context(), domain.SourceCollectorYoutube, param.Name) if err == nil { var dto []domain.SourceDto dto = append(dto, dtoconv.SourceToDto(item)) p.Payload = dto p.BaseResponse.IsError = false return c.JSON(http.StatusOK, p) } tags := fmt.Sprintf("twitch, %v", param.Name) rows, err := s.repo.Sources.Create(c.Request().Context(), domain.SourceCollectorYoutube, param.Name, param.Url, tags, true) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusInternalServerError, p) } if rows != 1 { p.BaseResponse.Message = ErrFailedToCreateRecord return c.JSON(http.StatusInternalServerError, p) } item, err = s.repo.Sources.GetBySourceAndName(c.Request().Context(), domain.SourceCollectorYoutube, param.Name) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusInternalServerError, p) } var dto []domain.SourceDto dto = append(dto, dtoconv.SourceToDto(item)) p.Payload = dto p.BaseResponse.IsError = false return c.JSON(http.StatusOK, p) } // NewTwitchSource // @Summary Creates a new twitch source to monitor. // @Param name query string true "name" // @Tags Source // @Router /v1/sources/new/twitch [post] // @Success 200 {object} domain.SourcesResponse "ok" // @Failure 400 {object} domain.SourcesResponse // @Failure 500 {object} domain.SourcesResponse // @Security Bearer func (s *Handler) newTwitchSource(c echo.Context) error { p := domain.SourcesResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, IsError: true, }, } _, err := s.ValidateJwtToken(c, domain.ScopeSourceCreate) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusUnauthorized, p) } var param domain.NewSourceParamRequest err = c.Bind(¶m) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusBadRequest, p) } tags := fmt.Sprintf("twitch, %v", param.Name) url := fmt.Sprintf("https://twitch.tv/%v", param.Name) // 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, dtoconv.SourceToDto(item)) p.Payload = dto p.BaseResponse.IsError = false return c.JSON(http.StatusOK, p) } rows, err := s.repo.Sources.Create(c.Request().Context(), domain.SourceCollectorTwitch, param.Name, url, tags, true) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusInternalServerError, p) } if rows != 1 { p.BaseResponse.Message = ErrFailedToCreateRecord return c.JSON(http.StatusInternalServerError, p) } item, _ = s.repo.Sources.GetBySourceAndName(c.Request().Context(), domain.SourceCollectorTwitch, param.Name) var dto []domain.SourceDto dto = append(dto, dtoconv.SourceToDto(item)) p.Payload = dto p.BaseResponse.IsError = false return c.JSON(http.StatusOK, p) } // NewRssSource // @Summary Creates a new rss source to monitor. // @Param name query string true "Site Name" // @Param url query string true "RSS Url" // @Tags Source // @Router /v1/sources/new/rss [post] // @Success 200 {object} domain.SourcesResponse "ok" // @Failure 400 {object} domain.SourcesResponse // @Failure 500 {object} domain.SourcesResponse // @Security Bearer func (s *Handler) newRssSource(c echo.Context) error { p := domain.SourcesResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, IsError: true, }, } _, err := s.ValidateJwtToken(c, domain.ScopeSourceCreate) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusUnauthorized, p) } var param domain.NewSourceParamRequest err = c.Bind(¶m) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusBadRequest, p) } if param.Url == "" { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusBadRequest, p) } // Check if the record already exists item, err := s.repo.Sources.GetBySourceAndName(c.Request().Context(), domain.SourceCollectorRss, param.Name) if err == nil { var dto []domain.SourceDto dto = append(dto, dtoconv.SourceToDto(item)) p.Payload = dto p.BaseResponse.IsError = false return c.JSON(http.StatusOK, p) } tags := fmt.Sprintf("rss, %v, %s", param.Name, param.Tags) rows, err := s.repo.Sources.Create(c.Request().Context(), domain.SourceCollectorRss, param.Name, param.Url, tags, true) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusInternalServerError, p) } if rows != 1 { p.BaseResponse.Message = ErrFailedToCreateRecord return c.JSON(http.StatusInternalServerError, p) } item, err = s.repo.Sources.GetBySourceAndName(c.Request().Context(), domain.SourceCollectorRss, param.Name) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusInternalServerError, p) } var dto []domain.SourceDto dto = append(dto, dtoconv.SourceToDto(item)) p.Payload = dto p.BaseResponse.IsError = false return c.JSON(http.StatusOK, p) } // DeleteSource // @Summary Marks a source as deleted based on its ID value. // @Param id path int true "id" // @Tags Source // @Router /v1/sources/{id} [POST] // @Success 200 {object} domain.SourcesResponse "ok" // @Failure 400 {object} domain.SourcesResponse // @Failure 500 {object} domain.SourcesResponse // @Security Bearer func (s *Handler) deleteSources(c echo.Context) error { p := domain.SourcesResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, IsError: true, }, } _, err := s.ValidateJwtToken(c, domain.ScopeAll) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusUnauthorized, p) } id, err := strconv.Atoi(c.Param("ID")) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusBadRequest, p) } // Check to make sure we can find the record _, err = s.repo.Sources.GetById(c.Request().Context(), int64(id)) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusInternalServerError, p) } // Delete the record rows, err := s.repo.Sources.SoftDelete(c.Request().Context(), int64(id)) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusInternalServerError, p) } if rows != 1 { p.BaseResponse.Message = ErrFailedToUpdateRecord return c.JSON(http.StatusInternalServerError, p) } // pull the record with its updated value item, err := s.repo.Sources.GetById(c.Request().Context(), int64(id)) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusInternalServerError, p) } var items []domain.SourceDto items = append(items, dtoconv.SourceToDto(item)) p.Payload = items p.IsError = false return c.JSON(http.StatusOK, p) } // DisableSource // @Summary Disables a source from processing. // @Param id path int true "id" // @Tags Source // @Router /v1/sources/{id}/disable [post] // @Success 200 {object} domain.SourcesResponse "ok" // @Failure 400 {object} domain.SourcesResponse // @Failure 500 {object} domain.SourcesResponse // @Security Bearer func (s *Handler) disableSource(c echo.Context) error { p := domain.SourcesResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, IsError: true, }, } _, err := s.ValidateJwtToken(c, domain.ScopeAll) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusUnauthorized, p) } id, err := strconv.Atoi(c.Param("ID")) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusBadRequest, p) } // Check to make sure we can find the record _, err = s.repo.Sources.GetById(c.Request().Context(), int64(id)) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusBadRequest, p) } _, err = s.repo.Sources.Disable(c.Request().Context(), int64(id)) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusInternalServerError, p) } item, err := s.repo.Sources.GetById(c.Request().Context(), int64(id)) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusInternalServerError, p) } var dto []domain.SourceDto dto = append(dto, dtoconv.SourceToDto(item)) p.Payload = dto p.BaseResponse.IsError = false return c.JSON(http.StatusOK, p) } // EnableSource // @Summary Enables a source to continue processing. // @Param id path string true "id" // @Tags Source // @Router /v1/sources/{id}/enable [post] // @Success 200 {object} domain.SourcesResponse "ok" // @Failure 400 {object} domain.SourcesResponse // @Failure 500 {object} domain.SourcesResponse // @Security Bearer func (s *Handler) enableSource(c echo.Context) error { p := domain.SourcesResponse{ BaseResponse: domain.BaseResponse{ Message: ResponseMessageSuccess, IsError: true, }, } _, err := s.ValidateJwtToken(c, domain.ScopeAll) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusUnauthorized, p) } id, err := strconv.Atoi(c.Param("ID")) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusBadRequest, p) } // Check to make sure we can find the record _, err = s.repo.Sources.GetById(c.Request().Context(), int64(id)) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusBadRequest, p) } _, err = s.repo.Sources.Enable(c.Request().Context(), int64(id)) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusInternalServerError, p) } item, err := s.repo.Sources.GetById(c.Request().Context(), int64(id)) if err != nil { p.BaseResponse.Message = err.Error() return c.JSON(http.StatusInternalServerError, p) } var dto []domain.SourceDto dto = append(dto, dtoconv.SourceToDto(item)) p.Payload = dto p.BaseResponse.IsError = false return c.JSON(http.StatusOK, p) }