1
Fork 0

Rename client status to data, add group data.

We now distinguish between status, which is maintained by the server,
and data, which is provided by the client.  In addition to client data,
we now support group data.
This commit is contained in:
Juliusz Chroboczek 2022-01-29 22:54:44 +01:00
parent 710cc3cc14
commit 24187430e8
9 changed files with 98 additions and 59 deletions

View File

@ -73,7 +73,7 @@ will trigger. There, you update your user interface and request incoming
streams:
```javascript
serverConnection.onjoined = function(kind, group, perms, status, message) {
serverConnection.onjoined = function(kind, group, perms, status, data, message) {
switch(kind) {
case 'join':
this.request({'':['audio','video']});

View File

@ -108,7 +108,7 @@ The `join` message requests that the sender join or leave a group:
group: group,
username: username,
password: password,
status: status
data: data
}
```
@ -125,6 +125,7 @@ its permissions or in the recommended RTC configuration.
username: username,
permissions: permissions,
status: status,
data: data,
rtcConfiguration: RTCConfiguration
}
```
@ -337,7 +338,7 @@ A user action requests that the server act upon a user.
}
```
Currently defined kinds include `op`, `unop`, `present`, `unpresent`,
`kick` and `setstatus`.
`kick` and `setdata`.
Finally, a group action requests that the server act on the current group.
@ -352,5 +353,5 @@ Finally, a group action requests that the server act on the current group.
```
Currently defined kinds include `clearchat` (not to be confused with the
`clearchat` user message), `lock`, `unlock`, `record`, `unrecord` and
`subgroups`.
`clearchat` user message), `lock`, `unlock`, `record`, `unrecord`,
`subgroups` and `setdata`.

View File

@ -69,11 +69,11 @@ func (client *Client) Permissions() group.ClientPermissions {
}
}
func (client *Client) Status() map[string]interface{} {
func (client *Client) Data() map[string]interface{} {
return nil
}
func (client *Client) PushClient(group, kind, id, username string, permissions group.ClientPermissions, status map[string]interface{}) error {
func (client *Client) PushClient(group, kind, id, username string, permissions group.ClientPermissions, data map[string]interface{}) error {
return nil
}

View File

@ -100,10 +100,10 @@ type Client interface {
Username() string
Permissions() ClientPermissions
SetPermissions(ClientPermissions)
Status() map[string]interface{}
Data() map[string]interface{}
PushConn(g *Group, id string, conn conn.Up, tracks []conn.UpTrack, replace string) error
RequestConns(target Client, g *Group, id string) error
Joined(group, kind string) error
PushClient(group, kind, id, username string, permissions ClientPermissions, status map[string]interface{}) error
PushClient(group, kind, id, username string, permissions ClientPermissions, data map[string]interface{}) error
Kick(id, user, message string) error
}

View File

@ -76,6 +76,7 @@ type Group struct {
clients map[string]Client
history []ChatHistoryEntry
timestamp time.Time
data map[string]interface{}
}
func (g *Group) Name() string {
@ -107,6 +108,32 @@ func (g *Group) SetLocked(locked bool, message string) {
}
}
func (g *Group) Data() map[string]interface{} {
g.mu.Lock()
defer g.mu.Unlock()
return g.data
}
func (g *Group) UpdateData(d map[string]interface{}) {
g.mu.Lock()
if g.data == nil {
g.data = make(map[string]interface{})
}
for k, v := range d {
if v == nil {
delete(g.data, k)
} else {
g.data[k] = v
}
}
clients := g.getClientsUnlocked(nil)
g.mu.Unlock()
for _, c := range clients {
c.Joined(g.Name(), "change")
}
}
func (g *Group) Description() *Description {
g.mu.Lock()
defer g.mu.Unlock()
@ -577,12 +604,12 @@ func AddClient(group string, c Client, creds ClientCredentials) (*Group, error)
id := c.Id()
u := c.Username()
p := c.Permissions()
s := c.Status()
s := c.Data()
c.PushClient(g.Name(), "add", c.Id(), u, p, s)
for _, cc := range clients {
pp := cc.Permissions()
c.PushClient(
g.Name(), "add", cc.Id(), cc.Username(), pp, cc.Status(),
g.Name(), "add", cc.Id(), cc.Username(), pp, cc.Data(),
)
cc.PushClient(g.Name(), "add", id, u, p, s)
}
@ -1070,7 +1097,7 @@ type Status struct {
ClientCount *int `json:"clientCount,omitempty"`
}
func GetStatus(g *Group, authentified bool) Status {
func (g *Group) Status (authentified bool) Status {
desc := g.Description()
d := Status{
Name: g.name,
@ -1092,7 +1119,7 @@ func GetPublic() []Status {
gs := make([]Status, 0)
Range(func(g *Group) bool {
if g.Description().Public {
gs = append(gs, GetStatus(g, false))
gs = append(gs, g.Status(false))
}
return true
})

View File

@ -57,7 +57,7 @@ type webClient struct {
id string
username string
permissions group.ClientPermissions
status map[string]interface{}
data map[string]interface{}
requested map[string][]string
done chan struct{}
writeCh chan interface{}
@ -90,17 +90,17 @@ func (c *webClient) Permissions() group.ClientPermissions {
return c.permissions
}
func (c *webClient) Status() map[string]interface{} {
return c.status
func (c *webClient) Data() map[string]interface{} {
return c.data
}
func (c *webClient) SetPermissions(perms group.ClientPermissions) {
c.permissions = perms
}
func (c *webClient) PushClient(group, kind, id, username string, permissions group.ClientPermissions, status map[string]interface{}) error {
func (c *webClient) PushClient(group, kind, id, username string, permissions group.ClientPermissions, data map[string]interface{}) error {
return c.action(pushClientAction{
group, kind, id, username, permissions, status,
group, kind, id, username, permissions, data,
})
}
@ -115,7 +115,8 @@ type clientMessage struct {
Password string `json:"password,omitempty"`
Privileged bool `json:"privileged,omitempty"`
Permissions *group.ClientPermissions `json:"permissions,omitempty"`
Status interface{} `json:"status,omitempty"`
Status *group.Status `json:"status,omitempty"`
Data map[string]interface{} `json:"data,omitempty"`
Group string `json:"group,omitempty"`
Value interface{} `json:"value,omitempty"`
NoEcho bool `json:"noecho,omitempty"`
@ -899,7 +900,7 @@ type pushClientAction struct {
id string
username string
permissions group.ClientPermissions
status map[string]interface{}
data map[string]interface{}
}
type permissionsChangedAction struct{}
@ -1106,14 +1107,17 @@ func handleAction(c *webClient, a interface{}) error {
Id: a.id,
Username: a.username,
Permissions: &a.permissions,
Status: a.status,
Data: a.data,
})
case joinedAction:
var status interface{}
var status *group.Status
var data map[string]interface{}
if a.group != "" {
g := group.Get(a.group)
if g != nil {
status = group.GetStatus(g, true)
s := g.Status(true)
status = &s
data = g.Data()
}
}
perms := c.permissions
@ -1124,6 +1128,7 @@ func handleAction(c *webClient, a interface{}) error {
Username: c.username,
Permissions: &perms,
Status: status,
Data: data,
RTCConfiguration: ice.ICEConfiguration(),
})
case permissionsChangedAction:
@ -1132,13 +1137,14 @@ func handleAction(c *webClient, a interface{}) error {
return errors.New("Permissions changed in no group")
}
perms := c.permissions
status := g.Status(true)
c.write(clientMessage{
Type: "joined",
Kind: "change",
Group: g.Name(),
Username: c.username,
Permissions: &perms,
Status: group.GetStatus(g, true),
Status: &status,
RTCConfiguration: ice.ICEConfiguration(),
})
if !c.permissions.Present {
@ -1157,12 +1163,12 @@ func handleAction(c *webClient, a interface{}) error {
}
id := c.Id()
user := c.Username()
s := c.Status()
d := c.Data()
clients := g.GetClients(nil)
go func(clients []group.Client) {
for _, cc := range clients {
cc.PushClient(
g.Name(), "change", id, user, perms, s,
g.Name(), "change", id, user, perms, d,
)
}
}(clients)
@ -1214,7 +1220,7 @@ func leaveGroup(c *webClient) {
group.DelClient(c)
c.permissions = group.ClientPermissions{}
c.status = nil
c.data = nil
c.requested = make(map[string][]string)
c.group = nil
}
@ -1321,15 +1327,7 @@ func handleClientMessage(c *webClient, m clientMessage) error {
)
}
c.username = m.Username
if m.Status != nil {
s, ok := m.Status.(map[string]interface{})
if !ok {
return group.ProtocolError(
"bad type for status",
)
}
c.status = s
}
c.data = m.Data
g, err := group.AddClient(m.Group, c,
group.ClientCredentials{
Username: m.Username,
@ -1615,6 +1613,17 @@ func handleClientMessage(c *webClient, m clientMessage) error {
Time: group.ToJSTime(time.Now()),
Value: s,
})
case "setdata":
if !c.permissions.Op {
return c.error(group.UserError("not authorised"))
}
data, ok := m.Value.(map[string]interface{})
if !ok {
return c.error(group.UserError(
"Bad value in setdata",
))
}
g.UpdateData(data)
default:
return group.ProtocolError("unknown group action")
}
@ -1645,35 +1654,35 @@ func handleClientMessage(c *webClient, m clientMessage) error {
if err != nil {
return c.error(err)
}
case "setstatus":
case "setdata":
if m.Dest != c.Id() {
return c.error(group.UserError("not authorised"))
}
s, ok := m.Value.(map[string]interface{})
data, ok := m.Value.(map[string]interface{})
if !ok {
return c.error(group.UserError(
"Bad value in setstatus",
"Bad value in setdata",
))
}
if c.status == nil {
c.status = make(map[string]interface{})
if c.data == nil {
c.data = make(map[string]interface{})
}
for k, v := range s {
for k, v := range data {
if v == nil {
delete(c.status, k)
delete(c.data, k)
} else {
c.status[k] = v
c.data[k] = v
}
}
id := c.Id()
user := c.Username()
perms := c.Permissions()
status := c.Status()
data = c.Data()
go func(clients []group.Client) {
for _, cc := range clients {
cc.PushClient(
g.Name(), "change",
id, user, perms, status,
id, user, perms, data,
)
}
}(g.GetClients(nil))

View File

@ -1969,7 +1969,7 @@ function addUser(id, userinfo) {
user.id = 'user-' + id;
user.classList.add("user-p");
user.textContent = userinfo.username ? userinfo.username : '(anon)';
if (userinfo.status.raisehand)
if (userinfo.data.raisehand)
user.classList.add('user-status-raisehand');
else
user.classList.remove('user-status-raisehand');
@ -2001,7 +2001,7 @@ function changeUser(id, userinfo) {
return;
}
user.textContent = userinfo.username ? userinfo.username : '(anon)';
if (userinfo.status.raisehand)
if (userinfo.data.raisehand)
user.classList.add('user-status-raisehand');
else
user.classList.remove('user-status-raisehand');
@ -2084,9 +2084,10 @@ function setTitle(title) {
* @param {string} group
* @param {Object<string,boolean>} perms
* @param {Object<string,any>} status
* @param {Object<string,any>} data
* @param {string} message
*/
async function gotJoined(kind, group, perms, status, message) {
async function gotJoined(kind, group, perms, status, data, message) {
let present = presentRequested;
presentRequested = null;
@ -2698,7 +2699,7 @@ commands.raise = {
description: 'raise hand',
f: (c, r) => {
serverConnection.userAction(
"setstatus", serverConnection.id, {"raisehand": true},
"setdata", serverConnection.id, {"raisehand": true},
);
}
}
@ -2707,7 +2708,7 @@ commands.unraise = {
description: 'unraise hand',
f: (c, r) => {
serverConnection.userAction(
"setstatus", serverConnection.id, {"raisehand": null},
"setdata", serverConnection.id, {"raisehand": null},
);
}
}

View File

@ -64,7 +64,7 @@ function newLocalId() {
* @typedef {Object} user
* @property {string} username
* @property {Object<string,boolean>} permissions
* @property {Object<string,any>} status
* @property {Object<string,any>} data
* @property {Object<string,Object<string,boolean>>} down
*/
@ -164,7 +164,7 @@ function ServerConnection() {
*
* kind is one of 'join', 'fail', 'change' or 'leave'.
*
* @type{(this: ServerConnection, kind: string, group: string, permissions: Object<string,boolean>, status: Object<string,any>, message: string) => void}
* @type{(this: ServerConnection, kind: string, group: string, permissions: Object<string,boolean>, status: Object<string,any>, data: Object<string,any>, message: string) => void}
*/
this.onjoined = null;
/**
@ -208,6 +208,7 @@ function ServerConnection() {
* @property {boolean} [privileged]
* @property {Object<string,boolean>} [permissions]
* @property {Object<string,any>} [status]
* @property {Object<string,any>} [data]
* @property {string} [group]
* @property {unknown} [value]
* @property {boolean} [noecho]
@ -284,7 +285,7 @@ ServerConnection.prototype.connect = async function(url) {
sc.onuser.call(sc, id, 'delete');
}
if(sc.group && sc.onjoined)
sc.onjoined.call(sc, 'leave', sc.group, {}, {}, '');
sc.onjoined.call(sc, 'leave', sc.group, {}, {}, {}, '');
sc.group = null;
sc.username = null;
if(sc.onclose)
@ -336,7 +337,7 @@ ServerConnection.prototype.connect = async function(url) {
if(sc.onjoined)
sc.onjoined.call(sc, m.kind, m.group,
m.permissions || {},
m.status,
m.status, m.data,
m.value || null);
break;
case 'user':
@ -347,7 +348,7 @@ ServerConnection.prototype.connect = async function(url) {
sc.users[m.id] = {
username: m.username,
permissions: m.permissions || {},
status: m.status || {},
data: m.data || {},
down: {},
};
break;
@ -357,13 +358,13 @@ ServerConnection.prototype.connect = async function(url) {
sc.users[m.id] = {
username: m.username,
permissions: m.permissions || {},
status: m.status || {},
data: m.data || {},
down: {},
};
} else {
sc.users[m.id].username = m.username;
sc.users[m.id].permissions = m.permissions || {};
sc.users[m.id].status = m.status || {};
sc.users[m.id].data = m.data || {};
}
break;
case 'delete':

View File

@ -339,7 +339,7 @@ func groupStatusHandler(w http.ResponseWriter, r *http.Request) {
return
}
d := group.GetStatus(g, false)
d := g.Status(false)
w.Header().Set("content-type", "application/json")
w.Header().Set("cache-control", "no-cache")