mirror of
https://github.com/owncast/owncast.git
synced 2024-11-21 20:28:15 +03:00
feat: first pass migrating all the handlers to the new architecture
This commit is contained in:
parent
551440f3b6
commit
db67603550
64 changed files with 1696 additions and 1733 deletions
|
@ -62,7 +62,7 @@ func ActorObjectHandler(w http.ResponseWriter, r *http.Request) {
|
|||
if err != nil {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
// controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
// responses.WriteSimpleResponse(w, false, err.Error())
|
||||
}
|
||||
|
||||
if _, err := w.Write([]byte(object)); err != nil {
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/owncast/owncast/core/rtmp"
|
||||
)
|
||||
|
||||
// DisconnectInboundConnection will force-disconnect an inbound stream.
|
||||
func DisconnectInboundConnection(w http.ResponseWriter, r *http.Request) {
|
||||
rtmp.Disconnect()
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
package admin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/owncast/owncast/controllers"
|
||||
"github.com/owncast/owncast/core/data"
|
||||
)
|
||||
|
||||
// SetCustomColorVariableValues sets the custom color variables.
|
||||
func SetCustomColorVariableValues(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
type request struct {
|
||||
Value map[string]string `json:"value"`
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var values request
|
||||
|
||||
if err := decoder.Decode(&values); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to update appearance variable values")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetCustomColorVariableValues(values.Value); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "custom appearance variables updated")
|
||||
}
|
|
@ -1,880 +0,0 @@
|
|||
package admin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/owncast/owncast/activitypub/outbox"
|
||||
"github.com/owncast/owncast/controllers"
|
||||
"github.com/owncast/owncast/core/chat"
|
||||
"github.com/owncast/owncast/core/data"
|
||||
"github.com/owncast/owncast/core/user"
|
||||
"github.com/owncast/owncast/core/webhooks"
|
||||
"github.com/owncast/owncast/models"
|
||||
"github.com/owncast/owncast/utils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/teris-io/shortid"
|
||||
)
|
||||
|
||||
// ConfigValue is a container object that holds a value, is encoded, and saved to the database.
|
||||
type ConfigValue struct {
|
||||
Value interface{} `json:"value"`
|
||||
}
|
||||
|
||||
// SetTags will handle the web config request to set tags.
|
||||
func SetTags(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValues, success := getValuesFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
tagStrings := make([]string, 0)
|
||||
for _, tag := range configValues {
|
||||
tagStrings = append(tagStrings, strings.TrimLeft(tag.Value.(string), "#"))
|
||||
}
|
||||
|
||||
if err := data.SetServerMetadataTags(tagStrings); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Update Fediverse followers about this change.
|
||||
if err := outbox.UpdateFollowersWithAccountUpdates(); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "changed")
|
||||
}
|
||||
|
||||
// SetStreamTitle will handle the web config request to set the current stream title.
|
||||
func SetStreamTitle(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
value := configValue.Value.(string)
|
||||
|
||||
if err := data.SetStreamTitle(value); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
if value != "" {
|
||||
sendSystemChatAction(fmt.Sprintf("Stream title changed to **%s**", value), true)
|
||||
go webhooks.SendStreamStatusEvent(models.StreamTitleUpdated)
|
||||
}
|
||||
controllers.WriteSimpleResponse(w, true, "changed")
|
||||
}
|
||||
|
||||
// ExternalSetStreamTitle will change the stream title on behalf of an external integration API request.
|
||||
func ExternalSetStreamTitle(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
||||
SetStreamTitle(w, r)
|
||||
}
|
||||
|
||||
func sendSystemChatAction(messageText string, ephemeral bool) {
|
||||
if err := chat.SendSystemAction(messageText, ephemeral); err != nil {
|
||||
log.Errorln(err)
|
||||
}
|
||||
}
|
||||
|
||||
// SetServerName will handle the web config request to set the server's name.
|
||||
func SetServerName(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetServerName(configValue.Value.(string)); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Update Fediverse followers about this change.
|
||||
if err := outbox.UpdateFollowersWithAccountUpdates(); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "changed")
|
||||
}
|
||||
|
||||
// SetServerSummary will handle the web config request to set the about/summary text.
|
||||
func SetServerSummary(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetServerSummary(configValue.Value.(string)); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Update Fediverse followers about this change.
|
||||
if err := outbox.UpdateFollowersWithAccountUpdates(); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "changed")
|
||||
}
|
||||
|
||||
// SetCustomOfflineMessage will set a message to display when the server is offline.
|
||||
func SetCustomOfflineMessage(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetCustomOfflineMessage(strings.TrimSpace(configValue.Value.(string))); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "changed")
|
||||
}
|
||||
|
||||
// SetServerWelcomeMessage will handle the web config request to set the welcome message text.
|
||||
func SetServerWelcomeMessage(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetServerWelcomeMessage(strings.TrimSpace(configValue.Value.(string))); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "changed")
|
||||
}
|
||||
|
||||
// SetExtraPageContent will handle the web config request to set the page markdown content.
|
||||
func SetExtraPageContent(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetExtraPageBodyContent(configValue.Value.(string)); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "changed")
|
||||
}
|
||||
|
||||
// SetAdminPassword will handle the web config request to set the server admin password.
|
||||
func SetAdminPassword(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetAdminPassword(configValue.Value.(string)); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "changed")
|
||||
}
|
||||
|
||||
// SetLogo will handle a new logo image file being uploaded.
|
||||
func SetLogo(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
value, ok := configValue.Value.(string)
|
||||
if !ok {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to find image data")
|
||||
return
|
||||
}
|
||||
bytes, extension, err := utils.DecodeBase64Image(value)
|
||||
if err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
imgPath := filepath.Join("data", "logo"+extension)
|
||||
if err := os.WriteFile(imgPath, bytes, 0o600); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetLogoPath("logo" + extension); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetLogoUniquenessString(shortid.MustGenerate()); err != nil {
|
||||
log.Error("Error saving logo uniqueness string: ", err)
|
||||
}
|
||||
|
||||
// Update Fediverse followers about this change.
|
||||
if err := outbox.UpdateFollowersWithAccountUpdates(); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "changed")
|
||||
}
|
||||
|
||||
// SetNSFW will handle the web config request to set the NSFW flag.
|
||||
func SetNSFW(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetNSFW(configValue.Value.(bool)); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "changed")
|
||||
}
|
||||
|
||||
// SetFfmpegPath will handle the web config request to validate and set an updated copy of ffmpg.
|
||||
func SetFfmpegPath(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
path := configValue.Value.(string)
|
||||
if err := utils.VerifyFFMpegPath(path); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetFfmpegPath(configValue.Value.(string)); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "changed")
|
||||
}
|
||||
|
||||
// SetWebServerPort will handle the web config request to set the server's HTTP port.
|
||||
func SetWebServerPort(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if port, ok := configValue.Value.(float64); ok {
|
||||
if (port < 1) || (port > 65535) {
|
||||
controllers.WriteSimpleResponse(w, false, "Port number must be between 1 and 65535")
|
||||
return
|
||||
}
|
||||
if err := data.SetHTTPPortNumber(port); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "HTTP port set")
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, false, "Invalid type or value, port must be a number")
|
||||
}
|
||||
|
||||
// SetWebServerIP will handle the web config request to set the server's HTTP listen address.
|
||||
func SetWebServerIP(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if input, ok := configValue.Value.(string); ok {
|
||||
if ip := net.ParseIP(input); ip != nil {
|
||||
if err := data.SetHTTPListenAddress(ip.String()); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "HTTP listen address set")
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, false, "Invalid IP address")
|
||||
return
|
||||
}
|
||||
controllers.WriteSimpleResponse(w, false, "Invalid type or value, IP address must be a string")
|
||||
}
|
||||
|
||||
// SetRTMPServerPort will handle the web config request to set the inbound RTMP port.
|
||||
func SetRTMPServerPort(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetRTMPPortNumber(configValue.Value.(float64)); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "rtmp port set")
|
||||
}
|
||||
|
||||
// SetServerURL will handle the web config request to set the full server URL.
|
||||
func SetServerURL(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
rawValue, ok := configValue.Value.(string)
|
||||
if !ok {
|
||||
controllers.WriteSimpleResponse(w, false, "could not read server url")
|
||||
return
|
||||
}
|
||||
|
||||
serverHostString := utils.GetHostnameFromURLString(rawValue)
|
||||
if serverHostString == "" {
|
||||
controllers.WriteSimpleResponse(w, false, "server url value invalid")
|
||||
return
|
||||
}
|
||||
|
||||
// Block Private IP URLs
|
||||
ipAddr, ipErr := netip.ParseAddr(utils.GetHostnameWithoutPortFromURLString(rawValue))
|
||||
|
||||
if ipErr == nil && ipAddr.IsPrivate() {
|
||||
controllers.WriteSimpleResponse(w, false, "Server URL cannot be private")
|
||||
return
|
||||
}
|
||||
|
||||
// Trim any trailing slash
|
||||
serverURL := strings.TrimRight(rawValue, "/")
|
||||
|
||||
if err := data.SetServerURL(serverURL); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "server url set")
|
||||
}
|
||||
|
||||
// SetSocketHostOverride will set the host override for the websocket.
|
||||
func SetSocketHostOverride(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetWebsocketOverrideHost(configValue.Value.(string)); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "websocket host override set")
|
||||
}
|
||||
|
||||
// SetDirectoryEnabled will handle the web config request to enable or disable directory registration.
|
||||
func SetDirectoryEnabled(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetDirectoryEnabled(configValue.Value.(bool)); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
controllers.WriteSimpleResponse(w, true, "directory state changed")
|
||||
}
|
||||
|
||||
// SetStreamLatencyLevel will handle the web config request to set the stream latency level.
|
||||
func SetStreamLatencyLevel(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetStreamLatencyLevel(configValue.Value.(float64)); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, "error setting stream latency "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "set stream latency")
|
||||
}
|
||||
|
||||
// SetS3Configuration will handle the web config request to set the storage configuration.
|
||||
func SetS3Configuration(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
type s3ConfigurationRequest struct {
|
||||
Value models.S3 `json:"value"`
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var newS3Config s3ConfigurationRequest
|
||||
if err := decoder.Decode(&newS3Config); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to update s3 config with provided values")
|
||||
return
|
||||
}
|
||||
|
||||
if newS3Config.Value.Enabled {
|
||||
if newS3Config.Value.Endpoint == "" || !utils.IsValidURL((newS3Config.Value.Endpoint)) {
|
||||
controllers.WriteSimpleResponse(w, false, "s3 support requires an endpoint")
|
||||
return
|
||||
}
|
||||
|
||||
if newS3Config.Value.AccessKey == "" || newS3Config.Value.Secret == "" {
|
||||
controllers.WriteSimpleResponse(w, false, "s3 support requires an access key and secret")
|
||||
return
|
||||
}
|
||||
|
||||
if newS3Config.Value.Region == "" {
|
||||
controllers.WriteSimpleResponse(w, false, "s3 support requires a region and endpoint")
|
||||
return
|
||||
}
|
||||
|
||||
if newS3Config.Value.Bucket == "" {
|
||||
controllers.WriteSimpleResponse(w, false, "s3 support requires a bucket created for storing public video segments")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := data.SetS3Config(newS3Config.Value); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
controllers.WriteSimpleResponse(w, true, "storage configuration changed")
|
||||
}
|
||||
|
||||
// SetStreamOutputVariants will handle the web config request to set the video output stream variants.
|
||||
func SetStreamOutputVariants(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
type streamOutputVariantRequest struct {
|
||||
Value []models.StreamOutputVariant `json:"value"`
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var videoVariants streamOutputVariantRequest
|
||||
if err := decoder.Decode(&videoVariants); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to update video config with provided values "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetStreamOutputVariants(videoVariants.Value); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to update video config with provided values "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "stream output variants updated")
|
||||
}
|
||||
|
||||
// SetSocialHandles will handle the web config request to set the external social profile links.
|
||||
func SetSocialHandles(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
type socialHandlesRequest struct {
|
||||
Value []models.SocialHandle `json:"value"`
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var socialHandles socialHandlesRequest
|
||||
if err := decoder.Decode(&socialHandles); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to update social handles with provided values")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetSocialHandles(socialHandles.Value); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to update social handles with provided values")
|
||||
return
|
||||
}
|
||||
|
||||
// Update Fediverse followers about this change.
|
||||
if err := outbox.UpdateFollowersWithAccountUpdates(); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "social handles updated")
|
||||
}
|
||||
|
||||
// SetChatDisabled will disable chat functionality.
|
||||
func SetChatDisabled(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to update chat disabled")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetChatDisabled(configValue.Value.(bool)); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "chat disabled status updated")
|
||||
}
|
||||
|
||||
// SetVideoCodec will change the codec used for video encoding.
|
||||
func SetVideoCodec(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to change video codec")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetVideoCodec(configValue.Value.(string)); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to update codec")
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "video codec updated")
|
||||
}
|
||||
|
||||
// SetExternalActions will set the 3rd party actions for the web interface.
|
||||
func SetExternalActions(w http.ResponseWriter, r *http.Request) {
|
||||
type externalActionsRequest struct {
|
||||
Value []models.ExternalAction `json:"value"`
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var actions externalActionsRequest
|
||||
if err := decoder.Decode(&actions); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to update external actions with provided values")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetExternalActions(actions.Value); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to update external actions with provided values")
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "external actions update")
|
||||
}
|
||||
|
||||
// SetCustomStyles will set the CSS string we insert into the page.
|
||||
func SetCustomStyles(w http.ResponseWriter, r *http.Request) {
|
||||
customStyles, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to update custom styles")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetCustomStyles(customStyles.Value.(string)); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "custom styles updated")
|
||||
}
|
||||
|
||||
// SetCustomJavascript will set the Javascript string we insert into the page.
|
||||
func SetCustomJavascript(w http.ResponseWriter, r *http.Request) {
|
||||
customJavascript, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to update custom javascript")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetCustomJavascript(customJavascript.Value.(string)); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "custom styles updated")
|
||||
}
|
||||
|
||||
// SetForbiddenUsernameList will set the list of usernames we do not allow to use.
|
||||
func SetForbiddenUsernameList(w http.ResponseWriter, r *http.Request) {
|
||||
type forbiddenUsernameListRequest struct {
|
||||
Value []string `json:"value"`
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var request forbiddenUsernameListRequest
|
||||
if err := decoder.Decode(&request); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to update forbidden usernames with provided values")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetForbiddenUsernameList(request.Value); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "forbidden username list updated")
|
||||
}
|
||||
|
||||
// SetSuggestedUsernameList will set the list of suggested usernames that newly registered users are assigned if it isn't inferred otherwise (i.e. through a proxy).
|
||||
func SetSuggestedUsernameList(w http.ResponseWriter, r *http.Request) {
|
||||
type suggestedUsernameListRequest struct {
|
||||
Value []string `json:"value"`
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var request suggestedUsernameListRequest
|
||||
|
||||
if err := decoder.Decode(&request); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to update suggested usernames with provided values")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetSuggestedUsernamesList(request.Value); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "suggested username list updated")
|
||||
}
|
||||
|
||||
// SetChatJoinMessagesEnabled will enable or disable the chat join messages.
|
||||
func SetChatJoinMessagesEnabled(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to update chat join messages enabled")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetChatJoinMessagesEnabled(configValue.Value.(bool)); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "chat join message status updated")
|
||||
}
|
||||
|
||||
// SetHideViewerCount will enable or disable hiding the viewer count.
|
||||
func SetHideViewerCount(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to update hiding viewer count")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetHideViewerCount(configValue.Value.(bool)); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "hide viewer count setting updated")
|
||||
}
|
||||
|
||||
// SetDisableSearchIndexing will set search indexing support.
|
||||
func SetDisableSearchIndexing(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to update search indexing")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetDisableSearchIndexing(configValue.Value.(bool)); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "search indexing support updated")
|
||||
}
|
||||
|
||||
// SetVideoServingEndpoint will save the video serving endpoint.
|
||||
func SetVideoServingEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
endpoint, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to update custom video serving endpoint")
|
||||
return
|
||||
}
|
||||
|
||||
value, ok := endpoint.Value.(string)
|
||||
if !ok {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to update custom video serving endpoint")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetVideoServingEndpoint(value); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "custom video serving endpoint updated")
|
||||
}
|
||||
|
||||
func requirePOST(w http.ResponseWriter, r *http.Request) bool {
|
||||
if r.Method != controllers.POST {
|
||||
controllers.WriteSimpleResponse(w, false, r.Method+" not supported")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func getValueFromRequest(w http.ResponseWriter, r *http.Request) (ConfigValue, bool) {
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var configValue ConfigValue
|
||||
if err := decoder.Decode(&configValue); err != nil {
|
||||
log.Warnln(err)
|
||||
controllers.WriteSimpleResponse(w, false, "unable to parse new value")
|
||||
return configValue, false
|
||||
}
|
||||
|
||||
return configValue, true
|
||||
}
|
||||
|
||||
func getValuesFromRequest(w http.ResponseWriter, r *http.Request) ([]ConfigValue, bool) {
|
||||
var values []ConfigValue
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var configValue ConfigValue
|
||||
if err := decoder.Decode(&configValue); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to parse array of values")
|
||||
return values, false
|
||||
}
|
||||
|
||||
object := reflect.ValueOf(configValue.Value)
|
||||
|
||||
for i := 0; i < object.Len(); i++ {
|
||||
values = append(values, ConfigValue{Value: object.Index(i).Interface()})
|
||||
}
|
||||
|
||||
return values, true
|
||||
}
|
||||
|
||||
// SetStreamKeys will set the valid stream keys.
|
||||
func SetStreamKeys(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
type streamKeysRequest struct {
|
||||
Value []models.StreamKey `json:"value"`
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var streamKeys streamKeysRequest
|
||||
if err := decoder.Decode(&streamKeys); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to update stream keys with provided values")
|
||||
return
|
||||
}
|
||||
|
||||
if len(streamKeys.Value) == 0 {
|
||||
controllers.WriteSimpleResponse(w, false, "must provide at least one valid stream key")
|
||||
return
|
||||
}
|
||||
|
||||
for _, streamKey := range streamKeys.Value {
|
||||
if streamKey.Key == "" {
|
||||
controllers.WriteSimpleResponse(w, false, "stream key cannot be empty")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := data.SetStreamKeys(streamKeys.Value); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "changed")
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
package admin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/owncast/owncast/controllers"
|
||||
"github.com/owncast/owncast/core"
|
||||
|
||||
"github.com/owncast/owncast/core/rtmp"
|
||||
)
|
||||
|
||||
// DisconnectInboundConnection will force-disconnect an inbound stream.
|
||||
func DisconnectInboundConnection(w http.ResponseWriter, r *http.Request) {
|
||||
if !core.GetStatus().Online {
|
||||
controllers.WriteSimpleResponse(w, false, "no inbound stream connected")
|
||||
return
|
||||
}
|
||||
|
||||
rtmp.Disconnect()
|
||||
controllers.WriteSimpleResponse(w, true, "inbound stream disconnected")
|
||||
}
|
|
@ -1,179 +0,0 @@
|
|||
package admin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/owncast/owncast/activitypub"
|
||||
"github.com/owncast/owncast/activitypub/outbox"
|
||||
"github.com/owncast/owncast/activitypub/persistence"
|
||||
"github.com/owncast/owncast/controllers"
|
||||
"github.com/owncast/owncast/core/data"
|
||||
)
|
||||
|
||||
// SendFederatedMessage will send a manual message to the fediverse.
|
||||
func SendFederatedMessage(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
message, ok := configValue.Value.(string)
|
||||
if !ok {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to send message")
|
||||
return
|
||||
}
|
||||
|
||||
if err := activitypub.SendPublicFederatedMessage(message); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "sent")
|
||||
}
|
||||
|
||||
// SetFederationEnabled will set if Federation features are enabled.
|
||||
func SetFederationEnabled(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetFederationEnabled(configValue.Value.(bool)); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
controllers.WriteSimpleResponse(w, true, "federation features saved")
|
||||
}
|
||||
|
||||
// SetFederationActivityPrivate will set if Federation features are private to followers.
|
||||
func SetFederationActivityPrivate(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetFederationIsPrivate(configValue.Value.(bool)); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Update Fediverse followers about this change.
|
||||
if err := outbox.UpdateFollowersWithAccountUpdates(); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "federation private saved")
|
||||
}
|
||||
|
||||
// SetFederationShowEngagement will set if Fedivese engagement shows in chat.
|
||||
func SetFederationShowEngagement(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetFederationShowEngagement(configValue.Value.(bool)); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
controllers.WriteSimpleResponse(w, true, "federation show engagement saved")
|
||||
}
|
||||
|
||||
// SetFederationUsername will set the local actor username used for federation activities.
|
||||
func SetFederationUsername(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetFederationUsername(configValue.Value.(string)); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "username saved")
|
||||
}
|
||||
|
||||
// SetFederationGoLiveMessage will set the federated message sent when the streamer goes live.
|
||||
func SetFederationGoLiveMessage(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetFederationGoLiveMessage(configValue.Value.(string)); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "message saved")
|
||||
}
|
||||
|
||||
// SetFederationBlockDomains saves a list of domains to block on the Fediverse.
|
||||
func SetFederationBlockDomains(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValues, success := getValuesFromRequest(w, r)
|
||||
if !success {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to handle provided domains")
|
||||
return
|
||||
}
|
||||
|
||||
domainStrings := make([]string, 0)
|
||||
for _, domain := range configValues {
|
||||
domainStrings = append(domainStrings, domain.Value.(string))
|
||||
}
|
||||
|
||||
if err := data.SetBlockedFederatedDomains(domainStrings); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "saved")
|
||||
}
|
||||
|
||||
// GetFederatedActions will return the saved list of accepted inbound
|
||||
// federated activities.
|
||||
func GetFederatedActions(page int, pageSize int, w http.ResponseWriter, r *http.Request) {
|
||||
offset := pageSize * page
|
||||
|
||||
activities, total, err := persistence.GetInboundActivities(pageSize, offset)
|
||||
if err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
response := controllers.PaginatedResponse{
|
||||
Total: total,
|
||||
Results: activities,
|
||||
}
|
||||
|
||||
controllers.WriteResponse(w, response)
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
package admin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/owncast/owncast/controllers"
|
||||
"github.com/owncast/owncast/core/data"
|
||||
"github.com/owncast/owncast/models"
|
||||
)
|
||||
|
||||
// SetDiscordNotificationConfiguration will set the discord notification configuration.
|
||||
func SetDiscordNotificationConfiguration(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
type request struct {
|
||||
Value models.DiscordConfiguration `json:"value"`
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var config request
|
||||
if err := decoder.Decode(&config); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to update discord config with provided values")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetDiscordConfig(config.Value); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to update discord config with provided values")
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "updated discord config with provided values")
|
||||
}
|
||||
|
||||
// SetBrowserNotificationConfiguration will set the browser notification configuration.
|
||||
func SetBrowserNotificationConfiguration(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
type request struct {
|
||||
Value models.BrowserNotificationConfiguration `json:"value"`
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var config request
|
||||
if err := decoder.Decode(&config); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to update browser push config with provided values")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetBrowserPushConfig(config.Value); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to update browser push config with provided values")
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "updated browser push config with provided values")
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
package controllers
|
||||
|
||||
// POST is the HTTP POST method.
|
||||
const POST = "POST"
|
||||
|
||||
// GET is the HTTP GET method.
|
||||
const GET = "GET"
|
|
@ -1,55 +0,0 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"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/utils"
|
||||
"github.com/owncast/owncast/webserver/middleware"
|
||||
)
|
||||
|
||||
// HandleHLSRequest will manage all requests to HLS content.
|
||||
func HandleHLSRequest(w http.ResponseWriter, r *http.Request) {
|
||||
// Sanity check to limit requests to HLS file types.
|
||||
if filepath.Ext(r.URL.Path) != ".m3u8" && filepath.Ext(r.URL.Path) != ".ts" {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
requestedPath := r.URL.Path
|
||||
relativePath := strings.Replace(requestedPath, "/hls/", "", 1)
|
||||
fullPath := filepath.Join(config.HLSStoragePath, relativePath)
|
||||
|
||||
// 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" {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle playlists
|
||||
if path.Ext(r.URL.Path) == ".m3u8" {
|
||||
// Playlists should never be cached.
|
||||
middleware.DisableCache(w)
|
||||
|
||||
// Force the correct content type
|
||||
w.Header().Set("Content-Type", "application/x-mpegURL")
|
||||
|
||||
// Use this as an opportunity to mark this viewer as active.
|
||||
viewer := models.GenerateViewerFromRequest(r)
|
||||
core.SetViewerActive(&viewer)
|
||||
} else {
|
||||
cacheTime := utils.GetCacheDurationSecondsForPath(relativePath)
|
||||
w.Header().Set("Cache-Control", "public, max-age="+strconv.Itoa(cacheTime))
|
||||
}
|
||||
|
||||
middleware.EnableCors(w)
|
||||
http.ServeFile(w, r, fullPath)
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
package handlers
|
36
webserver/handlers/adminApiAppearanceConfig.go
Normal file
36
webserver/handlers/adminApiAppearanceConfig.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/owncast/owncast/core/data"
|
||||
"github.com/owncast/owncast/webserver/requests"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
)
|
||||
|
||||
// SetCustomColorVariableValues sets the custom color variables.
|
||||
func (h *Handlers) SetCustomColorVariableValues(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
type request struct {
|
||||
Value map[string]string `json:"value"`
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var values request
|
||||
|
||||
if err := decoder.Decode(&values); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, "unable to update appearance variable values")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetCustomColorVariableValues(values.Value); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "custom appearance variables updated")
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package admin
|
||||
package handlers
|
||||
|
||||
// this is endpoint logic
|
||||
|
||||
|
@ -9,30 +9,31 @@ import (
|
|||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/owncast/owncast/controllers"
|
||||
"github.com/owncast/owncast/core/chat"
|
||||
"github.com/owncast/owncast/core/chat/events"
|
||||
"github.com/owncast/owncast/core/data"
|
||||
"github.com/owncast/owncast/core/user"
|
||||
"github.com/owncast/owncast/utils"
|
||||
"github.com/owncast/owncast/webserver/requests"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// ExternalUpdateMessageVisibility updates an array of message IDs to have the same visiblity.
|
||||
func ExternalUpdateMessageVisibility(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
||||
UpdateMessageVisibility(w, r)
|
||||
func (h *Handlers) ExternalUpdateMessageVisibility(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
||||
h.UpdateMessageVisibility(w, r)
|
||||
}
|
||||
|
||||
// UpdateMessageVisibility updates an array of message IDs to have the same visiblity.
|
||||
func UpdateMessageVisibility(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) UpdateMessageVisibility(w http.ResponseWriter, r *http.Request) {
|
||||
type messageVisibilityUpdateRequest struct {
|
||||
IDArray []string `json:"idArray"`
|
||||
Visible bool `json:"visible"`
|
||||
}
|
||||
|
||||
if r.Method != controllers.POST {
|
||||
// nolint:goconst
|
||||
controllers.WriteSimpleResponse(w, false, r.Method+" not supported")
|
||||
if r.Method != http.MethodPost {
|
||||
responses.WriteSimpleResponse(w, false, r.Method+" not supported")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -41,78 +42,78 @@ func UpdateMessageVisibility(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
if err := decoder.Decode(&request); err != nil {
|
||||
log.Errorln(err)
|
||||
controllers.WriteSimpleResponse(w, false, "")
|
||||
responses.WriteSimpleResponse(w, false, "")
|
||||
return
|
||||
}
|
||||
|
||||
if err := chat.SetMessagesVisibility(request.IDArray, request.Visible); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "changed")
|
||||
responses.WriteSimpleResponse(w, true, "changed")
|
||||
}
|
||||
|
||||
// BanIPAddress will manually ban an IP address.
|
||||
func BanIPAddress(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
func (h *Handlers) BanIPAddress(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to ban IP address")
|
||||
responses.WriteSimpleResponse(w, false, "unable to ban IP address")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.BanIPAddress(configValue.Value.(string), "manually added"); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, "error saving IP address ban")
|
||||
responses.WriteSimpleResponse(w, false, "error saving IP address ban")
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "IP address banned")
|
||||
responses.WriteSimpleResponse(w, true, "IP address banned")
|
||||
}
|
||||
|
||||
// UnBanIPAddress will remove an IP address ban.
|
||||
func UnBanIPAddress(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
func (h *Handlers) UnBanIPAddress(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to unban IP address")
|
||||
responses.WriteSimpleResponse(w, false, "unable to unban IP address")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.RemoveIPAddressBan(configValue.Value.(string)); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, "error removing IP address ban")
|
||||
responses.WriteSimpleResponse(w, false, "error removing IP address ban")
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "IP address unbanned")
|
||||
responses.WriteSimpleResponse(w, true, "IP address unbanned")
|
||||
}
|
||||
|
||||
// GetIPAddressBans will return all the banned IP addresses.
|
||||
func GetIPAddressBans(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetIPAddressBans(w http.ResponseWriter, r *http.Request) {
|
||||
bans, err := data.GetIPAddressBans()
|
||||
if err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteResponse(w, bans)
|
||||
responses.WriteResponse(w, bans)
|
||||
}
|
||||
|
||||
// UpdateUserEnabled enable or disable a single user by ID.
|
||||
func UpdateUserEnabled(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) UpdateUserEnabled(w http.ResponseWriter, r *http.Request) {
|
||||
type blockUserRequest struct {
|
||||
UserID string `json:"userId"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
if r.Method != controllers.POST {
|
||||
controllers.WriteSimpleResponse(w, false, r.Method+" not supported")
|
||||
if r.Method != http.MethodPost {
|
||||
responses.WriteSimpleResponse(w, false, r.Method+" not supported")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -121,19 +122,19 @@ func UpdateUserEnabled(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
if err := decoder.Decode(&request); err != nil {
|
||||
log.Errorln(err)
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if request.UserID == "" {
|
||||
controllers.WriteSimpleResponse(w, false, "must provide userId")
|
||||
responses.WriteSimpleResponse(w, false, "must provide userId")
|
||||
return
|
||||
}
|
||||
|
||||
// Disable/enable the user
|
||||
if err := user.SetEnabled(request.UserID, request.Enabled); err != nil {
|
||||
log.Errorln("error changing user enabled status", err)
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -142,7 +143,7 @@ func UpdateUserEnabled(w http.ResponseWriter, r *http.Request) {
|
|||
if !request.Enabled {
|
||||
if err := chat.SetMessageVisibilityForUserID(request.UserID, request.Enabled); err != nil {
|
||||
log.Errorln("error changing user messages visibility", err)
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -157,7 +158,7 @@ func UpdateUserEnabled(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
if err != nil {
|
||||
log.Errorln("error fetching clients for user: ", err)
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -180,26 +181,26 @@ func UpdateUserEnabled(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, fmt.Sprintf("%s enabled: %t", request.UserID, request.Enabled))
|
||||
responses.WriteSimpleResponse(w, true, fmt.Sprintf("%s enabled: %t", request.UserID, request.Enabled))
|
||||
}
|
||||
|
||||
// GetDisabledUsers will return all the disabled users.
|
||||
func GetDisabledUsers(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetDisabledUsers(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
users := user.GetDisabledUsers()
|
||||
controllers.WriteResponse(w, users)
|
||||
responses.WriteResponse(w, users)
|
||||
}
|
||||
|
||||
// UpdateUserModerator will set the moderator status for a user ID.
|
||||
func UpdateUserModerator(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) UpdateUserModerator(w http.ResponseWriter, r *http.Request) {
|
||||
type request struct {
|
||||
UserID string `json:"userId"`
|
||||
IsModerator bool `json:"isModerator"`
|
||||
}
|
||||
|
||||
if r.Method != controllers.POST {
|
||||
controllers.WriteSimpleResponse(w, false, r.Method+" not supported")
|
||||
if r.Method != http.MethodPost {
|
||||
responses.WriteSimpleResponse(w, false, r.Method+" not supported")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -207,13 +208,13 @@ func UpdateUserModerator(w http.ResponseWriter, r *http.Request) {
|
|||
var req request
|
||||
|
||||
if err := decoder.Decode(&req); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, "")
|
||||
responses.WriteSimpleResponse(w, false, "")
|
||||
return
|
||||
}
|
||||
|
||||
// Update the user object with new moderation access.
|
||||
if err := user.SetModerator(req.UserID, req.IsModerator); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -222,87 +223,87 @@ func UpdateUserModerator(w http.ResponseWriter, r *http.Request) {
|
|||
log.Debugln(err)
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, fmt.Sprintf("%s is moderator: %t", req.UserID, req.IsModerator))
|
||||
responses.WriteSimpleResponse(w, true, fmt.Sprintf("%s is moderator: %t", req.UserID, req.IsModerator))
|
||||
}
|
||||
|
||||
// GetModerators will return a list of moderator users.
|
||||
func GetModerators(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetModerators(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
users := user.GetModeratorUsers()
|
||||
controllers.WriteResponse(w, users)
|
||||
responses.WriteResponse(w, users)
|
||||
}
|
||||
|
||||
// GetChatMessages returns all of the chat messages, unfiltered.
|
||||
func GetChatMessages(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetAdminChatMessages(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
messages := chat.GetChatModerationHistory()
|
||||
controllers.WriteResponse(w, messages)
|
||||
responses.WriteResponse(w, messages)
|
||||
}
|
||||
|
||||
// SendSystemMessage will send an official "SYSTEM" message to chat on behalf of your server.
|
||||
func SendSystemMessage(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) SendSystemMessage(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
var message events.SystemMessageEvent
|
||||
if err := json.NewDecoder(r.Body).Decode(&message); err != nil {
|
||||
controllers.InternalErrorHandler(w, err)
|
||||
responses.InternalErrorHandler(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := chat.SendSystemMessage(message.Body, false); err != nil {
|
||||
controllers.BadRequestHandler(w, err)
|
||||
responses.BadRequestHandler(w, err)
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "sent")
|
||||
responses.WriteSimpleResponse(w, true, "sent")
|
||||
}
|
||||
|
||||
// SendSystemMessageToConnectedClient will handle incoming requests to send a single message to a single connected client by ID.
|
||||
func SendSystemMessageToConnectedClient(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) SendSystemMessageToConnectedClient(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
clientIDText, err := utils.ReadRestURLParameter(r, "clientId")
|
||||
if err != nil {
|
||||
controllers.BadRequestHandler(w, err)
|
||||
responses.BadRequestHandler(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
clientIDNumeric, err := strconv.ParseUint(clientIDText, 10, 32)
|
||||
if err != nil {
|
||||
controllers.BadRequestHandler(w, err)
|
||||
responses.BadRequestHandler(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
var message events.SystemMessageEvent
|
||||
if err := json.NewDecoder(r.Body).Decode(&message); err != nil {
|
||||
controllers.InternalErrorHandler(w, err)
|
||||
responses.InternalErrorHandler(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
chat.SendSystemMessageToClient(uint(clientIDNumeric), message.Body)
|
||||
controllers.WriteSimpleResponse(w, true, "sent")
|
||||
responses.WriteSimpleResponse(w, true, "sent")
|
||||
}
|
||||
|
||||
// SendUserMessage will send a message to chat on behalf of a user. *Depreciated*.
|
||||
func SendUserMessage(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) SendUserMessage(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
controllers.BadRequestHandler(w, errors.New("no longer supported. see /api/integrations/chat/send"))
|
||||
responses.BadRequestHandler(w, errors.New("no longer supported. see /api/integrations/chat/send"))
|
||||
}
|
||||
|
||||
// SendIntegrationChatMessage will send a chat message on behalf of an external chat integration.
|
||||
func SendIntegrationChatMessage(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) SendIntegrationChatMessage(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
name := integration.DisplayName
|
||||
|
||||
if name == "" {
|
||||
controllers.BadRequestHandler(w, errors.New("unknown integration for provided access token"))
|
||||
responses.BadRequestHandler(w, errors.New("unknown integration for provided access token"))
|
||||
return
|
||||
}
|
||||
|
||||
var event events.UserMessageEvent
|
||||
if err := json.NewDecoder(r.Body).Decode(&event); err != nil {
|
||||
controllers.InternalErrorHandler(w, err)
|
||||
responses.InternalErrorHandler(w, err)
|
||||
return
|
||||
}
|
||||
event.SetDefaults()
|
||||
|
@ -310,7 +311,7 @@ func SendIntegrationChatMessage(integration user.ExternalAPIUser, w http.Respons
|
|||
event.Type = "CHAT"
|
||||
|
||||
if event.Empty() {
|
||||
controllers.BadRequestHandler(w, errors.New("invalid message"))
|
||||
responses.BadRequestHandler(w, errors.New("invalid message"))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -323,22 +324,22 @@ func SendIntegrationChatMessage(integration user.ExternalAPIUser, w http.Respons
|
|||
}
|
||||
|
||||
if err := chat.Broadcast(&event); err != nil {
|
||||
controllers.BadRequestHandler(w, err)
|
||||
responses.BadRequestHandler(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
chat.SaveUserMessage(event)
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "sent")
|
||||
responses.WriteSimpleResponse(w, true, "sent")
|
||||
}
|
||||
|
||||
// SendChatAction will send a generic chat action.
|
||||
func SendChatAction(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) SendChatAction(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
var message events.SystemActionEvent
|
||||
if err := json.NewDecoder(r.Body).Decode(&message); err != nil {
|
||||
controllers.InternalErrorHandler(w, err)
|
||||
responses.InternalErrorHandler(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -346,30 +347,30 @@ func SendChatAction(integration user.ExternalAPIUser, w http.ResponseWriter, r *
|
|||
message.RenderBody()
|
||||
|
||||
if err := chat.SendSystemAction(message.Body, false); err != nil {
|
||||
controllers.BadRequestHandler(w, err)
|
||||
responses.BadRequestHandler(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "sent")
|
||||
responses.WriteSimpleResponse(w, true, "sent")
|
||||
}
|
||||
|
||||
// SetEnableEstablishedChatUserMode sets the requirement for a chat user
|
||||
// to be "established" for some time before taking part in chat.
|
||||
func SetEnableEstablishedChatUserMode(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
func (h *Handlers) SetEnableEstablishedChatUserMode(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := getValueFromRequest(w, r)
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to update chat established user only mode")
|
||||
responses.WriteSimpleResponse(w, false, "unable to update chat established user only mode")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetChatEstablishedUsersOnlyMode(configValue.Value.(bool)); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "chat established users only mode updated")
|
||||
responses.WriteSimpleResponse(w, true, "chat established users only mode updated")
|
||||
}
|
814
webserver/handlers/adminApiConfigHandlers.go
Normal file
814
webserver/handlers/adminApiConfigHandlers.go
Normal file
|
@ -0,0 +1,814 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/owncast/owncast/activitypub/outbox"
|
||||
"github.com/owncast/owncast/core/chat"
|
||||
"github.com/owncast/owncast/core/data"
|
||||
"github.com/owncast/owncast/core/user"
|
||||
"github.com/owncast/owncast/core/webhooks"
|
||||
"github.com/owncast/owncast/models"
|
||||
"github.com/owncast/owncast/utils"
|
||||
"github.com/owncast/owncast/webserver/requests"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/teris-io/shortid"
|
||||
)
|
||||
|
||||
// SetTags will handle the web config request to set tags.
|
||||
func (h *Handlers) SetTags(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValues, success := requests.GetValuesFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
tagStrings := make([]string, 0)
|
||||
for _, tag := range configValues {
|
||||
tagStrings = append(tagStrings, strings.TrimLeft(tag.Value.(string), "#"))
|
||||
}
|
||||
|
||||
if err := data.SetServerMetadataTags(tagStrings); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Update Fediverse followers about this change.
|
||||
if err := outbox.UpdateFollowersWithAccountUpdates(); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "changed")
|
||||
}
|
||||
|
||||
// SetStreamTitle will handle the web config request to set the current stream title.
|
||||
func (h *Handlers) SetStreamTitle(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
value := configValue.Value.(string)
|
||||
|
||||
if err := data.SetStreamTitle(value); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
if value != "" {
|
||||
sendSystemChatAction(fmt.Sprintf("Stream title changed to **%s**", value), true)
|
||||
go webhooks.SendStreamStatusEvent(models.StreamTitleUpdated)
|
||||
}
|
||||
responses.WriteSimpleResponse(w, true, "changed")
|
||||
}
|
||||
|
||||
// ExternalSetStreamTitle will change the stream title on behalf of an external integration API request.
|
||||
func (h *Handlers) ExternalSetStreamTitle(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
||||
h.SetStreamTitle(w, r)
|
||||
}
|
||||
|
||||
func sendSystemChatAction(messageText string, ephemeral bool) {
|
||||
if err := chat.SendSystemAction(messageText, ephemeral); err != nil {
|
||||
log.Errorln(err)
|
||||
}
|
||||
}
|
||||
|
||||
// SetServerName will handle the web config request to set the server's name.
|
||||
func (h *Handlers) SetServerName(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetServerName(configValue.Value.(string)); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Update Fediverse followers about this change.
|
||||
if err := outbox.UpdateFollowersWithAccountUpdates(); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "changed")
|
||||
}
|
||||
|
||||
// SetServerSummary will handle the web config request to set the about/summary text.
|
||||
func (h *Handlers) SetServerSummary(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetServerSummary(configValue.Value.(string)); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Update Fediverse followers about this change.
|
||||
if err := outbox.UpdateFollowersWithAccountUpdates(); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "changed")
|
||||
}
|
||||
|
||||
// SetCustomOfflineMessage will set a message to display when the server is offline.
|
||||
func (h *Handlers) SetCustomOfflineMessage(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetCustomOfflineMessage(strings.TrimSpace(configValue.Value.(string))); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "changed")
|
||||
}
|
||||
|
||||
// SetServerWelcomeMessage will handle the web config request to set the welcome message text.
|
||||
func (h *Handlers) SetServerWelcomeMessage(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetServerWelcomeMessage(strings.TrimSpace(configValue.Value.(string))); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "changed")
|
||||
}
|
||||
|
||||
// SetExtraPageContent will handle the web config request to set the page markdown content.
|
||||
func (h *Handlers) SetExtraPageContent(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetExtraPageBodyContent(configValue.Value.(string)); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "changed")
|
||||
}
|
||||
|
||||
// SetAdminPassword will handle the web config request to set the server admin password.
|
||||
func (h *Handlers) SetAdminPassword(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetAdminPassword(configValue.Value.(string)); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "changed")
|
||||
}
|
||||
|
||||
// SetLogo will handle a new logo image file being uploaded.
|
||||
func (h *Handlers) SetLogo(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
value, ok := configValue.Value.(string)
|
||||
if !ok {
|
||||
responses.WriteSimpleResponse(w, false, "unable to find image data")
|
||||
return
|
||||
}
|
||||
bytes, extension, err := utils.DecodeBase64Image(value)
|
||||
if err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
imgPath := filepath.Join("data", "logo"+extension)
|
||||
if err := os.WriteFile(imgPath, bytes, 0o600); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetLogoPath("logo" + extension); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetLogoUniquenessString(shortid.MustGenerate()); err != nil {
|
||||
log.Error("Error saving logo uniqueness string: ", err)
|
||||
}
|
||||
|
||||
// Update Fediverse followers about this change.
|
||||
if err := outbox.UpdateFollowersWithAccountUpdates(); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "changed")
|
||||
}
|
||||
|
||||
// SetNSFW will handle the web config request to set the NSFW flag.
|
||||
func (h *Handlers) SetNSFW(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetNSFW(configValue.Value.(bool)); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "changed")
|
||||
}
|
||||
|
||||
// SetFfmpegPath will handle the web config request to validate and set an updated copy of ffmpg.
|
||||
func (h *Handlers) SetFfmpegPath(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
path := configValue.Value.(string)
|
||||
if err := utils.VerifyFFMpegPath(path); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetFfmpegPath(configValue.Value.(string)); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "changed")
|
||||
}
|
||||
|
||||
// SetWebServerPort will handle the web config request to set the server's HTTP port.
|
||||
func (h *Handlers) SetWebServerPort(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if port, ok := configValue.Value.(float64); ok {
|
||||
if (port < 1) || (port > 65535) {
|
||||
responses.WriteSimpleResponse(w, false, "Port number must be between 1 and 65535")
|
||||
return
|
||||
}
|
||||
if err := data.SetHTTPPortNumber(port); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "HTTP port set")
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, false, "Invalid type or value, port must be a number")
|
||||
}
|
||||
|
||||
// SetWebServerIP will handle the web config request to set the server's HTTP listen address.
|
||||
func (h *Handlers) SetWebServerIP(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if input, ok := configValue.Value.(string); ok {
|
||||
if ip := net.ParseIP(input); ip != nil {
|
||||
if err := data.SetHTTPListenAddress(ip.String()); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "HTTP listen address set")
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, false, "Invalid IP address")
|
||||
return
|
||||
}
|
||||
responses.WriteSimpleResponse(w, false, "Invalid type or value, IP address must be a string")
|
||||
}
|
||||
|
||||
// SetRTMPServerPort will handle the web config request to set the inbound RTMP port.
|
||||
func (h *Handlers) SetRTMPServerPort(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetRTMPPortNumber(configValue.Value.(float64)); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "rtmp port set")
|
||||
}
|
||||
|
||||
// SetServerURL will handle the web config request to set the full server URL.
|
||||
func (h *Handlers) SetServerURL(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
rawValue, ok := configValue.Value.(string)
|
||||
if !ok {
|
||||
responses.WriteSimpleResponse(w, false, "could not read server url")
|
||||
return
|
||||
}
|
||||
|
||||
serverHostString := utils.GetHostnameFromURLString(rawValue)
|
||||
if serverHostString == "" {
|
||||
responses.WriteSimpleResponse(w, false, "server url value invalid")
|
||||
return
|
||||
}
|
||||
|
||||
// Trim any trailing slash
|
||||
serverURL := strings.TrimRight(rawValue, "/")
|
||||
|
||||
if err := data.SetServerURL(serverURL); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "server url set")
|
||||
}
|
||||
|
||||
// SetSocketHostOverride will set the host override for the websocket.
|
||||
func (h *Handlers) SetSocketHostOverride(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetWebsocketOverrideHost(configValue.Value.(string)); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "websocket host override set")
|
||||
}
|
||||
|
||||
// SetDirectoryEnabled will handle the web config request to enable or disable directory registration.
|
||||
func (h *Handlers) SetDirectoryEnabled(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetDirectoryEnabled(configValue.Value.(bool)); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
responses.WriteSimpleResponse(w, true, "directory state changed")
|
||||
}
|
||||
|
||||
// SetStreamLatencyLevel will handle the web config request to set the stream latency level.
|
||||
func (h *Handlers) SetStreamLatencyLevel(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetStreamLatencyLevel(configValue.Value.(float64)); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, "error setting stream latency "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "set stream latency")
|
||||
}
|
||||
|
||||
// SetS3Configuration will handle the web config request to set the storage configuration.
|
||||
func (h *Handlers) SetS3Configuration(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
type s3ConfigurationRequest struct {
|
||||
Value models.S3 `json:"value"`
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var newS3Config s3ConfigurationRequest
|
||||
if err := decoder.Decode(&newS3Config); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, "unable to update s3 config with provided values")
|
||||
return
|
||||
}
|
||||
|
||||
if newS3Config.Value.Enabled {
|
||||
if newS3Config.Value.Endpoint == "" || !utils.IsValidURL((newS3Config.Value.Endpoint)) {
|
||||
responses.WriteSimpleResponse(w, false, "s3 support requires an endpoint")
|
||||
return
|
||||
}
|
||||
|
||||
if newS3Config.Value.AccessKey == "" || newS3Config.Value.Secret == "" {
|
||||
responses.WriteSimpleResponse(w, false, "s3 support requires an access key and secret")
|
||||
return
|
||||
}
|
||||
|
||||
if newS3Config.Value.Region == "" {
|
||||
responses.WriteSimpleResponse(w, false, "s3 support requires a region and endpoint")
|
||||
return
|
||||
}
|
||||
|
||||
if newS3Config.Value.Bucket == "" {
|
||||
responses.WriteSimpleResponse(w, false, "s3 support requires a bucket created for storing public video segments")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := data.SetS3Config(newS3Config.Value); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
responses.WriteSimpleResponse(w, true, "storage configuration changed")
|
||||
}
|
||||
|
||||
// SetStreamOutputVariants will handle the web config request to set the video output stream variants.
|
||||
func (h *Handlers) SetStreamOutputVariants(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
type streamOutputVariantRequest struct {
|
||||
Value []models.StreamOutputVariant `json:"value"`
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var videoVariants streamOutputVariantRequest
|
||||
if err := decoder.Decode(&videoVariants); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, "unable to update video config with provided values "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetStreamOutputVariants(videoVariants.Value); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, "unable to update video config with provided values "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "stream output variants updated")
|
||||
}
|
||||
|
||||
// SetSocialHandles will handle the web config request to set the external social profile links.
|
||||
func (h *Handlers) SetSocialHandles(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
type socialHandlesRequest struct {
|
||||
Value []models.SocialHandle `json:"value"`
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var socialHandles socialHandlesRequest
|
||||
if err := decoder.Decode(&socialHandles); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, "unable to update social handles with provided values")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetSocialHandles(socialHandles.Value); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, "unable to update social handles with provided values")
|
||||
return
|
||||
}
|
||||
|
||||
// Update Fediverse followers about this change.
|
||||
if err := outbox.UpdateFollowersWithAccountUpdates(); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "social handles updated")
|
||||
}
|
||||
|
||||
// SetChatDisabled will disable chat functionality.
|
||||
func (h *Handlers) SetChatDisabled(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
responses.WriteSimpleResponse(w, false, "unable to update chat disabled")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetChatDisabled(configValue.Value.(bool)); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "chat disabled status updated")
|
||||
}
|
||||
|
||||
// SetVideoCodec will change the codec used for video encoding.
|
||||
func (h *Handlers) SetVideoCodec(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
responses.WriteSimpleResponse(w, false, "unable to change video codec")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetVideoCodec(configValue.Value.(string)); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, "unable to update codec")
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "video codec updated")
|
||||
}
|
||||
|
||||
// SetExternalActions will set the 3rd party actions for the web interface.
|
||||
func (h *Handlers) SetExternalActions(w http.ResponseWriter, r *http.Request) {
|
||||
type externalActionsRequest struct {
|
||||
Value []models.ExternalAction `json:"value"`
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var actions externalActionsRequest
|
||||
if err := decoder.Decode(&actions); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, "unable to update external actions with provided values")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetExternalActions(actions.Value); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, "unable to update external actions with provided values")
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "external actions update")
|
||||
}
|
||||
|
||||
// SetCustomStyles will set the CSS string we insert into the page.
|
||||
func (h *Handlers) SetCustomStyles(w http.ResponseWriter, r *http.Request) {
|
||||
customStyles, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
responses.WriteSimpleResponse(w, false, "unable to update custom styles")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetCustomStyles(customStyles.Value.(string)); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "custom styles updated")
|
||||
}
|
||||
|
||||
// SetCustomJavascript will set the Javascript string we insert into the page.
|
||||
func (h *Handlers) SetCustomJavascript(w http.ResponseWriter, r *http.Request) {
|
||||
customJavascript, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
responses.WriteSimpleResponse(w, false, "unable to update custom javascript")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetCustomJavascript(customJavascript.Value.(string)); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "custom styles updated")
|
||||
}
|
||||
|
||||
// SetForbiddenUsernameList will set the list of usernames we do not allow to use.
|
||||
func (h *Handlers) SetForbiddenUsernameList(w http.ResponseWriter, r *http.Request) {
|
||||
type forbiddenUsernameListRequest struct {
|
||||
Value []string `json:"value"`
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var request forbiddenUsernameListRequest
|
||||
if err := decoder.Decode(&request); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, "unable to update forbidden usernames with provided values")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetForbiddenUsernameList(request.Value); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "forbidden username list updated")
|
||||
}
|
||||
|
||||
// SetSuggestedUsernameList will set the list of suggested usernames that newly registered users are assigned if it isn't inferred otherwise (i.e. through a proxy).
|
||||
func (h *Handlers) SetSuggestedUsernameList(w http.ResponseWriter, r *http.Request) {
|
||||
type suggestedUsernameListRequest struct {
|
||||
Value []string `json:"value"`
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var request suggestedUsernameListRequest
|
||||
|
||||
if err := decoder.Decode(&request); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, "unable to update suggested usernames with provided values")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetSuggestedUsernamesList(request.Value); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "suggested username list updated")
|
||||
}
|
||||
|
||||
// SetChatJoinMessagesEnabled will enable or disable the chat join messages.
|
||||
func (h *Handlers) SetChatJoinMessagesEnabled(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
responses.WriteSimpleResponse(w, false, "unable to update chat join messages enabled")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetChatJoinMessagesEnabled(configValue.Value.(bool)); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "chat join message status updated")
|
||||
}
|
||||
|
||||
// SetHideViewerCount will enable or disable hiding the viewer count.
|
||||
func (h *Handlers) SetHideViewerCount(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
responses.WriteSimpleResponse(w, false, "unable to update hiding viewer count")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetHideViewerCount(configValue.Value.(bool)); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "hide viewer count setting updated")
|
||||
}
|
||||
|
||||
// SetDisableSearchIndexing will set search indexing support.
|
||||
func (h *Handlers) SetDisableSearchIndexing(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
responses.WriteSimpleResponse(w, false, "unable to update search indexing")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetDisableSearchIndexing(configValue.Value.(bool)); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "search indexing support updated")
|
||||
}
|
||||
|
||||
// SetVideoServingEndpoint will save the video serving endpoint.
|
||||
func (h *Handlers) SetVideoServingEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
endpoint, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
responses.WriteSimpleResponse(w, false, "unable to update custom video serving endpoint")
|
||||
return
|
||||
}
|
||||
|
||||
value, ok := endpoint.Value.(string)
|
||||
if !ok {
|
||||
responses.WriteSimpleResponse(w, false, "unable to update custom video serving endpoint")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetVideoServingEndpoint(value); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "custom video serving endpoint updated")
|
||||
}
|
||||
|
||||
// SetStreamKeys will set the valid stream keys.
|
||||
func (h *Handlers) SetStreamKeys(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
type streamKeysRequest struct {
|
||||
Value []models.StreamKey `json:"value"`
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var streamKeys streamKeysRequest
|
||||
if err := decoder.Decode(&streamKeys); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, "unable to update stream keys with provided values")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetStreamKeys(streamKeys.Value); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "changed")
|
||||
}
|
|
@ -1,25 +1,25 @@
|
|||
package admin
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/owncast/owncast/controllers"
|
||||
"github.com/owncast/owncast/core/chat"
|
||||
"github.com/owncast/owncast/core/user"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
)
|
||||
|
||||
// GetConnectedChatClients returns currently connected clients.
|
||||
func GetConnectedChatClients(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetConnectedChatClients(w http.ResponseWriter, r *http.Request) {
|
||||
clients := chat.GetClients()
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
if err := json.NewEncoder(w).Encode(clients); err != nil {
|
||||
controllers.InternalErrorHandler(w, err)
|
||||
responses.InternalErrorHandler(w, err)
|
||||
}
|
||||
}
|
||||
|
||||
// ExternalGetConnectedChatClients returns currently connected clients.
|
||||
func ExternalGetConnectedChatClients(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
||||
GetConnectedChatClients(w, r)
|
||||
func (h *Handlers) ExternalGetConnectedChatClients(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
||||
h.GetConnectedChatClients(w, r)
|
||||
}
|
|
@ -1,20 +1,20 @@
|
|||
package admin
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/owncast/owncast/controllers"
|
||||
"github.com/owncast/owncast/core/data"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// ResetYPRegistration will clear the YP protocol registration key.
|
||||
func ResetYPRegistration(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) ResetYPRegistration(w http.ResponseWriter, r *http.Request) {
|
||||
log.Traceln("Resetting YP registration key")
|
||||
if err := data.SetDirectoryRegistrationKey(""); err != nil {
|
||||
log.Errorln(err)
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
controllers.WriteSimpleResponse(w, true, "reset")
|
||||
responses.WriteSimpleResponse(w, true, "reset")
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package admin
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -8,13 +8,14 @@ import (
|
|||
"path/filepath"
|
||||
|
||||
"github.com/owncast/owncast/config"
|
||||
"github.com/owncast/owncast/controllers"
|
||||
"github.com/owncast/owncast/utils"
|
||||
"github.com/owncast/owncast/webserver/requests"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
)
|
||||
|
||||
// UploadCustomEmoji allows POSTing a new custom emoji to the server.
|
||||
func UploadCustomEmoji(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
func (h *Handlers) UploadCustomEmoji(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -26,13 +27,13 @@ func UploadCustomEmoji(w http.ResponseWriter, r *http.Request) {
|
|||
emoji := new(postEmoji)
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(emoji); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
bytes, _, err := utils.DecodeBase64Image(emoji.Data)
|
||||
if err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -42,26 +43,26 @@ func UploadCustomEmoji(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
err = os.MkdirAll(config.CustomEmojiPath, 0o700)
|
||||
if err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if utils.DoesFileExists(targetPath) {
|
||||
controllers.WriteSimpleResponse(w, false, fmt.Sprintf("An emoji with the name %q already exists", emojiFileName))
|
||||
responses.WriteSimpleResponse(w, false, fmt.Sprintf("An emoji with the name %q already exists", emojiFileName))
|
||||
return
|
||||
}
|
||||
|
||||
if err = os.WriteFile(targetPath, bytes, 0o600); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, fmt.Sprintf("Emoji %q has been uploaded", emojiFileName))
|
||||
responses.WriteSimpleResponse(w, true, fmt.Sprintf("Emoji %q has been uploaded", emojiFileName))
|
||||
}
|
||||
|
||||
// DeleteCustomEmoji deletes a custom emoji.
|
||||
func DeleteCustomEmoji(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
func (h *Handlers) DeleteCustomEmoji(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -72,7 +73,7 @@ func DeleteCustomEmoji(w http.ResponseWriter, r *http.Request) {
|
|||
emoji := new(deleteEmoji)
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(emoji); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -81,12 +82,12 @@ func DeleteCustomEmoji(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
if err := os.Remove(targetPath); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
controllers.WriteSimpleResponse(w, false, fmt.Sprintf("Emoji %q doesn't exist", emoji.Name))
|
||||
responses.WriteSimpleResponse(w, false, fmt.Sprintf("Emoji %q doesn't exist", emoji.Name))
|
||||
} else {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, fmt.Sprintf("Emoji %q has been deleted", emoji.Name))
|
||||
responses.WriteSimpleResponse(w, true, fmt.Sprintf("Emoji %q has been deleted", emoji.Name))
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package admin
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -7,9 +7,9 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/owncast/owncast/config"
|
||||
"github.com/owncast/owncast/controllers"
|
||||
"github.com/owncast/owncast/core/user"
|
||||
"github.com/owncast/owncast/utils"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
)
|
||||
|
||||
type deleteExternalAPIUserRequest struct {
|
||||
|
@ -22,35 +22,35 @@ type createExternalAPIUserRequest struct {
|
|||
}
|
||||
|
||||
// CreateExternalAPIUser will generate a 3rd party access token.
|
||||
func CreateExternalAPIUser(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) CreateExternalAPIUser(w http.ResponseWriter, r *http.Request) {
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var request createExternalAPIUserRequest
|
||||
if err := decoder.Decode(&request); err != nil {
|
||||
controllers.BadRequestHandler(w, err)
|
||||
responses.BadRequestHandler(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Verify all the scopes provided are valid
|
||||
if !user.HasValidScopes(request.Scopes) {
|
||||
controllers.BadRequestHandler(w, errors.New("one or more invalid scopes provided"))
|
||||
responses.BadRequestHandler(w, errors.New("one or more invalid scopes provided"))
|
||||
return
|
||||
}
|
||||
|
||||
token, err := utils.GenerateAccessToken()
|
||||
if err != nil {
|
||||
controllers.InternalErrorHandler(w, err)
|
||||
responses.InternalErrorHandler(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
color := utils.GenerateRandomDisplayColor(config.MaxUserColor)
|
||||
|
||||
if err := user.InsertExternalAPIUser(token, request.Name, color, request.Scopes); err != nil {
|
||||
controllers.InternalErrorHandler(w, err)
|
||||
responses.InternalErrorHandler(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
controllers.WriteResponse(w, user.ExternalAPIUser{
|
||||
responses.WriteResponse(w, user.ExternalAPIUser{
|
||||
AccessToken: token,
|
||||
DisplayName: request.Name,
|
||||
DisplayColor: color,
|
||||
|
@ -61,42 +61,42 @@ func CreateExternalAPIUser(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
// GetExternalAPIUsers will return all 3rd party access tokens.
|
||||
func GetExternalAPIUsers(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetExternalAPIUsers(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
tokens, err := user.GetExternalAPIUser()
|
||||
if err != nil {
|
||||
controllers.InternalErrorHandler(w, err)
|
||||
responses.InternalErrorHandler(w, err)
|
||||
return
|
||||
}
|
||||
controllers.WriteResponse(w, tokens)
|
||||
responses.WriteResponse(w, tokens)
|
||||
}
|
||||
|
||||
// DeleteExternalAPIUser will return a single 3rd party access token.
|
||||
func DeleteExternalAPIUser(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) DeleteExternalAPIUser(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
if r.Method != controllers.POST {
|
||||
controllers.WriteSimpleResponse(w, false, r.Method+" not supported")
|
||||
if r.Method != http.MethodPost {
|
||||
responses.WriteSimpleResponse(w, false, r.Method+" not supported")
|
||||
return
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var request deleteExternalAPIUserRequest
|
||||
if err := decoder.Decode(&request); err != nil {
|
||||
controllers.BadRequestHandler(w, err)
|
||||
responses.BadRequestHandler(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
if request.Token == "" {
|
||||
controllers.BadRequestHandler(w, errors.New("must provide a token"))
|
||||
responses.BadRequestHandler(w, errors.New("must provide a token"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := user.DeleteExternalAPIUser(request.Token); err != nil {
|
||||
controllers.InternalErrorHandler(w, err)
|
||||
responses.InternalErrorHandler(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "deleted token")
|
||||
responses.WriteSimpleResponse(w, true, "deleted token")
|
||||
}
|
180
webserver/handlers/adminApiFederationConfig.go
Normal file
180
webserver/handlers/adminApiFederationConfig.go
Normal file
|
@ -0,0 +1,180 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"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/webserver/requests"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
)
|
||||
|
||||
// SendFederatedMessage will send a manual message to the fediverse.
|
||||
func (h *Handlers) SendFederatedMessage(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
message, ok := configValue.Value.(string)
|
||||
if !ok {
|
||||
responses.WriteSimpleResponse(w, false, "unable to send message")
|
||||
return
|
||||
}
|
||||
|
||||
if err := activitypub.SendPublicFederatedMessage(message); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "sent")
|
||||
}
|
||||
|
||||
// SetFederationEnabled will set if Federation features are enabled.
|
||||
func (h *Handlers) SetFederationEnabled(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetFederationEnabled(configValue.Value.(bool)); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
responses.WriteSimpleResponse(w, true, "federation features saved")
|
||||
}
|
||||
|
||||
// SetFederationActivityPrivate will set if Federation features are private to followers.
|
||||
func (h *Handlers) SetFederationActivityPrivate(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetFederationIsPrivate(configValue.Value.(bool)); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Update Fediverse followers about this change.
|
||||
if err := outbox.UpdateFollowersWithAccountUpdates(); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "federation private saved")
|
||||
}
|
||||
|
||||
// SetFederationShowEngagement will set if Fedivese engagement shows in chat.
|
||||
func (h *Handlers) SetFederationShowEngagement(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetFederationShowEngagement(configValue.Value.(bool)); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
responses.WriteSimpleResponse(w, true, "federation show engagement saved")
|
||||
}
|
||||
|
||||
// SetFederationUsername will set the local actor username used for federation activities.
|
||||
func (h *Handlers) SetFederationUsername(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetFederationUsername(configValue.Value.(string)); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "username saved")
|
||||
}
|
||||
|
||||
// SetFederationGoLiveMessage will set the federated message sent when the streamer goes live.
|
||||
func (h *Handlers) SetFederationGoLiveMessage(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValue, success := requests.GetValueFromRequest(w, r)
|
||||
if !success {
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetFederationGoLiveMessage(configValue.Value.(string)); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "message saved")
|
||||
}
|
||||
|
||||
// SetFederationBlockDomains saves a list of domains to block on the Fediverse.
|
||||
func (h *Handlers) SetFederationBlockDomains(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
configValues, success := requests.GetValuesFromRequest(w, r)
|
||||
if !success {
|
||||
responses.WriteSimpleResponse(w, false, "unable to handle provided domains")
|
||||
return
|
||||
}
|
||||
|
||||
domainStrings := make([]string, 0)
|
||||
for _, domain := range configValues {
|
||||
domainStrings = append(domainStrings, domain.Value.(string))
|
||||
}
|
||||
|
||||
if err := data.SetBlockedFederatedDomains(domainStrings); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "saved")
|
||||
}
|
||||
|
||||
// GetFederatedActions will return the saved list of accepted inbound
|
||||
// federated activities.
|
||||
func (h *Handlers) GetFederatedActions(page int, pageSize int, w http.ResponseWriter, r *http.Request) {
|
||||
offset := pageSize * page
|
||||
|
||||
activities, total, err := persistence.GetInboundActivities(pageSize, offset)
|
||||
if err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
response := responses.PaginatedResponse{
|
||||
Total: total,
|
||||
Results: activities,
|
||||
}
|
||||
|
||||
responses.WriteResponse(w, response)
|
||||
}
|
|
@ -1,18 +1,19 @@
|
|||
package admin
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/owncast/owncast/activitypub/persistence"
|
||||
"github.com/owncast/owncast/activitypub/requests"
|
||||
"github.com/owncast/owncast/controllers"
|
||||
aprequests "github.com/owncast/owncast/activitypub/requests"
|
||||
"github.com/owncast/owncast/core/data"
|
||||
"github.com/owncast/owncast/webserver/requests"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
)
|
||||
|
||||
// ApproveFollower will approve a federated follow request.
|
||||
func ApproveFollower(w http.ResponseWriter, r *http.Request) {
|
||||
if !requirePOST(w, r) {
|
||||
func (h *Handlers) ApproveFollower(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -24,14 +25,14 @@ func ApproveFollower(w http.ResponseWriter, r *http.Request) {
|
|||
decoder := json.NewDecoder(r.Body)
|
||||
var approval approveFollowerRequest
|
||||
if err := decoder.Decode(&approval); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, "unable to handle follower state with provided values")
|
||||
responses.WriteSimpleResponse(w, false, "unable to handle follower state with provided values")
|
||||
return
|
||||
}
|
||||
|
||||
if approval.Approved {
|
||||
// Approve a follower
|
||||
if err := persistence.ApprovePreviousFollowRequest(approval.ActorIRI); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -39,44 +40,44 @@ func ApproveFollower(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
followRequest, err := persistence.GetFollower(approval.ActorIRI)
|
||||
if err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Send the approval to the follow requestor.
|
||||
if err := requests.SendFollowAccept(followRequest.Inbox, followRequest.RequestObject, localAccountName); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
if err := aprequests.SendFollowAccept(followRequest.Inbox, followRequest.RequestObject, localAccountName); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// Remove/block a follower
|
||||
if err := persistence.BlockOrRejectFollower(approval.ActorIRI); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "follower updated")
|
||||
responses.WriteSimpleResponse(w, true, "follower updated")
|
||||
}
|
||||
|
||||
// GetPendingFollowRequests will return a list of pending follow requests.
|
||||
func GetPendingFollowRequests(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetPendingFollowRequests(w http.ResponseWriter, r *http.Request) {
|
||||
requests, err := persistence.GetPendingFollowRequests()
|
||||
if err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteResponse(w, requests)
|
||||
responses.WriteResponse(w, requests)
|
||||
}
|
||||
|
||||
// GetBlockedAndRejectedFollowers will return blocked and rejected followers.
|
||||
func GetBlockedAndRejectedFollowers(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetBlockedAndRejectedFollowers(w http.ResponseWriter, r *http.Request) {
|
||||
rejections, err := persistence.GetBlockedAndRejectedFollowers()
|
||||
if err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteResponse(w, rejections)
|
||||
responses.WriteResponse(w, rejections)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package admin
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -9,7 +9,7 @@ import (
|
|||
)
|
||||
|
||||
// GetHardwareStats will return hardware utilization over time.
|
||||
func GetHardwareStats(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetHardwareStats(w http.ResponseWriter, r *http.Request) {
|
||||
m := metrics.GetMetrics()
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
|
@ -1,4 +1,4 @@
|
|||
package admin
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -11,7 +11,7 @@ import (
|
|||
)
|
||||
|
||||
// GetLogs will return all logs.
|
||||
func GetLogs(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetLogs(w http.ResponseWriter, r *http.Request) {
|
||||
logs := logging.Logger.AllEntries()
|
||||
response := make([]logsResponse, 0)
|
||||
|
||||
|
@ -26,7 +26,7 @@ func GetLogs(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
// GetWarnings will return only warning and error logs.
|
||||
func GetWarnings(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetWarnings(w http.ResponseWriter, r *http.Request) {
|
||||
logs := logging.Logger.WarningEntries()
|
||||
response := make([]logsResponse, 0)
|
||||
|
61
webserver/handlers/adminApiNotificationsConfig.go
Normal file
61
webserver/handlers/adminApiNotificationsConfig.go
Normal file
|
@ -0,0 +1,61 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/owncast/owncast/core/data"
|
||||
"github.com/owncast/owncast/models"
|
||||
"github.com/owncast/owncast/webserver/requests"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
)
|
||||
|
||||
// SetDiscordNotificationConfiguration will set the discord notification configuration.
|
||||
func (h *Handlers) SetDiscordNotificationConfiguration(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
type request struct {
|
||||
Value models.DiscordConfiguration `json:"value"`
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var config request
|
||||
if err := decoder.Decode(&config); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, "unable to update discord config with provided values")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetDiscordConfig(config.Value); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, "unable to update discord config with provided values")
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "updated discord config with provided values")
|
||||
}
|
||||
|
||||
// SetBrowserNotificationConfiguration will set the browser notification configuration.
|
||||
func (h *Handlers) SetBrowserNotificationConfiguration(w http.ResponseWriter, r *http.Request) {
|
||||
if !requests.RequirePOST(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
type request struct {
|
||||
Value models.BrowserNotificationConfiguration `json:"value"`
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var config request
|
||||
if err := decoder.Decode(&config); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, "unable to update browser push config with provided values")
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.SetBrowserPushConfig(config.Value); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, "unable to update browser push config with provided values")
|
||||
return
|
||||
}
|
||||
|
||||
responses.WriteSimpleResponse(w, true, "updated browser push config with provided values")
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package admin
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -14,7 +14,7 @@ import (
|
|||
)
|
||||
|
||||
// GetServerConfig gets the config details of the server.
|
||||
func GetServerConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetServerConfig(w http.ResponseWriter, r *http.Request) {
|
||||
ffmpeg := utils.ValidatedFfmpegPath(data.GetFfMpegPath())
|
||||
usernameBlocklist := data.GetForbiddenUsernameList()
|
||||
usernameSuggestions := data.GetSuggestedUsernamesList()
|
||||
|
@ -34,7 +34,7 @@ func GetServerConfig(w http.ResponseWriter, r *http.Request) {
|
|||
})
|
||||
}
|
||||
response := serverConfigAdminResponse{
|
||||
InstanceDetails: webConfigResponse{
|
||||
InstanceDetails: adminWebConfigResponse{
|
||||
Name: data.GetServerName(),
|
||||
Summary: data.GetServerSummary(),
|
||||
Tags: data.GetServerMetadataTags(),
|
||||
|
@ -77,7 +77,7 @@ func GetServerConfig(w http.ResponseWriter, r *http.Request) {
|
|||
VideoCodec: data.GetVideoCodec(),
|
||||
ForbiddenUsernames: usernameBlocklist,
|
||||
SuggestedUsernames: usernameSuggestions,
|
||||
Federation: federationConfigResponse{
|
||||
Federation: adminFederationConfigResponse{
|
||||
Enabled: data.GetFederationEnabled(),
|
||||
IsPrivate: data.GetFederationIsPrivate(),
|
||||
Username: data.GetFederationUsername(),
|
||||
|
@ -85,7 +85,7 @@ func GetServerConfig(w http.ResponseWriter, r *http.Request) {
|
|||
ShowEngagement: data.GetFederationShowEngagement(),
|
||||
BlockedDomains: data.GetBlockedFederatedDomains(),
|
||||
},
|
||||
Notifications: notificationsConfigResponse{
|
||||
Notifications: adminNotificationsConfigResponse{
|
||||
Discord: data.GetDiscordConfig(),
|
||||
Browser: data.GetBrowserPushConfig(),
|
||||
},
|
||||
|
@ -100,31 +100,31 @@ func GetServerConfig(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
type serverConfigAdminResponse struct {
|
||||
InstanceDetails webConfigResponse `json:"instanceDetails"`
|
||||
Notifications notificationsConfigResponse `json:"notifications"`
|
||||
YP yp `json:"yp"`
|
||||
FFmpegPath string `json:"ffmpegPath"`
|
||||
AdminPassword string `json:"adminPassword"`
|
||||
SocketHostOverride string `json:"socketHostOverride,omitempty"`
|
||||
WebServerIP string `json:"webServerIP"`
|
||||
VideoCodec string `json:"videoCodec"`
|
||||
VideoServingEndpoint string `json:"videoServingEndpoint"`
|
||||
S3 models.S3 `json:"s3"`
|
||||
Federation federationConfigResponse `json:"federation"`
|
||||
SupportedCodecs []string `json:"supportedCodecs"`
|
||||
ExternalActions []models.ExternalAction `json:"externalActions"`
|
||||
ForbiddenUsernames []string `json:"forbiddenUsernames"`
|
||||
SuggestedUsernames []string `json:"suggestedUsernames"`
|
||||
StreamKeys []models.StreamKey `json:"streamKeys"`
|
||||
VideoSettings videoSettings `json:"videoSettings"`
|
||||
RTMPServerPort int `json:"rtmpServerPort"`
|
||||
WebServerPort int `json:"webServerPort"`
|
||||
ChatDisabled bool `json:"chatDisabled"`
|
||||
ChatJoinMessagesEnabled bool `json:"chatJoinMessagesEnabled"`
|
||||
ChatEstablishedUserMode bool `json:"chatEstablishedUserMode"`
|
||||
DisableSearchIndexing bool `json:"disableSearchIndexing"`
|
||||
StreamKeyOverridden bool `json:"streamKeyOverridden"`
|
||||
HideViewerCount bool `json:"hideViewerCount"`
|
||||
InstanceDetails adminWebConfigResponse `json:"instanceDetails"`
|
||||
Notifications adminNotificationsConfigResponse `json:"notifications"`
|
||||
YP yp `json:"yp"`
|
||||
FFmpegPath string `json:"ffmpegPath"`
|
||||
AdminPassword string `json:"adminPassword"`
|
||||
SocketHostOverride string `json:"socketHostOverride,omitempty"`
|
||||
WebServerIP string `json:"webServerIP"`
|
||||
VideoCodec string `json:"videoCodec"`
|
||||
VideoServingEndpoint string `json:"videoServingEndpoint"`
|
||||
S3 models.S3 `json:"s3"`
|
||||
Federation adminFederationConfigResponse `json:"federation"`
|
||||
SupportedCodecs []string `json:"supportedCodecs"`
|
||||
ExternalActions []models.ExternalAction `json:"externalActions"`
|
||||
ForbiddenUsernames []string `json:"forbiddenUsernames"`
|
||||
SuggestedUsernames []string `json:"suggestedUsernames"`
|
||||
StreamKeys []models.StreamKey `json:"streamKeys"`
|
||||
VideoSettings videoSettings `json:"videoSettings"`
|
||||
RTMPServerPort int `json:"rtmpServerPort"`
|
||||
WebServerPort int `json:"webServerPort"`
|
||||
ChatDisabled bool `json:"chatDisabled"`
|
||||
ChatJoinMessagesEnabled bool `json:"chatJoinMessagesEnabled"`
|
||||
ChatEstablishedUserMode bool `json:"chatEstablishedUserMode"`
|
||||
DisableSearchIndexing bool `json:"disableSearchIndexing"`
|
||||
StreamKeyOverridden bool `json:"streamKeyOverridden"`
|
||||
HideViewerCount bool `json:"hideViewerCount"`
|
||||
}
|
||||
|
||||
type videoSettings struct {
|
||||
|
@ -132,7 +132,7 @@ type videoSettings struct {
|
|||
LatencyLevel int `json:"latencyLevel"`
|
||||
}
|
||||
|
||||
type webConfigResponse struct {
|
||||
type adminWebConfigResponse struct {
|
||||
AppearanceVariables map[string]string `json:"appearanceVariables"`
|
||||
Version string `json:"version"`
|
||||
WelcomeMessage string `json:"welcomeMessage"`
|
||||
|
@ -155,7 +155,7 @@ type yp struct {
|
|||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
type federationConfigResponse struct {
|
||||
type adminFederationConfigResponse struct {
|
||||
Username string `json:"username"`
|
||||
GoLiveMessage string `json:"goLiveMessage"`
|
||||
BlockedDomains []string `json:"blockedDomains"`
|
||||
|
@ -164,7 +164,7 @@ type federationConfigResponse struct {
|
|||
ShowEngagement bool `json:"showEngagement"`
|
||||
}
|
||||
|
||||
type notificationsConfigResponse struct {
|
||||
type adminNotificationsConfigResponse struct {
|
||||
Browser models.BrowserNotificationConfiguration `json:"browser"`
|
||||
Discord models.DiscordConfiguration `json:"discord"`
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package admin
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -10,7 +10,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/owncast/owncast/config"
|
||||
"github.com/owncast/owncast/controllers"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
@ -33,7 +33,7 @@ specific conditions are met.
|
|||
*/
|
||||
|
||||
// AutoUpdateOptions will return what auto update options are available.
|
||||
func AutoUpdateOptions(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) AutoUpdateOptions(w http.ResponseWriter, r *http.Request) {
|
||||
type autoUpdateOptionsResponse struct {
|
||||
SupportsUpdate bool `json:"supportsUpdate"`
|
||||
CanRestart bool `json:"canRestart"`
|
||||
|
@ -47,7 +47,7 @@ func AutoUpdateOptions(w http.ResponseWriter, r *http.Request) {
|
|||
// Nothing is supported when running under "dev" or the feature is
|
||||
// explicitly disabled.
|
||||
if config.BuildPlatform == "dev" || !config.EnableAutoUpdate {
|
||||
controllers.WriteResponse(w, updateOptions)
|
||||
responses.WriteResponse(w, updateOptions)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -58,11 +58,11 @@ func AutoUpdateOptions(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
updateOptions.CanRestart = isRunningUnderSystemD()
|
||||
|
||||
controllers.WriteResponse(w, updateOptions)
|
||||
responses.WriteResponse(w, updateOptions)
|
||||
}
|
||||
|
||||
// AutoUpdateStart will begin the auto update process.
|
||||
func AutoUpdateStart(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) AutoUpdateStart(w http.ResponseWriter, r *http.Request) {
|
||||
// We return the console output directly to the client.
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
|
||||
|
@ -70,7 +70,7 @@ func AutoUpdateStart(w http.ResponseWriter, r *http.Request) {
|
|||
updater, err := downloadInstaller()
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
controllers.WriteSimpleResponse(w, false, "failed to download and run installer")
|
||||
responses.WriteSimpleResponse(w, false, "failed to download and run installer")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -95,12 +95,12 @@ func AutoUpdateStart(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
// AutoUpdateForceQuit will force quit the service.
|
||||
func AutoUpdateForceQuit(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) AutoUpdateForceQuit(w http.ResponseWriter, r *http.Request) {
|
||||
log.Warnln("Owncast is exiting due to request.")
|
||||
go func() {
|
||||
os.Exit(0)
|
||||
}()
|
||||
controllers.WriteSimpleResponse(w, true, "forcing quit")
|
||||
responses.WriteSimpleResponse(w, true, "forcing quit")
|
||||
}
|
||||
|
||||
func downloadInstaller() (string, error) {
|
|
@ -1,4 +1,4 @@
|
|||
package admin
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -13,7 +13,7 @@ import (
|
|||
)
|
||||
|
||||
// Status gets the details of the inbound broadcaster.
|
||||
func Status(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetAdminStatus(w http.ResponseWriter, r *http.Request) {
|
||||
broadcaster := core.GetBroadcaster()
|
||||
status := core.GetStatus()
|
||||
currentBroadcast := core.GetCurrentBroadcast()
|
|
@ -1,4 +1,4 @@
|
|||
package admin
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -11,7 +11,7 @@ import (
|
|||
)
|
||||
|
||||
// GetVideoPlaybackMetrics returns video playback metrics.
|
||||
func GetVideoPlaybackMetrics(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetVideoPlaybackMetrics(w http.ResponseWriter, r *http.Request) {
|
||||
type response struct {
|
||||
Errors []metrics.TimestampedValue `json:"errors"`
|
||||
QualityVariantChanges []metrics.TimestampedValue `json:"qualityVariantChanges"`
|
|
@ -1,4 +1,4 @@
|
|||
package admin
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -6,20 +6,20 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/owncast/owncast/controllers"
|
||||
"github.com/owncast/owncast/core"
|
||||
"github.com/owncast/owncast/core/user"
|
||||
"github.com/owncast/owncast/metrics"
|
||||
"github.com/owncast/owncast/models"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// GetViewersOverTime will return the number of viewers at points in time.
|
||||
func GetViewersOverTime(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetViewersOverTime(w http.ResponseWriter, r *http.Request) {
|
||||
windowStartAtStr := r.URL.Query().Get("windowStart")
|
||||
windowStartAtUnix, err := strconv.Atoi(windowStartAtStr)
|
||||
if err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ func GetViewersOverTime(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
// GetActiveViewers returns currently connected clients.
|
||||
func GetActiveViewers(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetActiveViewers(w http.ResponseWriter, r *http.Request) {
|
||||
c := core.GetActiveViewers()
|
||||
viewers := make([]models.Viewer, 0, len(c))
|
||||
for _, v := range c {
|
||||
|
@ -45,11 +45,11 @@ func GetActiveViewers(w http.ResponseWriter, r *http.Request) {
|
|||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
if err := json.NewEncoder(w).Encode(viewers); err != nil {
|
||||
controllers.InternalErrorHandler(w, err)
|
||||
responses.InternalErrorHandler(w, err)
|
||||
}
|
||||
}
|
||||
|
||||
// ExternalGetActiveViewers returns currently connected clients.
|
||||
func ExternalGetActiveViewers(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
||||
GetConnectedChatClients(w, r)
|
||||
func (h *Handlers) ExternalGetActiveViewers(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
||||
h.GetConnectedChatClients(w, r)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package admin
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -6,9 +6,9 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/owncast/owncast/controllers"
|
||||
"github.com/owncast/owncast/core/data"
|
||||
"github.com/owncast/owncast/models"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
)
|
||||
|
||||
type deleteWebhookRequest struct {
|
||||
|
@ -21,27 +21,27 @@ type createWebhookRequest struct {
|
|||
}
|
||||
|
||||
// CreateWebhook will add a single webhook.
|
||||
func CreateWebhook(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) CreateWebhook(w http.ResponseWriter, r *http.Request) {
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var request createWebhookRequest
|
||||
if err := decoder.Decode(&request); err != nil {
|
||||
controllers.BadRequestHandler(w, err)
|
||||
responses.BadRequestHandler(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Verify all the scopes provided are valid
|
||||
if !models.HasValidEvents(request.Events) {
|
||||
controllers.BadRequestHandler(w, errors.New("one or more invalid event provided"))
|
||||
responses.BadRequestHandler(w, errors.New("one or more invalid event provided"))
|
||||
return
|
||||
}
|
||||
|
||||
newWebhookID, err := data.InsertWebhook(request.URL, request.Events)
|
||||
if err != nil {
|
||||
controllers.InternalErrorHandler(w, err)
|
||||
responses.InternalErrorHandler(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteResponse(w, models.Webhook{
|
||||
responses.WriteResponse(w, models.Webhook{
|
||||
ID: newWebhookID,
|
||||
URL: request.URL,
|
||||
Events: request.Events,
|
||||
|
@ -51,34 +51,34 @@ func CreateWebhook(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
// GetWebhooks will return all webhooks.
|
||||
func GetWebhooks(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetWebhooks(w http.ResponseWriter, r *http.Request) {
|
||||
webhooks, err := data.GetWebhooks()
|
||||
if err != nil {
|
||||
controllers.InternalErrorHandler(w, err)
|
||||
responses.InternalErrorHandler(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteResponse(w, webhooks)
|
||||
responses.WriteResponse(w, webhooks)
|
||||
}
|
||||
|
||||
// DeleteWebhook will delete a single webhook.
|
||||
func DeleteWebhook(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != controllers.POST {
|
||||
controllers.WriteSimpleResponse(w, false, r.Method+" not supported")
|
||||
func (h *Handlers) DeleteWebhook(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
responses.WriteSimpleResponse(w, false, r.Method+" not supported")
|
||||
return
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var request deleteWebhookRequest
|
||||
if err := decoder.Decode(&request); err != nil {
|
||||
controllers.BadRequestHandler(w, err)
|
||||
responses.BadRequestHandler(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := data.DeleteWebhook(request.ID); err != nil {
|
||||
controllers.InternalErrorHandler(w, err)
|
||||
responses.InternalErrorHandler(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "deleted webhook")
|
||||
responses.WriteSimpleResponse(w, true, "deleted webhook")
|
||||
}
|
|
@ -8,10 +8,10 @@ import (
|
|||
"github.com/owncast/owncast/activitypub"
|
||||
"github.com/owncast/owncast/auth"
|
||||
fediverseauth "github.com/owncast/owncast/auth/fediverse"
|
||||
"github.com/owncast/owncast/controllers"
|
||||
"github.com/owncast/owncast/core/chat"
|
||||
"github.com/owncast/owncast/core/data"
|
||||
"github.com/owncast/owncast/core/user"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
@ -23,29 +23,29 @@ func RegisterFediverseOTPRequest(u user.User, w http.ResponseWriter, r *http.Req
|
|||
var req request
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
if err := decoder.Decode(&req); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, "Could not decode request: "+err.Error())
|
||||
responses.WriteSimpleResponse(w, false, "Could not decode request: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
accessToken := r.URL.Query().Get("accessToken")
|
||||
reg, success, err := fediverseauth.RegisterFediverseOTP(accessToken, u.ID, u.DisplayName, req.FediverseAccount)
|
||||
if err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, "Could not register auth request: "+err.Error())
|
||||
responses.WriteSimpleResponse(w, false, "Could not register auth request: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if !success {
|
||||
controllers.WriteSimpleResponse(w, false, "Could not register auth request. One may already be pending. Try again later.")
|
||||
responses.WriteSimpleResponse(w, false, "Could not register auth request. One may already be pending. Try again later.")
|
||||
return
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("<p>This is an automated message from %s. If you did not request this message please ignore or block. Your requested one-time code is:</p><p>%s</p>", data.GetServerName(), reg.Code)
|
||||
if err := activitypub.SendDirectFederatedMessage(msg, reg.Account); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, "Could not send code to fediverse: "+err.Error())
|
||||
responses.WriteSimpleResponse(w, false, "Could not send code to fediverse: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "")
|
||||
responses.WriteSimpleResponse(w, true, "")
|
||||
}
|
||||
|
||||
// VerifyFediverseOTPRequest verifies the given OTP code for the given access token.
|
||||
|
@ -57,7 +57,7 @@ func VerifyFediverseOTPRequest(w http.ResponseWriter, r *http.Request) {
|
|||
var req request
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
if err := decoder.Decode(&req); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, "Could not decode request: "+err.Error())
|
||||
responses.WriteSimpleResponse(w, false, "Could not decode request: "+err.Error())
|
||||
return
|
||||
}
|
||||
accessToken := r.URL.Query().Get("accessToken")
|
||||
|
@ -75,7 +75,7 @@ func VerifyFediverseOTPRequest(w http.ResponseWriter, r *http.Request) {
|
|||
// Update the current user's access token to point to the existing user id.
|
||||
userID := u.ID
|
||||
if err := user.SetAccessTokenToOwner(accessToken, userID); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -86,7 +86,7 @@ func VerifyFediverseOTPRequest(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "")
|
||||
responses.WriteSimpleResponse(w, true, "")
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ func VerifyFediverseOTPRequest(w http.ResponseWriter, r *http.Request) {
|
|||
// Otherwise, save this as new auth.
|
||||
log.Debug("fediverse account does not already exist, saving it as a new one for the current user")
|
||||
if err := auth.AddAuth(authRegistration.UserID, authRegistration.Account, auth.Fediverse); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -104,5 +104,5 @@ func VerifyFediverseOTPRequest(w http.ResponseWriter, r *http.Request) {
|
|||
log.Errorln(err)
|
||||
}
|
||||
|
||||
controllers.WriteSimpleResponse(w, true, "")
|
||||
responses.WriteSimpleResponse(w, true, "")
|
||||
}
|
|
@ -8,9 +8,9 @@ import (
|
|||
|
||||
"github.com/owncast/owncast/auth"
|
||||
ia "github.com/owncast/owncast/auth/indieauth"
|
||||
"github.com/owncast/owncast/controllers"
|
||||
"github.com/owncast/owncast/core/chat"
|
||||
"github.com/owncast/owncast/core/user"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
@ -27,12 +27,12 @@ func StartAuthFlow(u user.User, w http.ResponseWriter, r *http.Request) {
|
|||
var authRequest request
|
||||
p, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(p, &authRequest); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -40,14 +40,14 @@ func StartAuthFlow(u user.User, w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
redirectURL, err := ia.StartAuthFlow(authRequest.AuthHost, u.ID, accessToken, u.DisplayName)
|
||||
if err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
redirectResponse := response{
|
||||
Redirect: redirectURL.String(),
|
||||
}
|
||||
controllers.WriteResponse(w, redirectResponse)
|
||||
responses.WriteResponse(w, redirectResponse)
|
||||
}
|
||||
|
||||
// HandleRedirect will handle the redirect from an IndieAuth server to
|
||||
|
@ -59,7 +59,7 @@ func HandleRedirect(w http.ResponseWriter, r *http.Request) {
|
|||
if err != nil {
|
||||
log.Debugln(err)
|
||||
msg := `Unable to complete authentication. <a href="/">Go back.</a><hr/>`
|
||||
_ = controllers.WriteString(w, msg, http.StatusBadRequest)
|
||||
_ = responses.WriteString(w, msg, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -72,7 +72,7 @@ func HandleRedirect(w http.ResponseWriter, r *http.Request) {
|
|||
accessToken := request.CurrentAccessToken
|
||||
userID := u.ID
|
||||
if err := user.SetAccessTokenToOwner(accessToken, userID); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,7 @@ func HandleRedirect(w http.ResponseWriter, r *http.Request) {
|
|||
// Otherwise, save this as new auth.
|
||||
log.Debug("indieauth token does not already exist, saving it as a new one for the current user")
|
||||
if err := auth.AddAuth(request.UserID, response.Me, auth.IndieAuth); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
|
@ -5,8 +5,8 @@ import (
|
|||
"net/url"
|
||||
|
||||
ia "github.com/owncast/owncast/auth/indieauth"
|
||||
"github.com/owncast/owncast/controllers"
|
||||
"github.com/owncast/owncast/webserver/middleware"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
)
|
||||
|
||||
// HandleAuthEndpoint will handle the IndieAuth auth endpoint.
|
||||
|
@ -33,7 +33,7 @@ func handleAuthEndpointGet(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
request, err := ia.StartServerAuth(clientID, redirectURI, codeChallenge, state, me)
|
||||
if err != nil {
|
||||
_ = controllers.WriteString(w, err.Error(), http.StatusInternalServerError)
|
||||
_ = responses.WriteString(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ func handleAuthEndpointGet(w http.ResponseWriter, r *http.Request) {
|
|||
// If the URL is invalid then return with specific "invalid_request" error.
|
||||
u, err := url.Parse(redirectURI)
|
||||
if err != nil {
|
||||
controllers.WriteResponse(w, ia.Response{
|
||||
responses.WriteResponse(w, ia.Response{
|
||||
Error: "invalid_request",
|
||||
ErrorDescription: err.Error(),
|
||||
})
|
||||
|
@ -59,7 +59,7 @@ func handleAuthEndpointGet(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
func handleAuthEndpointPost(w http.ResponseWriter, r *http.Request) {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -72,12 +72,12 @@ func handleAuthEndpointPost(w http.ResponseWriter, r *http.Request) {
|
|||
// "invalid_client" error.
|
||||
response, err := ia.CompleteServerAuth(code, redirectURI, clientID, codeVerifier)
|
||||
if err != nil {
|
||||
controllers.WriteResponse(w, ia.Response{
|
||||
responses.WriteResponse(w, ia.Response{
|
||||
Error: "invalid_client",
|
||||
ErrorDescription: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
controllers.WriteResponse(w, response)
|
||||
responses.WriteResponse(w, response)
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
package controllers
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/owncast/owncast/config"
|
||||
|
@ -9,17 +10,18 @@ import (
|
|||
"github.com/owncast/owncast/core/user"
|
||||
"github.com/owncast/owncast/utils"
|
||||
"github.com/owncast/owncast/webserver/middleware"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// ExternalGetChatMessages gets all of the chat messages.
|
||||
func ExternalGetChatMessages(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) ExternalGetChatMessages(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
||||
middleware.EnableCors(w)
|
||||
getChatMessages(w, r)
|
||||
}
|
||||
|
||||
// GetChatMessages gets all of the chat messages.
|
||||
func GetChatMessages(u user.User, w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetChatMessages(u user.User, w http.ResponseWriter, r *http.Request) {
|
||||
middleware.EnableCors(w)
|
||||
getChatMessages(w, r)
|
||||
}
|
||||
|
@ -36,14 +38,12 @@ func getChatMessages(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
if err := json.NewEncoder(w).Encode(j{"error": "method not implemented (PRs are accepted)"}); err != nil {
|
||||
InternalErrorHandler(w, err)
|
||||
}
|
||||
responses.BadRequestHandler(w, errors.New("method not implemented"))
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterAnonymousChatUser will register a new user.
|
||||
func RegisterAnonymousChatUser(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) RegisterAnonymousChatUser(w http.ResponseWriter, r *http.Request) {
|
||||
middleware.EnableCors(w)
|
||||
|
||||
if r.Method == "OPTIONS" {
|
||||
|
@ -54,8 +54,7 @@ func RegisterAnonymousChatUser(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
if r.Method != http.MethodPost {
|
||||
// nolint:goconst
|
||||
WriteSimpleResponse(w, false, r.Method+" not supported")
|
||||
responses.WriteSimpleResponse(w, false, r.Method+" not supported")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -82,7 +81,7 @@ func RegisterAnonymousChatUser(w http.ResponseWriter, r *http.Request) {
|
|||
proposedNewDisplayName := utils.MakeSafeStringOfLength(request.DisplayName, config.MaxChatDisplayNameLength)
|
||||
newUser, accessToken, err := user.CreateAnonymousUser(proposedNewDisplayName)
|
||||
if err != nil {
|
||||
WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -95,5 +94,5 @@ func RegisterAnonymousChatUser(w http.ResponseWriter, r *http.Request) {
|
|||
w.Header().Set("Content-Type", "application/json")
|
||||
middleware.DisableCache(w)
|
||||
|
||||
WriteResponse(w, response)
|
||||
responses.WriteResponse(w, response)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package controllers
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/owncast/owncast/models"
|
||||
"github.com/owncast/owncast/utils"
|
||||
"github.com/owncast/owncast/webserver/middleware"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
@ -58,7 +59,7 @@ type authenticationConfigResponse struct {
|
|||
}
|
||||
|
||||
// GetWebConfig gets the status of the server.
|
||||
func GetWebConfig(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetWebConfig(w http.ResponseWriter, r *http.Request) {
|
||||
middleware.EnableCors(w)
|
||||
middleware.DisableCache(w)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
@ -66,7 +67,7 @@ func GetWebConfig(w http.ResponseWriter, r *http.Request) {
|
|||
configuration := getConfigResponse()
|
||||
|
||||
if err := json.NewEncoder(w).Encode(configuration); err != nil {
|
||||
BadRequestHandler(w, err)
|
||||
responses.BadRequestHandler(w, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,12 +143,12 @@ func getConfigResponse() webConfigResponse {
|
|||
}
|
||||
|
||||
// GetAllSocialPlatforms will return a list of all social platform types.
|
||||
func GetAllSocialPlatforms(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetAllSocialPlatforms(w http.ResponseWriter, r *http.Request) {
|
||||
middleware.EnableCors(w)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
platforms := models.GetAllSocialHandles()
|
||||
if err := json.NewEncoder(w).Encode(platforms); err != nil {
|
||||
InternalErrorHandler(w, err)
|
||||
responses.InternalErrorHandler(w, err)
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package controllers
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
@ -7,7 +7,7 @@ import (
|
|||
)
|
||||
|
||||
// ServeCustomJavascript will serve optional custom Javascript.
|
||||
func ServeCustomJavascript(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) ServeCustomJavascript(w http.ResponseWriter, r *http.Request) {
|
||||
js := data.GetCustomJavascript()
|
||||
_, _ = w.Write([]byte(js))
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
package handlers
|
21
webserver/handlers/disconnect.go
Normal file
21
webserver/handlers/disconnect.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/owncast/owncast/core"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
|
||||
"github.com/owncast/owncast/core/rtmp"
|
||||
)
|
||||
|
||||
// DisconnectInboundConnection will force-disconnect an inbound stream.
|
||||
func (h *Handlers) DisconnectInboundConnection(w http.ResponseWriter, r *http.Request) {
|
||||
if !core.GetStatus().Online {
|
||||
responses.WriteSimpleResponse(w, false, "no inbound stream connected")
|
||||
return
|
||||
}
|
||||
|
||||
rtmp.Disconnect()
|
||||
responses.WriteSimpleResponse(w, true, "inbound stream disconnected")
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package controllers
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -9,20 +9,21 @@ import (
|
|||
"github.com/owncast/owncast/config"
|
||||
"github.com/owncast/owncast/core/data"
|
||||
"github.com/owncast/owncast/router/middleware"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
)
|
||||
|
||||
// GetCustomEmojiList returns a list of emoji via the API.
|
||||
func GetCustomEmojiList(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetCustomEmojiList(w http.ResponseWriter, r *http.Request) {
|
||||
emojiList := data.GetEmojiList()
|
||||
middleware.SetCachingHeaders(w, r)
|
||||
|
||||
if err := json.NewEncoder(w).Encode(emojiList); err != nil {
|
||||
InternalErrorHandler(w, err)
|
||||
responses.InternalErrorHandler(w, err)
|
||||
}
|
||||
}
|
||||
|
||||
// GetCustomEmojiImage returns a single emoji image.
|
||||
func GetCustomEmojiImage(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetCustomEmojiImage(w http.ResponseWriter, r *http.Request) {
|
||||
path := strings.TrimPrefix(r.URL.Path, "/img/emoji/")
|
||||
r.URL.Path = path
|
||||
|
|
@ -1 +0,0 @@
|
|||
package handlers
|
|
@ -1,22 +1,23 @@
|
|||
package controllers
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/owncast/owncast/activitypub/persistence"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
)
|
||||
|
||||
// GetFollowers will handle an API request to fetch the list of followers (non-activitypub response).
|
||||
func GetFollowers(offset int, limit int, w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetFollowers(offset int, limit int, w http.ResponseWriter, r *http.Request) {
|
||||
followers, total, err := persistence.GetFederationFollowers(limit, offset)
|
||||
if err != nil {
|
||||
WriteSimpleResponse(w, false, "unable to fetch followers")
|
||||
responses.WriteSimpleResponse(w, false, "unable to fetch followers")
|
||||
return
|
||||
}
|
||||
|
||||
response := PaginatedResponse{
|
||||
response := responses.PaginatedResponse{
|
||||
Total: total,
|
||||
Results: followers,
|
||||
}
|
||||
WriteResponse(w, response)
|
||||
responses.WriteResponse(w, response)
|
||||
}
|
|
@ -1 +1,55 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"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/utils"
|
||||
"github.com/owncast/owncast/webserver/middleware"
|
||||
)
|
||||
|
||||
// HandleHLSRequest will manage all requests to HLS content.
|
||||
func (h *Handlers) HandleHLSRequest(w http.ResponseWriter, r *http.Request) {
|
||||
// Sanity check to limit requests to HLS file types.
|
||||
if filepath.Ext(r.URL.Path) != ".m3u8" && filepath.Ext(r.URL.Path) != ".ts" {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
requestedPath := r.URL.Path
|
||||
relativePath := strings.Replace(requestedPath, "/hls/", "", 1)
|
||||
fullPath := filepath.Join(config.HLSStoragePath, relativePath)
|
||||
|
||||
// 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" {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle playlists
|
||||
if path.Ext(r.URL.Path) == ".m3u8" {
|
||||
// Playlists should never be cached.
|
||||
middleware.DisableCache(w)
|
||||
|
||||
// Force the correct content type
|
||||
w.Header().Set("Content-Type", "application/x-mpegURL")
|
||||
|
||||
// Use this as an opportunity to mark this viewer as active.
|
||||
viewer := models.GenerateViewerFromRequest(r)
|
||||
core.SetViewerActive(&viewer)
|
||||
} else {
|
||||
cacheTime := utils.GetCacheDurationSecondsForPath(relativePath)
|
||||
w.Header().Set("Cache-Control", "public, max-age="+strconv.Itoa(cacheTime))
|
||||
}
|
||||
|
||||
middleware.EnableCors(w)
|
||||
http.ServeFile(w, r, fullPath)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package controllers
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
"github.com/owncast/owncast/config"
|
||||
"github.com/owncast/owncast/utils"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -14,7 +15,7 @@ const (
|
|||
)
|
||||
|
||||
// GetThumbnail will return the thumbnail image as a response.
|
||||
func GetThumbnail(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetThumbnail(w http.ResponseWriter, r *http.Request) {
|
||||
imageFilename := "thumbnail.jpg"
|
||||
imagePath := filepath.Join(config.TempDir, imageFilename)
|
||||
|
||||
|
@ -24,21 +25,21 @@ func GetThumbnail(w http.ResponseWriter, r *http.Request) {
|
|||
if utils.DoesFileExists(imagePath) {
|
||||
imageBytes, err = getImage(imagePath)
|
||||
} else {
|
||||
GetLogo(w, r)
|
||||
h.GetLogo(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
GetLogo(w, r)
|
||||
h.GetLogo(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
cacheTime := utils.GetCacheDurationSecondsForPath(imagePath)
|
||||
writeBytesAsImage(imageBytes, contentTypeJPEG, w, cacheTime)
|
||||
responses.WriteBytesAsImage(imageBytes, contentTypeJPEG, w, cacheTime)
|
||||
}
|
||||
|
||||
// GetPreview will return the preview gif as a response.
|
||||
func GetPreview(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetPreview(w http.ResponseWriter, r *http.Request) {
|
||||
imageFilename := "preview.gif"
|
||||
imagePath := filepath.Join(config.TempDir, imageFilename)
|
||||
|
||||
|
@ -48,15 +49,15 @@ func GetPreview(w http.ResponseWriter, r *http.Request) {
|
|||
if utils.DoesFileExists(imagePath) {
|
||||
imageBytes, err = getImage(imagePath)
|
||||
} else {
|
||||
GetLogo(w, r)
|
||||
h.GetLogo(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
GetLogo(w, r)
|
||||
h.GetLogo(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
cacheTime := utils.GetCacheDurationSecondsForPath(imagePath)
|
||||
writeBytesAsImage(imageBytes, contentTypeGIF, w, cacheTime)
|
||||
responses.WriteBytesAsImage(imageBytes, contentTypeGIF, w, cacheTime)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package controllers
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -19,7 +19,7 @@ import (
|
|||
)
|
||||
|
||||
// IndexHandler handles the default index route.
|
||||
func IndexHandler(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) IndexHandler(w http.ResponseWriter, r *http.Request) {
|
||||
middleware.EnableCors(w)
|
||||
|
||||
isIndexRequest := r.URL.Path == "/" || filepath.Base(r.URL.Path) == "index.html" || filepath.Base(r.URL.Path) == ""
|
|
@ -1 +0,0 @@
|
|||
package handlers
|
|
@ -1,22 +1,22 @@
|
|||
package controllers
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/owncast/owncast/config"
|
||||
"github.com/owncast/owncast/core/data"
|
||||
"github.com/owncast/owncast/static"
|
||||
"github.com/owncast/owncast/utils"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var _hasWarnedSVGLogo = false
|
||||
|
||||
// GetLogo will return the logo image as a response.
|
||||
func GetLogo(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetLogo(w http.ResponseWriter, r *http.Request) {
|
||||
imageFilename := data.GetLogoPath()
|
||||
if imageFilename == "" {
|
||||
returnDefault(w)
|
||||
|
@ -39,20 +39,20 @@ func GetLogo(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
cacheTime := utils.GetCacheDurationSecondsForPath(imagePath)
|
||||
writeBytesAsImage(imageBytes, contentType, w, cacheTime)
|
||||
responses.WriteBytesAsImage(imageBytes, contentType, w, cacheTime)
|
||||
}
|
||||
|
||||
// GetCompatibleLogo will return the logo unless it's a SVG
|
||||
// and in that case will return a default placeholder.
|
||||
// Used for sharing to external social networks that generally
|
||||
// don't support SVG.
|
||||
func GetCompatibleLogo(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetCompatibleLogo(w http.ResponseWriter, r *http.Request) {
|
||||
imageFilename := data.GetLogoPath()
|
||||
|
||||
// If the logo image is not a SVG then we can return it
|
||||
// without any problems.
|
||||
if imageFilename != "" && filepath.Ext(imageFilename) != ".svg" {
|
||||
GetLogo(w, r)
|
||||
h.GetLogo(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,7 @@ func GetCompatibleLogo(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
cacheTime := utils.GetCacheDurationSecondsForPath(imagePath)
|
||||
writeBytesAsImage(imageBytes, contentType, w, cacheTime)
|
||||
responses.WriteBytesAsImage(imageBytes, contentType, w, cacheTime)
|
||||
|
||||
if !_hasWarnedSVGLogo {
|
||||
log.Warnf("an external site requested your logo. because many social networks do not support SVGs we returned a placeholder instead. change your current logo to a png or jpeg to be most compatible with external social networking sites.")
|
||||
|
@ -77,17 +77,7 @@ func GetCompatibleLogo(w http.ResponseWriter, r *http.Request) {
|
|||
func returnDefault(w http.ResponseWriter) {
|
||||
imageBytes := static.GetLogo()
|
||||
cacheTime := utils.GetCacheDurationSecondsForPath("logo.png")
|
||||
writeBytesAsImage(imageBytes, "image/png", w, cacheTime)
|
||||
}
|
||||
|
||||
func writeBytesAsImage(data []byte, contentType string, w http.ResponseWriter, cacheSeconds int) {
|
||||
w.Header().Set("Content-Type", contentType)
|
||||
w.Header().Set("Content-Length", strconv.Itoa(len(data)))
|
||||
w.Header().Set("Cache-Control", "public, max-age="+strconv.Itoa(cacheSeconds))
|
||||
|
||||
if _, err := w.Write(data); err != nil {
|
||||
log.Println("unable to write image.")
|
||||
}
|
||||
responses.WriteBytesAsImage(imageBytes, "image/png", w, cacheTime)
|
||||
}
|
||||
|
||||
func getImage(path string) ([]byte, error) {
|
|
@ -1,4 +1,4 @@
|
|||
package moderation
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -6,15 +6,15 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/owncast/owncast/controllers"
|
||||
"github.com/owncast/owncast/core/chat"
|
||||
"github.com/owncast/owncast/core/chat/events"
|
||||
"github.com/owncast/owncast/core/user"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// GetUserDetails returns the details of a chat user for moderators.
|
||||
func GetUserDetails(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetUserDetails(w http.ResponseWriter, r *http.Request) {
|
||||
type connectedClient struct {
|
||||
ConnectedAt time.Time `json:"connectedAt"`
|
||||
UserAgent string `json:"userAgent"`
|
||||
|
@ -68,6 +68,6 @@ func GetUserDetails(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if err := json.NewEncoder(w).Encode(res); err != nil {
|
||||
controllers.InternalErrorHandler(w, err)
|
||||
responses.InternalErrorHandler(w, err)
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package controllers
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
"github.com/owncast/owncast/core/user"
|
||||
"github.com/owncast/owncast/notifications"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
|
||||
"github.com/owncast/owncast/utils"
|
||||
|
||||
|
@ -14,9 +15,9 @@ import (
|
|||
|
||||
// RegisterForLiveNotifications will register a channel + destination to be
|
||||
// notified when a stream goes live.
|
||||
func RegisterForLiveNotifications(u user.User, w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != POST {
|
||||
WriteSimpleResponse(w, false, r.Method+" not supported")
|
||||
func (h *Handlers) RegisterForLiveNotifications(u user.User, w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
responses.WriteSimpleResponse(w, false, r.Method+" not supported")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -31,7 +32,7 @@ func RegisterForLiveNotifications(u user.User, w http.ResponseWriter, r *http.Re
|
|||
var req request
|
||||
if err := decoder.Decode(&req); err != nil {
|
||||
log.Errorln(err)
|
||||
WriteSimpleResponse(w, false, "unable to register for notifications")
|
||||
responses.WriteSimpleResponse(w, false, "unable to register for notifications")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -39,13 +40,13 @@ func RegisterForLiveNotifications(u user.User, w http.ResponseWriter, r *http.Re
|
|||
validTypes := []string{notifications.BrowserPushNotification}
|
||||
_, validChannel := utils.FindInSlice(validTypes, req.Channel)
|
||||
if !validChannel {
|
||||
WriteSimpleResponse(w, false, "invalid notification channel: "+req.Channel)
|
||||
responses.WriteSimpleResponse(w, false, "invalid notification channel: "+req.Channel)
|
||||
return
|
||||
}
|
||||
|
||||
if err := notifications.AddNotification(req.Channel, req.Destination); err != nil {
|
||||
log.Errorln(err)
|
||||
WriteSimpleResponse(w, false, "unable to save notification")
|
||||
responses.WriteSimpleResponse(w, false, "unable to save notification")
|
||||
return
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package controllers
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
@ -8,7 +8,7 @@ import (
|
|||
)
|
||||
|
||||
// Ping is fired by a client to show they are still an active viewer.
|
||||
func Ping(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) Ping(w http.ResponseWriter, r *http.Request) {
|
||||
viewer := models.GenerateViewerFromRequest(r)
|
||||
core.SetViewerActive(&viewer)
|
||||
w.WriteHeader(http.StatusOK)
|
|
@ -1,4 +1,4 @@
|
|||
package controllers
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -6,14 +6,15 @@ import (
|
|||
|
||||
"github.com/owncast/owncast/metrics"
|
||||
"github.com/owncast/owncast/utils"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// ReportPlaybackMetrics will accept playback metrics from a client and save
|
||||
// them for future video health reporting.
|
||||
func ReportPlaybackMetrics(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != POST {
|
||||
WriteSimpleResponse(w, false, r.Method+" not supported")
|
||||
func (h *Handlers) ReportPlaybackMetrics(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
responses.WriteSimpleResponse(w, false, r.Method+" not supported")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -29,7 +30,7 @@ func ReportPlaybackMetrics(w http.ResponseWriter, r *http.Request) {
|
|||
var request reportPlaybackMetricsRequest
|
||||
if err := decoder.Decode(&request); err != nil {
|
||||
log.Errorln("error decoding playback metrics payload:", err)
|
||||
WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package controllers
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -9,10 +9,11 @@ import (
|
|||
|
||||
"github.com/owncast/owncast/activitypub/webfinger"
|
||||
"github.com/owncast/owncast/core/data"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
)
|
||||
|
||||
// RemoteFollow handles a request to begin the remote follow redirect flow.
|
||||
func RemoteFollow(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) RemoteFollow(w http.ResponseWriter, r *http.Request) {
|
||||
type followRequest struct {
|
||||
Account string `json:"account"`
|
||||
}
|
||||
|
@ -24,12 +25,12 @@ func RemoteFollow(w http.ResponseWriter, r *http.Request) {
|
|||
var request followRequest
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
if err := decoder.Decode(&request); err != nil {
|
||||
WriteSimpleResponse(w, false, "unable to parse request")
|
||||
responses.WriteSimpleResponse(w, false, "unable to parse request")
|
||||
return
|
||||
}
|
||||
|
||||
if request.Account == "" {
|
||||
WriteSimpleResponse(w, false, "Remote Fediverse account is required to follow.")
|
||||
responses.WriteSimpleResponse(w, false, "Remote Fediverse account is required to follow.")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -38,7 +39,7 @@ func RemoteFollow(w http.ResponseWriter, r *http.Request) {
|
|||
var template string
|
||||
links, err := webfinger.GetWebfingerLinks(request.Account)
|
||||
if err != nil {
|
||||
WriteSimpleResponse(w, false, err.Error())
|
||||
responses.WriteSimpleResponse(w, false, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -52,7 +53,7 @@ func RemoteFollow(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
if localActorPath.String() == "" || template == "" {
|
||||
WriteSimpleResponse(w, false, "unable to determine remote follow information for "+request.Account)
|
||||
responses.WriteSimpleResponse(w, false, "unable to determine remote follow information for "+request.Account)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -61,5 +62,5 @@ func RemoteFollow(w http.ResponseWriter, r *http.Request) {
|
|||
RedirectURL: redirectURL,
|
||||
}
|
||||
|
||||
WriteResponse(w, response)
|
||||
responses.WriteResponse(w, response)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package controllers
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
@ -8,7 +8,7 @@ import (
|
|||
)
|
||||
|
||||
// GetRobotsDotTxt returns the contents of our robots.txt.
|
||||
func GetRobotsDotTxt(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetRobotsDotTxt(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
contents := []string{
|
||||
"User-agent: *",
|
|
@ -1 +0,0 @@
|
|||
package handlers
|
|
@ -1,4 +1,4 @@
|
|||
package controllers
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -9,17 +9,18 @@ import (
|
|||
"github.com/owncast/owncast/core/data"
|
||||
"github.com/owncast/owncast/utils"
|
||||
"github.com/owncast/owncast/webserver/middleware"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
)
|
||||
|
||||
// GetStatus gets the status of the server.
|
||||
func GetStatus(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetStatus(w http.ResponseWriter, r *http.Request) {
|
||||
response := getStatusResponse()
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
middleware.DisableCache(w)
|
||||
|
||||
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||||
InternalErrorHandler(w, err)
|
||||
responses.InternalErrorHandler(w, err)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package handlers
|
||||
|
||||
import "net/http"
|
||||
|
||||
func (s *Handlers) HandleTesting(w http.ResponseWriter, r *http.Request) {
|
||||
_, _ = w.Write([]byte("testing"))
|
||||
}
|
|
@ -1,10 +1,11 @@
|
|||
package controllers
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"sort"
|
||||
|
||||
"github.com/owncast/owncast/core/data"
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
)
|
||||
|
||||
type variantsSort struct {
|
||||
|
@ -20,7 +21,7 @@ type variantsResponse struct {
|
|||
}
|
||||
|
||||
// GetVideoStreamOutputVariants will return the video variants available.
|
||||
func GetVideoStreamOutputVariants(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetVideoStreamOutputVariants(w http.ResponseWriter, r *http.Request) {
|
||||
outputVariants := data.GetStreamOutputVariants()
|
||||
|
||||
streamSortVariants := make([]variantsSort, len(outputVariants))
|
||||
|
@ -55,5 +56,5 @@ func GetVideoStreamOutputVariants(w http.ResponseWriter, r *http.Request) {
|
|||
response[i] = variantResponse
|
||||
}
|
||||
|
||||
WriteResponse(w, response)
|
||||
responses.WriteResponse(w, response)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package controllers
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
|
@ -1 +0,0 @@
|
|||
package handlers
|
|
@ -1,9 +1,10 @@
|
|||
package yp
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/owncast/owncast/core"
|
||||
"github.com/owncast/owncast/core/data"
|
||||
"github.com/owncast/owncast/models"
|
||||
"github.com/owncast/owncast/utils"
|
||||
|
@ -27,13 +28,13 @@ type ypDetailsResponse struct {
|
|||
}
|
||||
|
||||
// GetYPResponse gets the status of the server for YP purposes.
|
||||
func GetYPResponse(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *Handlers) GetYPResponse(w http.ResponseWriter, r *http.Request) {
|
||||
if !data.GetDirectoryEnabled() {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
status := getStatus()
|
||||
status := core.GetStatus()
|
||||
|
||||
streamTitle := data.GetStreamTitle()
|
||||
|
6
webserver/requests/requestConfigValue.go
Normal file
6
webserver/requests/requestConfigValue.go
Normal file
|
@ -0,0 +1,6 @@
|
|||
package requests
|
||||
|
||||
// ConfigValue is a container object that holds a value, is encoded, and saved to the database.
|
||||
type ConfigValue struct {
|
||||
Value interface{} `json:"value"`
|
||||
}
|
50
webserver/requests/requests.go
Normal file
50
webserver/requests/requests.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
package requests
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
"github.com/owncast/owncast/webserver/responses"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func RequirePOST(w http.ResponseWriter, r *http.Request) bool {
|
||||
if r.Method != http.MethodPost {
|
||||
responses.WriteSimpleResponse(w, false, r.Method+" not supported")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func GetValueFromRequest(w http.ResponseWriter, r *http.Request) (ConfigValue, bool) {
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var configValue ConfigValue
|
||||
if err := decoder.Decode(&configValue); err != nil {
|
||||
log.Warnln(err)
|
||||
responses.WriteSimpleResponse(w, false, "unable to parse new value")
|
||||
return configValue, false
|
||||
}
|
||||
|
||||
return configValue, true
|
||||
}
|
||||
|
||||
func GetValuesFromRequest(w http.ResponseWriter, r *http.Request) ([]ConfigValue, bool) {
|
||||
var values []ConfigValue
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
var configValue ConfigValue
|
||||
if err := decoder.Decode(&configValue); err != nil {
|
||||
responses.WriteSimpleResponse(w, false, "unable to parse array of values")
|
||||
return values, false
|
||||
}
|
||||
|
||||
object := reflect.ValueOf(configValue.Value)
|
||||
|
||||
for i := 0; i < object.Len(); i++ {
|
||||
values = append(values, ConfigValue{Value: object.Index(i).Interface()})
|
||||
}
|
||||
|
||||
return values, true
|
||||
}
|
17
webserver/responses/image.go
Normal file
17
webserver/responses/image.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
package responses
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func WriteBytesAsImage(data []byte, contentType string, w http.ResponseWriter, cacheSeconds int) {
|
||||
w.Header().Set("Content-Type", contentType)
|
||||
w.Header().Set("Content-Length", strconv.Itoa(len(data)))
|
||||
w.Header().Set("Cache-Control", "public, max-age="+strconv.Itoa(cacheSeconds))
|
||||
|
||||
if _, err := w.Write(data); err != nil {
|
||||
log.Println("unable to write image.")
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package controllers
|
||||
package responses
|
||||
|
||||
// PaginatedResponse is a structure for returning a total count with results.
|
||||
type PaginatedResponse struct {
|
|
@ -1,4 +1,4 @@
|
|||
package controllers
|
||||
package responses
|
||||
|
||||
import (
|
||||
"encoding/json"
|
|
@ -5,30 +5,24 @@ import (
|
|||
|
||||
"github.com/owncast/owncast/activitypub"
|
||||
"github.com/owncast/owncast/config"
|
||||
"github.com/owncast/owncast/controllers"
|
||||
"github.com/owncast/owncast/controllers/admin"
|
||||
fediverseauth "github.com/owncast/owncast/controllers/auth/fediverse"
|
||||
"github.com/owncast/owncast/controllers/auth/indieauth"
|
||||
"github.com/owncast/owncast/controllers/moderation"
|
||||
"github.com/owncast/owncast/core/chat"
|
||||
"github.com/owncast/owncast/core/data"
|
||||
"github.com/owncast/owncast/core/user"
|
||||
"github.com/owncast/owncast/utils"
|
||||
fediverseauth "github.com/owncast/owncast/webserver/handlers/auth/fediverse"
|
||||
"github.com/owncast/owncast/webserver/handlers/auth/indieauth"
|
||||
"github.com/owncast/owncast/webserver/middleware"
|
||||
"github.com/owncast/owncast/yp"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
func (s *webServer) setupRoutes() {
|
||||
s.router.HandleFunc("/test", s.handlers.HandleTesting)
|
||||
|
||||
s.setupWebAssetRoutes()
|
||||
s.setupInternalAPIRoutes()
|
||||
s.setupAdminAPIRoutes()
|
||||
s.setupExternalThirdPartyAPIRoutes()
|
||||
s.setupModerationAPIRoutes()
|
||||
|
||||
s.router.HandleFunc("/hls/", controllers.HandleHLSRequest)
|
||||
s.router.HandleFunc("/hls/", s.handlers.HandleHLSRequest)
|
||||
|
||||
// websocket
|
||||
s.router.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -45,7 +39,7 @@ func (s *webServer) setupRoutes() {
|
|||
// Redirect /embed/chat
|
||||
http.Redirect(w, r, "/embed/chat/readonly", http.StatusTemporaryRedirect)
|
||||
} else {
|
||||
controllers.IndexHandler(w, r)
|
||||
s.handlers.IndexHandler(w, r)
|
||||
// s.ServeHTTP(w, r)
|
||||
}
|
||||
})
|
||||
|
@ -56,26 +50,26 @@ func (s *webServer) setupRoutes() {
|
|||
|
||||
func (s *webServer) setupWebAssetRoutes() {
|
||||
// The admin web app.
|
||||
s.router.HandleFunc("/admin/", middleware.RequireAdminAuth(controllers.IndexHandler))
|
||||
s.router.HandleFunc("/admin/", middleware.RequireAdminAuth(s.handlers.IndexHandler))
|
||||
|
||||
// Images
|
||||
s.router.HandleFunc("/thumbnail.jpg", controllers.GetThumbnail)
|
||||
s.router.HandleFunc("/preview.gif", controllers.GetPreview)
|
||||
s.router.HandleFunc("/logo", controllers.GetLogo)
|
||||
s.router.HandleFunc("/thumbnail.jpg", s.handlers.GetThumbnail)
|
||||
s.router.HandleFunc("/preview.gif", s.handlers.GetPreview)
|
||||
s.router.HandleFunc("/logo", s.handlers.GetLogo)
|
||||
|
||||
// Custom Javascript
|
||||
s.router.HandleFunc("/customjavascript", controllers.ServeCustomJavascript)
|
||||
s.router.HandleFunc("/customjavascript", s.handlers.ServeCustomJavascript)
|
||||
|
||||
// Return a single emoji image.
|
||||
s.router.HandleFunc(config.EmojiDir, controllers.GetCustomEmojiImage)
|
||||
s.router.HandleFunc(config.EmojiDir, s.handlers.GetCustomEmojiImage)
|
||||
|
||||
// return the logo
|
||||
|
||||
// return a logo that's compatible with external social networks
|
||||
s.router.HandleFunc("/logo/external", controllers.GetCompatibleLogo)
|
||||
s.router.HandleFunc("/logo/external", s.handlers.GetCompatibleLogo)
|
||||
|
||||
// robots.txt
|
||||
s.router.HandleFunc("/robots.txt", controllers.GetRobotsDotTxt)
|
||||
s.router.HandleFunc("/robots.txt", s.handlers.GetRobotsDotTxt)
|
||||
|
||||
// Optional public static files
|
||||
s.router.Handle("/public/", http.StripPrefix("/public/", http.FileServer(http.Dir(config.PublicFilesPath))))
|
||||
|
@ -85,289 +79,289 @@ func (s *webServer) setupInternalAPIRoutes() {
|
|||
// Internal APIs
|
||||
|
||||
// status of the system
|
||||
s.router.HandleFunc("/api/status", controllers.GetStatus)
|
||||
s.router.HandleFunc("/api/status", s.handlers.GetStatus)
|
||||
|
||||
// custom emoji supported in the chat
|
||||
s.router.HandleFunc("/api/emoji", controllers.GetCustomEmojiList)
|
||||
s.router.HandleFunc("/api/emoji", s.handlers.GetCustomEmojiList)
|
||||
|
||||
// chat history api
|
||||
s.router.HandleFunc("/api/chat", middleware.RequireUserAccessToken(controllers.GetChatMessages))
|
||||
s.router.HandleFunc("/api/chat", middleware.RequireUserAccessToken(s.handlers.GetChatMessages))
|
||||
|
||||
// web config api
|
||||
s.router.HandleFunc("/api/config", controllers.GetWebConfig)
|
||||
s.router.HandleFunc("/api/config", s.handlers.GetWebConfig)
|
||||
|
||||
// return the YP protocol data
|
||||
s.router.HandleFunc("/api/yp", yp.GetYPResponse)
|
||||
s.router.HandleFunc("/api/yp", s.handlers.GetYPResponse)
|
||||
|
||||
// list of all social platforms
|
||||
s.router.HandleFunc("/api/socialplatforms", controllers.GetAllSocialPlatforms)
|
||||
s.router.HandleFunc("/api/socialplatforms", s.handlers.GetAllSocialPlatforms)
|
||||
|
||||
// return the list of video variants available
|
||||
s.router.HandleFunc("/api/video/variants", controllers.GetVideoStreamOutputVariants)
|
||||
s.router.HandleFunc("/api/video/variants", s.handlers.GetVideoStreamOutputVariants)
|
||||
|
||||
// tell the backend you're an active viewer
|
||||
s.router.HandleFunc("/api/ping", controllers.Ping)
|
||||
s.router.HandleFunc("/api/ping", s.handlers.Ping)
|
||||
|
||||
// register a new chat user
|
||||
s.router.HandleFunc("/api/chat/register", controllers.RegisterAnonymousChatUser)
|
||||
s.router.HandleFunc("/api/chat/register", s.handlers.RegisterAnonymousChatUser)
|
||||
|
||||
// return remote follow details
|
||||
s.router.HandleFunc("/api/remotefollow", controllers.RemoteFollow)
|
||||
s.router.HandleFunc("/api/remotefollow", s.handlers.RemoteFollow)
|
||||
|
||||
// return followers
|
||||
s.router.HandleFunc("/api/followers", middleware.HandlePagination(controllers.GetFollowers))
|
||||
s.router.HandleFunc("/api/followers", middleware.HandlePagination(s.handlers.GetFollowers))
|
||||
|
||||
// save client video playback metrics
|
||||
s.router.HandleFunc("/api/metrics/playback", controllers.ReportPlaybackMetrics)
|
||||
s.router.HandleFunc("/api/metrics/playback", s.handlers.ReportPlaybackMetrics)
|
||||
|
||||
// Register for notifications
|
||||
s.router.HandleFunc("/api/notifications/register", middleware.RequireUserAccessToken(controllers.RegisterForLiveNotifications))
|
||||
s.router.HandleFunc("/api/notifications/register", middleware.RequireUserAccessToken(s.handlers.RegisterForLiveNotifications))
|
||||
|
||||
// Start auth flow
|
||||
http.HandleFunc("/api/auth/indieauth", middleware.RequireUserAccessToken(indieauth.StartAuthFlow))
|
||||
http.HandleFunc("/api/auth/indieauth/callback", indieauth.HandleRedirect)
|
||||
http.HandleFunc("/api/auth/provider/indieauth", indieauth.HandleAuthEndpoint)
|
||||
s.router.HandleFunc("/api/auth/indieauth", middleware.RequireUserAccessToken(indieauth.StartAuthFlow))
|
||||
s.router.HandleFunc("/api/auth/indieauth/callback", indieauth.HandleRedirect)
|
||||
s.router.HandleFunc("/api/auth/provider/indieauth", indieauth.HandleAuthEndpoint)
|
||||
|
||||
http.HandleFunc("/api/auth/fediverse", middleware.RequireUserAccessToken(fediverseauth.RegisterFediverseOTPRequest))
|
||||
http.HandleFunc("/api/auth/fediverse/verify", fediverseauth.VerifyFediverseOTPRequest)
|
||||
s.router.HandleFunc("/api/auth/fediverse", middleware.RequireUserAccessToken(fediverseauth.RegisterFediverseOTPRequest))
|
||||
s.router.HandleFunc("/api/auth/fediverse/verify", fediverseauth.VerifyFediverseOTPRequest)
|
||||
}
|
||||
|
||||
func (s *webServer) setupAdminAPIRoutes() {
|
||||
// Current inbound broadcaster
|
||||
s.router.HandleFunc("/api/admin/status", middleware.RequireAdminAuth(admin.Status))
|
||||
s.router.HandleFunc("/api/admin/status", middleware.RequireAdminAuth(s.handlers.GetAdminStatus))
|
||||
|
||||
// Disconnect inbound stream
|
||||
s.router.HandleFunc("/api/admin/disconnect", middleware.RequireAdminAuth(admin.DisconnectInboundConnection))
|
||||
s.router.HandleFunc("/api/admin/disconnect", middleware.RequireAdminAuth(s.handlers.DisconnectInboundConnection))
|
||||
|
||||
// Server config
|
||||
s.router.HandleFunc("/api/admin/serverconfig", middleware.RequireAdminAuth(admin.GetServerConfig))
|
||||
s.router.HandleFunc("/api/admin/serverconfig", middleware.RequireAdminAuth(s.handlers.GetServerConfig))
|
||||
|
||||
// Get viewer count over time
|
||||
s.router.HandleFunc("/api/admin/viewersOverTime", middleware.RequireAdminAuth(admin.GetViewersOverTime))
|
||||
s.router.HandleFunc("/api/admin/viewersOverTime", middleware.RequireAdminAuth(s.handlers.GetViewersOverTime))
|
||||
|
||||
// Get active viewers
|
||||
s.router.HandleFunc("/api/admin/viewers", middleware.RequireAdminAuth(admin.GetActiveViewers))
|
||||
s.router.HandleFunc("/api/admin/viewers", middleware.RequireAdminAuth(s.handlers.GetActiveViewers))
|
||||
|
||||
// Get hardware stats
|
||||
s.router.HandleFunc("/api/admin/hardwarestats", middleware.RequireAdminAuth(admin.GetHardwareStats))
|
||||
s.router.HandleFunc("/api/admin/hardwarestats", middleware.RequireAdminAuth(s.handlers.GetHardwareStats))
|
||||
|
||||
// Get a a detailed list of currently connected chat clients
|
||||
s.router.HandleFunc("/api/admin/chat/clients", middleware.RequireAdminAuth(admin.GetConnectedChatClients))
|
||||
s.router.HandleFunc("/api/admin/chat/clients", middleware.RequireAdminAuth(s.handlers.GetConnectedChatClients))
|
||||
|
||||
// Get all logs
|
||||
s.router.HandleFunc("/api/admin/logs", middleware.RequireAdminAuth(admin.GetLogs))
|
||||
s.router.HandleFunc("/api/admin/logs", middleware.RequireAdminAuth(s.handlers.GetLogs))
|
||||
|
||||
// Get warning/error logs
|
||||
s.router.HandleFunc("/api/admin/logs/warnings", middleware.RequireAdminAuth(admin.GetWarnings))
|
||||
s.router.HandleFunc("/api/admin/logs/warnings", middleware.RequireAdminAuth(s.handlers.GetWarnings))
|
||||
|
||||
// Get all chat messages for the admin, unfiltered.
|
||||
s.router.HandleFunc("/api/admin/chat/messages", middleware.RequireAdminAuth(admin.GetChatMessages))
|
||||
s.router.HandleFunc("/api/admin/chat/messages", middleware.RequireAdminAuth(s.handlers.GetAdminChatMessages))
|
||||
|
||||
// Update chat message visibility
|
||||
s.router.HandleFunc("/api/admin/chat/messagevisibility", middleware.RequireAdminAuth(admin.UpdateMessageVisibility))
|
||||
s.router.HandleFunc("/api/admin/chat/messagevisibility", middleware.RequireAdminAuth(s.handlers.UpdateMessageVisibility))
|
||||
|
||||
// Enable/disable a user
|
||||
s.router.HandleFunc("/api/admin/chat/users/setenabled", middleware.RequireAdminAuth(admin.UpdateUserEnabled))
|
||||
s.router.HandleFunc("/api/admin/chat/users/setenabled", middleware.RequireAdminAuth(s.handlers.UpdateUserEnabled))
|
||||
|
||||
// Ban/unban an IP address
|
||||
s.router.HandleFunc("/api/admin/chat/users/ipbans/create", middleware.RequireAdminAuth(admin.BanIPAddress))
|
||||
s.router.HandleFunc("/api/admin/chat/users/ipbans/create", middleware.RequireAdminAuth(s.handlers.BanIPAddress))
|
||||
|
||||
// Remove an IP address ban
|
||||
s.router.HandleFunc("/api/admin/chat/users/ipbans/remove", middleware.RequireAdminAuth(admin.UnBanIPAddress))
|
||||
s.router.HandleFunc("/api/admin/chat/users/ipbans/remove", middleware.RequireAdminAuth(s.handlers.UnBanIPAddress))
|
||||
|
||||
// Return all the banned IP addresses
|
||||
s.router.HandleFunc("/api/admin/chat/users/ipbans", middleware.RequireAdminAuth(admin.GetIPAddressBans))
|
||||
s.router.HandleFunc("/api/admin/chat/users/ipbans", middleware.RequireAdminAuth(s.handlers.GetIPAddressBans))
|
||||
|
||||
// Get a list of disabled users
|
||||
s.router.HandleFunc("/api/admin/chat/users/disabled", middleware.RequireAdminAuth(admin.GetDisabledUsers))
|
||||
s.router.HandleFunc("/api/admin/chat/users/disabled", middleware.RequireAdminAuth(s.handlers.GetDisabledUsers))
|
||||
|
||||
// Set moderator status for a user
|
||||
s.router.HandleFunc("/api/admin/chat/users/setmoderator", middleware.RequireAdminAuth(admin.UpdateUserModerator))
|
||||
s.router.HandleFunc("/api/admin/chat/users/setmoderator", middleware.RequireAdminAuth(s.handlers.UpdateUserModerator))
|
||||
|
||||
// Get a list of moderator users
|
||||
s.router.HandleFunc("/api/admin/chat/users/moderators", middleware.RequireAdminAuth(admin.GetModerators))
|
||||
s.router.HandleFunc("/api/admin/chat/users/moderators", middleware.RequireAdminAuth(s.handlers.GetModerators))
|
||||
|
||||
// return followers
|
||||
s.router.HandleFunc("/api/admin/followers", middleware.RequireAdminAuth(middleware.HandlePagination(controllers.GetFollowers)))
|
||||
s.router.HandleFunc("/api/admin/followers", middleware.RequireAdminAuth(middleware.HandlePagination(s.handlers.GetFollowers)))
|
||||
|
||||
// Get a list of pending follow requests
|
||||
s.router.HandleFunc("/api/admin/followers/pending", middleware.RequireAdminAuth(admin.GetPendingFollowRequests))
|
||||
s.router.HandleFunc("/api/admin/followers/pending", middleware.RequireAdminAuth(s.handlers.GetPendingFollowRequests))
|
||||
|
||||
// Get a list of rejected or blocked follows
|
||||
s.router.HandleFunc("/api/admin/followers/blocked", middleware.RequireAdminAuth(admin.GetBlockedAndRejectedFollowers))
|
||||
s.router.HandleFunc("/api/admin/followers/blocked", middleware.RequireAdminAuth(s.handlers.GetBlockedAndRejectedFollowers))
|
||||
|
||||
// Set the following state of a follower or follow request.
|
||||
s.router.HandleFunc("/api/admin/followers/approve", middleware.RequireAdminAuth(admin.ApproveFollower))
|
||||
s.router.HandleFunc("/api/admin/followers/approve", middleware.RequireAdminAuth(s.handlers.ApproveFollower))
|
||||
|
||||
// Upload custom emoji
|
||||
s.router.HandleFunc("/api/admin/emoji/upload", middleware.RequireAdminAuth(admin.UploadCustomEmoji))
|
||||
s.router.HandleFunc("/api/admin/emoji/upload", middleware.RequireAdminAuth(s.handlers.UploadCustomEmoji))
|
||||
|
||||
// Delete custom emoji
|
||||
s.router.HandleFunc("/api/admin/emoji/delete", middleware.RequireAdminAuth(admin.DeleteCustomEmoji))
|
||||
s.router.HandleFunc("/api/admin/emoji/delete", middleware.RequireAdminAuth(s.handlers.DeleteCustomEmoji))
|
||||
|
||||
// Update config values
|
||||
|
||||
// Change the current streaming key in memory
|
||||
s.router.HandleFunc("/api/admin/config/adminpass", middleware.RequireAdminAuth(admin.SetAdminPassword))
|
||||
s.router.HandleFunc("/api/admin/config/adminpass", middleware.RequireAdminAuth(s.handlers.SetAdminPassword))
|
||||
|
||||
// Set an array of valid stream keys
|
||||
s.router.HandleFunc("/api/admin/config/streamkeys", middleware.RequireAdminAuth(admin.SetStreamKeys))
|
||||
s.router.HandleFunc("/api/admin/config/streamkeys", middleware.RequireAdminAuth(s.handlers.SetStreamKeys))
|
||||
|
||||
// Change the extra page content in memory
|
||||
s.router.HandleFunc("/api/admin/config/pagecontent", middleware.RequireAdminAuth(admin.SetExtraPageContent))
|
||||
s.router.HandleFunc("/api/admin/config/pagecontent", middleware.RequireAdminAuth(s.handlers.SetExtraPageContent))
|
||||
|
||||
// Stream title
|
||||
s.router.HandleFunc("/api/admin/config/streamtitle", middleware.RequireAdminAuth(admin.SetStreamTitle))
|
||||
s.router.HandleFunc("/api/admin/config/streamtitle", middleware.RequireAdminAuth(s.handlers.SetStreamTitle))
|
||||
|
||||
// Server name
|
||||
s.router.HandleFunc("/api/admin/config/name", middleware.RequireAdminAuth(admin.SetServerName))
|
||||
s.router.HandleFunc("/api/admin/config/name", middleware.RequireAdminAuth(s.handlers.SetServerName))
|
||||
|
||||
// Server summary
|
||||
s.router.HandleFunc("/api/admin/config/serversummary", middleware.RequireAdminAuth(admin.SetServerSummary))
|
||||
s.router.HandleFunc("/api/admin/config/serversummary", middleware.RequireAdminAuth(s.handlers.SetServerSummary))
|
||||
|
||||
// Offline message
|
||||
s.router.HandleFunc("/api/admin/config/offlinemessage", middleware.RequireAdminAuth(admin.SetCustomOfflineMessage))
|
||||
s.router.HandleFunc("/api/admin/config/offlinemessage", middleware.RequireAdminAuth(s.handlers.SetCustomOfflineMessage))
|
||||
|
||||
// Server welcome message
|
||||
s.router.HandleFunc("/api/admin/config/welcomemessage", middleware.RequireAdminAuth(admin.SetServerWelcomeMessage))
|
||||
s.router.HandleFunc("/api/admin/config/welcomemessage", middleware.RequireAdminAuth(s.handlers.SetServerWelcomeMessage))
|
||||
|
||||
// Disable chat
|
||||
s.router.HandleFunc("/api/admin/config/chat/disable", middleware.RequireAdminAuth(admin.SetChatDisabled))
|
||||
s.router.HandleFunc("/api/admin/config/chat/disable", middleware.RequireAdminAuth(s.handlers.SetChatDisabled))
|
||||
|
||||
// Disable chat user join messages
|
||||
s.router.HandleFunc("/api/admin/config/chat/joinmessagesenabled", middleware.RequireAdminAuth(admin.SetChatJoinMessagesEnabled))
|
||||
s.router.HandleFunc("/api/admin/config/chat/joinmessagesenabled", middleware.RequireAdminAuth(s.handlers.SetChatJoinMessagesEnabled))
|
||||
|
||||
// Enable/disable chat established user mode
|
||||
s.router.HandleFunc("/api/admin/config/chat/establishedusermode", middleware.RequireAdminAuth(admin.SetEnableEstablishedChatUserMode))
|
||||
s.router.HandleFunc("/api/admin/config/chat/establishedusermode", middleware.RequireAdminAuth(s.handlers.SetEnableEstablishedChatUserMode))
|
||||
|
||||
// Set chat usernames that are not allowed
|
||||
s.router.HandleFunc("/api/admin/config/chat/forbiddenusernames", middleware.RequireAdminAuth(admin.SetForbiddenUsernameList))
|
||||
s.router.HandleFunc("/api/admin/config/chat/forbiddenusernames", middleware.RequireAdminAuth(s.handlers.SetForbiddenUsernameList))
|
||||
|
||||
// Set the suggested chat usernames that will be assigned automatically
|
||||
s.router.HandleFunc("/api/admin/config/chat/suggestedusernames", middleware.RequireAdminAuth(admin.SetSuggestedUsernameList))
|
||||
s.router.HandleFunc("/api/admin/config/chat/suggestedusernames", middleware.RequireAdminAuth(s.handlers.SetSuggestedUsernameList))
|
||||
|
||||
// Set video codec
|
||||
s.router.HandleFunc("/api/admin/config/video/codec", middleware.RequireAdminAuth(admin.SetVideoCodec))
|
||||
s.router.HandleFunc("/api/admin/config/video/codec", middleware.RequireAdminAuth(s.handlers.SetVideoCodec))
|
||||
|
||||
// Set style/color/css values
|
||||
s.router.HandleFunc("/api/admin/config/appearance", middleware.RequireAdminAuth(admin.SetCustomColorVariableValues))
|
||||
s.router.HandleFunc("/api/admin/config/appearance", middleware.RequireAdminAuth(s.handlers.SetCustomColorVariableValues))
|
||||
|
||||
// Return all webhooks
|
||||
s.router.HandleFunc("/api/admin/webhooks", middleware.RequireAdminAuth(admin.GetWebhooks))
|
||||
s.router.HandleFunc("/api/admin/webhooks", middleware.RequireAdminAuth(s.handlers.GetWebhooks))
|
||||
|
||||
// Delete a single webhook
|
||||
s.router.HandleFunc("/api/admin/webhooks/delete", middleware.RequireAdminAuth(admin.DeleteWebhook))
|
||||
s.router.HandleFunc("/api/admin/webhooks/delete", middleware.RequireAdminAuth(s.handlers.DeleteWebhook))
|
||||
|
||||
// Create a single webhook
|
||||
s.router.HandleFunc("/api/admin/webhooks/create", middleware.RequireAdminAuth(admin.CreateWebhook))
|
||||
s.router.HandleFunc("/api/admin/webhooks/create", middleware.RequireAdminAuth(s.handlers.CreateWebhook))
|
||||
|
||||
// Get all access tokens
|
||||
s.router.HandleFunc("/api/admin/accesstokens", middleware.RequireAdminAuth(admin.GetExternalAPIUsers))
|
||||
s.router.HandleFunc("/api/admin/accesstokens", middleware.RequireAdminAuth(s.handlers.GetExternalAPIUsers))
|
||||
|
||||
// Delete a single access token
|
||||
s.router.HandleFunc("/api/admin/accesstokens/delete", middleware.RequireAdminAuth(admin.DeleteExternalAPIUser))
|
||||
s.router.HandleFunc("/api/admin/accesstokens/delete", middleware.RequireAdminAuth(s.handlers.DeleteExternalAPIUser))
|
||||
|
||||
// Create a single access token
|
||||
s.router.HandleFunc("/api/admin/accesstokens/create", middleware.RequireAdminAuth(admin.CreateExternalAPIUser))
|
||||
s.router.HandleFunc("/api/admin/accesstokens/create", middleware.RequireAdminAuth(s.handlers.CreateExternalAPIUser))
|
||||
|
||||
// Return the auto-update features that are supported for this instance.
|
||||
s.router.HandleFunc("/api/admin/update/options", middleware.RequireAdminAuth(admin.AutoUpdateOptions))
|
||||
s.router.HandleFunc("/api/admin/update/options", middleware.RequireAdminAuth(s.handlers.AutoUpdateOptions))
|
||||
|
||||
// Begin the auto update
|
||||
s.router.HandleFunc("/api/admin/update/start", middleware.RequireAdminAuth(admin.AutoUpdateStart))
|
||||
s.router.HandleFunc("/api/admin/update/start", middleware.RequireAdminAuth(s.handlers.AutoUpdateStart))
|
||||
|
||||
// Force quit the service to restart it
|
||||
s.router.HandleFunc("/api/admin/update/forcequit", middleware.RequireAdminAuth(admin.AutoUpdateForceQuit))
|
||||
s.router.HandleFunc("/api/admin/update/forcequit", middleware.RequireAdminAuth(s.handlers.AutoUpdateForceQuit))
|
||||
|
||||
// Logo path
|
||||
s.router.HandleFunc("/api/admin/config/logo", middleware.RequireAdminAuth(admin.SetLogo))
|
||||
s.router.HandleFunc("/api/admin/config/logo", middleware.RequireAdminAuth(s.handlers.SetLogo))
|
||||
|
||||
// Server tags
|
||||
s.router.HandleFunc("/api/admin/config/tags", middleware.RequireAdminAuth(admin.SetTags))
|
||||
s.router.HandleFunc("/api/admin/config/tags", middleware.RequireAdminAuth(s.handlers.SetTags))
|
||||
|
||||
// ffmpeg
|
||||
s.router.HandleFunc("/api/admin/config/ffmpegpath", middleware.RequireAdminAuth(admin.SetFfmpegPath))
|
||||
s.router.HandleFunc("/api/admin/config/ffmpegpath", middleware.RequireAdminAuth(s.handlers.SetFfmpegPath))
|
||||
|
||||
// Server http port
|
||||
s.router.HandleFunc("/api/admin/config/webserverport", middleware.RequireAdminAuth(admin.SetWebServerPort))
|
||||
s.router.HandleFunc("/api/admin/config/webserverport", middleware.RequireAdminAuth(s.handlers.SetWebServerPort))
|
||||
|
||||
// Server http listen address
|
||||
s.router.HandleFunc("/api/admin/config/webserverip", middleware.RequireAdminAuth(admin.SetWebServerIP))
|
||||
s.router.HandleFunc("/api/admin/config/webserverip", middleware.RequireAdminAuth(s.handlers.SetWebServerIP))
|
||||
|
||||
// Server rtmp port
|
||||
s.router.HandleFunc("/api/admin/config/rtmpserverport", middleware.RequireAdminAuth(admin.SetRTMPServerPort))
|
||||
s.router.HandleFunc("/api/admin/config/rtmpserverport", middleware.RequireAdminAuth(s.handlers.SetRTMPServerPort))
|
||||
|
||||
// Websocket host override
|
||||
s.router.HandleFunc("/api/admin/config/sockethostoverride", middleware.RequireAdminAuth(admin.SetSocketHostOverride))
|
||||
s.router.HandleFunc("/api/admin/config/sockethostoverride", middleware.RequireAdminAuth(s.handlers.SetSocketHostOverride))
|
||||
|
||||
// Custom video serving endpoint
|
||||
s.router.HandleFunc("/api/admin/config/videoservingendpoint", middleware.RequireAdminAuth(admin.SetVideoServingEndpoint))
|
||||
s.router.HandleFunc("/api/admin/config/videoservingendpoint", middleware.RequireAdminAuth(s.handlers.SetVideoServingEndpoint))
|
||||
|
||||
// Is server marked as NSFW
|
||||
s.router.HandleFunc("/api/admin/config/nsfw", middleware.RequireAdminAuth(admin.SetNSFW))
|
||||
s.router.HandleFunc("/api/admin/config/nsfw", middleware.RequireAdminAuth(s.handlers.SetNSFW))
|
||||
|
||||
// directory enabled
|
||||
s.router.HandleFunc("/api/admin/config/directoryenabled", middleware.RequireAdminAuth(admin.SetDirectoryEnabled))
|
||||
s.router.HandleFunc("/api/admin/config/directoryenabled", middleware.RequireAdminAuth(s.handlers.SetDirectoryEnabled))
|
||||
|
||||
// social handles
|
||||
s.router.HandleFunc("/api/admin/config/socialhandles", middleware.RequireAdminAuth(admin.SetSocialHandles))
|
||||
s.router.HandleFunc("/api/admin/config/socialhandles", middleware.RequireAdminAuth(s.handlers.SetSocialHandles))
|
||||
|
||||
// set the number of video segments and duration per segment in a playlist
|
||||
s.router.HandleFunc("/api/admin/config/video/streamlatencylevel", middleware.RequireAdminAuth(admin.SetStreamLatencyLevel))
|
||||
s.router.HandleFunc("/api/admin/config/video/streamlatencylevel", middleware.RequireAdminAuth(s.handlers.SetStreamLatencyLevel))
|
||||
|
||||
// set an array of video output configurations
|
||||
s.router.HandleFunc("/api/admin/config/video/streamoutputvariants", middleware.RequireAdminAuth(admin.SetStreamOutputVariants))
|
||||
s.router.HandleFunc("/api/admin/config/video/streamoutputvariants", middleware.RequireAdminAuth(s.handlers.SetStreamOutputVariants))
|
||||
|
||||
// set s3 configuration
|
||||
s.router.HandleFunc("/api/admin/config/s3", middleware.RequireAdminAuth(admin.SetS3Configuration))
|
||||
s.router.HandleFunc("/api/admin/config/s3", middleware.RequireAdminAuth(s.handlers.SetS3Configuration))
|
||||
|
||||
// set server url
|
||||
s.router.HandleFunc("/api/admin/config/serverurl", middleware.RequireAdminAuth(admin.SetServerURL))
|
||||
s.router.HandleFunc("/api/admin/config/serverurl", middleware.RequireAdminAuth(s.handlers.SetServerURL))
|
||||
|
||||
// reset the YP registration
|
||||
s.router.HandleFunc("/api/admin/yp/reset", middleware.RequireAdminAuth(admin.ResetYPRegistration))
|
||||
s.router.HandleFunc("/api/admin/yp/reset", middleware.RequireAdminAuth(s.handlers.ResetYPRegistration))
|
||||
|
||||
// set external action links
|
||||
s.router.HandleFunc("/api/admin/config/externalactions", middleware.RequireAdminAuth(admin.SetExternalActions))
|
||||
s.router.HandleFunc("/api/admin/config/externalactions", middleware.RequireAdminAuth(s.handlers.SetExternalActions))
|
||||
|
||||
// set custom style css
|
||||
s.router.HandleFunc("/api/admin/config/customstyles", middleware.RequireAdminAuth(admin.SetCustomStyles))
|
||||
s.router.HandleFunc("/api/admin/config/customstyles", middleware.RequireAdminAuth(s.handlers.SetCustomStyles))
|
||||
|
||||
// set custom style javascript
|
||||
s.router.HandleFunc("/api/admin/config/customjavascript", middleware.RequireAdminAuth(admin.SetCustomJavascript))
|
||||
s.router.HandleFunc("/api/admin/config/customjavascript", middleware.RequireAdminAuth(s.handlers.SetCustomJavascript))
|
||||
|
||||
// Video playback metrics
|
||||
s.router.HandleFunc("/api/admin/metrics/video", middleware.RequireAdminAuth(admin.GetVideoPlaybackMetrics))
|
||||
s.router.HandleFunc("/api/admin/metrics/video", middleware.RequireAdminAuth(s.handlers.GetVideoPlaybackMetrics))
|
||||
|
||||
// Is the viewer count hidden from viewers
|
||||
s.router.HandleFunc("/api/admin/config/hideviewercount", middleware.RequireAdminAuth(admin.SetHideViewerCount))
|
||||
s.router.HandleFunc("/api/admin/config/hideviewercount", middleware.RequireAdminAuth(s.handlers.SetHideViewerCount))
|
||||
|
||||
// set disabling of search indexing
|
||||
s.router.HandleFunc("/api/admin/config/disablesearchindexing", middleware.RequireAdminAuth(admin.SetDisableSearchIndexing))
|
||||
s.router.HandleFunc("/api/admin/config/disablesearchindexing", middleware.RequireAdminAuth(s.handlers.SetDisableSearchIndexing))
|
||||
|
||||
// enable/disable federation features
|
||||
s.router.HandleFunc("/api/admin/config/federation/enable", middleware.RequireAdminAuth(admin.SetFederationEnabled))
|
||||
s.router.HandleFunc("/api/admin/config/federation/enable", middleware.RequireAdminAuth(s.handlers.SetFederationEnabled))
|
||||
|
||||
// set if federation activities are private
|
||||
s.router.HandleFunc("/api/admin/config/federation/private", middleware.RequireAdminAuth(admin.SetFederationActivityPrivate))
|
||||
s.router.HandleFunc("/api/admin/config/federation/private", middleware.RequireAdminAuth(s.handlers.SetFederationActivityPrivate))
|
||||
|
||||
// set if fediverse engagement appears in chat
|
||||
s.router.HandleFunc("/api/admin/config/federation/showengagement", middleware.RequireAdminAuth(admin.SetFederationShowEngagement))
|
||||
s.router.HandleFunc("/api/admin/config/federation/showengagement", middleware.RequireAdminAuth(s.handlers.SetFederationShowEngagement))
|
||||
|
||||
// set local federated username
|
||||
s.router.HandleFunc("/api/admin/config/federation/username", middleware.RequireAdminAuth(admin.SetFederationUsername))
|
||||
s.router.HandleFunc("/api/admin/config/federation/username", middleware.RequireAdminAuth(s.handlers.SetFederationUsername))
|
||||
|
||||
// set federated go live message
|
||||
s.router.HandleFunc("/api/admin/config/federation/livemessage", middleware.RequireAdminAuth(admin.SetFederationGoLiveMessage))
|
||||
s.router.HandleFunc("/api/admin/config/federation/livemessage", middleware.RequireAdminAuth(s.handlers.SetFederationGoLiveMessage))
|
||||
|
||||
// Federation blocked domains
|
||||
s.router.HandleFunc("/api/admin/config/federation/blockdomains", middleware.RequireAdminAuth(admin.SetFederationBlockDomains))
|
||||
s.router.HandleFunc("/api/admin/config/federation/blockdomains", middleware.RequireAdminAuth(s.handlers.SetFederationBlockDomains))
|
||||
|
||||
// send a public message to the Fediverse from the server's user
|
||||
s.router.HandleFunc("/api/admin/federation/send", middleware.RequireAdminAuth(admin.SendFederatedMessage))
|
||||
s.router.HandleFunc("/api/admin/federation/send", middleware.RequireAdminAuth(s.handlers.SendFederatedMessage))
|
||||
|
||||
// Return federated activities
|
||||
s.router.HandleFunc("/api/admin/federation/actions", middleware.RequireAdminAuth(middleware.HandlePagination(admin.GetFederatedActions)))
|
||||
s.router.HandleFunc("/api/admin/federation/actions", middleware.RequireAdminAuth(middleware.HandlePagination(s.handlers.GetFederatedActions)))
|
||||
|
||||
// Prometheus metrics
|
||||
s.router.Handle("/api/admin/prometheus", middleware.RequireAdminAuth(func(rw http.ResponseWriter, r *http.Request) {
|
||||
|
@ -375,46 +369,46 @@ func (s *webServer) setupAdminAPIRoutes() {
|
|||
}))
|
||||
|
||||
// Configure outbound notification channels.
|
||||
http.HandleFunc("/api/admin/config/notifications/discord", middleware.RequireAdminAuth(admin.SetDiscordNotificationConfiguration))
|
||||
http.HandleFunc("/api/admin/config/notifications/browser", middleware.RequireAdminAuth(admin.SetBrowserNotificationConfiguration))
|
||||
s.router.HandleFunc("/api/admin/config/notifications/discord", middleware.RequireAdminAuth(s.handlers.SetDiscordNotificationConfiguration))
|
||||
s.router.HandleFunc("/api/admin/config/notifications/browser", middleware.RequireAdminAuth(s.handlers.SetBrowserNotificationConfiguration))
|
||||
}
|
||||
|
||||
func (s *webServer) setupExternalThirdPartyAPIRoutes() {
|
||||
// Send a system message to chat
|
||||
s.router.HandleFunc("/api/integrations/chat/system", middleware.RequireExternalAPIAccessToken(user.ScopeCanSendSystemMessages, admin.SendSystemMessage))
|
||||
s.router.HandleFunc("/api/integrations/chat/system", middleware.RequireExternalAPIAccessToken(user.ScopeCanSendSystemMessages, s.handlers.SendSystemMessage))
|
||||
|
||||
// Send a system message to a single client
|
||||
s.router.HandleFunc(utils.RestEndpoint("/api/integrations/chat/system/client/{clientId}", middleware.RequireExternalAPIAccessToken(user.ScopeCanSendSystemMessages, admin.SendSystemMessageToConnectedClient)))
|
||||
s.router.HandleFunc(utils.RestEndpoint("/api/integrations/chat/system/client/{clientId}", middleware.RequireExternalAPIAccessToken(user.ScopeCanSendSystemMessages, s.handlers.SendSystemMessageToConnectedClient)))
|
||||
|
||||
// Send a user message to chat *NO LONGER SUPPORTED
|
||||
s.router.HandleFunc("/api/integrations/chat/user", middleware.RequireExternalAPIAccessToken(user.ScopeCanSendChatMessages, admin.SendUserMessage))
|
||||
s.router.HandleFunc("/api/integrations/chat/user", middleware.RequireExternalAPIAccessToken(user.ScopeCanSendChatMessages, s.handlers.SendUserMessage))
|
||||
|
||||
// Send a message to chat as a specific 3rd party bot/integration based on its access token
|
||||
s.router.HandleFunc("/api/integrations/chat/send", middleware.RequireExternalAPIAccessToken(user.ScopeCanSendChatMessages, admin.SendIntegrationChatMessage))
|
||||
s.router.HandleFunc("/api/integrations/chat/send", middleware.RequireExternalAPIAccessToken(user.ScopeCanSendChatMessages, s.handlers.SendIntegrationChatMessage))
|
||||
|
||||
// Send a user action to chat
|
||||
s.router.HandleFunc("/api/integrations/chat/action", middleware.RequireExternalAPIAccessToken(user.ScopeCanSendSystemMessages, admin.SendChatAction))
|
||||
s.router.HandleFunc("/api/integrations/chat/action", middleware.RequireExternalAPIAccessToken(user.ScopeCanSendSystemMessages, s.handlers.SendChatAction))
|
||||
|
||||
// Hide chat message
|
||||
s.router.HandleFunc("/api/integrations/chat/messagevisibility", middleware.RequireExternalAPIAccessToken(user.ScopeHasAdminAccess, admin.ExternalUpdateMessageVisibility))
|
||||
s.router.HandleFunc("/api/integrations/chat/messagevisibility", middleware.RequireExternalAPIAccessToken(user.ScopeHasAdminAccess, s.handlers.ExternalUpdateMessageVisibility))
|
||||
|
||||
// Stream title
|
||||
s.router.HandleFunc("/api/integrations/streamtitle", middleware.RequireExternalAPIAccessToken(user.ScopeHasAdminAccess, admin.ExternalSetStreamTitle))
|
||||
s.router.HandleFunc("/api/integrations/streamtitle", middleware.RequireExternalAPIAccessToken(user.ScopeHasAdminAccess, s.handlers.ExternalSetStreamTitle))
|
||||
|
||||
// Get chat history
|
||||
s.router.HandleFunc("/api/integrations/chat", middleware.RequireExternalAPIAccessToken(user.ScopeHasAdminAccess, controllers.ExternalGetChatMessages))
|
||||
s.router.HandleFunc("/api/integrations/chat", middleware.RequireExternalAPIAccessToken(user.ScopeHasAdminAccess, s.handlers.ExternalGetChatMessages))
|
||||
|
||||
// Connected clients
|
||||
s.router.HandleFunc("/api/integrations/clients", middleware.RequireExternalAPIAccessToken(user.ScopeHasAdminAccess, admin.ExternalGetConnectedChatClients))
|
||||
s.router.HandleFunc("/api/integrations/clients", middleware.RequireExternalAPIAccessToken(user.ScopeHasAdminAccess, s.handlers.ExternalGetConnectedChatClients))
|
||||
}
|
||||
|
||||
func (s *webServer) setupModerationAPIRoutes() {
|
||||
// Update chat message visibility
|
||||
s.router.HandleFunc("/api/chat/messagevisibility", middleware.RequireUserModerationScopeAccesstoken(admin.UpdateMessageVisibility))
|
||||
s.router.HandleFunc("/api/chat/messagevisibility", middleware.RequireUserModerationScopeAccesstoken(s.handlers.UpdateMessageVisibility))
|
||||
|
||||
// Enable/disable a user
|
||||
s.router.HandleFunc("/api/chat/users/setenabled", middleware.RequireUserModerationScopeAccesstoken(admin.UpdateUserEnabled))
|
||||
s.router.HandleFunc("/api/chat/users/setenabled", middleware.RequireUserModerationScopeAccesstoken(s.handlers.UpdateUserEnabled))
|
||||
|
||||
// Get a user's details
|
||||
s.router.HandleFunc("/api/moderation/chat/user/", middleware.RequireUserModerationScopeAccesstoken(moderation.GetUserDetails))
|
||||
s.router.HandleFunc("/api/moderation/chat/user/", middleware.RequireUserModerationScopeAccesstoken(s.handlers.GetUserDetails))
|
||||
}
|
||||
|
|
|
@ -33,12 +33,3 @@ func TestPrometheusDebugPath(t *testing.T) {
|
|||
t.Errorf("Expected 404, got %d", w.Result().StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTestingEndpoint(t *testing.T) {
|
||||
r := httptest.NewRequest(http.MethodGet, "/test", nil)
|
||||
w := httptest.NewRecorder()
|
||||
srv.ServeHTTP(w, r)
|
||||
if w.Result().StatusCode != http.StatusOK {
|
||||
t.Errorf("Expected 200, got %d", w.Result().StatusCode)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue