2020-12-30 00:35:33 +03:00
|
|
|
package admin
|
|
|
|
|
|
|
|
// this is endpoint logic
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2021-02-19 10:05:52 +03:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
2020-12-30 00:35:33 +03:00
|
|
|
"net/http"
|
2021-09-13 11:26:28 +03:00
|
|
|
"strconv"
|
2020-12-30 00:35:33 +03:00
|
|
|
|
|
|
|
"github.com/owncast/owncast/controllers"
|
|
|
|
"github.com/owncast/owncast/core/chat"
|
2021-07-20 05:22:29 +03:00
|
|
|
"github.com/owncast/owncast/core/chat/events"
|
2022-03-07 07:34:49 +03:00
|
|
|
"github.com/owncast/owncast/core/data"
|
2024-07-02 04:58:50 +03:00
|
|
|
"github.com/owncast/owncast/models"
|
|
|
|
"github.com/owncast/owncast/persistence/userrepository"
|
2021-09-13 11:26:28 +03:00
|
|
|
"github.com/owncast/owncast/utils"
|
2020-12-30 00:35:33 +03:00
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
)
|
|
|
|
|
2021-07-20 05:22:29 +03:00
|
|
|
// ExternalUpdateMessageVisibility updates an array of message IDs to have the same visiblity.
|
2024-07-02 04:58:50 +03:00
|
|
|
func ExternalUpdateMessageVisibility(integration models.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
2021-07-20 05:22:29 +03:00
|
|
|
UpdateMessageVisibility(w, r)
|
|
|
|
}
|
|
|
|
|
2020-12-30 00:35:33 +03:00
|
|
|
// UpdateMessageVisibility updates an array of message IDs to have the same visiblity.
|
|
|
|
func UpdateMessageVisibility(w http.ResponseWriter, r *http.Request) {
|
2021-07-20 05:22:29 +03:00
|
|
|
type messageVisibilityUpdateRequest struct {
|
|
|
|
IDArray []string `json:"idArray"`
|
|
|
|
Visible bool `json:"visible"`
|
|
|
|
}
|
|
|
|
|
2021-02-19 10:05:52 +03:00
|
|
|
if r.Method != controllers.POST {
|
2023-10-21 19:46:45 +03:00
|
|
|
// nolint:goconst
|
2020-12-30 00:35:33 +03:00
|
|
|
controllers.WriteSimpleResponse(w, false, r.Method+" not supported")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
decoder := json.NewDecoder(r.Body)
|
2021-02-19 10:05:52 +03:00
|
|
|
var request messageVisibilityUpdateRequest
|
2020-12-30 00:35:33 +03:00
|
|
|
|
2021-07-20 05:22:29 +03:00
|
|
|
if err := decoder.Decode(&request); err != nil {
|
2020-12-30 00:35:33 +03:00
|
|
|
log.Errorln(err)
|
|
|
|
controllers.WriteSimpleResponse(w, false, "")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := chat.SetMessagesVisibility(request.IDArray, request.Visible); err != nil {
|
|
|
|
controllers.WriteSimpleResponse(w, false, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
controllers.WriteSimpleResponse(w, true, "changed")
|
|
|
|
}
|
|
|
|
|
2022-03-07 07:34:49 +03:00
|
|
|
// BanIPAddress will manually ban an IP address.
|
|
|
|
func BanIPAddress(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !requirePOST(w, r) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
configValue, success := getValueFromRequest(w, r)
|
|
|
|
if !success {
|
|
|
|
controllers.WriteSimpleResponse(w, false, "unable to ban IP address")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := data.BanIPAddress(configValue.Value.(string), "manually added"); err != nil {
|
|
|
|
controllers.WriteSimpleResponse(w, false, "error saving IP address ban")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
controllers.WriteSimpleResponse(w, true, "IP address banned")
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnBanIPAddress will remove an IP address ban.
|
|
|
|
func UnBanIPAddress(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !requirePOST(w, r) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
configValue, success := getValueFromRequest(w, r)
|
|
|
|
if !success {
|
|
|
|
controllers.WriteSimpleResponse(w, false, "unable to unban IP address")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := data.RemoveIPAddressBan(configValue.Value.(string)); err != nil {
|
|
|
|
controllers.WriteSimpleResponse(w, false, "error removing IP address ban")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
controllers.WriteSimpleResponse(w, true, "IP address unbanned")
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetIPAddressBans will return all the banned IP addresses.
|
|
|
|
func GetIPAddressBans(w http.ResponseWriter, r *http.Request) {
|
|
|
|
bans, err := data.GetIPAddressBans()
|
|
|
|
if err != nil {
|
|
|
|
controllers.WriteSimpleResponse(w, false, err.Error())
|
2022-04-23 23:19:17 +03:00
|
|
|
return
|
2022-03-07 07:34:49 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
controllers.WriteResponse(w, bans)
|
|
|
|
}
|
|
|
|
|
2021-09-12 10:18:15 +03:00
|
|
|
// UpdateUserEnabled enable or disable a single user by ID.
|
2021-07-20 05:22:29 +03:00
|
|
|
func UpdateUserEnabled(w http.ResponseWriter, r *http.Request) {
|
|
|
|
type blockUserRequest struct {
|
|
|
|
UserID string `json:"userId"`
|
|
|
|
Enabled bool `json:"enabled"`
|
|
|
|
}
|
|
|
|
|
|
|
|
if r.Method != controllers.POST {
|
|
|
|
controllers.WriteSimpleResponse(w, false, r.Method+" not supported")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
decoder := json.NewDecoder(r.Body)
|
|
|
|
var request blockUserRequest
|
|
|
|
|
|
|
|
if err := decoder.Decode(&request); err != nil {
|
|
|
|
log.Errorln(err)
|
2022-01-19 02:38:23 +03:00
|
|
|
controllers.WriteSimpleResponse(w, false, err.Error())
|
2021-07-20 05:22:29 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-03-07 07:34:49 +03:00
|
|
|
if request.UserID == "" {
|
|
|
|
controllers.WriteSimpleResponse(w, false, "must provide userId")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-07-02 04:58:50 +03:00
|
|
|
userRepository := userrepository.Get()
|
|
|
|
|
2021-07-20 05:22:29 +03:00
|
|
|
// Disable/enable the user
|
2024-07-02 04:58:50 +03:00
|
|
|
if err := userRepository.SetEnabled(request.UserID, request.Enabled); err != nil {
|
2021-07-20 05:22:29 +03:00
|
|
|
log.Errorln("error changing user enabled status", err)
|
2022-01-19 02:38:23 +03:00
|
|
|
controllers.WriteSimpleResponse(w, false, err.Error())
|
|
|
|
return
|
2021-07-20 05:22:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Hide/show the user's chat messages if disabling.
|
|
|
|
// Leave hidden messages hidden to be safe.
|
|
|
|
if !request.Enabled {
|
2021-09-12 10:18:15 +03:00
|
|
|
if err := chat.SetMessageVisibilityForUserID(request.UserID, request.Enabled); err != nil {
|
2021-07-20 05:22:29 +03:00
|
|
|
log.Errorln("error changing user messages visibility", err)
|
2022-01-19 02:38:23 +03:00
|
|
|
controllers.WriteSimpleResponse(w, false, err.Error())
|
|
|
|
return
|
2021-07-20 05:22:29 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Forcefully disconnect the user from the chat
|
|
|
|
if !request.Enabled {
|
2022-03-07 07:34:49 +03:00
|
|
|
clients, err := chat.GetClientsForUser(request.UserID)
|
|
|
|
if len(clients) == 0 {
|
|
|
|
// Nothing to do
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Errorln("error fetching clients for user: ", err)
|
|
|
|
controllers.WriteSimpleResponse(w, false, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
chat.DisconnectClients(clients)
|
2024-07-02 04:58:50 +03:00
|
|
|
disconnectedUser := userRepository.GetUserByID(request.UserID)
|
2021-07-20 05:22:29 +03:00
|
|
|
_ = chat.SendSystemAction(fmt.Sprintf("**%s** has been removed from chat.", disconnectedUser.DisplayName), true)
|
2022-03-07 07:34:49 +03:00
|
|
|
|
2023-07-17 22:06:58 +03:00
|
|
|
localIP4Address := "127.0.0.1"
|
|
|
|
localIP6Address := "::1"
|
|
|
|
|
2022-03-07 07:34:49 +03:00
|
|
|
// Ban this user's IP address.
|
|
|
|
for _, client := range clients {
|
|
|
|
ipAddress := client.IPAddress
|
2023-07-17 22:06:58 +03:00
|
|
|
if ipAddress != localIP4Address && ipAddress != localIP6Address {
|
|
|
|
reason := fmt.Sprintf("Banning of %s", disconnectedUser.DisplayName)
|
|
|
|
if err := data.BanIPAddress(ipAddress, reason); err != nil {
|
|
|
|
log.Errorln("error banning IP address: ", err)
|
|
|
|
}
|
2022-03-07 07:34:49 +03:00
|
|
|
}
|
|
|
|
}
|
2021-07-20 05:22:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
controllers.WriteSimpleResponse(w, true, fmt.Sprintf("%s enabled: %t", request.UserID, request.Enabled))
|
|
|
|
}
|
|
|
|
|
2021-09-12 10:18:15 +03:00
|
|
|
// GetDisabledUsers will return all the disabled users.
|
2021-07-20 05:22:29 +03:00
|
|
|
func GetDisabledUsers(w http.ResponseWriter, r *http.Request) {
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
2024-07-02 04:58:50 +03:00
|
|
|
userRepository := userrepository.Get()
|
|
|
|
|
|
|
|
users := userRepository.GetDisabledUsers()
|
2021-07-20 05:22:29 +03:00
|
|
|
controllers.WriteResponse(w, users)
|
2020-12-30 00:35:33 +03:00
|
|
|
}
|
|
|
|
|
2021-11-03 05:27:41 +03:00
|
|
|
// UpdateUserModerator will set the moderator status for a user ID.
|
|
|
|
func UpdateUserModerator(w http.ResponseWriter, r *http.Request) {
|
|
|
|
type request struct {
|
|
|
|
UserID string `json:"userId"`
|
|
|
|
IsModerator bool `json:"isModerator"`
|
|
|
|
}
|
|
|
|
|
2024-07-02 04:58:50 +03:00
|
|
|
if r.Method != http.MethodPost {
|
2021-11-03 05:27:41 +03:00
|
|
|
controllers.WriteSimpleResponse(w, false, r.Method+" not supported")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
decoder := json.NewDecoder(r.Body)
|
|
|
|
var req request
|
|
|
|
|
|
|
|
if err := decoder.Decode(&req); err != nil {
|
|
|
|
controllers.WriteSimpleResponse(w, false, "")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-07-02 04:58:50 +03:00
|
|
|
userRepository := userrepository.Get()
|
|
|
|
|
2021-11-03 05:27:41 +03:00
|
|
|
// Update the user object with new moderation access.
|
2024-07-02 04:58:50 +03:00
|
|
|
if err := userRepository.SetModerator(req.UserID, req.IsModerator); err != nil {
|
2021-11-03 05:27:41 +03:00
|
|
|
controllers.WriteSimpleResponse(w, false, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the clients for this user to know about the moderator access change.
|
|
|
|
if err := chat.SendConnectedClientInfoToUser(req.UserID); err != nil {
|
|
|
|
log.Debugln(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
controllers.WriteSimpleResponse(w, true, fmt.Sprintf("%s is moderator: %t", req.UserID, req.IsModerator))
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetModerators will return a list of moderator users.
|
|
|
|
func GetModerators(w http.ResponseWriter, r *http.Request) {
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
2024-07-02 04:58:50 +03:00
|
|
|
userRepository := userrepository.Get()
|
|
|
|
|
|
|
|
users := userRepository.GetModeratorUsers()
|
2021-11-03 05:27:41 +03:00
|
|
|
controllers.WriteResponse(w, users)
|
|
|
|
}
|
|
|
|
|
2020-12-30 00:35:33 +03:00
|
|
|
// GetChatMessages returns all of the chat messages, unfiltered.
|
|
|
|
func GetChatMessages(w http.ResponseWriter, r *http.Request) {
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
2021-07-20 05:22:29 +03:00
|
|
|
messages := chat.GetChatModerationHistory()
|
|
|
|
controllers.WriteResponse(w, messages)
|
2020-12-30 00:35:33 +03:00
|
|
|
}
|
2021-02-19 10:05:52 +03:00
|
|
|
|
2021-03-12 11:43:10 +03:00
|
|
|
// SendSystemMessage will send an official "SYSTEM" message to chat on behalf of your server.
|
2024-07-02 04:58:50 +03:00
|
|
|
func SendSystemMessage(integration models.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
2021-02-19 10:05:52 +03:00
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
2021-07-20 05:22:29 +03:00
|
|
|
var message events.SystemMessageEvent
|
2021-02-19 10:05:52 +03:00
|
|
|
if err := json.NewDecoder(r.Body).Decode(&message); err != nil {
|
|
|
|
controllers.InternalErrorHandler(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-07-20 05:22:29 +03:00
|
|
|
if err := chat.SendSystemMessage(message.Body, false); err != nil {
|
2021-02-19 10:05:52 +03:00
|
|
|
controllers.BadRequestHandler(w, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
controllers.WriteSimpleResponse(w, true, "sent")
|
2021-09-13 11:26:28 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// SendSystemMessageToConnectedClient will handle incoming requests to send a single message to a single connected client by ID.
|
2024-07-02 04:58:50 +03:00
|
|
|
func SendSystemMessageToConnectedClient(integration models.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
2021-09-13 11:26:28 +03:00
|
|
|
w.Header().Set("Content-Type", "application/json")
|
2024-05-30 22:31:07 +03:00
|
|
|
clientIDText, err := utils.GetURLParam(r, "clientId")
|
2021-09-13 11:26:28 +03:00
|
|
|
if err != nil {
|
|
|
|
controllers.BadRequestHandler(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
clientIDNumeric, err := strconv.ParseUint(clientIDText, 10, 32)
|
|
|
|
if err != nil {
|
|
|
|
controllers.BadRequestHandler(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var message events.SystemMessageEvent
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&message); err != nil {
|
|
|
|
controllers.InternalErrorHandler(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
chat.SendSystemMessageToClient(uint(clientIDNumeric), message.Body)
|
|
|
|
controllers.WriteSimpleResponse(w, true, "sent")
|
2021-02-19 10:05:52 +03:00
|
|
|
}
|
|
|
|
|
2021-07-20 05:22:29 +03:00
|
|
|
// SendUserMessage will send a message to chat on behalf of a user. *Depreciated*.
|
2024-07-02 04:58:50 +03:00
|
|
|
func SendUserMessage(integration models.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
2021-02-19 10:05:52 +03:00
|
|
|
w.Header().Set("Content-Type", "application/json")
|
2021-07-20 05:22:29 +03:00
|
|
|
controllers.BadRequestHandler(w, errors.New("no longer supported. see /api/integrations/chat/send"))
|
|
|
|
}
|
2021-02-19 10:05:52 +03:00
|
|
|
|
2021-09-12 10:18:15 +03:00
|
|
|
// SendIntegrationChatMessage will send a chat message on behalf of an external chat integration.
|
2024-07-02 04:58:50 +03:00
|
|
|
func SendIntegrationChatMessage(integration models.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
2021-07-20 05:22:29 +03:00
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
|
|
|
name := integration.DisplayName
|
|
|
|
|
|
|
|
if name == "" {
|
|
|
|
controllers.BadRequestHandler(w, errors.New("unknown integration for provided access token"))
|
2021-02-19 10:05:52 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-07-20 05:22:29 +03:00
|
|
|
var event events.UserMessageEvent
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&event); err != nil {
|
|
|
|
controllers.InternalErrorHandler(w, err)
|
2021-02-19 10:05:52 +03:00
|
|
|
return
|
|
|
|
}
|
2021-07-20 05:22:29 +03:00
|
|
|
event.SetDefaults()
|
|
|
|
event.RenderBody()
|
|
|
|
event.Type = "CHAT"
|
2021-02-19 10:05:52 +03:00
|
|
|
|
2021-07-20 05:22:29 +03:00
|
|
|
if event.Empty() {
|
|
|
|
controllers.BadRequestHandler(w, errors.New("invalid message"))
|
|
|
|
return
|
|
|
|
}
|
2021-02-19 10:05:52 +03:00
|
|
|
|
2024-07-02 04:58:50 +03:00
|
|
|
event.User = &models.User{
|
2021-09-12 10:18:15 +03:00
|
|
|
ID: integration.ID,
|
2021-07-20 05:22:29 +03:00
|
|
|
DisplayName: name,
|
|
|
|
DisplayColor: integration.DisplayColor,
|
|
|
|
CreatedAt: integration.CreatedAt,
|
2022-03-07 07:09:55 +03:00
|
|
|
IsBot: true,
|
2021-07-20 05:22:29 +03:00
|
|
|
}
|
2021-02-19 10:05:52 +03:00
|
|
|
|
2021-07-20 05:22:29 +03:00
|
|
|
if err := chat.Broadcast(&event); err != nil {
|
2021-02-19 10:05:52 +03:00
|
|
|
controllers.BadRequestHandler(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-07-20 05:22:29 +03:00
|
|
|
chat.SaveUserMessage(event)
|
|
|
|
|
2021-02-19 10:05:52 +03:00
|
|
|
controllers.WriteSimpleResponse(w, true, "sent")
|
|
|
|
}
|
|
|
|
|
|
|
|
// SendChatAction will send a generic chat action.
|
2024-07-02 04:58:50 +03:00
|
|
|
func SendChatAction(integration models.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
|
2021-02-19 10:05:52 +03:00
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
2021-07-20 05:22:29 +03:00
|
|
|
var message events.SystemActionEvent
|
2021-02-19 10:05:52 +03:00
|
|
|
if err := json.NewDecoder(r.Body).Decode(&message); err != nil {
|
|
|
|
controllers.InternalErrorHandler(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
message.SetDefaults()
|
2021-07-20 05:22:29 +03:00
|
|
|
message.RenderBody()
|
2021-02-19 10:05:52 +03:00
|
|
|
|
2021-07-20 05:22:29 +03:00
|
|
|
if err := chat.SendSystemAction(message.Body, false); err != nil {
|
2021-02-19 10:05:52 +03:00
|
|
|
controllers.BadRequestHandler(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
controllers.WriteSimpleResponse(w, true, "sent")
|
|
|
|
}
|
2022-03-07 10:26:24 +03:00
|
|
|
|
|
|
|
// SetEnableEstablishedChatUserMode sets the requirement for a chat user
|
|
|
|
// to be "established" for some time before taking part in chat.
|
|
|
|
func SetEnableEstablishedChatUserMode(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if !requirePOST(w, r) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
configValue, success := getValueFromRequest(w, r)
|
|
|
|
if !success {
|
|
|
|
controllers.WriteSimpleResponse(w, false, "unable to update chat established user only mode")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := data.SetChatEstablishedUsersOnlyMode(configValue.Value.(bool)); err != nil {
|
|
|
|
controllers.WriteSimpleResponse(w, false, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
controllers.WriteSimpleResponse(w, true, "chat established users only mode updated")
|
|
|
|
}
|