add shortlink generator and handlers

This commit is contained in:
Sergey Chubaryan 2024-08-05 09:58:45 +03:00
parent 233c5cb057
commit 10805ef2d5
3 changed files with 130 additions and 0 deletions

View File

@ -90,6 +90,11 @@ func main() {
ActionTokenRepo: actionTokenRepo, ActionTokenRepo: actionTokenRepo,
}, },
) )
linkService := services.NewShortlinkSevice(
services.NewShortlinkServiceParams{
Cache: repo.NewCacheInmem[string, string](7 * 24 * 60 * 60),
},
)
if !debugMode { if !debugMode {
gin.SetMode(gin.ReleaseMode) gin.SetMode(gin.ReleaseMode)
@ -99,6 +104,10 @@ func main() {
r.Use(middleware.NewRequestLogMiddleware(logger)) r.Use(middleware.NewRequestLogMiddleware(logger))
r.Use(gin.Recovery()) r.Use(gin.Recovery())
linkGroup := r.Group("/s")
linkGroup.POST("/new", handlers.NewShortlinkCreateHandler(linkService))
linkGroup.GET("/:linkId", handlers.NewShortlinkResolveHandler(linkService))
userGroup := r.Group("/user") userGroup := r.Group("/user")
userGroup.POST("/create", handlers.NewUserCreateHandler(userService)) userGroup.POST("/create", handlers.NewUserCreateHandler(userService))
userGroup.POST("/login", handlers.NewUserLoginHandler(userService)) userGroup.POST("/login", handlers.NewUserLoginHandler(userService))

View File

@ -0,0 +1,61 @@
package handlers
import (
"backend/src/services"
"encoding/json"
"fmt"
"net/url"
"github.com/gin-gonic/gin"
)
type shortlinkCreateOutput struct {
Link string `json:"link"`
}
func NewShortlinkCreateHandler(shortlinkService services.ShortlinkService) gin.HandlerFunc {
return func(ctx *gin.Context) {
rawUrl := ctx.Query("url")
if rawUrl == "" {
ctx.AbortWithError(400, fmt.Errorf("no url param"))
return
}
u, err := url.Parse(rawUrl)
if err != nil {
ctx.Data(500, "plain/text", []byte(err.Error()))
return
}
u.Scheme = "https"
linkId, err := shortlinkService.CreateLink(u.String())
if err != nil {
ctx.Data(500, "plain/text", []byte(err.Error()))
return
}
resultBody, err := json.Marshal(shortlinkCreateOutput{
Link: "https:/nucrea.ru/s/" + linkId,
})
if err != nil {
ctx.AbortWithError(500, err)
return
}
ctx.Data(200, "application/json", resultBody)
}
}
func NewShortlinkResolveHandler(shortlinkService services.ShortlinkService) gin.HandlerFunc {
return func(ctx *gin.Context) {
linkId := ctx.Param("linkId")
linkUrl, err := shortlinkService.GetLink(linkId)
if err != nil {
ctx.AbortWithError(500, err)
return
}
ctx.Redirect(301, linkUrl)
}
}

View File

@ -0,0 +1,60 @@
package services
import (
"backend/src/repo"
"fmt"
"math/rand"
"strings"
"time"
)
type ShortlinkService interface {
CreateLink(in string) (string, error)
GetLink(id string) (string, error)
}
type NewShortlinkServiceParams struct {
Endpoint string
Cache repo.Cache[string, string]
}
func NewShortlinkSevice(params NewShortlinkServiceParams) ShortlinkService {
return &shortlinkService{
cache: params.Cache,
}
}
type shortlinkService struct {
cache repo.Cache[string, string]
}
func (s *shortlinkService) randomStr() string {
src := rand.NewSource(time.Now().UnixMicro())
randGen := rand.New(src)
builder := strings.Builder{}
for i := 0; i < 9; i++ {
offset := 0x41
if randGen.Int()%2 == 1 {
offset = 0x61
}
byte := offset + (randGen.Int() % 26)
builder.WriteRune(rune(byte))
}
return builder.String()
}
func (s *shortlinkService) CreateLink(in string) (string, error) {
str := s.randomStr()
s.cache.Set(str, in, 7*24*60*60)
return str, nil
}
func (s *shortlinkService) GetLink(id string) (string, error) {
val, ok := s.cache.Get(id)
if !ok {
return "", fmt.Errorf("link does not exist or expired")
}
return val, nil
}