Files
RenewCMS/internal/infrastructure/persistence/database/postgres.go
T

109 lines
2.7 KiB
Go

package database
import (
domainUpdate "RenewCMS/internal/domain/update"
"fmt"
"net"
"net/url"
"reflect"
"time"
"github.com/jackc/pgx/v5/pgconn"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
const (
DriverPostgres = "postgres"
postgresTimeZone = "UTC"
postgresConnectTimeoutSeconds = 5
postgresScheme = "postgres"
postgresUTCCallback = "renewcms:postgres_utc"
gormQueryCallback = "gorm:query"
)
var timeType = reflect.TypeOf(time.Time{})
type postgresDriver struct{}
type PostgresConfig struct {
Host string
Port string
User string
Password string
Name string
SSLMode string
}
func PostgresDSN(config PostgresConfig) string {
dsn := url.URL{
Scheme: postgresScheme,
User: url.UserPassword(config.User, config.Password),
Host: net.JoinHostPort(config.Host, config.Port),
Path: "/" + config.Name,
}
query := url.Values{}
query.Set("sslmode", config.SSLMode)
query.Set("timezone", postgresTimeZone)
query.Set("connect_timeout", fmt.Sprint(postgresConnectTimeoutSeconds))
dsn.RawQuery = query.Encode()
return dsn.String()
}
func (postgresDriver) Open(source string) (*gorm.DB, error) {
parsed, err := pgconn.ParseConfig(source)
if err != nil {
return nil, fmt.Errorf("the postgres connection settings are invalid")
}
db, err := gorm.Open(postgres.Open(source), &gorm.Config{})
if err != nil {
address := net.JoinHostPort(parsed.Host, fmt.Sprint(parsed.Port))
return nil, fmt.Errorf("cannot use the postgres server at %s: %s", address, redact(err.Error(), parsed.Password))
}
if err := db.Callback().Query().After(gormQueryCallback).Register(postgresUTCCallback, readTimesInUTC); err != nil {
return nil, err
}
return db, nil
}
func readTimesInUTC(db *gorm.DB) {
if db.Error != nil || db.Statement.Dest == nil {
return
}
convertTimesToUTC(reflect.ValueOf(db.Statement.Dest))
}
func convertTimesToUTC(value reflect.Value) {
switch value.Kind() {
case reflect.Pointer, reflect.Interface:
if !value.IsNil() {
convertTimesToUTC(value.Elem())
}
case reflect.Slice, reflect.Array:
for index := 0; index < value.Len(); index++ {
convertTimesToUTC(value.Index(index))
}
case reflect.Struct:
convertStructTimesToUTC(value)
}
}
func convertStructTimesToUTC(value reflect.Value) {
if value.Type() == timeType {
if value.CanSet() {
value.Set(reflect.ValueOf(value.Interface().(time.Time).UTC()))
}
return
}
for index := 0; index < value.NumField(); index++ {
if value.Type().Field(index).IsExported() {
convertTimesToUTC(value.Field(index))
}
}
}
func (postgresDriver) NewBackup(db *gorm.DB, directory string, now func() time.Time) domainUpdate.DatabaseBackup {
return NewPostgresBackup(db, directory, now)
}