add pkg folder

main
nqkhanh2003 2024-09-24 00:27:21 +07:00
parent 912ad7d22c
commit 489bd4b63c
22 changed files with 406 additions and 17 deletions

34
.dockerignore Normal file
View File

@ -0,0 +1,34 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
out/
**/.vagrant/
.idea/
local/data/
.env
.terraform
*tfstate*

6
.env Normal file
View File

@ -0,0 +1,6 @@
APP_ADDRESS = "localhost"
APP_PORT = "3000"
REDIS_ADDRESS="localhost"
REDIS_PORT="6379"
REDIS_PASSWORD = "110502"

32
.gitignore vendored Normal file
View File

@ -0,0 +1,32 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
github.com/
.vscode
.vs/
/vendor
**/*.log
**/.project
**/.factorypath
google
test_report*
# Go Workspaces (introduced in Go 1.18+)
go.work
*.env
.terraform
*tfstate*

9
docker-compose.yaml Normal file
View File

@ -0,0 +1,9 @@
version: '3'
services:
redis:
image: redis:latest
command: ["redis-server"]
environment:
- REDIS_PASSWORD=${REDIS_PASSWORD}
ports:
- "6379:6379"

4
docker/Dockerfile Normal file
View File

@ -0,0 +1,4 @@
FROM ubuntu:latest
LABEL authors="nqkhanh2003"
ENTRYPOINT ["top", "-b"]

21
go.mod
View File

@ -1 +1,22 @@
module url-shortener 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
)

46
go.sum Normal file
View File

@ -0,0 +1,46 @@
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=

50
main.go
View File

@ -1,25 +1,45 @@
package main package main
import ( import (
"encoding/json"
"fmt" "fmt"
"github.com/joho/godotenv"
"net/http"
) )
//TIP To run your code, right-click the code and select <b>Run</b>. Alternatively, click type ShorterRequest struct {
// the <icon src="AllIcons.Actions.Execute"/> icon in the gutter and select the <b>Run</b> menu item from here. Url string
}
func main() { func main() {
//TIP Press <shortcut actionId="ShowIntentionActions"/> when your caret is at the underlined or highlighted text envError := godotenv.Load()
// to see how GoLand suggests fixing it. if envError != nil {
s := "gopher" panic("Error loading .env file")
fmt.Println("Hello and welcome, %s!", s)
for i := 1; i <= 5; i++ {
//TIP You can try debugging your code. We have set one <icon src="AllIcons.Debugger.Db_set_breakpoint"/> breakpoint
// for you, but you can always add more by pressing <shortcut actionId="ToggleLineBreakpoint"/>. To start your debugging session,
// right-click your code in the editor and select the <b>Debug</b> option.
fmt.Println("i =", 100/i)
}
} }
//TIP See GoLand help at <a href="https://www.jetbrains.com/help/go/">jetbrains.com/help/go/</a>. router := http.NewServeMux()
// Also, you can try interactive lessons for GoLand by selecting 'Help | Learn IDE Features' from the main menu.
router.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World"))
})
router.HandleFunc("POST /shorten", func(w http.ResponseWriter, r *http.Request) {
requestData := ShorterRequest{}
err := json.NewDecoder(r.Body).Decode(&requestData)
if err != nil {
fmt.Println(err)
panic(err)
}
w.Write([]byte(requestData.Url))
})
server := http.Server{Addr: ":3000", Handler: router}
fmt.Println("URL Shortener is running on :3000")
err := server.ListenAndServe()
if err != nil {
return
}
}

17
pkg/config/configs.go Normal file
View File

@ -0,0 +1,17 @@
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"`
}
)

View File

@ -0,0 +1 @@
package consumer

View File

@ -0,0 +1 @@
package consumer

View File

@ -0,0 +1 @@
package consumer

1
pkg/kafka/kafka.go Normal file
View File

@ -0,0 +1 @@
package kafka

View File

@ -0,0 +1 @@
package producer

View File

@ -0,0 +1 @@
package producer

View File

@ -0,0 +1 @@
package producer

View File

@ -0,0 +1,79 @@
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
}

View File

@ -0,0 +1,9 @@
package postgres
import "database/sql"
type DBEngine interface {
GetDB() *sql.DB
Configure(...Option) DBEngine
Close()
}

17
pkg/postgres/option.go Normal file
View File

@ -0,0 +1,17 @@
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
}
}

70
pkg/postgres/postgres.go Normal file
View File

@ -0,0 +1,70 @@
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()
}
}

11
pkg/utils/utils.go Normal file
View File

@ -0,0 +1,11 @@
package utils
import "os"
func IsRunningInContainer() bool {
if _, err := os.Stat("/.dockerenv"); err != nil {
return false
}
return true
}

7
tools/tools.go Normal file
View File

@ -0,0 +1,7 @@
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"
)