This commit is contained in:
Gabe Kangas 2024-09-22 16:27:06 -07:00
parent cbd984282b
commit 15c687e3be
77 changed files with 1854 additions and 1483 deletions

View File

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

View File

@ -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)

View File

@ -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")
}

View File

@ -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 {
@ -56,8 +57,10 @@ func TestMain(m *testing.M) {
panic(err)
}
configRepository := configrepository.Get()
data.SetupPersistence(dbFile.Name())
data.SetServerURL("https://my.cool.site.biz")
configRepository.SetServerURL("https://my.cool.site.biz")
m.Run()
}

View File

@ -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"
}

View File

@ -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

View File

@ -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")
}

View File

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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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) {

View File

@ -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 {
@ -48,22 +49,26 @@ func makeFakePerson() vocab.ActivityStreamsPerson {
}
func TestMain(m *testing.M) {
configRepository := configrepository.Get()
data.SetupPersistence(":memory:")
data.SetServerURL("https://my.cool.site.biz")
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
}

View File

@ -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("<p>%s</p>", title)
}
textContent = fmt.Sprintf("<p>%s</p>%s<p>%s</p><p><a href=\"%s\">%s</a></p>", textContent, streamTitle, tagsString, data.GetServerURL(), data.GetServerURL())
textContent = fmt.Sprintf("<p>%s</p>%s<p>%s</p><p><a href=\"%s\">%s</a></p>", 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)

View File

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

View File

@ -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")
}

View File

@ -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()),
},
}

View File

@ -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(),
},
})

View File

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

View File

@ -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)

View File

@ -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(),
},
}
}

View File

@ -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(),
},
}
}

View File

@ -16,6 +16,7 @@ import (
"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/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,7 +203,9 @@ 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()
if configRepository.GetChatDisabled() {
_, _ = w.Write([]byte(events.ChatDisabled))
return
}
@ -377,12 +384,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 +418,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 +431,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{

View File

@ -10,12 +10,12 @@ import (
"github.com/owncast/owncast/auth"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/chat"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/core/rtmp"
"github.com/owncast/owncast/core/transcoder"
"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 +34,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
}
@ -56,8 +56,8 @@ func Start() error {
log.Errorln("storage error", err)
}
tables.SetupUsers(data.GetDatastore().DB)
auth.Setup(data.GetDatastore())
tables.SetupUsers(configRepository.GetDatastore().DB)
auth.Setup(configRepository.GetDatastore())
fileWriter.SetupFileWriterReceiverService(&handler)
@ -75,14 +75,14 @@ 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)
}
webhooks.SetupWebhooks(GetStatus)
notifications.Setup(data.GetStore())
notifications.Setup(configRepository.GetStore())
return nil
}
@ -113,7 +113,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 +131,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 {

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -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)
}
@ -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{&testMap, testKey}); err != nil {
t.Error(err)
}

View File

@ -11,6 +11,7 @@ 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"
)
@ -46,10 +47,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 +61,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 +74,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 +94,6 @@ func (ds *Datastore) Save(e ConfigEntry) error {
return err
}
_, err = stmt.Exec(e.Key, dataGob.Bytes())
if err != nil {
return err
}
@ -108,6 +108,15 @@ func (ds *Datastore) Save(e ConfigEntry) error {
return nil
}
// 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
}
// Setup will create the datastore table and perform initial initialization.
func (ds *Datastore) Setup() {
ds.cache = make(map[string][]byte)

View File

@ -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, 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, 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, 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, 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, key}
return ds.Save(configEntry)
}

View File

@ -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 != "" {

View File

@ -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,
}

View File

@ -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(),
}
}

View File

@ -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()

View File

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

View File

@ -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)

View File

