Skip to content

Commit

Permalink
[Backend] Refactor SaveUser func and Uber zap logger
Browse files Browse the repository at this point in the history
  • Loading branch information
Alfex4936 committed Mar 2, 2024
1 parent 210233b commit e8e3b65
Show file tree
Hide file tree
Showing 9 changed files with 211 additions and 63 deletions.
5 changes: 5 additions & 0 deletions backend/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ require (
github.com/go-openapi/spec v0.20.14 // indirect
github.com/go-openapi/swag v0.22.9 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/gofiber/contrib/fiberzap/v2 v2.1.2 // indirect
github.com/gofiber/swagger v1.0.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/josharian/intern v1.0.0 // indirect
Expand All @@ -51,12 +52,16 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/philhofer/fwd v1.1.2 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/swaggo/files/v2 v2.0.0 // indirect
github.com/swaggo/swag v1.16.3 // indirect
github.com/tinylib/msgp v1.1.8 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.52.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
Expand Down
17 changes: 17 additions & 0 deletions backend/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrt
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gofiber/contrib/fiberzap/v2 v2.1.2 h1:7Z1BqS1sYK9e9jTwqPcWx9qQt46PI8oeswgAp6YNZC4=
github.com/gofiber/contrib/fiberzap/v2 v2.1.2/go.mod h1:ulCCQOdDYABGsOQfbndASmCsCN86hsC96iKoOTNYfy8=
github.com/gofiber/fiber/v2 v2.52.1 h1:1RoU2NS+b98o1L77sdl5mboGPiW+0Ypsi5oLmcYlgHI=
github.com/gofiber/fiber/v2 v2.52.1/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
github.com/gofiber/swagger v1.0.0 h1:BzUzDS9ZT6fDUa692kxmfOjc1DZiloLiPK/W5z1H1tc=
Expand Down Expand Up @@ -95,6 +97,8 @@ github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZ
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
Expand All @@ -105,47 +109,60 @@ github.com/swaggo/files/v2 v2.0.0 h1:hmAt8Dkynw7Ssz46F6pn8ok6YmGZqHSVLZ+HQM7i0kw
github.com/swaggo/files/v2 v2.0.0/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM=
github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg=
github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk=
github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.52.0 h1:wqBQpxH71XW0e2g+Og4dzQM8pk34aFYlA1Ga8db7gU0=
github.com/valyala/fasthttp v1.52.0/go.mod h1:hf5C4QnVMkNXMspnsUlfM3WitlgYflyhHYoKol/szxQ=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg=
golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ=
golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
2 changes: 1 addition & 1 deletion backend/handlers/auth_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (

// SignUp User godoc
//
// @Summary Sign up a new user
// @Summary Sign up a new user [normal]
// @Description This endpoint is responsible for registering a new user in the system.
// @Description It checks the verification status of the user's email before proceeding.
// @Description If the email is not verified, it returns an error.
Expand Down
47 changes: 31 additions & 16 deletions backend/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,14 @@ import (
"github.com/goccy/go-json"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/fiber/v2/middleware/healthcheck"
"github.com/gofiber/fiber/v2/middleware/helmet"
"github.com/gofiber/fiber/v2/middleware/limiter"
"github.com/gofiber/fiber/v2/middleware/monitor"
"github.com/gofiber/fiber/v2/middleware/requestid"
"github.com/gofiber/swagger"
"github.com/joho/godotenv"
"go.uber.org/zap"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"

Expand Down Expand Up @@ -72,9 +77,31 @@ func main() {
JSONEncoder: json.Marshal,
JSONDecoder: json.Unmarshal,
})

app.Server().MaxConnsPerIP = 100

// Middlewares
app.Use(healthcheck.New(healthcheck.Config{
LivenessProbe: func(c *fiber.Ctx) bool {
return true
},
LivenessEndpoint: "/",
}))
logger, _ := zap.NewProduction()

app.Use(middlewares.ZapLogMiddleware(logger))

app.Use(helmet.New())
app.Use(limiter.New(limiter.Config{
Max: 100,
Expiration: 30 * time.Second,
LimiterMiddleware: limiter.SlidingWindow{},
}))
app.Get("/metrics", monitor.New(monitor.Config{
Title: "chulbong-kr System Metrics",
Refresh: time.Second * 60,
}))
app.Use(requestid.New())

// Enable CORS for all routes
app.Use(cors.New(cors.Config{
AllowOrigins: "http://localhost:5173,https://chulbong-kr.vercel.app", // List allowed origins
Expand All @@ -84,14 +111,14 @@ func main() {
AllowCredentials: true,
}))

app.Use(logger.New())
app.Get("/", healthCheck)
// app.Use(logger.New())
app.Get("/swagger/*", swagger.HandlerDefault) // default

// Setup routes
api := app.Group("/api/v1")

api.Get("/google", handlers.GetGoogleAuthHandler(conf))
api.Get("/admin", func(c *fiber.Ctx) error { return c.JSON("good") }, middlewares.AdminOnly)

// Authentication routes
authGroup := api.Group("/auth")
Expand Down Expand Up @@ -167,15 +194,3 @@ func setTokenExpirationTime() {
// Assign the converted duration to the global variable
services.TOKEN_DURATION = time.Duration(durationInt) * time.Hour
}

func healthCheck(c *fiber.Ctx) error {
res := map[string]interface{}{
"data": "Server is up and running",
}

if err := c.JSON(res); err != nil {
return err
}

return nil
}
41 changes: 41 additions & 0 deletions backend/middlewares/auth_middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,44 @@ func AuthMiddleware(c *fiber.Ctx) error {
log.Printf("[DEBUG] Authenticated. %s", email)
return c.Next()
}

// AdminOnly checks admin permission
func AdminOnly(c *fiber.Ctx) error {
// Check for the cookie
jwtCookie := c.Cookies(TOKEN_COOKIE)
if jwtCookie == "" {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "No authorization token provided"})
}

// query to also fetch the user's role
query := `
SELECT u.UserID, u.Role, ot.ExpiresAt
FROM OpaqueTokens ot
JOIN Users u ON ot.UserID = u.UserID
WHERE ot.OpaqueToken = ?`

var userID int
var role string
var expiresAt time.Time
err := database.DB.QueryRow(query, jwtCookie).Scan(&userID, &role, &expiresAt)

// Check for no rows found or other errors
if err == sql.ErrNoRows {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid or expired token"})
} else if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Server error"})
}

// Check if the token has expired
if time.Now().After(expiresAt) {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Token expired"})
}

// Check if the user role is not admin
if role != "admin" {
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "Access denied"})
}

