This commit is contained in:
koh
2025-04-12 15:25:34 +07:00
commit d83f6c1145
38 changed files with 2131 additions and 0 deletions

View File

@@ -0,0 +1,65 @@
package initialize
import (
"fmt"
"os"
"strings"
"github.com/dungnt11/todoms_golang/global"
"github.com/joho/godotenv"
)
func LoadConfig() error {
err := godotenv.Load()
if err != nil {
return err
}
// Cấu hình server
global.Config.Server.Port = os.Getenv("PORT")
global.Config.Server.AppEnv = os.Getenv("APP_ENV")
// Cấu hình database
global.Config.Database.Host = os.Getenv("BLUEPRINT_DB_HOST")
global.Config.Database.Port = os.Getenv("BLUEPRINT_DB_PORT")
global.Config.Database.Database = os.Getenv("BLUEPRINT_DB_DATABASE")
global.Config.Database.Username = os.Getenv("BLUEPRINT_DB_USERNAME")
global.Config.Database.Password = os.Getenv("BLUEPRINT_DB_PASSWORD")
global.Config.Database.RootPassword = os.Getenv("BLUEPRINT_DB_ROOT_PASSWORD")
// Chuyển đổi các giá trị cấu hình database từ string sang int
if maxIdleConns := os.Getenv("BLUEPRINT_DB_MAX_IDLE_CONNS"); maxIdleConns != "" {
fmt.Sscanf(maxIdleConns, "%d", &global.Config.Database.MaxIdleConns)
}
if maxOpenConns := os.Getenv("BLUEPRINT_DB_MAX_OPEN_CONNS"); maxOpenConns != "" {
fmt.Sscanf(maxOpenConns, "%d", &global.Config.Database.MaxOpenConns)
}
if connMaxLifetime := os.Getenv("BLUEPRINT_DB_CONN_MAX_LIFETIME"); connMaxLifetime != "" {
fmt.Sscanf(connMaxLifetime, "%d", &global.Config.Database.ConnMaxLifetime)
}
// Cấu hình logger
global.Config.Logger.LogLevel = os.Getenv("LOGGER_LOG_LEVEL")
global.Config.Logger.FileLogName = os.Getenv("LOGGER_FILE_LOG_NAME")
// Chuyển đổi từ string sang int và bool
if maxSize := os.Getenv("LOGGER_MAX_SIZE"); maxSize != "" {
fmt.Sscanf(maxSize, "%d", &global.Config.Logger.MaxSize)
}
if maxBackups := os.Getenv("LOGGER_MAX_BACKUPS"); maxBackups != "" {
fmt.Sscanf(maxBackups, "%d", &global.Config.Logger.MaxBackups)
}
if maxAge := os.Getenv("LOGGER_MAX_AGE"); maxAge != "" {
fmt.Sscanf(maxAge, "%d", &global.Config.Logger.MaxAge)
}
if compress := os.Getenv("LOGGER_COMPRESS"); compress != "" {
global.Config.Logger.Compress = strings.ToLower(compress) == "true"
}
return nil
}

View File

@@ -0,0 +1,103 @@
package initialize
import (
"os"
"github.com/dungnt11/todoms_golang/global"
"github.com/natefinch/lumberjack"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// InitLogger khởi tạo logger
func InitLogger() *zap.Logger {
// Đảm bảo thư mục logs tồn tại
ensureLogDir(global.Config.Logger.FileLogName)
// Khởi tạo logger
return newLogger(global.Config.Logger)
}
// ensureLogDir đảm bảo thư mục logs tồn tại
func ensureLogDir(logPath string) {
// Tìm vị trí thư mục cuối cùng trong đường dẫn
lastSlash := 0
for i := len(logPath) - 1; i >= 0; i-- {
if logPath[i] == '/' {
lastSlash = i
break
}
}
// Nếu không có thư mục, không cần tạo
if lastSlash == 0 {
return
}
// Tạo thư mục nếu chưa tồn tại
dirPath := logPath[:lastSlash]
if _, err := os.Stat(dirPath); os.IsNotExist(err) {
os.MkdirAll(dirPath, 0755)
}
}
// newLogger tạo một logger mới
func newLogger(config global.LoggerConfig) *zap.Logger {
// Xác định level log
var level zapcore.Level
switch config.LogLevel {
case "debug":
level = zapcore.DebugLevel
case "info":
level = zapcore.InfoLevel
case "warn":
level = zapcore.WarnLevel
case "error":
level = zapcore.ErrorLevel
default:
level = zapcore.InfoLevel
}
// Cấu hình encoder
encoder := getEncoder()
// Cấu hình lumberjack để xoay vòng file log
hook := lumberjack.Logger{
Filename: config.FileLogName,
MaxSize: config.MaxSize, // megabytes
MaxBackups: config.MaxBackups,
MaxAge: config.MaxAge, // days
Compress: config.Compress,
}
// Tạo core cho zap
core := zapcore.NewCore(
encoder,
zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(&hook)),
level,
)
// Tạo logger với các tùy chọn
return zap.New(core,
zap.AddCaller(),
zap.AddStacktrace(zapcore.ErrorLevel),
)
}
// getEncoder trả về encoder cho zap
func getEncoder() zapcore.Encoder {
encoderConfig := zap.NewProductionEncoderConfig()
// Cấu hình thời gian
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
encoderConfig.TimeKey = "time"
// Cấu hình level
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
// Cấu hình caller
encoderConfig.EncodeCaller = zapcore.ShortCallerEncoder
// Trả về encoder JSON
return zapcore.NewJSONEncoder(encoderConfig)
}

