diff --git a/activitypub/activitypub.go b/activitypub/activitypub.go index a6aa99be5..0d5af327a 100644 --- a/activitypub/activitypub.go +++ b/activitypub/activitypub.go @@ -6,6 +6,7 @@ import ( "github.com/owncast/owncast/activitypub/outbox" "github.com/owncast/owncast/activitypub/persistence" "github.com/owncast/owncast/activitypub/workerpool" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/models" @@ -14,15 +15,16 @@ import ( // Start will initialize and start the federation support. func Start(datastore *data.Datastore) { + configRepository := configrepository.Get() persistence.Setup(datastore) workerpool.InitOutboundWorkerPool() inbox.InitInboxWorkerPool() // Generate the keys for signing federated activity if needed. - if data.GetPrivateKey() == "" { + if configRepository.GetPrivateKey() == "" { privateKey, publicKey, err := crypto.GenerateKeys() - _ = data.SetPrivateKey(string(privateKey)) - _ = data.SetPublicKey(string(publicKey)) + _ = configRepository.SetPrivateKey(string(privateKey)) + _ = configRepository.SetPublicKey(string(publicKey)) if err != nil { log.Errorln("Unable to get private key", err) } diff --git a/activitypub/apmodels/activity.go b/activitypub/apmodels/activity.go index 35cd6e56c..fd4ac057c 100644 --- a/activitypub/apmodels/activity.go +++ b/activitypub/apmodels/activity.go @@ -6,7 +6,7 @@ import ( "github.com/go-fed/activity/streams" "github.com/go-fed/activity/streams/vocab" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" ) // PrivacyAudience represents the audience for an activity. @@ -87,8 +87,10 @@ func MakeActivityDirect(activity vocab.ActivityStreamsCreate, toIRI *url.URL) vo // MakeActivityPublic sets the required properties to make this activity // seen as public. func MakeActivityPublic(activity vocab.ActivityStreamsCreate) vocab.ActivityStreamsCreate { + configRepository := configrepository.Get() + // TO the public if we're not treating ActivityPub as "private". - if !data.GetFederationIsPrivate() { + if !configRepository.GetFederationIsPrivate() { public, _ := url.Parse(PUBLIC) to := streams.NewActivityStreamsToProperty() @@ -121,7 +123,9 @@ func MakeUpdateActivity(activityID *url.URL) vocab.ActivityStreamsUpdate { activity.SetJSONLDId(id) // CC the public if we're not treating ActivityPub as "private". - if !data.GetFederationIsPrivate() { + configRepository := configrepository.Get() + + if !configRepository.GetFederationIsPrivate() { public, _ := url.Parse(PUBLIC) cc := streams.NewActivityStreamsCcProperty() cc.AppendIRI(public) diff --git a/activitypub/apmodels/actor.go b/activitypub/apmodels/actor.go index ae5cf5b18..f3f2aa347 100644 --- a/activitypub/apmodels/actor.go +++ b/activitypub/apmodels/actor.go @@ -9,8 +9,8 @@ import ( "github.com/go-fed/activity/streams" "github.com/go-fed/activity/streams/vocab" "github.com/owncast/owncast/activitypub/crypto" - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/persistence/configrepository" log "github.com/sirupsen/logrus" ) @@ -101,11 +101,13 @@ func MakeActorPropertyWithID(idIRI *url.URL) vocab.ActivityStreamsActorProperty // MakeServiceForAccount will create a new local actor service with the the provided username. func MakeServiceForAccount(accountName string) vocab.ActivityStreamsService { + configRepository := configrepository.Get() + actorIRI := MakeLocalIRIForAccount(accountName) person := streams.NewActivityStreamsService() nameProperty := streams.NewActivityStreamsNameProperty() - nameProperty.AppendXMLSchemaString(data.GetServerName()) + nameProperty.AppendXMLSchemaString(configRepository.GetServerName()) person.SetActivityStreamsName(nameProperty) preferredUsernameProperty := streams.NewActivityStreamsPreferredUsernameProperty() @@ -119,7 +121,7 @@ func MakeServiceForAccount(accountName string) vocab.ActivityStreamsService { person.SetActivityStreamsInbox(inboxProp) needsFollowApprovalProperty := streams.NewActivityStreamsManuallyApprovesFollowersProperty() - needsFollowApprovalProperty.Set(data.GetFederationIsPrivate()) + needsFollowApprovalProperty.Set(configRepository.GetFederationIsPrivate()) person.SetActivityStreamsManuallyApprovesFollowers(needsFollowApprovalProperty) outboxIRI := MakeLocalIRIForResource("/user/" + accountName + "/outbox") @@ -152,7 +154,7 @@ func MakeServiceForAccount(accountName string) vocab.ActivityStreamsService { publicKeyProp.AppendW3IDSecurityV1PublicKey(publicKeyType) person.SetW3IDSecurityV1PublicKey(publicKeyProp) - if t, err := data.GetServerInitTime(); t != nil { + if t, err := configRepository.GetServerInitTime(); t != nil { publishedDateProp := streams.NewActivityStreamsPublishedProperty() publishedDateProp.Set(t.Time) person.SetActivityStreamsPublished(publishedDateProp) @@ -163,8 +165,8 @@ func MakeServiceForAccount(accountName string) vocab.ActivityStreamsService { // Profile properties // Avatar - uniquenessString := data.GetLogoUniquenessString() - userAvatarURLString := data.GetServerURL() + "/logo/external" + uniquenessString := configRepository.GetLogoUniquenessString() + userAvatarURLString := configRepository.GetServerURL() + "/logo/external" userAvatarURL, err := url.Parse(userAvatarURLString) userAvatarURL.RawQuery = "uc=" + uniquenessString if err != nil { @@ -195,14 +197,14 @@ func MakeServiceForAccount(accountName string) vocab.ActivityStreamsService { // Profile bio summaryProperty := streams.NewActivityStreamsSummaryProperty() - summaryProperty.AppendXMLSchemaString(data.GetServerSummary()) + summaryProperty.AppendXMLSchemaString(configRepository.GetServerSummary()) person.SetActivityStreamsSummary(summaryProperty) // Links - if serverURL := data.GetServerURL(); serverURL != "" { + if serverURL := configRepository.GetServerURL(); serverURL != "" { addMetadataLinkToProfile(person, "Stream", serverURL) } - for _, link := range data.GetSocialHandles() { + for _, link := range configRepository.GetSocialHandles() { addMetadataLinkToProfile(person, link.Platform, link.URL) } @@ -220,7 +222,7 @@ func MakeServiceForAccount(accountName string) vocab.ActivityStreamsService { // Tags tagProp := streams.NewActivityStreamsTagProperty() - for _, tagString := range data.GetServerMetadataTags() { + for _, tagString := range configRepository.GetServerMetadataTags() { hashtag := MakeHashtag(tagString) tagProp.AppendTootHashtag(hashtag) } @@ -229,7 +231,7 @@ func MakeServiceForAccount(accountName string) vocab.ActivityStreamsService { // Work around an issue where a single attachment will not serialize // as an array, so add another item to the mix. - if len(data.GetSocialHandles()) == 1 { + if len(configRepository.GetSocialHandles()) == 1 { addMetadataLinkToProfile(person, "Owncast", "https://owncast.online") } diff --git a/activitypub/apmodels/actor_test.go b/activitypub/apmodels/actor_test.go index 389969d46..cb00b7720 100644 --- a/activitypub/apmodels/actor_test.go +++ b/activitypub/apmodels/actor_test.go @@ -9,6 +9,7 @@ import ( "github.com/go-fed/activity/streams" "github.com/go-fed/activity/streams/vocab" "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" ) func makeFakeService() vocab.ActivityStreamsService { @@ -55,9 +56,11 @@ func TestMain(m *testing.M) { if err != nil { panic(err) } - data.SetupPersistence(dbFile.Name()) - data.SetServerURL("https://my.cool.site.biz") + + configRepository := configrepository.Get() + + configRepository.SetServerURL("https://my.cool.site.biz") m.Run() } diff --git a/activitypub/apmodels/utils.go b/activitypub/apmodels/utils.go index b5fcaf687..c65082a6a 100644 --- a/activitypub/apmodels/utils.go +++ b/activitypub/apmodels/utils.go @@ -8,7 +8,7 @@ import ( "github.com/go-fed/activity/streams" "github.com/go-fed/activity/streams/vocab" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" log "github.com/sirupsen/logrus" ) @@ -27,7 +27,9 @@ func MakeRemoteIRIForResource(resourcePath string, host string) (*url.URL, error // MakeLocalIRIForResource will create an IRI for the local server. func MakeLocalIRIForResource(resourcePath string) *url.URL { - host := data.GetServerURL() + configRepository := configrepository.Get() + + host := configRepository.GetServerURL() u, err := url.Parse(host) if err != nil { log.Errorln("unable to parse local IRI url", host, err) @@ -41,7 +43,9 @@ func MakeLocalIRIForResource(resourcePath string) *url.URL { // MakeLocalIRIForAccount will return a full IRI for the local server account username. func MakeLocalIRIForAccount(account string) *url.URL { - host := data.GetServerURL() + configRepository := configrepository.Get() + + host := configRepository.GetServerURL() u, err := url.Parse(host) if err != nil { log.Errorln("unable to parse local IRI account server url", err) @@ -64,7 +68,9 @@ func Serialize(obj vocab.Type) ([]byte, error) { // MakeLocalIRIForStreamURL will return a full IRI for the local server stream url. func MakeLocalIRIForStreamURL() *url.URL { - host := data.GetServerURL() + configRepository := configrepository.Get() + + host := configRepository.GetServerURL() u, err := url.Parse(host) if err != nil { log.Errorln("unable to parse local IRI stream url", err) @@ -78,7 +84,9 @@ func MakeLocalIRIForStreamURL() *url.URL { // MakeLocalIRIforLogo will return a full IRI for the local server logo. func MakeLocalIRIforLogo() *url.URL { - host := data.GetServerURL() + configRepository := configrepository.Get() + + host := configRepository.GetServerURL() u, err := url.Parse(host) if err != nil { log.Errorln("unable to parse local IRI stream url", err) @@ -93,7 +101,9 @@ func MakeLocalIRIforLogo() *url.URL { // GetLogoType will return the rel value for the webfinger response and // the default static image is of type png. func GetLogoType() string { - imageFilename := data.GetLogoPath() + configRepository := configrepository.Get() + + imageFilename := configRepository.GetLogoPath() if imageFilename == "" { return "image/png" } diff --git a/activitypub/controllers/actors.go b/activitypub/controllers/actors.go index c3d758569..9a8d62e62 100644 --- a/activitypub/controllers/actors.go +++ b/activitypub/controllers/actors.go @@ -9,12 +9,14 @@ import ( "github.com/owncast/owncast/activitypub/apmodels" "github.com/owncast/owncast/activitypub/crypto" "github.com/owncast/owncast/activitypub/requests" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" ) // ActorHandler handles requests for a single actor. func ActorHandler(w http.ResponseWriter, r *http.Request) { - if !data.GetFederationEnabled() { + configRepository := configrepository.Get() + + if !configRepository.GetFederationEnabled() { w.WriteHeader(http.StatusMethodNotAllowed) return } @@ -22,7 +24,7 @@ func ActorHandler(w http.ResponseWriter, r *http.Request) { pathComponents := strings.Split(r.URL.Path, "/") accountName := pathComponents[3] - if _, valid := data.GetFederatedInboxMap()[accountName]; !valid { + if _, valid := configRepository.GetFederatedInboxMap()[accountName]; !valid { // User is not valid w.WriteHeader(http.StatusNotFound) return diff --git a/activitypub/controllers/followers.go b/activitypub/controllers/followers.go index 9a1ba7bdf..43ff1a42a 100644 --- a/activitypub/controllers/followers.go +++ b/activitypub/controllers/followers.go @@ -16,7 +16,7 @@ import ( "github.com/owncast/owncast/activitypub/crypto" "github.com/owncast/owncast/activitypub/persistence" "github.com/owncast/owncast/activitypub/requests" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" ) const ( @@ -145,7 +145,9 @@ func getFollowersPage(page string, r *http.Request) (vocab.ActivityStreamsOrdere } func createPageURL(r *http.Request, page *string) (*url.URL, error) { - domain := data.GetServerURL() + configRepository := configrepository.Get() + + domain := configRepository.GetServerURL() if domain == "" { return nil, errors.New("unable to get server URL") } diff --git a/activitypub/controllers/inbox.go b/activitypub/controllers/inbox.go index 29955d47d..5d85fea79 100644 --- a/activitypub/controllers/inbox.go +++ b/activitypub/controllers/inbox.go @@ -7,7 +7,7 @@ import ( "github.com/owncast/owncast/activitypub/apmodels" "github.com/owncast/owncast/activitypub/inbox" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" log "github.com/sirupsen/logrus" ) @@ -22,7 +22,9 @@ func InboxHandler(w http.ResponseWriter, r *http.Request) { } func acceptInboxRequest(w http.ResponseWriter, r *http.Request) { - if !data.GetFederationEnabled() { + configRepository := configrepository.Get() + + if !configRepository.GetFederationEnabled() { w.WriteHeader(http.StatusMethodNotAllowed) return } @@ -39,7 +41,7 @@ func acceptInboxRequest(w http.ResponseWriter, r *http.Request) { // The account this request is for must match the account name we have set // for federation. - if forLocalAccount != data.GetFederationUsername() { + if forLocalAccount != configRepository.GetFederationUsername() { w.WriteHeader(http.StatusNotFound) return } diff --git a/activitypub/controllers/nodeinfo.go b/activitypub/controllers/nodeinfo.go index ddddf4def..07d093617 100644 --- a/activitypub/controllers/nodeinfo.go +++ b/activitypub/controllers/nodeinfo.go @@ -10,7 +10,7 @@ import ( "github.com/owncast/owncast/activitypub/persistence" "github.com/owncast/owncast/activitypub/requests" "github.com/owncast/owncast/config" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" log "github.com/sirupsen/logrus" ) @@ -25,12 +25,14 @@ func NodeInfoController(w http.ResponseWriter, r *http.Request) { Links []links `json:"links"` } - if !data.GetFederationEnabled() { + configRepository := configrepository.Get() + + if !configRepository.GetFederationEnabled() { w.WriteHeader(http.StatusMethodNotAllowed) return } - serverURL := data.GetServerURL() + serverURL := configRepository.GetServerURL() if serverURL == "" { w.WriteHeader(http.StatusNotFound) return @@ -89,7 +91,9 @@ func NodeInfoV2Controller(w http.ResponseWriter, r *http.Request) { Metadata metadata `json:"metadata"` } - if !data.GetFederationEnabled() { + configRepository := configrepository.Get() + + if !configRepository.GetFederationEnabled() { w.WriteHeader(http.StatusMethodNotAllowed) return } @@ -117,7 +121,7 @@ func NodeInfoV2Controller(w http.ResponseWriter, r *http.Request) { OpenRegistrations: false, Protocols: []string{"activitypub"}, Metadata: metadata{ - ChatEnabled: !data.GetChatDisabled(), + ChatEnabled: !configRepository.GetChatDisabled(), }, } @@ -163,12 +167,14 @@ func XNodeInfo2Controller(w http.ResponseWriter, r *http.Request) { OpenRegistrations bool `json:"openRegistrations"` } - if !data.GetFederationEnabled() { + configRepository := configrepository.Get() + + if !configRepository.GetFederationEnabled() { w.WriteHeader(http.StatusMethodNotAllowed) return } - serverURL := data.GetServerURL() + serverURL := configRepository.GetServerURL() if serverURL == "" { w.WriteHeader(http.StatusNotFound) return @@ -178,7 +184,7 @@ func XNodeInfo2Controller(w http.ResponseWriter, r *http.Request) { res := &response{ Organization: Organization{ - Name: data.GetServerName(), + Name: configRepository.GetServerName(), Contact: serverURL, }, Server: Server{ @@ -232,12 +238,14 @@ func InstanceV1Controller(w http.ResponseWriter, r *http.Request) { InvitesEnabled bool `json:"invites_enabled"` } - if !data.GetFederationEnabled() { + configRepository := configrepository.Get() + + if !configRepository.GetFederationEnabled() { w.WriteHeader(http.StatusMethodNotAllowed) return } - serverURL := data.GetServerURL() + serverURL := configRepository.GetServerURL() if serverURL == "" { w.WriteHeader(http.StatusNotFound) return @@ -254,9 +262,9 @@ func InstanceV1Controller(w http.ResponseWriter, r *http.Request) { res := response{ URI: serverURL, - Title: data.GetServerName(), - ShortDescription: data.GetServerSummary(), - Description: data.GetServerSummary(), + Title: configRepository.GetServerName(), + ShortDescription: configRepository.GetServerSummary(), + Description: configRepository.GetServerSummary(), Version: config.GetReleaseString(), Stats: Stats{ UserCount: 1, @@ -275,7 +283,9 @@ func InstanceV1Controller(w http.ResponseWriter, r *http.Request) { } func writeResponse(payload interface{}, w http.ResponseWriter) error { - accountName := data.GetDefaultFederationUsername() + configRepository := configrepository.Get() + + accountName := configRepository.GetDefaultFederationUsername() actorIRI := apmodels.MakeLocalIRIForAccount(accountName) publicKey := crypto.GetPublicKey(actorIRI) @@ -284,13 +294,15 @@ func writeResponse(payload interface{}, w http.ResponseWriter) error { // HostMetaController points to webfinger. func HostMetaController(w http.ResponseWriter, r *http.Request) { - if !data.GetFederationEnabled() { + configRepository := configrepository.Get() + + if !configRepository.GetFederationEnabled() { w.WriteHeader(http.StatusMethodNotAllowed) log.Debugln("host meta request rejected! Federation is not enabled") return } - serverURL := data.GetServerURL() + serverURL := configRepository.GetServerURL() if serverURL == "" { w.WriteHeader(http.StatusNotFound) return diff --git a/activitypub/controllers/object.go b/activitypub/controllers/object.go index bdb03d748..0bc3d8fce 100644 --- a/activitypub/controllers/object.go +++ b/activitypub/controllers/object.go @@ -8,31 +8,33 @@ import ( "github.com/owncast/owncast/activitypub/crypto" "github.com/owncast/owncast/activitypub/persistence" "github.com/owncast/owncast/activitypub/requests" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" log "github.com/sirupsen/logrus" ) // ObjectHandler handles requests for a single federated ActivityPub object. func ObjectHandler(w http.ResponseWriter, r *http.Request) { - if !data.GetFederationEnabled() { + configRepository := configrepository.Get() + + if !configRepository.GetFederationEnabled() { w.WriteHeader(http.StatusMethodNotAllowed) return } // If private federation mode is enabled do not allow access to objects. - if data.GetFederationIsPrivate() { + if configRepository.GetFederationIsPrivate() { w.WriteHeader(http.StatusNotFound) return } - iri := strings.Join([]string{strings.TrimSuffix(data.GetServerURL(), "/"), r.URL.Path}, "") + iri := strings.Join([]string{strings.TrimSuffix(configRepository.GetServerURL(), "/"), r.URL.Path}, "") object, _, _, err := persistence.GetObjectByIRI(iri) if err != nil { w.WriteHeader(http.StatusNotFound) return } - accountName := data.GetDefaultFederationUsername() + accountName := configRepository.GetDefaultFederationUsername() actorIRI := apmodels.MakeLocalIRIForAccount(accountName) publicKey := crypto.GetPublicKey(actorIRI) diff --git a/activitypub/controllers/webfinger.go b/activitypub/controllers/webfinger.go index 959d1fdf2..d79cd1588 100644 --- a/activitypub/controllers/webfinger.go +++ b/activitypub/controllers/webfinger.go @@ -6,20 +6,22 @@ import ( "strings" "github.com/owncast/owncast/activitypub/apmodels" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/utils" log "github.com/sirupsen/logrus" ) // WebfingerHandler will handle webfinger lookup requests. func WebfingerHandler(w http.ResponseWriter, r *http.Request) { - if !data.GetFederationEnabled() { + configRepository := configrepository.Get() + + if !configRepository.GetFederationEnabled() { w.WriteHeader(http.StatusMethodNotAllowed) log.Debugln("webfinger request rejected! Federation is not enabled") return } - instanceHostURL := data.GetServerURL() + instanceHostURL := configRepository.GetServerURL() if instanceHostURL == "" { w.WriteHeader(http.StatusNotFound) log.Warnln("webfinger request rejected! Federation is enabled but server URL is empty.") @@ -29,7 +31,7 @@ func WebfingerHandler(w http.ResponseWriter, r *http.Request) { instanceHostString := utils.GetHostnameFromURLString(instanceHostURL) if instanceHostString == "" { w.WriteHeader(http.StatusNotFound) - log.Warnln("webfinger request rejected! Federation is enabled but server URL is not set properly. data.GetServerURL(): " + data.GetServerURL()) + log.Warnln("webfinger request rejected! Federation is enabled but server URL is not set properly. data.GetServerURL(): " + configRepository.GetServerURL()) return } @@ -51,7 +53,7 @@ func WebfingerHandler(w http.ResponseWriter, r *http.Request) { host := userComponents[1] user := userComponents[0] - if _, valid := data.GetFederatedInboxMap()[user]; !valid { + if _, valid := configRepository.GetFederatedInboxMap()[user]; !valid { w.WriteHeader(http.StatusNotFound) log.Debugln("webfinger request rejected! Invalid user: " + user) return diff --git a/activitypub/crypto/keys.go b/activitypub/crypto/keys.go index 240f29163..16161f98d 100644 --- a/activitypub/crypto/keys.go +++ b/activitypub/crypto/keys.go @@ -8,13 +8,15 @@ import ( "errors" "net/url" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" log "github.com/sirupsen/logrus" ) // GetPublicKey will return the public key for the provided actor. func GetPublicKey(actorIRI *url.URL) PublicKey { - key := data.GetPublicKey() + configRepository := configrepository.Get() + + key := configRepository.GetPublicKey() idURL, err := url.Parse(actorIRI.String() + "#main-key") if err != nil { log.Errorln("unable to parse actor iri string", idURL, err) @@ -29,7 +31,9 @@ func GetPublicKey(actorIRI *url.URL) PublicKey { // GetPrivateKey will return the internal server private key. func GetPrivateKey() *rsa.PrivateKey { - key := data.GetPrivateKey() + configRepository := configrepository.Get() + + key := configRepository.GetPrivateKey() block, _ := pem.Decode([]byte(key)) if block == nil { diff --git a/activitypub/inbox/chat.go b/activitypub/inbox/chat.go index 3b75427d9..3823d9bfc 100644 --- a/activitypub/inbox/chat.go +++ b/activitypub/inbox/chat.go @@ -7,17 +7,19 @@ import ( "github.com/owncast/owncast/activitypub/resolvers" "github.com/owncast/owncast/core/chat" "github.com/owncast/owncast/core/chat/events" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" ) func handleEngagementActivity(eventType events.EventType, isLiveNotification bool, actorReference vocab.ActivityStreamsActorProperty, action string) error { + configRepository := configrepository.Get() + // Do nothing if displaying engagement actions has been turned off. - if !data.GetFederationShowEngagement() { + if !configRepository.GetFederationShowEngagement() { return nil } // Do nothing if chat is disabled - if data.GetChatDisabled() { + if configRepository.GetChatDisabled() { return nil } @@ -36,11 +38,11 @@ func handleEngagementActivity(eventType events.EventType, isLiveNotification boo if isLiveNotification && action == events.FediverseEngagementLike { suffix = "liked that this stream went live." } else if action == events.FediverseEngagementLike { - suffix = fmt.Sprintf("liked a post from %s.", data.GetServerName()) + suffix = fmt.Sprintf("liked a post from %s.", configRepository.GetServerName()) } else if isLiveNotification && action == events.FediverseEngagementRepost { suffix = "shared this stream with their followers." } else if action == events.FediverseEngagementRepost { - suffix = fmt.Sprintf("shared a post from %s.", data.GetServerName()) + suffix = fmt.Sprintf("shared a post from %s.", configRepository.GetServerName()) } else if action == events.FediverseEngagementFollow { suffix = "followed this stream." } else { diff --git a/activitypub/inbox/follow.go b/activitypub/inbox/follow.go index c392112be..6c4c3378b 100644 --- a/activitypub/inbox/follow.go +++ b/activitypub/inbox/follow.go @@ -10,13 +10,15 @@ import ( "github.com/owncast/owncast/activitypub/requests" "github.com/owncast/owncast/activitypub/resolvers" "github.com/owncast/owncast/core/chat/events" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" "github.com/pkg/errors" log "github.com/sirupsen/logrus" ) func handleFollowInboxRequest(c context.Context, activity vocab.ActivityStreamsFollow) error { + configRepository := configrepository.Get() + follow, err := resolvers.MakeFollowRequest(c, activity) if err != nil { log.Errorln("unable to create follow inbox request", err) @@ -27,7 +29,7 @@ func handleFollowInboxRequest(c context.Context, activity vocab.ActivityStreamsF return fmt.Errorf("unable to handle request") } - approved := !data.GetFederationIsPrivate() + approved := !configRepository.GetFederationIsPrivate() followRequest := *follow @@ -36,7 +38,7 @@ func handleFollowInboxRequest(c context.Context, activity vocab.ActivityStreamsF return err } - localAccountName := data.GetDefaultFederationUsername() + localAccountName := configRepository.GetDefaultFederationUsername() if approved { if err := requests.SendFollowAccept(follow.Inbox, activity, localAccountName); err != nil { diff --git a/activitypub/inbox/worker.go b/activitypub/inbox/worker.go index a3c2f19e0..1d8f4c37f 100644 --- a/activitypub/inbox/worker.go +++ b/activitypub/inbox/worker.go @@ -15,7 +15,7 @@ import ( "github.com/owncast/owncast/activitypub/apmodels" "github.com/owncast/owncast/activitypub/persistence" "github.com/owncast/owncast/activitypub/resolvers" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" log "github.com/sirupsen/logrus" ) @@ -131,7 +131,9 @@ func Verify(request *http.Request) (bool, error) { } func isBlockedDomain(domain string) bool { - blockedDomains := data.GetBlockedFederatedDomains() + configRepository := configrepository.Get() + + blockedDomains := configRepository.GetBlockedFederatedDomains() for _, blockedDomain := range blockedDomains { if strings.Contains(domain, blockedDomain) { diff --git a/activitypub/inbox/worker_test.go b/activitypub/inbox/worker_test.go index 33ba624a2..5ddc98331 100644 --- a/activitypub/inbox/worker_test.go +++ b/activitypub/inbox/worker_test.go @@ -9,6 +9,7 @@ import ( "github.com/owncast/owncast/activitypub/apmodels" "github.com/owncast/owncast/activitypub/persistence" "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" ) func makeFakePerson() vocab.ActivityStreamsPerson { @@ -49,21 +50,24 @@ func makeFakePerson() vocab.ActivityStreamsPerson { func TestMain(m *testing.M) { data.SetupPersistence(":memory:") - data.SetServerURL("https://my.cool.site.biz") + configRepository := configrepository.Get() + configRepository.SetServerURL("https://my.cool.site.biz") persistence.Setup(data.GetDatastore()) m.Run() } func TestBlockedDomains(t *testing.T) { + configRepository := configrepository.Get() + person := makeFakePerson() - data.SetBlockedFederatedDomains([]string{"freedom.eagle", "guns.life"}) + configRepository.SetBlockedFederatedDomains([]string{"freedom.eagle", "guns.life"}) - if len(data.GetBlockedFederatedDomains()) != 2 { + if len(configRepository.GetBlockedFederatedDomains()) != 2 { t.Error("Blocked federated domains is not set correctly") } - for _, domain := range data.GetBlockedFederatedDomains() { + for _, domain := range configRepository.GetBlockedFederatedDomains() { if domain == person.GetJSONLDId().GetIRI().Host { return } diff --git a/activitypub/outbox/outbox.go b/activitypub/outbox/outbox.go index e035dd297..f9cf7a315 100644 --- a/activitypub/outbox/outbox.go +++ b/activitypub/outbox/outbox.go @@ -16,10 +16,10 @@ import ( "github.com/owncast/owncast/activitypub/resolvers" "github.com/owncast/owncast/activitypub/webfinger" "github.com/owncast/owncast/activitypub/workerpool" + "github.com/owncast/owncast/persistence/configrepository" "github.com/pkg/errors" "github.com/owncast/owncast/config" - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/utils" log "github.com/sirupsen/logrus" "github.com/teris-io/shortid" @@ -27,7 +27,9 @@ import ( // SendLive will send all followers the message saying you started a live stream. func SendLive() error { - textContent := data.GetFederationGoLiveMessage() + configRepository := configrepository.Get() + + textContent := configRepository.GetFederationGoLiveMessage() // If the message is empty then do not send it. if textContent == "" { @@ -38,7 +40,7 @@ func SendLive() error { reg := regexp.MustCompile("[^a-zA-Z0-9]+") tagProp := streams.NewActivityStreamsTagProperty() - for _, tagString := range data.GetServerMetadataTags() { + for _, tagString := range configRepository.GetServerMetadataTags() { tagWithoutSpecialCharacters := reg.ReplaceAllString(tagString, "") hashtag := apmodels.MakeHashtag(tagWithoutSpecialCharacters) tagProp.AppendTootHashtag(hashtag) @@ -57,15 +59,15 @@ func SendLive() error { tagsString := strings.Join(tagStrings, " ") var streamTitle string - if title := data.GetStreamTitle(); title != "" { + if title := configRepository.GetStreamTitle(); title != "" { streamTitle = fmt.Sprintf("

