From c704005ed279f2655bc974c0226c92f9d23063cd Mon Sep 17 00:00:00 2001 From: Andrey Chervyakov Date: Mon, 15 Mar 2021 23:32:24 +0600 Subject: [PATCH 01/13] Add link update model --- link/dto.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/link/dto.go b/link/dto.go index 204aa01..d8b2e68 100644 --- a/link/dto.go +++ b/link/dto.go @@ -18,6 +18,11 @@ type ResourceModel struct { CreationTime int64 `json:"creationTime"` } +type UpdateModel struct { + Name string `json:"name,omitempty"` + RedirectURL string `json:"redirectUrl,omitempty"` +} + func (m *CreationModel) MapModelToEntity() (*Link, error) { u, err := url.Parse(m.RedirectURL) if err != nil { From ac066be7022bef39f1d51f4d7f5f27804e003545 Mon Sep 17 00:00:00 2001 From: Andrey Chervyakov Date: Mon, 15 Mar 2021 23:32:51 +0600 Subject: [PATCH 02/13] Add blueprint for link update endpoint --- link/handlers.go | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/link/handlers.go b/link/handlers.go index 804230d..da5fc13 100644 --- a/link/handlers.go +++ b/link/handlers.go @@ -82,6 +82,22 @@ func allRetrievalHandler(c echo.Context, serv *PgService) error { return c.JSON(http.StatusOK, models) } +/*func updateHandler(c echo.Context, serv *PgService) error { + linkId := c.Param("id") + + var model UpdateModel + if err := json.NewDecoder(c.Request().Body).Decode(&model); err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "Invalid data format.") + } + + l, err := serv.GetById(linkId) + if err != nil { + return err + } + + return c.NoContent(http.StatusOK) +}*/ + func removalHandler(c echo.Context, serv *PgService) error { linkId := c.Param("id") @@ -95,21 +111,26 @@ func removalHandler(c echo.Context, serv *PgService) error { func AddHandlers(s *echo.Echo, pool *pgxpool.Pool) { serv := NewPgService(PgRepository{pool: pool}) - gr := s.Group("/links") + linksGroup := s.Group("/links") + exactLinkGroup := linksGroup.Group("/:id") - gr.POST("", func(ctx echo.Context) error { + linksGroup.POST("", func(ctx echo.Context) error { return creationHandler(ctx, &serv) }) - gr.GET("", func(ctx echo.Context) error { + linksGroup.GET("", func(ctx echo.Context) error { return allRetrievalHandler(ctx, &serv) }) - gr.GET("/:id", func(ctx echo.Context) error { + exactLinkGroup.GET("", func(ctx echo.Context) error { return retrievalByIdHandler(ctx, &serv) }) - gr.DELETE("/:id", func(ctx echo.Context) error { + /*exactLinkGroup.PATCH("", func(ctx echo.Context) error { + return updateHandler(ctx, &serv) + })*/ + + exactLinkGroup.DELETE("", func(ctx echo.Context) error { return removalHandler(ctx, &serv) }) From 9ae733e618d98f3d8212a06e1eee1d9d70f41093 Mon Sep 17 00:00:00 2001 From: Andrey Chervyakov Date: Mon, 15 Mar 2021 23:42:08 +0600 Subject: [PATCH 03/13] Fix link resource endpoint being insecured --- server.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server.go b/server.go index b658279..08e7256 100644 --- a/server.go +++ b/server.go @@ -10,6 +10,7 @@ import ( "github.com/labstack/echo/v4/middleware" "github.com/rs/zerolog/log" "net/http" + "regexp" ) func NewServer(conf *koanf.Koanf, pool *pgxpool.Pool) *echo.Echo { @@ -30,7 +31,9 @@ func addMiddleware(s *echo.Echo, conf *koanf.Koanf) { s.Use(middleware.JWTWithConfig(middleware.JWTConfig{ Skipper: func(ctx echo.Context) bool { - return ctx.Request().Method == "GET" && ctx.Request().URL.Path != "/links" + matchesLinks, _ := regexp.MatchString("^/links", ctx.Request().URL.Path) + + return ctx.Request().Method == "GET" && !matchesLinks }, SigningMethod: conf.String("jwt.method"), SigningKey: conf.Bytes("jwt.key"), From 8a234258269c4a06dc927088780fbb02dcb96e05 Mon Sep 17 00:00:00 2001 From: Andrey Chervyakov Date: Tue, 16 Mar 2021 00:45:13 +0600 Subject: [PATCH 04/13] Add endpoint for updating link --- link/handlers.go | 33 ++++++++++++++++++++++++++++----- link/repository.go | 33 +++++++++++++++++++++++++++++++++ link/service.go | 9 +++++++++ 3 files changed, 70 insertions(+), 5 deletions(-) diff --git a/link/handlers.go b/link/handlers.go index da5fc13..6892a35 100644 --- a/link/handlers.go +++ b/link/handlers.go @@ -5,6 +5,7 @@ import ( "github.com/jackc/pgx/v4/pgxpool" "github.com/labstack/echo/v4" "net/http" + "net/url" "strconv" ) @@ -82,7 +83,7 @@ func allRetrievalHandler(c echo.Context, serv *PgService) error { return c.JSON(http.StatusOK, models) } -/*func updateHandler(c echo.Context, serv *PgService) error { +func updateHandler(c echo.Context, serv *PgService) error { linkId := c.Param("id") var model UpdateModel @@ -90,13 +91,35 @@ func allRetrievalHandler(c echo.Context, serv *PgService) error { return echo.NewHTTPError(http.StatusBadRequest, "Invalid data format.") } - l, err := serv.GetById(linkId) + updatingLink, err := serv.GetById(linkId) if err != nil { return err } + hasChanges := false + switch { + case model.Name != "" && model.Name != updatingLink.Name: + updatingLink.Name = model.Name + + hasChanges = true + case model.RedirectURL != "" && model.RedirectURL != updatingLink.RedirectURL.String(): + if parsedUrl, err := url.Parse(model.RedirectURL); err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "Invalid URL value.") + } else { + updatingLink.RedirectURL = *parsedUrl + } + + hasChanges = true + } + + if hasChanges { + if err = serv.Update(updatingLink); err != nil { + return err + } + } + return c.NoContent(http.StatusOK) -}*/ +} func removalHandler(c echo.Context, serv *PgService) error { linkId := c.Param("id") @@ -126,9 +149,9 @@ func AddHandlers(s *echo.Echo, pool *pgxpool.Pool) { return retrievalByIdHandler(ctx, &serv) }) - /*exactLinkGroup.PATCH("", func(ctx echo.Context) error { + exactLinkGroup.PATCH("", func(ctx echo.Context) error { return updateHandler(ctx, &serv) - })*/ + }) exactLinkGroup.DELETE("", func(ctx echo.Context) error { return removalHandler(ctx, &serv) diff --git a/link/repository.go b/link/repository.go index f07792e..6ab2fb2 100644 --- a/link/repository.go +++ b/link/repository.go @@ -16,6 +16,7 @@ type Repository interface { Save(link *Link) error FindById(id string) (*Link, error) FindAll(limit int, offset int) (Links, error) + Update(link *Link) error DeleteById(id string) error } @@ -135,6 +136,38 @@ func (r *PgRepository) FindAll(limit int, offset int) (Links, error) { return links, nil } +func (r *PgRepository) Update(link *Link) error { + if link.Id == "" { + return errors.New("link ID must not be empty") + } + + ctx, cancel := context.WithTimeout(context.Background(), defaultContextTimeout) + defer cancel() + + tx, err := r.pool.Begin(ctx) + if err != nil { + return err + } + + sql := ` + UPDATE links + SET name = $1, redirect_url = $2 + WHERE id = $3 + ` + + _, err = tx.Exec(ctx, sql, link.Name, link.RedirectURL.String(), link.Id) + if err != nil { + _ = tx.Rollback(ctx) + return err + } + + if err = tx.Commit(ctx); err != nil { + return err + } + + return nil +} + func (r *PgRepository) DeleteById(id string) error { ctx, cancel := context.WithTimeout(context.Background(), defaultContextTimeout) defer cancel() diff --git a/link/service.go b/link/service.go index 3c89321..f6acee1 100644 --- a/link/service.go +++ b/link/service.go @@ -10,6 +10,7 @@ type Service interface { Create(link *Link) error GetById(id string) (*Link, error) GetAll(limit int, offset int) (Links, error) + Update(data *Link) error DeleteById(id string) error } @@ -72,6 +73,14 @@ func (s *PgService) GetAll(limit int, offset int) (Links, error) { return links, nil } +func (s *PgService) Update(data *Link) error { + if err := s.rep.Update(data); err != nil { + return apperrors.UnknownError{Err: err} + } + + return nil +} + func (s *PgService) DeleteById(id string) error { s.cache.Delete(id) From ed027a03adf3e5c32d3d770b3f11df23415ff3dd Mon Sep 17 00:00:00 2001 From: Andrey Chervyakov Date: Tue, 16 Mar 2021 11:49:41 +0600 Subject: [PATCH 05/13] Update link repository to use DB pool directly --- link/repository.go | 62 ++++------------------------------------------ 1 file changed, 5 insertions(+), 57 deletions(-) diff --git a/link/repository.go b/link/repository.go index 6ab2fb2..f24a1f4 100644 --- a/link/repository.go +++ b/link/repository.go @@ -28,23 +28,13 @@ func (r *PgRepository) Save(link *Link) error { ctx, cancel := context.WithTimeout(context.Background(), defaultContextTimeout) defer cancel() - tx, err := r.pool.Begin(ctx) - if err != nil { - return err - } - sql := ` INSERT INTO links (id, name, redirect_url, creation_time) VALUES ($1, $2, $3, $4::timestamp) ` - _, err = tx.Exec(ctx, sql, link.Id, link.Name, link.RedirectURL.String(), link.CreationTime.Format("2006-01-02 15:04:05")) + _, err := r.pool.Exec(ctx, sql, link.Id, link.Name, link.RedirectURL.String(), link.CreationTime.Format("2006-01-02 15:04:05")) if err != nil { - _ = tx.Rollback(ctx) - return err - } - - if err = tx.Commit(ctx); err != nil { return err } @@ -55,31 +45,21 @@ func (r *PgRepository) FindById(id string) (*Link, error) { ctx, cancel := context.WithTimeout(context.Background(), defaultContextTimeout) defer cancel() - tx, err := r.pool.Begin(ctx) - if err != nil { - return nil, err - } - sql := ` SELECT id, name, redirect_url, creation_time FROM links WHERE id = $1 ` - entity, err := mapRowToEntity(tx.QueryRow(ctx, sql, id)) + entity, err := mapRowToEntity(r.pool.QueryRow(ctx, sql, id)) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return nil, nil } else { - _ = tx.Rollback(ctx) return nil, err } } - if err = tx.Commit(ctx); err != nil { - return nil, err - } - return entity, nil } @@ -94,11 +74,6 @@ func (r *PgRepository) FindAll(limit int, offset int) (Links, error) { ctx, cancel := context.WithTimeout(context.Background(), defaultContextTimeout) defer cancel() - tx, err := r.pool.Begin(ctx) - if err != nil { - return nil, err - } - sql := ` SELECT id, name, redirect_url, creation_time FROM links @@ -106,9 +81,8 @@ func (r *PgRepository) FindAll(limit int, offset int) (Links, error) { OFFSET $2 ` - rows, err := tx.Query(ctx, sql, limit, offset) + rows, err := r.pool.Query(ctx, sql, limit, offset) if err != nil { - _ = tx.Rollback(ctx) return nil, err } defer rows.Close() @@ -117,7 +91,6 @@ func (r *PgRepository) FindAll(limit int, offset int) (Links, error) { for rows.Next() { link, err := mapRowToEntity(rows) if err != nil { - _ = tx.Rollback(ctx) return nil, err } @@ -125,11 +98,6 @@ func (r *PgRepository) FindAll(limit int, offset int) (Links, error) { } if err = rows.Err(); err != nil { - _ = tx.Rollback(ctx) - return nil, err - } - - if err = tx.Commit(ctx); err != nil { return nil, err } @@ -144,24 +112,14 @@ func (r *PgRepository) Update(link *Link) error { ctx, cancel := context.WithTimeout(context.Background(), defaultContextTimeout) defer cancel() - tx, err := r.pool.Begin(ctx) - if err != nil { - return err - } - sql := ` UPDATE links SET name = $1, redirect_url = $2 WHERE id = $3 ` - _, err = tx.Exec(ctx, sql, link.Name, link.RedirectURL.String(), link.Id) + _, err := r.pool.Exec(ctx, sql, link.Name, link.RedirectURL.String(), link.Id) if err != nil { - _ = tx.Rollback(ctx) - return err - } - - if err = tx.Commit(ctx); err != nil { return err } @@ -172,22 +130,12 @@ func (r *PgRepository) DeleteById(id string) error { ctx, cancel := context.WithTimeout(context.Background(), defaultContextTimeout) defer cancel() - tx, err := r.pool.Begin(ctx) - if err != nil { - return err - } - sql := ` DELETE FROM links WHERE id = $1 ` - _, err = tx.Exec(ctx, sql, id) + _, err := r.pool.Exec(ctx, sql, id) if err != nil { - _ = tx.Rollback(ctx) - return err - } - - if err = tx.Commit(ctx); err != nil { return err } From 398d32f9674ce67c572827311dbde8519cb79f3e Mon Sep 17 00:00:00 2001 From: Andrey Chervyakov Date: Tue, 16 Mar 2021 13:24:44 +0600 Subject: [PATCH 06/13] Increase context timeout for link repository --- link/repository.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/link/repository.go b/link/repository.go index f24a1f4..a3a6319 100644 --- a/link/repository.go +++ b/link/repository.go @@ -10,7 +10,7 @@ import ( "time" ) -const defaultContextTimeout = 5 * time.Second +const defaultContextTimeout = 10 * time.Second type Repository interface { Save(link *Link) error From e426559cec5c3245f7a0a388799b07f16aa6ecec Mon Sep 17 00:00:00 2001 From: Andrey Chervyakov Date: Tue, 16 Mar 2021 13:26:25 +0600 Subject: [PATCH 07/13] Remove cache expiration for link service --- link/service.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/link/service.go b/link/service.go index f6acee1..5afecf4 100644 --- a/link/service.go +++ b/link/service.go @@ -3,7 +3,6 @@ package link import ( apperrors "cgnolink/errors" "github.com/patrickmn/go-cache" - "time" ) type Service interface { @@ -22,7 +21,7 @@ type PgService struct { func NewPgService(rep PgRepository) PgService { return PgService{ rep: rep, - cache: cache.New(1*time.Hour, 90*time.Minute), + cache: cache.New(cache.NoExpiration, 0), } } @@ -59,7 +58,7 @@ func (s *PgService) GetById(id string) (*Link, error) { return nil, apperrors.NotFoundError{Message: "Link with given ID was not found."} } - s.cache.Set(id, link, 0) + s.cache.Set(id, link, cache.DefaultExpiration) return link, nil } From ddaf0dcbfc415346ae44547f0e42a5e42b9bc230 Mon Sep 17 00:00:00 2001 From: Andrey Chervyakov Date: Wed, 17 Mar 2021 18:54:45 +0600 Subject: [PATCH 08/13] Refactor --- cmd/cgnolink/main.go | 5 +- link/handlers.go | 60 ++++++++++---------- link/repository.go | 4 ++ link/service.go | 6 +- middleware/logger.go => server/middleware.go | 2 +- server.go => server/server.go | 5 +- 6 files changed, 42 insertions(+), 40 deletions(-) rename middleware/logger.go => server/middleware.go (97%) rename server.go => server/server.go (95%) diff --git a/cmd/cgnolink/main.go b/cmd/cgnolink/main.go index 70635f9..562aa2a 100644 --- a/cmd/cgnolink/main.go +++ b/cmd/cgnolink/main.go @@ -3,10 +3,10 @@ package main import ( "cgnolink" "cgnolink/database" + appserver "cgnolink/server" "github.com/rs/zerolog" "github.com/rs/zerolog/log" "os" - "time" ) func main() { @@ -17,12 +17,11 @@ func main() { pool := database.Pool(conf) database.Migrate(pool) - server := cgnolink.NewServer(conf, pool) + server := appserver.NewServer(conf, pool) server.Logger.Fatal(server.Start(":8080")) } func configureLogger() { - zerolog.TimeFieldFormat = time.RFC3339Nano log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) } diff --git a/link/handlers.go b/link/handlers.go index 6892a35..aa7c178 100644 --- a/link/handlers.go +++ b/link/handlers.go @@ -9,20 +9,20 @@ import ( "strconv" ) -func redirectHandler(c echo.Context, serv *PgService) error { - linkId := c.Param("id") +func redirectHandler(ctx echo.Context, serv Service) error { + linkId := ctx.Param("id") link, err := serv.GetById(linkId) if err != nil { return err } - return c.Redirect(http.StatusSeeOther, link.RedirectURL.String()) + return ctx.Redirect(http.StatusSeeOther, link.RedirectURL.String()) } -func creationHandler(c echo.Context, serv *PgService) error { +func creationHandler(ctx echo.Context, serv Service) error { var model CreationModel - if err := json.NewDecoder(c.Request().Body).Decode(&model); err != nil { + if err := json.NewDecoder(ctx.Request().Body).Decode(&model); err != nil { return echo.NewHTTPError(http.StatusBadRequest, "Invalid data format.") } @@ -35,23 +35,23 @@ func creationHandler(c echo.Context, serv *PgService) error { return err } - return c.NoContent(http.StatusCreated) + return ctx.NoContent(http.StatusCreated) } -func retrievalByIdHandler(c echo.Context, serv *PgService) error { - linkId := c.Param("id") +func retrievalByIdHandler(ctx echo.Context, serv Service) error { + linkId := ctx.Param("id") l, err := serv.GetById(linkId) if err != nil { return err } - return c.JSON(http.StatusOK, MapEntityToModel(l)) + return ctx.JSON(http.StatusOK, MapEntityToModel(l)) } -func allRetrievalHandler(c echo.Context, serv *PgService) error { +func allRetrievalHandler(ctx echo.Context, serv Service) error { limit := 20 - if v := c.QueryParam("limit"); v != "" { + if v := ctx.QueryParam("limit"); v != "" { num, err := strconv.Atoi(v) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, "Invalid limit value.") @@ -61,7 +61,7 @@ func allRetrievalHandler(c echo.Context, serv *PgService) error { } offset := 0 - if v := c.QueryParam("offset"); v != "" { + if v := ctx.QueryParam("offset"); v != "" { num, err := strconv.Atoi(v) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, "Invalid offset value.") @@ -80,14 +80,14 @@ func allRetrievalHandler(c echo.Context, serv *PgService) error { models[i] = MapEntityToModel(v) } - return c.JSON(http.StatusOK, models) + return ctx.JSON(http.StatusOK, models) } -func updateHandler(c echo.Context, serv *PgService) error { - linkId := c.Param("id") +func updateHandler(ctx echo.Context, serv Service) error { + linkId := ctx.Param("id") var model UpdateModel - if err := json.NewDecoder(c.Request().Body).Decode(&model); err != nil { + if err := json.NewDecoder(ctx.Request().Body).Decode(&model); err != nil { return echo.NewHTTPError(http.StatusBadRequest, "Invalid data format.") } @@ -118,46 +118,46 @@ func updateHandler(c echo.Context, serv *PgService) error { } } - return c.NoContent(http.StatusOK) + return ctx.NoContent(http.StatusOK) } -func removalHandler(c echo.Context, serv *PgService) error { - linkId := c.Param("id") +func removalHandler(ctx echo.Context, serv Service) error { + linkId := ctx.Param("id") if err := serv.DeleteById(linkId); err != nil { return err } - return c.NoContent(http.StatusNoContent) + return ctx.NoContent(http.StatusNoContent) } -func AddHandlers(s *echo.Echo, pool *pgxpool.Pool) { - serv := NewPgService(PgRepository{pool: pool}) +func AddHandlers(server *echo.Echo, pool *pgxpool.Pool) { + serv := NewService(NewRepository(pool)) - linksGroup := s.Group("/links") + linksGroup := server.Group("/links") exactLinkGroup := linksGroup.Group("/:id") linksGroup.POST("", func(ctx echo.Context) error { - return creationHandler(ctx, &serv) + return creationHandler(ctx, serv) }) linksGroup.GET("", func(ctx echo.Context) error { - return allRetrievalHandler(ctx, &serv) + return allRetrievalHandler(ctx, serv) }) exactLinkGroup.GET("", func(ctx echo.Context) error { - return retrievalByIdHandler(ctx, &serv) + return retrievalByIdHandler(ctx, serv) }) exactLinkGroup.PATCH("", func(ctx echo.Context) error { - return updateHandler(ctx, &serv) + return updateHandler(ctx, serv) }) exactLinkGroup.DELETE("", func(ctx echo.Context) error { - return removalHandler(ctx, &serv) + return removalHandler(ctx, serv) }) - s.GET("/:id", func(ctx echo.Context) error { - return redirectHandler(ctx, &serv) + server.GET("/:id", func(ctx echo.Context) error { + return redirectHandler(ctx, serv) }) } diff --git a/link/repository.go b/link/repository.go index a3a6319..b31b9c3 100644 --- a/link/repository.go +++ b/link/repository.go @@ -24,6 +24,10 @@ type PgRepository struct { pool *pgxpool.Pool } +func NewRepository(pool *pgxpool.Pool) Repository { + return &PgRepository{pool: pool} +} + func (r *PgRepository) Save(link *Link) error { ctx, cancel := context.WithTimeout(context.Background(), defaultContextTimeout) defer cancel() diff --git a/link/service.go b/link/service.go index 5afecf4..241332c 100644 --- a/link/service.go +++ b/link/service.go @@ -14,12 +14,12 @@ type Service interface { } type PgService struct { - rep PgRepository + rep Repository cache *cache.Cache } -func NewPgService(rep PgRepository) PgService { - return PgService{ +func NewService(rep Repository) Service { + return &PgService{ rep: rep, cache: cache.New(cache.NoExpiration, 0), } diff --git a/middleware/logger.go b/server/middleware.go similarity index 97% rename from middleware/logger.go rename to server/middleware.go index b34af3c..04a7824 100644 --- a/middleware/logger.go +++ b/server/middleware.go @@ -1,4 +1,4 @@ -package middleware +package server import ( "github.com/labstack/echo/v4" diff --git a/server.go b/server/server.go similarity index 95% rename from server.go rename to server/server.go index 08e7256..11e23de 100644 --- a/server.go +++ b/server/server.go @@ -1,9 +1,8 @@ -package cgnolink +package server import ( apperrors "cgnolink/errors" "cgnolink/link" - appmiddleware "cgnolink/middleware" "github.com/jackc/pgx/v4/pgxpool" "github.com/knadh/koanf" "github.com/labstack/echo/v4" @@ -27,7 +26,7 @@ func NewServer(conf *koanf.Koanf, pool *pgxpool.Pool) *echo.Echo { func addMiddleware(s *echo.Echo, conf *koanf.Koanf) { s.Use(middleware.CORS()) - s.Use(appmiddleware.Logger()) + s.Use(Logger()) s.Use(middleware.JWTWithConfig(middleware.JWTConfig{ Skipper: func(ctx echo.Context) bool { From 458ae289016fda54a79a218a8b3618e98a188ed5 Mon Sep 17 00:00:00 2001 From: Andrey Chervyakov Date: Thu, 18 Mar 2021 20:53:59 +0600 Subject: [PATCH 09/13] Refactor link repository and improve logging --- cmd/cgnolink/main.go | 5 ++--- database/database.go | 26 ++++++++++++++++++++++---- database/migrate.go | 22 ++++++++++++++-------- database/util.go | 15 +++++++++++++++ link/repository.go | 10 ++++++++-- link/service.go | 2 +- server/middleware.go | 2 +- 7 files changed, 63 insertions(+), 19 deletions(-) create mode 100644 database/util.go diff --git a/cmd/cgnolink/main.go b/cmd/cgnolink/main.go index 562aa2a..4a72941 100644 --- a/cmd/cgnolink/main.go +++ b/cmd/cgnolink/main.go @@ -14,8 +14,7 @@ func main() { conf := cgnolink.NewConfig() - pool := database.Pool(conf) - database.Migrate(pool) + pool := database.InitPool(conf) server := appserver.NewServer(conf, pool) @@ -23,5 +22,5 @@ func main() { } func configureLogger() { - log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) + log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: "2006-01-02 15:04:05"}) } diff --git a/database/database.go b/database/database.go index a6314fb..5796452 100644 --- a/database/database.go +++ b/database/database.go @@ -6,16 +6,34 @@ import ( "fmt" "github.com/jackc/pgx/v4/pgxpool" "github.com/knadh/koanf" + "github.com/rs/zerolog/log" + "time" ) -func Pool(config *koanf.Koanf) *pgxpool.Pool { - connStr, err := getConnectionString(config) - if err != nil { +func InitPool(config *koanf.Koanf) *pgxpool.Pool { + pool := NewPool(config) + + if err := runMigrations(pool); err != nil { + log.Fatal().Err(err).Msg("Couldn't apply migrations") panic(err) } - pool, err := pgxpool.Connect(context.Background(), connStr) + return pool +} + +func NewPool(config *koanf.Koanf) *pgxpool.Pool { + connStr, err := getConnectionString(config) if err != nil { + log.Fatal().Err(err).Msg("Couldn't construct DB connection string") + panic(err) + } + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + pool, err := pgxpool.Connect(ctx, connStr) + if err != nil { + log.Fatal().Err(err).Msg("Couldn't connect to DB") panic(err) } diff --git a/database/migrate.go b/database/migrate.go index 4223e4a..44f96be 100644 --- a/database/migrate.go +++ b/database/migrate.go @@ -4,24 +4,30 @@ import ( "context" "github.com/jackc/pgx/v4/pgxpool" "github.com/jackc/tern/migrate" + "time" ) -func Migrate(pool *pgxpool.Pool) { - conn, err := pool.Acquire(context.Background()) +func runMigrations(pool *pgxpool.Pool) (err error) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + conn, err := pool.Acquire(ctx) if err != nil { - panic(err) + return } - migrator, err := migrate.NewMigrator(context.Background(), conn.Conn(), "schema_version") + migrator, err := migrate.NewMigrator(ctx, conn.Conn(), "schema_version") if err != nil { - panic(err) + return } if err = migrator.LoadMigrations("./migrations"); err != nil { - panic(err) + return } - if err = migrator.Migrate(context.Background()); err != nil { - panic(err) + if err = migrator.Migrate(ctx); err != nil { + return } + + return } diff --git a/database/util.go b/database/util.go new file mode 100644 index 0000000..9d31c75 --- /dev/null +++ b/database/util.go @@ -0,0 +1,15 @@ +package database + +import ( + "github.com/jackc/pgx/v4/pgxpool" + "github.com/rs/zerolog/log" +) + +func LogPoolState(pool *pgxpool.Pool, message string) { + stat := pool.Stat() + log.Info(). + Int32("acquired_conns", stat.AcquiredConns()). + Int32("idle_conns", stat.IdleConns()). + Int32("total_conns", stat.TotalConns()). + Msg(message) +} diff --git a/link/repository.go b/link/repository.go index b31b9c3..8751b3a 100644 --- a/link/repository.go +++ b/link/repository.go @@ -1,6 +1,7 @@ package link import ( + "cgnolink/database" "context" "errors" "github.com/jackc/pgtype" @@ -15,7 +16,7 @@ const defaultContextTimeout = 10 * time.Second type Repository interface { Save(link *Link) error FindById(id string) (*Link, error) - FindAll(limit int, offset int) (Links, error) + GetAll(limit int, offset int) (Links, error) Update(link *Link) error DeleteById(id string) error } @@ -37,6 +38,7 @@ func (r *PgRepository) Save(link *Link) error { VALUES ($1, $2, $3, $4::timestamp) ` + database.LogPoolState(r.pool, "Saving link") _, err := r.pool.Exec(ctx, sql, link.Id, link.Name, link.RedirectURL.String(), link.CreationTime.Format("2006-01-02 15:04:05")) if err != nil { return err @@ -55,6 +57,7 @@ func (r *PgRepository) FindById(id string) (*Link, error) { WHERE id = $1 ` + database.LogPoolState(r.pool, "Finding link by ID") entity, err := mapRowToEntity(r.pool.QueryRow(ctx, sql, id)) if err != nil { if errors.Is(err, pgx.ErrNoRows) { @@ -67,7 +70,7 @@ func (r *PgRepository) FindById(id string) (*Link, error) { return entity, nil } -func (r *PgRepository) FindAll(limit int, offset int) (Links, error) { +func (r *PgRepository) GetAll(limit int, offset int) (Links, error) { if limit < 0 { return nil, errors.New("limit can't be negative") } @@ -85,6 +88,7 @@ func (r *PgRepository) FindAll(limit int, offset int) (Links, error) { OFFSET $2 ` + database.LogPoolState(r.pool, "Getting all links") rows, err := r.pool.Query(ctx, sql, limit, offset) if err != nil { return nil, err @@ -122,6 +126,7 @@ func (r *PgRepository) Update(link *Link) error { WHERE id = $3 ` + database.LogPoolState(r.pool, "Updating link") _, err := r.pool.Exec(ctx, sql, link.Name, link.RedirectURL.String(), link.Id) if err != nil { return err @@ -138,6 +143,7 @@ func (r *PgRepository) DeleteById(id string) error { DELETE FROM links WHERE id = $1 ` + database.LogPoolState(r.pool, "Deleting link") _, err := r.pool.Exec(ctx, sql, id) if err != nil { return err diff --git a/link/service.go b/link/service.go index 241332c..b35d7d5 100644 --- a/link/service.go +++ b/link/service.go @@ -64,7 +64,7 @@ func (s *PgService) GetById(id string) (*Link, error) { } func (s *PgService) GetAll(limit int, offset int) (Links, error) { - links, err := s.rep.FindAll(limit, offset) + links, err := s.rep.GetAll(limit, offset) if err != nil { return nil, apperrors.UnknownError{Err: err} } diff --git a/server/middleware.go b/server/middleware.go index 04a7824..82c2901 100644 --- a/server/middleware.go +++ b/server/middleware.go @@ -26,7 +26,7 @@ func Logger() echo.MiddlewareFunc { Str("user_agent", req.UserAgent()). Int("status", res.Status). Str("latency", stop.Sub(start).String()). - Send() + Msg("Request") return } From 2baa74d5204a29150767cb090937e54620ec7b83 Mon Sep 17 00:00:00 2001 From: Andrey Chervyakov Date: Thu, 18 Mar 2021 23:17:18 +0600 Subject: [PATCH 10/13] Fix links retrieval handler allowing negative limit and offset values --- link/handlers.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/link/handlers.go b/link/handlers.go index aa7c178..912372d 100644 --- a/link/handlers.go +++ b/link/handlers.go @@ -53,7 +53,7 @@ func allRetrievalHandler(ctx echo.Context, serv Service) error { limit := 20 if v := ctx.QueryParam("limit"); v != "" { num, err := strconv.Atoi(v) - if err != nil { + if err != nil || num < 0 { return echo.NewHTTPError(http.StatusBadRequest, "Invalid limit value.") } @@ -63,7 +63,7 @@ func allRetrievalHandler(ctx echo.Context, serv Service) error { offset := 0 if v := ctx.QueryParam("offset"); v != "" { num, err := strconv.Atoi(v) - if err != nil { + if err != nil || num < 0 { return echo.NewHTTPError(http.StatusBadRequest, "Invalid offset value.") } From a01f540a296203497126b0828f9311c9c7b2dc5a Mon Sep 17 00:00:00 2001 From: Andrey Chervyakov Date: Sat, 3 Apr 2021 22:25:11 +0600 Subject: [PATCH 11/13] Add password protected links --- link/dto.go | 5 ++++ link/entity.go | 1 + link/handlers.go | 5 ++-- link/repository.go | 20 ++++++------- link/service.go | 51 ++++++++++++++++++++++----------- migrations/002_add_password.sql | 13 +++++++++ 6 files changed, 67 insertions(+), 28 deletions(-) create mode 100644 migrations/002_add_password.sql diff --git a/link/dto.go b/link/dto.go index d8b2e68..511f174 100644 --- a/link/dto.go +++ b/link/dto.go @@ -9,18 +9,21 @@ type CreationModel struct { Id string `json:"id"` Name string `json:"name"` RedirectURL string `json:"redirectUrl"` + Password string `json:"password"` } type ResourceModel struct { Id string `json:"id"` Name string `json:"name"` RedirectURL string `json:"redirectUrl"` + Password string `json:"password"` CreationTime int64 `json:"creationTime"` } type UpdateModel struct { Name string `json:"name,omitempty"` RedirectURL string `json:"redirectUrl,omitempty"` + Password string `json:"password"` } func (m *CreationModel) MapModelToEntity() (*Link, error) { @@ -33,6 +36,7 @@ func (m *CreationModel) MapModelToEntity() (*Link, error) { Id: m.Id, Name: m.Name, RedirectURL: *u, + Password: m.Password, CreationTime: time.Now().UTC(), }, nil } @@ -42,6 +46,7 @@ func MapEntityToModel(entity *Link) ResourceModel { Id: entity.Id, Name: entity.Name, RedirectURL: entity.RedirectURL.String(), + Password: entity.Password, CreationTime: entity.CreationTime.Unix(), } } diff --git a/link/entity.go b/link/entity.go index 50f6521..343743e 100644 --- a/link/entity.go +++ b/link/entity.go @@ -9,6 +9,7 @@ type Link struct { Id string Name string RedirectURL url.URL + Password string CreationTime time.Time } diff --git a/link/handlers.go b/link/handlers.go index 912372d..be10ada 100644 --- a/link/handlers.go +++ b/link/handlers.go @@ -11,13 +11,14 @@ import ( func redirectHandler(ctx echo.Context, serv Service) error { linkId := ctx.Param("id") + linkPassword := ctx.QueryParam("password") - link, err := serv.GetById(linkId) + redirectUrl, err := serv.AccessLink(linkId, linkPassword) if err != nil { return err } - return ctx.Redirect(http.StatusSeeOther, link.RedirectURL.String()) + return ctx.Redirect(http.StatusSeeOther, redirectUrl.String()) } func creationHandler(ctx echo.Context, serv Service) error { diff --git a/link/repository.go b/link/repository.go index 8751b3a..f8149d6 100644 --- a/link/repository.go +++ b/link/repository.go @@ -34,12 +34,12 @@ func (r *PgRepository) Save(link *Link) error { defer cancel() sql := ` - INSERT INTO links (id, name, redirect_url, creation_time) - VALUES ($1, $2, $3, $4::timestamp) + INSERT INTO links (id, name, redirect_url, password, creation_time) + VALUES ($1, $2, $3, $4, $5::timestamp) ` database.LogPoolState(r.pool, "Saving link") - _, err := r.pool.Exec(ctx, sql, link.Id, link.Name, link.RedirectURL.String(), link.CreationTime.Format("2006-01-02 15:04:05")) + _, err := r.pool.Exec(ctx, sql, link.Id, link.Name, link.RedirectURL.String(), link.Password, link.CreationTime.Format("2006-01-02 15:04:05")) if err != nil { return err } @@ -52,7 +52,7 @@ func (r *PgRepository) FindById(id string) (*Link, error) { defer cancel() sql := ` - SELECT id, name, redirect_url, creation_time + SELECT id, name, redirect_url, password, creation_time FROM links WHERE id = $1 ` @@ -82,7 +82,7 @@ func (r *PgRepository) GetAll(limit int, offset int) (Links, error) { defer cancel() sql := ` - SELECT id, name, redirect_url, creation_time + SELECT id, name, redirect_url, password, creation_time FROM links LIMIT $1 OFFSET $2 @@ -122,12 +122,12 @@ func (r *PgRepository) Update(link *Link) error { sql := ` UPDATE links - SET name = $1, redirect_url = $2 - WHERE id = $3 + SET name = $1, redirect_url = $2, password = $3 + WHERE id = $4 ` database.LogPoolState(r.pool, "Updating link") - _, err := r.pool.Exec(ctx, sql, link.Name, link.RedirectURL.String(), link.Id) + _, err := r.pool.Exec(ctx, sql, link.Name, link.RedirectURL.String(), link.Password, link.Id) if err != nil { return err } @@ -159,11 +159,11 @@ func mapRowToEntity(r interface{}) (*Link, error) { switch v := r.(type) { case pgx.Row: - if err := v.Scan(&entity.Id, &entity.Name, &urlStr, &t); err != nil { + if err := v.Scan(&entity.Id, &entity.Name, &urlStr, &entity.Password, &t); err != nil { return nil, err } case pgx.Rows: - if err := v.Scan(&entity.Id, &entity.Name, &urlStr, &t); err != nil { + if err := v.Scan(&entity.Id, &entity.Name, &urlStr, &entity.Password, &t); err != nil { return nil, err } default: diff --git a/link/service.go b/link/service.go index b35d7d5..5c4b092 100644 --- a/link/service.go +++ b/link/service.go @@ -3,11 +3,15 @@ package link import ( apperrors "cgnolink/errors" "github.com/patrickmn/go-cache" + "net/url" ) +var linkNotFoundError = apperrors.NotFoundError{Message: "Link with given ID was not found."} + type Service interface { Create(link *Link) error GetById(id string) (*Link, error) + AccessLink(id string, password string) (*url.URL, error) GetAll(limit int, offset int) (Links, error) Update(data *Link) error DeleteById(id string) error @@ -18,6 +22,21 @@ type PgService struct { cache *cache.Cache } +func (service *PgService) AccessLink(id string, password string) (*url.URL, error) { + link, err := service.GetById(id) + if err != nil { + return nil, err + } + + if link.Password != "" { + if password == "" || link.Password != password { + return nil, linkNotFoundError + } + } + + return &link.RedirectURL, nil +} + func NewService(rep Repository) Service { return &PgService{ rep: rep, @@ -25,8 +44,8 @@ func NewService(rep Repository) Service { } } -func (s *PgService) Create(link *Link) error { - existingLink, err := s.rep.FindById(link.Id) +func (service *PgService) Create(link *Link) error { + existingLink, err := service.rep.FindById(link.Id) if err != nil { return apperrors.UnknownError{Err: err} } @@ -35,36 +54,36 @@ func (s *PgService) Create(link *Link) error { return apperrors.AlreadyExistsError{Message: "Link with given ID already exists."} } - if err = s.rep.Save(link); err != nil { + if err = service.rep.Save(link); err != nil { return apperrors.UnknownError{Err: err} } return nil } -func (s *PgService) GetById(id string) (*Link, error) { - if v, found := s.cache.Get(id); found { +func (service *PgService) GetById(id string) (*Link, error) { + if v, found := service.cache.Get(id); found { if link, ok := v.(*Link); ok { return link, nil } } - link, err := s.rep.FindById(id) + link, err := service.rep.FindById(id) if err != nil { return nil, apperrors.UnknownError{Err: err} } if link == nil { - return nil, apperrors.NotFoundError{Message: "Link with given ID was not found."} + return nil, linkNotFoundError } - s.cache.Set(id, link, cache.DefaultExpiration) + service.cache.Set(id, link, cache.DefaultExpiration) return link, nil } -func (s *PgService) GetAll(limit int, offset int) (Links, error) { - links, err := s.rep.GetAll(limit, offset) +func (service *PgService) GetAll(limit int, offset int) (Links, error) { + links, err := service.rep.GetAll(limit, offset) if err != nil { return nil, apperrors.UnknownError{Err: err} } @@ -72,20 +91,20 @@ func (s *PgService) GetAll(limit int, offset int) (Links, error) { return links, nil } -func (s *PgService) Update(data *Link) error { - if err := s.rep.Update(data); err != nil { +func (service *PgService) Update(data *Link) error { + if err := service.rep.Update(data); err != nil { return apperrors.UnknownError{Err: err} } + service.cache.Delete(data.Id) return nil } -func (s *PgService) DeleteById(id string) error { - s.cache.Delete(id) - - if err := s.rep.DeleteById(id); err != nil { +func (service *PgService) DeleteById(id string) error { + if err := service.rep.DeleteById(id); err != nil { return apperrors.UnknownError{Err: err} } + service.cache.Delete(id) return nil } diff --git a/migrations/002_add_password.sql b/migrations/002_add_password.sql new file mode 100644 index 0000000..715fffb --- /dev/null +++ b/migrations/002_add_password.sql @@ -0,0 +1,13 @@ +ALTER TABLE links +ADD COLUMN password varchar; + +UPDATE links +SET password = ''; + +ALTER TABLE links +ALTER COLUMN password SET not null; + +---- create above / drop below ---- + +ALTER TABLE links +DROP COLUMN password; From f4684be37d13a93b103d7ed3652d89cbd7388bf1 Mon Sep 17 00:00:00 2001 From: Andrey Chervyakov Date: Sun, 4 Apr 2021 16:36:13 +0600 Subject: [PATCH 12/13] Add hashing for link password and update link update service method --- go.mod | 3 +- link/dto.go | 2 -- link/handlers.go | 33 +++++++--------------- link/service.go | 73 ++++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 79 insertions(+), 32 deletions(-) diff --git a/go.mod b/go.mod index ab59fcd..96d31a5 100644 --- a/go.mod +++ b/go.mod @@ -13,8 +13,9 @@ require ( github.com/knadh/koanf v0.15.0 github.com/labstack/echo/v4 v4.2.1 github.com/mitchellh/copystructure v1.1.1 // indirect - github.com/patrickmn/go-cache v2.1.0+incompatible // indirect + github.com/patrickmn/go-cache v2.1.0+incompatible github.com/rs/zerolog v1.20.0 + golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 // indirect golang.org/x/text v0.3.5 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect diff --git a/link/dto.go b/link/dto.go index 511f174..c811153 100644 --- a/link/dto.go +++ b/link/dto.go @@ -16,7 +16,6 @@ type ResourceModel struct { Id string `json:"id"` Name string `json:"name"` RedirectURL string `json:"redirectUrl"` - Password string `json:"password"` CreationTime int64 `json:"creationTime"` } @@ -46,7 +45,6 @@ func MapEntityToModel(entity *Link) ResourceModel { Id: entity.Id, Name: entity.Name, RedirectURL: entity.RedirectURL.String(), - Password: entity.Password, CreationTime: entity.CreationTime.Unix(), } } diff --git a/link/handlers.go b/link/handlers.go index be10ada..10a25c6 100644 --- a/link/handlers.go +++ b/link/handlers.go @@ -92,33 +92,20 @@ func updateHandler(ctx echo.Context, serv Service) error { return echo.NewHTTPError(http.StatusBadRequest, "Invalid data format.") } - updatingLink, err := serv.GetById(linkId) + var parsedUrl *url.URL + parsedUrl, err := url.Parse(model.RedirectURL) if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "Invalid URL value.") + } + + if err := serv.UpdateById(linkId, struct { + Name string + Password string + RedirectURL *url.URL + }{Name: model.Name, Password: model.Password, RedirectURL: parsedUrl}); err != nil { return err } - hasChanges := false - switch { - case model.Name != "" && model.Name != updatingLink.Name: - updatingLink.Name = model.Name - - hasChanges = true - case model.RedirectURL != "" && model.RedirectURL != updatingLink.RedirectURL.String(): - if parsedUrl, err := url.Parse(model.RedirectURL); err != nil { - return echo.NewHTTPError(http.StatusBadRequest, "Invalid URL value.") - } else { - updatingLink.RedirectURL = *parsedUrl - } - - hasChanges = true - } - - if hasChanges { - if err = serv.Update(updatingLink); err != nil { - return err - } - } - return ctx.NoContent(http.StatusOK) } diff --git a/link/service.go b/link/service.go index 5c4b092..26f048f 100644 --- a/link/service.go +++ b/link/service.go @@ -3,6 +3,7 @@ package link import ( apperrors "cgnolink/errors" "github.com/patrickmn/go-cache" + "golang.org/x/crypto/bcrypt" "net/url" ) @@ -13,7 +14,14 @@ type Service interface { GetById(id string) (*Link, error) AccessLink(id string, password string) (*url.URL, error) GetAll(limit int, offset int) (Links, error) - Update(data *Link) error + UpdateById( + id string, + data struct { + Name string + Password string + RedirectURL *url.URL + }, + ) error DeleteById(id string) error } @@ -29,7 +37,7 @@ func (service *PgService) AccessLink(id string, password string) (*url.URL, erro } if link.Password != "" { - if password == "" || link.Password != password { + if password == "" || bcrypt.CompareHashAndPassword([]byte(link.Password), []byte(password)) != nil { return nil, linkNotFoundError } } @@ -54,6 +62,15 @@ func (service *PgService) Create(link *Link) error { return apperrors.AlreadyExistsError{Message: "Link with given ID already exists."} } + if link.Password != "" { + hashedPassword, err := HashPassword(link.Password) + if err != nil { + return err + } + + link.Password = hashedPassword + } + if err = service.rep.Save(link); err != nil { return apperrors.UnknownError{Err: err} } @@ -91,11 +108,46 @@ func (service *PgService) GetAll(limit int, offset int) (Links, error) { return links, nil } -func (service *PgService) Update(data *Link) error { - if err := service.rep.Update(data); err != nil { - return apperrors.UnknownError{Err: err} +func (service *PgService) UpdateById( + id string, + data struct { + Name string + Password string + RedirectURL *url.URL + }, +) error { + link, err := service.GetById(id) + if err != nil { + return err + } + + hasChanges := false + switch { + case data.Name != "": + link.Name = data.Name + + hasChanges = true + case data.RedirectURL != nil: + link.RedirectURL = *data.RedirectURL + + hasChanges = true + case data.Password != "": + hashedPw, err := HashPassword(data.Password) + if err != nil { + return err + } + + link.Password = hashedPw + + hasChanges = true + } + + if hasChanges { + if err := service.rep.Update(link); err != nil { + return apperrors.UnknownError{Err: err} + } + service.cache.Delete(link.Id) } - service.cache.Delete(data.Id) return nil } @@ -108,3 +160,12 @@ func (service *PgService) DeleteById(id string) error { return nil } + +func HashPassword(password string) (string, error) { + hashedPw, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + return "", apperrors.UnknownError{Err: err} + } + + return string(hashedPw), nil +} From e1516e450b03a53878a63f911612a2a3195c06ea Mon Sep 17 00:00:00 2001 From: Andrey Chervyakov Date: Mon, 12 Apr 2021 17:17:09 +0600 Subject: [PATCH 13/13] Put project modules under pkg directory --- cmd/cgnolink/main.go | 6 +++--- config.go => pkg/cgnolink/config.go | 0 {database => pkg/cgnolink/database}/database.go | 0 {database => pkg/cgnolink/database}/migrate.go | 0 {database => pkg/cgnolink/database}/util.go | 0 {errors => pkg/cgnolink/errors}/errors.go | 0 {link => pkg/cgnolink/link}/dto.go | 0 {link => pkg/cgnolink/link}/entity.go | 0 {link => pkg/cgnolink/link}/handlers.go | 0 {link => pkg/cgnolink/link}/repository.go | 2 +- {link => pkg/cgnolink/link}/service.go | 2 +- {server => pkg/cgnolink/server}/middleware.go | 0 {server => pkg/cgnolink/server}/server.go | 4 ++-- 13 files changed, 7 insertions(+), 7 deletions(-) rename config.go => pkg/cgnolink/config.go (100%) rename {database => pkg/cgnolink/database}/database.go (100%) rename {database => pkg/cgnolink/database}/migrate.go (100%) rename {database => pkg/cgnolink/database}/util.go (100%) rename {errors => pkg/cgnolink/errors}/errors.go (100%) rename {link => pkg/cgnolink/link}/dto.go (100%) rename {link => pkg/cgnolink/link}/entity.go (100%) rename {link => pkg/cgnolink/link}/handlers.go (100%) rename {link => pkg/cgnolink/link}/repository.go (99%) rename {link => pkg/cgnolink/link}/service.go (98%) rename {server => pkg/cgnolink/server}/middleware.go (100%) rename {server => pkg/cgnolink/server}/server.go (95%) diff --git a/cmd/cgnolink/main.go b/cmd/cgnolink/main.go index 4a72941..45ce28a 100644 --- a/cmd/cgnolink/main.go +++ b/cmd/cgnolink/main.go @@ -1,9 +1,9 @@ package main import ( - "cgnolink" - "cgnolink/database" - appserver "cgnolink/server" + "cgnolink/pkg/cgnolink" + "cgnolink/pkg/cgnolink/database" + appserver "cgnolink/pkg/cgnolink/server" "github.com/rs/zerolog" "github.com/rs/zerolog/log" "os" diff --git a/config.go b/pkg/cgnolink/config.go similarity index 100% rename from config.go rename to pkg/cgnolink/config.go diff --git a/database/database.go b/pkg/cgnolink/database/database.go similarity index 100% rename from database/database.go rename to pkg/cgnolink/database/database.go diff --git a/database/migrate.go b/pkg/cgnolink/database/migrate.go similarity index 100% rename from database/migrate.go rename to pkg/cgnolink/database/migrate.go diff --git a/database/util.go b/pkg/cgnolink/database/util.go similarity index 100% rename from database/util.go rename to pkg/cgnolink/database/util.go diff --git a/errors/errors.go b/pkg/cgnolink/errors/errors.go similarity index 100% rename from errors/errors.go rename to pkg/cgnolink/errors/errors.go diff --git a/link/dto.go b/pkg/cgnolink/link/dto.go similarity index 100% rename from link/dto.go rename to pkg/cgnolink/link/dto.go diff --git a/link/entity.go b/pkg/cgnolink/link/entity.go similarity index 100% rename from link/entity.go rename to pkg/cgnolink/link/entity.go diff --git a/link/handlers.go b/pkg/cgnolink/link/handlers.go similarity index 100% rename from link/handlers.go rename to pkg/cgnolink/link/handlers.go diff --git a/link/repository.go b/pkg/cgnolink/link/repository.go similarity index 99% rename from link/repository.go rename to pkg/cgnolink/link/repository.go index f8149d6..aa19b82 100644 --- a/link/repository.go +++ b/pkg/cgnolink/link/repository.go @@ -1,7 +1,7 @@ package link import ( - "cgnolink/database" + "cgnolink/pkg/cgnolink/database" "context" "errors" "github.com/jackc/pgtype" diff --git a/link/service.go b/pkg/cgnolink/link/service.go similarity index 98% rename from link/service.go rename to pkg/cgnolink/link/service.go index 26f048f..b7f00f6 100644 --- a/link/service.go +++ b/pkg/cgnolink/link/service.go @@ -1,7 +1,7 @@ package link import ( - apperrors "cgnolink/errors" + apperrors "cgnolink/pkg/cgnolink/errors" "github.com/patrickmn/go-cache" "golang.org/x/crypto/bcrypt" "net/url" diff --git a/server/middleware.go b/pkg/cgnolink/server/middleware.go similarity index 100% rename from server/middleware.go rename to pkg/cgnolink/server/middleware.go diff --git a/server/server.go b/pkg/cgnolink/server/server.go similarity index 95% rename from server/server.go rename to pkg/cgnolink/server/server.go index 11e23de..254116b 100644 --- a/server/server.go +++ b/pkg/cgnolink/server/server.go @@ -1,8 +1,8 @@ package server import ( - apperrors "cgnolink/errors" - "cgnolink/link" + apperrors "cgnolink/pkg/cgnolink/errors" + "cgnolink/pkg/cgnolink/link" "github.com/jackc/pgx/v4/pgxpool" "github.com/knadh/koanf" "github.com/labstack/echo/v4"