summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--matrix-specification/Data/Capabilities.php44
-rw-r--r--matrix-specification/Data/Capability/BooleanCapability.php18
-rw-r--r--matrix-specification/Data/Capability/ProfileFieldsCapability.php26
-rw-r--r--matrix-specification/Data/Capability/RoomVersionsCapability.php23
-rw-r--r--migrations/20250819.php37
-rw-r--r--src/App.php9
-rw-r--r--src/Controllers/Client/ClientController.php62
-rw-r--r--src/Controllers/Client/KeyController.php85
-rw-r--r--src/Controllers/Client/MediaController.php23
-rwxr-xr-xsrc/Controllers/Client/RoomController.php45
-rwxr-xr-xsrc/Controllers/Client/UserController.php52
-rw-r--r--src/Models/RoomEvent.php2
-rw-r--r--src/Models/User.php27
-rw-r--r--src/Router.php2
-rw-r--r--src/Support/Logger.php11
15 files changed, 439 insertions, 27 deletions
diff --git a/matrix-specification/Data/Capabilities.php b/matrix-specification/Data/Capabilities.php
new file mode 100644
index 0000000..3aa54a7
--- /dev/null
+++ b/matrix-specification/Data/Capabilities.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Matrix\Data;
+
+use Matrix\Data\Capability\BooleanCapability;
+use Matrix\Data\Capability\ProfileFieldsCapability;
+use Matrix\Data\Capability\RoomVersionsCapability;
+
+class Capabilities implements \JsonSerializable
+{
+ /**
+ * @param array<string, mixed> $otherProperties
+ */
+ public function __construct(
+ private ?BooleanCapability $threePidChanges = null,
+ private ?BooleanCapability $changePassword = null,
+ private ?BooleanCapability $getLoginToken = null,
+ private ?ProfileFieldsCapability $profileFields = null,
+ private ?RoomVersionsCapability $roomVersions = null,
+ private ?BooleanCapability $setAvatarUrl = null,
+ private ?BooleanCapability $setDisplayname = null,
+ private ?array $otherProperties = null,
+ )
+ {}
+
+ public function jsonSerialize(): array
+ {
+ $data = [
+ "m.3pid_changes" => $this->threePidChanges,
+ "m.change_password" => $this->changePassword,
+ "m.get_login_token" => $this->getLoginToken,
+ "m.profile_fields" => $this->profileFields,
+ "m.room_versions" => $this->roomVersions,
+ "m.set_avatar_url" => $this->setAvatarUrl,
+ "m.set_displayname" => $this->setDisplayname,
+ ];
+
+ if (! empty($this->otherProperties)) {
+ $data += $this->otherProperties;
+ }
+
+ return array_filter($data, fn ($value) => ! is_null($value));
+ }
+}
diff --git a/matrix-specification/Data/Capability/BooleanCapability.php b/matrix-specification/Data/Capability/BooleanCapability.php
new file mode 100644
index 0000000..13cb4de
--- /dev/null
+++ b/matrix-specification/Data/Capability/BooleanCapability.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Matrix\Data\Capability;
+
+class BooleanCapability implements \JsonSerializable
+{
+ public function __construct(
+ private bool $enabled,
+ )
+ {}
+
+ public function jsonSerialize(): array
+ {
+ return [
+ "enabled" => $this->enabled,
+ ];
+ }
+}
diff --git a/matrix-specification/Data/Capability/ProfileFieldsCapability.php b/matrix-specification/Data/Capability/ProfileFieldsCapability.php
new file mode 100644
index 0000000..91b0fc4
--- /dev/null
+++ b/matrix-specification/Data/Capability/ProfileFieldsCapability.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace Matrix\Data\Capability;
+
+class ProfileFieldsCapability implements \JsonSerializable
+{
+ /**
+ * @param string[] $allowed
+ * @param string[] $disallowed
+ */
+ public function __construct(
+ private bool $enabled,
+ private ?array $allowed = null,
+ private ?array $disallowed = null,
+ )
+ {}
+
+ public function jsonSerialize(): array
+ {
+ return array_filter([
+ "allowed" => $this->allowed,
+ "disallowed" => $this->disallowed,
+ "enabled" => $this->enabled,
+ ], fn ($value) => ! is_null($value));
+ }
+}
diff --git a/matrix-specification/Data/Capability/RoomVersionsCapability.php b/matrix-specification/Data/Capability/RoomVersionsCapability.php
new file mode 100644
index 0000000..d2a5cd7
--- /dev/null
+++ b/matrix-specification/Data/Capability/RoomVersionsCapability.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Matrix\Data\Capability;
+
+class RoomVersionsCapability implements \JsonSerializable
+{
+ /**
+ * @param array<string, string> $available
+ */
+ public function __construct(
+ private array $available,
+ private string $default,
+ )
+ {}
+
+ public function jsonSerialize(): array
+ {
+ return [
+ "available" => $this->available,
+ "default" => $this->default,
+ ];
+ }
+}
diff --git a/migrations/20250819.php b/migrations/20250819.php
index b635201..3476081 100644
--- a/migrations/20250819.php
+++ b/migrations/20250819.php
@@ -54,7 +54,7 @@ Database::getInstance()->query(<<<SQL
"name" varchar(255) not null,
- "version" integer not null
+ "version" text not null
);
SQL);
@@ -96,7 +96,7 @@ SQL);
Database::getInstance()->query(<<<SQL
create table if not exists "filters" (
- "id" varchar(255) primary key,
+ "id" varchar(255) not null,
"account_data" jsonb,
"event_fields" jsonb,
"event_format" varchar(255),
@@ -105,6 +105,39 @@ Database::getInstance()->query(<<<SQL
"user_id" varchar(255) not null,
+ primary key (id, user_id),
foreign key (user_id) references users(id)
);
SQL);
+
+Database::getInstance()->query(<<<SQL
+ create table if not exists "device_keys" (
+ "supported_algorithms" json not null,
+ "keys" json not null,
+ "signatures" json not null,
+
+ "user_id" text not null,
+ "device_id" text not null,
+
+ foreign key (user_id, device_id) references devices(user_id, id)
+ );
+SQL);
+
+Database::getInstance()->query(<<<SQL
+ create table if not exists "one_time_keys" (
+ "id" text not null,
+
+ "key" text not null,
+ "algorithm" text not null,
+
+ "signature_key" text not null,
+ "signature_algorithm" text not null,
+
+ "is_fallback" bool not null default false,
+
+ "user_id" text not null,
+ "device_id" text not null,
+
+ foreign key (user_id, device_id) references devices(user_id, id)
+ );
+SQL);
diff --git a/src/App.php b/src/App.php
index 33f71ef..e2376b1 100644
--- a/src/App.php
+++ b/src/App.php
@@ -6,8 +6,12 @@ use Symfony\Component\Dotenv\Dotenv;
class App
{
+ private static float $executionStartTime;
+
public function __construct()
{
+ self::$executionStartTime = microtime(true);
+
$dotenv = new Dotenv();
$dotenv->load(dirname(__DIR__) . "/.env");
}
@@ -16,4 +20,9 @@ class App
{
Router::getInstance()->run()->send();
}
+
+ public static function getExectionTime(): float
+ {
+ return microtime(true) - self::$executionStartTime;
+ }
}
diff --git a/src/Controllers/Client/ClientController.php b/src/Controllers/Client/ClientController.php
index c9eb1fa..cdce91e 100644
--- a/src/Controllers/Client/ClientController.php
+++ b/src/Controllers/Client/ClientController.php
@@ -2,6 +2,7 @@
namespace App\Controllers\Client;
+use App\App;
use App\Database;
use App\Errors\AppException;
use App\Errors\ErrorResponse;
@@ -16,6 +17,7 @@ use App\Support\Logger;
use App\Support\Parser;
use App\Support\RequestValidator;
use Matrix\Data\AccountData;
+use Matrix\Data\Capabilities;
use Matrix\Data\DeviceLists;
use Matrix\Data\LoginFlow;
use Matrix\Data\Presence;
@@ -208,7 +210,7 @@ class ClientController
#[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", "");
@@ -216,7 +218,7 @@ class ClientController
$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);
+ $useStateAfter = $request->query->get("use_state_after", false) ?: $request->query->get("org.matrix.msc4222.use_state_after", false);
if (! empty($filter)) {
if (str_starts_with($filter, "{")) {
@@ -226,6 +228,18 @@ class ClientController
}
}
+ // device one time keys count
+ $deviceOneTimeKeysCount = Database::getInstance()
+ ->query("select count(*) from one_time_keys where user_id=:user_id and device_id=:device_id", [
+ "user_id" => $user->getId(),
+ "device_id" => $user->getDeviceId(),
+ ])
+ ->fetchColumn();
+ $deviceOneTimeKeysCount = [
+ "signed_curve25519" => $deviceOneTimeKeysCount,
+ ];
+
+ // rooms
$rooms = Database::getInstance()->query(<<<SQL
select * from rooms
left join room_memberships
@@ -240,20 +254,24 @@ class ClientController
$knockedRooms = [];
$leftRooms = [];
+ $nextBatch = (new \DateTime())->format("U");
+
foreach ($rooms as $room) {
$events = Database::getInstance()->query(<<<SQL
select * from room_events
where room_id = :room_id
+ and origin_server_timestamp > to_timestamp(:since)
SQL, [
"room_id" => $room["room_id"],
#"limit" => ($filter["room"]["timeline"]["limit"] ?? false) ? "limit " . $filter["room"]["timeline"]["limit"] : "",
+ "since" => (empty($since) ? \DateTime::createFromTimestamp(0) : \DateTime::createFromTimestamp($since))->format("U"),
])->fetchAll();
- if ($since === "" && MembershipState::tryFrom($room["state"]) === MembershipState::JOIN) {
+ if (! empty($events) && MembershipState::tryFrom($room["state"]) === MembershipState::JOIN) {
$joinedRooms[$room["room_id"]] = new JoinedRoom(
accountData: new AccountData([]),
ephemeral: new Ephemeral([]),
- state: new State([]),
+ state: new State(array_map([RoomEvent::class, "transformEvent"], $events)),
summary: new RoomSummary(
heroes: [],
invitedMemberCount: 0,
@@ -268,18 +286,25 @@ class ClientController
unreadThreadNotifications: [],
);
}
+
+ if (! empty($events)) {
+ $newestEvent = RoomEvent::transformEvent($events[array_key_last($events)]);
+ $nextBatch = \DateTime::createFromTimestamp($newestEvent->getOriginServerTimestamp())->format("U");
+ }
+ }
+
+ if (($timeout / 1000) > App::getExectionTime()) {
+ sleep(intval(($timeout / 1000) - App::getExectionTime()));
}
return new JsonResponse(new ClientSyncGetResponse(
- nextBatch: "1",
+ nextBatch: $nextBatch,
accountData: new AccountData([]),
deviceLists: new DeviceLists([], []),
- deviceOneTimeKeysCount: [
- "signed_curve25519" => 10,
- ],
+ deviceOneTimeKeysCount: $deviceOneTimeKeysCount,
presence: new Presence([
new PresenceEvent(
@@ -393,4 +418,25 @@ class ClientController
),
]);
}
+
+ #[Route(path: "/_matrix/client/v3/capabilities", methods: ["GET"])]
+ public function capabilities(Request $request): Response
+ {
+ $user = User::authenticateWithRequest($request);
+
+ return new JsonResponse(new Capabilities());
+ }
+
+ #[Route(path: "/_matrix/client/v3/voip/turnServer", methods: ["GET"])]
+ public function voipTurnServer(Request $request): Response
+ {
+ $user = User::authenticateWithRequest($request);
+
+ return new JsonResponse([
+ "password" => "",
+ "ttl" => 86400,
+ "uris" => [],
+ "username" => "",
+ ]);
+ }
}
diff --git a/src/Controllers/Client/KeyController.php b/src/Controllers/Client/KeyController.php
index b9ae61f..5e3245b 100644
--- a/src/Controllers/Client/KeyController.php
+++ b/src/Controllers/Client/KeyController.php
@@ -2,6 +2,7 @@
namespace App\Controllers\Client;
+use App\Database;
use App\Models\User;
use App\Support\RequestValidator;
use Matrix\Responses\ClientKeysUploadPostResponse;
@@ -12,6 +13,76 @@ use Symfony\Component\Routing\Attribute\Route;
class KeyController
{
+ #[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();
+
+ if (! empty($body["device_keys"])) {
+ if ($body["device_keys"]["user_id"] !== $user->getId()) {}
+ if ($body["device_keys"]["user_id"] !== $user->getDeviceId()) {}
+
+ Database::getInstance()->query(<<<SQL
+ insert into device_keys (supported_algorithms, keys, signatures, user_id, device_id)
+ values (:supported_algorithms, :keys, :signatures, :user_id, :device_id)
+ SQL, [
+ "supported_algorithms" => json_encode($body["device_keys"]["algorithms"]),
+ "keys" => json_encode($body["device_keys"]["keys"]),
+ "signatures" => json_encode($body["device_keys"]["signatures"]),
+ "user_id" => $user->getId(),
+ "device_id" => $user->getDeviceId(),
+ ]);
+ }
+
+ $oneTimeKeys = $body["one_time_keys"];
+ if (! empty($body["fallback_keys"])) {
+ $oneTimeKeys += $body["fallback_keys"];
+ }
+
+ foreach ($oneTimeKeys as $identifier => $object) {
+ $identifierParts = explode(":", $identifier);
+
+ $algorithm = $identifierParts[0];
+ $id = $identifierParts[1];
+
+ $signatures = array_values($object["signatures"])[0];
+ $signatureIdentifier = array_keys($signatures)[0];
+ $signatureAlgorithm = explode(":", $signatureIdentifier)[0];
+ $signatureKey = array_values($signatures)[0];
+
+ $deviceId = explode(":", $signatureIdentifier)[1];
+
+ Database::getInstance()->query(<<<SQL
+ insert into one_time_keys (id, key, algorithm, signature_key, signature_algorithm, is_fallback, user_id, device_id)
+ values (:id, :key, :algorithm, :signature_key, :signature_algorithm, :is_fallback, :user_id, :device_id)
+ SQL, [
+ "id" => $id,
+ "key" => $object["key"],
+ "algorithm" => $algorithm,
+ "signature_key" => $signatureKey,
+ "signature_algorithm" => $signatureAlgorithm,
+ "is_fallback" => ($object["fallback"] ?? false) ?: 0,
+ "user_id" => $user->getId(),
+ "device_id" => $deviceId,
+ ]);
+ }
+
+ # TODO: do that per algorithm
+ $currentCountOneTimeKeys = Database::getInstance()
+ ->query("select count(id) from one_time_keys where user_id=:userId and is_fallback=false", [
+ "userId" => $user->getId(),
+ ])
+ ->fetchColumn();
+
+ return new JsonResponse(new ClientKeysUploadPostResponse([
+ #"curve25519" => 0,
+ "signed_curve25519" => $currentCountOneTimeKeys,
+ ]));
+ }
+
#[Route(path: "/_matrix/client/v3/keys/query", methods: ["POST"])]
public function query(Request $request): Response
{
@@ -29,19 +100,13 @@ class KeyController
]);
}
- #[Route(path: "/_matrix/client/r0/keys/upload", methods: ["POST"])]
- #[Route(path: "/_matrix/client/v3/keys/upload", methods: ["POST"])]
- public function upload(Request $request): Response
+ #[Route(path: "/_matrix/client/v3/keys/claim", methods: ["POST"])]
+ public function claim(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"]),
- ]));
+
+ return new JsonResponse();
}
}
diff --git a/src/Controllers/Client/MediaController.php b/src/Controllers/Client/MediaController.php
new file mode 100644
index 0000000..73f4aa0
--- /dev/null
+++ b/src/Controllers/Client/MediaController.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace App\Controllers\Client;
+
+use App\Models\User;
+use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Routing\Attribute\Route;
+
+class MediaController
+{
+ #[Route(path: "/_matrix/media/v3/config", methods: ["GET"])]
+ #[Route(path: "/_matrix/client/v1/media/config", methods: ["GET"])]
+ public function mediaConfig(Request $request): Response
+ {
+ $user = User::authenticateWithRequest($request);
+
+ return new JsonResponse([
+ "m.upload.size" => 50000000,
+ ]);
+ }
+}
diff --git a/src/Controllers/Client/RoomController.php b/src/Controllers/Client/RoomController.php
index ec04a2f..07bad27 100755
--- a/src/Controllers/Client/RoomController.php
+++ b/src/Controllers/Client/RoomController.php
@@ -75,10 +75,11 @@ class RoomController
// create room
$roomId = Id::generateRoomId();
Database::getInstance()->query(<<<SQL
- insert into rooms (id, name) values (:id, :name)
+ insert into rooms (id, name, version) values (:id, :name, :version)
SQL, [
"id" => $roomId,
"name" => $roomAliasName, # "#$roomAliasName:$_ENV[DOMAIN]",
+ "version" => $roomVersion,
]);
$roomCreateEvent = new RoomEvent(new CreateEvent(
@@ -198,8 +199,47 @@ class RoomController
"servers" => [],
]);
}
+
+ #[Route(path: "/_matrix/client/v3/rooms/{roomId}/join", methods: ["POST"])]
+ public function joinRoomId(Request $request): Response
+ {
+ return new JsonResponse();
+ }
+
+ #[Route(path: "/_matrix/client/v3/join/{roomIdOrAlias}", methods: ["POST"])]
+ public function joinRoomIdOrAlias(Request $request): Response
+ {
+ $user = User::authenticateWithRequest($request);
+ $body = json_decode($request->getContent(), true);
+ RequestValidator::validateJson();
+
+ $roomIdOrAlias = $request->attributes->get("roomIdOrAlias");
+
+ $via = $request->query->get("via", "");
+
+ $reason = $body["reason"] ?? "";
+ #$thirdPartySigned
+
+ /*
+ if (isRoomId()) {
+ $request->attributes->set("roomId", $roomIdOrAlias);
+ return $this->joinRoomId($request);
+ }
+ */
+
+ # TODO: db query id or name = value
+ $roomId = Database::getInstance()
+ ->query("select id from rooms where id=:value or name=:value", [
+ "value" => $roomIdOrAlias,
+ ])
+ ->fetchColumn();
+
+ return new JsonResponse([
+ "room_id" => $roomId,
+ ]);
+ }
- #[Route(path: "/_matrix/client/v3/{roomId}/messages", methods: ["GET"])]
+ #[Route(path: "/_matrix/client/v3/rooms/{roomId}/messages", methods: ["GET"])]
public function getMessages(Request $request): Response
{
$user = User::authenticateWithRequest($request);
@@ -282,7 +322,6 @@ class RoomController
sender: $user->getId(),
type: $eventType,
unsigned: new UnsignedData(
- age: 1234, # TODO
membership: MembershipState::JOIN,
),
));
diff --git a/src/Controllers/Client/UserController.php b/src/Controllers/Client/UserController.php
index 038caba..277ba78 100755
--- a/src/Controllers/Client/UserController.php
+++ b/src/Controllers/Client/UserController.php
@@ -3,10 +3,12 @@
namespace App\Controllers\Client;
use App\Database;
+use App\Errors\AppException;
use App\Errors\UnauthorizedError;
use App\Models\Device;
use App\Models\User;
use App\Support\RequestValidator;
+use Matrix\Enums\ErrorCode;
use Matrix\Responses\ClientAccountWhoamiGetResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -68,4 +70,54 @@ class UserController
"filter_id" => $filterId,
]);
}
+
+ #[Route(path: "/_matrix/client/v3/user/{userId}/{filter}/{filterId}", methods: ["GET"])]
+ public function getFilter(Request $request): Response
+ {
+ $user = User::authenticateWithRequest($request);
+
+ $userId = $request->attributes->get("userId");
+ $filterId = $request->attributes->get("filterId");
+
+ $filter = Database::getInstance()
+ ->query("select * from filters where id=:id and user_id=:user_id", [
+ "id" => $filterId,
+ "user_id" => $userId,
+ ])
+ ->fetch();
+
+ if (empty($filter)) {
+ throw new AppException(
+ ErrorCode::NOT_FOUND,
+ "Unknown filter.",
+ Response::HTTP_NOT_FOUND
+ );
+ }
+
+ return new JsonResponse([
+ "account_data" => json_decode($filter["account_data"] ?? ""),
+ "event_fields" => json_decode($filter["event_fields"] ?? ""),
+ "event_format" => $filter["event_format"] ?? "",
+ "presence" => json_decode($filter["presence"] ?? ""),
+ "room" => json_decode($filter["room"] ?? ""),
+ ]);
+ }
+
+ #[Route(path: "/_matrix/client/v3/profile/{userId}", methods: ["GET"])]
+ public function getProfile(Request $request): Response
+ {
+ $userId = $request->attributes->get("userId");
+
+ $user = Database::getInstance()->query("select * from users where id=:user_id", ["user_id" => $userId])->fetch();
+
+ if (empty($user)) {
+ throw new AppException(
+ ErrorCode::NOT_FOUND,
+ "There is no profile information for this user or this user does not exist.",
+ Response::HTTP_NOT_FOUND
+ );
+ }
+
+ return new JsonResponse();
+ }
}
diff --git a/src/Models/RoomEvent.php b/src/Models/RoomEvent.php
index ce74cac..11d74b0 100644
--- a/src/Models/RoomEvent.php
+++ b/src/Models/RoomEvent.php
@@ -22,7 +22,7 @@ class RoomEvent
{
$rowUnsigned = json_decode($row["unsigned"], true);
$unsigned = new UnsignedData(
- age: $row["age"] ?? null,
+ age: $row["age"] ?? ((time() - new \DateTime($row["origin_server_timestamp"])->getTimestamp()) * 1000),
membership: $row["membership"] ?? null,
previousContent: $row["previous_content"] ?? null,
redactedBecause: $row["redacted_because"] ?? null,
diff --git a/src/Models/User.php b/src/Models/User.php
index 4c016ad..a30bee0 100644
--- a/src/Models/User.php
+++ b/src/Models/User.php
@@ -12,6 +12,8 @@ use Symfony\Component\HttpFoundation\Response;
class User implements ConnectsToDatabase
{
+ private string $deviceId;
+
public function __construct(
private string $id,
private string $name,
@@ -62,7 +64,7 @@ class User implements ConnectsToDatabase
public static function fetchWithAccessToken(string $accessToken): ?self
{
$row = Database::getInstance()->query(<<<SQL
- select users.* from users left join tokens on tokens.user_id = users.id where tokens.access_token=:access_token
+ select users.*, tokens.device_id from users left join tokens on tokens.user_id = users.id where tokens.access_token=:access_token
SQL, [
"access_token" => $accessToken,
])->fetch();
@@ -71,7 +73,10 @@ class User implements ConnectsToDatabase
return null;
}
- return self::fromDatabase($row);
+ $user = self::fromDatabase($row);
+ $user->setDeviceId($row["device_id"]);
+
+ return $user;
}
public static function new(string $id, string $name): self
@@ -137,9 +142,25 @@ class User implements ConnectsToDatabase
return $this->name;
}
+ public function setDeviceId(string $id): void
+ {
+ $this->deviceId = $id;
+ }
+
+ public function getDeviceId(): string
+ {
+ return $this->deviceId;
+ }
+
public function fetchDevice(string $id): ?Device
{
- return Device::fetch($id, $this->id);
+ $device = Device::fetch($id, $this->id);
+
+ if ($device) {
+ $this->setDeviceId($device->getId());
+ }
+
+ return $device;
}
/**
diff --git a/src/Router.php b/src/Router.php
index ab0c37c..12aa8fe 100644
--- a/src/Router.php
+++ b/src/Router.php
@@ -101,6 +101,8 @@ class Router
);
}
+ Logger::logResponseToFile($request, $response);
+
// add cors headers to all responses
$response->headers->add($corsHeaders);
diff --git a/src/Support/Logger.php b/src/Support/Logger.php
index b01af11..2712a6e 100644
--- a/src/Support/Logger.php
+++ b/src/Support/Logger.php
@@ -4,6 +4,7 @@ namespace App\Support;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
class Logger implements LoggerInterface
{
@@ -28,6 +29,16 @@ class Logger implements LoggerInterface
);
}
+ public static function logResponseToFile(Request $request, Response $response): void
+ {
+ $basePath = dirname(dirname(__DIR__)) . "/.cache/log/" . str_replace("/", "_", $request->getPathInfo());
+
+ file_put_contents(
+ $basePath . "-response.json",
+ json_encode(json_decode($response->getContent()), JSON_PRETTY_PRINT)
+ );
+ }
+
public function emergency($message, array $context = []): void
{
}