diff options
Diffstat (limited to 'src/Controllers/Client')
| -rw-r--r-- | src/Controllers/Client/ClientController.php | 292 | ||||
| -rw-r--r-- | src/Controllers/Client/KeyController.php | 47 | ||||
| -rwxr-xr-x | src/Controllers/Client/RoomController.php | 295 | ||||
| -rw-r--r-- | src/Controllers/Client/ServerInformationController.php | 57 | ||||
| -rwxr-xr-x | src/Controllers/Client/UserController.php | 71 |
5 files changed, 762 insertions, 0 deletions
diff --git a/src/Controllers/Client/ClientController.php b/src/Controllers/Client/ClientController.php new file mode 100644 index 0000000..053d288 --- /dev/null +++ b/src/Controllers/Client/ClientController.php @@ -0,0 +1,292 @@ +<?php + +namespace App\Controllers\Client; + +use App\Database; +use App\Errors\AppException; +use App\Errors\UnknownError; +use App\Models\Device; +use App\Models\RoomEvent; +use App\Models\Tokens; +use App\Models\User; +use App\Support\Logger; +use App\Support\Parser; +use App\Support\RequestValidator; +use Matrix\Data\AccountData; +use Matrix\Data\DeviceLists; +use Matrix\Data\LoginFlow; +use Matrix\Data\Presence; +use Matrix\Data\Room\Ephemeral; +use Matrix\Data\Room\JoinedRoom; +use Matrix\Data\Room\RoomSummary; +use Matrix\Data\Room\Rooms; +use Matrix\Data\Room\State; +use Matrix\Data\Room\Timeline; +use Matrix\Data\Room\UnreadNotificationCounts; +use Matrix\Data\ToDevice; +use Matrix\Enums\ErrorCode; +use Matrix\Enums\LoginType; +use Matrix\Enums\MembershipState; +use Matrix\Enums\PresenceState; +use Matrix\Enums\UserRegistrationKind; +use Matrix\Events\PresenceEvent; +use Matrix\Responses\ClientLoginGetResponse; +use Matrix\Responses\ClientLoginPostResponse; +use Matrix\Responses\ClientRefreshPostResponse; +use Matrix\Responses\ClientRegisterPostResponse; +use Matrix\Responses\ClientSyncGetResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\Routing\Attribute\Route; + +class ClientController +{ + #[Route(path: "_matrix/client/r0/login", methods: ["GET"])] + #[Route(path: "_matrix/client/v3/login", methods: ["GET"])] + public function supportedLoginTypes(Request $request): Response + { + return new JsonResponse(new ClientLoginGetResponse([ + (new LoginFlow(LoginType::PASSWORD)), + ])); + } + + #[Route(path: "_matrix/client/r0/login", methods: ["POST"])] + #[Route(path: "_matrix/client/v3/login", methods: ["POST"])] + public function login(Request $request): Response + { + Logger::logRequestToFile($request); + + $body = json_decode($request->getContent(), true); + RequestValidator::validateJson(); + + // validate login type + $loginType = null; + try { + $loginType = LoginType::from($body["type"]); + } catch (\ValueError $error) { + throw new UnknownError("Bad login type.", Response::HTTP_BAD_REQUEST); + } + + // get user id + $userId = Parser::parseUser($body["identifier"]["user"]); + if (empty($userId["server"])) { + $userId = "@$userId[username]:$_ENV[DOMAIN]"; + #$userId = "@$userId[username]:localhost"; + } else { + $userId = "@$userId[username]:$userId[server]"; + } + + if ($loginType !== LoginType::PASSWORD) { + throw new AppException(ErrorCode::UNRECOGNIZED, "only password login supported for now", Response::HTTP_SERVICE_UNAVAILABLE); + } + + $user = User::fetchWithPassword($userId, $body["password"]); + + if (! $user) { + throw new AppException(ErrorCode::FORBIDDEN, "Invalid credentials", Response::HTTP_FORBIDDEN); + } + + $deviceId = $body["device_id"] ?? ""; + + $device = null; + $tokens = null; + + // create new device with tokens + if (empty($deviceId)) { + $device = Device::new( + $user->getId(), + initialDisplayName: $body["initial_device_display_name"] ?? "", + ); + $device->insert(); + + $tokens = Tokens::new($userId, $device->getId()); + $tokens->insert(); + } else { // fetch existing device and tokens + $device = $user->fetchDevice($deviceId); + $tokens = Tokens::fetch($userId, $device->getId()); + + if (empty($tokens)) { + throw new AppException( + ErrorCode::UNKNOWN_TOKEN, + "Soft logged out", + Response::HTTP_UNAUTHORIZED, + ["soft_logout" => true], + ); + } + } + + return new JsonResponse(new ClientLoginPostResponse( + accessToken: $tokens->getAccessToken(), + deviceId:$device->getId(), + userId: $user->getId(), + expiresInMilliseconds: $tokens->getExpiresIn(), + refreshToken: $tokens->getRefreshToken(), + )); + } + + #[Route(path: "_matrix/client/v3/register", methods: ["POST"])] + public function register(Request $request): Response + { + $body = json_decode($request->getContent(), true); + RequestValidator::validateJson(); + + // validate kind + $kind = null; + try { + $kind = UserRegistrationKind::from($request->query->get("kind") ?? "user"); + } catch (\ValueError $error) { + throw new UnknownError("Bad registration kind.", Response::HTTP_BAD_REQUEST); + } + + $username = $body["username"]; + $userId = "@$username:$_ENV[DOMAIN]"; + + Database::getInstance()->query("insert into users (id, password) values (:id, :password)", [ + "id" => $userId, + "password" => $body["password"], + ]); + + $device_id = $body["device_id"] ?? ""; + $initialDeviceDisplayName = $body["initial_device_display_name"] ?? ""; + + $device = Device::new($userId, $device_id, $initialDeviceDisplayName); + $device->insert(); + + $tokens = Tokens::new($userId, $device->getId()); + $tokens->insert(); + + return new JsonResponse(new ClientRegisterPostResponse( + accessToken: $tokens->getAccessToken(), + deviceId: $device->getId(), + expiresInMilliseconds: $tokens->getExpiresIn(), + refreshToken: $tokens->getRefreshToken(), + userId: $userId, + )); + } + + /** + * @see https://spec.matrix.org/v1.15/client-server-api/#get_matrixclientv3sync + * @see https://spec.matrix.org/v1.15/client-server-api/#extensions-to-sync + */ + #[Route(path: "_matrix/client/r0/sync", methods: ["GET"])] + #[Route(path: "_matrix/client/v3/sync", methods: ["GET"])] + public function sync(Request $request): Response + { + $user = User::authenticateWithRequest($request); + + $filter = $request->query->get("filter", ""); + $syncFullState = $request->query->get("full_state", false); + $setPresence = PresenceState::tryFrom($request->query->get("set_presence") ?? "") ?? PresenceState::ONLINE; + $since = $request->query->get("since", ""); + $timeout = $request->query->get("timeout", 0); + $useStateAfter = $request->query->get("use_state_after", false); + + if (! empty($filter)) { + if (str_starts_with($filter, "{")) { + $filter = json_decode($filter, true); + } else { + $filter = Database::getInstance()->query("select * from filters where id=:id", ["id" => $filter])->fetch(); + } + } + + $rooms = Database::getInstance()->query(<<<SQL + select * from rooms + left join room_memberships + on rooms.id = room_memberships.room_id + where room_memberships.user_id = :user_id + SQL, [ + "user_id" => $user->getId(), + ])->fetchAll(); + + $invitedRooms = []; + $joinedRooms = []; + $knockedRooms = []; + $leftRooms = []; + + foreach ($rooms as $room) { + $events = Database::getInstance()->query(<<<SQL + select * from room_events + where room_id = :room_id + SQL, [ + "room_id" => $room["room_id"], + #"limit" => ($filter["room"]["timeline"]["limit"] ?? false) ? "limit " . $filter["room"]["timeline"]["limit"] : "", + ])->fetchAll(); + + if ($since === "" && MembershipState::tryFrom($room["state"]) === MembershipState::JOIN) { + $joinedRooms[$room["room_id"]] = new JoinedRoom( + accountData: new AccountData([]), + ephemeral: new Ephemeral([]), + state: new State([]), + summary: new RoomSummary( + heroes: [], + invitedMemberCount: 0, + joinedMemberCount: 1, + ), + timeline: new Timeline( + events: array_map([RoomEvent::class, "transformEvent"], $events), + limited: false,# $filter["room"]["timeline"]["limit"] ?? false, + previousBatch: null, + ), + unreadNotifications: new UnreadNotificationCounts(0, 0), + unreadThreadNotifications: [], + ); + } + } + + return new JsonResponse(new ClientSyncGetResponse( + nextBatch: "1", + + accountData: new AccountData([]), + + deviceLists: new DeviceLists([], []), + + deviceOneTimeKeysCount: [ + "signed_curve25519" => 10, + ], + + presence: new Presence([ + new PresenceEvent( + sender: $user->getId(), + presence: $setPresence, + ), + ]), + + rooms: new Rooms( + $invitedRooms, + $joinedRooms, + $knockedRooms, + $leftRooms, + ), + + toDevice: new ToDevice([]), + )); + } + + #[Route(path: "/_matrix/client/v3/refresh", methods: ["POST"])] + public function refresh(Request $request): Response + { + $body = json_decode($request->getContent(), true); + RequestValidator::validateJson(); + + $tokens = Tokens::fetchWithRefreshToken($body["refresh_token"]); + + if (empty($tokens)) { + throw new AppException( + ErrorCode::UNKNOWN_TOKEN, + "Soft logged out", + Response::HTTP_UNAUTHORIZED, + ["soft_logout" => true], + ); + } + + $newTokens = Tokens::new($tokens->getUserId(), $tokens->getDeviceId()); + $newTokens->insert(); + + return new JsonResponse(new ClientRefreshPostResponse( + accessToken: $newTokens->getAccessToken(), + expiresInMilliseconds: $newTokens->getExpiresIn(), + refreshToken: $newTokens->getRefreshToken(), + )); + } +} diff --git a/src/Controllers/Client/KeyController.php b/src/Controllers/Client/KeyController.php new file mode 100644 index 0000000..b9ae61f --- /dev/null +++ b/src/Controllers/Client/KeyController.php @@ -0,0 +1,47 @@ +<?php + +namespace App\Controllers\Client; + +use App\Models\User; +use App\Support\RequestValidator; +use Matrix\Responses\ClientKeysUploadPostResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\Routing\Attribute\Route; + +class KeyController +{ + #[Route(path: "/_matrix/client/v3/keys/query", methods: ["POST"])] + public function query(Request $request): Response + { + $user = User::authenticateWithRequest($request); + $body = json_decode($request->getContent(), true); + RequestValidator::validateJson(); + + $deviceKeys = $body["device_keys"]; + $timeout = $body["timeout"] ?? 10000; + + foreach ($deviceKeys as $keysUserId => $deviceIds) {} + + return new JsonResponse([ + "device_keys" => [], + ]); + } + + #[Route(path: "/_matrix/client/r0/keys/upload", methods: ["POST"])] + #[Route(path: "/_matrix/client/v3/keys/upload", methods: ["POST"])] + public function upload(Request $request): Response + { + $user = User::authenticateWithRequest($request); + $body = json_decode($request->getContent(), true); + RequestValidator::validateJson(); + + foreach ($body["one_time_keys"] as $identifier => $object) {} + + return new JsonResponse(new ClientKeysUploadPostResponse([ + #"curve25519" => 0, + "signed_curve25519" => count($body["one_time_keys"]), + ])); + } +} diff --git a/src/Controllers/Client/RoomController.php b/src/Controllers/Client/RoomController.php new file mode 100755 index 0000000..ec04a2f --- /dev/null +++ b/src/Controllers/Client/RoomController.php @@ -0,0 +1,295 @@ +<?php + +namespace App\Controllers\Client; + +use App\Database; +use App\Errors\AppException; +use App\Errors\Exception; +use App\Errors\UnauthorizedError; +use App\Models\RoomEvent; +use App\Models\User; +use App\Support\Id; +use App\Support\Parser; +use App\Support\RequestValidator; +use Matrix\Data\UnsignedData; +use Matrix\Enums\ErrorCode; +use Matrix\Enums\EventType; +use Matrix\Enums\MembershipState; +use Matrix\Enums\MessageType; +use Matrix\Enums\RoomGuestAccess; +use Matrix\Enums\RoomHistoryVisibility; +use Matrix\Enums\RoomJoinRule; +use Matrix\Enums\RoomVisibility; +use Matrix\Events\ClientEvent; +use Matrix\Events\Room\CreateEvent; +use Matrix\Events\Room\GuestAccessEvent; +use Matrix\Events\Room\HistoryVisibilityEvent; +use Matrix\Events\Room\JoinRulesEvent; +use Matrix\Events\Room\MemberEvent; +use Matrix\Events\Room\NameEvent; +use Matrix\Events\Room\PowerLevelsEvent; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\Routing\Attribute\Route; + +class RoomController +{ + #[Route(path: "/_matrix/client/v3/createRoom", methods: ["POST"])] + public function createRoom(Request $request): Response + { + $user = User::authenticateWithRequest($request); + $body = json_decode($request->getContent(), true); + RequestValidator::validateJson(); + + $creationContent = $body["creation_content"] ?? []; + $initialState = $body["initial_state"] ?? []; + $invite = $body["invite"] ?? []; + $body["invite_3pid"] ?? []; + $body["is_direct"] ?? false; + $name = $body["name"] ?? ""; + $body["power_level_content_override"] ?? []; + $preset = $body["preset"] ?? ""; + $roomAliasName = $body["room_alias_name"] ?? ""; + $roomVersion = $body["room_version"] ?? "DEFAULT_ROOM_VERSION"; + $topic = $body["topic"] ?? ""; + $visibility = RoomVisibility::tryFrom($body["visibility"] ?? "private"); + + if (! $preset) { + $preset = $visibility->value . "_chat"; + } + + # TODO: get events for preset + # TODO: override preset events with initial state + # TODO: override events with name and topic if applicable + $state = []; + + if ($name) { + $state[EventType::ROOM_NAME->value] = $name; + } + + if ($topic) { + $state[EventType::ROOM_TOPIC->value] = $topic; + } + + // create room + $roomId = Id::generateRoomId(); + Database::getInstance()->query(<<<SQL + insert into rooms (id, name) values (:id, :name) + SQL, [ + "id" => $roomId, + "name" => $roomAliasName, # "#$roomAliasName:$_ENV[DOMAIN]", + ]); + + $roomCreateEvent = new RoomEvent(new CreateEvent( + eventId: Id::generateEventId(), + originServerTimestamp: time(), + roomId: $roomId, + sender: $user->getId(), + roomVersion: "12", + )); + $roomCreateEvent->insert(); + + $roomMemberEvent = new RoomEvent(new MemberEvent( + eventId: Id::generateEventId(), + originServerTimestamp: time(), + roomId: $roomId, + sender: $user->getId(), + stateKey: $user->getId(), + isDirect: false, + membership: MembershipState::JOIN, + displayName: $user->getName(), + )); + $roomMemberEvent->insert(); + + $roomPowerLevelsEvent = new RoomEvent(new PowerLevelsEvent( + eventId: Id::generateEventId(), + originServerTimestamp: time(), + roomId: $roomId, + sender: $user->getId(), + )); + $roomPowerLevelsEvent->insert(); + + $roomJoinRulesEvent = new RoomEvent(new JoinRulesEvent( + eventId: Id::generateEventId(), + originServerTimestamp: time(), + roomId: $roomId, + sender: $user->getId(), + joinRule: RoomJoinRule::INVITE, + )); + $roomJoinRulesEvent->insert(); + + $roomHistoryVisibilityEvent = new RoomEvent(new HistoryVisibilityEvent( + eventId: Id::generateEventId(), + originServerTimestamp: time(), + roomId: $roomId, + sender: $user->getId(), + historyVisibility: RoomHistoryVisibility::SHARED, + )); + $roomHistoryVisibilityEvent->insert(); + + $roomGuestAccessEvent = new RoomEvent(new GuestAccessEvent( + eventId: Id::generateEventId(), + originServerTimestamp: time(), + roomId: $roomId, + sender: $user->getId(), + guestAccess: RoomGuestAccess::CAN_JOIN, + )); + $roomGuestAccessEvent->insert(); + + $roomNameEvent = new RoomEvent(new NameEvent( + eventId: Id::generateEventId(), + originServerTimestamp: time(), + roomId: $roomId, + sender: $user->getId(), + name: $roomAliasName, + )); + $roomNameEvent->insert(); + + Database::getInstance()->query(<<<SQL + insert into room_memberships (room_id, user_id, state) + values (:room_id, :user_id, :state) + SQL, [ + "room_id" => $roomId, + "user_id" => $user->getId(), + "state" => MembershipState::JOIN->value, + ]); + + return new JsonResponse([ + "room_id" => $roomId, + ]); + } + + /** + * @see https://spec.matrix.org/v1.15/client-server-api/#get_matrixclientv3directoryroomroomalias + */ + #[Route(path: "/_matrix/client/v3/directory/room/{roomAlias}", methods: ["GET"])] + public function resolveAlias(Request $request): Response + { + $alias = $request->attributes->get("roomAlias"); + + $roomAlias = Parser::parseRoomAlias($alias); # TODO: on parse error => 400 + $roomId = null; + + if ($roomAlias["server"] != $_ENV["DOMAIN"]) { + # TODO: federation API resolve + $roomId = -1; + } + else { + $room = Database::getInstance()->query(<<<SQL + select id from rooms where name = :name + SQL, [ + "name" => $roomAlias["name"], + ])->fetch(); + + $roomId = $room["id"] ?? null; + } + + if (empty($roomId)) { + throw new AppException( + ErrorCode::NOT_FOUND, + "Room alias $alias not found.", + Response::HTTP_NOT_FOUND + ); + } + + return new JsonResponse([ + "room_id" => $roomId, + "servers" => [], + ]); + } + + #[Route(path: "/_matrix/client/v3/{roomId}/messages", methods: ["GET"])] + public function getMessages(Request $request): Response + { + $user = User::authenticateWithRequest($request); + + $roomId = $request->attributes->get("roomId"); + + $membership = Database::getInstance() + ->query("select state from room_memberships where user_id=:user_id and room_id=:room_id", [ + "user_id" => $user->getId(), + "room_id" => $roomId, + ]) + ->fetchColumn(); + + if (MembershipState::from($membership) !== MembershipState::JOIN) { + throw new Exception(ErrorCode::FORBIDDEN, "You aren't a member of the room.", Response::HTTP_FORBIDDEN); + } + + $direction = $request->query->get("dir"); + $filter = $request->query->get("filter"); + $from = $request->query->get("from"); + $limit = $request->query->get("limit", 10); + $to = $request->query->get("to"); + + $events = Database::getInstance()->query(<<<SQL + select * from room_events + where room_id = :room_id + SQL, [ + "room_id" => $roomId, + #"limit" => ($filter["room"]["timeline"]["limit"] ?? false) ? "limit " . $filter["room"]["timeline"]["limit"] : "", + ])->fetchAll(); + + return new JsonResponse([ + "chunk" => array_map([RoomEvent::class, "transformEvent"], $events), + "end" => "", + "start" => "", + "state" => [], + ]); + } + + #[Route(path: "/_matrix/client/v3/rooms/{roomId}/read_markers", methods: ["POST"])] + public function readMarkers(Request $request): Response + { + $user = User::authenticateWithRequest($request); + $body = json_decode($request->getContent(), true); + RequestValidator::validateJson(); + + $roomId = $request->attributes->get("roomId"); + + return new JsonResponse(); + } + + /** + * @see https://spec.matrix.org/v1.15/client-server-api/#put_matrixclientv3roomsroomidsendeventtypetxnid + */ + #[Route(path: "/_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}", methods: ["PUT"])] + public function send(Request $request): Response + { + $user = User::authenticateWithRequest($request); + + if (empty($user)) { + throw new UnauthorizedError(); + } + + $roomId = $request->attributes->get("roomId"); + $eventType = EventType::from($request->attributes->get("eventType")); + $transactionId = $request->attributes->get("txnId"); + + $body = json_decode($request->getContent(), true); + RequestValidator::validateJson(); + + // validate msgtype + MessageType::from($body["msgtype"]); + + $eventId = Id::generateEventId(); + $event = new RoomEvent(new ClientEvent( + content: $body, + eventId: $eventId, + originServerTimestamp: time(), + roomId: $roomId, + sender: $user->getId(), + type: $eventType, + unsigned: new UnsignedData( + age: 1234, # TODO + membership: MembershipState::JOIN, + ), + )); + $event->insert(); + + return new JsonResponse([ + "event_id" => $eventId, + ]); + } +} diff --git a/src/Controllers/Client/ServerInformationController.php b/src/Controllers/Client/ServerInformationController.php new file mode 100644 index 0000000..51e68e6 --- /dev/null +++ b/src/Controllers/Client/ServerInformationController.php @@ -0,0 +1,57 @@ +<?php + +namespace App\Controllers\Client; + +use Matrix\Responses\ClientVersionsGetResponse; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Attribute\Route; + +class ServerInformationController +{ + #[Route(path: "/.well-known/matrix/client", methods: ["GET"])] + public function client(Request $request): Response + { + return new JsonResponse([ + "m.homeserver" => [ + "base_url" => "https://$_ENV[DOMAIN]", + ], + ]); + } + + #[Route(path: "/.well-known/matrix/support", methods: ["GET"])] + public function support(Request $request): Response + { + return new JsonResponse([ + "contacts" => [], + "support_page" => "", + ]); + } + + #[Route(path: "/_matrix/client/versions", methods: ["GET"])] + public function versions(Request $request): Response + { + return new JsonResponse(new ClientVersionsGetResponse([ + "r0.0.1", + "v1.1", + ])); + } + + public function server(Request $request): Response + { + return new JsonResponse([ + "m.server" => "$_ENV[DOMAIN]:443", + ]); + } + + public function version(Request $request): Response + { + return new JsonResponse([ + "server" => [ + "name" => "Matrix PHP", + "version" => "0.1.0", + ], + ]); + } +} diff --git a/src/Controllers/Client/UserController.php b/src/Controllers/Client/UserController.php new file mode 100755 index 0000000..038caba --- /dev/null +++ b/src/Controllers/Client/UserController.php @@ -0,0 +1,71 @@ +<?php + +namespace App\Controllers\Client; + +use App\Database; +use App\Errors\UnauthorizedError; +use App\Models\Device; +use App\Models\User; +use App\Support\RequestValidator; +use Matrix\Responses\ClientAccountWhoamiGetResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\Routing\Attribute\Route; + +class UserController +{ + #[Route(path: "/_matrix/client/v3/account/whoami", methods: ["GET"])] + public function whoami(Request $request): Response + { + $user = User::authenticateWithRequest($request); + $device = Device::fetch(userId: $user->getId()); + + return new JsonResponse(new ClientAccountWhoamiGetResponse( + userId: $user->getId(), + deviceId: $device->getId(), + )); + } + + /** + * @see https://spec.matrix.org/v1.16/client-server-api/#post_matrixclientv3useruseridfilter + */ + #[Route(path: "/_matrix/client/r0/user/{userId}/filter", methods: ["POST"])] + #[Route(path: "/_matrix/client/v3/user/{userId}/filter", methods: ["POST"])] + public function uploadFilter(Request $request): Response + { + $accessToken = str_replace("Bearer ", "", $request->headers->get("authorization") ?: ""); + $user = User::fetchWithAccessToken($accessToken); + + if (empty($user)) { + throw new UnauthorizedError(); + } + + $userId = $request->get("userId"); + if ($user->getId() !== $userId) { + throw new UnauthorizedError(); + } + + $body = json_decode($request->getContent(), true); + RequestValidator::validateJson(); + + $filterId = md5($userId . random_bytes(512)); + + Database::getInstance()->query(<<<SQL + insert into filters (id, account_data, event_fields, event_format, presence, room, user_id) + values (:id, :account_data, :event_fields, :event_format, :presence, :room, :user_id) + SQL, [ + "id" => $filterId, + "account_data" => isset($body["account_data"]) ? json_encode($body["account_data"]) : null, + "event_fields" => isset($body["event_fields"]) ? json_encode($body["event_fields"]) : null, + "event_format" => isset($body["event_format"]) ? json_encode($body["event_format"]) : null, + "presence" => isset($body["presence"]) ? json_encode($body["presence"]) : null, + "room" => isset($body["room"]) ? json_encode($body["room"]) : null, + "user_id" => $userId, + ]); + + return new JsonResponse([ + "filter_id" => $filterId, + ]); + } +} |
