2024-04-23 22:18:07 -07:00
|
|
|
package v1
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"database/sql"
|
2024-05-07 22:01:32 -07:00
|
|
|
"errors"
|
2024-04-23 22:18:07 -07:00
|
|
|
|
2024-05-04 11:47:30 -07:00
|
|
|
"github.com/golang-jwt/jwt/v5"
|
|
|
|
echojwt "github.com/labstack/echo-jwt/v4"
|
2024-04-23 22:18:07 -07:00
|
|
|
"github.com/labstack/echo/v4"
|
2024-05-02 17:36:39 -07:00
|
|
|
"github.com/labstack/echo/v4/middleware"
|
2024-04-23 22:18:07 -07:00
|
|
|
swagger "github.com/swaggo/echo-swagger"
|
|
|
|
|
2024-05-02 17:36:39 -07:00
|
|
|
_ "git.jamestombleson.com/jtom38/newsbot-api/docs"
|
2024-04-23 22:18:07 -07:00
|
|
|
"git.jamestombleson.com/jtom38/newsbot-api/internal/services"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Handler struct {
|
2024-04-28 11:40:51 -07:00
|
|
|
Router *echo.Echo
|
2024-05-07 19:14:37 -07:00
|
|
|
//Db *database.Queries
|
2024-04-28 11:40:51 -07:00
|
|
|
config services.Configs
|
|
|
|
repo services.RepositoryService
|
2024-04-23 22:18:07 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
2024-05-07 19:14:37 -07:00
|
|
|
ErrParameterIdMissing = "The requested parameter ID was not found."
|
|
|
|
ErrParameterMissing = "The requested parameter was not found found:"
|
|
|
|
ErrUnableToParseId = "Unable to parse the requested ID"
|
|
|
|
|
2024-04-28 19:29:49 -07:00
|
|
|
ErrRecordMissing = "The requested record was not found"
|
|
|
|
ErrFailedToCreateRecord = "The record was not created due to a database problem"
|
2024-05-07 19:14:37 -07:00
|
|
|
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"
|
2024-04-28 11:40:51 -07:00
|
|
|
|
|
|
|
ResponseMessageSuccess = "Success"
|
2024-04-23 22:18:07 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
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"
|
|
|
|
)
|
|
|
|
|
2024-05-02 17:36:39 -07:00
|
|
|
func NewServer(ctx context.Context, configs services.Configs, conn *sql.DB) *Handler {
|
2024-04-23 22:18:07 -07:00
|
|
|
s := &Handler{
|
2024-04-28 11:40:51 -07:00
|
|
|
config: configs,
|
|
|
|
repo: services.NewRepositoryService(conn),
|
2024-04-23 22:18:07 -07:00
|
|
|
}
|
|
|
|
|
2024-05-04 11:47:30 -07:00
|
|
|
jwtConfig := echojwt.Config{
|
|
|
|
NewClaimsFunc: func(c echo.Context) jwt.Claims {
|
|
|
|
return new(JwtToken)
|
|
|
|
},
|
|
|
|
SigningKey: []byte(configs.JwtSecret),
|
|
|
|
}
|
|
|
|
|
2024-04-23 22:18:07 -07:00
|
|
|
router := echo.New()
|
2024-05-02 17:36:39 -07:00
|
|
|
router.Pre(middleware.RemoveTrailingSlash())
|
|
|
|
router.Pre(middleware.Logger())
|
|
|
|
router.Pre(middleware.Recover())
|
2024-04-23 22:18:07 -07:00
|
|
|
router.GET("/swagger/*", swagger.WrapHandler)
|
|
|
|
|
|
|
|
v1 := router.Group("/api/v1")
|
|
|
|
articles := v1.Group("/articles")
|
2024-05-04 11:47:30 -07:00
|
|
|
articles.Use(echojwt.WithConfig(jwtConfig))
|
2024-05-02 17:36:39 -07:00
|
|
|
articles.GET("", s.listArticles)
|
|
|
|
articles.GET(":id", s.getArticle)
|
|
|
|
articles.GET(":id/details", s.getArticleDetails)
|
|
|
|
articles.GET("by/source/:id", s.ListArticlesBySourceId)
|
2024-04-23 22:18:07 -07:00
|
|
|
|
2024-04-28 12:32:51 -07:00
|
|
|
//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)
|
2024-04-23 22:18:07 -07:00
|
|
|
|
2024-04-28 11:40:51 -07:00
|
|
|
//queue := v1.Group("/queue")
|
|
|
|
//queue.GET("/discord/webhooks", s.ListDiscordWebhookQueue) // TODO this needs to be reworked
|
2024-04-23 22:18:07 -07:00
|
|
|
|
2024-04-28 11:40:51 -07:00
|
|
|
//settings := v1.Group("/settings")
|
|
|
|
//settings.GET("/", s.getSettings)
|
2024-04-23 22:18:07 -07:00
|
|
|
|
|
|
|
sources := v1.Group("/sources")
|
2024-05-04 11:47:30 -07:00
|
|
|
sources.Use(echojwt.WithConfig(jwtConfig))
|
2024-05-02 17:36:39 -07:00
|
|
|
sources.GET("", s.listSources)
|
2024-04-23 22:18:07 -07:00
|
|
|
sources.GET("/by/source", s.listSourcesBySource)
|
|
|
|
sources.GET("/by/sourceAndName", s.GetSourceBySourceAndName)
|
2024-04-28 19:29:49 -07:00
|
|
|
//sources.POST("/new/reddit", s.newRedditSource)
|
|
|
|
//sources.POST("/new/youtube", s.newYoutubeSource)
|
|
|
|
//sources.POST("/new/twitch", s.newTwitchSource)
|
|
|
|
sources.POST("/new/rss", s.newRssSource)
|
2024-06-02 19:54:04 -07:00
|
|
|
sources.GET("/:id", s.getSource)
|
|
|
|
sources.DELETE("/:id", s.deleteSources)
|
|
|
|
sources.POST("/:id/disable", s.disableSource)
|
|
|
|
sources.POST("/:id/enable", s.enableSource)
|
2024-04-23 22:18:07 -07:00
|
|
|
|
2024-05-07 18:19:41 -07:00
|
|
|
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)
|
2024-05-26 09:00:33 -07:00
|
|
|
users.POST("/refresh/token", s.RefreshJwtToken)
|
|
|
|
users.POST("/refresh/sessionToken", s.NewSessionToken)
|
2024-05-07 18:19:41 -07:00
|
|
|
|
2024-05-02 17:36:39 -07:00
|
|
|
s.Router = router
|
2024-04-23 22:18:07 -07:00
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
2024-05-07 22:01:32 -07:00
|
|
|
//type ApiStatusModel struct {
|
|
|
|
// StatusCode int `json:"status"`
|
|
|
|
// Message string `json:"message"`
|
|
|
|
//}
|
2024-04-23 22:18:07 -07:00
|
|
|
|
2024-05-07 22:01:32 -07:00
|
|
|
//type ApiError struct {
|
|
|
|
// *ApiStatusModel
|
|
|
|
//}
|
2024-04-23 22:18:07 -07:00
|
|
|
|
2024-06-02 17:27:36 -07:00
|
|
|
//func (s *Handler) WriteError(c echo.Context, errMessage error, HttpStatusCode int) error {
|
|
|
|
// return c.JSON(HttpStatusCode, domain.BaseResponse{
|
|
|
|
// Message: errMessage.Error(),
|
|
|
|
// IsError: true,
|
|
|
|
// })
|
|
|
|
//}
|
2024-04-28 11:40:51 -07:00
|
|
|
|
2024-06-02 17:27:36 -07:00
|
|
|
//func (s *Handler) WriteMessage(c echo.Context, msg string, HttpStatusCode int) error {
|
|
|
|
// return c.JSON(HttpStatusCode, domain.BaseResponse{
|
|
|
|
// Message: msg,
|
|
|
|
// })
|
|
|
|
//}
|
2024-05-04 11:47:30 -07:00
|
|
|
|
2024-06-02 17:27:36 -07:00
|
|
|
//func (s *Handler) InternalServerErrorResponse(c echo.Context, msg string) error {
|
|
|
|
// return c.JSON(http.StatusInternalServerError, domain.BaseResponse{
|
|
|
|
// Message: msg,
|
|
|
|
// })
|
|
|
|
//}
|
2024-05-04 11:47:30 -07:00
|
|
|
|
2024-06-02 17:27:36 -07:00
|
|
|
//func (s *Handler) UnauthorizedResponse(c echo.Context, msg string) error {
|
|
|
|
// return c.JSON(http.StatusUnauthorized, domain.BaseResponse{
|
|
|
|
// Message: msg,
|
|
|
|
// })
|
|
|
|
//}
|
2024-05-07 18:19:41 -07:00
|
|
|
|
|
|
|
// 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.
|
2024-05-07 22:01:32 -07:00
|
|
|
func (s *Handler) ValidateJwtToken(c echo.Context, requiredScope string) (JwtToken, error) {
|
2024-05-07 18:19:41 -07:00
|
|
|
token, err := s.getJwtTokenFromContext(c)
|
|
|
|
if err != nil {
|
2024-06-02 17:27:36 -07:00
|
|
|
return JwtToken{}, errors.New(ErrJwtMissing)
|
2024-05-07 18:19:41 -07:00
|
|
|
}
|
|
|
|
|
2024-05-07 22:01:32 -07:00
|
|
|
err = token.hasExpired()
|
2024-05-07 18:19:41 -07:00
|
|
|
if err != nil {
|
2024-05-07 22:01:32 -07:00
|
|
|
return JwtToken{}, errors.New(ErrJwtExpired)
|
2024-05-07 18:19:41 -07:00
|
|
|
}
|
|
|
|
|
2024-05-07 22:01:32 -07:00
|
|
|
err = token.hasScope(requiredScope)
|
|
|
|
if err != nil {
|
|
|
|
return JwtToken{}, errors.New(ErrJwtScopeMissing)
|
2024-05-07 18:19:41 -07:00
|
|
|
}
|
|
|
|
|
2024-05-07 22:01:32 -07:00
|
|
|
if token.Iss != s.config.ServerAddress {
|
|
|
|
return JwtToken{}, errors.New(ErrJwtInvalidIssuer)
|
2024-05-26 09:00:33 -07:00
|
|
|
}
|
|
|
|
|
2024-06-02 17:27:36 -07:00
|
|
|
// If you are the built in admin account, skip the username and session token check
|
|
|
|
if token.UserName == "admin" {
|
|
|
|
return token, nil
|
|
|
|
}
|
|
|
|
|
2024-05-26 09:00:33 -07:00
|
|
|
user, err := s.repo.Users.GetUser(c.Request().Context(), token.UserName)
|
|
|
|
if err != nil {
|
|
|
|
return JwtToken{}, errors.New("user record not found")
|
|
|
|
}
|
|
|
|
|
|
|
|
if user.SessionToken != token.SessionToken {
|
|
|
|
return JwtToken{}, errors.New("invalid session token")
|
2024-05-07 18:19:41 -07:00
|
|
|
}
|
|
|
|
|
2024-05-07 22:01:32 -07:00
|
|
|
return token, nil
|
2024-05-07 19:14:37 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Handler) GetUserIdFromJwtToken(c echo.Context) int64 {
|
|
|
|
token, err := s.getJwtTokenFromContext(c)
|
|
|
|
if err != nil {
|
2024-06-02 17:27:36 -07:00
|
|
|
return -1
|
2024-05-07 19:14:37 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return token.GetUserId()
|
|
|
|
}
|