// Proceed to the next handler if the user is an admin
return c.Next()
}
52 changes: 52 additions & 0 deletions backend/middlewares/log_middleware.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package middlewares

import (
"time"

"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
)

func ZapLogMiddleware(logger *zap.Logger) fiber.Handler {
return func(c *fiber.Ctx) error {
// Start timer
start := time.Now()

// Process request
err := c.Next()

// Calculate duration
duration := time.Since(start)

// Gather request/response details
statusCode := c.Response().StatusCode()
method := c.Method()
path := c.OriginalURL()
clientIP := c.IP()
userAgent := c.Get(fiber.HeaderUserAgent)
referer := c.Get(fiber.HeaderReferer)

// Choose the log level and construct the log message
level := zap.InfoLevel
if statusCode >= 500 {
level = zap.ErrorLevel
} else if statusCode >= 400 {
level = zap.WarnLevel
}

// Construct the structured log
logger.Check(level, "HTTP request processed").
Write(
zap.Int("status", statusCode),
zap.String("method", method),
zap.String("path", path),
zap.String("client_ip", clientIP),
zap.String("user_agent", userAgent),
zap.String("referer", referer),
zap.Duration("duration", duration),
zap.Error(err), // Include the error if present
)

return nil
}
}
1 change: 1 addition & 0 deletions backend/models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type User struct {
PasswordHash sql.NullString `json:"-" db:"PasswordHash"` // Can be empty for OAuth2 users
Provider sql.NullString `json:"-" db:"Provider"` // e.g., "google", "kakao"
ProviderID sql.NullString `json:"-" db:"ProviderID"` // Unique ID from the OAuth provider
Role string `json:"-" db:"Role"`
CreatedAt time.Time `json:"-" db:"CreatedAt"`
UpdatedAt time.Time `json:"-" db:"UpdatedAt"`
}
11 changes: 11 additions & 0 deletions backend/services/token_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import (
"chulbong-kr/database"
"chulbong-kr/middlewares"
"crypto/rand"

"encoding/base64"
"encoding/hex"
mrand "math/rand"
"time"

"github.com/gofiber/fiber/v2"
Expand All @@ -20,6 +22,15 @@ func GenerateOpaqueToken() (string, error) {
return hex.EncodeToString(bytes), nil
}

func GenerateRandomString(n int) string {
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
s := make([]rune, n)
for i := range s {
s[i] = rune(letters[mrand.Intn(len(letters))])
}
return string(s)
}

// GenerateAndSaveToken generates a new token for a user and saves it in the database.
func GenerateAndSaveToken(userID int) (string, error) {
token, err := GenerateOpaqueToken() // a secure, random token.
Expand Down
Loading

0 comments on commit e8e3b65

Please sign in to comment.