photoprism/internal/config/db.go
Michael Mayer c2fa3a7246 Add common feature flags incl upload #284
Signed-off-by: Michael Mayer <michael@liquidbytes.net>
2020-04-13 18:08:21 +02:00

218 lines
4.3 KiB
Go

package config
import (
"context"
"errors"
"io/ioutil"
"strings"
"time"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
_ "github.com/jinzhu/gorm/dialects/sqlite"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/mutex"
"github.com/photoprism/photoprism/internal/tidb"
"github.com/sirupsen/logrus"
)
// DatabaseDriver returns the database driver name.
func (c *Config) DatabaseDriver() string {
if c.params.DatabaseDriver == "" {
return DbTiDB
}
return c.params.DatabaseDriver
}
// DatabaseDsn returns the database data source name (DSN).
func (c *Config) DatabaseDsn() string {
if c.params.DatabaseDsn == "" {
return "root:photoprism@tcp(localhost:4000)/photoprism?parseTime=true"
}
return c.params.DatabaseDsn
}
// Db returns the db connection.
func (c *Config) Db() *gorm.DB {
if c.db == nil {
log.Fatal("config: database not initialised")
}
return c.db
}
// CloseDb closes the db connection (if any).
func (c *Config) CloseDb() error {
if c.db != nil {
if err := c.db.Close(); err == nil {
c.db = nil
} else {
return err
}
}
return nil
}
// MigrateDb will start a migration process.
func (c *Config) MigrateDb() {
db := c.Db()
db.AutoMigrate(
&entity.Account{},
&entity.File{},
&entity.FileShare{},
&entity.FileSync{},
&entity.Photo{},
&entity.Description{},
&entity.Event{},
&entity.Place{},
&entity.Location{},
&entity.Camera{},
&entity.Lens{},
&entity.Country{},
&entity.Album{},
&entity.PhotoAlbum{},
&entity.Label{},
&entity.Category{},
&entity.PhotoLabel{},
&entity.Keyword{},
&entity.PhotoKeyword{},
&entity.Link{},
)
entity.CreateUnknownPlace(db)
entity.CreateUnknownCountry(db)
}
// DropTables drops all tables in the currently configured database (be careful!).
func (c *Config) DropTables() {
db := c.Db()
logLevel := log.Level
log.SetLevel(logrus.FatalLevel)
db.SetLogger(log)
db.LogMode(false)
db.DropTableIfExists(
&entity.Account{},
&entity.File{},
&entity.FileShare{},
&entity.FileSync{},
&entity.Photo{},
&entity.Description{},
&entity.Event{},
&entity.Place{},
&entity.Location{},
&entity.Camera{},
&entity.Lens{},
&entity.Country{},
&entity.Album{},
&entity.PhotoAlbum{},
&entity.Label{},
&entity.Category{},
&entity.PhotoLabel{},
&entity.Keyword{},
&entity.PhotoKeyword{},
&entity.Link{},
)
log.SetLevel(logLevel)
}
// connectToDatabase establishes a database connection.
// When used with the internal driver, it may create a new database server instance.
// It tries to do this 12 times with a 5 second sleep interval in between.
func (c *Config) connectToDatabase(ctx context.Context) error {
mutex.Db.Lock()
defer mutex.Db.Unlock()
dbDriver := c.DatabaseDriver()
dbDsn := c.DatabaseDsn()
if dbDriver == "" {
return errors.New("config: database driver not specified")
}
if dbDsn == "" {
return errors.New("config: database DSN not specified")
}
isTiDB := false
initSuccess := false
if dbDriver == DbTiDB {
isTiDB = true
dbDriver = DbMySQL
}
db, err := gorm.Open(dbDriver, dbDsn)
if err != nil || db == nil {
if isTiDB {
log.Infof("starting database server at %s:%d\n", c.SqlServerHost(), c.SqlServerPort())
go tidb.Start(ctx, c.DatabasePath(), c.SqlServerPort(), c.SqlServerHost(), c.Debug())
}
for i := 1; i <= 12; i++ {
time.Sleep(5 * time.Second)
db, err = gorm.Open(dbDriver, dbDsn)
if db != nil && err == nil {
break
}
if isTiDB && !initSuccess {
err = tidb.InitDatabase(c.SqlServerPort(), c.SqlServerPassword())
if err != nil {
log.Debug(err)
} else {
initSuccess = true
}
}
}
if err != nil || db == nil {
log.Fatal(err)
}
}
db.LogMode(false)
db.SetLogger(log)
c.db = db
return err
}
// ImportSQL imports a file to the currently configured database.
func (c *Config) ImportSQL(filename string) {
contents, err := ioutil.ReadFile(filename)
if err != nil {
log.Error(err)
return
}
statements := strings.Split(string(contents), ";\n")
q := c.Db().Unscoped()
for _, stmt := range statements {
// Skip empty lines and comments
if len(stmt) < 3 || stmt[0] == '#' || stmt[0] == ';' {
continue
}
var result struct{}
err := q.Raw(stmt).Scan(&result).Error
if err != nil {
log.Error(err)
}
}
}