mirror of
https://github.com/photoprism/photoprism.git
synced 2025-09-26 12:51:31 +08:00
Backups: Detect server version to determine SSL support #4837
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
1
.my.cnf
1
.my.cnf
@@ -3,3 +3,4 @@ user=root
|
||||
password=photoprism
|
||||
host=mariadb
|
||||
port=4001
|
||||
skip-ssl=true
|
1
go.mod
1
go.mod
@@ -87,6 +87,7 @@ require (
|
||||
github.com/swaggo/gin-swagger v1.6.0
|
||||
github.com/urfave/cli/v2 v2.27.6
|
||||
github.com/zitadel/oidc/v3 v3.36.1
|
||||
golang.org/x/mod v0.24.0
|
||||
golang.org/x/sys v0.31.0
|
||||
)
|
||||
|
||||
|
4
go.sum
4
go.sum
@@ -489,8 +489,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
|
||||
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@@ -71,6 +71,7 @@ type Config struct {
|
||||
options *Options
|
||||
settings *customize.Settings
|
||||
db *gorm.DB
|
||||
dbVersion string
|
||||
hub *hub.Config
|
||||
token string
|
||||
serial string
|
||||
|
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/jinzhu/gorm"
|
||||
_ "github.com/jinzhu/gorm/dialects/mysql"
|
||||
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
||||
"golang.org/x/mod/semver"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/entity/migrate"
|
||||
@@ -56,6 +57,49 @@ func (c *Config) DatabaseDriver() string {
|
||||
return c.options.DatabaseDriver
|
||||
}
|
||||
|
||||
// DatabaseDriverName returns the formatted database driver name.
|
||||
func (c *Config) DatabaseDriverName() string {
|
||||
switch c.DatabaseDriver() {
|
||||
case MySQL, MariaDB:
|
||||
return "MariaDB"
|
||||
case SQLite3, "sqlite", "sqllite", "test", "file", "":
|
||||
return "SQLite"
|
||||
case "tidb":
|
||||
return "TiDB"
|
||||
default:
|
||||
return "unsupported database"
|
||||
}
|
||||
}
|
||||
|
||||
// DatabaseVersion returns the database version string, if known.
|
||||
func (c *Config) DatabaseVersion() string {
|
||||
return c.dbVersion
|
||||
}
|
||||
|
||||
// IsDatabaseVersion checks if the database version is at least the specified version in semver format.
|
||||
func (c *Config) IsDatabaseVersion(semverVersion string) bool {
|
||||
if semverVersion == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
return semver.Compare(c.DatabaseVersion(), semverVersion) >= 0
|
||||
}
|
||||
|
||||
// DatabaseSsl checks if the database supports SSL connections for backup and restore.
|
||||
func (c *Config) DatabaseSsl() bool {
|
||||
if c.dbVersion == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
switch c.DatabaseDriver() {
|
||||
case MySQL:
|
||||
// see https://mariadb.org/mission-impossible-zero-configuration-ssl/
|
||||
return c.IsDatabaseVersion("v11.3")
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// DatabaseDsn returns the database data source name (DSN).
|
||||
func (c *Config) DatabaseDsn() string {
|
||||
if c.options.DatabaseDsn == "" {
|
||||
@@ -343,15 +387,49 @@ func (c *Config) checkDb(db *gorm.DB) error {
|
||||
type Res struct {
|
||||
Value string `gorm:"column:Value;"`
|
||||
}
|
||||
|
||||
var res Res
|
||||
if err := db.Raw("SHOW VARIABLES LIKE 'innodb_version'").Scan(&res).Error; err != nil {
|
||||
|
||||
err := db.Raw("SHOW VARIABLES LIKE 'innodb_version'").Scan(&res).Error
|
||||
|
||||
if err != nil {
|
||||
err = db.Raw("SELECT VERSION() AS Value").Scan(&res).Error
|
||||
}
|
||||
|
||||
// Version query not supported.
|
||||
if err != nil {
|
||||
log.Tracef("config: failed to detect database version (%s)", err)
|
||||
return nil
|
||||
} else if v := strings.Split(res.Value, "."); len(v) < 3 {
|
||||
}
|
||||
|
||||
if v := strings.Split(res.Value, "."); len(v) < 3 {
|
||||
log.Warnf("config: unknown database server version")
|
||||
} else if major := txt.UInt(v[0]); major < 10 {
|
||||
return fmt.Errorf("config: MySQL %s is not supported, see https://docs.photoprism.app/getting-started/#databases", res.Value)
|
||||
} else if sub := txt.UInt(v[1]); sub < 5 || sub == 5 && txt.UInt(v[2]) < 12 {
|
||||
return fmt.Errorf("config: MariaDB %s is not supported, see https://docs.photoprism.app/getting-started/#databases", res.Value)
|
||||
} else {
|
||||
c.dbVersion = fmt.Sprintf("v%d.%d", major, sub)
|
||||
}
|
||||
case SQLite3:
|
||||
type Res struct {
|
||||
Value string `gorm:"column:Value;"`
|
||||
}
|
||||
|
||||
var res Res
|
||||
|
||||
err := db.Raw("SELECT sqlite_version() AS Value").Scan(&res).Error
|
||||
|
||||
// Version query not supported.
|
||||
if err != nil {
|
||||
log.Warnf("config: failed to detect database version (%s)", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if v := strings.Split(res.Value, "."); len(v) < 3 {
|
||||
log.Warnf("config: unknown database server version")
|
||||
} else {
|
||||
c.dbVersion = fmt.Sprintf("v%d.%d", txt.UInt(v[0]), txt.UInt(v[1]))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -414,6 +492,10 @@ func (c *Config) connectDb() error {
|
||||
}
|
||||
}
|
||||
|
||||
if dbVersion := c.DatabaseVersion(); dbVersion != "" {
|
||||
log.Infof("database: opened connection to %s %s", c.DatabaseDriverName(), dbVersion)
|
||||
}
|
||||
|
||||
// Ok.
|
||||
c.db = db
|
||||
|
||||
|
@@ -13,6 +13,26 @@ func TestConfig_DatabaseDriver(t *testing.T) {
|
||||
assert.Equal(t, SQLite3, driver)
|
||||
}
|
||||
|
||||
func TestConfig_DatabaseDriverName(t *testing.T) {
|
||||
c := NewConfig(CliTestContext())
|
||||
|
||||
driver := c.DatabaseDriverName()
|
||||
assert.Equal(t, "SQLite", driver)
|
||||
}
|
||||
|
||||
func TestConfig_DatabaseVersion(t *testing.T) {
|
||||
c := TestConfig()
|
||||
|
||||
assert.NotEmpty(t, c.DatabaseVersion())
|
||||
assert.True(t, c.IsDatabaseVersion("v3.45"))
|
||||
}
|
||||
|
||||
func TestConfig_DatabaseSsl(t *testing.T) {
|
||||
c := TestConfig()
|
||||
|
||||
assert.False(t, c.DatabaseSsl())
|
||||
}
|
||||
|
||||
func TestConfig_ParseDatabaseDsn(t *testing.T) {
|
||||
c := NewConfig(CliTestContext())
|
||||
|
||||
|
@@ -84,7 +84,10 @@ func Database(backupPath, fileName string, toStdOut, force bool, retain int) (er
|
||||
"-p"+c.DatabasePassword(),
|
||||
c.DatabaseName(),
|
||||
)
|
||||
} else {
|
||||
} else if c.DatabaseSsl() {
|
||||
// see https://mariadb.org/mission-impossible-zero-configuration-ssl/
|
||||
log.Infof("backup: server supports zero-configuration ssl")
|
||||
|
||||
cmd = exec.Command(
|
||||
c.MariadbDumpBin(),
|
||||
"--protocol", "tcp",
|
||||
@@ -94,7 +97,22 @@ func Database(backupPath, fileName string, toStdOut, force bool, retain int) (er
|
||||
"-p"+c.DatabasePassword(),
|
||||
c.DatabaseName(),
|
||||
)
|
||||
} else {
|
||||
// see https://mariadb.org/mission-impossible-zero-configuration-ssl/
|
||||
log.Infof("backup: zero-configuration ssl not supported by the server")
|
||||
|
||||
cmd = exec.Command(
|
||||
c.MariadbDumpBin(),
|
||||
"--protocol", "tcp",
|
||||
"--skip-ssl",
|
||||
"-h", c.DatabaseHost(),
|
||||
"-P", c.DatabasePortString(),
|
||||
"-u", c.DatabaseUser(),
|
||||
"-p"+c.DatabasePassword(),
|
||||
c.DatabaseName(),
|
||||
)
|
||||
}
|
||||
|
||||
case config.SQLite3:
|
||||
if !fs.FileExistsNotEmpty(c.DatabaseFile()) {
|
||||
return fmt.Errorf("sqlite database file %s not found", clean.LogQuote(c.DatabaseFile()))
|
||||
@@ -247,7 +265,10 @@ func RestoreDatabase(backupPath, fileName string, fromStdIn, force bool) (err er
|
||||
"-f",
|
||||
c.DatabaseName(),
|
||||
)
|
||||
} else {
|
||||
} else if c.DatabaseSsl() {
|
||||
// see https://mariadb.org/mission-impossible-zero-configuration-ssl/
|
||||
log.Infof("restore: server supports zero-configuration ssl")
|
||||
|
||||
cmd = exec.Command(
|
||||
c.MariadbBin(),
|
||||
"--protocol", "tcp",
|
||||
@@ -258,7 +279,23 @@ func RestoreDatabase(backupPath, fileName string, fromStdIn, force bool) (err er
|
||||
"-f",
|
||||
c.DatabaseName(),
|
||||
)
|
||||
} else {
|
||||
// see https://mariadb.org/mission-impossible-zero-configuration-ssl/
|
||||
log.Infof("restore: zero-configuration ssl not supported by the server")
|
||||
|
||||
cmd = exec.Command(
|
||||
c.MariadbBin(),
|
||||
"--protocol", "tcp",
|
||||
"--skip-ssl",
|
||||
"-h", c.DatabaseHost(),
|
||||
"-P", c.DatabasePortString(),
|
||||
"-u", c.DatabaseUser(),
|
||||
"-p"+c.DatabasePassword(),
|
||||
"-f",
|
||||
c.DatabaseName(),
|
||||
)
|
||||
}
|
||||
|
||||
case config.SQLite3:
|
||||
log.Infoln("restore: dropping existing sqlite database tables")
|
||||
tables.Drop(c.Db())
|
||||
|
Reference in New Issue
Block a user