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
|
password=photoprism
|
||||||
host=mariadb
|
host=mariadb
|
||||||
port=4001
|
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/swaggo/gin-swagger v1.6.0
|
||||||
github.com/urfave/cli/v2 v2.27.6
|
github.com/urfave/cli/v2 v2.27.6
|
||||||
github.com/zitadel/oidc/v3 v3.36.1
|
github.com/zitadel/oidc/v3 v3.36.1
|
||||||
|
golang.org/x/mod v0.24.0
|
||||||
golang.org/x/sys v0.31.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.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.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.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
|
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||||
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
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-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-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
@@ -66,16 +66,17 @@ var initThumbsMutex sync.Mutex
|
|||||||
|
|
||||||
// Config holds database, cache and all parameters of photoprism
|
// Config holds database, cache and all parameters of photoprism
|
||||||
type Config struct {
|
type Config struct {
|
||||||
once sync.Once
|
once sync.Once
|
||||||
cliCtx *cli.Context
|
cliCtx *cli.Context
|
||||||
options *Options
|
options *Options
|
||||||
settings *customize.Settings
|
settings *customize.Settings
|
||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
hub *hub.Config
|
dbVersion string
|
||||||
token string
|
hub *hub.Config
|
||||||
serial string
|
token string
|
||||||
env string
|
serial string
|
||||||
start bool
|
env string
|
||||||
|
start bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/jinzhu/gorm"
|
"github.com/jinzhu/gorm"
|
||||||
_ "github.com/jinzhu/gorm/dialects/mysql"
|
_ "github.com/jinzhu/gorm/dialects/mysql"
|
||||||
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
||||||
|
"golang.org/x/mod/semver"
|
||||||
|
|
||||||
"github.com/photoprism/photoprism/internal/entity"
|
"github.com/photoprism/photoprism/internal/entity"
|
||||||
"github.com/photoprism/photoprism/internal/entity/migrate"
|
"github.com/photoprism/photoprism/internal/entity/migrate"
|
||||||
@@ -56,6 +57,49 @@ func (c *Config) DatabaseDriver() string {
|
|||||||
return c.options.DatabaseDriver
|
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).
|
// DatabaseDsn returns the database data source name (DSN).
|
||||||
func (c *Config) DatabaseDsn() string {
|
func (c *Config) DatabaseDsn() string {
|
||||||
if c.options.DatabaseDsn == "" {
|
if c.options.DatabaseDsn == "" {
|
||||||
@@ -343,15 +387,49 @@ func (c *Config) checkDb(db *gorm.DB) error {
|
|||||||
type Res struct {
|
type Res struct {
|
||||||
Value string `gorm:"column:Value;"`
|
Value string `gorm:"column:Value;"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var res Res
|
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
|
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")
|
log.Warnf("config: unknown database server version")
|
||||||
} else if major := txt.UInt(v[0]); major < 10 {
|
} 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)
|
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 {
|
} 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)
|
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.
|
// Ok.
|
||||||
c.db = db
|
c.db = db
|
||||||
|
|
||||||
|
@@ -13,6 +13,26 @@ func TestConfig_DatabaseDriver(t *testing.T) {
|
|||||||
assert.Equal(t, SQLite3, driver)
|
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) {
|
func TestConfig_ParseDatabaseDsn(t *testing.T) {
|
||||||
c := NewConfig(CliTestContext())
|
c := NewConfig(CliTestContext())
|
||||||
|
|
||||||
|
@@ -84,7 +84,10 @@ func Database(backupPath, fileName string, toStdOut, force bool, retain int) (er
|
|||||||
"-p"+c.DatabasePassword(),
|
"-p"+c.DatabasePassword(),
|
||||||
c.DatabaseName(),
|
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(
|
cmd = exec.Command(
|
||||||
c.MariadbDumpBin(),
|
c.MariadbDumpBin(),
|
||||||
"--protocol", "tcp",
|
"--protocol", "tcp",
|
||||||
@@ -94,7 +97,22 @@ func Database(backupPath, fileName string, toStdOut, force bool, retain int) (er
|
|||||||
"-p"+c.DatabasePassword(),
|
"-p"+c.DatabasePassword(),
|
||||||
c.DatabaseName(),
|
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:
|
case config.SQLite3:
|
||||||
if !fs.FileExistsNotEmpty(c.DatabaseFile()) {
|
if !fs.FileExistsNotEmpty(c.DatabaseFile()) {
|
||||||
return fmt.Errorf("sqlite database file %s not found", clean.LogQuote(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",
|
"-f",
|
||||||
c.DatabaseName(),
|
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(
|
cmd = exec.Command(
|
||||||
c.MariadbBin(),
|
c.MariadbBin(),
|
||||||
"--protocol", "tcp",
|
"--protocol", "tcp",
|
||||||
@@ -258,7 +279,23 @@ func RestoreDatabase(backupPath, fileName string, fromStdIn, force bool) (err er
|
|||||||
"-f",
|
"-f",
|
||||||
c.DatabaseName(),
|
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:
|
case config.SQLite3:
|
||||||
log.Infoln("restore: dropping existing sqlite database tables")
|
log.Infoln("restore: dropping existing sqlite database tables")
|
||||||
tables.Drop(c.Db())
|
tables.Drop(c.Db())
|
||||||
|
Reference in New Issue
Block a user