View File

@@ -0,0 +1,47 @@
package initialize
import (
"fmt"
"time"
"github.com/dungnt11/todoms_golang/global"
"go.uber.org/zap"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// checkErrorPanic kiểm tra lỗi và gọi panic nếu có lỗi, đồng thời ghi log chi tiết.
func checkErrorPanic(err error, errString string) {
if err != nil {
global.Logger.Error(errString, zap.Error(err))
panic(err)
}
}
// InitMysql khởi tạo kết nối đến cơ sở dữ liệu MySQL và trả về đối tượng *gorm.DB.
func InitMysql() *gorm.DB {
m := global.Config.Database
dsn := "%s:%s@tcp(%s:%v)/%s?charset=utf8mb4&parseTime=True&loc=Local"
s := fmt.Sprintf(dsn, m.Username, m.Password, m.Host, m.Port, m.Database)
db, err := gorm.Open(mysql.Open(s), &gorm.Config{
SkipDefaultTransaction: false,
})
checkErrorPanic(err, "InitMysql initialization error")
global.Logger.Info("Initializing MySQL Successfully")
setPool(db)
return db
}
// setPool thiết lập các thông số cho connection pool của cơ sở dữ liệu.
func setPool(db *gorm.DB) {
m := global.Config.Database
sqlDb, err := db.DB()
if err != nil {
global.Logger.Error("Failed to get sql.DB from gorm.DB", zap.Error(err))
return
}
sqlDb.SetMaxIdleConns(m.MaxIdleConns)
sqlDb.SetMaxOpenConns(m.MaxOpenConns)
sqlDb.SetConnMaxLifetime(time.Duration(m.ConnMaxLifetime) * time.Second)
}

View File

@@ -0,0 +1,93 @@
package initialize
import (
"context"
"fmt"
"net/http"
"os"
"os/signal"
"syscall"
"time"
"github.com/dungnt11/todoms_golang/global"
"github.com/dungnt11/todoms_golang/internal/routers"
"github.com/dungnt11/todoms_golang/internal/wire"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
// Run khởi động ứng dụng
func Run() {
// Tải cấu hình từ file .env
if err := LoadConfig(); err != nil {
fmt.Printf("Không thể tải cấu hình: %s\n", err.Error())
return
}
// Khởi tạo logger
global.Logger = InitLogger()
if global.Logger == nil {
fmt.Println("Khởi tạo logger thất bại")
return
}
// Khởi tạo database
global.Mdb = InitMysql()
if global.Mdb == nil {
global.Logger.Error("Khởi tạo MySQL thất bại")
return
}
// Khởi tạo router
r := provideRouter()
// Khởi tạo controllers thông qua wire
controllers, err := wire.InitializeControllers()
if err != nil {
global.Logger.Error("Khởi tạo controllers thất bại", zap.Error(err))
return
}
// Khởi tạo routers
routers.InitRouters(r, controllers)
// Khởi động server
port := global.Config.Server.Port
server := &http.Server{
Addr: fmt.Sprintf(":%s", port),
Handler: r,
}
// Khởi động server trong goroutine riêng
go func() {
global.Logger.Info(fmt.Sprintf("Server đang chạy trên cổng %s", port))
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
global.Logger.Error("Server gặp lỗi", zap.Error(err))
}
}()
// Graceful shutdown
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
global.Logger.Info("Đang tắt server...")
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
global.Logger.Error("Server shutdown gặp lỗi", zap.Error(err))
}
global.Logger.Info("Server đã tắt thành công.")
}
func provideRouter() *gin.Engine {
if global.Config.Server.AppEnv == "local" {
gin.SetMode(gin.DebugMode)
gin.ForceConsoleColor()
return gin.Default()
} else {
gin.SetMode(gin.ReleaseMode)
return gin.New()
}
}