Blocklist improvements + WebSocket timeout tweak
This commit is contained in:
parent
4b971fcf41
commit
a1b0d2e965
|
@ -345,7 +345,7 @@ The `unmute` action does the opposite and removes the mute status:
|
|||
|
||||
## Block
|
||||
|
||||
Sent by: Client.
|
||||
Sent by: Client, Server.
|
||||
|
||||
The block command places a hard block between the current user and the target.
|
||||
|
||||
|
@ -364,6 +364,10 @@ When either user blocks the other:
|
|||
}
|
||||
```
|
||||
|
||||
The server may send a "block" message to the client in response to the BlockNow API endpoint: your main website can communicate that a block was just added, so if either user is currently in chat the block can apply immediately instead of at either user's next re-join of the room.
|
||||
|
||||
The server "block" message follows the same format, having the username of the other party.
|
||||
|
||||
## Blocklist
|
||||
|
||||
Sent by: Client.
|
||||
|
|
113
pkg/api.go
113
pkg/api.go
|
@ -354,6 +354,119 @@ func (s *Server) BlockList() http.HandlerFunc {
|
|||
})
|
||||
}
|
||||
|
||||
// BlockNow (/api/block/now) allows your website to add to a current online chatter's
|
||||
// blocked list immediately.
|
||||
//
|
||||
// For example: the BlockList endpoint does a bulk sync of the blocklist at the time
|
||||
// a user joins the chat room, but if users are already on chat when the blocking begins,
|
||||
// it doesn't take effect until one or the other re-joins the room. This API endpoint
|
||||
// can apply the blocking immediately to the currently online users.
|
||||
//
|
||||
// It is a POST request with a json body containing the following schema:
|
||||
//
|
||||
// {
|
||||
// "APIKey": "from settings.toml",
|
||||
// "Usernames": [ "source", "target" ]
|
||||
// }
|
||||
//
|
||||
// The pair of usernames will be the two users who block one another (in any order).
|
||||
// If any of the users are currently connected to the chat, they will all mutually
|
||||
// block one another immediately.
|
||||
func (s *Server) BlockNow() http.HandlerFunc {
|
||||
type request struct {
|
||||
APIKey string
|
||||
Usernames []string
|
||||
}
|
||||
|
||||
type result struct {
|
||||
OK bool
|
||||
Error string `json:",omitempty"`
|
||||
}
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// JSON writer for the response.
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
enc := json.NewEncoder(w)
|
||||
enc.SetIndent("", " ")
|
||||
|
||||
// Parse the request.
|
||||
if r.Method != http.MethodPost {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
enc.Encode(result{
|
||||
Error: "Only POST methods allowed",
|
||||
})
|
||||
return
|
||||
} else if r.Header.Get("Content-Type") != "application/json" {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
enc.Encode(result{
|
||||
Error: "Only application/json content-types allowed",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
defer r.Body.Close()
|
||||
|
||||
// Parse the request payload.
|
||||
var (
|
||||
params request
|
||||
dec = json.NewDecoder(r.Body)
|
||||
)
|
||||
if err := dec.Decode(¶ms); err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
enc.Encode(result{
|
||||
Error: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Validate the API key.
|
||||
if params.APIKey != config.Current.AdminAPIKey {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
enc.Encode(result{
|
||||
Error: "Authentication denied.",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Check if any of these users are online, and update their blocklist accordingly.
|
||||
var changed bool
|
||||
for _, username := range params.Usernames {
|
||||
if sub, err := s.GetSubscriber(username); err == nil {
|
||||
for _, otherName := range params.Usernames {
|
||||
if username == otherName {
|
||||
continue
|
||||
}
|
||||
log.Info("BlockNow API: %s is currently on chat, add block for %+v", username, otherName)
|
||||
|
||||
sub.muteMu.Lock()
|
||||
sub.muted[otherName] = struct{}{}
|
||||
sub.blocked[otherName] = struct{}{}
|
||||
sub.muteMu.Unlock()
|
||||
|
||||
// Changes have been made to online users.
|
||||
changed = true
|
||||
|
||||
// Send a server-side "block" command to the subscriber, so their front-end page might
|
||||
// update the cachedBlocklist so there's no leakage in case of chat server rebooting.
|
||||
sub.SendJSON(messages.Message{
|
||||
Action: messages.ActionBlock,
|
||||
Username: otherName,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If any changes to blocklists were made: send the Who List.
|
||||
if changed {
|
||||
s.SendWhoList()
|
||||
}
|
||||
|
||||
enc.Encode(result{
|
||||
OK: true,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Blocklist cache sent over from your website.
|
||||
var (
|
||||
// Map of username to the list of usernames they block.
|
||||
|
|
|
@ -179,6 +179,7 @@ func (s *Server) OnMessage(sub *Subscriber, msg messages.Message) {
|
|||
// If the user is OP, just tell them we would.
|
||||
if sub.IsAdmin() {
|
||||
sub.ChatServer("Your recent chat context would have been reported to your main website.")
|
||||
return
|
||||
}
|
||||
|
||||
// Send the report to the main website.
|
||||
|
@ -483,7 +484,7 @@ func (s *Server) OnBlock(sub *Subscriber, msg messages.Message) {
|
|||
|
||||
// OnBlocklist is a bulk user mute from the CachedBlocklist sent by the website.
|
||||
func (s *Server) OnBlocklist(sub *Subscriber, msg messages.Message) {
|
||||
log.Info("%s syncs their blocklist: %s", sub.Username, msg.Usernames)
|
||||
log.Info("[%s] syncs their blocklist: %s", sub.Username, msg.Usernames)
|
||||
|
||||
sub.muteMu.Lock()
|
||||
for _, username := range msg.Usernames {
|
||||
|
|
|
@ -28,6 +28,13 @@ func (s *Server) filterMessage(sub *Subscriber, rawMsg messages.Message, msg *me
|
|||
if strings.HasPrefix(msg.Channel, "@") {
|
||||
// DM
|
||||
pushDirectMessageContext(sub, sub.Username, msg.Channel[1:], rawMsg)
|
||||
|
||||
// If either party is an admin user, waive filtering this DM chat.
|
||||
if sub.IsAdmin() {
|
||||
return nil, false
|
||||
} else if other, err := s.GetSubscriber(msg.Channel[1:]); err == nil && other.IsAdmin() {
|
||||
return nil, false
|
||||
}
|
||||
} else {
|
||||
// Public channel
|
||||
pushMessageContext(sub, msg.Channel, rawMsg)
|
||||
|
|
|
@ -36,6 +36,7 @@ func (s *Server) Setup() error {
|
|||
mux.Handle("/ws", s.WebSocket())
|
||||
mux.Handle("/api/statistics", s.Statistics())
|
||||
mux.Handle("/api/blocklist", s.BlockList())
|
||||
mux.Handle("/api/block/now", s.BlockNow())
|
||||
mux.Handle("/api/authenticate", s.Authenticate())
|
||||
mux.Handle("/api/shutdown", s.ShutdownAPI())
|
||||
mux.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("dist/assets"))))
|
||||
|
|
|
@ -218,7 +218,7 @@ func (s *Server) WebSocket() http.HandlerFunc {
|
|||
for {
|
||||
select {
|
||||
case msg := <-sub.messages:
|
||||
err = writeTimeout(ctx, time.Second*5, c, msg)
|
||||
err = writeTimeout(ctx, time.Second*15, c, msg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
25
src/App.vue
25
src/App.vue
|
@ -957,6 +957,21 @@ export default {
|
|||
}
|
||||
},
|
||||
|
||||
// Server side "block" event: for when the main website sends a BlockNow API request.
|
||||
onBlock(msg) {
|
||||
// Close any video connections we had with this user.
|
||||
this.closeVideo(msg.username);
|
||||
|
||||
// Add it to our CachedBlocklist so in case the server reboots, we continue to sync it on reconnect.
|
||||
for (let existing of this.config.CachedBlocklist) {
|
||||
if (existing === msg.username) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.config.CachedBlocklist.push(msg.username);
|
||||
},
|
||||
|
||||
// Mute or unmute a user.
|
||||
muteUser(username) {
|
||||
username = this.normalizeUsername(username);
|
||||
|
@ -1184,6 +1199,11 @@ export default {
|
|||
this.ws.connected = true;
|
||||
this.ChatClient("Websocket connected!");
|
||||
|
||||
// Upload our blocklist to the server before login. This resolves a bug where if a block
|
||||
// was added recently (other user still online in chat), that user would briefly see your
|
||||
// "has entered the room" message followed by you immediately not being online.
|
||||
this.bulkMuteUsers();
|
||||
|
||||
// Tell the server our username.
|
||||
this.ws.conn.send(JSON.stringify({
|
||||
action: "login",
|
||||
|
@ -1249,6 +1269,9 @@ export default {
|
|||
case "unwatch":
|
||||
this.onUnwatch(msg);
|
||||
break;
|
||||
case "block":
|
||||
this.onBlock(msg);
|
||||
break;
|
||||
case "error":
|
||||
this.pushHistory({
|
||||
channel: msg.channel,
|
||||
|
@ -3733,7 +3756,7 @@ export default {
|
|||
label on the chat history panel -->
|
||||
<div class="dropdown-content p-0">
|
||||
<EmojiPicker
|
||||
:native="false"
|
||||
:native="true"
|
||||
:display-recent="true"
|
||||
:disable-skin-tones="true"
|
||||
theme="auto"
|
||||
|
|
Loading…
Reference in New Issue
Block a user