From c36e227133b3f93578012368add568aa7ef68fb0 Mon Sep 17 00:00:00 2001 From: quockhanh Date: Tue, 24 Sep 2024 18:59:27 +0700 Subject: [PATCH] feat: added shortener & redirect url into project --- .dockerignore | 4 +- .env | 6 -- Dockerfile | 9 +++ docker/Dockerfile | 4 - go.mod | 18 +---- go.sum | 50 +------------ main.go | 71 +++++++++++++----- pkg/config/configs.go | 17 ----- pkg/kafka/consumer/consumer.go | 1 - pkg/kafka/consumer/interfaces.go | 1 - pkg/kafka/consumer/option.go | 1 - pkg/kafka/kafka.go | 1 - pkg/kafka/producer/interfaces.go | 1 - pkg/kafka/producer/option.go | 1 - pkg/kafka/producer/producer.go | 1 - pkg/logger/logrus_adaptor.go | 79 -------------------- pkg/postgres/interfaces.go | 9 --- pkg/postgres/option.go | 17 ----- pkg/postgres/postgres.go | 70 ------------------ pkg/utils/utils.go | 11 --- postgres.go | 122 +++++++++++++++++++++++++++++++ tools/tools.go | 7 -- 22 files changed, 192 insertions(+), 309 deletions(-) delete mode 100644 .env create mode 100644 Dockerfile delete mode 100644 docker/Dockerfile delete mode 100644 pkg/config/configs.go delete mode 100644 pkg/kafka/consumer/consumer.go delete mode 100644 pkg/kafka/consumer/interfaces.go delete mode 100644 pkg/kafka/consumer/option.go delete mode 100644 pkg/kafka/kafka.go delete mode 100644 pkg/kafka/producer/interfaces.go delete mode 100644 pkg/kafka/producer/option.go delete mode 100644 pkg/kafka/producer/producer.go delete mode 100644 pkg/logger/logrus_adaptor.go delete mode 100644 pkg/postgres/interfaces.go delete mode 100644 pkg/postgres/option.go delete mode 100644 pkg/postgres/postgres.go delete mode 100644 pkg/utils/utils.go create mode 100644 postgres.go delete mode 100644 tools/tools.go diff --git a/.dockerignore b/.dockerignore index b98047c..b0577d0 100644 --- a/.dockerignore +++ b/.dockerignore @@ -15,7 +15,7 @@ **/bin **/charts **/docker-compose* -**/Dockerfile* +Dockerfile **/node_modules **/npm-debug.log **/obj @@ -28,7 +28,7 @@ out/ **/.vagrant/ .idea/ local/data/ -.env +.env .terraform *tfstate* \ No newline at end of file diff --git a/.env b/.env deleted file mode 100644 index f40d1f1..0000000 --- a/.env +++ /dev/null @@ -1,6 +0,0 @@ -APP_ADDRESS = "localhost" -APP_PORT = "3000" - -REDIS_ADDRESS="localhost" -REDIS_PORT="6379" -REDIS_PASSWORD = "110502" \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a077858 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM golang:1.19.2-alpine3.16 as builder +COPY . /app +WORKDIR /app +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build + +FROM scratch + +CMD ["/app"] \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile deleted file mode 100644 index 539394f..0000000 --- a/docker/Dockerfile +++ /dev/null @@ -1,4 +0,0 @@ -FROM ubuntu:latest -LABEL authors="nqkhanh2003" - -ENTRYPOINT ["top", "-b"] \ No newline at end of file diff --git a/go.mod b/go.mod index 7925dda..cd14291 100644 --- a/go.mod +++ b/go.mod @@ -3,20 +3,6 @@ module url-shortener go 1.23.1 require ( - github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 - github.com/joho/godotenv v1.5.1 - github.com/sirupsen/logrus v1.9.3 - golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 - google.golang.org/protobuf v1.34.2 -) - -require ( - github.com/kr/text v0.2.0 // indirect - github.com/rogpeppe/go-internal v1.13.1 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/text v0.17.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect - google.golang.org/grpc v1.64.1 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect + github.com/lib/pq v1.10.9 + github.com/sony/sonyflake v1.2.0 ) diff --git a/go.sum b/go.sum index 59f56ae..cef5426 100644 --- a/go.sum +++ b/go.sum @@ -1,46 +1,4 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= -github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= -github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= -github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 h1:wKguEg1hsxI2/L3hUYrpo1RVi48K+uTyzKqprwLXsb8= -google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= -google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= -google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/sony/sonyflake v1.2.0 h1:Pfr3A+ejSg+0SPqpoAmQgEtNDAhc2G1SUYk205qVMLQ= +github.com/sony/sonyflake v1.2.0/go.mod h1:LORtCywH/cq10ZbyfhKrHYgAUGH7mOBa76enV9txy/Y= diff --git a/main.go b/main.go index a92121c..bdd5f23 100644 --- a/main.go +++ b/main.go @@ -1,45 +1,80 @@ package main import ( - "encoding/json" "fmt" - "github.com/joho/godotenv" + "github.com/sony/sonyflake" + "log" "net/http" + "os" ) -type ShorterRequest struct { - Url string -} +var sf *sonyflake.Sonyflake + +const ( + base62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +) func main() { - envError := godotenv.Load() - if envError != nil { - panic("Error loading .env file") + db := connectDB() + router := http.NewServeMux() + port := os.Getenv("APP_PORT") + var st sonyflake.Settings + sf = sonyflake.NewSonyflake(st) + if sf == nil { + panic("Sonyflake not created") } - router := http.NewServeMux() - router.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte("Hello World")) + path := r.URL.Path + code := path[1:] + if code == "" { + http.Error(w, "Page not found", http.StatusNotFound) + return + } + url, err := getUrlByCode(db, code) + if url == "" || err != nil { + http.Error(w, "Page not found", http.StatusNotFound) + return + } + + http.Redirect(w, r, url, http.StatusFound) }) router.HandleFunc("POST /shorten", func(w http.ResponseWriter, r *http.Request) { - requestData := ShorterRequest{} - err := json.NewDecoder(r.Body).Decode(&requestData) + originalURL := r.FormValue("url") + if originalURL == "" { + http.Error(w, "URL parameter is missing", http.StatusBadRequest) + return + } + codeExisted, err := checkURLAlreadyExists(db, originalURL) if err != nil { - fmt.Println(err) - panic(err) + log.Fatalf("Error checking URL exists: %v\n", err) } - w.Write([]byte(requestData.Url)) + if codeExisted == "" { + code, err := insertURL(db, originalURL) + if err != nil { + log.Fatalf("Error when insert URL: %v\n", err) + } + _, writeErr := w.Write([]byte(fmt.Sprintf("%s/%s", r.Host, code))) + if writeErr != nil { + return + } + } + _, writeErr := w.Write([]byte(fmt.Sprintf("%s/%s", r.Host, codeExisted))) + if writeErr != nil { + return + } }) - server := http.Server{Addr: ":3000", Handler: router} + server := http.Server{Addr: fmt.Sprintf(":%s", port), Handler: router} - fmt.Println("URL Shortener is running on :3000") + fmt.Println(fmt.Sprintf("App is running in port: %s", port)) err := server.ListenAndServe() + if err != nil { + fmt.Println(err) return } } diff --git a/pkg/config/configs.go b/pkg/config/configs.go deleted file mode 100644 index 213b867..0000000 --- a/pkg/config/configs.go +++ /dev/null @@ -1,17 +0,0 @@ -package configs - -type ( - App struct { - Name string `env-required:"true" yaml:"name" env:"APP_NAME"` - Version string `env-required:"true" yaml:"version" env:"APP_VERSION"` - } - - HTTP struct { - Host string `env-required:"true" yaml:"host" env:"HTTP_HOST"` - Port int `env-required:"true" yaml:"port" env:"HTTP_PORT"` - } - - Log struct { - Level string `env-required:"true" yaml:"log_level" env:"LOG_LEVEL"` - } -) diff --git a/pkg/kafka/consumer/consumer.go b/pkg/kafka/consumer/consumer.go deleted file mode 100644 index b78b46c..0000000 --- a/pkg/kafka/consumer/consumer.go +++ /dev/null @@ -1 +0,0 @@ -package consumer diff --git a/pkg/kafka/consumer/interfaces.go b/pkg/kafka/consumer/interfaces.go deleted file mode 100644 index b78b46c..0000000 --- a/pkg/kafka/consumer/interfaces.go +++ /dev/null @@ -1 +0,0 @@ -package consumer diff --git a/pkg/kafka/consumer/option.go b/pkg/kafka/consumer/option.go deleted file mode 100644 index b78b46c..0000000 --- a/pkg/kafka/consumer/option.go +++ /dev/null @@ -1 +0,0 @@ -package consumer diff --git a/pkg/kafka/kafka.go b/pkg/kafka/kafka.go deleted file mode 100644 index 82b3441..0000000 --- a/pkg/kafka/kafka.go +++ /dev/null @@ -1 +0,0 @@ -package kafka diff --git a/pkg/kafka/producer/interfaces.go b/pkg/kafka/producer/interfaces.go deleted file mode 100644 index 30f1d3d..0000000 --- a/pkg/kafka/producer/interfaces.go +++ /dev/null @@ -1 +0,0 @@ -package producer diff --git a/pkg/kafka/producer/option.go b/pkg/kafka/producer/option.go deleted file mode 100644 index 30f1d3d..0000000 --- a/pkg/kafka/producer/option.go +++ /dev/null @@ -1 +0,0 @@ -package producer diff --git a/pkg/kafka/producer/producer.go b/pkg/kafka/producer/producer.go deleted file mode 100644 index 30f1d3d..0000000 --- a/pkg/kafka/producer/producer.go +++ /dev/null @@ -1 +0,0 @@ -package producer diff --git a/pkg/logger/logrus_adaptor.go b/pkg/logger/logrus_adaptor.go deleted file mode 100644 index f11d225..0000000 --- a/pkg/logger/logrus_adaptor.go +++ /dev/null @@ -1,79 +0,0 @@ -package logger - -// refs: -// https://josephwoodward.co.uk/2022/11/slog-structured-logging-proposal -// https://thedevelopercafe.com/articles/logging-in-go-with-slog-a7bb489755c2 - -import ( - "strings" - - "github.com/sirupsen/logrus" - "golang.org/x/exp/slog" -) - -type LogrusHandler struct { - logger *logrus.Logger -} - -func NewLogrusHandler(logger *logrus.Logger) *LogrusHandler { - return &LogrusHandler{ - logger: logger, - } -} - -func ConvertLogLevel(level string) logrus.Level { - var l logrus.Level - - switch strings.ToLower(level) { - case "error": - l = logrus.ErrorLevel - case "warm": - l = logrus.WarnLevel - case "info": - l = logrus.InfoLevel - case "debug": - l = logrus.DebugLevel - default: - l = logrus.InfoLevel - } - - return l -} - -func (h *LogrusHandler) Enabled(_ slog.Level) bool { - // support all logging levels - return true -} - -func (h *LogrusHandler) Handle(rec slog.Record) error { - fields := make(map[string]interface{}, rec.NumAttrs()) - - rec.Attrs(func(a slog.Attr) { - fields[a.Key] = a.Value.Any() - }) - - entry := h.logger.WithFields(fields) - - switch rec.Level { - case slog.DebugLevel: - entry.Debug(rec.Message) - case slog.InfoLevel.Level(): - entry.Info(rec.Message) - case slog.WarnLevel: - entry.Warn(rec.Message) - case slog.ErrorLevel: - entry.Error(rec.Message) - } - - return nil -} - -func (h *LogrusHandler) WithAttrs(attrs []slog.Attr) slog.Handler { - // not implemented for brevity - return h -} - -func (h *LogrusHandler) WithGroup(name string) slog.Handler { - // not implemented for brevity - return h -} diff --git a/pkg/postgres/interfaces.go b/pkg/postgres/interfaces.go deleted file mode 100644 index 3d7fd8f..0000000 --- a/pkg/postgres/interfaces.go +++ /dev/null @@ -1,9 +0,0 @@ -package postgres - -import "database/sql" - -type DBEngine interface { - GetDB() *sql.DB - Configure(...Option) DBEngine - Close() -} diff --git a/pkg/postgres/option.go b/pkg/postgres/option.go deleted file mode 100644 index 0907da4..0000000 --- a/pkg/postgres/option.go +++ /dev/null @@ -1,17 +0,0 @@ -package postgres - -import "time" - -type Option func(*postgres) - -func ConnAttempts(attempts int) Option { - return func(p *postgres) { - p.connAttempts = attempts - } -} - -func ConnTimeout(timeout time.Duration) Option { - return func(p *postgres) { - p.connTimeout = timeout - } -} diff --git a/pkg/postgres/postgres.go b/pkg/postgres/postgres.go deleted file mode 100644 index ee2f8b3..0000000 --- a/pkg/postgres/postgres.go +++ /dev/null @@ -1,70 +0,0 @@ -package postgres - -import ( - "database/sql" - "log" - "time" - - "golang.org/x/exp/slog" -) - -const ( - _defaultConnAttempts = 3 - _defaultConnTimeout = time.Second -) - -type DBConnString string - -type postgres struct { - connAttempts int - connTimeout time.Duration - - db *sql.DB -} - -var _ DBEngine = (*postgres)(nil) - -func NewPostgresDB(url DBConnString) (DBEngine, error) { - slog.Info("CONN", "connect string", url) - - pg := &postgres{ - connAttempts: _defaultConnAttempts, - connTimeout: _defaultConnTimeout, - } - - var err error - for pg.connAttempts > 0 { - pg.db, err = sql.Open("postgres", string(url)) - if err != nil { - break - } - - log.Printf("Postgres is trying to connect, attempts left: %d", pg.connAttempts) - - time.Sleep(pg.connTimeout) - - pg.connAttempts-- - } - - slog.Info("📰 connected to postgresdb 🎉") - - return pg, nil -} - -func (p *postgres) Configure(opts ...Option) DBEngine { - for _, opt := range opts { - opt(p) - } - - return p -} - -func (p *postgres) GetDB() *sql.DB { - return p.db -} - -func (p *postgres) Close() { - if p.db != nil { - p.db.Close() - } -} diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go deleted file mode 100644 index 4b99bc3..0000000 --- a/pkg/utils/utils.go +++ /dev/null @@ -1,11 +0,0 @@ -package utils - -import "os" - -func IsRunningInContainer() bool { - if _, err := os.Stat("/.dockerenv"); err != nil { - return false - } - - return true -} diff --git a/postgres.go b/postgres.go new file mode 100644 index 0000000..027b08c --- /dev/null +++ b/postgres.go @@ -0,0 +1,122 @@ +package main + +import ( + "database/sql" + "errors" + "fmt" + _ "github.com/lib/pq" + "log" + "os" +) + +type DbConfig struct { + dbName string + dbUser string + dbPassword string + dbHost string + dbPort string +} + +func connectDB() *sql.DB { + dbConfig := &DbConfig{ + dbName: os.Getenv("DB_NAME"), + dbUser: os.Getenv("DB_USER"), + dbHost: os.Getenv("DB_HOST"), + dbPort: os.Getenv("DB_PORT"), + dbPassword: os.Getenv("DB_PASSWORD"), + } + + connectStr := fmt.Sprintf("user=%s password=%s dbname=%s host=%s port=%s sslmode=disable", dbConfig.dbUser, dbConfig.dbPassword, dbConfig.dbName, dbConfig.dbHost, dbConfig.dbPort) + + db, err := sql.Open("postgres", connectStr) + if err != nil { + fmt.Println("Error connecting to database", err) + panic(err) + } + + err = db.Ping() + if err != nil { + log.Fatal("Failed to connect to the database:", err) + } + fmt.Println("Connected to the database") + + _, tbErr := db.Exec("CREATE TABLE IF NOT EXISTS urls (id bigint NOT NULL PRIMARY KEY , url TEXT NOT NULL, code varchar(18) NULL, createdAt TIMESTAMP DEFAULT NOW(), updatedAt TIMESTAMP DEFAULT NOW())") + if tbErr != nil { + log.Fatal("Error creating table:", tbErr) + } + fmt.Printf("Table '%s' created successfully.\n", "urls") + + return db +} + +func insertURL(db *sql.DB, url string) (string, error) { + id := generateNewID() + code := intToBase62(id) + _, err := db.Exec("INSERT INTO urls(id, url, code) VALUES ($1, $2, $3)", id, url, code) + if err != nil { + log.Fatal("Error inserting url:", err) + return "", err + } + return code, nil +} + +func checkURLAlreadyExists(db *sql.DB, url string) (string, error) { + var code string + rows, err := db.Query(`SELECT code from urls where url=$1`, url) + if err != nil { + log.Fatalln("Error querying rows:", err) + return "", err + } else { + for rows.Next() { + err := rows.Scan(&code) + if err != nil { + return "", err + } + return code, nil + } + } + return "", err +} + +func getUrlByCode(db *sql.DB, code string) (string, error) { + var url string + rows, err := db.Query("SELECT url FROM urls WHERE code=$1", code) + if err != nil { + log.Fatalln(err) + return "", err + } + + for rows.Next() { + err = rows.Scan(&url) + return url, err + } + + return "", errors.New("code not found") +} + +func generateNewID() uint64 { + id, err := sf.NextID() + if err != nil { + fmt.Println(err) + panic(err) + } + return id +} + +func intToBase62(n uint64) string { + if n == 0 { + return string(base62[0]) + } + + var result []byte + for n > 0 { + result = append(result, base62[n%62]) + n /= 62 + } + + for i, j := 0, len(result)-1; i < j; i, j = i+1, j-1 { + result[i], result[j] = result[j], result[i] + } + + return string(result) +} diff --git a/tools/tools.go b/tools/tools.go deleted file mode 100644 index 4bb0531..0000000 --- a/tools/tools.go +++ /dev/null @@ -1,7 +0,0 @@ -package tools - -import ( - _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway" - _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2" - _ "google.golang.org/protobuf/cmd/protoc-gen-go" -)