بنقرة واحدة
debug-channel
Debug WebSocket channel issues
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Debug WebSocket channel issues
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
| name | debug-channel |
| description | Debug WebSocket channel issues |
Guide for diagnosing and fixing WebSocket channel issues in Levee.
| File | Purpose |
|---|---|
lib/levee_web/channels/document_channel.ex | Channel message handlers |
lib/levee_web/channels/user_socket.ex | Socket configuration |
lib/levee/documents/session.ex | Document session GenServer |
test/levee_web/channels/document_channel_test.exs | Channel tests |
test/support/channel_case.ex | Test helpers |
1. WebSocket Connect → UserSocket.connect/3
2. Join Topic → DocumentChannel.join/3 (validates but doesn't auth)
3. Connect Document → handle_in("connect_document") (authenticates)
4. Operations → handle_in("submitOp"), handle_in("submitSignal")
5. Leave/Disconnect → terminate/2
Symptom: Client joins channel but submitOp returns errors.
Cause: Channel join succeeds without auth; connect_document must be called first.
Check:
# In document_channel.ex
def handle_in("submitOp", payload, socket) do
case socket.assigns[:authenticated] do
true -> # proceed
_ -> {:reply, {:error, %{reason: "not_authenticated"}}, socket}
end
end
Symptom: connect_document returns auth error.
Debug:
# Check token structure
JWT.verify_and_decode(tenant_id, token)
# Returns {:ok, claims} or {:error, reason}
# Common reasons:
# - :invalid_signature - wrong tenant secret
# - :token_expired - exp claim in past
# - :missing_claims - required fields missing
Symptom: Operations rejected with scope error.
Required Scopes:
| Operation | Required Scope |
|---|---|
| Join channel | None |
connect_document | doc:read |
submitOp | doc:write |
| Receive broadcasts | doc:read |
Check token scopes:
claims["scopes"] # Should include required scope
Symptom: Client doesn't receive broadcasts.
Debug Steps:
# Topic format
"document:#{tenant_id}:#{document_id}"
# In Session, verify broadcast
Phoenix.PubSub.broadcast(Levee.PubSub, topic, message)
Symptom: Operations fail with "session not found".
Cause: Session GenServer not started or crashed.
Debug:
# Check if session exists
Levee.Documents.Registry.lookup(tenant_id, document_id)
# Start session manually
Levee.Documents.Session.start_or_get(tenant_id, document_id)
defmodule LeveeWeb.DocumentChannelTest do
use LeveeWeb.ChannelCase, async: true
@tenant_id "test-tenant"
@document_id "test-doc"
setup do
TenantSecrets.register_tenant(@tenant_id, "secret")
on_exit(fn -> TenantSecrets.unregister_tenant(@tenant_id) end)
{:ok, _, socket} =
LeveeWeb.UserSocket
|> socket("user_id", %{})
|> subscribe_and_join(
LeveeWeb.DocumentChannel,
"document:#{@tenant_id}:#{@document_id}"
)
%{socket: socket}
end
end
test "connect_document with valid token", %{socket: socket} do
token = JWT.generate_test_token(@tenant_id, @document_id, "user-1")
ref = push(socket, "connect_document", %{"token" => token})
assert_reply ref, :ok, %{"clientId" => _}
end
test "connect_document with invalid token", %{socket: socket} do
ref = push(socket, "connect_document", %{"token" => "invalid"})
assert_reply ref, :error, %{"reason" => _}
end
test "submitOp after authentication", %{socket: socket} do
# First authenticate
token = JWT.generate_full_access_token(@tenant_id, @document_id, "user-1")
push(socket, "connect_document", %{"token" => token})
assert_reply _, :ok, _
# Then submit op
op = %{"type" => "op", "contents" => [...]}
ref = push(socket, "submitOp", op)
assert_reply ref, :ok, _
end
# Run channel tests with trace
mix test test/levee_web/channels/ --trace
# Run single test
mix test test/levee_web/channels/document_channel_test.exs:42
# Start server with debug logging
iex -S mix phx.server
# Then in IEx:
Logger.configure(level: :debug)
Client Channel Session
| | |
|---join(topic)----------->| |
|<--ok--------------------| |
| | |
|---connect_document------>| |
| |--validate_token------->|
| |<--{:ok, client_id}----|
|<--ok, clientId----------| |
| | |
|---submitOp-------------->| |
| |--process_op----------->|
| |<--{:ok, sequenced}----|
|<--ok--------------------| |
| | |
| |<--broadcast-----------|
|<--op broadcast----------| |