package v1 import ( "context" "database/sql" "errors" "net/http" "github.com/golang-jwt/jwt/v5" echojwt "github.com/labstack/echo-jwt/v4" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" swagger "github.com/swaggo/echo-swagger" _ "git.jamestombleson.com/jtom38/newsbot-api/docs" "git.jamestombleson.com/jtom38/newsbot-api/domain" "git.jamestombleson.com/jtom38/newsbot-api/internal/services" ) type Handler struct { Router *echo.Echo //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" ErrRecordMissing = "The requested record was not found" ErrFailedToCreateRecord = "The record was not created due to a database problem" 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" ) var ( ErrIdValueMissing string = "id value is missing" ErrValueNotUuid string = "a value given was expected to be a uuid but was not correct." ErrNoRecordFound string = "no record was found." ErrUnableToConvertToJson string = "Unable to convert to json" ) func NewServer(ctx context.Context, configs services.Configs, conn *sql.DB) *Handler { s := &Handler{ //Db: db, //dto: dto.NewDtoClient(db), config: configs, repo: services.NewRepositoryService(conn), } jwtConfig := echojwt.Config{ NewClaimsFunc: func(c echo.Context) jwt.Claims { return new(JwtToken) }, SigningKey: []byte(configs.JwtSecret), } router := echo.New() router.Pre(middleware.RemoveTrailingSlash()) router.Pre(middleware.Logger()) router.Pre(middleware.Recover()) router.GET("/swagger/*", swagger.WrapHandler) v1 := router.Group("/api/v1") articles := v1.Group("/articles") articles.Use(echojwt.WithConfig(jwtConfig)) articles.GET("", s.listArticles) articles.GET(":id", s.getArticle) articles.GET(":id/details", s.getArticleDetails) articles.GET("by/source/:id", s.ListArticlesBySourceId) //dwh := v1.Group("/discord/webhooks") //dwh.GET("/", s.ListDiscordWebHooks) //dwh.POST("/new", s.NewDiscordWebHook) //dwh.GET("/by/serverAndChannel", s.GetDiscordWebHooksByServerAndChannel) //dwh.GET("/:ID", s.GetDiscordWebHooksById) //dwh.DELETE("/:ID", s.deleteDiscordWebHook) //dwh.POST("/:ID/disable", s.disableDiscordWebHook) //dwh.POST("/:ID/enable", s.enableDiscordWebHook) //queue := v1.Group("/queue") //queue.GET("/discord/webhooks", s.ListDiscordWebhookQueue) // TODO this needs to be reworked //settings := v1.Group("/settings") //settings.GET("/", s.getSettings) sources := v1.Group("/sources") sources.Use(echojwt.WithConfig(jwtConfig)) sources.GET("", s.listSources) sources.GET("/by/source", s.listSourcesBySource) sources.GET("/by/sourceAndName", s.GetSourceBySourceAndName) //sources.POST("/new/reddit", s.newRedditSource) //sources.POST("/new/youtube", s.newYoutubeSource) //sources.POST("/new/twitch", s.newTwitchSource) sources.POST("/new/rss", s.newRssSource) sources.GET("/:ID/", s.getSource) sources.DELETE("/:ID/", s.deleteSources) sources.POST("/:ID/disable", s.disableSource) sources.POST("/:ID/enable", s.enableSource) users := v1.Group("/users") users.POST("/login", s.AuthLogin) users.POST("/register", s.AuthRegister) users.Use(echojwt.WithConfig(jwtConfig)) users.POST("/scopes/add", s.AddScopes) users.POST("/scopes/remove", s.RemoveScopes) users.POST("/refreshToken", s.RefreshJwtToken) s.Router = router return s } //type ApiStatusModel struct { // StatusCode int `json:"status"` // Message string `json:"message"` //} //type ApiError struct { // *ApiStatusModel //} func (s *Handler) WriteError(c echo.Context, errMessage error, HttpStatusCode int) error { return c.JSON(HttpStatusCode, domain.BaseResponse{ Message: errMessage.Error(), }) } func (s *Handler) WriteMessage(c echo.Context, msg string, HttpStatusCode int) error { return c.JSON(HttpStatusCode, domain.BaseResponse{ Message: msg, }) } func (s *Handler) InternalServerErrorResponse(c echo.Context, msg string) error { return c.JSON(http.StatusInternalServerError, domain.BaseResponse{ Message: msg, }) } func (s *Handler) UnauthorizedResponse(c echo.Context, msg string) error { return c.JSON(http.StatusUnauthorized, domain.BaseResponse{ Message: msg, }) } // If the token is not valid then an json error will be returned. // If the token has the wrong scope, a json error will be returned. // If the token passes all the checks, it is valid and is returned back to the caller. func (s *Handler) ValidateJwtToken(c echo.Context, requiredScope string) (JwtToken, error) { token, err := s.getJwtTokenFromContext(c) if err != nil { s.WriteMessage(c, ErrJwtMissing, http.StatusUnauthorized) } err = token.hasExpired() if err != nil { return JwtToken{}, errors.New(ErrJwtExpired) //s.WriteMessage(c, ErrJwtExpired, http.StatusUnauthorized) } err = token.hasScope(requiredScope) if err != nil { return JwtToken{}, errors.New(ErrJwtScopeMissing) //s.WriteMessage(c, ErrJwtScopeMissing, http.StatusUnauthorized) } if token.Iss != s.config.ServerAddress { return JwtToken{}, errors.New(ErrJwtInvalidIssuer) //s.WriteMessage(c, ErrJwtInvalidIssuer, http.StatusUnauthorized) } return token, nil } 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() }