From fc46164556820aa335c3cdcdceded7c8a844770b Mon Sep 17 00:00:00 2001 From: maotovisk Date: Wed, 6 Aug 2025 16:04:04 -0300 Subject: [PATCH] initial commit --- cmd/maot-shortner/main.go | 20 ++++++++++++++ cmd/maot-shortner/server.go | 32 ++++++++++++++++++++++ go.mod | 12 +++++++++ go.sum | 12 +++++++++ internal/app/routes.go | 19 +++++++++++++ internal/config/config.go | 1 + internal/database/database.go | 23 ++++++++++++++++ internal/database/link.go | 16 +++++++++++ internal/domain/link.go | 12 +++++++++ internal/domain/models.go | 7 +++++ internal/handlers/handler.go | 7 +++++ internal/handlers/link.go | 51 +++++++++++++++++++++++++++++++++++ internal/repositories/link.go | 10 +++++++ internal/services/link.go | 21 +++++++++++++++ 14 files changed, 243 insertions(+) create mode 100644 cmd/maot-shortner/main.go create mode 100644 cmd/maot-shortner/server.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/app/routes.go create mode 100644 internal/config/config.go create mode 100644 internal/database/database.go create mode 100644 internal/database/link.go create mode 100644 internal/domain/link.go create mode 100644 internal/domain/models.go create mode 100644 internal/handlers/handler.go create mode 100644 internal/handlers/link.go create mode 100644 internal/repositories/link.go create mode 100644 internal/services/link.go diff --git a/cmd/maot-shortner/main.go b/cmd/maot-shortner/main.go new file mode 100644 index 0000000..dd43586 --- /dev/null +++ b/cmd/maot-shortner/main.go @@ -0,0 +1,20 @@ +package main + +import ( + "fmt" + "maot-shortner/internal/database" +) + +func main() { + fmt.Println("Starting app...") + + db, err := database.InitDatabase() + if err != nil { + panic(err) + } + + server := NewServer(db) + + fmt.Println("App listening on port 3032") + server.ListenAndServe() +} diff --git a/cmd/maot-shortner/server.go b/cmd/maot-shortner/server.go new file mode 100644 index 0000000..2699c93 --- /dev/null +++ b/cmd/maot-shortner/server.go @@ -0,0 +1,32 @@ +package main + +import ( + "maot-shortner/internal/database" + "maot-shortner/internal/handlers" + "net/http" +) + +type Server struct { + http.Server + db *database.Database +} + +func NewServer(db *database.Database) *Server { + return &Server{ + Server: http.Server{ + Addr: ":3032", + Handler: createMux(db), + }, + db: db, + } +} + +func createMux(db *database.Database) http.Handler { + r := http.NewServeMux() + + linkHandler := handlers.NewLinkHandler(db) + r.HandleFunc("GET /api/links", linkHandler.ListLinks) + r.HandleFunc("GET /{id}", linkHandler.GetUrl) + + return r +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..941563d --- /dev/null +++ b/go.mod @@ -0,0 +1,12 @@ +module maot-shortner + +go 1.24.5 + +require ( + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/mattn/go-sqlite3 v1.14.30 // indirect + golang.org/x/text v0.27.0 // indirect + gorm.io/driver/sqlite v1.6.0 // indirect + gorm.io/gorm v1.30.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ef3bc6b --- /dev/null +++ b/go.sum @@ -0,0 +1,12 @@ +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/mattn/go-sqlite3 v1.14.30 h1:bVreufq3EAIG1Quvws73du3/QgdeZ3myglJlrzSYYCY= +github.com/mattn/go-sqlite3 v1.14.30/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ= +gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8= +gorm.io/gorm v1.30.1 h1:lSHg33jJTBxs2mgJRfRZeLDG+WZaHYCk3Wtfl6Ngzo4= +gorm.io/gorm v1.30.1/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= diff --git a/internal/app/routes.go b/internal/app/routes.go new file mode 100644 index 0000000..9f4c58a --- /dev/null +++ b/internal/app/routes.go @@ -0,0 +1,19 @@ +package app + +import ( + "maot-shortner/internal/handlers" + "net/http" +) + +type HandlerList struct { + *handlers.LinkHandler +} + +func CreateMux(handlers HandlerList) *http.ServeMux { + r := http.NewServeMux() + + r.HandleFunc("/links", handlers.LinkHandler.ListLinks) + r.HandleFunc("/", handlers.LinkHandler.Handle) + + return r +} diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..d912156 --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1 @@ +package config diff --git a/internal/database/database.go b/internal/database/database.go new file mode 100644 index 0000000..17291d7 --- /dev/null +++ b/internal/database/database.go @@ -0,0 +1,23 @@ +package database + +import ( + "log" + "maot-shortner/internal/domain" + + "gorm.io/driver/sqlite" + "gorm.io/gorm" +) + +type Database struct { + *gorm.DB +} + +func InitDatabase() (database *Database, err error) { + log.Println("Starting database...") + + db, err := gorm.Open(sqlite.Open("shortener.db"), &gorm.Config{}) + db.AutoMigrate(domain.ImportModels()...) + + database = &Database{db} + return database, err +} diff --git a/internal/database/link.go b/internal/database/link.go new file mode 100644 index 0000000..cd68408 --- /dev/null +++ b/internal/database/link.go @@ -0,0 +1,16 @@ +package database + +import ( + "context" + "maot-shortner/internal/domain" +) + +func (db *Database) GetLinks(ctx context.Context) ([]*domain.Link, error) { + links := make([]*domain.Link, 0) + + if err := db.WithContext(ctx).Find(&links).Error; err != nil { + return nil, err + } + + return links, nil +} diff --git a/internal/domain/link.go b/internal/domain/link.go new file mode 100644 index 0000000..0ad7c02 --- /dev/null +++ b/internal/domain/link.go @@ -0,0 +1,12 @@ +package domain + +import ( + "time" +) + +type Link struct { + ID int64 `json:"id"` + ShortURL string `json:"short_url"` + LongURL string `json:"long_url"` + CreatedAt time.Time `json:"created_at"` +} diff --git a/internal/domain/models.go b/internal/domain/models.go new file mode 100644 index 0000000..d607e12 --- /dev/null +++ b/internal/domain/models.go @@ -0,0 +1,7 @@ +package domain + +func ImportModels() []any { + return []any{ + &Link{}, + } +} diff --git a/internal/handlers/handler.go b/internal/handlers/handler.go new file mode 100644 index 0000000..605a32b --- /dev/null +++ b/internal/handlers/handler.go @@ -0,0 +1,7 @@ +package handlers + +import "net/http" + +type Handler interface { + Handle(w http.ResponseWriter, r *http.Request) +} diff --git a/internal/handlers/link.go b/internal/handlers/link.go new file mode 100644 index 0000000..2cd5f96 --- /dev/null +++ b/internal/handlers/link.go @@ -0,0 +1,51 @@ +package handlers + +import ( + "encoding/json" + "fmt" + "maot-shortner/internal/database" + "maot-shortner/internal/services" + "net/http" +) + +type Response struct { + Message string `json:"message"` +} + +type LinkHandler struct { + db *database.Database + linkService *services.LinkService +} + +func (h *LinkHandler) Handle(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + json.NewEncoder(w).Encode(&Response{Message: "Ola"}) +} + +func NewLinkHandler(db *database.Database) *LinkHandler { + return &LinkHandler{ + db: db, + linkService: services.NewLinkService(db), + } +} + +func (h *LinkHandler) ListLinks(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + linkList, err := h.linkService.GetLinks(r.Context()) + if err != nil { + http.Error(w, fmt.Sprintf("Failed to list links: %v", err), http.StatusInternalServerError) + return + } + + json.NewEncoder(w).Encode(linkList) +} + +func (h *LinkHandler) GetUrl(w http.ResponseWriter, r *http.Request) { + currentCode := r.PathValue("id") + + json.NewEncoder(w).Encode(&Response{Message: currentCode}) +} diff --git a/internal/repositories/link.go b/internal/repositories/link.go new file mode 100644 index 0000000..d20b7b8 --- /dev/null +++ b/internal/repositories/link.go @@ -0,0 +1,10 @@ +package repositories + +import ( + "context" + "maot-shortner/internal/domain" +) + +type LinkRepository interface { + GetLinks(ctx context.Context) ([]*domain.Link, error) +} diff --git a/internal/services/link.go b/internal/services/link.go new file mode 100644 index 0000000..9cda7ba --- /dev/null +++ b/internal/services/link.go @@ -0,0 +1,21 @@ +package services + +import ( + "context" + "maot-shortner/internal/domain" + "maot-shortner/internal/repositories" +) + +type LinkService struct { + repo repositories.LinkRepository +} + +func NewLinkService(repo repositories.LinkRepository) *LinkService { + return &LinkService{ + repo: repo, + } +} + +func (s *LinkService) GetLinks(ctx context.Context) ([]*domain.Link, error) { + return s.repo.GetLinks(ctx) +}