From 5d9fdce14f0336aa72191aeb01d061199bf8fb53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=BCtikofer?= Date: Fri, 22 Dec 2023 21:02:13 +0100 Subject: [PATCH] =?UTF-8?q?encode=20short=20urls,=20emojis=20=F0=9F=A5=B3?= =?UTF-8?q?=20(#10)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/api/url.go | 5 ++++- internal/model/url.go | 14 ++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/cmd/api/url.go b/cmd/api/url.go index 80ef9f9..1283b18 100644 --- a/cmd/api/url.go +++ b/cmd/api/url.go @@ -3,6 +3,7 @@ package main import ( "fmt" "net/http" + url2 "net/url" "os" "path/filepath" "strings" @@ -18,7 +19,7 @@ import ( func (app *application) redirectUrlHandler(c echo.Context) error { wildcardValue := c.Param("*") - shortUrl := strings.TrimSuffix(wildcardValue, "/") + shortUrl := url2.PathEscape(strings.TrimSuffix(wildcardValue, "/")) url, err := app.models.Urls.GetRedirect(shortUrl) if err != nil { return c.JSON(http.StatusInternalServerError, err.Error()) @@ -58,6 +59,8 @@ func (app *application) createUrlHandlerPost(c echo.Context) error { app.sessionManager.Put(c.Request().Context(), "flash_error", "URL cannot start with shrink.ch/s/") case "user id is required": app.sessionManager.Put(c.Request().Context(), "flash_error", "User ID is required.") + case "short_url is too long": + app.sessionManager.Put(c.Request().Context(), "flash_error", "Short URL is too long.") default: app.sessionManager.Put(c.Request().Context(), "flash_error", "Failed to create url.") } diff --git a/internal/model/url.go b/internal/model/url.go index 2935c2e..2462680 100644 --- a/internal/model/url.go +++ b/internal/model/url.go @@ -6,11 +6,13 @@ import ( "strings" "time" + url2 "net/url" + "github.com/google/uuid" "gorm.io/gorm" ) -// Create a UrlModel struct which wraps the connection pool. +// UrlModel is a struct which wraps the connection pool. type UrlModel struct { DB *gorm.DB } @@ -19,7 +21,7 @@ type Url struct { gorm.Model ID uuid.UUID `gorm:"type:uuid;default:gen_random_uuid();primary_key" json:"id,omitempty"` Original string `gorm:"type:varchar(2048);not null;uniqueIndex" json:"original"` - ShortUrl string `gorm:"type:varchar(11);not null;uniqueIndex" json:"short_url"` + ShortUrl string `gorm:"type:varchar(256);not null;uniqueIndex" json:"short_url"` QRCodeURL string `gorm:"type:varchar(2048)" json:"qr_code_url,omitempty"` UserID uuid.UUID `gorm:"type:uuid" json:"user_id"` User User `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"` @@ -71,7 +73,7 @@ func (u *UrlModel) Create(urlReq *UrlCreateRequest) (Url, error) { return Url{}, fmt.Errorf("url cannot start with shrink.ch/s/") } if urlReq.ShortCode != "" { - url.ShortUrl = urlReq.ShortCode + url.ShortUrl = url2.PathEscape(urlReq.ShortCode) } else { id := base62Encode(rand.Uint64()) url.ShortUrl = id @@ -85,6 +87,9 @@ func (u *UrlModel) Create(urlReq *UrlCreateRequest) (Url, error) { if strings.Contains(result.Error.Error(), "duplicate key value violates unique constraint") { return Url{}, fmt.Errorf("url already exists") } + if strings.Contains(result.Error.Error(), "value too long for type character") { + return Url{}, fmt.Errorf("short_url is too long") + } return Url{}, result.Error } @@ -125,10 +130,11 @@ func (u *UrlModel) GetUrlByUser(userId uuid.UUID) (*[]UrlByUserResponse, error) resp := []UrlByUserResponse{} for _, url := range urls { + shortUrl, _ := url2.PathUnescape(url.ShortUrl) resp = append(resp, UrlByUserResponse{ ID: url.ID, Original: url.Original, - ShortUrl: url.ShortUrl, + ShortUrl: shortUrl, Visits: url.Visits, QRCodeURL: url.QRCodeURL, CreatedAt: url.CreatedAt,