Skip to content
Open
4 changes: 3 additions & 1 deletion cmd/shortener/shortener.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ func main() {
var serverAddr string
var baseURL string
var dbFile string
var dbCredentials string

flag.StringVar(&serverAddr, "a", os.Getenv("SERVER_ADDRESS"), "server address")
flag.StringVar(&baseURL, "b", os.Getenv("BASE_URL"), "base URL")
flag.StringVar(&dbFile, "f", os.Getenv("FILE_STORAGE_PATH"), "file storage path")
flag.StringVar(&dbCredentials, "d", os.Getenv("DATABASE_DSN"), "database credentials")
flag.Parse()

if serverAddr == "" {
Expand All @@ -25,7 +27,7 @@ func main() {
baseURL = "http://" + serverAddr
}

err := app.Start(serverAddr, baseURL, dbFile)
err := app.Start(serverAddr, baseURL, dbFile, dbCredentials)
if err != nil {
panic(err)
}
Expand Down
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@ require (
)

require (
github.com/google/uuid v1.3.0
github.com/jmoiron/sqlx v1.3.5
github.com/labstack/echo v3.3.10+incompatible
github.com/labstack/gommon v0.3.1 // indirect
github.com/lib/pq v1.10.7
github.com/mattn/go-colorable v0.1.11 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
Expand Down
9 changes: 9 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,16 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
Expand All @@ -29,10 +34,14 @@ github.com/labstack/echo/v4 v4.8.0 h1:wdc6yKVaHxkNOEdz4cRZs1pQkwSXPiRjq69yWP4QQS
github.com/labstack/echo/v4 v4.8.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks=
github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o=
github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
Expand Down
6 changes: 4 additions & 2 deletions internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import (
"github.com/labstack/echo/v4/middleware"
)

func Start(serverAddr, baseURL, dbFile string) error {
h, err := handler.New(serverAddr, baseURL, dbFile)
func Start(serverAddr, baseURL, dbFile, dbCredentials string) error {
h, err := handler.New(serverAddr, baseURL, dbFile, dbCredentials)
if err != nil {
return fmt.Errorf("handler: %v", err)
}
Expand All @@ -22,6 +22,8 @@ func Start(serverAddr, baseURL, dbFile string) error {
e.POST("/", h.CreateURL)
e.GET("/:id", h.RetrieveURL)
e.POST("/api/shorten", h.CreateURLInJSON)
e.GET("/api/user/urls", h.ListURL)
e.GET("/ping", h.Ping)

e.Logger.Fatal(e.Start(serverAddr))

Expand Down
106 changes: 94 additions & 12 deletions internal/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package handler

import (
"crypto"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
Expand All @@ -12,24 +15,29 @@ import (
"github.com/combodga/Project/internal/storage"

"github.com/btcsuite/btcutil/base58"
"github.com/google/uuid"
"github.com/labstack/echo/v4"
)

type Handler struct {
ServerAddr string
BaseURL string
Storage *storage.Storage
ServerAddr string
BaseURL string
Storage *storage.Storage
DBCredentials string
Key string
}

func New(serverAddr, baseURL, dbFile string) (*Handler, error) {
s, err := storage.New(dbFile)
func New(serverAddr, baseURL, dbFile, dbCredentials string) (*Handler, error) {
s, err := storage.New(dbFile, dbCredentials)
if err != nil {
err = fmt.Errorf("storage: %v", err)
}
return &Handler{
ServerAddr: serverAddr,
BaseURL: baseURL,
Storage: s,
ServerAddr: serverAddr,
BaseURL: baseURL,
Storage: s,
DBCredentials: dbCredentials,
Key: "b8ffa0f4-3f11-44b1-b0bf-9109f47e468b",
}, err
}

Expand All @@ -38,14 +46,15 @@ type Link struct {
}

func (h *Handler) CreateURL(c echo.Context) error {
user := getUser(c, h.Key)
body, err := io.ReadAll(c.Request().Body)
if err != nil {
return err
}

link := string(body)

id, err := h.fetchID(c, link)
id, err := h.fetchID(c, user, link)
if err != nil {
return fmt.Errorf("fetch id: %v", err)
}
Expand All @@ -54,6 +63,7 @@ func (h *Handler) CreateURL(c echo.Context) error {
}

func (h *Handler) CreateURLInJSON(c echo.Context) error {
user := getUser(c, h.Key)
body, err := io.ReadAll(c.Request().Body)
if err != nil {
return err
Expand All @@ -70,7 +80,7 @@ func (h *Handler) CreateURLInJSON(c echo.Context) error {
return errors.New("error reading json")
}

id, err := h.fetchID(c, link)
id, err := h.fetchID(c, user, link)
if err != nil {
return fmt.Errorf("fetchID: %v", err)
}
Expand All @@ -92,7 +102,39 @@ func (h *Handler) RetrieveURL(c echo.Context) error {
return c.Redirect(http.StatusTemporaryRedirect, url)
}

func (h *Handler) fetchID(c echo.Context, link string) (string, error) {
type Element struct {
ShortURL string `json:"short_url"`
OriginalURL string `json:"original_url"`
}

func (h *Handler) ListURL(c echo.Context) error {
user := getUser(c, h.Key)
list, ok := h.Storage.ListURL(user)
if !ok {
return c.String(http.StatusNoContent, "error, you haven't any saved links")
}

var arr []*Element
for shortURL, originalURL := range list {
arr = append(arr, &Element{
ShortURL: h.BaseURL + "/" + shortURL,
OriginalURL: originalURL,
})
}

return c.JSON(http.StatusOK, arr)
}

func (h *Handler) Ping(c echo.Context) error {
ok := h.Storage.Ping()
if !ok {
return c.String(http.StatusInternalServerError, "error, no connection to db")
}

return c.String(http.StatusOK, "db connected")
}

func (h *Handler) fetchID(c echo.Context, user, link string) (string, error) {
if len(link) > 2048 {
return "", c.String(http.StatusBadRequest, "error, the link cannot be longer than 2048 characters")
}
Expand All @@ -110,7 +152,7 @@ func (h *Handler) fetchID(c echo.Context, link string) (string, error) {
}
}

err = h.Storage.SetURL(id, link)
err = h.Storage.SetURL(user, id, link)
if err != nil {
return "", c.String(http.StatusInternalServerError, "error, failed to store a shortened URL")
}
Expand All @@ -130,3 +172,43 @@ func shortener(s string) (string, error) {

return id, nil
}

func getUser(c echo.Context, key string) string {
user, err1 := readCookie(c, "user")
sign, err2 := readCookie(c, "sign")
if err1 == nil && err2 == nil && sign == getSign(user, key) {
return user
}

user = randUser()
writeCookie(c, "user", user)
writeCookie(c, "sign", getSign(user, key))
return user
}

func randUser() string {
uuidWithHyphen := uuid.New()
return uuidWithHyphen.String()
}

func getSign(user, key string) string {
h := hmac.New(sha256.New, []byte(key))
h.Write([]byte(user))
dst := h.Sum(nil)
return hex.EncodeToString(dst)[:32]
}

func writeCookie(c echo.Context, name, value string) {
cookie := new(http.Cookie)
cookie.Name = name
cookie.Value = value
c.SetCookie(cookie)
}

func readCookie(c echo.Context, name string) (string, error) {
cookie, err := c.Cookie(name)
if err != nil {
return "", err
}
return cookie.Value, nil
}
2 changes: 1 addition & 1 deletion internal/handler/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ var (

func TestInit(t *testing.T) {
var err error
H, err = New("localhost:8080", "http://localhost:8080", "")
H, err = New("localhost:8080", "http://localhost:8080", "", "")
if err != nil {
t.Fatal("can't start test")
}
Expand Down
Loading