appwrite/src/Appwrite/Messaging/Adapter/Realtime.php

554 lines
21 KiB
PHP
Raw Normal View History

2021-06-28 14:34:28 +00:00
<?php
namespace Appwrite\Messaging\Adapter;
use Appwrite\Messaging\Adapter as MessagingAdapter;
use Appwrite\PubSub\Adapter\Pool as PubSubPool;
use Appwrite\Utopia\Database\RuntimeQuery;
2024-03-06 17:34:21 +00:00
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Query as QueryException;
2022-12-14 15:42:25 +00:00
use Utopia\Database\Helpers\ID;
2022-12-14 16:04:06 +00:00
use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
2021-06-28 14:34:28 +00:00
class Realtime extends MessagingAdapter
2021-06-28 14:34:28 +00:00
{
/**
* Connection Tree
2022-05-23 14:54:50 +00:00
*
2022-05-10 13:32:16 +00:00
* [CONNECTION_ID] ->
2021-06-28 14:34:28 +00:00
* 'projectId' -> [PROJECT_ID]
* 'roles' -> [ROLE_x, ROLE_Y]
* 'channels' -> [CHANNEL_NAME_X, CHANNEL_NAME_Y, CHANNEL_NAME_Z]
*/
public array $connections = [];
/**
* Subscription Tree
2022-05-10 13:32:16 +00:00
*
* [PROJECT_ID] ->
* [ROLE_X] ->
* [CHANNEL_NAME_X] ->
* [CONNECTION_ID] ->
2026-02-05 05:47:49 +00:00
* [SUB_ID] -> ['strings' => [...], 'compiled' => [...]]
*
2026-02-05 05:47:49 +00:00
* Each subscription ID maps to query strings (for metadata) and pre-compiled query filters.
* Within a subscription: AND logic (all queries must match)
* Across subscriptions: OR logic (any subscription matching = send event)
2021-06-28 14:34:28 +00:00
*/
public array $subscriptions = [];
2026-02-04 09:00:24 +00:00
private ?PubSubPool $pubSubPool = null;
2026-02-04 09:00:24 +00:00
/**
* Get the PubSubPool instance, initializing it lazily if needed.
* This allows unit tests to work without requiring the global $register.
*
* @return PubSubPool
*/
private function getPubSubPool(): PubSubPool
{
2026-02-04 09:00:24 +00:00
if ($this->pubSubPool === null) {
global $register;
$this->pubSubPool = new PubSubPool($register->get('pools')->get('pubsub'));
}
return $this->pubSubPool;
}
2021-06-28 14:34:28 +00:00
/**
* Adds a subscription with a specific subscription ID.
2022-05-10 13:32:16 +00:00
*
* @param string $projectId
* @param mixed $identifier Connection ID
* @param string $subscriptionId Unique subscription ID
* @param array $roles User roles
* @param array $channels Channels to subscribe to (array of channel names)
* @param array $queryGroup Array of Query objects for this subscription (AND logic within subscription)
2022-05-10 13:32:16 +00:00
* @return void
2021-06-28 14:34:28 +00:00
*/
public function subscribe(string $projectId, mixed $identifier, string $subscriptionId, array $roles, array $channels, array $queryGroup = []): void
2021-06-28 14:34:28 +00:00
{
if (!isset($this->subscriptions[$projectId])) { // Init Project
$this->subscriptions[$projectId] = [];
}
2026-02-05 05:47:49 +00:00
$strings = [];
if (empty($queryGroup)) {
2026-02-05 05:47:49 +00:00
$strings[] = Query::select(['*'])->toString();
} else {
foreach ($queryGroup as $query) {
2026-02-05 05:47:49 +00:00
$strings[] = $query->toString();
}
}
2026-02-05 05:47:49 +00:00
$data = [
'strings' => $strings,
'compiled' => RuntimeQuery::compile($queryGroup),
2026-02-04 06:27:57 +00:00
];
2021-06-28 14:34:28 +00:00
foreach ($roles as $role) {
if (!isset($this->subscriptions[$projectId][$role])) {
2021-06-28 14:34:28 +00:00
$this->subscriptions[$projectId][$role] = [];
}
foreach ($channels as $channel) {
if (!isset($this->subscriptions[$projectId][$role][$channel])) {
$this->subscriptions[$projectId][$role][$channel] = [];
}
if (!isset($this->subscriptions[$projectId][$role][$channel][$identifier])) {
$this->subscriptions[$projectId][$role][$channel][$identifier] = [];
}
2026-02-05 05:47:49 +00:00
$this->subscriptions[$projectId][$role][$channel][$identifier][$subscriptionId] = $data;
2021-06-28 14:34:28 +00:00
}
}
// Update connection info
2021-08-19 08:24:41 +00:00
$this->connections[$identifier] = [
2021-06-28 14:34:28 +00:00
'projectId' => $projectId,
'roles' => $roles,
'channels' => $channels
2021-06-28 14:34:28 +00:00
];
}
/**
2026-02-03 06:13:23 +00:00
* Get subscription metadata for a connection.
* Retrieves subscription data including channels and queries directly from the subscriptions tree.
*
* @param mixed $connection Connection ID
* @return array Array of [subscriptionId => ['channels' => string[], 'queries' => string[]]]
*/
public function getSubscriptionMetadata(mixed $connection): array
2026-02-03 06:13:23 +00:00
{
$projectId = $this->connections[$connection]['projectId'] ?? null;
$roles = $this->connections[$connection]['roles'] ?? [];
$channels = $this->connections[$connection]['channels'] ?? [];
if (!$projectId || empty($roles) || empty($channels)) {
return [];
}
$subscriptions = [];
// Extract subscription data from subscriptions tree
foreach ($roles as $role) {
if (!isset($this->subscriptions[$projectId][$role])) {
continue;
}
foreach ($channels as $channel) {
if (!isset($this->subscriptions[$projectId][$role][$channel][$connection])) {
continue;
}
2026-02-05 05:47:49 +00:00
foreach ($this->subscriptions[$projectId][$role][$channel][$connection] as $subscriptionId => $data) {
if (!isset($subscriptions[$subscriptionId])) {
$subscriptions[$subscriptionId] = [
2026-02-03 06:13:23 +00:00
'channels' => [],
2026-02-05 05:47:49 +00:00
'queries' => $data['strings'] ?? []
2026-02-03 06:13:23 +00:00
];
}
2026-02-05 05:47:49 +00:00
if (!\in_array($channel, $subscriptions[$subscriptionId]['channels'])) {
$subscriptions[$subscriptionId]['channels'][] = $channel;
2026-02-03 06:13:23 +00:00
}
}
}
}
return $subscriptions;
2021-06-28 14:34:28 +00:00
}
/**
* Removes all subscriptions for a connection.
2022-05-23 14:54:50 +00:00
*
2021-06-28 14:34:28 +00:00
* @param mixed $connection
2022-05-10 13:32:16 +00:00
* @return void
2021-06-28 14:34:28 +00:00
*/
public function unsubscribe(mixed $connection): void
{
$projectId = $this->connections[$connection]['projectId'] ?? '';
$roles = $this->connections[$connection]['roles'] ?? [];
$channels = $this->connections[$connection]['channels'] ?? [];
2021-06-28 14:34:28 +00:00
foreach ($roles as $role) {
foreach ($channels as $channel) {
unset($this->subscriptions[$projectId][$role][$channel][$connection]); // dropping connection will drop all subscriptions
2021-06-28 14:34:28 +00:00
if (empty($this->subscriptions[$projectId][$role][$channel])) {
unset($this->subscriptions[$projectId][$role][$channel]); // Remove channel when no connections
}
}
if (empty($this->subscriptions[$projectId][$role])) {
unset($this->subscriptions[$projectId][$role]); // Remove role when no channels
}
}
if (empty($this->subscriptions[$projectId])) { // Remove project when no roles
unset($this->subscriptions[$projectId]);
}
2026-02-03 06:13:23 +00:00
if (isset($this->connections[$connection])) {
unset($this->connections[$connection]);
}
2021-06-28 14:34:28 +00:00
}
/**
* Checks if Channel has a subscriber.
2022-05-10 13:32:16 +00:00
* @param string $projectId
* @param string $role
* @param string $channel
2021-07-13 15:18:02 +00:00
* @return bool
2021-06-28 14:34:28 +00:00
*/
public function hasSubscriber(string $projectId, string $role, string $channel = ''): bool
{
2021-07-13 15:18:02 +00:00
//TODO: look into moving it to an abstract class in the parent class
2021-06-28 14:34:28 +00:00
if (empty($channel)) {
return array_key_exists($projectId, $this->subscriptions)
&& array_key_exists($role, $this->subscriptions[$projectId]);
}
return array_key_exists($projectId, $this->subscriptions)
&& array_key_exists($role, $this->subscriptions[$projectId])
&& array_key_exists($channel, $this->subscriptions[$projectId][$role])
&& !empty($this->subscriptions[$projectId][$role][$channel]);
2021-06-28 14:34:28 +00:00
}
/**
2022-05-10 13:32:16 +00:00
* Sends an event to the Realtime Server
* @param string $projectId
* @param array $payload
* @param array $events
2022-05-10 13:32:16 +00:00
* @param array $channels
* @param array $roles
* @param array $options
* @return void
* @throws \Exception
2021-06-28 14:34:28 +00:00
*/
public function send(string $projectId, array $payload, array $events, array $channels, array $roles, array $options = []): void
2021-06-28 14:34:28 +00:00
{
2022-05-23 14:54:50 +00:00
if (empty($channels) || empty($roles) || empty($projectId)) {
return;
}
2021-06-30 11:36:58 +00:00
$permissionsChanged = array_key_exists('permissionsChanged', $options) && $options['permissionsChanged'];
$userId = array_key_exists('userId', $options) ? $options['userId'] : null;
2026-02-04 09:00:24 +00:00
$this->getPubSubPool()->publish('realtime', json_encode([
'project' => $projectId,
'roles' => $roles,
'permissionsChanged' => $permissionsChanged,
'userId' => $userId,
'data' => [
'events' => $events,
'channels' => $channels,
'timestamp' => DateTime::formatTz(DateTime::now()),
'payload' => $payload
]
]));
2021-06-28 14:34:28 +00:00
}
/**
* Identifies the receivers of all subscriptions, based on the permissions and event.
2022-05-10 13:32:16 +00:00
*
2021-06-28 14:34:28 +00:00
* Example of performance with an event with user:XXX permissions and with X users spread across 10 different channels:
2026-02-05 05:47:49 +00:00
* - 0.013 ms | 10 Connections / 100 Subscriptions
* - 0.14 ms | 100 Connections / 1,000 Subscriptions
* - 1.5 ms | 1,000 Connections / 10,000 Subscriptions
* - 15 ms | 10,000 Connections / 100,000 Subscriptions
2022-05-10 13:32:16 +00:00
*
2021-06-28 14:34:28 +00:00
* @param array $event
* @return array<int|string, array> Map of connection IDs to matched query groups
2021-06-28 14:34:28 +00:00
*/
public function getSubscribers(array $event): array
2021-06-28 14:34:28 +00:00
{
$receivers = [];
2026-02-05 05:47:49 +00:00
if (!isset($this->subscriptions[$event['project']])) {
return $receivers;
}
$payload = $event['data']['payload'] ?? [];
foreach ($this->subscriptions[$event['project']] as $role => $subscription) {
foreach ($event['data']['channels'] as $channel) {
if (
!\array_key_exists($channel, $this->subscriptions[$event['project']][$role])
|| (!\in_array($role, $event['roles']) && !\in_array(Role::any()->toString(), $event['roles']))
) {
continue;
}
foreach ($this->subscriptions[$event['project']][$role][$channel] as $id => $subscriptions) {
$matched = [];
foreach ($subscriptions as $subscriptionId => $data) {
$compiled = $data['compiled'] ?? ['type' => 'selectAll'];
$strings = $data['strings'] ?? [];
if (!empty(RuntimeQuery::filter($compiled, $payload))) {
$matched[$subscriptionId] = $strings;
2021-06-28 14:34:28 +00:00
}
2026-02-05 05:47:49 +00:00
}
if (!empty($matched)) {
if (!isset($receivers[$id])) {
$receivers[$id] = [];
}
$receivers[$id] += $matched;
2021-06-28 14:34:28 +00:00
}
}
2026-02-05 05:47:49 +00:00
break;
2021-06-28 14:34:28 +00:00
}
}
return $receivers;
2021-06-28 14:34:28 +00:00
}
2021-06-30 11:36:58 +00:00
/**
2022-05-10 13:32:16 +00:00
* Converts the channels from the Query Params into an array.
2021-06-30 11:36:58 +00:00
* Also renames the account channel to account.USER_ID and removes all illegal account channel variations.
2022-05-10 13:32:16 +00:00
* @param array $channels
* @param string $userId
2022-05-23 14:54:50 +00:00
* @return array
2021-06-30 11:36:58 +00:00
*/
2021-07-13 15:18:02 +00:00
public static function convertChannels(array $channels, string $userId): array
2021-06-30 11:36:58 +00:00
{
$channels = array_flip($channels);
foreach ($channels as $key => $value) {
switch (true) {
2025-05-07 08:41:28 +00:00
case str_starts_with($key, 'account.'):
2021-06-30 11:36:58 +00:00
unset($channels[$key]);
break;
case $key === 'account':
2021-07-13 15:18:02 +00:00
if (!empty($userId)) {
$channels['account.' . $userId] = $value;
2021-06-30 11:36:58 +00:00
}
break;
}
}
return $channels;
}
/**
* Constructs subscriptions from query parameters.
2026-02-03 06:13:23 +00:00
*
2026-02-05 05:47:49 +00:00
* @param array $channelNames
* @param callable $getQueryParam
* @return array [index => ['channels' => string[], 'queries' => Query[]]]
* @throws QueryException
*/
public static function constructSubscriptions(array $channelNames, callable $getQueryParam): array
{
2026-02-05 05:47:49 +00:00
$subscriptions = [];
foreach ($channelNames as $channel) {
2026-02-05 05:47:49 +00:00
$params = $getQueryParam($channel);
if ($params === null) {
if (!isset($subscriptions[0])) {
$subscriptions[0] = ['channels' => [], 'queries' => []];
}
2026-02-05 05:47:49 +00:00
$subscriptions[0]['channels'][] = $channel;
if (empty($subscriptions[0]['queries'])) {
$subscriptions[0]['queries'] = [Query::select(['*'])];
}
continue;
}
2026-02-05 05:47:49 +00:00
if (!is_array($params)) {
$params = [$params];
}
2026-02-05 05:47:49 +00:00
foreach ($params as $index => $slot) {
if (!isset($subscriptions[$index])) {
$subscriptions[$index] = ['channels' => [], 'queries' => []];
}
2026-02-05 05:47:49 +00:00
if (!in_array($channel, $subscriptions[$index]['channels'])) {
$subscriptions[$index]['channels'][] = $channel;
}
2026-02-05 05:47:49 +00:00
if (empty($subscriptions[$index]['queries'])) {
$raw = is_array($slot) ? $slot : [$slot];
$subscriptions[$index]['queries'] = self::convertQueries($raw);
}
}
}
2026-02-05 05:47:49 +00:00
return $subscriptions;
}
/**
* Converts the queries from the Query Params into an array.
* @param array|string $queries
* @return array
* @throws QueryException
*/
public static function convertQueries(mixed $queries): array
{
$queries = Query::parseQueries($queries);
$stack = $queries;
2026-02-05 05:47:49 +00:00
$allowed = implode(', ', RuntimeQuery::ALLOWED_QUERIES);
while (!empty($stack)) {
$query = array_pop($stack);
$method = $query->getMethod();
2026-02-05 05:47:49 +00:00
if (!in_array($method, RuntimeQuery::ALLOWED_QUERIES, true)) {
throw new QueryException(
2026-02-05 05:47:49 +00:00
"Query method '{$method}' is not supported in Realtime queries. Allowed: {$allowed}"
);
}
if ($method === Query::TYPE_SELECT) {
RuntimeQuery::validateSelectQuery($query);
}
if (in_array($method, [Query::TYPE_AND, Query::TYPE_OR], true)) {
2026-02-04 06:27:18 +00:00
\array_push($stack, ...$query->getValues());
}
}
return $queries;
}
2021-06-30 11:36:58 +00:00
/**
* Create channels array based on the event name and payload.
2021-12-06 12:03:12 +00:00
*
2022-05-10 13:32:16 +00:00
* @param string $event
* @param Document $payload
* @param Document|null $project
2024-09-09 08:52:37 +00:00
* @param Document|null $database
2025-05-09 09:17:01 +00:00
* @param Document|null $collection
2024-09-09 08:52:37 +00:00
* @param Document|null $bucket
2022-05-23 14:54:50 +00:00
* @return array
2024-09-09 08:52:37 +00:00
* @throws \Exception
2021-06-30 11:36:58 +00:00
*/
2026-02-03 04:19:37 +00:00
public static function fromPayload(string $event, Document $payload, ?Document $project = null, ?Document $database = null, ?Document $collection = null, ?Document $bucket = null): array
2021-06-30 11:36:58 +00:00
{
$channels = [];
2021-07-13 15:18:02 +00:00
$roles = [];
2021-06-30 11:36:58 +00:00
$permissionsChanged = false;
2021-12-06 12:03:12 +00:00
$projectId = null;
2022-05-10 13:25:49 +00:00
// TODO: add method here to remove all the magic index accesses
$parts = explode('.', $event);
2021-06-30 11:36:58 +00:00
switch ($parts[0]) {
case 'users':
$channels[] = 'account';
$channels[] = 'account.' . $parts[1];
2022-08-19 04:04:33 +00:00
$roles = [Role::user(ID::custom($parts[1]))->toString()];
2021-06-30 11:36:58 +00:00
break;
2023-03-10 12:20:24 +00:00
case 'rules':
2025-04-27 05:03:44 +00:00
case 'migrations':
2023-03-14 11:13:03 +00:00
$channels[] = 'console';
2024-09-30 14:32:50 +00:00
$channels[] = 'projects.' . $project->getId();
2023-03-14 11:13:03 +00:00
$projectId = 'console';
2023-03-10 12:20:24 +00:00
$roles = [Role::team($project->getAttribute('teamId'))->toString()];
break;
2024-10-07 14:58:34 +00:00
case 'projects':
$channels[] = 'console';
$channels[] = 'projects.' . $parts[1];
$projectId = 'console';
$roles = [Role::team($project->getAttribute('teamId'))->toString()];
break;
case 'teams':
if ($parts[2] === 'memberships') {
$permissionsChanged = $parts[4] ?? false;
$channels[] = 'memberships';
$channels[] = 'memberships.' . $parts[3];
} else {
$permissionsChanged = $parts[2] === 'create';
$channels[] = 'teams';
$channels[] = 'teams.' . $parts[1];
}
2022-08-19 04:04:33 +00:00
$roles = [Role::team(ID::custom($parts[1]))->toString()];
2021-06-30 11:36:58 +00:00
break;
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
case 'databases':
2025-05-07 08:41:28 +00:00
$resource = $parts[4] ?? '';
if (in_array($resource, ['columns', 'attributes', 'indexes'])) {
$channels[] = 'console';
2024-09-30 14:32:50 +00:00
$channels[] = 'projects.' . $project->getId();
$projectId = 'console';
2022-08-19 04:04:33 +00:00
$roles = [Role::team($project->getAttribute('teamId'))->toString()];
2025-05-07 08:41:28 +00:00
} elseif (in_array($resource, ['rows', 'documents'])) {
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
if ($database->isEmpty()) {
2025-05-07 05:03:54 +00:00
throw new \Exception('Database needs to be passed to Realtime for Document/Row events in the Database.');
Database layer (#3338) * database response model * database collection config * new database scopes * database service update * database execption codes * remove read write permission from database model * updating tests and fixing some bugs * server side tests are now passing * databases api * tests for database endpoint * composer update * fix error * formatting * formatting fixes * get database test * more updates to events and usage * more usage updates * fix delete type * fix test * delete database * more fixes * databaseId in attributes and indexes * more fixes * fix issues * fix index subquery * fix console scope and index query * updating tests as required * fix phpcs errors and warnings * updates to review suggestions * UI progress * ui updates and cleaning up * fix type * rework database events * update tests * update types * event generation fixed * events config updated * updating context to support multiple * realtime updates * fix ids * update context * validator updates * fix naming conflict * fix tests * fix lint errors * fix wprler and realtime tests * fix webhooks test * fix event validator and other tests * formatting fixes * removing leftover var_dumps * remove leftover comment * update usage params * usage metrics updates * update database usage * fix usage * specs update * updates to usage * fix UI and usage * fix lints * internal id fixes * fixes for internal Id * renaming services and related files * rename tests * rename doc link * rename readme * fix test name * tests: fixes for 0.15.x sync Co-authored-by: Torsten Dittmann <torsten.dittmann@googlemail.com>
2022-06-22 10:51:49 +00:00
}
2025-05-09 09:17:01 +00:00
if ($collection->isEmpty()) {
2025-05-07 05:03:54 +00:00
throw new \Exception('Collection or the Table needs to be passed to Realtime for Document/Row events in the Database.');
}
2021-06-30 11:36:58 +00:00
$tableId = $payload->getAttribute('$tableId', '');
$collectionId = $payload->getAttribute('$collectionId', '');
$resourceId = $tableId ?: $collectionId;
2025-04-27 05:03:44 +00:00
$channels[] = 'rows';
$channels[] = 'databases.' . $database->getId() . '.tables.' . $resourceId . '.rows';
$channels[] = 'databases.' . $database->getId() . '.tables.' . $resourceId . '.rows.' . $payload->getId();
2022-08-19 04:04:33 +00:00
2025-05-06 07:22:05 +00:00
$channels[] = 'documents';
$channels[] = 'databases.' . $database->getId() . '.collections.' . $resourceId . '.documents';
$channels[] = 'databases.' . $database->getId() . '.collections.' . $resourceId . '.documents.' . $payload->getId();
2025-05-06 07:22:05 +00:00
2025-05-09 09:17:01 +00:00
$roles = $collection->getAttribute('documentSecurity', false)
? \array_merge($collection->getRead(), $payload->getRead())
: $collection->getRead();
2021-12-16 18:12:06 +00:00
}
2021-06-30 11:36:58 +00:00
break;
case 'buckets':
if ($parts[2] === 'files') {
2022-04-18 16:21:45 +00:00
if ($bucket->isEmpty()) {
2022-08-03 04:17:49 +00:00
throw new \Exception('Bucket needs to be passed to Realtime for File events in the Storage.');
}
$channels[] = 'files';
$channels[] = 'buckets.' . $payload->getAttribute('bucketId') . '.files';
$channels[] = 'buckets.' . $payload->getAttribute('bucketId') . '.files.' . $payload->getId();
2022-08-19 04:04:33 +00:00
2022-08-14 05:24:50 +00:00
$roles = $bucket->getAttribute('fileSecurity', false)
2022-08-13 14:55:15 +00:00
? \array_merge($bucket->getRead(), $payload->getRead())
: $bucket->getRead();
2022-02-13 07:02:34 +00:00
}
2021-06-30 11:36:58 +00:00
break;
case 'functions':
if ($parts[2] === 'executions') {
if (!empty($payload->getRead())) {
$channels[] = 'console';
2024-09-30 14:32:50 +00:00
$channels[] = 'projects.' . $project->getId();
$channels[] = 'executions';
$channels[] = 'executions.' . $payload->getId();
$channels[] = 'functions.' . $payload->getAttribute('functionId');
$roles = $payload->getRead();
}
} elseif ($parts[2] === 'deployments') {
2022-02-28 12:24:35 +00:00
$channels[] = 'console';
2024-09-30 14:32:50 +00:00
$channels[] = 'projects.' . $project->getId();
$projectId = 'console';
2022-08-19 04:04:33 +00:00
$roles = [Role::team($project->getAttribute('teamId'))->toString()];
2021-06-30 11:36:58 +00:00
}
2024-10-28 11:00:06 +00:00
break;
case 'sites':
if ($parts[2] === 'deployments') {
$channels[] = 'console';
$channels[] = 'projects.' . $project->getId();
$projectId = 'console';
$roles = [Role::team($project->getAttribute('teamId'))->toString()];
}
2021-06-30 11:36:58 +00:00
break;
}
return [
'channels' => $channels,
2021-07-13 15:18:02 +00:00
'roles' => $roles,
2021-12-06 12:03:12 +00:00
'permissionsChanged' => $permissionsChanged,
'projectId' => $projectId
2021-06-30 11:36:58 +00:00
];
}
2021-06-28 14:34:28 +00:00
}