summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--composer.json3
-rw-r--r--composer.lock235
-rw-r--r--src/App.php1
-rwxr-xr-xsrc/Controllers/AccountController.php29
-rw-r--r--src/Controllers/Client/ClientController.php292
-rw-r--r--src/Controllers/Client/KeyController.php47
-rwxr-xr-xsrc/Controllers/Client/RoomController.php (renamed from src/Controllers/RoomController.php)18
-rw-r--r--src/Controllers/Client/ServerInformationController.php57
-rwxr-xr-xsrc/Controllers/Client/UserController.php (renamed from src/Controllers/UserController.php)22
-rw-r--r--src/Controllers/KeyController.php78
-rw-r--r--src/Controllers/LoginController.php151
-rw-r--r--src/Controllers/Server/ServerInformationController.php (renamed from src/Controllers/ServerImplementationController.php)23
-rw-r--r--src/Controllers/ServerDiscoveryController.php34
-rwxr-xr-xsrc/Controllers/SyncController.php126
-rw-r--r--src/Models/User.php7
-rw-r--r--src/Router.php (renamed from src/Router/Router.php)44
-rw-r--r--src/Router/routes_client_server.php116
-rw-r--r--src/Router/routes_server_server.php24
18 files changed, 696 insertions, 611 deletions
diff --git a/composer.json b/composer.json
index 5a1178e..2b51506 100644
--- a/composer.json
+++ b/composer.json
@@ -12,7 +12,8 @@
"psr/log": "^3.0",
"symfony/dotenv": "^7.3",
"symfony/http-foundation": "^7.3",
- "symfony/routing": "^7.3"
+ "symfony/routing": "^7.3",
+ "symfony/config": "^8.0"
},
"require-dev": {
"guzzlehttp/guzzle": "^7.9",
diff --git a/composer.lock b/composer.lock
index d720eb4..253dba4 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "4964ee19b991107643bde192528a6119",
+ "content-hash": "1db0630d632f53cdc961a9f51269f376",
"packages": [
{
"name": "psr/http-message",
@@ -110,6 +110,84 @@
"time": "2024-09-11T13:17:53+00:00"
},
{
+ "name": "symfony/config",
+ "version": "v8.0.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/config.git",
+ "reference": "c7369cc1da250fcbfe0c5a9d109e419661549c39"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/config/zipball/c7369cc1da250fcbfe0c5a9d109e419661549c39",
+ "reference": "c7369cc1da250fcbfe0c5a9d109e419661549c39",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.4",
+ "symfony/deprecation-contracts": "^2.5|^3",
+ "symfony/filesystem": "^7.4|^8.0",
+ "symfony/polyfill-ctype": "^1.8"
+ },
+ "conflict": {
+ "symfony/service-contracts": "<2.5"
+ },
+ "require-dev": {
+ "symfony/event-dispatcher": "^7.4|^8.0",
+ "symfony/finder": "^7.4|^8.0",
+ "symfony/messenger": "^7.4|^8.0",
+ "symfony/service-contracts": "^2.5|^3",
+ "symfony/yaml": "^7.4|^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Config\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/config/tree/v8.0.8"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-03-30T15:14:47+00:00"
+ },
+ {
"name": "symfony/deprecation-contracts",
"version": "v3.6.0",
"source": {
@@ -255,6 +333,76 @@
"time": "2025-07-10T08:29:33+00:00"
},
{
+ "name": "symfony/filesystem",
+ "version": "v8.0.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/filesystem.git",
+ "reference": "66b769ae743ce2d13e435528fbef4af03d623e5a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/filesystem/zipball/66b769ae743ce2d13e435528fbef4af03d623e5a",
+ "reference": "66b769ae743ce2d13e435528fbef4af03d623e5a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.4",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-mbstring": "~1.8"
+ },
+ "require-dev": {
+ "symfony/process": "^7.4|^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Filesystem\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides basic utilities for the filesystem",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/filesystem/tree/v8.0.8"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-03-30T15:14:47+00:00"
+ },
+ {
"name": "symfony/http-foundation",
"version": "v7.3.3",
"source": {
@@ -338,6 +486,89 @@
"time": "2025-08-20T08:04:18+00:00"
},
{
+ "name": "symfony/polyfill-ctype",
+ "version": "v1.33.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-ctype.git",
+ "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
+ "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "provide": {
+ "ext-ctype": "*"
+ },
+ "suggest": {
+ "ext-ctype": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Ctype\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Gert de Pagter",
+ "email": "BackEndTea@gmail.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for ctype functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "ctype",
+ "polyfill",
+ "portable"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-09-09T11:45:10+00:00"
+ },
+ {
"name": "symfony/polyfill-mbstring",
"version": "v1.32.0",
"source": {
@@ -2691,5 +2922,5 @@
"prefer-lowest": false,
"platform": {},
"platform-dev": {},
- "plugin-api-version": "2.6.0"
+ "plugin-api-version": "2.9.0"
}
diff --git a/src/App.php b/src/App.php
index 9b1edf9..33f71ef 100644
--- a/src/App.php
+++ b/src/App.php
@@ -2,7 +2,6 @@
namespace App;
-use App\Router\Router;
use Symfony\Component\Dotenv\Dotenv;
class App
diff --git a/src/Controllers/AccountController.php b/src/Controllers/AccountController.php
deleted file mode 100755
index 8e20880..0000000
--- a/src/Controllers/AccountController.php
+++ /dev/null
@@ -1,29 +0,0 @@
-<?php
-
-namespace App\Controllers;
-
-use App\Models\Device;
-use App\Models\User;
-use Matrix\Responses\ClientAccountWhoamiGetResponse;
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpFoundation\Response;
-use Symfony\Component\HttpFoundation\JsonResponse;
-
-class AccountController
-{
- /**
- * GET /_matrix/client/v3/account/whoami
- *
- * @see https://spec.matrix.org/v1.15/client-server-api/#get_matrixclientv3accountwhoami
- */
- 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(),
- ));
- }
-}
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/RoomController.php b/src/Controllers/Client/RoomController.php
index 060a030..ec04a2f 100755
--- a/src/Controllers/RoomController.php
+++ b/src/Controllers/Client/RoomController.php
@@ -1,6 +1,6 @@
<?php
-namespace App\Controllers;
+namespace App\Controllers\Client;
use App\Database;
use App\Errors\AppException;
@@ -31,9 +31,11 @@ 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);
@@ -159,10 +161,9 @@ class RoomController
}
/**
- * GET /_matrix/client/v3/directory/room/{roomAlias}
- *
* @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");
@@ -198,9 +199,7 @@ class RoomController
]);
}
- /**
- * GET /_matrix/client/v3/rooms/{roomId}/messages
- */
+ #[Route(path: "/_matrix/client/v3/{roomId}/messages", methods: ["GET"])]
public function getMessages(Request $request): Response
{
$user = User::authenticateWithRequest($request);
@@ -240,9 +239,7 @@ class RoomController
]);
}
- /**
- * POST /_matrix/client/v3/rooms/{roomId}/read_markers
- */
+ #[Route(path: "/_matrix/client/v3/rooms/{roomId}/read_markers", methods: ["POST"])]
public function readMarkers(Request $request): Response
{
$user = User::authenticateWithRequest($request);
@@ -255,10 +252,9 @@ class RoomController
}
/**
- * PUT /_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}
- *
* @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);
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/UserController.php b/src/Controllers/Client/UserController.php
index d102160..038caba 100755
--- a/src/Controllers/UserController.php
+++ b/src/Controllers/Client/UserController.php
@@ -1,23 +1,37 @@
<?php
-namespace App\Controllers;
+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(),
+ ));
+ }
+
/**
- * POST /_matrix/client/r0/user/{userId}/filter
- * POST /_matrix/client/v3/user/{userId}/filter
- *
* @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") ?: "");
diff --git a/src/Controllers/KeyController.php b/src/Controllers/KeyController.php
deleted file mode 100644
index ebf580a..0000000
--- a/src/Controllers/KeyController.php
+++ /dev/null
@@ -1,78 +0,0 @@
-<?php
-
-namespace App\Controllers;
-
-use App\Errors\AppException;
-use App\Models\Tokens;
-use App\Models\User;
-use App\Support\RequestValidator;
-use Matrix\Enums\ErrorCode;
-use Matrix\Responses\ClientKeysUploadPostResponse;
-use Matrix\Responses\ClientRefreshPostResponse;
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpFoundation\Response;
-use Symfony\Component\HttpFoundation\JsonResponse;
-
-class KeyController
-{
- public function server(Request $request): Response
- {
- return new JsonResponse([
- "server" => [
- "name" => "Matrix PHP",
- "version" => "0.1.0",
- ],
- ]);
- }
-
- /**
- * POST /_matrix/client/v3/keys/upload
- */
- 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 => $data) {}
-
- return new JsonResponse(new ClientKeysUploadPostResponse([
- #"curve25519" => 0,
- "signed_curve25519" => count($body["one_time_keys"]),
- ]));
- }
-
- public function query(Request $request): Response
- {
- $serverName = $request->attributes->get("serverName");
- }
-
- /**
- * POST /_matrix/client/v3/refresh
- */
- 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/LoginController.php b/src/Controllers/LoginController.php
deleted file mode 100644
index f9576fb..0000000
--- a/src/Controllers/LoginController.php
+++ /dev/null
@@ -1,151 +0,0 @@
-<?php
-
-namespace App\Controllers;
-
-use App\Database;
-use App\Errors\AppException;
-use App\Errors\UnknownError;
-use App\Models\Device;
-use App\Models\Tokens;
-use App\Models\User;
-use App\Support\Logger;
-use App\Support\Parser;
-use App\Support\RequestValidator;
-use Matrix\Data\LoginFlow;
-use Matrix\Enums\ErrorCode;
-use Matrix\Enums\LoginType;
-use Matrix\Enums\UserRegistrationKind;
-use Matrix\Responses\ClientLoginGetResponse;
-use Matrix\Responses\ClientLoginPostResponse;
-use Matrix\Responses\ClientRegisterPostResponse;
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpFoundation\Response;
-use Symfony\Component\HttpFoundation\JsonResponse;
-
-class LoginController
-{
- /**
- * GET /_matrix/client/r0/login
- */
- public function supportedLoginTypes(Request $request): Response
- {
- return new JsonResponse(new ClientLoginGetResponse([
- (new LoginFlow(LoginType::PASSWORD)),
- ]));
- }
-
- /**
- * POST /_matrix/client/v3/login
- */
- 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) {}
-
- $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(),
- ));
- }
-
- /**
- * POST /_matrix/client/v3/register
- */
- 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,
- ));
- }
-}
diff --git a/src/Controllers/ServerImplementationController.php b/src/Controllers/Server/ServerInformationController.php
index 93b9a3f..a77bcca 100644
--- a/src/Controllers/ServerImplementationController.php
+++ b/src/Controllers/Server/ServerInformationController.php
@@ -1,28 +1,29 @@
<?php
-namespace App\Controllers;
+namespace App\Controllers\Server;
+use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
-use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\Routing\Attribute\Route;
-class ServerImplementationController
+class ServerInformationController
{
- public function version(Request $request): Response
+ #[Route(path: "/.well-known/matrix/server", methods: ["GET"])]
+ public function server(Request $request): Response
{
return new JsonResponse([
- "server" => [
- "name" => "Matrix PHP",
- "version" => "0.1.0",
- ],
+ "m.server" => "$_ENV[DOMAIN]:443",
]);
}
- public function versions(Request $request): Response
+ #[Route(path: "/_matrix/federation/v1/version", methods: ["GET"])]
+ public function version(Request $request): Response
{
return new JsonResponse([
- "versions" => [
- "v1.1",
+ "server" => [
+ "name" => "Matrix PHP",
+ "version" => "0.1.0",
],
]);
}
diff --git a/src/Controllers/ServerDiscoveryController.php b/src/Controllers/ServerDiscoveryController.php
deleted file mode 100644
index 917df14..0000000
--- a/src/Controllers/ServerDiscoveryController.php
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-
-namespace App\Controllers;
-
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpFoundation\Response;
-use Symfony\Component\HttpFoundation\JsonResponse;
-
-class ServerDiscoveryController
-{
- public function server(Request $request): Response
- {
- return new JsonResponse([
- "m.server" => "$_ENV[DOMAIN]:443",
- ]);
- }
-
- public function client(Request $request): Response
- {
- return new JsonResponse([
- "m.homeserver" => [
- "base_url" => "http://$_ENV[DOMAIN]",
- ],
- ]);
- }
-
- public function support(Request $request): Response
- {
- return new JsonResponse([
- "contacts" => [],
- "support_page" => "",
- ]);
- }
-}
diff --git a/src/Controllers/SyncController.php b/src/Controllers/SyncController.php
deleted file mode 100755
index acebb11..0000000
--- a/src/Controllers/SyncController.php
+++ /dev/null
@@ -1,126 +0,0 @@
-<?php
-
-namespace App\Controllers;
-
-use App\Database;
-use App\Models\RoomEvent;
-use App\Models\User;
-use Matrix\Data\AccountData;
-use Matrix\Data\DeviceLists;
-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\MembershipState;
-use Matrix\Enums\PresenceState;
-use Matrix\Events\PresenceEvent;
-use Matrix\Responses\ClientSyncGetResponse;
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpFoundation\Response;
-use Symfony\Component\HttpFoundation\JsonResponse;
-
-class SyncController
-{
- /**
- * GET /_matrix/client/v3/sync
- *
- * @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
- */
- 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([]),
- ));
- }
-}
diff --git a/src/Models/User.php b/src/Models/User.php
index b8aad62..b24afaf 100644
--- a/src/Models/User.php
+++ b/src/Models/User.php
@@ -22,7 +22,7 @@ class User implements ConnectsToDatabase
{
return new self(
$row["id"],
- $row["name"],
+ $row["name"] ?? "",
);
}
@@ -82,6 +82,11 @@ class User implements ConnectsToDatabase
public static function authenticateWithRequest(Request $request): self
{
$accessToken = str_replace("Bearer ", "", $request->headers->get("authorization") ?: "");
+
+ if (empty($accessToken)) {
+ throw new AppException(ErrorCode::UNAUTHORIZED, "Missing access token", Response::HTTP_UNAUTHORIZED);
+ }
+
$user = self::fetchWithAccessToken($accessToken);
if (empty($user)) {
diff --git a/src/Router/Router.php b/src/Router.php
index 6859771..cda3006 100644
--- a/src/Router/Router.php
+++ b/src/Router.php
@@ -1,19 +1,22 @@
<?php
-namespace App\Router;
+namespace App;
use App\Errors\ErrorResponse;
use App\Errors\Exception;
use App\Singleton;
use App\Support\Logger;
use Matrix\Enums\ErrorCode;
+use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
-use Symfony\Component\Routing\Loader\Configurator\RouteConfigurator;
+use Symfony\Component\Routing\Loader\AttributeClassLoader;
+use Symfony\Component\Routing\Loader\AttributeDirectoryLoader;
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
class Router
@@ -21,32 +24,38 @@ class Router
use Singleton;
private RouteCollection $routes;
- private RouteConfigurator $configurator;
public function __construct()
{
- $this->routes = new RouteCollection();
- $this->configurator = new RouteConfigurator($this->routes, $this->routes);
-
- $this->addRoutes();
+ // load routes
+ $loader = new AttributeDirectoryLoader(new FileLocator(), new class() extends AttributeClassLoader {
+ protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, object $attr) {
+ $route->setDefault("_controller", [$class->getName(), $method->getName()]);
+ }
+ });
+
+ $this->routes = $loader->load(__DIR__ . "/Controllers");
}
/**
* match the current url against the routes.
- * also add preflight CORS headers on OPTIONS requests.
+ * also add CORS headers.
*/
public function run(): Response
{
$request = Request::createFromGlobals();
$response = new Response();
- $response->headers->add([
+ $corsHeaders = [
"Access-Control-Allow-Origin" => "*",
"Access-Control-Allow-Methods" => "GET, POST, PUT, DELETE, OPTIONS, PATCH, HEAD",
"Access-Control-Allow-Headers" => "X-Requested-With, Content-Type, Authorization",
- ]);
+ ];
+ // handle OPTIONS
if ($request->isMethod("OPTIONS")) {
+ $response->headers->add($corsHeaders);
+
return $response;
}
@@ -92,18 +101,9 @@ class Router
);
}
- return $response;
- }
+ // add cors headers to all responses
+ $response->headers->add($corsHeaders);
- /**
- * add routes from the routes file
- */
- private function addRoutes(): void
- {
- $routesClientServer = include_once(__DIR__ . "/routes_client_server.php");
- $routesClientServer($this->configurator);
-
- $routesServerServer = include_once(__DIR__ . "/routes_server_server.php");
- $routesServerServer($this->configurator);
+ return $response;
}
}
diff --git a/src/Router/routes_client_server.php b/src/Router/routes_client_server.php
deleted file mode 100644
index e888782..0000000
--- a/src/Router/routes_client_server.php
+++ /dev/null
@@ -1,116 +0,0 @@
-<?php
-
-namespace App\Router;
-
-use App\Controllers\AccountController;
-use App\Controllers\KeyController;
-use App\Controllers\LoginController;
-use App\Controllers\RoomController;
-use App\Controllers\ServerDiscoveryController;
-use App\Controllers\ServerImplementationController;
-use App\Controllers\SyncController;
-use App\Controllers\UserController;
-use Symfony\Component\Routing\Loader\Configurator\RouteConfigurator;
-
-return function (RouteConfigurator $routes): void
-{
- $routes
- ->add("well_known_matrix_client", "/.well-known/matrix/client")
- ->controller([ServerDiscoveryController::class, "client"])
- ->methods(["GET"]);
-
- $routes
- ->add("well_known_matrix_support", "/.well-known/matrix/support")
- ->controller([ServerDiscoveryController::class, "support"])
- ->methods(["GET"]);
-
- $routes
- ->add("matrix_client_versions", "/_matrix/client/versions")
- ->controller([ServerImplementationController::class, "versions"])
- ->methods(["GET"]);
-
- $supportedLoginTypes = [LoginController::class, "supportedLoginTypes"];
- $routes
- ->add("matrix_client_r0_login_types", "/_matrix/client/r0/login")
- ->controller($supportedLoginTypes)
- ->methods(["GET"]);
- $routes
- ->add("matrix_client_v3_login_types", "/_matrix/client/v3/login")
- ->controller($supportedLoginTypes)
- ->methods(["GET"]);
-
- $routes
- ->add("matrix_client_r0_login", "/_matrix/client/r0/login")
- ->controller([LoginController::class, "login"])
- ->methods(["POST"]);
-
- $routes
- ->add("matrix_client_v3_login", "/_matrix/client/v3/login")
- ->controller([LoginController::class, "login"])
- ->methods(["POST"]);
-
- $routes
- ->add("matrix_client_v3_keys_upload", "/_matrix/client/v3/keys/upload")
- ->controller([KeyController::class, "upload"])
- ->methods(["POST"]);
-
- $routes
- ->add("matrix_client_r0_keys_upload", "/_matrix/client/r0/keys/upload")
- ->controller([KeyController::class, "upload"])
- ->methods(["POST"]);
-
- $routes
- ->add("matrix_client_v3_sync", "/_matrix/client/v3/sync")
- ->controller([SyncController::class, "sync"])
- ->methods(["GET"]);
-
- $routes
- ->add("matrix_client_r0_sync", "/_matrix/client/r0/sync")
- ->controller([SyncController::class, "sync"])
- ->methods(["GET"]);
-
- $routes
- ->add("matrix_client_v3_refresh", "/_matrix/client/v3/refresh")
- ->controller([KeyController::class, "refresh"])
- ->methods(["POST"]);
-
- $routes
- ->add("matrix_client_v3_directory_room_alias_get", "/_matrix/client/v3/directory/room/{roomAlias}")
- ->controller([RoomController::class, "resolveAlias"])
- ->methods(["GET"]);
-
- $routes
- ->add("matrix_client_v3_account_whoami", "/_matrix/client/v3/account/whoami")
- ->controller([AccountController::class, "whoami"])
- ->methods(["GET"]);
-
- $routes
- ->add("matrix_client_v3_rooms_id_send_event_transaction", "/_matrix/client/v3/rooms/{roomId}/send/{eventType}/{txnId}")
- ->controller([RoomController::class, "send"])
- ->methods(["PUT"]);
-
- $routes
- ->add("matrix_client_r0_user_id_filter", "/_matrix/client/r0/user/{userId}/filter")
- ->controller([UserController::class, "uploadFilter"])
- ->methods(["POST"]);
-
- $routes
- ->add("matrix_client_v3_user_id_filter", "/_matrix/client/v3/user/{userId}/filter")
- ->controller([UserController::class, "uploadFilter"])
- ->methods(["POST"]);
-
- $routes
- ->add("matrix_client_v3_room_create", "/_matrix/client/v3/createRoom")
- ->controller([RoomController::class, "createRoom"])
- ->methods(["POST"]);
-
- $routes
- ->add("matrix_client_v3_rooms_id_read_markers", "/_matrix/client/v3/rooms/{roomId}/read_markers")
- ->controller([RoomController::class, "readMarkers"])
- ->methods(["POST"]);
-
- $routes
- ->add("matrix_client_v3_rooms_id_messages", "/_matrix/client/v3/rooms/{roomId}/messages")
- ->controller([RoomController::class, "getMessages"])
- ->methods(["GET"]);
-};
diff --git a/src/Router/routes_server_server.php b/src/Router/routes_server_server.php
deleted file mode 100644
index 2e85a17..0000000
--- a/src/Router/routes_server_server.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-namespace App\Router;
-
-use App\Controllers\ServerDiscoveryController;
-use App\Controllers\ServerImplementationController;
-use Symfony\Component\Routing\Loader\Configurator\RouteConfigurator;
-
-return function (RouteConfigurator $routes): void
-{
- $routes
- ->add("well_known_matrix_server", "/.well-known/matrix/server")
- ->controller([ServerDiscoveryController::class, "server"])
- ->methods(["GET"]);
-
- $routes
- ->add("matrix_federation_version", "/_matrix/federation/v1/version")
- ->controller([ServerImplementationController::class, "version"])
- ->methods(["GET"]);
-
- # /_matrix/key/v2/server
- # /_matrix/key/v2/query
- # /_matrix/key/v2/query/{serverName}
-};