Add initial project structure with core functionality for book and stationery uploads
- Created main application entry point in `main.go`. - Added configuration management in `config/config.go` and tests in `config/config_test.go`. - Implemented handlers for book and stationery uploads in `handlers/book.go` and `handlers/stationery.go`, including validation logic. - Established database connection and services in `services/database.go` and `services/book_service.go`. - Defined models for books and stationery in `models/book.go` and `models/stationery.go`. - Set up Firebase integration for image uploads in `services/firebase.go`. - Created migration scripts for database schema in `migrations/001_create_tables.sql` and subsequent updates. - Added CORS and error handling middleware. - Included comprehensive tests for handlers, services, and utilities. - Documented API endpoints and usage in `README.md` and migration instructions in `migrations/README.md`. - Introduced `.gitignore` to exclude unnecessary files and directories. - Added Go module support with `go.mod` and `go.sum` files. - Implemented utility functions for slug generation and validation in `utils/slug.go` and `utils/validation.go`.
This commit is contained in:
85
services/database.go
Normal file
85
services/database.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"jd-book-uploader/config"
|
||||
)
|
||||
|
||||
// DB holds the database connection pool
|
||||
var DB *pgxpool.Pool
|
||||
|
||||
// NewDBPool creates a new database connection pool
|
||||
func NewDBPool(cfg *config.Config) (*pgxpool.Pool, error) {
|
||||
// Build connection string
|
||||
connString := fmt.Sprintf(
|
||||
"host=%s port=%s user=%s password=%s dbname=%s sslmode=disable",
|
||||
cfg.DBHost,
|
||||
cfg.DBPort,
|
||||
cfg.DBUser,
|
||||
cfg.DBPassword,
|
||||
cfg.DBName,
|
||||
)
|
||||
|
||||
// Parse connection string
|
||||
poolConfig, err := pgxpool.ParseConfig(connString)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse connection string: %w", err)
|
||||
}
|
||||
|
||||
// Configure connection pool
|
||||
poolConfig.MaxConns = 10
|
||||
poolConfig.MinConns = 2
|
||||
poolConfig.MaxConnLifetime = time.Hour
|
||||
poolConfig.MaxConnIdleTime = time.Minute * 30
|
||||
poolConfig.HealthCheckPeriod = time.Minute
|
||||
|
||||
// Set connection timeout
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Create connection pool
|
||||
pool, err := pgxpool.NewWithConfig(ctx, poolConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create connection pool: %w", err)
|
||||
}
|
||||
|
||||
// Test connection
|
||||
if err := pool.Ping(ctx); err != nil {
|
||||
pool.Close()
|
||||
return nil, fmt.Errorf("failed to ping database: %w", err)
|
||||
}
|
||||
|
||||
DB = pool
|
||||
return pool, nil
|
||||
}
|
||||
|
||||
// CloseDB closes the database connection pool
|
||||
func CloseDB() error {
|
||||
if DB != nil {
|
||||
DB.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RetryConnection attempts to reconnect to the database with retries
|
||||
func RetryConnection(cfg *config.Config, maxRetries int, retryDelay time.Duration) (*pgxpool.Pool, error) {
|
||||
var pool *pgxpool.Pool
|
||||
var err error
|
||||
|
||||
for i := 0; i < maxRetries; i++ {
|
||||
pool, err = NewDBPool(cfg)
|
||||
if err == nil {
|
||||
return pool, nil
|
||||
}
|
||||
|
||||
if i < maxRetries-1 {
|
||||
time.Sleep(retryDelay)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("failed to connect after %d attempts: %w", maxRetries, err)
|
||||
}
|
||||
Reference in New Issue
Block a user