1
Fork 0
photoview/api/database/database.go

250 lines
6.1 KiB
Go
Raw Normal View History

2020-01-30 14:49:39 +01:00
package database
import (
2021-01-12 19:12:56 +01:00
"context"
"fmt"
2020-01-30 14:49:39 +01:00
"log"
2020-01-31 15:22:58 +01:00
"net/url"
2021-01-12 19:12:56 +01:00
"time"
2020-01-30 14:49:39 +01:00
"github.com/photoview/photoview/api/database/drivers"
2020-12-17 23:18:00 +01:00
"github.com/photoview/photoview/api/graphql/models"
"github.com/photoview/photoview/api/utils"
"github.com/pkg/errors"
"github.com/go-sql-driver/mysql"
gorm_mysql "gorm.io/driver/mysql"
2021-01-31 17:06:25 +01:00
"gorm.io/driver/postgres"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
2020-11-30 21:29:49 +01:00
"gorm.io/gorm/logger"
2020-01-30 14:49:39 +01:00
)
2021-04-23 15:10:23 +02:00
func GetMysqlAddress(addressString string) (string, error) {
if addressString == "" {
return "", errors.New(fmt.Sprintf("Environment variable %s missing, exiting", utils.EnvMysqlURL.GetName()))
2020-01-31 15:22:58 +01:00
}
config, err := mysql.ParseDSN(addressString)
if err != nil {
return "", errors.Wrap(err, "Could not parse mysql url")
}
config.MultiStatements = true
config.ParseTime = true
2020-01-31 15:22:58 +01:00
return config.FormatDSN(), nil
}
2020-01-31 15:22:58 +01:00
2021-04-23 15:10:23 +02:00
func GetPostgresAddress(addressString string) (*url.URL, error) {
2021-01-31 17:06:25 +01:00
if addressString == "" {
return nil, errors.New(fmt.Sprintf("Environment variable %s missing, exiting", utils.EnvPostgresURL.GetName()))
}
address, err := url.Parse(addressString)
if err != nil {
return nil, errors.Wrap(err, "Could not parse postgres url")
}
return address, nil
}
2021-04-23 15:10:23 +02:00
func GetSqliteAddress(path string) (*url.URL, error) {
2021-01-17 12:45:23 +01:00
if path == "" {
path = "photoview.db"
}
address, err := url.Parse(path)
if err != nil {
return nil, errors.Wrapf(err, "Could not parse sqlite url (%s)", path)
}
queryValues := address.Query()
queryValues.Add("cache", "shared")
queryValues.Add("mode", "rwc")
// queryValues.Add("_busy_timeout", "60000") // 1 minute
address.RawQuery = queryValues.Encode()
// log.Panicf("%s", address.String())
return address, nil
}
2021-04-23 15:10:23 +02:00
func ConfigureDatabase(config *gorm.Config) (*gorm.DB, error) {
var databaseDialect gorm.Dialector
switch drivers.DatabaseDriver() {
case drivers.DatabaseDriverMysql:
2021-04-23 15:10:23 +02:00
mysqlAddress, err := GetMysqlAddress(utils.EnvMysqlURL.GetValue())
if err != nil {
return nil, err
}
log.Printf("Connecting to MYSQL database: %s", mysqlAddress)
databaseDialect = gorm_mysql.Open(mysqlAddress)
case drivers.DatabaseDriverSqlite:
2021-04-23 15:10:23 +02:00
sqliteAddress, err := GetSqliteAddress(utils.EnvSqlitePath.GetValue())
2021-01-17 12:45:23 +01:00
if err != nil {
return nil, err
}
log.Printf("Opening SQLITE database: %s", sqliteAddress)
2021-01-17 12:45:23 +01:00
databaseDialect = sqlite.Open(sqliteAddress.String())
2021-01-31 17:06:25 +01:00
case drivers.DatabaseDriverPostgres:
2021-04-23 15:10:23 +02:00
postgresAddress, err := GetPostgresAddress(utils.EnvPostgresURL.GetValue())
2021-01-31 17:06:25 +01:00
if err != nil {
return nil, err
}
log.Printf("Connecting to POSTGRES database: %s", postgresAddress.Redacted())
2021-01-31 17:06:25 +01:00
databaseDialect = postgres.Open(postgresAddress.String())
}
db, err := gorm.Open(databaseDialect, config)
if err != nil {
return nil, err
}
return db, nil
}
2021-01-12 19:12:56 +01:00
// SetupDatabase connects to the database using environment variables
func SetupDatabase() (*gorm.DB, error) {
config := gorm.Config{}
// Configure database logging
if utils.DevelopmentMode() {
config.Logger = logger.Default.LogMode(logger.Info)
} else {
config.Logger = logger.Default.LogMode(logger.Warn)
}
var db *gorm.DB
for retryCount := 1; retryCount <= 5; retryCount++ {
var err error
2021-04-23 15:10:23 +02:00
db, err = ConfigureDatabase(&config)
if err == nil {
sqlDB, dbErr := db.DB()
if dbErr != nil {
return nil, dbErr
2021-01-12 19:12:56 +01:00
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
err = sqlDB.PingContext(ctx)
cancel()
2021-01-19 16:59:10 +01:00
sqlDB.SetMaxOpenConns(80)
if err == nil {
return db, nil
}
2021-01-12 19:12:56 +01:00
}
log.Printf("WARN: Could not ping database: %s. Will retry after 5 seconds\n", err)
time.Sleep(time.Duration(5) * time.Second)
2021-01-12 19:12:56 +01:00
}
return db, nil
2020-01-30 14:49:39 +01:00
}
2020-01-31 15:22:58 +01:00
2021-04-23 15:10:23 +02:00
var database_models []interface{} = []interface{}{
&models.User{},
&models.AccessToken{},
&models.SiteInfo{},
&models.Media{},
&models.MediaURL{},
&models.Album{},
&models.MediaEXIF{},
&models.VideoMetadata{},
&models.ShareToken{},
&models.UserMediaData{},
&models.UserAlbums{},
2021-04-23 15:10:23 +02:00
&models.UserPreferences{},
// Face detection
&models.FaceGroup{},
&models.ImageFace{},
}
func MigrateDatabase(db *gorm.DB) error {
2020-01-31 15:22:58 +01:00
if err := db.SetupJoinTable(&models.User{}, "Albums", &models.UserAlbums{}); err != nil {
log.Printf("Setup UserAlbums join table failed: %v\n", err)
}
if err := db.AutoMigrate(database_models...); err != nil {
2021-04-13 22:15:47 +02:00
log.Printf("Auto migration failed: %v\n", err)
}
// v2.1.0 - Replaced by Media.CreatedAt
if db.Migrator().HasColumn(&models.Media{}, "date_imported") {
db.Migrator().DropColumn(&models.Media{}, "date_imported")
}
// v2.3.0 - Changed type of MediaEXIF.Exposure and MediaEXIF.Flash
// from string values to decimal and int respectively
if err := migrate_exif_fields(db); err != nil {
2021-04-13 22:15:47 +02:00
log.Printf("Failed to run exif fields migration: %v\n", err)
}
2021-09-18 21:21:40 +02:00
// // PJ-Watson: Attempt to add new column for FaceGroup.PreviewImageFace
if !(db.Migrator().HasColumn(&models.FaceGroup{}, "preview_image_face")) {
db.Migrator().AddColumn(&models.FaceGroup{}, "preview_image_face")
}
if err := migrate_face_preview(db); err != nil {
log.Printf("Failed to run face groups preview image migration: %v\n", err)
}
2020-01-31 15:22:58 +01:00
return nil
}
2021-04-23 15:10:23 +02:00
func ClearDatabase(db *gorm.DB) error {
err := db.Transaction(func(tx *gorm.DB) error {
2021-04-24 18:51:21 +02:00
2021-04-24 19:28:20 +02:00
db_driver := drivers.DatabaseDriver()
if db_driver == drivers.DatabaseDriverMysql {
if err := tx.Exec("SET FOREIGN_KEY_CHECKS = 0;").Error; err != nil {
return err
}
}
dry_run := tx.Session(&gorm.Session{DryRun: true})
2021-04-23 15:10:23 +02:00
for _, model := range database_models {
// get table name of model structure
table := dry_run.Find(model).Statement.Table
2021-04-24 19:28:20 +02:00
switch db_driver {
2021-04-24 19:21:10 +02:00
case drivers.DatabaseDriverPostgres:
if err := tx.Exec(fmt.Sprintf("TRUNCATE TABLE %s CASCADE", table)).Error; err != nil {
return err
}
case drivers.DatabaseDriverMysql:
if err := tx.Exec(fmt.Sprintf("TRUNCATE TABLE %s", table)).Error; err != nil {
return err
}
case drivers.DatabaseDriverSqlite:
if err := tx.Exec(fmt.Sprintf("DELETE FROM %s", table)).Error; err != nil {
return err
}
2021-04-23 15:10:23 +02:00
}
2021-04-24 19:21:10 +02:00
2021-04-23 15:10:23 +02:00
}
2021-04-24 19:28:20 +02:00
if db_driver == drivers.DatabaseDriverMysql {
if err := tx.Exec("SET FOREIGN_KEY_CHECKS = 1;").Error; err != nil {
return err
}
}
2021-04-23 15:10:23 +02:00
return nil
})
if err != nil {
return err
}
return nil
}