feat: real-time SPEAK permission enforcement in voice channels
Permission changes now take effect immediately without requiring disconnect/reconnect. Modeled as "permission mute" parallel to server mute — server recomputes SPEAK for all voice participants on role/override changes and broadcasts state via WebSocket. Includes amber UI indicators and mic toggle blocking.
This commit is contained in:
@@ -6,6 +6,7 @@ import { generateSnowflake } from '../utils/snowflake.js';
|
||||
import { isMember, hasPermission, getChannelSpaceId, PermissionBits, computePermissions } from '../utils/permissions.js';
|
||||
import { permissionsToString } from '@backspace/shared/src/permissions.js';
|
||||
import { connectionManager } from '../ws/handler.js';
|
||||
import { checkVoicePermissions } from '../ws/events.js';
|
||||
import type {
|
||||
CreateChannelRequest,
|
||||
UpdateChannelRequest,
|
||||
@@ -356,6 +357,7 @@ export async function channelRoutes(app: FastifyInstance): Promise<void> {
|
||||
|
||||
// Notify all space members of the permission change
|
||||
broadcastOverrideChange(channel.spaceId, id);
|
||||
checkVoicePermissions(channel.spaceId);
|
||||
|
||||
return reply.code(200).send({ success: true });
|
||||
});
|
||||
@@ -387,6 +389,7 @@ export async function channelRoutes(app: FastifyInstance): Promise<void> {
|
||||
|
||||
// Notify all space members of the permission change
|
||||
broadcastOverrideChange(channel.spaceId, id);
|
||||
checkVoicePermissions(channel.spaceId);
|
||||
|
||||
return reply.code(200).send({ success: true });
|
||||
},
|
||||
|
||||
@@ -19,6 +19,7 @@ import type {
|
||||
Role,
|
||||
} from '@backspace/shared';
|
||||
import { sanitizeUser } from '../utils/sanitize.js';
|
||||
import { checkVoicePermissions } from '../ws/events.js';
|
||||
|
||||
function rowToSpace(row: typeof schema.spaces.$inferSelect): Space {
|
||||
return {
|
||||
@@ -662,6 +663,7 @@ export async function spaceRoutes(app: FastifyInstance): Promise<void> {
|
||||
|
||||
// Force target user's client to re-sync with their new permissions
|
||||
connectionManager.pushReadyPayload(uid);
|
||||
checkVoicePermissions(id);
|
||||
|
||||
// Build response with populated roles
|
||||
const updatedMember = db.select()
|
||||
@@ -829,6 +831,7 @@ export async function spaceRoutes(app: FastifyInstance): Promise<void> {
|
||||
for (const m of memberRows) {
|
||||
connectionManager.pushReadyPayload(m.userId);
|
||||
}
|
||||
checkVoicePermissions(id);
|
||||
|
||||
return reply.code(201).send(role);
|
||||
});
|
||||
@@ -871,6 +874,7 @@ export async function spaceRoutes(app: FastifyInstance): Promise<void> {
|
||||
for (const m of memberRows) {
|
||||
connectionManager.pushReadyPayload(m.userId);
|
||||
}
|
||||
checkVoicePermissions(id);
|
||||
|
||||
return reply.code(200).send(updated);
|
||||
});
|
||||
@@ -903,6 +907,7 @@ export async function spaceRoutes(app: FastifyInstance): Promise<void> {
|
||||
for (const m of memberRows) {
|
||||
connectionManager.pushReadyPayload(m.userId);
|
||||
}
|
||||
checkVoicePermissions(id);
|
||||
|
||||
return reply.code(200).send({ success: true });
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user