filebrowser/storage/sql/settings.go

369 lines
9.1 KiB
Go

package sql
import (
"database/sql"
"encoding/base64"
"encoding/json"
"fmt"
"github.com/filebrowser/filebrowser/v2/auth"
"github.com/filebrowser/filebrowser/v2/files"
"github.com/filebrowser/filebrowser/v2/rules"
"github.com/filebrowser/filebrowser/v2/settings"
"github.com/filebrowser/filebrowser/v2/users"
)
func init() {
}
type settingsBackend struct {
db *sql.DB
dbType string
}
func InitSettingsTable(db *sql.DB, dbType string) error {
sql := fmt.Sprintf("create table if not exists %s (%s varchar(128) primary key, value text);", quoteName(dbType, SettingsTable), quoteName(dbType, "key"))
_, err := db.Exec(sql)
checkError(err, "Fail to create table settings")
return err
}
func bytesToString(data []byte) string {
return base64.RawStdEncoding.EncodeToString(data)
}
func bytesFromString(s string) ([]byte, error) {
return base64.RawStdEncoding.DecodeString(s)
}
func userDefaultsFromString(s string) settings.UserDefaults {
if s == "" {
return settings.UserDefaults{}
}
userDefaults := settings.UserDefaults{}
err := json.Unmarshal([]byte(s), &userDefaults)
checkError(err, "Fail to parse settings.UserDefaults")
return userDefaults
}
func userDefaultsToString(d settings.UserDefaults) string {
data, err := json.Marshal(d)
if checkError(err, "Fail to stringify settings.UserDefaults") {
return ""
}
return string(data)
}
func brandingFromString(s string) settings.Branding {
if s == "" {
return settings.Branding{}
}
branding := settings.Branding{}
err := json.Unmarshal([]byte(s), &branding)
checkError(err, "Fail to parse settings.Branding")
return branding
}
func brandingToString(s settings.Branding) string {
data, err := json.Marshal(s)
if checkError(err, "Fail to jsonify settings.Branding") {
return ""
}
return string(data)
}
func commandsToString(c map[string][]string) string {
data, err := json.Marshal(c)
if checkError(err, "Fail to jsonify commands") {
return ""
}
return string(data)
}
func commandsFromString(s string) map[string][]string {
c := make(map[string][]string)
if s == "" {
return c
}
err := json.Unmarshal([]byte(s), &c)
checkError(err, "Fail to parse commands")
return c
}
func stringsFromString(s string) []string {
c := make([]string, 0)
if s == "" {
return c
}
err := json.Unmarshal([]byte(s), &c)
checkError(err, "Fail to parse []string")
return c
}
func stringsToString(c []string) string {
data, err := json.Marshal(c)
if checkError(err, "Fail to jsonify strings") {
return ""
}
return string(data)
}
func boolToInt(b bool) int {
if b {
return 1
}
return 0
}
func boolFromInt(i int) bool {
if i == 0 {
return false
}
return true
}
func boolFromString(s string) bool {
if s == "0" || s == "" || s == "f" || s == "F" {
return false
}
return true
}
func boolToString(b bool) string {
if b {
return "1"
}
return "0"
}
func (s settingsBackend) Get() (*settings.Settings, error) {
sql := fmt.Sprintf("select %s, value from %s;", quoteName(s.dbType, "key"), quoteName(s.dbType, SettingsTable))
rows, err := s.db.Query(sql)
if checkError(err, "Fail to Query settings.Settings") {
return nil, err
}
key := ""
value := ""
settings1 := cloneSettings(defaultSettings)
for rows.Next() {
err = rows.Scan(&key, &value)
checkError(err, "Fail to query settings.Settings")
if key == "Key" {
val, err := bytesFromString(value)
if !checkError(err, "Fail to parse []byte from string") {
settings1.Key = val
}
} else if key == "Signup" {
settings1.Signup = boolFromString(value)
} else if key == "CreateUserDir" {
settings1.CreateUserDir = boolFromString(value)
} else if key == "UserHomeBasePath" {
settings1.UserHomeBasePath = value
} else if key == "Defaults" {
settings1.Defaults = userDefaultsFromString(value)
} else if key == "AuthMethod" {
settings1.AuthMethod = settings.AuthMethod(value)
} else if key == "Branding" {
settings1.Branding = brandingFromString(value)
} else if key == "Commands" {
settings1.Commands = commandsFromString(value)
} else if key == "Shell" {
settings1.Shell = stringsFromString(value)
} else if key == "Rules" {
settings1.Rules = rulesFromString(value)
}
}
if len(settings1.Key) == 0 {
fmt.Println("The tables may not exist. Please run 'filebrowser config init' first")
return &settings1, nil
}
return &settings1, nil
}
func (s settingsBackend) Save(ss *settings.Settings) error {
fields := []string{"Key", "Signup", "CreateUserDir", "UserHomeBasePath", "Defaults", "AuthMethod", "Branding", "Commands", "Shell", "Rules"}
values := []string{
bytesToString(ss.Key),
boolToString(ss.Signup),
boolToString(ss.CreateUserDir),
ss.UserHomeBasePath,
userDefaultsToString(ss.Defaults),
string(ss.AuthMethod),
brandingToString(ss.Branding),
commandsToString(ss.Commands),
stringsToString(ss.Shell),
RulesToString(ss.Rules),
}
tx, err := s.db.Begin()
if checkError(err, "Fail to begin db transaction") {
return err
}
table := quoteName(s.dbType, SettingsTable)
k := quoteName(s.dbType, "key")
p1 := placeHolder(s.dbType, 1)
p2 := placeHolder(s.dbType, 2)
for i, field := range fields {
exists := ContainKey(s.db, s.dbType, field)
sql := fmt.Sprintf("INSERT INTO %s (value, %s) VALUES(%s,%s);", table, k, p1, p2)
if exists {
sql = fmt.Sprintf("UPDATE %s set value = %s where %s = %s;", table, p1, k, p2)
}
stmt, err := s.db.Prepare(sql)
defer stmt.Close()
if checkError(err, "Fail to prepare statement") {
tx.Rollback()
break
}
_, err = stmt.Exec(values[i], field)
if checkError(err, "Fail to insert field "+field+" of settings") {
tx.Rollback()
break
}
}
err = tx.Commit()
if checkError(err, "Fail to commit") {
tx.Rollback()
return err
}
return err
}
var defaultSettings = settings.Settings{
Key: []byte(""),
Signup: false,
CreateUserDir: false,
UserHomeBasePath: "/users",
Defaults: settings.UserDefaults{
Scope: ".",
Locale: "en",
ViewMode: "mosaic",
SingleClick: false,
Sorting: files.Sorting{
By: "",
Asc: false,
},
Perm: users.Permissions{
Admin: false,
Execute: true,
Create: true,
Rename: true,
Modify: true,
Delete: true,
Share: true,
Download: true,
},
Commands: make([]string, 0),
HideDotfiles: false,
DateFormat: false,
},
AuthMethod: auth.MethodJSONAuth,
Branding: settings.Branding{
Name: "",
DisableExternal: false,
Files: "",
Theme: "",
Color: "",
},
Commands: make(map[string][]string),
Shell: make([]string, 0),
Rules: make([]rules.Rule, 0),
}
func cloneSettings(s settings.Settings) settings.Settings {
data, err := json.Marshal(s)
s1 := settings.Settings{}
if checkError(err, "Fail to clone settings.Settings") {
return s1
}
json.Unmarshal(data, &s1)
return s1
}
func SetSetting(db *sql.DB, dbType string, key string, value string) error {
t := quoteName(dbType, SettingsTable)
k := quoteName(dbType, "key")
sql := fmt.Sprintf("select count(%s) from %s where %s = '%s';", k, t, k, key)
count := 0
err := db.QueryRow(sql).Scan(&count)
if checkError(err, "Fail to QueryRow for key="+key) {
return err
}
if count == 0 {
return addSetting(db, dbType, key, value)
}
return updateSetting(db, dbType, key, value)
}
func GetSetting(db *sql.DB, dbType string, key string) string {
sql := fmt.Sprintf("select value from %s where %s = '%s';", quoteName(dbType, SettingsTable), quoteName(dbType, "key"), key)
value := ""
err := db.QueryRow(sql).Scan(&value)
if checkError(err, "") {
return value
}
return value
}
func addSetting(db *sql.DB, dbType string, key string, value string) error {
table := quoteName(dbType, SettingsTable)
k := quoteName(dbType, "key")
p1 := placeHolder(dbType, 1)
p2 := placeHolder(dbType, 2)
sql := fmt.Sprintf("insert into %s (%s, value) values(%s, %s);", table, k, p1, p2)
stmt, err := db.Prepare(sql)
if checkError(err, "Fail to prepare sql") {
return err
}
_, err = stmt.Exec(key, value)
checkError(err, "Fail to add settings")
return err
}
func updateSetting(db *sql.DB, dbType string, key string, value string) error {
sql := fmt.Sprintf(
"update %s set value = %s where %s = %s;",
quoteName(dbType, SettingsTable),
placeHolder(dbType, 1),
quoteName(dbType, "key"),
placeHolder(dbType, 2),
)
stmt, err := db.Prepare(sql)
if checkError(err, "Fail to prepare sql") {
return err
}
_, err = stmt.Exec(key, value)
checkError(err, "Fail to updateSetting")
return err
}
func HadSetting(db *sql.DB) bool {
dbType, err := GetDBType(db)
if checkError(err, "Fail to get db type") {
return false
}
key := GetSetting(db, dbType, "Key")
if key == "" {
return false
}
return true
}
func ContainKey(db *sql.DB, dbType string, key string) bool {
sql := fmt.Sprintf("select value from %s where %s = '%s';", quoteName(dbType, SettingsTable), quoteName(dbType, "key"), key)
value := ""
err := db.QueryRow(sql).Scan(&value)
if checkError(err, "") {
return false
}
return true
}
func HadSettingOfKey(db *sql.DB, dbType string, key string) bool {
return GetSetting(db, dbType, "Key") == key
}
func newSettingsBackend(db *sql.DB, dbType string) settingsBackend {
InitSettingsTable(db, dbType)
return settingsBackend{db: db, dbType: dbType}
}