Implement client joined rooms endpoint

Track which rooms a local account has joined
Add some documentation to modules
This commit is contained in:
Pim Kunis 2021-08-23 12:59:12 +02:00
parent 9d40f8bc8b
commit 6f8c224d50
8 changed files with 95 additions and 11 deletions

View file

@ -11,7 +11,7 @@ defmodule MatrixServer.RoomServer do
import Ecto.Query
import Ecto.Changeset
alias MatrixServer.{Repo, Room, Event, StateResolution}
alias MatrixServer.{Repo, Room, Event, StateResolution, JoinedRoom, Account}
alias MatrixServer.StateResolution.Authorization
alias MatrixServerWeb.Client.Request.CreateRoom
@ -199,6 +199,7 @@ defmodule MatrixServer.RoomServer do
join_creator <- Event.join(room, account, [create_room]),
{:ok, state_set, join_creator, room} <-
verify_and_insert_event(join_creator, state_set, room),
{:ok, _} <- insert_joined_room_assoc(account, room),
pls <- Event.power_levels(room, account, [create_room, join_creator]),
{:ok, state_set, pls, room} <- verify_and_insert_event(pls, state_set, room) do
auth_events = [create_room, join_creator, pls]
@ -236,6 +237,10 @@ defmodule MatrixServer.RoomServer do
end
end
defp insert_joined_room_assoc(%Account{localpart: localpart}, %Room{id: room_id}) do
Repo.insert(%JoinedRoom{localpart: localpart, room_id: room_id})
end
# TODO: trusted_private_chat:
# All invitees are given the same power level as the room creator.
defp room_creation_preset(account, nil, %Room{visibility: visibility} = room, auth_events) do

View file

@ -3,7 +3,7 @@ defmodule MatrixServer.Account do
import Ecto.{Changeset, Query}
alias MatrixServer.{Repo, Account, Device}
alias MatrixServer.{Repo, Account, Device, Room, JoinedRoom}
alias MatrixServerWeb.Client.Request.{Register, Login}
alias Ecto.Multi
@ -17,6 +17,11 @@ defmodule MatrixServer.Account do
schema "accounts" do
field :password_hash, :string, redact: true
has_many :devices, Device, foreign_key: :localpart
many_to_many :joined_rooms, Room,
join_through: JoinedRoom,
join_keys: [localpart: :localpart, room_id: :id]
timestamps(updated_at: false)
end

View file

@ -0,0 +1,16 @@
defmodule MatrixServer.JoinedRoom do
use Ecto.Schema
alias MatrixServer.{Account, Room}
@primary_key false
schema "joined_rooms" do
belongs_to :account, Account,
foreign_key: :localpart,
references: :localpart,
type: :string,
primary_key: true
belongs_to :room, Room, primary_key: true, type: :string
end
end

View file

@ -1,4 +1,23 @@
defmodule MatrixServer.StateResolution do
@moduledoc """
Functions for resolving the state of a Matrix room.
Currently, only state resolution from room version 2 is supported,
see [the Matrix docs](https://spec.matrix.org/unstable/rooms/v2/).
The current implementation of the state resolution algorithm performs
rather badly.
Each time state is resolved, all events in the room are fetched from
the database and loaded into memory.
This is mostly so I didn't have to worry about fetching events from the
database when developing this initial implementation.
Then, the state is calculated using the new event's previous events and auth
events.
To prevent loading all events into memory, and calculating the whole state each
time, we should make snapshots of the state of a room at regular intervals.
It looks like Dendrite does this too.
"""
import Ecto.Query
alias MatrixServer.{Repo, Event, Room}
@ -61,6 +80,13 @@ defmodule MatrixServer.StateResolution do
|> Enum.reduce(MapSet.new(), &MapSet.union/2)
end
def update_state_set(
%Event{type: event_type, state_key: state_key} = event,
state_set
) do
Map.put(state_set, {event_type, state_key}, event)
end
defp do_resolve([], _), do: %{}
defp do_resolve(state_sets, room_events) do
@ -265,13 +291,6 @@ defmodule MatrixServer.StateResolution do
end)
end
def update_state_set(
%Event{type: event_type, state_key: state_key} = event,
state_set
) do
Map.put(state_set, {event_type, state_key}, event)
end
defp authorized?(%Event{auth_events: auth_event_ids} = event, state_set, room_events) do
state_set =
auth_event_ids

View file

@ -1,4 +1,11 @@
defmodule MatrixServer.StateResolution.Authorization do
@moduledoc """
Implementation of Matrix event authorization rules for stat resolution.
Note that some authorization rules are already checked in
`MatrixServer.Event.prevalidate/1` so they are skipped here.
"""
import MatrixServer.StateResolution
import Ecto.Query

View file

@ -2,9 +2,9 @@ defmodule MatrixServerWeb.Client.RoomController do
use MatrixServerWeb, :controller
import MatrixServerWeb.Error
import Ecto.Changeset
import Ecto.{Changeset, Query}
alias MatrixServer.Room
alias MatrixServer.{Repo, Room}
alias MatrixServerWeb.Client.Request.CreateRoom
alias Ecto.Changeset
alias Plug.Conn
@ -31,4 +31,19 @@ defmodule MatrixServerWeb.Client.RoomController do
put_error(conn, :bad_json)
end
end
def joined_rooms(%Conn{assigns: %{account: account}} = conn, _params) do
joined_room_ids = account
|> Ecto.assoc(:joined_rooms)
|> select([jr], jr.id)
|> Repo.all()
data = %{
joined_rooms: joined_room_ids
}
conn
|> put_status(200)
|> json(data)
end
end

View file

@ -51,6 +51,7 @@ defmodule MatrixServerWeb.Router do
post "/logout", AccountController, :logout
post "/logout/all", AccountController, :logout_all
post "/createRoom", RoomController, :create
get "/joined_rooms", RoomController, :joined_rooms
scope "/directory/room" do
put "/:alias", AliasesController, :create

View file

@ -0,0 +1,16 @@
defmodule MatrixServer.Repo.Migrations.CreateJoinedRoomsTable do
use Ecto.Migration
def change do
create table(:joined_rooms, primary_key: false) do
add :localpart,
references(:accounts, column: :localpart, type: :string),
primary_key: true,
null: false
add :room_id, references(:rooms, type: :string),
primary_key: true,
null: false
end
end
end