@ -10,12 +10,12 @@ import (
"github.com/owncast/owncast/activitypub"
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/chat"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/core/rtmp"
"github.com/owncast/owncast/core/transcoder"
"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 +39,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 +71,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 +178,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)
@ -185,7 +188,7 @@ func startLiveStreamNotificationsTimer() context.CancelFunc {
}
// Send notification to those who have registered for them.
if notifier, err := notifications.New(data.GetDatastore()); err != nil {
if notifier, err := notifications.New(configRepository.GetDatastore()); err != nil {
log.Errorln(err)
} else {
notifier.Notify()

View File

@ -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/

View File

@ -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"
)
@ -273,15 +273,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

View File

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

View File

@ -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

View File

@ -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"
}

View File

@ -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

View File

@ -5,9 +5,9 @@ import (
"fmt"
"github.com/owncast/owncast/config"
"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,20 +18,21 @@ import (
// Notifier is an instance of the live stream notifier.
type Notifier struct {
datastore *data.Datastore
browser *browser.Browser
discord *discord.Discord
datastore *configrepository.Datastore
browser *browser.Browser
discord *discord.Discord
configRepository configrepository.ConfigRepository
}
// Setup will perform any pre-use setup for the notifier.
func Setup(datastore *data.Datastore) {
func Setup(datastore *configrepository.Datastore) {
tables.CreateNotificationsTable(datastore.DB)
initializeBrowserPushIfNeeded()
}
func initializeBrowserPushIfNeeded() {
pubKey, _ := data.GetBrowserPushPublicKey()
privKey, _ := data.GetBrowserPushPrivateKey()
pubKey, _ := configRepository.GetBrowserPushPublicKey()
privKey, _ := configRepository.GetBrowserPushPrivateKey()
// We need browser push keys so people can register for pushes.
if pubKey == "" || privKey == "" {
@ -40,26 +41,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) {
func New(datastore *configRepository.Datastore) (*Notifier, error) {
notifier := Notifier{
datastore: datastore,
datastore: datastore,
configRepository: configrepository.Get(),
}
if err := notifier.setupBrowserPush(); err != nil {
@ -73,13 +75,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 +101,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 +114,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 +134,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,7 +160,9 @@ 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{
configRepository := configrepository.Get()
return configRepository.GetDatastore().GetQueries().RemoveNotificationDestinationForChannel(context.Background(), db.RemoveNotificationDestinationForChannelParams{
Channel: channel,
Destination: destination,
})
@ -167,7 +171,9 @@ func RemoveNotificationForChannel(channel, destination string) error {
// GetNotificationDestinationsForChannel will return a collection of
// destinations to notify for a given channel.
func GetNotificationDestinationsForChannel(channel string) ([]string, error) {
result, err := data.GetDatastore().GetQueries().GetNotificationDestinationsForChannel(context.Background(), channel)
configRepository := configrepository.Get()
result, err := configRepository.GetDatastore().GetQueries().GetNotificationDestinationsForChannel(context.Background(), channel)
if err != nil {
return nil, errors.Wrap(err, "unable to query notification destinations for channel "+channel)
}
@ -177,7 +183,9 @@ func GetNotificationDestinationsForChannel(channel string) ([]string, error) {
// AddNotification saves a new user notification destination.
func AddNotification(channel, destination string) error {
return data.GetDatastore().GetQueries().AddNotification(context.Background(), db.AddNotificationParams{
configRepository := configrepository.Get()
return configRepository.GetDatastore().GetQueries().AddNotification(context.Background(), db.AddNotificationParams{
Channel: channel,
Destination: destination,
})

View File

@ -0,0 +1,13 @@
package configrepository
// GetFederatedInboxMap is a mapping between account names and their outbox.
func (r SqlConfigRepository) GetFederatedInboxMap() map[string]string {
return map[string]string{
r.GetDefaultFederationUsername(): r.GetDefaultFederationUsername(),
}
}
// GetDefaultFederationUsername will return the username used for sending federation activities.
func (r *SqlConfigRepository) GetDefaultFederationUsername() string {
return r.GetFederationUsername()
}

View File

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

View File

@ -0,0 +1,62 @@
package configrepository
const (
extraContentKey = "extra_page_content"
streamTitleKey = "stream_title"
adminPasswordKey = "admin_password_key"
logoPathKey = "logo_path"
logoUniquenessKey = "logo_uniqueness"
serverSummaryKey = "server_summary"
serverWelcomeMessageKey = "server_welcome_message"
serverNameKey = "server_name"
serverURLKey = "server_url"
httpPortNumberKey = "http_port_number"
httpListenAddressKey = "http_listen_address"
websocketHostOverrideKey = "websocket_host_override"
rtmpPortNumberKey = "rtmp_port_number"
serverMetadataTagsKey = "server_metadata_tags"
directoryEnabledKey = "directory_enabled"
directoryRegistrationKeyKey = "directory_registration_key"
socialHandlesKey = "social_handles"
peakViewersSessionKey = "peak_viewers_session"
peakViewersOverallKey = "peak_viewers_overall"
lastDisconnectTimeKey = "last_disconnect_time"
ffmpegPathKey = "ffmpeg_path"
nsfwKey = "nsfw"
s3StorageConfigKey = "s3_storage_config"
videoLatencyLevel = "video_latency_level"
videoStreamOutputVariantsKey = "video_stream_output_variants"
chatDisabledKey = "chat_disabled"
externalActionsKey = "external_actions"
customStylesKey = "custom_styles"
customJavascriptKey = "custom_javascript"
videoCodecKey = "video_codec"
blockedUsernamesKey = "blocked_usernames"
publicKeyKey = "public_key"
privateKeyKey = "private_key"
serverInitDateKey = "server_init_date"
federationEnabledKey = "federation_enabled"
federationUsernameKey = "federation_username"
federationPrivateKey = "federation_private"
federationGoLiveMessageKey = "federation_go_live_message"
federationShowEngagementKey = "federation_show_engagement"
federationBlockedDomainsKey = "federation_blocked_domains"
suggestedUsernamesKey = "suggested_usernames"
chatJoinMessagesEnabledKey = "chat_join_messages_enabled"
chatEstablishedUsersOnlyModeKey = "chat_established_users_only_mode"
chatSpamProtectionEnabledKey = "chat_spam_protection_enabled"
chatSlurFilterEnabledKey = "chat_slur_filter_enabled"
notificationsEnabledKey = "notifications_enabled"
discordConfigurationKey = "discord_configuration"
browserPushConfigurationKey = "browser_push_configuration"
browserPushPublicKeyKey = "browser_push_public_key"
// nolint:gosec
browserPushPrivateKeyKey = "browser_push_private_key"
hasConfiguredInitialNotificationsKey = "has_configured_initial_notifications"
hideViewerCountKey = "hide_viewer_count"
customOfflineMessageKey = "custom_offline_message"
customColorVariableValuesKey = "custom_color_variable_values"
streamKeysKey = "stream_keys"
disableSearchIndexingKey = "disable_search_indexing"
videoServingEndpointKey = "video_serving_endpoint"
)

View File

@ -0,0 +1,129 @@
package configrepository
import (
"time"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/utils"
)
type ConfigRepository interface {
GetExtraPageBodyContent() string
SetExtraPageBodyContent(content string) error
GetStreamTitle() string
SetStreamTitle(title string) error
GetAdminPassword() string
SetAdminPassword(key string) error
GetLogoPath() string
SetLogoPath(logo string) error
SetLogoUniquenessString(uniqueness string) error
GetLogoUniquenessString() string
GetServerSummary() string
SetServerSummary(summary string) error
GetServerWelcomeMessage() string
SetServerWelcomeMessage(welcomeMessage string) error
GetServerName() string
SetServerName(name string) error
GetServerURL() string
SetServerURL(url string) error
GetHTTPPortNumber() int
SetWebsocketOverrideHost(host string) error
GetWebsocketOverrideHost() string
SetHTTPPortNumber(port float64) error
GetHTTPListenAddress() string
SetHTTPListenAddress(address string) error
GetRTMPPortNumber() int
SetRTMPPortNumber(port float64) error
GetServerMetadataTags() []string
SetServerMetadataTags(tags []string) error
GetDirectoryEnabled() bool
SetDirectoryEnabled(enabled bool) error
SetDirectoryRegistrationKey(key string) error
GetDirectoryRegistrationKey() string
GetSocialHandles() []models.SocialHandle
SetSocialHandles(socialHandles []models.SocialHandle) error
GetPeakSessionViewerCount() int
SetPeakSessionViewerCount(count int) error
GetPeakOverallViewerCount() int
SetPeakOverallViewerCount(count int) error
GetLastDisconnectTime() (*utils.NullTime, error)
SetLastDisconnectTime(disconnectTime time.Time) error
SetNSFW(isNSFW bool) error
GetNSFW() bool
SetFfmpegPath(path string) error
GetFfMpegPath() string
GetS3Config() models.S3
SetS3Config(config models.S3) error
GetStreamLatencyLevel() models.LatencyLevel
SetStreamLatencyLevel(level float64) error
GetStreamOutputVariants() []models.StreamOutputVariant
SetStreamOutputVariants(variants []models.StreamOutputVariant) error
SetChatDisabled(disabled bool) error
GetChatDisabled() bool
SetChatEstablishedUsersOnlyMode(enabled bool) error
GetChatEstbalishedUsersOnlyMode() bool
SetChatSpamProtectionEnabled(enabled bool) error
GetChatSpamProtectionEnabled() bool
SetChatSlurFilterEnabled(enabled bool) error
GetChatSlurFilterEnabled() bool
GetExternalActions() []models.ExternalAction
SetExternalActions(actions []models.ExternalAction) error
SetCustomStyles(styles string) error
GetCustomStyles() string
SetCustomJavascript(styles string) error
GetCustomJavascript() string
SetVideoCodec(codec string) error
GetVideoCodec() string
VerifySettings() error
FindHighestVideoQualityIndex(qualities []models.StreamOutputVariant) (int, bool)
GetForbiddenUsernameList() []string
SetForbiddenUsernameList(usernames []string) error
GetSuggestedUsernamesList() []string
SetSuggestedUsernamesList(usernames []string) error
GetServerInitTime() (*utils.NullTime, error)
SetServerInitTime(t time.Time) error
SetFederationEnabled(enabled bool) error
GetFederationEnabled() bool
SetFederationUsername(username string) error
GetFederationUsername() string
SetFederationGoLiveMessage(message string) error
GetFederationGoLiveMessage() string
SetFederationIsPrivate(isPrivate bool) error
GetFederationIsPrivate() bool
SetFederationShowEngagement(showEngagement bool) error
GetFederationShowEngagement() bool
SetBlockedFederatedDomains(domains []string) error
GetBlockedFederatedDomains() []string
SetChatJoinMessagesEnabled(enabled bool) error
GetChatJoinPartMessagesEnabled() bool
SetNotificationsEnabled(enabled bool) error
GetNotificationsEnabled() bool
GetDiscordConfig() models.DiscordConfiguration
SetDiscordConfig(config models.DiscordConfiguration) error
GetBrowserPushConfig() models.BrowserNotificationConfiguration
SetBrowserPushConfig(config models.BrowserNotificationConfiguration) error
SetBrowserPushPublicKey(key string) error
GetBrowserPushPublicKey() (string, error)
SetBrowserPushPrivateKey(key string) error
GetBrowserPushPrivateKey() (string, error)
SetHasPerformedInitialNotificationsConfig(hasConfigured bool) error
GetHasPerformedInitialNotificationsConfig() bool
GetHideViewerCount() bool
SetHideViewerCount(hide bool) error
GetCustomOfflineMessage() string
SetCustomOfflineMessage(message string) error
SetCustomColorVariableValues(variables map[string]string) error
GetCustomColorVariableValues() map[string]string
GetStreamKeys() []models.StreamKey
SetStreamKeys(actions []models.StreamKey) error
SetDisableSearchIndexing(disableSearchIndexing bool) error
GetDisableSearchIndexing() bool
GetVideoServingEndpoint() string
SetVideoServingEndpoint(message string) error
GetFederatedInboxMap() map[string]string
GetDefaultFederationUsername() string
GetPublicKey() string
GetPrivateKey() string
SetPublicKey(key string) error
SetPrivateKey(key string) error
}

View File

@ -0,0 +1,23 @@
package configrepository
// GetPublicKey will return the public key.
func (r *SqlConfigRepository) GetPublicKey() string {
value, _ := r.datastore.GetString(publicKeyKey)
return value
}
// SetPublicKey will save the public key.
func (r *SqlConfigRepository) SetPublicKey(key string) error {
return r.datastore.SetString(publicKeyKey, key)
}
// GetPrivateKey will return the private key.
func (r *SqlConfigRepository) GetPrivateKey() string {
value, _ := r.datastore.GetString(privateKeyKey)
return value
}
// SetPrivateKey will save the private key.
func (r *SqlConfigRepository) SetPrivateKey(key string) error {
return r.datastore.SetString(privateKeyKey, key)
}

View File

@ -1,19 +1,10 @@
package data
package configrepository
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 {

View File

@ -0,0 +1,992 @@
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,
}
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)
}

View File

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

View File

@ -13,6 +13,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/configrepository"
"github.com/owncast/owncast/persistence/userrepository"
"github.com/owncast/owncast/utils"
"github.com/owncast/owncast/webserver/handlers/generated"
@ -366,7 +367,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
}

View File

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

View File

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

View File

@ -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 {

View File

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

View File

@ -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(),
},
}

View File

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

View File

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

View File

@ -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

View File

@ -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("<p>One-time code from %s: %s. If you did not request this message please ignore or block.</p>", data.GetServerName(), reg.Code)
configRepository := configrepository.Get()
msg := fmt.Sprintf("<p>One-time code from %s: %s. If you did not request this message please ignore or block.</p>", 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

View File

@ -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 {

View File

@ -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(),
}
}

View File

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

View File

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

View File

@ -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

View File

@ -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.

View File

@ -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 {

View File

@ -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: /")
}

View File

@ -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

View File

@ -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 {

View File

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

View File

@ -5,8 +5,8 @@ import (
"net/http"
"strings"
"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"
log "github.com/sirupsen/logrus"
@ -21,9 +21,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 +103,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 {
configRepository := configrepository.Get()
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
accessToken := r.URL.Query().Get("accessToken")
if accessToken == "" {
@ -111,7 +113,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 := configRepository.IsIPAddressBanned(ipAddress); blocked {
log.Debugln("Client ip address has been blocked. Rejecting.")
accessDenied(w)
return