wip: add api auth and /resume endpoint

This commit is contained in:
cătălin 2023-12-04 22:13:37 +01:00
commit aaec8ec08d
Signed by: catalin
GPG key ID: 0178DF42F43E5FD2
23 changed files with 1244 additions and 271 deletions

View file

@ -0,0 +1,43 @@
package http
import (
"git.roboces.dev/catalin/cvvvvv/internal/config"
v1 "git.roboces.dev/catalin/cvvvvv/internal/delivery/http/v1"
"git.roboces.dev/catalin/cvvvvv/internal/service"
"git.roboces.dev/catalin/cvvvvv/pkg/limiter"
"github.com/gin-gonic/gin"
)
type Handler struct {
services *service.Services
}
func NewHandler(services *service.Services) *Handler {
return &Handler{
services: services,
}
}
func (h *Handler) Init(cfg *config.Config) *gin.Engine {
// Init gin handler
router := gin.Default()
router.Use(
gin.Recovery(),
gin.Logger(),
limiter.Limit(cfg.Limiter.RPS, cfg.Limiter.Burst, cfg.Limiter.TTL),
corsMiddleware,
)
h.initAPI(router)
return router
}
func (h *Handler) initAPI(router *gin.Engine) {
handlerV1 := v1.NewHandler(h.services)
api := router.Group("/api")
{
handlerV1.Init(api)
}
}

View file

@ -0,0 +1,20 @@
package http
import (
"net/http"
"github.com/gin-gonic/gin"
)
func corsMiddleware(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "*")
c.Header("Access-Control-Allow-Headers", "*")
c.Header("Content-Type", "application/json")
if c.Request.Method != "OPTIONS" {
c.Next()
} else {
c.AbortWithStatus(http.StatusOK)
}
}

View file

@ -0,0 +1,25 @@
package v1
import (
"git.roboces.dev/catalin/cvvvvv/internal/service"
"github.com/gin-gonic/gin"
)
type Handler struct {
services *service.Services
}
func NewHandler(services *service.Services) *Handler {
return &Handler{
services: services,
}
}
func (h *Handler) Init(api *gin.RouterGroup) {
v1 := api.Group("/v1")
{
v1.GET("/usage", h.getUsage)
v1.GET("/ping", h.getPing)
v1.GET("/resume", h.getResume)
}
}

View file

@ -0,0 +1,30 @@
package v1
import (
"errors"
"github.com/gin-gonic/gin"
)
func containsValue(m map[string]string, value string) (string, error) {
for _, v := range m {
if v == value {
return v, nil
}
}
return value, errors.New("value is not in the provided map")
}
func (h *Handler) VerifyAuthentication(ctx *gin.Context) (string, error) {
header := ctx.GetHeader("X-API-KEY")
if header != "" {
return containsValue(h.services.Config.Auth.ApiKeys, header)
}
key := ctx.Query("api_key")
if key != "" {
return containsValue(h.services.Config.Auth.ApiKeys, key)
}
return "", errors.New("Missing API key")
}

View file

@ -0,0 +1,12 @@
package v1
import (
"net/http"
"github.com/gin-gonic/gin"
)
func (h *Handler) getPing(c *gin.Context) {
c.Writer.Header().Set("Content-Type", "text/plain")
c.String(http.StatusOK, "pong")
}

View file

@ -0,0 +1,19 @@
package v1
import (
"git.roboces.dev/catalin/cvvvvv/pkg/logger"
"github.com/gin-gonic/gin"
)
type dataResponse struct {
Data interface{} `json:"data"`
}
type response struct {
Message string `json:"message"`
}
func newResponse(c *gin.Context, statusCode int, message string) {
logger.Error(message)
c.AbortWithStatusJSON(statusCode, response{message})
}

View file

@ -0,0 +1,24 @@
package v1
import (
"net/http"
"git.roboces.dev/catalin/cvvvvv/internal/domain"
"github.com/gin-gonic/gin"
)
type ResumeResponse struct {
Resume domain.Resume `json:"resume"`
}
func (h *Handler) getResume(ctx *gin.Context) {
_, err := h.VerifyAuthentication(ctx)
if err != nil {
newResponse(ctx, http.StatusUnauthorized, "Api key not provided or invalid")
return
}
ctx.JSON(http.StatusOK, ResumeResponse{
Resume: h.services.Resumes.Get(),
})
}

View file

@ -0,0 +1,25 @@
package v1
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
)
func (h *Handler) getUsage(c *gin.Context) {
c.Writer.Header().Set("Content-Type", "text/plain")
message := fmt.Sprintf(`
Hello! If you're seeing this message it means you are authorized to view my CV.
You should have been provided an api key in order to query the different endpoints,
which are not public. You can put the key into a X-API-KEY header or use it
as a query param. Examples:
curl %s/api/v1/usage -H "X-API-KEY: verysecret"
curl %s/api/v1/usage?api_key=verysecret
`, c.Request.Host, c.Request.Host)
c.String(http.StatusOK, message)
}