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:
Gabe Kangas
2024-11-15 19:20:58 -08:00
committed by GitHub
parent 56d52c283c
commit 0b5d7c8a4d
88 changed files with 2078 additions and 1643 deletions

View 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
}

View 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)
}

View 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
}

View 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()
}

View 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)
}
}

View 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"
)

View 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
}

View 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)
}

View 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
}

File diff suppressed because it is too large Load Diff