mirror of
https://github.com/owncast/owncast.git
synced 2024-11-25 14:20:54 +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 {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
return
|
return
|
||||||
// controllers.WriteSimpleResponse(w, false, err.Error())
|
// responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := w.Write([]byte(object)); err != nil {
|
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
|
// this is endpoint logic
|
||||||
|
|
||||||
|
@ -9,30 +9,31 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/owncast/owncast/controllers"
|
|
||||||
"github.com/owncast/owncast/core/chat"
|
"github.com/owncast/owncast/core/chat"
|
||||||
"github.com/owncast/owncast/core/chat/events"
|
"github.com/owncast/owncast/core/chat/events"
|
||||||
"github.com/owncast/owncast/core/data"
|
"github.com/owncast/owncast/core/data"
|
||||||
"github.com/owncast/owncast/core/user"
|
"github.com/owncast/owncast/core/user"
|
||||||
"github.com/owncast/owncast/utils"
|
"github.com/owncast/owncast/utils"
|
||||||
|
"github.com/owncast/owncast/webserver/requests"
|
||||||
|
"github.com/owncast/owncast/webserver/responses"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExternalUpdateMessageVisibility updates an array of message IDs to have the same visiblity.
|
// ExternalUpdateMessageVisibility updates an array of message IDs to have the same visiblity.
|
||||||
func ExternalUpdateMessageVisibility(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
func (h *Handlers) ExternalUpdateMessageVisibility(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
||||||
UpdateMessageVisibility(w, r)
|
h.UpdateMessageVisibility(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateMessageVisibility updates an array of message IDs to have the same visiblity.
|
// 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 {
|
type messageVisibilityUpdateRequest struct {
|
||||||
IDArray []string `json:"idArray"`
|
IDArray []string `json:"idArray"`
|
||||||
Visible bool `json:"visible"`
|
Visible bool `json:"visible"`
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.Method != controllers.POST {
|
if r.Method != http.MethodPost {
|
||||||
// nolint:goconst
|
responses.WriteSimpleResponse(w, false, r.Method+" not supported")
|
||||||
controllers.WriteSimpleResponse(w, false, r.Method+" not supported")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,78 +42,78 @@ func UpdateMessageVisibility(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
if err := decoder.Decode(&request); err != nil {
|
if err := decoder.Decode(&request); err != nil {
|
||||||
log.Errorln(err)
|
log.Errorln(err)
|
||||||
controllers.WriteSimpleResponse(w, false, "")
|
responses.WriteSimpleResponse(w, false, "")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := chat.SetMessagesVisibility(request.IDArray, request.Visible); err != nil {
|
if err := chat.SetMessagesVisibility(request.IDArray, request.Visible); err != nil {
|
||||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
controllers.WriteSimpleResponse(w, true, "changed")
|
responses.WriteSimpleResponse(w, true, "changed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// BanIPAddress will manually ban an IP address.
|
// BanIPAddress will manually ban an IP address.
|
||||||
func BanIPAddress(w http.ResponseWriter, r *http.Request) {
|
func (h *Handlers) BanIPAddress(w http.ResponseWriter, r *http.Request) {
|
||||||
if !requirePOST(w, r) {
|
if !requests.RequirePOST(w, r) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
configValue, success := getValueFromRequest(w, r)
|
configValue, success := requests.GetValueFromRequest(w, r)
|
||||||
if !success {
|
if !success {
|
||||||
controllers.WriteSimpleResponse(w, false, "unable to ban IP address")
|
responses.WriteSimpleResponse(w, false, "unable to ban IP address")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := data.BanIPAddress(configValue.Value.(string), "manually added"); err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
controllers.WriteSimpleResponse(w, true, "IP address banned")
|
responses.WriteSimpleResponse(w, true, "IP address banned")
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnBanIPAddress will remove an IP address ban.
|
// UnBanIPAddress will remove an IP address ban.
|
||||||
func UnBanIPAddress(w http.ResponseWriter, r *http.Request) {
|
func (h *Handlers) UnBanIPAddress(w http.ResponseWriter, r *http.Request) {
|
||||||
if !requirePOST(w, r) {
|
if !requests.RequirePOST(w, r) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
configValue, success := getValueFromRequest(w, r)
|
configValue, success := requests.GetValueFromRequest(w, r)
|
||||||
if !success {
|
if !success {
|
||||||
controllers.WriteSimpleResponse(w, false, "unable to unban IP address")
|
responses.WriteSimpleResponse(w, false, "unable to unban IP address")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := data.RemoveIPAddressBan(configValue.Value.(string)); err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
controllers.WriteSimpleResponse(w, true, "IP address unbanned")
|
responses.WriteSimpleResponse(w, true, "IP address unbanned")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetIPAddressBans will return all the banned IP addresses.
|
// 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()
|
bans, err := data.GetIPAddressBans()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
controllers.WriteResponse(w, bans)
|
responses.WriteResponse(w, bans)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateUserEnabled enable or disable a single user by ID.
|
// 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 {
|
type blockUserRequest struct {
|
||||||
UserID string `json:"userId"`
|
UserID string `json:"userId"`
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.Method != controllers.POST {
|
if r.Method != http.MethodPost {
|
||||||
controllers.WriteSimpleResponse(w, false, r.Method+" not supported")
|
responses.WriteSimpleResponse(w, false, r.Method+" not supported")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,19 +122,19 @@ func UpdateUserEnabled(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
if err := decoder.Decode(&request); err != nil {
|
if err := decoder.Decode(&request); err != nil {
|
||||||
log.Errorln(err)
|
log.Errorln(err)
|
||||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if request.UserID == "" {
|
if request.UserID == "" {
|
||||||
controllers.WriteSimpleResponse(w, false, "must provide userId")
|
responses.WriteSimpleResponse(w, false, "must provide userId")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable/enable the user
|
// Disable/enable the user
|
||||||
if err := user.SetEnabled(request.UserID, request.Enabled); err != nil {
|
if err := user.SetEnabled(request.UserID, request.Enabled); err != nil {
|
||||||
log.Errorln("error changing user enabled status", err)
|
log.Errorln("error changing user enabled status", err)
|
||||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,7 +143,7 @@ func UpdateUserEnabled(w http.ResponseWriter, r *http.Request) {
|
||||||
if !request.Enabled {
|
if !request.Enabled {
|
||||||
if err := chat.SetMessageVisibilityForUserID(request.UserID, request.Enabled); err != nil {
|
if err := chat.SetMessageVisibilityForUserID(request.UserID, request.Enabled); err != nil {
|
||||||
log.Errorln("error changing user messages visibility", err)
|
log.Errorln("error changing user messages visibility", err)
|
||||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -157,7 +158,7 @@ func UpdateUserEnabled(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorln("error fetching clients for user: ", err)
|
log.Errorln("error fetching clients for user: ", err)
|
||||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
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.
|
// 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")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
users := user.GetDisabledUsers()
|
users := user.GetDisabledUsers()
|
||||||
controllers.WriteResponse(w, users)
|
responses.WriteResponse(w, users)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateUserModerator will set the moderator status for a user ID.
|
// 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 {
|
type request struct {
|
||||||
UserID string `json:"userId"`
|
UserID string `json:"userId"`
|
||||||
IsModerator bool `json:"isModerator"`
|
IsModerator bool `json:"isModerator"`
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.Method != controllers.POST {
|
if r.Method != http.MethodPost {
|
||||||
controllers.WriteSimpleResponse(w, false, r.Method+" not supported")
|
responses.WriteSimpleResponse(w, false, r.Method+" not supported")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,13 +208,13 @@ func UpdateUserModerator(w http.ResponseWriter, r *http.Request) {
|
||||||
var req request
|
var req request
|
||||||
|
|
||||||
if err := decoder.Decode(&req); err != nil {
|
if err := decoder.Decode(&req); err != nil {
|
||||||
controllers.WriteSimpleResponse(w, false, "")
|
responses.WriteSimpleResponse(w, false, "")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the user object with new moderation access.
|
// Update the user object with new moderation access.
|
||||||
if err := user.SetModerator(req.UserID, req.IsModerator); err != nil {
|
if err := user.SetModerator(req.UserID, req.IsModerator); err != nil {
|
||||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,87 +223,87 @@ func UpdateUserModerator(w http.ResponseWriter, r *http.Request) {
|
||||||
log.Debugln(err)
|
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.
|
// 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")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
users := user.GetModeratorUsers()
|
users := user.GetModeratorUsers()
|
||||||
controllers.WriteResponse(w, users)
|
responses.WriteResponse(w, users)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetChatMessages returns all of the chat messages, unfiltered.
|
// 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")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
messages := chat.GetChatModerationHistory()
|
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.
|
// 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")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
var message events.SystemMessageEvent
|
var message events.SystemMessageEvent
|
||||||
if err := json.NewDecoder(r.Body).Decode(&message); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&message); err != nil {
|
||||||
controllers.InternalErrorHandler(w, err)
|
responses.InternalErrorHandler(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := chat.SendSystemMessage(message.Body, false); err != nil {
|
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.
|
// 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")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
clientIDText, err := utils.ReadRestURLParameter(r, "clientId")
|
clientIDText, err := utils.ReadRestURLParameter(r, "clientId")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
controllers.BadRequestHandler(w, err)
|
responses.BadRequestHandler(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
clientIDNumeric, err := strconv.ParseUint(clientIDText, 10, 32)
|
clientIDNumeric, err := strconv.ParseUint(clientIDText, 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
controllers.BadRequestHandler(w, err)
|
responses.BadRequestHandler(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var message events.SystemMessageEvent
|
var message events.SystemMessageEvent
|
||||||
if err := json.NewDecoder(r.Body).Decode(&message); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&message); err != nil {
|
||||||
controllers.InternalErrorHandler(w, err)
|
responses.InternalErrorHandler(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
chat.SendSystemMessageToClient(uint(clientIDNumeric), message.Body)
|
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*.
|
// 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")
|
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.
|
// 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")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
name := integration.DisplayName
|
name := integration.DisplayName
|
||||||
|
|
||||||
if name == "" {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var event events.UserMessageEvent
|
var event events.UserMessageEvent
|
||||||
if err := json.NewDecoder(r.Body).Decode(&event); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&event); err != nil {
|
||||||
controllers.InternalErrorHandler(w, err)
|
responses.InternalErrorHandler(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
event.SetDefaults()
|
event.SetDefaults()
|
||||||
|
@ -310,7 +311,7 @@ func SendIntegrationChatMessage(integration user.ExternalAPIUser, w http.Respons
|
||||||
event.Type = "CHAT"
|
event.Type = "CHAT"
|
||||||
|
|
||||||
if event.Empty() {
|
if event.Empty() {
|
||||||
controllers.BadRequestHandler(w, errors.New("invalid message"))
|
responses.BadRequestHandler(w, errors.New("invalid message"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,22 +324,22 @@ func SendIntegrationChatMessage(integration user.ExternalAPIUser, w http.Respons
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := chat.Broadcast(&event); err != nil {
|
if err := chat.Broadcast(&event); err != nil {
|
||||||
controllers.BadRequestHandler(w, err)
|
responses.BadRequestHandler(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
chat.SaveUserMessage(event)
|
chat.SaveUserMessage(event)
|
||||||
|
|
||||||
controllers.WriteSimpleResponse(w, true, "sent")
|
responses.WriteSimpleResponse(w, true, "sent")
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendChatAction will send a generic chat action.
|
// 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")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
var message events.SystemActionEvent
|
var message events.SystemActionEvent
|
||||||
if err := json.NewDecoder(r.Body).Decode(&message); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&message); err != nil {
|
||||||
controllers.InternalErrorHandler(w, err)
|
responses.InternalErrorHandler(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,30 +347,30 @@ func SendChatAction(integration user.ExternalAPIUser, w http.ResponseWriter, r *
|
||||||
message.RenderBody()
|
message.RenderBody()
|
||||||
|
|
||||||
if err := chat.SendSystemAction(message.Body, false); err != nil {
|
if err := chat.SendSystemAction(message.Body, false); err != nil {
|
||||||
controllers.BadRequestHandler(w, err)
|
responses.BadRequestHandler(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
controllers.WriteSimpleResponse(w, true, "sent")
|
responses.WriteSimpleResponse(w, true, "sent")
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetEnableEstablishedChatUserMode sets the requirement for a chat user
|
// SetEnableEstablishedChatUserMode sets the requirement for a chat user
|
||||||
// to be "established" for some time before taking part in chat.
|
// to be "established" for some time before taking part in chat.
|
||||||
func SetEnableEstablishedChatUserMode(w http.ResponseWriter, r *http.Request) {
|
func (h *Handlers) SetEnableEstablishedChatUserMode(w http.ResponseWriter, r *http.Request) {
|
||||||
if !requirePOST(w, r) {
|
if !requests.RequirePOST(w, r) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
configValue, success := getValueFromRequest(w, r)
|
configValue, success := requests.GetValueFromRequest(w, r)
|
||||||
if !success {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := data.SetChatEstablishedUsersOnlyMode(configValue.Value.(bool)); err != nil {
|
if err := data.SetChatEstablishedUsersOnlyMode(configValue.Value.(bool)); err != nil {
|
||||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
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 (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/owncast/owncast/controllers"
|
|
||||||
"github.com/owncast/owncast/core/chat"
|
"github.com/owncast/owncast/core/chat"
|
||||||
"github.com/owncast/owncast/core/user"
|
"github.com/owncast/owncast/core/user"
|
||||||
|
"github.com/owncast/owncast/webserver/responses"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetConnectedChatClients returns currently connected clients.
|
// 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()
|
clients := chat.GetClients()
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
if err := json.NewEncoder(w).Encode(clients); err != nil {
|
if err := json.NewEncoder(w).Encode(clients); err != nil {
|
||||||
controllers.InternalErrorHandler(w, err)
|
responses.InternalErrorHandler(w, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExternalGetConnectedChatClients returns currently connected clients.
|
// ExternalGetConnectedChatClients returns currently connected clients.
|
||||||
func ExternalGetConnectedChatClients(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
func (h *Handlers) ExternalGetConnectedChatClients(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
||||||
GetConnectedChatClients(w, r)
|
h.GetConnectedChatClients(w, r)
|
||||||
}
|
}
|
|
@ -1,20 +1,20 @@
|
||||||
package admin
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/owncast/owncast/controllers"
|
|
||||||
"github.com/owncast/owncast/core/data"
|
"github.com/owncast/owncast/core/data"
|
||||||
|
"github.com/owncast/owncast/webserver/responses"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ResetYPRegistration will clear the YP protocol registration key.
|
// 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")
|
log.Traceln("Resetting YP registration key")
|
||||||
if err := data.SetDirectoryRegistrationKey(""); err != nil {
|
if err := data.SetDirectoryRegistrationKey(""); err != nil {
|
||||||
log.Errorln(err)
|
log.Errorln(err)
|
||||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
controllers.WriteSimpleResponse(w, true, "reset")
|
responses.WriteSimpleResponse(w, true, "reset")
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package admin
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -8,13 +8,14 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/owncast/owncast/config"
|
"github.com/owncast/owncast/config"
|
||||||
"github.com/owncast/owncast/controllers"
|
|
||||||
"github.com/owncast/owncast/utils"
|
"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.
|
// UploadCustomEmoji allows POSTing a new custom emoji to the server.
|
||||||
func UploadCustomEmoji(w http.ResponseWriter, r *http.Request) {
|
func (h *Handlers) UploadCustomEmoji(w http.ResponseWriter, r *http.Request) {
|
||||||
if !requirePOST(w, r) {
|
if !requests.RequirePOST(w, r) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,13 +27,13 @@ func UploadCustomEmoji(w http.ResponseWriter, r *http.Request) {
|
||||||
emoji := new(postEmoji)
|
emoji := new(postEmoji)
|
||||||
|
|
||||||
if err := json.NewDecoder(r.Body).Decode(emoji); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(emoji); err != nil {
|
||||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes, _, err := utils.DecodeBase64Image(emoji.Data)
|
bytes, _, err := utils.DecodeBase64Image(emoji.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,26 +43,26 @@ func UploadCustomEmoji(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
err = os.MkdirAll(config.CustomEmojiPath, 0o700)
|
err = os.MkdirAll(config.CustomEmojiPath, 0o700)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if utils.DoesFileExists(targetPath) {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = os.WriteFile(targetPath, bytes, 0o600); err != nil {
|
if err = os.WriteFile(targetPath, bytes, 0o600); err != nil {
|
||||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
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.
|
// DeleteCustomEmoji deletes a custom emoji.
|
||||||
func DeleteCustomEmoji(w http.ResponseWriter, r *http.Request) {
|
func (h *Handlers) DeleteCustomEmoji(w http.ResponseWriter, r *http.Request) {
|
||||||
if !requirePOST(w, r) {
|
if !requests.RequirePOST(w, r) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +73,7 @@ func DeleteCustomEmoji(w http.ResponseWriter, r *http.Request) {
|
||||||
emoji := new(deleteEmoji)
|
emoji := new(deleteEmoji)
|
||||||
|
|
||||||
if err := json.NewDecoder(r.Body).Decode(emoji); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(emoji); err != nil {
|
||||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,12 +82,12 @@ func DeleteCustomEmoji(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
if err := os.Remove(targetPath); err != nil {
|
if err := os.Remove(targetPath); err != nil {
|
||||||
if os.IsNotExist(err) {
|
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 {
|
} else {
|
||||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
}
|
}
|
||||||
return
|
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 (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -7,9 +7,9 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/owncast/owncast/config"
|
"github.com/owncast/owncast/config"
|
||||||
"github.com/owncast/owncast/controllers"
|
|
||||||
"github.com/owncast/owncast/core/user"
|
"github.com/owncast/owncast/core/user"
|
||||||
"github.com/owncast/owncast/utils"
|
"github.com/owncast/owncast/utils"
|
||||||
|
"github.com/owncast/owncast/webserver/responses"
|
||||||
)
|
)
|
||||||
|
|
||||||
type deleteExternalAPIUserRequest struct {
|
type deleteExternalAPIUserRequest struct {
|
||||||
|
@ -22,35 +22,35 @@ type createExternalAPIUserRequest struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateExternalAPIUser will generate a 3rd party access token.
|
// 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)
|
decoder := json.NewDecoder(r.Body)
|
||||||
var request createExternalAPIUserRequest
|
var request createExternalAPIUserRequest
|
||||||
if err := decoder.Decode(&request); err != nil {
|
if err := decoder.Decode(&request); err != nil {
|
||||||
controllers.BadRequestHandler(w, err)
|
responses.BadRequestHandler(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify all the scopes provided are valid
|
// Verify all the scopes provided are valid
|
||||||
if !user.HasValidScopes(request.Scopes) {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := utils.GenerateAccessToken()
|
token, err := utils.GenerateAccessToken()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
controllers.InternalErrorHandler(w, err)
|
responses.InternalErrorHandler(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
color := utils.GenerateRandomDisplayColor(config.MaxUserColor)
|
color := utils.GenerateRandomDisplayColor(config.MaxUserColor)
|
||||||
|
|
||||||
if err := user.InsertExternalAPIUser(token, request.Name, color, request.Scopes); err != nil {
|
if err := user.InsertExternalAPIUser(token, request.Name, color, request.Scopes); err != nil {
|
||||||
controllers.InternalErrorHandler(w, err)
|
responses.InternalErrorHandler(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
controllers.WriteResponse(w, user.ExternalAPIUser{
|
responses.WriteResponse(w, user.ExternalAPIUser{
|
||||||
AccessToken: token,
|
AccessToken: token,
|
||||||
DisplayName: request.Name,
|
DisplayName: request.Name,
|
||||||
DisplayColor: color,
|
DisplayColor: color,
|
||||||
|
@ -61,42 +61,42 @@ func CreateExternalAPIUser(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetExternalAPIUsers will return all 3rd party access tokens.
|
// 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")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
tokens, err := user.GetExternalAPIUser()
|
tokens, err := user.GetExternalAPIUser()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
controllers.InternalErrorHandler(w, err)
|
responses.InternalErrorHandler(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
controllers.WriteResponse(w, tokens)
|
responses.WriteResponse(w, tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteExternalAPIUser will return a single 3rd party access token.
|
// 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")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
if r.Method != controllers.POST {
|
if r.Method != http.MethodPost {
|
||||||
controllers.WriteSimpleResponse(w, false, r.Method+" not supported")
|
responses.WriteSimpleResponse(w, false, r.Method+" not supported")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
decoder := json.NewDecoder(r.Body)
|
decoder := json.NewDecoder(r.Body)
|
||||||
var request deleteExternalAPIUserRequest
|
var request deleteExternalAPIUserRequest
|
||||||
if err := decoder.Decode(&request); err != nil {
|
if err := decoder.Decode(&request); err != nil {
|
||||||
controllers.BadRequestHandler(w, err)
|
responses.BadRequestHandler(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if request.Token == "" {
|
if request.Token == "" {
|
||||||
controllers.BadRequestHandler(w, errors.New("must provide a token"))
|
responses.BadRequestHandler(w, errors.New("must provide a token"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := user.DeleteExternalAPIUser(request.Token); err != nil {
|
if err := user.DeleteExternalAPIUser(request.Token); err != nil {
|
||||||
controllers.InternalErrorHandler(w, err)
|
responses.InternalErrorHandler(w, err)
|
||||||
return
|
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 (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/owncast/owncast/activitypub/persistence"
|
"github.com/owncast/owncast/activitypub/persistence"
|
||||||
"github.com/owncast/owncast/activitypub/requests"
|
aprequests "github.com/owncast/owncast/activitypub/requests"
|
||||||
"github.com/owncast/owncast/controllers"
|
|
||||||
"github.com/owncast/owncast/core/data"
|
"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.
|
// ApproveFollower will approve a federated follow request.
|
||||||
func ApproveFollower(w http.ResponseWriter, r *http.Request) {
|
func (h *Handlers) ApproveFollower(w http.ResponseWriter, r *http.Request) {
|
||||||
if !requirePOST(w, r) {
|
if !requests.RequirePOST(w, r) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,14 +25,14 @@ func ApproveFollower(w http.ResponseWriter, r *http.Request) {
|
||||||
decoder := json.NewDecoder(r.Body)
|
decoder := json.NewDecoder(r.Body)
|
||||||
var approval approveFollowerRequest
|
var approval approveFollowerRequest
|
||||||
if err := decoder.Decode(&approval); err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if approval.Approved {
|
if approval.Approved {
|
||||||
// Approve a follower
|
// Approve a follower
|
||||||
if err := persistence.ApprovePreviousFollowRequest(approval.ActorIRI); err != nil {
|
if err := persistence.ApprovePreviousFollowRequest(approval.ActorIRI); err != nil {
|
||||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,44 +40,44 @@ func ApproveFollower(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
followRequest, err := persistence.GetFollower(approval.ActorIRI)
|
followRequest, err := persistence.GetFollower(approval.ActorIRI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the approval to the follow requestor.
|
// Send the approval to the follow requestor.
|
||||||
if err := requests.SendFollowAccept(followRequest.Inbox, followRequest.RequestObject, localAccountName); err != nil {
|
if err := aprequests.SendFollowAccept(followRequest.Inbox, followRequest.RequestObject, localAccountName); err != nil {
|
||||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Remove/block a follower
|
// Remove/block a follower
|
||||||
if err := persistence.BlockOrRejectFollower(approval.ActorIRI); err != nil {
|
if err := persistence.BlockOrRejectFollower(approval.ActorIRI); err != nil {
|
||||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
controllers.WriteSimpleResponse(w, true, "follower updated")
|
responses.WriteSimpleResponse(w, true, "follower updated")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPendingFollowRequests will return a list of pending follow requests.
|
// 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()
|
requests, err := persistence.GetPendingFollowRequests()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
controllers.WriteResponse(w, requests)
|
responses.WriteResponse(w, requests)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBlockedAndRejectedFollowers will return blocked and rejected followers.
|
// 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()
|
rejections, err := persistence.GetBlockedAndRejectedFollowers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
controllers.WriteResponse(w, rejections)
|
responses.WriteResponse(w, rejections)
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package admin
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -9,7 +9,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetHardwareStats will return hardware utilization over time.
|
// 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()
|
m := metrics.GetMetrics()
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
|
@ -1,4 +1,4 @@
|
||||||
package admin
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -11,7 +11,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetLogs will return all logs.
|
// 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()
|
logs := logging.Logger.AllEntries()
|
||||||
response := make([]logsResponse, 0)
|
response := make([]logsResponse, 0)
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ func GetLogs(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetWarnings will return only warning and error logs.
|
// 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()
|
logs := logging.Logger.WarningEntries()
|
||||||
response := make([]logsResponse, 0)
|
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 (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -14,7 +14,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetServerConfig gets the config details of the server.
|
// 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())
|
ffmpeg := utils.ValidatedFfmpegPath(data.GetFfMpegPath())
|
||||||
usernameBlocklist := data.GetForbiddenUsernameList()
|
usernameBlocklist := data.GetForbiddenUsernameList()
|
||||||
usernameSuggestions := data.GetSuggestedUsernamesList()
|
usernameSuggestions := data.GetSuggestedUsernamesList()
|
||||||
|
@ -34,7 +34,7 @@ func GetServerConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
response := serverConfigAdminResponse{
|
response := serverConfigAdminResponse{
|
||||||
InstanceDetails: webConfigResponse{
|
InstanceDetails: adminWebConfigResponse{
|
||||||
Name: data.GetServerName(),
|
Name: data.GetServerName(),
|
||||||
Summary: data.GetServerSummary(),
|
Summary: data.GetServerSummary(),
|
||||||
Tags: data.GetServerMetadataTags(),
|
Tags: data.GetServerMetadataTags(),
|
||||||
|
@ -77,7 +77,7 @@ func GetServerConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
VideoCodec: data.GetVideoCodec(),
|
VideoCodec: data.GetVideoCodec(),
|
||||||
ForbiddenUsernames: usernameBlocklist,
|
ForbiddenUsernames: usernameBlocklist,
|
||||||
SuggestedUsernames: usernameSuggestions,
|
SuggestedUsernames: usernameSuggestions,
|
||||||
Federation: federationConfigResponse{
|
Federation: adminFederationConfigResponse{
|
||||||
Enabled: data.GetFederationEnabled(),
|
Enabled: data.GetFederationEnabled(),
|
||||||
IsPrivate: data.GetFederationIsPrivate(),
|
IsPrivate: data.GetFederationIsPrivate(),
|
||||||
Username: data.GetFederationUsername(),
|
Username: data.GetFederationUsername(),
|
||||||
|
@ -85,7 +85,7 @@ func GetServerConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
ShowEngagement: data.GetFederationShowEngagement(),
|
ShowEngagement: data.GetFederationShowEngagement(),
|
||||||
BlockedDomains: data.GetBlockedFederatedDomains(),
|
BlockedDomains: data.GetBlockedFederatedDomains(),
|
||||||
},
|
},
|
||||||
Notifications: notificationsConfigResponse{
|
Notifications: adminNotificationsConfigResponse{
|
||||||
Discord: data.GetDiscordConfig(),
|
Discord: data.GetDiscordConfig(),
|
||||||
Browser: data.GetBrowserPushConfig(),
|
Browser: data.GetBrowserPushConfig(),
|
||||||
},
|
},
|
||||||
|
@ -100,8 +100,8 @@ func GetServerConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type serverConfigAdminResponse struct {
|
type serverConfigAdminResponse struct {
|
||||||
InstanceDetails webConfigResponse `json:"instanceDetails"`
|
InstanceDetails adminWebConfigResponse `json:"instanceDetails"`
|
||||||
Notifications notificationsConfigResponse `json:"notifications"`
|
Notifications adminNotificationsConfigResponse `json:"notifications"`
|
||||||
YP yp `json:"yp"`
|
YP yp `json:"yp"`
|
||||||
FFmpegPath string `json:"ffmpegPath"`
|
FFmpegPath string `json:"ffmpegPath"`
|
||||||
AdminPassword string `json:"adminPassword"`
|
AdminPassword string `json:"adminPassword"`
|
||||||
|
@ -110,7 +110,7 @@ type serverConfigAdminResponse struct {
|
||||||
VideoCodec string `json:"videoCodec"`
|
VideoCodec string `json:"videoCodec"`
|
||||||
VideoServingEndpoint string `json:"videoServingEndpoint"`
|
VideoServingEndpoint string `json:"videoServingEndpoint"`
|
||||||
S3 models.S3 `json:"s3"`
|
S3 models.S3 `json:"s3"`
|
||||||
Federation federationConfigResponse `json:"federation"`
|
Federation adminFederationConfigResponse `json:"federation"`
|
||||||
SupportedCodecs []string `json:"supportedCodecs"`
|
SupportedCodecs []string `json:"supportedCodecs"`
|
||||||
ExternalActions []models.ExternalAction `json:"externalActions"`
|
ExternalActions []models.ExternalAction `json:"externalActions"`
|
||||||
ForbiddenUsernames []string `json:"forbiddenUsernames"`
|
ForbiddenUsernames []string `json:"forbiddenUsernames"`
|
||||||
|
@ -132,7 +132,7 @@ type videoSettings struct {
|
||||||
LatencyLevel int `json:"latencyLevel"`
|
LatencyLevel int `json:"latencyLevel"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type webConfigResponse struct {
|
type adminWebConfigResponse struct {
|
||||||
AppearanceVariables map[string]string `json:"appearanceVariables"`
|
AppearanceVariables map[string]string `json:"appearanceVariables"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
WelcomeMessage string `json:"welcomeMessage"`
|
WelcomeMessage string `json:"welcomeMessage"`
|
||||||
|
@ -155,7 +155,7 @@ type yp struct {
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type federationConfigResponse struct {
|
type adminFederationConfigResponse struct {
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
GoLiveMessage string `json:"goLiveMessage"`
|
GoLiveMessage string `json:"goLiveMessage"`
|
||||||
BlockedDomains []string `json:"blockedDomains"`
|
BlockedDomains []string `json:"blockedDomains"`
|
||||||
|
@ -164,7 +164,7 @@ type federationConfigResponse struct {
|
||||||
ShowEngagement bool `json:"showEngagement"`
|
ShowEngagement bool `json:"showEngagement"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type notificationsConfigResponse struct {
|
type adminNotificationsConfigResponse struct {
|
||||||
Browser models.BrowserNotificationConfiguration `json:"browser"`
|
Browser models.BrowserNotificationConfiguration `json:"browser"`
|
||||||
Discord models.DiscordConfiguration `json:"discord"`
|
Discord models.DiscordConfiguration `json:"discord"`
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package admin
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -10,7 +10,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/owncast/owncast/config"
|
"github.com/owncast/owncast/config"
|
||||||
"github.com/owncast/owncast/controllers"
|
"github.com/owncast/owncast/webserver/responses"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ specific conditions are met.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// AutoUpdateOptions will return what auto update options are available.
|
// 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 {
|
type autoUpdateOptionsResponse struct {
|
||||||
SupportsUpdate bool `json:"supportsUpdate"`
|
SupportsUpdate bool `json:"supportsUpdate"`
|
||||||
CanRestart bool `json:"canRestart"`
|
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
|
// Nothing is supported when running under "dev" or the feature is
|
||||||
// explicitly disabled.
|
// explicitly disabled.
|
||||||
if config.BuildPlatform == "dev" || !config.EnableAutoUpdate {
|
if config.BuildPlatform == "dev" || !config.EnableAutoUpdate {
|
||||||
controllers.WriteResponse(w, updateOptions)
|
responses.WriteResponse(w, updateOptions)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,11 +58,11 @@ func AutoUpdateOptions(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
updateOptions.CanRestart = isRunningUnderSystemD()
|
updateOptions.CanRestart = isRunningUnderSystemD()
|
||||||
|
|
||||||
controllers.WriteResponse(w, updateOptions)
|
responses.WriteResponse(w, updateOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AutoUpdateStart will begin the auto update process.
|
// 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.
|
// We return the console output directly to the client.
|
||||||
w.Header().Set("Content-Type", "text/plain")
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ func AutoUpdateStart(w http.ResponseWriter, r *http.Request) {
|
||||||
updater, err := downloadInstaller()
|
updater, err := downloadInstaller()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorln(err)
|
log.Errorln(err)
|
||||||
controllers.WriteSimpleResponse(w, false, "failed to download and run installer")
|
responses.WriteSimpleResponse(w, false, "failed to download and run installer")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,12 +95,12 @@ func AutoUpdateStart(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// AutoUpdateForceQuit will force quit the service.
|
// 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.")
|
log.Warnln("Owncast is exiting due to request.")
|
||||||
go func() {
|
go func() {
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}()
|
}()
|
||||||
controllers.WriteSimpleResponse(w, true, "forcing quit")
|
responses.WriteSimpleResponse(w, true, "forcing quit")
|
||||||
}
|
}
|
||||||
|
|
||||||
func downloadInstaller() (string, error) {
|
func downloadInstaller() (string, error) {
|
|
@ -1,4 +1,4 @@
|
||||||
package admin
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -13,7 +13,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Status gets the details of the inbound broadcaster.
|
// 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()
|
broadcaster := core.GetBroadcaster()
|
||||||
status := core.GetStatus()
|
status := core.GetStatus()
|
||||||
currentBroadcast := core.GetCurrentBroadcast()
|
currentBroadcast := core.GetCurrentBroadcast()
|
|
@ -1,4 +1,4 @@
|
||||||
package admin
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -11,7 +11,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetVideoPlaybackMetrics returns video playback metrics.
|
// 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 {
|
type response struct {
|
||||||
Errors []metrics.TimestampedValue `json:"errors"`
|
Errors []metrics.TimestampedValue `json:"errors"`
|
||||||
QualityVariantChanges []metrics.TimestampedValue `json:"qualityVariantChanges"`
|
QualityVariantChanges []metrics.TimestampedValue `json:"qualityVariantChanges"`
|
|
@ -1,4 +1,4 @@
|
||||||
package admin
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -6,20 +6,20 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/owncast/owncast/controllers"
|
|
||||||
"github.com/owncast/owncast/core"
|
"github.com/owncast/owncast/core"
|
||||||
"github.com/owncast/owncast/core/user"
|
"github.com/owncast/owncast/core/user"
|
||||||
"github.com/owncast/owncast/metrics"
|
"github.com/owncast/owncast/metrics"
|
||||||
"github.com/owncast/owncast/models"
|
"github.com/owncast/owncast/models"
|
||||||
|
"github.com/owncast/owncast/webserver/responses"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetViewersOverTime will return the number of viewers at points in time.
|
// 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")
|
windowStartAtStr := r.URL.Query().Get("windowStart")
|
||||||
windowStartAtUnix, err := strconv.Atoi(windowStartAtStr)
|
windowStartAtUnix, err := strconv.Atoi(windowStartAtStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ func GetViewersOverTime(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetActiveViewers returns currently connected clients.
|
// 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()
|
c := core.GetActiveViewers()
|
||||||
viewers := make([]models.Viewer, 0, len(c))
|
viewers := make([]models.Viewer, 0, len(c))
|
||||||
for _, v := range c {
|
for _, v := range c {
|
||||||
|
@ -45,11 +45,11 @@ func GetActiveViewers(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
if err := json.NewEncoder(w).Encode(viewers); err != nil {
|
if err := json.NewEncoder(w).Encode(viewers); err != nil {
|
||||||
controllers.InternalErrorHandler(w, err)
|
responses.InternalErrorHandler(w, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExternalGetActiveViewers returns currently connected clients.
|
// ExternalGetActiveViewers returns currently connected clients.
|
||||||
func ExternalGetActiveViewers(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
func (h *Handlers) ExternalGetActiveViewers(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
||||||
GetConnectedChatClients(w, r)
|
h.GetConnectedChatClients(w, r)
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package admin
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -6,9 +6,9 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/owncast/owncast/controllers"
|
|
||||||
"github.com/owncast/owncast/core/data"
|
"github.com/owncast/owncast/core/data"
|
||||||
"github.com/owncast/owncast/models"
|
"github.com/owncast/owncast/models"
|
||||||
|
"github.com/owncast/owncast/webserver/responses"
|
||||||
)
|
)
|
||||||
|
|
||||||
type deleteWebhookRequest struct {
|
type deleteWebhookRequest struct {
|
||||||
|
@ -21,27 +21,27 @@ type createWebhookRequest struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateWebhook will add a single webhook.
|
// 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)
|
decoder := json.NewDecoder(r.Body)
|
||||||
var request createWebhookRequest
|
var request createWebhookRequest
|
||||||
if err := decoder.Decode(&request); err != nil {
|
if err := decoder.Decode(&request); err != nil {
|
||||||
controllers.BadRequestHandler(w, err)
|
responses.BadRequestHandler(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify all the scopes provided are valid
|
// Verify all the scopes provided are valid
|
||||||
if !models.HasValidEvents(request.Events) {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
newWebhookID, err := data.InsertWebhook(request.URL, request.Events)
|
newWebhookID, err := data.InsertWebhook(request.URL, request.Events)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
controllers.InternalErrorHandler(w, err)
|
responses.InternalErrorHandler(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
controllers.WriteResponse(w, models.Webhook{
|
responses.WriteResponse(w, models.Webhook{
|
||||||
ID: newWebhookID,
|
ID: newWebhookID,
|
||||||
URL: request.URL,
|
URL: request.URL,
|
||||||
Events: request.Events,
|
Events: request.Events,
|
||||||
|
@ -51,34 +51,34 @@ func CreateWebhook(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetWebhooks will return all webhooks.
|
// 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()
|
webhooks, err := data.GetWebhooks()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
controllers.InternalErrorHandler(w, err)
|
responses.InternalErrorHandler(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
controllers.WriteResponse(w, webhooks)
|
responses.WriteResponse(w, webhooks)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteWebhook will delete a single webhook.
|
// DeleteWebhook will delete a single webhook.
|
||||||
func DeleteWebhook(w http.ResponseWriter, r *http.Request) {
|
func (h *Handlers) DeleteWebhook(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method != controllers.POST {
|
if r.Method != http.MethodPost {
|
||||||
controllers.WriteSimpleResponse(w, false, r.Method+" not supported")
|
responses.WriteSimpleResponse(w, false, r.Method+" not supported")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
decoder := json.NewDecoder(r.Body)
|
decoder := json.NewDecoder(r.Body)
|
||||||
var request deleteWebhookRequest
|
var request deleteWebhookRequest
|
||||||
if err := decoder.Decode(&request); err != nil {
|
if err := decoder.Decode(&request); err != nil {
|
||||||
controllers.BadRequestHandler(w, err)
|
responses.BadRequestHandler(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := data.DeleteWebhook(request.ID); err != nil {
|
if err := data.DeleteWebhook(request.ID); err != nil {
|
||||||
controllers.InternalErrorHandler(w, err)
|
responses.InternalErrorHandler(w, err)
|
||||||
return
|
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/activitypub"
|
||||||
"github.com/owncast/owncast/auth"
|
"github.com/owncast/owncast/auth"
|
||||||
fediverseauth "github.com/owncast/owncast/auth/fediverse"
|
fediverseauth "github.com/owncast/owncast/auth/fediverse"
|
||||||
"github.com/owncast/owncast/controllers"
|
|
||||||
"github.com/owncast/owncast/core/chat"
|
"github.com/owncast/owncast/core/chat"
|
||||||
"github.com/owncast/owncast/core/data"
|
"github.com/owncast/owncast/core/data"
|
||||||
"github.com/owncast/owncast/core/user"
|
"github.com/owncast/owncast/core/user"
|
||||||
|
"github.com/owncast/owncast/webserver/responses"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -23,29 +23,29 @@ func RegisterFediverseOTPRequest(u user.User, w http.ResponseWriter, r *http.Req
|
||||||
var req request
|
var req request
|
||||||
decoder := json.NewDecoder(r.Body)
|
decoder := json.NewDecoder(r.Body)
|
||||||
if err := decoder.Decode(&req); err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
accessToken := r.URL.Query().Get("accessToken")
|
accessToken := r.URL.Query().Get("accessToken")
|
||||||
reg, success, err := fediverseauth.RegisterFediverseOTP(accessToken, u.ID, u.DisplayName, req.FediverseAccount)
|
reg, success, err := fediverseauth.RegisterFediverseOTP(accessToken, u.ID, u.DisplayName, req.FediverseAccount)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !success {
|
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
|
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)
|
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 {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
controllers.WriteSimpleResponse(w, true, "")
|
responses.WriteSimpleResponse(w, true, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyFediverseOTPRequest verifies the given OTP code for the given access token.
|
// 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
|
var req request
|
||||||
decoder := json.NewDecoder(r.Body)
|
decoder := json.NewDecoder(r.Body)
|
||||||
if err := decoder.Decode(&req); err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
accessToken := r.URL.Query().Get("accessToken")
|
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.
|
// Update the current user's access token to point to the existing user id.
|
||||||
userID := u.ID
|
userID := u.ID
|
||||||
if err := user.SetAccessTokenToOwner(accessToken, userID); err != nil {
|
if err := user.SetAccessTokenToOwner(accessToken, userID); err != nil {
|
||||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ func VerifyFediverseOTPRequest(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
controllers.WriteSimpleResponse(w, true, "")
|
responses.WriteSimpleResponse(w, true, "")
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ func VerifyFediverseOTPRequest(w http.ResponseWriter, r *http.Request) {
|
||||||
// Otherwise, save this as new auth.
|
// Otherwise, save this as new auth.
|
||||||
log.Debug("fediverse account does not already exist, saving it as a new one for the current user")
|
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 {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,5 +104,5 @@ func VerifyFediverseOTPRequest(w http.ResponseWriter, r *http.Request) {
|
||||||
log.Errorln(err)
|
log.Errorln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
controllers.WriteSimpleResponse(w, true, "")
|
responses.WriteSimpleResponse(w, true, "")
|
||||||
}
|
}
|
|
@ -8,9 +8,9 @@ import (
|
||||||
|
|
||||||
"github.com/owncast/owncast/auth"
|
"github.com/owncast/owncast/auth"
|
||||||
ia "github.com/owncast/owncast/auth/indieauth"
|
ia "github.com/owncast/owncast/auth/indieauth"
|
||||||
"github.com/owncast/owncast/controllers"
|
|
||||||
"github.com/owncast/owncast/core/chat"
|
"github.com/owncast/owncast/core/chat"
|
||||||
"github.com/owncast/owncast/core/user"
|
"github.com/owncast/owncast/core/user"
|
||||||
|
"github.com/owncast/owncast/webserver/responses"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -27,12 +27,12 @@ func StartAuthFlow(u user.User, w http.ResponseWriter, r *http.Request) {
|
||||||
var authRequest request
|
var authRequest request
|
||||||
p, err := io.ReadAll(r.Body)
|
p, err := io.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := json.Unmarshal(p, &authRequest); err != nil {
|
if err := json.Unmarshal(p, &authRequest); err != nil {
|
||||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
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)
|
redirectURL, err := ia.StartAuthFlow(authRequest.AuthHost, u.ID, accessToken, u.DisplayName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
redirectResponse := response{
|
redirectResponse := response{
|
||||||
Redirect: redirectURL.String(),
|
Redirect: redirectURL.String(),
|
||||||
}
|
}
|
||||||
controllers.WriteResponse(w, redirectResponse)
|
responses.WriteResponse(w, redirectResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleRedirect will handle the redirect from an IndieAuth server to
|
// 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 {
|
if err != nil {
|
||||||
log.Debugln(err)
|
log.Debugln(err)
|
||||||
msg := `Unable to complete authentication. <a href="/">Go back.</a><hr/>`
|
msg := `Unable to complete authentication. <a href="/">Go back.</a><hr/>`
|
||||||
_ = controllers.WriteString(w, msg, http.StatusBadRequest)
|
_ = responses.WriteString(w, msg, http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ func HandleRedirect(w http.ResponseWriter, r *http.Request) {
|
||||||
accessToken := request.CurrentAccessToken
|
accessToken := request.CurrentAccessToken
|
||||||
userID := u.ID
|
userID := u.ID
|
||||||
if err := user.SetAccessTokenToOwner(accessToken, userID); err != nil {
|
if err := user.SetAccessTokenToOwner(accessToken, userID); err != nil {
|
||||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ func HandleRedirect(w http.ResponseWriter, r *http.Request) {
|
||||||
// Otherwise, save this as new auth.
|
// Otherwise, save this as new auth.
|
||||||
log.Debug("indieauth token does not already exist, saving it as a new one for the current user")
|
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 {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,8 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
ia "github.com/owncast/owncast/auth/indieauth"
|
ia "github.com/owncast/owncast/auth/indieauth"
|
||||||
"github.com/owncast/owncast/controllers"
|
|
||||||
"github.com/owncast/owncast/webserver/middleware"
|
"github.com/owncast/owncast/webserver/middleware"
|
||||||
|
"github.com/owncast/owncast/webserver/responses"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HandleAuthEndpoint will handle the IndieAuth auth endpoint.
|
// 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)
|
request, err := ia.StartServerAuth(clientID, redirectURI, codeChallenge, state, me)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = controllers.WriteString(w, err.Error(), http.StatusInternalServerError)
|
_ = responses.WriteString(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
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.
|
// If the URL is invalid then return with specific "invalid_request" error.
|
||||||
u, err := url.Parse(redirectURI)
|
u, err := url.Parse(redirectURI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
controllers.WriteResponse(w, ia.Response{
|
responses.WriteResponse(w, ia.Response{
|
||||||
Error: "invalid_request",
|
Error: "invalid_request",
|
||||||
ErrorDescription: err.Error(),
|
ErrorDescription: err.Error(),
|
||||||
})
|
})
|
||||||
|
@ -59,7 +59,7 @@ func handleAuthEndpointGet(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
func handleAuthEndpointPost(w http.ResponseWriter, r *http.Request) {
|
func handleAuthEndpointPost(w http.ResponseWriter, r *http.Request) {
|
||||||
if err := r.ParseForm(); err != nil {
|
if err := r.ParseForm(); err != nil {
|
||||||
controllers.WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,12 +72,12 @@ func handleAuthEndpointPost(w http.ResponseWriter, r *http.Request) {
|
||||||
// "invalid_client" error.
|
// "invalid_client" error.
|
||||||
response, err := ia.CompleteServerAuth(code, redirectURI, clientID, codeVerifier)
|
response, err := ia.CompleteServerAuth(code, redirectURI, clientID, codeVerifier)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
controllers.WriteResponse(w, ia.Response{
|
responses.WriteResponse(w, ia.Response{
|
||||||
Error: "invalid_client",
|
Error: "invalid_client",
|
||||||
ErrorDescription: err.Error(),
|
ErrorDescription: err.Error(),
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
controllers.WriteResponse(w, response)
|
responses.WriteResponse(w, response)
|
||||||
}
|
}
|
|
@ -1,7 +1,8 @@
|
||||||
package controllers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/owncast/owncast/config"
|
"github.com/owncast/owncast/config"
|
||||||
|
@ -9,17 +10,18 @@ import (
|
||||||
"github.com/owncast/owncast/core/user"
|
"github.com/owncast/owncast/core/user"
|
||||||
"github.com/owncast/owncast/utils"
|
"github.com/owncast/owncast/utils"
|
||||||
"github.com/owncast/owncast/webserver/middleware"
|
"github.com/owncast/owncast/webserver/middleware"
|
||||||
|
"github.com/owncast/owncast/webserver/responses"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExternalGetChatMessages gets all of the chat messages.
|
// 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)
|
middleware.EnableCors(w)
|
||||||
getChatMessages(w, r)
|
getChatMessages(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetChatMessages gets all of the chat messages.
|
// 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)
|
middleware.EnableCors(w)
|
||||||
getChatMessages(w, r)
|
getChatMessages(w, r)
|
||||||
}
|
}
|
||||||
|
@ -36,14 +38,12 @@ func getChatMessages(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
w.WriteHeader(http.StatusNotImplemented)
|
w.WriteHeader(http.StatusNotImplemented)
|
||||||
if err := json.NewEncoder(w).Encode(j{"error": "method not implemented (PRs are accepted)"}); err != nil {
|
responses.BadRequestHandler(w, errors.New("method not implemented"))
|
||||||
InternalErrorHandler(w, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterAnonymousChatUser will register a new user.
|
// 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)
|
middleware.EnableCors(w)
|
||||||
|
|
||||||
if r.Method == "OPTIONS" {
|
if r.Method == "OPTIONS" {
|
||||||
|
@ -54,8 +54,7 @@ func RegisterAnonymousChatUser(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.Method != http.MethodPost {
|
if r.Method != http.MethodPost {
|
||||||
// nolint:goconst
|
responses.WriteSimpleResponse(w, false, r.Method+" not supported")
|
||||||
WriteSimpleResponse(w, false, r.Method+" not supported")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +81,7 @@ func RegisterAnonymousChatUser(w http.ResponseWriter, r *http.Request) {
|
||||||
proposedNewDisplayName := utils.MakeSafeStringOfLength(request.DisplayName, config.MaxChatDisplayNameLength)
|
proposedNewDisplayName := utils.MakeSafeStringOfLength(request.DisplayName, config.MaxChatDisplayNameLength)
|
||||||
newUser, accessToken, err := user.CreateAnonymousUser(proposedNewDisplayName)
|
newUser, accessToken, err := user.CreateAnonymousUser(proposedNewDisplayName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,5 +94,5 @@ func RegisterAnonymousChatUser(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
middleware.DisableCache(w)
|
middleware.DisableCache(w)
|
||||||
|
|
||||||
WriteResponse(w, response)
|
responses.WriteResponse(w, response)
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package controllers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/owncast/owncast/models"
|
"github.com/owncast/owncast/models"
|
||||||
"github.com/owncast/owncast/utils"
|
"github.com/owncast/owncast/utils"
|
||||||
"github.com/owncast/owncast/webserver/middleware"
|
"github.com/owncast/owncast/webserver/middleware"
|
||||||
|
"github.com/owncast/owncast/webserver/responses"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -58,7 +59,7 @@ type authenticationConfigResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetWebConfig gets the status of the server.
|
// 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.EnableCors(w)
|
||||||
middleware.DisableCache(w)
|
middleware.DisableCache(w)
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
@ -66,7 +67,7 @@ func GetWebConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
configuration := getConfigResponse()
|
configuration := getConfigResponse()
|
||||||
|
|
||||||
if err := json.NewEncoder(w).Encode(configuration); err != nil {
|
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.
|
// 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)
|
middleware.EnableCors(w)
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
platforms := models.GetAllSocialHandles()
|
platforms := models.GetAllSocialHandles()
|
||||||
if err := json.NewEncoder(w).Encode(platforms); err != nil {
|
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 (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -7,7 +7,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// ServeCustomJavascript will serve optional custom Javascript.
|
// 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()
|
js := data.GetCustomJavascript()
|
||||||
_, _ = w.Write([]byte(js))
|
_, _ = 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 (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -9,20 +9,21 @@ import (
|
||||||
"github.com/owncast/owncast/config"
|
"github.com/owncast/owncast/config"
|
||||||
"github.com/owncast/owncast/core/data"
|
"github.com/owncast/owncast/core/data"
|
||||||
"github.com/owncast/owncast/router/middleware"
|
"github.com/owncast/owncast/router/middleware"
|
||||||
|
"github.com/owncast/owncast/webserver/responses"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetCustomEmojiList returns a list of emoji via the API.
|
// 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()
|
emojiList := data.GetEmojiList()
|
||||||
middleware.SetCachingHeaders(w, r)
|
middleware.SetCachingHeaders(w, r)
|
||||||
|
|
||||||
if err := json.NewEncoder(w).Encode(emojiList); err != nil {
|
if err := json.NewEncoder(w).Encode(emojiList); err != nil {
|
||||||
InternalErrorHandler(w, err)
|
responses.InternalErrorHandler(w, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCustomEmojiImage returns a single emoji image.
|
// 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/")
|
path := strings.TrimPrefix(r.URL.Path, "/img/emoji/")
|
||||||
r.URL.Path = path
|
r.URL.Path = path
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
package handlers
|
|
|
@ -1,22 +1,23 @@
|
||||||
package controllers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/owncast/owncast/activitypub/persistence"
|
"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).
|
// 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)
|
followers, total, err := persistence.GetFederationFollowers(limit, offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
WriteSimpleResponse(w, false, "unable to fetch followers")
|
responses.WriteSimpleResponse(w, false, "unable to fetch followers")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
response := PaginatedResponse{
|
response := responses.PaginatedResponse{
|
||||||
Total: total,
|
Total: total,
|
||||||
Results: followers,
|
Results: followers,
|
||||||
}
|
}
|
||||||
WriteResponse(w, response)
|
responses.WriteResponse(w, response)
|
||||||
}
|
}
|
|
@ -1 +1,55 @@
|
||||||
package handlers
|
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 (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
"github.com/owncast/owncast/config"
|
"github.com/owncast/owncast/config"
|
||||||
"github.com/owncast/owncast/utils"
|
"github.com/owncast/owncast/utils"
|
||||||
|
"github.com/owncast/owncast/webserver/responses"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -14,7 +15,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetThumbnail will return the thumbnail image as a response.
|
// 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"
|
imageFilename := "thumbnail.jpg"
|
||||||
imagePath := filepath.Join(config.TempDir, imageFilename)
|
imagePath := filepath.Join(config.TempDir, imageFilename)
|
||||||
|
|
||||||
|
@ -24,21 +25,21 @@ func GetThumbnail(w http.ResponseWriter, r *http.Request) {
|
||||||
if utils.DoesFileExists(imagePath) {
|
if utils.DoesFileExists(imagePath) {
|
||||||
imageBytes, err = getImage(imagePath)
|
imageBytes, err = getImage(imagePath)
|
||||||
} else {
|
} else {
|
||||||
GetLogo(w, r)
|
h.GetLogo(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
GetLogo(w, r)
|
h.GetLogo(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheTime := utils.GetCacheDurationSecondsForPath(imagePath)
|
cacheTime := utils.GetCacheDurationSecondsForPath(imagePath)
|
||||||
writeBytesAsImage(imageBytes, contentTypeJPEG, w, cacheTime)
|
responses.WriteBytesAsImage(imageBytes, contentTypeJPEG, w, cacheTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPreview will return the preview gif as a response.
|
// 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"
|
imageFilename := "preview.gif"
|
||||||
imagePath := filepath.Join(config.TempDir, imageFilename)
|
imagePath := filepath.Join(config.TempDir, imageFilename)
|
||||||
|
|
||||||
|
@ -48,15 +49,15 @@ func GetPreview(w http.ResponseWriter, r *http.Request) {
|
||||||
if utils.DoesFileExists(imagePath) {
|
if utils.DoesFileExists(imagePath) {
|
||||||
imageBytes, err = getImage(imagePath)
|
imageBytes, err = getImage(imagePath)
|
||||||
} else {
|
} else {
|
||||||
GetLogo(w, r)
|
h.GetLogo(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
GetLogo(w, r)
|
h.GetLogo(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheTime := utils.GetCacheDurationSecondsForPath(imagePath)
|
cacheTime := utils.GetCacheDurationSecondsForPath(imagePath)
|
||||||
writeBytesAsImage(imageBytes, contentTypeGIF, w, cacheTime)
|
responses.WriteBytesAsImage(imageBytes, contentTypeGIF, w, cacheTime)
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package controllers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -19,7 +19,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// IndexHandler handles the default index route.
|
// 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)
|
middleware.EnableCors(w)
|
||||||
|
|
||||||
isIndexRequest := r.URL.Path == "/" || filepath.Base(r.URL.Path) == "index.html" || filepath.Base(r.URL.Path) == ""
|
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 (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/owncast/owncast/config"
|
"github.com/owncast/owncast/config"
|
||||||
"github.com/owncast/owncast/core/data"
|
"github.com/owncast/owncast/core/data"
|
||||||
"github.com/owncast/owncast/static"
|
"github.com/owncast/owncast/static"
|
||||||
"github.com/owncast/owncast/utils"
|
"github.com/owncast/owncast/utils"
|
||||||
|
"github.com/owncast/owncast/webserver/responses"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _hasWarnedSVGLogo = false
|
var _hasWarnedSVGLogo = false
|
||||||
|
|
||||||
// GetLogo will return the logo image as a response.
|
// 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()
|
imageFilename := data.GetLogoPath()
|
||||||
if imageFilename == "" {
|
if imageFilename == "" {
|
||||||
returnDefault(w)
|
returnDefault(w)
|
||||||
|
@ -39,20 +39,20 @@ func GetLogo(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheTime := utils.GetCacheDurationSecondsForPath(imagePath)
|
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
|
// GetCompatibleLogo will return the logo unless it's a SVG
|
||||||
// and in that case will return a default placeholder.
|
// and in that case will return a default placeholder.
|
||||||
// Used for sharing to external social networks that generally
|
// Used for sharing to external social networks that generally
|
||||||
// don't support SVG.
|
// 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()
|
imageFilename := data.GetLogoPath()
|
||||||
|
|
||||||
// If the logo image is not a SVG then we can return it
|
// If the logo image is not a SVG then we can return it
|
||||||
// without any problems.
|
// without any problems.
|
||||||
if imageFilename != "" && filepath.Ext(imageFilename) != ".svg" {
|
if imageFilename != "" && filepath.Ext(imageFilename) != ".svg" {
|
||||||
GetLogo(w, r)
|
h.GetLogo(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ func GetCompatibleLogo(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheTime := utils.GetCacheDurationSecondsForPath(imagePath)
|
cacheTime := utils.GetCacheDurationSecondsForPath(imagePath)
|
||||||
writeBytesAsImage(imageBytes, contentType, w, cacheTime)
|
responses.WriteBytesAsImage(imageBytes, contentType, w, cacheTime)
|
||||||
|
|
||||||
if !_hasWarnedSVGLogo {
|
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.")
|
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) {
|
func returnDefault(w http.ResponseWriter) {
|
||||||
imageBytes := static.GetLogo()
|
imageBytes := static.GetLogo()
|
||||||
cacheTime := utils.GetCacheDurationSecondsForPath("logo.png")
|
cacheTime := utils.GetCacheDurationSecondsForPath("logo.png")
|
||||||
writeBytesAsImage(imageBytes, "image/png", w, cacheTime)
|
responses.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.")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getImage(path string) ([]byte, error) {
|
func getImage(path string) ([]byte, error) {
|
|
@ -1,4 +1,4 @@
|
||||||
package moderation
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -6,15 +6,15 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/owncast/owncast/controllers"
|
|
||||||
"github.com/owncast/owncast/core/chat"
|
"github.com/owncast/owncast/core/chat"
|
||||||
"github.com/owncast/owncast/core/chat/events"
|
"github.com/owncast/owncast/core/chat/events"
|
||||||
"github.com/owncast/owncast/core/user"
|
"github.com/owncast/owncast/core/user"
|
||||||
|
"github.com/owncast/owncast/webserver/responses"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetUserDetails returns the details of a chat user for moderators.
|
// 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 {
|
type connectedClient struct {
|
||||||
ConnectedAt time.Time `json:"connectedAt"`
|
ConnectedAt time.Time `json:"connectedAt"`
|
||||||
UserAgent string `json:"userAgent"`
|
UserAgent string `json:"userAgent"`
|
||||||
|
@ -68,6 +68,6 @@ func GetUserDetails(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
if err := json.NewEncoder(w).Encode(res); err != nil {
|
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 (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
"github.com/owncast/owncast/core/user"
|
"github.com/owncast/owncast/core/user"
|
||||||
"github.com/owncast/owncast/notifications"
|
"github.com/owncast/owncast/notifications"
|
||||||
|
"github.com/owncast/owncast/webserver/responses"
|
||||||
|
|
||||||
"github.com/owncast/owncast/utils"
|
"github.com/owncast/owncast/utils"
|
||||||
|
|
||||||
|
@ -14,9 +15,9 @@ import (
|
||||||
|
|
||||||
// RegisterForLiveNotifications will register a channel + destination to be
|
// RegisterForLiveNotifications will register a channel + destination to be
|
||||||
// notified when a stream goes live.
|
// notified when a stream goes live.
|
||||||
func RegisterForLiveNotifications(u user.User, w http.ResponseWriter, r *http.Request) {
|
func (h *Handlers) RegisterForLiveNotifications(u user.User, w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method != POST {
|
if r.Method != http.MethodPost {
|
||||||
WriteSimpleResponse(w, false, r.Method+" not supported")
|
responses.WriteSimpleResponse(w, false, r.Method+" not supported")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +32,7 @@ func RegisterForLiveNotifications(u user.User, w http.ResponseWriter, r *http.Re
|
||||||
var req request
|
var req request
|
||||||
if err := decoder.Decode(&req); err != nil {
|
if err := decoder.Decode(&req); err != nil {
|
||||||
log.Errorln(err)
|
log.Errorln(err)
|
||||||
WriteSimpleResponse(w, false, "unable to register for notifications")
|
responses.WriteSimpleResponse(w, false, "unable to register for notifications")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,13 +40,13 @@ func RegisterForLiveNotifications(u user.User, w http.ResponseWriter, r *http.Re
|
||||||
validTypes := []string{notifications.BrowserPushNotification}
|
validTypes := []string{notifications.BrowserPushNotification}
|
||||||
_, validChannel := utils.FindInSlice(validTypes, req.Channel)
|
_, validChannel := utils.FindInSlice(validTypes, req.Channel)
|
||||||
if !validChannel {
|
if !validChannel {
|
||||||
WriteSimpleResponse(w, false, "invalid notification channel: "+req.Channel)
|
responses.WriteSimpleResponse(w, false, "invalid notification channel: "+req.Channel)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := notifications.AddNotification(req.Channel, req.Destination); err != nil {
|
if err := notifications.AddNotification(req.Channel, req.Destination); err != nil {
|
||||||
log.Errorln(err)
|
log.Errorln(err)
|
||||||
WriteSimpleResponse(w, false, "unable to save notification")
|
responses.WriteSimpleResponse(w, false, "unable to save notification")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package controllers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -8,7 +8,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Ping is fired by a client to show they are still an active viewer.
|
// 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)
|
viewer := models.GenerateViewerFromRequest(r)
|
||||||
core.SetViewerActive(&viewer)
|
core.SetViewerActive(&viewer)
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
|
@ -1,4 +1,4 @@
|
||||||
package controllers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -6,14 +6,15 @@ import (
|
||||||
|
|
||||||
"github.com/owncast/owncast/metrics"
|
"github.com/owncast/owncast/metrics"
|
||||||
"github.com/owncast/owncast/utils"
|
"github.com/owncast/owncast/utils"
|
||||||
|
"github.com/owncast/owncast/webserver/responses"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReportPlaybackMetrics will accept playback metrics from a client and save
|
// ReportPlaybackMetrics will accept playback metrics from a client and save
|
||||||
// them for future video health reporting.
|
// them for future video health reporting.
|
||||||
func ReportPlaybackMetrics(w http.ResponseWriter, r *http.Request) {
|
func (h *Handlers) ReportPlaybackMetrics(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method != POST {
|
if r.Method != http.MethodPost {
|
||||||
WriteSimpleResponse(w, false, r.Method+" not supported")
|
responses.WriteSimpleResponse(w, false, r.Method+" not supported")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +30,7 @@ func ReportPlaybackMetrics(w http.ResponseWriter, r *http.Request) {
|
||||||
var request reportPlaybackMetricsRequest
|
var request reportPlaybackMetricsRequest
|
||||||
if err := decoder.Decode(&request); err != nil {
|
if err := decoder.Decode(&request); err != nil {
|
||||||
log.Errorln("error decoding playback metrics payload:", err)
|
log.Errorln("error decoding playback metrics payload:", err)
|
||||||
WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package controllers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -9,10 +9,11 @@ import (
|
||||||
|
|
||||||
"github.com/owncast/owncast/activitypub/webfinger"
|
"github.com/owncast/owncast/activitypub/webfinger"
|
||||||
"github.com/owncast/owncast/core/data"
|
"github.com/owncast/owncast/core/data"
|
||||||
|
"github.com/owncast/owncast/webserver/responses"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RemoteFollow handles a request to begin the remote follow redirect flow.
|
// 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 {
|
type followRequest struct {
|
||||||
Account string `json:"account"`
|
Account string `json:"account"`
|
||||||
}
|
}
|
||||||
|
@ -24,12 +25,12 @@ func RemoteFollow(w http.ResponseWriter, r *http.Request) {
|
||||||
var request followRequest
|
var request followRequest
|
||||||
decoder := json.NewDecoder(r.Body)
|
decoder := json.NewDecoder(r.Body)
|
||||||
if err := decoder.Decode(&request); err != nil {
|
if err := decoder.Decode(&request); err != nil {
|
||||||
WriteSimpleResponse(w, false, "unable to parse request")
|
responses.WriteSimpleResponse(w, false, "unable to parse request")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if request.Account == "" {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +39,7 @@ func RemoteFollow(w http.ResponseWriter, r *http.Request) {
|
||||||
var template string
|
var template string
|
||||||
links, err := webfinger.GetWebfingerLinks(request.Account)
|
links, err := webfinger.GetWebfingerLinks(request.Account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
WriteSimpleResponse(w, false, err.Error())
|
responses.WriteSimpleResponse(w, false, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +53,7 @@ func RemoteFollow(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if localActorPath.String() == "" || template == "" {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,5 +62,5 @@ func RemoteFollow(w http.ResponseWriter, r *http.Request) {
|
||||||
RedirectURL: redirectURL,
|
RedirectURL: redirectURL,
|
||||||
}
|
}
|
||||||
|
|
||||||
WriteResponse(w, response)
|
responses.WriteResponse(w, response)
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package controllers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -8,7 +8,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetRobotsDotTxt returns the contents of our robots.txt.
|
// 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")
|
w.Header().Set("Content-Type", "text/plain")
|
||||||
contents := []string{
|
contents := []string{
|
||||||
"User-agent: *",
|
"User-agent: *",
|
|
@ -1 +0,0 @@
|
||||||
package handlers
|
|
|
@ -1,4 +1,4 @@
|
||||||
package controllers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -9,17 +9,18 @@ import (
|
||||||
"github.com/owncast/owncast/core/data"
|
"github.com/owncast/owncast/core/data"
|
||||||
"github.com/owncast/owncast/utils"
|
"github.com/owncast/owncast/utils"
|
||||||
"github.com/owncast/owncast/webserver/middleware"
|
"github.com/owncast/owncast/webserver/middleware"
|
||||||
|
"github.com/owncast/owncast/webserver/responses"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetStatus gets the status of the server.
|
// 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()
|
response := getStatusResponse()
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
middleware.DisableCache(w)
|
middleware.DisableCache(w)
|
||||||
|
|
||||||
if err := json.NewEncoder(w).Encode(response); err != nil {
|
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 (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/owncast/owncast/core/data"
|
"github.com/owncast/owncast/core/data"
|
||||||
|
"github.com/owncast/owncast/webserver/responses"
|
||||||
)
|
)
|
||||||
|
|
||||||
type variantsSort struct {
|
type variantsSort struct {
|
||||||
|
@ -20,7 +21,7 @@ type variantsResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetVideoStreamOutputVariants will return the video variants available.
|
// 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()
|
outputVariants := data.GetStreamOutputVariants()
|
||||||
|
|
||||||
streamSortVariants := make([]variantsSort, len(outputVariants))
|
streamSortVariants := make([]variantsSort, len(outputVariants))
|
||||||
|
@ -55,5 +56,5 @@ func GetVideoStreamOutputVariants(w http.ResponseWriter, r *http.Request) {
|
||||||
response[i] = variantResponse
|
response[i] = variantResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
WriteResponse(w, response)
|
responses.WriteResponse(w, response)
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package controllers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
|
@ -1 +0,0 @@
|
||||||
package handlers
|
|
|
@ -1,9 +1,10 @@
|
||||||
package yp
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/owncast/owncast/core"
|
||||||
"github.com/owncast/owncast/core/data"
|
"github.com/owncast/owncast/core/data"
|
||||||
"github.com/owncast/owncast/models"
|
"github.com/owncast/owncast/models"
|
||||||
"github.com/owncast/owncast/utils"
|
"github.com/owncast/owncast/utils"
|
||||||
|
@ -27,13 +28,13 @@ type ypDetailsResponse struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetYPResponse gets the status of the server for YP purposes.
|
// 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() {
|
if !data.GetDirectoryEnabled() {
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
status := getStatus()
|
status := core.GetStatus()
|
||||||
|
|
||||||
streamTitle := data.GetStreamTitle()
|
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.
|
// PaginatedResponse is a structure for returning a total count with results.
|
||||||
type PaginatedResponse struct {
|
type PaginatedResponse struct {
|
|
@ -1,4 +1,4 @@
|
||||||
package controllers
|
package responses
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
|
@ -5,30 +5,24 @@ import (
|
||||||
|
|
||||||
"github.com/owncast/owncast/activitypub"
|
"github.com/owncast/owncast/activitypub"
|
||||||
"github.com/owncast/owncast/config"
|
"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/chat"
|
||||||
"github.com/owncast/owncast/core/data"
|
"github.com/owncast/owncast/core/data"
|
||||||
"github.com/owncast/owncast/core/user"
|
"github.com/owncast/owncast/core/user"
|
||||||
"github.com/owncast/owncast/utils"
|
"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/webserver/middleware"
|
||||||
"github.com/owncast/owncast/yp"
|
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *webServer) setupRoutes() {
|
func (s *webServer) setupRoutes() {
|
||||||
s.router.HandleFunc("/test", s.handlers.HandleTesting)
|
|
||||||
|
|
||||||
s.setupWebAssetRoutes()
|
s.setupWebAssetRoutes()
|
||||||
s.setupInternalAPIRoutes()
|
s.setupInternalAPIRoutes()
|
||||||
s.setupAdminAPIRoutes()
|
s.setupAdminAPIRoutes()
|
||||||
s.setupExternalThirdPartyAPIRoutes()
|
s.setupExternalThirdPartyAPIRoutes()
|
||||||
s.setupModerationAPIRoutes()
|
s.setupModerationAPIRoutes()
|
||||||
|
|
||||||
s.router.HandleFunc("/hls/", controllers.HandleHLSRequest)
|
s.router.HandleFunc("/hls/", s.handlers.HandleHLSRequest)
|
||||||
|
|
||||||
// websocket
|
// websocket
|
||||||
s.router.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
|
s.router.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -45,7 +39,7 @@ func (s *webServer) setupRoutes() {
|
||||||
// Redirect /embed/chat
|
// Redirect /embed/chat
|
||||||
http.Redirect(w, r, "/embed/chat/readonly", http.StatusTemporaryRedirect)
|
http.Redirect(w, r, "/embed/chat/readonly", http.StatusTemporaryRedirect)
|
||||||
} else {
|
} else {
|
||||||
controllers.IndexHandler(w, r)
|
s.handlers.IndexHandler(w, r)
|
||||||
// s.ServeHTTP(w, r)
|
// s.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -56,26 +50,26 @@ func (s *webServer) setupRoutes() {
|
||||||
|
|
||||||
func (s *webServer) setupWebAssetRoutes() {
|
func (s *webServer) setupWebAssetRoutes() {
|
||||||
// The admin web app.
|
// The admin web app.
|
||||||
s.router.HandleFunc("/admin/", middleware.RequireAdminAuth(controllers.IndexHandler))
|
s.router.HandleFunc("/admin/", middleware.RequireAdminAuth(s.handlers.IndexHandler))
|
||||||
|
|
||||||
// Images
|
// Images
|
||||||
s.router.HandleFunc("/thumbnail.jpg", controllers.GetThumbnail)
|
s.router.HandleFunc("/thumbnail.jpg", s.handlers.GetThumbnail)
|
||||||
s.router.HandleFunc("/preview.gif", controllers.GetPreview)
|
s.router.HandleFunc("/preview.gif", s.handlers.GetPreview)
|
||||||
s.router.HandleFunc("/logo", controllers.GetLogo)
|
s.router.HandleFunc("/logo", s.handlers.GetLogo)
|
||||||
|
|
||||||
// Custom Javascript
|
// Custom Javascript
|
||||||
s.router.HandleFunc("/customjavascript", controllers.ServeCustomJavascript)
|
s.router.HandleFunc("/customjavascript", s.handlers.ServeCustomJavascript)
|
||||||
|
|
||||||
// Return a single emoji image.
|
// Return a single emoji image.
|
||||||
s.router.HandleFunc(config.EmojiDir, controllers.GetCustomEmojiImage)
|
s.router.HandleFunc(config.EmojiDir, s.handlers.GetCustomEmojiImage)
|
||||||
|
|
||||||
// return the logo
|
// return the logo
|
||||||
|
|
||||||
// return a logo that's compatible with external social networks
|
// 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
|
// robots.txt
|
||||||
s.router.HandleFunc("/robots.txt", controllers.GetRobotsDotTxt)
|
s.router.HandleFunc("/robots.txt", s.handlers.GetRobotsDotTxt)
|
||||||
|
|
||||||
// Optional public static files
|
// Optional public static files
|
||||||
s.router.Handle("/public/", http.StripPrefix("/public/", http.FileServer(http.Dir(config.PublicFilesPath))))
|
s.router.Handle("/public/", http.StripPrefix("/public/", http.FileServer(http.Dir(config.PublicFilesPath))))
|
||||||
|
@ -85,289 +79,289 @@ func (s *webServer) setupInternalAPIRoutes() {
|
||||||
// Internal APIs
|
// Internal APIs
|
||||||
|
|
||||||
// status of the system
|
// 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
|
// custom emoji supported in the chat
|
||||||
s.router.HandleFunc("/api/emoji", controllers.GetCustomEmojiList)
|
s.router.HandleFunc("/api/emoji", s.handlers.GetCustomEmojiList)
|
||||||
|
|
||||||
// chat history api
|
// 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
|
// web config api
|
||||||
s.router.HandleFunc("/api/config", controllers.GetWebConfig)
|
s.router.HandleFunc("/api/config", s.handlers.GetWebConfig)
|
||||||
|
|
||||||
// return the YP protocol data
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// return remote follow details
|
||||||
s.router.HandleFunc("/api/remotefollow", controllers.RemoteFollow)
|
s.router.HandleFunc("/api/remotefollow", s.handlers.RemoteFollow)
|
||||||
|
|
||||||
// return followers
|
// 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
|
// 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
|
// 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
|
// Start auth flow
|
||||||
http.HandleFunc("/api/auth/indieauth", middleware.RequireUserAccessToken(indieauth.StartAuthFlow))
|
s.router.HandleFunc("/api/auth/indieauth", middleware.RequireUserAccessToken(indieauth.StartAuthFlow))
|
||||||
http.HandleFunc("/api/auth/indieauth/callback", indieauth.HandleRedirect)
|
s.router.HandleFunc("/api/auth/indieauth/callback", indieauth.HandleRedirect)
|
||||||
http.HandleFunc("/api/auth/provider/indieauth", indieauth.HandleAuthEndpoint)
|
s.router.HandleFunc("/api/auth/provider/indieauth", indieauth.HandleAuthEndpoint)
|
||||||
|
|
||||||
http.HandleFunc("/api/auth/fediverse", middleware.RequireUserAccessToken(fediverseauth.RegisterFediverseOTPRequest))
|
s.router.HandleFunc("/api/auth/fediverse", middleware.RequireUserAccessToken(fediverseauth.RegisterFediverseOTPRequest))
|
||||||
http.HandleFunc("/api/auth/fediverse/verify", fediverseauth.VerifyFediverseOTPRequest)
|
s.router.HandleFunc("/api/auth/fediverse/verify", fediverseauth.VerifyFediverseOTPRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *webServer) setupAdminAPIRoutes() {
|
func (s *webServer) setupAdminAPIRoutes() {
|
||||||
// Current inbound broadcaster
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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.
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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.
|
// 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
|
// 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
|
// 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
|
// Update config values
|
||||||
|
|
||||||
// Change the current streaming key in memory
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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.
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// Prometheus metrics
|
||||||
s.router.Handle("/api/admin/prometheus", middleware.RequireAdminAuth(func(rw http.ResponseWriter, r *http.Request) {
|
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.
|
// Configure outbound notification channels.
|
||||||
http.HandleFunc("/api/admin/config/notifications/discord", middleware.RequireAdminAuth(admin.SetDiscordNotificationConfiguration))
|
s.router.HandleFunc("/api/admin/config/notifications/discord", middleware.RequireAdminAuth(s.handlers.SetDiscordNotificationConfiguration))
|
||||||
http.HandleFunc("/api/admin/config/notifications/browser", middleware.RequireAdminAuth(admin.SetBrowserNotificationConfiguration))
|
s.router.HandleFunc("/api/admin/config/notifications/browser", middleware.RequireAdminAuth(s.handlers.SetBrowserNotificationConfiguration))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *webServer) setupExternalThirdPartyAPIRoutes() {
|
func (s *webServer) setupExternalThirdPartyAPIRoutes() {
|
||||||
// Send a system message to chat
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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() {
|
func (s *webServer) setupModerationAPIRoutes() {
|
||||||
// Update chat message visibility
|
// 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
|
// 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
|
// 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)
|
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