%s

", title) } - textContent = fmt.Sprintf("

%s

%s

%s

%s

", textContent, streamTitle, tagsString, data.GetServerURL(), data.GetServerURL()) + textContent = fmt.Sprintf("

%s

%s

%s

%s

", textContent, streamTitle, tagsString, configRepository.GetServerURL(), configRepository.GetServerURL()) activity, _, note, noteID := createBaseOutboundMessage(textContent) // To the public if we're not treating ActivityPub as "private". - if !data.GetFederationIsPrivate() { + if !configRepository.GetFederationIsPrivate() { note = apmodels.MakeNotePublic(note) activity = apmodels.MakeActivityPublic(activity) } @@ -73,7 +75,7 @@ func SendLive() error { note.SetActivityStreamsTag(tagProp) // Attach an image along with the Federated message. - previewURL, err := url.Parse(data.GetServerURL()) + previewURL, err := url.Parse(configRepository.GetServerURL()) if err == nil { var imageToAttach string var mediaType string @@ -94,7 +96,7 @@ func SendLive() error { } } - if data.GetNSFW() { + if configRepository.GetNSFW() { // Mark content as sensitive. sensitive := streams.NewActivityStreamsSensitiveProperty() sensitive.AppendXMLSchemaBoolean(true) @@ -151,6 +153,8 @@ func SendDirectMessageToAccount(textContent, account string) error { // SendPublicMessage will send a public message to all followers. func SendPublicMessage(textContent string) error { + configRepository := configrepository.Get() + originalContent := textContent textContent = utils.RenderSimpleMarkdown(textContent) @@ -173,7 +177,7 @@ func SendPublicMessage(textContent string) error { activity, _, note, noteID := createBaseOutboundMessage(textContent) note.SetActivityStreamsTag(tagProp) - if !data.GetFederationIsPrivate() { + if !configRepository.GetFederationIsPrivate() { note = apmodels.MakeNotePublic(note) activity = apmodels.MakeActivityPublic(activity) } @@ -197,7 +201,8 @@ func SendPublicMessage(textContent string) error { // nolint: unparam func createBaseOutboundMessage(textContent string) (vocab.ActivityStreamsCreate, string, vocab.ActivityStreamsNote, string) { - localActor := apmodels.MakeLocalIRIForAccount(data.GetDefaultFederationUsername()) + configRepository := configrepository.Get() + localActor := apmodels.MakeLocalIRIForAccount(configRepository.GetDefaultFederationUsername()) noteID := shortid.MustGenerate() noteIRI := apmodels.MakeLocalIRIForResource(noteID) id := shortid.MustGenerate() @@ -218,7 +223,8 @@ func getHashtagLinkHTMLFromTagString(baseHashtag string) string { // SendToFollowers will send an arbitrary payload to all follower inboxes. func SendToFollowers(payload []byte) error { - localActor := apmodels.MakeLocalIRIForAccount(data.GetDefaultFederationUsername()) + configRepository := configrepository.Get() + localActor := apmodels.MakeLocalIRIForAccount(configRepository.GetDefaultFederationUsername()) followers, _, err := persistence.GetFederationFollowers(-1, 0) if err != nil { @@ -241,7 +247,8 @@ func SendToFollowers(payload []byte) error { // SendToUser will send a payload to a single specific inbox. func SendToUser(inbox *url.URL, payload []byte) error { - localActor := apmodels.MakeLocalIRIForAccount(data.GetDefaultFederationUsername()) + configRepository := configrepository.Get() + localActor := apmodels.MakeLocalIRIForAccount(configRepository.GetDefaultFederationUsername()) req, err := requests.CreateSignedRequest(payload, inbox, localActor) if err != nil { @@ -255,8 +262,10 @@ func SendToUser(inbox *url.URL, payload []byte) error { // UpdateFollowersWithAccountUpdates will send an update to all followers alerting of a profile update. func UpdateFollowersWithAccountUpdates() error { + configRepository := configrepository.Get() + // Don't do anything if federation is disabled. - if !data.GetFederationEnabled() { + if !configRepository.GetFederationEnabled() { return nil } @@ -265,7 +274,7 @@ func UpdateFollowersWithAccountUpdates() error { activity := apmodels.MakeUpdateActivity(objectID) actor := streams.NewActivityStreamsPerson() - actorID := apmodels.MakeLocalIRIForAccount(data.GetDefaultFederationUsername()) + actorID := apmodels.MakeLocalIRIForAccount(configRepository.GetDefaultFederationUsername()) actorIDProperty := streams.NewJSONLDIdProperty() actorIDProperty.Set(actorID) actor.SetJSONLDId(actorIDProperty) diff --git a/activitypub/resolvers/resolve.go b/activitypub/resolvers/resolve.go index 35c102bfb..bcf27d10d 100644 --- a/activitypub/resolvers/resolve.go +++ b/activitypub/resolvers/resolve.go @@ -10,7 +10,7 @@ import ( "github.com/go-fed/activity/streams/vocab" "github.com/owncast/owncast/activitypub/apmodels" "github.com/owncast/owncast/activitypub/crypto" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" "github.com/pkg/errors" log "github.com/sirupsen/logrus" ) @@ -47,11 +47,12 @@ func Resolve(c context.Context, data []byte, callbacks ...interface{}) error { // ResolveIRI will resolve an IRI ahd call the correct callback for the resolved type. func ResolveIRI(c context.Context, iri string, callbacks ...interface{}) error { + configRepository := configrepository.Get() log.Debugln("Resolving", iri) req, _ := http.NewRequest(http.MethodGet, iri, nil) - actor := apmodels.MakeLocalIRIForAccount(data.GetDefaultFederationUsername()) + actor := apmodels.MakeLocalIRIForAccount(configRepository.GetDefaultFederationUsername()) if err := crypto.SignRequest(req, nil, actor); err != nil { return err } diff --git a/auth/indieauth/client.go b/auth/indieauth/client.go index 9d7a4e736..0828d2b4e 100644 --- a/auth/indieauth/client.go +++ b/auth/indieauth/client.go @@ -11,7 +11,7 @@ import ( "sync" "time" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/utils" "github.com/pkg/errors" log "github.com/sirupsen/logrus" @@ -47,6 +47,8 @@ func setupExpiredRequestPruner() { // StartAuthFlow will begin the IndieAuth flow by generating an auth request. func StartAuthFlow(authHost, userID, accessToken, displayName string) (*url.URL, error) { + configRepository := configrepository.Get() + // Limit the number of pending requests if len(pendingAuthRequests) >= maxPendingRequests { return nil, errors.New("Please try again later. Too many pending requests.") @@ -68,7 +70,7 @@ func StartAuthFlow(authHost, userID, accessToken, displayName string) (*url.URL, return nil, errors.New("only servers secured with https are supported") } - serverURL := data.GetServerURL() + serverURL := configRepository.GetServerURL() if serverURL == "" { return nil, errors.New("Owncast server URL must be set when using auth") } diff --git a/auth/indieauth/server.go b/auth/indieauth/server.go index 0d2ddb378..8c0abd79c 100644 --- a/auth/indieauth/server.go +++ b/auth/indieauth/server.go @@ -4,7 +4,7 @@ import ( "fmt" "time" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" "github.com/pkg/errors" "github.com/teris-io/shortid" ) @@ -70,6 +70,8 @@ func StartServerAuth(clientID, redirectURI, codeChallenge, state, me string) (*S // CompleteServerAuth will verify that the values provided in the final step // of the IndieAuth flow are correct, and return some basic profile info. func CompleteServerAuth(code, redirectURI, clientID string, codeVerifier string) (*ServerProfileResponse, error) { + configRepository := configrepository.Get() + request, pending := pendingServerAuthRequests[code] if !pending { return nil, errors.New("no pending authentication request") @@ -89,11 +91,11 @@ func CompleteServerAuth(code, redirectURI, clientID string, codeVerifier string) } response := ServerProfileResponse{ - Me: data.GetServerURL(), + Me: configRepository.GetServerURL(), Profile: ServerProfile{ - Name: data.GetServerName(), - URL: data.GetServerURL(), - Photo: fmt.Sprintf("%s/%s", data.GetServerURL(), data.GetLogoPath()), + Name: configRepository.GetServerName(), + URL: configRepository.GetServerURL(), + Photo: fmt.Sprintf("%s/%s", configRepository.GetServerURL(), configRepository.GetLogoPath()), }, } diff --git a/core/chat/chat.go b/core/chat/chat.go index 03d32c6ec..bd7789412 100644 --- a/core/chat/chat.go +++ b/core/chat/chat.go @@ -7,8 +7,8 @@ import ( "github.com/owncast/owncast/config" "github.com/owncast/owncast/core/chat/events" - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/persistence/configrepository" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" log "github.com/sirupsen/logrus" @@ -23,6 +23,8 @@ var ( func Start(getStatusFunc func() models.Status) error { setupPersistence() + configRepository := configrepository.Get() + getStatus = getStatusFunc _server = NewChat() @@ -35,7 +37,7 @@ func Start(getStatusFunc func() models.Status) error { Help: "The number of chat messages incremented over time.", ConstLabels: map[string]string{ "version": config.VersionNumber, - "host": data.GetServerURL(), + "host": configRepository.GetServerURL(), }, }) diff --git a/core/chat/chatclient.go b/core/chat/chatclient.go index 7eb28543e..e15f41f85 100644 --- a/core/chat/chatclient.go +++ b/core/chat/chatclient.go @@ -13,8 +13,8 @@ import ( "github.com/gorilla/websocket" "github.com/owncast/owncast/config" "github.com/owncast/owncast/core/chat/events" - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/services/geoip" ) @@ -133,7 +133,9 @@ func (c *Client) readPump() { } // Check if this message passes the optional language filter - if data.GetChatSlurFilterEnabled() && !c.messageFilter.Allow(string(message)) { + configRepository := configrepository.Get() + + if configRepository.GetChatSlurFilterEnabled() && !c.messageFilter.Allow(string(message)) { c.sendAction("Sorry, that message contained language that is not allowed in this chat.") continue } @@ -209,9 +211,11 @@ func (c *Client) close() { } func (c *Client) passesRateLimit() bool { + configRepository := configrepository.Get() + // If spam rate limiting is disabled, or the user is a moderator, always // allow the message. - if !data.GetChatSpamProtectionEnabled() || c.User.IsModerator() { + if !configRepository.GetChatSpamProtectionEnabled() || c.User.IsModerator() { return true } diff --git a/core/chat/events.go b/core/chat/events.go index 38d06e77d..09fb29999 100644 --- a/core/chat/events.go +++ b/core/chat/events.go @@ -8,8 +8,8 @@ import ( "github.com/owncast/owncast/config" "github.com/owncast/owncast/core/chat/events" - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/core/webhooks" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/persistence/userrepository" "github.com/owncast/owncast/utils" log "github.com/sirupsen/logrus" @@ -22,10 +22,12 @@ func (s *Server) userNameChanged(eventData chatClientEvent) { return } + configRepository := configrepository.Get() + proposedUsername := receivedEvent.NewName // Check if name is on the blocklist - blocklist := data.GetForbiddenUsernameList() + blocklist := configRepository.GetForbiddenUsernameList() // Names have a max length proposedUsername = utils.MakeSafeStringOfLength(proposedUsername, config.MaxChatDisplayNameLength) diff --git a/core/chat/events/fediverseEngagementEvent.go b/core/chat/events/fediverseEngagementEvent.go index a447a64ac..2c797f7fc 100644 --- a/core/chat/events/fediverseEngagementEvent.go +++ b/core/chat/events/fediverseEngagementEvent.go @@ -1,6 +1,8 @@ package events -import "github.com/owncast/owncast/core/data" +import ( + "github.com/owncast/owncast/persistence/configrepository" +) // FediverseEngagementEvent is a message displayed in chat on representing an action on the Fediverse. type FediverseEngagementEvent struct { @@ -13,6 +15,8 @@ type FediverseEngagementEvent struct { // GetBroadcastPayload will return the object to send to all chat users. func (e *FediverseEngagementEvent) GetBroadcastPayload() EventPayload { + configRepository := configrepository.Get() + return EventPayload{ "id": e.ID, "timestamp": e.Timestamp, @@ -22,7 +26,7 @@ func (e *FediverseEngagementEvent) GetBroadcastPayload() EventPayload { "title": e.UserAccountName, "link": e.Link, "user": EventPayload{ - "displayName": data.GetServerName(), + "displayName": configRepository.GetServerName(), }, } } diff --git a/core/chat/events/systemMessageEvent.go b/core/chat/events/systemMessageEvent.go index 7f5f1e732..61a20d974 100644 --- a/core/chat/events/systemMessageEvent.go +++ b/core/chat/events/systemMessageEvent.go @@ -1,6 +1,8 @@ package events -import "github.com/owncast/owncast/core/data" +import ( + "github.com/owncast/owncast/persistence/configrepository" +) // SystemMessageEvent is a message displayed in chat on behalf of the server. type SystemMessageEvent struct { @@ -10,13 +12,15 @@ type SystemMessageEvent struct { // GetBroadcastPayload will return the object to send to all chat users. func (e *SystemMessageEvent) GetBroadcastPayload() EventPayload { + configRepository := configrepository.Get() + return EventPayload{ "id": e.ID, "timestamp": e.Timestamp, "body": e.Body, "type": SystemMessageSent, "user": EventPayload{ - "displayName": data.GetServerName(), + "displayName": configRepository.GetServerName(), }, } } diff --git a/core/chat/persistence.go b/core/chat/persistence.go index ccd817a18..eb2d71f9b 100644 --- a/core/chat/persistence.go +++ b/core/chat/persistence.go @@ -9,6 +9,7 @@ import ( "github.com/owncast/owncast/core/chat/events" "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/persistence/authrepository" "github.com/owncast/owncast/persistence/tables" log "github.com/sirupsen/logrus" @@ -24,7 +25,9 @@ const ( func setupPersistence() { _datastore = data.GetDatastore() tables.CreateMessagesTable(_datastore.DB) - data.CreateBanIPTable(_datastore.DB) + + authRepository := authrepository.Get() + authRepository.CreateBanIPTable(_datastore.DB) chatDataPruner := time.NewTicker(5 * time.Minute) go func() { diff --git a/core/chat/server.go b/core/chat/server.go index 29b4ccffc..f423b358f 100644 --- a/core/chat/server.go +++ b/core/chat/server.go @@ -13,9 +13,10 @@ import ( "github.com/owncast/owncast/config" "github.com/owncast/owncast/core/chat/events" - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/core/webhooks" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/persistence/authrepository" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/persistence/userrepository" "github.com/owncast/owncast/services/geoip" "github.com/owncast/owncast/utils" @@ -95,7 +96,9 @@ func (s *Server) Addclient(conn *websocket.Conn, user *models.User, accessToken ConnectedAt: time.Now(), } - shouldSendJoinedMessages := data.GetChatJoinPartMessagesEnabled() + configRepository := configrepository.Get() + + shouldSendJoinedMessages := configRepository.GetChatJoinPartMessagesEnabled() // If there are existing clients connected for this user do not send // a user joined message. Do not put this under a mutex, as @@ -186,8 +189,10 @@ func (s *Server) sendUserPartedMessage(c *Client) { userPartEvent.User = c.User userPartEvent.ClientID = c.Id + configRepository := configrepository.Get() + // If part messages are disabled. - if data.GetChatJoinPartMessagesEnabled() { + if configRepository.GetChatJoinPartMessagesEnabled() { if err := s.Broadcast(userPartEvent.GetBroadcastPayload()); err != nil { log.Errorln("error sending chat part message", err) } @@ -198,14 +203,17 @@ func (s *Server) sendUserPartedMessage(c *Client) { // HandleClientConnection is fired when a single client connects to the websocket. func (s *Server) HandleClientConnection(w http.ResponseWriter, r *http.Request) { - if data.GetChatDisabled() { + configRepository := configrepository.Get() + authRepository := authrepository.Get() + + if configRepository.GetChatDisabled() { _, _ = w.Write([]byte(events.ChatDisabled)) return } ipAddress := utils.GetIPAddressFromRequest(r) // Check if this client's IP address is banned. If so send a rejection. - if blocked, err := data.IsIPAddressBanned(ipAddress); blocked { + if blocked, err := authRepository.IsIPAddressBanned(ipAddress); blocked { log.Debugln("Client ip address has been blocked. Rejecting.") w.WriteHeader(http.StatusForbidden) @@ -377,12 +385,14 @@ func SendActionToUser(userID string, text string) error { } func (s *Server) eventReceived(event chatClientEvent) { + configRepository := configrepository.Get() + c := event.client u := c.User // If established chat user only mode is enabled and the user is not old // enough then reject this event and send them an informative message. - if u != nil && data.GetChatEstbalishedUsersOnlyMode() && time.Since(event.client.User.CreatedAt) < config.GetDefaults().ChatEstablishedUserModeTimeDuration && !u.IsModerator() { + if u != nil && configRepository.GetChatEstbalishedUsersOnlyMode() && time.Since(event.client.User.CreatedAt) < config.GetDefaults().ChatEstablishedUserModeTimeDuration && !u.IsModerator() { s.sendActionToClient(c, "You have not been an established chat participant long enough to take part in chat. Please enjoy the stream and try again later.") return } @@ -409,10 +419,12 @@ func (s *Server) eventReceived(event chatClientEvent) { } func (s *Server) sendWelcomeMessageToClient(c *Client) { + configRepository := configrepository.Get() + // Add an artificial delay so people notice this message come in. time.Sleep(7 * time.Second) - welcomeMessage := utils.RenderSimpleMarkdown(data.GetServerWelcomeMessage()) + welcomeMessage := utils.RenderSimpleMarkdown(configRepository.GetServerWelcomeMessage()) if welcomeMessage != "" { s.sendSystemMessageToClient(c, welcomeMessage) @@ -420,7 +432,9 @@ func (s *Server) sendWelcomeMessageToClient(c *Client) { } func (s *Server) sendAllWelcomeMessage() { - welcomeMessage := utils.RenderSimpleMarkdown(data.GetServerWelcomeMessage()) + configRepository := configrepository.Get() + + welcomeMessage := utils.RenderSimpleMarkdown(configRepository.GetServerWelcomeMessage()) if welcomeMessage != "" { clientMessage := events.SystemMessageEvent{ diff --git a/core/core.go b/core/core.go index 32e2b4592..5465ed8e7 100644 --- a/core/core.go +++ b/core/core.go @@ -16,6 +16,7 @@ import ( "github.com/owncast/owncast/core/webhooks" "github.com/owncast/owncast/models" "github.com/owncast/owncast/notifications" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/persistence/tables" "github.com/owncast/owncast/utils" "github.com/owncast/owncast/yp" @@ -34,10 +35,10 @@ var ( // Start starts up the core processing. func Start() error { resetDirectories() + configRepository := configrepository.Get() + // configRepository.PopulateDefaults() - data.PopulateDefaults() - - if err := data.VerifySettings(); err != nil { + if err := configRepository.VerifySettings(); err != nil { log.Error(err) return err } @@ -75,7 +76,7 @@ func Start() error { // start the rtmp server go rtmp.Start(setStreamAsConnected, setBroadcaster) - rtmpPort := data.GetRTMPPortNumber() + rtmpPort := configRepository.GetRTMPPortNumber() if rtmpPort != 1935 { log.Infof("RTMP is accepting inbound streams on port %d.", rtmpPort) } @@ -113,7 +114,8 @@ func transitionToOfflineVideoStreamContent() { go _transcoder.Start(false) // Copy the logo to be the thumbnail - logo := data.GetLogoPath() + configRepository := configrepository.Get() + logo := configRepository.GetLogoPath() dst := filepath.Join(config.TempDir, "thumbnail.jpg") if err = utils.Copy(filepath.Join("data", logo), dst); err != nil { log.Warnln(err) @@ -130,7 +132,8 @@ func resetDirectories() { utils.CleanupDirectory(config.HLSStoragePath) // Remove the previous thumbnail - logo := data.GetLogoPath() + configRepository := configrepository.Get() + logo := configRepository.GetLogoPath() if utils.DoesFileExists(logo) { err := utils.Copy(path.Join("data", logo), filepath.Join(config.DataDirectory, "thumbnail.jpg")) if err != nil { diff --git a/core/data/activitypub.go b/core/data/activitypub.go deleted file mode 100644 index fd5310b38..000000000 --- a/core/data/activitypub.go +++ /dev/null @@ -1,13 +0,0 @@ -package data - -// GetFederatedInboxMap is a mapping between account names and their outbox. -func GetFederatedInboxMap() map[string]string { - return map[string]string{ - GetDefaultFederationUsername(): GetDefaultFederationUsername(), - } -} - -// GetDefaultFederationUsername will return the username used for sending federation activities. -func GetDefaultFederationUsername() string { - return GetFederationUsername() -} diff --git a/core/data/config.go b/core/data/config.go deleted file mode 100644 index 2eff4095a..000000000 --- a/core/data/config.go +++ /dev/null @@ -1,1027 +0,0 @@ -package data - -import ( - "os" - "path/filepath" - "sort" - "strings" - "time" - - "github.com/owncast/owncast/config" - "github.com/owncast/owncast/models" - "github.com/owncast/owncast/static" - "github.com/owncast/owncast/utils" - "github.com/pkg/errors" - log "github.com/sirupsen/logrus" -) - -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" -) - -// GetExtraPageBodyContent will return the user-supplied body content. -func GetExtraPageBodyContent() string { - content, err := _datastore.GetString(extraContentKey) - if err != nil { - log.Traceln(extraContentKey, err) - return config.GetDefaults().PageBodyContent - } - - return content -} - -// SetExtraPageBodyContent will set the user-supplied body content. -func SetExtraPageBodyContent(content string) error { - return _datastore.SetString(extraContentKey, content) -} - -// GetStreamTitle will return the name of the current stream. -func GetStreamTitle() string { - title, err := _datastore.GetString(streamTitleKey) - if err != nil { - return "" - } - - return title -} - -// SetStreamTitle will set the name of the current stream. -func SetStreamTitle(title string) error { - return _datastore.SetString(streamTitleKey, title) -} - -// GetAdminPassword will return the admin password. -func GetAdminPassword() string { - key, _ := _datastore.GetString(adminPasswordKey) - return key -} - -// SetAdminPassword will set the admin password. -func SetAdminPassword(key string) error { - hashed_pass, err := utils.HashPassword(key) - if err != nil { - return err - } - return _datastore.SetString(adminPasswordKey, hashed_pass) -} - -// GetLogoPath will return the path for the logo, relative to webroot. -func GetLogoPath() string { - logo, err := _datastore.GetString(logoPathKey) - if err != nil { - log.Traceln(logoPathKey, err) - return config.GetDefaults().Logo - } - - if logo == "" { - return config.GetDefaults().Logo - } - - return logo -} - -// SetLogoPath will set the path for the logo, relative to webroot. -func SetLogoPath(logo string) error { - return _datastore.SetString(logoPathKey, logo) -} - -// SetLogoUniquenessString will set the logo cache busting string. -func SetLogoUniquenessString(uniqueness string) error { - return _datastore.SetString(logoUniquenessKey, uniqueness) -} - -// GetLogoUniquenessString will return the logo cache busting string. -func GetLogoUniquenessString() string { - uniqueness, err := _datastore.GetString(logoUniquenessKey) - if err != nil { - log.Traceln(logoUniquenessKey, err) - return "" - } - - return uniqueness -} - -// GetServerSummary will return the server summary text. -func GetServerSummary() string { - summary, err := _datastore.GetString(serverSummaryKey) - if err != nil { - log.Traceln(serverSummaryKey, err) - return "" - } - - return summary -} - -// SetServerSummary will set the server summary text. -func SetServerSummary(summary string) error { - return _datastore.SetString(serverSummaryKey, summary) -} - -// GetServerWelcomeMessage will return the server welcome message text. -func GetServerWelcomeMessage() string { - welcomeMessage, err := _datastore.GetString(serverWelcomeMessageKey) - if err != nil { - log.Traceln(serverWelcomeMessageKey, err) - return config.GetDefaults().ServerWelcomeMessage - } - - return welcomeMessage -} - -// SetServerWelcomeMessage will set the server welcome message text. -func SetServerWelcomeMessage(welcomeMessage string) error { - return _datastore.SetString(serverWelcomeMessageKey, welcomeMessage) -} - -// GetServerName will return the server name text. -func GetServerName() string { - name, err := _datastore.GetString(serverNameKey) - if err != nil { - log.Traceln(serverNameKey, err) - return config.GetDefaults().Name - } - - return name -} - -// SetServerName will set the server name text. -func SetServerName(name string) error { - return _datastore.SetString(serverNameKey, name) -} - -// GetServerURL will return the server URL. -func GetServerURL() string { - url, err := _datastore.GetString(serverURLKey) - if err != nil { - return "" - } - - return url -} - -// SetServerURL will set the server URL. -func SetServerURL(url string) error { - return _datastore.SetString(serverURLKey, url) -} - -// GetHTTPPortNumber will return the server HTTP port. -func GetHTTPPortNumber() int { - port, err := _datastore.GetNumber(httpPortNumberKey) - if err != nil { - log.Traceln(httpPortNumberKey, err) - return config.GetDefaults().WebServerPort - } - - if port == 0 { - return config.GetDefaults().WebServerPort - } - return int(port) -} - -// SetWebsocketOverrideHost will set the host override for websockets. -func SetWebsocketOverrideHost(host string) error { - return _datastore.SetString(websocketHostOverrideKey, host) -} - -// GetWebsocketOverrideHost will return the host override for websockets. -func GetWebsocketOverrideHost() string { - host, _ := _datastore.GetString(websocketHostOverrideKey) - - return host -} - -// SetHTTPPortNumber will set the server HTTP port. -func SetHTTPPortNumber(port float64) error { - return _datastore.SetNumber(httpPortNumberKey, port) -} - -// GetHTTPListenAddress will return the HTTP listen address. -func GetHTTPListenAddress() string { - address, err := _datastore.GetString(httpListenAddressKey) - if err != nil { - log.Traceln(httpListenAddressKey, err) - return config.GetDefaults().WebServerIP - } - return address -} - -// SetHTTPListenAddress will set the server HTTP listen address. -func SetHTTPListenAddress(address string) error { - return _datastore.SetString(httpListenAddressKey, address) -} - -// GetRTMPPortNumber will return the server RTMP port. -func GetRTMPPortNumber() int { - port, err := _datastore.GetNumber(rtmpPortNumberKey) - if err != nil { - log.Traceln(rtmpPortNumberKey, err) - return config.GetDefaults().RTMPServerPort - } - - if port == 0 { - return config.GetDefaults().RTMPServerPort - } - - return int(port) -} - -// SetRTMPPortNumber will set the server RTMP port. -func SetRTMPPortNumber(port float64) error { - return _datastore.SetNumber(rtmpPortNumberKey, port) -} - -// GetServerMetadataTags will return the metadata tags. -func GetServerMetadataTags() []string { - tagsString, err := _datastore.GetString(serverMetadataTagsKey) - if tagsString == "" { - return []string{} - } - - if err != nil { - log.Traceln(serverMetadataTagsKey, err) - return []string{} - } - - return strings.Split(tagsString, ",") -} - -// SetServerMetadataTags will return the metadata tags. -func SetServerMetadataTags(tags []string) error { - tagString := strings.Join(tags, ",") - return _datastore.SetString(serverMetadataTagsKey, tagString) -} - -// GetDirectoryEnabled will return if this server should register to YP. -func GetDirectoryEnabled() bool { - enabled, err := _datastore.GetBool(directoryEnabledKey) - if err != nil { - return config.GetDefaults().YPEnabled - } - - return enabled -} - -// SetDirectoryEnabled will set if this server should register to YP. -func SetDirectoryEnabled(enabled bool) error { - return _datastore.SetBool(directoryEnabledKey, enabled) -} - -// SetDirectoryRegistrationKey will set the YP protocol registration key. -func SetDirectoryRegistrationKey(key string) error { - return _datastore.SetString(directoryRegistrationKeyKey, key) -} - -// GetDirectoryRegistrationKey will return the YP protocol registration key. -func GetDirectoryRegistrationKey() string { - key, _ := _datastore.GetString(directoryRegistrationKeyKey) - return key -} - -// GetSocialHandles will return the external social links. -func GetSocialHandles() []models.SocialHandle { - var socialHandles []models.SocialHandle - - configEntry, err := _datastore.Get(socialHandlesKey) - if err != nil { - log.Traceln(socialHandlesKey, err) - return socialHandles - } - - if err := configEntry.getObject(&socialHandles); err != nil { - log.Traceln(err) - return socialHandles - } - - return socialHandles -} - -// SetSocialHandles will set the external social links. -func SetSocialHandles(socialHandles []models.SocialHandle) error { - configEntry := ConfigEntry{Key: socialHandlesKey, Value: socialHandles} - return _datastore.Save(configEntry) -} - -// GetPeakSessionViewerCount will return the max number of viewers for this stream. -func GetPeakSessionViewerCount() int { - count, err := _datastore.GetNumber(peakViewersSessionKey) - if err != nil { - return 0 - } - return int(count) -} - -// SetPeakSessionViewerCount will set the max number of viewers for this stream. -func SetPeakSessionViewerCount(count int) error { - return _datastore.SetNumber(peakViewersSessionKey, float64(count)) -} - -// GetPeakOverallViewerCount will return the overall max number of viewers. -func GetPeakOverallViewerCount() int { - count, err := _datastore.GetNumber(peakViewersOverallKey) - if err != nil { - return 0 - } - return int(count) -} - -// SetPeakOverallViewerCount will set the overall max number of viewers. -func SetPeakOverallViewerCount(count int) error { - return _datastore.SetNumber(peakViewersOverallKey, float64(count)) -} - -// GetLastDisconnectTime will return the time the last stream ended. -func GetLastDisconnectTime() (*utils.NullTime, error) { - var disconnectTime utils.NullTime - - configEntry, err := _datastore.Get(lastDisconnectTimeKey) - if err != nil { - return nil, err - } - - if err := configEntry.getObject(&disconnectTime); err != nil { - return nil, err - } - - if !disconnectTime.Valid || disconnectTime.Time.IsZero() { - return nil, err - } - - return &disconnectTime, nil -} - -// SetLastDisconnectTime will set the time the last stream ended. -func SetLastDisconnectTime(disconnectTime time.Time) error { - savedDisconnectTime := utils.NullTime{Time: disconnectTime, Valid: true} - configEntry := ConfigEntry{Key: lastDisconnectTimeKey, Value: savedDisconnectTime} - return _datastore.Save(configEntry) -} - -// SetNSFW will set if this stream has NSFW content. -func SetNSFW(isNSFW bool) error { - return _datastore.SetBool(nsfwKey, isNSFW) -} - -// GetNSFW will return if this stream has NSFW content. -func GetNSFW() bool { - nsfw, err := _datastore.GetBool(nsfwKey) - if err != nil { - return false - } - return nsfw -} - -// SetFfmpegPath will set the custom ffmpeg path. -func SetFfmpegPath(path string) error { - return _datastore.SetString(ffmpegPathKey, path) -} - -// GetFfMpegPath will return the ffmpeg path. -func GetFfMpegPath() string { - path, err := _datastore.GetString(ffmpegPathKey) - if err != nil { - return "" - } - return path -} - -// GetS3Config will return the external storage configuration. -func GetS3Config() models.S3 { - configEntry, err := _datastore.Get(s3StorageConfigKey) - if err != nil { - return models.S3{Enabled: false} - } - - var s3Config models.S3 - if err := configEntry.getObject(&s3Config); err != nil { - return models.S3{Enabled: false} - } - - return s3Config -} - -// SetS3Config will set the external storage configuration. -func SetS3Config(config models.S3) error { - configEntry := ConfigEntry{Key: s3StorageConfigKey, Value: config} - return _datastore.Save(configEntry) -} - -// GetStreamLatencyLevel will return the stream latency level. -func GetStreamLatencyLevel() models.LatencyLevel { - level, err := _datastore.GetNumber(videoLatencyLevel) - if err != nil { - level = 2 // default - } else if level > 4 { - level = 4 // highest - } - - return models.GetLatencyLevel(int(level)) -} - -// SetStreamLatencyLevel will set the stream latency level. -func SetStreamLatencyLevel(level float64) error { - return _datastore.SetNumber(videoLatencyLevel, level) -} - -// GetStreamOutputVariants will return all of the stream output variants. -func GetStreamOutputVariants() []models.StreamOutputVariant { - configEntry, err := _datastore.Get(videoStreamOutputVariantsKey) - if err != nil { - return config.GetDefaults().StreamVariants - } - - var streamOutputVariants []models.StreamOutputVariant - if err := configEntry.getObject(&streamOutputVariants); err != nil { - return config.GetDefaults().StreamVariants - } - - if len(streamOutputVariants) == 0 { - return config.GetDefaults().StreamVariants - } - - return streamOutputVariants -} - -// SetStreamOutputVariants will set the stream output variants. -func SetStreamOutputVariants(variants []models.StreamOutputVariant) error { - configEntry := ConfigEntry{Key: videoStreamOutputVariantsKey, Value: variants} - return _datastore.Save(configEntry) -} - -// SetChatDisabled will disable chat if set to true. -func SetChatDisabled(disabled bool) error { - return _datastore.SetBool(chatDisabledKey, disabled) -} - -// GetChatDisabled will return if chat is disabled. -func GetChatDisabled() bool { - disabled, err := _datastore.GetBool(chatDisabledKey) - if err == nil { - return disabled - } - - return false -} - -// SetChatEstablishedUsersOnlyMode sets the state of established user only mode. -func SetChatEstablishedUsersOnlyMode(enabled bool) error { - return _datastore.SetBool(chatEstablishedUsersOnlyModeKey, enabled) -} - -// GetChatEstbalishedUsersOnlyMode returns the state of established user only mode. -func GetChatEstbalishedUsersOnlyMode() bool { - enabled, err := _datastore.GetBool(chatEstablishedUsersOnlyModeKey) - if err == nil { - return enabled - } - - return false -} - -// SetChatSpamProtectionEnabled will enable chat spam protection if set to true. -func SetChatSpamProtectionEnabled(enabled bool) error { - return _datastore.SetBool(chatSpamProtectionEnabledKey, enabled) -} - -// GetChatSpamProtectionEnabled will return if chat spam protection is enabled. -func GetChatSpamProtectionEnabled() bool { - enabled, err := _datastore.GetBool(chatSpamProtectionEnabledKey) - if err == nil { - return enabled - } - - return true -} - -// SetChatSlurFilterEnabled will enable the chat slur filter. -func SetChatSlurFilterEnabled(enabled bool) error { - return _datastore.SetBool(chatSlurFilterEnabledKey, enabled) -} - -// GetChatSlurFilterEnabled will return if the chat slur filter is enabled. -func GetChatSlurFilterEnabled() bool { - enabled, err := _datastore.GetBool(chatSlurFilterEnabledKey) - if err == nil { - return enabled - } - - return false -} - -// GetExternalActions will return the registered external actions. -func GetExternalActions() []models.ExternalAction { - configEntry, err := _datastore.Get(externalActionsKey) - if err != nil { - return []models.ExternalAction{} - } - - var externalActions []models.ExternalAction - if err := configEntry.getObject(&externalActions); err != nil { - return []models.ExternalAction{} - } - - return externalActions -} - -// SetExternalActions will save external actions. -func SetExternalActions(actions []models.ExternalAction) error { - configEntry := ConfigEntry{Key: externalActionsKey, Value: actions} - return _datastore.Save(configEntry) -} - -// SetCustomStyles will save a string with CSS to insert into the page. -func SetCustomStyles(styles string) error { - return _datastore.SetString(customStylesKey, styles) -} - -// GetCustomStyles will return a string with CSS to insert into the page. -func GetCustomStyles() string { - style, err := _datastore.GetString(customStylesKey) - if err != nil { - return "" - } - - return style -} - -// SetCustomJavascript will save a string with Javascript to insert into the page. -func SetCustomJavascript(styles string) error { - return _datastore.SetString(customJavascriptKey, styles) -} - -// GetCustomJavascript will return a string with Javascript to insert into the page. -func GetCustomJavascript() string { - style, err := _datastore.GetString(customJavascriptKey) - if err != nil { - return "" - } - - return style -} - -// SetVideoCodec will set the codec used for video encoding. -func SetVideoCodec(codec string) error { - return _datastore.SetString(videoCodecKey, codec) -} - -// GetVideoCodec returns the codec to use for transcoding video. -func GetVideoCodec() string { - codec, err := _datastore.GetString(videoCodecKey) - if codec == "" || err != nil { - return "libx264" // Default value - } - - return codec -} - -// VerifySettings will perform a sanity check for specific settings values. -func VerifySettings() error { - if len(GetStreamKeys()) == 0 && config.TemporaryStreamKey == "" { - log.Errorln("No stream key set. Streaming is disabled. Please set one via the admin or command line arguments") - } - - if GetAdminPassword() == "" { - return errors.New("no admin password set. Please set one via the admin or command line arguments") - } - - logoPath := GetLogoPath() - if !utils.DoesFileExists(filepath.Join(config.DataDirectory, logoPath)) { - log.Traceln(logoPath, "not found in the data directory. copying a default logo.") - logo := static.GetLogo() - if err := os.WriteFile(filepath.Join(config.DataDirectory, "logo.png"), logo, 0o600); err != nil { - return errors.Wrap(err, "failed to write logo to disk") - } - if err := SetLogoPath("logo.png"); err != nil { - return errors.Wrap(err, "failed to save logo filename") - } - } - - return nil -} - -// FindHighestVideoQualityIndex will return the highest quality from a slice of variants. -func FindHighestVideoQualityIndex(qualities []models.StreamOutputVariant) (int, bool) { - type IndexedQuality struct { - quality models.StreamOutputVariant - index int - } - - if len(qualities) < 2 { - return 0, qualities[0].IsVideoPassthrough - } - - indexedQualities := make([]IndexedQuality, 0) - for index, quality := range qualities { - indexedQuality := IndexedQuality{quality, index} - indexedQualities = append(indexedQualities, indexedQuality) - } - - sort.Slice(indexedQualities, func(a, b int) bool { - if indexedQualities[a].quality.IsVideoPassthrough && !indexedQualities[b].quality.IsVideoPassthrough { - return true - } - - if !indexedQualities[a].quality.IsVideoPassthrough && indexedQualities[b].quality.IsVideoPassthrough { - return false - } - - return indexedQualities[a].quality.VideoBitrate > indexedQualities[b].quality.VideoBitrate - }) - - // nolint:gosec - selectedQuality := indexedQualities[0] - return selectedQuality.index, selectedQuality.quality.IsVideoPassthrough -} - -// GetForbiddenUsernameList will return the blocked usernames as a comma separated string. -func GetForbiddenUsernameList() []string { - usernames, err := _datastore.GetStringSlice(blockedUsernamesKey) - if err != nil { - return config.DefaultForbiddenUsernames - } - - if len(usernames) == 0 { - return config.DefaultForbiddenUsernames - } - - return usernames -} - -// SetForbiddenUsernameList set the username blocklist as a comma separated string. -func SetForbiddenUsernameList(usernames []string) error { - return _datastore.SetStringSlice(blockedUsernamesKey, usernames) -} - -// GetSuggestedUsernamesList will return the suggested usernames. -// If the number of suggested usernames is smaller than 10, the number pool is -// not used (see code in the CreateAnonymousUser function). -func GetSuggestedUsernamesList() []string { - usernames, err := _datastore.GetStringSlice(suggestedUsernamesKey) - - if err != nil || len(usernames) == 0 { - return []string{} - } - - return usernames -} - -// SetSuggestedUsernamesList sets the username suggestion list. -func SetSuggestedUsernamesList(usernames []string) error { - return _datastore.SetStringSlice(suggestedUsernamesKey, usernames) -} - -// GetServerInitTime will return when the server was first setup. -func GetServerInitTime() (*utils.NullTime, error) { - var t utils.NullTime - - configEntry, err := _datastore.Get(serverInitDateKey) - if err != nil { - return nil, err - } - - if err := configEntry.getObject(&t); err != nil { - return nil, err - } - - if !t.Valid { - return nil, err - } - - return &t, nil -} - -// SetServerInitTime will set when the server was first created. -func SetServerInitTime(t time.Time) error { - nt := utils.NullTime{Time: t, Valid: true} - configEntry := ConfigEntry{Key: serverInitDateKey, Value: nt} - return _datastore.Save(configEntry) -} - -// SetFederationEnabled will enable federation if set to true. -func SetFederationEnabled(enabled bool) error { - return _datastore.SetBool(federationEnabledKey, enabled) -} - -// GetFederationEnabled will return if federation is enabled. -func GetFederationEnabled() bool { - enabled, err := _datastore.GetBool(federationEnabledKey) - if err == nil { - return enabled - } - - return false -} - -// SetFederationUsername will set the username used in federated activities. -func SetFederationUsername(username string) error { - return _datastore.SetString(federationUsernameKey, username) -} - -// GetFederationUsername will return the username used in federated activities. -func GetFederationUsername() string { - username, err := _datastore.GetString(federationUsernameKey) - if username == "" || err != nil { - return config.GetDefaults().FederationUsername - } - - return username -} - -// SetFederationGoLiveMessage will set the message sent when going live. -func SetFederationGoLiveMessage(message string) error { - return _datastore.SetString(federationGoLiveMessageKey, message) -} - -// GetFederationGoLiveMessage will return the message sent when going live. -func GetFederationGoLiveMessage() string { - // Empty message means it's disabled. - message, err := _datastore.GetString(federationGoLiveMessageKey) - if err != nil { - log.Traceln("unable to fetch go live message.", err) - } - - return message -} - -// SetFederationIsPrivate will set if federation activity is private. -func SetFederationIsPrivate(isPrivate bool) error { - return _datastore.SetBool(federationPrivateKey, isPrivate) -} - -// GetFederationIsPrivate will return if federation is private. -func GetFederationIsPrivate() bool { - isPrivate, err := _datastore.GetBool(federationPrivateKey) - if err == nil { - return isPrivate - } - - return false -} - -// SetFederationShowEngagement will set if fediverse engagement shows in chat. -func SetFederationShowEngagement(showEngagement bool) error { - return _datastore.SetBool(federationShowEngagementKey, showEngagement) -} - -// GetFederationShowEngagement will return if fediverse engagement shows in chat. -func GetFederationShowEngagement() bool { - showEngagement, err := _datastore.GetBool(federationShowEngagementKey) - if err == nil { - return showEngagement - } - - return true -} - -// SetBlockedFederatedDomains will set the blocked federated domains. -func SetBlockedFederatedDomains(domains []string) error { - return _datastore.SetString(federationBlockedDomainsKey, strings.Join(domains, ",")) -} - -// GetBlockedFederatedDomains will return a list of blocked federated domains. -func GetBlockedFederatedDomains() []string { - domains, err := _datastore.GetString(federationBlockedDomainsKey) - if err != nil { - return []string{} - } - - if domains == "" { - return []string{} - } - - return strings.Split(domains, ",") -} - -// SetChatJoinMessagesEnabled will set if chat join messages are enabled. -func SetChatJoinMessagesEnabled(enabled bool) error { - return _datastore.SetBool(chatJoinMessagesEnabledKey, enabled) -} - -// GetChatJoinPartMessagesEnabled will return if chat join messages are enabled. -func GetChatJoinPartMessagesEnabled() bool { - enabled, err := _datastore.GetBool(chatJoinMessagesEnabledKey) - if err != nil { - return true - } - - return enabled -} - -// SetNotificationsEnabled will save the enabled state of notifications. -func SetNotificationsEnabled(enabled bool) error { - return _datastore.SetBool(notificationsEnabledKey, enabled) -} - -// GetNotificationsEnabled will return the enabled state of notifications. -func GetNotificationsEnabled() bool { - enabled, _ := _datastore.GetBool(notificationsEnabledKey) - return enabled -} - -// GetDiscordConfig will return the Discord configuration. -func GetDiscordConfig() models.DiscordConfiguration { - configEntry, err := _datastore.Get(discordConfigurationKey) - if err != nil { - return models.DiscordConfiguration{Enabled: false} - } - - var config models.DiscordConfiguration - if err := configEntry.getObject(&config); err != nil { - return models.DiscordConfiguration{Enabled: false} - } - - return config -} - -// SetDiscordConfig will set the Discord configuration. -func SetDiscordConfig(config models.DiscordConfiguration) error { - configEntry := ConfigEntry{Key: discordConfigurationKey, Value: config} - return _datastore.Save(configEntry) -} - -// GetBrowserPushConfig will return the browser push configuration. -func GetBrowserPushConfig() models.BrowserNotificationConfiguration { - configEntry, err := _datastore.Get(browserPushConfigurationKey) - if err != nil { - return models.BrowserNotificationConfiguration{Enabled: false} - } - - var config models.BrowserNotificationConfiguration - if err := configEntry.getObject(&config); err != nil { - return models.BrowserNotificationConfiguration{Enabled: false} - } - - return config -} - -// SetBrowserPushConfig will set the browser push configuration. -func SetBrowserPushConfig(config models.BrowserNotificationConfiguration) error { - configEntry := ConfigEntry{Key: browserPushConfigurationKey, Value: config} - return _datastore.Save(configEntry) -} - -// SetBrowserPushPublicKey will set the public key for browser pushes. -func SetBrowserPushPublicKey(key string) error { - return _datastore.SetString(browserPushPublicKeyKey, key) -} - -// GetBrowserPushPublicKey will return the public key for browser pushes. -func GetBrowserPushPublicKey() (string, error) { - return _datastore.GetString(browserPushPublicKeyKey) -} - -// SetBrowserPushPrivateKey will set the private key for browser pushes. -func SetBrowserPushPrivateKey(key string) error { - return _datastore.SetString(browserPushPrivateKeyKey, key) -} - -// GetBrowserPushPrivateKey will return the private key for browser pushes. -func GetBrowserPushPrivateKey() (string, error) { - return _datastore.GetString(browserPushPrivateKeyKey) -} - -// SetHasPerformedInitialNotificationsConfig sets when performed initial setup. -func SetHasPerformedInitialNotificationsConfig(hasConfigured bool) error { - return _datastore.SetBool(hasConfiguredInitialNotificationsKey, true) -} - -// GetHasPerformedInitialNotificationsConfig gets when performed initial setup. -func GetHasPerformedInitialNotificationsConfig() bool { - configured, _ := _datastore.GetBool(hasConfiguredInitialNotificationsKey) - return configured -} - -// GetHideViewerCount will return if the viewer count shold be hidden. -func GetHideViewerCount() bool { - hide, _ := _datastore.GetBool(hideViewerCountKey) - return hide -} - -// SetHideViewerCount will set if the viewer count should be hidden. -func SetHideViewerCount(hide bool) error { - return _datastore.SetBool(hideViewerCountKey, hide) -} - -// GetCustomOfflineMessage will return the custom offline message. -func GetCustomOfflineMessage() string { - message, _ := _datastore.GetString(customOfflineMessageKey) - return message -} - -// SetCustomOfflineMessage will set the custom offline message. -func SetCustomOfflineMessage(message string) error { - return _datastore.SetString(customOfflineMessageKey, message) -} - -// SetCustomColorVariableValues sets CSS variable names and values. -func SetCustomColorVariableValues(variables map[string]string) error { - return _datastore.SetStringMap(customColorVariableValuesKey, variables) -} - -// GetCustomColorVariableValues gets CSS variable names and values. -func GetCustomColorVariableValues() map[string]string { - values, _ := _datastore.GetStringMap(customColorVariableValuesKey) - return values -} - -// GetStreamKeys will return valid stream keys. -func GetStreamKeys() []models.StreamKey { - configEntry, err := _datastore.Get(streamKeysKey) - if err != nil { - return []models.StreamKey{} - } - - var streamKeys []models.StreamKey - if err := configEntry.getObject(&streamKeys); err != nil { - return []models.StreamKey{} - } - - return streamKeys -} - -// SetStreamKeys will set valid stream keys. -func SetStreamKeys(actions []models.StreamKey) error { - configEntry := ConfigEntry{Key: streamKeysKey, Value: actions} - return _datastore.Save(configEntry) -} - -// SetDisableSearchIndexing will set if the web server should be indexable. -func SetDisableSearchIndexing(disableSearchIndexing bool) error { - return _datastore.SetBool(disableSearchIndexingKey, disableSearchIndexing) -} - -// GetDisableSearchIndexing will return if the web server should be indexable. -func GetDisableSearchIndexing() bool { - disableSearchIndexing, err := _datastore.GetBool(disableSearchIndexingKey) - if err != nil { - return false - } - return disableSearchIndexing -} - -// GetVideoServingEndpoint returns the custom video endpont. -func GetVideoServingEndpoint() string { - message, _ := _datastore.GetString(videoServingEndpointKey) - return message -} - -// SetVideoServingEndpoint sets the custom video endpoint. -func SetVideoServingEndpoint(message string) error { - return _datastore.SetString(videoServingEndpointKey, message) -} diff --git a/core/data/crypto.go b/core/data/crypto.go deleted file mode 100644 index 8bd394a8a..000000000 --- a/core/data/crypto.go +++ /dev/null @@ -1,23 +0,0 @@ -package data - -// GetPublicKey will return the public key. -func GetPublicKey() string { - value, _ := _datastore.GetString(publicKeyKey) - return value -} - -// SetPublicKey will save the public key. -func SetPublicKey(key string) error { - return _datastore.SetString(publicKeyKey, key) -} - -// GetPrivateKey will return the private key. -func GetPrivateKey() string { - value, _ := _datastore.GetString(privateKeyKey) - return value -} - -// SetPrivateKey will save the private key. -func SetPrivateKey(key string) error { - return _datastore.SetString(privateKeyKey, key) -} diff --git a/core/data/data_test.go b/core/data/data_test.go index d3e113e5b..8b3f1f5f2 100644 --- a/core/data/data_test.go +++ b/core/data/data_test.go @@ -4,6 +4,8 @@ import ( "fmt" "os" "testing" + + "github.com/owncast/owncast/models" ) func TestMain(m *testing.M) { @@ -89,7 +91,7 @@ func TestCustomType(t *testing.T) { } // Save config entry to the database - if err := _datastore.Save(ConfigEntry{&testStruct, testKey}); err != nil { + if err := _datastore.Save(models.ConfigEntry{&testStruct, testKey}); err != nil { t.Error(err) } @@ -101,7 +103,7 @@ func TestCustomType(t *testing.T) { // Get a typed struct out of it var testResult TestStruct - if err := entryResult.getObject(&testResult); err != nil { + if err := entryResult.GetObject(&testResult); err != nil { t.Error(err) } @@ -121,7 +123,7 @@ func TestStringMap(t *testing.T) { } // Save config entry to the database - if err := _datastore.Save(ConfigEntry{&testMap, testKey}); err != nil { + if err := _datastore.Save(models.ConfigEntry{Value: &testMap, Key: testKey}); err != nil { t.Error(err) } @@ -131,7 +133,7 @@ func TestStringMap(t *testing.T) { t.Error(err) } - testResult, err := entryResult.getStringMap() + testResult, err := entryResult.GetStringMap() if err != nil { t.Error(err) } diff --git a/core/data/persistence.go b/core/data/datastore.go similarity index 79% rename from core/data/persistence.go rename to core/data/datastore.go index a765f0b72..4f96a14a2 100644 --- a/core/data/persistence.go +++ b/core/data/datastore.go @@ -5,12 +5,11 @@ import ( "database/sql" "encoding/gob" "sync" - "time" // sqlite requires a blank import. _ "github.com/mattn/go-sqlite3" - "github.com/owncast/owncast/config" "github.com/owncast/owncast/db" + "github.com/owncast/owncast/models" log "github.com/sirupsen/logrus" ) @@ -21,7 +20,8 @@ type Datastore struct { DbLock *sync.Mutex } -func (ds *Datastore) warmCache() { +// WarmCache pre-caches all configuration values in memory. +func (ds *Datastore) WarmCache() { log.Traceln("Warming config value cache") res, err := ds.DB.Query("SELECT key, value FROM datastore") @@ -46,10 +46,10 @@ func (ds *Datastore) GetQueries() *db.Queries { } // Get will query the database for the key and return the entry. -func (ds *Datastore) Get(key string) (ConfigEntry, error) { +func (ds *Datastore) Get(key string) (models.ConfigEntry, error) { cachedValue, err := ds.GetCachedValue(key) if err == nil { - return ConfigEntry{ + return models.ConfigEntry{ Key: key, Value: cachedValue, }, nil @@ -60,10 +60,10 @@ func (ds *Datastore) Get(key string) (ConfigEntry, error) { row := ds.DB.QueryRow("SELECT key, value FROM datastore WHERE key = ? LIMIT 1", key) if err := row.Scan(&resultKey, &resultValue); err != nil { - return ConfigEntry{}, err + return models.ConfigEntry{}, err } - result := ConfigEntry{ + result := models.ConfigEntry{ Key: resultKey, Value: resultValue, } @@ -73,7 +73,7 @@ func (ds *Datastore) Get(key string) (ConfigEntry, error) { } // Save will save the ConfigEntry to the database. -func (ds *Datastore) Save(e ConfigEntry) error { +func (ds *Datastore) Save(e models.ConfigEntry) error { ds.DbLock.Lock() defer ds.DbLock.Unlock() @@ -93,7 +93,6 @@ func (ds *Datastore) Save(e ConfigEntry) error { return err } _, err = stmt.Exec(e.Key, dataGob.Bytes()) - if err != nil { return err } @@ -121,26 +120,6 @@ func (ds *Datastore) Setup() { );` ds.MustExec(createTableSQL) - - if !HasPopulatedDefaults() { - PopulateDefaults() - } - - if !hasPopulatedFederationDefaults() { - if err := SetFederationGoLiveMessage(config.GetDefaults().FederationGoLiveMessage); err != nil { - log.Errorln(err) - } - if err := _datastore.SetBool("HAS_POPULATED_FEDERATION_DEFAULTS", true); err != nil { - log.Errorln(err) - } - } - - // Set the server initialization date if needed. - if hasSetInitDate, _ := GetServerInitTime(); hasSetInitDate == nil || !hasSetInitDate.Valid { - _ = SetServerInitTime(time.Now()) - } - - migrateDatastoreValues(_datastore) } // Reset will delete all config entries in the datastore and start over. @@ -156,8 +135,6 @@ func (ds *Datastore) Reset() { if _, err = stmt.Exec(); err != nil { log.Fatalln(err) } - - PopulateDefaults() } // GetDatastore returns the shared instance of the owncast datastore. diff --git a/core/data/defaults.go b/core/data/defaults.go deleted file mode 100644 index 5a8415765..000000000 --- a/core/data/defaults.go +++ /dev/null @@ -1,54 +0,0 @@ -package data - -import ( - "github.com/owncast/owncast/config" - "github.com/owncast/owncast/models" -) - -// HasPopulatedDefaults will determine if the defaults have been inserted into the database. -func HasPopulatedDefaults() bool { - hasPopulated, err := _datastore.GetBool("HAS_POPULATED_DEFAULTS") - if err != nil { - return false - } - return hasPopulated -} - -func hasPopulatedFederationDefaults() bool { - hasPopulated, err := _datastore.GetBool("HAS_POPULATED_FEDERATION_DEFAULTS") - if err != nil { - return false - } - return hasPopulated -} - -// PopulateDefaults will set default values in the database. -func PopulateDefaults() { - _datastore.warmCache() - - defaults := config.GetDefaults() - - if HasPopulatedDefaults() { - return - } - - _ = SetAdminPassword(defaults.AdminPassword) - _ = SetStreamKeys(defaults.StreamKeys) - _ = SetHTTPPortNumber(float64(defaults.WebServerPort)) - _ = SetRTMPPortNumber(float64(defaults.RTMPServerPort)) - _ = SetLogoPath(defaults.Logo) - _ = SetServerMetadataTags([]string{"owncast", "streaming"}) - _ = SetServerSummary(defaults.Summary) - _ = SetServerWelcomeMessage("") - _ = SetServerName(defaults.Name) - _ = SetExtraPageBodyContent(defaults.PageBodyContent) - _ = SetFederationGoLiveMessage(defaults.FederationGoLiveMessage) - _ = SetSocialHandles([]models.SocialHandle{ - { - Platform: "github", - URL: "https://github.com/owncast/owncast", - }, - }) - - _ = _datastore.SetBool("HAS_POPULATED_DEFAULTS", true) -} diff --git a/core/data/messages.go b/core/data/messages.go index 3374ea54f..b3b8ac828 100644 --- a/core/data/messages.go +++ b/core/data/messages.go @@ -1,14 +1,5 @@ package data -import ( - "context" - "database/sql" - - "github.com/owncast/owncast/db" - "github.com/owncast/owncast/models" - log "github.com/sirupsen/logrus" -) - // GetMessagesCount will return the number of messages in the database. func GetMessagesCount() int64 { query := `SELECT COUNT(*) FROM messages` @@ -25,58 +16,3 @@ func GetMessagesCount() int64 { } return count } - -// CreateBanIPTable will create the IP ban table if needed. -func 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 BanIPAddress(address, note string) error { - return _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 IsIPAddressBanned(address string) (bool, error) { - blocked, error := _datastore.GetQueries().IsIPAddressBlocked(context.Background(), address) - return blocked > 0, error -} - -// GetIPAddressBans will return all the banned IP addresses. -func GetIPAddressBans() ([]models.IPAddress, error) { - result, err := _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 RemoveIPAddressBan(address string) error { - return _datastore.GetQueries().RemoveIPAddressBan(context.Background(), address) -} diff --git a/core/data/types.go b/core/data/types.go index 9d421b7c6..60d399a64 100644 --- a/core/data/types.go +++ b/core/data/types.go @@ -1,17 +1,19 @@ package data +import "github.com/owncast/owncast/models" + // GetStringSlice will return the string slice value for a key. func (ds *Datastore) GetStringSlice(key string) ([]string, error) { configEntry, err := ds.Get(key) if err != nil { return []string{}, err } - return configEntry.getStringSlice() + return configEntry.GetStringSlice() } // SetStringSlice will set the string slice value for a key. func (ds *Datastore) SetStringSlice(key string, value []string) error { - configEntry := ConfigEntry{value, key} + configEntry := models.ConfigEntry{Value: value, Key: key} return ds.Save(configEntry) } @@ -21,12 +23,12 @@ func (ds *Datastore) GetString(key string) (string, error) { if err != nil { return "", err } - return configEntry.getString() + return configEntry.GetString() } // SetString will set the string value for a key. func (ds *Datastore) SetString(key string, value string) error { - configEntry := ConfigEntry{value, key} + configEntry := models.ConfigEntry{Value: value, Key: key} return ds.Save(configEntry) } @@ -36,12 +38,12 @@ func (ds *Datastore) GetNumber(key string) (float64, error) { if err != nil { return 0, err } - return configEntry.getNumber() + return configEntry.GetNumber() } // SetNumber will set the numeric value for a key. func (ds *Datastore) SetNumber(key string, value float64) error { - configEntry := ConfigEntry{value, key} + configEntry := models.ConfigEntry{Value: value, Key: key} return ds.Save(configEntry) } @@ -51,12 +53,12 @@ func (ds *Datastore) GetBool(key string) (bool, error) { if err != nil { return false, err } - return configEntry.getBool() + return configEntry.GetBool() } // SetBool will set the boolean value for a key. func (ds *Datastore) SetBool(key string, value bool) error { - configEntry := ConfigEntry{value, key} + configEntry := models.ConfigEntry{Value: value, Key: key} return ds.Save(configEntry) } @@ -66,11 +68,11 @@ func (ds *Datastore) GetStringMap(key string) (map[string]string, error) { if err != nil { return map[string]string{}, err } - return configEntry.getStringMap() + return configEntry.GetStringMap() } // SetStringMap will set the string map value for a key. func (ds *Datastore) SetStringMap(key string, value map[string]string) error { - configEntry := ConfigEntry{value, key} + configEntry := models.ConfigEntry{Value: value, Key: key} return ds.Save(configEntry) } diff --git a/core/rtmp/rtmp.go b/core/rtmp/rtmp.go index 0d71321bc..42b7818af 100644 --- a/core/rtmp/rtmp.go +++ b/core/rtmp/rtmp.go @@ -12,8 +12,8 @@ import ( "github.com/nareix/joy5/format/rtmp" "github.com/owncast/owncast/config" - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/persistence/configrepository" ) var _hasInboundRTMPConnection = false @@ -33,7 +33,9 @@ func Start(setStreamAsConnected func(*io.PipeReader), setBroadcaster func(models _setStreamAsConnected = setStreamAsConnected _setBroadcaster = setBroadcaster - port := data.GetRTMPPortNumber() + configRepository := configrepository.Get() + + port := configRepository.GetRTMPPortNumber() s := rtmp.NewServer() var lis net.Listener var err error @@ -78,8 +80,10 @@ func HandleConn(c *rtmp.Conn, nc net.Conn) { return } + configRepository := configrepository.Get() + accessGranted := false - validStreamingKeys := data.GetStreamKeys() + validStreamingKeys := configRepository.GetStreamKeys() // If a stream key override was specified then use that instead. if config.TemporaryStreamKey != "" { diff --git a/core/stats.go b/core/stats.go index 8eef760ff..40251b453 100644 --- a/core/stats.go +++ b/core/stats.go @@ -7,8 +7,8 @@ import ( log "github.com/sirupsen/logrus" - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/services/geoip" ) @@ -48,7 +48,8 @@ func IsStreamConnected() bool { // Kind of a hack. It takes a handful of seconds between a RTMP connection and when HLS data is available. // So account for that with an artificial buffer of four segments. timeSinceLastConnected := time.Since(_stats.LastConnectTime.Time).Seconds() - waitTime := math.Max(float64(data.GetStreamLatencyLevel().SecondsPerSegment)*3.0, 7) + configRepository := configrepository.Get() + waitTime := math.Max(float64(configRepository.GetStreamLatencyLevel().SecondsPerSegment)*3.0, 7) if timeSinceLastConnected < waitTime { return false } @@ -75,7 +76,7 @@ func SetViewerActive(viewer *models.Viewer) { l.Lock() defer l.Unlock() - // Asynchronously, optionally, fetch GeoIP data. + // Asynchronously, optionally, fetch GeoIP configRepository. go func(viewer *models.Viewer) { viewer.Geo = _geoIPClient.GetGeoFromIP(viewer.IPAddress) }(viewer) @@ -111,27 +112,29 @@ func pruneViewerCount() { } func saveStats() { - if err := data.SetPeakOverallViewerCount(_stats.OverallMaxViewerCount); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetPeakOverallViewerCount(_stats.OverallMaxViewerCount); err != nil { log.Errorln("error saving viewer count", err) } - if err := data.SetPeakSessionViewerCount(_stats.SessionMaxViewerCount); err != nil { + if err := configRepository.SetPeakSessionViewerCount(_stats.SessionMaxViewerCount); err != nil { log.Errorln("error saving viewer count", err) } if _stats.LastDisconnectTime != nil && _stats.LastDisconnectTime.Valid { - if err := data.SetLastDisconnectTime(_stats.LastDisconnectTime.Time); err != nil { + if err := configRepository.SetLastDisconnectTime(_stats.LastDisconnectTime.Time); err != nil { log.Errorln("error saving disconnect time", err) } } } func getSavedStats() models.Stats { - savedLastDisconnectTime, _ := data.GetLastDisconnectTime() + configRepository := configrepository.Get() + savedLastDisconnectTime, _ := configRepository.GetLastDisconnectTime() result := models.Stats{ ChatClients: make(map[string]models.Client), Viewers: make(map[string]*models.Viewer), - SessionMaxViewerCount: data.GetPeakSessionViewerCount(), - OverallMaxViewerCount: data.GetPeakOverallViewerCount(), + SessionMaxViewerCount: configRepository.GetPeakSessionViewerCount(), + OverallMaxViewerCount: configRepository.GetPeakOverallViewerCount(), LastDisconnectTime: savedLastDisconnectTime, } diff --git a/core/status.go b/core/status.go index e0408b828..e6213ada5 100644 --- a/core/status.go +++ b/core/status.go @@ -2,8 +2,8 @@ package core import ( "github.com/owncast/owncast/config" - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/persistence/configrepository" ) // GetStatus gets the status of the system. @@ -17,6 +17,7 @@ func GetStatus() models.Status { viewerCount = len(_stats.Viewers) } + configRepository := configrepository.Get() return models.Status{ Online: IsStreamConnected(), ViewerCount: viewerCount, @@ -25,7 +26,7 @@ func GetStatus() models.Status { LastDisconnectTime: _stats.LastDisconnectTime, LastConnectTime: _stats.LastConnectTime, VersionNumber: config.VersionNumber, - StreamTitle: data.GetStreamTitle(), + StreamTitle: configRepository.GetStreamTitle(), } } diff --git a/core/storage.go b/core/storage.go index 90900d945..e0941ad8a 100644 --- a/core/storage.go +++ b/core/storage.go @@ -1,12 +1,13 @@ package core import ( - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/core/storageproviders" + "github.com/owncast/owncast/persistence/configrepository" ) func setupStorage() error { - s3Config := data.GetS3Config() + configRepository := configrepository.Get() + s3Config := configRepository.GetS3Config() if s3Config.Enabled { _storage = storageproviders.NewS3Storage() diff --git a/core/storageproviders/local.go b/core/storageproviders/local.go index b7baabb72..2e43f9878 100644 --- a/core/storageproviders/local.go +++ b/core/storageproviders/local.go @@ -5,9 +5,8 @@ import ( "path/filepath" "sort" + "github.com/owncast/owncast/persistence/configrepository" log "github.com/sirupsen/logrus" - - "github.com/owncast/owncast/core/data" ) // LocalStorage represents an instance of the local storage provider for HLS video. @@ -22,7 +21,8 @@ func NewLocalStorage() *LocalStorage { // Setup configures this storage provider. func (s *LocalStorage) Setup() error { - s.host = data.GetVideoServingEndpoint() + configRepository := configrepository.Get() + s.host = configRepository.GetVideoServingEndpoint() return nil } @@ -63,7 +63,8 @@ func (s *LocalStorage) Save(filePath string, retryCount int) (string, error) { // Cleanup will remove old files from the storage provider. func (s *LocalStorage) Cleanup() error { // Determine how many files we should keep on disk - maxNumber := data.GetStreamLatencyLevel().SegmentCount + configRepository := configrepository.Get() + maxNumber := configRepository.GetStreamLatencyLevel().SegmentCount buffer := 10 return localCleanup(maxNumber + buffer) } diff --git a/core/storageproviders/s3Storage.go b/core/storageproviders/s3Storage.go index c74c025e7..b839ed7e0 100644 --- a/core/storageproviders/s3Storage.go +++ b/core/storageproviders/s3Storage.go @@ -11,7 +11,7 @@ import ( "sync" "time" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/utils" "github.com/pkg/errors" log "github.com/sirupsen/logrus" @@ -64,9 +64,9 @@ func NewS3Storage() *S3Storage { // Setup sets up the s3 storage for saving the video to s3. func (s *S3Storage) Setup() error { log.Trace("Setting up S3 for external storage of video...") - - s3Config := data.GetS3Config() - customVideoServingEndpoint := data.GetVideoServingEndpoint() + configRepository := configrepository.Get() + s3Config := configRepository.GetS3Config() + customVideoServingEndpoint := configRepository.GetVideoServingEndpoint() if customVideoServingEndpoint != "" { s.host = customVideoServingEndpoint @@ -106,8 +106,9 @@ func (s *S3Storage) SegmentWritten(localFilePath string) { averagePerformance := utils.GetAveragePerformance(performanceMonitorKey) // Warn the user about long-running save operations + configRepository := configrepository.Get() if averagePerformance != 0 { - if averagePerformance > float64(data.GetStreamLatencyLevel().SecondsPerSegment)*0.9 { + if averagePerformance > float64(configRepository.GetStreamLatencyLevel().SecondsPerSegment)*0.9 { log.Warnln("Possible slow uploads: average upload S3 save duration", averagePerformance, "s. troubleshoot this issue by visiting https://owncast.online/docs/troubleshooting/") } } @@ -220,7 +221,8 @@ func (s *S3Storage) Cleanup() error { // RemoteCleanup will remove old files from the remote storage provider. func (s *S3Storage) RemoteCleanup() error { // Determine how many files we should keep on S3 storage - maxNumber := data.GetStreamLatencyLevel().SegmentCount + configRepository := configrepository.Get() + maxNumber := configRepository.GetStreamLatencyLevel().SegmentCount buffer := 20 keys, err := s.getDeletableVideoSegmentsWithOffset(maxNumber + buffer) diff --git a/core/streamState.go b/core/streamState.go index cb6f10f5a..d3f5068be 100644 --- a/core/streamState.go +++ b/core/streamState.go @@ -16,6 +16,7 @@ import ( "github.com/owncast/owncast/core/webhooks" "github.com/owncast/owncast/models" "github.com/owncast/owncast/notifications" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/utils" ) @@ -39,9 +40,11 @@ func setStreamAsConnected(rtmpOut *io.PipeReader) { _stats.LastConnectTime = &now _stats.SessionMaxViewerCount = 0 + configRepository := configrepository.Get() + _currentBroadcast = &models.CurrentBroadcast{ - LatencyLevel: data.GetStreamLatencyLevel(), - OutputSettings: data.GetStreamOutputVariants(), + LatencyLevel: configRepository.GetStreamLatencyLevel(), + OutputSettings: configRepository.GetStreamOutputVariants(), } StopOfflineCleanupTimer() @@ -69,7 +72,7 @@ func setStreamAsConnected(rtmpOut *io.PipeReader) { }() go webhooks.SendStreamStatusEvent(models.StreamStarted) - selectedThumbnailVideoQualityIndex, isVideoPassthrough := data.FindHighestVideoQualityIndex(_currentBroadcast.OutputSettings) + selectedThumbnailVideoQualityIndex, isVideoPassthrough := configRepository.FindHighestVideoQualityIndex(_currentBroadcast.OutputSettings) transcoder.StartThumbnailGenerator(segmentPath, selectedThumbnailVideoQualityIndex, isVideoPassthrough) _ = chat.SendSystemAction("Stay tuned, the stream is **starting**!", true) @@ -176,8 +179,9 @@ func startLiveStreamNotificationsTimer() context.CancelFunc { return } + configRepository := configrepository.Get() // Send Fediverse message. - if data.GetFederationEnabled() { + if configRepository.GetFederationEnabled() { log.Traceln("Sending Federated Go Live message.") if err := activitypub.SendLive(); err != nil { log.Errorln(err) diff --git a/core/transcoder/thumbnailGenerator.go b/core/transcoder/thumbnailGenerator.go index 311e98b96..cbf3b8db0 100644 --- a/core/transcoder/thumbnailGenerator.go +++ b/core/transcoder/thumbnailGenerator.go @@ -11,7 +11,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/owncast/owncast/config" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/utils" ) @@ -88,9 +88,9 @@ func fireThumbnailGenerator(segmentPath string, variantIndex int) error { if len(names) == 0 { return nil } - + configRepository := configrepository.Get() mostRecentFile := path.Join(framePath, names[0]) - ffmpegPath := utils.ValidatedFfmpegPath(data.GetFfMpegPath()) + ffmpegPath := utils.ValidatedFfmpegPath(configRepository.GetFfMpegPath()) outputFileTemp := path.Join(config.TempDir, "tempthumbnail.jpg") thumbnailCmdFlags := []string{ @@ -120,7 +120,8 @@ func fireThumbnailGenerator(segmentPath string, variantIndex int) error { } func makeAnimatedGifPreview(sourceFile string, outputFile string) { - ffmpegPath := utils.ValidatedFfmpegPath(data.GetFfMpegPath()) + configRepository := configrepository.Get() + ffmpegPath := utils.ValidatedFfmpegPath(configRepository.GetFfMpegPath()) outputFileTemp := path.Join(config.TempDir, "temppreview.gif") // Filter is pulled from https://engineering.giphy.com/how-to-make-gifs-with-ffmpeg/ diff --git a/core/transcoder/transcoder.go b/core/transcoder/transcoder.go index db4018afa..36a5587a7 100644 --- a/core/transcoder/transcoder.go +++ b/core/transcoder/transcoder.go @@ -12,9 +12,9 @@ import ( "github.com/teris-io/shortid" "github.com/owncast/owncast/config" - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/logging" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/utils" ) @@ -275,15 +275,16 @@ func getVariantFromConfigQuality(quality models.StreamOutputVariant, index int) // NewTranscoder will return a new Transcoder, populated by the config. func NewTranscoder() *Transcoder { - ffmpegPath := utils.ValidatedFfmpegPath(data.GetFfMpegPath()) + configRepository := configrepository.Get() + ffmpegPath := utils.ValidatedFfmpegPath(configRepository.GetFfMpegPath()) transcoder := new(Transcoder) transcoder.ffmpegPath = ffmpegPath transcoder.internalListenerPort = config.InternalHLSListenerPort - transcoder.currentStreamOutputSettings = data.GetStreamOutputVariants() - transcoder.currentLatencyLevel = data.GetStreamLatencyLevel() - transcoder.codec = getCodec(data.GetVideoCodec()) + transcoder.currentStreamOutputSettings = configRepository.GetStreamOutputVariants() + transcoder.currentLatencyLevel = configRepository.GetStreamLatencyLevel() + transcoder.codec = getCodec(configRepository.GetVideoCodec()) transcoder.segmentOutputPath = config.HLSStoragePath transcoder.playlistOutputPath = config.HLSStoragePath diff --git a/core/transcoder/utils.go b/core/transcoder/utils.go index 18c9c8a2d..1928ae5ed 100644 --- a/core/transcoder/utils.go +++ b/core/transcoder/utils.go @@ -8,7 +8,7 @@ import ( "sync" "github.com/owncast/owncast/config" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/utils" log "github.com/sirupsen/logrus" ) @@ -99,9 +99,9 @@ func handleTranscoderMessage(message string) { func createVariantDirectories() { // Create private hls data dirs utils.CleanupDirectory(config.HLSStoragePath) - - if len(data.GetStreamOutputVariants()) != 0 { - for index := range data.GetStreamOutputVariants() { + configRepository := configrepository.Get() + if len(configRepository.GetStreamOutputVariants()) != 0 { + for index := range configRepository.GetStreamOutputVariants() { if err := os.MkdirAll(path.Join(config.HLSStoragePath, strconv.Itoa(index)), 0o750); err != nil { log.Fatalln(err) } diff --git a/core/webhooks/stream.go b/core/webhooks/stream.go index b44dcb07c..d15efe732 100644 --- a/core/webhooks/stream.go +++ b/core/webhooks/stream.go @@ -3,8 +3,8 @@ package webhooks import ( "time" - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/persistence/configrepository" "github.com/teris-io/shortid" ) @@ -14,13 +14,15 @@ func SendStreamStatusEvent(eventType models.EventType) { } func sendStreamStatusEvent(eventType models.EventType, id string, timestamp time.Time) { + configRepository := configrepository.Get() + SendEventToWebhooks(WebhookEvent{ Type: eventType, EventData: map[string]interface{}{ "id": id, - "name": data.GetServerName(), - "summary": data.GetServerSummary(), - "streamTitle": data.GetStreamTitle(), + "name": configRepository.GetServerName(), + "summary": configRepository.GetServerSummary(), + "streamTitle": configRepository.GetStreamTitle(), "status": getStatus(), "timestamp": timestamp, }, diff --git a/core/webhooks/stream_test.go b/core/webhooks/stream_test.go index 3da067812..651bbe700 100644 --- a/core/webhooks/stream_test.go +++ b/core/webhooks/stream_test.go @@ -5,14 +5,16 @@ import ( "time" "github.com/owncast/owncast/core/chat/events" - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/persistence/configrepository" ) func TestSendStreamStatusEvent(t *testing.T) { - data.SetServerName("my server") - data.SetServerSummary("my server where I stream") - data.SetStreamTitle("my stream") + configRepository := configrepository.Get() + + configRepository.SetServerName("my server") + configRepository.SetServerSummary("my server where I stream") + configRepository.SetStreamTitle("my stream") checkPayload(t, models.StreamStarted, func() { sendStreamStatusEvent(events.StreamStarted, "id", time.Unix(72, 6).UTC()) diff --git a/main.go b/main.go index e3ac8180b..562ebd19a 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,7 @@ import ( "strconv" "github.com/owncast/owncast/logging" + "github.com/owncast/owncast/persistence/configrepository" log "github.com/sirupsen/logrus" "github.com/owncast/owncast/config" @@ -111,8 +112,10 @@ func main() { } func handleCommandLineFlags() { + configRepository := configrepository.Get() + if *newAdminPassword != "" { - if err := data.SetAdminPassword(*newAdminPassword); err != nil { + if err := configRepository.SetAdminPassword(*newAdminPassword); err != nil { log.Errorln("Error setting your admin password.", err) log.Exit(1) } else { @@ -134,25 +137,25 @@ func handleCommandLineFlags() { } log.Println("Saving new web server port number to", portNumber) - if err := data.SetHTTPPortNumber(float64(portNumber)); err != nil { + if err := configRepository.SetHTTPPortNumber(float64(portNumber)); err != nil { log.Errorln(err) } } - config.WebServerPort = data.GetHTTPPortNumber() + config.WebServerPort = configRepository.GetHTTPPortNumber() // Set the web server ip if *webServerIPOverride != "" { log.Println("Saving new web server listen IP address to", *webServerIPOverride) - if err := data.SetHTTPListenAddress(*webServerIPOverride); err != nil { + if err := configRepository.SetHTTPListenAddress(*webServerIPOverride); err != nil { log.Errorln(err) } } - config.WebServerIP = data.GetHTTPListenAddress() + config.WebServerIP = configRepository.GetHTTPListenAddress() // Set the rtmp server port if *rtmpPortOverride > 0 { log.Println("Saving new RTMP server port number to", *rtmpPortOverride) - if err := data.SetRTMPPortNumber(float64(*rtmpPortOverride)); err != nil { + if err := configRepository.SetRTMPPortNumber(float64(*rtmpPortOverride)); err != nil { log.Errorln(err) } } diff --git a/metrics/healthOverview.go b/metrics/healthOverview.go index 67a369847..2c966d532 100644 --- a/metrics/healthOverview.go +++ b/metrics/healthOverview.go @@ -5,8 +5,8 @@ import ( "sort" "github.com/owncast/owncast/core" - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/utils" ) @@ -68,8 +68,8 @@ func networkSpeedHealthOverviewMessage() string { isVideoPassthrough bool bitrate int } - - outputVariants := data.GetStreamOutputVariants() + configRepository := configrepository.Get() + outputVariants := configRepository.GetStreamOutputVariants() streamSortVariants := make([]singleVariant, len(outputVariants)) for i, variant := range outputVariants { @@ -155,7 +155,8 @@ func wastefulBitrateOverviewMessage() string { return "" } - outputVariants := data.GetStreamOutputVariants() + configRepository := configrepository.Get() + outputVariants := configRepository.GetStreamOutputVariants() type singleVariant struct { isVideoPassthrough bool @@ -229,7 +230,8 @@ func errorCountHealthOverviewMessage() string { healthyPercentage := utils.IntPercentage(clientsWithErrors, totalNumberOfClients) isUsingPassthrough := false - outputVariants := data.GetStreamOutputVariants() + configRepository := configrepository.Get() + outputVariants := configRepository.GetStreamOutputVariants() for _, variant := range outputVariants { if variant.IsVideoPassthrough { isUsingPassthrough = true diff --git a/metrics/metrics.go b/metrics/metrics.go index 2453e2300..3d82f102c 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -5,8 +5,8 @@ import ( "time" "github.com/owncast/owncast/config" - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/persistence/configrepository" ) // How often we poll for updates. @@ -56,8 +56,9 @@ var _getStatus func() models.Status // Start will begin the metrics collection and alerting. func Start(getStatus func() models.Status) { + configRepository := configrepository.Get() _getStatus = getStatus - host := data.GetServerURL() + host := configRepository.GetServerURL() if host == "" { host = "unknown" } diff --git a/core/data/configEntry.go b/models/configEntry.go similarity index 56% rename from core/data/configEntry.go rename to models/configEntry.go index 8486c8c0b..4eb5a59e4 100644 --- a/core/data/configEntry.go +++ b/models/configEntry.go @@ -1,4 +1,4 @@ -package data +package models import ( "bytes" @@ -12,48 +12,48 @@ type ConfigEntry struct { Key string } -func (c *ConfigEntry) getStringSlice() ([]string, error) { - decoder := c.getDecoder() +func (c *ConfigEntry) GetStringSlice() ([]string, error) { + decoder := c.GetDecoder() var result []string err := decoder.Decode(&result) return result, err } -func (c *ConfigEntry) getStringMap() (map[string]string, error) { - decoder := c.getDecoder() +func (c *ConfigEntry) GetStringMap() (map[string]string, error) { + decoder := c.GetDecoder() var result map[string]string err := decoder.Decode(&result) return result, err } -func (c *ConfigEntry) getString() (string, error) { - decoder := c.getDecoder() +func (c *ConfigEntry) GetString() (string, error) { + decoder := c.GetDecoder() var result string err := decoder.Decode(&result) return result, err } -func (c *ConfigEntry) getNumber() (float64, error) { - decoder := c.getDecoder() +func (c *ConfigEntry) GetNumber() (float64, error) { + decoder := c.GetDecoder() var result float64 err := decoder.Decode(&result) return result, err } -func (c *ConfigEntry) getBool() (bool, error) { - decoder := c.getDecoder() +func (c *ConfigEntry) GetBool() (bool, error) { + decoder := c.GetDecoder() var result bool err := decoder.Decode(&result) return result, err } -func (c *ConfigEntry) getObject(result interface{}) error { - decoder := c.getDecoder() +func (c *ConfigEntry) GetObject(result interface{}) error { + decoder := c.GetDecoder() err := decoder.Decode(result) return err } -func (c *ConfigEntry) getDecoder() *gob.Decoder { +func (c *ConfigEntry) GetDecoder() *gob.Decoder { valueBytes := c.Value.([]byte) decoder := gob.NewDecoder(bytes.NewBuffer(valueBytes)) return decoder diff --git a/notifications/notifications.go b/notifications/notifications.go index c293208b5..85247d5d3 100644 --- a/notifications/notifications.go +++ b/notifications/notifications.go @@ -8,6 +8,7 @@ import ( "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/db" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/persistence/tables" "github.com/owncast/owncast/notifications/browser" @@ -18,9 +19,10 @@ import ( // Notifier is an instance of the live stream notifier. type Notifier struct { - datastore *data.Datastore - browser *browser.Browser - discord *discord.Discord + datastore *data.Datastore + browser *browser.Browser + discord *discord.Discord + configRepository configrepository.ConfigRepository } // Setup will perform any pre-use setup for the notifier. @@ -30,8 +32,10 @@ func Setup(datastore *data.Datastore) { } func initializeBrowserPushIfNeeded() { - pubKey, _ := data.GetBrowserPushPublicKey() - privKey, _ := data.GetBrowserPushPrivateKey() + configRepository := configrepository.Get() + + pubKey, _ := configRepository.GetBrowserPushPublicKey() + privKey, _ := configRepository.GetBrowserPushPrivateKey() // We need browser push keys so people can register for pushes. if pubKey == "" || privKey == "" { @@ -40,26 +44,27 @@ func initializeBrowserPushIfNeeded() { log.Errorln("unable to initialize browser push notification keys", err) } - if err := data.SetBrowserPushPrivateKey(browserPrivateKey); err != nil { + if err := configRepository.SetBrowserPushPrivateKey(browserPrivateKey); err != nil { log.Errorln("unable to set browser push private key", err) } - if err := data.SetBrowserPushPublicKey(browserPublicKey); err != nil { + if err := configRepository.SetBrowserPushPublicKey(browserPublicKey); err != nil { log.Errorln("unable to set browser push public key", err) } } // Enable browser push notifications by default. - if !data.GetHasPerformedInitialNotificationsConfig() { - _ = data.SetBrowserPushConfig(models.BrowserNotificationConfiguration{Enabled: true, GoLiveMessage: config.GetDefaults().FederationGoLiveMessage}) - _ = data.SetHasPerformedInitialNotificationsConfig(true) + if !configRepository.GetHasPerformedInitialNotificationsConfig() { + _ = configRepository.SetBrowserPushConfig(models.BrowserNotificationConfiguration{Enabled: true, GoLiveMessage: config.GetDefaults().FederationGoLiveMessage}) + _ = configRepository.SetHasPerformedInitialNotificationsConfig(true) } } // New creates a new instance of the Notifier. func New(datastore *data.Datastore) (*Notifier, error) { notifier := Notifier{ - datastore: datastore, + datastore: datastore, + configRepository: configrepository.Get(), } if err := notifier.setupBrowserPush(); err != nil { @@ -73,13 +78,13 @@ func New(datastore *data.Datastore) (*Notifier, error) { } func (n *Notifier) setupBrowserPush() error { - if data.GetBrowserPushConfig().Enabled { - publicKey, err := data.GetBrowserPushPublicKey() + if n.configRepository.GetBrowserPushConfig().Enabled { + publicKey, err := n.configRepository.GetBrowserPushPublicKey() if err != nil || publicKey == "" { return errors.Wrap(err, "browser notifier disabled, failed to get browser push public key") } - privateKey, err := data.GetBrowserPushPrivateKey() + privateKey, err := n.configRepository.GetBrowserPushPrivateKey() if err != nil || privateKey == "" { return errors.Wrap(err, "browser notifier disabled, failed to get browser push private key") } @@ -99,7 +104,7 @@ func (n *Notifier) notifyBrowserPush() { log.Errorln("error getting browser push notification destinations", err) } for _, destination := range destinations { - unsubscribed, err := n.browser.Send(destination, data.GetServerName(), data.GetBrowserPushConfig().GoLiveMessage) + unsubscribed, err := n.browser.Send(destination, n.configRepository.GetServerName(), n.configRepository.GetBrowserPushConfig().GoLiveMessage) if unsubscribed { // If the error is "unsubscribed", then remove the destination from the database. if err := RemoveNotificationForChannel(BrowserPushNotification, destination); err != nil { @@ -112,14 +117,14 @@ func (n *Notifier) notifyBrowserPush() { } func (n *Notifier) setupDiscord() error { - discordConfig := data.GetDiscordConfig() + discordConfig := n.configRepository.GetDiscordConfig() if discordConfig.Enabled && discordConfig.Webhook != "" { var image string - if serverURL := data.GetServerURL(); serverURL != "" { + if serverURL := n.configRepository.GetServerURL(); serverURL != "" { image = serverURL + "/logo" } discordNotifier, err := discord.New( - data.GetServerName(), + n.configRepository.GetServerName(), image, discordConfig.Webhook, ) @@ -132,12 +137,12 @@ func (n *Notifier) setupDiscord() error { } func (n *Notifier) notifyDiscord() { - goLiveMessage := data.GetDiscordConfig().GoLiveMessage - streamTitle := data.GetStreamTitle() + goLiveMessage := n.configRepository.GetDiscordConfig().GoLiveMessage + streamTitle := n.configRepository.GetStreamTitle() if streamTitle != "" { goLiveMessage += "\n" + streamTitle } - message := fmt.Sprintf("%s\n\n%s", goLiveMessage, data.GetServerURL()) + message := fmt.Sprintf("%s\n\n%s", goLiveMessage, n.configRepository.GetServerURL()) if err := n.discord.Send(message); err != nil { log.Errorln("error sending discord message", err) @@ -158,6 +163,7 @@ func (n *Notifier) Notify() { // RemoveNotificationForChannel removes a notification destination. func RemoveNotificationForChannel(channel, destination string) error { log.Debugln("Removing notification for channel", channel) + return data.GetDatastore().GetQueries().RemoveNotificationDestinationForChannel(context.Background(), db.RemoveNotificationDestinationForChannelParams{ Channel: channel, Destination: destination, diff --git a/persistence/authrepository/authrepository.go b/persistence/authrepository/authrepository.go new file mode 100644 index 000000000..a1ca46715 --- /dev/null +++ b/persistence/authrepository/authrepository.go @@ -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 +} diff --git a/persistence/authrepository/bans.go b/persistence/authrepository/bans.go new file mode 100644 index 000000000..2262a2832 --- /dev/null +++ b/persistence/authrepository/bans.go @@ -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) +} diff --git a/persistence/authrepository/sqlauthrepository.go b/persistence/authrepository/sqlauthrepository.go new file mode 100644 index 000000000..7ab667df5 --- /dev/null +++ b/persistence/authrepository/sqlauthrepository.go @@ -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 +} diff --git a/persistence/configrepository/activitypub.go b/persistence/configrepository/activitypub.go new file mode 100644 index 000000000..7c752bc29 --- /dev/null +++ b/persistence/configrepository/activitypub.go @@ -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() +} diff --git a/core/data/datastoreMigrations.go b/persistence/configrepository/configMigrations.go similarity index 70% rename from core/data/datastoreMigrations.go rename to persistence/configrepository/configMigrations.go index 4047c5dbf..6b9fa1a53 100644 --- a/core/data/datastoreMigrations.go +++ b/persistence/configrepository/configMigrations.go @@ -1,8 +1,9 @@ -package data +package configrepository import ( "strings" + "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/models" log "github.com/sirupsen/logrus" ) @@ -12,8 +13,8 @@ const ( datastoreValueVersionKey = "DATA_STORE_VERSION" ) -func migrateDatastoreValues(datastore *Datastore) { - currentVersion, _ := _datastore.GetNumber(datastoreValueVersionKey) +func migrateDatastoreValues(datastore *data.Datastore) { + currentVersion, _ := datastore.GetNumber(datastoreValueVersionKey) if currentVersion == 0 { currentVersion = datastoreValuesVersion } @@ -33,12 +34,12 @@ func migrateDatastoreValues(datastore *Datastore) { log.Fatalln("missing datastore values migration step") } } - if err := _datastore.SetNumber(datastoreValueVersionKey, datastoreValuesVersion); err != nil { + if err := datastore.SetNumber(datastoreValueVersionKey, datastoreValuesVersion); err != nil { log.Errorln("error setting datastore value version:", err) } } -func migrateToDatastoreValues1(datastore *Datastore) { +func migrateToDatastoreValues1(datastore *data.Datastore) { // Migrate the forbidden usernames to be a slice instead of a string. forbiddenUsernamesString, _ := datastore.GetString(blockedUsernamesKey) if forbiddenUsernamesString != "" { @@ -58,28 +59,32 @@ func migrateToDatastoreValues1(datastore *Datastore) { } } -func migrateToDatastoreValues2(datastore *Datastore) { +func migrateToDatastoreValues2(datastore *data.Datastore) { + configRepository := Get() + oldAdminPassword, _ := datastore.GetString("stream_key") // Avoids double hashing the password _ = datastore.SetString("admin_password_key", oldAdminPassword) - _ = SetStreamKeys([]models.StreamKey{ + _ = configRepository.SetStreamKeys([]models.StreamKey{ {Key: oldAdminPassword, Comment: "Default stream key"}, }) } -func migrateToDatastoreValues3ServingEndpoint3(_ *Datastore) { - s3Config := GetS3Config() +func migrateToDatastoreValues3ServingEndpoint3(_ *data.Datastore) { + configRepository := Get() + s3Config := configRepository.GetS3Config() if !s3Config.Enabled { return } - _ = SetVideoServingEndpoint(s3Config.ServingEndpoint) + _ = configRepository.SetVideoServingEndpoint(s3Config.ServingEndpoint) } -func migrateToDatastoreValues4(datastore *Datastore) { +func migrateToDatastoreValues4(datastore *data.Datastore) { + configRepository := Get() unhashed_pass, _ := datastore.GetString("admin_password_key") - err := SetAdminPassword(unhashed_pass) + err := configRepository.SetAdminPassword(unhashed_pass) if err != nil { log.Fatalln("error migrating admin password:", err) } diff --git a/persistence/configrepository/configkeys.go b/persistence/configrepository/configkeys.go new file mode 100644 index 000000000..7218bf9f4 --- /dev/null +++ b/persistence/configrepository/configkeys.go @@ -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" +) diff --git a/persistence/configrepository/configrepository.go b/persistence/configrepository/configrepository.go new file mode 100644 index 000000000..9c9c18ed2 --- /dev/null +++ b/persistence/configrepository/configrepository.go @@ -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 +} diff --git a/persistence/configrepository/crypto.go b/persistence/configrepository/crypto.go new file mode 100644 index 000000000..ac4396e06 --- /dev/null +++ b/persistence/configrepository/crypto.go @@ -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) +} diff --git a/persistence/configrepository/defaults.go b/persistence/configrepository/defaults.go new file mode 100644 index 000000000..6d05eb1ab --- /dev/null +++ b/persistence/configrepository/defaults.go @@ -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 +} diff --git a/persistence/configrepository/sqlconfigrepository.go b/persistence/configrepository/sqlconfigrepository.go new file mode 100644 index 000000000..6528ee40f --- /dev/null +++ b/persistence/configrepository/sqlconfigrepository.go @@ -0,0 +1,1003 @@ +package configrepository + +import ( + "os" + "path/filepath" + "sort" + "strings" + "time" + + "github.com/owncast/owncast/config" + "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/models" + "github.com/owncast/owncast/static" + "github.com/owncast/owncast/utils" + "github.com/pkg/errors" + log "github.com/sirupsen/logrus" +) + +type SqlConfigRepository struct { + datastore *data.Datastore +} + +// NOTE: This is temporary during the transition period. +var temporaryGlobalInstance ConfigRepository + +// Get will return the user repository. +func Get() ConfigRepository { + 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) ConfigRepository { + r := SqlConfigRepository{ + datastore: datastore, + } + + migrateDatastoreValues(datastore) + + // Set the server initialization date if needed. + if hasSetInitDate, _ := r.GetServerInitTime(); hasSetInitDate == nil || !hasSetInitDate.Valid { + _ = r.SetServerInitTime(time.Now()) + } + + if !r.HasPopulatedDefaults() { + r.PopulateDefaults() + } + + return &r +} + +// GetExtraPageBodyContent will return the user-supplied body content. +func (r *SqlConfigRepository) GetExtraPageBodyContent() string { + content, err := r.datastore.GetString(extraContentKey) + if err != nil { + log.Traceln(extraContentKey, err) + return config.GetDefaults().PageBodyContent + } + + return content +} + +// SetExtraPageBodyContent will set the user-supplied body content. +func (r *SqlConfigRepository) SetExtraPageBodyContent(content string) error { + return r.datastore.SetString(extraContentKey, content) +} + +// GetStreamTitle will return the name of the current stream. +func (r *SqlConfigRepository) GetStreamTitle() string { + title, err := r.datastore.GetString(streamTitleKey) + if err != nil { + return "" + } + + return title +} + +// SetStreamTitle will set the name of the current stream. +func (r *SqlConfigRepository) SetStreamTitle(title string) error { + return r.datastore.SetString(streamTitleKey, title) +} + +// GetAdminPassword will return the admin password. +func (r *SqlConfigRepository) GetAdminPassword() string { + key, _ := r.datastore.GetString(adminPasswordKey) + return key +} + +// SetAdminPassword will set the admin password. +func (r *SqlConfigRepository) SetAdminPassword(key string) error { + hashed_pass, err := utils.HashPassword(key) + if err != nil { + return err + } + return r.datastore.SetString(adminPasswordKey, hashed_pass) +} + +// GetLogoPath will return the path for the logo, relative to webroot. +func (r *SqlConfigRepository) GetLogoPath() string { + logo, err := r.datastore.GetString(logoPathKey) + if err != nil { + log.Traceln(logoPathKey, err) + return config.GetDefaults().Logo + } + + if logo == "" { + return config.GetDefaults().Logo + } + + return logo +} + +// SetLogoPath will set the path for the logo, relative to webroot. +func (r *SqlConfigRepository) SetLogoPath(logo string) error { + return r.datastore.SetString(logoPathKey, logo) +} + +// SetLogoUniquenessString will set the logo cache busting string. +func (r *SqlConfigRepository) SetLogoUniquenessString(uniqueness string) error { + return r.datastore.SetString(logoUniquenessKey, uniqueness) +} + +// GetLogoUniquenessString will return the logo cache busting string. +func (r *SqlConfigRepository) GetLogoUniquenessString() string { + uniqueness, err := r.datastore.GetString(logoUniquenessKey) + if err != nil { + log.Traceln(logoUniquenessKey, err) + return "" + } + + return uniqueness +} + +// GetServerSummary will return the server summary text. +func (r *SqlConfigRepository) GetServerSummary() string { + summary, err := r.datastore.GetString(serverSummaryKey) + if err != nil { + log.Traceln(serverSummaryKey, err) + return "" + } + + return summary +} + +// SetServerSummary will set the server summary text. +func (r *SqlConfigRepository) SetServerSummary(summary string) error { + return r.datastore.SetString(serverSummaryKey, summary) +} + +// GetServerWelcomeMessage will return the server welcome message text. +func (r *SqlConfigRepository) GetServerWelcomeMessage() string { + welcomeMessage, err := r.datastore.GetString(serverWelcomeMessageKey) + if err != nil { + log.Traceln(serverWelcomeMessageKey, err) + return config.GetDefaults().ServerWelcomeMessage + } + + return welcomeMessage +} + +// SetServerWelcomeMessage will set the server welcome message text. +func (r *SqlConfigRepository) SetServerWelcomeMessage(welcomeMessage string) error { + return r.datastore.SetString(serverWelcomeMessageKey, welcomeMessage) +} + +// GetServerName will return the server name text. +func (r *SqlConfigRepository) GetServerName() string { + name, err := r.datastore.GetString(serverNameKey) + if err != nil { + log.Traceln(serverNameKey, err) + return config.GetDefaults().Name + } + + return name +} + +// SetServerName will set the server name text. +func (r *SqlConfigRepository) SetServerName(name string) error { + return r.datastore.SetString(serverNameKey, name) +} + +// GetServerURL will return the server URL. +func (r *SqlConfigRepository) GetServerURL() string { + url, err := r.datastore.GetString(serverURLKey) + if err != nil { + return "" + } + + return url +} + +// SetServerURL will set the server URL. +func (r *SqlConfigRepository) SetServerURL(url string) error { + return r.datastore.SetString(serverURLKey, url) +} + +// GetHTTPPortNumber will return the server HTTP port. +func (r *SqlConfigRepository) GetHTTPPortNumber() int { + port, err := r.datastore.GetNumber(httpPortNumberKey) + if err != nil { + log.Traceln(httpPortNumberKey, err) + return config.GetDefaults().WebServerPort + } + + if port == 0 { + return config.GetDefaults().WebServerPort + } + return int(port) +} + +// SetWebsocketOverrideHost will set the host override for websockets. +func (r *SqlConfigRepository) SetWebsocketOverrideHost(host string) error { + return r.datastore.SetString(websocketHostOverrideKey, host) +} + +// GetWebsocketOverrideHost will return the host override for websockets. +func (r *SqlConfigRepository) GetWebsocketOverrideHost() string { + host, _ := r.datastore.GetString(websocketHostOverrideKey) + + return host +} + +// SetHTTPPortNumber will set the server HTTP port. +func (r *SqlConfigRepository) SetHTTPPortNumber(port float64) error { + return r.datastore.SetNumber(httpPortNumberKey, port) +} + +// GetHTTPListenAddress will return the HTTP listen address. +func (r *SqlConfigRepository) GetHTTPListenAddress() string { + address, err := r.datastore.GetString(httpListenAddressKey) + if err != nil { + log.Traceln(httpListenAddressKey, err) + return config.GetDefaults().WebServerIP + } + return address +} + +// SetHTTPListenAddress will set the server HTTP listen address. +func (r *SqlConfigRepository) SetHTTPListenAddress(address string) error { + return r.datastore.SetString(httpListenAddressKey, address) +} + +// GetRTMPPortNumber will return the server RTMP port. +func (r *SqlConfigRepository) GetRTMPPortNumber() int { + port, err := r.datastore.GetNumber(rtmpPortNumberKey) + if err != nil { + log.Traceln(rtmpPortNumberKey, err) + return config.GetDefaults().RTMPServerPort + } + + if port == 0 { + return config.GetDefaults().RTMPServerPort + } + + return int(port) +} + +// SetRTMPPortNumber will set the server RTMP port. +func (r *SqlConfigRepository) SetRTMPPortNumber(port float64) error { + return r.datastore.SetNumber(rtmpPortNumberKey, port) +} + +// GetServerMetadataTags will return the metadata tags. +func (r *SqlConfigRepository) GetServerMetadataTags() []string { + tagsString, err := r.datastore.GetString(serverMetadataTagsKey) + if tagsString == "" { + return []string{} + } + + if err != nil { + log.Traceln(serverMetadataTagsKey, err) + return []string{} + } + + return strings.Split(tagsString, ",") +} + +// SetServerMetadataTags will return the metadata tags. +func (r *SqlConfigRepository) SetServerMetadataTags(tags []string) error { + tagString := strings.Join(tags, ",") + return r.datastore.SetString(serverMetadataTagsKey, tagString) +} + +// GetDirectoryEnabled will return if this server should register to YP. +func (r *SqlConfigRepository) GetDirectoryEnabled() bool { + enabled, err := r.datastore.GetBool(directoryEnabledKey) + if err != nil { + return config.GetDefaults().YPEnabled + } + + return enabled +} + +// SetDirectoryEnabled will set if this server should register to YP. +func (r *SqlConfigRepository) SetDirectoryEnabled(enabled bool) error { + return r.datastore.SetBool(directoryEnabledKey, enabled) +} + +// SetDirectoryRegistrationKey will set the YP protocol registration key. +func (r *SqlConfigRepository) SetDirectoryRegistrationKey(key string) error { + return r.datastore.SetString(directoryRegistrationKeyKey, key) +} + +// GetDirectoryRegistrationKey will return the YP protocol registration key. +func (r *SqlConfigRepository) GetDirectoryRegistrationKey() string { + key, _ := r.datastore.GetString(directoryRegistrationKeyKey) + return key +} + +// GetSocialHandles will return the external social links. +func (r *SqlConfigRepository) GetSocialHandles() []models.SocialHandle { + var socialHandles []models.SocialHandle + + configEntry, err := r.datastore.Get(socialHandlesKey) + if err != nil { + log.Traceln(socialHandlesKey, err) + return socialHandles + } + + if err := configEntry.GetObject(&socialHandles); err != nil { + log.Traceln(err) + return socialHandles + } + + return socialHandles +} + +// SetSocialHandles will set the external social links. +func (r *SqlConfigRepository) SetSocialHandles(socialHandles []models.SocialHandle) error { + configEntry := models.ConfigEntry{Key: socialHandlesKey, Value: socialHandles} + return r.datastore.Save(configEntry) +} + +// GetPeakSessionViewerCount will return the max number of viewers for this stream. +func (r *SqlConfigRepository) GetPeakSessionViewerCount() int { + count, err := r.datastore.GetNumber(peakViewersSessionKey) + if err != nil { + return 0 + } + return int(count) +} + +// SetPeakSessionViewerCount will set the max number of viewers for this stream. +func (r *SqlConfigRepository) SetPeakSessionViewerCount(count int) error { + return r.datastore.SetNumber(peakViewersSessionKey, float64(count)) +} + +// GetPeakOverallViewerCount will return the overall max number of viewers. +func (r *SqlConfigRepository) GetPeakOverallViewerCount() int { + count, err := r.datastore.GetNumber(peakViewersOverallKey) + if err != nil { + return 0 + } + return int(count) +} + +// SetPeakOverallViewerCount will set the overall max number of viewers. +func (r *SqlConfigRepository) SetPeakOverallViewerCount(count int) error { + return r.datastore.SetNumber(peakViewersOverallKey, float64(count)) +} + +// GetLastDisconnectTime will return the time the last stream ended. +func (r *SqlConfigRepository) GetLastDisconnectTime() (*utils.NullTime, error) { + var disconnectTime utils.NullTime + + configEntry, err := r.datastore.Get(lastDisconnectTimeKey) + if err != nil { + return nil, err + } + + if err := configEntry.GetObject(&disconnectTime); err != nil { + return nil, err + } + + if !disconnectTime.Valid || disconnectTime.Time.IsZero() { + return nil, err + } + + return &disconnectTime, nil +} + +// SetLastDisconnectTime will set the time the last stream ended. +func (r *SqlConfigRepository) SetLastDisconnectTime(disconnectTime time.Time) error { + savedDisconnectTime := utils.NullTime{Time: disconnectTime, Valid: true} + configEntry := models.ConfigEntry{Key: lastDisconnectTimeKey, Value: savedDisconnectTime} + return r.datastore.Save(configEntry) +} + +// SetNSFW will set if this stream has NSFW content. +func (r *SqlConfigRepository) SetNSFW(isNSFW bool) error { + return r.datastore.SetBool(nsfwKey, isNSFW) +} + +// GetNSFW will return if this stream has NSFW content. +func (r *SqlConfigRepository) GetNSFW() bool { + nsfw, err := r.datastore.GetBool(nsfwKey) + if err != nil { + return false + } + return nsfw +} + +// SetFfmpegPath will set the custom ffmpeg path. +func (r *SqlConfigRepository) SetFfmpegPath(path string) error { + return r.datastore.SetString(ffmpegPathKey, path) +} + +// GetFfMpegPath will return the ffmpeg path. +func (r *SqlConfigRepository) GetFfMpegPath() string { + path, err := r.datastore.GetString(ffmpegPathKey) + if err != nil { + return "" + } + return path +} + +// GetS3Config will return the external storage configuration. +func (r *SqlConfigRepository) GetS3Config() models.S3 { + configEntry, err := r.datastore.Get(s3StorageConfigKey) + if err != nil { + return models.S3{Enabled: false} + } + + var s3Config models.S3 + if err := configEntry.GetObject(&s3Config); err != nil { + return models.S3{Enabled: false} + } + + return s3Config +} + +// SetS3Config will set the external storage configuration. +func (r *SqlConfigRepository) SetS3Config(config models.S3) error { + configEntry := models.ConfigEntry{Key: s3StorageConfigKey, Value: config} + return r.datastore.Save(configEntry) +} + +// GetStreamLatencyLevel will return the stream latency level. +func (r *SqlConfigRepository) GetStreamLatencyLevel() models.LatencyLevel { + level, err := r.datastore.GetNumber(videoLatencyLevel) + if err != nil { + level = 2 // default + } else if level > 4 { + level = 4 // highest + } + + return models.GetLatencyLevel(int(level)) +} + +// SetStreamLatencyLevel will set the stream latency level. +func (r *SqlConfigRepository) SetStreamLatencyLevel(level float64) error { + return r.datastore.SetNumber(videoLatencyLevel, level) +} + +// GetStreamOutputVariants will return all of the stream output variants. +func (r *SqlConfigRepository) GetStreamOutputVariants() []models.StreamOutputVariant { + configEntry, err := r.datastore.Get(videoStreamOutputVariantsKey) + if err != nil { + return config.GetDefaults().StreamVariants + } + + var streamOutputVariants []models.StreamOutputVariant + if err := configEntry.GetObject(&streamOutputVariants); err != nil { + return config.GetDefaults().StreamVariants + } + + if len(streamOutputVariants) == 0 { + return config.GetDefaults().StreamVariants + } + + return streamOutputVariants +} + +// SetStreamOutputVariants will set the stream output variants. +func (r *SqlConfigRepository) SetStreamOutputVariants(variants []models.StreamOutputVariant) error { + configEntry := models.ConfigEntry{Key: videoStreamOutputVariantsKey, Value: variants} + return r.datastore.Save(configEntry) +} + +// SetChatDisabled will disable chat if set to true. +func (r *SqlConfigRepository) SetChatDisabled(disabled bool) error { + return r.datastore.SetBool(chatDisabledKey, disabled) +} + +// GetChatDisabled will return if chat is disabled. +func (r *SqlConfigRepository) GetChatDisabled() bool { + disabled, err := r.datastore.GetBool(chatDisabledKey) + if err == nil { + return disabled + } + + return false +} + +// SetChatEstablishedUsersOnlyMode sets the state of established user only mode. +func (r *SqlConfigRepository) SetChatEstablishedUsersOnlyMode(enabled bool) error { + return r.datastore.SetBool(chatEstablishedUsersOnlyModeKey, enabled) +} + +// GetChatEstbalishedUsersOnlyMode returns the state of established user only mode. +func (r *SqlConfigRepository) GetChatEstbalishedUsersOnlyMode() bool { + enabled, err := r.datastore.GetBool(chatEstablishedUsersOnlyModeKey) + if err == nil { + return enabled + } + + return false +} + +// SetChatSpamProtectionEnabled will enable chat spam protection if set to true. +func (r *SqlConfigRepository) SetChatSpamProtectionEnabled(enabled bool) error { + return r.datastore.SetBool(chatSpamProtectionEnabledKey, enabled) +} + +// GetChatSpamProtectionEnabled will return if chat spam protection is enabled. +func (r *SqlConfigRepository) GetChatSpamProtectionEnabled() bool { + enabled, err := r.datastore.GetBool(chatSpamProtectionEnabledKey) + if err == nil { + return enabled + } + + return true +} + +// SetChatSlurFilterEnabled will enable the chat slur filter. +func (r *SqlConfigRepository) SetChatSlurFilterEnabled(enabled bool) error { + return r.datastore.SetBool(chatSlurFilterEnabledKey, enabled) +} + +// GetChatSlurFilterEnabled will return if the chat slur filter is enabled. +func (r *SqlConfigRepository) GetChatSlurFilterEnabled() bool { + enabled, err := r.datastore.GetBool(chatSlurFilterEnabledKey) + if err == nil { + return enabled + } + + return false +} + +// GetExternalActions will return the registered external actions. +func (r *SqlConfigRepository) GetExternalActions() []models.ExternalAction { + configEntry, err := r.datastore.Get(externalActionsKey) + if err != nil { + return []models.ExternalAction{} + } + + var externalActions []models.ExternalAction + if err := configEntry.GetObject(&externalActions); err != nil { + return []models.ExternalAction{} + } + + return externalActions +} + +// SetExternalActions will save external actions. +func (r *SqlConfigRepository) SetExternalActions(actions []models.ExternalAction) error { + configEntry := models.ConfigEntry{Key: externalActionsKey, Value: actions} + return r.datastore.Save(configEntry) +} + +// SetCustomStyles will save a string with CSS to insert into the page. +func (r *SqlConfigRepository) SetCustomStyles(styles string) error { + return r.datastore.SetString(customStylesKey, styles) +} + +// GetCustomStyles will return a string with CSS to insert into the page. +func (r *SqlConfigRepository) GetCustomStyles() string { + style, err := r.datastore.GetString(customStylesKey) + if err != nil { + return "" + } + + return style +} + +// SetCustomJavascript will save a string with Javascript to insert into the page. +func (r *SqlConfigRepository) SetCustomJavascript(styles string) error { + return r.datastore.SetString(customJavascriptKey, styles) +} + +// GetCustomJavascript will return a string with Javascript to insert into the page. +func (r *SqlConfigRepository) GetCustomJavascript() string { + style, err := r.datastore.GetString(customJavascriptKey) + if err != nil { + return "" + } + + return style +} + +// SetVideoCodec will set the codec used for video encoding. +func (r *SqlConfigRepository) SetVideoCodec(codec string) error { + return r.datastore.SetString(videoCodecKey, codec) +} + +// GetVideoCodec returns the codec to use for transcoding video. +func (r *SqlConfigRepository) GetVideoCodec() string { + codec, err := r.datastore.GetString(videoCodecKey) + if codec == "" || err != nil { + return "libx264" // Default value + } + + return codec +} + +// VerifySettings will perform a sanity check for specific settings values. +func (r *SqlConfigRepository) VerifySettings() error { + if len(r.GetStreamKeys()) == 0 && config.TemporaryStreamKey == "" { + log.Errorln("No stream key set. Streaming is disabled. Please set one via the admin or command line arguments") + } + + if r.GetAdminPassword() == "" { + return errors.New("no admin password set. Please set one via the admin or command line arguments") + } + + logoPath := r.GetLogoPath() + if !utils.DoesFileExists(filepath.Join(config.DataDirectory, logoPath)) { + log.Traceln(logoPath, "not found in the data directory. copying a default logo.") + logo := static.GetLogo() + if err := os.WriteFile(filepath.Join(config.DataDirectory, "logo.png"), logo, 0o600); err != nil { + return errors.Wrap(err, "failed to write logo to disk") + } + if err := r.SetLogoPath("logo.png"); err != nil { + return errors.Wrap(err, "failed to save logo filename") + } + } + + return nil +} + +// FindHighestVideoQualityIndex will return the highest quality from a slice of variants. +func (r *SqlConfigRepository) FindHighestVideoQualityIndex(qualities []models.StreamOutputVariant) (int, bool) { + type IndexedQuality struct { + quality models.StreamOutputVariant + index int + } + + if len(qualities) < 2 { + return 0, qualities[0].IsVideoPassthrough + } + + indexedQualities := make([]IndexedQuality, 0) + for index, quality := range qualities { + indexedQuality := IndexedQuality{quality, index} + indexedQualities = append(indexedQualities, indexedQuality) + } + + sort.Slice(indexedQualities, func(a, b int) bool { + if indexedQualities[a].quality.IsVideoPassthrough && !indexedQualities[b].quality.IsVideoPassthrough { + return true + } + + if !indexedQualities[a].quality.IsVideoPassthrough && indexedQualities[b].quality.IsVideoPassthrough { + return false + } + + return indexedQualities[a].quality.VideoBitrate > indexedQualities[b].quality.VideoBitrate + }) + + // nolint:gosec + selectedQuality := indexedQualities[0] + return selectedQuality.index, selectedQuality.quality.IsVideoPassthrough +} + +// GetForbiddenUsernameList will return the blocked usernames as a comma separated string. +func (r *SqlConfigRepository) GetForbiddenUsernameList() []string { + usernames, err := r.datastore.GetStringSlice(blockedUsernamesKey) + if err != nil { + return config.DefaultForbiddenUsernames + } + + if len(usernames) == 0 { + return config.DefaultForbiddenUsernames + } + + return usernames +} + +// SetForbiddenUsernameList set the username blocklist as a comma separated string. +func (r *SqlConfigRepository) SetForbiddenUsernameList(usernames []string) error { + return r.datastore.SetStringSlice(blockedUsernamesKey, usernames) +} + +// GetSuggestedUsernamesList will return the suggested usernames. +// If the number of suggested usernames is smaller than 10, the number pool is +// not used (see code in the CreateAnonymousUser function). +func (r *SqlConfigRepository) GetSuggestedUsernamesList() []string { + usernames, err := r.datastore.GetStringSlice(suggestedUsernamesKey) + + if err != nil || len(usernames) == 0 { + return []string{} + } + + return usernames +} + +// SetSuggestedUsernamesList sets the username suggestion list. +func (r *SqlConfigRepository) SetSuggestedUsernamesList(usernames []string) error { + return r.datastore.SetStringSlice(suggestedUsernamesKey, usernames) +} + +// GetServerInitTime will return when the server was first setup. +func (r *SqlConfigRepository) GetServerInitTime() (*utils.NullTime, error) { + var t utils.NullTime + + configEntry, err := r.datastore.Get(serverInitDateKey) + if err != nil { + return nil, err + } + + if err := configEntry.GetObject(&t); err != nil { + return nil, err + } + + if !t.Valid { + return nil, err + } + + return &t, nil +} + +// SetServerInitTime will set when the server was first created. +func (r *SqlConfigRepository) SetServerInitTime(t time.Time) error { + nt := utils.NullTime{Time: t, Valid: true} + configEntry := models.ConfigEntry{Key: serverInitDateKey, Value: nt} + return r.datastore.Save(configEntry) +} + +// SetFederationEnabled will enable federation if set to true. +func (r *SqlConfigRepository) SetFederationEnabled(enabled bool) error { + return r.datastore.SetBool(federationEnabledKey, enabled) +} + +// GetFederationEnabled will return if federation is enabled. +func (r *SqlConfigRepository) GetFederationEnabled() bool { + enabled, err := r.datastore.GetBool(federationEnabledKey) + if err == nil { + return enabled + } + + return false +} + +// SetFederationUsername will set the username used in federated activities. +func (r *SqlConfigRepository) SetFederationUsername(username string) error { + return r.datastore.SetString(federationUsernameKey, username) +} + +// GetFederationUsername will return the username used in federated activities. +func (r *SqlConfigRepository) GetFederationUsername() string { + username, err := r.datastore.GetString(federationUsernameKey) + if username == "" || err != nil { + return config.GetDefaults().FederationUsername + } + + return username +} + +// SetFederationGoLiveMessage will set the message sent when going live. +func (r *SqlConfigRepository) SetFederationGoLiveMessage(message string) error { + return r.datastore.SetString(federationGoLiveMessageKey, message) +} + +// GetFederationGoLiveMessage will return the message sent when going live. +func (r *SqlConfigRepository) GetFederationGoLiveMessage() string { + // Empty message means it's disabled. + message, err := r.datastore.GetString(federationGoLiveMessageKey) + if err != nil { + log.Traceln("unable to fetch go live message.", err) + } + + return message +} + +// SetFederationIsPrivate will set if federation activity is private. +func (r *SqlConfigRepository) SetFederationIsPrivate(isPrivate bool) error { + return r.datastore.SetBool(federationPrivateKey, isPrivate) +} + +// GetFederationIsPrivate will return if federation is private. +func (r *SqlConfigRepository) GetFederationIsPrivate() bool { + isPrivate, err := r.datastore.GetBool(federationPrivateKey) + if err == nil { + return isPrivate + } + + return false +} + +// SetFederationShowEngagement will set if fediverse engagement shows in chat. +func (r *SqlConfigRepository) SetFederationShowEngagement(showEngagement bool) error { + return r.datastore.SetBool(federationShowEngagementKey, showEngagement) +} + +// GetFederationShowEngagement will return if fediverse engagement shows in chat. +func (r *SqlConfigRepository) GetFederationShowEngagement() bool { + showEngagement, err := r.datastore.GetBool(federationShowEngagementKey) + if err == nil { + return showEngagement + } + + return true +} + +// SetBlockedFederatedDomains will set the blocked federated domains. +func (r *SqlConfigRepository) SetBlockedFederatedDomains(domains []string) error { + return r.datastore.SetString(federationBlockedDomainsKey, strings.Join(domains, ",")) +} + +// GetBlockedFederatedDomains will return a list of blocked federated domains. +func (r *SqlConfigRepository) GetBlockedFederatedDomains() []string { + domains, err := r.datastore.GetString(federationBlockedDomainsKey) + if err != nil { + return []string{} + } + + if domains == "" { + return []string{} + } + + return strings.Split(domains, ",") +} + +// SetChatJoinMessagesEnabled will set if chat join messages are enabled. +func (r *SqlConfigRepository) SetChatJoinMessagesEnabled(enabled bool) error { + return r.datastore.SetBool(chatJoinMessagesEnabledKey, enabled) +} + +// GetChatJoinPartMessagesEnabled will return if chat join messages are enabled. +func (r *SqlConfigRepository) GetChatJoinPartMessagesEnabled() bool { + enabled, err := r.datastore.GetBool(chatJoinMessagesEnabledKey) + if err != nil { + return true + } + + return enabled +} + +// SetNotificationsEnabled will save the enabled state of notifications. +func (r *SqlConfigRepository) SetNotificationsEnabled(enabled bool) error { + return r.datastore.SetBool(notificationsEnabledKey, enabled) +} + +// GetNotificationsEnabled will return the enabled state of notifications. +func (r *SqlConfigRepository) GetNotificationsEnabled() bool { + enabled, _ := r.datastore.GetBool(notificationsEnabledKey) + return enabled +} + +// GetDiscordConfig will return the Discord configuration. +func (r *SqlConfigRepository) GetDiscordConfig() models.DiscordConfiguration { + configEntry, err := r.datastore.Get(discordConfigurationKey) + if err != nil { + return models.DiscordConfiguration{Enabled: false} + } + + var config models.DiscordConfiguration + if err := configEntry.GetObject(&config); err != nil { + return models.DiscordConfiguration{Enabled: false} + } + + return config +} + +// SetDiscordConfig will set the Discord configuration. +func (r *SqlConfigRepository) SetDiscordConfig(config models.DiscordConfiguration) error { + configEntry := models.ConfigEntry{Key: discordConfigurationKey, Value: config} + return r.datastore.Save(configEntry) +} + +// GetBrowserPushConfig will return the browser push configuration. +func (r *SqlConfigRepository) GetBrowserPushConfig() models.BrowserNotificationConfiguration { + configEntry, err := r.datastore.Get(browserPushConfigurationKey) + if err != nil { + return models.BrowserNotificationConfiguration{Enabled: false} + } + + var config models.BrowserNotificationConfiguration + if err := configEntry.GetObject(&config); err != nil { + return models.BrowserNotificationConfiguration{Enabled: false} + } + + return config +} + +// SetBrowserPushConfig will set the browser push configuration. +func (r *SqlConfigRepository) SetBrowserPushConfig(config models.BrowserNotificationConfiguration) error { + configEntry := models.ConfigEntry{Key: browserPushConfigurationKey, Value: config} + return r.datastore.Save(configEntry) +} + +// SetBrowserPushPublicKey will set the public key for browser pushes. +func (r *SqlConfigRepository) SetBrowserPushPublicKey(key string) error { + return r.datastore.SetString(browserPushPublicKeyKey, key) +} + +// GetBrowserPushPublicKey will return the public key for browser pushes. +func (r *SqlConfigRepository) GetBrowserPushPublicKey() (string, error) { + return r.datastore.GetString(browserPushPublicKeyKey) +} + +// SetBrowserPushPrivateKey will set the private key for browser pushes. +func (r *SqlConfigRepository) SetBrowserPushPrivateKey(key string) error { + return r.datastore.SetString(browserPushPrivateKeyKey, key) +} + +// GetBrowserPushPrivateKey will return the private key for browser pushes. +func (r *SqlConfigRepository) GetBrowserPushPrivateKey() (string, error) { + return r.datastore.GetString(browserPushPrivateKeyKey) +} + +// SetHasPerformedInitialNotificationsConfig sets when performed initial setup. +func (r *SqlConfigRepository) SetHasPerformedInitialNotificationsConfig(hasConfigured bool) error { + return r.datastore.SetBool(hasConfiguredInitialNotificationsKey, true) +} + +// GetHasPerformedInitialNotificationsConfig gets when performed initial setup. +func (r *SqlConfigRepository) GetHasPerformedInitialNotificationsConfig() bool { + configured, _ := r.datastore.GetBool(hasConfiguredInitialNotificationsKey) + return configured +} + +// GetHideViewerCount will return if the viewer count shold be hidden. +func (r *SqlConfigRepository) GetHideViewerCount() bool { + hide, _ := r.datastore.GetBool(hideViewerCountKey) + return hide +} + +// SetHideViewerCount will set if the viewer count should be hidden. +func (r *SqlConfigRepository) SetHideViewerCount(hide bool) error { + return r.datastore.SetBool(hideViewerCountKey, hide) +} + +// GetCustomOfflineMessage will return the custom offline message. +func (r *SqlConfigRepository) GetCustomOfflineMessage() string { + message, _ := r.datastore.GetString(customOfflineMessageKey) + return message +} + +// SetCustomOfflineMessage will set the custom offline message. +func (r *SqlConfigRepository) SetCustomOfflineMessage(message string) error { + return r.datastore.SetString(customOfflineMessageKey, message) +} + +// SetCustomColorVariableValues sets CSS variable names and values. +func (r *SqlConfigRepository) SetCustomColorVariableValues(variables map[string]string) error { + return r.datastore.SetStringMap(customColorVariableValuesKey, variables) +} + +// GetCustomColorVariableValues gets CSS variable names and values. +func (r *SqlConfigRepository) GetCustomColorVariableValues() map[string]string { + values, _ := r.datastore.GetStringMap(customColorVariableValuesKey) + return values +} + +// GetStreamKeys will return valid stream keys. +func (r *SqlConfigRepository) GetStreamKeys() []models.StreamKey { + configEntry, err := r.datastore.Get(streamKeysKey) + if err != nil { + return []models.StreamKey{} + } + + var streamKeys []models.StreamKey + if err := configEntry.GetObject(&streamKeys); err != nil { + return []models.StreamKey{} + } + + return streamKeys +} + +// SetStreamKeys will set valid stream keys. +func (r *SqlConfigRepository) SetStreamKeys(actions []models.StreamKey) error { + configEntry := models.ConfigEntry{Key: streamKeysKey, Value: actions} + return r.datastore.Save(configEntry) +} + +// SetDisableSearchIndexing will set if the web server should be indexable. +func (r *SqlConfigRepository) SetDisableSearchIndexing(disableSearchIndexing bool) error { + return r.datastore.SetBool(disableSearchIndexingKey, disableSearchIndexing) +} + +// GetDisableSearchIndexing will return if the web server should be indexable. +func (r *SqlConfigRepository) GetDisableSearchIndexing() bool { + disableSearchIndexing, err := r.datastore.GetBool(disableSearchIndexingKey) + if err != nil { + return false + } + return disableSearchIndexing +} + +// GetVideoServingEndpoint returns the custom video endpont. +func (r *SqlConfigRepository) GetVideoServingEndpoint() string { + message, _ := r.datastore.GetString(videoServingEndpointKey) + return message +} + +// SetVideoServingEndpoint sets the custom video endpoint. +func (r *SqlConfigRepository) SetVideoServingEndpoint(message string) error { + return r.datastore.SetString(videoServingEndpointKey, message) +} diff --git a/webserver/handlers/admin/appearance.go b/webserver/handlers/admin/appearance.go index 0312abe47..c1f27e053 100644 --- a/webserver/handlers/admin/appearance.go +++ b/webserver/handlers/admin/appearance.go @@ -4,7 +4,7 @@ import ( "encoding/json" "net/http" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/webserver/handlers/generated" webutils "github.com/owncast/owncast/webserver/utils" ) @@ -23,7 +23,8 @@ func SetCustomColorVariableValues(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetCustomColorVariableValues(*values.Value); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetCustomColorVariableValues(*values.Value); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } diff --git a/webserver/handlers/admin/chat.go b/webserver/handlers/admin/chat.go index f95468cd2..be67cf3ba 100644 --- a/webserver/handlers/admin/chat.go +++ b/webserver/handlers/admin/chat.go @@ -11,8 +11,9 @@ import ( "github.com/owncast/owncast/core/chat" "github.com/owncast/owncast/core/chat/events" - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/persistence/authrepository" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/persistence/userrepository" "github.com/owncast/owncast/utils" "github.com/owncast/owncast/webserver/handlers/generated" @@ -62,7 +63,9 @@ func BanIPAddress(w http.ResponseWriter, r *http.Request) { return } - if err := data.BanIPAddress(configValue.Value.(string), "manually added"); err != nil { + authRepository := authrepository.Get() + + if err := authRepository.BanIPAddress(configValue.Value.(string), "manually added"); err != nil { webutils.WriteSimpleResponse(w, false, "error saving IP address ban") return } @@ -82,7 +85,9 @@ func UnBanIPAddress(w http.ResponseWriter, r *http.Request) { return } - if err := data.RemoveIPAddressBan(configValue.Value.(string)); err != nil { + authRepository := authrepository.Get() + + if err := authRepository.RemoveIPAddressBan(configValue.Value.(string)); err != nil { webutils.WriteSimpleResponse(w, false, "error removing IP address ban") return } @@ -92,7 +97,9 @@ func UnBanIPAddress(w http.ResponseWriter, r *http.Request) { // GetIPAddressBans will return all the banned IP addresses. func GetIPAddressBans(w http.ResponseWriter, r *http.Request) { - bans, err := data.GetIPAddressBans() + authRepository := authrepository.Get() + + bans, err := authRepository.GetIPAddressBans() if err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return @@ -168,11 +175,13 @@ func UpdateUserEnabled(w http.ResponseWriter, r *http.Request) { localIP6Address := "::1" // Ban this user's IP address. + authRepository := authrepository.Get() + for _, client := range clients { ipAddress := client.IPAddress if ipAddress != localIP4Address && ipAddress != localIP6Address { reason := fmt.Sprintf("Banning of %s", disconnectedUser.DisplayName) - if err := data.BanIPAddress(ipAddress, reason); err != nil { + if err := authRepository.BanIPAddress(ipAddress, reason); err != nil { log.Errorln("error banning IP address: ", err) } } @@ -366,7 +375,8 @@ func SetEnableEstablishedChatUserMode(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetChatEstablishedUsersOnlyMode(configValue.Value.(bool)); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetChatEstablishedUsersOnlyMode(configValue.Value.(bool)); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } diff --git a/webserver/handlers/admin/config.go b/webserver/handlers/admin/config.go index bc9c0a104..a766ad817 100644 --- a/webserver/handlers/admin/config.go +++ b/webserver/handlers/admin/config.go @@ -13,9 +13,9 @@ import ( "github.com/owncast/owncast/activitypub/outbox" "github.com/owncast/owncast/core/chat" - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/core/webhooks" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/utils" "github.com/owncast/owncast/webserver/handlers/generated" webutils "github.com/owncast/owncast/webserver/utils" @@ -44,7 +44,8 @@ func SetTags(w http.ResponseWriter, r *http.Request) { tagStrings = append(tagStrings, strings.TrimLeft(tag.Value.(string), "#")) } - if err := data.SetServerMetadataTags(tagStrings); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetServerMetadataTags(tagStrings); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -70,8 +71,9 @@ func SetStreamTitle(w http.ResponseWriter, r *http.Request) { } value := configValue.Value.(string) + configRepository := configrepository.Get() - if err := data.SetStreamTitle(value); err != nil { + if err := configRepository.SetStreamTitle(value); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -104,7 +106,8 @@ func SetServerName(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetServerName(configValue.Value.(string)); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetServerName(configValue.Value.(string)); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -129,7 +132,8 @@ func SetServerSummary(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetServerSummary(configValue.Value.(string)); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetServerSummary(configValue.Value.(string)); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -154,7 +158,8 @@ func SetCustomOfflineMessage(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetCustomOfflineMessage(strings.TrimSpace(configValue.Value.(string))); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetCustomOfflineMessage(strings.TrimSpace(configValue.Value.(string))); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -173,7 +178,8 @@ func SetServerWelcomeMessage(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetServerWelcomeMessage(strings.TrimSpace(configValue.Value.(string))); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetServerWelcomeMessage(strings.TrimSpace(configValue.Value.(string))); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -192,7 +198,8 @@ func SetExtraPageContent(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetExtraPageBodyContent(configValue.Value.(string)); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetExtraPageBodyContent(configValue.Value.(string)); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -211,7 +218,8 @@ func SetAdminPassword(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetAdminPassword(configValue.Value.(string)); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetAdminPassword(configValue.Value.(string)); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -247,12 +255,14 @@ func SetLogo(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetLogoPath("logo" + extension); err != nil { + configRepository := configrepository.Get() + + if err := configRepository.SetLogoPath("logo" + extension); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } - if err := data.SetLogoUniquenessString(shortid.MustGenerate()); err != nil { + if err := configRepository.SetLogoUniquenessString(shortid.MustGenerate()); err != nil { log.Error("Error saving logo uniqueness string: ", err) } @@ -276,7 +286,8 @@ func SetNSFW(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetNSFW(configValue.Value.(bool)); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetNSFW(configValue.Value.(bool)); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -301,7 +312,8 @@ func SetFfmpegPath(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetFfmpegPath(configValue.Value.(string)); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetFfmpegPath(configValue.Value.(string)); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -320,12 +332,13 @@ func SetWebServerPort(w http.ResponseWriter, r *http.Request) { return } + configRepository := configrepository.Get() if port, ok := configValue.Value.(float64); ok { if (port < 1) || (port > 65535) { webutils.WriteSimpleResponse(w, false, "Port number must be between 1 and 65535") return } - if err := data.SetHTTPPortNumber(port); err != nil { + if err := configRepository.SetHTTPPortNumber(port); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -348,9 +361,10 @@ func SetWebServerIP(w http.ResponseWriter, r *http.Request) { return } + configRepository := configrepository.Get() if input, ok := configValue.Value.(string); ok { if ip := net.ParseIP(input); ip != nil { - if err := data.SetHTTPListenAddress(ip.String()); err != nil { + if err := configRepository.SetHTTPListenAddress(ip.String()); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -376,7 +390,8 @@ func SetRTMPServerPort(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetRTMPPortNumber(configValue.Value.(float64)); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetRTMPPortNumber(configValue.Value.(float64)); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -415,10 +430,12 @@ func SetServerURL(w http.ResponseWriter, r *http.Request) { return } + configRepository := configrepository.Get() + // Trim any trailing slash serverURL := strings.TrimRight(rawValue, "/") - if err := data.SetServerURL(serverURL); err != nil { + if err := configRepository.SetServerURL(serverURL); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -437,7 +454,9 @@ func SetSocketHostOverride(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetWebsocketOverrideHost(configValue.Value.(string)); err != nil { + configRepository := configrepository.Get() + + if err := configRepository.SetWebsocketOverrideHost(configValue.Value.(string)); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -456,7 +475,9 @@ func SetDirectoryEnabled(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetDirectoryEnabled(configValue.Value.(bool)); err != nil { + configRepository := configrepository.Get() + + if err := configRepository.SetDirectoryEnabled(configValue.Value.(bool)); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -474,7 +495,9 @@ func SetStreamLatencyLevel(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetStreamLatencyLevel(configValue.Value.(float64)); err != nil { + configRepository := configrepository.Get() + + if err := configRepository.SetStreamLatencyLevel(configValue.Value.(float64)); err != nil { webutils.WriteSimpleResponse(w, false, "error setting stream latency "+err.Error()) return } @@ -521,7 +544,8 @@ func SetS3Configuration(w http.ResponseWriter, r *http.Request) { } } - if err := data.SetS3Config(newS3Config.Value); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetS3Config(newS3Config.Value); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -545,7 +569,8 @@ func SetStreamOutputVariants(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetStreamOutputVariants(videoVariants.Value); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetStreamOutputVariants(videoVariants.Value); err != nil { webutils.WriteSimpleResponse(w, false, "unable to update video config with provided values "+err.Error()) return } @@ -570,7 +595,8 @@ func SetSocialHandles(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetSocialHandles(socialHandles.Value); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetSocialHandles(socialHandles.Value); err != nil { webutils.WriteSimpleResponse(w, false, "unable to update social handles with provided values") return } @@ -596,7 +622,8 @@ func SetChatDisabled(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetChatDisabled(configValue.Value.(bool)); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetChatDisabled(configValue.Value.(bool)); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -616,7 +643,8 @@ func SetVideoCodec(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetVideoCodec(configValue.Value.(string)); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetVideoCodec(configValue.Value.(string)); err != nil { webutils.WriteSimpleResponse(w, false, "unable to update codec") return } @@ -637,7 +665,8 @@ func SetExternalActions(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetExternalActions(actions.Value); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetExternalActions(actions.Value); err != nil { webutils.WriteSimpleResponse(w, false, "unable to update external actions with provided values") return } @@ -653,7 +682,8 @@ func SetCustomStyles(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetCustomStyles(customStyles.Value.(string)); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetCustomStyles(customStyles.Value.(string)); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -669,7 +699,8 @@ func SetCustomJavascript(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetCustomJavascript(customJavascript.Value.(string)); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetCustomJavascript(customJavascript.Value.(string)); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -687,7 +718,8 @@ func SetForbiddenUsernameList(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetForbiddenUsernameList(*request.Value); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetForbiddenUsernameList(*request.Value); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -705,7 +737,8 @@ func SetSuggestedUsernameList(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetSuggestedUsernamesList(*request.Value); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetSuggestedUsernamesList(*request.Value); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -725,7 +758,8 @@ func SetChatJoinMessagesEnabled(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetChatJoinMessagesEnabled(configValue.Value.(bool)); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetChatJoinMessagesEnabled(configValue.Value.(bool)); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -745,7 +779,8 @@ func SetHideViewerCount(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetHideViewerCount(configValue.Value.(bool)); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetHideViewerCount(configValue.Value.(bool)); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -765,7 +800,8 @@ func SetDisableSearchIndexing(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetDisableSearchIndexing(configValue.Value.(bool)); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetDisableSearchIndexing(configValue.Value.(bool)); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -787,7 +823,8 @@ func SetVideoServingEndpoint(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetVideoServingEndpoint(value); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetVideoServingEndpoint(value); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -806,7 +843,8 @@ func SetChatSpamProtectionEnabled(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetChatSpamProtectionEnabled(configValue.Value.(bool)); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetChatSpamProtectionEnabled(configValue.Value.(bool)); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -824,7 +862,8 @@ func SetChatSlurFilterEnabled(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetChatSlurFilterEnabled(configValue.Value.(bool)); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetChatSlurFilterEnabled(configValue.Value.(bool)); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -900,7 +939,8 @@ func SetStreamKeys(w http.ResponseWriter, r *http.Request) { } } - if err := data.SetStreamKeys(streamKeys.Value); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetStreamKeys(streamKeys.Value); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } diff --git a/webserver/handlers/admin/federation.go b/webserver/handlers/admin/federation.go index bd739a817..fc8aa3d73 100644 --- a/webserver/handlers/admin/federation.go +++ b/webserver/handlers/admin/federation.go @@ -6,7 +6,7 @@ import ( "github.com/owncast/owncast/activitypub" "github.com/owncast/owncast/activitypub/outbox" "github.com/owncast/owncast/activitypub/persistence" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" webutils "github.com/owncast/owncast/webserver/utils" ) @@ -46,7 +46,9 @@ func SetFederationEnabled(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetFederationEnabled(configValue.Value.(bool)); err != nil { + configRepository := configrepository.Get() + + if err := configRepository.SetFederationEnabled(configValue.Value.(bool)); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -64,7 +66,9 @@ func SetFederationActivityPrivate(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetFederationIsPrivate(configValue.Value.(bool)); err != nil { + configRepository := configrepository.Get() + + if err := configRepository.SetFederationIsPrivate(configValue.Value.(bool)); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -89,7 +93,8 @@ func SetFederationShowEngagement(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetFederationShowEngagement(configValue.Value.(bool)); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetFederationShowEngagement(configValue.Value.(bool)); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -107,7 +112,8 @@ func SetFederationUsername(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetFederationUsername(configValue.Value.(string)); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetFederationUsername(configValue.Value.(string)); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -126,7 +132,8 @@ func SetFederationGoLiveMessage(w http.ResponseWriter, r *http.Request) { return } - if err := data.SetFederationGoLiveMessage(configValue.Value.(string)); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetFederationGoLiveMessage(configValue.Value.(string)); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } @@ -151,7 +158,8 @@ func SetFederationBlockDomains(w http.ResponseWriter, r *http.Request) { domainStrings = append(domainStrings, domain.Value.(string)) } - if err := data.SetBlockedFederatedDomains(domainStrings); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetBlockedFederatedDomains(domainStrings); err != nil { webutils.WriteSimpleResponse(w, false, err.Error()) return } diff --git a/webserver/handlers/admin/followers.go b/webserver/handlers/admin/followers.go index 2bea8e9cb..cc28d230e 100644 --- a/webserver/handlers/admin/followers.go +++ b/webserver/handlers/admin/followers.go @@ -6,7 +6,7 @@ import ( "github.com/owncast/owncast/activitypub/persistence" "github.com/owncast/owncast/activitypub/requests" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/webserver/handlers/generated" webutils "github.com/owncast/owncast/webserver/utils" ) @@ -36,7 +36,8 @@ func ApproveFollower(w http.ResponseWriter, r *http.Request) { return } - localAccountName := data.GetDefaultFederationUsername() + configRepository := configrepository.Get() + localAccountName := configRepository.GetDefaultFederationUsername() followRequest, err := persistence.GetFollower(*approval.ActorIRI) if err != nil { diff --git a/webserver/handlers/admin/notifications.go b/webserver/handlers/admin/notifications.go index b7f29c413..6ba893127 100644 --- a/webserver/handlers/admin/notifications.go +++ b/webserver/handlers/admin/notifications.go @@ -4,8 +4,8 @@ import ( "encoding/json" "net/http" - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/persistence/configrepository" webutils "github.com/owncast/owncast/webserver/utils" ) @@ -19,6 +19,8 @@ func SetDiscordNotificationConfiguration(w http.ResponseWriter, r *http.Request) Value models.DiscordConfiguration `json:"value"` } + configRepository := configrepository.Get() + decoder := json.NewDecoder(r.Body) var config request if err := decoder.Decode(&config); err != nil { @@ -26,7 +28,7 @@ func SetDiscordNotificationConfiguration(w http.ResponseWriter, r *http.Request) return } - if err := data.SetDiscordConfig(config.Value); err != nil { + if err := configRepository.SetDiscordConfig(config.Value); err != nil { webutils.WriteSimpleResponse(w, false, "unable to update discord config with provided values") return } @@ -44,6 +46,7 @@ func SetBrowserNotificationConfiguration(w http.ResponseWriter, r *http.Request) Value models.BrowserNotificationConfiguration `json:"value"` } + configRepository := configrepository.Get() decoder := json.NewDecoder(r.Body) var config request if err := decoder.Decode(&config); err != nil { @@ -51,7 +54,7 @@ func SetBrowserNotificationConfiguration(w http.ResponseWriter, r *http.Request) return } - if err := data.SetBrowserPushConfig(config.Value); err != nil { + if err := configRepository.SetBrowserPushConfig(config.Value); err != nil { webutils.WriteSimpleResponse(w, false, "unable to update browser push config with provided values") return } diff --git a/webserver/handlers/admin/serverConfig.go b/webserver/handlers/admin/serverConfig.go index 880905888..3c4e9f717 100644 --- a/webserver/handlers/admin/serverConfig.go +++ b/webserver/handlers/admin/serverConfig.go @@ -5,9 +5,9 @@ import ( "net/http" "github.com/owncast/owncast/config" - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/core/transcoder" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/utils" "github.com/owncast/owncast/webserver/router/middleware" log "github.com/sirupsen/logrus" @@ -15,12 +15,13 @@ import ( // GetServerConfig gets the config details of the server. func GetServerConfig(w http.ResponseWriter, r *http.Request) { - ffmpeg := utils.ValidatedFfmpegPath(data.GetFfMpegPath()) - usernameBlocklist := data.GetForbiddenUsernameList() - usernameSuggestions := data.GetSuggestedUsernamesList() + configRepository := configrepository.Get() + ffmpeg := utils.ValidatedFfmpegPath(configRepository.GetFfMpegPath()) + usernameBlocklist := configRepository.GetForbiddenUsernameList() + usernameSuggestions := configRepository.GetSuggestedUsernamesList() videoQualityVariants := make([]models.StreamOutputVariant, 0) - for _, variant := range data.GetStreamOutputVariants() { + for _, variant := range configRepository.GetStreamOutputVariants() { videoQualityVariants = append(videoQualityVariants, models.StreamOutputVariant{ Name: variant.GetName(), IsAudioPassthrough: variant.GetIsAudioPassthrough(), @@ -35,61 +36,61 @@ func GetServerConfig(w http.ResponseWriter, r *http.Request) { } response := serverConfigAdminResponse{ InstanceDetails: webConfigResponse{ - Name: data.GetServerName(), - Summary: data.GetServerSummary(), - Tags: data.GetServerMetadataTags(), - ExtraPageContent: data.GetExtraPageBodyContent(), - StreamTitle: data.GetStreamTitle(), - WelcomeMessage: data.GetServerWelcomeMessage(), - OfflineMessage: data.GetCustomOfflineMessage(), - Logo: data.GetLogoPath(), - SocialHandles: data.GetSocialHandles(), - NSFW: data.GetNSFW(), - CustomStyles: data.GetCustomStyles(), - CustomJavascript: data.GetCustomJavascript(), - AppearanceVariables: data.GetCustomColorVariableValues(), + Name: configRepository.GetServerName(), + Summary: configRepository.GetServerSummary(), + Tags: configRepository.GetServerMetadataTags(), + ExtraPageContent: configRepository.GetExtraPageBodyContent(), + StreamTitle: configRepository.GetStreamTitle(), + WelcomeMessage: configRepository.GetServerWelcomeMessage(), + OfflineMessage: configRepository.GetCustomOfflineMessage(), + Logo: configRepository.GetLogoPath(), + SocialHandles: configRepository.GetSocialHandles(), + NSFW: configRepository.GetNSFW(), + CustomStyles: configRepository.GetCustomStyles(), + CustomJavascript: configRepository.GetCustomJavascript(), + AppearanceVariables: configRepository.GetCustomColorVariableValues(), }, FFmpegPath: ffmpeg, - AdminPassword: data.GetAdminPassword(), - StreamKeys: data.GetStreamKeys(), + AdminPassword: configRepository.GetAdminPassword(), + StreamKeys: configRepository.GetStreamKeys(), StreamKeyOverridden: config.TemporaryStreamKey != "", WebServerPort: config.WebServerPort, WebServerIP: config.WebServerIP, - RTMPServerPort: data.GetRTMPPortNumber(), - ChatDisabled: data.GetChatDisabled(), - ChatJoinMessagesEnabled: data.GetChatJoinPartMessagesEnabled(), - SocketHostOverride: data.GetWebsocketOverrideHost(), - VideoServingEndpoint: data.GetVideoServingEndpoint(), - ChatEstablishedUserMode: data.GetChatEstbalishedUsersOnlyMode(), - ChatSpamProtectionEnabled: data.GetChatSpamProtectionEnabled(), - ChatSlurFilterEnabled: data.GetChatSlurFilterEnabled(), - HideViewerCount: data.GetHideViewerCount(), - DisableSearchIndexing: data.GetDisableSearchIndexing(), + RTMPServerPort: configRepository.GetRTMPPortNumber(), + ChatDisabled: configRepository.GetChatDisabled(), + ChatJoinMessagesEnabled: configRepository.GetChatJoinPartMessagesEnabled(), + SocketHostOverride: configRepository.GetWebsocketOverrideHost(), + VideoServingEndpoint: configRepository.GetVideoServingEndpoint(), + ChatEstablishedUserMode: configRepository.GetChatEstbalishedUsersOnlyMode(), + ChatSpamProtectionEnabled: configRepository.GetChatSpamProtectionEnabled(), + ChatSlurFilterEnabled: configRepository.GetChatSlurFilterEnabled(), + HideViewerCount: configRepository.GetHideViewerCount(), + DisableSearchIndexing: configRepository.GetDisableSearchIndexing(), VideoSettings: videoSettings{ VideoQualityVariants: videoQualityVariants, - LatencyLevel: data.GetStreamLatencyLevel().Level, + LatencyLevel: configRepository.GetStreamLatencyLevel().Level, }, YP: yp{ - Enabled: data.GetDirectoryEnabled(), - InstanceURL: data.GetServerURL(), + Enabled: configRepository.GetDirectoryEnabled(), + InstanceURL: configRepository.GetServerURL(), }, - S3: data.GetS3Config(), - ExternalActions: data.GetExternalActions(), + S3: configRepository.GetS3Config(), + ExternalActions: configRepository.GetExternalActions(), SupportedCodecs: transcoder.GetCodecs(ffmpeg), - VideoCodec: data.GetVideoCodec(), + VideoCodec: configRepository.GetVideoCodec(), ForbiddenUsernames: usernameBlocklist, SuggestedUsernames: usernameSuggestions, Federation: federationConfigResponse{ - Enabled: data.GetFederationEnabled(), - IsPrivate: data.GetFederationIsPrivate(), - Username: data.GetFederationUsername(), - GoLiveMessage: data.GetFederationGoLiveMessage(), - ShowEngagement: data.GetFederationShowEngagement(), - BlockedDomains: data.GetBlockedFederatedDomains(), + Enabled: configRepository.GetFederationEnabled(), + IsPrivate: configRepository.GetFederationIsPrivate(), + Username: configRepository.GetFederationUsername(), + GoLiveMessage: configRepository.GetFederationGoLiveMessage(), + ShowEngagement: configRepository.GetFederationShowEngagement(), + BlockedDomains: configRepository.GetBlockedFederatedDomains(), }, Notifications: notificationsConfigResponse{ - Discord: data.GetDiscordConfig(), - Browser: data.GetBrowserPushConfig(), + Discord: configRepository.GetDiscordConfig(), + Browser: configRepository.GetBrowserPushConfig(), }, } diff --git a/webserver/handlers/admin/status.go b/webserver/handlers/admin/status.go index 55769288a..e8cf205a9 100644 --- a/webserver/handlers/admin/status.go +++ b/webserver/handlers/admin/status.go @@ -5,15 +5,17 @@ import ( "net/http" "github.com/owncast/owncast/core" - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/metrics" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/webserver/router/middleware" log "github.com/sirupsen/logrus" ) // Status gets the details of the inbound broadcaster. func Status(w http.ResponseWriter, r *http.Request) { + configRepository := configrepository.Get() + broadcaster := core.GetBroadcaster() status := core.GetStatus() currentBroadcast := core.GetCurrentBroadcast() @@ -27,7 +29,7 @@ func Status(w http.ResponseWriter, r *http.Request) { OverallPeakViewerCount: status.OverallMaxViewerCount, SessionPeakViewerCount: status.SessionMaxViewerCount, VersionNumber: status.VersionNumber, - StreamTitle: data.GetStreamTitle(), + StreamTitle: configRepository.GetStreamTitle(), } w.Header().Set("Content-Type", "application/json") diff --git a/webserver/handlers/admin/video.go b/webserver/handlers/admin/video.go index 65f7a69e1..18521fe11 100644 --- a/webserver/handlers/admin/video.go +++ b/webserver/handlers/admin/video.go @@ -5,8 +5,8 @@ import ( "net/http" "github.com/owncast/owncast/core" - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/metrics" + "github.com/owncast/owncast/persistence/configrepository" log "github.com/sirupsen/logrus" ) @@ -40,8 +40,9 @@ func GetVideoPlaybackMetrics(w http.ResponseWriter, r *http.Request) { availableBitrates = append(availableBitrates, variants.VideoBitrate) } } else { - segmentLength = data.GetStreamLatencyLevel().SecondsPerSegment - for _, variants := range data.GetStreamOutputVariants() { + configRepository := configrepository.Get() + segmentLength = configRepository.GetStreamLatencyLevel().SecondsPerSegment + for _, variants := range configRepository.GetStreamOutputVariants() { availableBitrates = append(availableBitrates, variants.VideoBitrate) } } diff --git a/webserver/handlers/admin/yp.go b/webserver/handlers/admin/yp.go index 530fbf00a..75b622922 100644 --- a/webserver/handlers/admin/yp.go +++ b/webserver/handlers/admin/yp.go @@ -3,7 +3,7 @@ package admin import ( "net/http" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" webutils "github.com/owncast/owncast/webserver/utils" log "github.com/sirupsen/logrus" ) @@ -11,7 +11,8 @@ import ( // ResetYPRegistration will clear the YP protocol registration key. func ResetYPRegistration(w http.ResponseWriter, r *http.Request) { log.Traceln("Resetting YP registration key") - if err := data.SetDirectoryRegistrationKey(""); err != nil { + configRepository := configrepository.Get() + if err := configRepository.SetDirectoryRegistrationKey(""); err != nil { log.Errorln(err) webutils.WriteSimpleResponse(w, false, err.Error()) return diff --git a/webserver/handlers/auth/fediverse/fediverse.go b/webserver/handlers/auth/fediverse/fediverse.go index b8e7026bd..b63bf333b 100644 --- a/webserver/handlers/auth/fediverse/fediverse.go +++ b/webserver/handlers/auth/fediverse/fediverse.go @@ -8,8 +8,8 @@ import ( "github.com/owncast/owncast/activitypub" fediverseauth "github.com/owncast/owncast/auth/fediverse" "github.com/owncast/owncast/core/chat" - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/persistence/userrepository" webutils "github.com/owncast/owncast/webserver/utils" log "github.com/sirupsen/logrus" @@ -39,7 +39,8 @@ func RegisterFediverseOTPRequest(u models.User, w http.ResponseWriter, r *http.R return } - msg := fmt.Sprintf("

One-time code from %s: %s. If you did not request this message please ignore or block.

", data.GetServerName(), reg.Code) + configRepository := configrepository.Get() + msg := fmt.Sprintf("

One-time code from %s: %s. If you did not request this message please ignore or block.

", configRepository.GetServerName(), reg.Code) if err := activitypub.SendDirectFederatedMessage(msg, reg.Account); err != nil { webutils.WriteSimpleResponse(w, false, "Could not send code to fediverse: "+err.Error()) return diff --git a/webserver/handlers/chat.go b/webserver/handlers/chat.go index a7112f8e3..a402d84ac 100644 --- a/webserver/handlers/chat.go +++ b/webserver/handlers/chat.go @@ -6,8 +6,8 @@ import ( "github.com/owncast/owncast/config" "github.com/owncast/owncast/core/chat" - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/persistence/userrepository" "github.com/owncast/owncast/utils" "github.com/owncast/owncast/webserver/handlers/generated" @@ -105,7 +105,8 @@ func RegisterAnonymousChatUser(w http.ResponseWriter, r *http.Request) { } func generateDisplayName() string { - suggestedUsernamesList := data.GetSuggestedUsernamesList() + configRepository := configrepository.Get() + suggestedUsernamesList := configRepository.GetSuggestedUsernamesList() minSuggestedUsernamePoolLength := 10 if len(suggestedUsernamesList) >= minSuggestedUsernamePoolLength { diff --git a/webserver/handlers/config.go b/webserver/handlers/config.go index d7487a954..a6efecfeb 100644 --- a/webserver/handlers/config.go +++ b/webserver/handlers/config.go @@ -8,8 +8,8 @@ import ( "github.com/owncast/owncast/activitypub" "github.com/owncast/owncast/config" - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/utils" "github.com/owncast/owncast/webserver/router/middleware" webutils "github.com/owncast/owncast/webserver/utils" @@ -73,9 +73,10 @@ func GetWebConfig(w http.ResponseWriter, r *http.Request) { } func getConfigResponse() webConfigResponse { - pageContent := utils.RenderPageContentMarkdown(data.GetExtraPageBodyContent()) - offlineMessage := utils.RenderSimpleMarkdown(data.GetCustomOfflineMessage()) - socialHandles := data.GetSocialHandles() + configRepository := configrepository.Get() + pageContent := utils.RenderPageContentMarkdown(configRepository.GetExtraPageBodyContent()) + offlineMessage := utils.RenderSimpleMarkdown(configRepository.GetCustomOfflineMessage()) + socialHandles := configRepository.GetSocialHandles() for i, handle := range socialHandles { platform := models.GetSocialHandle(handle.Platform) if platform != nil { @@ -84,16 +85,16 @@ func getConfigResponse() webConfigResponse { } } - serverSummary := data.GetServerSummary() + serverSummary := configRepository.GetServerSummary() var federationResponse federationConfigResponse - federationEnabled := data.GetFederationEnabled() + federationEnabled := configRepository.GetFederationEnabled() followerCount, _ := activitypub.GetFollowerCount() if federationEnabled { - serverURLString := data.GetServerURL() + serverURLString := configRepository.GetServerURL() serverURL, _ := url.Parse(serverURLString) - account := fmt.Sprintf("%s@%s", data.GetDefaultFederationUsername(), serverURL.Host) + account := fmt.Sprintf("%s@%s", configRepository.GetDefaultFederationUsername(), serverURL.Host) federationResponse = federationConfigResponse{ Enabled: federationEnabled, FollowerCount: int(followerCount), @@ -101,8 +102,8 @@ func getConfigResponse() webConfigResponse { } } - browserPushEnabled := data.GetBrowserPushConfig().Enabled - browserPushPublicKey, err := data.GetBrowserPushPublicKey() + browserPushEnabled := configRepository.GetBrowserPushConfig().Enabled + browserPushPublicKey, err := configRepository.GetBrowserPushPublicKey() if err != nil { log.Errorln("unable to fetch browser push notifications public key", err) browserPushEnabled = false @@ -116,31 +117,31 @@ func getConfigResponse() webConfigResponse { } authenticationResponse := authenticationConfigResponse{ - IndieAuthEnabled: data.GetServerURL() != "", + IndieAuthEnabled: configRepository.GetServerURL() != "", } return webConfigResponse{ - Name: data.GetServerName(), + Name: configRepository.GetServerName(), Summary: serverSummary, OfflineMessage: offlineMessage, Logo: "/logo", - Tags: data.GetServerMetadataTags(), + Tags: configRepository.GetServerMetadataTags(), Version: config.GetReleaseString(), - NSFW: data.GetNSFW(), - SocketHostOverride: data.GetWebsocketOverrideHost(), + NSFW: configRepository.GetNSFW(), + SocketHostOverride: configRepository.GetWebsocketOverrideHost(), ExtraPageContent: pageContent, - StreamTitle: data.GetStreamTitle(), + StreamTitle: configRepository.GetStreamTitle(), SocialHandles: socialHandles, - ChatDisabled: data.GetChatDisabled(), - ChatSpamProtectionDisabled: data.GetChatSpamProtectionEnabled(), - ExternalActions: data.GetExternalActions(), - CustomStyles: data.GetCustomStyles(), + ChatDisabled: configRepository.GetChatDisabled(), + ChatSpamProtectionDisabled: configRepository.GetChatSpamProtectionEnabled(), + ExternalActions: configRepository.GetExternalActions(), + CustomStyles: configRepository.GetCustomStyles(), MaxSocketPayloadSize: config.MaxSocketPayloadSize, Federation: federationResponse, Notifications: notificationsResponse, Authentication: authenticationResponse, - AppearanceVariables: data.GetCustomColorVariableValues(), - HideViewerCount: data.GetHideViewerCount(), + AppearanceVariables: configRepository.GetCustomColorVariableValues(), + HideViewerCount: configRepository.GetHideViewerCount(), } } diff --git a/webserver/handlers/customJavascript.go b/webserver/handlers/customJavascript.go index d2c2da79a..d350de9f5 100644 --- a/webserver/handlers/customJavascript.go +++ b/webserver/handlers/customJavascript.go @@ -3,13 +3,14 @@ package handlers import ( "net/http" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" ) // ServeCustomJavascript will serve optional custom Javascript. func ServeCustomJavascript(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/javascript; charset=utf-8") - js := data.GetCustomJavascript() + configRepository := configrepository.Get() + js := configRepository.GetCustomJavascript() _, _ = w.Write([]byte(js)) } diff --git a/webserver/handlers/hls.go b/webserver/handlers/hls.go index bee58c95e..2ba4ec350 100644 --- a/webserver/handlers/hls.go +++ b/webserver/handlers/hls.go @@ -9,8 +9,8 @@ import ( "github.com/owncast/owncast/config" "github.com/owncast/owncast/core" - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/utils" "github.com/owncast/owncast/webserver/router/middleware" ) @@ -29,7 +29,8 @@ func HandleHLSRequest(w http.ResponseWriter, r *http.Request) { // If using external storage then only allow requests for the // master playlist at stream.m3u8, no variants or segments. - if data.GetS3Config().Enabled && relativePath != "stream.m3u8" { + configRepository := configrepository.Get() + if configRepository.GetS3Config().Enabled && relativePath != "stream.m3u8" { w.WriteHeader(http.StatusNotFound) return } diff --git a/webserver/handlers/index.go b/webserver/handlers/index.go index 29f47508c..a849650ae 100644 --- a/webserver/handlers/index.go +++ b/webserver/handlers/index.go @@ -13,8 +13,8 @@ import ( "github.com/owncast/owncast/config" "github.com/owncast/owncast/core" "github.com/owncast/owncast/core/cache" - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/static" "github.com/owncast/owncast/utils" "github.com/owncast/owncast/webserver/router/middleware" @@ -86,11 +86,12 @@ func renderIndexHtml(w http.ResponseWriter, nonce string) { return } + configRepository := configrepository.Get() content := serverSideContent{ - Name: data.GetServerName(), - Summary: data.GetServerSummary(), - RequestedURL: fmt.Sprintf("%s%s", data.GetServerURL(), "/"), - TagsString: strings.Join(data.GetServerMetadataTags(), ","), + Name: configRepository.GetServerName(), + Summary: configRepository.GetServerSummary(), + RequestedURL: fmt.Sprintf("%s%s", configRepository.GetServerURL(), "/"), + TagsString: strings.Join(configRepository.GetServerMetadataTags(), ","), ThumbnailURL: "thumbnail.jpg", Thumbnail: "thumbnail.jpg", Image: "logo/external", @@ -145,8 +146,8 @@ func handleScraperMetadataPage(w http.ResponseWriter, r *http.Request) { } scheme := "http" - - if siteURL := data.GetServerURL(); siteURL != "" { + configRepository := configrepository.Get() + if siteURL := configRepository.GetServerURL(); siteURL != "" { if parsed, err := url.Parse(siteURL); err == nil && parsed.Scheme != "" { scheme = parsed.Scheme } @@ -177,16 +178,16 @@ func handleScraperMetadataPage(w http.ResponseWriter, r *http.Request) { thumbnailURL = imageURL.String() } - tagsString := strings.Join(data.GetServerMetadataTags(), ",") + tagsString := strings.Join(configRepository.GetServerMetadataTags(), ",") metadata := MetadataPage{ - Name: data.GetServerName(), + Name: configRepository.GetServerName(), RequestedURL: fullURL.String(), Image: imageURL.String(), - Summary: data.GetServerSummary(), + Summary: configRepository.GetServerSummary(), Thumbnail: thumbnailURL, TagsString: tagsString, - Tags: data.GetServerMetadataTags(), - SocialHandles: data.GetSocialHandles(), + Tags: configRepository.GetServerMetadataTags(), + SocialHandles: configRepository.GetSocialHandles(), } // Cache the rendered HTML diff --git a/webserver/handlers/logo.go b/webserver/handlers/logo.go index 6dff20201..2775027bf 100644 --- a/webserver/handlers/logo.go +++ b/webserver/handlers/logo.go @@ -7,7 +7,7 @@ import ( "strconv" "github.com/owncast/owncast/config" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/static" "github.com/owncast/owncast/utils" log "github.com/sirupsen/logrus" @@ -17,7 +17,8 @@ var _hasWarnedSVGLogo = false // GetLogo will return the logo image as a response. func GetLogo(w http.ResponseWriter, r *http.Request) { - imageFilename := data.GetLogoPath() + configRepository := configrepository.Get() + imageFilename := configRepository.GetLogoPath() if imageFilename == "" { returnDefault(w) return @@ -47,7 +48,8 @@ func GetLogo(w http.ResponseWriter, r *http.Request) { // Used for sharing to external social networks that generally // don't support SVG. func GetCompatibleLogo(w http.ResponseWriter, r *http.Request) { - imageFilename := data.GetLogoPath() + configRepository := configrepository.Get() + imageFilename := configRepository.GetLogoPath() // If the logo image is not a SVG then we can return it // without any problems. diff --git a/webserver/handlers/remoteFollow.go b/webserver/handlers/remoteFollow.go index 5dcd63ac1..a6a4b3aec 100644 --- a/webserver/handlers/remoteFollow.go +++ b/webserver/handlers/remoteFollow.go @@ -8,7 +8,7 @@ import ( "strings" "github.com/owncast/owncast/activitypub/webfinger" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/webserver/handlers/generated" webutils "github.com/owncast/owncast/webserver/utils" ) @@ -31,8 +31,9 @@ func RemoteFollow(w http.ResponseWriter, r *http.Request) { return } - localActorPath, _ := url.Parse(data.GetServerURL()) - localActorPath.Path = fmt.Sprintf("/federation/user/%s", data.GetDefaultFederationUsername()) + configRepository := configrepository.Get() + localActorPath, _ := url.Parse(configRepository.GetServerURL()) + localActorPath.Path = fmt.Sprintf("/federation/user/%s", configRepository.GetDefaultFederationUsername()) var template string links, err := webfinger.GetWebfingerLinks(*request.Account) if err != nil { diff --git a/webserver/handlers/robots.go b/webserver/handlers/robots.go index 69b702cf3..95d1155fd 100644 --- a/webserver/handlers/robots.go +++ b/webserver/handlers/robots.go @@ -4,7 +4,7 @@ import ( "net/http" "strings" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" ) // GetRobotsDotTxt returns the contents of our robots.txt. @@ -16,7 +16,8 @@ func GetRobotsDotTxt(w http.ResponseWriter, r *http.Request) { "Disallow: /api", } - if data.GetDisableSearchIndexing() { + configRepository := configrepository.Get() + if configRepository.GetDisableSearchIndexing() { contents = append(contents, "Disallow: /") } diff --git a/webserver/handlers/status.go b/webserver/handlers/status.go index 63853bc98..037bd07f0 100644 --- a/webserver/handlers/status.go +++ b/webserver/handlers/status.go @@ -6,7 +6,7 @@ import ( "time" "github.com/owncast/owncast/core" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/utils" "github.com/owncast/owncast/webserver/router/middleware" webutils "github.com/owncast/owncast/webserver/utils" @@ -34,7 +34,8 @@ func getStatusResponse() webStatusResponse { VersionNumber: status.VersionNumber, StreamTitle: status.StreamTitle, } - if !data.GetHideViewerCount() { + configRepository := configrepository.Get() + if !configRepository.GetHideViewerCount() { response.ViewerCount = status.ViewerCount } return response diff --git a/webserver/handlers/video.go b/webserver/handlers/video.go index 6e862319c..fdfe0db4a 100644 --- a/webserver/handlers/video.go +++ b/webserver/handlers/video.go @@ -4,7 +4,7 @@ import ( "net/http" "sort" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" webutils "github.com/owncast/owncast/webserver/utils" ) @@ -22,7 +22,8 @@ type variantsResponse struct { // GetVideoStreamOutputVariants will return the video variants available. func GetVideoStreamOutputVariants(w http.ResponseWriter, r *http.Request) { - outputVariants := data.GetStreamOutputVariants() + configRepository := configrepository.Get() + outputVariants := configRepository.GetStreamOutputVariants() streamSortVariants := make([]variantsSort, len(outputVariants)) for i, variant := range outputVariants { diff --git a/webserver/router/middleware/activityPub.go b/webserver/router/middleware/activityPub.go index 51fdc09ca..2558619e2 100644 --- a/webserver/router/middleware/activityPub.go +++ b/webserver/router/middleware/activityPub.go @@ -4,15 +4,16 @@ import ( "net/http" "strings" - "github.com/owncast/owncast/core/data" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/utils" ) // RequireActivityPubOrRedirect will validate the requested content types and // redirect to the main Owncast page if it doesn't match. func RequireActivityPubOrRedirect(handler http.HandlerFunc) http.HandlerFunc { + configRepository := configrepository.Get() return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if !data.GetFederationEnabled() { + if !configRepository.GetFederationEnabled() { w.WriteHeader(http.StatusMethodNotAllowed) return } diff --git a/webserver/router/middleware/auth.go b/webserver/router/middleware/auth.go index 4fe3521b7..c0c5045e5 100644 --- a/webserver/router/middleware/auth.go +++ b/webserver/router/middleware/auth.go @@ -5,8 +5,9 @@ import ( "net/http" "strings" - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/persistence/authrepository" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/persistence/userrepository" "github.com/owncast/owncast/utils" log "github.com/sirupsen/logrus" @@ -21,9 +22,10 @@ type UserAccessTokenHandlerFunc func(models.User, http.ResponseWriter, *http.Req // RequireAdminAuth wraps a handler requiring HTTP basic auth for it using the given // the stream key as the password and and a hardcoded "admin" for username. func RequireAdminAuth(handler http.HandlerFunc) http.HandlerFunc { + configRepository := configrepository.Get() return func(w http.ResponseWriter, r *http.Request) { username := "admin" - password := data.GetAdminPassword() + password := configRepository.GetAdminPassword() realm := "Owncast Authenticated Request" // Alow CORS only for localhost:3000 to support Owncast development. @@ -102,6 +104,7 @@ func RequireExternalAPIAccessToken(scope string, handler ExternalAccessTokenHand // RequireUserAccessToken will validate a provided user's access token and make sure the associated user is enabled. // Not to be used for validating 3rd party access. func RequireUserAccessToken(handler UserAccessTokenHandlerFunc) http.HandlerFunc { + authRepository := authrepository.Get() return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { accessToken := r.URL.Query().Get("accessToken") if accessToken == "" { @@ -111,7 +114,7 @@ func RequireUserAccessToken(handler UserAccessTokenHandlerFunc) http.HandlerFunc ipAddress := utils.GetIPAddressFromRequest(r) // Check if this client's IP address is banned. - if blocked, err := data.IsIPAddressBanned(ipAddress); blocked { + if blocked, err := authRepository.IsIPAddressBanned(ipAddress); blocked { log.Debugln("Client ip address has been blocked. Rejecting.") accessDenied(w) return diff --git a/yp/api.go b/yp/api.go index ccfcb97e8..cfffb9e4e 100644 --- a/yp/api.go +++ b/yp/api.go @@ -4,8 +4,8 @@ import ( "encoding/json" "net/http" - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/persistence/configrepository" "github.com/owncast/owncast/utils" log "github.com/sirupsen/logrus" ) @@ -28,28 +28,29 @@ type ypDetailsResponse struct { // GetYPResponse gets the status of the server for YP purposes. func GetYPResponse(w http.ResponseWriter, r *http.Request) { - if !data.GetDirectoryEnabled() { + configRepository := configrepository.Get() + if !configRepository.GetDirectoryEnabled() { w.WriteHeader(http.StatusNotFound) return } status := getStatus() - streamTitle := data.GetStreamTitle() + streamTitle := configRepository.GetStreamTitle() response := ypDetailsResponse{ - Name: data.GetServerName(), - Description: data.GetServerSummary(), + Name: configRepository.GetServerName(), + Description: configRepository.GetServerSummary(), StreamTitle: streamTitle, Logo: "/logo", - NSFW: data.GetNSFW(), - Tags: data.GetServerMetadataTags(), + NSFW: configRepository.GetNSFW(), + Tags: configRepository.GetServerMetadataTags(), Online: status.Online, ViewerCount: status.ViewerCount, OverallMaxViewerCount: status.OverallMaxViewerCount, SessionMaxViewerCount: status.SessionMaxViewerCount, LastConnectTime: status.LastConnectTime, - Social: data.GetSocialHandles(), + Social: configRepository.GetSocialHandles(), } w.Header().Set("Content-Type", "application/json") diff --git a/yp/yp.go b/yp/yp.go index 055dd1ec8..1221ed908 100644 --- a/yp/yp.go +++ b/yp/yp.go @@ -9,8 +9,8 @@ import ( "time" "github.com/owncast/owncast/config" - "github.com/owncast/owncast/core/data" "github.com/owncast/owncast/models" + "github.com/owncast/owncast/persistence/configrepository" log "github.com/sirupsen/logrus" ) @@ -61,7 +61,9 @@ func (yp *YP) Stop() { } func (yp *YP) ping() { - if !data.GetDirectoryEnabled() { + configRepository := configrepository.Get() + + if !configRepository.GetDirectoryEnabled() { return } @@ -71,7 +73,7 @@ func (yp *YP) ping() { return } - myInstanceURL := data.GetServerURL() + myInstanceURL := configRepository.GetServerURL() if myInstanceURL == "" { log.Warnln("Server URL not set in the configuration. Directory access is disabled until this is set.") return @@ -85,9 +87,9 @@ func (yp *YP) ping() { return } - key := data.GetDirectoryRegistrationKey() + key := configRepository.GetDirectoryRegistrationKey() - log.Traceln("Pinging YP as: ", data.GetServerName(), "with key", key) + log.Traceln("Pinging YP as: ", configRepository.GetServerName(), "with key", key) request := ypPingRequest{ Key: key, @@ -129,7 +131,7 @@ func (yp *YP) ping() { _inErrorState = false if pingResponse.Key != key { - if err := data.SetDirectoryRegistrationKey(key); err != nil { + if err := configRepository.SetDirectoryRegistrationKey(key); err != nil { log.Errorln("unable to save directory key:", err) } }