Create custom database type for state sets
This commit is contained in:
parent
731143775d
commit
f94c156f95
4 changed files with 125 additions and 111 deletions
|
@ -6,8 +6,6 @@ defmodule Architex.RoomServer do
|
||||||
The RoomServers are supervised by a DynamicSupervisor RoomServer.Supervisor.
|
The RoomServers are supervised by a DynamicSupervisor RoomServer.Supervisor.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@typep t :: map()
|
|
||||||
|
|
||||||
use GenServer
|
use GenServer
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
@ -25,7 +23,7 @@ defmodule Architex.RoomServer do
|
||||||
Alias
|
Alias
|
||||||
}
|
}
|
||||||
|
|
||||||
alias Architex.Types.UserId
|
alias Architex.Types.{UserId, StateSet}
|
||||||
|
|
||||||
alias Architex.StateResolution.Authorization
|
alias Architex.StateResolution.Authorization
|
||||||
alias ArchitexWeb.Client.Request.{CreateRoom, Kick, Ban}
|
alias ArchitexWeb.Client.Request.{CreateRoom, Kick, Ban}
|
||||||
|
@ -49,8 +47,13 @@ defmodule Architex.RoomServer do
|
||||||
@spec get_room_server(String.t()) :: {:error, :not_found} | DynamicSupervisor.on_start_child()
|
@spec get_room_server(String.t()) :: {:error, :not_found} | DynamicSupervisor.on_start_child()
|
||||||
def get_room_server(room_id) do
|
def get_room_server(room_id) do
|
||||||
# TODO: Might be wise to use a transaction here to prevent race conditions.
|
# TODO: Might be wise to use a transaction here to prevent race conditions.
|
||||||
case Repo.one(from r in Room, where: r.id == ^room_id) do
|
query =
|
||||||
%Room{state: serialized_state_set} = room ->
|
Room
|
||||||
|
|> where([r], r.id == ^room_id)
|
||||||
|
|> select([:id, :forward_extremities, :state_set, :visibility])
|
||||||
|
|
||||||
|
case Repo.one(query) do
|
||||||
|
%Room{} = room ->
|
||||||
case Registry.lookup(@registry, room_id) do
|
case Registry.lookup(@registry, room_id) do
|
||||||
[{pid, _}] ->
|
[{pid, _}] ->
|
||||||
{:ok, pid}
|
{:ok, pid}
|
||||||
|
@ -58,8 +61,7 @@ defmodule Architex.RoomServer do
|
||||||
[] ->
|
[] ->
|
||||||
opts = [
|
opts = [
|
||||||
name: {:via, Registry, {@registry, room_id}},
|
name: {:via, Registry, {@registry, room_id}},
|
||||||
room: room,
|
room: room
|
||||||
serialized_state_set: serialized_state_set
|
|
||||||
]
|
]
|
||||||
|
|
||||||
DynamicSupervisor.start_child(@supervisor, {__MODULE__, opts})
|
DynamicSupervisor.start_child(@supervisor, {__MODULE__, opts})
|
||||||
|
@ -210,19 +212,7 @@ defmodule Architex.RoomServer do
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def init(opts) do
|
def init(opts) do
|
||||||
room = Keyword.fetch!(opts, :room)
|
{:ok, %{room: Keyword.fetch!(opts, :room)}}
|
||||||
serialized_state_set = Keyword.fetch!(opts, :serialized_state_set)
|
|
||||||
state_event_ids = Enum.map(serialized_state_set, fn [_, _, event_id] -> event_id end)
|
|
||||||
|
|
||||||
state_set =
|
|
||||||
Event
|
|
||||||
|> where([e], e.id in ^state_event_ids)
|
|
||||||
|> Repo.all()
|
|
||||||
|> Enum.into(%{}, fn %Event{type: type, state_key: state_key} = event ->
|
|
||||||
{{type, state_key}, event}
|
|
||||||
end)
|
|
||||||
|
|
||||||
{:ok, %{room: room, state_set: state_set}}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
@ -242,9 +232,9 @@ defmodule Architex.RoomServer do
|
||||||
{:ok, alias_} ->
|
{:ok, alias_} ->
|
||||||
events = create_room_events(room, account, request, alias_)
|
events = create_room_events(room, account, request, alias_)
|
||||||
|
|
||||||
case Repo.transaction(process_events(room, %{}, events)) do
|
case Repo.transaction(process_events(room, events)) do
|
||||||
{:ok, {state_set, room}} ->
|
{:ok, room} ->
|
||||||
{:reply, {:ok, room_id}, %{state | state_set: state_set, room: room}}
|
{:reply, {:ok, room_id}, %{state | room: room}}
|
||||||
|
|
||||||
{:error, reason} ->
|
{:error, reason} ->
|
||||||
{:reply, {:error, reason}, state}
|
{:reply, {:error, reason}, state}
|
||||||
|
@ -258,7 +248,7 @@ defmodule Architex.RoomServer do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_call({:server_in_room?, domain}, _from, %{state_set: state_set} = state) do
|
def handle_call({:server_in_room?, domain}, _from, %{room: %Room{state_set: state_set}} = state) do
|
||||||
result =
|
result =
|
||||||
Enum.any?(state_set, fn
|
Enum.any?(state_set, fn
|
||||||
{{"m.room.member", user_id}, %Event{content: %{"membership" => "join"}}} ->
|
{{"m.room.member", user_id}, %Event{content: %{"membership" => "join"}}} ->
|
||||||
|
@ -314,12 +304,12 @@ defmodule Architex.RoomServer do
|
||||||
def handle_call(
|
def handle_call(
|
||||||
{:invite, account, user_id, avatar_url, displayname},
|
{:invite, account, user_id, avatar_url, displayname},
|
||||||
_from,
|
_from,
|
||||||
%{room: room, state_set: state_set} = state
|
%{room: room} = state
|
||||||
) do
|
) do
|
||||||
invite_event = Event.Invite.new(room, account, user_id, avatar_url, displayname)
|
invite_event = Event.Invite.new(room, account, user_id, avatar_url, displayname)
|
||||||
|
|
||||||
case Repo.transaction(process_event(room, state_set, invite_event)) do
|
case Repo.transaction(process_event(room, invite_event)) do
|
||||||
{:ok, {state_set, room, _}} -> {:reply, :ok, %{state | state_set: state_set, room: room}}
|
{:ok, {room, _}} -> {:reply, :ok, %{state | room: room}}
|
||||||
{:error, reason} -> {:reply, {:error, reason}, state}
|
{:error, reason} -> {:reply, {:error, reason}, state}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -327,24 +317,24 @@ defmodule Architex.RoomServer do
|
||||||
def handle_call(
|
def handle_call(
|
||||||
{:join, account},
|
{:join, account},
|
||||||
_from,
|
_from,
|
||||||
%{room: %Room{id: room_id} = room, state_set: state_set} = state
|
%{room: %Room{id: room_id} = room} = state
|
||||||
) do
|
) do
|
||||||
join_event = Event.Join.new(room, account)
|
join_event = Event.Join.new(room, account)
|
||||||
|
|
||||||
case Repo.transaction(process_event(room, state_set, join_event)) do
|
case Repo.transaction(process_event(room, join_event)) do
|
||||||
{:ok, {state_set, room, _}} ->
|
{:ok, {room, _}} ->
|
||||||
{:reply, {:ok, room_id}, %{state | state_set: state_set, room: room}}
|
{:reply, {:ok, room_id}, %{state | room: room}}
|
||||||
|
|
||||||
{:error, reason} ->
|
{:error, reason} ->
|
||||||
{:reply, {:error, reason}, state}
|
{:reply, {:error, reason}, state}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_call({:leave, account}, _from, %{room: room, state_set: state_set} = state) do
|
def handle_call({:leave, account}, _from, %{room: room} = state) do
|
||||||
leave_event = Event.Leave.new(room, account)
|
leave_event = Event.Leave.new(room, account)
|
||||||
|
|
||||||
case Repo.transaction(process_event(room, state_set, leave_event)) do
|
case Repo.transaction(process_event(room, leave_event)) do
|
||||||
{:ok, {state_set, room, _}} -> {:reply, :ok, %{state | state_set: state_set, room: room}}
|
{:ok, {room, _}} -> {:reply, :ok, %{state | room: room}}
|
||||||
{:error, reason} -> {:reply, {:error, reason}, state}
|
{:error, reason} -> {:reply, {:error, reason}, state}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -352,13 +342,13 @@ defmodule Architex.RoomServer do
|
||||||
def handle_call(
|
def handle_call(
|
||||||
{:kick, account, %Kick{user_id: user_id, reason: reason}, avatar_url, displayname},
|
{:kick, account, %Kick{user_id: user_id, reason: reason}, avatar_url, displayname},
|
||||||
_from,
|
_from,
|
||||||
%{room: room, state_set: state_set} = state
|
%{room: room} = state
|
||||||
) do
|
) do
|
||||||
kick_event =
|
kick_event =
|
||||||
Event.Kick.new(room, account, to_string(user_id), avatar_url, displayname, reason)
|
Event.Kick.new(room, account, to_string(user_id), avatar_url, displayname, reason)
|
||||||
|
|
||||||
case Repo.transaction(process_event(room, state_set, kick_event)) do
|
case Repo.transaction(process_event(room, kick_event)) do
|
||||||
{:ok, {state_set, room, _}} -> {:reply, :ok, %{state | state_set: state_set, room: room}}
|
{:ok, {room, _}} -> {:reply, :ok, %{state | room: room}}
|
||||||
{:error, reason} -> {:reply, {:error, reason}, state}
|
{:error, reason} -> {:reply, {:error, reason}, state}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -366,12 +356,12 @@ defmodule Architex.RoomServer do
|
||||||
def handle_call(
|
def handle_call(
|
||||||
{:ban, account, %Ban{user_id: user_id, reason: reason}, avatar_url, displayname},
|
{:ban, account, %Ban{user_id: user_id, reason: reason}, avatar_url, displayname},
|
||||||
_from,
|
_from,
|
||||||
%{room: room, state_set: state_set} = state
|
%{room: room} = state
|
||||||
) do
|
) do
|
||||||
ban_event = Event.Ban.new(room, account, to_string(user_id), avatar_url, displayname, reason)
|
ban_event = Event.Ban.new(room, account, to_string(user_id), avatar_url, displayname, reason)
|
||||||
|
|
||||||
case Repo.transaction(process_event(room, state_set, ban_event)) do
|
case Repo.transaction(process_event(room, ban_event)) do
|
||||||
{:ok, {state_set, room, _}} -> {:reply, :ok, %{state | state_set: state_set, room: room}}
|
{:ok, {room, _}} -> {:reply, :ok, %{state | room: room}}
|
||||||
{:error, reason} -> {:reply, {:error, reason}, state}
|
{:error, reason} -> {:reply, {:error, reason}, state}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -379,12 +369,12 @@ defmodule Architex.RoomServer do
|
||||||
def handle_call(
|
def handle_call(
|
||||||
{:unban, account, user_id, avatar_url, displayname},
|
{:unban, account, user_id, avatar_url, displayname},
|
||||||
_from,
|
_from,
|
||||||
%{room: room, state_set: state_set} = state
|
%{room: room} = state
|
||||||
) do
|
) do
|
||||||
unban_event = Event.Unban.new(room, account, user_id, avatar_url, displayname)
|
unban_event = Event.Unban.new(room, account, user_id, avatar_url, displayname)
|
||||||
|
|
||||||
case Repo.transaction(process_event(room, state_set, unban_event)) do
|
case Repo.transaction(process_event(room, unban_event)) do
|
||||||
{:ok, {state_set, room, _}} -> {:reply, :ok, %{state | state_set: state_set, room: room}}
|
{:ok, {room, _}} -> {:reply, :ok, %{state | room: room}}
|
||||||
{:error, reason} -> {:reply, {:error, reason}, state}
|
{:error, reason} -> {:reply, {:error, reason}, state}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -392,7 +382,7 @@ defmodule Architex.RoomServer do
|
||||||
def handle_call(
|
def handle_call(
|
||||||
{:set_visibility, account, visibility},
|
{:set_visibility, account, visibility},
|
||||||
_from,
|
_from,
|
||||||
%{room: room, state_set: state_set} = state
|
%{room: %Room{state_set: state_set} = room} = state
|
||||||
) do
|
) do
|
||||||
case state_set do
|
case state_set do
|
||||||
%{{"m.room.create", ""} => %Event{content: %{"creator" => creator}}} ->
|
%{{"m.room.create", ""} => %Event{content: %{"creator" => creator}}} ->
|
||||||
|
@ -412,13 +402,13 @@ defmodule Architex.RoomServer do
|
||||||
def handle_call(
|
def handle_call(
|
||||||
{:send_message_event, account, device, event_type, content, txn_id},
|
{:send_message_event, account, device, event_type, content, txn_id},
|
||||||
_from,
|
_from,
|
||||||
%{room: room, state_set: state_set} = state
|
%{room: room} = state
|
||||||
) do
|
) do
|
||||||
message_event = Event.custom_event(room, account, event_type, content)
|
message_event = Event.custom_event(room, account, event_type, content)
|
||||||
|
|
||||||
case Repo.transaction(process_event_with_txn(state_set, room, device, message_event, txn_id)) do
|
case Repo.transaction(process_event_with_txn(room, device, message_event, txn_id)) do
|
||||||
{:ok, {state_set, room, event_id}} ->
|
{:ok, {room, event_id}} ->
|
||||||
{:reply, {:ok, event_id}, %{state | state_set: state_set, room: room}}
|
{:reply, {:ok, event_id}, %{state | room: room}}
|
||||||
|
|
||||||
{:error, reason} ->
|
{:error, reason} ->
|
||||||
{:reply, {:error, reason}, state}
|
{:reply, {:error, reason}, state}
|
||||||
|
@ -428,20 +418,24 @@ defmodule Architex.RoomServer do
|
||||||
def handle_call(
|
def handle_call(
|
||||||
{:send_state_event, account, event_type, content, state_key},
|
{:send_state_event, account, event_type, content, state_key},
|
||||||
_from,
|
_from,
|
||||||
%{room: room, state_set: state_set} = state
|
%{room: room} = state
|
||||||
) do
|
) do
|
||||||
state_event = Event.custom_state_event(room, account, event_type, content, state_key)
|
state_event = Event.custom_state_event(room, account, event_type, content, state_key)
|
||||||
|
|
||||||
case Repo.transaction(process_event(room, state_set, state_event)) do
|
case Repo.transaction(process_event(room, state_event)) do
|
||||||
{:ok, {state_set, room, %Event{id: event_id}}} ->
|
{:ok, {room, %Event{id: event_id}}} ->
|
||||||
{:reply, {:ok, event_id}, %{state | state_set: state_set, room: room}}
|
{:reply, {:ok, event_id}, %{state | room: room}}
|
||||||
|
|
||||||
{:error, reason} ->
|
{:error, reason} ->
|
||||||
{:reply, {:error, reason}, state}
|
{:reply, {:error, reason}, state}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_call({:get_current_state, account}, _from, %{state_set: state_set} = state) do
|
def handle_call(
|
||||||
|
{:get_current_state, account},
|
||||||
|
_from,
|
||||||
|
%{room: %Room{state_set: state_set}} = state
|
||||||
|
) do
|
||||||
mxid = Account.get_mxid(account)
|
mxid = Account.get_mxid(account)
|
||||||
|
|
||||||
case state_set[{"m.room.member", mxid}] do
|
case state_set[{"m.room.member", mxid}] do
|
||||||
|
@ -466,7 +460,7 @@ defmodule Architex.RoomServer do
|
||||||
def handle_call(
|
def handle_call(
|
||||||
{:get_state_event, account, event_type, state_key},
|
{:get_state_event, account, event_type, state_key},
|
||||||
_from,
|
_from,
|
||||||
%{state_set: state_set} = state
|
%{room: %Room{state_set: state_set}} = state
|
||||||
) do
|
) do
|
||||||
mxid = Account.get_mxid(account)
|
mxid = Account.get_mxid(account)
|
||||||
|
|
||||||
|
@ -491,10 +485,9 @@ defmodule Architex.RoomServer do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec process_event_with_txn(t(), Room.t(), Device.t(), %Event{}, String.t()) ::
|
@spec process_event_with_txn(Room.t(), Device.t(), %Event{}, String.t()) ::
|
||||||
(() -> {t(), Room.t(), String.t()} | {:error, atom()})
|
(() -> {Room.t(), String.t()} | {:error, atom()})
|
||||||
defp process_event_with_txn(
|
defp process_event_with_txn(
|
||||||
state_set,
|
|
||||||
room,
|
room,
|
||||||
%Device{nid: device_nid} = device,
|
%Device{nid: device_nid} = device,
|
||||||
message_event,
|
message_event,
|
||||||
|
@ -507,53 +500,42 @@ defmodule Architex.RoomServer do
|
||||||
where: dt.txn_id == ^txn_id and dt.device_nid == ^device_nid
|
where: dt.txn_id == ^txn_id and dt.device_nid == ^device_nid
|
||||||
) do
|
) do
|
||||||
%DeviceTransaction{event_id: event_id} ->
|
%DeviceTransaction{event_id: event_id} ->
|
||||||
{state_set, room, event_id}
|
{room, event_id}
|
||||||
|
|
||||||
nil ->
|
nil ->
|
||||||
with {state_set, room, %Event{id: event_id}} <-
|
with {room, %Event{id: event_id}} <- process_event(room, message_event).() do
|
||||||
process_event(room, state_set, message_event).() do
|
|
||||||
# Mark this transaction as done.
|
# Mark this transaction as done.
|
||||||
Ecto.build_assoc(device, :device_transactions, txn_id: txn_id, event_id: event_id)
|
Ecto.build_assoc(device, :device_transactions, txn_id: txn_id, event_id: event_id)
|
||||||
|> Repo.insert!()
|
|> Repo.insert!()
|
||||||
|
|
||||||
{state_set, room, event_id}
|
{room, event_id}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec process_event(Room.t(), t(), %Event{}) ::
|
@spec process_event(Room.t(), %Event{}) :: (() -> {Room.t(), Event.t()} | {:error, atom()})
|
||||||
(() -> {t(), Room.t(), Event.t()} | {:error, atom()})
|
defp process_event(room, event) do
|
||||||
defp process_event(room, state_set, event) do
|
|
||||||
fn ->
|
fn ->
|
||||||
case finalize_and_process_event(event, state_set, room) do
|
case finalize_and_process_event(event, room) do
|
||||||
{:ok, state_set, room, event} ->
|
{:ok, room, event} -> {room, event}
|
||||||
_ = update_room_state_set(room, state_set)
|
{:error, reason} -> Repo.rollback(reason)
|
||||||
{state_set, room, event}
|
|
||||||
|
|
||||||
{:error, reason} ->
|
|
||||||
Repo.rollback(reason)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec process_events(Room.t(), t(), [%Event{}]) ::
|
@spec process_events(Room.t(), [%Event{}]) :: (() -> Room.t() | {:error, atom()})
|
||||||
(() -> {t(), Room.t()} | {:error, atom()})
|
defp process_events(room, events) do
|
||||||
defp process_events(room, state_set, events) do
|
|
||||||
fn ->
|
fn ->
|
||||||
Enum.reduce_while(events, {state_set, room}, fn event, {state_set, room} ->
|
Enum.reduce_while(events, room, fn event, room ->
|
||||||
case finalize_and_process_event(event, state_set, room) do
|
case finalize_and_process_event(event, room) do
|
||||||
{:ok, state_set, room, _} -> {:cont, {state_set, room}}
|
{:ok, room, _} -> {:cont, room}
|
||||||
{:error, reason} -> {:halt, {:error, reason}}
|
{:error, reason} -> {:halt, {:error, reason}}
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|> then(fn
|
|> then(fn
|
||||||
{:error, reason} ->
|
{:error, reason} -> Repo.rollback(reason)
|
||||||
Repo.rollback(reason)
|
room -> room
|
||||||
|
|
||||||
{state_set, room} ->
|
|
||||||
_ = update_room_state_set(room, state_set)
|
|
||||||
{state_set, room}
|
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -618,20 +600,6 @@ defmodule Architex.RoomServer do
|
||||||
preset_events ++ initial_state_events ++ name_and_topic_events ++ invite_events
|
preset_events ++ initial_state_events ++ name_and_topic_events ++ invite_events
|
||||||
end
|
end
|
||||||
|
|
||||||
# Update the given room in the database with the given state set.
|
|
||||||
@spec update_room_state_set(Room.t(), t()) :: Room.t()
|
|
||||||
defp update_room_state_set(room, state_set) do
|
|
||||||
# TODO: We might as well hold state in the Room struct,
|
|
||||||
# instead of the state_set state.
|
|
||||||
# Create custom type for this.
|
|
||||||
serialized_state_set =
|
|
||||||
Enum.map(state_set, fn {{type, state_key}, %Event{id: event_id}} ->
|
|
||||||
[type, state_key, event_id]
|
|
||||||
end)
|
|
||||||
|
|
||||||
Repo.update!(change(room, state: serialized_state_set))
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get the events for room creation as dictated by the given preset.
|
# Get the events for room creation as dictated by the given preset.
|
||||||
@spec room_creation_preset(Account.t(), String.t() | nil, Room.t()) :: [%Event{}]
|
@spec room_creation_preset(Account.t(), String.t() | nil, Room.t()) :: [%Event{}]
|
||||||
defp room_creation_preset(account, nil, %Room{visibility: visibility} = room) do
|
defp room_creation_preset(account, nil, %Room{visibility: visibility} = room) do
|
||||||
|
@ -687,12 +655,11 @@ defmodule Architex.RoomServer do
|
||||||
# - Content hash
|
# - Content hash
|
||||||
# - Event ID
|
# - Event ID
|
||||||
# - Signature
|
# - Signature
|
||||||
@spec finalize_and_process_event(%Event{}, t(), Room.t()) ::
|
@spec finalize_and_process_event(%Event{}, Room.t()) ::
|
||||||
{:ok, t(), Room.t(), Event.t()} | {:error, atom()}
|
{:ok, Room.t(), Event.t()} | {:error, atom()}
|
||||||
defp finalize_and_process_event(
|
defp finalize_and_process_event(
|
||||||
event,
|
event,
|
||||||
state_set,
|
%Room{forward_extremities: forward_extremities, state_set: state_set} = room
|
||||||
%Room{forward_extremities: forward_extremities} = room
|
|
||||||
) do
|
) do
|
||||||
event =
|
event =
|
||||||
event
|
event
|
||||||
|
@ -701,7 +668,7 @@ defmodule Architex.RoomServer do
|
||||||
|> Map.put(:depth, get_depth(forward_extremities))
|
|> Map.put(:depth, get_depth(forward_extremities))
|
||||||
|
|
||||||
case Event.post_process(event) do
|
case Event.post_process(event) do
|
||||||
{:ok, event} -> authenticate_and_process_event(event, state_set, room)
|
{:ok, event} -> authenticate_and_process_event(event, room)
|
||||||
_ -> {:error, :event_creation}
|
_ -> {:error, :event_creation}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -716,7 +683,7 @@ defmodule Architex.RoomServer do
|
||||||
end
|
end
|
||||||
|
|
||||||
# Get the auth events for an events.
|
# Get the auth events for an events.
|
||||||
@spec auth_events_for_event(%Event{}, t()) :: [Event.t()]
|
@spec auth_events_for_event(%Event{}, StateSet.t()) :: [Event.t()]
|
||||||
defp auth_events_for_event(%Event{type: "m.room.create"}, _), do: []
|
defp auth_events_for_event(%Event{type: "m.room.create"}, _), do: []
|
||||||
|
|
||||||
defp auth_events_for_event(
|
defp auth_events_for_event(
|
||||||
|
@ -768,9 +735,9 @@ defmodule Architex.RoomServer do
|
||||||
# Authenticate and insert a new event using state resolution.
|
# Authenticate and insert a new event using state resolution.
|
||||||
# Implements the checks as described in the
|
# Implements the checks as described in the
|
||||||
# [Matrix docs](https://matrix.org/docs/spec/server_server/latest#checks-performed-on-receipt-of-a-pdu).
|
# [Matrix docs](https://matrix.org/docs/spec/server_server/latest#checks-performed-on-receipt-of-a-pdu).
|
||||||
@spec authenticate_and_process_event(Event.t(), t(), Room.t()) ::
|
@spec authenticate_and_process_event(Event.t(), Room.t()) ::
|
||||||
{:ok, t(), Room.t(), Event.t()} | {:error, atom()}
|
{:ok, Room.t(), Event.t()} | {:error, atom()}
|
||||||
defp authenticate_and_process_event(event, current_state_set, room) do
|
defp authenticate_and_process_event(event, %Room{state_set: current_state_set} = room) do
|
||||||
# TODO: Correctly handle soft fails.
|
# TODO: Correctly handle soft fails.
|
||||||
# Check the following things:
|
# Check the following things:
|
||||||
# 1. TODO: Is a valid event, otherwise it is dropped.
|
# 1. TODO: Is a valid event, otherwise it is dropped.
|
||||||
|
@ -788,8 +755,9 @@ defmodule Architex.RoomServer do
|
||||||
event = Repo.insert!(event)
|
event = Repo.insert!(event)
|
||||||
state_set = StateResolution.resolve_forward_extremities(event)
|
state_set = StateResolution.resolve_forward_extremities(event)
|
||||||
:ok = update_membership(room, state_set)
|
:ok = update_membership(room, state_set)
|
||||||
|
room = Repo.update!(change(room, state_set: state_set))
|
||||||
|
|
||||||
{:ok, state_set, room, event}
|
{:ok, room, event}
|
||||||
else
|
else
|
||||||
_ -> {:error, :authorization}
|
_ -> {:error, :authorization}
|
||||||
end
|
end
|
||||||
|
@ -800,7 +768,7 @@ defmodule Architex.RoomServer do
|
||||||
# could access rooms they are not allowed to. Then again, maybe we should perform
|
# could access rooms they are not allowed to. Then again, maybe we should perform
|
||||||
# the "normal" authorization flow for local users as well, and treat the Membership
|
# the "normal" authorization flow for local users as well, and treat the Membership
|
||||||
# table only as informational.
|
# table only as informational.
|
||||||
@spec update_membership(Room.t(), t()) :: :ok
|
@spec update_membership(Room.t(), StateSet.t()) :: :ok
|
||||||
defp update_membership(%Room{id: room_id}, state_set) do
|
defp update_membership(%Room{id: room_id}, state_set) do
|
||||||
server_name = Architex.server_name()
|
server_name = Architex.server_name()
|
||||||
|
|
||||||
|
|
|
@ -5,18 +5,19 @@ defmodule Architex.Room do
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
alias Architex.{Repo, Room, Event, Alias, RoomServer, Account}
|
alias Architex.{Repo, Room, Event, Alias, RoomServer, Account}
|
||||||
|
alias Architex.Types.StateSet
|
||||||
alias ArchitexWeb.Client.Request.{CreateRoom, Messages}
|
alias ArchitexWeb.Client.Request.{CreateRoom, Messages}
|
||||||
|
|
||||||
@type t :: %__MODULE__{
|
@type t :: %__MODULE__{
|
||||||
visibility: :public | :private,
|
visibility: :public | :private,
|
||||||
state: list(list(String.t())),
|
state_set: StateSet.t(),
|
||||||
forward_extremities: list(String.t())
|
forward_extremities: list(String.t())
|
||||||
}
|
}
|
||||||
|
|
||||||
@primary_key {:id, :string, []}
|
@primary_key {:id, :string, []}
|
||||||
schema "rooms" do
|
schema "rooms" do
|
||||||
field :visibility, Ecto.Enum, values: [:public, :private]
|
field :visibility, Ecto.Enum, values: [:public, :private]
|
||||||
field :state, {:array, {:array, :string}}
|
field :state_set, StateSet, load_in_query: false
|
||||||
field :forward_extremities, {:array, :string}
|
field :forward_extremities, {:array, :string}
|
||||||
has_many :events, Event, foreign_key: :room_id
|
has_many :events, Event, foreign_key: :room_id
|
||||||
has_many :aliases, Alias, foreign_key: :room_id
|
has_many :aliases, Alias, foreign_key: :room_id
|
||||||
|
|
45
lib/architex/types/state_set.ex
Normal file
45
lib/architex/types/state_set.ex
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
defmodule Architex.Types.StateSet do
|
||||||
|
use Ecto.Type
|
||||||
|
|
||||||
|
import Ecto.Query
|
||||||
|
|
||||||
|
alias Architex.{Repo, Event}
|
||||||
|
|
||||||
|
@type t :: %{optional({String.t(), String.t()}) => Event.t()}
|
||||||
|
|
||||||
|
def type(), do: {:array, :string}
|
||||||
|
|
||||||
|
def cast(_), do: :error
|
||||||
|
|
||||||
|
def load(event_ids) when is_list(event_ids) do
|
||||||
|
events =
|
||||||
|
Event
|
||||||
|
|> where([e], e.id in ^event_ids)
|
||||||
|
|> Repo.all()
|
||||||
|
|> IO.inspect()
|
||||||
|
|
||||||
|
if length(events) == length(event_ids) do
|
||||||
|
state_set =
|
||||||
|
Enum.into(events, %{}, fn %Event{type: type, state_key: state_key} = event ->
|
||||||
|
{{type, state_key}, event}
|
||||||
|
end)
|
||||||
|
|
||||||
|
{:ok, state_set}
|
||||||
|
else
|
||||||
|
:error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def load(_), do: :error
|
||||||
|
|
||||||
|
def dump(state_set) when is_map(state_set) do
|
||||||
|
dumped =
|
||||||
|
Enum.map(state_set, fn {_, %Event{id: event_id}} ->
|
||||||
|
event_id
|
||||||
|
end)
|
||||||
|
|
||||||
|
{:ok, dumped}
|
||||||
|
end
|
||||||
|
|
||||||
|
def dump(_), do: :error
|
||||||
|
end
|
|
@ -14,7 +14,7 @@ defmodule Architex.Repo.Migrations.CreateInitialTables do
|
||||||
|
|
||||||
create table(:rooms, primary_key: false) do
|
create table(:rooms, primary_key: false) do
|
||||||
add :id, :string, primary_key: true
|
add :id, :string, primary_key: true
|
||||||
add :state, {:array, {:array, :string}}, default: [], null: false
|
add :state_set, {:array, :string}, default: [], null: false
|
||||||
add :forward_extremities, {:array, :string}, default: [], null: false
|
add :forward_extremities, {:array, :string}, default: [], null: false
|
||||||
add :visibility, :string, null: false, default: "public"
|
add :visibility, :string, null: false, default: "public"
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue