Files
OwnCord/Server/db/queries/sqlite/voice.sql
T
J3vbandClaude Fable 5 c86d803a18 fix: resolve the six blocked batch-4 ledger findings (#1393)
* fix(ws): resolve an empty READ audience for a channel whose row is gone

channelReadAudience already failed closed on a GetChannel error; a
deleted channel returns (nil, nil) and fell through to the role scan.
Return nobody for a missing row too — voice teardown callers union the
room's participants and the leaver back in, so their signals still land.

Test locks both halves: the non-participant hears nothing, the leaver
still gets voice_leave. (OC-0090)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(ws): re-elect the key holder in CleanupVoiceForChannel

Every other voice-removal path re-elects (finishVoiceLeave, the LiveKit
webhook, registerNow, rollbackVoiceJoin, sweepStaleVoiceStates); the
channel delete/archive path did not, so a torn-down channel's
voiceKeyHolders entry lived for the process lifetime. One updateKeyHolder
call at the end of the teardown deletes it. (OC-0012)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(ws): implement BroadcastMemberUnban so unban reaches connected clients

The admin unban path reaches the hub through an optional-capability type
assertion that *ws.Hub never satisfied, so it always missed silently and
clients connected during a ban kept the user missing from their member
store. Implement the mirror of BroadcastMemberBan: fan out the same
member_join a fresh connect sends (clients already map it to addMember),
reporting offline since the unbanned user cannot be connected. A
compile-time assertion in admin pins the wiring so the assertion can
never silently miss again. (OC-0058)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(db): exclude the requester's own flag from the video-cap stream count

EnableCameraIfUnderLimit and EnableScreenshareIfUnderLimit counted every
stream in the channel including the very flag the UPDATE sets, so a user
whose server-side flag was already 1 (client lost track and retried) was
refused at the cap against their own stream, with no path out. Subtract
the outer row's own bit from the correlated count: re-enable becomes
idempotent while the requester's other stream and everyone else's still
count. sqlc layer regenerated. (OC-0081)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore(ledger): resolve the six blocked batch-4 findings

Four fixed in this branch (OC-0012, OC-0058, OC-0081, OC-0090), each
with an independent revert-proof pass. Two were already fixed on main by
later sibling fixes and are recorded as such: OC-0086 by the OC-0017
pre-delete re-check (#1374), OC-0101 by the OC-0206 early watermark bump
(#1375). The ledger holds zero open and zero blocked findings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-19 18:28:00 +02:00

132 lines
5.6 KiB
SQL

-- server_muted / server_deafened are deliberately absent from both upserts'
-- reset lists: a moderator-imposed mute must survive a channel switch, which
-- reaches the ON CONFLICT branch. It is scoped to the voice session:
-- leaving voice deletes the row, so a rejoin starts clean.
-- name: JoinVoiceChannel :exec
INSERT INTO voice_states (user_id, channel_id, muted, deafened, speaking, camera, screenshare, joined_at)
VALUES (?, ?, 0, 0, 0, 0, 0, ?)
ON CONFLICT(user_id) DO UPDATE SET
channel_id = excluded.channel_id,
muted = 0,
deafened = 0,
speaking = 0,
camera = 0,
screenshare = 0,
joined_at = excluded.joined_at;
-- name: JoinVoiceChannelIfCapacity :execresult
INSERT INTO voice_states (user_id, channel_id, muted, deafened, speaking, camera, screenshare, joined_at)
SELECT ?, ?, 0, 0, 0, 0, 0, ?
WHERE (SELECT COUNT(*) FROM voice_states AS vs2 WHERE vs2.channel_id = ?) < ?
ON CONFLICT(user_id) DO UPDATE SET
channel_id = excluded.channel_id,
muted = 0,
deafened = 0,
speaking = 0,
camera = 0,
screenshare = 0,
joined_at = excluded.joined_at;
-- name: LeaveVoiceChannel :exec
DELETE FROM voice_states WHERE user_id = ?;
-- name: LeaveVoiceChannelIfMatch :execresult
DELETE FROM voice_states WHERE user_id = ? AND channel_id = ? AND joined_at = ?;
-- name: GetUserVoiceState :one
SELECT vs.user_id, vs.channel_id, u.username,
vs.muted, vs.deafened, vs.speaking,
vs.camera, vs.screenshare,
vs.server_muted, vs.server_deafened, vs.joined_at
FROM voice_states vs
JOIN users u ON u.id = vs.user_id
WHERE vs.user_id = ?;
-- name: GetChannelVoiceStates :many
SELECT vs.user_id, vs.channel_id, u.username,
vs.muted, vs.deafened, vs.speaking,
vs.camera, vs.screenshare,
vs.server_muted, vs.server_deafened, vs.joined_at
FROM voice_states vs
JOIN users u ON u.id = vs.user_id
WHERE vs.channel_id = ?
ORDER BY vs.joined_at ASC;
-- name: GetAllVoiceStates :many
SELECT vs.user_id, vs.channel_id, u.username,
vs.muted, vs.deafened, vs.speaking,
vs.camera, vs.screenshare,
vs.server_muted, vs.server_deafened, vs.joined_at
FROM voice_states vs
JOIN users u ON u.id = vs.user_id
ORDER BY vs.channel_id, vs.joined_at ASC;
-- name: UpdateVoiceMute :exec
UPDATE voice_states SET muted = ? WHERE user_id = ?;
-- name: UpdateVoiceDeafen :exec
UPDATE voice_states SET deafened = ? WHERE user_id = ?;
-- name: UpdateVoiceCamera :exec
UPDATE voice_states SET camera = ? WHERE user_id = ?;
-- name: UpdateVoiceScreenshare :exec
UPDATE voice_states SET screenshare = ? WHERE user_id = ?;
-- Scoped to channel_id as well as user_id: the moderator's authorization is
-- checked against a channel snapshot several round trips before this write
-- lands, so an unscoped `WHERE user_id = ?` would follow the target onto
-- whatever channel their row points at by then -- including a DM call the
-- moderator was never authorized against (OC-0005). :execresult so the
-- caller can tell a real no-op (target moved) from a normal apply.
-- name: ApplyVoiceServerMute :execresult
UPDATE voice_states SET server_muted = 1, muted = 1 WHERE user_id = ? AND channel_id = ?;
-- name: ClearVoiceServerMute :execresult
UPDATE voice_states SET server_muted = 0 WHERE user_id = ? AND channel_id = ?;
-- name: ApplyVoiceServerDeafen :execresult
UPDATE voice_states SET server_deafened = 1, deafened = 1 WHERE user_id = ? AND channel_id = ?;
-- name: ClearVoiceServerDeafen :execresult
UPDATE voice_states SET server_deafened = 0 WHERE user_id = ? AND channel_id = ?;
-- Camera and screenshare share one voice_max_video budget, counted in
-- STREAMS, not rows: a channel capped at N simultaneous video streams must
-- not let a camera publish ignore screenshare occupants (or vice versa,
-- OC-0023), and a single user with both flags set must consume two of the N
-- slots, not one (OC-0006) -- so both gates sum `vs2.camera + vs2.screenshare`
-- across the channel's rows rather than counting rows where either is set.
-- The enabling user's own bit for the flag being set CAN already be 1 at
-- gate time (a client that lost track of the server-side flag retries the
-- enable), so each gate excludes exactly that one bit from the count --
-- see the per-query comments below (OC-0081).
-- name: EnableCameraIfUnderLimit :execresult
-- The channel-wide stream count excludes the requester's own camera flag
-- (subtracted via the correlated outer-row reference), so re-enabling an
-- already-set camera is idempotent at the cap instead of being refused
-- against the requester's own stream (OC-0081). Their screenshare, and
-- every other user's streams, still count.
UPDATE voice_states SET camera = 1
WHERE voice_states.user_id = ? AND voice_states.channel_id = ?
AND (SELECT COALESCE(SUM(vs2.camera), 0) + COALESCE(SUM(vs2.screenshare), 0) FROM voice_states AS vs2 WHERE vs2.channel_id = ?) - voice_states.camera < sqlc.arg(max_video);
-- name: EnableScreenshareIfUnderLimit :execresult
-- Mirror of EnableCameraIfUnderLimit: the count excludes the requester's
-- own screenshare flag so re-enable is idempotent at the cap (OC-0081).
UPDATE voice_states SET screenshare = 1
WHERE voice_states.user_id = ? AND voice_states.channel_id = ?
AND (SELECT COALESCE(SUM(vs2.camera), 0) + COALESCE(SUM(vs2.screenshare), 0) FROM voice_states AS vs2 WHERE vs2.channel_id = ?) - voice_states.screenshare < sqlc.arg(max_video);
-- name: ClearVoiceState :exec
DELETE FROM voice_states WHERE user_id = ?;
-- name: ClearAllVoiceStates :exec
DELETE FROM voice_states;
-- name: CountActiveCameras :one
SELECT COUNT(*) FROM voice_states WHERE channel_id = ? AND camera = 1;