Config repository (#3988)
* WIP * fix(test): fix ap test failing * fix: fix unkeyed fields being used * chore(tests): clean up browser tests by splitting out federation UI tests
This commit is contained in:
15
persistence/authrepository/authrepository.go
Normal file
15
persistence/authrepository/authrepository.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package authrepository
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/owncast/owncast/models"
|
||||
)
|
||||
|
||||
type AuthRepository interface {
|
||||
CreateBanIPTable(db *sql.DB)
|
||||
BanIPAddress(address, note string) error
|
||||
IsIPAddressBanned(address string) (bool, error)
|
||||
GetIPAddressBans() ([]models.IPAddress, error)
|
||||
RemoveIPAddressBan(address string) error
|
||||
}
|
||||
65
persistence/authrepository/bans.go
Normal file
65
persistence/authrepository/bans.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package authrepository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"log"
|
||||
|
||||
"github.com/owncast/owncast/db"
|
||||
"github.com/owncast/owncast/models"
|
||||
)
|
||||
|
||||
// CreateBanIPTable will create the IP ban table if needed.
|
||||
func (r *SqlAuthRepository) CreateBanIPTable(db *sql.DB) {
|
||||
createTableSQL := ` CREATE TABLE IF NOT EXISTS ip_bans (
|
||||
"ip_address" TEXT NOT NULL PRIMARY KEY,
|
||||
"notes" TEXT,
|
||||
"created_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);`
|
||||
|
||||
stmt, err := db.Prepare(createTableSQL)
|
||||
if err != nil {
|
||||
log.Fatal("error creating ip ban table", err)
|
||||
}
|
||||
defer stmt.Close()
|
||||
if _, err := stmt.Exec(); err != nil {
|
||||
log.Fatal("error creating ip ban table", err)
|
||||
}
|
||||
}
|
||||
|
||||
// BanIPAddress will persist a new IP address ban to the datastore.
|
||||
func (r *SqlAuthRepository) BanIPAddress(address, note string) error {
|
||||
return r.datastore.GetQueries().BanIPAddress(context.Background(), db.BanIPAddressParams{
|
||||
IpAddress: address,
|
||||
Notes: sql.NullString{String: note, Valid: true},
|
||||
})
|
||||
}
|
||||
|
||||
// IsIPAddressBanned will return if an IP address has been previously blocked.
|
||||
func (r *SqlAuthRepository) IsIPAddressBanned(address string) (bool, error) {
|
||||
blocked, error := r.datastore.GetQueries().IsIPAddressBlocked(context.Background(), address)
|
||||
return blocked > 0, error
|
||||
}
|
||||
|
||||
// GetIPAddressBans will return all the banned IP addresses.
|
||||
func (r *SqlAuthRepository) GetIPAddressBans() ([]models.IPAddress, error) {
|
||||
result, err := r.datastore.GetQueries().GetIPAddressBans(context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response := []models.IPAddress{}
|
||||
for _, ip := range result {
|
||||
response = append(response, models.IPAddress{
|
||||
IPAddress: ip.IpAddress,
|
||||
Notes: ip.Notes.String,
|
||||
CreatedAt: ip.CreatedAt.Time,
|
||||
})
|
||||
}
|
||||
return response, err
|
||||
}
|
||||
|
||||
// RemoveIPAddressBan will remove a previously banned IP address.
|
||||
func (r *SqlAuthRepository) RemoveIPAddressBan(address string) error {
|
||||
return r.datastore.GetQueries().RemoveIPAddressBan(context.Background(), address)
|
||||
}
|
||||
30
persistence/authrepository/sqlauthrepository.go
Normal file
30
persistence/authrepository/sqlauthrepository.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package authrepository
|
||||
|
||||
import (
|
||||
"github.com/owncast/owncast/core/data"
|
||||
)
|
||||
|
||||
type SqlAuthRepository struct {
|
||||
datastore *data.Datastore
|
||||
}
|
||||
|
||||
// NOTE: This is temporary during the transition period.
|
||||
var temporaryGlobalInstance AuthRepository
|
||||
|
||||
// Get will return the user repository.
|
||||
func Get() AuthRepository {
|
||||
if temporaryGlobalInstance == nil {
|
||||
i := New(data.GetDatastore())
|
||||
temporaryGlobalInstance = i
|
||||
}
|
||||
return temporaryGlobalInstance
|
||||
}
|
||||
|
||||
// New will create a new instance of the UserRepository.
|
||||
func New(datastore *data.Datastore) *SqlAuthRepository {
|
||||
r := &SqlAuthRepository{
|
||||
datastore: datastore,
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
13
persistence/configrepository/activitypub.go
Normal file
13
persistence/configrepository/activitypub.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package configrepository
|
||||
|
||||
// GetFederatedInboxMap is a mapping between account names and their outbox.
|
||||
func (r SqlConfigRepository) GetFederatedInboxMap() map[string]string {
|
||||
return map[string]string{
|
||||
r.GetDefaultFederationUsername(): r.GetDefaultFederationUsername(),
|
||||
}
|
||||
}
|
||||
|
||||
// GetDefaultFederationUsername will return the username used for sending federation activities.
|
||||
func (r *SqlConfigRepository) GetDefaultFederationUsername() string {
|
||||
return r.GetFederationUsername()
|
||||
}
|
||||
91
persistence/configrepository/configMigrations.go
Normal file
91
persistence/configrepository/configMigrations.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package configrepository
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/owncast/owncast/core/data"
|
||||
"github.com/owncast/owncast/models"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
datastoreValuesVersion = 4
|
||||
datastoreValueVersionKey = "DATA_STORE_VERSION"
|
||||
)
|
||||
|
||||
func migrateDatastoreValues(datastore *data.Datastore) {
|
||||
currentVersion, _ := datastore.GetNumber(datastoreValueVersionKey)
|
||||
if currentVersion == 0 {
|
||||
currentVersion = datastoreValuesVersion
|
||||
}
|
||||
|
||||
for v := currentVersion; v < datastoreValuesVersion; v++ {
|
||||
log.Infof("Migration datastore values from %d to %d\n", int(v), int(v+1))
|
||||
switch v {
|
||||
case 0:
|
||||
migrateToDatastoreValues1(datastore)
|
||||
case 1:
|
||||
migrateToDatastoreValues2(datastore)
|
||||
case 2:
|
||||
migrateToDatastoreValues3ServingEndpoint3(datastore)
|
||||
case 3:
|
||||
migrateToDatastoreValues4(datastore)
|
||||
default:
|
||||
log.Fatalln("missing datastore values migration step")
|
||||
}
|
||||
}
|
||||
if err := datastore.SetNumber(datastoreValueVersionKey, datastoreValuesVersion); err != nil {
|
||||
log.Errorln("error setting datastore value version:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func migrateToDatastoreValues1(datastore *data.Datastore) {
|
||||
// Migrate the forbidden usernames to be a slice instead of a string.
|
||||
forbiddenUsernamesString, _ := datastore.GetString(blockedUsernamesKey)
|
||||
if forbiddenUsernamesString != "" {
|
||||
forbiddenUsernamesSlice := strings.Split(forbiddenUsernamesString, ",")
|
||||
if err := datastore.SetStringSlice(blockedUsernamesKey, forbiddenUsernamesSlice); err != nil {
|
||||
log.Errorln("error migrating blocked username list:", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Migrate the suggested usernames to be a slice instead of a string.
|
||||
suggestedUsernamesString, _ := datastore.GetString(suggestedUsernamesKey)
|
||||
if suggestedUsernamesString != "" {
|
||||
suggestedUsernamesSlice := strings.Split(suggestedUsernamesString, ",")
|
||||
if err := datastore.SetStringSlice(suggestedUsernamesKey, suggestedUsernamesSlice); err != nil {
|
||||
log.Errorln("error migrating suggested username list:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func migrateToDatastoreValues2(datastore *data.Datastore) {
|
||||
configRepository := Get()
|
||||
|
||||
oldAdminPassword, _ := datastore.GetString("stream_key")
|
||||
// Avoids double hashing the password
|
||||
_ = datastore.SetString("admin_password_key", oldAdminPassword)
|
||||
_ = configRepository.SetStreamKeys([]models.StreamKey{
|
||||
{Key: oldAdminPassword, Comment: "Default stream key"},
|
||||
})
|
||||
}
|
||||
|
||||
func migrateToDatastoreValues3ServingEndpoint3(_ *data.Datastore) {
|
||||
configRepository := Get()
|
||||
s3Config := configRepository.GetS3Config()
|
||||
|
||||
if !s3Config.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
_ = configRepository.SetVideoServingEndpoint(s3Config.ServingEndpoint)
|
||||
}
|
||||
|
||||
func migrateToDatastoreValues4(datastore *data.Datastore) {
|
||||
configRepository := Get()
|
||||
unhashed_pass, _ := datastore.GetString("admin_password_key")
|
||||
err := configRepository.SetAdminPassword(unhashed_pass)
|
||||
if err != nil {
|
||||
log.Fatalln("error migrating admin password:", err)
|
||||
}
|
||||
}
|
||||
62
persistence/configrepository/configkeys.go
Normal file
62
persistence/configrepository/configkeys.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package configrepository
|
||||
|
||||
const (
|
||||
extraContentKey = "extra_page_content"
|
||||
streamTitleKey = "stream_title"
|
||||
adminPasswordKey = "admin_password_key"
|
||||
logoPathKey = "logo_path"
|
||||
logoUniquenessKey = "logo_uniqueness"
|
||||
serverSummaryKey = "server_summary"
|
||||
serverWelcomeMessageKey = "server_welcome_message"
|
||||
serverNameKey = "server_name"
|
||||
serverURLKey = "server_url"
|
||||
httpPortNumberKey = "http_port_number"
|
||||
httpListenAddressKey = "http_listen_address"
|
||||
websocketHostOverrideKey = "websocket_host_override"
|
||||
rtmpPortNumberKey = "rtmp_port_number"
|
||||
serverMetadataTagsKey = "server_metadata_tags"
|
||||
directoryEnabledKey = "directory_enabled"
|
||||
directoryRegistrationKeyKey = "directory_registration_key"
|
||||
socialHandlesKey = "social_handles"
|
||||
peakViewersSessionKey = "peak_viewers_session"
|
||||
peakViewersOverallKey = "peak_viewers_overall"
|
||||
lastDisconnectTimeKey = "last_disconnect_time"
|
||||
ffmpegPathKey = "ffmpeg_path"
|
||||
nsfwKey = "nsfw"
|
||||
s3StorageConfigKey = "s3_storage_config"
|
||||
videoLatencyLevel = "video_latency_level"
|
||||
videoStreamOutputVariantsKey = "video_stream_output_variants"
|
||||
chatDisabledKey = "chat_disabled"
|
||||
externalActionsKey = "external_actions"
|
||||
customStylesKey = "custom_styles"
|
||||
customJavascriptKey = "custom_javascript"
|
||||
videoCodecKey = "video_codec"
|
||||
blockedUsernamesKey = "blocked_usernames"
|
||||
publicKeyKey = "public_key"
|
||||
privateKeyKey = "private_key"
|
||||
serverInitDateKey = "server_init_date"
|
||||
federationEnabledKey = "federation_enabled"
|
||||
federationUsernameKey = "federation_username"
|
||||
federationPrivateKey = "federation_private"
|
||||
federationGoLiveMessageKey = "federation_go_live_message"
|
||||
federationShowEngagementKey = "federation_show_engagement"
|
||||
federationBlockedDomainsKey = "federation_blocked_domains"
|
||||
suggestedUsernamesKey = "suggested_usernames"
|
||||
chatJoinMessagesEnabledKey = "chat_join_messages_enabled"
|
||||
chatEstablishedUsersOnlyModeKey = "chat_established_users_only_mode"
|
||||
chatSpamProtectionEnabledKey = "chat_spam_protection_enabled"
|
||||
chatSlurFilterEnabledKey = "chat_slur_filter_enabled"
|
||||
notificationsEnabledKey = "notifications_enabled"
|
||||
discordConfigurationKey = "discord_configuration"
|
||||
browserPushConfigurationKey = "browser_push_configuration"
|
||||
browserPushPublicKeyKey = "browser_push_public_key"
|
||||
// nolint:gosec
|
||||
browserPushPrivateKeyKey = "browser_push_private_key"
|
||||
hasConfiguredInitialNotificationsKey = "has_configured_initial_notifications"
|
||||
hideViewerCountKey = "hide_viewer_count"
|
||||
customOfflineMessageKey = "custom_offline_message"
|
||||
customColorVariableValuesKey = "custom_color_variable_values"
|
||||
streamKeysKey = "stream_keys"
|
||||
disableSearchIndexingKey = "disable_search_indexing"
|
||||
videoServingEndpointKey = "video_serving_endpoint"
|
||||
)
|
||||
129
persistence/configrepository/configrepository.go
Normal file
129
persistence/configrepository/configrepository.go
Normal file
@@ -0,0 +1,129 @@
|
||||
package configrepository
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/owncast/owncast/models"
|
||||
"github.com/owncast/owncast/utils"
|
||||
)
|
||||
|
||||
type ConfigRepository interface {
|
||||
GetExtraPageBodyContent() string
|
||||
SetExtraPageBodyContent(content string) error
|
||||
GetStreamTitle() string
|
||||
SetStreamTitle(title string) error
|
||||
GetAdminPassword() string
|
||||
SetAdminPassword(key string) error
|
||||
GetLogoPath() string
|
||||
SetLogoPath(logo string) error
|
||||
SetLogoUniquenessString(uniqueness string) error
|
||||
GetLogoUniquenessString() string
|
||||
GetServerSummary() string
|
||||
SetServerSummary(summary string) error
|
||||
GetServerWelcomeMessage() string
|
||||
SetServerWelcomeMessage(welcomeMessage string) error
|
||||
GetServerName() string
|
||||
SetServerName(name string) error
|
||||
GetServerURL() string
|
||||
SetServerURL(url string) error
|
||||
GetHTTPPortNumber() int
|
||||
SetWebsocketOverrideHost(host string) error
|
||||
GetWebsocketOverrideHost() string
|
||||
SetHTTPPortNumber(port float64) error
|
||||
GetHTTPListenAddress() string
|
||||
SetHTTPListenAddress(address string) error
|
||||
GetRTMPPortNumber() int
|
||||
SetRTMPPortNumber(port float64) error
|
||||
GetServerMetadataTags() []string
|
||||
SetServerMetadataTags(tags []string) error
|
||||
GetDirectoryEnabled() bool
|
||||
SetDirectoryEnabled(enabled bool) error
|
||||
SetDirectoryRegistrationKey(key string) error
|
||||
GetDirectoryRegistrationKey() string
|
||||
GetSocialHandles() []models.SocialHandle
|
||||
SetSocialHandles(socialHandles []models.SocialHandle) error
|
||||
GetPeakSessionViewerCount() int
|
||||
SetPeakSessionViewerCount(count int) error
|
||||
GetPeakOverallViewerCount() int
|
||||
SetPeakOverallViewerCount(count int) error
|
||||
GetLastDisconnectTime() (*utils.NullTime, error)
|
||||
SetLastDisconnectTime(disconnectTime time.Time) error
|
||||
SetNSFW(isNSFW bool) error
|
||||
GetNSFW() bool
|
||||
SetFfmpegPath(path string) error
|
||||
GetFfMpegPath() string
|
||||
GetS3Config() models.S3
|
||||
SetS3Config(config models.S3) error
|
||||
GetStreamLatencyLevel() models.LatencyLevel
|
||||
SetStreamLatencyLevel(level float64) error
|
||||
GetStreamOutputVariants() []models.StreamOutputVariant
|
||||
SetStreamOutputVariants(variants []models.StreamOutputVariant) error
|
||||
SetChatDisabled(disabled bool) error
|
||||
GetChatDisabled() bool
|
||||
SetChatEstablishedUsersOnlyMode(enabled bool) error
|
||||
GetChatEstbalishedUsersOnlyMode() bool
|
||||
SetChatSpamProtectionEnabled(enabled bool) error
|
||||
GetChatSpamProtectionEnabled() bool
|
||||
SetChatSlurFilterEnabled(enabled bool) error
|
||||
GetChatSlurFilterEnabled() bool
|
||||
GetExternalActions() []models.ExternalAction
|
||||
SetExternalActions(actions []models.ExternalAction) error
|
||||
SetCustomStyles(styles string) error
|
||||
GetCustomStyles() string
|
||||
SetCustomJavascript(styles string) error
|
||||
GetCustomJavascript() string
|
||||
SetVideoCodec(codec string) error
|
||||
GetVideoCodec() string
|
||||
VerifySettings() error
|
||||
FindHighestVideoQualityIndex(qualities []models.StreamOutputVariant) (int, bool)
|
||||
GetForbiddenUsernameList() []string
|
||||
SetForbiddenUsernameList(usernames []string) error
|
||||
GetSuggestedUsernamesList() []string
|
||||
SetSuggestedUsernamesList(usernames []string) error
|
||||
GetServerInitTime() (*utils.NullTime, error)
|
||||
SetServerInitTime(t time.Time) error
|
||||
SetFederationEnabled(enabled bool) error
|
||||
GetFederationEnabled() bool
|
||||
SetFederationUsername(username string) error
|
||||
GetFederationUsername() string
|
||||
SetFederationGoLiveMessage(message string) error
|
||||
GetFederationGoLiveMessage() string
|
||||
SetFederationIsPrivate(isPrivate bool) error
|
||||
GetFederationIsPrivate() bool
|
||||
SetFederationShowEngagement(showEngagement bool) error
|
||||
GetFederationShowEngagement() bool
|
||||
SetBlockedFederatedDomains(domains []string) error
|
||||
GetBlockedFederatedDomains() []string
|
||||
SetChatJoinMessagesEnabled(enabled bool) error
|
||||
GetChatJoinPartMessagesEnabled() bool
|
||||
SetNotificationsEnabled(enabled bool) error
|
||||
GetNotificationsEnabled() bool
|
||||
GetDiscordConfig() models.DiscordConfiguration
|
||||
SetDiscordConfig(config models.DiscordConfiguration) error
|
||||
GetBrowserPushConfig() models.BrowserNotificationConfiguration
|
||||
SetBrowserPushConfig(config models.BrowserNotificationConfiguration) error
|
||||
SetBrowserPushPublicKey(key string) error
|
||||
GetBrowserPushPublicKey() (string, error)
|
||||
SetBrowserPushPrivateKey(key string) error
|
||||
GetBrowserPushPrivateKey() (string, error)
|
||||
SetHasPerformedInitialNotificationsConfig(hasConfigured bool) error
|
||||
GetHasPerformedInitialNotificationsConfig() bool
|
||||
GetHideViewerCount() bool
|
||||
SetHideViewerCount(hide bool) error
|
||||
GetCustomOfflineMessage() string
|
||||
SetCustomOfflineMessage(message string) error
|
||||
SetCustomColorVariableValues(variables map[string]string) error
|
||||
GetCustomColorVariableValues() map[string]string
|
||||
GetStreamKeys() []models.StreamKey
|
||||
SetStreamKeys(actions []models.StreamKey) error
|
||||
SetDisableSearchIndexing(disableSearchIndexing bool) error
|
||||
GetDisableSearchIndexing() bool
|
||||
GetVideoServingEndpoint() string
|
||||
SetVideoServingEndpoint(message string) error
|
||||
GetFederatedInboxMap() map[string]string
|
||||
GetDefaultFederationUsername() string
|
||||
GetPublicKey() string
|
||||
GetPrivateKey() string
|
||||
SetPublicKey(key string) error
|
||||
SetPrivateKey(key string) error
|
||||
}
|
||||
23
persistence/configrepository/crypto.go
Normal file
23
persistence/configrepository/crypto.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package configrepository
|
||||
|
||||
// GetPublicKey will return the public key.
|
||||
func (r *SqlConfigRepository) GetPublicKey() string {
|
||||
value, _ := r.datastore.GetString(publicKeyKey)
|
||||
return value
|
||||
}
|
||||
|
||||
// SetPublicKey will save the public key.
|
||||
func (r *SqlConfigRepository) SetPublicKey(key string) error {
|
||||
return r.datastore.SetString(publicKeyKey, key)
|
||||
}
|
||||
|
||||
// GetPrivateKey will return the private key.
|
||||
func (r *SqlConfigRepository) GetPrivateKey() string {
|
||||
value, _ := r.datastore.GetString(privateKeyKey)
|
||||
return value
|
||||
}
|
||||
|
||||
// SetPrivateKey will save the private key.
|
||||
func (r *SqlConfigRepository) SetPrivateKey(key string) error {
|
||||
return r.datastore.SetString(privateKeyKey, key)
|
||||
}
|
||||
62
persistence/configrepository/defaults.go
Normal file
62
persistence/configrepository/defaults.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package configrepository
|
||||
|
||||
import (
|
||||
"github.com/owncast/owncast/config"
|
||||
"github.com/owncast/owncast/models"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// PopulateDefaults will set default values in the database.
|
||||
func (r *SqlConfigRepository) PopulateDefaults() {
|
||||
key := "HAS_POPULATED_DEFAULTS"
|
||||
|
||||
r.datastore.WarmCache()
|
||||
|
||||
defaults := config.GetDefaults()
|
||||
|
||||
_ = r.SetAdminPassword(defaults.AdminPassword)
|
||||
_ = r.SetStreamKeys(defaults.StreamKeys)
|
||||
_ = r.SetHTTPPortNumber(float64(defaults.WebServerPort))
|
||||
_ = r.SetRTMPPortNumber(float64(defaults.RTMPServerPort))
|
||||
_ = r.SetLogoPath(defaults.Logo)
|
||||
_ = r.SetServerMetadataTags([]string{"owncast", "streaming"})
|
||||
_ = r.SetServerSummary(defaults.Summary)
|
||||
_ = r.SetServerWelcomeMessage("")
|
||||
_ = r.SetServerName(defaults.Name)
|
||||
_ = r.SetExtraPageBodyContent(defaults.PageBodyContent)
|
||||
_ = r.SetFederationGoLiveMessage(defaults.FederationGoLiveMessage)
|
||||
_ = r.SetSocialHandles([]models.SocialHandle{
|
||||
{
|
||||
Platform: "github",
|
||||
URL: "https://github.com/owncast/owncast",
|
||||
},
|
||||
})
|
||||
|
||||
if !r.HasPopulatedFederationDefaults() {
|
||||
if err := r.SetFederationGoLiveMessage(config.GetDefaults().FederationGoLiveMessage); err != nil {
|
||||
log.Errorln(err)
|
||||
}
|
||||
if err := r.datastore.SetBool("HAS_POPULATED_FEDERATION_DEFAULTS", true); err != nil {
|
||||
log.Errorln(err)
|
||||
}
|
||||
}
|
||||
|
||||
_ = r.datastore.SetBool(key, true)
|
||||
}
|
||||
|
||||
// HasPopulatedDefaults will determine if the defaults have been inserted into the database.
|
||||
func (r *SqlConfigRepository) HasPopulatedDefaults() bool {
|
||||
hasPopulated, err := r.datastore.GetBool("HAS_POPULATED_DEFAULTS")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return hasPopulated
|
||||
}
|
||||
|
||||
func (r *SqlConfigRepository) HasPopulatedFederationDefaults() bool {
|
||||
hasPopulated, err := r.datastore.GetBool("HAS_POPULATED_FEDERATION_DEFAULTS")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return hasPopulated
|
||||
}
|
||||
1003
persistence/configrepository/sqlconfigrepository.go
Normal file
1003
persistence/configrepository/sqlconfigrepository.go
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user